diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
index 8d0d78120a..010cf962cc 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
@@ -176,16 +176,23 @@ private Pattern generateRandomHoldNotes(double startTime, int noteCount)
int nextColumn = Random.Next(RandomStart, TotalColumns);
for (int i = 0; i < Math.Min(usableColumns, noteCount); i++)
{
- while (pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn)) //find available column
+ // Find available column
+ RunWhile(() => pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn), () =>
+ {
nextColumn = Random.Next(RandomStart, TotalColumns);
+ });
+
addToPattern(pattern, nextColumn, startTime, EndTime);
}
// This is can't be combined with the above loop due to RNG
for (int i = 0; i < noteCount - usableColumns; i++)
{
- while (pattern.ColumnHasObject(nextColumn))
+ RunWhile(() => pattern.ColumnHasObject(nextColumn), () =>
+ {
nextColumn = Random.Next(RandomStart, TotalColumns);
+ });
+
addToPattern(pattern, nextColumn, startTime, EndTime);
}
@@ -211,16 +218,21 @@ private Pattern generateRandomNotes(double startTime, int noteCount)
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
if (convertType.HasFlag(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns)
{
- while (PreviousPattern.ColumnHasObject(nextColumn))
+ RunWhile(() => PreviousPattern.ColumnHasObject(nextColumn), () =>
+ {
nextColumn = Random.Next(RandomStart, TotalColumns);
+ });
}
int lastColumn = nextColumn;
for (int i = 0; i < noteCount; i++)
{
addToPattern(pattern, nextColumn, startTime, startTime);
- while (nextColumn == lastColumn)
+
+ RunWhile(() => nextColumn == lastColumn, () =>
+ {
nextColumn = Random.Next(RandomStart, TotalColumns);
+ });
lastColumn = nextColumn;
startTime += SegmentDuration;
@@ -393,14 +405,18 @@ private Pattern generateTiledHoldNotes(double startTime)
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
if (convertType.HasFlag(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns)
{
- while (PreviousPattern.ColumnHasObject(nextColumn))
+ RunWhile(() => PreviousPattern.ColumnHasObject(nextColumn), () =>
+ {
nextColumn = Random.Next(RandomStart, TotalColumns);
+ });
}
for (int i = 0; i < columnRepeat; i++)
{
- while (pattern.ColumnHasObject(nextColumn))
+ RunWhile(() => pattern.ColumnHasObject(nextColumn), () =>
+ {
nextColumn = Random.Next(RandomStart, TotalColumns);
+ });
addToPattern(pattern, nextColumn, startTime, EndTime);
startTime += SegmentDuration;
@@ -427,8 +443,10 @@ private Pattern generateHoldAndNormalNotes(double startTime)
int holdColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
if (convertType.HasFlag(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns)
{
- while (PreviousPattern.ColumnHasObject(holdColumn))
+ RunWhile(() => PreviousPattern.ColumnHasObject(holdColumn), () =>
+ {
holdColumn = Random.Next(RandomStart, TotalColumns);
+ });
}
// Create the hold note
@@ -455,8 +473,11 @@ private Pattern generateHoldAndNormalNotes(double startTime)
{
for (int j = 0; j < noteCount; j++)
{
- while (rowPattern.ColumnHasObject(nextColumn) || nextColumn == holdColumn)
+ RunWhile(() => rowPattern.ColumnHasObject(nextColumn) || nextColumn == holdColumn, () =>
+ {
nextColumn = Random.Next(RandomStart, TotalColumns);
+ });
+
addToPattern(rowPattern, nextColumn, startTime, startTime);
}
}
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs
index 06b4b8a27e..eae9a0fc3b 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs
@@ -59,8 +59,10 @@ private int getNextRandomColumn(int start)
{
int nextColumn = Random.Next(start, TotalColumns);
- while (PreviousPattern.ColumnHasObject(nextColumn))
+ RunWhile(() => PreviousPattern.ColumnHasObject(nextColumn), () =>
+ {
nextColumn = Random.Next(start, TotalColumns);
+ });
return nextColumn;
}
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs
index 84ebfdb839..930ca26660 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs
@@ -234,7 +234,7 @@ private Pattern generateRandomNotes(int noteCount)
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
for (int i = 0; i < noteCount; i++)
{
- while (pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn) && !allowStacking)
+ RunWhile(() => pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn) && !allowStacking, () =>
{
if (convertType.HasFlag(PatternType.Gathered))
{
@@ -244,7 +244,7 @@ private Pattern generateRandomNotes(int noteCount)
}
else
nextColumn = Random.Next(RandomStart, TotalColumns);
- }
+ });
addToPattern(pattern, nextColumn);
}
@@ -295,8 +295,10 @@ private Pattern generateRandomPatternWithMirrored(double centreProbability, doub
int nextColumn = Random.Next(RandomStart, columnLimit);
for (int i = 0; i < noteCount; i++)
{
- while (pattern.ColumnHasObject(nextColumn))
+ RunWhile(() => pattern.ColumnHasObject(nextColumn), () =>
+ {
nextColumn = Random.Next(RandomStart, columnLimit);
+ });
// Add normal note
addToPattern(pattern, nextColumn);
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs
index a42d57cdd1..74164d70c1 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs
@@ -3,6 +3,9 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
+using JetBrains.Annotations;
+using osu.Framework.Logging;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
@@ -12,6 +15,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
///
internal abstract class PatternGenerator
{
+ private const int max_rng_iterations = 100;
+
///
/// The last pattern.
///
@@ -42,6 +47,21 @@ protected PatternGenerator(HitObject hitObject, ManiaBeatmap beatmap, Pattern pr
TotalColumns = Beatmap.TotalColumns;
}
+ protected void RunWhile([InstantHandle] Func condition, Action action)
+ {
+ int iterations = 0;
+
+ while (condition() && iterations++ < max_rng_iterations)
+ action();
+
+ if (iterations < max_rng_iterations)
+ return;
+
+ // Generate + log an error/stacktrace
+
+ Logger.Log($"Allowable time exceeded for hitobject generation:\n{new StackTrace(0)}", level: LogLevel.Error);
+ }
+
///
/// Generates the patterns for , each filled with hit objects.
///