From 619dfe06907d74d3054dbdb3f23c9d0444b505d7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Oct 2021 14:34:11 +0900 Subject: [PATCH 01/18] Add new interface base types for models --- osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs | 90 +++++++++++++++ osu.Game/Beatmaps/IBeatmapInfo.cs | 74 +++++++++++++ osu.Game/Beatmaps/IBeatmapMetadataInfo.cs | 117 ++++++++++++++++++++ osu.Game/Beatmaps/IBeatmapSetInfo.cs | 57 ++++++++++ osu.Game/Beatmaps/IFileInfo.cs | 18 +++ osu.Game/Beatmaps/IHasOnlineID.cs | 15 +++ osu.Game/Beatmaps/INamedFileUsage.cs | 23 ++++ osu.Game/Beatmaps/IRulesetInfo.cs | 46 ++++++++ 8 files changed, 440 insertions(+) create mode 100644 osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs create mode 100644 osu.Game/Beatmaps/IBeatmapInfo.cs create mode 100644 osu.Game/Beatmaps/IBeatmapMetadataInfo.cs create mode 100644 osu.Game/Beatmaps/IBeatmapSetInfo.cs create mode 100644 osu.Game/Beatmaps/IFileInfo.cs create mode 100644 osu.Game/Beatmaps/IHasOnlineID.cs create mode 100644 osu.Game/Beatmaps/INamedFileUsage.cs create mode 100644 osu.Game/Beatmaps/IRulesetInfo.cs diff --git a/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs b/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs new file mode 100644 index 000000000000..6d9fcfcb0636 --- /dev/null +++ b/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs @@ -0,0 +1,90 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +namespace osu.Game.Beatmaps +{ + /// + /// A representation of all top-level difficulty settings for a beatmap. + /// + public interface IBeatmapDifficultyInfo + { + /// + /// The default value used for all difficulty settings except and . + /// + const float DEFAULT_DIFFICULTY = 5; + + /// + /// The drain rate of the associated beatmap. + /// + float DrainRate { get; } + + /// + /// The circle size of the associated beatmap. + /// + float CircleSize { get; } + + /// + /// The overall difficulty of the associated beatmap. + /// + float OverallDifficulty { get; } + + /// + /// The approach rate of the associated beatmap. + /// + float ApproachRate { get; } + + /// + /// The slider multiplier of the associated beatmap. + /// + double SliderMultiplier { get; } + + /// + /// The slider tick rate of the associated beatmap. + /// + double SliderTickRate { get; } + + /// + /// Maps a difficulty value [0, 10] to a two-piece linear range of values. + /// + /// The difficulty value to be mapped. + /// Minimum of the resulting range which will be achieved by a difficulty value of 0. + /// Midpoint of the resulting range which will be achieved by a difficulty value of 5. + /// Maximum of the resulting range which will be achieved by a difficulty value of 10. + /// Value to which the difficulty value maps in the specified range. + static double DifficultyRange(double difficulty, double min, double mid, double max) + { + if (difficulty > 5) + return mid + (max - mid) * (difficulty - 5) / 5; + if (difficulty < 5) + return mid - (mid - min) * (5 - difficulty) / 5; + + return mid; + } + + /// + /// Maps a difficulty value [0, 10] to a two-piece linear range of values. + /// + /// The difficulty value to be mapped. + /// The values that define the two linear ranges. + /// + /// + /// od0 + /// Minimum of the resulting range which will be achieved by a difficulty value of 0. + /// + /// + /// od5 + /// Midpoint of the resulting range which will be achieved by a difficulty value of 5. + /// + /// + /// od10 + /// Maximum of the resulting range which will be achieved by a difficulty value of 10. + /// + /// + /// + /// Value to which the difficulty value maps in the specified range. + public static double DifficultyRange(double difficulty, (double od0, double od5, double od10) range) + => DifficultyRange(difficulty, range.od0, range.od5, range.od10); + } +} diff --git a/osu.Game/Beatmaps/IBeatmapInfo.cs b/osu.Game/Beatmaps/IBeatmapInfo.cs new file mode 100644 index 000000000000..72a04621f2b5 --- /dev/null +++ b/osu.Game/Beatmaps/IBeatmapInfo.cs @@ -0,0 +1,74 @@ +// 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; + +#nullable enable + +namespace osu.Game.Beatmaps +{ + /// + /// A single beatmap difficulty. + /// + public interface IBeatmapInfo : IHasOnlineID + { + /// + /// The user-specified name given to this beatmap. + /// + string DifficultyName { get; } + + /// + /// The metadata representing this beatmap. May be shared between multiple beatmaps. + /// + IBeatmapMetadataInfo Metadata { get; } + + /// + /// The difficulty settings for this beatmap. + /// + IBeatmapDifficultyInfo Difficulty { get; } + + /// + /// The playable length in milliseconds of this beatmap. + /// + double Length { get; } + + /// + /// The most common BPM of this beatmap. + /// + double BPM { get; } + + /// + /// The SHA-256 hash representing this beatmap's contents. + /// + string Hash { get; } + + /// + /// MD5 is kept for legacy support (matching against replays, osu-web-10 etc.). + /// + string MD5Hash { get; } + + /// + /// The ruleset this beatmap was made for. + /// + IRulesetInfo Ruleset { get; } + + /// + /// The basic star rating for this beatmap (with no mods applied). + /// + double StarRating { get; } + + string DisplayTitle => $"{Metadata} {versionString}".Trim(); + + RomanisableString DisplayTitleRomanisable + { + get + { + var metadata = Metadata.DisplayTitleRomanisable; + + return new RomanisableString($"{metadata.GetPreferred(true)} {versionString}".Trim(), $"{metadata.GetPreferred(false)} {versionString}".Trim()); + } + } + + private string versionString => string.IsNullOrEmpty(DifficultyName) ? string.Empty : $"[{DifficultyName}]"; + } +} diff --git a/osu.Game/Beatmaps/IBeatmapMetadataInfo.cs b/osu.Game/Beatmaps/IBeatmapMetadataInfo.cs new file mode 100644 index 000000000000..18dd38b404e8 --- /dev/null +++ b/osu.Game/Beatmaps/IBeatmapMetadataInfo.cs @@ -0,0 +1,117 @@ +// 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; + +#nullable enable + +namespace osu.Game.Beatmaps +{ + /// + /// Metadata representing a beatmap. May be shared between multiple beatmap difficulties. + /// + public interface IBeatmapMetadataInfo : IEquatable + { + /// + /// The romanised title of this beatmap. + /// + string Title { get; } + + /// + /// The unicode title of this beatmap. + /// + string TitleUnicode { get; } + + /// + /// The romanised artist of this beatmap. + /// + string Artist { get; } + + /// + /// The unicode artist of this beatmap. + /// + string ArtistUnicode { get; } + + /// + /// The author of this beatmap. + /// + string Author { get; } // eventually should be linked to a persisted User. + + /// + /// The source of this beatmap. + /// + string Source { get; } + + /// + /// The tags of this beatmap. + /// + string Tags { get; } + + /// + /// The time in milliseconds to begin playing the track for preview purposes. + /// If -1, the track should begin playing at 40% of its length. + /// + int PreviewTime { get; } + + /// + /// The filename of the audio file consumed by this beatmap. + /// + string AudioFile { get; } + + /// + /// The filename of the background image file consumed by this beatmap. + /// + string BackgroundFile { get; } + + string DisplayTitle + { + get + { + string author = string.IsNullOrEmpty(Author) ? string.Empty : $"({Author})"; + return $"{Artist} - {Title} {author}".Trim(); + } + } + + RomanisableString DisplayTitleRomanisable + { + get + { + string author = string.IsNullOrEmpty(Author) ? string.Empty : $"({Author})"; + var artistUnicode = string.IsNullOrEmpty(ArtistUnicode) ? Artist : ArtistUnicode; + var titleUnicode = string.IsNullOrEmpty(TitleUnicode) ? Title : TitleUnicode; + + return new RomanisableString($"{artistUnicode} - {titleUnicode} {author}".Trim(), $"{Artist} - {Title} {author}".Trim()); + } + } + + string[] SearchableTerms => new[] + { + Author, + Artist, + ArtistUnicode, + Title, + TitleUnicode, + Source, + Tags + }.Where(s => !string.IsNullOrEmpty(s)).ToArray(); + + bool IEquatable.Equals(IBeatmapMetadataInfo? other) + { + if (other == null) + return false; + + return Title == other.Title + && TitleUnicode == other.TitleUnicode + && Artist == other.Artist + && ArtistUnicode == other.ArtistUnicode + && Author == other.Author + && Source == other.Source + && Tags == other.Tags + && PreviewTime == other.PreviewTime + && AudioFile == other.AudioFile + && BackgroundFile == other.BackgroundFile; + } + } +} diff --git a/osu.Game/Beatmaps/IBeatmapSetInfo.cs b/osu.Game/Beatmaps/IBeatmapSetInfo.cs new file mode 100644 index 000000000000..f22115e08f7a --- /dev/null +++ b/osu.Game/Beatmaps/IBeatmapSetInfo.cs @@ -0,0 +1,57 @@ +// 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; + +#nullable enable + +namespace osu.Game.Beatmaps +{ + /// + /// A representation of a collection of beatmap difficulties, generally packaged as an ".osz" archive. + /// + public interface IBeatmapSetInfo : IHasOnlineID + { + /// + /// The date when this beatmap was imported. + /// + DateTimeOffset DateAdded { get; } + + /// + /// The best-effort metadata representing this set. In the case metadata differs between contained beatmaps, one is arbitrarily chosen. + /// + IBeatmapMetadataInfo? Metadata { get; } + + /// + /// All beatmaps contained in this set. + /// + IEnumerable Beatmaps { get; } + + /// + /// All files used by this set. + /// + IEnumerable Files { get; } + + /// + /// The maximum star difficulty of all beatmaps in this set. + /// + double MaxStarDifficulty { get; } + + /// + /// The maximum playable length in milliseconds of all beatmaps in this set. + /// + double MaxLength { get; } + + /// + /// The maximum BPM of all beatmaps in this set. + /// + double MaxBPM { get; } + + /// + /// The filename for the storyboard. + /// + string StoryboardFile => Files.FirstOrDefault(f => f.Filename.EndsWith(".osb", StringComparison.OrdinalIgnoreCase))?.Filename ?? string.Empty; + } +} diff --git a/osu.Game/Beatmaps/IFileInfo.cs b/osu.Game/Beatmaps/IFileInfo.cs new file mode 100644 index 000000000000..50eb223fc4b0 --- /dev/null +++ b/osu.Game/Beatmaps/IFileInfo.cs @@ -0,0 +1,18 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +namespace osu.Game.Beatmaps +{ + /// + /// A representation of a tracked file. + /// + public interface IFileInfo + { + /// + /// SHA-256 hash of the file content. + /// + string Hash { get; } + } +} diff --git a/osu.Game/Beatmaps/IHasOnlineID.cs b/osu.Game/Beatmaps/IHasOnlineID.cs new file mode 100644 index 000000000000..dc2793afe5c4 --- /dev/null +++ b/osu.Game/Beatmaps/IHasOnlineID.cs @@ -0,0 +1,15 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +namespace osu.Game.Beatmaps +{ + public interface IHasOnlineID + { + /// + /// The server-side ID representing this instance, if one exists. + /// + int? OnlineID { get; } + } +} diff --git a/osu.Game/Beatmaps/INamedFileUsage.cs b/osu.Game/Beatmaps/INamedFileUsage.cs new file mode 100644 index 000000000000..aa7a3852a778 --- /dev/null +++ b/osu.Game/Beatmaps/INamedFileUsage.cs @@ -0,0 +1,23 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +namespace osu.Game.Beatmaps +{ + /// + /// A usage of a file, with a local filename attached. + /// + public interface INamedFileUsage + { + /// + /// The underlying file on disk. + /// + IFileInfo File { get; } + + /// + /// The filename for this usage. + /// + string Filename { get; } + } +} diff --git a/osu.Game/Beatmaps/IRulesetInfo.cs b/osu.Game/Beatmaps/IRulesetInfo.cs new file mode 100644 index 000000000000..b31ebdfbfdf3 --- /dev/null +++ b/osu.Game/Beatmaps/IRulesetInfo.cs @@ -0,0 +1,46 @@ +// 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 osu.Game.Rulesets; + +#nullable enable + +namespace osu.Game.Beatmaps +{ + /// + /// A representation of a ruleset's metadata. + /// + public interface IRulesetInfo : IHasOnlineID + { + /// + /// The user-exposed name of this ruleset. + /// + string Name { get; } + + /// + /// An acronym defined by the ruleset that can be used as a permanent identifier. + /// + string ShortName { get; } + + /// + /// A string representation of this ruleset, to be used with reflection to instantiate the ruleset represented by this metadata. + /// + string InstantiationInfo { get; } + + public Ruleset? CreateInstance() + { + var type = Type.GetType(InstantiationInfo); + + if (type == null) + return null; + + var ruleset = Activator.CreateInstance(type) as Ruleset; + + // overwrite the pre-populated RulesetInfo with a potentially database attached copy. + // ruleset.RulesetInfo = this; + + return ruleset; + } + } +} From d30963646077df2eb6f1d2377d7bd56585071e8c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Oct 2021 16:31:11 +0900 Subject: [PATCH 02/18] Update all EF based models to implement new read only interfaces --- osu.Game/Beatmaps/BeatmapInfo.cs | 19 ++++++++++++++++++- osu.Game/Beatmaps/BeatmapMetadata.cs | 4 +++- osu.Game/Beatmaps/BeatmapSetFileInfo.cs | 4 +++- osu.Game/Beatmaps/BeatmapSetInfo.cs | 16 +++++++++++++++- osu.Game/Beatmaps/IBeatmapInfo.cs | 2 ++ osu.Game/Beatmaps/IBeatmapSetInfo.cs | 1 + .../{Beatmaps => Database}/IHasOnlineID.cs | 2 +- .../{Beatmaps => Database}/INamedFileUsage.cs | 4 +++- osu.Game/IO/FileInfo.cs | 2 +- osu.Game/{Beatmaps => IO}/IFileInfo.cs | 2 +- .../{Beatmaps => Rulesets}/IRulesetInfo.cs | 4 ++-- osu.Game/Rulesets/RulesetInfo.cs | 8 +++++++- 12 files changed, 57 insertions(+), 11 deletions(-) rename osu.Game/{Beatmaps => Database}/IHasOnlineID.cs (92%) rename osu.Game/{Beatmaps => Database}/INamedFileUsage.cs (92%) rename osu.Game/{Beatmaps => IO}/IFileInfo.cs (93%) rename osu.Game/{Beatmaps => Rulesets}/IRulesetInfo.cs (96%) diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 8cb5da808366..d2b47ef1a4c5 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -17,13 +17,14 @@ namespace osu.Game.Beatmaps { [ExcludeFromDynamicCompile] [Serializable] - public class BeatmapInfo : IEquatable, IHasPrimaryKey + public class BeatmapInfo : IEquatable, IHasPrimaryKey, IBeatmapInfo { public int ID { get; set; } public int BeatmapVersion; private int? onlineBeatmapID; + private IRulesetInfo ruleset; [JsonProperty("id")] public int? OnlineBeatmapID @@ -187,5 +188,21 @@ public bool BackgroundEquals(BeatmapInfo other) => other != null && BeatmapSet ! /// Returns a shallow-clone of this . /// public BeatmapInfo Clone() => (BeatmapInfo)MemberwiseClone(); + + #region Implementation of IHasOnlineID + + public int? OnlineID => ID; + + #endregion + + #region Implementation of IBeatmapInfo + + public string DifficultyName => Version; + IBeatmapMetadataInfo IBeatmapInfo.Metadata => Metadata; + public IBeatmapDifficultyInfo Difficulty => BaseDifficulty; + IRulesetInfo IBeatmapInfo.Ruleset => Ruleset; + public double StarRating => StarDifficulty; + + #endregion } } diff --git a/osu.Game/Beatmaps/BeatmapMetadata.cs b/osu.Game/Beatmaps/BeatmapMetadata.cs index 713f80d1feac..fbd47d2614a6 100644 --- a/osu.Game/Beatmaps/BeatmapMetadata.cs +++ b/osu.Game/Beatmaps/BeatmapMetadata.cs @@ -15,7 +15,7 @@ namespace osu.Game.Beatmaps { [ExcludeFromDynamicCompile] [Serializable] - public class BeatmapMetadata : IEquatable, IHasPrimaryKey + public class BeatmapMetadata : IEquatable, IHasPrimaryKey, IBeatmapMetadataInfo { public int ID { get; set; } @@ -128,5 +128,7 @@ public bool Equals(BeatmapMetadata other) && AudioFile == other.AudioFile && BackgroundFile == other.BackgroundFile; } + + string IBeatmapMetadataInfo.Author => AuthorString; } } diff --git a/osu.Game/Beatmaps/BeatmapSetFileInfo.cs b/osu.Game/Beatmaps/BeatmapSetFileInfo.cs index 3a55dc1577e3..ce50463f05bd 100644 --- a/osu.Game/Beatmaps/BeatmapSetFileInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetFileInfo.cs @@ -7,7 +7,7 @@ namespace osu.Game.Beatmaps { - public class BeatmapSetFileInfo : INamedFileInfo, IHasPrimaryKey + public class BeatmapSetFileInfo : INamedFileInfo, IHasPrimaryKey, INamedFileUsage { public int ID { get; set; } @@ -19,5 +19,7 @@ public class BeatmapSetFileInfo : INamedFileInfo, IHasPrimaryKey [Required] public string Filename { get; set; } + + public IFileInfo File => FileInfo; } } diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index 3b1ff4ced0c6..739acb9a8dde 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -12,7 +12,7 @@ namespace osu.Game.Beatmaps { [ExcludeFromDynamicCompile] - public class BeatmapSetInfo : IHasPrimaryKey, IHasFiles, ISoftDelete, IEquatable + public class BeatmapSetInfo : IHasPrimaryKey, IHasFiles, ISoftDelete, IEquatable, IBeatmapSetInfo { public int ID { get; set; } @@ -90,5 +90,19 @@ public bool Equals(BeatmapSetInfo other) return ReferenceEquals(this, other); } + + #region Implementation of IHasOnlineID + + public int? OnlineID => ID; + + #endregion + + #region Implementation of IBeatmapSetInfo + + IBeatmapMetadataInfo IBeatmapSetInfo.Metadata => Metadata; + IEnumerable IBeatmapSetInfo.Beatmaps => Beatmaps; + IEnumerable IBeatmapSetInfo.Files => Files; + + #endregion } } diff --git a/osu.Game/Beatmaps/IBeatmapInfo.cs b/osu.Game/Beatmaps/IBeatmapInfo.cs index 72a04621f2b5..8ba8f316ed98 100644 --- a/osu.Game/Beatmaps/IBeatmapInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapInfo.cs @@ -2,6 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Localisation; +using osu.Game.Database; +using osu.Game.Rulesets; #nullable enable diff --git a/osu.Game/Beatmaps/IBeatmapSetInfo.cs b/osu.Game/Beatmaps/IBeatmapSetInfo.cs index f22115e08f7a..548a48367c76 100644 --- a/osu.Game/Beatmaps/IBeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapSetInfo.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using osu.Game.Database; #nullable enable diff --git a/osu.Game/Beatmaps/IHasOnlineID.cs b/osu.Game/Database/IHasOnlineID.cs similarity index 92% rename from osu.Game/Beatmaps/IHasOnlineID.cs rename to osu.Game/Database/IHasOnlineID.cs index dc2793afe5c4..c55c461d2d68 100644 --- a/osu.Game/Beatmaps/IHasOnlineID.cs +++ b/osu.Game/Database/IHasOnlineID.cs @@ -3,7 +3,7 @@ #nullable enable -namespace osu.Game.Beatmaps +namespace osu.Game.Database { public interface IHasOnlineID { diff --git a/osu.Game/Beatmaps/INamedFileUsage.cs b/osu.Game/Database/INamedFileUsage.cs similarity index 92% rename from osu.Game/Beatmaps/INamedFileUsage.cs rename to osu.Game/Database/INamedFileUsage.cs index aa7a3852a778..e558ffe0fb55 100644 --- a/osu.Game/Beatmaps/INamedFileUsage.cs +++ b/osu.Game/Database/INamedFileUsage.cs @@ -1,9 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Game.IO; + #nullable enable -namespace osu.Game.Beatmaps +namespace osu.Game.Database { /// /// A usage of a file, with a local filename attached. diff --git a/osu.Game/IO/FileInfo.cs b/osu.Game/IO/FileInfo.cs index e04bfb46cc31..331546f9f8ff 100644 --- a/osu.Game/IO/FileInfo.cs +++ b/osu.Game/IO/FileInfo.cs @@ -6,7 +6,7 @@ namespace osu.Game.IO { - public class FileInfo : IHasPrimaryKey + public class FileInfo : IHasPrimaryKey, IFileInfo { public int ID { get; set; } diff --git a/osu.Game/Beatmaps/IFileInfo.cs b/osu.Game/IO/IFileInfo.cs similarity index 93% rename from osu.Game/Beatmaps/IFileInfo.cs rename to osu.Game/IO/IFileInfo.cs index 50eb223fc4b0..080d8e57f5ac 100644 --- a/osu.Game/Beatmaps/IFileInfo.cs +++ b/osu.Game/IO/IFileInfo.cs @@ -3,7 +3,7 @@ #nullable enable -namespace osu.Game.Beatmaps +namespace osu.Game.IO { /// /// A representation of a tracked file. diff --git a/osu.Game/Beatmaps/IRulesetInfo.cs b/osu.Game/Rulesets/IRulesetInfo.cs similarity index 96% rename from osu.Game/Beatmaps/IRulesetInfo.cs rename to osu.Game/Rulesets/IRulesetInfo.cs index b31ebdfbfdf3..d4dec0de64e5 100644 --- a/osu.Game/Beatmaps/IRulesetInfo.cs +++ b/osu.Game/Rulesets/IRulesetInfo.cs @@ -2,11 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using System; -using osu.Game.Rulesets; +using osu.Game.Database; #nullable enable -namespace osu.Game.Beatmaps +namespace osu.Game.Rulesets { /// /// A representation of a ruleset's metadata. diff --git a/osu.Game/Rulesets/RulesetInfo.cs b/osu.Game/Rulesets/RulesetInfo.cs index 59ec9cdd7ec7..ca6a083a58e7 100644 --- a/osu.Game/Rulesets/RulesetInfo.cs +++ b/osu.Game/Rulesets/RulesetInfo.cs @@ -10,7 +10,7 @@ namespace osu.Game.Rulesets { [ExcludeFromDynamicCompile] - public class RulesetInfo : IEquatable + public class RulesetInfo : IEquatable, IRulesetInfo { public int? ID { get; set; } @@ -54,5 +54,11 @@ public override int GetHashCode() } public override string ToString() => Name ?? $"{Name} ({ShortName}) ID: {ID}"; + + #region Implementation of IHasOnlineID + + public int? OnlineID => ID; + + #endregion } } From 8595eb2d11d8540b01587efe5bd2ec0494639c34 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Oct 2021 14:38:30 +0900 Subject: [PATCH 03/18] Switch `BeatmapDifficulty` usages to use interface type --- osu.Game/Beatmaps/BeatmapDifficulty.cs | 45 +------------------ osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs | 2 +- .../Scoring/DrainingHealthProcessor.cs | 3 +- osu.Game/Rulesets/Scoring/HitWindows.cs | 4 +- 4 files changed, 7 insertions(+), 47 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapDifficulty.cs b/osu.Game/Beatmaps/BeatmapDifficulty.cs index 1844b193f226..0443422b3135 100644 --- a/osu.Game/Beatmaps/BeatmapDifficulty.cs +++ b/osu.Game/Beatmaps/BeatmapDifficulty.cs @@ -2,10 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Database; +using osu.Game.Models.Interfaces; namespace osu.Game.Beatmaps { - public class BeatmapDifficulty : IHasPrimaryKey + public class BeatmapDifficulty : IHasPrimaryKey, IBeatmapDifficultyInfo { /// /// The default value used for all difficulty settings except and . @@ -49,47 +50,5 @@ public void CopyTo(BeatmapDifficulty difficulty) difficulty.SliderMultiplier = SliderMultiplier; difficulty.SliderTickRate = SliderTickRate; } - - /// - /// Maps a difficulty value [0, 10] to a two-piece linear range of values. - /// - /// The difficulty value to be mapped. - /// Minimum of the resulting range which will be achieved by a difficulty value of 0. - /// Midpoint of the resulting range which will be achieved by a difficulty value of 5. - /// Maximum of the resulting range which will be achieved by a difficulty value of 10. - /// Value to which the difficulty value maps in the specified range. - public static double DifficultyRange(double difficulty, double min, double mid, double max) - { - if (difficulty > 5) - return mid + (max - mid) * (difficulty - 5) / 5; - if (difficulty < 5) - return mid - (mid - min) * (5 - difficulty) / 5; - - return mid; - } - - /// - /// Maps a difficulty value [0, 10] to a two-piece linear range of values. - /// - /// The difficulty value to be mapped. - /// The values that define the two linear ranges. - /// - /// - /// od0 - /// Minimum of the resulting range which will be achieved by a difficulty value of 0. - /// - /// - /// od5 - /// Midpoint of the resulting range which will be achieved by a difficulty value of 5. - /// - /// - /// od10 - /// Maximum of the resulting range which will be achieved by a difficulty value of 10. - /// - /// - /// - /// Value to which the difficulty value maps in the specified range. - public static double DifficultyRange(double difficulty, (double od0, double od5, double od10) range) - => DifficultyRange(difficulty, range.od0, range.od5, range.od10); } } diff --git a/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs b/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs index 6d9fcfcb0636..339364d44278 100644 --- a/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs @@ -84,7 +84,7 @@ static double DifficultyRange(double difficulty, double min, double mid, double /// /// /// Value to which the difficulty value maps in the specified range. - public static double DifficultyRange(double difficulty, (double od0, double od5, double od10) range) + static double DifficultyRange(double difficulty, (double od0, double od5, double od10) range) => DifficultyRange(difficulty, range.od0, range.od5, range.od10); } } diff --git a/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs b/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs index cae41e22f495..785a729c6d1e 100644 --- a/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs +++ b/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; +using osu.Game.Models.Interfaces; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Utils; @@ -100,7 +101,7 @@ public override void ApplyBeatmap(IBeatmap beatmap) .First() ))); - targetMinimumHealth = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, min_health_target, mid_health_target, max_health_target); + targetMinimumHealth = IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, min_health_target, mid_health_target, max_health_target); // Add back a portion of the amount of HP to be drained, depending on the lenience requested. targetMinimumHealth += drainLenience * (1 - targetMinimumHealth); diff --git a/osu.Game/Rulesets/Scoring/HitWindows.cs b/osu.Game/Rulesets/Scoring/HitWindows.cs index 410614de07a2..71a2af03fc94 100644 --- a/osu.Game/Rulesets/Scoring/HitWindows.cs +++ b/osu.Game/Rulesets/Scoring/HitWindows.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using osu.Game.Beatmaps; +using osu.Game.Models.Interfaces; using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Scoring @@ -86,7 +86,7 @@ public void SetDifficulty(double difficulty) { foreach (var range in GetRanges()) { - var value = BeatmapDifficulty.DifficultyRange(difficulty, (range.Min, range.Average, range.Max)); + var value = IBeatmapDifficultyInfo.DifficultyRange(difficulty, (range.Min, range.Average, range.Max)); switch (range.Result) { From 05996cc2e9232e7c54e1892ca2c3a253e7783513 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Oct 2021 17:03:08 +0900 Subject: [PATCH 04/18] Add changes that got forgotted in branch surgery --- .../Difficulty/CatchDifficultyCalculator.cs | 2 +- osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs | 2 +- osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs | 2 +- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs | 2 +- osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs | 2 +- osu.Game.Rulesets.Osu/Objects/Spinner.cs | 2 +- osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs | 2 +- osu.Game.Rulesets.Taiko/Scoring/TaikoHealthProcessor.cs | 4 ++-- osu.Game.Tournament/Components/SongBar.cs | 2 +- osu.Game/Beatmaps/BeatmapDifficulty.cs | 1 - osu.Game/Beatmaps/BeatmapInfo.cs | 1 - osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs | 1 - osu.Game/Rulesets/Scoring/HitWindows.cs | 2 +- 13 files changed, 11 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 82d76252d220..5b1f613f8db7 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -34,7 +34,7 @@ protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beat return new CatchDifficultyAttributes { Mods = mods, Skills = skills }; // this is the same as osu!, so there's potential to share the implementation... maybe - double preempt = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate; + double preempt = IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate; return new CatchDifficultyAttributes { diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index d43e6f1c8bce..32fdc9f62dd7 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -132,7 +132,7 @@ protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, B { base.ApplyDefaultsToSelf(controlPointInfo, difficulty); - TimePreempt = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450); + TimePreempt = (float)IBeatmapDifficultyInfo.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450); Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2; } diff --git a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs index 8b6a07442617..ba6e9224c9e9 100644 --- a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs @@ -27,7 +27,7 @@ public DrawableCatchRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList new CatchFramedReplayInputHandler(replay); diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 4c8d0b2ce691..a8f10f44dc1d 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -53,7 +53,7 @@ protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beat double starRating = basePerformance > 0.00001 ? Math.Cbrt(1.12) * 0.027 * (Math.Cbrt(100000 / Math.Pow(2, 1 / 1.1) * basePerformance) + 4) : 0; - double preempt = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate; + double preempt = (int)IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate; int maxCombo = beatmap.HitObjects.Count; // Add the ticks + tail of the slider. 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above) diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs index 36629fa41e1f..7015c5bce250 100644 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs @@ -126,7 +126,7 @@ protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, B { base.ApplyDefaultsToSelf(controlPointInfo, difficulty); - TimePreempt = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, PREEMPT_MIN); + TimePreempt = (float)IBeatmapDifficultyInfo.DifficultyRange(difficulty.ApproachRate, 1800, 1200, PREEMPT_MIN); // Preempt time can go below 450ms. Normally, this is achieved via the DT mod which uniformly speeds up all animations game wide regardless of AR. // This uniform speedup is hard to match 1:1, however we can at least make AR>10 (via mods) feel good by extending the upper linear function above. diff --git a/osu.Game.Rulesets.Osu/Objects/Spinner.cs b/osu.Game.Rulesets.Osu/Objects/Spinner.cs index 194aa640f9e6..d5f08f049c2a 100644 --- a/osu.Game.Rulesets.Osu/Objects/Spinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Spinner.cs @@ -43,7 +43,7 @@ protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, B double secondsDuration = Duration / 1000; - double minimumRotationsPerSecond = stable_matching_fudge * BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 3, 5, 7.5); + double minimumRotationsPerSecond = stable_matching_fudge * IBeatmapDifficultyInfo.DifficultyRange(difficulty.OverallDifficulty, 3, 5, 7.5); SpinsRequired = (int)(secondsDuration * minimumRotationsPerSecond); MaximumBonusSpins = (int)((maximum_rotations_per_second - minimumRotationsPerSecond) * secondsDuration); diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 9b73e644c56c..7068e469d235 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -117,7 +117,7 @@ protected override IEnumerable ConvertHitObject(HitObject obj, I case IHasDuration endTimeData: { - double hitMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 3, 5, 7.5) * swell_hit_multiplier; + double hitMultiplier = IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 3, 5, 7.5) * swell_hit_multiplier; yield return new Swell { diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoHealthProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoHealthProcessor.cs index f7a1d130eb82..94cd411d7be0 100644 --- a/osu.Game.Rulesets.Taiko/Scoring/TaikoHealthProcessor.cs +++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoHealthProcessor.cs @@ -40,8 +40,8 @@ public override void ApplyBeatmap(IBeatmap beatmap) { base.ApplyBeatmap(beatmap); - hpMultiplier = 1 / (object_count_factor * Math.Max(1, beatmap.HitObjects.OfType().Count()) * BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.5, 0.75, 0.98)); - hpMissMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.0018, 0.0075, 0.0120); + hpMultiplier = 1 / (object_count_factor * Math.Max(1, beatmap.HitObjects.OfType().Count()) * IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.5, 0.75, 0.98)); + hpMissMultiplier = IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.0018, 0.0075, 0.0120); } protected override double GetHealthIncreaseFor(JudgementResult result) diff --git a/osu.Game.Tournament/Components/SongBar.cs b/osu.Game.Tournament/Components/SongBar.cs index 6080f7b6360c..7bb01ddc6db6 100644 --- a/osu.Game.Tournament/Components/SongBar.cs +++ b/osu.Game.Tournament/Components/SongBar.cs @@ -117,7 +117,7 @@ private void update() if ((mods & LegacyMods.DoubleTime) > 0) { // temporary local calculation (taken from OsuDifficultyCalculator) - double preempt = (int)BeatmapDifficulty.DifficultyRange(ar, 1800, 1200, 450) / 1.5; + double preempt = (int)IBeatmapDifficultyInfo.DifficultyRange(ar, 1800, 1200, 450) / 1.5; ar = (float)(preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5); bpm *= 1.5f; diff --git a/osu.Game/Beatmaps/BeatmapDifficulty.cs b/osu.Game/Beatmaps/BeatmapDifficulty.cs index 0443422b3135..1bb1c86c1f22 100644 --- a/osu.Game/Beatmaps/BeatmapDifficulty.cs +++ b/osu.Game/Beatmaps/BeatmapDifficulty.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Database; -using osu.Game.Models.Interfaces; namespace osu.Game.Beatmaps { diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index d2b47ef1a4c5..d6f3bf0de422 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -24,7 +24,6 @@ public class BeatmapInfo : IEquatable, IHasPrimaryKey, IBeatmapInfo public int BeatmapVersion; private int? onlineBeatmapID; - private IRulesetInfo ruleset; [JsonProperty("id")] public int? OnlineBeatmapID diff --git a/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs b/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs index 785a729c6d1e..85693abb9397 100644 --- a/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs +++ b/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; -using osu.Game.Models.Interfaces; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Utils; diff --git a/osu.Game/Rulesets/Scoring/HitWindows.cs b/osu.Game/Rulesets/Scoring/HitWindows.cs index 71a2af03fc94..3ffd1eb66ba9 100644 --- a/osu.Game/Rulesets/Scoring/HitWindows.cs +++ b/osu.Game/Rulesets/Scoring/HitWindows.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using osu.Game.Models.Interfaces; +using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Scoring From 00e33a1da75496db67ccea0269e7f5d174b02f3d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Oct 2021 17:06:09 +0900 Subject: [PATCH 05/18] Fix incorrect `OnlineID` mappings --- osu.Game/Beatmaps/BeatmapInfo.cs | 2 +- osu.Game/Beatmaps/BeatmapSetInfo.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index d6f3bf0de422..1b3e67e2e713 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -190,7 +190,7 @@ public bool BackgroundEquals(BeatmapInfo other) => other != null && BeatmapSet ! #region Implementation of IHasOnlineID - public int? OnlineID => ID; + public int? OnlineID => OnlineBeatmapID; #endregion diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index 739acb9a8dde..7e26b154a9bc 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -93,7 +93,7 @@ public bool Equals(BeatmapSetInfo other) #region Implementation of IHasOnlineID - public int? OnlineID => ID; + public int? OnlineID => OnlineBeatmapSetID; #endregion From 9dae92e78cf9db6caf0b1e659d17be04017630d6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Oct 2021 17:22:25 +0900 Subject: [PATCH 06/18] Add missing backlink to `BeatmapSet` from `Beatmap` and fix non-explicit implementations --- osu.Game/Beatmaps/BeatmapInfo.cs | 7 ++++--- osu.Game/Beatmaps/IBeatmapInfo.cs | 5 +++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 1b3e67e2e713..83e547218ba4 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -196,11 +196,12 @@ public bool BackgroundEquals(BeatmapInfo other) => other != null && BeatmapSet ! #region Implementation of IBeatmapInfo - public string DifficultyName => Version; + string IBeatmapInfo.DifficultyName => Version; IBeatmapMetadataInfo IBeatmapInfo.Metadata => Metadata; - public IBeatmapDifficultyInfo Difficulty => BaseDifficulty; + IBeatmapDifficultyInfo IBeatmapInfo.Difficulty => BaseDifficulty; + IBeatmapSetInfo IBeatmapInfo.BeatmapSet => BeatmapSet; IRulesetInfo IBeatmapInfo.Ruleset => Ruleset; - public double StarRating => StarDifficulty; + double IBeatmapInfo.StarRating => StarDifficulty; #endregion } diff --git a/osu.Game/Beatmaps/IBeatmapInfo.cs b/osu.Game/Beatmaps/IBeatmapInfo.cs index 8ba8f316ed98..fa2ce2949b7e 100644 --- a/osu.Game/Beatmaps/IBeatmapInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapInfo.cs @@ -29,6 +29,11 @@ public interface IBeatmapInfo : IHasOnlineID /// IBeatmapDifficultyInfo Difficulty { get; } + /// + /// The beatmap set this beatmap is part of. + /// + IBeatmapSetInfo BeatmapSet { get; } + /// /// The playable length in milliseconds of this beatmap. /// From d6618a99a3b885e0163043c76b011d648c5367af Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Oct 2021 17:31:27 +0900 Subject: [PATCH 07/18] Redirect more methods to interface implementations --- osu.Game/Beatmaps/BeatmapInfo.cs | 8 ++--- osu.Game/Beatmaps/BeatmapMetadata.cs | 46 +++------------------------- osu.Game/Beatmaps/BeatmapSetInfo.cs | 2 +- 3 files changed, 8 insertions(+), 48 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 83e547218ba4..09f237a5deb1 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -157,13 +157,9 @@ public string StoredBookmarks Version }.Concat(Metadata?.SearchableTerms ?? Enumerable.Empty()).Where(s => !string.IsNullOrEmpty(s)).ToArray(); - public override string ToString() => $"{Metadata ?? BeatmapSet?.Metadata} {versionString}".Trim(); + public override string ToString() => ((IBeatmapInfo)this).DisplayTitle; - public RomanisableString ToRomanisableString() - { - var metadata = (Metadata ?? BeatmapSet?.Metadata)?.ToRomanisableString() ?? new RomanisableString(null, null); - return new RomanisableString($"{metadata.GetPreferred(true)} {versionString}".Trim(), $"{metadata.GetPreferred(false)} {versionString}".Trim()); - } + public RomanisableString ToRomanisableString() => ((IBeatmapInfo)this).DisplayTitleRomanisable; public bool Equals(BeatmapInfo other) { diff --git a/osu.Game/Beatmaps/BeatmapMetadata.cs b/osu.Game/Beatmaps/BeatmapMetadata.cs index fbd47d2614a6..3da80580cbaf 100644 --- a/osu.Game/Beatmaps/BeatmapMetadata.cs +++ b/osu.Game/Beatmaps/BeatmapMetadata.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; using Newtonsoft.Json; using osu.Framework.Localisation; using osu.Framework.Testing; @@ -83,51 +82,16 @@ public string AuthorString public int PreviewTime { get; set; } public string AudioFile { get; set; } + public string BackgroundFile { get; set; } - public override string ToString() - { - string author = Author == null ? string.Empty : $"({Author})"; - return $"{Artist} - {Title} {author}".Trim(); - } + public bool Equals(BeatmapMetadata other) => ((IBeatmapMetadataInfo)this).Equals(other); - public RomanisableString ToRomanisableString() - { - string author = Author == null ? string.Empty : $"({Author})"; - var artistUnicode = string.IsNullOrEmpty(ArtistUnicode) ? Artist : ArtistUnicode; - var titleUnicode = string.IsNullOrEmpty(TitleUnicode) ? Title : TitleUnicode; + public override string ToString() => ((IBeatmapMetadataInfo)this).DisplayTitle; - return new RomanisableString($"{artistUnicode} - {titleUnicode} {author}".Trim(), $"{Artist} - {Title} {author}".Trim()); - } + public RomanisableString ToRomanisableString() => ((IBeatmapMetadataInfo)this).DisplayTitleRomanisable; - [JsonIgnore] - public string[] SearchableTerms => new[] - { - Author?.Username, - Artist, - ArtistUnicode, - Title, - TitleUnicode, - Source, - Tags - }.Where(s => !string.IsNullOrEmpty(s)).ToArray(); - - public bool Equals(BeatmapMetadata other) - { - if (other == null) - return false; - - return Title == other.Title - && TitleUnicode == other.TitleUnicode - && Artist == other.Artist - && ArtistUnicode == other.ArtistUnicode - && AuthorString == other.AuthorString - && Source == other.Source - && Tags == other.Tags - && PreviewTime == other.PreviewTime - && AudioFile == other.AudioFile - && BackgroundFile == other.BackgroundFile; - } + public IEnumerable SearchableTerms => ((IBeatmapMetadataInfo)this).SearchableTerms; string IBeatmapMetadataInfo.Author => AuthorString; } diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index 7e26b154a9bc..4804c7032c73 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -61,7 +61,7 @@ public int? OnlineBeatmapSetID public string Hash { get; set; } - public string StoryboardFile => Files.Find(f => f.Filename.EndsWith(".osb", StringComparison.OrdinalIgnoreCase))?.Filename; + public string StoryboardFile => ((IBeatmapSetInfo)this).StoryboardFile; /// /// Returns the storage path for the file in this beatmapset with the given filename, if any exists, otherwise null. From 0daf8937e3a954f0ea595e5a2c4131bdb616e0a2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Oct 2021 17:33:57 +0900 Subject: [PATCH 08/18] Add missing xmldoc --- osu.Game/Beatmaps/IBeatmapInfo.cs | 6 ++++++ osu.Game/Beatmaps/IBeatmapMetadataInfo.cs | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/osu.Game/Beatmaps/IBeatmapInfo.cs b/osu.Game/Beatmaps/IBeatmapInfo.cs index fa2ce2949b7e..6153a0af087d 100644 --- a/osu.Game/Beatmaps/IBeatmapInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapInfo.cs @@ -64,8 +64,14 @@ public interface IBeatmapInfo : IHasOnlineID /// double StarRating { get; } + /// + /// A user-presentable display title representing this metadata. + /// string DisplayTitle => $"{Metadata} {versionString}".Trim(); + /// + /// A user-presentable display title representing this beatmap, with localisation handling for potentially romanisable fields. + /// RomanisableString DisplayTitleRomanisable { get diff --git a/osu.Game/Beatmaps/IBeatmapMetadataInfo.cs b/osu.Game/Beatmaps/IBeatmapMetadataInfo.cs index 18dd38b404e8..d0dae296a0b0 100644 --- a/osu.Game/Beatmaps/IBeatmapMetadataInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapMetadataInfo.cs @@ -65,6 +65,9 @@ public interface IBeatmapMetadataInfo : IEquatable /// string BackgroundFile { get; } + /// + /// A user-presentable display title representing this metadata. + /// string DisplayTitle { get @@ -74,6 +77,9 @@ string DisplayTitle } } + /// + /// A user-presentable display title representing this metadata, with localisation handling for potentially romanisable fields. + /// RomanisableString DisplayTitleRomanisable { get @@ -86,6 +92,9 @@ RomanisableString DisplayTitleRomanisable } } + /// + /// An array of all searchable terms provided in contained metadata. + /// string[] SearchableTerms => new[] { Author, From dcd7d7a709beb3b5b837b4c88c9861c764613072 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 2 Oct 2021 03:05:04 +0900 Subject: [PATCH 09/18] Add `JsonIgnore` rule for `StoryboardFile` Not sure why this is required, doesn't make much sense. --- osu.Game/Beatmaps/BeatmapSetInfo.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index 4804c7032c73..9f5a07ec43bb 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -6,6 +6,7 @@ using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using JetBrains.Annotations; +using Newtonsoft.Json; using osu.Framework.Testing; using osu.Game.Database; @@ -61,6 +62,7 @@ public int? OnlineBeatmapSetID public string Hash { get; set; } + [JsonIgnore] public string StoryboardFile => ((IBeatmapSetInfo)this).StoryboardFile; /// From 8bfdfe3672997fcacfed8a571629c17403348d9e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Oct 2021 16:54:00 +0900 Subject: [PATCH 10/18] Add literal string marker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Beatmaps/IBeatmapSetInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/IBeatmapSetInfo.cs b/osu.Game/Beatmaps/IBeatmapSetInfo.cs index 548a48367c76..d5aded7b53a0 100644 --- a/osu.Game/Beatmaps/IBeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapSetInfo.cs @@ -53,6 +53,6 @@ public interface IBeatmapSetInfo : IHasOnlineID /// /// The filename for the storyboard. /// - string StoryboardFile => Files.FirstOrDefault(f => f.Filename.EndsWith(".osb", StringComparison.OrdinalIgnoreCase))?.Filename ?? string.Empty; + string StoryboardFile => Files.FirstOrDefault(f => f.Filename.EndsWith(@".osb", StringComparison.OrdinalIgnoreCase))?.Filename ?? string.Empty; } } From 4df5f931522f63553e4d2049b3147f5c7fbb650b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Oct 2021 16:50:29 +0900 Subject: [PATCH 11/18] Inline single usage of `StoryboardFile` to avoid interface default method woes --- osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs | 6 ++++-- osu.Game/Beatmaps/BeatmapSetInfo.cs | 4 ---- osu.Game/Beatmaps/IBeatmapSetInfo.cs | 6 ------ 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs index 45112ae74c3d..f9889474aca8 100644 --- a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs @@ -107,12 +107,14 @@ protected override Storyboard GetStoryboard() { var decoder = Decoder.GetDecoder(stream); + var storyboardFilename = BeatmapSetInfo?.Files.FirstOrDefault(f => f.Filename.EndsWith(".osb", StringComparison.OrdinalIgnoreCase))?.Filename; + // todo: support loading from both set-wide storyboard *and* beatmap specific. - if (BeatmapSetInfo?.StoryboardFile == null) + if (string.IsNullOrEmpty(storyboardFilename)) storyboard = decoder.Decode(stream); else { - using (var secondaryStream = new LineBufferedReader(GetStream(BeatmapSetInfo.GetPathForFile(BeatmapSetInfo.StoryboardFile)))) + using (var secondaryStream = new LineBufferedReader(GetStream(BeatmapSetInfo.GetPathForFile(storyboardFilename)))) storyboard = decoder.Decode(stream, secondaryStream); } } diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index 9f5a07ec43bb..8b01831b3cce 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -6,7 +6,6 @@ using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using JetBrains.Annotations; -using Newtonsoft.Json; using osu.Framework.Testing; using osu.Game.Database; @@ -62,9 +61,6 @@ public int? OnlineBeatmapSetID public string Hash { get; set; } - [JsonIgnore] - public string StoryboardFile => ((IBeatmapSetInfo)this).StoryboardFile; - /// /// Returns the storage path for the file in this beatmapset with the given filename, if any exists, otherwise null. /// The path returned is relative to the user file storage. diff --git a/osu.Game/Beatmaps/IBeatmapSetInfo.cs b/osu.Game/Beatmaps/IBeatmapSetInfo.cs index d5aded7b53a0..0cfb0c42429e 100644 --- a/osu.Game/Beatmaps/IBeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapSetInfo.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Linq; using osu.Game.Database; #nullable enable @@ -49,10 +48,5 @@ public interface IBeatmapSetInfo : IHasOnlineID /// The maximum BPM of all beatmaps in this set. /// double MaxBPM { get; } - - /// - /// The filename for the storyboard. - /// - string StoryboardFile => Files.FirstOrDefault(f => f.Filename.EndsWith(@".osb", StringComparison.OrdinalIgnoreCase))?.Filename ?? string.Empty; } } From fd6b10656c638753a15efcb004f8848e109852bf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Oct 2021 16:55:16 +0900 Subject: [PATCH 12/18] Add TODO reminder about ruleset reference transfer quirk --- osu.Game/Rulesets/IRulesetInfo.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Rulesets/IRulesetInfo.cs b/osu.Game/Rulesets/IRulesetInfo.cs index d4dec0de64e5..ded3ac4b58e7 100644 --- a/osu.Game/Rulesets/IRulesetInfo.cs +++ b/osu.Game/Rulesets/IRulesetInfo.cs @@ -38,6 +38,7 @@ public interface IRulesetInfo : IHasOnlineID var ruleset = Activator.CreateInstance(type) as Ruleset; // overwrite the pre-populated RulesetInfo with a potentially database attached copy. + // TODO: figure if we still want/need this after switching to realm. // ruleset.RulesetInfo = this; return ruleset; From 51b7dce16f2bfb031a135183cdabecbbf2a2359b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Oct 2021 16:55:51 +0900 Subject: [PATCH 13/18] Remove reference to `osu-web-10` --- osu.Game/Beatmaps/IBeatmapInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/IBeatmapInfo.cs b/osu.Game/Beatmaps/IBeatmapInfo.cs index 6153a0af087d..d552a3f1e303 100644 --- a/osu.Game/Beatmaps/IBeatmapInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapInfo.cs @@ -50,7 +50,7 @@ public interface IBeatmapInfo : IHasOnlineID string Hash { get; } /// - /// MD5 is kept for legacy support (matching against replays, osu-web-10 etc.). + /// MD5 is kept for legacy support (matching against replays etc.). /// string MD5Hash { get; } From f293e008d953d334ebb291481502679519d2c88c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Oct 2021 17:00:22 +0900 Subject: [PATCH 14/18] Move `BeatmapInfo`'s `SearchableTerms` implementation to interface --- osu.Game/Beatmaps/BeatmapInfo.cs | 5 +---- osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs | 1 + osu.Game/Beatmaps/IBeatmapInfo.cs | 13 +++++++++++-- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 09f237a5deb1..cd5f8fb9a149 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -152,10 +152,7 @@ public string StoredBookmarks [JsonIgnore] public DifficultyRating DifficultyRating => BeatmapDifficultyCache.GetDifficultyRating(StarDifficulty); - public string[] SearchableTerms => new[] - { - Version - }.Concat(Metadata?.SearchableTerms ?? Enumerable.Empty()).Where(s => !string.IsNullOrEmpty(s)).ToArray(); + public IEnumerable SearchableTerms => ((IBeatmapInfo)this).SearchableTerms; public override string ToString() => ((IBeatmapInfo)this).DisplayTitle; diff --git a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs index f9889474aca8..5fec8f7a89c0 100644 --- a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Linq; using osu.Framework.Audio.Track; using osu.Framework.Graphics.Textures; using osu.Framework.Logging; diff --git a/osu.Game/Beatmaps/IBeatmapInfo.cs b/osu.Game/Beatmaps/IBeatmapInfo.cs index d552a3f1e303..fb30b0279c01 100644 --- a/osu.Game/Beatmaps/IBeatmapInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapInfo.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using osu.Framework.Localisation; using osu.Game.Database; using osu.Game.Rulesets; @@ -22,7 +23,7 @@ public interface IBeatmapInfo : IHasOnlineID /// /// The metadata representing this beatmap. May be shared between multiple beatmaps. /// - IBeatmapMetadataInfo Metadata { get; } + IBeatmapMetadataInfo? Metadata { get; } /// /// The difficulty settings for this beatmap. @@ -76,12 +77,20 @@ RomanisableString DisplayTitleRomanisable { get { - var metadata = Metadata.DisplayTitleRomanisable; + var metadata = closestMetadata.DisplayTitleRomanisable; return new RomanisableString($"{metadata.GetPreferred(true)} {versionString}".Trim(), $"{metadata.GetPreferred(false)} {versionString}".Trim()); } } + string[] SearchableTerms => new[] + { + DifficultyName + }.Concat(closestMetadata.SearchableTerms).Where(s => !string.IsNullOrEmpty(s)).ToArray(); + private string versionString => string.IsNullOrEmpty(DifficultyName) ? string.Empty : $"[{DifficultyName}]"; + + // temporary helper methods until we figure which metadata should be where. + private IBeatmapMetadataInfo closestMetadata => (Metadata ?? BeatmapSet.Metadata)!; } } From d3b9660148d3ebc79660915c1db35b779b001402 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Oct 2021 14:41:14 +0900 Subject: [PATCH 15/18] Move common interface implementations to extension methods --- .../BeatmapMetadataRomanisationTest.cs | 4 +- osu.Game/Beatmaps/BeatmapInfo.cs | 7 +-- osu.Game/Beatmaps/BeatmapInfoExtensions.cs | 37 +++++++++++++++ osu.Game/Beatmaps/BeatmapMetadata.cs | 7 +-- .../Beatmaps/BeatmapMetadataInfoExtensions.cs | 46 +++++++++++++++++++ osu.Game/Beatmaps/IBeatmapInfo.cs | 30 ------------ osu.Game/Beatmaps/IBeatmapMetadataInfo.cs | 43 ----------------- osu.Game/Overlays/Music/PlaylistItem.cs | 2 +- .../OnlinePlay/DrawableRoomPlaylistItem.cs | 2 +- .../Lounge/Components/DrawableRoom.cs | 2 +- .../Select/Carousel/CarouselBeatmap.cs | 2 +- 11 files changed, 91 insertions(+), 91 deletions(-) create mode 100644 osu.Game/Beatmaps/BeatmapInfoExtensions.cs create mode 100644 osu.Game/Beatmaps/BeatmapMetadataInfoExtensions.cs diff --git a/osu.Game.Tests/Localisation/BeatmapMetadataRomanisationTest.cs b/osu.Game.Tests/Localisation/BeatmapMetadataRomanisationTest.cs index dab4825919ce..9926acf77213 100644 --- a/osu.Game.Tests/Localisation/BeatmapMetadataRomanisationTest.cs +++ b/osu.Game.Tests/Localisation/BeatmapMetadataRomanisationTest.cs @@ -19,7 +19,7 @@ public void TestRomanisation() Title = "Romanised title", TitleUnicode = "Unicode Title" }; - var romanisableString = metadata.ToRomanisableString(); + var romanisableString = metadata.GetDisplayTitleRomanisable(); Assert.AreEqual(metadata.ToString(), romanisableString.Romanised); Assert.AreEqual($"{metadata.ArtistUnicode} - {metadata.TitleUnicode}", romanisableString.Original); @@ -33,7 +33,7 @@ public void TestRomanisationNoUnicode() Artist = "Romanised Artist", Title = "Romanised title" }; - var romanisableString = metadata.ToRomanisableString(); + var romanisableString = metadata.GetDisplayTitleRomanisable(); Assert.AreEqual(romanisableString.Romanised, romanisableString.Original); } diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index cd5f8fb9a149..ac5b5d7a8a31 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -7,7 +7,6 @@ using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using Newtonsoft.Json; -using osu.Framework.Localisation; using osu.Framework.Testing; using osu.Game.Database; using osu.Game.Rulesets; @@ -152,11 +151,7 @@ public string StoredBookmarks [JsonIgnore] public DifficultyRating DifficultyRating => BeatmapDifficultyCache.GetDifficultyRating(StarDifficulty); - public IEnumerable SearchableTerms => ((IBeatmapInfo)this).SearchableTerms; - - public override string ToString() => ((IBeatmapInfo)this).DisplayTitle; - - public RomanisableString ToRomanisableString() => ((IBeatmapInfo)this).DisplayTitleRomanisable; + public override string ToString() => this.GetDisplayTitle(); public bool Equals(BeatmapInfo other) { diff --git a/osu.Game/Beatmaps/BeatmapInfoExtensions.cs b/osu.Game/Beatmaps/BeatmapInfoExtensions.cs new file mode 100644 index 000000000000..deab8b915aff --- /dev/null +++ b/osu.Game/Beatmaps/BeatmapInfoExtensions.cs @@ -0,0 +1,37 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Framework.Localisation; + +namespace osu.Game.Beatmaps +{ + public static class BeatmapInfoExtensions + { + /// + /// A user-presentable display title representing this beatmap. + /// + public static string GetDisplayTitle(this IBeatmapInfo beatmapInfo) => $"{getClosestMetadata(beatmapInfo)} {getVersionString(beatmapInfo)}".Trim(); + + /// + /// A user-presentable display title representing this beatmap, with localisation handling for potentially romanisable fields. + /// + public static RomanisableString GetDisplayTitleRomanisable(this IBeatmapInfo beatmapInfo) + { + var metadata = getClosestMetadata(beatmapInfo).GetDisplayTitleRomanisable(); + var versionString = getVersionString(beatmapInfo); + + return new RomanisableString($"{metadata.GetPreferred(true)} {versionString}".Trim(), $"{metadata.GetPreferred(false)} {versionString}".Trim()); + } + + public static string[] GetSearchableTerms(this IBeatmapInfo beatmapInfo) => new[] + { + beatmapInfo.DifficultyName + }.Concat(getClosestMetadata(beatmapInfo).GetSearchableTerms()).Where(s => !string.IsNullOrEmpty(s)).ToArray(); + + private static string getVersionString(IBeatmapInfo beatmapInfo) => string.IsNullOrEmpty(beatmapInfo.DifficultyName) ? string.Empty : $"[{beatmapInfo.DifficultyName}]"; + + // temporary helper methods until we figure which metadata should be where. + private static IBeatmapMetadataInfo getClosestMetadata(IBeatmapInfo beatmapInfo) => (beatmapInfo.Metadata ?? beatmapInfo.BeatmapSet.Metadata)!; + } +} diff --git a/osu.Game/Beatmaps/BeatmapMetadata.cs b/osu.Game/Beatmaps/BeatmapMetadata.cs index 3da80580cbaf..711533e1181e 100644 --- a/osu.Game/Beatmaps/BeatmapMetadata.cs +++ b/osu.Game/Beatmaps/BeatmapMetadata.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using Newtonsoft.Json; -using osu.Framework.Localisation; using osu.Framework.Testing; using osu.Game.Database; using osu.Game.Users; @@ -87,11 +86,7 @@ public string AuthorString public bool Equals(BeatmapMetadata other) => ((IBeatmapMetadataInfo)this).Equals(other); - public override string ToString() => ((IBeatmapMetadataInfo)this).DisplayTitle; - - public RomanisableString ToRomanisableString() => ((IBeatmapMetadataInfo)this).DisplayTitleRomanisable; - - public IEnumerable SearchableTerms => ((IBeatmapMetadataInfo)this).SearchableTerms; + public override string ToString() => this.GetDisplayTitle(); string IBeatmapMetadataInfo.Author => AuthorString; } diff --git a/osu.Game/Beatmaps/BeatmapMetadataInfoExtensions.cs b/osu.Game/Beatmaps/BeatmapMetadataInfoExtensions.cs new file mode 100644 index 000000000000..ee946eeeecd4 --- /dev/null +++ b/osu.Game/Beatmaps/BeatmapMetadataInfoExtensions.cs @@ -0,0 +1,46 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Framework.Localisation; + +namespace osu.Game.Beatmaps +{ + public static class BeatmapMetadataInfoExtensions + { + /// + /// An array of all searchable terms provided in contained metadata. + /// + public static string[] GetSearchableTerms(this IBeatmapMetadataInfo metadataInfo) => new[] + { + metadataInfo.Author, + metadataInfo.Artist, + metadataInfo.ArtistUnicode, + metadataInfo.Title, + metadataInfo.TitleUnicode, + metadataInfo.Source, + metadataInfo.Tags + }.Where(s => !string.IsNullOrEmpty(s)).ToArray(); + + /// + /// A user-presentable display title representing this metadata. + /// + public static string GetDisplayTitle(this IBeatmapMetadataInfo metadataInfo) + { + string author = string.IsNullOrEmpty(metadataInfo.Author) ? string.Empty : $"({metadataInfo.Author})"; + return $"{metadataInfo.Artist} - {metadataInfo.Title} {author}".Trim(); + } + + /// + /// A user-presentable display title representing this beatmap, with localisation handling for potentially romanisable fields. + /// + public static RomanisableString GetDisplayTitleRomanisable(this IBeatmapMetadataInfo metadataInfo) + { + string author = string.IsNullOrEmpty(metadataInfo.Author) ? string.Empty : $"({metadataInfo.Author})"; + var artistUnicode = string.IsNullOrEmpty(metadataInfo.ArtistUnicode) ? metadataInfo.Artist : metadataInfo.ArtistUnicode; + var titleUnicode = string.IsNullOrEmpty(metadataInfo.TitleUnicode) ? metadataInfo.Title : metadataInfo.TitleUnicode; + + return new RomanisableString($"{artistUnicode} - {titleUnicode} {author}".Trim(), $"{metadataInfo.Artist} - {metadataInfo.Title} {author}".Trim()); + } + } +} diff --git a/osu.Game/Beatmaps/IBeatmapInfo.cs b/osu.Game/Beatmaps/IBeatmapInfo.cs index fb30b0279c01..6a3f1b43d871 100644 --- a/osu.Game/Beatmaps/IBeatmapInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapInfo.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Linq; -using osu.Framework.Localisation; using osu.Game.Database; using osu.Game.Rulesets; @@ -64,33 +62,5 @@ public interface IBeatmapInfo : IHasOnlineID /// The basic star rating for this beatmap (with no mods applied). /// double StarRating { get; } - - /// - /// A user-presentable display title representing this metadata. - /// - string DisplayTitle => $"{Metadata} {versionString}".Trim(); - - /// - /// A user-presentable display title representing this beatmap, with localisation handling for potentially romanisable fields. - /// - RomanisableString DisplayTitleRomanisable - { - get - { - var metadata = closestMetadata.DisplayTitleRomanisable; - - return new RomanisableString($"{metadata.GetPreferred(true)} {versionString}".Trim(), $"{metadata.GetPreferred(false)} {versionString}".Trim()); - } - } - - string[] SearchableTerms => new[] - { - DifficultyName - }.Concat(closestMetadata.SearchableTerms).Where(s => !string.IsNullOrEmpty(s)).ToArray(); - - private string versionString => string.IsNullOrEmpty(DifficultyName) ? string.Empty : $"[{DifficultyName}]"; - - // temporary helper methods until we figure which metadata should be where. - private IBeatmapMetadataInfo closestMetadata => (Metadata ?? BeatmapSet.Metadata)!; } } diff --git a/osu.Game/Beatmaps/IBeatmapMetadataInfo.cs b/osu.Game/Beatmaps/IBeatmapMetadataInfo.cs index d0dae296a0b0..55aee7d7bca2 100644 --- a/osu.Game/Beatmaps/IBeatmapMetadataInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapMetadataInfo.cs @@ -2,8 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Linq; -using osu.Framework.Localisation; #nullable enable @@ -65,47 +63,6 @@ public interface IBeatmapMetadataInfo : IEquatable /// string BackgroundFile { get; } - /// - /// A user-presentable display title representing this metadata. - /// - string DisplayTitle - { - get - { - string author = string.IsNullOrEmpty(Author) ? string.Empty : $"({Author})"; - return $"{Artist} - {Title} {author}".Trim(); - } - } - - /// - /// A user-presentable display title representing this metadata, with localisation handling for potentially romanisable fields. - /// - RomanisableString DisplayTitleRomanisable - { - get - { - string author = string.IsNullOrEmpty(Author) ? string.Empty : $"({Author})"; - var artistUnicode = string.IsNullOrEmpty(ArtistUnicode) ? Artist : ArtistUnicode; - var titleUnicode = string.IsNullOrEmpty(TitleUnicode) ? Title : TitleUnicode; - - return new RomanisableString($"{artistUnicode} - {titleUnicode} {author}".Trim(), $"{Artist} - {Title} {author}".Trim()); - } - } - - /// - /// An array of all searchable terms provided in contained metadata. - /// - string[] SearchableTerms => new[] - { - Author, - Artist, - ArtistUnicode, - Title, - TitleUnicode, - Source, - Tags - }.Where(s => !string.IsNullOrEmpty(s)).ToArray(); - bool IEquatable.Equals(IBeatmapMetadataInfo? other) { if (other == null) diff --git a/osu.Game/Overlays/Music/PlaylistItem.cs b/osu.Game/Overlays/Music/PlaylistItem.cs index 571b14428e2d..ef25de77c68f 100644 --- a/osu.Game/Overlays/Music/PlaylistItem.cs +++ b/osu.Game/Overlays/Music/PlaylistItem.cs @@ -38,7 +38,7 @@ public PlaylistItem(BeatmapSetInfo item) { Padding = new MarginPadding { Left = 5 }; - FilterTerms = item.Metadata.SearchableTerms; + FilterTerms = item.Metadata.GetSearchableTerms(); } [BackgroundDependencyLoader] diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index 69eb85766172..585b02462379 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -108,7 +108,7 @@ private void refresh() difficultyIconContainer.Child = new DifficultyIcon(beatmap.Value, ruleset.Value, requiredMods) { Size = new Vector2(32) }; beatmapText.Clear(); - beatmapText.AddLink(Item.Beatmap.Value.ToRomanisableString(), LinkAction.OpenBeatmap, Item.Beatmap.Value.OnlineBeatmapID.ToString(), null, text => + beatmapText.AddLink(Item.Beatmap.Value.GetDisplayTitleRomanisable(), LinkAction.OpenBeatmap, Item.Beatmap.Value.OnlineBeatmapID.ToString(), null, text => { text.Truncate = true; text.RelativeSizeAxes = Axes.X; diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 03d13c353ada..acd87ed8644a 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -379,7 +379,7 @@ private void onSelectedItemChanged(ValueChangedEvent item) if (item.NewValue?.Beatmap.Value != null) { statusText.Text = "Currently playing "; - beatmapText.AddLink(item.NewValue.Beatmap.Value.ToRomanisableString(), + beatmapText.AddLink(item.NewValue.Beatmap.Value.GetDisplayTitleRomanisable(), LinkAction.OpenBeatmap, item.NewValue.Beatmap.Value.OnlineBeatmapID.ToString(), creationParameters: s => diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs index 3f729d947770..d8c5aa760ee5 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs @@ -57,7 +57,7 @@ public override void Filter(FilterCriteria criteria) if (match) { - var terms = BeatmapInfo.SearchableTerms; + var terms = BeatmapInfo.GetSearchableTerms(); foreach (var criteriaTerm in criteria.SearchTerms) match &= terms.Any(term => term.Contains(criteriaTerm, StringComparison.InvariantCultureIgnoreCase)); From a5aa32811aaf215f7d96d601a82d44362d140ad3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Oct 2021 14:49:59 +0900 Subject: [PATCH 16/18] Remove null check suppression and add non-null fallback --- osu.Game/Beatmaps/BeatmapInfoExtensions.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapInfoExtensions.cs b/osu.Game/Beatmaps/BeatmapInfoExtensions.cs index deab8b915aff..eba19ac1a1c8 100644 --- a/osu.Game/Beatmaps/BeatmapInfoExtensions.cs +++ b/osu.Game/Beatmaps/BeatmapInfoExtensions.cs @@ -32,6 +32,7 @@ public static string[] GetSearchableTerms(this IBeatmapInfo beatmapInfo) => new[ private static string getVersionString(IBeatmapInfo beatmapInfo) => string.IsNullOrEmpty(beatmapInfo.DifficultyName) ? string.Empty : $"[{beatmapInfo.DifficultyName}]"; // temporary helper methods until we figure which metadata should be where. - private static IBeatmapMetadataInfo getClosestMetadata(IBeatmapInfo beatmapInfo) => (beatmapInfo.Metadata ?? beatmapInfo.BeatmapSet.Metadata)!; + private static IBeatmapMetadataInfo getClosestMetadata(IBeatmapInfo beatmapInfo) => + beatmapInfo.Metadata ?? beatmapInfo.BeatmapSet?.Metadata ?? new BeatmapMetadata(); } } From 4d5696959b0435bf4bcb0fad96b7c233eaf2bdb6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Oct 2021 05:52:28 +0900 Subject: [PATCH 17/18] Remove unnecessary access modifier in interface Co-authored-by: Dan Balasescu --- osu.Game/Rulesets/IRulesetInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/IRulesetInfo.cs b/osu.Game/Rulesets/IRulesetInfo.cs index ded3ac4b58e7..779433dc8179 100644 --- a/osu.Game/Rulesets/IRulesetInfo.cs +++ b/osu.Game/Rulesets/IRulesetInfo.cs @@ -28,7 +28,7 @@ public interface IRulesetInfo : IHasOnlineID /// string InstantiationInfo { get; } - public Ruleset? CreateInstance() + Ruleset? CreateInstance() { var type = Type.GetType(InstantiationInfo); From 4f59fc15a50910eb4716f3968779e64b28856acc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Oct 2021 05:54:37 +0900 Subject: [PATCH 18/18] Mark `BeatmapSet` as nullable for the time being --- osu.Game/Beatmaps/IBeatmapInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/IBeatmapInfo.cs b/osu.Game/Beatmaps/IBeatmapInfo.cs index 6a3f1b43d871..3d51c5d4b658 100644 --- a/osu.Game/Beatmaps/IBeatmapInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapInfo.cs @@ -31,7 +31,7 @@ public interface IBeatmapInfo : IHasOnlineID /// /// The beatmap set this beatmap is part of. /// - IBeatmapSetInfo BeatmapSet { get; } + IBeatmapSetInfo? BeatmapSet { get; } /// /// The playable length in milliseconds of this beatmap.