Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove LegacyDifficultyControlPoint / LegacyBpmMultiplier #24738

Merged
merged 8 commits into from
Sep 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@
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.Legacy;
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
Expand Down Expand Up @@ -50,10 +51,9 @@ public DistanceObjectPatternGenerator(LegacyRandom random, HitObject hitObject,
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 = LegacyRulesetExtensions.GetPrecisionAdjustedBeatLength(hasSliderVelocity, timingPoint, ManiaRuleset.SHORT_NAME);
else
beatLength = timingPoint.BeatLength;

Expand Down
1 change: 0 additions & 1 deletion osu.Game.Rulesets.Osu/Objects/Slider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,6 @@ public int RepeatCount

public BindableNumber<double> SliderVelocityMultiplierBindable { get; } = new BindableDouble(1)
{
Precision = 0.01,
MinValue = 0.1,
MaxValue = 10
};
Expand Down
8 changes: 4 additions & 4 deletions osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
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
{
Expand Down Expand Up @@ -186,10 +187,9 @@ private bool shouldConvertSliderToHits(HitObject obj, IBeatmap beatmap, IHasDist
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 = LegacyRulesetExtensions.GetPrecisionAdjustedBeatLength(hasSliderVelocity, timingPoint, TaikoRuleset.SHORT_NAME);
else
beatLength = timingPoint.BeatLength;

Expand Down
6 changes: 2 additions & 4 deletions osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1024,10 +1024,8 @@ public void TestNaNControlPoints()
Assert.That(controlPoints.DifficultyPointAt(2000).SliderVelocity, Is.EqualTo(1));
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);
#pragma warning restore 618
Assert.That(controlPoints.DifficultyPointAt(2000).GenerateTicks, Is.False);
Assert.That(controlPoints.DifficultyPointAt(3000).GenerateTicks, Is.True);
}
}
}
Expand Down
13 changes: 11 additions & 2 deletions osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,16 @@ public class DifficultyControlPoint : ControlPoint, IEquatable<DifficultyControl
/// </summary>
public readonly BindableDouble SliderVelocityBindable = new BindableDouble(1)
{
Precision = 0.01,
MinValue = 0.1,
MaxValue = 10
};

/// <summary>
/// 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).
/// </summary>
public bool GenerateTicks { get; set; } = true;

public override Color4 GetRepresentingColour(OsuColour colours) => colours.Lime1;

/// <summary>
Expand All @@ -41,11 +46,13 @@ public double SliderVelocity

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);
}
Expand All @@ -56,8 +63,10 @@ public override bool Equals(ControlPoint? other)

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);
}
}
13 changes: 4 additions & 9 deletions osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <[email protected]>. 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;
Expand Down Expand Up @@ -103,12 +101,8 @@ private void applyDefaults(HitObject hitObject)
{
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;
Expand Down Expand Up @@ -497,8 +491,9 @@ private void handleTimingPoint(string line)

int onlineRulesetID = beatmap.BeatmapInfo.Ruleset.OnlineID;

addControlPoint(time, new LegacyDifficultyControlPoint(onlineRulesetID, beatLength)
addControlPoint(time, new DifficultyControlPoint
{
GenerateTicks = !double.IsNaN(beatLength),
SliderVelocity = speedMultiplier,
}, timingChange);

Expand Down
57 changes: 0 additions & 57 deletions osu.Game/Beatmaps/Formats/LegacyDecoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -163,63 +163,6 @@ public enum Section
Mania,
}

[Obsolete("Do not use unless you're a legacy ruleset and 100% sure.")]
public class LegacyDifficultyControlPoint : DifficultyControlPoint, IEquatable<LegacyDifficultyControlPoint>
{
/// <summary>
/// Legacy BPM multiplier that introduces floating-point errors for rulesets that depend on it.
/// DO NOT USE THIS UNLESS 100% SURE.
/// </summary>
public double BpmMultiplier { get; private set; }

/// <summary>
/// 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).
/// </summary>
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<LegacySampleControlPoint>
{
public int CustomSampleBank;
Expand Down
6 changes: 0 additions & 6 deletions osu.Game/Rulesets/Objects/HitObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,6 @@ public IList<HitSampleInfo> Samples
/// </summary>
public virtual IList<HitSampleInfo> AuxiliarySamples => ImmutableList<HitSampleInfo>.Empty;

/// <summary>
/// Legacy BPM multiplier that introduces floating-point errors for rulesets that depend on it.
/// DO NOT USE THIS UNLESS 100% SURE.
/// </summary>
public double? LegacyBpmMultiplier { get; set; }

/// <summary>
/// Whether this <see cref="HitObject"/> is in Kiai time.
/// </summary>
Expand Down
42 changes: 42 additions & 0 deletions osu.Game/Rulesets/Objects/Legacy/LegacyRulesetExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) ppy Pty Ltd <[email protected]>. 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
{
/// <summary>
/// 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.
/// </summary>
public static double GetPrecisionAdjustedBeatLength(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;
}
}
}