From b34a36f6cec639d1ac8d987002dd0a893214c9cd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Sep 2023 17:41:28 +0900 Subject: [PATCH 1/7] Remove all usage of `LegacyDifficultyControlPoint` --- .../Legacy/DistanceObjectPatternGenerator.cs | 13 +++-- .../Beatmaps/TaikoBeatmapConverter.cs | 7 +-- .../Formats/LegacyBeatmapDecoderTest.cs | 4 +- .../ControlPoints/DifficultyControlPoint.cs | 12 +++- .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 11 ++-- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 57 ------------------- osu.Game/Rulesets/Objects/HitObject.cs | 6 -- .../Objects/Types/IHasSliderVelocity.cs | 19 +++++++ 8 files changed, 46 insertions(+), 83 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index 49c1a055a6..fec4dbf285 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -10,10 +10,10 @@ using System.Linq; using osu.Framework.Extensions.EnumExtensions; using osu.Game.Audio; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; -using osu.Game.Rulesets.Mania.Objects; -using osu.Game.Beatmaps.ControlPoints; using osu.Game.Utils; namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy @@ -50,10 +50,11 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime); double beatLength; - if (hitObject.LegacyBpmMultiplier.HasValue) - beatLength = timingPoint.BeatLength * hitObject.LegacyBpmMultiplier.Value; - else if (hitObject is IHasSliderVelocity hasSliderVelocity) - beatLength = timingPoint.BeatLength / hasSliderVelocity.SliderVelocityMultiplier; + + if (hitObject is IHasSliderVelocity hasSliderVelocity) + { + beatLength = timingPoint.BeatLength / hasSliderVelocity.GetPrecisionAdjustedSliderVelocityMultiplier(ManiaRuleset.SHORT_NAME); + } else beatLength = timingPoint.BeatLength; diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 3e9252e1fd..339c671943 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -186,10 +186,9 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(obj.StartTime); double beatLength; - if (obj.LegacyBpmMultiplier.HasValue) - beatLength = timingPoint.BeatLength * obj.LegacyBpmMultiplier.Value; - else if (obj is IHasSliderVelocity hasSliderVelocity) - beatLength = timingPoint.BeatLength / hasSliderVelocity.SliderVelocityMultiplier; + + if (obj is IHasSliderVelocity hasSliderVelocity) + beatLength = timingPoint.BeatLength / hasSliderVelocity.GetPrecisionAdjustedSliderVelocityMultiplier(TaikoRuleset.SHORT_NAME); else beatLength = timingPoint.BeatLength; diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 970b6aaf60..65ea228996 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -1025,8 +1025,8 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.That(controlPoints.DifficultyPointAt(3000).SliderVelocity, Is.EqualTo(1)); #pragma warning disable 618 - Assert.That(((LegacyBeatmapDecoder.LegacyDifficultyControlPoint)controlPoints.DifficultyPointAt(2000)).GenerateTicks, Is.False); - Assert.That(((LegacyBeatmapDecoder.LegacyDifficultyControlPoint)controlPoints.DifficultyPointAt(3000)).GenerateTicks, Is.True); + Assert.That(controlPoints.DifficultyPointAt(2000).GenerateTicks, Is.False); + Assert.That(controlPoints.DifficultyPointAt(3000).GenerateTicks, Is.True); #pragma warning restore 618 } } diff --git a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs index e6f1609d7f..2827ea6513 100644 --- a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs @@ -28,6 +28,12 @@ namespace osu.Game.Beatmaps.ControlPoints MaxValue = 10 }; + /// + /// Whether or not slider ticks should be generated at this control point. + /// This exists for backwards compatibility with maps that abuse NaN slider velocity behavior on osu!stable (e.g. /b/2628991). + /// + public bool GenerateTicks { get; set; } = true; + public override Color4 GetRepresentingColour(OsuColour colours) => colours.Lime1; /// @@ -41,11 +47,13 @@ namespace osu.Game.Beatmaps.ControlPoints public override bool IsRedundant(ControlPoint? existing) => existing is DifficultyControlPoint existingDifficulty + && GenerateTicks == existingDifficulty.GenerateTicks && SliderVelocity == existingDifficulty.SliderVelocity; public override void CopyFrom(ControlPoint other) { SliderVelocity = ((DifficultyControlPoint)other).SliderVelocity; + GenerateTicks = ((DifficultyControlPoint)other).GenerateTicks; base.CopyFrom(other); } @@ -56,8 +64,10 @@ namespace osu.Game.Beatmaps.ControlPoints public bool Equals(DifficultyControlPoint? other) => base.Equals(other) + && GenerateTicks == other.GenerateTicks && SliderVelocity == other.SliderVelocity; - public override int GetHashCode() => HashCode.Combine(base.GetHashCode(), SliderVelocity); + // ReSharper disable once NonReadonlyMemberInGetHashCode + public override int GetHashCode() => HashCode.Combine(base.GetHashCode(), SliderVelocity, GenerateTicks); } } diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 7c375b7604..f47a555847 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -103,12 +103,8 @@ namespace osu.Game.Beatmaps.Formats { DifficultyControlPoint difficultyControlPoint = (beatmap.ControlPointInfo as LegacyControlPointInfo)?.DifficultyPointAt(hitObject.StartTime) ?? DifficultyControlPoint.DEFAULT; - if (difficultyControlPoint is LegacyDifficultyControlPoint legacyDifficultyControlPoint) - { - hitObject.LegacyBpmMultiplier = legacyDifficultyControlPoint.BpmMultiplier; - if (hitObject is IHasGenerateTicks hasGenerateTicks) - hasGenerateTicks.GenerateTicks = legacyDifficultyControlPoint.GenerateTicks; - } + if (hitObject is IHasGenerateTicks hasGenerateTicks) + hasGenerateTicks.GenerateTicks = difficultyControlPoint.GenerateTicks; if (hitObject is IHasSliderVelocity hasSliderVelocity) hasSliderVelocity.SliderVelocityMultiplier = difficultyControlPoint.SliderVelocity; @@ -497,8 +493,9 @@ namespace osu.Game.Beatmaps.Formats int onlineRulesetID = beatmap.BeatmapInfo.Ruleset.OnlineID; - addControlPoint(time, new LegacyDifficultyControlPoint(onlineRulesetID, beatLength) + addControlPoint(time, new DifficultyControlPoint { + GenerateTicks = !double.IsNaN(beatLength), SliderVelocity = speedMultiplier, }, timingChange); diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 23440b8a1d..93af9cf41c 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -163,63 +163,6 @@ namespace osu.Game.Beatmaps.Formats Mania, } - [Obsolete("Do not use unless you're a legacy ruleset and 100% sure.")] - public class LegacyDifficultyControlPoint : DifficultyControlPoint, IEquatable - { - /// - /// Legacy BPM multiplier that introduces floating-point errors for rulesets that depend on it. - /// DO NOT USE THIS UNLESS 100% SURE. - /// - public double BpmMultiplier { get; private set; } - - /// - /// Whether or not slider ticks should be generated at this control point. - /// This exists for backwards compatibility with maps that abuse NaN slider velocity behavior on osu!stable (e.g. /b/2628991). - /// - public bool GenerateTicks { get; private set; } = true; - - public LegacyDifficultyControlPoint(int rulesetId, double beatLength) - : this() - { - // Note: In stable, the division occurs on floats, but with compiler optimisations turned on actually seems to occur on doubles via some .NET black magic (possibly inlining?). - if (rulesetId == 1 || rulesetId == 3) - BpmMultiplier = beatLength < 0 ? Math.Clamp((float)-beatLength, 10, 10000) / 100.0 : 1; - else - BpmMultiplier = beatLength < 0 ? Math.Clamp((float)-beatLength, 10, 1000) / 100.0 : 1; - - GenerateTicks = !double.IsNaN(beatLength); - } - - public LegacyDifficultyControlPoint() - { - SliderVelocityBindable.Precision = double.Epsilon; - } - - public override bool IsRedundant(ControlPoint? existing) - => base.IsRedundant(existing) - && GenerateTicks == ((existing as LegacyDifficultyControlPoint)?.GenerateTicks ?? true); - - public override void CopyFrom(ControlPoint other) - { - base.CopyFrom(other); - - BpmMultiplier = ((LegacyDifficultyControlPoint)other).BpmMultiplier; - GenerateTicks = ((LegacyDifficultyControlPoint)other).GenerateTicks; - } - - public override bool Equals(ControlPoint? other) - => other is LegacyDifficultyControlPoint otherLegacyDifficultyControlPoint - && Equals(otherLegacyDifficultyControlPoint); - - public bool Equals(LegacyDifficultyControlPoint? other) - => base.Equals(other) - && BpmMultiplier == other.BpmMultiplier - && GenerateTicks == other.GenerateTicks; - - // ReSharper disable twice NonReadonlyMemberInGetHashCode - public override int GetHashCode() => HashCode.Combine(base.GetHashCode(), BpmMultiplier, GenerateTicks); - } - internal class LegacySampleControlPoint : SampleControlPoint, IEquatable { public int CustomSampleBank; diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index ed3d3a6eb2..ec2a4a31f6 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -76,12 +76,6 @@ namespace osu.Game.Rulesets.Objects /// public virtual IList AuxiliarySamples => ImmutableList.Empty; - /// - /// Legacy BPM multiplier that introduces floating-point errors for rulesets that depend on it. - /// DO NOT USE THIS UNLESS 100% SURE. - /// - public double? LegacyBpmMultiplier { get; set; } - /// /// Whether this is in Kiai time. /// diff --git a/osu.Game/Rulesets/Objects/Types/IHasSliderVelocity.cs b/osu.Game/Rulesets/Objects/Types/IHasSliderVelocity.cs index e665dcc752..34c2bda05c 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasSliderVelocity.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasSliderVelocity.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 osu.Framework.Bindables; namespace osu.Game.Rulesets.Objects.Types @@ -16,5 +17,23 @@ namespace osu.Game.Rulesets.Objects.Types double SliderVelocityMultiplier { get; set; } BindableNumber SliderVelocityMultiplierBindable { get; } + + /// + /// Introduces floating-point errors for rulesets that depend on it. + /// + public double GetPrecisionAdjustedSliderVelocityMultiplier(string rulesetShortName) + { + double beatLength = -100 / SliderVelocityMultiplier; + + switch (rulesetShortName) + { + case "taiko": + case "mania": + return 1 / (beatLength < 0 ? Math.Clamp((float)-beatLength, 10, 10000) / 100.0 : 1); + + default: + return 1 / (beatLength < 0 ? Math.Clamp((float)-beatLength, 10, 1000) / 100.0 : 1); + } + } } } From dcbdc114ce9669146544f37c6b1d3cac0e44537e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Sep 2023 17:41:50 +0900 Subject: [PATCH 2/7] Remove precision limitations of `SliderVelocity` --- osu.Game.Rulesets.Osu/Objects/Slider.cs | 1 - osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 1e7e8e2981..8bf19f030f 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -136,7 +136,6 @@ namespace osu.Game.Rulesets.Osu.Objects public BindableNumber SliderVelocityMultiplierBindable { get; } = new BindableDouble(1) { - Precision = 0.01, MinValue = 0.1, MaxValue = 10 }; diff --git a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs index 2827ea6513..05230c85f4 100644 --- a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs @@ -23,7 +23,6 @@ namespace osu.Game.Beatmaps.ControlPoints /// public readonly BindableDouble SliderVelocityBindable = new BindableDouble(1) { - Precision = 0.01, MinValue = 0.1, MaxValue = 10 }; From 2bb869732e1b64c0886b33b070a0c78b693cdc07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 7 Sep 2023 18:41:40 +0200 Subject: [PATCH 3/7] Delete outdated warning disables --- osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs | 2 -- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 2 -- 2 files changed, 4 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 65ea228996..be53a3e18a 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -1024,10 +1024,8 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.That(controlPoints.DifficultyPointAt(2000).SliderVelocity, Is.EqualTo(1)); Assert.That(controlPoints.DifficultyPointAt(3000).SliderVelocity, Is.EqualTo(1)); -#pragma warning disable 618 Assert.That(controlPoints.DifficultyPointAt(2000).GenerateTicks, Is.False); Assert.That(controlPoints.DifficultyPointAt(3000).GenerateTicks, Is.True); -#pragma warning restore 618 } } } diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index f47a555847..37aa7950d3 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#pragma warning disable 618 - using System; using System.Collections.Generic; using System.IO; From e71fef4b6aa60812eae044916330071acd049758 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 7 Sep 2023 18:43:36 +0200 Subject: [PATCH 4/7] Remove braces --- .../Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index fec4dbf285..749ab78fa5 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -52,9 +52,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy double beatLength; if (hitObject is IHasSliderVelocity hasSliderVelocity) - { beatLength = timingPoint.BeatLength / hasSliderVelocity.GetPrecisionAdjustedSliderVelocityMultiplier(ManiaRuleset.SHORT_NAME); - } else beatLength = timingPoint.BeatLength; From 4ecc4632aa9849865f438401783172169d6aac71 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Sep 2023 18:41:08 +0900 Subject: [PATCH 5/7] Make rounding error even less precise Basically matching the old code more closely to avoid too much precision from doing math in a slightly different way. --- .../Legacy/DistanceObjectPatternGenerator.cs | 2 +- .../Beatmaps/TaikoBeatmapConverter.cs | 2 +- .../Objects/Types/IHasSliderVelocity.cs | 18 +++++++++++++----- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index 749ab78fa5..476bcda36f 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy double beatLength; if (hitObject is IHasSliderVelocity hasSliderVelocity) - beatLength = timingPoint.BeatLength / hasSliderVelocity.GetPrecisionAdjustedSliderVelocityMultiplier(ManiaRuleset.SHORT_NAME); + beatLength = hasSliderVelocity.GetPrecisionAdjustedBeatLength(timingPoint, ManiaRuleset.SHORT_NAME); else beatLength = timingPoint.BeatLength; diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 339c671943..ff6809e69a 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -188,7 +188,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps double beatLength; if (obj is IHasSliderVelocity hasSliderVelocity) - beatLength = timingPoint.BeatLength / hasSliderVelocity.GetPrecisionAdjustedSliderVelocityMultiplier(TaikoRuleset.SHORT_NAME); + beatLength = hasSliderVelocity.GetPrecisionAdjustedBeatLength(timingPoint, TaikoRuleset.SHORT_NAME); else beatLength = timingPoint.BeatLength; diff --git a/osu.Game/Rulesets/Objects/Types/IHasSliderVelocity.cs b/osu.Game/Rulesets/Objects/Types/IHasSliderVelocity.cs index 34c2bda05c..73582b8fdc 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasSliderVelocity.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasSliderVelocity.cs @@ -3,6 +3,7 @@ using System; using osu.Framework.Bindables; +using osu.Game.Beatmaps.ControlPoints; namespace osu.Game.Rulesets.Objects.Types { @@ -19,21 +20,28 @@ namespace osu.Game.Rulesets.Objects.Types BindableNumber SliderVelocityMultiplierBindable { get; } /// - /// Introduces floating-point errors for rulesets that depend on it. + /// Introduces floating-point errors to post-multiplied beat length for rulesets that depend on it. /// - public double GetPrecisionAdjustedSliderVelocityMultiplier(string rulesetShortName) + public double GetPrecisionAdjustedBeatLength(TimingControlPoint timingControlPoint, string rulesetShortName) { - double beatLength = -100 / SliderVelocityMultiplier; + double sliderVelocityAsBeatLength = -100 / SliderVelocityMultiplier; + + // Note: In stable, the division occurs on floats, but with compiler optimisations turned on actually seems to occur on doubles via some .NET black magic (possibly inlining?). + double bpmMultiplier; switch (rulesetShortName) { case "taiko": case "mania": - return 1 / (beatLength < 0 ? Math.Clamp((float)-beatLength, 10, 10000) / 100.0 : 1); + bpmMultiplier = sliderVelocityAsBeatLength < 0 ? Math.Clamp((float)-sliderVelocityAsBeatLength, 10, 10000) / 100.0 : 1; + break; default: - return 1 / (beatLength < 0 ? Math.Clamp((float)-beatLength, 10, 1000) / 100.0 : 1); + bpmMultiplier = sliderVelocityAsBeatLength < 0 ? Math.Clamp((float)-sliderVelocityAsBeatLength, 10, 1000) / 100.0 : 1; + break; } + + return timingControlPoint.BeatLength * bpmMultiplier; } } } From 0031da76ffa68b8d7e3a7eae1155393e2ec85cf7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Sep 2023 17:38:34 +0900 Subject: [PATCH 6/7] Move to extension method and throw on non-legacy ruleset --- .../Legacy/DistanceObjectPatternGenerator.cs | 1 + .../Beatmaps/TaikoBeatmapConverter.cs | 1 + .../Objects/Legacy/LegacyRulesetExtensions.cs | 42 +++++++++++++++++++ .../Objects/Types/IHasSliderVelocity.cs | 27 ------------ 4 files changed, 44 insertions(+), 27 deletions(-) create mode 100644 osu.Game/Rulesets/Objects/Legacy/LegacyRulesetExtensions.cs diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index 476bcda36f..07df79783c 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -13,6 +13,7 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Legacy; using osu.Game.Rulesets.Objects.Types; using osu.Game.Utils; diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index ff6809e69a..054ae9a011 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -14,6 +14,7 @@ using JetBrains.Annotations; using osu.Game.Audio; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Formats; +using osu.Game.Rulesets.Objects.Legacy; namespace osu.Game.Rulesets.Taiko.Beatmaps { diff --git a/osu.Game/Rulesets/Objects/Legacy/LegacyRulesetExtensions.cs b/osu.Game/Rulesets/Objects/Legacy/LegacyRulesetExtensions.cs new file mode 100644 index 0000000000..0bce269668 --- /dev/null +++ b/osu.Game/Rulesets/Objects/Legacy/LegacyRulesetExtensions.cs @@ -0,0 +1,42 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Objects.Types; + +namespace osu.Game.Rulesets.Objects.Legacy +{ + public static class LegacyRulesetExtensions + { + /// + /// Introduces floating-point errors to post-multiplied beat length for legacy rulesets that depend on it. + /// You should definitely not use this unless you know exactly what you're doing. + /// + public static double GetPrecisionAdjustedBeatLength(this IHasSliderVelocity hasSliderVelocity, TimingControlPoint timingControlPoint, string rulesetShortName) + { + double sliderVelocityAsBeatLength = -100 / hasSliderVelocity.SliderVelocityMultiplier; + + // Note: In stable, the division occurs on floats, but with compiler optimisations turned on actually seems to occur on doubles via some .NET black magic (possibly inlining?). + double bpmMultiplier; + + switch (rulesetShortName) + { + case "taiko": + case "mania": + bpmMultiplier = sliderVelocityAsBeatLength < 0 ? Math.Clamp((float)-sliderVelocityAsBeatLength, 10, 10000) / 100.0 : 1; + break; + + case "osu": + case "fruits": + bpmMultiplier = sliderVelocityAsBeatLength < 0 ? Math.Clamp((float)-sliderVelocityAsBeatLength, 10, 1000) / 100.0 : 1; + break; + + default: + throw new ArgumentException("Must be a legacy ruleset", nameof(rulesetShortName)); + } + + return timingControlPoint.BeatLength * bpmMultiplier; + } + } +} diff --git a/osu.Game/Rulesets/Objects/Types/IHasSliderVelocity.cs b/osu.Game/Rulesets/Objects/Types/IHasSliderVelocity.cs index 73582b8fdc..e665dcc752 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasSliderVelocity.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasSliderVelocity.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 osu.Framework.Bindables; -using osu.Game.Beatmaps.ControlPoints; namespace osu.Game.Rulesets.Objects.Types { @@ -18,30 +16,5 @@ namespace osu.Game.Rulesets.Objects.Types double SliderVelocityMultiplier { get; set; } BindableNumber SliderVelocityMultiplierBindable { get; } - - /// - /// Introduces floating-point errors to post-multiplied beat length for rulesets that depend on it. - /// - public double GetPrecisionAdjustedBeatLength(TimingControlPoint timingControlPoint, string rulesetShortName) - { - double sliderVelocityAsBeatLength = -100 / SliderVelocityMultiplier; - - // Note: In stable, the division occurs on floats, but with compiler optimisations turned on actually seems to occur on doubles via some .NET black magic (possibly inlining?). - double bpmMultiplier; - - switch (rulesetShortName) - { - case "taiko": - case "mania": - bpmMultiplier = sliderVelocityAsBeatLength < 0 ? Math.Clamp((float)-sliderVelocityAsBeatLength, 10, 10000) / 100.0 : 1; - break; - - default: - bpmMultiplier = sliderVelocityAsBeatLength < 0 ? Math.Clamp((float)-sliderVelocityAsBeatLength, 10, 1000) / 100.0 : 1; - break; - } - - return timingControlPoint.BeatLength * bpmMultiplier; - } } } From 56cc2b62f01c930d433a25c4cd65402c0ec2396d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Sep 2023 18:13:04 +0900 Subject: [PATCH 7/7] Make not extension method --- .../Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs | 2 +- osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs | 2 +- osu.Game/Rulesets/Objects/Legacy/LegacyRulesetExtensions.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index 07df79783c..cce0944564 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy double beatLength; if (hitObject is IHasSliderVelocity hasSliderVelocity) - beatLength = hasSliderVelocity.GetPrecisionAdjustedBeatLength(timingPoint, ManiaRuleset.SHORT_NAME); + beatLength = LegacyRulesetExtensions.GetPrecisionAdjustedBeatLength(hasSliderVelocity, timingPoint, ManiaRuleset.SHORT_NAME); else beatLength = timingPoint.BeatLength; diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 054ae9a011..e46e2ec09c 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -189,7 +189,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps double beatLength; if (obj is IHasSliderVelocity hasSliderVelocity) - beatLength = hasSliderVelocity.GetPrecisionAdjustedBeatLength(timingPoint, TaikoRuleset.SHORT_NAME); + beatLength = LegacyRulesetExtensions.GetPrecisionAdjustedBeatLength(hasSliderVelocity, timingPoint, TaikoRuleset.SHORT_NAME); else beatLength = timingPoint.BeatLength; diff --git a/osu.Game/Rulesets/Objects/Legacy/LegacyRulesetExtensions.cs b/osu.Game/Rulesets/Objects/Legacy/LegacyRulesetExtensions.cs index 0bce269668..6cff4b12c4 100644 --- a/osu.Game/Rulesets/Objects/Legacy/LegacyRulesetExtensions.cs +++ b/osu.Game/Rulesets/Objects/Legacy/LegacyRulesetExtensions.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Objects.Legacy /// Introduces floating-point errors to post-multiplied beat length for legacy rulesets that depend on it. /// You should definitely not use this unless you know exactly what you're doing. /// - public static double GetPrecisionAdjustedBeatLength(this IHasSliderVelocity hasSliderVelocity, TimingControlPoint timingControlPoint, string rulesetShortName) + public static double GetPrecisionAdjustedBeatLength(IHasSliderVelocity hasSliderVelocity, TimingControlPoint timingControlPoint, string rulesetShortName) { double sliderVelocityAsBeatLength = -100 / hasSliderVelocity.SliderVelocityMultiplier;