1
0
mirror of https://github.com/ppy/osu.git synced 2024-09-21 20:47:28 +08:00

Merge pull request #14575 from peppy/add-legacy-convert-encode-stability-tests

Fix multiple conversions of a beatmap to taiko ruleset applying speed multiplier multiple times
This commit is contained in:
Dan Balasescu 2021-08-30 18:47:09 +09:00 committed by GitHub
commit c4da966ad9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 59 additions and 16 deletions

View File

@ -18,12 +18,6 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
{ {
internal class TaikoBeatmapConverter : BeatmapConverter<TaikoHitObject> internal class TaikoBeatmapConverter : BeatmapConverter<TaikoHitObject>
{ {
/// <summary>
/// osu! is generally slower than taiko, so a factor is added to increase
/// speed. This must be used everywhere slider length or beat length is used.
/// </summary>
public const float LEGACY_VELOCITY_MULTIPLIER = 1.4f;
/// <summary> /// <summary>
/// Because swells are easier in taiko than spinners are in osu!, /// Because swells are easier in taiko than spinners are in osu!,
/// legacy taiko multiplies a factor when converting the number of required hits. /// legacy taiko multiplies a factor when converting the number of required hits.
@ -52,10 +46,12 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
protected override Beatmap<TaikoHitObject> ConvertBeatmap(IBeatmap original, CancellationToken cancellationToken) protected override Beatmap<TaikoHitObject> ConvertBeatmap(IBeatmap original, CancellationToken cancellationToken)
{ {
// Rewrite the beatmap info to add the slider velocity multiplier if (!(original.BeatmapInfo.BaseDifficulty is TaikoMutliplierAppliedDifficulty))
original.BeatmapInfo = original.BeatmapInfo.Clone(); {
original.BeatmapInfo.BaseDifficulty = original.BeatmapInfo.BaseDifficulty.Clone(); // Rewrite the beatmap info to add the slider velocity multiplier
original.BeatmapInfo.BaseDifficulty.SliderMultiplier *= LEGACY_VELOCITY_MULTIPLIER; original.BeatmapInfo = original.BeatmapInfo.Clone();
original.BeatmapInfo.BaseDifficulty = new TaikoMutliplierAppliedDifficulty(original.BeatmapInfo.BaseDifficulty);
}
Beatmap<TaikoHitObject> converted = base.ConvertBeatmap(original, cancellationToken); Beatmap<TaikoHitObject> converted = base.ConvertBeatmap(original, cancellationToken);
@ -155,7 +151,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
// The true distance, accounting for any repeats. This ends up being the drum roll distance later // The true distance, accounting for any repeats. This ends up being the drum roll distance later
int spans = (obj as IHasRepeats)?.SpanCount() ?? 1; int spans = (obj as IHasRepeats)?.SpanCount() ?? 1;
double distance = distanceData.Distance * spans * LEGACY_VELOCITY_MULTIPLIER; double distance = distanceData.Distance * spans * LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER;
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(obj.StartTime); TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(obj.StartTime);
DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(obj.StartTime); DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(obj.StartTime);
@ -194,5 +190,14 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
} }
protected override Beatmap<TaikoHitObject> CreateBeatmap() => new TaikoBeatmap(); protected override Beatmap<TaikoHitObject> CreateBeatmap() => new TaikoBeatmap();
private class TaikoMutliplierAppliedDifficulty : BeatmapDifficulty
{
public TaikoMutliplierAppliedDifficulty(BeatmapDifficulty difficulty)
{
difficulty.CopyTo(this);
SliderMultiplier *= LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER;
}
}
} }
} }

View File

@ -6,10 +6,10 @@ using System;
using System.Threading; using System.Threading;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Beatmaps.Formats;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Beatmaps;
using osu.Game.Rulesets.Taiko.Judgements; using osu.Game.Rulesets.Taiko.Judgements;
using osuTK; using osuTK;
@ -120,7 +120,7 @@ namespace osu.Game.Rulesets.Taiko.Objects
double IHasDistance.Distance => Duration * Velocity; double IHasDistance.Distance => Duration * Velocity;
SliderPath IHasPath.Path SliderPath IHasPath.Path
=> new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(1) }, ((IHasDistance)this).Distance / TaikoBeatmapConverter.LEGACY_VELOCITY_MULTIPLIER); => new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(1) }, ((IHasDistance)this).Distance / LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER);
#endregion #endregion
} }

View File

