From f58534f60c76e4b0f9874eb91be336a49cb62035 Mon Sep 17 00:00:00 2001 From: Terochi Date: Sun, 5 Feb 2023 16:35:11 +0100 Subject: [PATCH 001/111] Extended the length of replay at the end of map --- osu.Game/Screens/Play/Player.cs | 88 +++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 36 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index bf7f38cdd3..fda6bdaa8f 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -613,6 +613,8 @@ namespace osu.Game.Screens.Play // if an exit has been requested, cancel any pending completion (the user has shown intention to exit). resultsDisplayDelegate?.Cancel(); + beginScoreImport(); + // The actual exit is performed if // - the pause / fail dialog was not requested // - the pause / fail dialog was requested but is already displayed (user showing intention to exit). @@ -735,14 +737,9 @@ namespace osu.Game.Screens.Play // is no chance that a user could return to the (already completed) Player instance from a child screen. ValidForResume = false; - // Ensure we are not writing to the replay any more, as we are about to consume and store the score. - DrawableRuleset.SetRecordTarget(null); - if (!Configuration.ShowResults) return; - prepareScoreForDisplayTask ??= Task.Run(prepareAndImportScore); - bool storyboardHasOutro = DimmableStoryboard.ContentDisplayed && !DimmableStoryboard.HasStoryboardEnded.Value; if (storyboardHasOutro) @@ -756,6 +753,56 @@ namespace osu.Game.Screens.Play progressToResults(true); } + /// + /// Queue the results screen for display. + /// + /// + /// A final display will only occur once all work is completed in . This means that even after calling this method, the results screen will never be shown until ScoreProcessor.HasCompleted becomes . + /// + /// Whether a minimum delay () should be added before the screen is displayed. + private void progressToResults(bool withDelay) + { + resultsDisplayDelegate?.Cancel(); + + double delay = withDelay ? RESULTS_DISPLAY_DELAY : 0; + + resultsDisplayDelegate = new ScheduledDelegate(() => + { + if (prepareScoreForDisplayTask == null) + { + beginScoreImport(); + return; + } + + if (!prepareScoreForDisplayTask.IsCompleted) + // If the asynchronous preparation has not completed, keep repeating this delegate. + return; + + resultsDisplayDelegate?.Cancel(); + + if (!this.IsCurrentScreen()) + // This player instance may already be in the process of exiting. + return; + + this.Push(CreateResults(prepareScoreForDisplayTask.GetResultSafely())); + }, Time.Current + delay, 50); + + Scheduler.Add(resultsDisplayDelegate); + } + + private void beginScoreImport() + { + // We do not want to import the score in cases where we don't show results + bool canShowResults = Configuration.ShowResults && ScoreProcessor.HasCompleted.Value && GameplayState.HasPassed; + if (!canShowResults) + return; + + // Ensure we are not writing to the replay any more, as we are about to consume and store the score. + DrawableRuleset.SetRecordTarget(null); + + prepareScoreForDisplayTask ??= Task.Run(prepareAndImportScore); + } + /// /// Asynchronously run score preparation operations (database import, online submission etc.). /// @@ -785,37 +832,6 @@ namespace osu.Game.Screens.Play return scoreCopy.ScoreInfo; } - /// - /// Queue the results screen for display. - /// - /// - /// A final display will only occur once all work is completed in . This means that even after calling this method, the results screen will never be shown until ScoreProcessor.HasCompleted becomes . - /// - /// Whether a minimum delay () should be added before the screen is displayed. - private void progressToResults(bool withDelay) - { - resultsDisplayDelegate?.Cancel(); - - double delay = withDelay ? RESULTS_DISPLAY_DELAY : 0; - - resultsDisplayDelegate = new ScheduledDelegate(() => - { - if (prepareScoreForDisplayTask?.IsCompleted != true) - // If the asynchronous preparation has not completed, keep repeating this delegate. - return; - - resultsDisplayDelegate?.Cancel(); - - if (!this.IsCurrentScreen()) - // This player instance may already be in the process of exiting. - return; - - this.Push(CreateResults(prepareScoreForDisplayTask.GetResultSafely())); - }, Time.Current + delay, 50); - - Scheduler.Add(resultsDisplayDelegate); - } - protected override bool OnScroll(ScrollEvent e) { // During pause, allow global volume adjust regardless of settings. From 45981125860b331d5f5dbaa59966e6d354f2fd4c Mon Sep 17 00:00:00 2001 From: Cootz Date: Sun, 5 Feb 2023 21:46:38 +0300 Subject: [PATCH 002/111] Add OriginalBeatmapHash to ScoreInfo. Update db schema_version, migration --- osu.Game/Beatmaps/BeatmapManager.cs | 8 ++++++++ osu.Game/Database/RealmAccess.cs | 15 +++++++++++++++ osu.Game/Scoring/ScoreInfo.cs | 2 ++ 3 files changed, 25 insertions(+) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index ad56bbbc3a..e972f067ca 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -13,6 +13,7 @@ using System.Threading.Tasks; using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Extensions; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.IO.Stores; using osu.Framework.Platform; using osu.Framework.Testing; @@ -25,6 +26,7 @@ using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.Notifications; using osu.Game.Rulesets; +using osu.Game.Scoring; using osu.Game.Skinning; using osu.Game.Utils; @@ -454,6 +456,12 @@ namespace osu.Game.Beatmaps if (transferCollections) beatmapInfo.TransferCollectionReferences(r, oldMd5Hash); + //Unlinking all scores from this beatmap + r.All().Where(s => s.BeatmapInfoID == beatmapInfo.ID).ForEach(s => s.BeatmapInfo = new BeatmapInfo()); + + //Linking all the previos scores + r.All().Where(s => s.OriginalBeatmapHash == beatmapInfo.Hash).ForEach(s => s.BeatmapInfo = beatmapInfo); + ProcessBeatmap?.Invoke((liveBeatmapSet, false)); }); } diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index 177c671bca..422ceb8af3 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -70,6 +70,7 @@ namespace osu.Game.Database /// 23 2022-08-01 Added LastLocalUpdate to BeatmapInfo. /// 24 2022-08-22 Added MaximumStatistics to ScoreInfo. /// 25 2022-09-18 Remove skins to add with new naming. + /// 26 2023-02-05 Added OriginalBeatmapHash to ScoreInfo. /// private const int schema_version = 25; @@ -865,6 +866,20 @@ namespace osu.Game.Database case 25: // Remove the default skins so they can be added back by SkinManager with updated naming. migration.NewRealm.RemoveRange(migration.NewRealm.All().Where(s => s.Protected)); + break; + case 26: + // Adding origin beatmap hash property to ensure the score corresponds to the version of beatmap it should + // See: https://github.com/ppy/osu/issues/22062 + string ScoreInfoName = getMappedOrOriginalName(typeof(ScoreInfo)); + + var oldScoreInfos = migration.OldRealm.DynamicApi.All(ScoreInfoName); + var newScoreInfos = migration.NewRealm.All(); + + for (int i = 0; i < newScoreInfos.Count(); i++) + { + newScoreInfos.ElementAt(i).OriginalBeatmapHash = oldScoreInfos.ElementAt(i).BeatmapInfo.Hash; + } + break; } } diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 1009474d89..2c029bbe68 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -66,6 +66,8 @@ namespace osu.Game.Scoring [MapTo("MaximumStatistics")] public string MaximumStatisticsJson { get; set; } = string.Empty; + public string OriginalBeatmapHash { get; set; } = string.Empty; + public ScoreInfo(BeatmapInfo? beatmap = null, RulesetInfo? ruleset = null, RealmUser? realmUser = null) { Ruleset = ruleset ?? new RulesetInfo(); From d23e787bc1f341ae41c3fa5a21467d0c9f320973 Mon Sep 17 00:00:00 2001 From: Cootz Date: Sun, 5 Feb 2023 21:55:50 +0300 Subject: [PATCH 003/111] Update `schema_version` --- osu.Game/Database/RealmAccess.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index 422ceb8af3..6f85b3f1be 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -72,7 +72,7 @@ namespace osu.Game.Database /// 25 2022-09-18 Remove skins to add with new naming. /// 26 2023-02-05 Added OriginalBeatmapHash to ScoreInfo. /// - private const int schema_version = 25; + private const int schema_version = 26; /// /// Lock object which is held during sections, blocking realm retrieval during blocking periods. From 4f23e096d76e9ab33140b77472dd4fa6247d9b0c Mon Sep 17 00:00:00 2001 From: Terochi Date: Mon, 6 Feb 2023 07:59:37 +0100 Subject: [PATCH 004/111] Improved readability --- osu.Game/Screens/Play/Player.cs | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index fda6bdaa8f..443108cf34 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -613,6 +613,7 @@ namespace osu.Game.Screens.Play // if an exit has been requested, cancel any pending completion (the user has shown intention to exit). resultsDisplayDelegate?.Cancel(); + // import current score if possible. beginScoreImport(); // The actual exit is performed if @@ -770,7 +771,11 @@ namespace osu.Game.Screens.Play { if (prepareScoreForDisplayTask == null) { - beginScoreImport(); + // Try importing score since the task hasn't been invoked yet. + if (!beginScoreImport()) + // If the task hasn't started, the score will never be imported. + resultsDisplayDelegate?.Cancel(); + return; } @@ -790,17 +795,25 @@ namespace osu.Game.Screens.Play Scheduler.Add(resultsDisplayDelegate); } - private void beginScoreImport() + /// + /// Ends replay recording and runs only when results can be shown + /// + /// + /// Whether the task has been invoked + /// + private bool beginScoreImport() { - // We do not want to import the score in cases where we don't show results - bool canShowResults = Configuration.ShowResults && ScoreProcessor.HasCompleted.Value && GameplayState.HasPassed; - if (!canShowResults) - return; - // Ensure we are not writing to the replay any more, as we are about to consume and store the score. DrawableRuleset.SetRecordTarget(null); + // We do not want to import the score in cases where we don't show results + bool canShowResults = Configuration.ShowResults && ScoreProcessor.HasCompleted.Value && GameplayState.HasPassed; + if (!canShowResults) + return false; + prepareScoreForDisplayTask ??= Task.Run(prepareAndImportScore); + + return true; } /// From 43f7665c9eabe7105bb1a7c1c2ace3784214d28f Mon Sep 17 00:00:00 2001 From: Terochi Date: Mon, 6 Feb 2023 09:49:42 +0100 Subject: [PATCH 005/111] Improved readability again --- osu.Game/Screens/Play/Player.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 443108cf34..2f81b7154a 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -614,7 +614,7 @@ namespace osu.Game.Screens.Play resultsDisplayDelegate?.Cancel(); // import current score if possible. - beginScoreImport(); + attemptScoreImport(); // The actual exit is performed if // - the pause / fail dialog was not requested @@ -772,8 +772,8 @@ namespace osu.Game.Screens.Play if (prepareScoreForDisplayTask == null) { // Try importing score since the task hasn't been invoked yet. - if (!beginScoreImport()) - // If the task hasn't started, the score will never be imported. + if (!attemptScoreImport()) + // If attempt failed, trying again is unnecessary resultsDisplayDelegate?.Cancel(); return; @@ -796,12 +796,12 @@ namespace osu.Game.Screens.Play } /// - /// Ends replay recording and runs only when results can be shown + /// Attempts to run /// /// - /// Whether the task has been invoked + /// Whether the attempt was successful /// - private bool beginScoreImport() + private bool attemptScoreImport() { // Ensure we are not writing to the replay any more, as we are about to consume and store the score. DrawableRuleset.SetRecordTarget(null); From b00848e742d48ef08849d5115690909109c88de9 Mon Sep 17 00:00:00 2001 From: Cootz Date: Mon, 6 Feb 2023 13:58:41 +0300 Subject: [PATCH 006/111] Fix realm error. Apply `OriginalBeatmapHash` on import --- osu.Game/Beatmaps/BeatmapManager.cs | 6 ------ osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs | 1 + 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index e972f067ca..b46859cc59 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -456,12 +456,6 @@ namespace osu.Game.Beatmaps if (transferCollections) beatmapInfo.TransferCollectionReferences(r, oldMd5Hash); - //Unlinking all scores from this beatmap - r.All().Where(s => s.BeatmapInfoID == beatmapInfo.ID).ForEach(s => s.BeatmapInfo = new BeatmapInfo()); - - //Linking all the previos scores - r.All().Where(s => s.OriginalBeatmapHash == beatmapInfo.Hash).ForEach(s => s.BeatmapInfo = beatmapInfo); - ProcessBeatmap?.Invoke((liveBeatmapSet, false)); }); } diff --git a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs index 6f0b0c62f8..4bd068ca0f 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs @@ -123,6 +123,7 @@ namespace osu.Game.Scoring.Legacy // before returning for database import, we must restore the database-sourced BeatmapInfo. // if not, the clone operation in GetPlayableBeatmap will cause a dereference and subsequent database exception. score.ScoreInfo.BeatmapInfo = workingBeatmap.BeatmapInfo; + score.ScoreInfo.OriginalBeatmapHash = workingBeatmap.BeatmapInfo.Hash; return score; } From 2c7386db39d894cfa8ef335e7c58659399ed2e19 Mon Sep 17 00:00:00 2001 From: Cootz Date: Mon, 6 Feb 2023 15:14:14 +0300 Subject: [PATCH 007/111] FIx score appearing on `BeatmapLeaderboard` and `TopLocalRank` --- osu.Game/Screens/Play/Player.cs | 1 + osu.Game/Screens/Select/Carousel/TopLocalRank.cs | 1 + osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs | 1 + 3 files changed, 3 insertions(+) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 0d208e6d9b..7bd020db93 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -248,6 +248,7 @@ namespace osu.Game.Screens.Play // ensure the score is in a consistent state with the current player. Score.ScoreInfo.BeatmapInfo = Beatmap.Value.BeatmapInfo; + Score.ScoreInfo.OriginalBeatmapHash = Beatmap.Value.BeatmapInfo.Hash; Score.ScoreInfo.Ruleset = ruleset.RulesetInfo; Score.ScoreInfo.Mods = gameplayMods; diff --git a/osu.Game/Screens/Select/Carousel/TopLocalRank.cs b/osu.Game/Screens/Select/Carousel/TopLocalRank.cs index f1b773c831..3df72f7d3b 100644 --- a/osu.Game/Screens/Select/Carousel/TopLocalRank.cs +++ b/osu.Game/Screens/Select/Carousel/TopLocalRank.cs @@ -65,6 +65,7 @@ namespace osu.Game.Screens.Select.Carousel 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.OriginalBeatmapHash)}" + $" && {nameof(ScoreInfo.Ruleset)}.{nameof(RulesetInfo.ShortName)} == $2" + $" && {nameof(ScoreInfo.DeletePending)} == false", api.LocalUser.Value.Id, beatmapInfo.ID, ruleset.Value.ShortName), localScoresChanged); diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index 5c720c8491..3e90d2465b 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -191,6 +191,7 @@ namespace osu.Game.Screens.Select.Leaderboards scoreSubscription = realm.RegisterForNotifications(r => r.All().Filter($"{nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.ID)} == $0" + + $" AND {nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.Hash)} == {nameof(ScoreInfo.OriginalBeatmapHash)}" + $" AND {nameof(ScoreInfo.Ruleset)}.{nameof(RulesetInfo.ShortName)} == $1" + $" AND {nameof(ScoreInfo.DeletePending)} == false" , beatmapInfo.ID, ruleset.Value.ShortName), localScoresChanged); From cb26601cb4be90a62d898d230a14ac9cb667ecc3 Mon Sep 17 00:00:00 2001 From: Cootz Date: Tue, 7 Feb 2023 02:29:28 +0300 Subject: [PATCH 008/111] Fix test's score generation --- osu.Game.Tests/Resources/TestResources.cs | 1 + .../Visual/SongSelect/TestSceneBeatmapLeaderboard.cs | 10 ++++++++++ .../Visual/UserInterface/TestSceneDeleteLocalScore.cs | 1 + 3 files changed, 12 insertions(+) diff --git a/osu.Game.Tests/Resources/TestResources.cs b/osu.Game.Tests/Resources/TestResources.cs index adf28afc8e..f65d427649 100644 --- a/osu.Game.Tests/Resources/TestResources.cs +++ b/osu.Game.Tests/Resources/TestResources.cs @@ -176,6 +176,7 @@ namespace osu.Game.Tests.Resources CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", }, BeatmapInfo = beatmap, + OriginalBeatmapHash = beatmap.Hash, Ruleset = beatmap.Ruleset, Mods = new Mod[] { new TestModHardRock(), new TestModDoubleTime() }, TotalScore = 2845370, diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs index ef0ad6c25c..7a578230d9 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs @@ -210,6 +210,7 @@ namespace osu.Game.Tests.Visual.SongSelect }, Ruleset = new OsuRuleset().RulesetInfo, BeatmapInfo = beatmapInfo, + OriginalBeatmapHash = beatmapInfo.Hash, User = new APIUser { Id = 6602580, @@ -226,6 +227,7 @@ namespace osu.Game.Tests.Visual.SongSelect Date = DateTime.Now.AddSeconds(-30), Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, BeatmapInfo = beatmapInfo, + OriginalBeatmapHash = beatmapInfo.Hash, Ruleset = new OsuRuleset().RulesetInfo, User = new APIUser { @@ -243,6 +245,7 @@ namespace osu.Game.Tests.Visual.SongSelect Date = DateTime.Now.AddSeconds(-70), Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, BeatmapInfo = beatmapInfo, + OriginalBeatmapHash = beatmapInfo.Hash, Ruleset = new OsuRuleset().RulesetInfo, User = new APIUser @@ -261,6 +264,7 @@ namespace osu.Game.Tests.Visual.SongSelect Date = DateTime.Now.AddMinutes(-40), Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, BeatmapInfo = beatmapInfo, + OriginalBeatmapHash = beatmapInfo.Hash, Ruleset = new OsuRuleset().RulesetInfo, User = new APIUser @@ -279,6 +283,7 @@ namespace osu.Game.Tests.Visual.SongSelect Date = DateTime.Now.AddHours(-2), Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, BeatmapInfo = beatmapInfo, + OriginalBeatmapHash = beatmapInfo.Hash, Ruleset = new OsuRuleset().RulesetInfo, User = new APIUser @@ -297,6 +302,7 @@ namespace osu.Game.Tests.Visual.SongSelect Date = DateTime.Now.AddHours(-25), Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, BeatmapInfo = beatmapInfo, + OriginalBeatmapHash = beatmapInfo.Hash, Ruleset = new OsuRuleset().RulesetInfo, User = new APIUser @@ -315,6 +321,7 @@ namespace osu.Game.Tests.Visual.SongSelect Date = DateTime.Now.AddHours(-50), Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, BeatmapInfo = beatmapInfo, + OriginalBeatmapHash = beatmapInfo.Hash, Ruleset = new OsuRuleset().RulesetInfo, User = new APIUser @@ -333,6 +340,7 @@ namespace osu.Game.Tests.Visual.SongSelect Date = DateTime.Now.AddHours(-72), Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, BeatmapInfo = beatmapInfo, + OriginalBeatmapHash = beatmapInfo.Hash, Ruleset = new OsuRuleset().RulesetInfo, User = new APIUser @@ -351,6 +359,7 @@ namespace osu.Game.Tests.Visual.SongSelect Date = DateTime.Now.AddMonths(-3), Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, BeatmapInfo = beatmapInfo, + OriginalBeatmapHash = beatmapInfo.Hash, Ruleset = new OsuRuleset().RulesetInfo, User = new APIUser @@ -369,6 +378,7 @@ namespace osu.Game.Tests.Visual.SongSelect Date = DateTime.Now.AddYears(-2), Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, BeatmapInfo = beatmapInfo, + OriginalBeatmapHash = beatmapInfo.Hash, Ruleset = new OsuRuleset().RulesetInfo, User = new APIUser diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs index 7635c61867..14193b1ac8 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs @@ -94,6 +94,7 @@ namespace osu.Game.Tests.Visual.UserInterface { OnlineID = i, BeatmapInfo = beatmapInfo, + OriginalBeatmapHash = beatmapInfo.Hash, Accuracy = RNG.NextDouble(), TotalScore = RNG.Next(1, 1000000), MaxCombo = RNG.Next(1, 1000), From 723f13af259da2e022a5c25b61d84b67c8ad75a9 Mon Sep 17 00:00:00 2001 From: Cootz Date: Tue, 7 Feb 2023 02:43:27 +0300 Subject: [PATCH 009/111] Add summary for `OriginalBeatmapHash` --- osu.Game/Scoring/ScoreInfo.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 2c029bbe68..7ad2d9203a 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -66,6 +66,9 @@ namespace osu.Game.Scoring [MapTo("MaximumStatistics")] public string MaximumStatisticsJson { get; set; } = string.Empty; + /// + /// Hash of the beatmap where it scored + /// public string OriginalBeatmapHash { get; set; } = string.Empty; public ScoreInfo(BeatmapInfo? beatmap = null, RulesetInfo? ruleset = null, RealmUser? realmUser = null) From 1470ea0a311bec07ba3ba6525025939e51322a68 Mon Sep 17 00:00:00 2001 From: Cootz Date: Tue, 7 Feb 2023 03:07:53 +0300 Subject: [PATCH 010/111] Remove unnecessary using directives --- osu.Game/Beatmaps/BeatmapManager.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index b46859cc59..ad56bbbc3a 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -13,7 +13,6 @@ using System.Threading.Tasks; using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Extensions; -using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.IO.Stores; using osu.Framework.Platform; using osu.Framework.Testing; @@ -26,7 +25,6 @@ using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.Notifications; using osu.Game.Rulesets; -using osu.Game.Scoring; using osu.Game.Skinning; using osu.Game.Utils; From a1ee3df453f46a271d7ba7450f791340119cc539 Mon Sep 17 00:00:00 2001 From: Cootz Date: Tue, 7 Feb 2023 03:16:25 +0300 Subject: [PATCH 011/111] Improve local variable naming --- osu.Game/Database/RealmAccess.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index 6f85b3f1be..1ef904fbc3 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -867,12 +867,13 @@ namespace osu.Game.Database // Remove the default skins so they can be added back by SkinManager with updated naming. migration.NewRealm.RemoveRange(migration.NewRealm.All().Where(s => s.Protected)); break; + case 26: // Adding origin beatmap hash property to ensure the score corresponds to the version of beatmap it should // See: https://github.com/ppy/osu/issues/22062 - string ScoreInfoName = getMappedOrOriginalName(typeof(ScoreInfo)); + string scoreInfoName = getMappedOrOriginalName(typeof(ScoreInfo)); - var oldScoreInfos = migration.OldRealm.DynamicApi.All(ScoreInfoName); + var oldScoreInfos = migration.OldRealm.DynamicApi.All(scoreInfoName); var newScoreInfos = migration.NewRealm.All(); for (int i = 0; i < newScoreInfos.Count(); i++) From 957c9e7e276037c9c0db101f5e77cbaf3ceda5da Mon Sep 17 00:00:00 2001 From: Cootz <50776304+Cootz@users.noreply.github.com> Date: Tue, 7 Feb 2023 11:23:39 +0300 Subject: [PATCH 012/111] Update osu.Game/Scoring/ScoreInfo.cs Co-authored-by: Dean Herbert --- osu.Game/Scoring/ScoreInfo.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 7ad2d9203a..2ea40df44d 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -67,9 +67,9 @@ namespace osu.Game.Scoring public string MaximumStatisticsJson { get; set; } = string.Empty; /// - /// Hash of the beatmap where it scored + /// The beatmap's at the point in time when the score was set. /// - public string OriginalBeatmapHash { get; set; } = string.Empty; + public string BeatmapHash { get; set; } = string.Empty; public ScoreInfo(BeatmapInfo? beatmap = null, RulesetInfo? ruleset = null, RealmUser? realmUser = null) { From 7e127dafe21a89f5059f59de5cff7904f34e9783 Mon Sep 17 00:00:00 2001 From: PC Date: Tue, 7 Feb 2023 11:52:47 +0300 Subject: [PATCH 013/111] Update reference --- osu.Game.Tests/Resources/TestResources.cs | 2 +- .../SongSelect/TestSceneBeatmapLeaderboard.cs | 20 +++++++++---------- .../TestSceneDeleteLocalScore.cs | 2 +- osu.Game/Database/RealmAccess.cs | 4 ++-- osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs | 2 +- osu.Game/Scoring/ScoreInfo.cs | 2 +- osu.Game/Screens/Play/Player.cs | 2 +- .../Screens/Select/Carousel/TopLocalRank.cs | 2 +- .../Select/Leaderboards/BeatmapLeaderboard.cs | 2 +- 9 files changed, 19 insertions(+), 19 deletions(-) diff --git a/osu.Game.Tests/Resources/TestResources.cs b/osu.Game.Tests/Resources/TestResources.cs index f65d427649..a2d81c0a75 100644 --- a/osu.Game.Tests/Resources/TestResources.cs +++ b/osu.Game.Tests/Resources/TestResources.cs @@ -176,7 +176,7 @@ namespace osu.Game.Tests.Resources CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", }, BeatmapInfo = beatmap, - OriginalBeatmapHash = beatmap.Hash, + BeatmapHash = beatmap.Hash, Ruleset = beatmap.Ruleset, Mods = new Mod[] { new TestModHardRock(), new TestModDoubleTime() }, TotalScore = 2845370, diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs index 7a578230d9..c4bca79480 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs @@ -210,7 +210,7 @@ namespace osu.Game.Tests.Visual.SongSelect }, Ruleset = new OsuRuleset().RulesetInfo, BeatmapInfo = beatmapInfo, - OriginalBeatmapHash = beatmapInfo.Hash, + BeatmapHash = beatmapInfo.Hash, User = new APIUser { Id = 6602580, @@ -227,7 +227,7 @@ namespace osu.Game.Tests.Visual.SongSelect Date = DateTime.Now.AddSeconds(-30), Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, BeatmapInfo = beatmapInfo, - OriginalBeatmapHash = beatmapInfo.Hash, + BeatmapHash = beatmapInfo.Hash, Ruleset = new OsuRuleset().RulesetInfo, User = new APIUser { @@ -245,7 +245,7 @@ namespace osu.Game.Tests.Visual.SongSelect Date = DateTime.Now.AddSeconds(-70), Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, BeatmapInfo = beatmapInfo, - OriginalBeatmapHash = beatmapInfo.Hash, + BeatmapHash = beatmapInfo.Hash, Ruleset = new OsuRuleset().RulesetInfo, User = new APIUser @@ -264,7 +264,7 @@ namespace osu.Game.Tests.Visual.SongSelect Date = DateTime.Now.AddMinutes(-40), Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, BeatmapInfo = beatmapInfo, - OriginalBeatmapHash = beatmapInfo.Hash, + BeatmapHash = beatmapInfo.Hash, Ruleset = new OsuRuleset().RulesetInfo, User = new APIUser @@ -283,7 +283,7 @@ namespace osu.Game.Tests.Visual.SongSelect Date = DateTime.Now.AddHours(-2), Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, BeatmapInfo = beatmapInfo, - OriginalBeatmapHash = beatmapInfo.Hash, + BeatmapHash = beatmapInfo.Hash, Ruleset = new OsuRuleset().RulesetInfo, User = new APIUser @@ -302,7 +302,7 @@ namespace osu.Game.Tests.Visual.SongSelect Date = DateTime.Now.AddHours(-25), Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, BeatmapInfo = beatmapInfo, - OriginalBeatmapHash = beatmapInfo.Hash, + BeatmapHash = beatmapInfo.Hash, Ruleset = new OsuRuleset().RulesetInfo, User = new APIUser @@ -321,7 +321,7 @@ namespace osu.Game.Tests.Visual.SongSelect Date = DateTime.Now.AddHours(-50), Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, BeatmapInfo = beatmapInfo, - OriginalBeatmapHash = beatmapInfo.Hash, + BeatmapHash = beatmapInfo.Hash, Ruleset = new OsuRuleset().RulesetInfo, User = new APIUser @@ -340,7 +340,7 @@ namespace osu.Game.Tests.Visual.SongSelect Date = DateTime.Now.AddHours(-72), Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, BeatmapInfo = beatmapInfo, - OriginalBeatmapHash = beatmapInfo.Hash, + BeatmapHash = beatmapInfo.Hash, Ruleset = new OsuRuleset().RulesetInfo, User = new APIUser @@ -359,7 +359,7 @@ namespace osu.Game.Tests.Visual.SongSelect Date = DateTime.Now.AddMonths(-3), Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, BeatmapInfo = beatmapInfo, - OriginalBeatmapHash = beatmapInfo.Hash, + BeatmapHash = beatmapInfo.Hash, Ruleset = new OsuRuleset().RulesetInfo, User = new APIUser @@ -378,7 +378,7 @@ namespace osu.Game.Tests.Visual.SongSelect Date = DateTime.Now.AddYears(-2), Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, BeatmapInfo = beatmapInfo, - OriginalBeatmapHash = beatmapInfo.Hash, + BeatmapHash = beatmapInfo.Hash, Ruleset = new OsuRuleset().RulesetInfo, User = new APIUser diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs index 14193b1ac8..529874b71e 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs @@ -94,7 +94,7 @@ namespace osu.Game.Tests.Visual.UserInterface { OnlineID = i, BeatmapInfo = beatmapInfo, - OriginalBeatmapHash = beatmapInfo.Hash, + BeatmapHash = beatmapInfo.Hash, Accuracy = RNG.NextDouble(), TotalScore = RNG.Next(1, 1000000), MaxCombo = RNG.Next(1, 1000), diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index 1ef904fbc3..b151fc6474 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -70,7 +70,7 @@ namespace osu.Game.Database /// 23 2022-08-01 Added LastLocalUpdate to BeatmapInfo. /// 24 2022-08-22 Added MaximumStatistics to ScoreInfo. /// 25 2022-09-18 Remove skins to add with new naming. - /// 26 2023-02-05 Added OriginalBeatmapHash to ScoreInfo. + /// 26 2023-02-05 Added BeatmapHash to ScoreInfo. /// private const int schema_version = 26; @@ -878,7 +878,7 @@ namespace osu.Game.Database for (int i = 0; i < newScoreInfos.Count(); i++) { - newScoreInfos.ElementAt(i).OriginalBeatmapHash = oldScoreInfos.ElementAt(i).BeatmapInfo.Hash; + newScoreInfos.ElementAt(i).BeatmapHash = oldScoreInfos.ElementAt(i).BeatmapInfo.Hash; } break; diff --git a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs index 4bd068ca0f..9b145ad56e 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs @@ -123,7 +123,7 @@ namespace osu.Game.Scoring.Legacy // before returning for database import, we must restore the database-sourced BeatmapInfo. // if not, the clone operation in GetPlayableBeatmap will cause a dereference and subsequent database exception. score.ScoreInfo.BeatmapInfo = workingBeatmap.BeatmapInfo; - score.ScoreInfo.OriginalBeatmapHash = workingBeatmap.BeatmapInfo.Hash; + score.ScoreInfo.BeatmapHash = workingBeatmap.BeatmapInfo.Hash; return score; } diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 2ea40df44d..62adcb9f94 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -67,7 +67,7 @@ namespace osu.Game.Scoring public string MaximumStatisticsJson { get; set; } = string.Empty; /// - /// The beatmap's at the point in time when the score was set. + /// The at the point in time when the score was set. /// public string BeatmapHash { get; set; } = string.Empty; diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 7bd020db93..a3d8d3237c 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -248,7 +248,7 @@ namespace osu.Game.Screens.Play // ensure the score is in a consistent state with the current player. Score.ScoreInfo.BeatmapInfo = Beatmap.Value.BeatmapInfo; - Score.ScoreInfo.OriginalBeatmapHash = Beatmap.Value.BeatmapInfo.Hash; + Score.ScoreInfo.BeatmapHash = Beatmap.Value.BeatmapInfo.Hash; Score.ScoreInfo.Ruleset = ruleset.RulesetInfo; Score.ScoreInfo.Mods = gameplayMods; diff --git a/osu.Game/Screens/Select/Carousel/TopLocalRank.cs b/osu.Game/Screens/Select/Carousel/TopLocalRank.cs index 3df72f7d3b..a57a8b0f27 100644 --- a/osu.Game/Screens/Select/Carousel/TopLocalRank.cs +++ b/osu.Game/Screens/Select/Carousel/TopLocalRank.cs @@ -65,7 +65,7 @@ namespace osu.Game.Screens.Select.Carousel 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.OriginalBeatmapHash)}" + + $" && {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), localScoresChanged); diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index 3e90d2465b..2b40b9faf8 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -191,7 +191,7 @@ namespace osu.Game.Screens.Select.Leaderboards scoreSubscription = realm.RegisterForNotifications(r => r.All().Filter($"{nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.ID)} == $0" - + $" AND {nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.Hash)} == {nameof(ScoreInfo.OriginalBeatmapHash)}" + + $" AND {nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.Hash)} == {nameof(ScoreInfo.BeatmapHash)}" + $" AND {nameof(ScoreInfo.Ruleset)}.{nameof(RulesetInfo.ShortName)} == $1" + $" AND {nameof(ScoreInfo.DeletePending)} == false" , beatmapInfo.ID, ruleset.Value.ShortName), localScoresChanged); From 338d96534a2ad7b27d82eeeafb2c778fb67d0b67 Mon Sep 17 00:00:00 2001 From: Cootz Date: Wed, 8 Feb 2023 05:01:54 +0300 Subject: [PATCH 014/111] Add leaderboard test on beatmap update --- .../SongSelect/TestSceneBeatmapLeaderboard.cs | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs index c4bca79480..13a24cd490 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs @@ -96,6 +96,37 @@ namespace osu.Game.Tests.Visual.SongSelect checkCount(0); } + [Test] + public void TestLocalScoresDisplayOnBeatmapEdit() + { + BeatmapInfo beatmapInfo = null!; + + AddStep(@"Set scope", () => leaderboard.Scope = BeatmapLeaderboardScope.Local); + + AddStep(@"Set beatmap", () => + { + beatmapManager.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely(); + beatmapInfo = beatmapManager.GetAllUsableBeatmapSets().First().Beatmaps.First(); + + leaderboard.BeatmapInfo = beatmapInfo; + }); + + clearScores(); + checkCount(0); + + loadMoreScores(() => beatmapInfo); + checkCount(10); + + beatmapEdit(() => beatmapInfo); + checkCount(0); + + loadMoreScores(() => beatmapInfo); + checkCount(10); + + clearScores(); + checkCount(0); + } + [Test] public void TestGlobalScoresDisplay() { @@ -123,6 +154,21 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep(@"None selected", () => leaderboard.SetErrorState(LeaderboardState.NoneSelected)); } + private void beatmapEdit(Func beatmapInfo) + { + AddStep(@"Update beatmap via BeatmapManager", () => + { + BeatmapInfo info = beatmapInfo(); + IBeatmap beatmap = beatmapManager.GetWorkingBeatmap(info).Beatmap; + + beatmap.Difficulty.ApproachRate = 11; + beatmap.Difficulty.DrainRate = 11; + beatmap.Difficulty.OverallDifficulty = 11; + + beatmapManager.Save(info, beatmap); + }); + } + private void showPersonalBestWithNullPosition() { leaderboard.SetScores(leaderboard.Scores, new ScoreInfo From 391af2791b875a9928bba1d92dbcbae2128c0dd0 Mon Sep 17 00:00:00 2001 From: Cootz Date: Wed, 8 Feb 2023 05:23:42 +0300 Subject: [PATCH 015/111] Fix CSharpWarnings::CS1574,CS1584,CS1581,CS1580 --- osu.Game/Scoring/ScoreInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 62adcb9f94..8c912ef32a 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -67,7 +67,7 @@ namespace osu.Game.Scoring public string MaximumStatisticsJson { get; set; } = string.Empty; /// - /// The at the point in time when the score was set. + /// The .Hash at the point in time when the score was set. /// public string BeatmapHash { get; set; } = string.Empty; From 6bf56aff73e07b7cd92241b251ae0ea0d9787b99 Mon Sep 17 00:00:00 2001 From: Cootz Date: Wed, 8 Feb 2023 05:40:20 +0300 Subject: [PATCH 016/111] Add warning for `ScoreInfo` --- osu.Game/Scoring/ScoreInfo.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 8c912ef32a..c57e06bee5 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -22,6 +22,13 @@ using Realms; namespace osu.Game.Scoring { + + /// + /// Store information about the score + /// + /// + /// If you work on inporting/adding score please ensure you provide both BeatmapInfo and BeatmapHash + /// [ExcludeFromDynamicCompile] [MapTo("Score")] public class ScoreInfo : RealmObject, IHasGuidPrimaryKey, IHasRealmFiles, ISoftDelete, IEquatable, IScoreInfo From ab7c9a200bdcc7804927ca68b9c59da58d22d49f Mon Sep 17 00:00:00 2001 From: Cootz Date: Wed, 8 Feb 2023 05:42:06 +0300 Subject: [PATCH 017/111] Fix a typo --- osu.Game/Scoring/ScoreInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index c57e06bee5..85452ede17 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -27,7 +27,7 @@ namespace osu.Game.Scoring /// Store information about the score /// /// - /// If you work on inporting/adding score please ensure you provide both BeatmapInfo and BeatmapHash + /// Warning: If you work on importing/adding score please ensure you provide both BeatmapInfo and BeatmapHash /// [ExcludeFromDynamicCompile] [MapTo("Score")] From 4ba915268c5e0a34824d2a03cb501a12574707a4 Mon Sep 17 00:00:00 2001 From: Cootz Date: Wed, 8 Feb 2023 05:46:47 +0300 Subject: [PATCH 018/111] Change a comment into `RealmAccess` --- osu.Game/Database/RealmAccess.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index b151fc6474..861a74e163 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -869,7 +869,7 @@ namespace osu.Game.Database break; case 26: - // Adding origin beatmap hash property to ensure the score corresponds to the version of beatmap it should + // Add ScoreInfo.BeatmapHash property to ensure the score corresponds to the version of beatmap it should // See: https://github.com/ppy/osu/issues/22062 string scoreInfoName = getMappedOrOriginalName(typeof(ScoreInfo)); From 086b3eb542348048da062fde8234239e8d7d3ef6 Mon Sep 17 00:00:00 2001 From: Cootz Date: Wed, 8 Feb 2023 05:50:52 +0300 Subject: [PATCH 019/111] Fix minor formating issues --- osu.Game/Scoring/ScoreInfo.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 85452ede17..d2ec4ebd5f 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -22,10 +22,9 @@ using Realms; namespace osu.Game.Scoring { - /// /// Store information about the score - /// + /// /// /// Warning: If you work on importing/adding score please ensure you provide both BeatmapInfo and BeatmapHash /// From 5c113ddb030445c5099fdd2334d6b2608f5851ef Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 8 Feb 2023 14:20:58 +0900 Subject: [PATCH 020/111] Reword xmldoc to read better --- osu.Game/Scoring/ScoreInfo.cs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index d2ec4ebd5f..6213c65c75 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -23,11 +23,8 @@ using Realms; namespace osu.Game.Scoring { /// - /// Store information about the score + /// A realm model containing metadata for a single score. /// - /// - /// Warning: If you work on importing/adding score please ensure you provide both BeatmapInfo and BeatmapHash - /// [ExcludeFromDynamicCompile] [MapTo("Score")] public class ScoreInfo : RealmObject, IHasGuidPrimaryKey, IHasRealmFiles, ISoftDelete, IEquatable, IScoreInfo @@ -35,8 +32,19 @@ namespace osu.Game.Scoring [PrimaryKey] public Guid ID { get; set; } + /// + /// The this score was made against. + /// + /// + /// When setting this, make sure to also set to allow relational consistency when a beatmap is potentially changed. + /// public BeatmapInfo BeatmapInfo { get; set; } = null!; + /// + /// The at the point in time when the score was set. + /// + public string BeatmapHash { get; set; } = string.Empty; + public RulesetInfo Ruleset { get; set; } = null!; public IList Files { get; } = null!; @@ -72,11 +80,6 @@ namespace osu.Game.Scoring [MapTo("MaximumStatistics")] public string MaximumStatisticsJson { get; set; } = string.Empty; - /// - /// The .Hash at the point in time when the score was set. - /// - public string BeatmapHash { get; set; } = string.Empty; - public ScoreInfo(BeatmapInfo? beatmap = null, RulesetInfo? ruleset = null, RealmUser? realmUser = null) { Ruleset = ruleset ?? new RulesetInfo(); From c50ea89bc99f3dc142e85ecec3bf2504d3fd5f32 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 8 Feb 2023 14:24:06 +0900 Subject: [PATCH 021/111] Simplify migration to not rely on old/dynamic schema --- osu.Game/Database/RealmAccess.cs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index 861a74e163..831e328439 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -869,17 +869,11 @@ namespace osu.Game.Database break; case 26: - // Add ScoreInfo.BeatmapHash property to ensure the score corresponds to the version of beatmap it should - // See: https://github.com/ppy/osu/issues/22062 - string scoreInfoName = getMappedOrOriginalName(typeof(ScoreInfo)); + // Add ScoreInfo.BeatmapHash property to ensure scores correspond to the correct version of beatmap. + var scores = migration.NewRealm.All(); - var oldScoreInfos = migration.OldRealm.DynamicApi.All(scoreInfoName); - var newScoreInfos = migration.NewRealm.All(); - - for (int i = 0; i < newScoreInfos.Count(); i++) - { - newScoreInfos.ElementAt(i).BeatmapHash = oldScoreInfos.ElementAt(i).BeatmapInfo.Hash; - } + foreach (var score in scores) + score.BeatmapHash = score.BeatmapInfo.Hash; break; } From c7eec371f52dd403a0bfaeea6f645d4a56dd3857 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 8 Feb 2023 15:22:17 +0900 Subject: [PATCH 022/111] Clean up tests somewhat --- .../SongSelect/TestSceneBeatmapLeaderboard.cs | 41 ++++++++----------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs index 13a24cd490..83bb58804d 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs @@ -86,10 +86,10 @@ namespace osu.Game.Tests.Visual.SongSelect clearScores(); checkCount(0); - loadMoreScores(() => beatmapInfo); + importMoreScores(() => beatmapInfo); checkCount(10); - loadMoreScores(() => beatmapInfo); + importMoreScores(() => beatmapInfo); checkCount(20); clearScores(); @@ -114,13 +114,23 @@ namespace osu.Game.Tests.Visual.SongSelect clearScores(); checkCount(0); - loadMoreScores(() => beatmapInfo); + importMoreScores(() => beatmapInfo); checkCount(10); - beatmapEdit(() => beatmapInfo); + AddStep(@"Save beatmap with changes", () => + { + IBeatmap beatmap = beatmapManager.GetWorkingBeatmap(beatmapInfo).Beatmap; + + beatmap.Difficulty.ApproachRate = 11; + beatmap.Difficulty.DrainRate = 11; + beatmap.Difficulty.OverallDifficulty = 11; + + beatmapManager.Save(beatmapInfo, beatmap); + }); + checkCount(0); - loadMoreScores(() => beatmapInfo); + importMoreScores(() => beatmapInfo); checkCount(10); clearScores(); @@ -154,21 +164,6 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep(@"None selected", () => leaderboard.SetErrorState(LeaderboardState.NoneSelected)); } - private void beatmapEdit(Func beatmapInfo) - { - AddStep(@"Update beatmap via BeatmapManager", () => - { - BeatmapInfo info = beatmapInfo(); - IBeatmap beatmap = beatmapManager.GetWorkingBeatmap(info).Beatmap; - - beatmap.Difficulty.ApproachRate = 11; - beatmap.Difficulty.DrainRate = 11; - beatmap.Difficulty.OverallDifficulty = 11; - - beatmapManager.Save(info, beatmap); - }); - } - private void showPersonalBestWithNullPosition() { leaderboard.SetScores(leaderboard.Scores, new ScoreInfo @@ -208,9 +203,9 @@ namespace osu.Game.Tests.Visual.SongSelect }); } - private void loadMoreScores(Func beatmapInfo) + private void importMoreScores(Func beatmapInfo) { - AddStep(@"Load new scores via manager", () => + AddStep(@"Import new scores", () => { foreach (var score in generateSampleScores(beatmapInfo())) scoreManager.Import(score); @@ -223,7 +218,7 @@ namespace osu.Game.Tests.Visual.SongSelect } private void checkCount(int expected) => - AddUntilStep("Correct count displayed", () => leaderboard.ChildrenOfType().Count() == expected); + AddUntilStep($"{expected} scores displayed", () => leaderboard.ChildrenOfType().Count(), () => Is.EqualTo(expected)); private static ScoreInfo[] generateSampleScores(BeatmapInfo beatmapInfo) { From d4d985ba0f0378d3c93a71b17e5cfcb793a7b41b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 8 Feb 2023 15:34:46 +0900 Subject: [PATCH 023/111] Improve test to also check that reverting hash restores old scores --- .../SongSelect/TestSceneBeatmapLeaderboard.cs | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs index 83bb58804d..01397563fd 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs @@ -100,10 +100,11 @@ namespace osu.Game.Tests.Visual.SongSelect public void TestLocalScoresDisplayOnBeatmapEdit() { BeatmapInfo beatmapInfo = null!; + string originalHash = string.Empty; AddStep(@"Set scope", () => leaderboard.Scope = BeatmapLeaderboardScope.Local); - AddStep(@"Set beatmap", () => + AddStep(@"Import beatmap", () => { beatmapManager.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely(); beatmapInfo = beatmapManager.GetAllUsableBeatmapSets().First().Beatmaps.First(); @@ -114,23 +115,39 @@ namespace osu.Game.Tests.Visual.SongSelect clearScores(); checkCount(0); + AddStep(@"Perform initial save to guarantee stable hash", () => + { + IBeatmap beatmap = beatmapManager.GetWorkingBeatmap(beatmapInfo).Beatmap; + beatmapManager.Save(beatmapInfo, beatmap); + + originalHash = beatmapInfo.Hash; + }); + importMoreScores(() => beatmapInfo); checkCount(10); - AddStep(@"Save beatmap with changes", () => + AddStep(@"Save with changes", () => { IBeatmap beatmap = beatmapManager.GetWorkingBeatmap(beatmapInfo).Beatmap; - - beatmap.Difficulty.ApproachRate = 11; - beatmap.Difficulty.DrainRate = 11; - beatmap.Difficulty.OverallDifficulty = 11; - + beatmap.Difficulty.ApproachRate = 12; beatmapManager.Save(beatmapInfo, beatmap); }); + AddAssert("Hash changed", () => beatmapInfo.Hash, () => Is.Not.EqualTo(originalHash)); checkCount(0); importMoreScores(() => beatmapInfo); + importMoreScores(() => beatmapInfo); + checkCount(20); + + AddStep(@"Revert changes", () => + { + IBeatmap beatmap = beatmapManager.GetWorkingBeatmap(beatmapInfo).Beatmap; + beatmap.Difficulty.ApproachRate = 8; + beatmapManager.Save(beatmapInfo, beatmap); + }); + + AddAssert("Hash restored", () => beatmapInfo.Hash, () => Is.EqualTo(originalHash)); checkCount(10); clearScores(); From 38031fdf2327cb50705cd14fa55c4cb1cb058a9d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 8 Feb 2023 15:38:07 +0900 Subject: [PATCH 024/111] Add test coverage of stores stored in database as well --- .../SongSelect/TestSceneBeatmapLeaderboard.cs | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs index 01397563fd..c234cc8a9c 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs @@ -84,16 +84,16 @@ namespace osu.Game.Tests.Visual.SongSelect }); clearScores(); - checkCount(0); + checkDisplayedCount(0); importMoreScores(() => beatmapInfo); - checkCount(10); + checkDisplayedCount(10); importMoreScores(() => beatmapInfo); - checkCount(20); + checkDisplayedCount(20); clearScores(); - checkCount(0); + checkDisplayedCount(0); } [Test] @@ -113,7 +113,7 @@ namespace osu.Game.Tests.Visual.SongSelect }); clearScores(); - checkCount(0); + checkDisplayedCount(0); AddStep(@"Perform initial save to guarantee stable hash", () => { @@ -124,7 +124,9 @@ namespace osu.Game.Tests.Visual.SongSelect }); importMoreScores(() => beatmapInfo); - checkCount(10); + + checkDisplayedCount(10); + checkStoredCount(10); AddStep(@"Save with changes", () => { @@ -134,11 +136,13 @@ namespace osu.Game.Tests.Visual.SongSelect }); AddAssert("Hash changed", () => beatmapInfo.Hash, () => Is.Not.EqualTo(originalHash)); - checkCount(0); + checkDisplayedCount(0); + checkStoredCount(10); importMoreScores(() => beatmapInfo); importMoreScores(() => beatmapInfo); - checkCount(20); + checkDisplayedCount(20); + checkStoredCount(30); AddStep(@"Revert changes", () => { @@ -148,10 +152,12 @@ namespace osu.Game.Tests.Visual.SongSelect }); AddAssert("Hash restored", () => beatmapInfo.Hash, () => Is.EqualTo(originalHash)); - checkCount(10); + checkDisplayedCount(10); + checkStoredCount(30); clearScores(); - checkCount(0); + checkDisplayedCount(0); + checkStoredCount(0); } [Test] @@ -234,9 +240,12 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("Clear all scores", () => scoreManager.Delete()); } - private void checkCount(int expected) => + private void checkDisplayedCount(int expected) => AddUntilStep($"{expected} scores displayed", () => leaderboard.ChildrenOfType().Count(), () => Is.EqualTo(expected)); + private void checkStoredCount(int expected) => + AddUntilStep($"Total scores stored is {expected}", () => Realm.Run(r => r.All().Count(s => !s.DeletePending)), () => Is.EqualTo(expected)); + private static ScoreInfo[] generateSampleScores(BeatmapInfo beatmapInfo) { return new[] From 4fdba880b1efc2dcb49a2f3c64585f2e896a64ba Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 8 Feb 2023 15:39:18 +0900 Subject: [PATCH 025/111] Fix xmldoc reference fail at CI --- osu.Game/Scoring/ScoreInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 6213c65c75..02c7acf350 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -41,7 +41,7 @@ namespace osu.Game.Scoring public BeatmapInfo BeatmapInfo { get; set; } = null!; /// - /// The at the point in time when the score was set. + /// The at the point in time when the score was set. /// public string BeatmapHash { get; set; } = string.Empty; From 7aaaf7fca2d598998d7c407a3b259db985484b06 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Feb 2023 16:55:35 +0900 Subject: [PATCH 026/111] Combine and attempt to simplify the score import / preparation process further --- osu.Game/Rulesets/UI/RulesetInputManager.cs | 3 + osu.Game/Screens/Play/Player.cs | 82 ++++++++++----------- 2 files changed, 43 insertions(+), 42 deletions(-) diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index a5e442b7de..7bf0482673 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -39,6 +39,9 @@ namespace osu.Game.Rulesets.UI { set { + if (value == recorder) + return; + if (value != null && recorder != null) throw new InvalidOperationException("Cannot attach more than one recorder"); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 0825f36a0a..bc453d2151 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -276,7 +276,7 @@ namespace osu.Game.Screens.Play }, FailOverlay = new FailOverlay { - SaveReplay = prepareAndImportScore, + SaveReplay = async () => await prepareAndImportScoreAsync(true).ConfigureAwait(false), OnRetry = () => Restart(), OnQuit = () => PerformExit(true), }, @@ -614,7 +614,7 @@ namespace osu.Game.Screens.Play resultsDisplayDelegate?.Cancel(); // import current score if possible. - attemptScoreImport(); + prepareAndImportScoreAsync(); // The actual exit is performed if // - the pause / fail dialog was not requested @@ -772,10 +772,7 @@ namespace osu.Game.Screens.Play if (prepareScoreForDisplayTask == null) { // Try importing score since the task hasn't been invoked yet. - if (!attemptScoreImport()) - // If attempt failed, trying again is unnecessary - resultsDisplayDelegate?.Cancel(); - + prepareAndImportScoreAsync(); return; } @@ -785,6 +782,12 @@ namespace osu.Game.Screens.Play resultsDisplayDelegate?.Cancel(); + if (prepareScoreForDisplayTask.GetResultSafely() == null) + { + // If score import did not occur, we do not want to show the results screen. + return; + } + if (!this.IsCurrentScreen()) // This player instance may already be in the process of exiting. return; @@ -796,53 +799,48 @@ namespace osu.Game.Screens.Play } /// - /// Attempts to run + /// Asynchronously run score preparation operations (database import, online submission etc.). /// - /// - /// Whether the attempt was successful - /// - private bool attemptScoreImport() + /// Whether the score should be imported even if non-passing (or the current configuration doesn't allow for it). + /// The final score. + [ItemCanBeNull] + private Task prepareAndImportScoreAsync(bool forceImport = false) { // Ensure we are not writing to the replay any more, as we are about to consume and store the score. DrawableRuleset.SetRecordTarget(null); + if (prepareScoreForDisplayTask != null) + return prepareScoreForDisplayTask; + // We do not want to import the score in cases where we don't show results bool canShowResults = Configuration.ShowResults && ScoreProcessor.HasCompleted.Value && GameplayState.HasPassed; - if (!canShowResults) - return false; + if (!canShowResults && !forceImport) + return Task.FromResult(null); - prepareScoreForDisplayTask ??= Task.Run(prepareAndImportScore); - - return true; - } - - /// - /// Asynchronously run score preparation operations (database import, online submission etc.). - /// - /// The final score. - private async Task prepareAndImportScore() - { - var scoreCopy = Score.DeepClone(); - - try + return prepareScoreForDisplayTask = Task.Run(async () => { - await PrepareScoreForResultsAsync(scoreCopy).ConfigureAwait(false); - } - catch (Exception ex) - { - Logger.Error(ex, @"Score preparation failed!"); - } + var scoreCopy = Score.DeepClone(); - try - { - await ImportScore(scoreCopy).ConfigureAwait(false); - } - catch (Exception ex) - { - Logger.Error(ex, @"Score import failed!"); - } + try + { + await PrepareScoreForResultsAsync(scoreCopy).ConfigureAwait(false); + } + catch (Exception ex) + { + Logger.Error(ex, @"Score preparation failed!"); + } - return scoreCopy.ScoreInfo; + try + { + await ImportScore(scoreCopy).ConfigureAwait(false); + } + catch (Exception ex) + { + Logger.Error(ex, @"Score import failed!"); + } + + return scoreCopy.ScoreInfo; + }); } protected override bool OnScroll(ScrollEvent e) From 1f586c129c1b27fee148db26846e69eb6febc1d8 Mon Sep 17 00:00:00 2001 From: Cootz Date: Wed, 15 Feb 2023 22:15:44 +0300 Subject: [PATCH 027/111] fix applied --- osu.Game/Database/LegacyExporter.cs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/osu.Game/Database/LegacyExporter.cs b/osu.Game/Database/LegacyExporter.cs index 09d6913dd9..c56c17222d 100644 --- a/osu.Game/Database/LegacyExporter.cs +++ b/osu.Game/Database/LegacyExporter.cs @@ -3,6 +3,7 @@ #nullable disable +using System; using System.Collections.Generic; using System.IO; using osu.Framework.Platform; @@ -18,6 +19,14 @@ namespace osu.Game.Database public abstract class LegacyExporter where TModel : class, IHasNamedFiles { + /// + /// Max length of filename (including extension) + /// + /// + /// This constant is smaller 256 because adds additional "_" to the end of the path + /// + private const int max_path = 255 - (32 + 4 + 2); //max path - (Guid + Guid "D" format chars + Storage.CreateFileSafely chars) + /// /// The file extension for exports (including the leading '.'). /// @@ -33,7 +42,16 @@ namespace osu.Game.Database UserFileStorage = storage.GetStorageForDirectory(@"files"); } - protected virtual string GetFilename(TModel item) => item.GetDisplayString(); + protected virtual string GetFilename(TModel item) + { + string fileName = item.GetDisplayString(); + + int fileNameLength = fileName.Length - FileExtension.Length; + if (fileNameLength > max_path) + fileName = fileName.Remove(max_path - FileExtension.Length); //Truncating the name to fit the path limit + + return fileName; + } /// /// Exports an item to a legacy (.zip based) package. From 387a6f1330b3e22360565dd07a4a7048eae3e4d8 Mon Sep 17 00:00:00 2001 From: Cootz Date: Wed, 15 Feb 2023 22:43:43 +0300 Subject: [PATCH 028/111] Move logic to `Export` method --- osu.Game/Database/LegacyExporter.cs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/osu.Game/Database/LegacyExporter.cs b/osu.Game/Database/LegacyExporter.cs index cd0b50c109..483c3cbd5c 100644 --- a/osu.Game/Database/LegacyExporter.cs +++ b/osu.Game/Database/LegacyExporter.cs @@ -43,16 +43,7 @@ namespace osu.Game.Database UserFileStorage = storage.GetStorageForDirectory(@"files"); } - protected virtual string GetFilename(TModel item) - { - string fileName = item.GetDisplayString(); - - int fileNameLength = fileName.Length - FileExtension.Length; - if (fileNameLength > max_path) - fileName = fileName.Remove(max_path - FileExtension.Length); //Truncating the name to fit the path limit - - return fileName; - } + protected virtual string GetFilename(TModel item) => item.GetDisplayString(); /// /// Exports an item to a legacy (.zip based) package. @@ -68,6 +59,15 @@ namespace osu.Game.Database .Concat(exportStorage.GetDirectories(string.Empty)); string filename = NamingUtils.GetNextBestFilename(existingExports, $"{itemFilename}{FileExtension}"); + + if (filename.Length > max_path) + { + string filenameWithoutExtension = Path.GetFileNameWithoutExtension(filename); + + filenameWithoutExtension = filenameWithoutExtension.Remove(max_path - FileExtension.Length); //Truncating the name to fit the path limit + filename = $"{filenameWithoutExtension}{FileExtension}"; + } + using (var stream = exportStorage.CreateFileSafely(filename)) ExportModelTo(item, stream); From f1da213beaaaba85b82c785def1c43f6cb1f62ec Mon Sep 17 00:00:00 2001 From: Cootz Date: Thu, 16 Feb 2023 16:26:57 +0300 Subject: [PATCH 029/111] Add tests --- osu.Game.Tests/Database/LegacyExporterTest.cs | 85 +++++++++++++++++++ osu.Game/Database/LegacyExporter.cs | 6 +- 2 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 osu.Game.Tests/Database/LegacyExporterTest.cs diff --git a/osu.Game.Tests/Database/LegacyExporterTest.cs b/osu.Game.Tests/Database/LegacyExporterTest.cs new file mode 100644 index 0000000000..5f07e6c917 --- /dev/null +++ b/osu.Game.Tests/Database/LegacyExporterTest.cs @@ -0,0 +1,85 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Text; +using NUnit.Framework; +using osu.Framework.Platform; +using osu.Framework.Testing; +using osu.Game.Database; + +namespace osu.Game.Tests.Database +{ + public class LegacyExporterTest + { + private TestLegacyExporter? legacyExporter; + private TemporaryNativeStorage? storage; + + [SetUp] + public void SetupLegacyExporter() + { + storage = new TemporaryNativeStorage("export-storage"); + legacyExporter = new TestLegacyExporter(storage); + } + + [Test] + public void ExportFileWithNormalName() + { + var exportStorage = storage?.GetStorageForDirectory(@"exports"); + + string filename = "normal file name"; + var item = new TestPathInfo(filename); + + Assert.That(item.FileName.Length < TestLegacyExporter.GetMaxPath(), Is.True); + Assert.DoesNotThrow(() => legacyExporter?.Export(item)); + Assert.That(exportStorage?.Exists($"{filename}{legacyExporter?.GetExtension()}"), Is.True); + } + + [Test] + public void ExportFileWithSuperLongName() + { + var exportStorage = storage?.GetStorageForDirectory(@"exports"); + + string fullname = "some file with super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name"; + + int capacity = TestLegacyExporter.GetMaxPath() - (legacyExporter?.GetExtension().Length ?? 0); + string expectedName = fullname.Remove(capacity); + + var item = new TestPathInfo(fullname); + + Assert.That(item.FileName.Length > TestLegacyExporter.GetMaxPath(), Is.True); + Assert.DoesNotThrow(() => legacyExporter?.Export(item)); + Assert.That(exportStorage?.Exists($"{expectedName}{legacyExporter?.GetExtension()}"), Is.True); + } + + [TearDown] + public void CleanupAfterTest() + { + storage?.Dispose(); + } + + private class TestPathInfo : IHasNamedFiles + { + public string FileName { get; set; } = string.Empty; + + public TestPathInfo(string fileName) => FileName = fileName; + + public IEnumerable Files { get; set; } = new List(); + + public override string ToString() => FileName; + } + + private class TestLegacyExporter : LegacyExporter + { + public TestLegacyExporter(Storage storage) : base(storage) { } + + public static int GetMaxPath() => MAX_PATH; + + public string GetExtension() => FileExtension; + + protected override string FileExtension => ".ots"; + } + } +} diff --git a/osu.Game/Database/LegacyExporter.cs b/osu.Game/Database/LegacyExporter.cs index 483c3cbd5c..9041f58368 100644 --- a/osu.Game/Database/LegacyExporter.cs +++ b/osu.Game/Database/LegacyExporter.cs @@ -26,7 +26,7 @@ namespace osu.Game.Database /// /// This constant is smaller 256 because adds additional "_" to the end of the path /// - private const int max_path = 255 - (32 + 4 + 2); //max path - (Guid + Guid "D" format chars + Storage.CreateFileSafely chars) + protected const int MAX_PATH = 255 - (32 + 4 + 2); //max path - (Guid + Guid "D" format chars + Storage.CreateFileSafely chars) /// /// The file extension for exports (including the leading '.'). @@ -60,11 +60,11 @@ namespace osu.Game.Database string filename = NamingUtils.GetNextBestFilename(existingExports, $"{itemFilename}{FileExtension}"); - if (filename.Length > max_path) + if (filename.Length > MAX_PATH) { string filenameWithoutExtension = Path.GetFileNameWithoutExtension(filename); - filenameWithoutExtension = filenameWithoutExtension.Remove(max_path - FileExtension.Length); //Truncating the name to fit the path limit + filenameWithoutExtension = filenameWithoutExtension.Remove(MAX_PATH - FileExtension.Length); //Truncating the name to fit the path limit filename = $"{filenameWithoutExtension}{FileExtension}"; } From 6819a45a1bdba3fdd2b28a6d7983b73116ddbded Mon Sep 17 00:00:00 2001 From: Cootz Date: Thu, 16 Feb 2023 16:42:07 +0300 Subject: [PATCH 030/111] Improve code slightly --- osu.Game.Tests/Database/LegacyExporterTest.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Database/LegacyExporterTest.cs b/osu.Game.Tests/Database/LegacyExporterTest.cs index 5f07e6c917..c464eb5c28 100644 --- a/osu.Game.Tests/Database/LegacyExporterTest.cs +++ b/osu.Game.Tests/Database/LegacyExporterTest.cs @@ -2,9 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Text; using NUnit.Framework; using osu.Framework.Platform; using osu.Framework.Testing; @@ -29,7 +26,7 @@ namespace osu.Game.Tests.Database { var exportStorage = storage?.GetStorageForDirectory(@"exports"); - string filename = "normal file name"; + const string filename = "normal file name"; var item = new TestPathInfo(filename); Assert.That(item.FileName.Length < TestLegacyExporter.GetMaxPath(), Is.True); @@ -42,7 +39,7 @@ namespace osu.Game.Tests.Database { var exportStorage = storage?.GetStorageForDirectory(@"exports"); - string fullname = "some file with super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name"; + const string fullname = "some file with super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name"; int capacity = TestLegacyExporter.GetMaxPath() - (legacyExporter?.GetExtension().Length ?? 0); string expectedName = fullname.Remove(capacity); @@ -62,7 +59,7 @@ namespace osu.Game.Tests.Database private class TestPathInfo : IHasNamedFiles { - public string FileName { get; set; } = string.Empty; + public string FileName { get; set; } public TestPathInfo(string fileName) => FileName = fileName; @@ -73,7 +70,10 @@ namespace osu.Game.Tests.Database private class TestLegacyExporter : LegacyExporter { - public TestLegacyExporter(Storage storage) : base(storage) { } + public TestLegacyExporter(Storage storage) + : base(storage) + { + } public static int GetMaxPath() => MAX_PATH; From 6cb00cd42f22b7324df66a2863a0fe333875d2f8 Mon Sep 17 00:00:00 2001 From: Cootz Date: Fri, 17 Feb 2023 01:44:45 +0300 Subject: [PATCH 031/111] Add more test cases --- osu.Game.Tests/Database/LegacyExporterTest.cs | 54 ++++++++++++++++--- 1 file changed, 48 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Database/LegacyExporterTest.cs b/osu.Game.Tests/Database/LegacyExporterTest.cs index c464eb5c28..c67ec75e92 100644 --- a/osu.Game.Tests/Database/LegacyExporterTest.cs +++ b/osu.Game.Tests/Database/LegacyExporterTest.cs @@ -22,7 +22,7 @@ namespace osu.Game.Tests.Database } [Test] - public void ExportFileWithNormalName() + public void ExportFileWithNormalNameTest() { var exportStorage = storage?.GetStorageForDirectory(@"exports"); @@ -30,23 +30,65 @@ namespace osu.Game.Tests.Database var item = new TestPathInfo(filename); Assert.That(item.FileName.Length < TestLegacyExporter.GetMaxPath(), Is.True); - Assert.DoesNotThrow(() => legacyExporter?.Export(item)); - Assert.That(exportStorage?.Exists($"{filename}{legacyExporter?.GetExtension()}"), Is.True); + exportItemWithLongNameAndAssert(item, exportStorage, filename); } [Test] - public void ExportFileWithSuperLongName() + public void ExportFileWithNormalNameMultipleTimesTest() + { + var exportStorage = storage?.GetStorageForDirectory(@"exports"); + + const string filename = "normal file name"; + var item = new TestPathInfo(filename); + + Assert.That(item.FileName.Length < TestLegacyExporter.GetMaxPath(), Is.True); + + //Export multiple times + string expectedFileName; + for (int i = 0; i < 10; i++) + { + expectedFileName = i == 0 ? filename : $"{filename} ({i})"; + exportItemWithLongNameAndAssert(item, exportStorage, expectedFileName); + } + } + + [Test] + public void ExportFileWithSuperLongNameTest() { var exportStorage = storage?.GetStorageForDirectory(@"exports"); const string fullname = "some file with super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name"; - int capacity = TestLegacyExporter.GetMaxPath() - (legacyExporter?.GetExtension().Length ?? 0); - string expectedName = fullname.Remove(capacity); + int expectedLength = TestLegacyExporter.GetMaxPath() - (legacyExporter?.GetExtension().Length ?? 0); + string expectedName = fullname.Remove(expectedLength); var item = new TestPathInfo(fullname); Assert.That(item.FileName.Length > TestLegacyExporter.GetMaxPath(), Is.True); + exportItemWithLongNameAndAssert(item, exportStorage, expectedName); + } + + [Test] + public void ExportFileWithSuperLongNameMultipleTimesTest() + { + var exportStorage = storage?.GetStorageForDirectory(@"exports"); + + const string fullname = "some file with super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name"; + + int expectedLength = TestLegacyExporter.GetMaxPath() - (legacyExporter?.GetExtension().Length ?? 0); + string expectedName = fullname.Remove(expectedLength); + + var item = new TestPathInfo(fullname); + + Assert.That(item.FileName.Length > TestLegacyExporter.GetMaxPath(), Is.True); + + //Export multiple times + for (int i = 0; i < 10; i++) + exportItemWithLongNameAndAssert(item, exportStorage, expectedName); + } + + private void exportItemWithLongNameAndAssert(IHasNamedFiles item, Storage? exportStorage, string expectedName) + { Assert.DoesNotThrow(() => legacyExporter?.Export(item)); Assert.That(exportStorage?.Exists($"{expectedName}{legacyExporter?.GetExtension()}"), Is.True); } From 1d8b348e4c72d29d44ce0f56d9d6e71f98000187 Mon Sep 17 00:00:00 2001 From: Cootz Date: Fri, 17 Feb 2023 01:46:15 +0300 Subject: [PATCH 032/111] Improve naming --- osu.Game.Tests/Database/LegacyExporterTest.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Database/LegacyExporterTest.cs b/osu.Game.Tests/Database/LegacyExporterTest.cs index c67ec75e92..97b32b0c51 100644 --- a/osu.Game.Tests/Database/LegacyExporterTest.cs +++ b/osu.Game.Tests/Database/LegacyExporterTest.cs @@ -30,7 +30,7 @@ namespace osu.Game.Tests.Database var item = new TestPathInfo(filename); Assert.That(item.FileName.Length < TestLegacyExporter.GetMaxPath(), Is.True); - exportItemWithLongNameAndAssert(item, exportStorage, filename); + exportItemAndAssert(item, exportStorage, filename); } [Test] @@ -48,7 +48,7 @@ namespace osu.Game.Tests.Database for (int i = 0; i < 10; i++) { expectedFileName = i == 0 ? filename : $"{filename} ({i})"; - exportItemWithLongNameAndAssert(item, exportStorage, expectedFileName); + exportItemAndAssert(item, exportStorage, expectedFileName); } } @@ -65,7 +65,7 @@ namespace osu.Game.Tests.Database var item = new TestPathInfo(fullname); Assert.That(item.FileName.Length > TestLegacyExporter.GetMaxPath(), Is.True); - exportItemWithLongNameAndAssert(item, exportStorage, expectedName); + exportItemAndAssert(item, exportStorage, expectedName); } [Test] @@ -84,10 +84,10 @@ namespace osu.Game.Tests.Database //Export multiple times for (int i = 0; i < 10; i++) - exportItemWithLongNameAndAssert(item, exportStorage, expectedName); + exportItemAndAssert(item, exportStorage, expectedName); } - private void exportItemWithLongNameAndAssert(IHasNamedFiles item, Storage? exportStorage, string expectedName) + private void exportItemAndAssert(IHasNamedFiles item, Storage? exportStorage, string expectedName) { Assert.DoesNotThrow(() => legacyExporter?.Export(item)); Assert.That(exportStorage?.Exists($"{expectedName}{legacyExporter?.GetExtension()}"), Is.True); From f4038a49a17f8670f3b3ed8613e12d197d4e96b6 Mon Sep 17 00:00:00 2001 From: Cootz Date: Fri, 17 Feb 2023 01:50:24 +0300 Subject: [PATCH 033/111] Fix inspectCode issues --- osu.Game.Tests/Database/LegacyExporterTest.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Tests/Database/LegacyExporterTest.cs b/osu.Game.Tests/Database/LegacyExporterTest.cs index 97b32b0c51..d67cb8abf1 100644 --- a/osu.Game.Tests/Database/LegacyExporterTest.cs +++ b/osu.Game.Tests/Database/LegacyExporterTest.cs @@ -44,10 +44,9 @@ namespace osu.Game.Tests.Database Assert.That(item.FileName.Length < TestLegacyExporter.GetMaxPath(), Is.True); //Export multiple times - string expectedFileName; for (int i = 0; i < 10; i++) { - expectedFileName = i == 0 ? filename : $"{filename} ({i})"; + string expectedFileName = i == 0 ? filename : $"{filename} ({i})"; exportItemAndAssert(item, exportStorage, expectedFileName); } } From 415220a44769fa8a7027ec8d56eb38b63fac6e04 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Feb 2023 13:30:00 +0900 Subject: [PATCH 034/111] Tidy up new test method code quality --- osu.Game.Tests/Database/LegacyExporterTest.cs | 55 +++++++++++-------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/osu.Game.Tests/Database/LegacyExporterTest.cs b/osu.Game.Tests/Database/LegacyExporterTest.cs index d67cb8abf1..803cd40132 100644 --- a/osu.Game.Tests/Database/LegacyExporterTest.cs +++ b/osu.Game.Tests/Database/LegacyExporterTest.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using NUnit.Framework; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Platform; using osu.Framework.Testing; using osu.Game.Database; @@ -11,11 +12,11 @@ namespace osu.Game.Tests.Database { public class LegacyExporterTest { - private TestLegacyExporter? legacyExporter; - private TemporaryNativeStorage? storage; + private TestLegacyExporter legacyExporter = null!; + private TemporaryNativeStorage storage = null!; [SetUp] - public void SetupLegacyExporter() + public void SetUp() { storage = new TemporaryNativeStorage("export-storage"); legacyExporter = new TestLegacyExporter(storage); @@ -24,24 +25,24 @@ namespace osu.Game.Tests.Database [Test] public void ExportFileWithNormalNameTest() { - var exportStorage = storage?.GetStorageForDirectory(@"exports"); + var exportStorage = storage.GetStorageForDirectory(@"exports"); const string filename = "normal file name"; var item = new TestPathInfo(filename); - Assert.That(item.FileName.Length < TestLegacyExporter.GetMaxPath(), Is.True); + Assert.That(item.Filename.Length < TestLegacyExporter.GetMaxPath(), Is.True); exportItemAndAssert(item, exportStorage, filename); } [Test] public void ExportFileWithNormalNameMultipleTimesTest() { - var exportStorage = storage?.GetStorageForDirectory(@"exports"); + var exportStorage = storage.GetStorageForDirectory(@"exports"); const string filename = "normal file name"; var item = new TestPathInfo(filename); - Assert.That(item.FileName.Length < TestLegacyExporter.GetMaxPath(), Is.True); + Assert.That(item.Filename.Length < TestLegacyExporter.GetMaxPath(), Is.True); //Export multiple times for (int i = 0; i < 10; i++) @@ -54,59 +55,65 @@ namespace osu.Game.Tests.Database [Test] public void ExportFileWithSuperLongNameTest() { - var exportStorage = storage?.GetStorageForDirectory(@"exports"); + var exportStorage = storage.GetStorageForDirectory(@"exports"); - const string fullname = "some file with super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name"; + const string fullname = + "some file with super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name"; - int expectedLength = TestLegacyExporter.GetMaxPath() - (legacyExporter?.GetExtension().Length ?? 0); + int expectedLength = TestLegacyExporter.GetMaxPath() - (legacyExporter.GetExtension().Length); string expectedName = fullname.Remove(expectedLength); var item = new TestPathInfo(fullname); - Assert.That(item.FileName.Length > TestLegacyExporter.GetMaxPath(), Is.True); + Assert.That(item.Filename.Length > TestLegacyExporter.GetMaxPath(), Is.True); exportItemAndAssert(item, exportStorage, expectedName); } [Test] public void ExportFileWithSuperLongNameMultipleTimesTest() { - var exportStorage = storage?.GetStorageForDirectory(@"exports"); + var exportStorage = storage.GetStorageForDirectory(@"exports"); - const string fullname = "some file with super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name"; + const string fullname = + "some file with super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name"; - int expectedLength = TestLegacyExporter.GetMaxPath() - (legacyExporter?.GetExtension().Length ?? 0); + int expectedLength = TestLegacyExporter.GetMaxPath() - (legacyExporter.GetExtension().Length); string expectedName = fullname.Remove(expectedLength); var item = new TestPathInfo(fullname); - Assert.That(item.FileName.Length > TestLegacyExporter.GetMaxPath(), Is.True); + Assert.That(item.Filename.Length > TestLegacyExporter.GetMaxPath(), Is.True); //Export multiple times for (int i = 0; i < 10; i++) exportItemAndAssert(item, exportStorage, expectedName); } - private void exportItemAndAssert(IHasNamedFiles item, Storage? exportStorage, string expectedName) + private void exportItemAndAssert(IHasNamedFiles item, Storage exportStorage, string expectedName) { - Assert.DoesNotThrow(() => legacyExporter?.Export(item)); - Assert.That(exportStorage?.Exists($"{expectedName}{legacyExporter?.GetExtension()}"), Is.True); + Assert.DoesNotThrow(() => legacyExporter.Export(item)); + Assert.That(exportStorage.Exists($"{expectedName}{legacyExporter.GetExtension()}"), Is.True); } [TearDown] - public void CleanupAfterTest() + public void TearDown() { - storage?.Dispose(); + if (storage.IsNotNull()) + storage.Dispose(); } private class TestPathInfo : IHasNamedFiles { - public string FileName { get; set; } + public string Filename { get; } - public TestPathInfo(string fileName) => FileName = fileName; + public IEnumerable Files { get; } = new List(); - public IEnumerable Files { get; set; } = new List(); + public TestPathInfo(string filename) + { + Filename = filename; + } - public override string ToString() => FileName; + public override string ToString() => Filename; } private class TestLegacyExporter : LegacyExporter From 96b1498932c4b93126a477cf1acc1b93d11c1b57 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Feb 2023 13:33:22 +0900 Subject: [PATCH 035/111] Rename max length variable to make sense (it's a filename limit, not path) --- osu.Game.Tests/Database/LegacyExporterTest.cs | 14 +++++++------- osu.Game/Database/LegacyExporter.cs | 12 +++++++----- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/osu.Game.Tests/Database/LegacyExporterTest.cs b/osu.Game.Tests/Database/LegacyExporterTest.cs index 803cd40132..6f144ee833 100644 --- a/osu.Game.Tests/Database/LegacyExporterTest.cs +++ b/osu.Game.Tests/Database/LegacyExporterTest.cs @@ -30,7 +30,7 @@ namespace osu.Game.Tests.Database const string filename = "normal file name"; var item = new TestPathInfo(filename); - Assert.That(item.Filename.Length < TestLegacyExporter.GetMaxPath(), Is.True); + Assert.That(item.Filename.Length, Is.LessThan(TestLegacyExporter.GetMaxPathLength())); exportItemAndAssert(item, exportStorage, filename); } @@ -42,7 +42,7 @@ namespace osu.Game.Tests.Database const string filename = "normal file name"; var item = new TestPathInfo(filename); - Assert.That(item.Filename.Length < TestLegacyExporter.GetMaxPath(), Is.True); + Assert.That(item.Filename.Length < TestLegacyExporter.GetMaxPathLength(), Is.True); //Export multiple times for (int i = 0; i < 10; i++) @@ -60,12 +60,12 @@ namespace osu.Game.Tests.Database const string fullname = "some file with super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name"; - int expectedLength = TestLegacyExporter.GetMaxPath() - (legacyExporter.GetExtension().Length); + int expectedLength = TestLegacyExporter.GetMaxPathLength() - (legacyExporter.GetExtension().Length); string expectedName = fullname.Remove(expectedLength); var item = new TestPathInfo(fullname); - Assert.That(item.Filename.Length > TestLegacyExporter.GetMaxPath(), Is.True); + Assert.That(item.Filename.Length > TestLegacyExporter.GetMaxPathLength(), Is.True); exportItemAndAssert(item, exportStorage, expectedName); } @@ -77,12 +77,12 @@ namespace osu.Game.Tests.Database const string fullname = "some file with super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name"; - int expectedLength = TestLegacyExporter.GetMaxPath() - (legacyExporter.GetExtension().Length); + int expectedLength = TestLegacyExporter.GetMaxPathLength() - (legacyExporter.GetExtension().Length); string expectedName = fullname.Remove(expectedLength); var item = new TestPathInfo(fullname); - Assert.That(item.Filename.Length > TestLegacyExporter.GetMaxPath(), Is.True); + Assert.That(item.Filename.Length > TestLegacyExporter.GetMaxPathLength(), Is.True); //Export multiple times for (int i = 0; i < 10; i++) @@ -123,7 +123,7 @@ namespace osu.Game.Tests.Database { } - public static int GetMaxPath() => MAX_PATH; + public static int GetMaxPathLength() => MAX_FILENAME_LENGTH; public string GetExtension() => FileExtension; diff --git a/osu.Game/Database/LegacyExporter.cs b/osu.Game/Database/LegacyExporter.cs index 9041f58368..1b74454027 100644 --- a/osu.Game/Database/LegacyExporter.cs +++ b/osu.Game/Database/LegacyExporter.cs @@ -21,12 +21,14 @@ namespace osu.Game.Database where TModel : class, IHasNamedFiles { /// - /// Max length of filename (including extension) + /// Max length of filename (including extension). /// /// - /// This constant is smaller 256 because adds additional "_" to the end of the path + /// TODO: WHY? DOCUMENTATION PER PLATFORM LINKS HERE. + /// + /// This actual usable length is smaller 256 because adds additional "_" to the end of the path /// - protected const int MAX_PATH = 255 - (32 + 4 + 2); //max path - (Guid + Guid "D" format chars + Storage.CreateFileSafely chars) + protected const int MAX_FILENAME_LENGTH = 255 - (32 + 4 + 2); //max path - (Guid + Guid "D" format chars + Storage.CreateFileSafely chars) /// /// The file extension for exports (including the leading '.'). @@ -60,11 +62,11 @@ namespace osu.Game.Database string filename = NamingUtils.GetNextBestFilename(existingExports, $"{itemFilename}{FileExtension}"); - if (filename.Length > MAX_PATH) + if (filename.Length > MAX_FILENAME_LENGTH) { string filenameWithoutExtension = Path.GetFileNameWithoutExtension(filename); - filenameWithoutExtension = filenameWithoutExtension.Remove(MAX_PATH - FileExtension.Length); //Truncating the name to fit the path limit + filenameWithoutExtension = filenameWithoutExtension.Remove(MAX_FILENAME_LENGTH - FileExtension.Length); //Truncating the name to fit the path limit filename = $"{filenameWithoutExtension}{FileExtension}"; } From 8c772a723f0f8ae52cf3141fab286c64c2eba198 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Feb 2023 13:34:19 +0900 Subject: [PATCH 036/111] Expose constant `public`ly rather than reexposing business --- osu.Game/Database/LegacyExporter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/LegacyExporter.cs b/osu.Game/Database/LegacyExporter.cs index 1b74454027..7434a46602 100644 --- a/osu.Game/Database/LegacyExporter.cs +++ b/osu.Game/Database/LegacyExporter.cs @@ -28,7 +28,7 @@ namespace osu.Game.Database /// /// This actual usable length is smaller 256 because adds additional "_" to the end of the path /// - protected const int MAX_FILENAME_LENGTH = 255 - (32 + 4 + 2); //max path - (Guid + Guid "D" format chars + Storage.CreateFileSafely chars) + public const int MAX_FILENAME_LENGTH = 255 - (32 + 4 + 2); //max path - (Guid + Guid "D" format chars + Storage.CreateFileSafely chars) /// /// The file extension for exports (including the leading '.'). From 99236f0ae80399d9fade78c7331dff63d59301e4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Feb 2023 13:39:24 +0900 Subject: [PATCH 037/111] Move long filename to fixture level --- osu.Game.Tests/Database/LegacyExporterTest.cs | 31 ++++++++----------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/osu.Game.Tests/Database/LegacyExporterTest.cs b/osu.Game.Tests/Database/LegacyExporterTest.cs index 6f144ee833..114872bcf8 100644 --- a/osu.Game.Tests/Database/LegacyExporterTest.cs +++ b/osu.Game.Tests/Database/LegacyExporterTest.cs @@ -15,6 +15,9 @@ namespace osu.Game.Tests.Database private TestLegacyExporter legacyExporter = null!; private TemporaryNativeStorage storage = null!; + private const string long_filename = + "some file with super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name"; + [SetUp] public void SetUp() { @@ -30,7 +33,7 @@ namespace osu.Game.Tests.Database const string filename = "normal file name"; var item = new TestPathInfo(filename); - Assert.That(item.Filename.Length, Is.LessThan(TestLegacyExporter.GetMaxPathLength())); + Assert.That(item.Filename.Length, Is.LessThan(TestLegacyExporter.MAX_FILENAME_LENGTH)); exportItemAndAssert(item, exportStorage, filename); } @@ -42,7 +45,7 @@ namespace osu.Game.Tests.Database const string filename = "normal file name"; var item = new TestPathInfo(filename); - Assert.That(item.Filename.Length < TestLegacyExporter.GetMaxPathLength(), Is.True); + Assert.That(item.Filename.Length < TestLegacyExporter.MAX_FILENAME_LENGTH, Is.True); //Export multiple times for (int i = 0; i < 10; i++) @@ -57,15 +60,12 @@ namespace osu.Game.Tests.Database { var exportStorage = storage.GetStorageForDirectory(@"exports"); - const string fullname = - "some file with super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name"; + int expectedLength = TestLegacyExporter.MAX_FILENAME_LENGTH - (legacyExporter.GetExtension().Length); + string expectedName = long_filename.Remove(expectedLength); - int expectedLength = TestLegacyExporter.GetMaxPathLength() - (legacyExporter.GetExtension().Length); - string expectedName = fullname.Remove(expectedLength); + var item = new TestPathInfo(long_filename); - var item = new TestPathInfo(fullname); - - Assert.That(item.Filename.Length > TestLegacyExporter.GetMaxPathLength(), Is.True); + Assert.That(item.Filename.Length > TestLegacyExporter.MAX_FILENAME_LENGTH, Is.True); exportItemAndAssert(item, exportStorage, expectedName); } @@ -74,15 +74,12 @@ namespace osu.Game.Tests.Database { var exportStorage = storage.GetStorageForDirectory(@"exports"); - const string fullname = - "some file with super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name"; + int expectedLength = TestLegacyExporter.MAX_FILENAME_LENGTH - (legacyExporter.GetExtension().Length); + string expectedName = long_filename.Remove(expectedLength); - int expectedLength = TestLegacyExporter.GetMaxPathLength() - (legacyExporter.GetExtension().Length); - string expectedName = fullname.Remove(expectedLength); + var item = new TestPathInfo(long_filename); - var item = new TestPathInfo(fullname); - - Assert.That(item.Filename.Length > TestLegacyExporter.GetMaxPathLength(), Is.True); + Assert.That(item.Filename.Length > TestLegacyExporter.MAX_FILENAME_LENGTH, Is.True); //Export multiple times for (int i = 0; i < 10; i++) @@ -123,8 +120,6 @@ namespace osu.Game.Tests.Database { } - public static int GetMaxPathLength() => MAX_FILENAME_LENGTH; - public string GetExtension() => FileExtension; protected override string FileExtension => ".ots"; From 4560ae6b026bd1ce575713ccab7d9052f73af40b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Feb 2023 13:39:31 +0900 Subject: [PATCH 038/111] Mark test as fixture --- osu.Game.Tests/Database/LegacyExporterTest.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Database/LegacyExporterTest.cs b/osu.Game.Tests/Database/LegacyExporterTest.cs index 114872bcf8..ec20028a2c 100644 --- a/osu.Game.Tests/Database/LegacyExporterTest.cs +++ b/osu.Game.Tests/Database/LegacyExporterTest.cs @@ -10,6 +10,7 @@ using osu.Game.Database; namespace osu.Game.Tests.Database { + [TestFixture] public class LegacyExporterTest { private TestLegacyExporter legacyExporter = null!; From 86d110e893cd2572377c575fc67e5260a1199e27 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Feb 2023 13:43:42 +0900 Subject: [PATCH 039/111] Simplify test storage by removing nested storage --- osu.Game.Tests/Database/LegacyExporterTest.cs | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Database/LegacyExporterTest.cs b/osu.Game.Tests/Database/LegacyExporterTest.cs index ec20028a2c..f38ffb7db6 100644 --- a/osu.Game.Tests/Database/LegacyExporterTest.cs +++ b/osu.Game.Tests/Database/LegacyExporterTest.cs @@ -29,20 +29,16 @@ namespace osu.Game.Tests.Database [Test] public void ExportFileWithNormalNameTest() { - var exportStorage = storage.GetStorageForDirectory(@"exports"); - const string filename = "normal file name"; var item = new TestPathInfo(filename); Assert.That(item.Filename.Length, Is.LessThan(TestLegacyExporter.MAX_FILENAME_LENGTH)); - exportItemAndAssert(item, exportStorage, filename); + exportItemAndAssert(item, filename); } [Test] public void ExportFileWithNormalNameMultipleTimesTest() { - var exportStorage = storage.GetStorageForDirectory(@"exports"); - const string filename = "normal file name"; var item = new TestPathInfo(filename); @@ -52,29 +48,25 @@ namespace osu.Game.Tests.Database for (int i = 0; i < 10; i++) { string expectedFileName = i == 0 ? filename : $"{filename} ({i})"; - exportItemAndAssert(item, exportStorage, expectedFileName); + exportItemAndAssert(item, expectedFileName); } } [Test] public void ExportFileWithSuperLongNameTest() { - var exportStorage = storage.GetStorageForDirectory(@"exports"); - int expectedLength = TestLegacyExporter.MAX_FILENAME_LENGTH - (legacyExporter.GetExtension().Length); string expectedName = long_filename.Remove(expectedLength); var item = new TestPathInfo(long_filename); Assert.That(item.Filename.Length > TestLegacyExporter.MAX_FILENAME_LENGTH, Is.True); - exportItemAndAssert(item, exportStorage, expectedName); + exportItemAndAssert(item, expectedName); } [Test] public void ExportFileWithSuperLongNameMultipleTimesTest() { - var exportStorage = storage.GetStorageForDirectory(@"exports"); - int expectedLength = TestLegacyExporter.MAX_FILENAME_LENGTH - (legacyExporter.GetExtension().Length); string expectedName = long_filename.Remove(expectedLength); @@ -84,13 +76,13 @@ namespace osu.Game.Tests.Database //Export multiple times for (int i = 0; i < 10; i++) - exportItemAndAssert(item, exportStorage, expectedName); + exportItemAndAssert(item, expectedName); } - private void exportItemAndAssert(IHasNamedFiles item, Storage exportStorage, string expectedName) + private void exportItemAndAssert(IHasNamedFiles item, string expectedName) { Assert.DoesNotThrow(() => legacyExporter.Export(item)); - Assert.That(exportStorage.Exists($"{expectedName}{legacyExporter.GetExtension()}"), Is.True); + Assert.That(storage.Exists($"exports/{expectedName}{legacyExporter.GetExtension()}"), Is.True); } [TearDown] From 8ef3fb26e056b2c271a690be5539983f6d9d09b0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Feb 2023 13:45:27 +0900 Subject: [PATCH 040/111] More constants and assert fixes --- osu.Game.Tests/Database/LegacyExporterTest.cs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Database/LegacyExporterTest.cs b/osu.Game.Tests/Database/LegacyExporterTest.cs index f38ffb7db6..b8dca6dc7b 100644 --- a/osu.Game.Tests/Database/LegacyExporterTest.cs +++ b/osu.Game.Tests/Database/LegacyExporterTest.cs @@ -16,6 +16,8 @@ namespace osu.Game.Tests.Database private TestLegacyExporter legacyExporter = null!; private TemporaryNativeStorage storage = null!; + private const string short_filename = "normal file name"; + private const string long_filename = "some file with super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name"; @@ -29,25 +31,24 @@ namespace osu.Game.Tests.Database [Test] public void ExportFileWithNormalNameTest() { - const string filename = "normal file name"; - var item = new TestPathInfo(filename); + var item = new TestPathInfo(short_filename); Assert.That(item.Filename.Length, Is.LessThan(TestLegacyExporter.MAX_FILENAME_LENGTH)); - exportItemAndAssert(item, filename); + + exportItemAndAssert(item, short_filename); } [Test] public void ExportFileWithNormalNameMultipleTimesTest() { - const string filename = "normal file name"; - var item = new TestPathInfo(filename); + var item = new TestPathInfo(short_filename); - Assert.That(item.Filename.Length < TestLegacyExporter.MAX_FILENAME_LENGTH, Is.True); + Assert.That(item.Filename.Length, Is.LessThan(TestLegacyExporter.MAX_FILENAME_LENGTH)); //Export multiple times for (int i = 0; i < 10; i++) { - string expectedFileName = i == 0 ? filename : $"{filename} ({i})"; + string expectedFileName = i == 0 ? short_filename : $"{short_filename} ({i})"; exportItemAndAssert(item, expectedFileName); } } @@ -60,7 +61,7 @@ namespace osu.Game.Tests.Database var item = new TestPathInfo(long_filename); - Assert.That(item.Filename.Length > TestLegacyExporter.MAX_FILENAME_LENGTH, Is.True); + Assert.That(item.Filename.Length, Is.GreaterThan(TestLegacyExporter.MAX_FILENAME_LENGTH)); exportItemAndAssert(item, expectedName); } @@ -72,7 +73,7 @@ namespace osu.Game.Tests.Database var item = new TestPathInfo(long_filename); - Assert.That(item.Filename.Length > TestLegacyExporter.MAX_FILENAME_LENGTH, Is.True); + Assert.That(item.Filename.Length, Is.GreaterThan(TestLegacyExporter.MAX_FILENAME_LENGTH)); //Export multiple times for (int i = 0; i < 10; i++) From 372b6b794cae69dc16a882f0caa3b91dcfef0491 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Feb 2023 13:45:43 +0900 Subject: [PATCH 041/111] I don't know what .ots is but let's not use random file extension that make no sense --- osu.Game.Tests/Database/LegacyExporterTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Database/LegacyExporterTest.cs b/osu.Game.Tests/Database/LegacyExporterTest.cs index b8dca6dc7b..c9aea80585 100644 --- a/osu.Game.Tests/Database/LegacyExporterTest.cs +++ b/osu.Game.Tests/Database/LegacyExporterTest.cs @@ -116,7 +116,7 @@ namespace osu.Game.Tests.Database public string GetExtension() => FileExtension; - protected override string FileExtension => ".ots"; + protected override string FileExtension => ".test"; } } } From ceed3606cd7a3b0666c345ea768219ac5e65b91d Mon Sep 17 00:00:00 2001 From: Cootz <50776304+Cootz@users.noreply.github.com> Date: Fri, 17 Feb 2023 13:46:06 +0300 Subject: [PATCH 042/111] Remove redundant comment Co-authored-by: Dean Herbert --- osu.Game/Database/LegacyExporter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/LegacyExporter.cs b/osu.Game/Database/LegacyExporter.cs index 7434a46602..7fe0428db3 100644 --- a/osu.Game/Database/LegacyExporter.cs +++ b/osu.Game/Database/LegacyExporter.cs @@ -66,7 +66,7 @@ namespace osu.Game.Database { string filenameWithoutExtension = Path.GetFileNameWithoutExtension(filename); - filenameWithoutExtension = filenameWithoutExtension.Remove(MAX_FILENAME_LENGTH - FileExtension.Length); //Truncating the name to fit the path limit + filenameWithoutExtension = filenameWithoutExtension.Remove(MAX_FILENAME_LENGTH - FileExtension.Length); filename = $"{filenameWithoutExtension}{FileExtension}"; } From a3b440493aeae5fc3fbea99e36be28758c8fe2ba Mon Sep 17 00:00:00 2001 From: Cootz Date: Fri, 17 Feb 2023 15:23:43 +0300 Subject: [PATCH 043/111] Update xml doc --- osu.Game/Database/LegacyExporter.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/osu.Game/Database/LegacyExporter.cs b/osu.Game/Database/LegacyExporter.cs index 7fe0428db3..2d0a1d3528 100644 --- a/osu.Game/Database/LegacyExporter.cs +++ b/osu.Game/Database/LegacyExporter.cs @@ -24,9 +24,18 @@ namespace osu.Game.Database /// Max length of filename (including extension). /// /// - /// TODO: WHY? DOCUMENTATION PER PLATFORM LINKS HERE. - /// - /// This actual usable length is smaller 256 because adds additional "_" to the end of the path + /// + /// Filename limit for most OSs is 255, this actual usable length is smaller because adds additional "_" to the end of the path. + /// + /// + /// For more information see: + ///
+ /// File specification syntax + ///
+ ///
+ /// File systems limitations + ///
+ ///
///
public const int MAX_FILENAME_LENGTH = 255 - (32 + 4 + 2); //max path - (Guid + Guid "D" format chars + Storage.CreateFileSafely chars) From fd1beaef87ef2156b5f5e56bf6d1656ece326a16 Mon Sep 17 00:00:00 2001 From: Cootz Date: Fri, 17 Feb 2023 15:24:27 +0300 Subject: [PATCH 044/111] Fix typo --- osu.Game/Database/LegacyExporter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/LegacyExporter.cs b/osu.Game/Database/LegacyExporter.cs index 2d0a1d3528..04d5a3aebc 100644 --- a/osu.Game/Database/LegacyExporter.cs +++ b/osu.Game/Database/LegacyExporter.cs @@ -25,7 +25,7 @@ namespace osu.Game.Database ///
/// /// - /// Filename limit for most OSs is 255, this actual usable length is smaller because adds additional "_" to the end of the path. + /// The filename limit for most OSs is 255. This actual usable length is smaller because adds an additional "_" to the end of the path. /// /// /// For more information see: From e3bdb3d852a85748032ea468d1763849aee74249 Mon Sep 17 00:00:00 2001 From: Cootz Date: Fri, 17 Feb 2023 15:32:36 +0300 Subject: [PATCH 045/111] Align links in one line --- osu.Game/Database/LegacyExporter.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/osu.Game/Database/LegacyExporter.cs b/osu.Game/Database/LegacyExporter.cs index 04d5a3aebc..cf88efb151 100644 --- a/osu.Game/Database/LegacyExporter.cs +++ b/osu.Game/Database/LegacyExporter.cs @@ -28,13 +28,7 @@ namespace osu.Game.Database /// The filename limit for most OSs is 255. This actual usable length is smaller because adds an additional "_" to the end of the path. /// /// - /// For more information see: - ///
- /// File specification syntax - ///
- ///
- /// File systems limitations - ///
+ /// For more information see file specification syntax, file systems limitations ///
///
public const int MAX_FILENAME_LENGTH = 255 - (32 + 4 + 2); //max path - (Guid + Guid "D" format chars + Storage.CreateFileSafely chars) From 90aa4288d0a9a265d666c8e66545108bce4d8edd Mon Sep 17 00:00:00 2001 From: Cootz Date: Tue, 21 Feb 2023 18:35:53 +0300 Subject: [PATCH 046/111] Reduce the allowed length by 5 to account for (99) suffix. Move truncating logic to `GetFilename`. Update tests. --- osu.Game.Tests/Database/LegacyExporterTest.cs | 9 ++++++--- osu.Game/Database/LegacyExporter.cs | 20 +++++++++---------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Database/LegacyExporterTest.cs b/osu.Game.Tests/Database/LegacyExporterTest.cs index c9aea80585..d41b3a5017 100644 --- a/osu.Game.Tests/Database/LegacyExporterTest.cs +++ b/osu.Game.Tests/Database/LegacyExporterTest.cs @@ -46,7 +46,7 @@ namespace osu.Game.Tests.Database Assert.That(item.Filename.Length, Is.LessThan(TestLegacyExporter.MAX_FILENAME_LENGTH)); //Export multiple times - for (int i = 0; i < 10; i++) + for (int i = 0; i < 100; i++) { string expectedFileName = i == 0 ? short_filename : $"{short_filename} ({i})"; exportItemAndAssert(item, expectedFileName); @@ -76,8 +76,11 @@ namespace osu.Game.Tests.Database Assert.That(item.Filename.Length, Is.GreaterThan(TestLegacyExporter.MAX_FILENAME_LENGTH)); //Export multiple times - for (int i = 0; i < 10; i++) - exportItemAndAssert(item, expectedName); + for (int i = 0; i < 100; i++) + { + string expectedFilename = i == 0 ? expectedName : $"{expectedName} ({i})"; + exportItemAndAssert(item, expectedFilename); + } } private void exportItemAndAssert(IHasNamedFiles item, string expectedName) diff --git a/osu.Game/Database/LegacyExporter.cs b/osu.Game/Database/LegacyExporter.cs index cf88efb151..0fa7b9e03c 100644 --- a/osu.Game/Database/LegacyExporter.cs +++ b/osu.Game/Database/LegacyExporter.cs @@ -31,7 +31,7 @@ namespace osu.Game.Database /// For more information see file specification syntax, file systems limitations /// /// - public const int MAX_FILENAME_LENGTH = 255 - (32 + 4 + 2); //max path - (Guid + Guid "D" format chars + Storage.CreateFileSafely chars) + public const int MAX_FILENAME_LENGTH = 255 - (32 + 4 + 2 + 5); //max path - (Guid + Guid "D" format chars + Storage.CreateFileSafely chars + account for ' (99)' suffix) /// /// The file extension for exports (including the leading '.'). @@ -48,7 +48,15 @@ namespace osu.Game.Database UserFileStorage = storage.GetStorageForDirectory(@"files"); } - protected virtual string GetFilename(TModel item) => item.GetDisplayString(); + protected virtual string GetFilename(TModel item) + { + string filename = item.GetDisplayString(); + + if (filename.Length > MAX_FILENAME_LENGTH - FileExtension.Length) + return filename.Remove(MAX_FILENAME_LENGTH - FileExtension.Length); + + return filename; + } /// /// Exports an item to a legacy (.zip based) package. @@ -65,14 +73,6 @@ namespace osu.Game.Database string filename = NamingUtils.GetNextBestFilename(existingExports, $"{itemFilename}{FileExtension}"); - if (filename.Length > MAX_FILENAME_LENGTH) - { - string filenameWithoutExtension = Path.GetFileNameWithoutExtension(filename); - - filenameWithoutExtension = filenameWithoutExtension.Remove(MAX_FILENAME_LENGTH - FileExtension.Length); - filename = $"{filenameWithoutExtension}{FileExtension}"; - } - using (var stream = exportStorage.CreateFileSafely(filename)) ExportModelTo(item, stream); From dd9748a25cb3d29537b3abf01a382a9f2be2d7e9 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sat, 25 Feb 2023 01:21:37 +0900 Subject: [PATCH 047/111] Adjust DrawNodes to use UBOs --- .../UI/Cursor/CursorTrail.cs | 22 ++++++++++++-- .../TestSceneTriangleBorderShader.cs | 19 ++++++++++-- .../Backgrounds/TriangleBorderData.cs | 16 ++++++++++ osu.Game/Graphics/Backgrounds/Triangles.cs | 17 +++++++---- osu.Game/Graphics/Backgrounds/TrianglesV2.cs | 13 ++++++-- osu.Game/Graphics/Sprites/LogoAnimation.cs | 22 +++++++++++++- osu.Game/Rulesets/Mods/ModFlashlight.cs | 30 +++++++++++++++---- 7 files changed, 121 insertions(+), 18 deletions(-) create mode 100644 osu.Game/Graphics/Backgrounds/TriangleBorderData.cs diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index 9ecabd1d5e..a29faac5a0 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -13,6 +13,7 @@ using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Rendering; using osu.Framework.Graphics.Rendering.Vertices; using osu.Framework.Graphics.Shaders; +using osu.Framework.Graphics.Shaders.Types; using osu.Framework.Graphics.Textures; using osu.Framework.Input; using osu.Framework.Input.Events; @@ -255,15 +256,23 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor Source.parts.CopyTo(parts, 0); } + private IUniformBuffer cursorTrailParameters; + public override void Draw(IRenderer renderer) { base.Draw(renderer); vertexBatch ??= renderer.CreateQuadBatch(max_sprites, 1); + cursorTrailParameters ??= renderer.CreateUniformBuffer(); + cursorTrailParameters.Data = cursorTrailParameters.Data with + { + FadeClock = time, + FadeExponent = fadeExponent + }; + shader.Bind(); - shader.GetUniform("g_FadeClock").UpdateValue(ref time); - shader.GetUniform("g_FadeExponent").UpdateValue(ref fadeExponent); + shader.BindUniformBlock("m_CursorTrailParameters", cursorTrailParameters); texture.Bind(); @@ -323,6 +332,15 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor base.Dispose(isDisposing); vertexBatch?.Dispose(); + cursorTrailParameters?.Dispose(); + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + private record struct CursorTrailParameters + { + public UniformFloat FadeClock; + public UniformFloat FadeExponent; + private readonly UniformPadding8 pad1; } } diff --git a/osu.Game.Tests/Visual/Background/TestSceneTriangleBorderShader.cs b/osu.Game.Tests/Visual/Background/TestSceneTriangleBorderShader.cs index 271642edd1..07427c242f 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneTriangleBorderShader.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneTriangleBorderShader.cs @@ -9,6 +9,7 @@ using osuTK; using osuTK.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Rendering; +using osu.Game.Graphics.Backgrounds; namespace osu.Game.Tests.Visual.Background { @@ -97,15 +98,29 @@ namespace osu.Game.Tests.Visual.Background texelSize = Source.texelSize; } + private IUniformBuffer? borderDataBuffer; + public override void Draw(IRenderer renderer) { - TextureShader.GetUniform("thickness").UpdateValue(ref thickness); - TextureShader.GetUniform("texelSize").UpdateValue(ref texelSize); + borderDataBuffer ??= renderer.CreateUniformBuffer(); + borderDataBuffer.Data = borderDataBuffer.Data with + { + Thickness = thickness, + TexelSize = texelSize + }; + + TextureShader.BindUniformBlock("m_BorderData", borderDataBuffer); base.Draw(renderer); } protected override bool CanDrawOpaqueInterior => false; + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + borderDataBuffer?.Dispose(); + } } } } diff --git a/osu.Game/Graphics/Backgrounds/TriangleBorderData.cs b/osu.Game/Graphics/Backgrounds/TriangleBorderData.cs new file mode 100644 index 0000000000..f4d327dc8e --- /dev/null +++ b/osu.Game/Graphics/Backgrounds/TriangleBorderData.cs @@ -0,0 +1,16 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Runtime.InteropServices; +using osu.Framework.Graphics.Shaders.Types; + +namespace osu.Game.Graphics.Backgrounds +{ + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public record struct TriangleBorderData + { + public UniformFloat Thickness; + public UniformFloat TexelSize; + private readonly UniformPadding8 pad1; + } +} diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index 68ece56d8a..f3ebdd875a 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -284,6 +284,8 @@ namespace osu.Game.Graphics.Backgrounds parts.AddRange(Source.parts); } + private IUniformBuffer borderDataBuffer; + public override void Draw(IRenderer renderer) { base.Draw(renderer); @@ -294,14 +296,16 @@ namespace osu.Game.Graphics.Backgrounds vertexBatch = renderer.CreateQuadBatch(Source.AimCount, 1); } - // Due to triangles having various sizes we would need to set a different "texelSize" value for each of them, which is insanely expensive, thus we should use one single value. - // texelSize computed for an average triangle (size 100) will result in big triangles becoming blurry, so we may just use 0 for all of them. - // But we still need to specify at least something, because otherwise other shader usages will override this value. - float texelSize = 0f; + borderDataBuffer ??= renderer.CreateUniformBuffer(); + borderDataBuffer.Data = borderDataBuffer.Data with + { + Thickness = fill, + // Due to triangles having various sizes we would need to set a different "TexelSize" value for each of them, which is insanely expensive, thus we should use one single value. + // TexelSize computed for an average triangle (size 100) will result in big triangles becoming blurry, so we may just use 0 for all of them. + TexelSize = 0 + }; shader.Bind(); - shader.GetUniform("thickness").UpdateValue(ref fill); - shader.GetUniform("texelSize").UpdateValue(ref texelSize); foreach (TriangleParticle particle in parts) { @@ -352,6 +356,7 @@ namespace osu.Game.Graphics.Backgrounds base.Dispose(isDisposing); vertexBatch?.Dispose(); + borderDataBuffer?.Dispose(); } } diff --git a/osu.Game/Graphics/Backgrounds/TrianglesV2.cs b/osu.Game/Graphics/Backgrounds/TrianglesV2.cs index 3bc8bb6df1..b108649ce4 100644 --- a/osu.Game/Graphics/Backgrounds/TrianglesV2.cs +++ b/osu.Game/Graphics/Backgrounds/TrianglesV2.cs @@ -227,6 +227,8 @@ namespace osu.Game.Graphics.Backgrounds parts.AddRange(Source.parts); } + private IUniformBuffer? borderDataBuffer; + public override void Draw(IRenderer renderer) { base.Draw(renderer); @@ -240,9 +242,15 @@ namespace osu.Game.Graphics.Backgrounds vertexBatch = renderer.CreateQuadBatch(Source.AimCount, 1); } + borderDataBuffer ??= renderer.CreateUniformBuffer(); + borderDataBuffer.Data = borderDataBuffer.Data with + { + Thickness = thickness, + TexelSize = texelSize + }; + shader.Bind(); - shader.GetUniform("thickness").UpdateValue(ref thickness); - shader.GetUniform("texelSize").UpdateValue(ref texelSize); + shader.BindUniformBlock("m_BorderData", borderDataBuffer); Vector2 relativeSize = Vector2.Divide(triangleSize, size); @@ -303,6 +311,7 @@ namespace osu.Game.Graphics.Backgrounds base.Dispose(isDisposing); vertexBatch?.Dispose(); + borderDataBuffer?.Dispose(); } } diff --git a/osu.Game/Graphics/Sprites/LogoAnimation.cs b/osu.Game/Graphics/Sprites/LogoAnimation.cs index e177cc604b..220f57e9fa 100644 --- a/osu.Game/Graphics/Sprites/LogoAnimation.cs +++ b/osu.Game/Graphics/Sprites/LogoAnimation.cs @@ -3,10 +3,12 @@ #nullable disable +using System.Runtime.InteropServices; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Rendering; using osu.Framework.Graphics.Shaders; +using osu.Framework.Graphics.Shaders.Types; using osu.Framework.Graphics.Sprites; namespace osu.Game.Graphics.Sprites @@ -55,14 +57,32 @@ namespace osu.Game.Graphics.Sprites progress = source.animationProgress; } + private IUniformBuffer animationDataBuffer; + protected override void Blit(IRenderer renderer) { - TextureShader.GetUniform("progress").UpdateValue(ref progress); + animationDataBuffer ??= renderer.CreateUniformBuffer(); + animationDataBuffer.Data = animationDataBuffer.Data with { Progress = progress }; + + TextureShader.BindUniformBlock("m_AnimationData", animationDataBuffer); base.Blit(renderer); } protected override bool CanDrawOpaqueInterior => false; + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + animationDataBuffer?.Dispose(); + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + private record struct AnimationData + { + public UniformFloat Progress; + private readonly UniformPadding12 pad1; + } } } } diff --git a/osu.Game/Rulesets/Mods/ModFlashlight.cs b/osu.Game/Rulesets/Mods/ModFlashlight.cs index 45fa55c7f2..d1446444bf 100644 --- a/osu.Game/Rulesets/Mods/ModFlashlight.cs +++ b/osu.Game/Rulesets/Mods/ModFlashlight.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Runtime.InteropServices; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -9,6 +10,7 @@ using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Rendering; using osu.Framework.Graphics.Rendering.Vertices; using osu.Framework.Graphics.Shaders; +using osu.Framework.Graphics.Shaders.Types; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Game.Configuration; @@ -245,6 +247,8 @@ namespace osu.Game.Rulesets.Mods flashlightSmoothness = Source.flashlightSmoothness; } + private IUniformBuffer? flashlightParametersBuffer; + public override void Draw(IRenderer renderer) { base.Draw(renderer); @@ -259,12 +263,17 @@ namespace osu.Game.Rulesets.Mods }); } - shader.Bind(); + flashlightParametersBuffer ??= renderer.CreateUniformBuffer(); + flashlightParametersBuffer.Data = flashlightParametersBuffer.Data with + { + Position = flashlightPosition, + Size = flashlightSize, + Dim = flashlightDim, + Smoothness = flashlightSmoothness + }; - shader.GetUniform("flashlightPos").UpdateValue(ref flashlightPosition); - shader.GetUniform("flashlightSize").UpdateValue(ref flashlightSize); - shader.GetUniform("flashlightDim").UpdateValue(ref flashlightDim); - shader.GetUniform("flashlightSmoothness").UpdateValue(ref flashlightSmoothness); + shader.Bind(); + shader.BindUniformBlock("m_FlashlightParameters", flashlightParametersBuffer); renderer.DrawQuad(renderer.WhitePixel, screenSpaceDrawQuad, DrawColourInfo.Colour, vertexAction: addAction); @@ -275,6 +284,17 @@ namespace osu.Game.Rulesets.Mods { base.Dispose(isDisposing); quadBatch?.Dispose(); + flashlightParametersBuffer?.Dispose(); + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + private record struct FlashlightParameters + { + public UniformVector2 Position; + public UniformVector2 Size; + public UniformFloat Dim; + public UniformFloat Smoothness; + private readonly UniformPadding8 pad1; } } } From 069b77dd23ffc9aaa04ebd1995f9fd37f1a1c076 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sat, 25 Feb 2023 02:15:56 +0900 Subject: [PATCH 048/111] Update language version --- osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj | 1 + osu.Game/osu.Game.csproj | 1 + 2 files changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index bf776fe5dd..75656e2976 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -4,6 +4,7 @@ Library true click the circles. to the beat. + 10 diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 65ea301cbd..c5001cdc93 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -3,6 +3,7 @@ net6.0 Library true + 10 osu! From a44c9d10d77047669db8e67f474c4427f1afe4db Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sat, 25 Feb 2023 02:18:40 +0900 Subject: [PATCH 049/111] Fix buffer not being bound --- osu.Game/Graphics/Backgrounds/Triangles.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index f3ebdd875a..5b7b94ff4c 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -306,6 +306,7 @@ namespace osu.Game.Graphics.Backgrounds }; shader.Bind(); + shader.BindUniformBlock("m_BorderData", borderDataBuffer); foreach (TriangleParticle particle in parts) { From a705698ab64c7cfe63c82f7be048ed03de54aed8 Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Sun, 26 Feb 2023 21:13:05 +0900 Subject: [PATCH 050/111] beatmapset that already deletePending should not be fetched --- osu.Game/Scoring/Legacy/DatabasedLegacyScoreDecoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Scoring/Legacy/DatabasedLegacyScoreDecoder.cs b/osu.Game/Scoring/Legacy/DatabasedLegacyScoreDecoder.cs index 8908163646..8ee2cc6241 100644 --- a/osu.Game/Scoring/Legacy/DatabasedLegacyScoreDecoder.cs +++ b/osu.Game/Scoring/Legacy/DatabasedLegacyScoreDecoder.cs @@ -24,6 +24,6 @@ namespace osu.Game.Scoring.Legacy } protected override Ruleset GetRuleset(int rulesetId) => rulesets.GetRuleset(rulesetId)?.CreateInstance(); - protected override WorkingBeatmap GetBeatmap(string md5Hash) => beatmaps.GetWorkingBeatmap(beatmaps.QueryBeatmap(b => b.MD5Hash == md5Hash)); + protected override WorkingBeatmap GetBeatmap(string md5Hash) => beatmaps.GetWorkingBeatmap(beatmaps.QueryBeatmap(b => b.MD5Hash == md5Hash && !b.BeatmapSet.DeletePending)); } } From 2b9d13cfee0e3c7fc46a2bf9d25077e75e01f02d Mon Sep 17 00:00:00 2001 From: EXtremeExploit Date: Sun, 26 Feb 2023 20:58:20 -0300 Subject: [PATCH 051/111] Add group badges to list view --- .../Visual/Online/TestSceneFriendDisplay.cs | 17 +++++++++++++++-- .../Profile/Header/Components/GroupBadgeFlow.cs | 6 ++++++ osu.Game/Users/ExtendedUserPanel.cs | 8 ++++++++ osu.Game/Users/UserListPanel.cs | 5 +++++ 4 files changed, 34 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneFriendDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneFriendDisplay.cs index 7925b252b6..5028a532b7 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneFriendDisplay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneFriendDisplay.cs @@ -56,7 +56,13 @@ namespace osu.Game.Tests.Visual.Online IsOnline = true, Statistics = new UserStatistics { GlobalRank = 1111 }, CountryCode = CountryCode.JP, - CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c6.jpg" + CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c6.jpg", + IsSupporter = true, + SupportLevel = 1, + Groups = new[] + { + new APIUserGroup { Colour = "#EB47D0", ShortName = "DEV", Name = "Developers" } + } }, new APIUser { @@ -68,6 +74,11 @@ namespace osu.Game.Tests.Visual.Online CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", IsSupporter = true, SupportLevel = 3, + Groups = new[] + { + new APIUserGroup { Colour = "#0066FF", ShortName = "PPY", Name = "peppy" }, + new APIUserGroup { Colour = "#EB47D0", ShortName = "DEV", Name = "Developers" } + } }, new APIUser { @@ -76,7 +87,9 @@ namespace osu.Game.Tests.Visual.Online CountryCode = CountryCode.BY, CoverUrl = "https://assets.ppy.sh/user-profile-covers/8195163/4a8e2ad5a02a2642b631438cfa6c6bd7e2f9db289be881cb27df18331f64144c.jpeg", IsOnline = false, - LastVisit = DateTimeOffset.Now + LastVisit = DateTimeOffset.Now, + IsSupporter = true, + SupportLevel = 2 } }; } diff --git a/osu.Game/Overlays/Profile/Header/Components/GroupBadgeFlow.cs b/osu.Game/Overlays/Profile/Header/Components/GroupBadgeFlow.cs index 33b3de94db..2498543b11 100644 --- a/osu.Game/Overlays/Profile/Header/Components/GroupBadgeFlow.cs +++ b/osu.Game/Overlays/Profile/Header/Components/GroupBadgeFlow.cs @@ -25,8 +25,14 @@ namespace osu.Game.Overlays.Profile.Header.Components Clear(true); if (user.NewValue?.Groups != null) + { AddRange(user.NewValue.Groups.Select(g => new GroupBadge(g))); + Show(); + } + else + Hide(); }); + } } } diff --git a/osu.Game/Users/ExtendedUserPanel.cs b/osu.Game/Users/ExtendedUserPanel.cs index 3c1b68f9ef..af4da22906 100644 --- a/osu.Game/Users/ExtendedUserPanel.cs +++ b/osu.Game/Users/ExtendedUserPanel.cs @@ -13,6 +13,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Users.Drawables; using osu.Framework.Input.Events; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Overlays.Profile.Header.Components; namespace osu.Game.Users { @@ -97,6 +98,13 @@ namespace osu.Game.Users return statusContainer; } + protected FillFlowContainer CreateGroupBadges() + { + var groupBadgeFlow = new GroupBadgeFlow(); + groupBadgeFlow.User.Value = User; + return groupBadgeFlow; + } + private void displayStatus(UserStatus status, UserActivity activity = null) { if (status != null) diff --git a/osu.Game/Users/UserListPanel.cs b/osu.Game/Users/UserListPanel.cs index bbd3c60a33..ebe58c2b93 100644 --- a/osu.Game/Users/UserListPanel.cs +++ b/osu.Game/Users/UserListPanel.cs @@ -67,6 +67,11 @@ namespace osu.Game.Users { username.Anchor = Anchor.CentreLeft; username.Origin = Anchor.CentreLeft; + }), + CreateGroupBadges().With(badges => + { + badges.Anchor = Anchor.CentreLeft; + badges.Origin = Anchor.CentreLeft; }) } }, From d722e09b2cc2840edfc540e9b52de00370930fe3 Mon Sep 17 00:00:00 2001 From: EXtremeExploit Date: Sun, 26 Feb 2023 21:05:41 -0300 Subject: [PATCH 052/111] oops --- osu.Game/Overlays/Profile/Header/Components/GroupBadgeFlow.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/GroupBadgeFlow.cs b/osu.Game/Overlays/Profile/Header/Components/GroupBadgeFlow.cs index 2498543b11..4d4b16d7f5 100644 --- a/osu.Game/Overlays/Profile/Header/Components/GroupBadgeFlow.cs +++ b/osu.Game/Overlays/Profile/Header/Components/GroupBadgeFlow.cs @@ -32,7 +32,6 @@ namespace osu.Game.Overlays.Profile.Header.Components else Hide(); }); - } } } From 17a05de107f15a3336b3410ce87c4c276e22f2ac Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Mon, 13 Feb 2023 21:25:52 +0000 Subject: [PATCH 053/111] Initial Taiko single tap implementation (still needs code to allow for Strong hits) --- .../Mods/InputBlockingMod.cs | 131 ++++++++++++++++++ .../Mods/TaikoModSingleTap.cs | 16 +++ osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 1 + 3 files changed, 148 insertions(+) create mode 100644 osu.Game.Rulesets.Taiko/Mods/InputBlockingMod.cs create mode 100644 osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs diff --git a/osu.Game.Rulesets.Taiko/Mods/InputBlockingMod.cs b/osu.Game.Rulesets.Taiko/Mods/InputBlockingMod.cs new file mode 100644 index 0000000000..c815d62336 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Mods/InputBlockingMod.cs @@ -0,0 +1,131 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Graphics; +using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; +using osu.Game.Beatmaps.Timing; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; +using osu.Game.Screens.Play; +using osu.Game.Utils; +using osu.Game.Rulesets.Taiko.Mods; +using osu.Game.Rulesets.Taiko; + +namespace osu.Game.Rulesets.Taiko.Mods +{ + public abstract partial class InputBlockingMod : Mod, IApplicableToDrawableRuleset, IUpdatableByPlayfield + { + public override double ScoreMultiplier => 1.0; + public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModRelax), typeof(TaikoModCinema) }; + public override ModType Type => ModType.Conversion; + + private const double flash_duration = 1000; + + private DrawableRuleset ruleset = null!; + + protected TaikoAction? LastAcceptedDonAction { get; private set; } + protected TaikoAction? LastAcceptedKatAction { get; private set; } + + /// + /// A tracker for periods where alternate should not be forced (i.e. non-gameplay periods). + /// + /// + /// This is different from in that the periods here end strictly at the first object after the break, rather than the break's end time. + /// + private PeriodTracker nonGameplayPeriods = null!; + + private IFrameStableClock gameplayClock = null!; + + public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) + { + ruleset = drawableRuleset; + drawableRuleset.KeyBindingInputManager.Add(new InputInterceptor(this)); + + var periods = new List(); + + if (drawableRuleset.Objects.Any()) + { + periods.Add(new Period(int.MinValue, getValidJudgementTime(ruleset.Objects.First()) - 1)); + + foreach (BreakPeriod b in drawableRuleset.Beatmap.Breaks) + periods.Add(new Period(b.StartTime, getValidJudgementTime(ruleset.Objects.First(h => h.StartTime >= b.EndTime)) - 1)); + + static double getValidJudgementTime(HitObject hitObject) => hitObject.StartTime - hitObject.HitWindows.WindowFor(HitResult.Meh); + } + + nonGameplayPeriods = new PeriodTracker(periods); + + gameplayClock = drawableRuleset.FrameStableClock; + } + + public void Update(Playfield playfield) + { + if (nonGameplayPeriods.IsInAny(gameplayClock.CurrentTime)) + { + if (LastAcceptedDonAction != null) + LastAcceptedDonAction = null; + + if (LastAcceptedKatAction != null) + LastAcceptedKatAction = null; + } + } + + protected abstract bool CheckValidNewAction(TaikoAction action); + + private bool checkCorrectAction(TaikoAction action) + { + if (nonGameplayPeriods.IsInAny(gameplayClock.CurrentTime)) + return true; + + switch (action) + { + case TaikoAction.LeftCentre: + case TaikoAction.RightCentre: + case TaikoAction.LeftRim: + case TaikoAction.RightRim: + break; + + // Any action which is not left or right button should be ignored. + default: + return true; + } + + if (CheckValidNewAction(action)) + { + if (action == TaikoAction.LeftCentre || action == TaikoAction.RightCentre) + LastAcceptedDonAction = action; + if (action == TaikoAction.LeftRim || action == TaikoAction.RightRim) + LastAcceptedKatAction = action; + return true; + } + + //ruleset.Cursor.FlashColour(Colour4.Red, flash_duration, Easing.OutQuint); + return false; + } + + private partial class InputInterceptor : Component, IKeyBindingHandler + { + private readonly InputBlockingMod mod; + + public InputInterceptor(InputBlockingMod mod) + { + this.mod = mod; + } + + public bool OnPressed(KeyBindingPressEvent e) + // if the pressed action is incorrect, block it from reaching gameplay. + => !mod.checkCorrectAction(e.Action); + + public void OnReleased(KeyBindingReleaseEvent e) + { + } + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs new file mode 100644 index 0000000000..20c37fcb71 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs @@ -0,0 +1,16 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Rulesets.Taiko.Mods +{ + public class TaikoModSingleTap : InputBlockingMod + { + public override string Name => @"Single Tap"; + public override string Acronym => @"SG"; + public override LocalisableString Description => @"You must only use one key!"; + + protected override bool CheckValidNewAction(TaikoAction action) => LastAcceptedDonAction == null || LastAcceptedDonAction == action || LastAcceptedKatAction == null || LastAcceptedKatAction == action; + } +} diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 4f435e73b3..a35fdb890d 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -158,6 +158,7 @@ namespace osu.Game.Rulesets.Taiko new TaikoModDifficultyAdjust(), new TaikoModClassic(), new TaikoModSwap(), + new TaikoModSingleTap(), }; case ModType.Automation: From 715df42a690ca1d33792f8be5b1d0e860c700d77 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Sun, 26 Feb 2023 22:22:45 +0000 Subject: [PATCH 054/111] Strong hits are now allowed to be hit --- osu.Game.Rulesets.Taiko/Mods/InputBlockingMod.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/InputBlockingMod.cs b/osu.Game.Rulesets.Taiko/Mods/InputBlockingMod.cs index c815d62336..7613ce7c67 100644 --- a/osu.Game.Rulesets.Taiko/Mods/InputBlockingMod.cs +++ b/osu.Game.Rulesets.Taiko/Mods/InputBlockingMod.cs @@ -15,8 +15,7 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Screens.Play; using osu.Game.Utils; -using osu.Game.Rulesets.Taiko.Mods; -using osu.Game.Rulesets.Taiko; +using osu.Game.Rulesets.Taiko.UI; namespace osu.Game.Rulesets.Taiko.Mods { @@ -30,6 +29,8 @@ namespace osu.Game.Rulesets.Taiko.Mods private DrawableRuleset ruleset = null!; + private TaikoPlayfield playfield { get; set; } = null!; + protected TaikoAction? LastAcceptedDonAction { get; private set; } protected TaikoAction? LastAcceptedKatAction { get; private set; } @@ -46,6 +47,7 @@ namespace osu.Game.Rulesets.Taiko.Mods public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { ruleset = drawableRuleset; + playfield = (TaikoPlayfield)ruleset.Playfield; drawableRuleset.KeyBindingInputManager.Add(new InputInterceptor(this)); var periods = new List(); @@ -97,6 +99,10 @@ namespace osu.Game.Rulesets.Taiko.Mods return true; } + // If next hit object is strong, allow usage of all actions + if (playfield.HitObjectContainer.AliveObjects.FirstOrDefault(h => h.Result?.HasResult != true)?.HitObject is TaikoStrongableHitObject hitObject && hitObject.IsStrong) + return true; + if (CheckValidNewAction(action)) { if (action == TaikoAction.LeftCentre || action == TaikoAction.RightCentre) @@ -106,7 +112,6 @@ namespace osu.Game.Rulesets.Taiko.Mods return true; } - //ruleset.Cursor.FlashColour(Colour4.Red, flash_duration, Easing.OutQuint); return false; } From b883c6af34a7601023d7fe84c37cd3ae8f96d652 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Mon, 27 Feb 2023 10:30:57 +0000 Subject: [PATCH 055/111] Renamed all instances of Taiko's InputBlockingMod to TaikoInputBlockingMod --- .../Mods/{InputBlockingMod.cs => TaikoInputBlockingMod.cs} | 6 +++--- osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) rename osu.Game.Rulesets.Taiko/Mods/{InputBlockingMod.cs => TaikoInputBlockingMod.cs} (95%) diff --git a/osu.Game.Rulesets.Taiko/Mods/InputBlockingMod.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs similarity index 95% rename from osu.Game.Rulesets.Taiko/Mods/InputBlockingMod.cs rename to osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs index 7613ce7c67..a843e99671 100644 --- a/osu.Game.Rulesets.Taiko/Mods/InputBlockingMod.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs @@ -19,7 +19,7 @@ using osu.Game.Rulesets.Taiko.UI; namespace osu.Game.Rulesets.Taiko.Mods { - public abstract partial class InputBlockingMod : Mod, IApplicableToDrawableRuleset, IUpdatableByPlayfield + public abstract partial class TaikoInputBlockingMod : Mod, IApplicableToDrawableRuleset, IUpdatableByPlayfield { public override double ScoreMultiplier => 1.0; public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModRelax), typeof(TaikoModCinema) }; @@ -117,9 +117,9 @@ namespace osu.Game.Rulesets.Taiko.Mods private partial class InputInterceptor : Component, IKeyBindingHandler { - private readonly InputBlockingMod mod; + private readonly TaikoInputBlockingMod mod; - public InputInterceptor(InputBlockingMod mod) + public InputInterceptor(TaikoInputBlockingMod mod) { this.mod = mod; } diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs index 20c37fcb71..57e041f151 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs @@ -5,7 +5,7 @@ using osu.Framework.Localisation; namespace osu.Game.Rulesets.Taiko.Mods { - public class TaikoModSingleTap : InputBlockingMod + public class TaikoModSingleTap : TaikoInputBlockingMod { public override string Name => @"Single Tap"; public override string Acronym => @"SG"; From f2cada4584db82428ebcee47914084fc4bdad7e7 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Mon, 27 Feb 2023 12:22:38 +0000 Subject: [PATCH 056/111] Strong drumrolls no longer allow all actions to be used --- osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs index a843e99671..fe9f190d91 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs @@ -99,8 +99,10 @@ namespace osu.Game.Rulesets.Taiko.Mods return true; } - // If next hit object is strong, allow usage of all actions - if (playfield.HitObjectContainer.AliveObjects.FirstOrDefault(h => h.Result?.HasResult != true)?.HitObject is TaikoStrongableHitObject hitObject && hitObject.IsStrong) + // If next hit object is strong, allow usage of all actions. Strong drumrolls are ignored in this check. + if (playfield.HitObjectContainer.AliveObjects.FirstOrDefault(h => h.Result?.HasResult != true)?.HitObject is TaikoStrongableHitObject hitObject + && hitObject.IsStrong + && hitObject as DrumRoll == null) return true; if (CheckValidNewAction(action)) From 12b983495c9883ca0fd691f95ec93143a7688096 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Mon, 27 Feb 2023 13:40:55 +0000 Subject: [PATCH 057/111] Fixed issues caused by branch being stale --- osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs | 6 +++--- osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs index fe9f190d91..eecb8c0bbd 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Taiko.Mods private const double flash_duration = 1000; - private DrawableRuleset ruleset = null!; + private DrawableTaikoRuleset ruleset = null!; private TaikoPlayfield playfield { get; set; } = null!; @@ -46,9 +46,9 @@ namespace osu.Game.Rulesets.Taiko.Mods public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { - ruleset = drawableRuleset; + ruleset = (DrawableTaikoRuleset)drawableRuleset; + ruleset.InputManager.Add(new InputInterceptor(this)); playfield = (TaikoPlayfield)ruleset.Playfield; - drawableRuleset.KeyBindingInputManager.Add(new InputInterceptor(this)); var periods = new List(); diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs index 40203440c5..5390d3b138 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs @@ -33,6 +33,8 @@ namespace osu.Game.Rulesets.Taiko.UI public readonly BindableBool LockPlayfieldMaxAspect = new BindableBool(true); + public TaikoInputManager InputManager { get; private set; } + protected override ScrollVisualisationMethod VisualisationMethod => ScrollVisualisationMethod.Overlapping; protected override bool UserScrollSpeedAdjustment => false; @@ -93,7 +95,7 @@ namespace osu.Game.Rulesets.Taiko.UI LockPlayfieldMaxAspect = { BindTarget = LockPlayfieldMaxAspect } }; - protected override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo); + protected override PassThroughInputManager CreateInputManager() => InputManager = new TaikoInputManager(Ruleset.RulesetInfo); protected override Playfield CreatePlayfield() => new TaikoPlayfield(); From dd9194b976089afc985589fa3100338b896e1596 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Mon, 27 Feb 2023 13:58:40 +0000 Subject: [PATCH 058/111] Code formatting fix --- osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs index eecb8c0bbd..63bc156d2d 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs @@ -101,8 +101,8 @@ namespace osu.Game.Rulesets.Taiko.Mods // If next hit object is strong, allow usage of all actions. Strong drumrolls are ignored in this check. if (playfield.HitObjectContainer.AliveObjects.FirstOrDefault(h => h.Result?.HasResult != true)?.HitObject is TaikoStrongableHitObject hitObject - && hitObject.IsStrong - && hitObject as DrumRoll == null) + && hitObject.IsStrong + && hitObject as DrumRoll == null) return true; if (CheckValidNewAction(action)) From d823faed081db916e967747ac0076bb631e8c0d1 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Mon, 27 Feb 2023 14:33:47 +0000 Subject: [PATCH 059/111] Remove unused `flash_duration` const --- osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs index 63bc156d2d..5512a7d775 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs @@ -25,8 +25,6 @@ namespace osu.Game.Rulesets.Taiko.Mods public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModRelax), typeof(TaikoModCinema) }; public override ModType Type => ModType.Conversion; - private const double flash_duration = 1000; - private DrawableTaikoRuleset ruleset = null!; private TaikoPlayfield playfield { get; set; } = null!; From 12a2037086c8878173f85c8af60a14dad0fec538 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Mon, 27 Feb 2023 14:39:12 +0000 Subject: [PATCH 060/111] Removed useless TaikoAction check from TaikoInputBlockingMod --- .../Mods/TaikoInputBlockingMod.cs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs index 5512a7d775..f437d53b93 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs @@ -84,19 +84,6 @@ namespace osu.Game.Rulesets.Taiko.Mods if (nonGameplayPeriods.IsInAny(gameplayClock.CurrentTime)) return true; - switch (action) - { - case TaikoAction.LeftCentre: - case TaikoAction.RightCentre: - case TaikoAction.LeftRim: - case TaikoAction.RightRim: - break; - - // Any action which is not left or right button should be ignored. - default: - return true; - } - // If next hit object is strong, allow usage of all actions. Strong drumrolls are ignored in this check. if (playfield.HitObjectContainer.AliveObjects.FirstOrDefault(h => h.Result?.HasResult != true)?.HitObject is TaikoStrongableHitObject hitObject && hitObject.IsStrong From 25ef1f199d64c32760ad694826970e90a74761f3 Mon Sep 17 00:00:00 2001 From: OpenSauce <48618519+OpenSauce04@users.noreply.github.com> Date: Mon, 27 Feb 2023 15:22:06 +0000 Subject: [PATCH 061/111] Update description for Taiko Single Tap mod --- osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs index 57e041f151..666e550c93 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs @@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Taiko.Mods { public override string Name => @"Single Tap"; public override string Acronym => @"SG"; - public override LocalisableString Description => @"You must only use one key!"; + public override LocalisableString Description => @"One key for dons, one key for kats."; protected override bool CheckValidNewAction(TaikoAction action) => LastAcceptedDonAction == null || LastAcceptedDonAction == action || LastAcceptedKatAction == null || LastAcceptedKatAction == action; } From beb04379f9a518c78802da385caa92f5f05c3670 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Mon, 27 Feb 2023 22:36:09 +0000 Subject: [PATCH 062/111] Inverted an if statement for code clarity --- .../Mods/TaikoInputBlockingMod.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs index f437d53b93..16d885b5d5 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs @@ -67,14 +67,13 @@ namespace osu.Game.Rulesets.Taiko.Mods public void Update(Playfield playfield) { - if (nonGameplayPeriods.IsInAny(gameplayClock.CurrentTime)) - { - if (LastAcceptedDonAction != null) - LastAcceptedDonAction = null; + if (!nonGameplayPeriods.IsInAny(gameplayClock.CurrentTime)) return; - if (LastAcceptedKatAction != null) - LastAcceptedKatAction = null; - } + if (LastAcceptedDonAction != null) + LastAcceptedDonAction = null; + + if (LastAcceptedKatAction != null) + LastAcceptedKatAction = null; } protected abstract bool CheckValidNewAction(TaikoAction action); From 7c84a84fbedc90032e29906090b0d7a362341fbc Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Tue, 28 Feb 2023 15:14:03 +0000 Subject: [PATCH 063/111] Removed TaikoInputBlockingMod, and instead moved code into TaikoModSingleTap --- .../Mods/TaikoInputBlockingMod.cs | 122 ------------------ .../Mods/TaikoModSingleTap.cs | 115 ++++++++++++++++- 2 files changed, 113 insertions(+), 124 deletions(-) delete mode 100644 osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs deleted file mode 100644 index 16d885b5d5..0000000000 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Graphics; -using osu.Framework.Input.Bindings; -using osu.Framework.Input.Events; -using osu.Game.Beatmaps.Timing; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Taiko.Objects; -using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.UI; -using osu.Game.Screens.Play; -using osu.Game.Utils; -using osu.Game.Rulesets.Taiko.UI; - -namespace osu.Game.Rulesets.Taiko.Mods -{ - public abstract partial class TaikoInputBlockingMod : Mod, IApplicableToDrawableRuleset, IUpdatableByPlayfield - { - public override double ScoreMultiplier => 1.0; - public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModRelax), typeof(TaikoModCinema) }; - public override ModType Type => ModType.Conversion; - - private DrawableTaikoRuleset ruleset = null!; - - private TaikoPlayfield playfield { get; set; } = null!; - - protected TaikoAction? LastAcceptedDonAction { get; private set; } - protected TaikoAction? LastAcceptedKatAction { get; private set; } - - /// - /// A tracker for periods where alternate should not be forced (i.e. non-gameplay periods). - /// - /// - /// This is different from in that the periods here end strictly at the first object after the break, rather than the break's end time. - /// - private PeriodTracker nonGameplayPeriods = null!; - - private IFrameStableClock gameplayClock = null!; - - public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) - { - ruleset = (DrawableTaikoRuleset)drawableRuleset; - ruleset.InputManager.Add(new InputInterceptor(this)); - playfield = (TaikoPlayfield)ruleset.Playfield; - - var periods = new List(); - - if (drawableRuleset.Objects.Any()) - { - periods.Add(new Period(int.MinValue, getValidJudgementTime(ruleset.Objects.First()) - 1)); - - foreach (BreakPeriod b in drawableRuleset.Beatmap.Breaks) - periods.Add(new Period(b.StartTime, getValidJudgementTime(ruleset.Objects.First(h => h.StartTime >= b.EndTime)) - 1)); - - static double getValidJudgementTime(HitObject hitObject) => hitObject.StartTime - hitObject.HitWindows.WindowFor(HitResult.Meh); - } - - nonGameplayPeriods = new PeriodTracker(periods); - - gameplayClock = drawableRuleset.FrameStableClock; - } - - public void Update(Playfield playfield) - { - if (!nonGameplayPeriods.IsInAny(gameplayClock.CurrentTime)) return; - - if (LastAcceptedDonAction != null) - LastAcceptedDonAction = null; - - if (LastAcceptedKatAction != null) - LastAcceptedKatAction = null; - } - - protected abstract bool CheckValidNewAction(TaikoAction action); - - private bool checkCorrectAction(TaikoAction action) - { - if (nonGameplayPeriods.IsInAny(gameplayClock.CurrentTime)) - return true; - - // If next hit object is strong, allow usage of all actions. Strong drumrolls are ignored in this check. - if (playfield.HitObjectContainer.AliveObjects.FirstOrDefault(h => h.Result?.HasResult != true)?.HitObject is TaikoStrongableHitObject hitObject - && hitObject.IsStrong - && hitObject as DrumRoll == null) - return true; - - if (CheckValidNewAction(action)) - { - if (action == TaikoAction.LeftCentre || action == TaikoAction.RightCentre) - LastAcceptedDonAction = action; - if (action == TaikoAction.LeftRim || action == TaikoAction.RightRim) - LastAcceptedKatAction = action; - return true; - } - - return false; - } - - private partial class InputInterceptor : Component, IKeyBindingHandler - { - private readonly TaikoInputBlockingMod mod; - - public InputInterceptor(TaikoInputBlockingMod mod) - { - this.mod = mod; - } - - public bool OnPressed(KeyBindingPressEvent e) - // if the pressed action is incorrect, block it from reaching gameplay. - => !mod.checkCorrectAction(e.Action); - - public void OnReleased(KeyBindingReleaseEvent e) - { - } - } - } -} diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs index 666e550c93..fe3b81d1d2 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs @@ -2,15 +2,126 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Localisation; +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Graphics; +using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; +using osu.Game.Beatmaps.Timing; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; +using osu.Game.Screens.Play; +using osu.Game.Utils; +using osu.Game.Rulesets.Taiko.UI; namespace osu.Game.Rulesets.Taiko.Mods { - public class TaikoModSingleTap : TaikoInputBlockingMod + public class TaikoModSingleTap : Mod, IApplicableToDrawableRuleset, IUpdatableByPlayfield { public override string Name => @"Single Tap"; public override string Acronym => @"SG"; public override LocalisableString Description => @"One key for dons, one key for kats."; - protected override bool CheckValidNewAction(TaikoAction action) => LastAcceptedDonAction == null || LastAcceptedDonAction == action || LastAcceptedKatAction == null || LastAcceptedKatAction == action; + protected bool CheckValidNewAction(TaikoAction action) => LastAcceptedDonAction == null || LastAcceptedDonAction == action || LastAcceptedKatAction == null || LastAcceptedKatAction == action; + + public override double ScoreMultiplier => 1.0; + public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModRelax), typeof(TaikoModCinema) }; + public override ModType Type => ModType.Conversion; + + private DrawableTaikoRuleset ruleset = null!; + + private TaikoPlayfield playfield { get; set; } = null!; + + protected TaikoAction? LastAcceptedDonAction { get; private set; } + protected TaikoAction? LastAcceptedKatAction { get; private set; } + + /// + /// A tracker for periods where alternate should not be forced (i.e. non-gameplay periods). + /// + /// + /// This is different from in that the periods here end strictly at the first object after the break, rather than the break's end time. + /// + private PeriodTracker nonGameplayPeriods = null!; + + private IFrameStableClock gameplayClock = null!; + + public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) + { + ruleset = (DrawableTaikoRuleset)drawableRuleset; + ruleset.InputManager.Add(new InputInterceptor(this)); + playfield = (TaikoPlayfield)ruleset.Playfield; + + var periods = new List(); + + if (drawableRuleset.Objects.Any()) + { + periods.Add(new Period(int.MinValue, getValidJudgementTime(ruleset.Objects.First()) - 1)); + + foreach (BreakPeriod b in drawableRuleset.Beatmap.Breaks) + periods.Add(new Period(b.StartTime, getValidJudgementTime(ruleset.Objects.First(h => h.StartTime >= b.EndTime)) - 1)); + + static double getValidJudgementTime(HitObject hitObject) => hitObject.StartTime - hitObject.HitWindows.WindowFor(HitResult.Meh); + } + + nonGameplayPeriods = new PeriodTracker(periods); + + gameplayClock = drawableRuleset.FrameStableClock; + } + + public void Update(Playfield playfield) + { + if (!nonGameplayPeriods.IsInAny(gameplayClock.CurrentTime)) return; + + if (LastAcceptedDonAction != null) + LastAcceptedDonAction = null; + + if (LastAcceptedKatAction != null) + LastAcceptedKatAction = null; + } + + private bool checkCorrectAction(TaikoAction action) + { + if (nonGameplayPeriods.IsInAny(gameplayClock.CurrentTime)) + return true; + + // If next hit object is strong, allow usage of all actions. Strong drumrolls are ignored in this check. + if (playfield.HitObjectContainer.AliveObjects.FirstOrDefault(h => h.Result?.HasResult != true)?.HitObject is TaikoStrongableHitObject hitObject + && hitObject.IsStrong + && hitObject as DrumRoll == null) + return true; + + if (CheckValidNewAction(action)) + { + if (action == TaikoAction.LeftCentre || action == TaikoAction.RightCentre) + LastAcceptedDonAction = action; + if (action == TaikoAction.LeftRim || action == TaikoAction.RightRim) + LastAcceptedKatAction = action; + return true; + } + + return false; + } + + private partial class InputInterceptor : Component, IKeyBindingHandler + { + private readonly TaikoModSingleTap mod; + + public InputInterceptor(TaikoModSingleTap mod) + { + this.mod = mod; + } + + public bool OnPressed(KeyBindingPressEvent e) + // if the pressed action is incorrect, block it from reaching gameplay. + => !mod.checkCorrectAction(e.Action); + + public void OnReleased(KeyBindingReleaseEvent e) + { + } + } } } From 3aa8c81c5d62c7fae355e8a161f8891b13d06ad3 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Tue, 28 Feb 2023 15:20:53 +0000 Subject: [PATCH 064/111] Added missing mod incompatabilities --- osu.Game.Rulesets.Taiko/Mods/TaikoModAutoplay.cs | 4 ++++ osu.Game.Rulesets.Taiko/Mods/TaikoModCinema.cs | 4 ++++ osu.Game.Rulesets.Taiko/Mods/TaikoModRelax.cs | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModAutoplay.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModAutoplay.cs index 4b74b4991e..1e09061859 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModAutoplay.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModAutoplay.cs @@ -1,7 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; +using System.Linq; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Taiko.Replays; @@ -12,5 +14,7 @@ namespace osu.Game.Rulesets.Taiko.Mods { public override ModReplayData CreateReplayData(IBeatmap beatmap, IReadOnlyList mods) => new ModReplayData(new TaikoAutoGenerator(beatmap).Generate(), new ModCreatedUser { Username = "mekkadosu!" }); + + public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(TaikoModSingleTap) }).ToArray(); } } diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModCinema.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModCinema.cs index fee0cb2744..c268087f0a 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModCinema.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModCinema.cs @@ -1,7 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; +using System.Linq; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Taiko.Objects; @@ -13,5 +15,7 @@ namespace osu.Game.Rulesets.Taiko.Mods { public override ModReplayData CreateReplayData(IBeatmap beatmap, IReadOnlyList mods) => new ModReplayData(new TaikoAutoGenerator(beatmap).Generate(), new ModCreatedUser { Username = "mekkadosu!" }); + + public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(TaikoModSingleTap) }).ToArray(); } } diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModRelax.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModRelax.cs index d1e9ab1428..f97a2d2027 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModRelax.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModRelax.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.Linq; using osu.Framework.Localisation; using osu.Game.Rulesets.Mods; @@ -9,5 +11,7 @@ namespace osu.Game.Rulesets.Taiko.Mods public class TaikoModRelax : ModRelax { public override LocalisableString Description => @"No ninja-like spinners, demanding drumrolls or unexpected katu's."; + + public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(TaikoModSingleTap) }).ToArray(); } } From 616f7b3f64a31bd265112c126d6a0844b204ae4a Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Tue, 28 Feb 2023 15:37:46 +0000 Subject: [PATCH 065/111] Simplified a type check --- osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs index fe3b81d1d2..67921fc7a9 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs @@ -91,7 +91,7 @@ namespace osu.Game.Rulesets.Taiko.Mods // If next hit object is strong, allow usage of all actions. Strong drumrolls are ignored in this check. if (playfield.HitObjectContainer.AliveObjects.FirstOrDefault(h => h.Result?.HasResult != true)?.HitObject is TaikoStrongableHitObject hitObject && hitObject.IsStrong - && hitObject as DrumRoll == null) + && !(hitObject is DrumRoll)) return true; if (CheckValidNewAction(action)) From 0a76c8ee7f4fa95f91f03bb585587d2158dcfc6f Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Tue, 28 Feb 2023 15:41:06 +0000 Subject: [PATCH 066/111] Further simplified a type check --- osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs index 67921fc7a9..ff8e4dc298 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs @@ -91,7 +91,7 @@ namespace osu.Game.Rulesets.Taiko.Mods // If next hit object is strong, allow usage of all actions. Strong drumrolls are ignored in this check. if (playfield.HitObjectContainer.AliveObjects.FirstOrDefault(h => h.Result?.HasResult != true)?.HitObject is TaikoStrongableHitObject hitObject && hitObject.IsStrong - && !(hitObject is DrumRoll)) + && hitObject is not DrumRoll) return true; if (CheckValidNewAction(action)) From d4bb08a4408250754acc6866b5e21b359ef7cc69 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Tue, 28 Feb 2023 18:38:09 +0000 Subject: [PATCH 067/111] Fixed CodeInspector complaining --- osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs index ff8e4dc298..f08e83d76c 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs @@ -20,7 +20,7 @@ using osu.Game.Rulesets.Taiko.UI; namespace osu.Game.Rulesets.Taiko.Mods { - public class TaikoModSingleTap : Mod, IApplicableToDrawableRuleset, IUpdatableByPlayfield + public partial class TaikoModSingleTap : Mod, IApplicableToDrawableRuleset, IUpdatableByPlayfield { public override string Name => @"Single Tap"; public override string Acronym => @"SG"; From bb5791a7140ba6a617657238684ebddf898f5f7b Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Wed, 1 Mar 2023 21:41:01 -0800 Subject: [PATCH 068/111] Fix overlay sidebars not scrolling to end due to parent scroll view --- osu.Game/Overlays/OverlaySidebar.cs | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/OverlaySidebar.cs b/osu.Game/Overlays/OverlaySidebar.cs index 87ce1b7e8c..b8c0032e87 100644 --- a/osu.Game/Overlays/OverlaySidebar.cs +++ b/osu.Game/Overlays/OverlaySidebar.cs @@ -8,6 +8,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; using osu.Game.Graphics.Containers; namespace osu.Game.Overlays @@ -39,7 +40,7 @@ namespace osu.Game.Overlays { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Right = -3 }, // Compensate for scrollbar margin - Child = new OsuScrollContainer + Child = new SidebarScrollContainer { RelativeSizeAxes = Axes.Both, Child = new Container @@ -74,5 +75,30 @@ namespace osu.Game.Overlays [NotNull] protected virtual Drawable CreateContent() => Empty(); + + private partial class SidebarScrollContainer : OsuScrollContainer + { + protected override bool OnScroll(ScrollEvent e) + { + if (e.ScrollDelta.Y > 0 && IsScrolledToStart()) + return false; + + if (e.ScrollDelta.Y < 0 && IsScrolledToEnd()) + return false; + + return base.OnScroll(e); + } + + protected override bool OnDragStart(DragStartEvent e) + { + if (e.Delta.Y > 0 && IsScrolledToStart()) + return false; + + if (e.Delta.Y < 0 && IsScrolledToEnd()) + return false; + + return base.OnDragStart(e); + } + } } } From ec95d0031354d3ff900dcc24fce84a113fe4cf47 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Fri, 3 Mar 2023 16:57:09 +0000 Subject: [PATCH 069/111] Added Taiko Single Tap test scene --- .../Mods/TestSceneTaikoModSingleTap.cs | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs new file mode 100644 index 0000000000..24cad6fee3 --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs @@ -0,0 +1,107 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Replays; +using osu.Game.Rulesets.Taiko.Mods; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Taiko.Replays; + +namespace osu.Game.Rulesets.Taiko.Tests.Mods +{ + public partial class TestSceneTaikoModSingleTap : TaikoModTestScene + { + [Test] + public void TestInputAlternate() => CreateModTest(new ModTestData + { + Mod = new TaikoModSingleTap(), + Autoplay = false, + Beatmap = new Beatmap + { + HitObjects = new List + { + new Hit + { + StartTime = 100, + Type = HitType.Rim + }, + new Hit + { + StartTime = 300, + Type = HitType.Rim + }, + new Hit + { + StartTime = 500, + Type = HitType.Rim + }, + new Hit + { + StartTime = 700, + Type = HitType.Rim + }, + }, + }, + ReplayFrames = new List + { + new TaikoReplayFrame(100, TaikoAction.RightRim), + new TaikoReplayFrame(120), + new TaikoReplayFrame(300, TaikoAction.LeftRim), + new TaikoReplayFrame(320), + new TaikoReplayFrame(500, TaikoAction.RightRim), + new TaikoReplayFrame(520), + new TaikoReplayFrame(700, TaikoAction.LeftRim), + new TaikoReplayFrame(720), + }, + PassCondition = () => Player.ScoreProcessor.Combo.Value == 4 + }); + + [Test] + public void TestInputSameKey() => CreateModTest(new ModTestData + { + Mod = new TaikoModSingleTap(), + Autoplay = false, + Beatmap = new Beatmap + { + HitObjects = new List + { + new Hit + { + StartTime = 100, + Type = HitType.Rim + }, + new Hit + { + StartTime = 300, + Type = HitType.Rim + }, + new Hit + { + StartTime = 500, + Type = HitType.Rim + }, + new Hit + { + StartTime = 700, + Type = HitType.Rim + }, + }, + }, + ReplayFrames = new List + { + new TaikoReplayFrame(100, TaikoAction.RightRim), + new TaikoReplayFrame(120), + new TaikoReplayFrame(300, TaikoAction.RightRim), + new TaikoReplayFrame(320), + new TaikoReplayFrame(500, TaikoAction.RightRim), + new TaikoReplayFrame(520), + new TaikoReplayFrame(700, TaikoAction.RightRim), + new TaikoReplayFrame(720), + }, + PassCondition = () => Player.ScoreProcessor.Combo.Value == 4 + }); + } +} From 865f785f506a69020dce0cb9d8e913fd7f652960 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Fri, 3 Mar 2023 17:13:39 +0000 Subject: [PATCH 070/111] Fixed an issue where Taiko's Single Tap could allow alternation under very specific circumstances --- osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs index f08e83d76c..eed4e8a280 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs @@ -26,7 +26,14 @@ namespace osu.Game.Rulesets.Taiko.Mods public override string Acronym => @"SG"; public override LocalisableString Description => @"One key for dons, one key for kats."; - protected bool CheckValidNewAction(TaikoAction action) => LastAcceptedDonAction == null || LastAcceptedDonAction == action || LastAcceptedKatAction == null || LastAcceptedKatAction == action; + protected bool CheckValidNewAction(TaikoAction action) + { + if (action == TaikoAction.LeftCentre || action == TaikoAction.RightCentre) + return LastAcceptedDonAction == null || LastAcceptedDonAction == action; + if (action == TaikoAction.LeftRim || action == TaikoAction.RightRim) + return LastAcceptedKatAction == null || LastAcceptedKatAction == action; + return true; + } public override double ScoreMultiplier => 1.0; public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModRelax), typeof(TaikoModCinema) }; From 4dbe589de2423749e4b088dc1bd3357db972cda2 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Fri, 3 Mar 2023 17:18:02 +0000 Subject: [PATCH 071/111] Fixed Taiko Single Tap test pass condition --- .../Mods/TestSceneTaikoModSingleTap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs index 24cad6fee3..75d4ef5c7f 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs @@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Mods new TaikoReplayFrame(700, TaikoAction.LeftRim), new TaikoReplayFrame(720), }, - PassCondition = () => Player.ScoreProcessor.Combo.Value == 4 + PassCondition = () => Player.ScoreProcessor.Combo.Value == 0 && Player.ScoreProcessor.HighestCombo.Value == 1 }); [Test] From 50b0fca2643a924344a327282e823f3c19a723e5 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Fri, 3 Mar 2023 18:11:29 +0000 Subject: [PATCH 072/111] Code formatting --- osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs index eed4e8a280..1130338521 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs @@ -32,6 +32,7 @@ namespace osu.Game.Rulesets.Taiko.Mods return LastAcceptedDonAction == null || LastAcceptedDonAction == action; if (action == TaikoAction.LeftRim || action == TaikoAction.RightRim) return LastAcceptedKatAction == null || LastAcceptedKatAction == action; + return true; } @@ -107,6 +108,7 @@ namespace osu.Game.Rulesets.Taiko.Mods LastAcceptedDonAction = action; if (action == TaikoAction.LeftRim || action == TaikoAction.RightRim) LastAcceptedKatAction = action; + return true; } From e23db6238622cc3fbe58ace064ffe5fe941275c5 Mon Sep 17 00:00:00 2001 From: EXtremeExploit Date: Fri, 3 Mar 2023 16:14:19 -0300 Subject: [PATCH 073/111] Do list group'badges in a nicer way --- .../Profile/Header/Components/GroupBadgeFlow.cs | 5 ----- osu.Game/Users/ExtendedUserPanel.cs | 7 ------- osu.Game/Users/UserListPanel.cs | 15 ++++++++++----- 3 files changed, 10 insertions(+), 17 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/GroupBadgeFlow.cs b/osu.Game/Overlays/Profile/Header/Components/GroupBadgeFlow.cs index 4d4b16d7f5..33b3de94db 100644 --- a/osu.Game/Overlays/Profile/Header/Components/GroupBadgeFlow.cs +++ b/osu.Game/Overlays/Profile/Header/Components/GroupBadgeFlow.cs @@ -25,12 +25,7 @@ namespace osu.Game.Overlays.Profile.Header.Components Clear(true); if (user.NewValue?.Groups != null) - { AddRange(user.NewValue.Groups.Select(g => new GroupBadge(g))); - Show(); - } - else - Hide(); }); } } diff --git a/osu.Game/Users/ExtendedUserPanel.cs b/osu.Game/Users/ExtendedUserPanel.cs index af4da22906..c004918d87 100644 --- a/osu.Game/Users/ExtendedUserPanel.cs +++ b/osu.Game/Users/ExtendedUserPanel.cs @@ -98,13 +98,6 @@ namespace osu.Game.Users return statusContainer; } - protected FillFlowContainer CreateGroupBadges() - { - var groupBadgeFlow = new GroupBadgeFlow(); - groupBadgeFlow.User.Value = User; - return groupBadgeFlow; - } - private void displayStatus(UserStatus status, UserActivity activity = null) { if (status != null) diff --git a/osu.Game/Users/UserListPanel.cs b/osu.Game/Users/UserListPanel.cs index ebe58c2b93..a11494e849 100644 --- a/osu.Game/Users/UserListPanel.cs +++ b/osu.Game/Users/UserListPanel.cs @@ -68,11 +68,6 @@ namespace osu.Game.Users username.Anchor = Anchor.CentreLeft; username.Origin = Anchor.CentreLeft; }), - CreateGroupBadges().With(badges => - { - badges.Anchor = Anchor.CentreLeft; - badges.Origin = Anchor.CentreLeft; - }) } }, new FillFlowContainer @@ -100,6 +95,16 @@ namespace osu.Game.Users } }; + if (User.Groups != null) + { + details.Add(new GroupBadgeFlow + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + User = { Value = User } + }); + } + if (User.IsSupporter) { details.Add(new SupporterIcon From ada610b907d8316d3ac7679a3e6a0ac0d94f2e71 Mon Sep 17 00:00:00 2001 From: EXtremeExploit Date: Fri, 3 Mar 2023 16:15:49 -0300 Subject: [PATCH 074/111] Revert my changes on tests --- .../Visual/Online/TestSceneFriendDisplay.cs | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneFriendDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneFriendDisplay.cs index 5028a532b7..7925b252b6 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneFriendDisplay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneFriendDisplay.cs @@ -56,13 +56,7 @@ namespace osu.Game.Tests.Visual.Online IsOnline = true, Statistics = new UserStatistics { GlobalRank = 1111 }, CountryCode = CountryCode.JP, - CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c6.jpg", - IsSupporter = true, - SupportLevel = 1, - Groups = new[] - { - new APIUserGroup { Colour = "#EB47D0", ShortName = "DEV", Name = "Developers" } - } + CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c6.jpg" }, new APIUser { @@ -74,11 +68,6 @@ namespace osu.Game.Tests.Visual.Online CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", IsSupporter = true, SupportLevel = 3, - Groups = new[] - { - new APIUserGroup { Colour = "#0066FF", ShortName = "PPY", Name = "peppy" }, - new APIUserGroup { Colour = "#EB47D0", ShortName = "DEV", Name = "Developers" } - } }, new APIUser { @@ -87,9 +76,7 @@ namespace osu.Game.Tests.Visual.Online CountryCode = CountryCode.BY, CoverUrl = "https://assets.ppy.sh/user-profile-covers/8195163/4a8e2ad5a02a2642b631438cfa6c6bd7e2f9db289be881cb27df18331f64144c.jpeg", IsOnline = false, - LastVisit = DateTimeOffset.Now, - IsSupporter = true, - SupportLevel = 2 + LastVisit = DateTimeOffset.Now } }; } From edd37a9a7c1fc5af60db80718d9135a1e4dd3ec2 Mon Sep 17 00:00:00 2001 From: EXtremeExploit Date: Fri, 3 Mar 2023 16:16:45 -0300 Subject: [PATCH 075/111] cleanup --- osu.Game/Users/ExtendedUserPanel.cs | 1 - osu.Game/Users/UserListPanel.cs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Users/ExtendedUserPanel.cs b/osu.Game/Users/ExtendedUserPanel.cs index c004918d87..3c1b68f9ef 100644 --- a/osu.Game/Users/ExtendedUserPanel.cs +++ b/osu.Game/Users/ExtendedUserPanel.cs @@ -13,7 +13,6 @@ using osu.Game.Graphics.Sprites; using osu.Game.Users.Drawables; using osu.Framework.Input.Events; using osu.Game.Online.API.Requests.Responses; -using osu.Game.Overlays.Profile.Header.Components; namespace osu.Game.Users { diff --git a/osu.Game/Users/UserListPanel.cs b/osu.Game/Users/UserListPanel.cs index a11494e849..9b5b0e9f6d 100644 --- a/osu.Game/Users/UserListPanel.cs +++ b/osu.Game/Users/UserListPanel.cs @@ -67,7 +67,7 @@ namespace osu.Game.Users { username.Anchor = Anchor.CentreLeft; username.Origin = Anchor.CentreLeft; - }), + }) } }, new FillFlowContainer From 93fd9401642147be1a5f98b627259a92813d6e45 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Fri, 3 Mar 2023 20:35:28 +0000 Subject: [PATCH 076/111] Added new tests for Taiko Single Tap mod --- .../Mods/TestSceneTaikoModSingleTap.cs | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs index 75d4ef5c7f..7d544200cd 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using NUnit.Framework; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Taiko.Mods; @@ -103,5 +104,103 @@ namespace osu.Game.Rulesets.Taiko.Tests.Mods }, PassCondition = () => Player.ScoreProcessor.Combo.Value == 4 }); + + [Test] + public void TestInputIntro() => CreateModTest(new ModTestData + { + Mod = new TaikoModSingleTap(), + Autoplay = false, + Beatmap = new Beatmap + { + HitObjects = new List + { + new Hit + { + StartTime = 100, + Type = HitType.Rim + }, + }, + }, + ReplayFrames = new List + { + new TaikoReplayFrame(0, TaikoAction.RightRim), + new TaikoReplayFrame(20), + new TaikoReplayFrame(100, TaikoAction.LeftRim), + new TaikoReplayFrame(120), + }, + PassCondition = () => Player.ScoreProcessor.Combo.Value == 1 + }); + + [Test] + public void TestInputStrong() => CreateModTest(new ModTestData + { + Mod = new TaikoModSingleTap(), + Autoplay = false, + Beatmap = new Beatmap + { + HitObjects = new List + { + new Hit + { + StartTime = 100, + Type = HitType.Rim + }, + new Hit + { + StartTime = 200, + Type = HitType.Rim, + IsStrong = true + }, + }, + }, + ReplayFrames = new List + { + new TaikoReplayFrame(100, TaikoAction.RightRim), + new TaikoReplayFrame(120), + new TaikoReplayFrame(200, TaikoAction.LeftRim), + new TaikoReplayFrame(220), + }, + PassCondition = () => Player.ScoreProcessor.Combo.Value == 2 + }); + + [Test] + public void TestInputBreaks() => CreateModTest(new ModTestData + { + Mod = new TaikoModSingleTap(), + Autoplay = false, + Beatmap = new Beatmap + { + Breaks = new List + { + new BreakPeriod(100, 1600), + }, + HitObjects = new List + { + new Hit + { + StartTime = 100, + Type = HitType.Rim + }, + new Hit + { + StartTime = 2000, + Type = HitType.Rim, + IsStrong = true + }, + }, + }, + ReplayFrames = new List + { + new TaikoReplayFrame(100, TaikoAction.RightRim), + new TaikoReplayFrame(120), + // Press different key after break but before hit object. + new TaikoReplayFrame(1900, TaikoAction.LeftRim), + new TaikoReplayFrame(1820), + // Press original key at second hitobject and ensure it has been hit. + new TaikoReplayFrame(2000, TaikoAction.RightRim), + new TaikoReplayFrame(2020), + }, + PassCondition = () => Player.ScoreProcessor.Combo.Value == 2 + }); } } From 41e6956432cb5c1687ee664666d1ff4cb8b82978 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Fri, 3 Mar 2023 20:37:58 +0000 Subject: [PATCH 077/111] Improved single tap strong hit test --- .../Mods/TestSceneTaikoModSingleTap.cs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs index 7d544200cd..079f061c63 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs @@ -147,20 +147,27 @@ namespace osu.Game.Rulesets.Taiko.Tests.Mods }, new Hit { - StartTime = 200, + StartTime = 300, Type = HitType.Rim, IsStrong = true }, + new Hit + { + StartTime = 500, + Type = HitType.Rim, + }, }, }, ReplayFrames = new List { new TaikoReplayFrame(100, TaikoAction.RightRim), new TaikoReplayFrame(120), - new TaikoReplayFrame(200, TaikoAction.LeftRim), - new TaikoReplayFrame(220), + new TaikoReplayFrame(300, TaikoAction.LeftRim), + new TaikoReplayFrame(320), + new TaikoReplayFrame(500, TaikoAction.LeftRim), + new TaikoReplayFrame(520), }, - PassCondition = () => Player.ScoreProcessor.Combo.Value == 2 + PassCondition = () => Player.ScoreProcessor.Combo.Value == 0 && Player.ScoreProcessor.HighestCombo.Value == 2 }); [Test] From b4ed2db15e5954152660e80fb600f952a7393592 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Fri, 3 Mar 2023 20:40:30 +0000 Subject: [PATCH 078/111] Removed accidental strong hit in single tap break test --- osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs index 079f061c63..0cd3b85f8e 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs @@ -192,7 +192,6 @@ namespace osu.Game.Rulesets.Taiko.Tests.Mods { StartTime = 2000, Type = HitType.Rim, - IsStrong = true }, }, }, From df34bdc825e5496339de30ad3173f93dd28f40df Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 6 Mar 2023 19:31:45 +0900 Subject: [PATCH 079/111] Use lang version 10 --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 3b6b985961..734374c840 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@  - 9.0 + 10.0 true enable From a7be59175d15298d1a7eff2c9e090dca03c8492f Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Mon, 6 Mar 2023 10:57:09 +0000 Subject: [PATCH 080/111] Made private + renamed Taiko Single Tap tracking variables for better readability --- .../Mods/TaikoModSingleTap.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs index 1130338521..30e4946833 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs @@ -29,9 +29,9 @@ namespace osu.Game.Rulesets.Taiko.Mods protected bool CheckValidNewAction(TaikoAction action) { if (action == TaikoAction.LeftCentre || action == TaikoAction.RightCentre) - return LastAcceptedDonAction == null || LastAcceptedDonAction == action; + return lastAcceptedCentreAction == null || lastAcceptedCentreAction == action; if (action == TaikoAction.LeftRim || action == TaikoAction.RightRim) - return LastAcceptedKatAction == null || LastAcceptedKatAction == action; + return lastAcceptedRimAction == null || lastAcceptedRimAction == action; return true; } @@ -44,8 +44,8 @@ namespace osu.Game.Rulesets.Taiko.Mods private TaikoPlayfield playfield { get; set; } = null!; - protected TaikoAction? LastAcceptedDonAction { get; private set; } - protected TaikoAction? LastAcceptedKatAction { get; private set; } + private TaikoAction? lastAcceptedCentreAction { get; set; } + private TaikoAction? lastAcceptedRimAction { get; set; } /// /// A tracker for periods where alternate should not be forced (i.e. non-gameplay periods). @@ -84,11 +84,11 @@ namespace osu.Game.Rulesets.Taiko.Mods { if (!nonGameplayPeriods.IsInAny(gameplayClock.CurrentTime)) return; - if (LastAcceptedDonAction != null) - LastAcceptedDonAction = null; + if (lastAcceptedCentreAction != null) + lastAcceptedCentreAction = null; - if (LastAcceptedKatAction != null) - LastAcceptedKatAction = null; + if (lastAcceptedRimAction != null) + lastAcceptedRimAction = null; } private bool checkCorrectAction(TaikoAction action) @@ -105,9 +105,9 @@ namespace osu.Game.Rulesets.Taiko.Mods if (CheckValidNewAction(action)) { if (action == TaikoAction.LeftCentre || action == TaikoAction.RightCentre) - LastAcceptedDonAction = action; + lastAcceptedCentreAction = action; if (action == TaikoAction.LeftRim || action == TaikoAction.RightRim) - LastAcceptedKatAction = action; + lastAcceptedRimAction = action; return true; } From ac6c8e600a4ef58cedae8f19b8fabf50fa8aa6fc Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Mon, 6 Mar 2023 10:59:47 +0000 Subject: [PATCH 081/111] Updated a comment for correctness --- osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs index 30e4946833..9b411b49d4 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs @@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Taiko.Mods private TaikoAction? lastAcceptedRimAction { get; set; } /// - /// A tracker for periods where alternate should not be forced (i.e. non-gameplay periods). + /// A tracker for periods where single tap should not be enforced (i.e. non-gameplay periods). /// /// /// This is different from in that the periods here end strictly at the first object after the break, rather than the break's end time. From 4ae2661f1bd90181b54917ef55df431c7622ad57 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Mon, 6 Mar 2023 11:00:37 +0000 Subject: [PATCH 082/111] Removed useless null checks --- osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs index 9b411b49d4..92aff90ae0 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs @@ -84,11 +84,8 @@ namespace osu.Game.Rulesets.Taiko.Mods { if (!nonGameplayPeriods.IsInAny(gameplayClock.CurrentTime)) return; - if (lastAcceptedCentreAction != null) - lastAcceptedCentreAction = null; - - if (lastAcceptedRimAction != null) - lastAcceptedRimAction = null; + lastAcceptedCentreAction = null; + lastAcceptedRimAction = null; } private bool checkCorrectAction(TaikoAction action) From 36108ea841603febdc2602e9f4a85ad69c25f34a Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Mon, 6 Mar 2023 11:10:57 +0000 Subject: [PATCH 083/111] Moved `CheckValidNewAction` checks into `checkCorrectAction` --- .../Mods/TaikoModSingleTap.cs | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs index 92aff90ae0..556e31d2c2 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs @@ -26,16 +26,6 @@ namespace osu.Game.Rulesets.Taiko.Mods public override string Acronym => @"SG"; public override LocalisableString Description => @"One key for dons, one key for kats."; - protected bool CheckValidNewAction(TaikoAction action) - { - if (action == TaikoAction.LeftCentre || action == TaikoAction.RightCentre) - return lastAcceptedCentreAction == null || lastAcceptedCentreAction == action; - if (action == TaikoAction.LeftRim || action == TaikoAction.RightRim) - return lastAcceptedRimAction == null || lastAcceptedRimAction == action; - - return true; - } - public override double ScoreMultiplier => 1.0; public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModRelax), typeof(TaikoModCinema) }; public override ModType Type => ModType.Conversion; @@ -99,13 +89,17 @@ namespace osu.Game.Rulesets.Taiko.Mods && hitObject is not DrumRoll) return true; - if (CheckValidNewAction(action)) + if ((action == TaikoAction.LeftCentre || action == TaikoAction.RightCentre) + && (lastAcceptedCentreAction == null || lastAcceptedCentreAction == action)) { - if (action == TaikoAction.LeftCentre || action == TaikoAction.RightCentre) - lastAcceptedCentreAction = action; - if (action == TaikoAction.LeftRim || action == TaikoAction.RightRim) - lastAcceptedRimAction = action; + lastAcceptedCentreAction = action; + return true; + } + if ((action == TaikoAction.LeftRim || action == TaikoAction.RightRim) + && (lastAcceptedRimAction == null || lastAcceptedRimAction == action)) + { + lastAcceptedRimAction = action; return true; } From b2d453fd2edae6478bb0dfbfa860bea39a8251c3 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Mon, 6 Mar 2023 11:44:00 +0000 Subject: [PATCH 084/111] Fixed indentation --- osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs index 556e31d2c2..fa876555e1 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs @@ -90,14 +90,14 @@ namespace osu.Game.Rulesets.Taiko.Mods return true; if ((action == TaikoAction.LeftCentre || action == TaikoAction.RightCentre) - && (lastAcceptedCentreAction == null || lastAcceptedCentreAction == action)) + && (lastAcceptedCentreAction == null || lastAcceptedCentreAction == action)) { lastAcceptedCentreAction = action; return true; } if ((action == TaikoAction.LeftRim || action == TaikoAction.RightRim) - && (lastAcceptedRimAction == null || lastAcceptedRimAction == action)) + && (lastAcceptedRimAction == null || lastAcceptedRimAction == action)) { lastAcceptedRimAction = action; return true; From 5191204569241e0e92c93b51681a9384d711a019 Mon Sep 17 00:00:00 2001 From: Cootz Date: Tue, 7 Mar 2023 15:39:43 +0300 Subject: [PATCH 085/111] Bring truncating logic back to `Export` method --- osu.Game/Database/LegacyExporter.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game/Database/LegacyExporter.cs b/osu.Game/Database/LegacyExporter.cs index 0fa7b9e03c..9981e08442 100644 --- a/osu.Game/Database/LegacyExporter.cs +++ b/osu.Game/Database/LegacyExporter.cs @@ -48,15 +48,7 @@ namespace osu.Game.Database UserFileStorage = storage.GetStorageForDirectory(@"files"); } - protected virtual string GetFilename(TModel item) - { - string filename = item.GetDisplayString(); - - if (filename.Length > MAX_FILENAME_LENGTH - FileExtension.Length) - return filename.Remove(MAX_FILENAME_LENGTH - FileExtension.Length); - - return filename; - } + protected virtual string GetFilename(TModel item) => item.GetDisplayString(); /// /// Exports an item to a legacy (.zip based) package. @@ -73,6 +65,14 @@ namespace osu.Game.Database string filename = NamingUtils.GetNextBestFilename(existingExports, $"{itemFilename}{FileExtension}"); + if (filename.Length > MAX_FILENAME_LENGTH) + { + string filenameWithoutExtension = Path.GetFileNameWithoutExtension(filename); + + filenameWithoutExtension = filenameWithoutExtension.Remove(MAX_FILENAME_LENGTH - FileExtension.Length); + filename = $"{filenameWithoutExtension}{FileExtension}"; + } + using (var stream = exportStorage.CreateFileSafely(filename)) ExportModelTo(item, stream); From 7107834b9ec297062ff29442fa1a550c03ff5a94 Mon Sep 17 00:00:00 2001 From: Cootz Date: Tue, 7 Mar 2023 15:43:03 +0300 Subject: [PATCH 086/111] Fix truncating bug --- osu.Game/Database/LegacyExporter.cs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/osu.Game/Database/LegacyExporter.cs b/osu.Game/Database/LegacyExporter.cs index 9981e08442..8da285daa3 100644 --- a/osu.Game/Database/LegacyExporter.cs +++ b/osu.Game/Database/LegacyExporter.cs @@ -58,6 +58,9 @@ namespace osu.Game.Database { string itemFilename = GetFilename(item).GetValidFilename(); + if (itemFilename.Length > MAX_FILENAME_LENGTH - FileExtension.Length) + itemFilename = itemFilename.Remove(MAX_FILENAME_LENGTH - FileExtension.Length); + IEnumerable existingExports = exportStorage .GetFiles(string.Empty, $"{itemFilename}*{FileExtension}") @@ -65,14 +68,6 @@ namespace osu.Game.Database string filename = NamingUtils.GetNextBestFilename(existingExports, $"{itemFilename}{FileExtension}"); - if (filename.Length > MAX_FILENAME_LENGTH) - { - string filenameWithoutExtension = Path.GetFileNameWithoutExtension(filename); - - filenameWithoutExtension = filenameWithoutExtension.Remove(MAX_FILENAME_LENGTH - FileExtension.Length); - filename = $"{filenameWithoutExtension}{FileExtension}"; - } - using (var stream = exportStorage.CreateFileSafely(filename)) ExportModelTo(item, stream); From 686259a33c62aed9ce227001f8cedccdf3031f56 Mon Sep 17 00:00:00 2001 From: EXtremeExploit Date: Wed, 8 Mar 2023 13:57:20 -0300 Subject: [PATCH 087/111] Make support badge in list match groups --- osu.Game/Users/UserListPanel.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Users/UserListPanel.cs b/osu.Game/Users/UserListPanel.cs index 9b5b0e9f6d..3047e70a1a 100644 --- a/osu.Game/Users/UserListPanel.cs +++ b/osu.Game/Users/UserListPanel.cs @@ -67,6 +67,7 @@ namespace osu.Game.Users { username.Anchor = Anchor.CentreLeft; username.Origin = Anchor.CentreLeft; + username.UseFullGlyphHeight = false; }) } }, @@ -111,7 +112,7 @@ namespace osu.Game.Users { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Height = 20, + Height = 16, SupportLevel = User.SupportLevel }); } From 430b09acb211f1b8ab44c7e889d7b2d22cc7ea0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 8 Mar 2023 22:52:06 +0100 Subject: [PATCH 088/111] Expose taiko input manager in same manner as osu! --- osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs | 2 +- osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs index fa876555e1..a5cffca06f 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs @@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Taiko.Mods public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { ruleset = (DrawableTaikoRuleset)drawableRuleset; - ruleset.InputManager.Add(new InputInterceptor(this)); + ruleset.KeyBindingInputManager.Add(new InputInterceptor(this)); playfield = (TaikoPlayfield)ruleset.Playfield; var periods = new List(); diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs index 5390d3b138..a08877e2dd 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Taiko.UI public readonly BindableBool LockPlayfieldMaxAspect = new BindableBool(true); - public TaikoInputManager InputManager { get; private set; } + public new TaikoInputManager KeyBindingInputManager => (TaikoInputManager)base.KeyBindingInputManager; protected override ScrollVisualisationMethod VisualisationMethod => ScrollVisualisationMethod.Overlapping; @@ -95,7 +95,7 @@ namespace osu.Game.Rulesets.Taiko.UI LockPlayfieldMaxAspect = { BindTarget = LockPlayfieldMaxAspect } }; - protected override PassThroughInputManager CreateInputManager() => InputManager = new TaikoInputManager(Ruleset.RulesetInfo); + protected override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo); protected override Playfield CreatePlayfield() => new TaikoPlayfield(); From febdca45470f34a7cdfc493cddc13f371593099c Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Sun, 12 Mar 2023 02:08:00 +0100 Subject: [PATCH 089/111] Fix argon progress bar fill being oversized --- osu.Game/Screens/Play/HUD/ArgonSongProgressBar.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonSongProgressBar.cs b/osu.Game/Screens/Play/HUD/ArgonSongProgressBar.cs index 6db1072fbb..dd6e10ba5d 100644 --- a/osu.Game/Screens/Play/HUD/ArgonSongProgressBar.cs +++ b/osu.Game/Screens/Play/HUD/ArgonSongProgressBar.cs @@ -242,7 +242,6 @@ namespace osu.Game.Screens.Play.HUD { length = value; mask.Width = value * DrawWidth; - fill.Width = value * DrawWidth; } } From 0cf69a10846b59934a837d3a39b43c812092f848 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sat, 11 Mar 2023 21:35:52 -0800 Subject: [PATCH 090/111] Make yaml line strings verbatim --- osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs b/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs index 5f8bee7558..8dc058bd69 100644 --- a/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs +++ b/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs @@ -30,11 +30,11 @@ namespace osu.Game.Overlays.Wiki.Markdown { switch (line.ToString()) { - case "outdated: true": + case @"outdated: true": isOutdated = true; break; - case "needs_cleanup: true": + case @"needs_cleanup: true": needsCleanup = true; break; } From ea88aee41f27491d4022c5bb38a4661f55661e3c Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sat, 11 Mar 2023 21:36:15 -0800 Subject: [PATCH 091/111] Display stub notice in marked wiki articles --- .../Overlays/Wiki/Markdown/WikiNoticeContainer.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs b/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs index 8dc058bd69..df800f47b1 100644 --- a/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs +++ b/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs @@ -19,6 +19,7 @@ namespace osu.Game.Overlays.Wiki.Markdown { private readonly bool isOutdated; private readonly bool needsCleanup; + private readonly bool isStub; public WikiNoticeContainer(YamlFrontMatterBlock yamlFrontMatterBlock) { @@ -37,6 +38,10 @@ namespace osu.Game.Overlays.Wiki.Markdown case @"needs_cleanup: true": needsCleanup = true; break; + + case @"stub: true": + isStub = true; + break; } } } @@ -60,6 +65,14 @@ namespace osu.Game.Overlays.Wiki.Markdown Text = WikiStrings.ShowNeedsCleanupOrRewrite, }); } + + if (isStub) + { + Add(new NoticeBox + { + Text = WikiStrings.ShowStub, + }); + } } private partial class NoticeBox : Container From 6e5b1280b7261d37e89a494b973f35905099d630 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sat, 11 Mar 2023 21:18:56 -0800 Subject: [PATCH 092/111] Fix multiple notice boxes having no spacing --- osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs b/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs index df800f47b1..a40bd14878 100644 --- a/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs +++ b/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Resources.Localisation.Web; +using osuTK; namespace osu.Game.Overlays.Wiki.Markdown { @@ -26,6 +27,7 @@ namespace osu.Game.Overlays.Wiki.Markdown RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; Direction = FillDirection.Vertical; + Spacing = new Vector2(10); foreach (object line in yamlFrontMatterBlock.Lines) { From c1618a7a16f923cf57f891150d19fd9afde06aa7 Mon Sep 17 00:00:00 2001 From: Rovearix Date: Sun, 12 Mar 2023 10:57:53 -0400 Subject: [PATCH 093/111] Prevent elements that are anchored in the center from blocking loading --- osu.Game/Graphics/UserInterface/LoadingLayer.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Graphics/UserInterface/LoadingLayer.cs b/osu.Game/Graphics/UserInterface/LoadingLayer.cs index 9059b61a33..7cd54304b7 100644 --- a/osu.Game/Graphics/UserInterface/LoadingLayer.cs +++ b/osu.Game/Graphics/UserInterface/LoadingLayer.cs @@ -69,6 +69,10 @@ namespace osu.Game.Graphics.UserInterface // note that this will not work well if touch handling elements are beneath this loading layer (something to consider for the future). case TouchEvent: return false; + + // blocking drag events to prevent unintended ui pausing while loading a beat map (see https://github.com/ppy/osu/issues/22657) + case DragEvent: + return false; } return true; From 1f7721786b02d628d2b2c7c1d16c83b2d1a4a170 Mon Sep 17 00:00:00 2001 From: rozukke Date: Mon, 13 Mar 2023 22:01:26 +1100 Subject: [PATCH 094/111] Perform check to account for non-ASCII characters --- osu.Game/Skinning/SkinImporter.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/SkinImporter.cs b/osu.Game/Skinning/SkinImporter.cs index 9e2e02876d..43760c4a19 100644 --- a/osu.Game/Skinning/SkinImporter.cs +++ b/osu.Game/Skinning/SkinImporter.cs @@ -101,7 +101,8 @@ namespace osu.Game.Skinning // In both of these cases, the expectation from the user is that the filename or folder name is displayed somewhere to identify the skin. if (archiveName != item.Name // lazer exports use this format - && archiveName != item.GetDisplayString()) + // GetValidFilename accounts for skins with non-ASCII characters in the name that have been exported by lazer. + && archiveName != item.GetDisplayString().GetValidFilename()) item.Name = @$"{item.Name} [{archiveName}]"; } From ba728bdab1b4662366e3737fd4f84cf413e2d0e1 Mon Sep 17 00:00:00 2001 From: Rovearix Date: Mon, 13 Mar 2023 07:49:51 -0400 Subject: [PATCH 095/111] Changed the event to be the more correct one to block --- osu.Game/Graphics/UserInterface/LoadingLayer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/LoadingLayer.cs b/osu.Game/Graphics/UserInterface/LoadingLayer.cs index 7cd54304b7..3f9856eddd 100644 --- a/osu.Game/Graphics/UserInterface/LoadingLayer.cs +++ b/osu.Game/Graphics/UserInterface/LoadingLayer.cs @@ -71,7 +71,7 @@ namespace osu.Game.Graphics.UserInterface return false; // blocking drag events to prevent unintended ui pausing while loading a beat map (see https://github.com/ppy/osu/issues/22657) - case DragEvent: + case DragStartEvent: return false; } From 9ac9287dbd89051a685d9821cbbf951bdef37d7d Mon Sep 17 00:00:00 2001 From: Rovearix Date: Mon, 13 Mar 2023 08:07:55 -0400 Subject: [PATCH 096/111] Switched the implementation to set the blockInput flag for the BeatmapMetadataDisplay's LoadingLayer. This prevents the UIEvents from being being handled in this case without modifying the class --- osu.Game/Graphics/UserInterface/LoadingLayer.cs | 4 ---- osu.Game/Screens/Play/BeatmapMetadataDisplay.cs | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/LoadingLayer.cs b/osu.Game/Graphics/UserInterface/LoadingLayer.cs index 3f9856eddd..9059b61a33 100644 --- a/osu.Game/Graphics/UserInterface/LoadingLayer.cs +++ b/osu.Game/Graphics/UserInterface/LoadingLayer.cs @@ -69,10 +69,6 @@ namespace osu.Game.Graphics.UserInterface // note that this will not work well if touch handling elements are beneath this loading layer (something to consider for the future). case TouchEvent: return false; - - // blocking drag events to prevent unintended ui pausing while loading a beat map (see https://github.com/ppy/osu/issues/22657) - case DragStartEvent: - return false; } return true; diff --git a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs index 06509b6465..a152f4be19 100644 --- a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs +++ b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs @@ -114,7 +114,7 @@ namespace osu.Game.Screens.Play Anchor = Anchor.Centre, FillMode = FillMode.Fill, }, - loading = new LoadingLayer(true) + loading = new LoadingLayer(dimBackground: true, blockInput: false) } }, versionFlow = new FillFlowContainer From 4570c0030f1211bc166eff6be86ced372c1e0907 Mon Sep 17 00:00:00 2001 From: rozukke Date: Mon, 13 Mar 2023 23:37:45 +1100 Subject: [PATCH 097/111] Add test to check for import of exported skin with non-ASCII name --- osu.Game.Tests/Skins/IO/ImportSkinTest.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs index 0bd40e9962..81ebc59729 100644 --- a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs +++ b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs @@ -133,6 +133,25 @@ namespace osu.Game.Tests.Skins.IO assertImportedOnce(import1, import2); }); + [Test] + public Task TestImportExportedNonAsciiSkinFilename() => runSkinTest(async osu => + { + MemoryStream exportStream = new MemoryStream(); + + var import1 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni("name 『1』", "author 1"), "custom.osk")); + assertCorrectMetadata(import1, "name 『1』 [custom]", "author 1", osu); + + import1.PerformRead(s => + { + new LegacySkinExporter(osu.Dependencies.Get()).ExportModelTo(s, exportStream); + }); + + string exportFilename = import1.GetDisplayString().GetValidFilename(); + + var import2 = await loadSkinIntoOsu(osu, new ImportTask(exportStream, $"{exportFilename}.osk")); + assertCorrectMetadata(import2, "name 『1』 [custom]", "author 1", osu); + }); + [Test] public Task TestSameMetadataNameSameFolderName([Values] bool batchImport) => runSkinTest(async osu => { From 48d11610b344ac23dd0ff3628b90ee4b4b40402d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Mar 2023 17:44:51 +0900 Subject: [PATCH 098/111] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 234575fc62..4e580a6919 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -11,7 +11,7 @@ manifestmerger.jar - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 103ac99172..a84b42d9a4 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index fde45817cf..8738979c57 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -16,6 +16,6 @@ iossimulator-x64 - + From 0b23809585d73617942c7f5a0edebfcab2ca02ee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Mar 2023 17:52:17 +0900 Subject: [PATCH 099/111] Update resources --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index a84b42d9a4..c08dc9ed8f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + From d65d09e45f9c02186bec88fcb9ab01867ead71b7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Mar 2023 20:24:39 +0900 Subject: [PATCH 100/111] Change field to `const` --- osu.Game/Graphics/Backgrounds/Triangles.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index 5b7b94ff4c..28a715ca0b 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -252,7 +252,7 @@ namespace osu.Game.Graphics.Backgrounds private class TrianglesDrawNode : DrawNode { - private float fill = 1f; + private const float fill = 1f; protected new Triangles Source => (Triangles)base.Source; From b5ea855b6cf8ff9faa5659c1a2e6f4e41b5f5739 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Mar 2023 20:37:50 +0900 Subject: [PATCH 101/111] Fix failing `DrawableRulesetDependencies` test --- .../Rulesets/TestSceneDrawableRulesetDependencies.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Rulesets/TestSceneDrawableRulesetDependencies.cs b/osu.Game.Tests/Rulesets/TestSceneDrawableRulesetDependencies.cs index 5cfcca303f..f8248e88bb 100644 --- a/osu.Game.Tests/Rulesets/TestSceneDrawableRulesetDependencies.cs +++ b/osu.Game.Tests/Rulesets/TestSceneDrawableRulesetDependencies.cs @@ -80,7 +80,7 @@ namespace osu.Game.Tests.Rulesets dependencies.CacheAs(ParentTextureStore = new TestTextureStore(parent.Get().Renderer)); dependencies.CacheAs(ParentSampleStore = new TestSampleStore()); - dependencies.CacheAs(ParentShaderManager = new TestShaderManager(parent.Get().Renderer)); + dependencies.CacheAs(ParentShaderManager = new TestShaderManager(parent.Get().Renderer, parent.Get())); return new DrawableRulesetDependencies(new OsuRuleset(), dependencies); } @@ -156,12 +156,15 @@ namespace osu.Game.Tests.Rulesets private class TestShaderManager : ShaderManager { - public TestShaderManager(IRenderer renderer) + private readonly ShaderManager parentManager; + + public TestShaderManager(IRenderer renderer, ShaderManager parentManager) : base(renderer, new ResourceStore()) { + this.parentManager = parentManager; } - public override byte[] LoadRaw(string name) => null; + public override byte[] LoadRaw(string name) => parentManager.LoadRaw(name); public bool IsDisposed { get; private set; } From cb9b14b30fd8c3620d5b45aadb9152a9b29f3617 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Mar 2023 19:48:36 +0900 Subject: [PATCH 102/111] Revert "Merge pull request #22741 from cdwcgt/do-not-fetch-deletePending" This reverts commit 15c44a281725189671c29fb569949f7c8be66cbe, reversing changes made to de2ab05e785dfaf2fb108c68c4c7424c8db417a5. --- osu.Game/Scoring/Legacy/DatabasedLegacyScoreDecoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Scoring/Legacy/DatabasedLegacyScoreDecoder.cs b/osu.Game/Scoring/Legacy/DatabasedLegacyScoreDecoder.cs index 8ee2cc6241..8908163646 100644 --- a/osu.Game/Scoring/Legacy/DatabasedLegacyScoreDecoder.cs +++ b/osu.Game/Scoring/Legacy/DatabasedLegacyScoreDecoder.cs @@ -24,6 +24,6 @@ namespace osu.Game.Scoring.Legacy } protected override Ruleset GetRuleset(int rulesetId) => rulesets.GetRuleset(rulesetId)?.CreateInstance(); - protected override WorkingBeatmap GetBeatmap(string md5Hash) => beatmaps.GetWorkingBeatmap(beatmaps.QueryBeatmap(b => b.MD5Hash == md5Hash && !b.BeatmapSet.DeletePending)); + protected override WorkingBeatmap GetBeatmap(string md5Hash) => beatmaps.GetWorkingBeatmap(beatmaps.QueryBeatmap(b => b.MD5Hash == md5Hash)); } } From 1c3b60b9e6c6d31a2d955fcf0dafe41b20774abf Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 16 Mar 2023 23:56:41 +0900 Subject: [PATCH 103/111] Use custom vertex shader for logo animation --- osu.Game/Graphics/Sprites/LogoAnimation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Sprites/LogoAnimation.cs b/osu.Game/Graphics/Sprites/LogoAnimation.cs index 220f57e9fa..619a28ecad 100644 --- a/osu.Game/Graphics/Sprites/LogoAnimation.cs +++ b/osu.Game/Graphics/Sprites/LogoAnimation.cs @@ -18,7 +18,7 @@ namespace osu.Game.Graphics.Sprites [BackgroundDependencyLoader] private void load(ShaderManager shaders) { - TextureShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, @"LogoAnimation"); + TextureShader = shaders.Load(@"LogoAnimation", @"LogoAnimation"); } private float animationProgress; From 8bdb89d05dbc779fda0a6b9f924b6e07eca2b63c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Mar 2023 19:12:43 +0900 Subject: [PATCH 104/111] Update resources --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index c08dc9ed8f..ce9cf37ec3 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + From bcd24873d6361992339ca2ef5e1fdd6053419c33 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 17 Mar 2023 20:37:05 +0900 Subject: [PATCH 105/111] Use custom vertex type --- osu.Game/Graphics/Sprites/LogoAnimation.cs | 37 ++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/osu.Game/Graphics/Sprites/LogoAnimation.cs b/osu.Game/Graphics/Sprites/LogoAnimation.cs index 619a28ecad..3b8b0bfc88 100644 --- a/osu.Game/Graphics/Sprites/LogoAnimation.cs +++ b/osu.Game/Graphics/Sprites/LogoAnimation.cs @@ -3,13 +3,18 @@ #nullable disable +using System; using System.Runtime.InteropServices; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Rendering; +using osu.Framework.Graphics.Rendering.Vertices; using osu.Framework.Graphics.Shaders; using osu.Framework.Graphics.Shaders.Types; using osu.Framework.Graphics.Sprites; +using osuTK; +using osuTK.Graphics; +using osuTK.Graphics.ES30; namespace osu.Game.Graphics.Sprites { @@ -43,11 +48,22 @@ namespace osu.Game.Graphics.Sprites { private LogoAnimation source => (LogoAnimation)Source; + private readonly Action addVertexAction; + private float progress; public LogoAnimationDrawNode(LogoAnimation source) : base(source) { + addVertexAction = v => + { + animationVertexBatch!.Add(new LogoAnimationVertex + { + Position = v.Position, + Colour = v.Colour, + TexturePosition = v.TexturePosition, + }); + }; } public override void ApplyState() @@ -58,10 +74,13 @@ namespace osu.Game.Graphics.Sprites } private IUniformBuffer animationDataBuffer; + private IVertexBatch animationVertexBatch; protected override void Blit(IRenderer renderer) { animationDataBuffer ??= renderer.CreateUniformBuffer(); + animationVertexBatch ??= renderer.CreateQuadBatch(1, 2); + animationDataBuffer.Data = animationDataBuffer.Data with { Progress = progress }; TextureShader.BindUniformBlock("m_AnimationData", animationDataBuffer); @@ -83,6 +102,24 @@ namespace osu.Game.Graphics.Sprites public UniformFloat Progress; private readonly UniformPadding12 pad1; } + + [StructLayout(LayoutKind.Sequential)] + private struct LogoAnimationVertex : IEquatable, IVertex + { + [VertexMember(2, VertexAttribPointerType.Float)] + public Vector2 Position; + + [VertexMember(4, VertexAttribPointerType.Float)] + public Color4 Colour; + + [VertexMember(2, VertexAttribPointerType.Float)] + public Vector2 TexturePosition; + + public readonly bool Equals(LogoAnimationVertex other) => + Position.Equals(other.Position) + && TexturePosition.Equals(other.TexturePosition) + && Colour.Equals(other.Colour); + } } } } From c08513d59049e78c8151a2b2fbaaa4df1921ccef Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 17 Mar 2023 20:47:11 +0900 Subject: [PATCH 106/111] Actually use custom vertex action --- osu.Game/Graphics/Sprites/LogoAnimation.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Sprites/LogoAnimation.cs b/osu.Game/Graphics/Sprites/LogoAnimation.cs index 3b8b0bfc88..fbf8717d3c 100644 --- a/osu.Game/Graphics/Sprites/LogoAnimation.cs +++ b/osu.Game/Graphics/Sprites/LogoAnimation.cs @@ -78,6 +78,9 @@ namespace osu.Game.Graphics.Sprites protected override void Blit(IRenderer renderer) { + if (DrawRectangle.Width == 0 || DrawRectangle.Height == 0) + return; + animationDataBuffer ??= renderer.CreateUniformBuffer(); animationVertexBatch ??= renderer.CreateQuadBatch(1, 2); @@ -85,7 +88,13 @@ namespace osu.Game.Graphics.Sprites TextureShader.BindUniformBlock("m_AnimationData", animationDataBuffer); - base.Blit(renderer); + renderer.DrawQuad( + Texture, + ScreenSpaceDrawQuad, + DrawColourInfo.Colour, + inflationPercentage: new Vector2(InflationAmount.X / DrawRectangle.Width, InflationAmount.Y / DrawRectangle.Height), + textureCoords: TextureCoords, + vertexAction: addVertexAction); } protected override bool CanDrawOpaqueInterior => false; From 970df5d88a09d6d55797739164ce20cb08b9aed4 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sun, 12 Mar 2023 18:46:34 -0700 Subject: [PATCH 107/111] Update profile kudosu section in line with web --- .../Profile/Sections/Kudosu/KudosuInfo.cs | 24 +------------------ 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs index d2f01ef9f7..7b26640e50 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs @@ -2,15 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Bindables; -using osuTK; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; -using osu.Framework.Allocation; using osu.Framework.Extensions.LocalisationExtensions; using osu.Game.Resources.Localisation.Web; using osu.Framework.Localisation; @@ -52,7 +49,6 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu { private readonly OsuSpriteText valueText; protected readonly LinkFlowContainer DescriptionText; - private readonly Box lineBackground; public new int Count { @@ -63,25 +59,14 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; - Padding = new MarginPadding { Top = 10, Bottom = 20 }; + Padding = new MarginPadding { Bottom = 20 }; Child = new FillFlowContainer { AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 5), Children = new Drawable[] { - new CircularContainer - { - Masking = true, - RelativeSizeAxes = Axes.X, - Height = 2, - Child = lineBackground = new Box - { - RelativeSizeAxes = Axes.Both, - } - }, new OsuSpriteText { Text = header, @@ -91,7 +76,6 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu { Text = "0", Font = OsuFont.GetFont(size: 40, weight: FontWeight.Light), - UseFullGlyphHeight = false, }, DescriptionText = new LinkFlowContainer(t => t.Font = t.Font.With(size: 14)) { @@ -101,12 +85,6 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu } }; } - - [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) - { - lineBackground.Colour = colourProvider.Highlight1; - } } } } From 48b6e214af56fab60571964a91474c5f8d3ce296 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Sun, 19 Mar 2023 17:12:12 +0100 Subject: [PATCH 108/111] Fix URL handling on macOS --- osu.Desktop/OsuGameDesktop.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index b3f6370ccb..3fe251cb00 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -139,7 +139,17 @@ namespace osu.Desktop desktopWindow.CursorState |= CursorState.Hidden; desktopWindow.Title = Name; - desktopWindow.DragDrop += f => fileDrop(new[] { f }); + desktopWindow.DragDrop += f => + { + // on macOS, URL associations are handled via SDL_DROPFILE events. + if (f.StartsWith(OSU_PROTOCOL, StringComparison.Ordinal)) + { + HandleLink(f); + return; + } + + fileDrop(new[] { f }); + }; } protected override BatteryInfo CreateBatteryInfo() => new SDL2BatteryInfo(); From b254dbd7ca409e502b9d041f9c0c623ea3afd841 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Sun, 19 Mar 2023 17:37:49 +0100 Subject: [PATCH 109/111] Remove arbitrary extension limitation from drag and drop imports `OsuGameBase` already properly handles multiple extensions in the same import. --- osu.Desktop/OsuGameDesktop.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index b3f6370ccb..6b5c5b809b 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -151,10 +151,6 @@ namespace osu.Desktop { lock (importableFiles) { - string firstExtension = Path.GetExtension(filePaths.First()); - - if (filePaths.Any(f => Path.GetExtension(f) != firstExtension)) return; - importableFiles.AddRange(filePaths); Logger.Log($"Adding {filePaths.Length} files for import"); From 11f52d5bf4440d5012dbaff2167e921d7f2fd49c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Mar 2023 14:01:35 +0900 Subject: [PATCH 110/111] Remove unused using statement --- osu.Desktop/OsuGameDesktop.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 6b5c5b809b..f094785a5b 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.Linq; using System.Reflection; using System.Runtime.Versioning; using System.Threading.Tasks; From 8557589a3542c7bc26969c9c8cbdac5f3a418ec5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Mar 2023 15:28:13 +0900 Subject: [PATCH 111/111] Update resources --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index ce9cf37ec3..54d6533713 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - +