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)