@ -49,6 +49,22 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.IsTrue(areComboColoursEqual(decodedAfterEncode.beatmapSkin.Configuration, decoded.beatmapSkin.Configuration)); Assert.IsTrue(areComboColoursEqual(decodedAfterEncode.beatmapSkin.Configuration, decoded.beatmapSkin.Configuration));
} }
[TestCaseSource(nameof(allBeatmaps))]
public void TestEncodeDecodeStabilityDoubleConvert(string name)
{
var decoded = decodeFromLegacy(beatmaps_resource_store.GetStream(name), name);
var decodedAfterEncode = decodeFromLegacy(encodeToLegacy(decoded), name);
// run an extra convert. this is expected to be stable.
decodedAfterEncode.beatmap = convert(decodedAfterEncode.beatmap);
sort(decoded.beatmap);
sort(decodedAfterEncode.beatmap);
Assert.That(decodedAfterEncode.beatmap.Serialize(), Is.EqualTo(decoded.beatmap.Serialize()));
Assert.IsTrue(areComboColoursEqual(decodedAfterEncode.beatmapSkin.Configuration, decoded.beatmapSkin.Configuration));
}
[Test] [Test]
public void TestEncodeMultiSegmentSliderWithFloatingPointError() public void TestEncodeMultiSegmentSliderWithFloatingPointError()
{ {

View File

@ -32,7 +32,23 @@ namespace osu.Game.Beatmaps
/// <summary> /// <summary>
/// Returns a shallow-clone of this <see cref="BeatmapDifficulty"/>. /// Returns a shallow-clone of this <see cref="BeatmapDifficulty"/>.
/// </summary> /// </summary>
public BeatmapDifficulty Clone() => (BeatmapDifficulty)MemberwiseClone(); public BeatmapDifficulty Clone()
{
var diff = new BeatmapDifficulty();
CopyTo(diff);
return diff;
}
public void CopyTo(BeatmapDifficulty difficulty)
{
difficulty.ApproachRate = ApproachRate;
difficulty.DrainRate = DrainRate;
difficulty.CircleSize = CircleSize;
difficulty.OverallDifficulty = OverallDifficulty;
difficulty.SliderMultiplier = SliderMultiplier;
difficulty.SliderTickRate = SliderTickRate;
}
/// <summary> /// <summary>
/// Maps a difficulty value [0, 10] to a two-piece linear range of values. /// Maps a difficulty value [0, 10] to a two-piece linear range of values.

View File

@ -24,6 +24,12 @@ namespace osu.Game.Beatmaps.Formats
{ {
public const int LATEST_VERSION = 128; public const int LATEST_VERSION = 128;
/// <summary>
/// osu! is generally slower than taiko, so a factor is added to increase
/// speed. This must be used everywhere slider length or beat length is used.
/// </summary>
public const float LEGACY_TAIKO_VELOCITY_MULTIPLIER = 1.4f;
private readonly IBeatmap beatmap; private readonly IBeatmap beatmap;
[CanBeNull] [CanBeNull]
@ -140,9 +146,9 @@ namespace osu.Game.Beatmaps.Formats
writer.WriteLine(FormattableString.Invariant($"OverallDifficulty: {beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty}")); writer.WriteLine(FormattableString.Invariant($"OverallDifficulty: {beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty}"));
writer.WriteLine(FormattableString.Invariant($"ApproachRate: {beatmap.BeatmapInfo.BaseDifficulty.ApproachRate}")); writer.WriteLine(FormattableString.Invariant($"ApproachRate: {beatmap.BeatmapInfo.BaseDifficulty.ApproachRate}"));
// Taiko adjusts the slider multiplier (see: TaikoBeatmapConverter.LEGACY_VELOCITY_MULTIPLIER) // Taiko adjusts the slider multiplier (see: LEGACY_TAIKO_VELOCITY_MULTIPLIER)
writer.WriteLine(beatmap.BeatmapInfo.RulesetID == 1 writer.WriteLine(beatmap.BeatmapInfo.RulesetID == 1
? FormattableString.Invariant($"SliderMultiplier: {beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier / 1.4f}") ? FormattableString.Invariant($"SliderMultiplier: {beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier / LEGACY_TAIKO_VELOCITY_MULTIPLIER}")
: FormattableString.Invariant($"SliderMultiplier: {beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier}")); : FormattableString.Invariant($"SliderMultiplier: {beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier}"));
writer.WriteLine(FormattableString.Invariant($"SliderTickRate: {beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate}")); writer.WriteLine(FormattableString.Invariant($"SliderTickRate: {beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate}"));