From b24ce66a0d4bfcc9f53d84fe998b6d4027ef8a5a Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Wed, 7 Apr 2021 14:35:33 +0200 Subject: [PATCH] Add check/issue classes --- osu.Game/Rulesets/Edit/Checker.cs | 25 ++++++ .../Edit/Verify/Components/BeatmapCheck.cs | 19 +++++ .../Screens/Edit/Verify/Components/Check.cs | 41 +++++++++ .../Edit/Verify/Components/CheckMetadata.cs | 62 ++++++++++++++ .../Screens/Edit/Verify/Components/Issue.cs | 83 ++++++++++++++++++ .../Edit/Verify/Components/IssueTemplate.cs | 84 +++++++++++++++++++ osu.Game/Screens/Edit/Verify/Issue.cs | 10 --- 7 files changed, 314 insertions(+), 10 deletions(-) create mode 100644 osu.Game/Rulesets/Edit/Checker.cs create mode 100644 osu.Game/Screens/Edit/Verify/Components/BeatmapCheck.cs create mode 100644 osu.Game/Screens/Edit/Verify/Components/Check.cs create mode 100644 osu.Game/Screens/Edit/Verify/Components/CheckMetadata.cs create mode 100644 osu.Game/Screens/Edit/Verify/Components/Issue.cs create mode 100644 osu.Game/Screens/Edit/Verify/Components/IssueTemplate.cs delete mode 100644 osu.Game/Screens/Edit/Verify/Issue.cs diff --git a/osu.Game/Rulesets/Edit/Checker.cs b/osu.Game/Rulesets/Edit/Checker.cs new file mode 100644 index 0000000000..1c267c3435 --- /dev/null +++ b/osu.Game/Rulesets/Edit/Checker.cs @@ -0,0 +1,25 @@ +// 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.Checks; +using osu.Game.Screens.Edit.Verify.Components; + +namespace osu.Game.Rulesets.Edit +{ + public abstract class Checker + { + // These are all mode-invariant, hence here instead of in e.g. `OsuChecker`. + private readonly List beatmapChecks = new List + { + new CheckMetadataVowels() + }; + + public virtual IEnumerable Run(IBeatmap beatmap) + { + return beatmapChecks.SelectMany(check => check.Run(beatmap)); + } + } +} diff --git a/osu.Game/Screens/Edit/Verify/Components/BeatmapCheck.cs b/osu.Game/Screens/Edit/Verify/Components/BeatmapCheck.cs new file mode 100644 index 0000000000..7297dab60d --- /dev/null +++ b/osu.Game/Screens/Edit/Verify/Components/BeatmapCheck.cs @@ -0,0 +1,19 @@ +// 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; + +namespace osu.Game.Screens.Edit.Verify.Components +{ + public abstract class BeatmapCheck : Check + { + /// + /// Returns zero, one, or several issues detected by this + /// check on the given beatmap. + /// + /// The beatmap to run the check on. + /// + public abstract override IEnumerable Run(IBeatmap beatmap); + } +} diff --git a/osu.Game/Screens/Edit/Verify/Components/Check.cs b/osu.Game/Screens/Edit/Verify/Components/Check.cs new file mode 100644 index 0000000000..2ae21fd350 --- /dev/null +++ b/osu.Game/Screens/Edit/Verify/Components/Check.cs @@ -0,0 +1,41 @@ +// 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; + +namespace osu.Game.Screens.Edit.Verify.Components +{ + public abstract class Check + { + /// + /// Returns the for this check. + /// Basically, its information. + /// + /// + public abstract CheckMetadata Metadata(); + + /// + /// The templates for issues that this check may use. + /// Basically, what issues this check can detect. + /// + /// + public abstract IEnumerable Templates(); + + protected Check() + { + foreach (var template in Templates()) + template.Origin = this; + } + } + + public abstract class Check : Check + { + /// + /// Returns zero, one, or several issues detected by + /// this check on the given object. + /// + /// The object to run the check on. + /// + public abstract IEnumerable Run(T obj); + } +} diff --git a/osu.Game/Screens/Edit/Verify/Components/CheckMetadata.cs b/osu.Game/Screens/Edit/Verify/Components/CheckMetadata.cs new file mode 100644 index 0000000000..1cac99d47d --- /dev/null +++ b/osu.Game/Screens/Edit/Verify/Components/CheckMetadata.cs @@ -0,0 +1,62 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Screens.Edit.Verify +{ + public class CheckMetadata + { + /// + /// The category of an issue. + /// + public enum CheckCategory + { + /// Anything to do with control points. + Timing, + + /// Anything to do with artist, title, creator, etc. + Metadata, + + /// Anything to do with non-audio files, e.g. background, skin, sprites, and video. + Resources, + + /// Anything to do with audio files, e.g. song and hitsounds. + Audio, + + /// Anything to do with files that don't fit into the above, e.g. unused, osu, or osb. + Files, + + /// Anything to do with hitobjects unrelated to spread. + Compose, + + /// Anything to do with difficulty levels or their progression. + Spread, + + /// Anything to do with variables like CS, OD, AR, HP, and global SV. + Settings, + + /// Anything to do with hitobject feedback. + Hitsounds, + + /// Anything to do with storyboarding, breaks, video offset, etc. + Events + } + + /// + /// The category this check belongs to. E.g. , + /// , or . + /// + public readonly CheckCategory Category; + + /// + /// Describes the issue(s) that this check looks for. Keep this brief, such that + /// it fits into "No {description}". E.g. "Offscreen objects" / "Too short sliders". + /// + public readonly string Description; + + public CheckMetadata(CheckCategory category, string description) + { + Category = category; + Description = description; + } + } +} diff --git a/osu.Game/Screens/Edit/Verify/Components/Issue.cs b/osu.Game/Screens/Edit/Verify/Components/Issue.cs new file mode 100644 index 0000000000..fe81cb9335 --- /dev/null +++ b/osu.Game/Screens/Edit/Verify/Components/Issue.cs @@ -0,0 +1,83 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Game.Extensions; +using osu.Game.Rulesets.Objects; + +namespace osu.Game.Screens.Edit.Verify.Components +{ + public class Issue + { + /// + /// The time which this issue is associated with, if any, otherwise null. + /// + public double? Time; + + /// + /// The hitobjects which this issue is associated with. Empty by default. + /// + public IReadOnlyList HitObjects; + + /// + /// The template which this issue is using. This provides properties + /// such as the , and the + /// . + /// + public IssueTemplate Template; + + /// + /// The arguments that give this issue its context, based on the + /// . These are then substituted into the + /// . + /// E.g. timestamps, which diff is being compared to, what some volume is, etc. + /// + public object[] Arguments; + + public Issue(IssueTemplate template, params object[] args) + { + Time = null; + HitObjects = System.Array.Empty(); + Template = template; + Arguments = args; + + if (template.Origin == null) + { + throw new ArgumentException( + "A template had no origin. Make sure the `Templates()` method contains all templates used." + ); + } + } + + public Issue(double? time, IssueTemplate template, params object[] args) + : this(template, args) + { + Time = time; + } + + public Issue(IEnumerable hitObjects, IssueTemplate template, params object[] args) + : this(template, args) + { + Time = hitObjects.FirstOrDefault()?.StartTime; + HitObjects = hitObjects.ToArray(); + } + + public override string ToString() + { + return Template.Message(Arguments); + } + + public string GetEditorTimestamp() + { + // TODO: Editor timestamp formatting is handled in https://github.com/ppy/osu/pull/12030 + // We may be able to use that here too (if we decouple it from the HitObjectComposer class). + + if (Time == null) + return string.Empty; + + return Time.Value.ToEditorFormattedString(); + } + } +} diff --git a/osu.Game/Screens/Edit/Verify/Components/IssueTemplate.cs b/osu.Game/Screens/Edit/Verify/Components/IssueTemplate.cs new file mode 100644 index 0000000000..b178fa7122 --- /dev/null +++ b/osu.Game/Screens/Edit/Verify/Components/IssueTemplate.cs @@ -0,0 +1,84 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Humanizer; +using osu.Framework.Graphics; +using osuTK.Graphics; + +namespace osu.Game.Screens.Edit.Verify.Components +{ + public class IssueTemplate + { + /// + /// The type, or severity, of an issue. This decides its priority. + /// + public enum IssueType + { + /// A must-fix in the vast majority of cases. + Problem = 3, + + /// A possible mistake. Often requires critical thinking. + Warning = 2, + + // TODO: Try/catch all checks run and return error templates if exceptions occur. + /// An error occurred and a complete check could not be made. + Error = 1, + + // TODO: Negligible issues should be hidden by default. + /// A possible mistake so minor/unlikely that it can often be safely ignored. + Negligible = 0, + } + + /// + /// The check that this template originates from. + /// + public Check Origin; + + /// + /// The type of the issue. E.g. , + /// , or . + /// + public readonly IssueType Type; + + /// + /// The unformatted message given when this issue is detected. + /// This gets populated later when an issue is constructed with this template. + /// E.g. "Inconsistent snapping (1/{0}) with [{1}] (1/{2})." + /// + public readonly string UnformattedMessage; + + public IssueTemplate(IssueType type, string unformattedMessage) + { + Type = type; + UnformattedMessage = unformattedMessage; + } + + /// + /// Returns the formatted message given the arguments used to format it. + /// + /// The arguments used to format the message. + /// + public string Message(params object[] args) => UnformattedMessage.FormatWith(args); + + public static readonly Color4 PROBLEM_RED = new Colour4(1.0f, 0.4f, 0.4f, 1.0f); + public static readonly Color4 WARNING_YELLOW = new Colour4(1.0f, 0.8f, 0.2f, 1.0f); + public static readonly Color4 NEGLIGIBLE_GREEN = new Colour4(0.33f, 0.8f, 0.5f, 1.0f); + public static readonly Color4 ERROR_GRAY = new Colour4(0.5f, 0.5f, 0.5f, 1.0f); + + /// + /// Returns the colour corresponding to the type of this issue. + /// + /// + public Colour4 TypeColour() + { + return Type switch + { + IssueType.Problem => PROBLEM_RED, + IssueType.Warning => WARNING_YELLOW, + IssueType.Negligible => NEGLIGIBLE_GREEN, + IssueType.Error => ERROR_GRAY, + _ => Color4.White + }; + } + } +} diff --git a/osu.Game/Screens/Edit/Verify/Issue.cs b/osu.Game/Screens/Edit/Verify/Issue.cs deleted file mode 100644 index 25e913d819..0000000000 --- a/osu.Game/Screens/Edit/Verify/Issue.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -namespace osu.Game.Screens.Edit.Verify -{ - public class Issue - { - public readonly double Time; - } -}