From e70f257947b64af00a4d0dc8354ef3877990223c Mon Sep 17 00:00:00 2001 From: minisbett <39670899+minisbett@users.noreply.github.com> Date: Sat, 16 Nov 2024 16:10:21 +0100 Subject: [PATCH 01/15] make performance attributes a struct --- .../Difficulty/CatchPerformanceAttributes.cs | 14 +++++++++++++- .../Difficulty/CatchPerformanceCalculator.cs | 2 +- .../Difficulty/ManiaPerformanceAttributes.cs | 14 +++++++++----- .../Difficulty/ManiaPerformanceCalculator.cs | 2 +- .../Difficulty/OsuPerformanceAttributes.cs | 14 +++++++++----- .../Difficulty/OsuPerformanceCalculator.cs | 2 +- .../Difficulty/TaikoPerformanceAttributes.cs | 14 +++++++++----- .../Difficulty/TaikoPerformanceCalculator.cs | 2 +- .../Rulesets/Difficulty/PerformanceAttributes.cs | 7 ++----- .../Rulesets/Difficulty/PerformanceBreakdown.cs | 6 +++--- .../Difficulty/PerformanceBreakdownCalculator.cs | 13 ++++++++++--- .../Rulesets/Difficulty/PerformanceCalculator.cs | 10 +++++----- .../Statistics/PerformanceBreakdownChart.cs | 6 +++--- 13 files changed, 67 insertions(+), 39 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceAttributes.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceAttributes.cs index 1335fc2d23..3673e79159 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceAttributes.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceAttributes.cs @@ -1,11 +1,23 @@ // 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 Newtonsoft.Json; using osu.Game.Rulesets.Difficulty; namespace osu.Game.Rulesets.Catch.Difficulty { - public class CatchPerformanceAttributes : PerformanceAttributes + public struct CatchPerformanceAttributes : IPerformanceAttributes { + /// + /// Calculated score performance points. + /// + [JsonProperty("pp")] + public double Total { get; set; } + + public IEnumerable GetAttributesForDisplay() + { + yield return new PerformanceDisplayAttribute(nameof(Total), "Achieved PP", Total); + } } } diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs index 55232a9598..efca1e5e77 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty { } - protected override PerformanceAttributes CreatePerformanceAttributes(ScoreInfo score, DifficultyAttributes attributes) + protected override IPerformanceAttributes CreatePerformanceAttributes(ScoreInfo score, DifficultyAttributes attributes) { var catchAttributes = (CatchDifficultyAttributes)attributes; diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceAttributes.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceAttributes.cs index 64f8b026c2..6a90a6f117 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceAttributes.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceAttributes.cs @@ -7,16 +7,20 @@ using osu.Game.Rulesets.Difficulty; namespace osu.Game.Rulesets.Mania.Difficulty { - public class ManiaPerformanceAttributes : PerformanceAttributes + public struct ManiaPerformanceAttributes : IPerformanceAttributes { + /// + /// Calculated score performance points. + /// + [JsonProperty("pp")] + public double Total { get; set; } + [JsonProperty("difficulty")] public double Difficulty { get; set; } - public override IEnumerable GetAttributesForDisplay() + public IEnumerable GetAttributesForDisplay() { - foreach (var attribute in base.GetAttributesForDisplay()) - yield return attribute; - + yield return new PerformanceDisplayAttribute(nameof(Total), "Achieved PP", Total); yield return new PerformanceDisplayAttribute(nameof(Difficulty), "Difficulty", Difficulty); } } diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs index 778d569cf2..4d1b9b5766 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty { } - protected override PerformanceAttributes CreatePerformanceAttributes(ScoreInfo score, DifficultyAttributes attributes) + protected override IPerformanceAttributes CreatePerformanceAttributes(ScoreInfo score, DifficultyAttributes attributes) { var maniaAttributes = (ManiaDifficultyAttributes)attributes; diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceAttributes.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceAttributes.cs index 0aeaf7669f..aefe416d87 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceAttributes.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceAttributes.cs @@ -7,8 +7,14 @@ using osu.Game.Rulesets.Difficulty; namespace osu.Game.Rulesets.Osu.Difficulty { - public class OsuPerformanceAttributes : PerformanceAttributes + public struct OsuPerformanceAttributes : IPerformanceAttributes { + /// + /// Calculated score performance points. + /// + [JsonProperty("pp")] + public double Total { get; set; } + [JsonProperty("aim")] public double Aim { get; set; } @@ -24,11 +30,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty [JsonProperty("effective_miss_count")] public double EffectiveMissCount { get; set; } - public override IEnumerable GetAttributesForDisplay() + public IEnumerable GetAttributesForDisplay() { - foreach (var attribute in base.GetAttributesForDisplay()) - yield return attribute; - + yield return new PerformanceDisplayAttribute(nameof(Total), "Achieved PP", Total); yield return new PerformanceDisplayAttribute(nameof(Aim), "Aim", Aim); yield return new PerformanceDisplayAttribute(nameof(Speed), "Speed", Speed); yield return new PerformanceDisplayAttribute(nameof(Accuracy), "Accuracy", Accuracy); diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index ddabf866ff..8425f437cc 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty { } - protected override PerformanceAttributes CreatePerformanceAttributes(ScoreInfo score, DifficultyAttributes attributes) + protected override IPerformanceAttributes CreatePerformanceAttributes(ScoreInfo score, DifficultyAttributes attributes) { var osuAttributes = (OsuDifficultyAttributes)attributes; diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceAttributes.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceAttributes.cs index 7c74e43db1..496311a9aa 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceAttributes.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceAttributes.cs @@ -7,8 +7,14 @@ using osu.Game.Rulesets.Difficulty; namespace osu.Game.Rulesets.Taiko.Difficulty { - public class TaikoPerformanceAttributes : PerformanceAttributes + public struct TaikoPerformanceAttributes : IPerformanceAttributes { + /// + /// Calculated score performance points. + /// + [JsonProperty("pp")] + public double Total { get; set; } + [JsonProperty("difficulty")] public double Difficulty { get; set; } @@ -21,11 +27,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty [JsonProperty("estimated_unstable_rate")] public double? EstimatedUnstableRate { get; set; } - public override IEnumerable GetAttributesForDisplay() + public IEnumerable GetAttributesForDisplay() { - foreach (var attribute in base.GetAttributesForDisplay()) - yield return attribute; - + yield return new PerformanceDisplayAttribute(nameof(Total), "Achieved PP", Total); yield return new PerformanceDisplayAttribute(nameof(Difficulty), "Difficulty", Difficulty); yield return new PerformanceDisplayAttribute(nameof(Accuracy), "Accuracy", Accuracy); } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs index e42b015176..29eadf417f 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty { } - protected override PerformanceAttributes CreatePerformanceAttributes(ScoreInfo score, DifficultyAttributes attributes) + protected override IPerformanceAttributes CreatePerformanceAttributes(ScoreInfo score, DifficultyAttributes attributes) { var taikoAttributes = (TaikoDifficultyAttributes)attributes; diff --git a/osu.Game/Rulesets/Difficulty/PerformanceAttributes.cs b/osu.Game/Rulesets/Difficulty/PerformanceAttributes.cs index e8c4c71913..bf4e8bd247 100644 --- a/osu.Game/Rulesets/Difficulty/PerformanceAttributes.cs +++ b/osu.Game/Rulesets/Difficulty/PerformanceAttributes.cs @@ -6,7 +6,7 @@ using Newtonsoft.Json; namespace osu.Game.Rulesets.Difficulty { - public class PerformanceAttributes + public interface IPerformanceAttributes { /// /// Calculated score performance points. @@ -19,9 +19,6 @@ namespace osu.Game.Rulesets.Difficulty /// Some attributes may be omitted if they are not meant for display. /// /// - public virtual IEnumerable GetAttributesForDisplay() - { - yield return new PerformanceDisplayAttribute(nameof(Total), "Achieved PP", Total); - } + public IEnumerable GetAttributesForDisplay(); } } diff --git a/osu.Game/Rulesets/Difficulty/PerformanceBreakdown.cs b/osu.Game/Rulesets/Difficulty/PerformanceBreakdown.cs index 6e41855ca3..31b9909999 100644 --- a/osu.Game/Rulesets/Difficulty/PerformanceBreakdown.cs +++ b/osu.Game/Rulesets/Difficulty/PerformanceBreakdown.cs @@ -11,19 +11,19 @@ namespace osu.Game.Rulesets.Difficulty /// /// Actual gameplay performance. /// - public PerformanceAttributes Performance { get; set; } + public IPerformanceAttributes Performance { get; set; } /// /// Performance of a perfect play for comparison. /// - public PerformanceAttributes PerfectPerformance { get; set; } + public IPerformanceAttributes PerfectPerformance { get; set; } /// /// Create a new performance breakdown. /// /// Actual gameplay performance. /// Performance of a perfect play for comparison. - public PerformanceBreakdown(PerformanceAttributes performance, PerformanceAttributes perfectPerformance) + public PerformanceBreakdown(IPerformanceAttributes performance, IPerformanceAttributes perfectPerformance) { Performance = performance; PerfectPerformance = perfectPerformance; diff --git a/osu.Game/Rulesets/Difficulty/PerformanceBreakdownCalculator.cs b/osu.Game/Rulesets/Difficulty/PerformanceBreakdownCalculator.cs index 946d83b14b..994d8cc52c 100644 --- a/osu.Game/Rulesets/Difficulty/PerformanceBreakdownCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/PerformanceBreakdownCalculator.cs @@ -41,18 +41,18 @@ namespace osu.Game.Rulesets.Difficulty cancellationToken.ThrowIfCancellationRequested(); - PerformanceAttributes[] performanceArray = await Task.WhenAll( + IPerformanceAttributes[] performanceArray = await Task.WhenAll( // compute actual performance performanceCalculator.CalculateAsync(score, attributes.Value.Attributes, cancellationToken), // compute performance for perfect play getPerfectPerformance(score, cancellationToken) ).ConfigureAwait(false); - return new PerformanceBreakdown(performanceArray[0] ?? new PerformanceAttributes(), performanceArray[1] ?? new PerformanceAttributes()); + return new PerformanceBreakdown(performanceArray[0] ?? new EmptyPerformanceAttributes(), performanceArray[1] ?? new EmptyPerformanceAttributes()); } [ItemCanBeNull] - private Task getPerfectPerformance(ScoreInfo score, CancellationToken cancellationToken = default) + private Task getPerfectPerformance(ScoreInfo score, CancellationToken cancellationToken = default) { return Task.Run(async () => { @@ -117,5 +117,12 @@ namespace osu.Game.Rulesets.Difficulty yield return hitObject.Judgement.MaxResult; } + + public class EmptyPerformanceAttributes : IPerformanceAttributes + { + public double Total { get; set; } = 0; + + public IEnumerable GetAttributesForDisplay() => []; + } } } diff --git a/osu.Game/Rulesets/Difficulty/PerformanceCalculator.cs b/osu.Game/Rulesets/Difficulty/PerformanceCalculator.cs index 966da0ff12..ee7f02f9be 100644 --- a/osu.Game/Rulesets/Difficulty/PerformanceCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/PerformanceCalculator.cs @@ -17,20 +17,20 @@ namespace osu.Game.Rulesets.Difficulty Ruleset = ruleset; } - public Task CalculateAsync(ScoreInfo score, DifficultyAttributes attributes, CancellationToken cancellationToken) + public Task CalculateAsync(ScoreInfo score, DifficultyAttributes attributes, CancellationToken cancellationToken) => Task.Run(() => CreatePerformanceAttributes(score, attributes), cancellationToken); - public PerformanceAttributes Calculate(ScoreInfo score, DifficultyAttributes attributes) + public IPerformanceAttributes Calculate(ScoreInfo score, DifficultyAttributes attributes) => CreatePerformanceAttributes(score, attributes); - public PerformanceAttributes Calculate(ScoreInfo score, IWorkingBeatmap beatmap) + public IPerformanceAttributes Calculate(ScoreInfo score, IWorkingBeatmap beatmap) => Calculate(score, Ruleset.CreateDifficultyCalculator(beatmap).Calculate(score.Mods)); /// - /// Creates to describe a score's performance. + /// Creates to describe a score's performance. /// /// The score to create the attributes for. /// The difficulty attributes for the beatmap relating to the score. - protected abstract PerformanceAttributes CreatePerformanceAttributes(ScoreInfo score, DifficultyAttributes attributes); + protected abstract IPerformanceAttributes CreatePerformanceAttributes(ScoreInfo score, DifficultyAttributes attributes); } } diff --git a/osu.Game/Screens/Ranking/Statistics/PerformanceBreakdownChart.cs b/osu.Game/Screens/Ranking/Statistics/PerformanceBreakdownChart.cs index b5eed2d12a..cbbd6d016b 100644 --- a/osu.Game/Screens/Ranking/Statistics/PerformanceBreakdownChart.cs +++ b/osu.Game/Screens/Ranking/Statistics/PerformanceBreakdownChart.cs @@ -156,8 +156,8 @@ namespace osu.Game.Screens.Ranking.Statistics var perfectDisplayAttributes = breakdown.PerfectPerformance.GetAttributesForDisplay(); setTotalValues( - displayAttributes.First(a => a.PropertyName == nameof(PerformanceAttributes.Total)), - perfectDisplayAttributes.First(a => a.PropertyName == nameof(PerformanceAttributes.Total)) + displayAttributes.First(a => a.PropertyName == nameof(IPerformanceAttributes.Total)), + perfectDisplayAttributes.First(a => a.PropertyName == nameof(IPerformanceAttributes.Total)) ); var rowDimensions = new List(); @@ -165,7 +165,7 @@ namespace osu.Game.Screens.Ranking.Statistics foreach (PerformanceDisplayAttribute attr in displayAttributes) { - if (attr.PropertyName == nameof(PerformanceAttributes.Total)) continue; + if (attr.PropertyName == nameof(IPerformanceAttributes.Total)) continue; var row = createAttributeRow(attr, perfectDisplayAttributes.First(a => a.PropertyName == attr.PropertyName)); From e88dff0c8771e68ea59e34e610ca2bf27138233a Mon Sep 17 00:00:00 2001 From: minisbett <39670899+minisbett@users.noreply.github.com> Date: Sat, 16 Nov 2024 16:31:36 +0100 Subject: [PATCH 02/15] turn difficulty attributes into a struct --- .../EmptyFreeformDifficultyCalculator.cs | 4 +- .../PippidonDifficultyCalculator.cs | 4 +- .../EmptyScrollingDifficultyCalculator.cs | 4 +- .../PippidonDifficultyCalculator.cs | 4 +- .../Difficulty/CatchDifficultyAttributes.cs | 34 ++++++---- .../Difficulty/CatchDifficultyCalculator.cs | 2 +- .../Difficulty/CatchPerformanceCalculator.cs | 2 +- .../Difficulty/ManiaDifficultyAttributes.cs | 34 ++++++---- .../Difficulty/ManiaDifficultyCalculator.cs | 2 +- .../Difficulty/ManiaPerformanceCalculator.cs | 2 +- .../Difficulty/OsuDifficultyAttributes.cs | 65 +++++++++++-------- .../Difficulty/OsuDifficultyCalculator.cs | 2 +- .../Difficulty/OsuPerformanceCalculator.cs | 2 +- .../Difficulty/TaikoDifficultyAttributes.cs | 39 +++++++---- .../Difficulty/TaikoDifficultyCalculator.cs | 2 +- .../Difficulty/TaikoPerformanceCalculator.cs | 2 +- ...DifficultyAdjustmentModCombinationsTest.cs | 2 +- .../TestSceneTimedDifficultyCalculation.cs | 4 +- osu.Game/Beatmaps/StarDifficulty.cs | 8 +-- .../Difficulty/DifficultyCalculator.cs | 10 +-- ...Attributes.cs => IDifficultyAttributes.cs} | 37 ++--------- ...ttributes.cs => IPerformanceAttributes.cs} | 0 .../Difficulty/PerformanceCalculator.cs | 6 +- .../Difficulty/TimedDifficultyAttributes.cs | 6 +- .../Play/HUD/PerformancePointsCounter.cs | 2 +- 25 files changed, 147 insertions(+), 132 deletions(-) rename osu.Game/Rulesets/Difficulty/{DifficultyAttributes.cs => IDifficultyAttributes.cs} (65%) rename osu.Game/Rulesets/Difficulty/{PerformanceAttributes.cs => IPerformanceAttributes.cs} (100%) diff --git a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformDifficultyCalculator.cs b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformDifficultyCalculator.cs index 312d3d5e9a..2178941fd8 100644 --- a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformDifficultyCalculator.cs +++ b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformDifficultyCalculator.cs @@ -19,9 +19,9 @@ namespace osu.Game.Rulesets.EmptyFreeform { } - protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) + protected override IDifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) { - return new DifficultyAttributes(mods, 0); + return new IDifficultyAttributes(mods, 0); } protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty(); diff --git a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs index f6addab279..55cf4fb42d 100644 --- a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs +++ b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs @@ -19,9 +19,9 @@ namespace osu.Game.Rulesets.Pippidon { } - protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) + protected override IDifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) { - return new DifficultyAttributes(mods, 0); + return new IDifficultyAttributes(mods, 0); } protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty(); diff --git a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingDifficultyCalculator.cs b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingDifficultyCalculator.cs index a4dc1762d5..47215ce7d9 100644 --- a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingDifficultyCalculator.cs +++ b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingDifficultyCalculator.cs @@ -19,9 +19,9 @@ namespace osu.Game.Rulesets.EmptyScrolling { } - protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) + protected override IDifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) { - return new DifficultyAttributes(mods, 0); + return new IDifficultyAttributes(mods, 0); } protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty(); diff --git a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs index f6addab279..55cf4fb42d 100644 --- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs +++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs @@ -19,9 +19,9 @@ namespace osu.Game.Rulesets.Pippidon { } - protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) + protected override IDifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) { - return new DifficultyAttributes(mods, 0); + return new IDifficultyAttributes(mods, 0); } protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty(); diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs index 5c64643fd4..3ff1c0dd01 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs @@ -1,15 +1,28 @@ // 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 Newtonsoft.Json; using osu.Game.Beatmaps; using osu.Game.Rulesets.Difficulty; +using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Catch.Difficulty { - public class CatchDifficultyAttributes : DifficultyAttributes + public struct CatchDifficultyAttributes : IDifficultyAttributes { + public CatchDifficultyAttributes() { } + + /// + public Mod[] Mods { get; set; } = Array.Empty(); + + /// + public double StarRating { get; set; } + + /// + public int MaxCombo { get; set; } + /// /// The perceived approach rate inclusive of rate-adjusting mods (DT/HT/etc). /// @@ -19,22 +32,19 @@ namespace osu.Game.Rulesets.Catch.Difficulty [JsonProperty("approach_rate")] public double ApproachRate { get; set; } - public override IEnumerable<(int attributeId, object value)> ToDatabaseAttributes() + public IEnumerable<(int attributeId, object value)> ToDatabaseAttributes() { - foreach (var v in base.ToDatabaseAttributes()) - yield return v; - + yield return (IDifficultyAttributes.ATTRIB_ID_MAX_COMBO, MaxCombo); // Todo: osu!catch should not output star rating in the 'aim' attribute. - yield return (ATTRIB_ID_AIM, StarRating); - yield return (ATTRIB_ID_APPROACH_RATE, ApproachRate); + yield return (IDifficultyAttributes.ATTRIB_ID_AIM, StarRating); + yield return (IDifficultyAttributes.ATTRIB_ID_APPROACH_RATE, ApproachRate); } - public override void FromDatabaseAttributes(IReadOnlyDictionary values, IBeatmapOnlineInfo onlineInfo) + public void FromDatabaseAttributes(IReadOnlyDictionary values, IBeatmapOnlineInfo onlineInfo) { - base.FromDatabaseAttributes(values, onlineInfo); - - StarRating = values[ATTRIB_ID_AIM]; - ApproachRate = values[ATTRIB_ID_APPROACH_RATE]; + MaxCombo = (int)values[IDifficultyAttributes.ATTRIB_ID_MAX_COMBO]; + StarRating = values[IDifficultyAttributes.ATTRIB_ID_AIM]; + ApproachRate = values[IDifficultyAttributes.ATTRIB_ID_APPROACH_RATE]; } } } diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 7d21409ee8..4637fd3c0a 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty { } - protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) + protected override IDifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) { if (beatmap.HitObjects.Count == 0) return new CatchDifficultyAttributes { Mods = mods }; diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs index efca1e5e77..a8a0e3c444 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty { } - protected override IPerformanceAttributes CreatePerformanceAttributes(ScoreInfo score, DifficultyAttributes attributes) + protected override IPerformanceAttributes CreatePerformanceAttributes(ScoreInfo score, IDifficultyAttributes attributes) { var catchAttributes = (CatchDifficultyAttributes)attributes; diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs index db60e757e1..8244334748 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs @@ -1,15 +1,28 @@ // 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 Newtonsoft.Json; using osu.Game.Beatmaps; using osu.Game.Rulesets.Difficulty; +using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Mania.Difficulty { - public class ManiaDifficultyAttributes : DifficultyAttributes + public struct ManiaDifficultyAttributes : IDifficultyAttributes { + public ManiaDifficultyAttributes() { } + + /// + public Mod[] Mods { get; set; } = Array.Empty(); + + /// + public double StarRating { get; set; } + + /// + public int MaxCombo { get; set; } + /// /// The hit window for a GREAT hit inclusive of rate-adjusting mods (DT/HT/etc). /// @@ -19,21 +32,18 @@ namespace osu.Game.Rulesets.Mania.Difficulty [JsonProperty("great_hit_window")] public double GreatHitWindow { get; set; } - public override IEnumerable<(int attributeId, object value)> ToDatabaseAttributes() + public IEnumerable<(int attributeId, object value)> ToDatabaseAttributes() { - foreach (var v in base.ToDatabaseAttributes()) - yield return v; - - yield return (ATTRIB_ID_DIFFICULTY, StarRating); - yield return (ATTRIB_ID_GREAT_HIT_WINDOW, GreatHitWindow); + yield return (IDifficultyAttributes.ATTRIB_ID_MAX_COMBO, MaxCombo); + yield return (IDifficultyAttributes.ATTRIB_ID_DIFFICULTY, StarRating); + yield return (IDifficultyAttributes.ATTRIB_ID_GREAT_HIT_WINDOW, GreatHitWindow); } - public override void FromDatabaseAttributes(IReadOnlyDictionary values, IBeatmapOnlineInfo onlineInfo) + public void FromDatabaseAttributes(IReadOnlyDictionary values, IBeatmapOnlineInfo onlineInfo) { - base.FromDatabaseAttributes(values, onlineInfo); - - StarRating = values[ATTRIB_ID_DIFFICULTY]; - GreatHitWindow = values[ATTRIB_ID_GREAT_HIT_WINDOW]; + MaxCombo = (int)values[IDifficultyAttributes.ATTRIB_ID_MAX_COMBO]; + StarRating = values[IDifficultyAttributes.ATTRIB_ID_DIFFICULTY]; + GreatHitWindow = values[IDifficultyAttributes.ATTRIB_ID_GREAT_HIT_WINDOW]; } } } diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs index ff9aa4aa7b..ae49c495c5 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty originalOverallDifficulty = beatmap.BeatmapInfo.Difficulty.OverallDifficulty; } - protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) + protected override IDifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) { if (beatmap.HitObjects.Count == 0) return new ManiaDifficultyAttributes { Mods = mods }; diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs index 4d1b9b5766..b01bcc2d04 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty { } - protected override IPerformanceAttributes CreatePerformanceAttributes(ScoreInfo score, DifficultyAttributes attributes) + protected override IPerformanceAttributes CreatePerformanceAttributes(ScoreInfo score, IDifficultyAttributes attributes) { var maniaAttributes = (ManiaDifficultyAttributes)attributes; diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs index a3c0209a08..315b7b5fbc 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.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; using System.Collections.Generic; using System.Linq; using JetBrains.Annotations; @@ -11,8 +12,19 @@ using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Osu.Difficulty { - public class OsuDifficultyAttributes : DifficultyAttributes + public struct OsuDifficultyAttributes : IDifficultyAttributes { + public OsuDifficultyAttributes() { } + + /// + public Mod[] Mods { get; set; } = Array.Empty(); + + /// + public double StarRating { get; set; } + + /// + public int MaxCombo { get; set; } + /// /// The difficulty corresponding to the aim skill. /// @@ -90,41 +102,38 @@ namespace osu.Game.Rulesets.Osu.Difficulty /// public int SpinnerCount { get; set; } - public override IEnumerable<(int attributeId, object value)> ToDatabaseAttributes() + public IEnumerable<(int attributeId, object value)> ToDatabaseAttributes() { - foreach (var v in base.ToDatabaseAttributes()) - yield return v; - - yield return (ATTRIB_ID_AIM, AimDifficulty); - yield return (ATTRIB_ID_SPEED, SpeedDifficulty); - yield return (ATTRIB_ID_OVERALL_DIFFICULTY, OverallDifficulty); - yield return (ATTRIB_ID_APPROACH_RATE, ApproachRate); - yield return (ATTRIB_ID_DIFFICULTY, StarRating); + yield return (IDifficultyAttributes.ATTRIB_ID_MAX_COMBO, MaxCombo); + yield return (IDifficultyAttributes.ATTRIB_ID_AIM, AimDifficulty); + yield return (IDifficultyAttributes.ATTRIB_ID_SPEED, SpeedDifficulty); + yield return (IDifficultyAttributes.ATTRIB_ID_OVERALL_DIFFICULTY, OverallDifficulty); + yield return (IDifficultyAttributes.ATTRIB_ID_APPROACH_RATE, ApproachRate); + yield return (IDifficultyAttributes.ATTRIB_ID_DIFFICULTY, StarRating); if (ShouldSerializeFlashlightDifficulty()) - yield return (ATTRIB_ID_FLASHLIGHT, FlashlightDifficulty); + yield return (IDifficultyAttributes.ATTRIB_ID_FLASHLIGHT, FlashlightDifficulty); - yield return (ATTRIB_ID_SLIDER_FACTOR, SliderFactor); + yield return (IDifficultyAttributes.ATTRIB_ID_SLIDER_FACTOR, SliderFactor); - yield return (ATTRIB_ID_AIM_DIFFICULT_STRAIN_COUNT, AimDifficultStrainCount); - yield return (ATTRIB_ID_SPEED_DIFFICULT_STRAIN_COUNT, SpeedDifficultStrainCount); - yield return (ATTRIB_ID_SPEED_NOTE_COUNT, SpeedNoteCount); + yield return (IDifficultyAttributes.ATTRIB_ID_AIM_DIFFICULT_STRAIN_COUNT, AimDifficultStrainCount); + yield return (IDifficultyAttributes.ATTRIB_ID_SPEED_DIFFICULT_STRAIN_COUNT, SpeedDifficultStrainCount); + yield return (IDifficultyAttributes.ATTRIB_ID_SPEED_NOTE_COUNT, SpeedNoteCount); } - public override void FromDatabaseAttributes(IReadOnlyDictionary values, IBeatmapOnlineInfo onlineInfo) + public void FromDatabaseAttributes(IReadOnlyDictionary values, IBeatmapOnlineInfo onlineInfo) { - base.FromDatabaseAttributes(values, onlineInfo); - - AimDifficulty = values[ATTRIB_ID_AIM]; - SpeedDifficulty = values[ATTRIB_ID_SPEED]; - OverallDifficulty = values[ATTRIB_ID_OVERALL_DIFFICULTY]; - ApproachRate = values[ATTRIB_ID_APPROACH_RATE]; - StarRating = values[ATTRIB_ID_DIFFICULTY]; - FlashlightDifficulty = values.GetValueOrDefault(ATTRIB_ID_FLASHLIGHT); - SliderFactor = values[ATTRIB_ID_SLIDER_FACTOR]; - AimDifficultStrainCount = values[ATTRIB_ID_AIM_DIFFICULT_STRAIN_COUNT]; - SpeedDifficultStrainCount = values[ATTRIB_ID_SPEED_DIFFICULT_STRAIN_COUNT]; - SpeedNoteCount = values[ATTRIB_ID_SPEED_NOTE_COUNT]; + MaxCombo = (int)values[IDifficultyAttributes.ATTRIB_ID_MAX_COMBO]; + AimDifficulty = values[IDifficultyAttributes.ATTRIB_ID_AIM]; + SpeedDifficulty = values[IDifficultyAttributes.ATTRIB_ID_SPEED]; + OverallDifficulty = values[IDifficultyAttributes.ATTRIB_ID_OVERALL_DIFFICULTY]; + ApproachRate = values[IDifficultyAttributes.ATTRIB_ID_APPROACH_RATE]; + StarRating = values[IDifficultyAttributes.ATTRIB_ID_DIFFICULTY]; + FlashlightDifficulty = values.GetValueOrDefault(IDifficultyAttributes.ATTRIB_ID_FLASHLIGHT); + SliderFactor = values[IDifficultyAttributes.ATTRIB_ID_SLIDER_FACTOR]; + AimDifficultStrainCount = values[IDifficultyAttributes.ATTRIB_ID_AIM_DIFFICULT_STRAIN_COUNT]; + SpeedDifficultStrainCount = values[IDifficultyAttributes.ATTRIB_ID_SPEED_DIFFICULT_STRAIN_COUNT]; + SpeedNoteCount = values[IDifficultyAttributes.ATTRIB_ID_SPEED_NOTE_COUNT]; DrainRate = onlineInfo.DrainRate; HitCircleCount = onlineInfo.CircleCount; SliderCount = onlineInfo.SliderCount; diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index acf01b2a83..97603bb16a 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty { } - protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) + protected override IDifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) { if (beatmap.HitObjects.Count == 0) return new OsuDifficultyAttributes { Mods = mods }; diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 8425f437cc..b79a8feb96 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty { } - protected override IPerformanceAttributes CreatePerformanceAttributes(ScoreInfo score, DifficultyAttributes attributes) + protected override IPerformanceAttributes CreatePerformanceAttributes(ScoreInfo score, IDifficultyAttributes attributes) { var osuAttributes = (OsuDifficultyAttributes)attributes; diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs index 451aed183d..337e1c1488 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs @@ -1,15 +1,29 @@ // 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 Newtonsoft.Json; using osu.Game.Beatmaps; using osu.Game.Rulesets.Difficulty; +using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Taiko.Difficulty { - public class TaikoDifficultyAttributes : DifficultyAttributes + public struct TaikoDifficultyAttributes : IDifficultyAttributes { + public TaikoDifficultyAttributes() { } + + /// + public Mod[] Mods { get; set; } = Array.Empty(); + + /// + public double StarRating { get; set; } + + /// + [JsonProperty("max_combo", Order = -2)] + public int MaxCombo { get; set; } + /// /// The difficulty corresponding to the stamina skill. /// @@ -52,23 +66,20 @@ namespace osu.Game.Rulesets.Taiko.Difficulty [JsonProperty("ok_hit_window")] public double OkHitWindow { get; set; } - public override IEnumerable<(int attributeId, object value)> ToDatabaseAttributes() + public IEnumerable<(int attributeId, object value)> ToDatabaseAttributes() { - foreach (var v in base.ToDatabaseAttributes()) - yield return v; - - yield return (ATTRIB_ID_DIFFICULTY, StarRating); - yield return (ATTRIB_ID_GREAT_HIT_WINDOW, GreatHitWindow); - yield return (ATTRIB_ID_OK_HIT_WINDOW, OkHitWindow); + yield return (IDifficultyAttributes.ATTRIB_ID_MAX_COMBO, MaxCombo); + yield return (IDifficultyAttributes.ATTRIB_ID_DIFFICULTY, StarRating); + yield return (IDifficultyAttributes.ATTRIB_ID_GREAT_HIT_WINDOW, GreatHitWindow); + yield return (IDifficultyAttributes.ATTRIB_ID_OK_HIT_WINDOW, OkHitWindow); } - public override void FromDatabaseAttributes(IReadOnlyDictionary values, IBeatmapOnlineInfo onlineInfo) + public void FromDatabaseAttributes(IReadOnlyDictionary values, IBeatmapOnlineInfo onlineInfo) { - base.FromDatabaseAttributes(values, onlineInfo); - - StarRating = values[ATTRIB_ID_DIFFICULTY]; - GreatHitWindow = values[ATTRIB_ID_GREAT_HIT_WINDOW]; - OkHitWindow = values[ATTRIB_ID_OK_HIT_WINDOW]; + MaxCombo = (int)values[IDifficultyAttributes.ATTRIB_ID_MAX_COMBO]; + StarRating = values[IDifficultyAttributes.ATTRIB_ID_DIFFICULTY]; + GreatHitWindow = values[IDifficultyAttributes.ATTRIB_ID_GREAT_HIT_WINDOW]; + OkHitWindow = values[IDifficultyAttributes.ATTRIB_ID_OK_HIT_WINDOW]; } } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index 18223e74fa..4b86620b39 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -71,7 +71,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty return difficultyHitObjects; } - protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) + protected override IDifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) { if (beatmap.HitObjects.Count == 0) return new TaikoDifficultyAttributes { Mods = mods }; diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs index 29eadf417f..7373993629 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty { } - protected override IPerformanceAttributes CreatePerformanceAttributes(ScoreInfo score, DifficultyAttributes attributes) + protected override IPerformanceAttributes CreatePerformanceAttributes(ScoreInfo score, IDifficultyAttributes attributes) { var taikoAttributes = (TaikoDifficultyAttributes)attributes; diff --git a/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs b/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs index 6b1b883ce7..7c63bbd9a3 100644 --- a/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs +++ b/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs @@ -222,7 +222,7 @@ namespace osu.Game.Tests.NonVisual protected override Mod[] DifficultyAdjustmentMods { get; } - protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) + protected override IDifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) { throw new NotImplementedException(); } diff --git a/osu.Game.Tests/NonVisual/TestSceneTimedDifficultyCalculation.cs b/osu.Game.Tests/NonVisual/TestSceneTimedDifficultyCalculation.cs index f860cd097a..5db37dd810 100644 --- a/osu.Game.Tests/NonVisual/TestSceneTimedDifficultyCalculation.cs +++ b/osu.Game.Tests/NonVisual/TestSceneTimedDifficultyCalculation.cs @@ -172,7 +172,7 @@ namespace osu.Game.Tests.NonVisual { } - protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) + protected override IDifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) => new TestDifficultyAttributes { Objects = beatmap.HitObjects.ToArray() }; protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) @@ -208,7 +208,7 @@ namespace osu.Game.Tests.NonVisual } } - private class TestDifficultyAttributes : DifficultyAttributes + private class TestDifficultyAttributes : IDifficultyAttributes { public HitObject[] Objects = Array.Empty(); } diff --git a/osu.Game/Beatmaps/StarDifficulty.cs b/osu.Game/Beatmaps/StarDifficulty.cs index 6aac275a6a..e0a7e724a3 100644 --- a/osu.Game/Beatmaps/StarDifficulty.cs +++ b/osu.Game/Beatmaps/StarDifficulty.cs @@ -26,13 +26,13 @@ namespace osu.Game.Beatmaps /// Might not be available if the star difficulty is associated with a beatmap that's not locally available. /// [CanBeNull] - public readonly DifficultyAttributes Attributes; + public readonly IDifficultyAttributes Attributes; /// - /// Creates a structure based on computed + /// Creates a structure based on computed /// by a . /// - public StarDifficulty([NotNull] DifficultyAttributes attributes) + public StarDifficulty([NotNull] IDifficultyAttributes attributes) { Stars = double.IsFinite(attributes.StarRating) ? attributes.StarRating : 0; MaxCombo = attributes.MaxCombo; @@ -42,7 +42,7 @@ namespace osu.Game.Beatmaps /// /// Creates a structure with a pre-populated star difficulty and max combo - /// in scenarios where computing is not feasible (i.e. when working with online sources). + /// in scenarios where computing is not feasible (i.e. when working with online sources). /// public StarDifficulty(double starDifficulty, int maxCombo) { diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index 63b27243d0..39340776f0 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Difficulty /// /// The cancellation token. /// A structure describing the difficulty of the beatmap. - public DifficultyAttributes Calculate(CancellationToken cancellationToken = default) + public IDifficultyAttributes Calculate(CancellationToken cancellationToken = default) => Calculate(Array.Empty(), cancellationToken); /// @@ -60,7 +60,7 @@ namespace osu.Game.Rulesets.Difficulty /// The mods that should be applied to the beatmap. /// The cancellation token. /// A structure describing the difficulty of the beatmap. - public DifficultyAttributes Calculate([NotNull] IEnumerable mods, CancellationToken cancellationToken = default) + public IDifficultyAttributes Calculate([NotNull] IEnumerable mods, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); preProcess(mods, cancellationToken); @@ -140,7 +140,7 @@ namespace osu.Game.Rulesets.Difficulty /// This can only be used to compute difficulties for legacy mod combinations. /// /// A collection of structures describing the difficulty of the beatmap for each mod combination. - public IEnumerable CalculateAllLegacyCombinations(CancellationToken cancellationToken = default) + public IEnumerable CalculateAllLegacyCombinations(CancellationToken cancellationToken = default) { var rulesetInstance = ruleset.CreateInstance(); @@ -263,14 +263,14 @@ namespace osu.Game.Rulesets.Difficulty protected virtual Mod[] DifficultyAdjustmentMods => Array.Empty(); /// - /// Creates to describe beatmap's calculated difficulty. + /// Creates to describe beatmap's calculated difficulty. /// /// The whose difficulty was calculated. /// This may differ from in the case of timed calculation. /// The s that difficulty was calculated with. /// The skills which processed the beatmap. /// The rate at which the gameplay clock is run at. - protected abstract DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate); + protected abstract IDifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate); /// /// Enumerates s to be processed from s in the . diff --git a/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs b/osu.Game/Rulesets/Difficulty/IDifficultyAttributes.cs similarity index 65% rename from osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs rename to osu.Game/Rulesets/Difficulty/IDifficultyAttributes.cs index ae4239c148..b951b5fb11 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs +++ b/osu.Game/Rulesets/Difficulty/IDifficultyAttributes.cs @@ -1,7 +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; using System.Collections.Generic; using Newtonsoft.Json; using osu.Game.Beatmaps; @@ -13,7 +12,7 @@ namespace osu.Game.Rulesets.Difficulty /// Describes the difficulty of a beatmap, as output by a . /// [JsonObject(MemberSerialization.OptIn)] - public class DifficultyAttributes + public interface IDifficultyAttributes { protected const int ATTRIB_ID_AIM = 1; protected const int ATTRIB_ID_SPEED = 3; @@ -33,7 +32,7 @@ namespace osu.Game.Rulesets.Difficulty /// /// The mods which were applied to the beatmap. /// - public Mod[] Mods { get; set; } = Array.Empty(); + public Mod[] Mods { get; set; } /// /// The combined star rating of all skills. @@ -48,42 +47,18 @@ namespace osu.Game.Rulesets.Difficulty public int MaxCombo { get; set; } /// - /// Creates new . - /// - public DifficultyAttributes() - { - } - - /// - /// Creates new . - /// - /// The mods which were applied to the beatmap. - /// The combined star rating of all skills. - public DifficultyAttributes(Mod[] mods, double starRating) - { - Mods = mods; - StarRating = starRating; - } - - /// - /// Converts this to osu-web compatible database attribute mappings. + /// Converts this to osu-web compatible database attribute mappings. /// /// /// See: osu_difficulty_attribs table. /// - public virtual IEnumerable<(int attributeId, object value)> ToDatabaseAttributes() - { - yield return (ATTRIB_ID_MAX_COMBO, MaxCombo); - } + public IEnumerable<(int attributeId, object value)> ToDatabaseAttributes(); /// - /// Reads osu-web database attribute mappings into this object. + /// Reads osu-web database attribute mappings into this object. /// /// The attribute mappings. /// The where more information about the beatmap may be extracted from (such as AR/CS/OD/etc). - public virtual void FromDatabaseAttributes(IReadOnlyDictionary values, IBeatmapOnlineInfo onlineInfo) - { - MaxCombo = (int)values[ATTRIB_ID_MAX_COMBO]; - } + public void FromDatabaseAttributes(IReadOnlyDictionary values, IBeatmapOnlineInfo onlineInfo); } } diff --git a/osu.Game/Rulesets/Difficulty/PerformanceAttributes.cs b/osu.Game/Rulesets/Difficulty/IPerformanceAttributes.cs similarity index 100% rename from osu.Game/Rulesets/Difficulty/PerformanceAttributes.cs rename to osu.Game/Rulesets/Difficulty/IPerformanceAttributes.cs diff --git a/osu.Game/Rulesets/Difficulty/PerformanceCalculator.cs b/osu.Game/Rulesets/Difficulty/PerformanceCalculator.cs index ee7f02f9be..b7ff4b9812 100644 --- a/osu.Game/Rulesets/Difficulty/PerformanceCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/PerformanceCalculator.cs @@ -17,10 +17,10 @@ namespace osu.Game.Rulesets.Difficulty Ruleset = ruleset; } - public Task CalculateAsync(ScoreInfo score, DifficultyAttributes attributes, CancellationToken cancellationToken) + public Task CalculateAsync(ScoreInfo score, IDifficultyAttributes attributes, CancellationToken cancellationToken) => Task.Run(() => CreatePerformanceAttributes(score, attributes), cancellationToken); - public IPerformanceAttributes Calculate(ScoreInfo score, DifficultyAttributes attributes) + public IPerformanceAttributes Calculate(ScoreInfo score, IDifficultyAttributes attributes) => CreatePerformanceAttributes(score, attributes); public IPerformanceAttributes Calculate(ScoreInfo score, IWorkingBeatmap beatmap) @@ -31,6 +31,6 @@ namespace osu.Game.Rulesets.Difficulty /// /// The score to create the attributes for. /// The difficulty attributes for the beatmap relating to the score. - protected abstract IPerformanceAttributes CreatePerformanceAttributes(ScoreInfo score, DifficultyAttributes attributes); + protected abstract IPerformanceAttributes CreatePerformanceAttributes(ScoreInfo score, IDifficultyAttributes attributes); } } diff --git a/osu.Game/Rulesets/Difficulty/TimedDifficultyAttributes.cs b/osu.Game/Rulesets/Difficulty/TimedDifficultyAttributes.cs index a07827d50b..b6a7aa26da 100644 --- a/osu.Game/Rulesets/Difficulty/TimedDifficultyAttributes.cs +++ b/osu.Game/Rulesets/Difficulty/TimedDifficultyAttributes.cs @@ -8,7 +8,7 @@ using System; namespace osu.Game.Rulesets.Difficulty { /// - /// Wraps a object and adds a time value for which the attribute is valid. + /// Wraps a object and adds a time value for which the attribute is valid. /// Output by DifficultyCalculator.CalculateTimed methods. /// public class TimedDifficultyAttributes : IComparable @@ -21,14 +21,14 @@ namespace osu.Game.Rulesets.Difficulty /// /// The attributes. /// - public readonly DifficultyAttributes Attributes; + public readonly IDifficultyAttributes Attributes; /// /// Creates new . /// /// The non-clock-adjusted time value at which the attributes take effect. /// The attributes. - public TimedDifficultyAttributes(double time, DifficultyAttributes attributes) + public TimedDifficultyAttributes(double time, IDifficultyAttributes attributes) { Time = time; Attributes = attributes; diff --git a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs index 25c1387220..a48124fb68 100644 --- a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs @@ -106,7 +106,7 @@ namespace osu.Game.Screens.Play.HUD } [CanBeNull] - private DifficultyAttributes getAttributeAtTime(JudgementResult judgement) + private IDifficultyAttributes getAttributeAtTime(JudgementResult judgement) { if (timedAttributes == null || timedAttributes.Count == 0) return null; From 6e2f8cf4c2130f3723b3ca2ba80bfd845db97fce Mon Sep 17 00:00:00 2001 From: minisbett <39670899+minisbett@users.noreply.github.com> Date: Sun, 17 Nov 2024 18:14:25 +0100 Subject: [PATCH 03/15] Remove mods from difficulty attributes --- .../Difficulty/CatchDifficultyAttributes.cs | 5 ----- .../Difficulty/CatchDifficultyCalculator.cs | 3 +-- .../Difficulty/ManiaDifficultyAttributes.cs | 5 ----- .../Difficulty/ManiaDifficultyCalculator.cs | 3 +-- .../Difficulty/OsuDifficultyAttributes.cs | 13 ++++--------- .../Difficulty/OsuDifficultyCalculator.cs | 7 +++---- .../Difficulty/TaikoDifficultyAttributes.cs | 5 ----- .../Difficulty/TaikoDifficultyCalculator.cs | 3 +-- .../Rulesets/Difficulty/IDifficultyAttributes.cs | 6 ------ 9 files changed, 10 insertions(+), 40 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs index 3ff1c0dd01..af25ef3b5b 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs @@ -1,12 +1,10 @@ // 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 Newtonsoft.Json; using osu.Game.Beatmaps; using osu.Game.Rulesets.Difficulty; -using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Catch.Difficulty { @@ -14,9 +12,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty { public CatchDifficultyAttributes() { } - /// - public Mod[] Mods { get; set; } = Array.Empty(); - /// public double StarRating { get; set; } diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 4637fd3c0a..8978d764ef 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty protected override IDifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) { if (beatmap.HitObjects.Count == 0) - return new CatchDifficultyAttributes { Mods = mods }; + return new CatchDifficultyAttributes(); // this is the same as osu!, so there's potential to share the implementation... maybe double preempt = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.ApproachRate, 1800, 1200, 450) / clockRate; @@ -41,7 +41,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty CatchDifficultyAttributes attributes = new CatchDifficultyAttributes { StarRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier, - Mods = mods, ApproachRate = preempt > 1200.0 ? -(preempt - 1800.0) / 120.0 : -(preempt - 1200.0) / 150.0 + 5.0, MaxCombo = beatmap.GetMaxCombo(), }; diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs index 8244334748..c5c280b4dd 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs @@ -1,12 +1,10 @@ // 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 Newtonsoft.Json; using osu.Game.Beatmaps; using osu.Game.Rulesets.Difficulty; -using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Mania.Difficulty { @@ -14,9 +12,6 @@ namespace osu.Game.Rulesets.Mania.Difficulty { public ManiaDifficultyAttributes() { } - /// - public Mod[] Mods { get; set; } = Array.Empty(); - /// public double StarRating { get; set; } diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs index ae49c495c5..7f3cb867a4 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty protected override IDifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) { if (beatmap.HitObjects.Count == 0) - return new ManiaDifficultyAttributes { Mods = mods }; + return new ManiaDifficultyAttributes(); HitWindows hitWindows = new ManiaHitWindows(); hitWindows.SetDifficulty(beatmap.Difficulty.OverallDifficulty); @@ -49,7 +49,6 @@ namespace osu.Game.Rulesets.Mania.Difficulty ManiaDifficultyAttributes attributes = new ManiaDifficultyAttributes { StarRating = skills[0].DifficultyValue() * difficulty_multiplier, - Mods = mods, // In osu-stable mania, rate-adjustment mods don't affect the hit window. // This is done the way it is to introduce fractional differences in order to match osu-stable for the time being. GreatHitWindow = Math.Ceiling((int)(getHitWindow300(mods) * clockRate) / clockRate), diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs index 315b7b5fbc..ab4fc510c4 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs @@ -1,9 +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; using System.Collections.Generic; -using System.Linq; using JetBrains.Annotations; using Newtonsoft.Json; using osu.Game.Beatmaps; @@ -16,9 +14,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty { public OsuDifficultyAttributes() { } - /// - public Mod[] Mods { get; set; } = Array.Empty(); - /// public double StarRating { get; set; } @@ -45,10 +40,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty public double SpeedNoteCount { get; set; } /// - /// The difficulty corresponding to the flashlight skill. + /// The difficulty corresponding to the flashlight skill. A null value indicates the non-existence of . /// [JsonProperty("flashlight_difficulty")] - public double FlashlightDifficulty { get; set; } + public double? FlashlightDifficulty { get; set; } /// /// Describes how much of is contributed to by hitcircles or sliders. @@ -112,7 +107,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty yield return (IDifficultyAttributes.ATTRIB_ID_DIFFICULTY, StarRating); if (ShouldSerializeFlashlightDifficulty()) - yield return (IDifficultyAttributes.ATTRIB_ID_FLASHLIGHT, FlashlightDifficulty); + yield return (IDifficultyAttributes.ATTRIB_ID_FLASHLIGHT, FlashlightDifficulty!); yield return (IDifficultyAttributes.ATTRIB_ID_SLIDER_FACTOR, SliderFactor); @@ -147,7 +142,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty // unless the fields are also renamed. [UsedImplicitly] - public bool ShouldSerializeFlashlightDifficulty() => Mods.Any(m => m is ModFlashlight); + public bool ShouldSerializeFlashlightDifficulty() => FlashlightDifficulty is not null; #endregion } diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 97603bb16a..41acd23a78 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -34,14 +34,14 @@ namespace osu.Game.Rulesets.Osu.Difficulty protected override IDifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) { if (beatmap.HitObjects.Count == 0) - return new OsuDifficultyAttributes { Mods = mods }; + return new OsuDifficultyAttributes(); double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier; double aimRatingNoSliders = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier; double speedRating = Math.Sqrt(skills[2].DifficultyValue()) * difficulty_multiplier; double speedNotes = ((Speed)skills[2]).RelevantNoteCount(); - double flashlightRating = 0.0; + double flashlightRating = 0; if (mods.Any(h => h is OsuModFlashlight)) flashlightRating = Math.Sqrt(skills[3].DifficultyValue()) * difficulty_multiplier; @@ -97,11 +97,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty OsuDifficultyAttributes attributes = new OsuDifficultyAttributes { StarRating = starRating, - Mods = mods, AimDifficulty = aimRating, SpeedDifficulty = speedRating, SpeedNoteCount = speedNotes, - FlashlightDifficulty = flashlightRating, + FlashlightDifficulty = mods.Any(x => x is OsuModFlashlight) ? flashlightRating : null, SliderFactor = sliderFactor, AimDifficultStrainCount = aimDifficultyStrainCount, SpeedDifficultStrainCount = speedDifficultyStrainCount, diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs index 337e1c1488..8fa17e4c93 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs @@ -1,12 +1,10 @@ // 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 Newtonsoft.Json; using osu.Game.Beatmaps; using osu.Game.Rulesets.Difficulty; -using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Taiko.Difficulty { @@ -14,9 +12,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty { public TaikoDifficultyAttributes() { } - /// - public Mod[] Mods { get; set; } = Array.Empty(); - /// public double StarRating { get; set; } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index 4b86620b39..5038bb8357 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -74,7 +74,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty protected override IDifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) { if (beatmap.HitObjects.Count == 0) - return new TaikoDifficultyAttributes { Mods = mods }; + return new TaikoDifficultyAttributes(); Colour colour = (Colour)skills.First(x => x is Colour); Rhythm rhythm = (Rhythm)skills.First(x => x is Rhythm); @@ -93,7 +93,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty TaikoDifficultyAttributes attributes = new TaikoDifficultyAttributes { StarRating = starRating, - Mods = mods, StaminaDifficulty = staminaRating, RhythmDifficulty = rhythmRating, ColourDifficulty = colourRating, diff --git a/osu.Game/Rulesets/Difficulty/IDifficultyAttributes.cs b/osu.Game/Rulesets/Difficulty/IDifficultyAttributes.cs index b951b5fb11..56a22d2043 100644 --- a/osu.Game/Rulesets/Difficulty/IDifficultyAttributes.cs +++ b/osu.Game/Rulesets/Difficulty/IDifficultyAttributes.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using Newtonsoft.Json; using osu.Game.Beatmaps; -using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Difficulty { @@ -29,11 +28,6 @@ namespace osu.Game.Rulesets.Difficulty protected const int ATTRIB_ID_AIM_DIFFICULT_STRAIN_COUNT = 25; protected const int ATTRIB_ID_OK_HIT_WINDOW = 27; - /// - /// The mods which were applied to the beatmap. - /// - public Mod[] Mods { get; set; } - /// /// The combined star rating of all skills. /// From ae25f27785e400dca8d412097d5856b70fe345df Mon Sep 17 00:00:00 2001 From: minisbett <39670899+minisbett@users.noreply.github.com> Date: Sun, 17 Nov 2024 18:37:43 +0100 Subject: [PATCH 04/15] fix usage of flashlight difficulty --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index b79a8feb96..6dcf8e61c5 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -283,7 +283,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (!score.Mods.Any(h => h is OsuModFlashlight)) return 0.0; - double flashlightValue = Flashlight.DifficultyToPerformance(attributes.FlashlightDifficulty); + double flashlightValue = Flashlight.DifficultyToPerformance(attributes.FlashlightDifficulty!.Value); // Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses. if (effectiveMissCount > 0) From d7ae4a0775684793bc7e8e96ed977ae0f4683681 Mon Sep 17 00:00:00 2001 From: minisbett <39670899+minisbett@users.noreply.github.com> Date: Sun, 17 Nov 2024 20:45:16 +0100 Subject: [PATCH 05/15] Fix usages of IDifficultyAttributes --- .../EmptyFreeformDifficultyCalculator.cs | 2 +- .../PippidonDifficultyCalculator.cs | 2 +- .../EmptyScrollingDifficultyCalculator.cs | 2 +- .../PippidonDifficultyCalculator.cs | 2 +- .../TestSceneTimedDifficultyCalculation.cs | 2 +- .../Difficulty/IDifficultyAttributes.cs | 22 +++++++++++++++++++ 6 files changed, 27 insertions(+), 5 deletions(-) diff --git a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformDifficultyCalculator.cs b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformDifficultyCalculator.cs index 2178941fd8..bb7b2ce5dd 100644 --- a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformDifficultyCalculator.cs +++ b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformDifficultyCalculator.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.EmptyFreeform protected override IDifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) { - return new IDifficultyAttributes(mods, 0); + return new EmptyDifficultyAttributes(); } protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty(); diff --git a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs index 55cf4fb42d..696b98d015 100644 --- a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs +++ b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Pippidon protected override IDifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) { - return new IDifficultyAttributes(mods, 0); + return new EmptyDifficultyAttributes(); } protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty(); diff --git a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingDifficultyCalculator.cs b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingDifficultyCalculator.cs index 47215ce7d9..4ccc0d370f 100644 --- a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingDifficultyCalculator.cs +++ b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingDifficultyCalculator.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.EmptyScrolling protected override IDifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) { - return new IDifficultyAttributes(mods, 0); + return new EmptyDifficultyAttributes(); } protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty(); diff --git a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs index 55cf4fb42d..696b98d015 100644 --- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs +++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Pippidon protected override IDifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) { - return new IDifficultyAttributes(mods, 0); + return new EmptyDifficultyAttributes(); } protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty(); diff --git a/osu.Game.Tests/NonVisual/TestSceneTimedDifficultyCalculation.cs b/osu.Game.Tests/NonVisual/TestSceneTimedDifficultyCalculation.cs index 5db37dd810..bbb33dee4b 100644 --- a/osu.Game.Tests/NonVisual/TestSceneTimedDifficultyCalculation.cs +++ b/osu.Game.Tests/NonVisual/TestSceneTimedDifficultyCalculation.cs @@ -208,7 +208,7 @@ namespace osu.Game.Tests.NonVisual } } - private class TestDifficultyAttributes : IDifficultyAttributes + private class TestDifficultyAttributes : EmptyDifficultyAttributes { public HitObject[] Objects = Array.Empty(); } diff --git a/osu.Game/Rulesets/Difficulty/IDifficultyAttributes.cs b/osu.Game/Rulesets/Difficulty/IDifficultyAttributes.cs index 56a22d2043..2616fc6e55 100644 --- a/osu.Game/Rulesets/Difficulty/IDifficultyAttributes.cs +++ b/osu.Game/Rulesets/Difficulty/IDifficultyAttributes.cs @@ -55,4 +55,26 @@ namespace osu.Game.Rulesets.Difficulty /// The where more information about the beatmap may be extracted from (such as AR/CS/OD/etc). public void FromDatabaseAttributes(IReadOnlyDictionary values, IBeatmapOnlineInfo onlineInfo); } + + /// + /// Represents a full, minimal implementation of . + /// + public class EmptyDifficultyAttributes : IDifficultyAttributes + { + public double StarRating { get; set; } + + public int MaxCombo { get; set; } + + public IEnumerable<(int attributeId, object value)> ToDatabaseAttributes() + { + yield return (IDifficultyAttributes.ATTRIB_ID_MAX_COMBO, MaxCombo); + yield return (IDifficultyAttributes.ATTRIB_ID_AIM, StarRating); + } + + public void FromDatabaseAttributes(IReadOnlyDictionary values, IBeatmapOnlineInfo onlineInfo) + { + MaxCombo = (int)values[IDifficultyAttributes.ATTRIB_ID_MAX_COMBO]; + StarRating = values[IDifficultyAttributes.ATTRIB_ID_AIM]; + } + } } From 4114ff5cf0b4496f973eadb986d8fa4c3080e8a1 Mon Sep 17 00:00:00 2001 From: minisbett <39670899+minisbett@users.noreply.github.com> Date: Sun, 17 Nov 2024 20:45:39 +0100 Subject: [PATCH 06/15] Add tests --- .../OsuDifficultyCalculatorTest.cs | 13 +++++++++++++ osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs | 4 ++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs index efda3fa369..d98c406a53 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.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 NUnit.Framework; using osu.Game.Beatmaps; using osu.Game.Rulesets.Difficulty; @@ -34,6 +35,18 @@ namespace osu.Game.Rulesets.Osu.Tests public void TestClassicMod(double expectedStarRating, int expectedMaxCombo, string name) => Test(expectedStarRating, expectedMaxCombo, name, new OsuModClassic()); + [Test] + public void TestFlashlightDifficultyNullability() + { + IWorkingBeatmap beatmap = GetBeatmap("diffcalc-test"); + + OsuDifficultyAttributes attributes = (OsuDifficultyAttributes)CreateDifficultyCalculator(beatmap).Calculate(); + Assert.IsNull(attributes.FlashlightDifficulty); + + attributes = (OsuDifficultyAttributes)CreateDifficultyCalculator(GetBeatmap("diffcalc-test")).Calculate([new OsuModFlashlight()]); + Assert.IsNotNull(attributes.FlashlightDifficulty); + } + protected override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new OsuDifficultyCalculator(new OsuRuleset().RulesetInfo, beatmap); protected override Ruleset CreateRuleset() => new OsuRuleset(); diff --git a/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs b/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs index 16434406b5..a7238f349f 100644 --- a/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs +++ b/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs @@ -25,14 +25,14 @@ namespace osu.Game.Tests.Beatmaps protected void Test(double expectedStarRating, int expectedMaxCombo, string name, params Mod[] mods) { - var attributes = CreateDifficultyCalculator(getBeatmap(name)).Calculate(mods); + var attributes = CreateDifficultyCalculator(GetBeatmap(name)).Calculate(mods); // Platform-dependent math functions (Pow, Cbrt, Exp, etc) may result in minute differences. Assert.That(attributes.StarRating, Is.EqualTo(expectedStarRating).Within(0.00001)); Assert.That(attributes.MaxCombo, Is.EqualTo(expectedMaxCombo)); } - private IWorkingBeatmap getBeatmap(string name) + protected IWorkingBeatmap GetBeatmap(string name) { using (var resStream = openResource($"{resource_namespace}.{name}.osu")) using (var stream = new LineBufferedReader(resStream)) From c5d5a5b342b8b06e5fbaabf5f45b2a4ada43be81 Mon Sep 17 00:00:00 2001 From: minisbett <39670899+minisbett@users.noreply.github.com> Date: Sun, 17 Nov 2024 23:58:36 +0100 Subject: [PATCH 07/15] fix test issues --- .../UserInterface/TestSceneBeatmapAttributeText.cs | 4 ++-- osu.Game/Beatmaps/BeatmapDifficultyCache.cs | 2 +- .../Rulesets/Difficulty/IPerformanceAttributes.cs | 13 +++++++++++++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapAttributeText.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapAttributeText.cs index 236a0c5946..58469d956f 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapAttributeText.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapAttributeText.cs @@ -209,7 +209,7 @@ namespace osu.Game.Tests.Visual.UserInterface } protected override IDifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) - => new IDifficultyAttributes(mods, mods.OfType().SingleOrDefault()?.Difficulty.Value ?? 0); + => new EmptyDifficultyAttributes(); protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Array.Empty(); @@ -226,7 +226,7 @@ namespace osu.Game.Tests.Visual.UserInterface } protected override IPerformanceAttributes CreatePerformanceAttributes(ScoreInfo score, IDifficultyAttributes attributes) - => new IPerformanceAttributes { Total = score.Mods.OfType().SingleOrDefault()?.Performance.Value ?? 0 }; + => new EmptyPerformanceAttributes(); } private class TestMod : Mod diff --git a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs index fc4175415c..ef08314066 100644 --- a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs +++ b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs @@ -252,7 +252,7 @@ namespace osu.Game.Beatmaps var performanceCalculator = ruleset.CreatePerformanceCalculator(); if (performanceCalculator == null) - return new StarDifficulty(difficulty, new PerformanceAttributes()); + return new StarDifficulty(difficulty, new EmptyPerformanceAttributes()); ScoreProcessor scoreProcessor = ruleset.CreateScoreProcessor(); scoreProcessor.Mods.Value = key.OrderedMods; diff --git a/osu.Game/Rulesets/Difficulty/IPerformanceAttributes.cs b/osu.Game/Rulesets/Difficulty/IPerformanceAttributes.cs index bf4e8bd247..d98bc42c47 100644 --- a/osu.Game/Rulesets/Difficulty/IPerformanceAttributes.cs +++ b/osu.Game/Rulesets/Difficulty/IPerformanceAttributes.cs @@ -21,4 +21,17 @@ namespace osu.Game.Rulesets.Difficulty /// public IEnumerable GetAttributesForDisplay(); } + + /// + /// Represents a full, minimal implementation of . + /// + public class EmptyPerformanceAttributes : IPerformanceAttributes + { + public double Total { get; set; } + + public IEnumerable GetAttributesForDisplay() + { + yield return new PerformanceDisplayAttribute(nameof(Total), "Achieved PP", Total); + } + } } From 11cb9e72f5fb99f2afae40008cf24b59a27b4c13 Mon Sep 17 00:00:00 2001 From: minisbett <39670899+minisbett@users.noreply.github.com> Date: Mon, 18 Nov 2024 17:16:07 +0100 Subject: [PATCH 08/15] Fix beatmap attribute tests --- .../Visual/UserInterface/TestSceneBeatmapAttributeText.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapAttributeText.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapAttributeText.cs index 58469d956f..206edbb58d 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapAttributeText.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapAttributeText.cs @@ -209,7 +209,7 @@ namespace osu.Game.Tests.Visual.UserInterface } protected override IDifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) - => new EmptyDifficultyAttributes(); + => new EmptyDifficultyAttributes { StarRating = mods.OfType().SingleOrDefault()?.Difficulty.Value ?? 0 }; protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Array.Empty(); @@ -226,7 +226,7 @@ namespace osu.Game.Tests.Visual.UserInterface } protected override IPerformanceAttributes CreatePerformanceAttributes(ScoreInfo score, IDifficultyAttributes attributes) - => new EmptyPerformanceAttributes(); + => new EmptyPerformanceAttributes() { Total = score.Mods.OfType().SingleOrDefault()?.Performance.Value ?? 0 }; } private class TestMod : Mod From b7ae1521cc3534933df005c2d8c7fb77396d88e0 Mon Sep 17 00:00:00 2001 From: minisbett <39670899+minisbett@users.noreply.github.com> Date: Mon, 18 Nov 2024 18:52:53 +0100 Subject: [PATCH 09/15] remove empty constructors --- .../Difficulty/CatchDifficultyAttributes.cs | 2 -- .../Difficulty/ManiaDifficultyAttributes.cs | 2 -- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs | 3 --- .../Difficulty/TaikoDifficultyAttributes.cs | 2 -- 4 files changed, 9 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs index af25ef3b5b..c166017c8f 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs @@ -10,8 +10,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty { public struct CatchDifficultyAttributes : IDifficultyAttributes { - public CatchDifficultyAttributes() { } - /// public double StarRating { get; set; } diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs index c5c280b4dd..114fd4b082 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs @@ -10,8 +10,6 @@ namespace osu.Game.Rulesets.Mania.Difficulty { public struct ManiaDifficultyAttributes : IDifficultyAttributes { - public ManiaDifficultyAttributes() { } - /// public double StarRating { get; set; } diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs index ab4fc510c4..8e946c8278 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs @@ -6,14 +6,11 @@ using JetBrains.Annotations; using Newtonsoft.Json; using osu.Game.Beatmaps; using osu.Game.Rulesets.Difficulty; -using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Osu.Difficulty { public struct OsuDifficultyAttributes : IDifficultyAttributes { - public OsuDifficultyAttributes() { } - /// public double StarRating { get; set; } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs index d7305a807d..e134c7535d 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs @@ -10,8 +10,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty { public struct TaikoDifficultyAttributes : IDifficultyAttributes { - public TaikoDifficultyAttributes() { } - /// public double StarRating { get; set; } From aa5dba67e093ff9b09ff3ca3ec20dddda46a77c5 Mon Sep 17 00:00:00 2001 From: minisbett <39670899+minisbett@users.noreply.github.com> Date: Mon, 18 Nov 2024 20:01:49 +0100 Subject: [PATCH 10/15] Add performance tests --- .../BenchmarkDifficultyCalculation.cs | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 osu.Game.Benchmarks/BenchmarkDifficultyCalculation.cs diff --git a/osu.Game.Benchmarks/BenchmarkDifficultyCalculation.cs b/osu.Game.Benchmarks/BenchmarkDifficultyCalculation.cs new file mode 100644 index 0000000000..ebd70a7e91 --- /dev/null +++ b/osu.Game.Benchmarks/BenchmarkDifficultyCalculation.cs @@ -0,0 +1,93 @@ +using System.IO; +using BenchmarkDotNet.Attributes; +using osu.Framework.IO.Stores; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Formats; +using osu.Game.IO; +using osu.Game.IO.Archives; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Catch; +using osu.Game.Rulesets.Difficulty; +using osu.Game.Rulesets.Mania; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Taiko; +using osu.Game.Scoring; +using osu.Game.Tests.Resources; + +namespace osu.Game.Benchmarks +{ + public class BenchmarkDifficultyCalculation : BenchmarkTest + { + private FlatWorkingBeatmap beatmap = null!; + + private IDifficultyAttributes osuAttributes = null!; + private IDifficultyAttributes taikoAttributes = null!; + private IDifficultyAttributes catchAttributes = null!; + private IDifficultyAttributes maniaAttributes = null!; + + public override void SetUp() + { + using var resources = new DllResourceStore(typeof(TestResources).Assembly); + using var archive = resources.GetStream("Resources/Archives/241526 Soleily - Renatus.osz"); + using var zipReader = new ZipArchiveReader(archive); + + using var beatmapStream = new MemoryStream(); + zipReader.GetStream("Soleily - Renatus (Gamu) [Insane].osu").CopyTo(beatmapStream); + beatmapStream.Seek(0, SeekOrigin.Begin); + var reader = new LineBufferedReader(beatmapStream); + var decoder = Decoder.GetDecoder(reader); + + beatmap = new FlatWorkingBeatmap(decoder.Decode(reader)); + + // Prepare difficulty attributes for an isolated performance calculation in every mode. + osuAttributes = DifficultyOsu(); + taikoAttributes = DifficultyTaiko(); + catchAttributes = DifficultyCatch(); + maniaAttributes = DifficultyMania(); + } + + [Benchmark] + public IDifficultyAttributes DifficultyOsu() => new OsuRuleset().CreateDifficultyCalculator(beatmap).Calculate(); + + [Benchmark] + public IDifficultyAttributes DifficultyTaiko() => new TaikoRuleset().CreateDifficultyCalculator(beatmap).Calculate(); + + [Benchmark] + public IDifficultyAttributes DifficultyCatch() => new CatchRuleset().CreateDifficultyCalculator(beatmap).Calculate(); + + [Benchmark] + public IDifficultyAttributes DifficultyMania() => new ManiaRuleset().CreateDifficultyCalculator(beatmap).Calculate(); + + [Benchmark] + public void PerformanceOsu() + { + Ruleset ruleset = new OsuRuleset(); + ScoreInfo score = new ScoreInfo(beatmap.BeatmapInfo, ruleset.RulesetInfo); + ruleset.CreatePerformanceCalculator()!.Calculate(score, osuAttributes); + } + + [Benchmark] + public void PerformanceTaiko() + { + Ruleset ruleset = new TaikoRuleset(); + ScoreInfo score = new ScoreInfo(beatmap.BeatmapInfo, ruleset.RulesetInfo); + ruleset.CreatePerformanceCalculator()!.Calculate(score, taikoAttributes); + } + + [Benchmark] + public void PerformanceCatch() + { + Ruleset ruleset = new CatchRuleset(); + ScoreInfo score = new ScoreInfo(beatmap.BeatmapInfo, ruleset.RulesetInfo); + ruleset.CreatePerformanceCalculator()!.Calculate(score, catchAttributes); + } + + [Benchmark] + public void PerformanceMania() + { + Ruleset ruleset = new ManiaRuleset(); + ScoreInfo score = new ScoreInfo(beatmap.BeatmapInfo, ruleset.RulesetInfo); + ruleset.CreatePerformanceCalculator()!.Calculate(score, maniaAttributes); + } + } +} From 5a989ecdd852829d73d379e611e9229fa97e6b4e Mon Sep 17 00:00:00 2001 From: minisbett <39670899+minisbett@users.noreply.github.com> Date: Mon, 18 Nov 2024 20:02:04 +0100 Subject: [PATCH 11/15] Fix xmldoc --- osu.Game/Rulesets/Difficulty/PerformanceDisplayAttribute.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Difficulty/PerformanceDisplayAttribute.cs b/osu.Game/Rulesets/Difficulty/PerformanceDisplayAttribute.cs index a654652ef8..1ff66f5ea3 100644 --- a/osu.Game/Rulesets/Difficulty/PerformanceDisplayAttribute.cs +++ b/osu.Game/Rulesets/Difficulty/PerformanceDisplayAttribute.cs @@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Difficulty public class PerformanceDisplayAttribute { /// - /// Name of the attribute property in . + /// Name of the attribute property in . /// public string PropertyName { get; } From 26e9c21d9ad5f5cacaffe4a1a82af7ef30e6f9e5 Mon Sep 17 00:00:00 2001 From: minisbett <39670899+minisbett@users.noreply.github.com> Date: Mon, 18 Nov 2024 21:27:23 +0100 Subject: [PATCH 12/15] Fix code quality --- osu.Game.Benchmarks/BenchmarkDifficultyCalculation.cs | 5 ++++- osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs | 1 - 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game.Benchmarks/BenchmarkDifficultyCalculation.cs b/osu.Game.Benchmarks/BenchmarkDifficultyCalculation.cs index ebd70a7e91..39eb5b78da 100644 --- a/osu.Game.Benchmarks/BenchmarkDifficultyCalculation.cs +++ b/osu.Game.Benchmarks/BenchmarkDifficultyCalculation.cs @@ -1,4 +1,7 @@ -using System.IO; +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.IO; using BenchmarkDotNet.Attributes; using osu.Framework.IO.Stores; using osu.Game.Beatmaps; diff --git a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs index d98c406a53..760374b5a2 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs @@ -1,7 +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 NUnit.Framework; using osu.Game.Beatmaps; using osu.Game.Rulesets.Difficulty; From d75eb3a88ada68b6cc6f3598f653ae7ec2d5efcd Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 20 Nov 2024 15:46:45 +0900 Subject: [PATCH 13/15] Return mods from `CalculateAllLegacyCombinations` --- osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index 39340776f0..718184e041 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -140,7 +140,7 @@ namespace osu.Game.Rulesets.Difficulty /// This can only be used to compute difficulties for legacy mod combinations. /// /// A collection of structures describing the difficulty of the beatmap for each mod combination. - public IEnumerable CalculateAllLegacyCombinations(CancellationToken cancellationToken = default) + public IEnumerable<(Mod[] Mods, IDifficultyAttributes Attributes)> CalculateAllLegacyCombinations(CancellationToken cancellationToken = default) { var rulesetInstance = ruleset.CreateInstance(); @@ -148,11 +148,13 @@ namespace osu.Game.Rulesets.Difficulty { Mod classicMod = rulesetInstance.CreateMod(); - var finalCombination = ModUtils.FlattenMod(combination); + IEnumerable finalCombination = ModUtils.FlattenMod(combination); if (classicMod != null) finalCombination = finalCombination.Append(classicMod); - yield return Calculate(finalCombination.ToArray(), cancellationToken); + Mod[] finalMods = finalCombination.ToArray(); + + yield return (finalMods, Calculate(finalMods, cancellationToken)); } } From d12fd89f44ba0a3f5d9ad1f2877fcffe12c9e860 Mon Sep 17 00:00:00 2001 From: minisbett <39670899+minisbett@users.noreply.github.com> Date: Thu, 21 Nov 2024 19:45:37 +0100 Subject: [PATCH 14/15] Rename empty attributes, make attributes json OptIn --- .../Difficulty/CatchDifficultyAttributes.cs | 1 + .../Difficulty/ManiaDifficultyAttributes.cs | 1 + osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs | 1 + .../Difficulty/TaikoDifficultyAttributes.cs | 2 +- osu.Game/Beatmaps/BeatmapDifficultyCache.cs | 2 +- osu.Game/Rulesets/Difficulty/IDifficultyAttributes.cs | 3 +-- osu.Game/Rulesets/Difficulty/IPerformanceAttributes.cs | 2 +- 7 files changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs index c166017c8f..4f4ea014d0 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs @@ -8,6 +8,7 @@ using osu.Game.Rulesets.Difficulty; namespace osu.Game.Rulesets.Catch.Difficulty { + [JsonObject(MemberSerialization.OptIn)] public struct CatchDifficultyAttributes : IDifficultyAttributes { /// diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs index 114fd4b082..7929ab53ee 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs @@ -8,6 +8,7 @@ using osu.Game.Rulesets.Difficulty; namespace osu.Game.Rulesets.Mania.Difficulty { + [JsonObject(MemberSerialization.OptIn)] public struct ManiaDifficultyAttributes : IDifficultyAttributes { /// diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs index 8e946c8278..d97bf4cdf5 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs @@ -9,6 +9,7 @@ using osu.Game.Rulesets.Difficulty; namespace osu.Game.Rulesets.Osu.Difficulty { + [JsonObject(MemberSerialization.OptIn)] public struct OsuDifficultyAttributes : IDifficultyAttributes { /// diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs index e134c7535d..279e0dba4d 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs @@ -8,13 +8,13 @@ using osu.Game.Rulesets.Difficulty; namespace osu.Game.Rulesets.Taiko.Difficulty { + [JsonObject(MemberSerialization.OptIn)] public struct TaikoDifficultyAttributes : IDifficultyAttributes { /// public double StarRating { get; set; } /// - [JsonProperty("max_combo", Order = -2)] public int MaxCombo { get; set; } /// diff --git a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs index ef08314066..fc4175415c 100644 --- a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs +++ b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs @@ -252,7 +252,7 @@ namespace osu.Game.Beatmaps var performanceCalculator = ruleset.CreatePerformanceCalculator(); if (performanceCalculator == null) - return new StarDifficulty(difficulty, new EmptyPerformanceAttributes()); + return new StarDifficulty(difficulty, new PerformanceAttributes()); ScoreProcessor scoreProcessor = ruleset.CreateScoreProcessor(); scoreProcessor.Mods.Value = key.OrderedMods; diff --git a/osu.Game/Rulesets/Difficulty/IDifficultyAttributes.cs b/osu.Game/Rulesets/Difficulty/IDifficultyAttributes.cs index f14c28dc17..d907692f27 100644 --- a/osu.Game/Rulesets/Difficulty/IDifficultyAttributes.cs +++ b/osu.Game/Rulesets/Difficulty/IDifficultyAttributes.cs @@ -10,7 +10,6 @@ namespace osu.Game.Rulesets.Difficulty /// /// Describes the difficulty of a beatmap, as output by a . /// - [JsonObject(MemberSerialization.OptIn)] public interface IDifficultyAttributes { protected const int ATTRIB_ID_AIM = 1; @@ -60,7 +59,7 @@ namespace osu.Game.Rulesets.Difficulty /// /// Represents a full, minimal implementation of . /// - public class EmptyDifficultyAttributes : IDifficultyAttributes + public class DifficultyAttributes : IDifficultyAttributes { public double StarRating { get; set; } diff --git a/osu.Game/Rulesets/Difficulty/IPerformanceAttributes.cs b/osu.Game/Rulesets/Difficulty/IPerformanceAttributes.cs index d98bc42c47..9d861c59d1 100644 --- a/osu.Game/Rulesets/Difficulty/IPerformanceAttributes.cs +++ b/osu.Game/Rulesets/Difficulty/IPerformanceAttributes.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Difficulty /// /// Represents a full, minimal implementation of . /// - public class EmptyPerformanceAttributes : IPerformanceAttributes + public class PerformanceAttributes : IPerformanceAttributes { public double Total { get; set; } From d6e5a47e52afe8f60d1562d6d4902278115d4b5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 25 Nov 2024 11:27:14 +0100 Subject: [PATCH 15/15] Fix compile failures --- .../EmptyFreeformDifficultyCalculator.cs | 2 +- .../PippidonDifficultyCalculator.cs | 2 +- .../EmptyScrollingDifficultyCalculator.cs | 2 +- .../PippidonDifficultyCalculator.cs | 2 +- .../NonVisual/TestSceneTimedDifficultyCalculation.cs | 2 +- .../Visual/UserInterface/TestSceneBeatmapAttributeText.cs | 4 ++-- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformDifficultyCalculator.cs b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformDifficultyCalculator.cs index bb7b2ce5dd..6c49efa6b9 100644 --- a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformDifficultyCalculator.cs +++ b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformDifficultyCalculator.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.EmptyFreeform protected override IDifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) { - return new EmptyDifficultyAttributes(); + return new DifficultyAttributes(); } protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty(); diff --git a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs index 696b98d015..3fe024086c 100644 --- a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs +++ b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Pippidon protected override IDifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) { - return new EmptyDifficultyAttributes(); + return new DifficultyAttributes(); } protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty(); diff --git a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingDifficultyCalculator.cs b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingDifficultyCalculator.cs index 4ccc0d370f..7060872bda 100644 --- a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingDifficultyCalculator.cs +++ b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingDifficultyCalculator.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.EmptyScrolling protected override IDifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) { - return new EmptyDifficultyAttributes(); + return new DifficultyAttributes(); } protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty(); diff --git a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs index 696b98d015..3fe024086c 100644 --- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs +++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Pippidon protected override IDifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) { - return new EmptyDifficultyAttributes(); + return new DifficultyAttributes(); } protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty(); diff --git a/osu.Game.Tests/NonVisual/TestSceneTimedDifficultyCalculation.cs b/osu.Game.Tests/NonVisual/TestSceneTimedDifficultyCalculation.cs index bbb33dee4b..c5441dd43c 100644 --- a/osu.Game.Tests/NonVisual/TestSceneTimedDifficultyCalculation.cs +++ b/osu.Game.Tests/NonVisual/TestSceneTimedDifficultyCalculation.cs @@ -208,7 +208,7 @@ namespace osu.Game.Tests.NonVisual } } - private class TestDifficultyAttributes : EmptyDifficultyAttributes + private class TestDifficultyAttributes : DifficultyAttributes { public HitObject[] Objects = Array.Empty(); } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapAttributeText.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapAttributeText.cs index 206edbb58d..8fcba6202e 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapAttributeText.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapAttributeText.cs @@ -209,7 +209,7 @@ namespace osu.Game.Tests.Visual.UserInterface } protected override IDifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) - => new EmptyDifficultyAttributes { StarRating = mods.OfType().SingleOrDefault()?.Difficulty.Value ?? 0 }; + => new DifficultyAttributes { StarRating = mods.OfType().SingleOrDefault()?.Difficulty.Value ?? 0 }; protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Array.Empty(); @@ -226,7 +226,7 @@ namespace osu.Game.Tests.Visual.UserInterface } protected override IPerformanceAttributes CreatePerformanceAttributes(ScoreInfo score, IDifficultyAttributes attributes) - => new EmptyPerformanceAttributes() { Total = score.Mods.OfType().SingleOrDefault()?.Performance.Value ?? 0 }; + => new PerformanceAttributes { Total = score.Mods.OfType().SingleOrDefault()?.Performance.Value ?? 0 }; } private class TestMod : Mod