mirror of
https://github.com/ppy/osu.git
synced 2025-02-11 09:25:35 +08:00
Compute maximum performance along with difficulty
This commit is contained in:
parent
f9a9ceb41c
commit
d195a69447
@ -4,12 +4,15 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Audio.Track;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
|
using osu.Framework.Graphics.Textures;
|
||||||
using osu.Framework.Lists;
|
using osu.Framework.Lists;
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
using osu.Framework.Threading;
|
using osu.Framework.Threading;
|
||||||
@ -18,7 +21,12 @@ using osu.Game.Database;
|
|||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Difficulty;
|
using osu.Game.Rulesets.Difficulty;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
using osu.Game.Storyboards;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps
|
namespace osu.Game.Beatmaps
|
||||||
{
|
{
|
||||||
@ -237,10 +245,13 @@ namespace osu.Game.Beatmaps
|
|||||||
var ruleset = rulesetInfo.CreateInstance();
|
var ruleset = rulesetInfo.CreateInstance();
|
||||||
Debug.Assert(ruleset != null);
|
Debug.Assert(ruleset != null);
|
||||||
|
|
||||||
var calculator = ruleset.CreateDifficultyCalculator(beatmapManager.GetWorkingBeatmap(key.BeatmapInfo));
|
PlayableCachedWorkingBeatmap working = new PlayableCachedWorkingBeatmap(beatmapManager.GetWorkingBeatmap(key.BeatmapInfo));
|
||||||
var attributes = calculator.Calculate(key.OrderedMods, cancellationToken);
|
|
||||||
|
|
||||||
return new StarDifficulty(attributes);
|
var difficulty = ruleset.CreateDifficultyCalculator(working).Calculate(key.OrderedMods, cancellationToken);
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
var performance = computeMaxPerformance(working, key.BeatmapInfo, ruleset, key.OrderedMods, difficulty);
|
||||||
|
|
||||||
|
return new StarDifficulty(difficulty, performance);
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
@ -262,6 +273,60 @@ namespace osu.Game.Beatmaps
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static PerformanceAttributes computeMaxPerformance(IWorkingBeatmap working, BeatmapInfo beatmap, Ruleset ruleset, Mod[] mods, DifficultyAttributes attributes)
|
||||||
|
{
|
||||||
|
var performanceCalculator = ruleset.CreatePerformanceCalculator();
|
||||||
|
if (performanceCalculator == null)
|
||||||
|
return new PerformanceAttributes();
|
||||||
|
|
||||||
|
IBeatmap playableBeatmap = working.GetPlayableBeatmap(ruleset.RulesetInfo, mods);
|
||||||
|
|
||||||
|
// create statistics assuming all hit objects have perfect hit result
|
||||||
|
var statistics = playableBeatmap.HitObjects
|
||||||
|
.SelectMany(getPerfectHitResults)
|
||||||
|
.GroupBy(hr => hr, (hr, list) => (hitResult: hr, count: list.Count()))
|
||||||
|
.ToDictionary(pair => pair.hitResult, pair => pair.count);
|
||||||
|
|
||||||
|
// compute maximum total score
|
||||||
|
ScoreProcessor scoreProcessor = ruleset.CreateScoreProcessor();
|
||||||
|
scoreProcessor.Mods.Value = mods;
|
||||||
|
scoreProcessor.ApplyBeatmap(playableBeatmap);
|
||||||
|
long maxScore = scoreProcessor.MaximumTotalScore;
|
||||||
|
|
||||||
|
// todo: Get max combo from difficulty calculator instead when diffcalc properly supports lazer-first scores
|
||||||
|
int maxCombo = calculateMaxCombo(playableBeatmap);
|
||||||
|
|
||||||
|
// compute maximum rank - default to SS, then adjust the rank with mods
|
||||||
|
ScoreRank maxRank = ScoreRank.X;
|
||||||
|
foreach (IApplicableToScoreProcessor mod in mods.OfType<IApplicableToScoreProcessor>())
|
||||||
|
maxRank = mod.AdjustRank(maxRank, 1);
|
||||||
|
|
||||||
|
ScoreInfo perfectScore = new ScoreInfo(beatmap, ruleset.RulesetInfo)
|
||||||
|
{
|
||||||
|
Accuracy = 1,
|
||||||
|
Passed = true,
|
||||||
|
MaxCombo = maxCombo,
|
||||||
|
Combo = maxCombo,
|
||||||
|
Mods = mods,
|
||||||
|
TotalScore = maxScore,
|
||||||
|
Statistics = statistics,
|
||||||
|
MaximumStatistics = statistics
|
||||||
|
};
|
||||||
|
|
||||||
|
return performanceCalculator.Calculate(perfectScore, attributes);
|
||||||
|
|
||||||
|
static int calculateMaxCombo(IBeatmap beatmap)
|
||||||
|
=> beatmap.HitObjects.SelectMany(getPerfectHitResults).Count(r => r.AffectsCombo());
|
||||||
|
|
||||||
|
static IEnumerable<HitResult> getPerfectHitResults(HitObject hitObject)
|
||||||
|
{
|
||||||
|
foreach (HitObject nested in hitObject.NestedHitObjects)
|
||||||
|
yield return nested.Judgement.MaxResult;
|
||||||
|
|
||||||
|
yield return hitObject.Judgement.MaxResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
{
|
{
|
||||||
base.Dispose(isDisposing);
|
base.Dispose(isDisposing);
|
||||||
@ -276,7 +341,6 @@ namespace osu.Game.Beatmaps
|
|||||||
{
|
{
|
||||||
public readonly BeatmapInfo BeatmapInfo;
|
public readonly BeatmapInfo BeatmapInfo;
|
||||||
public readonly RulesetInfo Ruleset;
|
public readonly RulesetInfo Ruleset;
|
||||||
|
|
||||||
public readonly Mod[] OrderedMods;
|
public readonly Mod[] OrderedMods;
|
||||||
|
|
||||||
public DifficultyCacheLookup(BeatmapInfo beatmapInfo, RulesetInfo? ruleset, IEnumerable<Mod>? mods)
|
public DifficultyCacheLookup(BeatmapInfo beatmapInfo, RulesetInfo? ruleset, IEnumerable<Mod>? mods)
|
||||||
@ -317,5 +381,42 @@ namespace osu.Game.Beatmaps
|
|||||||
CancellationToken = cancellationToken;
|
CancellationToken = cancellationToken;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A working beatmap that caches its playable representation.
|
||||||
|
/// This is intended as single-use for when it is guaranteed that the playable beatmap can be reused.
|
||||||
|
/// </summary>
|
||||||
|
private class PlayableCachedWorkingBeatmap : IWorkingBeatmap
|
||||||
|
{
|
||||||
|
private readonly IWorkingBeatmap working;
|
||||||
|
private IBeatmap? playable;
|
||||||
|
|
||||||
|
public PlayableCachedWorkingBeatmap(IWorkingBeatmap working)
|
||||||
|
{
|
||||||
|
this.working = working;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IBeatmap GetPlayableBeatmap(IRulesetInfo ruleset, IReadOnlyList<Mod> mods)
|
||||||
|
=> playable ??= working.GetPlayableBeatmap(ruleset, mods);
|
||||||
|
|
||||||
|
public IBeatmap GetPlayableBeatmap(IRulesetInfo ruleset, IReadOnlyList<Mod> mods, CancellationToken cancellationToken)
|
||||||
|
=> playable ??= working.GetPlayableBeatmap(ruleset, mods, cancellationToken);
|
||||||
|
|
||||||
|
IBeatmapInfo IWorkingBeatmap.BeatmapInfo => working.BeatmapInfo;
|
||||||
|
bool IWorkingBeatmap.BeatmapLoaded => working.BeatmapLoaded;
|
||||||
|
bool IWorkingBeatmap.TrackLoaded => working.TrackLoaded;
|
||||||
|
IBeatmap IWorkingBeatmap.Beatmap => working.Beatmap;
|
||||||
|
Texture IWorkingBeatmap.GetBackground() => working.GetBackground();
|
||||||
|
Texture IWorkingBeatmap.GetPanelBackground() => working.GetPanelBackground();
|
||||||
|
Waveform IWorkingBeatmap.Waveform => working.Waveform;
|
||||||
|
Storyboard IWorkingBeatmap.Storyboard => working.Storyboard;
|
||||||
|
ISkin IWorkingBeatmap.Skin => working.Skin;
|
||||||
|
Track IWorkingBeatmap.Track => working.Track;
|
||||||
|
Track IWorkingBeatmap.LoadTrack() => working.LoadTrack();
|
||||||
|
Stream IWorkingBeatmap.GetStream(string storagePath) => working.GetStream(storagePath);
|
||||||
|
void IWorkingBeatmap.BeginAsyncLoad() => working.BeginAsyncLoad();
|
||||||
|
void IWorkingBeatmap.CancelAsyncLoad() => working.CancelAsyncLoad();
|
||||||
|
void IWorkingBeatmap.PrepareTrackForPreview(bool looping, double offsetFromPreviewPoint) => working.PrepareTrackForPreview(looping, offsetFromPreviewPoint);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,29 +26,36 @@ namespace osu.Game.Beatmaps
|
|||||||
/// Might not be available if the star difficulty is associated with a beatmap that's not locally available.
|
/// Might not be available if the star difficulty is associated with a beatmap that's not locally available.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[CanBeNull]
|
[CanBeNull]
|
||||||
public readonly DifficultyAttributes Attributes;
|
public readonly DifficultyAttributes DifficultyAttributes;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a <see cref="StarDifficulty"/> structure based on <see cref="DifficultyAttributes"/> computed
|
/// The performance attributes computed for a perfect score on the given beatmap.
|
||||||
/// by a <see cref="DifficultyCalculator"/>.
|
/// Might not be available if the star difficulty is associated with a beatmap that's not locally available.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public StarDifficulty([NotNull] DifficultyAttributes attributes)
|
[CanBeNull]
|
||||||
|
public readonly PerformanceAttributes PerformanceAttributes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <see cref="StarDifficulty"/> structure.
|
||||||
|
/// </summary>
|
||||||
|
public StarDifficulty([NotNull] DifficultyAttributes difficulty, [NotNull] PerformanceAttributes performance)
|
||||||
{
|
{
|
||||||
Stars = double.IsFinite(attributes.StarRating) ? attributes.StarRating : 0;
|
Stars = double.IsFinite(difficulty.StarRating) ? difficulty.StarRating : 0;
|
||||||
MaxCombo = attributes.MaxCombo;
|
MaxCombo = difficulty.MaxCombo;
|
||||||
Attributes = attributes;
|
DifficultyAttributes = difficulty;
|
||||||
|
PerformanceAttributes = performance;
|
||||||
// Todo: Add more members (BeatmapInfo.DifficultyRating? Attributes? Etc...)
|
// Todo: Add more members (BeatmapInfo.DifficultyRating? Attributes? Etc...)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a <see cref="StarDifficulty"/> structure with a pre-populated star difficulty and max combo
|
/// Creates a <see cref="StarDifficulty"/> structure with a pre-populated star difficulty and max combo
|
||||||
/// in scenarios where computing <see cref="DifficultyAttributes"/> is not feasible (i.e. when working with online sources).
|
/// in scenarios where computing <see cref="Rulesets.Difficulty.DifficultyAttributes"/> is not feasible (i.e. when working with online sources).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public StarDifficulty(double starDifficulty, int maxCombo)
|
public StarDifficulty(double starDifficulty, int maxCombo)
|
||||||
{
|
{
|
||||||
Stars = double.IsFinite(starDifficulty) ? starDifficulty : 0;
|
Stars = double.IsFinite(starDifficulty) ? starDifficulty : 0;
|
||||||
MaxCombo = maxCombo;
|
MaxCombo = maxCombo;
|
||||||
Attributes = null;
|
DifficultyAttributes = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DifficultyRating DifficultyRating => GetDifficultyRating(Stars);
|
public DifficultyRating DifficultyRating => GetDifficultyRating(Stars);
|
||||||
|
@ -1,121 +0,0 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using JetBrains.Annotations;
|
|
||||||
using osu.Framework.Extensions.ObjectExtensions;
|
|
||||||
using osu.Game.Beatmaps;
|
|
||||||
using osu.Game.Rulesets.Mods;
|
|
||||||
using osu.Game.Rulesets.Objects;
|
|
||||||
using osu.Game.Rulesets.Scoring;
|
|
||||||
using osu.Game.Scoring;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Difficulty
|
|
||||||
{
|
|
||||||
public class PerformanceBreakdownCalculator
|
|
||||||
{
|
|
||||||
private readonly IBeatmap playableBeatmap;
|
|
||||||
private readonly BeatmapDifficultyCache difficultyCache;
|
|
||||||
|
|
||||||
public PerformanceBreakdownCalculator(IBeatmap playableBeatmap, BeatmapDifficultyCache difficultyCache)
|
|
||||||
{
|
|
||||||
this.playableBeatmap = playableBeatmap;
|
|
||||||
this.difficultyCache = difficultyCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
[ItemCanBeNull]
|
|
||||||
public async Task<PerformanceBreakdown> CalculateAsync(ScoreInfo score, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
var attributes = await difficultyCache.GetDifficultyAsync(score.BeatmapInfo!, score.Ruleset, score.Mods, cancellationToken).ConfigureAwait(false);
|
|
||||||
|
|
||||||
var performanceCalculator = score.Ruleset.CreateInstance().CreatePerformanceCalculator();
|
|
||||||
|
|
||||||
// Performance calculation requires the beatmap and ruleset to be locally available. If not, return a default value.
|
|
||||||
if (attributes?.Attributes == null || performanceCalculator == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
|
||||||
|
|
||||||
PerformanceAttributes[] performanceArray = await Task.WhenAll(
|
|
||||||
// compute actual performance
|
|
||||||
performanceCalculator.CalculateAsync(score, attributes.Value.Attributes, cancellationToken),
|
|
||||||
// compute performance for perfect play
|
|
||||||
getPerfectPerformance(score, cancellationToken)
|
|
||||||
).ConfigureAwait(false);
|
|
||||||
|
|
||||||
return new PerformanceBreakdown(performanceArray[0] ?? new PerformanceAttributes(), performanceArray[1] ?? new PerformanceAttributes());
|
|
||||||
}
|
|
||||||
|
|
||||||
[ItemCanBeNull]
|
|
||||||
private Task<PerformanceAttributes> getPerfectPerformance(ScoreInfo score, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
return Task.Run(async () =>
|
|
||||||
{
|
|
||||||
Ruleset ruleset = score.Ruleset.CreateInstance();
|
|
||||||
ScoreInfo perfectPlay = score.DeepClone();
|
|
||||||
perfectPlay.Accuracy = 1;
|
|
||||||
perfectPlay.Passed = true;
|
|
||||||
|
|
||||||
// calculate max combo
|
|
||||||
// todo: Get max combo from difficulty calculator instead when diffcalc properly supports lazer-first scores
|
|
||||||
perfectPlay.MaxCombo = calculateMaxCombo(playableBeatmap);
|
|
||||||
|
|
||||||
// create statistics assuming all hit objects have perfect hit result
|
|
||||||
var statistics = playableBeatmap.HitObjects
|
|
||||||
.SelectMany(getPerfectHitResults)
|
|
||||||
.GroupBy(hr => hr, (hr, list) => (hitResult: hr, count: list.Count()))
|
|
||||||
.ToDictionary(pair => pair.hitResult, pair => pair.count);
|
|
||||||
perfectPlay.Statistics = statistics;
|
|
||||||
perfectPlay.MaximumStatistics = statistics;
|
|
||||||
|
|
||||||
// calculate total score
|
|
||||||
ScoreProcessor scoreProcessor = ruleset.CreateScoreProcessor();
|
|
||||||
scoreProcessor.Mods.Value = perfectPlay.Mods;
|
|
||||||
scoreProcessor.ApplyBeatmap(playableBeatmap);
|
|
||||||
perfectPlay.TotalScore = scoreProcessor.MaximumTotalScore;
|
|
||||||
|
|
||||||
// compute rank achieved
|
|
||||||
// default to SS, then adjust the rank with mods
|
|
||||||
perfectPlay.Rank = ScoreRank.X;
|
|
||||||
|
|
||||||
foreach (IApplicableToScoreProcessor mod in perfectPlay.Mods.OfType<IApplicableToScoreProcessor>())
|
|
||||||
{
|
|
||||||
perfectPlay.Rank = mod.AdjustRank(perfectPlay.Rank, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// calculate performance for this perfect score
|
|
||||||
var difficulty = await difficultyCache.GetDifficultyAsync(
|
|
||||||
playableBeatmap.BeatmapInfo,
|
|
||||||
score.Ruleset,
|
|
||||||
score.Mods,
|
|
||||||
cancellationToken
|
|
||||||
).ConfigureAwait(false);
|
|
||||||
|
|
||||||
var performanceCalculator = ruleset.CreatePerformanceCalculator();
|
|
||||||
|
|
||||||
if (performanceCalculator == null || difficulty == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return await performanceCalculator.CalculateAsync(perfectPlay, difficulty.Value.Attributes.AsNonNull(), cancellationToken).ConfigureAwait(false);
|
|
||||||
}, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int calculateMaxCombo(IBeatmap beatmap)
|
|
||||||
{
|
|
||||||
return beatmap.HitObjects.SelectMany(getPerfectHitResults).Count(r => r.AffectsCombo());
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerable<HitResult> getPerfectHitResults(HitObject hitObject)
|
|
||||||
{
|
|
||||||
foreach (HitObject nested in hitObject.NestedHitObjects)
|
|
||||||
yield return nested.Judgement.MaxResult;
|
|
||||||
|
|
||||||
yield return hitObject.Judgement.MaxResult;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -53,10 +53,10 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics
|
|||||||
var performanceCalculator = score.Ruleset.CreateInstance().CreatePerformanceCalculator();
|
var performanceCalculator = score.Ruleset.CreateInstance().CreatePerformanceCalculator();
|
||||||
|
|
||||||
// Performance calculation requires the beatmap and ruleset to be locally available. If not, return a default value.
|
// Performance calculation requires the beatmap and ruleset to be locally available. If not, return a default value.
|
||||||
if (attributes?.Attributes == null || performanceCalculator == null)
|
if (attributes?.DifficultyAttributes == null || performanceCalculator == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var result = await performanceCalculator.CalculateAsync(score, attributes.Value.Attributes, cancellationToken ?? default).ConfigureAwait(false);
|
var result = await performanceCalculator.CalculateAsync(score, attributes.Value.DifficultyAttributes, cancellationToken ?? default).ConfigureAwait(false);
|
||||||
|
|
||||||
Schedule(() => setPerformanceValue(score, result.Total));
|
Schedule(() => setPerformanceValue(score, result.Total));
|
||||||
}, cancellationToken ?? default);
|
}, cancellationToken ?? default);
|
||||||
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
@ -26,7 +27,6 @@ namespace osu.Game.Screens.Ranking.Statistics
|
|||||||
public partial class PerformanceBreakdownChart : Container
|
public partial class PerformanceBreakdownChart : Container
|
||||||
{
|
{
|
||||||
private readonly ScoreInfo score;
|
private readonly ScoreInfo score;
|
||||||
private readonly IBeatmap playableBeatmap;
|
|
||||||
|
|
||||||
private Drawable spinner = null!;
|
private Drawable spinner = null!;
|
||||||
private Drawable content = null!;
|
private Drawable content = null!;
|
||||||
@ -42,7 +42,6 @@ namespace osu.Game.Screens.Ranking.Statistics
|
|||||||
public PerformanceBreakdownChart(ScoreInfo score, IBeatmap playableBeatmap)
|
public PerformanceBreakdownChart(ScoreInfo score, IBeatmap playableBeatmap)
|
||||||
{
|
{
|
||||||
this.score = score;
|
this.score = score;
|
||||||
this.playableBeatmap = playableBeatmap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -142,12 +141,33 @@ namespace osu.Game.Screens.Ranking.Statistics
|
|||||||
|
|
||||||
spinner.Show();
|
spinner.Show();
|
||||||
|
|
||||||
new PerformanceBreakdownCalculator(playableBeatmap, difficultyCache)
|
computePerformance(cancellationTokenSource.Token)
|
||||||
.CalculateAsync(score, cancellationTokenSource.Token)
|
.ContinueWith(t => Schedule(() =>
|
||||||
.ContinueWith(t => Schedule(() => setPerformanceValue(t.GetResultSafely()!)));
|
{
|
||||||
|
if (t.GetResultSafely() is PerformanceBreakdown breakdown)
|
||||||
|
setPerformance(breakdown);
|
||||||
|
}), TaskContinuationOptions.OnlyOnRanToCompletion);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setPerformanceValue(PerformanceBreakdown breakdown)
|
private async Task<PerformanceBreakdown?> computePerformance(CancellationToken token)
|
||||||
|
{
|
||||||
|
var performanceCalculator = score.Ruleset.CreateInstance().CreatePerformanceCalculator();
|
||||||
|
if (performanceCalculator == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var starsTask = difficultyCache.GetDifficultyAsync(score.BeatmapInfo!, score.Ruleset, score.Mods, token).ConfigureAwait(false);
|
||||||
|
if (await starsTask is not StarDifficulty stars)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (stars.DifficultyAttributes == null || stars.PerformanceAttributes == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return new PerformanceBreakdown(
|
||||||
|
await performanceCalculator.CalculateAsync(score, stars.DifficultyAttributes, token).ConfigureAwait(false),
|
||||||
|
stars.PerformanceAttributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setPerformance(PerformanceBreakdown breakdown)
|
||||||
{
|
{
|
||||||
spinner.Hide();
|
spinner.Hide();
|
||||||
content.FadeIn(200);
|
content.FadeIn(200);
|
||||||
@ -236,6 +256,8 @@ namespace osu.Game.Screens.Ranking.Statistics
|
|||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
{
|
{
|
||||||
cancellationTokenSource.Cancel();
|
cancellationTokenSource.Cancel();
|
||||||
|
cancellationTokenSource.Dispose();
|
||||||
|
|
||||||
base.Dispose(isDisposing);
|
base.Dispose(isDisposing);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user