1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-11 12:57:36 +08:00

Add a background beatmap difficulty manager

This commit is contained in:
smoogipoo 2020-07-16 20:38:33 +09:00
parent 5049977395
commit 6df1b1d9ea
2 changed files with 100 additions and 0 deletions

View File

@ -0,0 +1,99 @@
// 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.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
using osu.Framework.Threading;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Beatmaps
{
public class BeatmapDifficultyManager : CompositeDrawable
{
// Too many simultaneous updates can lead to stutters. One thread seems to work fine for song select display purposes.
private readonly ThreadedTaskScheduler updateScheduler = new ThreadedTaskScheduler(1, nameof(BeatmapDifficultyManager));
private readonly TimedExpiryCache<DifficultyCacheLookup, double> difficultyCache = new TimedExpiryCache<DifficultyCacheLookup, double> { ExpiryTime = 60000 };
private readonly BeatmapManager beatmapManager;
public BeatmapDifficultyManager(BeatmapManager beatmapManager)
{
this.beatmapManager = beatmapManager;
}
public Task<double> GetDifficultyAsync([NotNull] BeatmapInfo beatmapInfo, [CanBeNull] RulesetInfo rulesetInfo = null, [CanBeNull] IReadOnlyList<Mod> mods = null,
CancellationToken cancellationToken = default)
=> Task.Factory.StartNew(() => GetDifficulty(beatmapInfo, rulesetInfo, mods), cancellationToken,
TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously,
updateScheduler);
public double GetDifficulty([NotNull] BeatmapInfo beatmapInfo, [CanBeNull] RulesetInfo rulesetInfo = null, [CanBeNull] IReadOnlyList<Mod> mods = null)
{
// Difficulty can only be computed if the beatmap is locally available.
if (beatmapInfo.ID == 0)
return 0;
// In the case that the user hasn't given us a ruleset, use the beatmap's default ruleset.
rulesetInfo ??= beatmapInfo.Ruleset;
var key = new DifficultyCacheLookup(beatmapInfo, rulesetInfo, mods);
if (difficultyCache.TryGetValue(key, out var existing))
return existing;
try
{
var ruleset = rulesetInfo.CreateInstance();
Debug.Assert(ruleset != null);
var calculator = ruleset.CreateDifficultyCalculator(beatmapManager.GetWorkingBeatmap(beatmapInfo));
var attributes = calculator.Calculate(mods?.ToArray() ?? Array.Empty<Mod>());
difficultyCache.Add(key, attributes.StarRating);
return attributes.StarRating;
}
catch
{
difficultyCache.Add(key, 0);
return 0;
}
}
private readonly struct DifficultyCacheLookup : IEquatable<DifficultyCacheLookup>
{
private readonly BeatmapInfo beatmapInfo;
private readonly RulesetInfo rulesetInfo;
private readonly IReadOnlyList<Mod> mods;
public DifficultyCacheLookup(BeatmapInfo beatmapInfo, RulesetInfo rulesetInfo, IEnumerable<Mod> mods)
{
this.beatmapInfo = beatmapInfo;
this.rulesetInfo = rulesetInfo;
this.mods = mods?.OrderBy(m => m.Acronym).ToArray() ?? Array.Empty<Mod>();
}
public bool Equals(DifficultyCacheLookup other)
=> beatmapInfo.Equals(other.beatmapInfo)
&& mods.SequenceEqual(other.mods);
public override int GetHashCode()
{
var hashCode = new HashCode();
hashCode.Add(beatmapInfo.Hash);
hashCode.Add(rulesetInfo.GetHashCode());
foreach (var mod in mods)
hashCode.Add(mod.Acronym);
return hashCode.ToHashCode();
}
}
}
}

View File

@ -199,6 +199,7 @@ namespace osu.Game
ScoreManager.Undelete(getBeatmapScores(item), true);
});
dependencies.Cache(new BeatmapDifficultyManager(BeatmapManager));
dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory, RulesetStore));
dependencies.Cache(SettingsStore = new SettingsStore(contextFactory));
dependencies.Cache(RulesetConfigCache = new RulesetConfigCache(SettingsStore));