From 1c1afa1c962b99fe082d8f7e1dfc06336922ec7f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 28 Aug 2020 19:16:20 +0900 Subject: [PATCH 01/13] Move MaxCombo to base DifficultyAttributes --- osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs | 1 - osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs | 1 - osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs | 1 - osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs | 1 + 4 files changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs index 75f5b18607..fa9011d826 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs @@ -8,6 +8,5 @@ namespace osu.Game.Rulesets.Catch.Difficulty public class CatchDifficultyAttributes : DifficultyAttributes { public double ApproachRate; - public int MaxCombo; } } diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs index 6e991a1d08..a9879013f8 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs @@ -11,6 +11,5 @@ namespace osu.Game.Rulesets.Osu.Difficulty public double SpeedStrain; public double ApproachRate; public double OverallDifficulty; - public int MaxCombo; } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs index 75d3807bba..00ad956c8f 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs @@ -8,6 +8,5 @@ namespace osu.Game.Rulesets.Taiko.Difficulty public class TaikoDifficultyAttributes : DifficultyAttributes { public double GreatHitWindow; - public int MaxCombo; } } diff --git a/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs b/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs index b4b4bb9cd1..732dc772b7 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs @@ -12,6 +12,7 @@ namespace osu.Game.Rulesets.Difficulty public Skill[] Skills; public double StarRating; + public int MaxCombo; public DifficultyAttributes() { From 85bda29b71a790cb7675d46b86dce2462deae3c7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 28 Aug 2020 19:16:24 +0900 Subject: [PATCH 02/13] Add mania max combo attribute --- osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs index 37cba1fd3c..b08c520c54 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs @@ -11,6 +11,7 @@ using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Difficulty.Preprocessing; using osu.Game.Rulesets.Mania.Difficulty.Skills; using osu.Game.Rulesets.Mania.Mods; +using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Scoring; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; @@ -43,6 +44,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty Mods = mods, // Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future GreatHitWindow = (int)(hitWindows.WindowFor(HitResult.Great)) / clockRate, + MaxCombo = beatmap.HitObjects.Sum(h => h is HoldNote ? 2 : 1), Skills = skills }; } From 4d15f0fe520f188c9219aa7e716679967d4c5f49 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 28 Aug 2020 19:16:46 +0900 Subject: [PATCH 03/13] Implement basic score recalculation --- osu.Game/Beatmaps/BeatmapDifficultyManager.cs | 10 ++++--- .../Online/Leaderboards/LeaderboardScore.cs | 4 +-- osu.Game/OsuGameBase.cs | 9 +++--- .../Overlays/BeatmapSet/Scores/ScoreTable.cs | 5 +++- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 13 +++++--- osu.Game/Scoring/ScoreManager.cs | 30 ++++++++++++++++++- 6 files changed, 55 insertions(+), 16 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapDifficultyManager.cs b/osu.Game/Beatmaps/BeatmapDifficultyManager.cs index b80b4e45ed..c02b6002d9 100644 --- a/osu.Game/Beatmaps/BeatmapDifficultyManager.cs +++ b/osu.Game/Beatmaps/BeatmapDifficultyManager.cs @@ -201,11 +201,11 @@ namespace osu.Game.Beatmaps var calculator = ruleset.CreateDifficultyCalculator(beatmapManager.GetWorkingBeatmap(beatmapInfo)); var attributes = calculator.Calculate(key.Mods); - return difficultyCache[key] = new StarDifficulty(attributes.StarRating); + return difficultyCache[key] = new StarDifficulty(attributes.StarRating, attributes.MaxCombo); } catch { - return difficultyCache[key] = new StarDifficulty(0); + return difficultyCache[key] = new StarDifficulty(); } } @@ -227,7 +227,7 @@ namespace osu.Game.Beatmaps if (beatmapInfo.ID == 0 || rulesetInfo.ID == null) { // If not, fall back to the existing star difficulty (e.g. from an online source). - existingDifficulty = new StarDifficulty(beatmapInfo.StarDifficulty); + existingDifficulty = new StarDifficulty(beatmapInfo.StarDifficulty, beatmapInfo.MaxCombo ?? 0); key = default; return true; @@ -292,10 +292,12 @@ namespace osu.Game.Beatmaps public readonly struct StarDifficulty { public readonly double Stars; + public readonly int MaxCombo; - public StarDifficulty(double stars) + public StarDifficulty(double stars, int maxCombo) { Stars = stars; + MaxCombo = maxCombo; // Todo: Add more members (BeatmapInfo.DifficultyRating? Attributes? Etc...) } diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index 87b283f6b5..3c7ef73594 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -72,7 +72,7 @@ namespace osu.Game.Online.Leaderboards } [BackgroundDependencyLoader] - private void load(IAPIProvider api, OsuColour colour) + private void load(IAPIProvider api, OsuColour colour, ScoreManager scoreManager) { var user = score.User; @@ -194,7 +194,7 @@ namespace osu.Game.Online.Leaderboards { TextColour = Color4.White, GlowColour = Color4Extensions.FromHex(@"83ccfa"), - Text = score.TotalScore.ToString(@"N0"), + Text = scoreManager.GetTotalScore(score).ToString(@"N0"), Font = OsuFont.Numeric.With(size: 23), }, RankContainer = new Container diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 98f60d52d3..1d92315b1f 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -57,6 +57,8 @@ namespace osu.Game protected ScoreManager ScoreManager; + protected BeatmapDifficultyManager DifficultyManager; + protected SkinManager SkinManager; protected RulesetStore RulesetStore; @@ -194,7 +196,7 @@ namespace osu.Game dependencies.Cache(FileStore = new FileStore(contextFactory, Storage)); // ordering is important here to ensure foreign keys rules are not broken in ModelStore.Cleanup() - dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, () => BeatmapManager, Storage, API, contextFactory, Host)); + dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, () => BeatmapManager, Storage, API, contextFactory, Host, () => DifficultyManager)); dependencies.Cache(BeatmapManager = new BeatmapManager(Storage, contextFactory, RulesetStore, API, Audio, Host, defaultBeatmap)); // this should likely be moved to ArchiveModelManager when another case appers where it is necessary @@ -218,9 +220,8 @@ namespace osu.Game ScoreManager.Undelete(getBeatmapScores(item), true); }); - var difficultyManager = new BeatmapDifficultyManager(); - dependencies.Cache(difficultyManager); - AddInternal(difficultyManager); + dependencies.Cache(DifficultyManager = new BeatmapDifficultyManager()); + AddInternal(DifficultyManager); dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory, RulesetStore)); dependencies.Cache(SettingsStore = new SettingsStore(contextFactory)); diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs index 097ca27bf7..a3500826a0 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs @@ -25,6 +25,9 @@ namespace osu.Game.Overlays.BeatmapSet.Scores private const float row_height = 22; private const int text_size = 12; + [Resolved] + private ScoreManager scoreManager { get; set; } + private readonly FillFlowContainer backgroundFlow; private Color4 highAccuracyColour; @@ -121,7 +124,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores new OsuSpriteText { Margin = new MarginPadding { Right = horizontal_inset }, - Text = $@"{score.TotalScore:N0}", + Text = $@"{scoreManager.GetTotalScore(score):N0}", Font = OsuFont.GetFont(size: text_size, weight: index == 0 ? FontWeight.Bold : FontWeight.Medium) }, new OsuSpriteText diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index eac47aa089..057b1820f7 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -203,19 +203,24 @@ namespace osu.Game.Rulesets.Scoring } private double getScore(ScoringMode mode) + { + return GetScore(baseScore / maxBaseScore, HighestCombo.Value / maxHighestCombo, bonusScore, mode); + } + + public double GetScore(double accuracyRatio, double comboRatio, double bonus, ScoringMode mode) { switch (mode) { default: case ScoringMode.Standardised: - double accuracyScore = accuracyPortion * baseScore / maxBaseScore; - double comboScore = comboPortion * HighestCombo.Value / maxHighestCombo; + double accuracyScore = accuracyPortion * accuracyRatio; + double comboScore = comboPortion * comboRatio; - return (max_score * (accuracyScore + comboScore) + bonusScore) * scoreMultiplier; + return (max_score * (accuracyScore + comboScore) + bonus) * scoreMultiplier; case ScoringMode.Classic: // should emulate osu-stable's scoring as closely as we can (https://osu.ppy.sh/help/wiki/Score/ScoreV1) - return bonusScore + baseScore * (1 + Math.Max(0, HighestCombo.Value - 1) * scoreMultiplier / 25); + return bonus + (accuracyRatio * maxBaseScore) * (1 + Math.Max(0, (comboRatio * maxHighestCombo) - 1) * scoreMultiplier / 25); } } diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index d5bd486e43..a82970d3df 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Linq.Expressions; +using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; using osu.Framework.Logging; using osu.Framework.Platform; @@ -15,6 +16,7 @@ using osu.Game.IO.Archives; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Rulesets; +using osu.Game.Rulesets.Scoring; using osu.Game.Scoring.Legacy; namespace osu.Game.Scoring @@ -30,11 +32,16 @@ namespace osu.Game.Scoring private readonly RulesetStore rulesets; private readonly Func beatmaps; - public ScoreManager(RulesetStore rulesets, Func beatmaps, Storage storage, IAPIProvider api, IDatabaseContextFactory contextFactory, IIpcHost importHost = null) + [CanBeNull] + private readonly Func difficulties; + + public ScoreManager(RulesetStore rulesets, Func beatmaps, Storage storage, IAPIProvider api, IDatabaseContextFactory contextFactory, IIpcHost importHost = null, + Func difficulties = null) : base(storage, contextFactory, api, new ScoreStore(contextFactory, storage), importHost) { this.rulesets = rulesets; this.beatmaps = beatmaps; + this.difficulties = difficulties; } protected override ScoreInfo CreateModel(ArchiveReader archive) @@ -72,5 +79,26 @@ namespace osu.Game.Scoring protected override bool CheckLocalAvailability(ScoreInfo model, IQueryable items) => base.CheckLocalAvailability(model, items) || (model.OnlineScoreID != null && items.Any(i => i.OnlineScoreID == model.OnlineScoreID)); + + public long GetTotalScore(ScoreInfo score) + { + int? beatmapMaxCombo = score.Beatmap.MaxCombo; + + if (beatmapMaxCombo == null) + { + if (score.Beatmap.ID == 0 || difficulties == null) + return score.TotalScore; // Can't do anything. + + // We can compute the max combo locally. + beatmapMaxCombo = difficulties().GetDifficulty(score.Beatmap, score.Ruleset, score.Mods).MaxCombo; + } + + var ruleset = score.Ruleset.CreateInstance(); + var scoreProcessor = ruleset.CreateScoreProcessor(); + + scoreProcessor.Mods.Value = score.Mods; + + return (long)Math.Round(scoreProcessor.GetScore(score.Accuracy, (double)score.MaxCombo / beatmapMaxCombo.Value, 0, ScoringMode.Standardised)); + } } } From 1e5e5cae0cbeee115aa09337051e22c943164893 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 28 Aug 2020 21:34:34 +0900 Subject: [PATCH 04/13] Add support for standardised -> classic changes --- .../Graphics/Sprites/GlowingSpriteText.cs | 7 +++ .../Online/Leaderboards/LeaderboardScore.cs | 2 +- osu.Game/OsuGameBase.cs | 2 +- .../Overlays/BeatmapSet/Scores/ScoreTable.cs | 2 +- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 8 +-- osu.Game/Scoring/ScoreManager.cs | 60 +++++++++++++++---- 6 files changed, 62 insertions(+), 19 deletions(-) diff --git a/osu.Game/Graphics/Sprites/GlowingSpriteText.cs b/osu.Game/Graphics/Sprites/GlowingSpriteText.cs index 4aea5aa518..85df2d167f 100644 --- a/osu.Game/Graphics/Sprites/GlowingSpriteText.cs +++ b/osu.Game/Graphics/Sprites/GlowingSpriteText.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; @@ -55,6 +56,12 @@ namespace osu.Game.Graphics.Sprites set => spriteText.UseFullGlyphHeight = blurredText.UseFullGlyphHeight = value; } + public Bindable Current + { + get => spriteText.Current; + set => spriteText.Current = value; + } + public GlowingSpriteText() { AutoSizeAxes = Axes.Both; diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index 3c7ef73594..24bb43f1b7 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -194,7 +194,7 @@ namespace osu.Game.Online.Leaderboards { TextColour = Color4.White, GlowColour = Color4Extensions.FromHex(@"83ccfa"), - Text = scoreManager.GetTotalScore(score).ToString(@"N0"), + Current = scoreManager.GetTotalScore(score), Font = OsuFont.Numeric.With(size: 23), }, RankContainer = new Container diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 1d92315b1f..3839d4e734 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -196,7 +196,7 @@ namespace osu.Game dependencies.Cache(FileStore = new FileStore(contextFactory, Storage)); // ordering is important here to ensure foreign keys rules are not broken in ModelStore.Cleanup() - dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, () => BeatmapManager, Storage, API, contextFactory, Host, () => DifficultyManager)); + dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, () => BeatmapManager, Storage, API, contextFactory, Host, () => DifficultyManager, LocalConfig)); dependencies.Cache(BeatmapManager = new BeatmapManager(Storage, contextFactory, RulesetStore, API, Audio, Host, defaultBeatmap)); // this should likely be moved to ArchiveModelManager when another case appers where it is necessary diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs index a3500826a0..832ac75882 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs @@ -124,7 +124,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores new OsuSpriteText { Margin = new MarginPadding { Right = horizontal_inset }, - Text = $@"{scoreManager.GetTotalScore(score):N0}", + Current = scoreManager.GetTotalScore(score), Font = OsuFont.GetFont(size: text_size, weight: index == 0 ? FontWeight.Bold : FontWeight.Medium) }, new OsuSpriteText diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 057b1820f7..7d138bd878 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -204,10 +204,10 @@ namespace osu.Game.Rulesets.Scoring private double getScore(ScoringMode mode) { - return GetScore(baseScore / maxBaseScore, HighestCombo.Value / maxHighestCombo, bonusScore, mode); + return GetScore(mode, maxBaseScore, maxHighestCombo, baseScore / maxBaseScore, HighestCombo.Value / maxHighestCombo, bonusScore); } - public double GetScore(double accuracyRatio, double comboRatio, double bonus, ScoringMode mode) + public double GetScore(ScoringMode mode, double maxBaseScore, double maxHighestCombo, double accuracyRatio, double comboRatio, double bonusScore) { switch (mode) { @@ -216,11 +216,11 @@ namespace osu.Game.Rulesets.Scoring double accuracyScore = accuracyPortion * accuracyRatio; double comboScore = comboPortion * comboRatio; - return (max_score * (accuracyScore + comboScore) + bonus) * scoreMultiplier; + return (max_score * (accuracyScore + comboScore) + bonusScore) * scoreMultiplier; case ScoringMode.Classic: // should emulate osu-stable's scoring as closely as we can (https://osu.ppy.sh/help/wiki/Score/ScoreV1) - return bonus + (accuracyRatio * maxBaseScore) * (1 + Math.Max(0, (comboRatio * maxHighestCombo) - 1) * scoreMultiplier / 25); + return bonusScore + (accuracyRatio * maxBaseScore) * (1 + Math.Max(0, (comboRatio * maxHighestCombo) - 1) * scoreMultiplier / 25); } } diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index a82970d3df..134a41a7d4 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -8,9 +8,11 @@ using System.Linq; using System.Linq.Expressions; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using osu.Framework.Bindables; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game.Beatmaps; +using osu.Game.Configuration; using osu.Game.Database; using osu.Game.IO.Archives; using osu.Game.Online.API; @@ -35,13 +37,17 @@ namespace osu.Game.Scoring [CanBeNull] private readonly Func difficulties; + [CanBeNull] + private readonly OsuConfigManager configManager; + public ScoreManager(RulesetStore rulesets, Func beatmaps, Storage storage, IAPIProvider api, IDatabaseContextFactory contextFactory, IIpcHost importHost = null, - Func difficulties = null) + Func difficulties = null, OsuConfigManager configManager = null) : base(storage, contextFactory, api, new ScoreStore(contextFactory, storage), importHost) { this.rulesets = rulesets; this.beatmaps = beatmaps; this.difficulties = difficulties; + this.configManager = configManager; } protected override ScoreInfo CreateModel(ArchiveReader archive) @@ -80,25 +86,55 @@ namespace osu.Game.Scoring => base.CheckLocalAvailability(model, items) || (model.OnlineScoreID != null && items.Any(i => i.OnlineScoreID == model.OnlineScoreID)); - public long GetTotalScore(ScoreInfo score) + public Bindable GetTotalScore(ScoreInfo score) { - int? beatmapMaxCombo = score.Beatmap.MaxCombo; + var bindable = new TotalScoreBindable(score, difficulties); + configManager?.BindWith(OsuSetting.ScoreDisplayMode, bindable.ScoringMode); + return bindable; + } - if (beatmapMaxCombo == null) + private class TotalScoreBindable : Bindable + { + public readonly Bindable ScoringMode = new Bindable(); + + private readonly ScoreInfo score; + private readonly Func difficulties; + + public TotalScoreBindable(ScoreInfo score, Func difficulties) { - if (score.Beatmap.ID == 0 || difficulties == null) - return score.TotalScore; // Can't do anything. + this.score = score; + this.difficulties = difficulties; - // We can compute the max combo locally. - beatmapMaxCombo = difficulties().GetDifficulty(score.Beatmap, score.Ruleset, score.Mods).MaxCombo; + ScoringMode.BindValueChanged(onScoringModeChanged, true); } - var ruleset = score.Ruleset.CreateInstance(); - var scoreProcessor = ruleset.CreateScoreProcessor(); + private void onScoringModeChanged(ValueChangedEvent mode) + { + int? beatmapMaxCombo = score.Beatmap.MaxCombo; - scoreProcessor.Mods.Value = score.Mods; + if (beatmapMaxCombo == null) + { + if (score.Beatmap.ID == 0 || difficulties == null) + { + // We don't have enough information (max combo) to compute the score, so let's use the provided score. + Value = score.TotalScore.ToString("N0"); + return; + } - return (long)Math.Round(scoreProcessor.GetScore(score.Accuracy, (double)score.MaxCombo / beatmapMaxCombo.Value, 0, ScoringMode.Standardised)); + // We can compute the max combo locally. + beatmapMaxCombo = difficulties().GetDifficulty(score.Beatmap, score.Ruleset, score.Mods).MaxCombo; + } + + var ruleset = score.Ruleset.CreateInstance(); + var scoreProcessor = ruleset.CreateScoreProcessor(); + + scoreProcessor.Mods.Value = score.Mods; + + double maxBaseScore = 300 * beatmapMaxCombo.Value; + double maxHighestCombo = beatmapMaxCombo.Value; + + Value = Math.Round(scoreProcessor.GetScore(mode.NewValue, maxBaseScore, maxHighestCombo, score.Accuracy, score.MaxCombo / maxHighestCombo, 0)).ToString("N0"); + } } } } From 39f8b5eb854df85ff989c71e57aad50dfb558bbf Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 28 Aug 2020 21:45:27 +0900 Subject: [PATCH 05/13] Use async difficulty calculation --- osu.Game/Scoring/ScoreManager.cs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index 134a41a7d4..8d3872cda0 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -108,6 +108,8 @@ namespace osu.Game.Scoring ScoringMode.BindValueChanged(onScoringModeChanged, true); } + private IBindable difficultyBindable; + private void onScoringModeChanged(ValueChangedEvent mode) { int? beatmapMaxCombo = score.Beatmap.MaxCombo; @@ -121,19 +123,25 @@ namespace osu.Game.Scoring return; } - // We can compute the max combo locally. - beatmapMaxCombo = difficulties().GetDifficulty(score.Beatmap, score.Ruleset, score.Mods).MaxCombo; + // We can compute the max combo locally after the async beatmap difficulty computation. + difficultyBindable = difficulties().GetBindableDifficulty(score.Beatmap, score.Ruleset, score.Mods); + difficultyBindable.BindValueChanged(d => updateScore(d.NewValue.MaxCombo), true); } + else + updateScore(beatmapMaxCombo.Value); + } + private void updateScore(int beatmapMaxCombo) + { var ruleset = score.Ruleset.CreateInstance(); var scoreProcessor = ruleset.CreateScoreProcessor(); scoreProcessor.Mods.Value = score.Mods; - double maxBaseScore = 300 * beatmapMaxCombo.Value; - double maxHighestCombo = beatmapMaxCombo.Value; + double maxBaseScore = 300 * beatmapMaxCombo; + double maxHighestCombo = beatmapMaxCombo; - Value = Math.Round(scoreProcessor.GetScore(mode.NewValue, maxBaseScore, maxHighestCombo, score.Accuracy, score.MaxCombo / maxHighestCombo, 0)).ToString("N0"); + Value = Math.Round(scoreProcessor.GetScore(ScoringMode.Value, maxBaseScore, maxHighestCombo, score.Accuracy, score.MaxCombo / maxHighestCombo, 0)).ToString("N0"); } } } From 8ffc4309fb14937c57dfad9270968d3488cbc91c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 28 Aug 2020 22:23:44 +0900 Subject: [PATCH 06/13] Fix possible NaN values --- osu.Game/Scoring/ScoreManager.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index 8d3872cda0..0165c5dc82 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -105,6 +105,8 @@ namespace osu.Game.Scoring this.score = score; this.difficulties = difficulties; + Value = "0"; + ScoringMode.BindValueChanged(onScoringModeChanged, true); } @@ -133,6 +135,12 @@ namespace osu.Game.Scoring private void updateScore(int beatmapMaxCombo) { + if (beatmapMaxCombo == 0) + { + Value = "0"; + return; + } + var ruleset = score.Ruleset.CreateInstance(); var scoreProcessor = ruleset.CreateScoreProcessor(); From d7bbb362bf5f0051bbecd4468e63be079a3252f3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 28 Aug 2020 22:51:19 +0900 Subject: [PATCH 07/13] Separate bindables --- .../Online/Leaderboards/LeaderboardScore.cs | 2 +- .../Overlays/BeatmapSet/Scores/ScoreTable.cs | 2 +- osu.Game/Scoring/ScoreManager.cs | 26 ++++++++++++++----- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index 24bb43f1b7..846bebe347 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -194,7 +194,7 @@ namespace osu.Game.Online.Leaderboards { TextColour = Color4.White, GlowColour = Color4Extensions.FromHex(@"83ccfa"), - Current = scoreManager.GetTotalScore(score), + Current = scoreManager.GetTotalScoreString(score), Font = OsuFont.Numeric.With(size: 23), }, RankContainer = new Container diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs index 832ac75882..6bebd98eef 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs @@ -124,7 +124,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores new OsuSpriteText { Margin = new MarginPadding { Right = horizontal_inset }, - Current = scoreManager.GetTotalScore(score), + Current = scoreManager.GetTotalScoreString(score), Font = OsuFont.GetFont(size: text_size, weight: index == 0 ? FontWeight.Bold : FontWeight.Medium) }, new OsuSpriteText diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index 0165c5dc82..1943cab992 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -86,14 +86,16 @@ namespace osu.Game.Scoring => base.CheckLocalAvailability(model, items) || (model.OnlineScoreID != null && items.Any(i => i.OnlineScoreID == model.OnlineScoreID)); - public Bindable GetTotalScore(ScoreInfo score) + public Bindable GetTotalScore(ScoreInfo score) { var bindable = new TotalScoreBindable(score, difficulties); configManager?.BindWith(OsuSetting.ScoreDisplayMode, bindable.ScoringMode); return bindable; } - private class TotalScoreBindable : Bindable + public Bindable GetTotalScoreString(ScoreInfo score) => new TotalScoreStringBindable(GetTotalScore(score)); + + private class TotalScoreBindable : Bindable { public readonly Bindable ScoringMode = new Bindable(); @@ -105,7 +107,7 @@ namespace osu.Game.Scoring this.score = score; this.difficulties = difficulties; - Value = "0"; + Value = 0; ScoringMode.BindValueChanged(onScoringModeChanged, true); } @@ -121,7 +123,7 @@ namespace osu.Game.Scoring if (score.Beatmap.ID == 0 || difficulties == null) { // We don't have enough information (max combo) to compute the score, so let's use the provided score. - Value = score.TotalScore.ToString("N0"); + Value = score.TotalScore; return; } @@ -137,7 +139,7 @@ namespace osu.Game.Scoring { if (beatmapMaxCombo == 0) { - Value = "0"; + Value = 0; return; } @@ -149,7 +151,19 @@ namespace osu.Game.Scoring double maxBaseScore = 300 * beatmapMaxCombo; double maxHighestCombo = beatmapMaxCombo; - Value = Math.Round(scoreProcessor.GetScore(ScoringMode.Value, maxBaseScore, maxHighestCombo, score.Accuracy, score.MaxCombo / maxHighestCombo, 0)).ToString("N0"); + Value = (long)Math.Round(scoreProcessor.GetScore(ScoringMode.Value, maxBaseScore, maxHighestCombo, score.Accuracy, score.MaxCombo / maxHighestCombo, 0)); + } + } + + private class TotalScoreStringBindable : Bindable + { + // ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable (need to hold a reference) + private readonly IBindable totalScore; + + public TotalScoreStringBindable(IBindable totalScore) + { + this.totalScore = totalScore; + this.totalScore.BindValueChanged(v => Value = v.NewValue.ToString("N0"), true); } } } From ec2674e1ea60be3b7a649b38da3ff5ce39eadbe0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 28 Aug 2020 22:51:39 +0900 Subject: [PATCH 08/13] Fix nullref with null beatmap --- osu.Game/Scoring/ScoreManager.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index 1943cab992..634cca159a 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -116,6 +116,12 @@ namespace osu.Game.Scoring private void onScoringModeChanged(ValueChangedEvent mode) { + if (score.Beatmap == null) + { + Value = score.TotalScore; + return; + } + int? beatmapMaxCombo = score.Beatmap.MaxCombo; if (beatmapMaxCombo == null) From c1838902a669f3e8fb7c4b0c1e25bf50dfa36c84 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 28 Aug 2020 22:51:48 +0900 Subject: [PATCH 09/13] Add to more places --- .../Graphics/UserInterface/RollingCounter.cs | 14 ++++++---- .../Scores/TopScoreStatisticsSection.cs | 28 ++++++++++++++++++- .../ContractedPanelMiddleContent.cs | 5 +++- .../Expanded/ExpandedPanelMiddleContent.cs | 9 +++--- 4 files changed, 45 insertions(+), 11 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/RollingCounter.cs b/osu.Game/Graphics/UserInterface/RollingCounter.cs index 6763198213..a469927595 100644 --- a/osu.Game/Graphics/UserInterface/RollingCounter.cs +++ b/osu.Game/Graphics/UserInterface/RollingCounter.cs @@ -9,16 +9,20 @@ using System; using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Graphics.UserInterface; namespace osu.Game.Graphics.UserInterface { - public abstract class RollingCounter : Container + public abstract class RollingCounter : Container, IHasCurrentValue where T : struct, IEquatable { - /// - /// The current value. - /// - public Bindable Current = new Bindable(); + private readonly BindableWithCurrent current = new BindableWithCurrent(); + + public Bindable Current + { + get => current.Current; + set => current.Current = value; + } private SpriteText displayedCountSpriteText; diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs index a92346e0fe..507c692eb1 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -38,6 +39,9 @@ namespace osu.Game.Overlays.BeatmapSet.Scores private readonly FillFlowContainer statisticsColumns; private readonly ModsInfoColumn modsColumn; + [Resolved] + private ScoreManager scoreManager { get; set; } + public TopScoreStatisticsSection() { RelativeSizeAxes = Axes.X; @@ -87,6 +91,15 @@ namespace osu.Game.Overlays.BeatmapSet.Scores }; } + [BackgroundDependencyLoader] + private void load() + { + if (score != null) + totalScoreColumn.Current = scoreManager.GetTotalScoreString(score); + } + + private ScoreInfo score; + /// /// Sets the score to be displayed. /// @@ -94,7 +107,11 @@ namespace osu.Game.Overlays.BeatmapSet.Scores { set { - totalScoreColumn.Text = $@"{value.TotalScore:N0}"; + if (score == value) + return; + + score = value; + accuracyColumn.Text = value.DisplayAccuracy; maxComboColumn.Text = $@"{value.MaxCombo:N0}x"; ppColumn.Alpha = value.Beatmap?.Status == BeatmapSetOnlineStatus.Ranked ? 1 : 0; @@ -102,6 +119,9 @@ namespace osu.Game.Overlays.BeatmapSet.Scores statisticsColumns.ChildrenEnumerable = value.SortedStatistics.Select(kvp => createStatisticsColumn(kvp.Key, kvp.Value)); modsColumn.Mods = value.Mods; + + if (IsLoaded) + totalScoreColumn.Current = scoreManager.GetTotalScoreString(value); } } @@ -190,6 +210,12 @@ namespace osu.Game.Overlays.BeatmapSet.Scores { set => text.Text = value; } + + public Bindable Current + { + get => text.Current; + set => text.Current = value; + } } private class ModsInfoColumn : InfoColumn diff --git a/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs index 8cd0e7025e..b37b89e6c0 100644 --- a/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs @@ -30,6 +30,9 @@ namespace osu.Game.Screens.Ranking.Contracted { private readonly ScoreInfo score; + [Resolved] + private ScoreManager scoreManager { get; set; } + /// /// Creates a new . /// @@ -160,7 +163,7 @@ namespace osu.Game.Screens.Ranking.Contracted { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Text = score.TotalScore.ToString("N0"), + Current = scoreManager.GetTotalScoreString(score), Font = OsuFont.GetFont(size: 20, weight: FontWeight.Medium, fixedWidth: true), Spacing = new Vector2(-1, 0) }, diff --git a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs index 01502c0913..3433410d3c 100644 --- a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs @@ -25,15 +25,16 @@ namespace osu.Game.Screens.Ranking.Expanded /// public class ExpandedPanelMiddleContent : CompositeDrawable { - private readonly ScoreInfo score; + private const float padding = 10; + private readonly ScoreInfo score; private readonly List statisticDisplays = new List(); private FillFlowContainer starAndModDisplay; - private RollingCounter scoreCounter; - private const float padding = 10; + [Resolved] + private ScoreManager scoreManager { get; set; } /// /// Creates a new . @@ -238,7 +239,7 @@ namespace osu.Game.Screens.Ranking.Expanded using (BeginDelayedSequence(AccuracyCircle.ACCURACY_TRANSFORM_DELAY, true)) { scoreCounter.FadeIn(); - scoreCounter.Current.Value = score.TotalScore; + scoreCounter.Current = scoreManager.GetTotalScore(score); double delay = 0; From e271408fca532fd6e1dcd93fc68c380e1b19eab4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 9 Sep 2020 16:51:53 +0900 Subject: [PATCH 10/13] Move max score calculation inside ScoreProcessor --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 8 ++++---- osu.Game/Scoring/ScoreManager.cs | 5 +---- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 7d138bd878..46994d4f18 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Scoring private readonly double accuracyPortion; private readonly double comboPortion; - private double maxHighestCombo; + private int maxHighestCombo; private double maxBaseScore; private double rollingMaxBaseScore; private double baseScore; @@ -204,10 +204,10 @@ namespace osu.Game.Rulesets.Scoring private double getScore(ScoringMode mode) { - return GetScore(mode, maxBaseScore, maxHighestCombo, baseScore / maxBaseScore, HighestCombo.Value / maxHighestCombo, bonusScore); + return GetScore(mode, maxHighestCombo, baseScore / maxBaseScore, (double)HighestCombo.Value / maxHighestCombo, bonusScore); } - public double GetScore(ScoringMode mode, double maxBaseScore, double maxHighestCombo, double accuracyRatio, double comboRatio, double bonusScore) + public double GetScore(ScoringMode mode, int maxCombo, double accuracyRatio, double comboRatio, double bonusScore) { switch (mode) { @@ -220,7 +220,7 @@ namespace osu.Game.Rulesets.Scoring case ScoringMode.Classic: // should emulate osu-stable's scoring as closely as we can (https://osu.ppy.sh/help/wiki/Score/ScoreV1) - return bonusScore + (accuracyRatio * maxBaseScore) * (1 + Math.Max(0, (comboRatio * maxHighestCombo) - 1) * scoreMultiplier / 25); + return bonusScore + (accuracyRatio * maxCombo * 300) * (1 + Math.Max(0, (comboRatio * maxCombo) - 1) * scoreMultiplier / 25); } } diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index 634cca159a..5518c86910 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -154,10 +154,7 @@ namespace osu.Game.Scoring scoreProcessor.Mods.Value = score.Mods; - double maxBaseScore = 300 * beatmapMaxCombo; - double maxHighestCombo = beatmapMaxCombo; - - Value = (long)Math.Round(scoreProcessor.GetScore(ScoringMode.Value, maxBaseScore, maxHighestCombo, score.Accuracy, score.MaxCombo / maxHighestCombo, 0)); + Value = (long)Math.Round(scoreProcessor.GetScore(ScoringMode.Value, beatmapMaxCombo, score.Accuracy, (double)score.MaxCombo / beatmapMaxCombo, 0)); } } From 37a659b2af22ca0246cd9833ed1d21fe37726fa6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 9 Sep 2020 17:04:02 +0900 Subject: [PATCH 11/13] Refactor/add xmldocs --- .../Online/Leaderboards/LeaderboardScore.cs | 2 +- .../Overlays/BeatmapSet/Scores/ScoreTable.cs | 2 +- .../Scores/TopScoreStatisticsSection.cs | 4 +-- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 14 +++++--- osu.Game/Scoring/ScoreManager.cs | 33 ++++++++++++++++--- .../ContractedPanelMiddleContent.cs | 2 +- .../Expanded/ExpandedPanelMiddleContent.cs | 2 +- 7 files changed, 45 insertions(+), 14 deletions(-) diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index 846bebe347..dcd0cb435a 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -194,7 +194,7 @@ namespace osu.Game.Online.Leaderboards { TextColour = Color4.White, GlowColour = Color4Extensions.FromHex(@"83ccfa"), - Current = scoreManager.GetTotalScoreString(score), + Current = scoreManager.GetBindableTotalScoreString(score), Font = OsuFont.Numeric.With(size: 23), }, RankContainer = new Container diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs index 6bebd98eef..56866765b6 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs @@ -124,7 +124,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores new OsuSpriteText { Margin = new MarginPadding { Right = horizontal_inset }, - Current = scoreManager.GetTotalScoreString(score), + Current = scoreManager.GetBindableTotalScoreString(score), Font = OsuFont.GetFont(size: text_size, weight: index == 0 ? FontWeight.Bold : FontWeight.Medium) }, new OsuSpriteText diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs index 507c692eb1..2fd522dc9d 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs @@ -95,7 +95,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores private void load() { if (score != null) - totalScoreColumn.Current = scoreManager.GetTotalScoreString(score); + totalScoreColumn.Current = scoreManager.GetBindableTotalScoreString(score); } private ScoreInfo score; @@ -121,7 +121,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores modsColumn.Mods = value.Mods; if (IsLoaded) - totalScoreColumn.Current = scoreManager.GetTotalScoreString(value); + totalScoreColumn.Current = scoreManager.GetBindableTotalScoreString(value); } } diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 46994d4f18..983f9a3abf 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -202,11 +202,17 @@ namespace osu.Game.Rulesets.Scoring TotalScore.Value = getScore(Mode.Value); } - private double getScore(ScoringMode mode) - { - return GetScore(mode, maxHighestCombo, baseScore / maxBaseScore, (double)HighestCombo.Value / maxHighestCombo, bonusScore); - } + private double getScore(ScoringMode mode) => GetScore(mode, maxHighestCombo, baseScore / maxBaseScore, (double)HighestCombo.Value / maxHighestCombo, bonusScore); + /// + /// Computes the total score. + /// + /// The to compute the total score in. + /// The maximum combo achievable in the beatmap. + /// The accuracy percentage achieved by the player. + /// The proportion of achieved by the player. + /// Any bonus score to be added. + /// The total score. public double GetScore(ScoringMode mode, int maxCombo, double accuracyRatio, double comboRatio, double bonusScore) { switch (mode) diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index 5518c86910..5a6ef6945c 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -86,15 +86,34 @@ namespace osu.Game.Scoring => base.CheckLocalAvailability(model, items) || (model.OnlineScoreID != null && items.Any(i => i.OnlineScoreID == model.OnlineScoreID)); - public Bindable GetTotalScore(ScoreInfo score) + /// + /// Retrieves a bindable that represents the total score of a . + /// + /// + /// Responds to changes in the currently-selected . + /// + /// The to retrieve the bindable for. + /// The bindable containing the total score. + public Bindable GetBindableTotalScore(ScoreInfo score) { var bindable = new TotalScoreBindable(score, difficulties); configManager?.BindWith(OsuSetting.ScoreDisplayMode, bindable.ScoringMode); return bindable; } - public Bindable GetTotalScoreString(ScoreInfo score) => new TotalScoreStringBindable(GetTotalScore(score)); + /// + /// Retrieves a bindable that represents the formatted total score string of a . + /// + /// + /// Responds to changes in the currently-selected . + /// + /// The to retrieve the bindable for. + /// The bindable containing the formatted total score string. + public Bindable GetBindableTotalScoreString(ScoreInfo score) => new TotalScoreStringBindable(GetBindableTotalScore(score)); + /// + /// Provides the total score of a . Responds to changes in the currently-selected . + /// private class TotalScoreBindable : Bindable { public readonly Bindable ScoringMode = new Bindable(); @@ -102,13 +121,16 @@ namespace osu.Game.Scoring private readonly ScoreInfo score; private readonly Func difficulties; + /// + /// Creates a new . + /// + /// The to provide the total score of. + /// A function to retrieve the . public TotalScoreBindable(ScoreInfo score, Func difficulties) { this.score = score; this.difficulties = difficulties; - Value = 0; - ScoringMode.BindValueChanged(onScoringModeChanged, true); } @@ -158,6 +180,9 @@ namespace osu.Game.Scoring } } + /// + /// Provides the total score of a as a formatted string. Responds to changes in the currently-selected . + /// private class TotalScoreStringBindable : Bindable { // ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable (need to hold a reference) diff --git a/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs index b37b89e6c0..0b85eeafa8 100644 --- a/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs @@ -163,7 +163,7 @@ namespace osu.Game.Screens.Ranking.Contracted { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Current = scoreManager.GetTotalScoreString(score), + Current = scoreManager.GetBindableTotalScoreString(score), Font = OsuFont.GetFont(size: 20, weight: FontWeight.Medium, fixedWidth: true), Spacing = new Vector2(-1, 0) }, diff --git a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs index 3433410d3c..0033cd1f43 100644 --- a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs @@ -239,7 +239,7 @@ namespace osu.Game.Screens.Ranking.Expanded using (BeginDelayedSequence(AccuracyCircle.ACCURACY_TRANSFORM_DELAY, true)) { scoreCounter.FadeIn(); - scoreCounter.Current = scoreManager.GetTotalScore(score); + scoreCounter.Current = scoreManager.GetBindableTotalScore(score); double delay = 0; From 5cdc8d2e7b46c092031a7a0a7daf8f645cae4c13 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 9 Sep 2020 17:37:11 +0900 Subject: [PATCH 12/13] Add cancellation support --- osu.Game/Scoring/ScoreManager.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index 5a6ef6945c..619ca76598 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Linq.Expressions; +using System.Threading; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; using osu.Framework.Bindables; @@ -135,9 +136,13 @@ namespace osu.Game.Scoring } private IBindable difficultyBindable; + private CancellationTokenSource difficultyCancellationSource; private void onScoringModeChanged(ValueChangedEvent mode) { + difficultyCancellationSource?.Cancel(); + difficultyCancellationSource = null; + if (score.Beatmap == null) { Value = score.TotalScore; @@ -156,7 +161,7 @@ namespace osu.Game.Scoring } // We can compute the max combo locally after the async beatmap difficulty computation. - difficultyBindable = difficulties().GetBindableDifficulty(score.Beatmap, score.Ruleset, score.Mods); + difficultyBindable = difficulties().GetBindableDifficulty(score.Beatmap, score.Ruleset, score.Mods, (difficultyCancellationSource = new CancellationTokenSource()).Token); difficultyBindable.BindValueChanged(d => updateScore(d.NewValue.MaxCombo), true); } else From d7ca2cf1cca15da8fe476dfd4292d5bbd77cd167 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 9 Sep 2020 22:01:09 +0900 Subject: [PATCH 13/13] Replace loaded check with better variation --- .../Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs index 2fd522dc9d..3a842d0a43 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs @@ -120,7 +120,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores statisticsColumns.ChildrenEnumerable = value.SortedStatistics.Select(kvp => createStatisticsColumn(kvp.Key, kvp.Value)); modsColumn.Mods = value.Mods; - if (IsLoaded) + if (scoreManager != null) totalScoreColumn.Current = scoreManager.GetBindableTotalScoreString(value); } }