diff --git a/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs b/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs
index b941cd8973..3d3c07a5ad 100644
--- a/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs
+++ b/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs
@@ -38,6 +38,7 @@ namespace osu.Game.Online.API.Requests.Responses
Rank = Rank,
Ruleset = ruleset,
Mods = mods,
+ IsLegacyScore = true
};
if (Statistics != null)
diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs
index 4ed3f92e25..0206989231 100644
--- a/osu.Game/Scoring/ScoreInfo.cs
+++ b/osu.Game/Scoring/ScoreInfo.cs
@@ -185,6 +185,34 @@ namespace osu.Game.Scoring
[JsonProperty("position")]
public int? Position { get; set; }
+ private bool isLegacyScore;
+
+ ///
+ /// Whether this represents a legacy (osu!stable) score.
+ ///
+ [JsonIgnore]
+ [NotMapped]
+ public bool IsLegacyScore
+ {
+ get
+ {
+ if (isLegacyScore)
+ return true;
+
+ // The above check will catch legacy online scores that have an appropriate UserString + UserId.
+ // For non-online scores such as those imported in, a heuristic is used based on the following table:
+ //
+ // Mode | UserString | UserId
+ // --------------- | ---------- | ---------
+ // stable | | 1
+ // lazer | |
+ // lazer (offline) | Guest | 1
+
+ return ID > 0 && UserID == 1 && UserString != "Guest";
+ }
+ set => isLegacyScore = value;
+ }
+
public IEnumerable<(HitResult result, int count, int? maxCount)> GetStatisticsForDisplay()
{
foreach (var key in OrderAttributeUtils.GetValuesInOrder())
diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs
index 561ca631b3..8e8147ff39 100644
--- a/osu.Game/Scoring/ScoreManager.cs
+++ b/osu.Game/Scoring/ScoreManager.cs
@@ -10,6 +10,7 @@ using System.Threading;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
using osu.Framework.Bindables;
+using osu.Framework.Extensions;
using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Game.Beatmaps;
@@ -149,23 +150,38 @@ namespace osu.Game.Scoring
return;
}
- int? beatmapMaxCombo = score.Beatmap.MaxCombo;
+ int beatmapMaxCombo;
- if (beatmapMaxCombo == null)
+ if (score.IsLegacyScore)
{
- if (score.Beatmap.ID == 0 || difficulties == null)
+ // This score is guaranteed to be an osu!stable score.
+ // The combo must be determined through either the beatmap's max combo value or the difficulty calculator, as lazer's scoring has changed and the score statistics cannot be used.
+ if (score.Beatmap.MaxCombo == null)
{
- // We don't have enough information (max combo) to compute the score, so let's use the provided score.
- Value = score.TotalScore;
+ if (score.Beatmap.ID == 0 || difficulties == null)
+ {
+ // We don't have enough information (max combo) to compute the score, so use the provided score.
+ Value = score.TotalScore;
+ return;
+ }
+
+ // We can compute the max combo locally after the async beatmap difficulty computation.
+ difficultyBindable = difficulties().GetBindableDifficulty(score.Beatmap, score.Ruleset, score.Mods, (difficultyCancellationSource = new CancellationTokenSource()).Token);
+ difficultyBindable.BindValueChanged(d => updateScore(d.NewValue.MaxCombo), true);
+
return;
}
- // We can compute the max combo locally after the async beatmap difficulty computation.
- difficultyBindable = difficulties().GetBindableDifficulty(score.Beatmap, score.Ruleset, score.Mods, (difficultyCancellationSource = new CancellationTokenSource()).Token);
- difficultyBindable.BindValueChanged(d => updateScore(d.NewValue.MaxCombo), true);
+ beatmapMaxCombo = score.Beatmap.MaxCombo.Value;
}
else
- updateScore(beatmapMaxCombo.Value);
+ {
+ // This score is guaranteed to be an osu!lazer score.
+ // The combo must be determined through the score's statistics, as both the beatmap's max combo and the difficulty calculator will provide osu!stable combo values.
+ beatmapMaxCombo = Enum.GetValues(typeof(HitResult)).OfType().Where(r => r.AffectsCombo()).Select(r => score.Statistics.GetOrDefault(r)).Sum();
+ }
+
+ updateScore(beatmapMaxCombo);
}
private void updateScore(int beatmapMaxCombo)