From bc4f3351f38bdf5d7e5812db554ceceeaa6ec2cc Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Sat, 10 Apr 2021 13:03:16 +0200 Subject: [PATCH] Replace checks with realistic ones --- .../Edit/Checks/CheckConsecutiveCircles.cs | 86 -------------- .../Edit/Checks/CheckOffscreenObjects.cs | 110 ++++++++++++++++++ osu.Game.Rulesets.Osu/Edit/OsuChecker.cs | 2 +- osu.Game/Checks/CheckMetadataVowels.cs | 65 ----------- osu.Game/Rulesets/Edit/Checker.cs | 2 +- .../Rulesets/Edit/Checks/CheckBackground.cs | 57 +++++++++ 6 files changed, 169 insertions(+), 153 deletions(-) delete mode 100644 osu.Game.Rulesets.Osu/Edit/Checks/CheckConsecutiveCircles.cs create mode 100644 osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs delete mode 100644 osu.Game/Checks/CheckMetadataVowels.cs create mode 100644 osu.Game/Rulesets/Edit/Checks/CheckBackground.cs diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckConsecutiveCircles.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckConsecutiveCircles.cs deleted file mode 100644 index c41c0dac2b..0000000000 --- a/osu.Game.Rulesets.Osu/Edit/Checks/CheckConsecutiveCircles.cs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System.Collections.Generic; -using System.Linq; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Screens.Edit.Verify; -using osu.Game.Screens.Edit.Verify.Components; - -namespace osu.Game.Rulesets.Osu.Edit.Checks -{ - public class CheckConsecutiveCircles : BeatmapCheck - { - private const double consecutive_threshold = 3; - private const double delta_time_min_expected = 300; - private const double delta_time_min_threshold = 100; - - public override CheckMetadata Metadata() => new CheckMetadata - ( - category: CheckMetadata.CheckCategory.Spread, - description: "Too many or fast consecutive circles." - ); - - private IssueTemplate templateManyInARow = new IssueTemplate - ( - type: IssueTemplate.IssueType.Problem, - unformattedMessage: "There are {0} circles in a row here, expected at most {1}." - ); - - private IssueTemplate templateTooFast = new IssueTemplate - ( - type: IssueTemplate.IssueType.Warning, - unformattedMessage: "These circles are too fast ({0:0} ms), expected at least {1:0} ms." - ); - - private IssueTemplate templateAlmostTooFast = new IssueTemplate - ( - type: IssueTemplate.IssueType.Negligible, - unformattedMessage: "These circles are almost too fast ({0:0} ms), expected at least {1:0} ms." - ); - - public override IEnumerable Templates() => new[] - { - templateManyInARow, - templateTooFast, - templateAlmostTooFast - }; - - public override IEnumerable Run(IBeatmap beatmap) - { - List prevCircles = new List(); - - foreach (HitObject hitobject in beatmap.HitObjects) - { - if (!(hitobject is HitCircle circle) || hitobject == beatmap.HitObjects.Last()) - { - if (prevCircles.Count > consecutive_threshold) - { - yield return new Issue( - prevCircles, - templateManyInARow, - prevCircles.Count, consecutive_threshold - ); - } - - prevCircles.Clear(); - continue; - } - - double? prevDeltaTime = circle.StartTime - prevCircles.LastOrDefault()?.StartTime; - prevCircles.Add(circle); - - if (prevDeltaTime == null || prevDeltaTime >= delta_time_min_expected) - continue; - - yield return new Issue( - prevCircles.TakeLast(2), - prevDeltaTime < delta_time_min_threshold ? templateTooFast : templateAlmostTooFast, - prevDeltaTime, delta_time_min_expected - ); - } - } - } -} diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs new file mode 100644 index 0000000000..c47864855b --- /dev/null +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs @@ -0,0 +1,110 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Edit.Checks.Components; +using osu.Game.Rulesets.Osu.Objects; +using osuTK; + +namespace osu.Game.Rulesets.Osu.Edit.Checks +{ + public class CheckOffscreenObjects : BeatmapCheck + { + // These are close approximates to the edges of the screen + // in gameplay on a 4:3 aspect ratio for osu!stable. + private const int min_x = -67; + private const int min_y = -60; + private const int max_x = 579; + private const int max_y = 428; + + // The amount of milliseconds to step through a slider path at a time + // (higher = more performant, but higher false-negative chance). + private const int path_step_size = 5; + + public override CheckMetadata Metadata() => new CheckMetadata + ( + category: CheckMetadata.CheckCategory.Compose, + description: "Offscreen hitobjects." + ); + + public override IEnumerable Templates() => new[] + { + templateOffscreen, + templateOffscreenSliderPath + }; + + private readonly IssueTemplate templateOffscreen = new IssueTemplate + ( + type: IssueTemplate.IssueType.Problem, + unformattedMessage: "This object goes offscreen on a 4:3 aspect ratio." + ); + + private readonly IssueTemplate templateOffscreenSliderPath = new IssueTemplate + ( + type: IssueTemplate.IssueType.Problem, + unformattedMessage: "This slider goes offscreen here on a 4:3 aspect ratio." + ); + + public override IEnumerable Run(IBeatmap beatmap) + { + foreach (var hitobject in beatmap.HitObjects) + { + switch (hitobject) + { + case Slider slider: + { + foreach (var issue in sliderIssues(slider)) + yield return issue; + + break; + } + + case HitCircle circle: + { + if (isOffscreen(circle.StackedPosition, circle.Radius)) + yield return new Issue(circle, templateOffscreen); + + break; + } + } + } + } + + /// + /// Steps through points on the slider to ensure the entire path is on-screen. + /// Returns at most one issue. + /// + /// The slider whose path to check. + /// + private IEnumerable sliderIssues(Slider slider) + { + for (int i = 0; i < slider.Distance; i += path_step_size) + { + double progress = i / slider.Distance; + Vector2 position = slider.StackedPositionAt(progress); + + if (!isOffscreen(position, slider.Radius)) + continue; + + // `SpanDuration` ensures we don't include reverses. + double time = slider.StartTime + progress * slider.SpanDuration; + yield return new Issue(slider, templateOffscreenSliderPath) { Time = time }; + + yield break; + } + + // Above loop may skip the last position in the slider due to step size. + if (!isOffscreen(slider.StackedEndPosition, slider.Radius)) + yield break; + + yield return new Issue(slider, templateOffscreenSliderPath) { Time = slider.EndTime }; + } + + private bool isOffscreen(Vector2 position, double radius) + { + return position.X - radius < min_x || position.X + radius > max_x || + position.Y - radius < min_y || position.Y + radius > max_y; + } + } +} diff --git a/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs b/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs index df3847f2fe..ee3b230679 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Osu.Edit { public readonly List beatmapChecks = new List { - new CheckConsecutiveCircles() + new CheckOffscreenObjects() }; public override IEnumerable Run(IBeatmap beatmap) diff --git a/osu.Game/Checks/CheckMetadataVowels.cs b/osu.Game/Checks/CheckMetadataVowels.cs deleted file mode 100644 index 8bcfe89c3a..0000000000 --- a/osu.Game/Checks/CheckMetadataVowels.cs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System.Collections.Generic; -using System.Linq; -using osu.Game.Beatmaps; -using osu.Game.Screens.Edit.Verify; -using osu.Game.Screens.Edit.Verify.Components; - -namespace osu.Game.Checks -{ - public class CheckMetadataVowels : BeatmapCheck - { - private static readonly char[] vowels = { 'a', 'e', 'i', 'o', 'u' }; - - public override CheckMetadata Metadata() => new CheckMetadata - ( - category: CheckMetadata.CheckCategory.Metadata, - description: "Metadata fields contain vowels" - ); - - public override IEnumerable Templates() => new[] - { - templateArtistHasVowels - }; - - private IssueTemplate templateArtistHasVowels = new IssueTemplate - ( - type: IssueTemplate.IssueType.Warning, - unformattedMessage: "The {0} field \"{1}\" contains the vowel(s) {2}." - ); - - public override IEnumerable Run(IBeatmap beatmap) - { - foreach (var issue in GetVowelIssues("artist", beatmap.Metadata.Artist)) - yield return issue; - - foreach (var issue in GetVowelIssues("unicode artist", beatmap.Metadata.ArtistUnicode)) - yield return issue; - - foreach (var issue in GetVowelIssues("title", beatmap.Metadata.Title)) - yield return issue; - - foreach (var issue in GetVowelIssues("unicode title", beatmap.Metadata.TitleUnicode)) - yield return issue; - } - - private IEnumerable GetVowelIssues(string fieldName, string fieldValue) - { - if (fieldValue == null) - // Unicode fields can be null if same as respective romanized fields. - yield break; - - List matches = vowels.Where(c => fieldValue.ToLower().Contains(c)).ToList(); - - if (!matches.Any()) - yield break; - - yield return new Issue( - templateArtistHasVowels, - fieldName, fieldValue, string.Join(", ", matches) - ); - } - } -} diff --git a/osu.Game/Rulesets/Edit/Checker.cs b/osu.Game/Rulesets/Edit/Checker.cs index 6687160b10..65d7fc5913 100644 --- a/osu.Game/Rulesets/Edit/Checker.cs +++ b/osu.Game/Rulesets/Edit/Checker.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Edit // These are all ruleset-invariant, hence here instead of in e.g. `OsuChecker`. private readonly List beatmapChecks = new List { - new CheckMetadataVowels() + new CheckBackground() }; public virtual IEnumerable Run(IBeatmap beatmap) diff --git a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs new file mode 100644 index 0000000000..9376f9568a --- /dev/null +++ b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs @@ -0,0 +1,57 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Edit.Checks.Components; + +namespace osu.Game.Rulesets.Edit.Checks +{ + public class CheckBackground : BeatmapCheck + { + public override CheckMetadata Metadata() => new CheckMetadata + ( + category: CheckMetadata.CheckCategory.Resources, + description: "Missing background." + ); + + public override IEnumerable Templates() => new[] + { + templateNoneSet, + templateDoesNotExist + }; + + private readonly IssueTemplate templateNoneSet = new IssueTemplate + ( + type: IssueTemplate.IssueType.Problem, + unformattedMessage: "No background has been set." + ); + + private readonly IssueTemplate templateDoesNotExist = new IssueTemplate + ( + type: IssueTemplate.IssueType.Problem, + unformattedMessage: "The background file \"{0}\" is does not exist." + ); + + public override IEnumerable Run(IBeatmap beatmap) + { + if (beatmap.Metadata.BackgroundFile == null) + { + yield return new Issue(templateNoneSet); + + yield break; + } + + // If the background is set, also make sure it still exists. + + var set = beatmap.BeatmapInfo.BeatmapSet; + var file = set.Files.FirstOrDefault(f => f.Filename == beatmap.Metadata.BackgroundFile); + + if (file != null) + yield break; + + yield return new Issue(templateDoesNotExist, beatmap.Metadata.BackgroundFile); + } + } +}