Merge pull request #26471 from bdach/fix-incorrect-difficulty-peppy-stars

Fix incorrect score conversion on selected beatmaps due to incorrect `difficultyPeppyStars` rounding
This commit is contained in:
Dean Herbert 2024-01-12 22:34:54 +09:00 committed by GitHub
commit 02975b9498
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 47 additions and 20 deletions

View File

@ -10,6 +10,7 @@ using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Scoring;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Scoring.Legacy;
@ -62,13 +63,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty
drainLength = ((int)Math.Round(baseBeatmap.HitObjects[^1].StartTime) - (int)Math.Round(baseBeatmap.HitObjects[0].StartTime) - breakLength) / 1000;
}
int difficultyPeppyStars = (int)Math.Round(
(baseBeatmap.Difficulty.DrainRate
+ baseBeatmap.Difficulty.OverallDifficulty
+ baseBeatmap.Difficulty.CircleSize
+ Math.Clamp((float)objectCount / drainLength * 8, 0, 16)) / 38 * 5);
scoreMultiplier = difficultyPeppyStars;
scoreMultiplier = LegacyRulesetExtensions.CalculateDifficultyPeppyStars(baseBeatmap.Difficulty, objectCount, drainLength);
LegacyScoreAttributes attributes = new LegacyScoreAttributes();

View File

@ -7,6 +7,7 @@ using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Osu.Objects;
@ -62,13 +63,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
drainLength = ((int)Math.Round(baseBeatmap.HitObjects[^1].StartTime) - (int)Math.Round(baseBeatmap.HitObjects[0].StartTime) - breakLength) / 1000;
}
int difficultyPeppyStars = (int)Math.Round(
(baseBeatmap.Difficulty.DrainRate
+ baseBeatmap.Difficulty.OverallDifficulty
+ baseBeatmap.Difficulty.CircleSize
+ Math.Clamp((float)objectCount / drainLength * 8, 0, 16)) / 38 * 5);
scoreMultiplier = difficultyPeppyStars;
scoreMultiplier = LegacyRulesetExtensions.CalculateDifficultyPeppyStars(baseBeatmap.Difficulty, objectCount, drainLength);
LegacyScoreAttributes attributes = new LegacyScoreAttributes();

View File

@ -7,6 +7,7 @@ using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Scoring.Legacy;
@ -65,11 +66,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
drainLength = ((int)Math.Round(baseBeatmap.HitObjects[^1].StartTime) - (int)Math.Round(baseBeatmap.HitObjects[0].StartTime) - breakLength) / 1000;
}
difficultyPeppyStars = (int)Math.Round(
(baseBeatmap.Difficulty.DrainRate
+ baseBeatmap.Difficulty.OverallDifficulty
+ baseBeatmap.Difficulty.CircleSize
+ Math.Clamp((float)objectCount / drainLength * 8, 0, 16)) / 38 * 5);
difficultyPeppyStars = LegacyRulesetExtensions.CalculateDifficultyPeppyStars(baseBeatmap.Difficulty, objectCount, drainLength);
LegacyScoreAttributes attributes = new LegacyScoreAttributes();

View File

@ -57,5 +57,40 @@ namespace osu.Game.Rulesets.Objects.Legacy
return (float)(1.0f - 0.7f * IBeatmapDifficultyInfo.DifficultyRange(circleSize)) / 2 * (applyFudge ? broken_gamefield_rounding_allowance : 1);
}
public static int CalculateDifficultyPeppyStars(BeatmapDifficulty difficulty, int objectCount, int drainLength)
{
/*
* WARNING: DO NOT TOUCH IF YOU DO NOT KNOW WHAT YOU ARE DOING
*
* It so happens that in stable, due to .NET Framework internals, float math would be performed
* using x87 registers and opcodes.
* .NET (Core) however uses SSE instructions on 32- and 64-bit words.
* x87 registers are _80 bits_ wide. Which is notably wider than _both_ float and double.
* Therefore, on a significant number of beatmaps, the rounding would not produce correct values.
*
* Thus, to crudely - but, seemingly *mostly* accurately, after checking across all ranked maps - emulate this,
* use `decimal`, which is slow, but has bigger precision than `double`.
* At the time of writing, there is _one_ ranked exception to this - namely https://osu.ppy.sh/beatmapsets/1156087#osu/2625853 -
* but it is considered an "acceptable casualty", since in that case scores aren't inflated by _that_ much compared to others.
*/
decimal objectToDrainRatio = drainLength != 0
? Math.Clamp((decimal)objectCount / drainLength * 8, 0, 16)
: 16;
/*
* Notably, THE `double` CASTS BELOW ARE IMPORTANT AND MUST REMAIN.
* Their goal is to trick the compiler / runtime into NOT promoting from single-precision float, as doing so would prompt it
* to attempt to "silently" fix the single-precision values when converting to decimal,
* which is NOT what the x87 FPU does.
*/
decimal drainRate = (decimal)(double)difficulty.DrainRate;
decimal overallDifficulty = (decimal)(double)difficulty.OverallDifficulty;
decimal circleSize = (decimal)(double)difficulty.CircleSize;
return (int)Math.Round((drainRate + overallDifficulty + circleSize + objectToDrainRatio) / 38 * 5);
}
}
}

View File

@ -13,6 +13,7 @@ using osu.Game.Extensions;
using osu.Game.IO.Legacy;
using osu.Game.IO.Serialization;
using osu.Game.Replays.Legacy;
using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Replays.Types;
using SharpCompress.Compressors.LZMA;
@ -38,9 +39,13 @@ namespace osu.Game.Scoring.Legacy
/// <item><description>30000009: Fix edge cases in conversion for scores which have 0.0x mod multiplier on stable. Reconvert all scores.</description></item>
/// <item><description>30000010: Fix mania score V1 conversion using score V1 accuracy rather than V2 accuracy. Reconvert all scores.</description></item>
/// <item><description>30000011: Re-do catch scoring to mirror stable Score V2 as closely as feasible. Reconvert all scores.</description></item>
/// <item><description>
/// 30000012: Fix incorrect total score conversion on selected beatmaps after implementing the more correct
/// <see cref="LegacyRulesetExtensions.CalculateDifficultyPeppyStars"/> method. Reconvert all scores.
/// </description></item>
/// </list>
/// </remarks>
public const int LATEST_VERSION = 30000011;
public const int LATEST_VERSION = 30000012;
/// <summary>
/// The first stable-compatible YYYYMMDD format version given to lazer usage of replays.