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 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy 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 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy 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 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy 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 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy 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 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy { 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 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy { 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 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy 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 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy } else nextColumn = Random.Next(RandomStart, TotalColumns); - } + }); addToPattern(pattern, nextColumn); } @@ -295,8 +295,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy 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 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns 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. ///