osu icon indicating copy to clipboard operation
osu copied to clipboard

Add enough metadata to `SoloScoreInfo` to allow full score calculation with `DifficultyCalculator`

Open peppy opened this issue 3 years ago • 3 comments

Right now, we need to run difficulty calculation to get some statistics (like max base/bonus score) in order to display scores correctly. This complicates the process of displaying scores anywhere in the game, and will also become more of an issue for osu-web as we move forward.

This task involves storing enough metadata to not require any diffcalc to calculate classic/standardised scores, in both pass and fail situations.

peppy avatar Aug 01 '22 09:08 peppy

With https://github.com/ppy/osu/pull/19380, we should be able to ditch difficulty calculator completely, with the presumption that all legacy scores will be reprocessed to contain the correct legacy maximum combo value in the newly added MaximumScoringValues property (naming is completely TBD).

Here's a snippet of how I imagine the ScoreManager methods to be like with #19380 (after all scores are reprocessed to store the new properties and the nullability is removed):

        /// <summary>
        /// Orders an array of <see cref="ScoreInfo"/>s by total score.
        /// </summary>
        /// <param name="scores">The array of <see cref="ScoreInfo"/>s to reorder.</param>
        /// <returns>The given <paramref name="scores"/> ordered by decreasing total score.</returns>
        public ScoreInfo[] OrderByTotalScore(ScoreInfo[] scores) => scores.Select((score, index) => (score, totalScore: GetTotalScore(scores[index])))
                                                                          .OrderByDescending(g => g.totalScore)
                                                                          .ThenBy(g => g.score.OnlineID)
                                                                          .Select(g => g.score)
                                                                          .ToArray();

        /// <summary>
        /// Retrieves the total score of a <see cref="ScoreInfo"/> in the given <see cref="ScoringMode"/>.
        /// </summary>
        /// <param name="score">The <see cref="ScoreInfo"/> to calculate the total score of.</param>
        /// <param name="mode">The <see cref="ScoringMode"/> to return the total score as.</param>
        /// <returns>The total score.</returns>
        public long GetTotalScore([NotNull] ScoreInfo score, ScoringMode mode = ScoringMode.Standardised)
        {
            // TODO: This is required for playlist aggregate scores. They should likely not be getting here in the first place.
            if (string.IsNullOrEmpty(score.BeatmapInfo.MD5Hash))
                return score.TotalScore;

            int? maxAchievableCombo = score.MaximumScoringValues.MaxCombo;
            if (maxAchievableCombo == null)
                return score.TotalScore;

            if (maxAchievableCombo == 0)
                return 0;

            var ruleset = score.Ruleset.CreateInstance();
            var scoreProcessor = ruleset.CreateScoreProcessor();
            scoreProcessor.Mods.Value = score.Mods;

            return (long)Math.Round(scoreProcessor.ComputeFinalLegacyScore(mode, score, maxAchievableCombo.Value));
        }

frenzibyte avatar Aug 02 '22 22:08 frenzibyte

Can probably be moved to a helper method at that point right? Or at least made static.

peppy avatar Aug 03 '22 06:08 peppy

Yeah for sure, can be moved to a ScoreInfoExtensions class.

frenzibyte avatar Aug 03 '22 06:08 frenzibyte