From f1302d16006b567343c12a2624eeb544c7eae9bf Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Thu, 11 Mar 2021 19:23:56 +0300 Subject: [PATCH 01/34] Update Microsoft.EntityFrameworkCore --- osu.Desktop/osu.Desktop.csproj | 7 +++++-- osu.Game/Database/OsuDbContext.cs | 3 --- osu.Game/osu.Game.csproj | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 3e0f0cb7f6..4af69c573d 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -27,8 +27,11 @@ - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs index 2aae62edea..d27da50448 100644 --- a/osu.Game/Database/OsuDbContext.cs +++ b/osu.Game/Database/OsuDbContext.cs @@ -111,9 +111,6 @@ namespace osu.Game.Database { base.OnConfiguring(optionsBuilder); optionsBuilder - // this is required for the time being due to the way we are querying in places like BeatmapStore. - // if we ever move to having consumers file their own .Includes, or get eager loading support, this could be re-enabled. - .ConfigureWarnings(warnings => warnings.Ignore(CoreEventId.IncludeIgnoredWarning)) .UseSqlite(connectionString, sqliteOptions => sqliteOptions.CommandTimeout(10)) .UseLoggerFactory(logger.Value); } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 90c8b98f42..fa1b0a95c3 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -24,10 +24,10 @@ - - + + - + From 47b80d2474f6c2a371dd91aa48fda003a60e808e Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Thu, 11 Mar 2021 20:51:54 +0300 Subject: [PATCH 02/34] Workaround InvalidOperation exceptions --- osu.Game/Beatmaps/BeatmapManager.cs | 16 ++++++++++++++++ osu.Game/Database/ArchiveModelManager.cs | 8 +++++++- osu.Game/Skinning/SkinManager.cs | 12 ++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 29b3f5d3a3..3254f53574 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -174,6 +174,22 @@ namespace osu.Game.Beatmaps if (beatmapSet.Beatmaps.Any(b => b.BaseDifficulty == null)) throw new InvalidOperationException($"Cannot import {nameof(BeatmapInfo)} with null {nameof(BeatmapInfo.BaseDifficulty)}."); + var dbContext = ContextFactory.Get(); + + // Workaround System.InvalidOperationException + // The instance of entity type 'RulesetInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. + foreach (var beatmap in beatmapSet.Beatmaps) + { + beatmap.Ruleset = dbContext.RulesetInfo.Find(beatmap.RulesetID); + } + + // Workaround System.InvalidOperationException + // The instance of entity type 'FileInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. + foreach (var file in beatmapSet.Files) + { + file.FileInfo = dbContext.FileInfo.Find(file.FileInfoID); + } + // check if a set already exists with the same online id, delete if it does. if (beatmapSet.OnlineBeatmapSetID != null) { diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index d809dbcb01..fe2caaa0b7 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -462,6 +462,10 @@ namespace osu.Game.Database // Dereference the existing file info, since the file model will be removed. if (file.FileInfo != null) { + // Workaround System.InvalidOperationException + // The instance of entity type 'FileInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. + file.FileInfo = usage.Context.FileInfo.Find(file.FileInfoID); + Files.Dereference(file.FileInfo); // This shouldn't be required, but here for safety in case the provided TModel is not being change tracked @@ -635,10 +639,12 @@ namespace osu.Game.Database { using (Stream s = reader.GetStream(file)) { + var fileInfo = files.Add(s); fileInfos.Add(new TFileModel { Filename = file.Substring(prefix.Length).ToStandardisedPath(), - FileInfo = files.Add(s) + FileInfo = fileInfo, + FileInfoID = fileInfo.ID }); } } diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index fcde9f041b..2bb27b60d6 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -142,6 +142,18 @@ namespace osu.Game.Skinning } } + protected override void PreImport(SkinInfo model) + { + var dbContext = ContextFactory.Get(); + + // Workaround System.InvalidOperationException + // The instance of entity type 'FileInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. + foreach (var file in model.Files) + { + file.FileInfo = dbContext.FileInfo.Find(file.FileInfoID); + } + } + /// /// Retrieve a instance for the provided /// From c6c616f244eb08e664c04937234ffcd6dd84b9c0 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Thu, 11 Mar 2021 21:02:40 +0300 Subject: [PATCH 03/34] Actualize tests --- .../Multiplayer/TestSceneMultiplayerMatchSongSelect.cs | 1 + .../SongSelect/TestSceneBeatmapRecommendations.cs | 1 + .../Visual/SongSelect/TestScenePlaySongSelect.cs | 4 +++- osu.Game/Scoring/ScoreInfo.cs | 10 ++-------- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs index faa5d9e6fc..4a9eaa1842 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs @@ -56,6 +56,7 @@ namespace osu.Game.Tests.Visual.Multiplayer beatmaps.Add(new BeatmapInfo { Ruleset = rulesets.GetRuleset(i % 4), + RulesetID = i % 4, OnlineBeatmapID = beatmapId, Length = length, BPM = bpm, diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs index 53a956c77c..223ace6ca5 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs @@ -186,6 +186,7 @@ namespace osu.Game.Tests.Visual.SongSelect Metadata = metadata, BaseDifficulty = new BeatmapDifficulty(), Ruleset = ruleset, + RulesetID = ruleset.ID.GetValueOrDefault(), StarDifficulty = difficultyIndex + 1, Version = $"SR{difficultyIndex + 1}" }).ToList() diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 35c6d62cb7..4b402d0c54 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -911,9 +911,11 @@ namespace osu.Game.Tests.Visual.SongSelect int length = RNG.Next(30000, 200000); double bpm = RNG.NextSingle(80, 200); + var ruleset = getRuleset(); beatmaps.Add(new BeatmapInfo { - Ruleset = getRuleset(), + Ruleset = ruleset, + RulesetID = ruleset.ID.GetValueOrDefault(), OnlineBeatmapID = beatmapId, Version = $"{beatmapId} (length {TimeSpan.FromMilliseconds(length):m\\:ss}, bpm {bpm:0.#})", Length = length, diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index f5192f3a40..c5ad43abba 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -73,7 +73,7 @@ namespace osu.Game.Scoring } set { - modsJson = null; + modsJson = JsonConvert.SerializeObject(value.Select(m => new DeserializedMod { Acronym = m.Acronym })); mods = value; } } @@ -88,13 +88,7 @@ namespace osu.Game.Scoring { get { - if (modsJson != null) - return modsJson; - - if (mods == null) - return null; - - return modsJson = JsonConvert.SerializeObject(mods.Select(m => new DeserializedMod { Acronym = m.Acronym })); + return modsJson; } set { From d2f943395d349e08ae8e5a72b1f2996ea0d1d539 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Thu, 11 Mar 2021 22:12:47 +0300 Subject: [PATCH 04/34] Hotfix importing scores from stable --- osu.Game/Scoring/ScoreManager.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index 96ec9644b5..a97c516a1b 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -52,6 +52,23 @@ namespace osu.Game.Scoring this.configManager = configManager; } + protected override void PreImport(ScoreInfo model) + { + var dbContext = ContextFactory.Get(); + + // Workaround System.InvalidOperationException + // The instance of entity type 'FileInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. + foreach (var file in model.Files) + { + file.FileInfo = dbContext.FileInfo.Find(file.FileInfoID); + } + + foreach (var file in model.Beatmap.BeatmapSet.Files) + { + file.FileInfo = dbContext.FileInfo.Find(file.FileInfoID); + } + } + protected override ScoreInfo CreateModel(ArchiveReader archive) { if (archive == null) From 5a4b0174b187e649b9fb739fafd076ea19817f23 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Thu, 11 Mar 2021 22:40:35 +0300 Subject: [PATCH 05/34] Ignore MultipleCollectionIncludeWarning --- osu.Game/Database/OsuDbContext.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs index d27da50448..689f248de8 100644 --- a/osu.Game/Database/OsuDbContext.cs +++ b/osu.Game/Database/OsuDbContext.cs @@ -112,6 +112,7 @@ namespace osu.Game.Database base.OnConfiguring(optionsBuilder); optionsBuilder .UseSqlite(connectionString, sqliteOptions => sqliteOptions.CommandTimeout(10)) + .ConfigureWarnings(w => w.Ignore(RelationalEventId.MultipleCollectionIncludeWarning)) .UseLoggerFactory(logger.Value); } From a60ff80c04850c1f09ad9f741197378e691fc78d Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Fri, 12 Mar 2021 00:02:29 +0300 Subject: [PATCH 06/34] Use expression body in ModsJson get accessor --- osu.Game/Scoring/ScoreInfo.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index c5ad43abba..78101991f6 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -86,10 +86,7 @@ namespace osu.Game.Scoring [Column("Mods")] public string ModsJson { - get - { - return modsJson; - } + get => modsJson; set { modsJson = value; From 0a1e325fc774f371785ba95b618f71bd0637bb2e Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Sun, 14 Mar 2021 19:34:53 +0300 Subject: [PATCH 07/34] Extract requerying of navigational properties from DbContext --- osu.Game/Beatmaps/BeatmapManager.cs | 16 +---------- osu.Game/Database/Extensions.cs | 44 +++++++++++++++++++++++++++++ osu.Game/Scoring/ScoreManager.cs | 14 +-------- osu.Game/Skinning/SkinManager.cs | 9 +----- 4 files changed, 47 insertions(+), 36 deletions(-) create mode 100644 osu.Game/Database/Extensions.cs diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 3254f53574..f42fba79cb 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -174,21 +174,7 @@ namespace osu.Game.Beatmaps if (beatmapSet.Beatmaps.Any(b => b.BaseDifficulty == null)) throw new InvalidOperationException($"Cannot import {nameof(BeatmapInfo)} with null {nameof(BeatmapInfo.BaseDifficulty)}."); - var dbContext = ContextFactory.Get(); - - // Workaround System.InvalidOperationException - // The instance of entity type 'RulesetInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. - foreach (var beatmap in beatmapSet.Beatmaps) - { - beatmap.Ruleset = dbContext.RulesetInfo.Find(beatmap.RulesetID); - } - - // Workaround System.InvalidOperationException - // The instance of entity type 'FileInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. - foreach (var file in beatmapSet.Files) - { - file.FileInfo = dbContext.FileInfo.Find(file.FileInfoID); - } + beatmapSet.Requery(ContextFactory); // check if a set already exists with the same online id, delete if it does. if (beatmapSet.OnlineBeatmapSetID != null) diff --git a/osu.Game/Database/Extensions.cs b/osu.Game/Database/Extensions.cs new file mode 100644 index 0000000000..3af26c348e --- /dev/null +++ b/osu.Game/Database/Extensions.cs @@ -0,0 +1,44 @@ +// 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 osu.Game.Beatmaps; +using osu.Game.Scoring; + +namespace osu.Game.Database +{ + public static class Extensions + { + public static void Requery(this BeatmapSetInfo beatmapSetInfo, IDatabaseContextFactory databaseContextFactory) + { + var dbContext = databaseContextFactory.Get(); + + foreach (var beatmap in beatmapSetInfo.Beatmaps) + { + // Workaround System.InvalidOperationException + // The instance of entity type 'RulesetInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. + beatmap.Ruleset = dbContext.RulesetInfo.Find(beatmap.RulesetID); + } + + beatmapSetInfo.Files.Requery(databaseContextFactory); + } + + public static void Requery(this ScoreInfo scoreInfo, IDatabaseContextFactory databaseContextFactory) + { + scoreInfo.Files.Requery(databaseContextFactory); + scoreInfo.Beatmap.BeatmapSet.Files.Requery(databaseContextFactory); + } + + public static void Requery(this List files, IDatabaseContextFactory databaseContextFactory) where T : class, INamedFileInfo + { + var dbContext = databaseContextFactory.Get(); + + foreach (var file in files) + { + // Workaround System.InvalidOperationException + // The instance of entity type 'FileInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. + file.FileInfo = dbContext.FileInfo.Find(file.FileInfoID); + } + } + } +} diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index a97c516a1b..1e90ee1ac7 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -54,19 +54,7 @@ namespace osu.Game.Scoring protected override void PreImport(ScoreInfo model) { - var dbContext = ContextFactory.Get(); - - // Workaround System.InvalidOperationException - // The instance of entity type 'FileInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. - foreach (var file in model.Files) - { - file.FileInfo = dbContext.FileInfo.Find(file.FileInfoID); - } - - foreach (var file in model.Beatmap.BeatmapSet.Files) - { - file.FileInfo = dbContext.FileInfo.Find(file.FileInfoID); - } + model.Requery(ContextFactory); } protected override ScoreInfo CreateModel(ArchiveReader archive) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 2bb27b60d6..c25f00eccb 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -144,14 +144,7 @@ namespace osu.Game.Skinning protected override void PreImport(SkinInfo model) { - var dbContext = ContextFactory.Get(); - - // Workaround System.InvalidOperationException - // The instance of entity type 'FileInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. - foreach (var file in model.Files) - { - file.FileInfo = dbContext.FileInfo.Find(file.FileInfoID); - } + model.Files.Requery(ContextFactory); } /// From 61d5a6cc57941cab31e2abb0acee00c8fad3f80f Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Sun, 14 Mar 2021 19:47:14 +0300 Subject: [PATCH 08/34] Simplify Microsoft.EntityFrameworkCore.Design PackageReference --- osu.Desktop/osu.Desktop.csproj | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 4af69c573d..d9d23dea6b 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -28,10 +28,7 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - + From 28ef64b62a5d873110a163c4275fb48c9c47b262 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Sun, 14 Mar 2021 21:43:27 +0300 Subject: [PATCH 09/34] Explicitly specify SingleQuery behavior --- osu.Game/Database/OsuDbContext.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs index 689f248de8..e5ae530018 100644 --- a/osu.Game/Database/OsuDbContext.cs +++ b/osu.Game/Database/OsuDbContext.cs @@ -3,7 +3,6 @@ using System; using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.Extensions.Logging; using osu.Framework.Logging; using osu.Framework.Statistics; @@ -111,8 +110,7 @@ namespace osu.Game.Database { base.OnConfiguring(optionsBuilder); optionsBuilder - .UseSqlite(connectionString, sqliteOptions => sqliteOptions.CommandTimeout(10)) - .ConfigureWarnings(w => w.Ignore(RelationalEventId.MultipleCollectionIncludeWarning)) + .UseSqlite(connectionString, sqliteOptions => sqliteOptions.CommandTimeout(10).UseQuerySplittingBehavior(QuerySplittingBehavior.SingleQuery)) .UseLoggerFactory(logger.Value); } From 900da7b891c3a74faa3ebbe0f030e9cf3350b273 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Mar 2021 12:42:41 +0900 Subject: [PATCH 10/34] Rename and refactor extenion methods to be easier to read --- .../Database/DatabaseWorkaroundExtensions.cs | 48 +++++++++++++++++++ osu.Game/Database/Extensions.cs | 44 ----------------- 2 files changed, 48 insertions(+), 44 deletions(-) create mode 100644 osu.Game/Database/DatabaseWorkaroundExtensions.cs delete mode 100644 osu.Game/Database/Extensions.cs diff --git a/osu.Game/Database/DatabaseWorkaroundExtensions.cs b/osu.Game/Database/DatabaseWorkaroundExtensions.cs new file mode 100644 index 0000000000..07ce7e8529 --- /dev/null +++ b/osu.Game/Database/DatabaseWorkaroundExtensions.cs @@ -0,0 +1,48 @@ +// 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 osu.Game.Beatmaps; +using osu.Game.Scoring; + +namespace osu.Game.Database +{ + public static class DatabaseWorkaroundExtensions + { + public static void Requery(this IHasPrimaryKey model, IDatabaseContextFactory contextFactory) + { + switch (model) + { + case ScoreInfo scoreInfo: + scoreInfo.Beatmap.BeatmapSet.Requery(contextFactory); + scoreInfo.Files.RequeryFiles(contextFactory); + break; + + case BeatmapSetInfo beatmapSetInfo: + var context = contextFactory.Get(); + + foreach (var beatmap in beatmapSetInfo.Beatmaps) + { + // Workaround System.InvalidOperationException + // The instance of entity type 'RulesetInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. + beatmap.Ruleset = context.RulesetInfo.Find(beatmap.RulesetID); + } + + requeryFiles(beatmapSetInfo.Files, contextFactory); + break; + } + } + + public static void RequeryFiles(this List files, IDatabaseContextFactory databaseContextFactory) where T : class, INamedFileInfo + { + var dbContext = databaseContextFactory.Get(); + + foreach (var file in files) + { + // Workaround System.InvalidOperationException + // The instance of entity type 'FileInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. + file.FileInfo = dbContext.FileInfo.Find(file.FileInfoID); + } + } + } +} diff --git a/osu.Game/Database/Extensions.cs b/osu.Game/Database/Extensions.cs deleted file mode 100644 index 3af26c348e..0000000000 --- a/osu.Game/Database/Extensions.cs +++ /dev/null @@ -1,44 +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.Collections.Generic; -using osu.Game.Beatmaps; -using osu.Game.Scoring; - -namespace osu.Game.Database -{ - public static class Extensions - { - public static void Requery(this BeatmapSetInfo beatmapSetInfo, IDatabaseContextFactory databaseContextFactory) - { - var dbContext = databaseContextFactory.Get(); - - foreach (var beatmap in beatmapSetInfo.Beatmaps) - { - // Workaround System.InvalidOperationException - // The instance of entity type 'RulesetInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. - beatmap.Ruleset = dbContext.RulesetInfo.Find(beatmap.RulesetID); - } - - beatmapSetInfo.Files.Requery(databaseContextFactory); - } - - public static void Requery(this ScoreInfo scoreInfo, IDatabaseContextFactory databaseContextFactory) - { - scoreInfo.Files.Requery(databaseContextFactory); - scoreInfo.Beatmap.BeatmapSet.Files.Requery(databaseContextFactory); - } - - public static void Requery(this List files, IDatabaseContextFactory databaseContextFactory) where T : class, INamedFileInfo - { - var dbContext = databaseContextFactory.Get(); - - foreach (var file in files) - { - // Workaround System.InvalidOperationException - // The instance of entity type 'FileInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. - file.FileInfo = dbContext.FileInfo.Find(file.FileInfoID); - } - } - } -} From 2bdffd10044984ea0a5639754b8ecff8f4fc979b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Mar 2021 12:47:58 +0900 Subject: [PATCH 11/34] Move skin requery logic into extension methods --- .../Database/DatabaseWorkaroundExtensions.cs | 19 +++++++++++++++++-- osu.Game/Skinning/SkinManager.cs | 2 +- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/osu.Game/Database/DatabaseWorkaroundExtensions.cs b/osu.Game/Database/DatabaseWorkaroundExtensions.cs index 07ce7e8529..39bf358071 100644 --- a/osu.Game/Database/DatabaseWorkaroundExtensions.cs +++ b/osu.Game/Database/DatabaseWorkaroundExtensions.cs @@ -1,21 +1,36 @@ // 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 osu.Game.Beatmaps; using osu.Game.Scoring; +using osu.Game.Skinning; namespace osu.Game.Database { + /// + /// Extension methods which contain workarounds to make EFcore 5.x work with our existing (incorrect) thread safety. + /// The intention is to avoid blocking package updates while we consider the future of the database backend, with a potential backend switch imminent. + /// public static class DatabaseWorkaroundExtensions { + /// + /// Re-query the provided model to ensure it is in a sane state. This method requires explicit implementation per model type. + /// + /// + /// public static void Requery(this IHasPrimaryKey model, IDatabaseContextFactory contextFactory) { switch (model) { + case SkinInfo skinInfo: + requeryFiles(skinInfo.Files, contextFactory); + break; + case ScoreInfo scoreInfo: scoreInfo.Beatmap.BeatmapSet.Requery(contextFactory); - scoreInfo.Files.RequeryFiles(contextFactory); + requeryFiles(scoreInfo.Files, contextFactory); break; case BeatmapSetInfo beatmapSetInfo: @@ -33,7 +48,7 @@ namespace osu.Game.Database } } - public static void RequeryFiles(this List files, IDatabaseContextFactory databaseContextFactory) where T : class, INamedFileInfo + private static void requeryFiles(List files, IDatabaseContextFactory databaseContextFactory) where T : class, INamedFileInfo { var dbContext = databaseContextFactory.Get(); diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index c25f00eccb..601b77e782 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -144,7 +144,7 @@ namespace osu.Game.Skinning protected override void PreImport(SkinInfo model) { - model.Files.Requery(ContextFactory); + model.Requery(ContextFactory); } /// From 8a3553388972349a6833afcf915f44a7dae19995 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Mar 2021 12:48:23 +0900 Subject: [PATCH 12/34] Add fall-through case to catch a potential requery for unsupported model type --- osu.Game/Database/DatabaseWorkaroundExtensions.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Database/DatabaseWorkaroundExtensions.cs b/osu.Game/Database/DatabaseWorkaroundExtensions.cs index 39bf358071..1d5c98ed8d 100644 --- a/osu.Game/Database/DatabaseWorkaroundExtensions.cs +++ b/osu.Game/Database/DatabaseWorkaroundExtensions.cs @@ -45,6 +45,9 @@ namespace osu.Game.Database requeryFiles(beatmapSetInfo.Files, contextFactory); break; + + default: + throw new ArgumentException($"{nameof(Requery)} does not have support for the provided model type", nameof(model)); } } From 1573298e682329d133059a74dd56c8c849b86bba Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Mar 2021 13:12:10 +0900 Subject: [PATCH 13/34] Update remaining package references to point to efcore5 --- .../osu.Game.Rulesets.Catch.Tests.csproj | 2 +- .../osu.Game.Rulesets.Mania.Tests.csproj | 2 +- osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj | 2 +- .../osu.Game.Rulesets.Taiko.Tests.csproj | 2 +- osu.Game.Tests/osu.Game.Tests.csproj | 2 +- osu.iOS.props | 2 -- 6 files changed, 5 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj index 728af5124e..42f70151ac 100644 --- a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj +++ b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj @@ -5,7 +5,7 @@ - + WinExe diff --git a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj index af16f39563..e51b20c9fe 100644 --- a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj +++ b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj @@ -5,7 +5,7 @@ - + WinExe diff --git a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj index 3d2d1f3fec..f1f75148ef 100644 --- a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj +++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj @@ -5,7 +5,7 @@ - + WinExe diff --git a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj index fa00922706..c9a320bdd5 100644 --- a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj +++ b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj @@ -5,7 +5,7 @@ - + WinExe diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index e36b3cdc74..6f8e0fac6f 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -6,7 +6,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index ccd33bf88c..71fcdd45f3 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -90,8 +90,6 @@ - - From 79d3379f55b2b23dbfdd613c58adc8a8b3259768 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Mar 2021 13:20:22 +0900 Subject: [PATCH 14/34] Reformat application of configuration --- osu.Game/Database/OsuDbContext.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs index e5ae530018..2342ab07d4 100644 --- a/osu.Game/Database/OsuDbContext.cs +++ b/osu.Game/Database/OsuDbContext.cs @@ -110,7 +110,10 @@ namespace osu.Game.Database { base.OnConfiguring(optionsBuilder); optionsBuilder - .UseSqlite(connectionString, sqliteOptions => sqliteOptions.CommandTimeout(10).UseQuerySplittingBehavior(QuerySplittingBehavior.SingleQuery)) + .UseSqlite(connectionString, + sqliteOptions => sqliteOptions + .CommandTimeout(10) + .UseQuerySplittingBehavior(QuerySplittingBehavior.SingleQuery)) .UseLoggerFactory(logger.Value); } From 2904f479c65bf9f2cd76a3164ab51d13ff53dc96 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Mar 2021 13:26:14 +0900 Subject: [PATCH 15/34] Share file lookup workaround in ArchiveModelManager with workaround extensions class --- osu.Game/Database/ArchiveModelManager.cs | 4 +--- .../Database/DatabaseWorkaroundExtensions.cs | 23 +++++++++++-------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index fe2caaa0b7..31c365b478 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -462,9 +462,7 @@ namespace osu.Game.Database // Dereference the existing file info, since the file model will be removed. if (file.FileInfo != null) { - // Workaround System.InvalidOperationException - // The instance of entity type 'FileInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. - file.FileInfo = usage.Context.FileInfo.Find(file.FileInfoID); + file.Requery(usage.Context); Files.Dereference(file.FileInfo); diff --git a/osu.Game/Database/DatabaseWorkaroundExtensions.cs b/osu.Game/Database/DatabaseWorkaroundExtensions.cs index 1d5c98ed8d..8ac05f78e0 100644 --- a/osu.Game/Database/DatabaseWorkaroundExtensions.cs +++ b/osu.Game/Database/DatabaseWorkaroundExtensions.cs @@ -49,18 +49,23 @@ namespace osu.Game.Database default: throw new ArgumentException($"{nameof(Requery)} does not have support for the provided model type", nameof(model)); } + + void requeryFiles(List files, IDatabaseContextFactory databaseContextFactory) where T : class, INamedFileInfo + { + var dbContext = databaseContextFactory.Get(); + + foreach (var file in files) + { + Requery(file, dbContext); + } + } } - private static void requeryFiles(List files, IDatabaseContextFactory databaseContextFactory) where T : class, INamedFileInfo + public static void Requery(this INamedFileInfo file, OsuDbContext dbContext) { - var dbContext = databaseContextFactory.Get(); - - foreach (var file in files) - { - // Workaround System.InvalidOperationException - // The instance of entity type 'FileInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. - file.FileInfo = dbContext.FileInfo.Find(file.FileInfoID); - } + // Workaround System.InvalidOperationException + // The instance of entity type 'FileInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. + file.FileInfo = dbContext.FileInfo.Find(file.FileInfoID); } } } From fce21f23d687ab72be391c23987ad1edff8740f0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Mar 2021 13:29:26 +0900 Subject: [PATCH 16/34] Add comments marking workarounds required for EFcore 5 --- .../Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs | 2 +- .../Visual/SongSelect/TestSceneBeatmapRecommendations.cs | 2 +- osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs | 2 +- osu.Game/Database/ArchiveModelManager.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs index 4a9eaa1842..8cfe5d8af2 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs @@ -56,7 +56,7 @@ namespace osu.Game.Tests.Visual.Multiplayer beatmaps.Add(new BeatmapInfo { Ruleset = rulesets.GetRuleset(i % 4), - RulesetID = i % 4, + RulesetID = i % 4, // workaround for efcore 5 compatibility. OnlineBeatmapID = beatmapId, Length = length, BPM = bpm, diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs index 223ace6ca5..9b8b74e6f6 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs @@ -186,7 +186,7 @@ namespace osu.Game.Tests.Visual.SongSelect Metadata = metadata, BaseDifficulty = new BeatmapDifficulty(), Ruleset = ruleset, - RulesetID = ruleset.ID.GetValueOrDefault(), + RulesetID = ruleset.ID.GetValueOrDefault(), // workaround for efcore 5 compatibility. StarDifficulty = difficultyIndex + 1, Version = $"SR{difficultyIndex + 1}" }).ToList() diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 4b402d0c54..2d192ae207 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -915,7 +915,7 @@ namespace osu.Game.Tests.Visual.SongSelect beatmaps.Add(new BeatmapInfo { Ruleset = ruleset, - RulesetID = ruleset.ID.GetValueOrDefault(), + RulesetID = ruleset.ID.GetValueOrDefault(), // workaround for efcore 5 compatibility. OnlineBeatmapID = beatmapId, Version = $"{beatmapId} (length {TimeSpan.FromMilliseconds(length):m\\:ss}, bpm {bpm:0.#})", Length = length, diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 31c365b478..64428882ac 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -642,7 +642,7 @@ namespace osu.Game.Database { Filename = file.Substring(prefix.Length).ToStandardisedPath(), FileInfo = fileInfo, - FileInfoID = fileInfo.ID + FileInfoID = fileInfo.ID // workaround for efcore 5 compatibility. }); } } From 6d4c1ba2aee43c9fdab49f83ba16c0bcdedcce78 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Mar 2021 13:35:08 +0900 Subject: [PATCH 17/34] Fix a couple of new inspections introduced in Rider EAPs --- osu.Game/Beatmaps/BeatmapConverter.cs | 10 +++++----- osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 3 --- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapConverter.cs b/osu.Game/Beatmaps/BeatmapConverter.cs index cb0b3a8d09..b291edd19d 100644 --- a/osu.Game/Beatmaps/BeatmapConverter.cs +++ b/osu.Game/Beatmaps/BeatmapConverter.cs @@ -17,12 +17,12 @@ namespace osu.Game.Beatmaps public abstract class BeatmapConverter : IBeatmapConverter where T : HitObject { - private event Action> ObjectConverted; + private event Action> objectConverted; event Action> IBeatmapConverter.ObjectConverted { - add => ObjectConverted += value; - remove => ObjectConverted -= value; + add => objectConverted += value; + remove => objectConverted -= value; } public IBeatmap Beatmap { get; } @@ -92,10 +92,10 @@ namespace osu.Game.Beatmaps var converted = ConvertHitObject(obj, beatmap, cancellationToken); - if (ObjectConverted != null) + if (objectConverted != null) { converted = converted.ToList(); - ObjectConverted.Invoke(obj, converted); + objectConverted.Invoke(obj, converted); } foreach (var c in converted) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index df940e8c8e..d06478b9de 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -471,9 +471,6 @@ namespace osu.Game.Beatmaps.Formats private string toLegacyCustomSampleBank(HitSampleInfo hitSampleInfo) { - if (hitSampleInfo == null) - return "0"; - if (hitSampleInfo is ConvertHitObjectParser.LegacyHitSampleInfo legacy) return legacy.CustomSampleBank.ToString(CultureInfo.InvariantCulture); From 1e519f0d31125a3bb508be2dd97777556f69f0b1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Mar 2021 14:20:59 +0900 Subject: [PATCH 18/34] Fix seemingly innocent logic change causing breakage in score imports --- osu.Game/Database/DatabaseWorkaroundExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/DatabaseWorkaroundExtensions.cs b/osu.Game/Database/DatabaseWorkaroundExtensions.cs index 8ac05f78e0..a3a982f232 100644 --- a/osu.Game/Database/DatabaseWorkaroundExtensions.cs +++ b/osu.Game/Database/DatabaseWorkaroundExtensions.cs @@ -29,7 +29,7 @@ namespace osu.Game.Database break; case ScoreInfo scoreInfo: - scoreInfo.Beatmap.BeatmapSet.Requery(contextFactory); + requeryFiles(scoreInfo.Beatmap.BeatmapSet.Files, contextFactory); requeryFiles(scoreInfo.Files, contextFactory); break; From 3dd72d6f7d229bd7cb8311e707ace797fed31d3e Mon Sep 17 00:00:00 2001 From: Joehu Date: Sun, 14 Mar 2021 22:47:05 -0700 Subject: [PATCH 19/34] Fix disable mouse buttons setting not showing default indicator when using keybind --- osu.Game/OsuGame.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index dd775888a1..eb34ba4a37 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -886,7 +886,9 @@ namespace osu.Game return true; case GlobalAction.ToggleGameplayMouseButtons: - LocalConfig.Set(OsuSetting.MouseDisableButtons, !LocalConfig.Get(OsuSetting.MouseDisableButtons)); + var mouseDisableButtons = LocalConfig.GetBindable(OsuSetting.MouseDisableButtons); + + mouseDisableButtons.Value = !mouseDisableButtons.Value; return true; case GlobalAction.RandomSkin: From 393f1fbd3f7f4169fefb1df16b689e66314e6fdf Mon Sep 17 00:00:00 2001 From: Joehu Date: Mon, 15 Mar 2021 10:07:50 -0700 Subject: [PATCH 20/34] Remove skype --- osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs | 1 - osu.Game/Users/User.cs | 3 --- 2 files changed, 4 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs index 2925107766..662f55317b 100644 --- a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs @@ -138,7 +138,6 @@ namespace osu.Game.Overlays.Profile.Header if (!string.IsNullOrEmpty(user.Twitter)) anyInfoAdded |= tryAddInfo(FontAwesome.Brands.Twitter, "@" + user.Twitter, $@"https://twitter.com/{user.Twitter}"); anyInfoAdded |= tryAddInfo(FontAwesome.Brands.Discord, user.Discord); - anyInfoAdded |= tryAddInfo(FontAwesome.Brands.Skype, user.Skype, @"skype:" + user.Skype + @"?chat"); anyInfoAdded |= tryAddInfo(FontAwesome.Solid.Link, websiteWithoutProtocol, user.Website); // If no information was added to the bottomLinkContainer, hide it to avoid unwanted padding diff --git a/osu.Game/Users/User.cs b/osu.Game/Users/User.cs index 4d537b91bd..6c45417db0 100644 --- a/osu.Game/Users/User.cs +++ b/osu.Game/Users/User.cs @@ -111,9 +111,6 @@ namespace osu.Game.Users [JsonProperty(@"twitter")] public string Twitter; - [JsonProperty(@"skype")] - public string Skype; - [JsonProperty(@"discord")] public string Discord; From da3dc61aae29202b7a0ebb498c274852c1d955ab Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 16 Mar 2021 10:58:42 +0900 Subject: [PATCH 21/34] Remove newline --- osu.Game/OsuGame.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index eb34ba4a37..7d11029a9c 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -887,7 +887,6 @@ namespace osu.Game case GlobalAction.ToggleGameplayMouseButtons: var mouseDisableButtons = LocalConfig.GetBindable(OsuSetting.MouseDisableButtons); - mouseDisableButtons.Value = !mouseDisableButtons.Value; return true; From c7740d1181a165466b9960ed54f10c7e14e85075 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Mar 2021 15:52:24 +0900 Subject: [PATCH 22/34] Fix opening the editor occasionally causing a hard crash due to incorrect threading logic Setting one of the global screen `Bindable`s (in this case, `Beatmap`) is not valid from anywhere but the update thread. This changes the order in which things happen during the editor startup process to ensure correctness. Closes #11968. --- osu.Game/Screens/Edit/Editor.cs | 67 +++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 25 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 0ba202b082..3a4c3491ff 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -106,26 +106,29 @@ namespace osu.Game.Screens.Edit [BackgroundDependencyLoader] private void load(OsuColour colours, GameHost host, OsuConfigManager config) { - if (Beatmap.Value is DummyWorkingBeatmap) + var loadableBeatmap = Beatmap.Value; + + if (loadableBeatmap is DummyWorkingBeatmap) { isNewBeatmap = true; - var newBeatmap = beatmapManager.CreateNew(Ruleset.Value, api.LocalUser.Value); + loadableBeatmap = beatmapManager.CreateNew(Ruleset.Value, api.LocalUser.Value); + + // required so we can get the track length in EditorClock. + // this is safe as nothing has yet got a reference to this new beatmap. + loadableBeatmap.LoadTrack(); // this is a bit haphazard, but guards against setting the lease Beatmap bindable if // the editor has already been exited. if (!ValidForPush) return; - - // this probably shouldn't be set in the asynchronous load method, but everything following relies on it. - Beatmap.Value = newBeatmap; } - beatDivisor.Value = Beatmap.Value.BeatmapInfo.BeatDivisor; - beatDivisor.BindValueChanged(divisor => Beatmap.Value.BeatmapInfo.BeatDivisor = divisor.NewValue); + beatDivisor.Value = loadableBeatmap.BeatmapInfo.BeatDivisor; + beatDivisor.BindValueChanged(divisor => loadableBeatmap.BeatmapInfo.BeatDivisor = divisor.NewValue); // Todo: should probably be done at a DrawableRuleset level to share logic with Player. - clock = new EditorClock(Beatmap.Value, beatDivisor) { IsCoupled = false }; + clock = new EditorClock(loadableBeatmap, beatDivisor) { IsCoupled = false }; UpdateClockSource(); @@ -139,7 +142,7 @@ namespace osu.Game.Screens.Edit try { - playableBeatmap = Beatmap.Value.GetPlayableBeatmap(Beatmap.Value.BeatmapInfo.Ruleset); + playableBeatmap = loadableBeatmap.GetPlayableBeatmap(loadableBeatmap.BeatmapInfo.Ruleset); // clone these locally for now to avoid incurring overhead on GetPlayableBeatmap usages. // eventually we will want to improve how/where this is done as there are issues with *not* cloning it in all cases. @@ -153,13 +156,21 @@ namespace osu.Game.Screens.Edit return; } - AddInternal(editorBeatmap = new EditorBeatmap(playableBeatmap, Beatmap.Value.Skin)); + AddInternal(editorBeatmap = new EditorBeatmap(playableBeatmap, loadableBeatmap.Skin)); dependencies.CacheAs(editorBeatmap); changeHandler = new EditorChangeHandler(editorBeatmap); dependencies.CacheAs(changeHandler); updateLastSavedHash(); + Schedule(() => + { + // we need to avoid changing the beatmap from an asynchronous load thread. it can potentially cause weirdness including crashes. + // this assumes that nothing during the rest of this load() method is accessing Beatmap.Value (loadableBeatmap should be preferred). + // generally this is quite safe, as the actual load of editor content comes after menuBar.Mode.ValueChanged is fired in its own LoadComplete. + Beatmap.Value = loadableBeatmap; + }); + OsuMenuItem undoMenuItem; OsuMenuItem redoMenuItem; @@ -167,17 +178,6 @@ namespace osu.Game.Screens.Edit EditorMenuItem copyMenuItem; EditorMenuItem pasteMenuItem; - var fileMenuItems = new List - { - new EditorMenuItem("Save", MenuItemType.Standard, Save) - }; - - if (RuntimeInfo.IsDesktop) - fileMenuItems.Add(new EditorMenuItem("Export package", MenuItemType.Standard, exportBeatmap)); - - fileMenuItems.Add(new EditorMenuItemSpacer()); - fileMenuItems.Add(new EditorMenuItem("Exit", MenuItemType.Standard, this.Exit)); - AddInternal(new OsuContextMenuContainer { RelativeSizeAxes = Axes.Both, @@ -209,7 +209,7 @@ namespace osu.Game.Screens.Edit { new MenuItem("File") { - Items = fileMenuItems + Items = createFileMenuItems() }, new MenuItem("Edit") { @@ -242,7 +242,11 @@ namespace osu.Game.Screens.Edit Height = 60, Children = new Drawable[] { - bottomBackground = new Box { RelativeSizeAxes = Axes.Both }, + bottomBackground = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colours.Gray2 + }, new Container { RelativeSizeAxes = Axes.Both, @@ -299,8 +303,6 @@ namespace osu.Game.Screens.Edit clipboard.BindValueChanged(content => pasteMenuItem.Action.Disabled = string.IsNullOrEmpty(content.NewValue)); menuBar.Mode.ValueChanged += onModeChanged; - - bottomBackground.Colour = colours.Gray2; } /// @@ -681,6 +683,21 @@ namespace osu.Game.Screens.Edit lastSavedHash = changeHandler.CurrentStateHash; } + private List createFileMenuItems() + { + var fileMenuItems = new List + { + new EditorMenuItem("Save", MenuItemType.Standard, Save) + }; + + if (RuntimeInfo.IsDesktop) + fileMenuItems.Add(new EditorMenuItem("Export package", MenuItemType.Standard, exportBeatmap)); + + fileMenuItems.Add(new EditorMenuItemSpacer()); + fileMenuItems.Add(new EditorMenuItem("Exit", MenuItemType.Standard, this.Exit)); + return fileMenuItems; + } + public double SnapTime(double time, double? referenceTime) => editorBeatmap.SnapTime(time, referenceTime); public double GetBeatLengthAtTime(double referenceTime) => editorBeatmap.GetBeatLengthAtTime(referenceTime); From eda891223c469cdb23a07fa8faeac7331db2aa33 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Mar 2021 16:47:12 +0900 Subject: [PATCH 23/34] Start the editor with empty artist/creator/difficulty name fields --- osu.Game/Beatmaps/BeatmapManager.cs | 3 --- osu.Game/Screens/Edit/Setup/MetadataSection.cs | 8 ++++---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index f42fba79cb..115d1b33bb 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -113,8 +113,6 @@ namespace osu.Game.Beatmaps { var metadata = new BeatmapMetadata { - Artist = "artist", - Title = "title", Author = user, }; @@ -128,7 +126,6 @@ namespace osu.Game.Beatmaps BaseDifficulty = new BeatmapDifficulty(), Ruleset = ruleset, Metadata = metadata, - Version = "difficulty" } } }; diff --git a/osu.Game/Screens/Edit/Setup/MetadataSection.cs b/osu.Game/Screens/Edit/Setup/MetadataSection.cs index e812c042fb..2b10be0423 100644 --- a/osu.Game/Screens/Edit/Setup/MetadataSection.cs +++ b/osu.Game/Screens/Edit/Setup/MetadataSection.cs @@ -28,25 +28,25 @@ namespace osu.Game.Screens.Edit.Setup }, artistTextBox = new LabelledTextBox { - Label = "Artist", + PlaceholderText = "Artist", Current = { Value = Beatmap.Metadata.Artist }, TabbableContentContainer = this }, titleTextBox = new LabelledTextBox { - Label = "Title", + PlaceholderText = "Title", Current = { Value = Beatmap.Metadata.Title }, TabbableContentContainer = this }, creatorTextBox = new LabelledTextBox { - Label = "Creator", + PlaceholderText = "Creator", Current = { Value = Beatmap.Metadata.AuthorString }, TabbableContentContainer = this }, difficultyTextBox = new LabelledTextBox { - Label = "Difficulty Name", + PlaceholderText = "Difficulty Name", Current = { Value = Beatmap.BeatmapInfo.Version }, TabbableContentContainer = this }, From 26d6f96c4e2decdba1836a98280a2ba6381c379d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Mar 2021 16:56:58 +0900 Subject: [PATCH 24/34] Fix LabelledTextBox not correctly forwarding focus to its underlying TextBox component --- osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs b/osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs index 4aeda74be8..266eb11319 100644 --- a/osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs +++ b/osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Events; using osu.Game.Graphics.UserInterface; namespace osu.Game.Graphics.UserInterfaceV2 @@ -53,6 +54,14 @@ namespace osu.Game.Graphics.UserInterfaceV2 CornerRadius = CORNER_RADIUS, }; + public override bool AcceptsFocus => true; + + protected override void OnFocus(FocusEvent e) + { + base.OnFocus(e); + GetContainingInputManager().ChangeFocus(Component); + } + protected override OsuTextBox CreateComponent() => CreateTextBox().With(t => { t.OnCommit += (sender, newText) => OnCommit?.Invoke(sender, newText); From 5adc675862afb015ad737db47ebda917fc24c2f2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Mar 2021 16:57:14 +0900 Subject: [PATCH 25/34] Focus artist textbox on entering song setup if fields are empty --- osu.Game/Screens/Edit/Setup/MetadataSection.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Screens/Edit/Setup/MetadataSection.cs b/osu.Game/Screens/Edit/Setup/MetadataSection.cs index 2b10be0423..c5a2b77ab4 100644 --- a/osu.Game/Screens/Edit/Setup/MetadataSection.cs +++ b/osu.Game/Screens/Edit/Setup/MetadataSection.cs @@ -56,6 +56,14 @@ namespace osu.Game.Screens.Edit.Setup item.OnCommit += onCommit; } + protected override void LoadComplete() + { + base.LoadComplete(); + + if (string.IsNullOrEmpty(artistTextBox.Current.Value)) + GetContainingInputManager().ChangeFocus(artistTextBox); + } + private void onCommit(TextBox sender, bool newText) { if (!newText) return; From 3b6a1180b68515b82a9e970f84a3e51d5f908f4e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Mar 2021 17:02:11 +0900 Subject: [PATCH 26/34] Remove non-accessed field --- osu.Game/Screens/Edit/Editor.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 3a4c3491ff..0c24eb6a4d 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -74,7 +74,6 @@ namespace osu.Game.Screens.Edit private string lastSavedHash; - private Box bottomBackground; private Container screenContainer; private EditorScreen currentScreen; @@ -242,7 +241,7 @@ namespace osu.Game.Screens.Edit Height = 60, Children = new Drawable[] { - bottomBackground = new Box + new Box { RelativeSizeAxes = Axes.Both, Colour = colours.Gray2 From d0e61e5b4d609c342e55e2b8a2ed86c66977d11e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Mar 2021 17:14:04 +0900 Subject: [PATCH 27/34] Put back the label --- osu.Game/Screens/Edit/Setup/MetadataSection.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Setup/MetadataSection.cs b/osu.Game/Screens/Edit/Setup/MetadataSection.cs index c5a2b77ab4..f429164ece 100644 --- a/osu.Game/Screens/Edit/Setup/MetadataSection.cs +++ b/osu.Game/Screens/Edit/Setup/MetadataSection.cs @@ -28,25 +28,25 @@ namespace osu.Game.Screens.Edit.Setup }, artistTextBox = new LabelledTextBox { - PlaceholderText = "Artist", + Label = "Artist", Current = { Value = Beatmap.Metadata.Artist }, TabbableContentContainer = this }, titleTextBox = new LabelledTextBox { - PlaceholderText = "Title", + Label = "Title", Current = { Value = Beatmap.Metadata.Title }, TabbableContentContainer = this }, creatorTextBox = new LabelledTextBox { - PlaceholderText = "Creator", + Label = "Creator", Current = { Value = Beatmap.Metadata.AuthorString }, TabbableContentContainer = this }, difficultyTextBox = new LabelledTextBox { - PlaceholderText = "Difficulty Name", + Label = "Difficulty Name", Current = { Value = Beatmap.BeatmapInfo.Version }, TabbableContentContainer = this }, From 6cea74f0fada8f90bf27e3005cccd2ca5dc42706 Mon Sep 17 00:00:00 2001 From: Joehu Date: Wed, 17 Mar 2021 13:13:13 -0700 Subject: [PATCH 28/34] Remove available kudosu section from user profile overlay in line with web --- .../Profile/Sections/Kudosu/KudosuInfo.cs | 34 ++----------------- 1 file changed, 3 insertions(+), 31 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs index d4d0976724..e5b4193f3b 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs @@ -23,44 +23,17 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu { this.user.BindTo(user); CountSection total; - CountSection avaliable; RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; Masking = true; CornerRadius = 3; - Children = new Drawable[] - { - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(5, 0), - Children = new[] - { - total = new CountTotal(), - avaliable = new CountAvailable() - } - } - }; - this.user.ValueChanged += u => - { - total.Count = u.NewValue?.Kudosu.Total ?? 0; - avaliable.Count = u.NewValue?.Kudosu.Available ?? 0; - }; + Child = total = new CountTotal(); + + this.user.ValueChanged += u => total.Count = u.NewValue?.Kudosu.Total ?? 0; } protected override bool OnClick(ClickEvent e) => true; - private class CountAvailable : CountSection - { - public CountAvailable() - : base("Kudosu Avaliable") - { - DescriptionText.Text = "Kudosu can be traded for kudosu stars, which will help your beatmap get more attention. This is the number of kudosu you haven't traded in yet."; - } - } - private class CountTotal : CountSection { public CountTotal() @@ -86,7 +59,6 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu public CountSection(string header) { RelativeSizeAxes = Axes.X; - Width = 0.5f; AutoSizeAxes = Axes.Y; Padding = new MarginPadding { Top = 10, Bottom = 20 }; Child = new FillFlowContainer From 599c55fca5d08f0f2a30cde6be78273b6ad24404 Mon Sep 17 00:00:00 2001 From: Joehu Date: Wed, 17 Mar 2021 13:14:18 -0700 Subject: [PATCH 29/34] Update total kudosu earned description text in line with web --- osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs index e5b4193f3b..87622939e2 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs @@ -40,7 +40,7 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu : base("Total Kudosu Earned") { DescriptionText.AddText("Based on how much of a contribution the user has made to beatmap moderation. See "); - DescriptionText.AddLink("this link", "https://osu.ppy.sh/wiki/Kudosu"); + DescriptionText.AddLink("this page", "https://osu.ppy.sh/wiki/Kudosu"); DescriptionText.AddText(" for more information."); } } From 2e63c2ce20ef4c707150f2eaf3c5a640a9c963c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 17 Mar 2021 21:50:45 +0100 Subject: [PATCH 30/34] Fix selection box operation hotkeys not registering in change handler Could lead to crashes after reversing a note cluster and playing it back. The root cause of the crash was that the hotkey operations were not ran inside of an editor change handler operation. This, in turn, caused the autoplay replay to not be regenerated after flipping an object cluster, therefore finally manifesting as a hard crash due to negative time offsets appearing in judgement results, which interfered with the default implementation of note lock. Note that this incidentally also fixes the fact that selection box hotkey operations (reverse and flip) did not handle undo/redo. --- .../Edit/Compose/Components/SelectionBox.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs index 2f4721f63e..9d6b44e207 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs @@ -113,16 +113,25 @@ namespace osu.Game.Screens.Edit.Compose.Components if (e.Repeat || !e.ControlPressed) return false; + bool runOperationFromHotkey(Func operation) + { + operationStarted(); + bool result = operation?.Invoke() ?? false; + operationEnded(); + + return result; + } + switch (e.Key) { case Key.G: - return CanReverse && OnReverse?.Invoke() == true; + return CanReverse && runOperationFromHotkey(OnReverse); case Key.H: - return CanScaleX && OnFlip?.Invoke(Direction.Horizontal) == true; + return CanScaleX && runOperationFromHotkey(() => OnFlip?.Invoke(Direction.Horizontal) ?? false); case Key.J: - return CanScaleY && OnFlip?.Invoke(Direction.Vertical) == true; + return CanScaleY && runOperationFromHotkey(() => OnFlip?.Invoke(Direction.Vertical) ?? false); } return base.OnKeyDown(e); From 08ffe425f9b9a1cd9eba6c3cd903264680244090 Mon Sep 17 00:00:00 2001 From: Joehu Date: Wed, 17 Mar 2021 14:46:23 -0700 Subject: [PATCH 31/34] Update kudosu description color in line with web --- osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs index 87622939e2..115d705766 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs @@ -103,7 +103,6 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu private void load(OverlayColourProvider colourProvider) { lineBackground.Colour = colourProvider.Highlight1; - DescriptionText.Colour = colourProvider.Foreground1; } } } From f95ce90c95495691e1d7c57e10a8c456ffbfc82c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 17 Mar 2021 23:32:08 +0100 Subject: [PATCH 32/34] Adjust kudosu count formatting --- osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs index 115d705766..cdb24b784c 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs @@ -53,7 +53,7 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu public new int Count { - set => valueText.Text = value.ToString(); + set => valueText.Text = value.ToString("N0"); } public CountSection(string header) From bb3c3f302aa4b0cd2f0e5e8a9d80235fc7d810fd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Mar 2021 15:36:07 +0900 Subject: [PATCH 33/34] Fix skin parser not stripping whitespace before parsing --- osu.Game.Tests/Resources/skin-with-space.ini | 2 ++ osu.Game.Tests/Skins/LegacySkinDecoderTest.cs | 9 +++++++++ osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 2 ++ 3 files changed, 13 insertions(+) create mode 100644 osu.Game.Tests/Resources/skin-with-space.ini diff --git a/osu.Game.Tests/Resources/skin-with-space.ini b/osu.Game.Tests/Resources/skin-with-space.ini new file mode 100644 index 0000000000..3e64257a3e --- /dev/null +++ b/osu.Game.Tests/Resources/skin-with-space.ini @@ -0,0 +1,2 @@ +[General] +Version: 2 \ No newline at end of file diff --git a/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs b/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs index aedf26ee75..dcb866c99f 100644 --- a/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs +++ b/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs @@ -91,6 +91,15 @@ namespace osu.Game.Tests.Skins Assert.AreEqual(2.0m, decoder.Decode(stream).LegacyVersion); } + [Test] + public void TestStripWhitespace() + { + var decoder = new LegacySkinDecoder(); + using (var resStream = TestResources.OpenResource("skin-with-space.ini")) + using (var stream = new LineBufferedReader(resStream)) + Assert.AreEqual(2.0m, decoder.Decode(stream).LegacyVersion); + } + [Test] public void TestDecodeLatestVersion() { diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 2fb24c24e0..bd1b6627b4 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -36,6 +36,8 @@ namespace osu.Game.Beatmaps.Formats if (ShouldSkipLine(line)) continue; + line = line.Trim(); + if (line.StartsWith('[') && line.EndsWith(']')) { if (!Enum.TryParse(line[1..^1], out section)) From 5b0d75ee56690dbb3a121d741768515417f51ee8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Mar 2021 16:30:30 +0900 Subject: [PATCH 34/34] Only trim trailing spaces to avoid breakage in storyboard parsing --- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 14 ++++++-------- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 4 +--- .../Beatmaps/Formats/LegacyStoryboardDecoder.cs | 2 -- osu.Game/Skinning/LegacyManiaSkinDecoder.cs | 2 -- 4 files changed, 7 insertions(+), 15 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 99dffa7041..40bc75e847 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -67,16 +67,14 @@ namespace osu.Game.Beatmaps.Formats protected override void ParseLine(Beatmap beatmap, Section section, string line) { - var strippedLine = StripComments(line); - switch (section) { case Section.General: - handleGeneral(strippedLine); + handleGeneral(line); return; case Section.Editor: - handleEditor(strippedLine); + handleEditor(line); return; case Section.Metadata: @@ -84,19 +82,19 @@ namespace osu.Game.Beatmaps.Formats return; case Section.Difficulty: - handleDifficulty(strippedLine); + handleDifficulty(line); return; case Section.Events: - handleEvent(strippedLine); + handleEvent(line); return; case Section.TimingPoints: - handleTimingPoint(strippedLine); + handleTimingPoint(line); return; case Section.HitObjects: - handleHitObject(strippedLine); + handleHitObject(line); return; } diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index bd1b6627b4..10a716963e 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -36,7 +36,7 @@ namespace osu.Game.Beatmaps.Formats if (ShouldSkipLine(line)) continue; - line = line.Trim(); + line = StripComments(line).TrimEnd(); if (line.StartsWith('[') && line.EndsWith(']')) { @@ -73,8 +73,6 @@ namespace osu.Game.Beatmaps.Formats protected virtual void ParseLine(T output, Section section, string line) { - line = StripComments(line); - switch (section) { case Section.Colours: diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index b9bf6823b5..6301c42deb 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -45,8 +45,6 @@ namespace osu.Game.Beatmaps.Formats protected override void ParseLine(Storyboard storyboard, Section section, string line) { - line = StripComments(line); - switch (section) { case Section.General: diff --git a/osu.Game/Skinning/LegacyManiaSkinDecoder.cs b/osu.Game/Skinning/LegacyManiaSkinDecoder.cs index 0a1de461ea..5308640bdd 100644 --- a/osu.Game/Skinning/LegacyManiaSkinDecoder.cs +++ b/osu.Game/Skinning/LegacyManiaSkinDecoder.cs @@ -31,8 +31,6 @@ namespace osu.Game.Skinning protected override void ParseLine(List output, Section section, string line) { - line = StripComments(line); - switch (section) { case Section.Mania: