Skip to content

Commit b2de06b

Browse files
committed
Implement lazy timed difficulty calculation
1 parent c9a50b4 commit b2de06b

File tree

1 file changed

+43
-15
lines changed

1 file changed

+43
-15
lines changed

osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ public DifficultyAttributes Calculate([NotNull] IEnumerable<Mod> mods, Cancellat
9494
/// <summary>
9595
/// Calculates the difficulty of the beatmap with no mods applied and returns a set of <see cref="TimedDifficultyAttributes"/> representing the difficulty at every relevant time value in the beatmap.
9696
/// </summary>
97+
/// <remarks>
98+
/// Unless a <paramref name="cancellationToken"/> is specified, calculation times out after 10 seconds.
99+
/// </remarks>
97100
/// <param name="cancellationToken">The cancellation token.</param>
98101
/// <returns>The set of <see cref="TimedDifficultyAttributes"/>.</returns>
99102
public List<TimedDifficultyAttributes> CalculateTimed(CancellationToken cancellationToken = default)
@@ -102,50 +105,75 @@ public List<TimedDifficultyAttributes> CalculateTimed(CancellationToken cancella
102105
/// <summary>
103106
/// Calculates the difficulty of the beatmap using a specific mod combination and returns a set of <see cref="TimedDifficultyAttributes"/> representing the difficulty at every relevant time value in the beatmap.
104107
/// </summary>
108+
/// <remarks>
109+
/// Unless a <paramref name="cancellationToken"/> is specified, calculation times out after 10 seconds.
110+
/// </remarks>
105111
/// <param name="mods">The mods that should be applied to the beatmap.</param>
106112
/// <param name="cancellationToken">The cancellation token.</param>
107113
/// <returns>The set of <see cref="TimedDifficultyAttributes"/>.</returns>
108114
public List<TimedDifficultyAttributes> CalculateTimed([NotNull] IEnumerable<Mod> mods, CancellationToken cancellationToken = default)
109115
{
116+
List<TimedDifficultyAttributes> attribs = [];
110117
using var timedCancellationSource = new CancellationTokenSource(TimeSpan.FromSeconds(10));
111118

112119
if (!cancellationToken.CanBeCanceled)
113120
cancellationToken = timedCancellationSource.Token;
114121

122+
foreach (var timedAttr in CalculateTimedLazy(mods, cancellationToken))
123+
{
124+
attribs.Add(timedAttr);
125+
}
126+
127+
return attribs;
128+
}
129+
130+
/// <summary>
131+
/// Lazily calculates the difficulty of the beatmap on-demand using a specific mod combination and yields <see cref="TimedDifficultyAttributes"/> representing the difficulty until every relevant time value in the beatmap.
132+
/// </summary>
133+
/// <remarks>
134+
/// 1. Pre-processing is done before this method returns.<br />
135+
/// 2. Cancelling the <paramref name="cancellationToken"/> will throw while enumerating.
136+
/// </remarks>
137+
/// <param name="mods">The mods that should be applied to the beatmap.</param>
138+
/// <param name="cancellationToken">The cancellation token.</param>
139+
/// <returns>The enumerated <see cref="TimedDifficultyAttributes"/>.</returns>
140+
public IEnumerable<TimedDifficultyAttributes> CalculateTimedLazy([NotNull] IEnumerable<Mod> mods, CancellationToken cancellationToken = default)
141+
{
115142
cancellationToken.ThrowIfCancellationRequested();
116143
// ReSharper disable once PossiblyMistakenUseOfCancellationToken
117144
preProcess(mods, cancellationToken);
118145

119-
var attribs = new List<TimedDifficultyAttributes>();
120-
121146
if (!Beatmap.HitObjects.Any())
122-
return attribs;
147+
return [];
123148

124149
var skills = CreateSkills(Beatmap, playableMods, clockRate);
125150
var progressiveBeatmap = new ProgressiveCalculationBeatmap(Beatmap);
126151
var difficultyObjects = getDifficultyHitObjects().ToArray();
127152

128-
int currentIndex = 0;
153+
return enumerate();
129154

130-
foreach (var obj in Beatmap.HitObjects)
155+
IEnumerable<TimedDifficultyAttributes> enumerate()
131156
{
132-
progressiveBeatmap.HitObjects.Add(obj);
157+
int currentIndex = 0;
133158

134-
while (currentIndex < difficultyObjects.Length && difficultyObjects[currentIndex].BaseObject.GetEndTime() <= obj.GetEndTime())
159+
foreach (var obj in Beatmap.HitObjects)
135160
{
136-
foreach (var skill in skills)
161+
progressiveBeatmap.HitObjects.Add(obj);
162+
163+
while (currentIndex < difficultyObjects.Length && difficultyObjects[currentIndex].BaseObject.GetEndTime() <= obj.GetEndTime())
137164
{
138-
cancellationToken.ThrowIfCancellationRequested();
139-
skill.Process(difficultyObjects[currentIndex]);
165+
foreach (var skill in skills)
166+
{
167+
cancellationToken.ThrowIfCancellationRequested();
168+
skill.Process(difficultyObjects[currentIndex]);
169+
}
170+
171+
currentIndex++;
140172
}
141173

142-
currentIndex++;
174+
yield return new TimedDifficultyAttributes(obj.GetEndTime(), CreateDifficultyAttributes(progressiveBeatmap, playableMods, skills, clockRate));
143175
}
144-
145-
attribs.Add(new TimedDifficultyAttributes(obj.GetEndTime(), CreateDifficultyAttributes(progressiveBeatmap, playableMods, skills, clockRate)));
146176
}
147-
148-
return attribs;
149177
}
150178

151179
/// <summary>

0 commit comments

Comments
 (0)