1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-13 08:32:57 +08:00

Fix osu! standardised score conversion sometimes exceeding bounds

Co-authored-by: Zyf <zyfarok@gmail.com>

Closes https://github.com/ppy/osu/issues/25860

Users reported that some stable scores would convert to large negative
total scores in lazer after the introduction of combo exponent. Those
large negative total scores were actually mangled NaNs.

The root cause of this was the following calculation going below zero
unexpectedly:

	8e8d9b2cd9/osu.Game/Database/StandardisedScoreMigrationTools.cs (L323)

which then propagates negative numbers onward until

	8e8d9b2cd9/osu.Game/Database/StandardisedScoreMigrationTools.cs (L337)

which yields a NaN due to attempting to take the square root of a
negative number.

To fix, clamp `comboPortionInScoreV1` to sane limits: to
`comboPortionFromLongestComboInScoreV1` from below, and to
`maximumAchievableComboPortionInScoreV1` from above. This is a less
direct fix than perhaps imagined, but it seems like a better one as it
will also affect the calculation of both the lower and the upper
estimate of the score.
This commit is contained in:
Bartłomiej Dach 2023-12-18 21:56:56 +01:00
parent 8e8d9b2cd9
commit 017003deea
No known key found for this signature in database
2 changed files with 15 additions and 3 deletions

View File

@ -293,13 +293,24 @@ namespace osu.Game.Database
// Roughly corresponds to integrating f(combo) = combo ^ COMBO_EXPONENT (omitting constants)
double maximumAchievableComboPortionInStandardisedScore = Math.Pow(maximumLegacyCombo, 1 + ScoreProcessor.COMBO_EXPONENT);
double comboPortionInScoreV1 = maximumAchievableComboPortionInScoreV1 * comboProportion / score.Accuracy;
// This is - roughly - how much score, in the combo portion, the longest combo on this particular play would gain in score V1.
double comboPortionFromLongestComboInScoreV1 = Math.Pow(score.MaxCombo, 2);
// Same for standardised score.
double comboPortionFromLongestComboInStandardisedScore = Math.Pow(score.MaxCombo, 1 + ScoreProcessor.COMBO_EXPONENT);
// We estimate the combo portion of the score in score V1 terms.
// The division by accuracy is supposed to lessen the impact of accuracy on the combo portion,
// but in some edge cases it cannot sanely undo it.
// Therefore the resultant value is clamped from both sides for sanity.
// The clamp from below to `comboPortionFromLongestComboInScoreV1` targets near-FC scores wherein
// the player had bad accuracy at the end of their longest combo, which causes the division by accuracy
// to underestimate the combo portion.
// The clamp from above to `maximumAchievableComboPortionInScoreV1` targets FC scores wherein
// the player had bad accuracy at the start of the map, which causes the division by accuracy
// to overestimate the combo portion.
double comboPortionInScoreV1 = Math.Clamp(maximumAchievableComboPortionInScoreV1 * comboProportion / score.Accuracy,
comboPortionFromLongestComboInScoreV1, maximumAchievableComboPortionInScoreV1);
// Calculate how many times the longest combo the user has achieved in the play can repeat
// without exceeding the combo portion in score V1 as achieved by the player.
// This is a pessimistic estimate; it intentionally does not operate on object count and uses only score instead.

View File

@ -32,9 +32,10 @@ namespace osu.Game.Scoring.Legacy
/// <item><description>30000003: First version after converting legacy total score to standardised.</description></item>
/// <item><description>30000004: Fixed mod multipliers during legacy score conversion. Reconvert all scores.</description></item>
/// <item><description>30000005: Introduce combo exponent in the osu! gamemode. Reconvert all scores.</description></item>
/// <item><description>30000006: Fix edge cases in conversion after combo exponent introduction that lead to NaNs. Reconvert all scores.</description></item>
/// </list>
/// </remarks>
public const int LATEST_VERSION = 30000005;
public const int LATEST_VERSION = 30000006;
/// <summary>
/// The first stable-compatible YYYYMMDD format version given to lazer usage of replays.