1
0
mirror of https://github.com/ppy/osu.git synced 2025-02-21 23:23:52 +08:00

Merge pull request #9033 from smoogipoo/applydefaults-cancellation

Add cancellation token support to CreateNestedHitObjects()
This commit is contained in:
Dean Herbert 2020-05-26 12:04:06 +09:00 committed by GitHub
commit 0cc6d6d97c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 85 additions and 37 deletions

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System.Threading;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
@ -14,13 +15,13 @@ namespace osu.Game.Rulesets.Catch.Objects
public override Judgement CreateJudgement() => new IgnoreJudgement(); public override Judgement CreateJudgement() => new IgnoreJudgement();
protected override void CreateNestedHitObjects() protected override void CreateNestedHitObjects(CancellationToken cancellationToken)
{ {
base.CreateNestedHitObjects(); base.CreateNestedHitObjects(cancellationToken);
createBananas(); createBananas(cancellationToken);
} }
private void createBananas() private void createBananas(CancellationToken cancellationToken)
{ {
double spacing = Duration; double spacing = Duration;
while (spacing > 100) while (spacing > 100)
@ -31,6 +32,8 @@ namespace osu.Game.Rulesets.Catch.Objects
for (double i = StartTime; i <= EndTime; i += spacing) for (double i = StartTime; i <= EndTime; i += spacing)
{ {
cancellationToken.ThrowIfCancellationRequested();
AddNested(new Banana AddNested(new Banana
{ {
Samples = Samples, Samples = Samples,

View File

@ -3,6 +3,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading;
using osu.Game.Audio; using osu.Game.Audio;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
@ -45,9 +46,9 @@ namespace osu.Game.Rulesets.Catch.Objects
TickDistance = scoringDistance / difficulty.SliderTickRate; TickDistance = scoringDistance / difficulty.SliderTickRate;
} }
protected override void CreateNestedHitObjects() protected override void CreateNestedHitObjects(CancellationToken cancellationToken)
{ {
base.CreateNestedHitObjects(); base.CreateNestedHitObjects(cancellationToken);
var dropletSamples = Samples.Select(s => new HitSampleInfo var dropletSamples = Samples.Select(s => new HitSampleInfo
{ {
@ -58,7 +59,7 @@ namespace osu.Game.Rulesets.Catch.Objects
SliderEventDescriptor? lastEvent = null; SliderEventDescriptor? lastEvent = null;
foreach (var e in SliderEventGenerator.Generate(StartTime, SpanDuration, Velocity, TickDistance, Path.Distance, this.SpanCount(), LegacyLastTickOffset)) foreach (var e in SliderEventGenerator.Generate(StartTime, SpanDuration, Velocity, TickDistance, Path.Distance, this.SpanCount(), LegacyLastTickOffset, cancellationToken))
{ {
// generate tiny droplets since the last point // generate tiny droplets since the last point
if (lastEvent != null) if (lastEvent != null)
@ -73,6 +74,8 @@ namespace osu.Game.Rulesets.Catch.Objects
for (double t = timeBetweenTiny; t < sinceLastTick; t += timeBetweenTiny) for (double t = timeBetweenTiny; t < sinceLastTick; t += timeBetweenTiny)
{ {
cancellationToken.ThrowIfCancellationRequested();
AddNested(new TinyDroplet AddNested(new TinyDroplet
{ {
StartTime = t + lastEvent.Value.Time, StartTime = t + lastEvent.Value.Time,

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading;
using osu.Game.Audio; using osu.Game.Audio;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
@ -91,11 +92,11 @@ namespace osu.Game.Rulesets.Mania.Objects
tickSpacing = timingPoint.BeatLength / difficulty.SliderTickRate; tickSpacing = timingPoint.BeatLength / difficulty.SliderTickRate;
} }
protected override void CreateNestedHitObjects() protected override void CreateNestedHitObjects(CancellationToken cancellationToken)
{ {
base.CreateNestedHitObjects(); base.CreateNestedHitObjects(cancellationToken);
createTicks(); createTicks(cancellationToken);
AddNested(Head = new Note AddNested(Head = new Note
{ {
@ -112,13 +113,15 @@ namespace osu.Game.Rulesets.Mania.Objects
}); });
} }
private void createTicks() private void createTicks(CancellationToken cancellationToken)
{ {
if (tickSpacing == 0) if (tickSpacing == 0)
return; return;
for (double t = StartTime + tickSpacing; t <= EndTime - tickSpacing; t += tickSpacing) for (double t = StartTime + tickSpacing; t <= EndTime - tickSpacing; t += tickSpacing)
{ {
cancellationToken.ThrowIfCancellationRequested();
AddNested(new HoldNoteTick AddNested(new HoldNoteTick
{ {
StartTime = t, StartTime = t,

View File

@ -6,6 +6,7 @@ using osu.Game.Rulesets.Objects.Types;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using System.Linq; using System.Linq;
using System.Threading;
using osu.Framework.Caching; using osu.Framework.Caching;
using osu.Game.Audio; using osu.Game.Audio;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
@ -133,12 +134,12 @@ namespace osu.Game.Rulesets.Osu.Objects
TickDistance = scoringDistance / difficulty.SliderTickRate * TickDistanceMultiplier; TickDistance = scoringDistance / difficulty.SliderTickRate * TickDistanceMultiplier;
} }
protected override void CreateNestedHitObjects() protected override void CreateNestedHitObjects(CancellationToken cancellationToken)
{ {
base.CreateNestedHitObjects(); base.CreateNestedHitObjects(cancellationToken);
foreach (var e in foreach (var e in
SliderEventGenerator.Generate(StartTime, SpanDuration, Velocity, TickDistance, Path.Distance, this.SpanCount(), LegacyLastTickOffset)) SliderEventGenerator.Generate(StartTime, SpanDuration, Velocity, TickDistance, Path.Distance, this.SpanCount(), LegacyLastTickOffset, cancellationToken))
{ {
switch (e.Type) switch (e.Type)
{ {

View File

@ -4,6 +4,7 @@
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading;
using osu.Game.Audio; using osu.Game.Audio;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
@ -73,17 +74,17 @@ namespace osu.Game.Rulesets.Taiko.Objects
overallDifficulty = difficulty.OverallDifficulty; overallDifficulty = difficulty.OverallDifficulty;
} }
protected override void CreateNestedHitObjects() protected override void CreateNestedHitObjects(CancellationToken cancellationToken)
{ {
createTicks(); createTicks(cancellationToken);
RequiredGoodHits = NestedHitObjects.Count * Math.Min(0.15, 0.05 + 0.10 / 6 * overallDifficulty); RequiredGoodHits = NestedHitObjects.Count * Math.Min(0.15, 0.05 + 0.10 / 6 * overallDifficulty);
RequiredGreatHits = NestedHitObjects.Count * Math.Min(0.30, 0.10 + 0.20 / 6 * overallDifficulty); RequiredGreatHits = NestedHitObjects.Count * Math.Min(0.30, 0.10 + 0.20 / 6 * overallDifficulty);
base.CreateNestedHitObjects(); base.CreateNestedHitObjects(cancellationToken);
} }
private void createTicks() private void createTicks(CancellationToken cancellationToken)
{ {
if (tickSpacing == 0) if (tickSpacing == 0)
return; return;
@ -92,6 +93,8 @@ namespace osu.Game.Rulesets.Taiko.Objects
for (double t = StartTime; t < EndTime + tickSpacing / 2; t += tickSpacing) for (double t = StartTime; t < EndTime + tickSpacing / 2; t += tickSpacing)
{ {
cancellationToken.ThrowIfCancellationRequested();
AddNested(new DrumRollTick AddNested(new DrumRollTick
{ {
FirstTick = first, FirstTick = first,

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using System.Threading;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
@ -29,12 +30,15 @@ namespace osu.Game.Rulesets.Taiko.Objects
set => throw new NotSupportedException($"{nameof(Swell)} cannot be a strong hitobject."); set => throw new NotSupportedException($"{nameof(Swell)} cannot be a strong hitobject.");
} }
protected override void CreateNestedHitObjects() protected override void CreateNestedHitObjects(CancellationToken cancellationToken)
{ {
base.CreateNestedHitObjects(); base.CreateNestedHitObjects(cancellationToken);
for (int i = 0; i < RequiredHits; i++) for (int i = 0; i < RequiredHits; i++)
{
cancellationToken.ThrowIfCancellationRequested();
AddNested(new SwellTick()); AddNested(new SwellTick());
}
} }
public override Judgement CreateJudgement() => new TaikoSwellJudgement(); public override Judgement CreateJudgement() => new TaikoSwellJudgement();

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System.Threading;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
@ -32,9 +33,9 @@ namespace osu.Game.Rulesets.Taiko.Objects
/// </summary> /// </summary>
public virtual bool IsStrong { get; set; } public virtual bool IsStrong { get; set; }
protected override void CreateNestedHitObjects() protected override void CreateNestedHitObjects(CancellationToken cancellationToken)
{ {
base.CreateNestedHitObjects(); base.CreateNestedHitObjects(cancellationToken);
if (IsStrong) if (IsStrong)
AddNested(new StrongHitObject { StartTime = this.GetEndTime() }); AddNested(new StrongHitObject { StartTime = this.GetEndTime() });

View File

@ -16,7 +16,7 @@ namespace osu.Game.Tests.Beatmaps
[Test] [Test]
public void TestSingleSpan() public void TestSingleSpan()
{ {
var events = SliderEventGenerator.Generate(start_time, span_duration, 1, span_duration / 2, span_duration, 1, null).ToArray(); var events = SliderEventGenerator.Generate(start_time, span_duration, 1, span_duration / 2, span_duration, 1, null, default).ToArray();
Assert.That(events[0].Type, Is.EqualTo(SliderEventType.Head)); Assert.That(events[0].Type, Is.EqualTo(SliderEventType.Head));
Assert.That(events[0].Time, Is.EqualTo(start_time)); Assert.That(events[0].Time, Is.EqualTo(start_time));
@ -31,7 +31,7 @@ namespace osu.Game.Tests.Beatmaps
[Test] [Test]
public void TestRepeat() public void TestRepeat()
{ {
var events = SliderEventGenerator.Generate(start_time, span_duration, 1, span_duration / 2, span_duration, 2, null).ToArray(); var events = SliderEventGenerator.Generate(start_time, span_duration, 1, span_duration / 2, span_duration, 2, null, default).ToArray();
Assert.That(events[0].Type, Is.EqualTo(SliderEventType.Head)); Assert.That(events[0].Type, Is.EqualTo(SliderEventType.Head));
Assert.That(events[0].Time, Is.EqualTo(start_time)); Assert.That(events[0].Time, Is.EqualTo(start_time));
@ -52,7 +52,7 @@ namespace osu.Game.Tests.Beatmaps
[Test] [Test]
public void TestNonEvenTicks() public void TestNonEvenTicks()
{ {
var events = SliderEventGenerator.Generate(start_time, span_duration, 1, 300, span_duration, 2, null).ToArray(); var events = SliderEventGenerator.Generate(start_time, span_duration, 1, 300, span_duration, 2, null, default).ToArray();
Assert.That(events[0].Type, Is.EqualTo(SliderEventType.Head)); Assert.That(events[0].Type, Is.EqualTo(SliderEventType.Head));
Assert.That(events[0].Time, Is.EqualTo(start_time)); Assert.That(events[0].Time, Is.EqualTo(start_time));
@ -85,7 +85,7 @@ namespace osu.Game.Tests.Beatmaps
[Test] [Test]
public void TestLegacyLastTickOffset() public void TestLegacyLastTickOffset()
{ {
var events = SliderEventGenerator.Generate(start_time, span_duration, 1, span_duration / 2, span_duration, 1, 100).ToArray(); var events = SliderEventGenerator.Generate(start_time, span_duration, 1, span_duration / 2, span_duration, 1, 100, default).ToArray();
Assert.That(events[2].Type, Is.EqualTo(SliderEventType.LegacyLastTick)); Assert.That(events[2].Type, Is.EqualTo(SliderEventType.LegacyLastTick));
Assert.That(events[2].Time, Is.EqualTo(900)); Assert.That(events[2].Time, Is.EqualTo(900));
@ -97,7 +97,7 @@ namespace osu.Game.Tests.Beatmaps
const double velocity = 5; const double velocity = 5;
const double min_distance = velocity * 10; const double min_distance = velocity * 10;
var events = SliderEventGenerator.Generate(start_time, span_duration, velocity, velocity, span_duration, 2, 0).ToArray(); var events = SliderEventGenerator.Generate(start_time, span_duration, velocity, velocity, span_duration, 2, 0, default).ToArray();
Assert.Multiple(() => Assert.Multiple(() =>
{ {

View File

@ -129,12 +129,19 @@ namespace osu.Game.Beatmaps
processor?.PreProcess(); processor?.PreProcess();
// Compute default values for hitobjects, including creating nested hitobjects in-case they're needed // Compute default values for hitobjects, including creating nested hitobjects in-case they're needed
foreach (var obj in converted.HitObjects) try
{ {
if (cancellationSource.IsCancellationRequested) foreach (var obj in converted.HitObjects)
throw new BeatmapLoadTimeoutException(BeatmapInfo); {
if (cancellationSource.IsCancellationRequested)
throw new BeatmapLoadTimeoutException(BeatmapInfo);
obj.ApplyDefaults(converted.ControlPointInfo, converted.BeatmapInfo.BaseDifficulty); obj.ApplyDefaults(converted.ControlPointInfo, converted.BeatmapInfo.BaseDifficulty, cancellationSource.Token);
}
}
catch (OperationCanceledException)
{
throw new BeatmapLoadTimeoutException(BeatmapInfo);
} }
foreach (var mod in mods.OfType<IApplicableToHitObject>()) foreach (var mod in mods.OfType<IApplicableToHitObject>())

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System.Threading;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
@ -94,7 +95,7 @@ namespace osu.Game.Rulesets.Edit
} }
/// <summary> /// <summary>
/// Invokes <see cref="Objects.HitObject.ApplyDefaults(ControlPointInfo,BeatmapDifficulty)"/>, /// Invokes <see cref="Objects.HitObject.ApplyDefaults(ControlPointInfo,BeatmapDifficulty, CancellationToken)"/>,
/// refreshing <see cref="Objects.HitObject.NestedHitObjects"/> and parameters for the <see cref="HitObject"/>. /// refreshing <see cref="Objects.HitObject.NestedHitObjects"/> and parameters for the <see cref="HitObject"/>.
/// </summary> /// </summary>
protected void ApplyDefaultsToHitObject() => HitObject.ApplyDefaults(beatmap.Value.Beatmap.ControlPointInfo, beatmap.Value.Beatmap.BeatmapInfo.BaseDifficulty); protected void ApplyDefaultsToHitObject() => HitObject.ApplyDefaults(beatmap.Value.Beatmap.ControlPointInfo, beatmap.Value.Beatmap.BeatmapInfo.BaseDifficulty);

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading;
using JetBrains.Annotations; using JetBrains.Annotations;
using Newtonsoft.Json; using Newtonsoft.Json;
using osu.Framework.Bindables; using osu.Framework.Bindables;
@ -99,7 +100,8 @@ namespace osu.Game.Rulesets.Objects
/// </summary> /// </summary>
/// <param name="controlPointInfo">The control points.</param> /// <param name="controlPointInfo">The control points.</param>
/// <param name="difficulty">The difficulty settings to use.</param> /// <param name="difficulty">The difficulty settings to use.</param>
public void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) /// <param name="cancellationToken">The cancellation token.</param>
public void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty, CancellationToken cancellationToken = default)
{ {
ApplyDefaultsToSelf(controlPointInfo, difficulty); ApplyDefaultsToSelf(controlPointInfo, difficulty);
@ -108,7 +110,7 @@ namespace osu.Game.Rulesets.Objects
nestedHitObjects.Clear(); nestedHitObjects.Clear();
CreateNestedHitObjects(); CreateNestedHitObjects(cancellationToken);
if (this is IHasComboInformation hasCombo) if (this is IHasComboInformation hasCombo)
{ {
@ -122,7 +124,7 @@ namespace osu.Game.Rulesets.Objects
nestedHitObjects.Sort((h1, h2) => h1.StartTime.CompareTo(h2.StartTime)); nestedHitObjects.Sort((h1, h2) => h1.StartTime.CompareTo(h2.StartTime));
foreach (var h in nestedHitObjects) foreach (var h in nestedHitObjects)
h.ApplyDefaults(controlPointInfo, difficulty); h.ApplyDefaults(controlPointInfo, difficulty, cancellationToken);
DefaultsApplied?.Invoke(this); DefaultsApplied?.Invoke(this);
} }
@ -136,6 +138,14 @@ namespace osu.Game.Rulesets.Objects
HitWindows?.SetDifficulty(difficulty.OverallDifficulty); HitWindows?.SetDifficulty(difficulty.OverallDifficulty);
} }
protected virtual void CreateNestedHitObjects(CancellationToken cancellationToken)
{
// ReSharper disable once MethodSupportsCancellation (https://youtrack.jetbrains.com/issue/RIDER-44520)
#pragma warning disable 618
CreateNestedHitObjects();
#pragma warning restore 618
}
protected virtual void CreateNestedHitObjects() protected virtual void CreateNestedHitObjects()
{ {
} }

View File

@ -4,13 +4,22 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading;
namespace osu.Game.Rulesets.Objects namespace osu.Game.Rulesets.Objects
{ {
public static class SliderEventGenerator public static class SliderEventGenerator
{ {
[Obsolete("Use the overload with cancellation support instead.")] // can be removed 20201115
public static IEnumerable<SliderEventDescriptor> Generate(double startTime, double spanDuration, double velocity, double tickDistance, double totalDistance, int spanCount, public static IEnumerable<SliderEventDescriptor> Generate(double startTime, double spanDuration, double velocity, double tickDistance, double totalDistance, int spanCount,
double? legacyLastTickOffset) double? legacyLastTickOffset)
{
return Generate(startTime, spanDuration, velocity, tickDistance, totalDistance, spanCount, legacyLastTickOffset, default);
}
// ReSharper disable once MethodOverloadWithOptionalParameter
public static IEnumerable<SliderEventDescriptor> Generate(double startTime, double spanDuration, double velocity, double tickDistance, double totalDistance, int spanCount,
double? legacyLastTickOffset, CancellationToken cancellationToken = default)
{ {
// A very lenient maximum length of a slider for ticks to be generated. // A very lenient maximum length of a slider for ticks to be generated.
// This exists for edge cases such as /b/1573664 where the beatmap has been edited by the user, and should never be reached in normal usage. // This exists for edge cases such as /b/1573664 where the beatmap has been edited by the user, and should never be reached in normal usage.
@ -37,7 +46,7 @@ namespace osu.Game.Rulesets.Objects
var spanStartTime = startTime + span * spanDuration; var spanStartTime = startTime + span * spanDuration;
var reversed = span % 2 == 1; var reversed = span % 2 == 1;
var ticks = generateTicks(span, spanStartTime, spanDuration, reversed, length, tickDistance, minDistanceFromEnd); var ticks = generateTicks(span, spanStartTime, spanDuration, reversed, length, tickDistance, minDistanceFromEnd, cancellationToken);
if (reversed) if (reversed)
{ {
@ -108,12 +117,15 @@ namespace osu.Game.Rulesets.Objects
/// <param name="length">The length of the path.</param> /// <param name="length">The length of the path.</param>
/// <param name="tickDistance">The distance between each tick.</param> /// <param name="tickDistance">The distance between each tick.</param>
/// <param name="minDistanceFromEnd">The distance from the end of the path at which ticks are not allowed to be added.</param> /// <param name="minDistanceFromEnd">The distance from the end of the path at which ticks are not allowed to be added.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>A <see cref="SliderEventDescriptor"/> for each tick. If <paramref name="reversed"/> is true, the ticks will be returned in reverse-StartTime order.</returns> /// <returns>A <see cref="SliderEventDescriptor"/> for each tick. If <paramref name="reversed"/> is true, the ticks will be returned in reverse-StartTime order.</returns>
private static IEnumerable<SliderEventDescriptor> generateTicks(int spanIndex, double spanStartTime, double spanDuration, bool reversed, double length, double tickDistance, private static IEnumerable<SliderEventDescriptor> generateTicks(int spanIndex, double spanStartTime, double spanDuration, bool reversed, double length, double tickDistance,
double minDistanceFromEnd) double minDistanceFromEnd, CancellationToken cancellationToken = default)
{ {
for (var d = tickDistance; d <= length; d += tickDistance) for (var d = tickDistance; d <= length; d += tickDistance)
{ {
cancellationToken.ThrowIfCancellationRequested();
if (d >= length - minDistanceFromEnd) if (d >= length - minDistanceFromEnd)
break; break;