From d58ef5310bf534f76a8d10170cdd71cd098bace0 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Sun, 28 Mar 2021 17:36:22 +0200 Subject: [PATCH 001/150] Add verify tab Currently empty, but works. --- .../Input/Bindings/GlobalActionContainer.cs | 3 + osu.Game/Screens/Edit/Editor.cs | 9 + osu.Game/Screens/Edit/EditorScreenMode.cs | 3 + osu.Game/Screens/Edit/Verify/Issue.cs | 10 + osu.Game/Screens/Edit/Verify/IssueTable.cs | 191 ++++++++++++++++++ osu.Game/Screens/Edit/Verify/VerifyScreen.cs | 68 +++++++ 6 files changed, 284 insertions(+) create mode 100644 osu.Game/Screens/Edit/Verify/Issue.cs create mode 100644 osu.Game/Screens/Edit/Verify/IssueTable.cs create mode 100644 osu.Game/Screens/Edit/Verify/VerifyScreen.cs diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 8ccdb9249e..6705d3ee61 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -193,6 +193,9 @@ namespace osu.Game.Input.Bindings [Description("Timing mode")] EditorTimingMode, + [Description("Verify mode")] + EditorVerifyMode, + [Description("Hold for HUD")] HoldForHUD, diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 0c24eb6a4d..57499bf219 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -36,6 +36,7 @@ using osu.Game.Screens.Edit.Compose; using osu.Game.Screens.Edit.Design; using osu.Game.Screens.Edit.Setup; using osu.Game.Screens.Edit.Timing; +using osu.Game.Screens.Edit.Verify; using osu.Game.Screens.Play; using osu.Game.Users; using osuTK.Graphics; @@ -445,6 +446,10 @@ namespace osu.Game.Screens.Edit menuBar.Mode.Value = EditorScreenMode.SongSetup; return true; + case GlobalAction.EditorVerifyMode: + menuBar.Mode.Value = EditorScreenMode.Verify; + return true; + default: return false; } @@ -632,6 +637,10 @@ namespace osu.Game.Screens.Edit case EditorScreenMode.Timing: currentScreen = new TimingScreen(); break; + + case EditorScreenMode.Verify: + currentScreen = new VerifyScreen(); + break; } LoadComponentAsync(currentScreen, newScreen => diff --git a/osu.Game/Screens/Edit/EditorScreenMode.cs b/osu.Game/Screens/Edit/EditorScreenMode.cs index 12cfcc605b..ecd39f9b57 100644 --- a/osu.Game/Screens/Edit/EditorScreenMode.cs +++ b/osu.Game/Screens/Edit/EditorScreenMode.cs @@ -18,5 +18,8 @@ namespace osu.Game.Screens.Edit [Description("timing")] Timing, + + [Description("verify")] + Verify, } } diff --git a/osu.Game/Screens/Edit/Verify/Issue.cs b/osu.Game/Screens/Edit/Verify/Issue.cs new file mode 100644 index 0000000000..25e913d819 --- /dev/null +++ b/osu.Game/Screens/Edit/Verify/Issue.cs @@ -0,0 +1,10 @@ +// 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; + } +} diff --git a/osu.Game/Screens/Edit/Verify/IssueTable.cs b/osu.Game/Screens/Edit/Verify/IssueTable.cs new file mode 100644 index 0000000000..6476cebe48 --- /dev/null +++ b/osu.Game/Screens/Edit/Verify/IssueTable.cs @@ -0,0 +1,191 @@ +// 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.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; +using osu.Game.Extensions; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osuTK.Graphics; + +namespace osu.Game.Screens.Edit.Verify +{ + public class IssueTable : TableContainer + { + private const float horizontal_inset = 20; + private const float row_height = 25; + private const int text_size = 14; + + private readonly FillFlowContainer backgroundFlow; + + public IssueTable() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + Padding = new MarginPadding { Horizontal = horizontal_inset }; + RowSize = new Dimension(GridSizeMode.Absolute, row_height); + + AddInternal(backgroundFlow = new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Depth = 1f, + Padding = new MarginPadding { Horizontal = -horizontal_inset }, + Margin = new MarginPadding { Top = row_height } + }); + } + + public IEnumerable Issues + { + set + { + Content = null; + backgroundFlow.Clear(); + + if (value?.Any() != true) + return; + + foreach (var issue in value) + { + backgroundFlow.Add(new IssueTable.RowBackground(issue)); + } + + Columns = createHeaders(); + Content = value.Select((g, i) => createContent(i, g)).ToArray().ToRectangular(); + } + } + + private TableColumn[] createHeaders() + { + var columns = new List + { + new TableColumn(string.Empty, Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new TableColumn("Time", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new TableColumn(), + new TableColumn("Attributes", Anchor.CentreLeft), + }; + + return columns.ToArray(); + } + + private Drawable[] createContent(int index, Issue issue) => new Drawable[] + { + new OsuSpriteText + { + Text = $"#{index + 1}", + Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold), + Margin = new MarginPadding(10) + }, + new OsuSpriteText + { + Text = issue.Time.ToEditorFormattedString(), + Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold) + }, + null, + null //new ControlGroupAttributes(issue), + }; + + public class RowBackground : OsuClickableContainer + { + private readonly Issue issue; + private const int fade_duration = 100; + + private readonly Box hoveredBackground; + + [Resolved] + private EditorClock clock { get; set; } + + [Resolved] + private Bindable selectedIssue { get; set; } + + public RowBackground(Issue issue) + { + this.issue = issue; + RelativeSizeAxes = Axes.X; + Height = 25; + + AlwaysPresent = true; + + CornerRadius = 3; + Masking = true; + + Children = new Drawable[] + { + hoveredBackground = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + }, + }; + + Action = () => + { + selectedIssue.Value = issue; + clock.SeekSmoothlyTo(issue.Time); + }; + } + + private Color4 colourHover; + private Color4 colourSelected; + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + hoveredBackground.Colour = colourHover = colours.BlueDarker; + colourSelected = colours.YellowDarker; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + selectedIssue.BindValueChanged(group => { Selected = issue == group.NewValue; }, true); + } + + private bool selected; + + protected bool Selected + { + get => selected; + set + { + if (value == selected) + return; + + selected = value; + updateState(); + } + } + + protected override bool OnHover(HoverEvent e) + { + updateState(); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + updateState(); + base.OnHoverLost(e); + } + + private void updateState() + { + hoveredBackground.FadeColour(selected ? colourSelected : colourHover, 450, Easing.OutQuint); + + if (selected || IsHovered) + hoveredBackground.FadeIn(fade_duration, Easing.OutQuint); + else + hoveredBackground.FadeOut(fade_duration, Easing.OutQuint); + } + } + } +} diff --git a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs new file mode 100644 index 0000000000..c15cefae83 --- /dev/null +++ b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs @@ -0,0 +1,68 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; + +namespace osu.Game.Screens.Edit.Verify +{ + public class VerifyScreen : EditorScreenWithTimeline + { + public VerifyScreen() + : base(EditorScreenMode.Verify) + { + } + + protected override Drawable CreateMainContent() => new GridContainer + { + RelativeSizeAxes = Axes.Both, + ColumnDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.Absolute, 200), + }, + Content = new[] + { + new Drawable[] + { + new ControlPointList() + }, + } + }; + + public class ControlPointList : CompositeDrawable + { + private IssueTable table; + + [Resolved] + private EditorClock clock { get; set; } + + [Resolved] + protected EditorBeatmap Beatmap { get; private set; } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + RelativeSizeAxes = Axes.Both; + + InternalChildren = new Drawable[] + { + new Box + { + Colour = colours.Gray0, + RelativeSizeAxes = Axes.Both, + }, + new OsuScrollContainer + { + RelativeSizeAxes = Axes.Both, + Child = table = new IssueTable(), + } + }; + } + } + } +} 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 002/150] 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; - } -} From 0343ef7f147b12a8bd71dfced08c6b503b6b8b79 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Wed, 7 Apr 2021 14:36:43 +0200 Subject: [PATCH 003/150] Add ruleset-specific checker --- osu.Game.Rulesets.Osu/Edit/OsuChecker.cs | 30 ++++++++++++++++++++++++ osu.Game.Rulesets.Osu/OsuRuleset.cs | 2 ++ osu.Game/Rulesets/Ruleset.cs | 2 ++ 3 files changed, 34 insertions(+) create mode 100644 osu.Game.Rulesets.Osu/Edit/OsuChecker.cs diff --git a/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs b/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs new file mode 100644 index 0000000000..9918b53c85 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs @@ -0,0 +1,30 @@ +// 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; +using osu.Game.Rulesets.Osu.Edit.Checks; +using osu.Game.Screens.Edit.Verify.Components; + +namespace osu.Game.Rulesets.Osu.Edit +{ + public class OsuChecker : Checker + { + public readonly List beatmapChecks = new List + { + new CheckConsecutiveCircles() + }; + + public override IEnumerable Run(IBeatmap beatmap) + { + // Also run mode-invariant checks. + foreach (var issue in base.Run(beatmap)) + yield return issue; + + foreach (var issue in beatmapChecks.SelectMany(check => check.Run(beatmap))) + yield return issue; + } + } +} diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 838d707d64..74679bd578 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -206,6 +206,8 @@ namespace osu.Game.Rulesets.Osu public override HitObjectComposer CreateHitObjectComposer() => new OsuHitObjectComposer(this); + public override Checker CreateChecker() => new OsuChecker(); + public override string Description => "osu!"; public override string ShortName => SHORT_NAME; diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 38d30a2e31..71f80c9982 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -202,6 +202,8 @@ namespace osu.Game.Rulesets public virtual HitObjectComposer CreateHitObjectComposer() => null; + public virtual Checker CreateChecker() => null; + public virtual Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.Solid.QuestionCircle }; public virtual IResourceStore CreateResourceStore() => new NamespacedResourceStore(new DllResourceStore(GetType().Assembly), @"Resources"); From 9c4604e3c5fdbfa9f5201f39ee41584fa5b47d18 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Wed, 7 Apr 2021 14:36:53 +0200 Subject: [PATCH 004/150] Add example checks --- .../Edit/Checks/CheckConsecutiveCircles.cs | 86 +++++++++++++++++++ osu.Game/Checks/CheckMetadataVowels.cs | 65 ++++++++++++++ 2 files changed, 151 insertions(+) create mode 100644 osu.Game.Rulesets.Osu/Edit/Checks/CheckConsecutiveCircles.cs create mode 100644 osu.Game/Checks/CheckMetadataVowels.cs diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckConsecutiveCircles.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckConsecutiveCircles.cs new file mode 100644 index 0000000000..c41c0dac2b --- /dev/null +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckConsecutiveCircles.cs @@ -0,0 +1,86 @@ +// 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/Checks/CheckMetadataVowels.cs b/osu.Game/Checks/CheckMetadataVowels.cs new file mode 100644 index 0000000000..8bcfe89c3a --- /dev/null +++ b/osu.Game/Checks/CheckMetadataVowels.cs @@ -0,0 +1,65 @@ +// 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) + ); + } + } +} From bab36e529a5b9e1ff5b2300e9def2b5bc11687a7 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Wed, 7 Apr 2021 14:38:43 +0200 Subject: [PATCH 005/150] Update UI with new components --- osu.Game/Screens/Edit/Verify/IssueSettings.cs | 47 +++++++++ osu.Game/Screens/Edit/Verify/IssueTable.cs | 91 +++++++++-------- osu.Game/Screens/Edit/Verify/VerifyScreen.cs | 98 +++++++++++++++---- .../Screens/Edit/Verify/VisibilitySettings.cs | 52 ++++++++++ 4 files changed, 229 insertions(+), 59 deletions(-) create mode 100644 osu.Game/Screens/Edit/Verify/IssueSettings.cs create mode 100644 osu.Game/Screens/Edit/Verify/VisibilitySettings.cs diff --git a/osu.Game/Screens/Edit/Verify/IssueSettings.cs b/osu.Game/Screens/Edit/Verify/IssueSettings.cs new file mode 100644 index 0000000000..608be877de --- /dev/null +++ b/osu.Game/Screens/Edit/Verify/IssueSettings.cs @@ -0,0 +1,47 @@ +// 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.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; + +namespace osu.Game.Screens.Edit.Verify +{ + public class IssueSettings : CompositeDrawable + { + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + RelativeSizeAxes = Axes.Both; + + InternalChildren = new Drawable[] + { + new Box + { + Colour = colours.Gray3, + RelativeSizeAxes = Axes.Both, + }, + new OsuScrollContainer + { + RelativeSizeAxes = Axes.Both, + Child = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Children = createSections() + }, + } + }; + } + + private IReadOnlyList createSections() => new Drawable[] + { + new VisibilitySettings() + }; + } +} diff --git a/osu.Game/Screens/Edit/Verify/IssueTable.cs b/osu.Game/Screens/Edit/Verify/IssueTable.cs index 6476cebe48..c70695a849 100644 --- a/osu.Game/Screens/Edit/Verify/IssueTable.cs +++ b/osu.Game/Screens/Edit/Verify/IssueTable.cs @@ -4,16 +4,16 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; -using osu.Game.Extensions; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Input.Bindings; +using osu.Game.Screens.Edit.Verify.Components; using osuTK.Graphics; namespace osu.Game.Screens.Edit.Verify @@ -34,6 +34,9 @@ namespace osu.Game.Screens.Edit.Verify Padding = new MarginPadding { Horizontal = horizontal_inset }; RowSize = new Dimension(GridSizeMode.Absolute, row_height); + Masking = true; + CornerRadius = 6; + AddInternal(backgroundFlow = new FillFlowContainer { RelativeSizeAxes = Axes.Both, @@ -55,7 +58,7 @@ namespace osu.Game.Screens.Edit.Verify foreach (var issue in value) { - backgroundFlow.Add(new IssueTable.RowBackground(issue)); + backgroundFlow.Add(new RowBackground(issue)); } Columns = createHeaders(); @@ -68,9 +71,10 @@ namespace osu.Game.Screens.Edit.Verify var columns = new List { new TableColumn(string.Empty, Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new TableColumn("Type", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), new TableColumn("Time", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new TableColumn(), - new TableColumn("Attributes", Anchor.CentreLeft), + new TableColumn("Message", Anchor.CentreLeft), + new TableColumn("Category", Anchor.CentreRight, new Dimension(GridSizeMode.AutoSize)), }; return columns.ToArray(); @@ -81,21 +85,36 @@ namespace osu.Game.Screens.Edit.Verify new OsuSpriteText { Text = $"#{index + 1}", + Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Medium) + }, + new OsuSpriteText + { + Text = issue.Template.Type.ToString(), + Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold), + Margin = new MarginPadding { Left = 10 }, + Colour = issue.Template.TypeColour() + }, + new OsuSpriteText + { + Text = issue.GetEditorTimestamp(), Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold), Margin = new MarginPadding(10) }, new OsuSpriteText { - Text = issue.Time.ToEditorFormattedString(), - Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold) + Text = issue.ToString(), + Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Medium) }, - null, - null //new ControlGroupAttributes(issue), + new OsuSpriteText + { + Text = issue.Template.Origin.Metadata().Category.ToString(), + Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold), + Margin = new MarginPadding(10) + } }; public class RowBackground : OsuClickableContainer { - private readonly Issue issue; private const int fade_duration = 100; private readonly Box hoveredBackground; @@ -104,13 +123,15 @@ namespace osu.Game.Screens.Edit.Verify private EditorClock clock { get; set; } [Resolved] - private Bindable selectedIssue { get; set; } + private Editor editor { get; set; } + + [Resolved] + private EditorBeatmap editorBeatmap { get; set; } public RowBackground(Issue issue) { - this.issue = issue; RelativeSizeAxes = Axes.X; - Height = 25; + Height = row_height; AlwaysPresent = true; @@ -128,41 +149,29 @@ namespace osu.Game.Screens.Edit.Verify Action = () => { - selectedIssue.Value = issue; - clock.SeekSmoothlyTo(issue.Time); + // Supposed to work like clicking timestamps outside of the game. + // TODO: Is there already defined behaviour for this I may be able to call? + + if (issue.Time != null) + { + clock.Seek(issue.Time.Value); + editor.OnPressed(GlobalAction.EditorComposeMode); + } + + if (!issue.HitObjects.Any()) + return; + + editorBeatmap.SelectedHitObjects.Clear(); + editorBeatmap.SelectedHitObjects.AddRange(issue.HitObjects); }; } private Color4 colourHover; - private Color4 colourSelected; [BackgroundDependencyLoader] private void load(OsuColour colours) { hoveredBackground.Colour = colourHover = colours.BlueDarker; - colourSelected = colours.YellowDarker; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - selectedIssue.BindValueChanged(group => { Selected = issue == group.NewValue; }, true); - } - - private bool selected; - - protected bool Selected - { - get => selected; - set - { - if (value == selected) - return; - - selected = value; - updateState(); - } } protected override bool OnHover(HoverEvent e) @@ -179,9 +188,9 @@ namespace osu.Game.Screens.Edit.Verify private void updateState() { - hoveredBackground.FadeColour(selected ? colourSelected : colourHover, 450, Easing.OutQuint); + hoveredBackground.FadeColour(colourHover, 450, Easing.OutQuint); - if (selected || IsHovered) + if (IsHovered) hoveredBackground.FadeIn(fade_duration, Easing.OutQuint); else hoveredBackground.FadeOut(fade_duration, Easing.OutQuint); diff --git a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs index c15cefae83..88397fdbff 100644 --- a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs +++ b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs @@ -1,40 +1,70 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Edit; +using osuTK; namespace osu.Game.Screens.Edit.Verify { - public class VerifyScreen : EditorScreenWithTimeline + public class VerifyScreen : EditorScreen { + private Ruleset ruleset; + private static Checker checker; // TODO: Should not be static, but apparently needs to be? + public VerifyScreen() : base(EditorScreenMode.Verify) { } - protected override Drawable CreateMainContent() => new GridContainer + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { - RelativeSizeAxes = Axes.Both, - ColumnDimensions = new[] - { - new Dimension(), - new Dimension(GridSizeMode.Absolute, 200), - }, - Content = new[] - { - new Drawable[] - { - new ControlPointList() - }, - } - }; + var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - public class ControlPointList : CompositeDrawable + ruleset = parent.Get>().Value.BeatmapInfo.Ruleset?.CreateInstance(); + checker = ruleset?.CreateChecker(); + + return dependencies; + } + + [BackgroundDependencyLoader] + private void load() + { + Child = new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding(20), + Child = new GridContainer + { + RelativeSizeAxes = Axes.Both, + ColumnDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.Absolute, 200), + }, + Content = new[] + { + new Drawable[] + { + new IssueList(), + new IssueSettings(), + }, + } + } + }; + } + + public class IssueList : CompositeDrawable { private IssueTable table; @@ -60,9 +90,41 @@ namespace osu.Game.Screens.Edit.Verify { RelativeSizeAxes = Axes.Both, Child = table = new IssueTable(), - } + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Margin = new MarginPadding(20), + Children = new Drawable[] + { + new TriangleButton + { + Text = "Refresh", + Action = refresh, + Size = new Vector2(120, 40), + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + }, + } + }, }; } + + protected override void LoadComplete() + { + base.LoadComplete(); + + refresh(); + } + + private void refresh() + { + table.Issues = checker.Run(Beatmap) + .OrderByDescending(issue => issue.Template.Type) + .ThenByDescending(issue => issue.Template.Origin.Metadata().Category); + } } } } diff --git a/osu.Game/Screens/Edit/Verify/VisibilitySettings.cs b/osu.Game/Screens/Edit/Verify/VisibilitySettings.cs new file mode 100644 index 0000000000..6488c616e4 --- /dev/null +++ b/osu.Game/Screens/Edit/Verify/VisibilitySettings.cs @@ -0,0 +1,52 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.UserInterfaceV2; +using osuTK; + +namespace osu.Game.Screens.Edit.Verify +{ + internal class VisibilitySettings : CompositeDrawable + { + [BackgroundDependencyLoader] + private void load() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + Padding = new MarginPadding(10); + + InternalChildren = new Drawable[] + { + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(10), + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new LabelledSwitchButton + { + Label = "Show problems", + Current = new Bindable(true) + }, + new LabelledSwitchButton + { + Label = "Show warnings", + Current = new Bindable(true) + }, + new LabelledSwitchButton + { + Label = "Show negligibles" + } + } + }, + }; + } + } +} From 0a6baf670eff5a90704b95277cf8a8b89dd44375 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Wed, 7 Apr 2021 14:41:21 -0400 Subject: [PATCH 006/150] Send a warning notification if device is unplugged and low battery - Uses Xamarin.Essentials in osu.Game.PlayerLoader to check battery level - Encapsulated battery checking in the public BatteryManager class so battery level and plugged in status can be accessed and edited in TestPlayerLoader - When checking battery level, catch NotImplementedException thrown by Xamarin.Essentials.Battery on non-mobile platforms - Added visual unit tests for battery notification To mock battery status and level, we had to define a batteryManager object in TestPlayerLoader and add a new function ResetPlayerWithBattery() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Marlina José --- osu.Android/Properties/AndroidManifest.xml | 1 + .../Visual/Gameplay/TestScenePlayerLoader.cs | 48 +++++++++++++ osu.Game.Tests/osu.Game.Tests.csproj | 2 +- osu.Game/Configuration/SessionStatics.cs | 2 + osu.Game/Screens/Play/PlayerLoader.cs | 71 +++++++++++++++++++ osu.Game/osu.Game.csproj | 3 +- 6 files changed, 125 insertions(+), 2 deletions(-) diff --git a/osu.Android/Properties/AndroidManifest.xml b/osu.Android/Properties/AndroidManifest.xml index 770eaf2222..e717bab310 100644 --- a/osu.Android/Properties/AndroidManifest.xml +++ b/osu.Android/Properties/AndroidManifest.xml @@ -6,5 +6,6 @@ + \ No newline at end of file diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index 88fbf09ef4..a31d53e3b6 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -93,6 +93,26 @@ namespace osu.Game.Tests.Visual.Gameplay LoadScreen(loader = new TestPlayerLoader(() => player = new TestPlayer(interactive, interactive))); } + /// + /// Sets the input manager child to a new test player loader container instance with a custom battery level + /// + /// If the test player should behave like the production one. + /// If the player's device is plugged in. + /// A custom battery level for the test player. + private void resetPlayerWithBattery(bool interactive, bool pluggedIn, double batteryLevel) + { + Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); + Beatmap.Value.BeatmapInfo.EpilepsyWarning = epilepsyWarning; + + foreach (var mod in SelectedMods.Value.OfType()) + mod.ApplyToTrack(Beatmap.Value.Track); + + loader = new TestPlayerLoader(() => player = new TestPlayer(interactive, interactive)); + loader.batteryManager.ChargeLevel = batteryLevel; + loader.batteryManager.PluggedIn = pluggedIn; + LoadScreen(loader); + } + [Test] public void TestEarlyExitBeforePlayerConstruction() { @@ -270,6 +290,32 @@ namespace osu.Game.Tests.Visual.Gameplay AddUntilStep("wait for player load", () => player.IsLoaded); } + [TestCase(false, 1.0)] // not plugged in, full battery, no notification + [TestCase(false, 0.2)] // not plugged in, at warning level, no notification + [TestCase(true, 0.1)] // plugged in, charging, below warning level, no notification + [TestCase(false, 0.1)] // not plugged in, below warning level, notification + public void TestLowBatteryNotification(bool pluggedIn, double batteryLevel) + { + AddStep("reset notification lock", () => sessionStatics.GetBindable(Static.BatteryLowNotificationShownOnce).Value = false); + + // mock phone on battery + AddStep("load player", () => resetPlayerWithBattery(false, pluggedIn, batteryLevel)); + AddUntilStep("wait for player", () => player?.LoadState == LoadState.Ready); + int notificationCount = !pluggedIn && batteryLevel < PlayerLoader.battery_tolerance ? 1 : 0; + AddAssert("check for notification", () => notificationOverlay.UnreadCount.Value == notificationCount); + AddStep("click notification", () => + { + var scrollContainer = (OsuScrollContainer)notificationOverlay.Children.Last(); + var flowContainer = scrollContainer.Children.OfType>().First(); + var notification = flowContainer.First(); + + InputManager.MoveMouseTo(notification); + InputManager.Click(MouseButton.Left); + }); + + AddUntilStep("wait for player load", () => player.IsLoaded); + } + [TestCase(true)] [TestCase(false)] public void TestEpilepsyWarning(bool warning) @@ -310,6 +356,8 @@ namespace osu.Game.Tests.Visual.Gameplay public IReadOnlyList DisplayedMods => MetadataInfo.Mods.Value; + public new BatteryManager batteryManager => base.batteryManager; + public TestPlayerLoader(Func createPlayer) : base(createPlayer) { diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 0e1f6f6b0c..df6d17f615 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -22,4 +22,4 @@ - \ No newline at end of file + diff --git a/osu.Game/Configuration/SessionStatics.cs b/osu.Game/Configuration/SessionStatics.cs index 36eb6964dd..e8ee04731c 100644 --- a/osu.Game/Configuration/SessionStatics.cs +++ b/osu.Game/Configuration/SessionStatics.cs @@ -16,6 +16,7 @@ namespace osu.Game.Configuration { SetDefault(Static.LoginOverlayDisplayed, false); SetDefault(Static.MutedAudioNotificationShownOnce, false); + SetDefault(Static.BatteryLowNotificationShownOnce, false); SetDefault(Static.LastHoverSoundPlaybackTime, (double?)null); SetDefault(Static.SeasonalBackgrounds, null); } @@ -25,6 +26,7 @@ namespace osu.Game.Configuration { LoginOverlayDisplayed, MutedAudioNotificationShownOnce, + BatteryLowNotificationShownOnce, /// /// Info about seasonal backgrounds available fetched from API - see . diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 679b3c7313..01a51e6e7a 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -26,6 +26,7 @@ using osu.Game.Screens.Play.PlayerSettings; using osu.Game.Users; using osuTK; using osuTK.Graphics; +using Xamarin.Essentials; namespace osu.Game.Screens.Play { @@ -121,6 +122,7 @@ namespace osu.Game.Screens.Play private void load(SessionStatics sessionStatics) { muteWarningShownOnce = sessionStatics.GetBindable(Static.MutedAudioNotificationShownOnce); + batteryWarningShownOnce = sessionStatics.GetBindable(Static.BatteryLowNotificationShownOnce); InternalChild = (content = new LogoTrackingContainer { @@ -196,6 +198,7 @@ namespace osu.Game.Screens.Play Scheduler.Add(new ScheduledDelegate(pushWhenLoaded, Clock.CurrentTime + 1800, 0)); showMuteWarningIfNeeded(); + showBatteryWarningIfNeeded(); } public override void OnResuming(IScreen last) @@ -470,5 +473,73 @@ namespace osu.Game.Screens.Play } #endregion + + #region Low battery warning + private Bindable batteryWarningShownOnce; + + // Send a warning if battery is less than 20% + public const double battery_tolerance = 0.2; + + private void showBatteryWarningIfNeeded() + { + if (!batteryWarningShownOnce.Value) + { + // Checks if the notification has not been shown yet, device is unplugged and if device battery is low. + if (!batteryManager.PluggedIn && batteryManager.ChargeLevel < battery_tolerance) + { + Console.WriteLine("Battery level: {0}", batteryManager.ChargeLevel); + notificationOverlay?.Post(new BatteryWarningNotification()); + batteryWarningShownOnce.Value = true; + } + } + } + public BatteryManager batteryManager = new BatteryManager(); + public class BatteryManager + { + public double ChargeLevel; + public bool PluggedIn; + public BatteryManager() + { + // Attempt to get battery information using Xamarin.Essentials + // Xamarin.Essentials throws a NotSupportedOrImplementedException on .NET standard so this only works on iOS/Android + // https://github.com/xamarin/Essentials/blob/7218ab88f7fbe00ec3379bd54e6c0ce35ffc0c22/Xamarin.Essentials/Battery/Battery.netstandard.tvos.cs + try + { + ChargeLevel = Battery.ChargeLevel; + PluggedIn = Battery.PowerSource == BatteryPowerSource.Battery; + } + catch (NotImplementedException e) + { + Console.WriteLine("Could not access battery info: {0}", e); + ChargeLevel = 1.0; + PluggedIn = false; + } + } + } + + private class BatteryWarningNotification : SimpleNotification + { + public override bool IsImportant => true; + + public BatteryWarningNotification() + { + Text = "Your battery level is low! Charge your device to prevent interruptions."; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours, NotificationOverlay notificationOverlay) + { + Icon = FontAwesome.Solid.BatteryQuarter; + IconBackgound.Colour = colours.RedDark; + + Activated = delegate + { + notificationOverlay.Hide(); + return true; + }; + } + } + + #endregion } } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 292e5b932f..c68be5313d 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -1,4 +1,4 @@ - + netstandard2.1 Library @@ -35,5 +35,6 @@ + From 6bccb3aab6487e0edd0d8030378c2f18c2868194 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Thu, 8 Apr 2021 19:34:35 -0400 Subject: [PATCH 007/150] Use DI to implement battery detection, add BatteryCutoff property - Removed the Xamarin.Essentials package from osu.Game and added it to osu.iOS and osu.Android only. - iOS and Android implementations use Xamarin.Essentials.Battery, while the Desktop implementation only returns 100% battery for now. - Added a BatteryCutoff property to PowerStatus so it can be different for each platform (default 20%, 25% on iOS) --- osu.Android/OsuGameAndroid.cs | 9 +++ osu.Android/osu.Android.csproj | 4 + osu.Desktop/OsuGameDesktop.cs | 3 + .../Visual/Gameplay/TestScenePlayerLoader.cs | 76 +++++++------------ osu.Game/OsuGameBase.cs | 5 ++ osu.Game/Screens/Play/PlayerLoader.cs | 37 ++------- osu.Game/Tests/OsuTestBrowser.cs | 5 ++ osu.Game/Utils/PowerStatus.cs | 22 ++++++ osu.iOS/OsuGameIOS.cs | 17 +++++ osu.iOS/osu.iOS.csproj | 4 + 10 files changed, 103 insertions(+), 79 deletions(-) create mode 100644 osu.Game/Utils/PowerStatus.cs diff --git a/osu.Android/OsuGameAndroid.cs b/osu.Android/OsuGameAndroid.cs index 21d6336b2c..4c82a175fc 100644 --- a/osu.Android/OsuGameAndroid.cs +++ b/osu.Android/OsuGameAndroid.cs @@ -7,6 +7,8 @@ using Android.OS; using osu.Framework.Allocation; using osu.Game; using osu.Game.Updater; +using osu.Game.Utils; +using Xamarin.Essentials; namespace osu.Android { @@ -19,6 +21,7 @@ namespace osu.Android : base(null) { gameActivity = activity; + powerStatus = new AndroidPowerStatus(); } public override Version AssemblyVersion @@ -72,5 +75,11 @@ namespace osu.Android } protected override UpdateManager CreateUpdateManager() => new SimpleUpdateManager(); + public class AndroidPowerStatus : PowerStatus + { + public override double ChargeLevel => Battery.ChargeLevel; + + public override bool IsCharging => Battery.PowerSource != BatteryPowerSource.Battery; + } } } diff --git a/osu.Android/osu.Android.csproj b/osu.Android/osu.Android.csproj index 54857ac87d..64d5e5b1c8 100644 --- a/osu.Android/osu.Android.csproj +++ b/osu.Android/osu.Android.csproj @@ -63,5 +63,9 @@ 5.0.0 + + + + \ No newline at end of file diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 0c21c75290..b6c57b219d 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -21,6 +21,8 @@ using osu.Game.Updater; using osu.Desktop.Windows; using osu.Framework.Threading; using osu.Game.IO; +using osu.Game.Utils; +using osu.Framework.Allocation; namespace osu.Desktop { @@ -33,6 +35,7 @@ namespace osu.Desktop : base(args) { noVersionOverlay = args?.Any(a => a == "--no-version-overlay") ?? false; + powerStatus = new DefaultPowerStatus(); } public override StableStorage GetStorageForStableInstall() diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index a31d53e3b6..c5cc10993c 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -25,6 +25,7 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Play; using osu.Game.Screens.Play.PlayerSettings; +using osu.Game.Utils; using osuTK.Input; namespace osu.Game.Tests.Visual.Gameplay @@ -48,6 +49,9 @@ namespace osu.Game.Tests.Visual.Gameplay [Cached] private readonly VolumeOverlay volumeOverlay; + [Resolved] + private PowerStatus powerStatus { get; set; } + private readonly ChangelogOverlay changelogOverlay; public TestScenePlayerLoader() @@ -93,26 +97,6 @@ namespace osu.Game.Tests.Visual.Gameplay LoadScreen(loader = new TestPlayerLoader(() => player = new TestPlayer(interactive, interactive))); } - /// - /// Sets the input manager child to a new test player loader container instance with a custom battery level - /// - /// If the test player should behave like the production one. - /// If the player's device is plugged in. - /// A custom battery level for the test player. - private void resetPlayerWithBattery(bool interactive, bool pluggedIn, double batteryLevel) - { - Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); - Beatmap.Value.BeatmapInfo.EpilepsyWarning = epilepsyWarning; - - foreach (var mod in SelectedMods.Value.OfType()) - mod.ApplyToTrack(Beatmap.Value.Track); - - loader = new TestPlayerLoader(() => player = new TestPlayer(interactive, interactive)); - loader.batteryManager.ChargeLevel = batteryLevel; - loader.batteryManager.PluggedIn = pluggedIn; - LoadScreen(loader); - } - [Test] public void TestEarlyExitBeforePlayerConstruction() { @@ -290,32 +274,6 @@ namespace osu.Game.Tests.Visual.Gameplay AddUntilStep("wait for player load", () => player.IsLoaded); } - [TestCase(false, 1.0)] // not plugged in, full battery, no notification - [TestCase(false, 0.2)] // not plugged in, at warning level, no notification - [TestCase(true, 0.1)] // plugged in, charging, below warning level, no notification - [TestCase(false, 0.1)] // not plugged in, below warning level, notification - public void TestLowBatteryNotification(bool pluggedIn, double batteryLevel) - { - AddStep("reset notification lock", () => sessionStatics.GetBindable(Static.BatteryLowNotificationShownOnce).Value = false); - - // mock phone on battery - AddStep("load player", () => resetPlayerWithBattery(false, pluggedIn, batteryLevel)); - AddUntilStep("wait for player", () => player?.LoadState == LoadState.Ready); - int notificationCount = !pluggedIn && batteryLevel < PlayerLoader.battery_tolerance ? 1 : 0; - AddAssert("check for notification", () => notificationOverlay.UnreadCount.Value == notificationCount); - AddStep("click notification", () => - { - var scrollContainer = (OsuScrollContainer)notificationOverlay.Children.Last(); - var flowContainer = scrollContainer.Children.OfType>().First(); - var notification = flowContainer.First(); - - InputManager.MoveMouseTo(notification); - InputManager.Click(MouseButton.Left); - }); - - AddUntilStep("wait for player load", () => player.IsLoaded); - } - [TestCase(true)] [TestCase(false)] public void TestEpilepsyWarning(bool warning) @@ -334,6 +292,30 @@ namespace osu.Game.Tests.Visual.Gameplay } } + [TestCase(false, 1.0)] // not charging, full battery --> no warning + [TestCase(false, 0.2)] // not charging, at cutoff --> warning + [TestCase(false, 0.1)] // charging, below cutoff --> warning + public void TestLowBatteryNotification(bool isCharging, double chargeLevel) + { + AddStep("reset notification lock", () => sessionStatics.GetBindable(Static.BatteryLowNotificationShownOnce).Value = false); + + // set charge status and level + AddStep("load player", () => resetPlayer(false, () => { powerStatus.IsCharging = isCharging; powerStatus.ChargeLevel = chargeLevel; })); + AddUntilStep("wait for player", () => player?.LoadState == LoadState.Ready); + int notificationCount = !isCharging && chargeLevel <= powerStatus.BatteryCutoff ? 1 : 0; + AddAssert("check for notification", () => notificationOverlay.UnreadCount.Value == notificationCount); + AddStep("click notification", () => + { + var scrollContainer = (OsuScrollContainer)notificationOverlay.Children.Last(); + var flowContainer = scrollContainer.Children.OfType>().First(); + var notification = flowContainer.First(); + + InputManager.MoveMouseTo(notification); + InputManager.Click(MouseButton.Left); + }); + AddUntilStep("wait for player load", () => player.IsLoaded); + } + [Test] public void TestEpilepsyWarningEarlyExit() { @@ -356,8 +338,6 @@ namespace osu.Game.Tests.Visual.Gameplay public IReadOnlyList DisplayedMods => MetadataInfo.Mods.Value; - public new BatteryManager batteryManager => base.batteryManager; - public TestPlayerLoader(Func createPlayer) : base(createPlayer) { diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 21313d0596..53873b0127 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -40,6 +40,7 @@ using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Scoring; using osu.Game.Skinning; +using osu.Game.Utils; using osuTK.Input; using RuntimeInfo = osu.Framework.RuntimeInfo; @@ -95,6 +96,8 @@ namespace osu.Game protected Storage Storage { get; set; } + protected PowerStatus powerStatus; + [Cached] [Cached(typeof(IBindable))] protected readonly Bindable Ruleset = new Bindable(); @@ -329,6 +332,8 @@ namespace osu.Game dependencies.CacheAs(MusicController); Ruleset.BindValueChanged(onRulesetChanged); + + dependencies.CacheAs(powerStatus); } private void onRulesetChanged(ValueChangedEvent r) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 01a51e6e7a..9710fa7e46 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -24,9 +24,9 @@ using osu.Game.Overlays.Notifications; using osu.Game.Screens.Menu; using osu.Game.Screens.Play.PlayerSettings; using osu.Game.Users; +using osu.Game.Utils; using osuTK; using osuTK.Graphics; -using Xamarin.Essentials; namespace osu.Game.Screens.Play { @@ -113,6 +113,9 @@ namespace osu.Game.Screens.Play [Resolved] private AudioManager audioManager { get; set; } + [Resolved] + private PowerStatus powerStatus { get; set; } + public PlayerLoader(Func createPlayer) { this.createPlayer = createPlayer; @@ -265,7 +268,6 @@ namespace osu.Game.Screens.Play } #endregion - protected override void Update() { base.Update(); @@ -477,45 +479,18 @@ namespace osu.Game.Screens.Play #region Low battery warning private Bindable batteryWarningShownOnce; - // Send a warning if battery is less than 20% - public const double battery_tolerance = 0.2; - private void showBatteryWarningIfNeeded() { if (!batteryWarningShownOnce.Value) { - // Checks if the notification has not been shown yet, device is unplugged and if device battery is low. - if (!batteryManager.PluggedIn && batteryManager.ChargeLevel < battery_tolerance) + // Checks if the notification has not been shown yet, device is unplugged and if device battery is at or below the cutoff + if (!powerStatus.IsCharging && powerStatus.ChargeLevel <= powerStatus.BatteryCutoff) { - Console.WriteLine("Battery level: {0}", batteryManager.ChargeLevel); notificationOverlay?.Post(new BatteryWarningNotification()); batteryWarningShownOnce.Value = true; } } } - public BatteryManager batteryManager = new BatteryManager(); - public class BatteryManager - { - public double ChargeLevel; - public bool PluggedIn; - public BatteryManager() - { - // Attempt to get battery information using Xamarin.Essentials - // Xamarin.Essentials throws a NotSupportedOrImplementedException on .NET standard so this only works on iOS/Android - // https://github.com/xamarin/Essentials/blob/7218ab88f7fbe00ec3379bd54e6c0ce35ffc0c22/Xamarin.Essentials/Battery/Battery.netstandard.tvos.cs - try - { - ChargeLevel = Battery.ChargeLevel; - PluggedIn = Battery.PowerSource == BatteryPowerSource.Battery; - } - catch (NotImplementedException e) - { - Console.WriteLine("Could not access battery info: {0}", e); - ChargeLevel = 1.0; - PluggedIn = false; - } - } - } private class BatteryWarningNotification : SimpleNotification { diff --git a/osu.Game/Tests/OsuTestBrowser.cs b/osu.Game/Tests/OsuTestBrowser.cs index 71b0b02fa6..afce7dd38b 100644 --- a/osu.Game/Tests/OsuTestBrowser.cs +++ b/osu.Game/Tests/OsuTestBrowser.cs @@ -7,11 +7,16 @@ using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Graphics; using osu.Game.Screens.Backgrounds; +using osu.Game.Utils; namespace osu.Game.Tests { public class OsuTestBrowser : OsuGameBase { + public OsuTestBrowser() + { + powerStatus = new DefaultPowerStatus(); + } protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Game/Utils/PowerStatus.cs b/osu.Game/Utils/PowerStatus.cs new file mode 100644 index 0000000000..0657c98af1 --- /dev/null +++ b/osu.Game/Utils/PowerStatus.cs @@ -0,0 +1,22 @@ +// 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.Utils +{ + public abstract class PowerStatus + { + /// + /// The maximum battery level before a warning notification + /// is sent. + /// + public virtual double BatteryCutoff { get; } = 0.2; + public virtual double ChargeLevel { get; set; } + public virtual bool IsCharging { get; set; } + } + + public class DefaultPowerStatus : PowerStatus + { + public override double ChargeLevel { get; set; } = 1; + public override bool IsCharging { get; set; } = true; + } +} diff --git a/osu.iOS/OsuGameIOS.cs b/osu.iOS/OsuGameIOS.cs index 5125ad81e0..21dce338dc 100644 --- a/osu.iOS/OsuGameIOS.cs +++ b/osu.iOS/OsuGameIOS.cs @@ -5,13 +5,30 @@ using System; using Foundation; using osu.Game; using osu.Game.Updater; +using osu.Game.Utils; +using Xamarin.Essentials; namespace osu.iOS { public class OsuGameIOS : OsuGame { + public OsuGameIOS() + : base(null) + { + powerStatus = new IOSPowerStatus(); + } public override Version AssemblyVersion => new Version(NSBundle.MainBundle.InfoDictionary["CFBundleVersion"].ToString()); protected override UpdateManager CreateUpdateManager() => new SimpleUpdateManager(); + + public class IOSPowerStatus : PowerStatus + { + // The low battery alert appears at 20% on iOS + // https://github.com/ppy/osu/issues/12239 + public override double BatteryCutoff => 0.25; + public override double ChargeLevel => Battery.ChargeLevel; + + public override bool IsCharging => Battery.PowerSource != BatteryPowerSource.Battery; + } } } diff --git a/osu.iOS/osu.iOS.csproj b/osu.iOS/osu.iOS.csproj index 1e9a21865d..ed6f52c60e 100644 --- a/osu.iOS/osu.iOS.csproj +++ b/osu.iOS/osu.iOS.csproj @@ -116,5 +116,9 @@ false + + + + From 493c095535c3acdddfada39ab29ecfd430c7afe9 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Thu, 8 Apr 2021 20:28:23 -0400 Subject: [PATCH 008/150] Fixed code style --- osu.Android/OsuGameAndroid.cs | 2 +- osu.Desktop/OsuGameDesktop.cs | 3 +-- osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs | 6 +++++- osu.Game/OsuGameBase.cs | 4 ++-- osu.Game/Screens/Play/PlayerLoader.cs | 1 + osu.Game/Tests/OsuTestBrowser.cs | 3 ++- osu.Game/Utils/PowerStatus.cs | 1 + osu.iOS/OsuGameIOS.cs | 2 +- 8 files changed, 14 insertions(+), 8 deletions(-) diff --git a/osu.Android/OsuGameAndroid.cs b/osu.Android/OsuGameAndroid.cs index 4c82a175fc..eb0d499c8f 100644 --- a/osu.Android/OsuGameAndroid.cs +++ b/osu.Android/OsuGameAndroid.cs @@ -21,7 +21,7 @@ namespace osu.Android : base(null) { gameActivity = activity; - powerStatus = new AndroidPowerStatus(); + PowerStatus = new AndroidPowerStatus(); } public override Version AssemblyVersion diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index b6c57b219d..48e28fa251 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -22,7 +22,6 @@ using osu.Desktop.Windows; using osu.Framework.Threading; using osu.Game.IO; using osu.Game.Utils; -using osu.Framework.Allocation; namespace osu.Desktop { @@ -35,7 +34,7 @@ namespace osu.Desktop : base(args) { noVersionOverlay = args?.Any(a => a == "--no-version-overlay") ?? false; - powerStatus = new DefaultPowerStatus(); + PowerStatus = new DefaultPowerStatus(); } public override StableStorage GetStorageForStableInstall() diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index c5cc10993c..b885f0a9be 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -300,7 +300,11 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("reset notification lock", () => sessionStatics.GetBindable(Static.BatteryLowNotificationShownOnce).Value = false); // set charge status and level - AddStep("load player", () => resetPlayer(false, () => { powerStatus.IsCharging = isCharging; powerStatus.ChargeLevel = chargeLevel; })); + AddStep("load player", () => resetPlayer(false, () => + { + powerStatus.IsCharging = isCharging; + powerStatus.ChargeLevel = chargeLevel; + })); AddUntilStep("wait for player", () => player?.LoadState == LoadState.Ready); int notificationCount = !isCharging && chargeLevel <= powerStatus.BatteryCutoff ? 1 : 0; AddAssert("check for notification", () => notificationOverlay.UnreadCount.Value == notificationCount); diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 53873b0127..a907afd8af 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -96,7 +96,7 @@ namespace osu.Game protected Storage Storage { get; set; } - protected PowerStatus powerStatus; + protected PowerStatus PowerStatus; [Cached] [Cached(typeof(IBindable))] @@ -333,7 +333,7 @@ namespace osu.Game Ruleset.BindValueChanged(onRulesetChanged); - dependencies.CacheAs(powerStatus); + dependencies.CacheAs(PowerStatus); } private void onRulesetChanged(ValueChangedEvent r) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 9710fa7e46..6ba99d65d5 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -477,6 +477,7 @@ namespace osu.Game.Screens.Play #endregion #region Low battery warning + private Bindable batteryWarningShownOnce; private void showBatteryWarningIfNeeded() diff --git a/osu.Game/Tests/OsuTestBrowser.cs b/osu.Game/Tests/OsuTestBrowser.cs index afce7dd38b..d4fa2a11d3 100644 --- a/osu.Game/Tests/OsuTestBrowser.cs +++ b/osu.Game/Tests/OsuTestBrowser.cs @@ -15,8 +15,9 @@ namespace osu.Game.Tests { public OsuTestBrowser() { - powerStatus = new DefaultPowerStatus(); + PowerStatus = new DefaultPowerStatus(); } + protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Game/Utils/PowerStatus.cs b/osu.Game/Utils/PowerStatus.cs index 0657c98af1..1caed5b6fa 100644 --- a/osu.Game/Utils/PowerStatus.cs +++ b/osu.Game/Utils/PowerStatus.cs @@ -10,6 +10,7 @@ namespace osu.Game.Utils /// is sent. /// public virtual double BatteryCutoff { get; } = 0.2; + public virtual double ChargeLevel { get; set; } public virtual bool IsCharging { get; set; } } diff --git a/osu.iOS/OsuGameIOS.cs b/osu.iOS/OsuGameIOS.cs index 21dce338dc..d417fa8f8d 100644 --- a/osu.iOS/OsuGameIOS.cs +++ b/osu.iOS/OsuGameIOS.cs @@ -15,7 +15,7 @@ namespace osu.iOS public OsuGameIOS() : base(null) { - powerStatus = new IOSPowerStatus(); + PowerStatus = new IOSPowerStatus(); } public override Version AssemblyVersion => new Version(NSBundle.MainBundle.InfoDictionary["CFBundleVersion"].ToString()); From 6b6a71d3c38e47f2e6441cb6a03dcb04c43b51ac Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Thu, 8 Apr 2021 20:38:16 -0400 Subject: [PATCH 009/150] trim whitespace --- osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index b885f0a9be..2a0f46f5ff 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -301,7 +301,7 @@ namespace osu.Game.Tests.Visual.Gameplay // set charge status and level AddStep("load player", () => resetPlayer(false, () => - { + { powerStatus.IsCharging = isCharging; powerStatus.ChargeLevel = chargeLevel; })); From 59d13b0dd3faa9baca04c6664437294a2ed820f6 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Thu, 8 Apr 2021 21:53:42 -0400 Subject: [PATCH 010/150] Fixed indentation sorry about the style fixes... I'm using JetBrains Rider from now on. --- osu.Android/OsuGameAndroid.cs | 1 + .../Visual/Gameplay/TestScenePlayerLoader.cs | 18 +++++++++--------- osu.Game/Screens/Play/PlayerLoader.cs | 1 + 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/osu.Android/OsuGameAndroid.cs b/osu.Android/OsuGameAndroid.cs index eb0d499c8f..a0c6a1e354 100644 --- a/osu.Android/OsuGameAndroid.cs +++ b/osu.Android/OsuGameAndroid.cs @@ -75,6 +75,7 @@ namespace osu.Android } protected override UpdateManager CreateUpdateManager() => new SimpleUpdateManager(); + public class AndroidPowerStatus : PowerStatus { public override double ChargeLevel => Battery.ChargeLevel; diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index 2a0f46f5ff..1377459c62 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -302,21 +302,21 @@ namespace osu.Game.Tests.Visual.Gameplay // set charge status and level AddStep("load player", () => resetPlayer(false, () => { - powerStatus.IsCharging = isCharging; - powerStatus.ChargeLevel = chargeLevel; + powerStatus.IsCharging = isCharging; + powerStatus.ChargeLevel = chargeLevel; })); AddUntilStep("wait for player", () => player?.LoadState == LoadState.Ready); int notificationCount = !isCharging && chargeLevel <= powerStatus.BatteryCutoff ? 1 : 0; AddAssert("check for notification", () => notificationOverlay.UnreadCount.Value == notificationCount); AddStep("click notification", () => - { - var scrollContainer = (OsuScrollContainer)notificationOverlay.Children.Last(); - var flowContainer = scrollContainer.Children.OfType>().First(); - var notification = flowContainer.First(); + { + var scrollContainer = (OsuScrollContainer)notificationOverlay.Children.Last(); + var flowContainer = scrollContainer.Children.OfType>().First(); + var notification = flowContainer.First(); - InputManager.MoveMouseTo(notification); - InputManager.Click(MouseButton.Left); - }); + InputManager.MoveMouseTo(notification); + InputManager.Click(MouseButton.Left); + }); AddUntilStep("wait for player load", () => player.IsLoaded); } diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 6ba99d65d5..e1ab0b8ef5 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -268,6 +268,7 @@ namespace osu.Game.Screens.Play } #endregion + protected override void Update() { base.Update(); From 08311abc5ec93907337976e0d8ae0209684ef769 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Fri, 9 Apr 2021 17:55:41 -0400 Subject: [PATCH 011/150] Remove setters, cache CreatePowerStatus() and use a dummy LocalPowerStatus class in test scene --- osu.Android/OsuGameAndroid.cs | 7 ++- osu.Desktop/OsuGameDesktop.cs | 2 - .../Visual/Gameplay/TestScenePlayerLoader.cs | 44 +++++++++++++++---- osu.Game/Configuration/SessionStatics.cs | 4 +- osu.Game/OsuGame.cs | 5 +++ osu.Game/OsuGameBase.cs | 4 +- osu.Game/Screens/Play/PlayerLoader.cs | 11 ++--- osu.Game/Tests/OsuTestBrowser.cs | 6 --- osu.Game/Utils/PowerStatus.cs | 26 ++++++----- osu.iOS/OsuGameIOS.cs | 12 ++--- 10 files changed, 74 insertions(+), 47 deletions(-) diff --git a/osu.Android/OsuGameAndroid.cs b/osu.Android/OsuGameAndroid.cs index a0c6a1e354..02f4fa1ad6 100644 --- a/osu.Android/OsuGameAndroid.cs +++ b/osu.Android/OsuGameAndroid.cs @@ -21,7 +21,6 @@ namespace osu.Android : base(null) { gameActivity = activity; - PowerStatus = new AndroidPowerStatus(); } public override Version AssemblyVersion @@ -76,8 +75,12 @@ namespace osu.Android protected override UpdateManager CreateUpdateManager() => new SimpleUpdateManager(); - public class AndroidPowerStatus : PowerStatus + protected override PowerStatus CreatePowerStatus() => new AndroidPowerStatus(); + + private class AndroidPowerStatus : PowerStatus { + public override double BatteryCutoff => 0.20; + public override double ChargeLevel => Battery.ChargeLevel; public override bool IsCharging => Battery.PowerSource != BatteryPowerSource.Battery; diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 48e28fa251..0c21c75290 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -21,7 +21,6 @@ using osu.Game.Updater; using osu.Desktop.Windows; using osu.Framework.Threading; using osu.Game.IO; -using osu.Game.Utils; namespace osu.Desktop { @@ -34,7 +33,6 @@ namespace osu.Desktop : base(args) { noVersionOverlay = args?.Any(a => a == "--no-version-overlay") ?? false; - PowerStatus = new DefaultPowerStatus(); } public override StableStorage GetStorageForStableInstall() diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index 1377459c62..36958b0741 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -49,8 +49,8 @@ namespace osu.Game.Tests.Visual.Gameplay [Cached] private readonly VolumeOverlay volumeOverlay; - [Resolved] - private PowerStatus powerStatus { get; set; } + [Cached(typeof(PowerStatus))] + private readonly LocalPowerStatus powerStatus = new LocalPowerStatus(); private readonly ChangelogOverlay changelogOverlay; @@ -292,22 +292,22 @@ namespace osu.Game.Tests.Visual.Gameplay } } - [TestCase(false, 1.0)] // not charging, full battery --> no warning + [TestCase(false, 1.0)] // not charging, above cutoff --> no warning [TestCase(false, 0.2)] // not charging, at cutoff --> warning - [TestCase(false, 0.1)] // charging, below cutoff --> warning + [TestCase(true, 0.1)] // charging, below cutoff --> no warning public void TestLowBatteryNotification(bool isCharging, double chargeLevel) { - AddStep("reset notification lock", () => sessionStatics.GetBindable(Static.BatteryLowNotificationShownOnce).Value = false); + AddStep("reset notification lock", () => sessionStatics.GetBindable(Static.LowBatteryNotificationShownOnce).Value = false); // set charge status and level AddStep("load player", () => resetPlayer(false, () => { - powerStatus.IsCharging = isCharging; - powerStatus.ChargeLevel = chargeLevel; + powerStatus.SetCharging(isCharging); + powerStatus.SetChargeLevel(chargeLevel); })); AddUntilStep("wait for player", () => player?.LoadState == LoadState.Ready); - int notificationCount = !isCharging && chargeLevel <= powerStatus.BatteryCutoff ? 1 : 0; - AddAssert("check for notification", () => notificationOverlay.UnreadCount.Value == notificationCount); + int warning = !isCharging && chargeLevel <= powerStatus.BatteryCutoff ? 1 : 0; + AddAssert($"notification {(warning == 1 ? "triggered" : "not triggered")}", () => notificationOverlay.UnreadCount.Value == warning); AddStep("click notification", () => { var scrollContainer = (OsuScrollContainer)notificationOverlay.Children.Last(); @@ -380,5 +380,31 @@ namespace osu.Game.Tests.Visual.Gameplay throw new TimeoutException(); } } + + /// + /// Mutable dummy PowerStatus class for + /// + /// + private class LocalPowerStatus : PowerStatus + { + private bool isCharging = true; + private double chargeLevel = 1; + + public override double BatteryCutoff => 0.2; + + public override bool IsCharging => isCharging; + + public override double ChargeLevel => chargeLevel; + + public void SetCharging(bool value) + { + isCharging = value; + } + + public void SetChargeLevel(double value) + { + chargeLevel = value; + } + } } } diff --git a/osu.Game/Configuration/SessionStatics.cs b/osu.Game/Configuration/SessionStatics.cs index e8ee04731c..71e1a1efcc 100644 --- a/osu.Game/Configuration/SessionStatics.cs +++ b/osu.Game/Configuration/SessionStatics.cs @@ -16,7 +16,7 @@ namespace osu.Game.Configuration { SetDefault(Static.LoginOverlayDisplayed, false); SetDefault(Static.MutedAudioNotificationShownOnce, false); - SetDefault(Static.BatteryLowNotificationShownOnce, false); + SetDefault(Static.LowBatteryNotificationShownOnce, false); SetDefault(Static.LastHoverSoundPlaybackTime, (double?)null); SetDefault(Static.SeasonalBackgrounds, null); } @@ -26,7 +26,7 @@ namespace osu.Game.Configuration { LoginOverlayDisplayed, MutedAudioNotificationShownOnce, - BatteryLowNotificationShownOnce, + LowBatteryNotificationShownOnce, /// /// Info about seasonal backgrounds available fetched from API - see . diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 809e5d3c1b..277455b7d3 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -694,6 +694,11 @@ namespace osu.Game loadComponentSingleFile(new AccountCreationOverlay(), topMostOverlayContent.Add, true); loadComponentSingleFile(new DialogOverlay(), topMostOverlayContent.Add, true); + if (CreatePowerStatus() != null) + { + dependencies.CacheAs(CreatePowerStatus()); + } + chatOverlay.State.ValueChanged += state => channelManager.HighPollRate.Value = state.NewValue == Visibility.Visible; Add(externalLinkOpener = new ExternalLinkOpener()); diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index a907afd8af..1b10de614a 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -96,7 +96,7 @@ namespace osu.Game protected Storage Storage { get; set; } - protected PowerStatus PowerStatus; + protected virtual PowerStatus CreatePowerStatus() => null; [Cached] [Cached(typeof(IBindable))] @@ -332,8 +332,6 @@ namespace osu.Game dependencies.CacheAs(MusicController); Ruleset.BindValueChanged(onRulesetChanged); - - dependencies.CacheAs(PowerStatus); } private void onRulesetChanged(ValueChangedEvent r) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index e1ab0b8ef5..d342bf8273 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -113,7 +113,7 @@ namespace osu.Game.Screens.Play [Resolved] private AudioManager audioManager { get; set; } - [Resolved] + [Resolved(CanBeNull = true)] private PowerStatus powerStatus { get; set; } public PlayerLoader(Func createPlayer) @@ -125,7 +125,7 @@ namespace osu.Game.Screens.Play private void load(SessionStatics sessionStatics) { muteWarningShownOnce = sessionStatics.GetBindable(Static.MutedAudioNotificationShownOnce); - batteryWarningShownOnce = sessionStatics.GetBindable(Static.BatteryLowNotificationShownOnce); + batteryWarningShownOnce = sessionStatics.GetBindable(Static.LowBatteryNotificationShownOnce); InternalChild = (content = new LogoTrackingContainer { @@ -483,10 +483,11 @@ namespace osu.Game.Screens.Play private void showBatteryWarningIfNeeded() { + if (powerStatus == null) return; + if (!batteryWarningShownOnce.Value) { - // Checks if the notification has not been shown yet, device is unplugged and if device battery is at or below the cutoff - if (!powerStatus.IsCharging && powerStatus.ChargeLevel <= powerStatus.BatteryCutoff) + if (powerStatus.IsLowBattery) { notificationOverlay?.Post(new BatteryWarningNotification()); batteryWarningShownOnce.Value = true; @@ -500,7 +501,7 @@ namespace osu.Game.Screens.Play public BatteryWarningNotification() { - Text = "Your battery level is low! Charge your device to prevent interruptions."; + Text = "Your battery level is low! Charge your device to prevent interruptions during gameplay."; } [BackgroundDependencyLoader] diff --git a/osu.Game/Tests/OsuTestBrowser.cs b/osu.Game/Tests/OsuTestBrowser.cs index d4fa2a11d3..71b0b02fa6 100644 --- a/osu.Game/Tests/OsuTestBrowser.cs +++ b/osu.Game/Tests/OsuTestBrowser.cs @@ -7,17 +7,11 @@ using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Graphics; using osu.Game.Screens.Backgrounds; -using osu.Game.Utils; namespace osu.Game.Tests { public class OsuTestBrowser : OsuGameBase { - public OsuTestBrowser() - { - PowerStatus = new DefaultPowerStatus(); - } - protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Game/Utils/PowerStatus.cs b/osu.Game/Utils/PowerStatus.cs index 1caed5b6fa..77d531710e 100644 --- a/osu.Game/Utils/PowerStatus.cs +++ b/osu.Game/Utils/PowerStatus.cs @@ -3,21 +3,27 @@ namespace osu.Game.Utils { + /// + /// Provides access to the system's power status. + /// Currently implemented on iOS and Android only. + /// public abstract class PowerStatus { /// - /// The maximum battery level before a warning notification - /// is sent. + /// The maximum battery level considered as low, from 0 to 1. /// - public virtual double BatteryCutoff { get; } = 0.2; + public virtual double BatteryCutoff { get; } = 0; - public virtual double ChargeLevel { get; set; } - public virtual bool IsCharging { get; set; } - } + /// + /// The charge level of the battery, from 0 to 1. + /// + public virtual double ChargeLevel { get; } = 0; - public class DefaultPowerStatus : PowerStatus - { - public override double ChargeLevel { get; set; } = 1; - public override bool IsCharging { get; set; } = true; + public virtual bool IsCharging { get; } = false; + + /// + /// Returns true if = false and <= . + /// + public bool IsLowBattery => !IsCharging && ChargeLevel <= BatteryCutoff; } } diff --git a/osu.iOS/OsuGameIOS.cs b/osu.iOS/OsuGameIOS.cs index d417fa8f8d..b53b594eae 100644 --- a/osu.iOS/OsuGameIOS.cs +++ b/osu.iOS/OsuGameIOS.cs @@ -12,20 +12,16 @@ namespace osu.iOS { public class OsuGameIOS : OsuGame { - public OsuGameIOS() - : base(null) - { - PowerStatus = new IOSPowerStatus(); - } public override Version AssemblyVersion => new Version(NSBundle.MainBundle.InfoDictionary["CFBundleVersion"].ToString()); protected override UpdateManager CreateUpdateManager() => new SimpleUpdateManager(); - public class IOSPowerStatus : PowerStatus + protected override PowerStatus CreatePowerStatus() => new IOSPowerStatus(); + + private class IOSPowerStatus : PowerStatus { - // The low battery alert appears at 20% on iOS - // https://github.com/ppy/osu/issues/12239 public override double BatteryCutoff => 0.25; + public override double ChargeLevel => Battery.ChargeLevel; public override bool IsCharging => Battery.PowerSource != BatteryPowerSource.Battery; From 43174b708c007e7bf8156fd3f6a01a6f8313a024 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Sat, 10 Apr 2021 12:58:40 +0200 Subject: [PATCH 012/150] Remove visibility settings Can look into this later, not really important for a first iteration. --- osu.Game/Screens/Edit/Verify/IssueSettings.cs | 1 - .../Screens/Edit/Verify/VisibilitySettings.cs | 52 ------------------- 2 files changed, 53 deletions(-) delete mode 100644 osu.Game/Screens/Edit/Verify/VisibilitySettings.cs diff --git a/osu.Game/Screens/Edit/Verify/IssueSettings.cs b/osu.Game/Screens/Edit/Verify/IssueSettings.cs index 608be877de..4519231cd2 100644 --- a/osu.Game/Screens/Edit/Verify/IssueSettings.cs +++ b/osu.Game/Screens/Edit/Verify/IssueSettings.cs @@ -41,7 +41,6 @@ namespace osu.Game.Screens.Edit.Verify private IReadOnlyList createSections() => new Drawable[] { - new VisibilitySettings() }; } } diff --git a/osu.Game/Screens/Edit/Verify/VisibilitySettings.cs b/osu.Game/Screens/Edit/Verify/VisibilitySettings.cs deleted file mode 100644 index 6488c616e4..0000000000 --- a/osu.Game/Screens/Edit/Verify/VisibilitySettings.cs +++ /dev/null @@ -1,52 +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 osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics.UserInterfaceV2; -using osuTK; - -namespace osu.Game.Screens.Edit.Verify -{ - internal class VisibilitySettings : CompositeDrawable - { - [BackgroundDependencyLoader] - private void load() - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - - Padding = new MarginPadding(10); - - InternalChildren = new Drawable[] - { - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Spacing = new Vector2(10), - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - new LabelledSwitchButton - { - Label = "Show problems", - Current = new Bindable(true) - }, - new LabelledSwitchButton - { - Label = "Show warnings", - Current = new Bindable(true) - }, - new LabelledSwitchButton - { - Label = "Show negligibles" - } - } - }, - }; - } - } -} From d1007ff26aa9b2f9827e666125d0161dba6fd35f Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Sat, 10 Apr 2021 13:02:22 +0200 Subject: [PATCH 013/150] Move components to more appropriate spot --- osu.Game.Rulesets.Osu/Edit/OsuChecker.cs | 2 +- osu.Game/Rulesets/Edit/Checker.cs | 4 +-- .../Edit/Checks}/Components/BeatmapCheck.cs | 2 +- .../Edit/Checks}/Components/Check.cs | 2 +- .../Edit/Checks}/Components/CheckMetadata.cs | 2 +- .../Edit/Checks}/Components/Issue.cs | 25 +++++++++++-------- .../Edit/Checks}/Components/IssueTemplate.cs | 2 +- osu.Game/Screens/Edit/Verify/IssueTable.cs | 2 +- 8 files changed, 22 insertions(+), 19 deletions(-) rename osu.Game/{Screens/Edit/Verify => Rulesets/Edit/Checks}/Components/BeatmapCheck.cs (92%) rename osu.Game/{Screens/Edit/Verify => Rulesets/Edit/Checks}/Components/Check.cs (96%) rename osu.Game/{Screens/Edit/Verify => Rulesets/Edit/Checks}/Components/CheckMetadata.cs (97%) rename osu.Game/{Screens/Edit/Verify => Rulesets/Edit/Checks}/Components/Issue.cs (79%) rename osu.Game/{Screens/Edit/Verify => Rulesets/Edit/Checks}/Components/IssueTemplate.cs (98%) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs b/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs index 9918b53c85..df3847f2fe 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs @@ -5,8 +5,8 @@ using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Checks.Components; using osu.Game.Rulesets.Osu.Edit.Checks; -using osu.Game.Screens.Edit.Verify.Components; namespace osu.Game.Rulesets.Osu.Edit { diff --git a/osu.Game/Rulesets/Edit/Checker.cs b/osu.Game/Rulesets/Edit/Checker.cs index 1c267c3435..9bebfc0668 100644 --- a/osu.Game/Rulesets/Edit/Checker.cs +++ b/osu.Game/Rulesets/Edit/Checker.cs @@ -4,8 +4,8 @@ using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; -using osu.Game.Checks; -using osu.Game.Screens.Edit.Verify.Components; +using osu.Game.Rulesets.Edit.Checks; +using osu.Game.Rulesets.Edit.Checks.Components; namespace osu.Game.Rulesets.Edit { diff --git a/osu.Game/Screens/Edit/Verify/Components/BeatmapCheck.cs b/osu.Game/Rulesets/Edit/Checks/Components/BeatmapCheck.cs similarity index 92% rename from osu.Game/Screens/Edit/Verify/Components/BeatmapCheck.cs rename to osu.Game/Rulesets/Edit/Checks/Components/BeatmapCheck.cs index 7297dab60d..d0f93b5eef 100644 --- a/osu.Game/Screens/Edit/Verify/Components/BeatmapCheck.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/BeatmapCheck.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using osu.Game.Beatmaps; -namespace osu.Game.Screens.Edit.Verify.Components +namespace osu.Game.Rulesets.Edit.Checks.Components { public abstract class BeatmapCheck : Check { diff --git a/osu.Game/Screens/Edit/Verify/Components/Check.cs b/osu.Game/Rulesets/Edit/Checks/Components/Check.cs similarity index 96% rename from osu.Game/Screens/Edit/Verify/Components/Check.cs rename to osu.Game/Rulesets/Edit/Checks/Components/Check.cs index 2ae21fd350..228c11f0f3 100644 --- a/osu.Game/Screens/Edit/Verify/Components/Check.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/Check.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; -namespace osu.Game.Screens.Edit.Verify.Components +namespace osu.Game.Rulesets.Edit.Checks.Components { public abstract class Check { diff --git a/osu.Game/Screens/Edit/Verify/Components/CheckMetadata.cs b/osu.Game/Rulesets/Edit/Checks/Components/CheckMetadata.cs similarity index 97% rename from osu.Game/Screens/Edit/Verify/Components/CheckMetadata.cs rename to osu.Game/Rulesets/Edit/Checks/Components/CheckMetadata.cs index 1cac99d47d..5a226729ad 100644 --- a/osu.Game/Screens/Edit/Verify/Components/CheckMetadata.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/CheckMetadata.cs @@ -1,7 +1,7 @@ // 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 +namespace osu.Game.Rulesets.Edit.Checks.Components { public class CheckMetadata { diff --git a/osu.Game/Screens/Edit/Verify/Components/Issue.cs b/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs similarity index 79% rename from osu.Game/Screens/Edit/Verify/Components/Issue.cs rename to osu.Game/Rulesets/Edit/Checks/Components/Issue.cs index fe81cb9335..0f835202e7 100644 --- a/osu.Game/Screens/Edit/Verify/Components/Issue.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs @@ -7,7 +7,7 @@ using System.Linq; using osu.Game.Extensions; using osu.Game.Rulesets.Objects; -namespace osu.Game.Screens.Edit.Verify.Components +namespace osu.Game.Rulesets.Edit.Checks.Components { public class Issue { @@ -39,7 +39,7 @@ namespace osu.Game.Screens.Edit.Verify.Components public Issue(IssueTemplate template, params object[] args) { Time = null; - HitObjects = System.Array.Empty(); + HitObjects = Array.Empty(); Template = template; Arguments = args; @@ -57,11 +57,20 @@ namespace osu.Game.Screens.Edit.Verify.Components Time = time; } + public Issue(HitObject hitObject, IssueTemplate template, params object[] args) + : this(template, args) + { + Time = hitObject.StartTime; + HitObjects = new[] { hitObject }; + } + public Issue(IEnumerable hitObjects, IssueTemplate template, params object[] args) : this(template, args) { - Time = hitObjects.FirstOrDefault()?.StartTime; - HitObjects = hitObjects.ToArray(); + var hitObjectList = hitObjects.ToList(); + + Time = hitObjectList.FirstOrDefault()?.StartTime; + HitObjects = hitObjectList; } public override string ToString() @@ -71,13 +80,7 @@ namespace osu.Game.Screens.Edit.Verify.Components 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(); + return Time == null ? string.Empty : Time.Value.ToEditorFormattedString(); } } } diff --git a/osu.Game/Screens/Edit/Verify/Components/IssueTemplate.cs b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs similarity index 98% rename from osu.Game/Screens/Edit/Verify/Components/IssueTemplate.cs rename to osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs index b178fa7122..7eaabdc59b 100644 --- a/osu.Game/Screens/Edit/Verify/Components/IssueTemplate.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs @@ -5,7 +5,7 @@ using Humanizer; using osu.Framework.Graphics; using osuTK.Graphics; -namespace osu.Game.Screens.Edit.Verify.Components +namespace osu.Game.Rulesets.Edit.Checks.Components { public class IssueTemplate { diff --git a/osu.Game/Screens/Edit/Verify/IssueTable.cs b/osu.Game/Screens/Edit/Verify/IssueTable.cs index c70695a849..20dceb5333 100644 --- a/osu.Game/Screens/Edit/Verify/IssueTable.cs +++ b/osu.Game/Screens/Edit/Verify/IssueTable.cs @@ -13,7 +13,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Input.Bindings; -using osu.Game.Screens.Edit.Verify.Components; +using osu.Game.Rulesets.Edit.Checks.Components; using osuTK.Graphics; namespace osu.Game.Screens.Edit.Verify From b30e41b805ffb7196851c42fc7c8b36c2828df0d Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Sat, 10 Apr 2021 13:02:36 +0200 Subject: [PATCH 014/150] Fix comment; mode -> ruleset --- osu.Game/Rulesets/Edit/Checker.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/Checker.cs b/osu.Game/Rulesets/Edit/Checker.cs index 9bebfc0668..6687160b10 100644 --- a/osu.Game/Rulesets/Edit/Checker.cs +++ b/osu.Game/Rulesets/Edit/Checker.cs @@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Edit { public abstract class Checker { - // These are all mode-invariant, hence here instead of in e.g. `OsuChecker`. + // These are all ruleset-invariant, hence here instead of in e.g. `OsuChecker`. private readonly List beatmapChecks = new List { new CheckMetadataVowels() 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 015/150] 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); + } + } +} From 6d3cf78e4a4e50bd40c2b5f667d7e805e5bb93e5 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Sat, 10 Apr 2021 13:04:39 +0200 Subject: [PATCH 016/150] Add issue selection This mainly helps with keeping track of which issue was clicked, since doing so switches tab. --- osu.Game/Screens/Edit/Verify/IssueTable.cs | 42 ++++++++++++++++++-- osu.Game/Screens/Edit/Verify/VerifyScreen.cs | 7 ++++ 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Verify/IssueTable.cs b/osu.Game/Screens/Edit/Verify/IssueTable.cs index 20dceb5333..458b0184b6 100644 --- a/osu.Game/Screens/Edit/Verify/IssueTable.cs +++ b/osu.Game/Screens/Edit/Verify/IssueTable.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -26,6 +27,9 @@ namespace osu.Game.Screens.Edit.Verify private readonly FillFlowContainer backgroundFlow; + [Resolved] + private Bindable selectedIssue { get; set; } + public IssueTable() { RelativeSizeAxes = Axes.X; @@ -115,6 +119,7 @@ namespace osu.Game.Screens.Edit.Verify public class RowBackground : OsuClickableContainer { + private readonly Issue issue; private const int fade_duration = 100; private readonly Box hoveredBackground; @@ -128,13 +133,16 @@ namespace osu.Game.Screens.Edit.Verify [Resolved] private EditorBeatmap editorBeatmap { get; set; } + [Resolved] + private Bindable selectedIssue { get; set; } + public RowBackground(Issue issue) { + this.issue = issue; + RelativeSizeAxes = Axes.X; Height = row_height; - AlwaysPresent = true; - CornerRadius = 3; Masking = true; @@ -152,6 +160,8 @@ namespace osu.Game.Screens.Edit.Verify // Supposed to work like clicking timestamps outside of the game. // TODO: Is there already defined behaviour for this I may be able to call? + selectedIssue.Value = issue; + if (issue.Time != null) { clock.Seek(issue.Time.Value); @@ -167,11 +177,35 @@ namespace osu.Game.Screens.Edit.Verify } private Color4 colourHover; + private Color4 colourSelected; [BackgroundDependencyLoader] private void load(OsuColour colours) { hoveredBackground.Colour = colourHover = colours.BlueDarker; + colourSelected = colours.YellowDarker; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + selectedIssue.BindValueChanged(change => { Selected = issue == change.NewValue; }, true); + } + + private bool selected; + + protected bool Selected + { + get => selected; + set + { + if (value == selected) + return; + + selected = value; + updateState(); + } } protected override bool OnHover(HoverEvent e) @@ -188,9 +222,9 @@ namespace osu.Game.Screens.Edit.Verify private void updateState() { - hoveredBackground.FadeColour(colourHover, 450, Easing.OutQuint); + hoveredBackground.FadeColour(selected ? colourSelected : colourHover, 450, Easing.OutQuint); - if (IsHovered) + if (selected || IsHovered) hoveredBackground.FadeIn(fade_duration, Easing.OutQuint); else hoveredBackground.FadeOut(fade_duration, Easing.OutQuint); diff --git a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs index 88397fdbff..ea9f986eb6 100644 --- a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs +++ b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs @@ -13,6 +13,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets; using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Checks.Components; using osuTK; namespace osu.Game.Screens.Edit.Verify @@ -22,6 +23,9 @@ namespace osu.Game.Screens.Edit.Verify private Ruleset ruleset; private static Checker checker; // TODO: Should not be static, but apparently needs to be? + [Cached] + private Bindable selectedIssue = new Bindable(); + public VerifyScreen() : base(EditorScreenMode.Verify) { @@ -74,6 +78,9 @@ namespace osu.Game.Screens.Edit.Verify [Resolved] protected EditorBeatmap Beatmap { get; private set; } + [Resolved] + private Bindable selectedIssue { get; set; } + [BackgroundDependencyLoader] private void load(OsuColour colours) { From c995eca029f5f126e734507018b62fb3ea717bb5 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Sat, 10 Apr 2021 13:05:24 +0200 Subject: [PATCH 017/150] Remove todo Doesn't really matter in the end, as only one checker will run at a time in this case. --- osu.Game/Screens/Edit/Verify/VerifyScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs index ea9f986eb6..b9fd93ac19 100644 --- a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs +++ b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs @@ -21,7 +21,7 @@ namespace osu.Game.Screens.Edit.Verify public class VerifyScreen : EditorScreen { private Ruleset ruleset; - private static Checker checker; // TODO: Should not be static, but apparently needs to be? + private static Checker checker; [Cached] private Bindable selectedIssue = new Bindable(); From 3a4f2e3d7e393d29d1f0f4ad8dcbf989f4f19073 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Sat, 10 Apr 2021 13:09:16 +0200 Subject: [PATCH 018/150] Show table even if no issues --- osu.Game/Screens/Edit/Verify/IssueTable.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Verify/IssueTable.cs b/osu.Game/Screens/Edit/Verify/IssueTable.cs index 458b0184b6..e4612927fd 100644 --- a/osu.Game/Screens/Edit/Verify/IssueTable.cs +++ b/osu.Game/Screens/Edit/Verify/IssueTable.cs @@ -57,7 +57,7 @@ namespace osu.Game.Screens.Edit.Verify Content = null; backgroundFlow.Clear(); - if (value?.Any() != true) + if (value == null) return; foreach (var issue in value) From 747e0f00dc63e060feb246f69a5ffe6c33249604 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Sat, 10 Apr 2021 13:10:05 +0200 Subject: [PATCH 019/150] Improve table formatting --- osu.Game/Screens/Edit/Verify/IssueTable.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Edit/Verify/IssueTable.cs b/osu.Game/Screens/Edit/Verify/IssueTable.cs index e4612927fd..40bd134551 100644 --- a/osu.Game/Screens/Edit/Verify/IssueTable.cs +++ b/osu.Game/Screens/Edit/Verify/IssueTable.cs @@ -74,9 +74,9 @@ namespace osu.Game.Screens.Edit.Verify { var columns = new List { - new TableColumn(string.Empty, Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new TableColumn("Type", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new TableColumn("Time", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new TableColumn(string.Empty, Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize)), + new TableColumn("Type", Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize, minSize: 60)), + new TableColumn("Time", Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize, minSize: 60)), new TableColumn("Message", Anchor.CentreLeft), new TableColumn("Category", Anchor.CentreRight, new Dimension(GridSizeMode.AutoSize)), }; @@ -89,20 +89,21 @@ namespace osu.Game.Screens.Edit.Verify new OsuSpriteText { Text = $"#{index + 1}", - Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Medium) + Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Medium), + Margin = new MarginPadding { Right = 10 } }, new OsuSpriteText { Text = issue.Template.Type.ToString(), Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold), - Margin = new MarginPadding { Left = 10 }, + Margin = new MarginPadding { Right = 10 }, Colour = issue.Template.TypeColour() }, new OsuSpriteText { Text = issue.GetEditorTimestamp(), Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold), - Margin = new MarginPadding(10) + Margin = new MarginPadding { Right = 10 }, }, new OsuSpriteText { From 3289bb03791a11f0181d86a4c21ce13d1e214c2b Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Sat, 10 Apr 2021 14:56:30 +0200 Subject: [PATCH 020/150] Merge `Check` and `BeatmapCheck` We're probably not going to need GeneralChecks or BeatmapsetChecks. The verify tab is only available to a single difficulty at a time, and we already have access to the rest of the set through `IBeatmap`. --- .../Edit/Checks/CheckOffscreenObjects.cs | 2 +- osu.Game.Rulesets.Osu/Edit/OsuChecker.cs | 2 +- osu.Game/Rulesets/Edit/Checker.cs | 4 ++-- .../Rulesets/Edit/Checks/CheckBackground.cs | 2 +- .../Edit/Checks/Components/BeatmapCheck.cs | 19 ------------------ .../Rulesets/Edit/Checks/Components/Check.cs | 20 +++++++++---------- 6 files changed, 14 insertions(+), 35 deletions(-) delete mode 100644 osu.Game/Rulesets/Edit/Checks/Components/BeatmapCheck.cs diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs index c47864855b..3d411d6e12 100644 --- a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs @@ -9,7 +9,7 @@ using osuTK; namespace osu.Game.Rulesets.Osu.Edit.Checks { - public class CheckOffscreenObjects : BeatmapCheck + public class CheckOffscreenObjects : Check { // These are close approximates to the edges of the screen // in gameplay on a 4:3 aspect ratio for osu!stable. diff --git a/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs b/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs index ee3b230679..d0dbc043d4 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Edit { public class OsuChecker : Checker { - public readonly List beatmapChecks = new List + public readonly List beatmapChecks = new List { new CheckOffscreenObjects() }; diff --git a/osu.Game/Rulesets/Edit/Checker.cs b/osu.Game/Rulesets/Edit/Checker.cs index 65d7fc5913..6ab6ed75e8 100644 --- a/osu.Game/Rulesets/Edit/Checker.cs +++ b/osu.Game/Rulesets/Edit/Checker.cs @@ -12,14 +12,14 @@ namespace osu.Game.Rulesets.Edit public abstract class Checker { // These are all ruleset-invariant, hence here instead of in e.g. `OsuChecker`. - private readonly List beatmapChecks = new List + private readonly IReadOnlyList checks = new List { new CheckBackground() }; public virtual IEnumerable Run(IBeatmap beatmap) { - return beatmapChecks.SelectMany(check => check.Run(beatmap)); + return checks.SelectMany(check => check.Run(beatmap)); } } } diff --git a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs index 9376f9568a..aa10fad77b 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs @@ -8,7 +8,7 @@ using osu.Game.Rulesets.Edit.Checks.Components; namespace osu.Game.Rulesets.Edit.Checks { - public class CheckBackground : BeatmapCheck + public class CheckBackground : Check { public override CheckMetadata Metadata() => new CheckMetadata ( diff --git a/osu.Game/Rulesets/Edit/Checks/Components/BeatmapCheck.cs b/osu.Game/Rulesets/Edit/Checks/Components/BeatmapCheck.cs deleted file mode 100644 index d0f93b5eef..0000000000 --- a/osu.Game/Rulesets/Edit/Checks/Components/BeatmapCheck.cs +++ /dev/null @@ -1,19 +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 osu.Game.Beatmaps; - -namespace osu.Game.Rulesets.Edit.Checks.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/Rulesets/Edit/Checks/Components/Check.cs b/osu.Game/Rulesets/Edit/Checks/Components/Check.cs index 228c11f0f3..4e444cacbf 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/Check.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/Check.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using osu.Game.Beatmaps; namespace osu.Game.Rulesets.Edit.Checks.Components { @@ -21,21 +22,18 @@ namespace osu.Game.Rulesets.Edit.Checks.Components /// public abstract IEnumerable Templates(); + /// + /// Returns zero, one, or several issues detected by this + /// check on the given beatmap. + /// + /// The beatmap to run the check on. + /// + public abstract IEnumerable Run(IBeatmap beatmap); + 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); - } } From 7d40b017223a008e17cf53cf0b05006e8dfe288f Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Sat, 10 Apr 2021 15:18:15 +0200 Subject: [PATCH 021/150] Remove old todo --- osu.Game/Screens/Edit/Verify/IssueTable.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Verify/IssueTable.cs b/osu.Game/Screens/Edit/Verify/IssueTable.cs index 40bd134551..5f04c7c4e8 100644 --- a/osu.Game/Screens/Edit/Verify/IssueTable.cs +++ b/osu.Game/Screens/Edit/Verify/IssueTable.cs @@ -158,9 +158,6 @@ namespace osu.Game.Screens.Edit.Verify Action = () => { - // Supposed to work like clicking timestamps outside of the game. - // TODO: Is there already defined behaviour for this I may be able to call? - selectedIssue.Value = issue; if (issue.Time != null) From dac733cced62e30089f52fa6147f5210197b3e20 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Sat, 10 Apr 2021 15:49:57 +0200 Subject: [PATCH 022/150] Fix field name and accessibility --- osu.Game.Rulesets.Osu/Edit/OsuChecker.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs b/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs index d0dbc043d4..10f0ecf0cf 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Edit { public class OsuChecker : Checker { - public readonly List beatmapChecks = new List + private readonly List checks = new List { new CheckOffscreenObjects() }; @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Edit foreach (var issue in base.Run(beatmap)) yield return issue; - foreach (var issue in beatmapChecks.SelectMany(check => check.Run(beatmap))) + foreach (var issue in checks.SelectMany(check => check.Run(beatmap))) yield return issue; } } From 2b947a44da2d320669f61d13d6c1ee8c7c660001 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 11 Apr 2021 09:00:37 +0300 Subject: [PATCH 023/150] Cache power status at base instead --- osu.Game/OsuGame.cs | 5 ----- osu.Game/OsuGameBase.cs | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 277455b7d3..809e5d3c1b 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -694,11 +694,6 @@ namespace osu.Game loadComponentSingleFile(new AccountCreationOverlay(), topMostOverlayContent.Add, true); loadComponentSingleFile(new DialogOverlay(), topMostOverlayContent.Add, true); - if (CreatePowerStatus() != null) - { - dependencies.CacheAs(CreatePowerStatus()); - } - chatOverlay.State.ValueChanged += state => channelManager.HighPollRate.Value = state.NewValue == Visibility.Visible; Add(externalLinkOpener = new ExternalLinkOpener()); diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 41d790ea4a..fec8272076 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -284,6 +284,11 @@ namespace osu.Game dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory, RulesetStore)); dependencies.Cache(SettingsStore = new SettingsStore(contextFactory)); dependencies.Cache(RulesetConfigCache = new RulesetConfigCache(SettingsStore)); + + var powerStatus = CreatePowerStatus(); + if (powerStatus != null) + dependencies.CacheAs(powerStatus); + dependencies.Cache(new SessionStatics()); dependencies.Cache(new OsuColour()); From 3d85dc11c62aedccda43b373326463c9d5be5162 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 11 Apr 2021 09:02:32 +0300 Subject: [PATCH 024/150] Adjust documentation --- osu.Game/Utils/PowerStatus.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Utils/PowerStatus.cs b/osu.Game/Utils/PowerStatus.cs index 77d531710e..a53f50b620 100644 --- a/osu.Game/Utils/PowerStatus.cs +++ b/osu.Game/Utils/PowerStatus.cs @@ -22,7 +22,8 @@ namespace osu.Game.Utils public virtual bool IsCharging { get; } = false; /// - /// Returns true if = false and <= . + /// Whether the battery is currently low in charge. + /// Returns true if not charging and current charge level is lower than or equal to . /// public bool IsLowBattery => !IsCharging && ChargeLevel <= BatteryCutoff; } From 07ee1b4d0b73edebaaffc632acd13dc993f0871c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 11 Apr 2021 09:11:28 +0300 Subject: [PATCH 025/150] Make power status properties abstract --- osu.Game/Utils/PowerStatus.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Utils/PowerStatus.cs b/osu.Game/Utils/PowerStatus.cs index a53f50b620..46f7e32b9e 100644 --- a/osu.Game/Utils/PowerStatus.cs +++ b/osu.Game/Utils/PowerStatus.cs @@ -12,14 +12,14 @@ namespace osu.Game.Utils /// /// The maximum battery level considered as low, from 0 to 1. /// - public virtual double BatteryCutoff { get; } = 0; + public abstract double BatteryCutoff { get; } /// /// The charge level of the battery, from 0 to 1. /// - public virtual double ChargeLevel { get; } = 0; + public abstract double ChargeLevel { get; } - public virtual bool IsCharging { get; } = false; + public abstract bool IsCharging { get; } /// /// Whether the battery is currently low in charge. From cb947a3b2710d1c22a55a1ac91262c83bc68591c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 11 Apr 2021 10:02:51 +0300 Subject: [PATCH 026/150] Add expected output in test case rather than determining internally --- .../Visual/Gameplay/TestScenePlayerLoader.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index 36958b0741..657c1dd47e 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -292,10 +292,10 @@ namespace osu.Game.Tests.Visual.Gameplay } } - [TestCase(false, 1.0)] // not charging, above cutoff --> no warning - [TestCase(false, 0.2)] // not charging, at cutoff --> warning - [TestCase(true, 0.1)] // charging, below cutoff --> no warning - public void TestLowBatteryNotification(bool isCharging, double chargeLevel) + [TestCase(false, 1.0, false)] // not charging, above cutoff --> no warning + [TestCase(true, 0.1, false)] // charging, below cutoff --> no warning + [TestCase(false, 0.2, true)] // not charging, at cutoff --> warning + public void TestLowBatteryNotification(bool isCharging, double chargeLevel, bool shouldWarn) { AddStep("reset notification lock", () => sessionStatics.GetBindable(Static.LowBatteryNotificationShownOnce).Value = false); @@ -306,8 +306,7 @@ namespace osu.Game.Tests.Visual.Gameplay powerStatus.SetChargeLevel(chargeLevel); })); AddUntilStep("wait for player", () => player?.LoadState == LoadState.Ready); - int warning = !isCharging && chargeLevel <= powerStatus.BatteryCutoff ? 1 : 0; - AddAssert($"notification {(warning == 1 ? "triggered" : "not triggered")}", () => notificationOverlay.UnreadCount.Value == warning); + AddAssert($"notification {(shouldWarn ? "triggered" : "not triggered")}", () => notificationOverlay.UnreadCount.Value == (shouldWarn ? 1 : 0)); AddStep("click notification", () => { var scrollContainer = (OsuScrollContainer)notificationOverlay.Children.Last(); From 419fd4470cbb818298cd5fe324d8ff528b2f3901 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 11 Apr 2021 08:57:44 +0300 Subject: [PATCH 027/150] Reorder method declaration --- osu.Game/OsuGameBase.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index fec8272076..96aabf0024 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -96,8 +96,6 @@ namespace osu.Game protected Storage Storage { get; set; } - protected virtual PowerStatus CreatePowerStatus() => null; - [Cached] [Cached(typeof(IBindable))] protected readonly Bindable Ruleset = new Bindable(); @@ -159,6 +157,8 @@ namespace osu.Game protected override UserInputManager CreateUserInputManager() => new OsuUserInputManager(); + protected virtual PowerStatus CreatePowerStatus() => null; + /// /// The maximum volume at which audio tracks should playback. This can be set lower than 1 to create some head-room for sound effects. /// From b7e16c2fcc8cad36ab2f40cc59c3f254088624e9 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Sun, 11 Apr 2021 15:47:38 -0400 Subject: [PATCH 028/150] Remove Xamarin.Essentials package from main project --- osu.Game/osu.Game.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index d0a918a8f5..471eced55a 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -35,6 +35,5 @@ - From d6d8ea5b6b5ce956d81863136f5eff3f62bf5cd6 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 12 Apr 2021 11:17:56 +0900 Subject: [PATCH 029/150] Throw when getting a frame of an empty replay --- .../Gameplay/TestSceneSpectatorPlayback.cs | 30 +++++++++---------- osu.Game/Input/Handlers/ReplayInputHandler.cs | 2 -- .../Replays/FramedReplayInputHandler.cs | 9 ++++-- osu.Game/Rulesets/UI/RulesetInputManager.cs | 10 +++++++ 4 files changed, 32 insertions(+), 19 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs index 35b3bfc1f8..9c763814f3 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs @@ -204,27 +204,27 @@ namespace osu.Game.Tests.Visual.Gameplay return; } - if (replayHandler.NextFrame != null) - { - var lastFrame = replay.Frames.LastOrDefault(); + if (!replayHandler.HasFrames) + return; - // this isn't perfect as we basically can't be aware of the rate-of-send here (the streamer is not sending data when not being moved). - // in gameplay playback, the case where NextFrame is null would pause gameplay and handle this correctly; it's strictly a test limitation / best effort implementation. - if (lastFrame != null) - latency = Math.Max(latency, Time.Current - lastFrame.Time); + var lastFrame = replay.Frames.LastOrDefault(); - latencyDisplay.Text = $"latency: {latency:N1}"; + // this isn't perfect as we basically can't be aware of the rate-of-send here (the streamer is not sending data when not being moved). + // in gameplay playback, the case where NextFrame is null would pause gameplay and handle this correctly; it's strictly a test limitation / best effort implementation. + if (lastFrame != null) + latency = Math.Max(latency, Time.Current - lastFrame.Time); - double proposedTime = Time.Current - latency + Time.Elapsed; + latencyDisplay.Text = $"latency: {latency:N1}"; - // this will either advance by one or zero frames. - double? time = replayHandler.SetFrameFromTime(proposedTime); + double proposedTime = Time.Current - latency + Time.Elapsed; - if (time == null) - return; + // this will either advance by one or zero frames. + double? time = replayHandler.SetFrameFromTime(proposedTime); - manualClock.CurrentTime = time.Value; - } + if (time == null) + return; + + manualClock.CurrentTime = time.Value; } [TearDownSteps] diff --git a/osu.Game/Input/Handlers/ReplayInputHandler.cs b/osu.Game/Input/Handlers/ReplayInputHandler.cs index fba1bee0b8..cd76000f98 100644 --- a/osu.Game/Input/Handlers/ReplayInputHandler.cs +++ b/osu.Game/Input/Handlers/ReplayInputHandler.cs @@ -32,8 +32,6 @@ namespace osu.Game.Input.Handlers public override bool Initialize(GameHost host) => true; - public override bool IsActive => true; - public class ReplayState : IInput where T : struct { diff --git a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs index 0b41ca31ea..5eaccc766e 100644 --- a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs +++ b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs @@ -17,6 +17,8 @@ namespace osu.Game.Rulesets.Replays public abstract class FramedReplayInputHandler : ReplayInputHandler where TFrame : ReplayFrame { + public override bool IsActive => HasFrames; + private readonly Replay replay; protected List Frames => replay.Frames; @@ -25,7 +27,10 @@ namespace osu.Game.Rulesets.Replays { get { - if (!HasFrames || !currentFrameIndex.HasValue) + if (!HasFrames) + throw new InvalidOperationException($"Cannot get {nameof(CurrentFrame)} of the empty replay"); + + if (!currentFrameIndex.HasValue) return null; return (TFrame)Frames[currentFrameIndex.Value]; @@ -37,7 +42,7 @@ namespace osu.Game.Rulesets.Replays get { if (!HasFrames) - return null; + throw new InvalidOperationException($"Cannot get {nameof(NextFrame)} of the empty replay"); if (!currentFrameIndex.HasValue) return currentDirection > 0 ? (TFrame)Frames[0] : null; diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index d6f002ea2c..fb56a5d93d 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -10,6 +11,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Framework.Input.StateChanges; using osu.Framework.Input.StateChanges.Events; using osu.Framework.Input.States; using osu.Game.Configuration; @@ -100,6 +102,14 @@ namespace osu.Game.Rulesets.UI #endregion + protected override List GetPendingInputs() + { + if (replayInputHandler != null && !replayInputHandler.IsActive) + return new List(); + + return base.GetPendingInputs(); + } + #region Setting application (disables etc.) private Bindable mouseDisabled; From 995c244ceef985c3b381b6ec6af54f8d5d940b0d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Apr 2021 14:00:26 +0900 Subject: [PATCH 030/150] Remove alt-mousewheel bindings for volume adjustment With the recent changes to the order of processing key bindings (`GlobalAction`s are handled first), having the alt-wheel bindings in here causes a regression as they are handled before `OnScroll` events. Specifically, this means editor alt-scroll functionality no longer works with the default bindings. Removing the bindings fixes this, while also still allowing alt-wheel adjustment of the volume via `VolumeControlReceptor`: https://github.com/ppy/osu/blob/a2f50af4243dfde95ec556859666b65e34f3007b/osu.Game/Overlays/Volume/VolumeControlReceptor.cs#L21-L26 In conjunction with the special case in `OsuScrollContainer`: https://github.com/ppy/osu/blob/02d5b1352b6c9971ea83a131c0d2637e167b56b3/osu.Game/Graphics/Containers/OsuScrollContainer.cs#L103-L105 --- .../Input/Bindings/GlobalActionContainer.cs | 2 - ...700_RefreshVolumeBindingsAgain.Designer.cs | 506 ++++++++++++++++++ ...210412045700_RefreshVolumeBindingsAgain.cs | 16 + 3 files changed, 522 insertions(+), 2 deletions(-) create mode 100644 osu.Game/Migrations/20210412045700_RefreshVolumeBindingsAgain.Designer.cs create mode 100644 osu.Game/Migrations/20210412045700_RefreshVolumeBindingsAgain.cs diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 671c3bc8bc..ba4d757cd7 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -97,9 +97,7 @@ namespace osu.Game.Input.Bindings public IEnumerable AudioControlKeyBindings => new[] { new KeyBinding(new[] { InputKey.Alt, InputKey.Up }, GlobalAction.IncreaseVolume), - new KeyBinding(new[] { InputKey.Alt, InputKey.MouseWheelUp }, GlobalAction.IncreaseVolume), new KeyBinding(new[] { InputKey.Alt, InputKey.Down }, GlobalAction.DecreaseVolume), - new KeyBinding(new[] { InputKey.Alt, InputKey.MouseWheelDown }, GlobalAction.DecreaseVolume), new KeyBinding(new[] { InputKey.Control, InputKey.F4 }, GlobalAction.ToggleMute), diff --git a/osu.Game/Migrations/20210412045700_RefreshVolumeBindingsAgain.Designer.cs b/osu.Game/Migrations/20210412045700_RefreshVolumeBindingsAgain.Designer.cs new file mode 100644 index 0000000000..2c100d39b9 --- /dev/null +++ b/osu.Game/Migrations/20210412045700_RefreshVolumeBindingsAgain.Designer.cs @@ -0,0 +1,506 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using osu.Game.Database; + +namespace osu.Game.Migrations +{ + [DbContext(typeof(OsuDbContext))] + [Migration("20210412045700_RefreshVolumeBindingsAgain")] + partial class RefreshVolumeBindingsAgain + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.6-servicing-10079"); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("ApproachRate"); + + b.Property("CircleSize"); + + b.Property("DrainRate"); + + b.Property("OverallDifficulty"); + + b.Property("SliderMultiplier"); + + b.Property("SliderTickRate"); + + b.HasKey("ID"); + + b.ToTable("BeatmapDifficulty"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("AudioLeadIn"); + + b.Property("BPM"); + + b.Property("BaseDifficultyID"); + + b.Property("BeatDivisor"); + + b.Property("BeatmapSetInfoID"); + + b.Property("Countdown"); + + b.Property("DistanceSpacing"); + + b.Property("GridSize"); + + b.Property("Hash"); + + b.Property("Hidden"); + + b.Property("Length"); + + b.Property("LetterboxInBreaks"); + + b.Property("MD5Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapID"); + + b.Property("Path"); + + b.Property("RulesetID"); + + b.Property("SpecialStyle"); + + b.Property("StackLeniency"); + + b.Property("StarDifficulty"); + + b.Property("Status"); + + b.Property("StoredBookmarks"); + + b.Property("TimelineZoom"); + + b.Property("Version"); + + b.Property("WidescreenStoryboard"); + + b.HasKey("ID"); + + b.HasIndex("BaseDifficultyID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("Hash"); + + b.HasIndex("MD5Hash"); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapID") + .IsUnique(); + + b.HasIndex("RulesetID"); + + b.ToTable("BeatmapInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Artist"); + + b.Property("ArtistUnicode"); + + b.Property("AudioFile"); + + b.Property("AuthorString") + .HasColumnName("Author"); + + b.Property("BackgroundFile"); + + b.Property("PreviewTime"); + + b.Property("Source"); + + b.Property("Tags"); + + b.Property("Title"); + + b.Property("TitleUnicode"); + + b.Property("VideoFile"); + + b.HasKey("ID"); + + b.ToTable("BeatmapMetadata"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("BeatmapSetInfoID"); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.HasKey("ID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("FileInfoID"); + + b.ToTable("BeatmapSetFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapSetID"); + + b.Property("Protected"); + + b.Property("Status"); + + b.HasKey("ID"); + + b.HasIndex("DeletePending"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapSetID") + .IsUnique(); + + b.ToTable("BeatmapSetInfo"); + }); + + modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Key") + .HasColumnName("Key"); + + b.Property("RulesetID"); + + b.Property("SkinInfoID"); + + b.Property("StringValue") + .HasColumnName("Value"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("SkinInfoID"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("Settings"); + }); + + modelBuilder.Entity("osu.Game.IO.FileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Hash"); + + b.Property("ReferenceCount"); + + b.HasKey("ID"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("ReferenceCount"); + + b.ToTable("FileInfo"); + }); + + modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("IntAction") + .HasColumnName("Action"); + + b.Property("KeysString") + .HasColumnName("Keys"); + + b.Property("RulesetID"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("IntAction"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("KeyBinding"); + }); + + modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Available"); + + b.Property("InstantiationInfo"); + + b.Property("Name"); + + b.Property("ShortName"); + + b.HasKey("ID"); + + b.HasIndex("Available"); + + b.HasIndex("ShortName") + .IsUnique(); + + b.ToTable("RulesetInfo"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.Property("ScoreInfoID"); + + b.HasKey("ID"); + + b.HasIndex("FileInfoID"); + + b.HasIndex("ScoreInfoID"); + + b.ToTable("ScoreFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Accuracy") + .HasColumnType("DECIMAL(1,4)"); + + b.Property("BeatmapInfoID"); + + b.Property("Combo"); + + b.Property("Date"); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("MaxCombo"); + + b.Property("ModsJson") + .HasColumnName("Mods"); + + b.Property("OnlineScoreID"); + + b.Property("PP"); + + b.Property("Rank"); + + b.Property("RulesetID"); + + b.Property("StatisticsJson") + .HasColumnName("Statistics"); + + b.Property("TotalScore"); + + b.Property("UserID") + .HasColumnName("UserID"); + + b.Property("UserString") + .HasColumnName("User"); + + b.HasKey("ID"); + + b.HasIndex("BeatmapInfoID"); + + b.HasIndex("OnlineScoreID") + .IsUnique(); + + b.HasIndex("RulesetID"); + + b.ToTable("ScoreInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.Property("SkinInfoID"); + + b.HasKey("ID"); + + b.HasIndex("FileInfoID"); + + b.HasIndex("SkinInfoID"); + + b.ToTable("SkinFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Creator"); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("Name"); + + b.HasKey("ID"); + + b.HasIndex("DeletePending"); + + b.HasIndex("Hash") + .IsUnique(); + + b.ToTable("SkinInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") + .WithMany() + .HasForeignKey("BaseDifficultyID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") + .WithMany("Beatmaps") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("Beatmaps") + .HasForeignKey("MetadataID"); + + b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") + .WithMany() + .HasForeignKey("RulesetID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") + .WithMany("Files") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("BeatmapSets") + .HasForeignKey("MetadataID"); + }); + + modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => + { + b.HasOne("osu.Game.Skinning.SkinInfo") + .WithMany("Settings") + .HasForeignKey("SkinInfoID"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => + { + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Scoring.ScoreInfo") + .WithMany("Files") + .HasForeignKey("ScoreInfoID"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap") + .WithMany("Scores") + .HasForeignKey("BeatmapInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") + .WithMany() + .HasForeignKey("RulesetID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Skinning.SkinInfo") + .WithMany("Files") + .HasForeignKey("SkinInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/osu.Game/Migrations/20210412045700_RefreshVolumeBindingsAgain.cs b/osu.Game/Migrations/20210412045700_RefreshVolumeBindingsAgain.cs new file mode 100644 index 0000000000..155d6670a8 --- /dev/null +++ b/osu.Game/Migrations/20210412045700_RefreshVolumeBindingsAgain.cs @@ -0,0 +1,16 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace osu.Game.Migrations +{ + public partial class RefreshVolumeBindingsAgain : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql("DELETE FROM KeyBinding WHERE action in (6,7)"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + } + } +} From 1c553b5d481d21186eed0498160f90a6e51eedee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Apr 2021 15:14:53 +0900 Subject: [PATCH 031/150] Checker -> BeatmapVerifier --- .../Edit/{OsuChecker.cs => OsuBeatmapVerifier.cs} | 2 +- osu.Game.Rulesets.Osu/OsuRuleset.cs | 2 +- .../Rulesets/Edit/{Checker.cs => BeatmapVerifier.cs} | 2 +- osu.Game/Rulesets/Ruleset.cs | 2 +- osu.Game/Screens/Edit/Verify/VerifyScreen.cs | 10 +++++----- 5 files changed, 9 insertions(+), 9 deletions(-) rename osu.Game.Rulesets.Osu/Edit/{OsuChecker.cs => OsuBeatmapVerifier.cs} (94%) rename osu.Game/Rulesets/Edit/{Checker.cs => BeatmapVerifier.cs} (94%) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs b/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs similarity index 94% rename from osu.Game.Rulesets.Osu/Edit/OsuChecker.cs rename to osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs index 10f0ecf0cf..272612dbaf 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs @@ -10,7 +10,7 @@ using osu.Game.Rulesets.Osu.Edit.Checks; namespace osu.Game.Rulesets.Osu.Edit { - public class OsuChecker : Checker + public class OsuBeatmapVerifier : BeatmapVerifier { private readonly List checks = new List { diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 74679bd578..1658846e98 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -206,7 +206,7 @@ namespace osu.Game.Rulesets.Osu public override HitObjectComposer CreateHitObjectComposer() => new OsuHitObjectComposer(this); - public override Checker CreateChecker() => new OsuChecker(); + public override BeatmapVerifier CreateChecker() => new OsuBeatmapVerifier(); public override string Description => "osu!"; diff --git a/osu.Game/Rulesets/Edit/Checker.cs b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs similarity index 94% rename from osu.Game/Rulesets/Edit/Checker.cs rename to osu.Game/Rulesets/Edit/BeatmapVerifier.cs index 6ab6ed75e8..230b128cc1 100644 --- a/osu.Game/Rulesets/Edit/Checker.cs +++ b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs @@ -9,7 +9,7 @@ using osu.Game.Rulesets.Edit.Checks.Components; namespace osu.Game.Rulesets.Edit { - public abstract class Checker + public abstract class BeatmapVerifier { // These are all ruleset-invariant, hence here instead of in e.g. `OsuChecker`. private readonly IReadOnlyList checks = new List diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 71f80c9982..088bcc7712 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -202,7 +202,7 @@ namespace osu.Game.Rulesets public virtual HitObjectComposer CreateHitObjectComposer() => null; - public virtual Checker CreateChecker() => null; + public virtual BeatmapVerifier CreateChecker() => null; public virtual Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.Solid.QuestionCircle }; diff --git a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs index b9fd93ac19..7a520826f5 100644 --- a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs +++ b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs @@ -21,7 +21,7 @@ namespace osu.Game.Screens.Edit.Verify public class VerifyScreen : EditorScreen { private Ruleset ruleset; - private static Checker checker; + private static BeatmapVerifier beatmapVerifier; [Cached] private Bindable selectedIssue = new Bindable(); @@ -36,7 +36,7 @@ namespace osu.Game.Screens.Edit.Verify var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); ruleset = parent.Get>().Value.BeatmapInfo.Ruleset?.CreateInstance(); - checker = ruleset?.CreateChecker(); + beatmapVerifier = ruleset?.CreateChecker(); return dependencies; } @@ -128,9 +128,9 @@ namespace osu.Game.Screens.Edit.Verify private void refresh() { - table.Issues = checker.Run(Beatmap) - .OrderByDescending(issue => issue.Template.Type) - .ThenByDescending(issue => issue.Template.Origin.Metadata().Category); + table.Issues = beatmapVerifier.Run(Beatmap) + .OrderByDescending(issue => issue.Template.Type) + .ThenByDescending(issue => issue.Template.Origin.Metadata().Category); } } } From 136627b9ac59697129ccd1ae94a67e50442d3d61 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Apr 2021 15:27:12 +0900 Subject: [PATCH 032/150] Wrap xmldoc less and make a few fixes --- osu.Game/Rulesets/Edit/BeatmapVerifier.cs | 9 ++++++--- osu.Game/Rulesets/Edit/Checks/Components/Check.cs | 12 +++--------- .../Rulesets/Edit/Checks/Components/CheckMetadata.cs | 6 ++---- osu.Game/Rulesets/Edit/Checks/Components/Issue.cs | 10 +++------- .../Rulesets/Edit/Checks/Components/IssueTemplate.cs | 5 +---- 5 files changed, 15 insertions(+), 27 deletions(-) diff --git a/osu.Game/Rulesets/Edit/BeatmapVerifier.cs b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs index 230b128cc1..f67a068525 100644 --- a/osu.Game/Rulesets/Edit/BeatmapVerifier.cs +++ b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs @@ -11,15 +11,18 @@ namespace osu.Game.Rulesets.Edit { public abstract class BeatmapVerifier { - // These are all ruleset-invariant, hence here instead of in e.g. `OsuChecker`. - private readonly IReadOnlyList checks = new List + /// + /// Checks which are performed regardless of ruleset. + /// These handle things like beatmap metadata, timing, and other ruleset agnostic elements. + /// + private readonly IReadOnlyList generalChecks = new List { new CheckBackground() }; public virtual IEnumerable Run(IBeatmap beatmap) { - return checks.SelectMany(check => check.Run(beatmap)); + return generalChecks.SelectMany(check => check.Run(beatmap)); } } } diff --git a/osu.Game/Rulesets/Edit/Checks/Components/Check.cs b/osu.Game/Rulesets/Edit/Checks/Components/Check.cs index 4e444cacbf..5a922fde56 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/Check.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/Check.cs @@ -9,25 +9,19 @@ namespace osu.Game.Rulesets.Edit.Checks.Components public abstract class Check { /// - /// Returns the for this check. - /// Basically, its information. + /// The metadata for this check. /// - /// public abstract CheckMetadata Metadata(); /// - /// The templates for issues that this check may use. - /// Basically, what issues this check can detect. + /// All possible templates for issues that this check may return. /// - /// public abstract IEnumerable Templates(); /// - /// Returns zero, one, or several issues detected by this - /// check on the given beatmap. + /// Runs this check and returns any issues detected for the provided beatmap. /// /// The beatmap to run the check on. - /// public abstract IEnumerable Run(IBeatmap beatmap); protected Check() diff --git a/osu.Game/Rulesets/Edit/Checks/Components/CheckMetadata.cs b/osu.Game/Rulesets/Edit/Checks/Components/CheckMetadata.cs index 5a226729ad..38a9a16325 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/CheckMetadata.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/CheckMetadata.cs @@ -42,14 +42,12 @@ namespace osu.Game.Rulesets.Edit.Checks.Components } /// - /// The category this check belongs to. E.g. , - /// , or . + /// 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". + /// 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; diff --git a/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs b/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs index 0f835202e7..6f6ba2a323 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs @@ -22,17 +22,13 @@ namespace osu.Game.Rulesets.Edit.Checks.Components public IReadOnlyList HitObjects; /// - /// The template which this issue is using. This provides properties - /// such as the , and the - /// . + /// 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. + /// The arguments that give this issue its context, based on the . These are then substituted into the . + /// This could for instance include timestamps, which diff is being compared to, what some volume is, etc. /// public object[] Arguments; diff --git a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs index 7eaabdc59b..69c421140b 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs @@ -35,8 +35,7 @@ namespace osu.Game.Rulesets.Edit.Checks.Components public Check Origin; /// - /// The type of the issue. E.g. , - /// , or . + /// The type of the issue. /// public readonly IssueType Type; @@ -57,7 +56,6 @@ namespace osu.Game.Rulesets.Edit.Checks.Components /// 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); @@ -68,7 +66,6 @@ namespace osu.Game.Rulesets.Edit.Checks.Components /// /// Returns the colour corresponding to the type of this issue. /// - /// public Colour4 TypeColour() { return Type switch From 257acf9cd88f3826e6da336fe66e71d3a7834c60 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Apr 2021 15:28:13 +0900 Subject: [PATCH 033/150] Colour constants to private --- .../Edit/Checks/Components/IssueTemplate.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs index 69c421140b..5381d54600 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs @@ -9,6 +9,11 @@ namespace osu.Game.Rulesets.Edit.Checks.Components { public class IssueTemplate { + private static readonly Color4 problem_red = new Colour4(1.0f, 0.4f, 0.4f, 1.0f); + private static readonly Color4 warning_yellow = new Colour4(1.0f, 0.8f, 0.2f, 1.0f); + private static readonly Color4 negligible_green = new Colour4(0.33f, 0.8f, 0.5f, 1.0f); + private static readonly Color4 error_gray = new Colour4(0.5f, 0.5f, 0.5f, 1.0f); + /// /// The type, or severity, of an issue. This decides its priority. /// @@ -58,11 +63,6 @@ namespace osu.Game.Rulesets.Edit.Checks.Components /// 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. /// @@ -70,10 +70,10 @@ namespace osu.Game.Rulesets.Edit.Checks.Components { return Type switch { - IssueType.Problem => PROBLEM_RED, - IssueType.Warning => WARNING_YELLOW, - IssueType.Negligible => NEGLIGIBLE_GREEN, - IssueType.Error => ERROR_GRAY, + IssueType.Problem => problem_red, + IssueType.Warning => warning_yellow, + IssueType.Negligible => negligible_green, + IssueType.Error => error_gray, _ => Color4.White }; } From 3551322f1d95738e09c9e5f9c847abf0163d1d3b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Apr 2021 15:30:33 +0900 Subject: [PATCH 034/150] Fix formatting of colour getter --- .../Edit/Checks/Components/IssueTemplate.cs | 28 +++++++++++++------ osu.Game/Screens/Edit/Verify/IssueTable.cs | 2 +- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs index 5381d54600..6fe8b8de87 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs @@ -66,16 +66,28 @@ namespace osu.Game.Rulesets.Edit.Checks.Components /// /// Returns the colour corresponding to the type of this issue. /// - public Colour4 TypeColour() + public Colour4 Colour { - return Type switch + get { - IssueType.Problem => problem_red, - IssueType.Warning => warning_yellow, - IssueType.Negligible => negligible_green, - IssueType.Error => error_gray, - _ => Color4.White - }; + switch (Type) + { + case IssueType.Problem: + return problem_red; + + case IssueType.Warning: + return warning_yellow; + + case IssueType.Negligible: + return negligible_green; + + case IssueType.Error: + return error_gray; + + default: + return Color4.White; + } + } } } } diff --git a/osu.Game/Screens/Edit/Verify/IssueTable.cs b/osu.Game/Screens/Edit/Verify/IssueTable.cs index 5f04c7c4e8..1f275ca537 100644 --- a/osu.Game/Screens/Edit/Verify/IssueTable.cs +++ b/osu.Game/Screens/Edit/Verify/IssueTable.cs @@ -97,7 +97,7 @@ namespace osu.Game.Screens.Edit.Verify Text = issue.Template.Type.ToString(), Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold), Margin = new MarginPadding { Right = 10 }, - Colour = issue.Template.TypeColour() + Colour = issue.Template.Colour }, new OsuSpriteText { From f78239c7f238bd64a6272de1c3b5648511f8c581 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Apr 2021 15:32:52 +0900 Subject: [PATCH 035/150] Move enums out of nesting --- .../Edit/Checks/CheckOffscreenObjects.cs | 6 +-- .../Rulesets/Edit/Checks/CheckBackground.cs | 6 +-- .../Edit/Checks/Components/CheckCategory.cs | 41 +++++++++++++++++++ .../Edit/Checks/Components/CheckMetadata.cs | 36 ---------------- .../Rulesets/Edit/Checks/Components/Issue.cs | 2 +- .../Edit/Checks/Components/IssueTemplate.cs | 20 --------- .../Edit/Checks/Components/IssueType.cs | 25 +++++++++++ 7 files changed, 73 insertions(+), 63 deletions(-) create mode 100644 osu.Game/Rulesets/Edit/Checks/Components/CheckCategory.cs create mode 100644 osu.Game/Rulesets/Edit/Checks/Components/IssueType.cs diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs index 3d411d6e12..910de76c5f 100644 --- a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks public override CheckMetadata Metadata() => new CheckMetadata ( - category: CheckMetadata.CheckCategory.Compose, + category: CheckCategory.Compose, description: "Offscreen hitobjects." ); @@ -36,13 +36,13 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks private readonly IssueTemplate templateOffscreen = new IssueTemplate ( - type: IssueTemplate.IssueType.Problem, + type: IssueType.Problem, unformattedMessage: "This object goes offscreen on a 4:3 aspect ratio." ); private readonly IssueTemplate templateOffscreenSliderPath = new IssueTemplate ( - type: IssueTemplate.IssueType.Problem, + type: IssueType.Problem, unformattedMessage: "This slider goes offscreen here on a 4:3 aspect ratio." ); diff --git a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs index aa10fad77b..2de9d9daae 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Edit.Checks { public override CheckMetadata Metadata() => new CheckMetadata ( - category: CheckMetadata.CheckCategory.Resources, + category: CheckCategory.Resources, description: "Missing background." ); @@ -24,13 +24,13 @@ namespace osu.Game.Rulesets.Edit.Checks private readonly IssueTemplate templateNoneSet = new IssueTemplate ( - type: IssueTemplate.IssueType.Problem, + type: IssueType.Problem, unformattedMessage: "No background has been set." ); private readonly IssueTemplate templateDoesNotExist = new IssueTemplate ( - type: IssueTemplate.IssueType.Problem, + type: IssueType.Problem, unformattedMessage: "The background file \"{0}\" is does not exist." ); diff --git a/osu.Game/Rulesets/Edit/Checks/Components/CheckCategory.cs b/osu.Game/Rulesets/Edit/Checks/Components/CheckCategory.cs new file mode 100644 index 0000000000..c37a580dd8 --- /dev/null +++ b/osu.Game/Rulesets/Edit/Checks/Components/CheckCategory.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. + +namespace osu.Game.Rulesets.Edit.Checks.Components +{ + /// + /// 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 + } +} diff --git a/osu.Game/Rulesets/Edit/Checks/Components/CheckMetadata.cs b/osu.Game/Rulesets/Edit/Checks/Components/CheckMetadata.cs index 38a9a16325..cebb2f5455 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/CheckMetadata.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/CheckMetadata.cs @@ -5,42 +5,6 @@ namespace osu.Game.Rulesets.Edit.Checks.Components { 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 . /// diff --git a/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs b/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs index 6f6ba2a323..f392fa8ef4 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Edit.Checks.Components public IReadOnlyList HitObjects; /// - /// The template which this issue is using. This provides properties such as the , and the . + /// The template which this issue is using. This provides properties such as the , and the . /// public IssueTemplate Template; diff --git a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs index 6fe8b8de87..976341e41c 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs @@ -14,26 +14,6 @@ namespace osu.Game.Rulesets.Edit.Checks.Components private static readonly Color4 negligible_green = new Colour4(0.33f, 0.8f, 0.5f, 1.0f); private static readonly Color4 error_gray = new Colour4(0.5f, 0.5f, 0.5f, 1.0f); - /// - /// 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. /// diff --git a/osu.Game/Rulesets/Edit/Checks/Components/IssueType.cs b/osu.Game/Rulesets/Edit/Checks/Components/IssueType.cs new file mode 100644 index 0000000000..be43060cfc --- /dev/null +++ b/osu.Game/Rulesets/Edit/Checks/Components/IssueType.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. + +namespace osu.Game.Rulesets.Edit.Checks.Components +{ + /// + /// 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, + } +} From 8c31e96cdfd657c537dce50a722438fdad7b63dc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Apr 2021 15:37:41 +0900 Subject: [PATCH 036/150] Change some methods to get properties --- osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs | 2 +- osu.Game/Rulesets/Edit/Checks/CheckBackground.cs | 2 +- osu.Game/Rulesets/Edit/Checks/Components/Check.cs | 4 ++-- osu.Game/Rulesets/Edit/Checks/Components/Issue.cs | 5 +---- osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs | 2 +- 5 files changed, 6 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs index 910de76c5f..b2a00da12a 100644 --- a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks description: "Offscreen hitobjects." ); - public override IEnumerable Templates() => new[] + public override IEnumerable PossibleTemplates => new[] { templateOffscreen, templateOffscreenSliderPath diff --git a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs index 2de9d9daae..89b2e8293c 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Edit.Checks description: "Missing background." ); - public override IEnumerable Templates() => new[] + public override IEnumerable PossibleTemplates => new[] { templateNoneSet, templateDoesNotExist diff --git a/osu.Game/Rulesets/Edit/Checks/Components/Check.cs b/osu.Game/Rulesets/Edit/Checks/Components/Check.cs index 5a922fde56..550d2ea0a2 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/Check.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/Check.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Edit.Checks.Components /// /// All possible templates for issues that this check may return. /// - public abstract IEnumerable Templates(); + public abstract IEnumerable PossibleTemplates { get; } /// /// Runs this check and returns any issues detected for the provided beatmap. @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Edit.Checks.Components protected Check() { - foreach (var template in Templates()) + foreach (var template in PossibleTemplates) template.Origin = this; } } diff --git a/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs b/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs index f392fa8ef4..0d5c5411fd 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs @@ -69,10 +69,7 @@ namespace osu.Game.Rulesets.Edit.Checks.Components HitObjects = hitObjectList; } - public override string ToString() - { - return Template.Message(Arguments); - } + public override string ToString() => Template.GetMessage(Arguments); public string GetEditorTimestamp() { diff --git a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs index 976341e41c..a1156c7c72 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Edit.Checks.Components /// 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 string GetMessage(params object[] args) => UnformattedMessage.FormatWith(args); /// /// Returns the colour corresponding to the type of this issue. From 78bbc8f5c824ffd35a8dab92ca0082c4e7244d7c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Apr 2021 15:47:32 +0900 Subject: [PATCH 037/150] Tidy some remaining code --- .../Edit/Checks/Components/CheckCategory.cs | 42 ++++++++++++++----- .../Rulesets/Edit/Checks/Components/Issue.cs | 6 +-- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/osu.Game/Rulesets/Edit/Checks/Components/CheckCategory.cs b/osu.Game/Rulesets/Edit/Checks/Components/CheckCategory.cs index c37a580dd8..ae943cfda9 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/CheckCategory.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/CheckCategory.cs @@ -8,34 +8,54 @@ namespace osu.Game.Rulesets.Edit.Checks.Components /// public enum CheckCategory { - /// Anything to do with control points. + /// + /// Anything to do with control points. + /// Timing, - /// Anything to do with artist, title, creator, etc. + /// + /// Anything to do with artist, title, creator, etc. + /// Metadata, - /// Anything to do with non-audio files, e.g. background, skin, sprites, and video. + /// + /// 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. + /// + /// 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. + /// + /// 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. + /// + /// Anything to do with hitobjects unrelated to spread. + /// Compose, - /// Anything to do with difficulty levels or their progression. + /// + /// Anything to do with difficulty levels or their progression. + /// Spread, - /// Anything to do with variables like CS, OD, AR, HP, and global SV. + /// + /// 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 hitobject feedback. + /// + HitObjects, - /// Anything to do with storyboarding, breaks, video offset, etc. + /// + /// Anything to do with storyboarding, breaks, video offset, etc. + /// Events } } diff --git a/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs b/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs index 0d5c5411fd..7241fabf5b 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs @@ -40,11 +40,7 @@ namespace osu.Game.Rulesets.Edit.Checks.Components Arguments = args; if (template.Origin == null) - { - throw new ArgumentException( - "A template had no origin. Make sure the `Templates()` method contains all templates used." - ); - } + 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) From 8bf85d737c75a71ba9e388842770cfc987f2bed7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Apr 2021 15:52:29 +0900 Subject: [PATCH 038/150] Change Metadata into a get property --- osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs | 2 +- osu.Game/Rulesets/Edit/Checks/CheckBackground.cs | 2 +- osu.Game/Rulesets/Edit/Checks/Components/Check.cs | 2 +- osu.Game/Screens/Edit/Verify/IssueTable.cs | 2 +- osu.Game/Screens/Edit/Verify/VerifyScreen.cs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs index b2a00da12a..252f65ab5a 100644 --- a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks // (higher = more performant, but higher false-negative chance). private const int path_step_size = 5; - public override CheckMetadata Metadata() => new CheckMetadata + public override CheckMetadata Metadata { get; } = new CheckMetadata ( category: CheckCategory.Compose, description: "Offscreen hitobjects." diff --git a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs index 89b2e8293c..22f98b6fd7 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs @@ -10,7 +10,7 @@ namespace osu.Game.Rulesets.Edit.Checks { public class CheckBackground : Check { - public override CheckMetadata Metadata() => new CheckMetadata + public override CheckMetadata Metadata { get; } = new CheckMetadata ( category: CheckCategory.Resources, description: "Missing background." diff --git a/osu.Game/Rulesets/Edit/Checks/Components/Check.cs b/osu.Game/Rulesets/Edit/Checks/Components/Check.cs index 550d2ea0a2..7c039d1572 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/Check.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/Check.cs @@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Edit.Checks.Components /// /// The metadata for this check. /// - public abstract CheckMetadata Metadata(); + public abstract CheckMetadata Metadata { get; } /// /// All possible templates for issues that this check may return. diff --git a/osu.Game/Screens/Edit/Verify/IssueTable.cs b/osu.Game/Screens/Edit/Verify/IssueTable.cs index 1f275ca537..84dbabf300 100644 --- a/osu.Game/Screens/Edit/Verify/IssueTable.cs +++ b/osu.Game/Screens/Edit/Verify/IssueTable.cs @@ -112,7 +112,7 @@ namespace osu.Game.Screens.Edit.Verify }, new OsuSpriteText { - Text = issue.Template.Origin.Metadata().Category.ToString(), + Text = issue.Template.Origin.Metadata.Category.ToString(), Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold), Margin = new MarginPadding(10) } diff --git a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs index 7a520826f5..0c4aa04ff3 100644 --- a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs +++ b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs @@ -130,7 +130,7 @@ namespace osu.Game.Screens.Edit.Verify { table.Issues = beatmapVerifier.Run(Beatmap) .OrderByDescending(issue => issue.Template.Type) - .ThenByDescending(issue => issue.Template.Origin.Metadata().Category); + .ThenByDescending(issue => issue.Template.Origin.Metadata.Category); } } } From 42604afcdcf9ed0ae922b07434b4587ad4b4c85d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Apr 2021 16:15:27 +0900 Subject: [PATCH 039/150] Add binding for verify mode (and move enum entry to end) --- osu.Game/Input/Bindings/GlobalActionContainer.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 3ea7f5f5fa..36e465720a 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -70,6 +70,7 @@ namespace osu.Game.Input.Bindings new KeyBinding(new[] { InputKey.F2 }, GlobalAction.EditorDesignMode), new KeyBinding(new[] { InputKey.F3 }, GlobalAction.EditorTimingMode), new KeyBinding(new[] { InputKey.F4 }, GlobalAction.EditorSetupMode), + new KeyBinding(new[] { InputKey.Control, InputKey.Shift, InputKey.A }, GlobalAction.EditorVerifyMode), }; public IEnumerable InGameKeyBindings => new[] @@ -225,9 +226,6 @@ namespace osu.Game.Input.Bindings [Description("Timing mode")] EditorTimingMode, - [Description("Verify mode")] - EditorVerifyMode, - [Description("Hold for HUD")] HoldForHUD, @@ -252,5 +250,8 @@ namespace osu.Game.Input.Bindings [Description("Beatmap Options")] ToggleBeatmapOptions, + + [Description("Verify mode")] + EditorVerifyMode, } } From e19e8ff2a3252b9adde46dae589ce8d2a134f4dc Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 12 Apr 2021 15:52:43 +0900 Subject: [PATCH 040/150] Rewrite FramedReplayInputHandler for robustness This commit changes the semantics of `CurrentFrame` and `NextFrame` of the class. The ordering of `NextFrame.Time` and `CurrentFrame.Time` was dependent on the current direction. Now, it should always satisfy `CurrentFrame.Time <= CurrentTime <= NextFrame.Time` except at the start/end. This change, however, doesn't break existing deriving classes if the template code pattern usage of interpolation is used. The deriving class code can be simplified due to the elimination of nullable types. I didn't include those changes in this commit. I removed `StreamingFramedReplayInputHandlerTest` for now, as it is almost-duplicate of `FramedReplayInputHandlerTest`. I'll include more tests in later commits. This commit fixes #6150. --- .../NonVisual/FramedReplayInputHandlerTest.cs | 81 ++--- .../StreamingFramedReplayInputHandlerTest.cs | 296 ------------------ .../Visual/Gameplay/TestSceneSpectator.cs | 4 +- .../Replays/FramedReplayInputHandler.cs | 167 +++++----- 4 files changed, 109 insertions(+), 439 deletions(-) delete mode 100644 osu.Game.Tests/NonVisual/StreamingFramedReplayInputHandlerTest.cs diff --git a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs index 92a60663de..b4fc081a2a 100644 --- a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs +++ b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs @@ -37,11 +37,6 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestNormalPlayback() { - Assert.IsNull(handler.CurrentFrame); - - confirmCurrentFrame(null); - confirmNextFrame(0); - setTime(0, 0); confirmCurrentFrame(0); confirmNextFrame(1); @@ -97,22 +92,22 @@ namespace osu.Game.Tests.NonVisual // exited important section setTime(8200, 8000); confirmCurrentFrame(7); - confirmNextFrame(null); + confirmNextFrame(7); setTime(8200, 8200); confirmCurrentFrame(7); - confirmNextFrame(null); + confirmNextFrame(7); } [Test] public void TestIntroTime() { setTime(-1000, -1000); - confirmCurrentFrame(null); + confirmCurrentFrame(0); confirmNextFrame(0); setTime(-500, -500); - confirmCurrentFrame(null); + confirmCurrentFrame(0); confirmNextFrame(0); setTime(0, 0); @@ -133,29 +128,29 @@ namespace osu.Game.Tests.NonVisual // pivot without crossing a frame boundary setTime(2700, 2700); confirmCurrentFrame(2); - confirmNextFrame(1); + confirmNextFrame(3); - // cross current frame boundary; should not yet update frame - setTime(1980, 1980); + // cross current frame boundary + setTime(1980, 2000); confirmCurrentFrame(2); - confirmNextFrame(1); + confirmNextFrame(3); setTime(1200, 1200); - confirmCurrentFrame(2); - confirmNextFrame(1); + confirmCurrentFrame(1); + confirmNextFrame(2); // ensure each frame plays out until start setTime(-500, 1000); confirmCurrentFrame(1); - confirmNextFrame(0); + confirmNextFrame(2); setTime(-500, 0); confirmCurrentFrame(0); - confirmNextFrame(null); + confirmNextFrame(1); setTime(-500, -500); confirmCurrentFrame(0); - confirmNextFrame(null); + confirmNextFrame(0); } [Test] @@ -168,12 +163,12 @@ namespace osu.Game.Tests.NonVisual confirmNextFrame(5); setTime(3500, null); - confirmCurrentFrame(4); - confirmNextFrame(3); + confirmCurrentFrame(3); + confirmNextFrame(4); setTime(3000, 3000); confirmCurrentFrame(3); - confirmNextFrame(2); + confirmNextFrame(4); setTime(3500, null); confirmCurrentFrame(3); @@ -187,17 +182,17 @@ namespace osu.Game.Tests.NonVisual confirmCurrentFrame(4); confirmNextFrame(5); - setTime(4000, null); + setTime(4000, 4000); confirmCurrentFrame(4); confirmNextFrame(5); setTime(3500, null); - confirmCurrentFrame(4); - confirmNextFrame(3); + confirmCurrentFrame(3); + confirmNextFrame(4); setTime(3000, 3000); confirmCurrentFrame(3); - confirmNextFrame(2); + confirmNextFrame(4); } [Test] @@ -209,24 +204,24 @@ namespace osu.Game.Tests.NonVisual confirmNextFrame(4); setTime(3200, null); - // next frame doesn't change even though direction reversed, because of important section. confirmCurrentFrame(3); confirmNextFrame(4); - setTime(3000, null); + setTime(3000, 3000); confirmCurrentFrame(3); confirmNextFrame(4); setTime(2800, 2800); - confirmCurrentFrame(3); - confirmNextFrame(2); + confirmCurrentFrame(2); + confirmNextFrame(3); } private void fastForwardToPoint(double destination) { for (int i = 0; i < 1000; i++) { - if (handler.SetFrameFromTime(destination) == null) + var time = handler.SetFrameFromTime(destination); + if (time == null || time == destination) return; } @@ -235,33 +230,17 @@ namespace osu.Game.Tests.NonVisual private void setTime(double set, double? expect) { - Assert.AreEqual(expect, handler.SetFrameFromTime(set)); + Assert.AreEqual(expect, handler.SetFrameFromTime(set), "Unexpected return value"); } - private void confirmCurrentFrame(int? frame) + private void confirmCurrentFrame(int frame) { - if (frame.HasValue) - { - Assert.IsNotNull(handler.CurrentFrame); - Assert.AreEqual(replay.Frames[frame.Value].Time, handler.CurrentFrame.Time); - } - else - { - Assert.IsNull(handler.CurrentFrame); - } + Assert.AreEqual(replay.Frames[frame].Time, handler.CurrentFrame.Time, "Unexpected current frame"); } - private void confirmNextFrame(int? frame) + private void confirmNextFrame(int frame) { - if (frame.HasValue) - { - Assert.IsNotNull(handler.NextFrame); - Assert.AreEqual(replay.Frames[frame.Value].Time, handler.NextFrame.Time); - } - else - { - Assert.IsNull(handler.NextFrame); - } + Assert.AreEqual(replay.Frames[frame].Time, handler.NextFrame.Time, "Unexpected next frame"); } private class TestReplayFrame : ReplayFrame diff --git a/osu.Game.Tests/NonVisual/StreamingFramedReplayInputHandlerTest.cs b/osu.Game.Tests/NonVisual/StreamingFramedReplayInputHandlerTest.cs deleted file mode 100644 index 21ec29b10b..0000000000 --- a/osu.Game.Tests/NonVisual/StreamingFramedReplayInputHandlerTest.cs +++ /dev/null @@ -1,296 +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; -using System.Collections.Generic; -using NUnit.Framework; -using osu.Game.Replays; -using osu.Game.Rulesets.Replays; - -namespace osu.Game.Tests.NonVisual -{ - [TestFixture] - public class StreamingFramedReplayInputHandlerTest - { - private Replay replay; - private TestInputHandler handler; - - [SetUp] - public void SetUp() - { - handler = new TestInputHandler(replay = new Replay - { - HasReceivedAllFrames = false, - Frames = new List - { - new TestReplayFrame(0), - new TestReplayFrame(1000), - new TestReplayFrame(2000), - new TestReplayFrame(3000, true), - new TestReplayFrame(4000, true), - new TestReplayFrame(5000, true), - new TestReplayFrame(7000, true), - new TestReplayFrame(8000), - } - }); - } - - [Test] - public void TestNormalPlayback() - { - Assert.IsNull(handler.CurrentFrame); - - confirmCurrentFrame(null); - confirmNextFrame(0); - - setTime(0, 0); - confirmCurrentFrame(0); - confirmNextFrame(1); - - // if we hit the first frame perfectly, time should progress to it. - setTime(1000, 1000); - confirmCurrentFrame(1); - confirmNextFrame(2); - - // in between non-important frames should progress based on input. - setTime(1200, 1200); - confirmCurrentFrame(1); - - setTime(1400, 1400); - confirmCurrentFrame(1); - - // progressing beyond the next frame should force time to that frame once. - setTime(2200, 2000); - confirmCurrentFrame(2); - - // second attempt should progress to input time - setTime(2200, 2200); - confirmCurrentFrame(2); - - // entering important section - setTime(3000, 3000); - confirmCurrentFrame(3); - - // cannot progress within - setTime(3500, null); - confirmCurrentFrame(3); - - setTime(4000, 4000); - confirmCurrentFrame(4); - - // still cannot progress - setTime(4500, null); - confirmCurrentFrame(4); - - setTime(5200, 5000); - confirmCurrentFrame(5); - - // important section AllowedImportantTimeSpan allowance - setTime(5200, 5200); - confirmCurrentFrame(5); - - setTime(7200, 7000); - confirmCurrentFrame(6); - - setTime(7200, null); - confirmCurrentFrame(6); - - // exited important section - setTime(8200, 8000); - confirmCurrentFrame(7); - confirmNextFrame(null); - - setTime(8200, null); - confirmCurrentFrame(7); - confirmNextFrame(null); - - setTime(8400, null); - confirmCurrentFrame(7); - confirmNextFrame(null); - } - - [Test] - public void TestIntroTime() - { - setTime(-1000, -1000); - confirmCurrentFrame(null); - confirmNextFrame(0); - - setTime(-500, -500); - confirmCurrentFrame(null); - confirmNextFrame(0); - - setTime(0, 0); - confirmCurrentFrame(0); - confirmNextFrame(1); - } - - [Test] - public void TestBasicRewind() - { - setTime(2800, 0); - setTime(2800, 1000); - setTime(2800, 2000); - setTime(2800, 2800); - confirmCurrentFrame(2); - confirmNextFrame(3); - - // pivot without crossing a frame boundary - setTime(2700, 2700); - confirmCurrentFrame(2); - confirmNextFrame(1); - - // cross current frame boundary; should not yet update frame - setTime(1980, 1980); - confirmCurrentFrame(2); - confirmNextFrame(1); - - setTime(1200, 1200); - confirmCurrentFrame(2); - confirmNextFrame(1); - - // ensure each frame plays out until start - setTime(-500, 1000); - confirmCurrentFrame(1); - confirmNextFrame(0); - - setTime(-500, 0); - confirmCurrentFrame(0); - confirmNextFrame(null); - - setTime(-500, -500); - confirmCurrentFrame(0); - confirmNextFrame(null); - } - - [Test] - public void TestRewindInsideImportantSection() - { - fastForwardToPoint(3000); - - setTime(4000, 4000); - confirmCurrentFrame(4); - confirmNextFrame(5); - - setTime(3500, null); - confirmCurrentFrame(4); - confirmNextFrame(3); - - setTime(3000, 3000); - confirmCurrentFrame(3); - confirmNextFrame(2); - - setTime(3500, null); - confirmCurrentFrame(3); - confirmNextFrame(4); - - setTime(4000, 4000); - confirmCurrentFrame(4); - confirmNextFrame(5); - - setTime(4500, null); - confirmCurrentFrame(4); - confirmNextFrame(5); - - setTime(4000, null); - confirmCurrentFrame(4); - confirmNextFrame(5); - - setTime(3500, null); - confirmCurrentFrame(4); - confirmNextFrame(3); - - setTime(3000, 3000); - confirmCurrentFrame(3); - confirmNextFrame(2); - } - - [Test] - public void TestRewindOutOfImportantSection() - { - fastForwardToPoint(3500); - - confirmCurrentFrame(3); - confirmNextFrame(4); - - setTime(3200, null); - // next frame doesn't change even though direction reversed, because of important section. - confirmCurrentFrame(3); - confirmNextFrame(4); - - setTime(3000, null); - confirmCurrentFrame(3); - confirmNextFrame(4); - - setTime(2800, 2800); - confirmCurrentFrame(3); - confirmNextFrame(2); - } - - private void fastForwardToPoint(double destination) - { - for (int i = 0; i < 1000; i++) - { - if (handler.SetFrameFromTime(destination) == null) - return; - } - - throw new TimeoutException("Seek was never fulfilled"); - } - - private void setTime(double set, double? expect) - { - Assert.AreEqual(expect, handler.SetFrameFromTime(set)); - } - - private void confirmCurrentFrame(int? frame) - { - if (frame.HasValue) - { - Assert.IsNotNull(handler.CurrentFrame); - Assert.AreEqual(replay.Frames[frame.Value].Time, handler.CurrentFrame.Time); - } - else - { - Assert.IsNull(handler.CurrentFrame); - } - } - - private void confirmNextFrame(int? frame) - { - if (frame.HasValue) - { - Assert.IsNotNull(handler.NextFrame); - Assert.AreEqual(replay.Frames[frame.Value].Time, handler.NextFrame.Time); - } - else - { - Assert.IsNull(handler.NextFrame); - } - } - - private class TestReplayFrame : ReplayFrame - { - public readonly bool IsImportant; - - public TestReplayFrame(double time, bool isImportant = false) - : base(time) - { - IsImportant = isImportant; - } - } - - private class TestInputHandler : FramedReplayInputHandler - { - public TestInputHandler(Replay replay) - : base(replay) - { - FrameAccuratePlayback = true; - } - - protected override double AllowedImportantTimeSpan => 1000; - - protected override bool IsImportant(TestReplayFrame frame) => frame.IsImportant; - } - } -} diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs index 9d85a9995d..9f1faa8e26 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs @@ -83,7 +83,7 @@ namespace osu.Game.Tests.Visual.Gameplay waitForPlayer(); AddAssert("ensure frames arrived", () => replayHandler.HasFrames); - AddUntilStep("wait for frame starvation", () => replayHandler.NextFrame == null); + AddUntilStep("wait for frame starvation", () => replayHandler.WaitingNextFrame); checkPaused(true); double? pausedTime = null; @@ -92,7 +92,7 @@ namespace osu.Game.Tests.Visual.Gameplay sendFrames(); - AddUntilStep("wait for frame starvation", () => replayHandler.NextFrame == null); + AddUntilStep("wait for frame starvation", () => replayHandler.WaitingNextFrame); checkPaused(true); AddAssert("time advanced", () => currentFrameStableTime > pausedTime); diff --git a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs index 5eaccc766e..f527c0e105 100644 --- a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs +++ b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs @@ -1,9 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + using System; using System.Collections.Generic; -using System.Diagnostics; using JetBrains.Annotations; using osu.Game.Input.Handlers; using osu.Game.Replays; @@ -17,70 +18,80 @@ namespace osu.Game.Rulesets.Replays public abstract class FramedReplayInputHandler : ReplayInputHandler where TFrame : ReplayFrame { - public override bool IsActive => HasFrames; + /// + /// Whether we have at least one replay frame. + /// + public bool HasFrames => Frames.Count != 0; - private readonly Replay replay; - - protected List Frames => replay.Frames; + /// + /// Whether we are waiting for new frames to be received. + /// + public bool WaitingNextFrame => !replay.HasReceivedAllFrames && currentFrameIndex == Frames.Count - 1; + /// + /// The current frame of the replay. + /// The current time is always between the start and the end time of the current frame. + /// + /// The replay is empty. public TFrame CurrentFrame { get { if (!HasFrames) - throw new InvalidOperationException($"Cannot get {nameof(CurrentFrame)} of the empty replay"); + throw new InvalidOperationException($"Attempted to get {nameof(CurrentFrame)} of an empty replay"); - if (!currentFrameIndex.HasValue) - return null; - - return (TFrame)Frames[currentFrameIndex.Value]; + return (TFrame)Frames[Math.Max(0, currentFrameIndex)]; } } + /// + /// The next frame of the replay. + /// The start time is always greater or equals to the start time of regardless of the seeking direction. + /// If it is before the first frame of the replay or the after the last frame of the replay, and agree. + /// + /// The replay is empty. public TFrame NextFrame { get { if (!HasFrames) - throw new InvalidOperationException($"Cannot get {nameof(NextFrame)} of the empty replay"); + throw new InvalidOperationException($"Attempted to get {nameof(NextFrame)} of an empty replay"); - if (!currentFrameIndex.HasValue) - return currentDirection > 0 ? (TFrame)Frames[0] : null; - - int nextFrame = clampedNextFrameIndex; - - if (nextFrame == currentFrameIndex.Value) - return null; - - return (TFrame)Frames[clampedNextFrameIndex]; + return (TFrame)Frames[Math.Min(currentFrameIndex + 1, Frames.Count - 1)]; } } - private int? currentFrameIndex; - - private int clampedNextFrameIndex => - currentFrameIndex.HasValue ? Math.Clamp(currentFrameIndex.Value + currentDirection, 0, Frames.Count - 1) : 0; - - protected FramedReplayInputHandler(Replay replay) - { - this.replay = replay; - } - - private const double sixty_frame_time = 1000.0 / 60; - - protected virtual double AllowedImportantTimeSpan => sixty_frame_time * 1.2; - - protected double? CurrentTime { get; private set; } - - private int currentDirection = 1; - /// /// When set, we will ensure frames executed by nested drawables are frame-accurate to replay data. /// Disabling this can make replay playback smoother (useful for autoplay, currently). /// public bool FrameAccuratePlayback; - public bool HasFrames => Frames.Count > 0; + // This input handler should be enabled only if there is at least one replay frame. + public override bool IsActive => HasFrames; + + // Can make it non-null but that is a breaking change. + protected double? CurrentTime { get; private set; } + + protected virtual double AllowedImportantTimeSpan => sixty_frame_time * 1.2; + + protected List Frames => replay.Frames; + + private readonly Replay replay; + + private int currentFrameIndex; + + private const double sixty_frame_time = 1000.0 / 60; + + protected FramedReplayInputHandler(Replay replay) + { + // This replay frame ordering should be enforced on the Replay type + replay.Frames.Sort((x, y) => x.Time.CompareTo(y.Time)); + + this.replay = replay; + currentFrameIndex = -1; + CurrentTime = double.NegativeInfinity; + } private bool inImportantSection { @@ -89,13 +100,8 @@ namespace osu.Game.Rulesets.Replays if (!HasFrames || !FrameAccuratePlayback) return false; - var frame = currentDirection > 0 ? CurrentFrame : NextFrame; - - if (frame == null) - return false; - - return IsImportant(frame) && // a button is in a pressed state - Math.Abs(CurrentTime - NextFrame?.Time ?? 0) <= AllowedImportantTimeSpan; // the next frame is within an allowable time span + return IsImportant(CurrentFrame) && // a button is in a pressed state + Math.Abs(CurrentTime - NextFrame.Time ?? 0) <= AllowedImportantTimeSpan; // the next frame is within an allowable time span } } @@ -110,71 +116,52 @@ namespace osu.Game.Rulesets.Replays /// The usable time value. If null, we should not advance time as we do not have enough data. public override double? SetFrameFromTime(double time) { - updateDirection(time); - - Debug.Assert(currentDirection != 0); - if (!HasFrames) { - // in the case all frames are received, allow time to progress regardless. + // In the case all frames are received, allow time to progress regardless. if (replay.HasReceivedAllFrames) return CurrentTime = time; return null; } - TFrame next = NextFrame; + double frameStart = getFrameTime(currentFrameIndex); + double frameEnd = getFrameTime(currentFrameIndex + 1); - // if we have a next frame, check if it is before or at the current time in playback, and advance time to it if so. - if (next != null) + // If the proposed time is after the current frame end time, we progress forwards. + // If the proposed time is before the current frame start time, and we are at the frame boundary, we progress backwards. + if (frameEnd <= time) { - int compare = time.CompareTo(next.Time); - - if (compare == 0 || compare == currentDirection) - { - currentFrameIndex = clampedNextFrameIndex; - return CurrentTime = CurrentFrame.Time; - } + time = frameEnd; + currentFrameIndex++; } + else if (time < frameStart && CurrentTime == frameStart) + currentFrameIndex--; - // at this point, the frame index can't be advanced. - // even so, we may be able to propose the clock progresses forward due to being at an extent of the replay, - // or moving towards the next valid frame (ie. interpolating in a non-important section). + frameStart = getFrameTime(currentFrameIndex); + frameEnd = getFrameTime(currentFrameIndex + 1); - // the exception is if currently in an important section, which is respected above all. - if (inImportantSection) + // Pause until more frames are arrived. + if (WaitingNextFrame && frameStart < time) { - Debug.Assert(next != null || !replay.HasReceivedAllFrames); + CurrentTime = frameStart; return null; } - // if a next frame does exist, allow interpolation. - if (next != null) - return CurrentTime = time; + CurrentTime = Math.Clamp(time, frameStart, frameEnd); - // if all frames have been received, allow playing beyond extents. - if (replay.HasReceivedAllFrames) - return CurrentTime = time; - - // if not all frames are received but we are before the first frame, allow playing. - if (time < Frames[0].Time) - return CurrentTime = time; - - // in the case we have no next frames and haven't received enough frame data, block. - return null; + // In an important section, a mid-frame time cannot be used and a null is returned instead. + return inImportantSection && frameStart < time && time < frameEnd ? null : CurrentTime; } - private void updateDirection(double time) + private double getFrameTime(int index) { - if (!CurrentTime.HasValue) - { - currentDirection = 1; - } - else - { - currentDirection = time.CompareTo(CurrentTime); - if (currentDirection == 0) currentDirection = 1; - } + if (index < 0) + return double.NegativeInfinity; + if (index >= Frames.Count) + return double.PositiveInfinity; + + return Frames[index].Time; } } } From 3c28c09ab5073c20a8eaebb8ce043a6cdc16b77f Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 12 Apr 2021 16:18:35 +0900 Subject: [PATCH 041/150] Add more FramedReplayInputHandler tests --- .../NonVisual/FramedReplayInputHandlerTest.cs | 100 ++++++++++++++++-- 1 file changed, 89 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs index b4fc081a2a..64af4b6db6 100644 --- a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs +++ b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs @@ -20,23 +20,15 @@ namespace osu.Game.Tests.NonVisual { handler = new TestInputHandler(replay = new Replay { - Frames = new List - { - new TestReplayFrame(0), - new TestReplayFrame(1000), - new TestReplayFrame(2000), - new TestReplayFrame(3000, true), - new TestReplayFrame(4000, true), - new TestReplayFrame(5000, true), - new TestReplayFrame(7000, true), - new TestReplayFrame(8000), - } + HasReceivedAllFrames = false }); } [Test] public void TestNormalPlayback() { + setReplayFrames(); + setTime(0, 0); confirmCurrentFrame(0); confirmNextFrame(1); @@ -102,6 +94,8 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestIntroTime() { + setReplayFrames(); + setTime(-1000, -1000); confirmCurrentFrame(0); confirmNextFrame(0); @@ -118,6 +112,8 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestBasicRewind() { + setReplayFrames(); + setTime(2800, 0); setTime(2800, 1000); setTime(2800, 2000); @@ -156,6 +152,7 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestRewindInsideImportantSection() { + setReplayFrames(); fastForwardToPoint(3000); setTime(4000, 4000); @@ -198,6 +195,7 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestRewindOutOfImportantSection() { + setReplayFrames(); fastForwardToPoint(3500); confirmCurrentFrame(3); @@ -216,6 +214,86 @@ namespace osu.Game.Tests.NonVisual confirmNextFrame(3); } + [Test] + public void TestReplayStreaming() + { + // no frames are arrived yet + setTime(0, null); + setTime(1000, null); + Assert.IsTrue(handler.WaitingNextFrame, "Should be waiting for the first frame"); + + replay.Frames.Add(new TestReplayFrame(0)); + replay.Frames.Add(new TestReplayFrame(1000)); + + // should always play from beginning + setTime(1000, 0); + confirmCurrentFrame(0); + Assert.IsFalse(handler.WaitingNextFrame, "Should not be waiting yet"); + setTime(1000, 1000); + confirmCurrentFrame(1); + confirmNextFrame(1); + Assert.IsTrue(handler.WaitingNextFrame, "Should be waiting"); + + // cannot seek beyond the last frame + setTime(1500, null); + confirmCurrentFrame(1); + + setTime(-100, 0); + confirmCurrentFrame(0); + + // can seek to the point before the first frame, however + setTime(-100, -100); + confirmCurrentFrame(0); + confirmNextFrame(0); + + fastForwardToPoint(1000); + setTime(3000, null); + replay.Frames.Add(new TestReplayFrame(2000)); + confirmCurrentFrame(1); + setTime(1000, 1000); + setTime(3000, 2000); + } + + [Test] + public void TestMultipleFramesSameTime() + { + replay.Frames.Add(new TestReplayFrame(0)); + replay.Frames.Add(new TestReplayFrame(0)); + replay.Frames.Add(new TestReplayFrame(1000)); + replay.Frames.Add(new TestReplayFrame(1000)); + replay.Frames.Add(new TestReplayFrame(2000)); + + // forward direction is prioritized when multiple frames have the same time. + setTime(0, 0); + setTime(0, 0); + + setTime(2000, 1000); + setTime(2000, 1000); + + setTime(1000, 1000); + setTime(1000, 1000); + setTime(-100, 1000); + setTime(-100, 0); + setTime(-100, 0); + setTime(-100, -100); + } + + private void setReplayFrames() + { + replay.Frames = new List + { + new TestReplayFrame(0), + new TestReplayFrame(1000), + new TestReplayFrame(2000), + new TestReplayFrame(3000, true), + new TestReplayFrame(4000, true), + new TestReplayFrame(5000, true), + new TestReplayFrame(7000, true), + new TestReplayFrame(8000), + }; + replay.HasReceivedAllFrames = true; + } + private void fastForwardToPoint(double destination) { for (int i = 0; i < 1000; i++) From 0eab9daf13bb406d2b82c1f624dbb7e483f0ab40 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Apr 2021 16:41:36 +0900 Subject: [PATCH 042/150] Update existing overlay containers to not block scroll input --- osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs | 2 ++ .../Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs | 2 ++ osu.Game/Screens/Play/GameplayMenuOverlay.cs | 2 ++ osu.Game/Screens/Play/SongProgress.cs | 2 ++ 4 files changed, 8 insertions(+) diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index fbf2ffd4bd..e168f265dd 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -23,6 +23,8 @@ namespace osu.Game.Graphics.Containers protected virtual string PopInSampleName => "UI/overlay-pop-in"; protected virtual string PopOutSampleName => "UI/overlay-pop-out"; + protected override bool BlockScrollInput => false; + protected override bool BlockNonPositionalInput => true; /// diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs index ea3951fc3b..5699da740c 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs @@ -19,6 +19,8 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components protected OnlinePlayComposite Settings { get; set; } + protected override bool BlockScrollInput => false; + [BackgroundDependencyLoader] private void load() { diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs index f938839be3..4a28da0dde 100644 --- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -31,6 +31,8 @@ namespace osu.Game.Screens.Play protected override bool BlockNonPositionalInput => true; + protected override bool BlockScrollInput => false; + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; public Action OnRetry; diff --git a/osu.Game/Screens/Play/SongProgress.cs b/osu.Game/Screens/Play/SongProgress.cs index acf4640aa4..6c7cb9376c 100644 --- a/osu.Game/Screens/Play/SongProgress.cs +++ b/osu.Game/Screens/Play/SongProgress.cs @@ -45,6 +45,8 @@ namespace osu.Game.Screens.Play public override bool HandleNonPositionalInput => AllowSeeking.Value; public override bool HandlePositionalInput => AllowSeeking.Value; + protected override bool BlockScrollInput => false; + private double firstHitTime => objects.First().StartTime; private IEnumerable objects; From 65ebdd8f7a48c57f31e46acd21ac3369f8521be9 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Mon, 12 Apr 2021 10:08:08 +0200 Subject: [PATCH 043/150] Move check origin from `IssueTemplate` to `Issue` As a result we can also make check an interface, and need to provide the check itself when constructing an issue. --- .../Edit/Checks/CheckOffscreenObjects.cs | 14 +++++------ .../Edit/OsuBeatmapVerifier.cs | 2 +- osu.Game/Rulesets/Edit/BeatmapVerifier.cs | 2 +- .../Rulesets/Edit/Checks/CheckBackground.cs | 12 +++++----- .../Rulesets/Edit/Checks/Components/Check.cs | 14 ++++------- .../Rulesets/Edit/Checks/Components/Issue.cs | 23 +++++++++++-------- .../Edit/Checks/Components/IssueTemplate.cs | 5 ---- osu.Game/Screens/Edit/Verify/VerifyScreen.cs | 2 +- 8 files changed, 33 insertions(+), 41 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs index 252f65ab5a..b34c9966a4 100644 --- a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs @@ -9,7 +9,7 @@ using osuTK; namespace osu.Game.Rulesets.Osu.Edit.Checks { - public class CheckOffscreenObjects : Check + public class CheckOffscreenObjects : ICheck { // These are close approximates to the edges of the screen // in gameplay on a 4:3 aspect ratio for osu!stable. @@ -22,13 +22,13 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks // (higher = more performant, but higher false-negative chance). private const int path_step_size = 5; - public override CheckMetadata Metadata { get; } = new CheckMetadata + public CheckMetadata Metadata { get; } = new CheckMetadata ( category: CheckCategory.Compose, description: "Offscreen hitobjects." ); - public override IEnumerable PossibleTemplates => new[] + public IEnumerable PossibleTemplates => new[] { templateOffscreen, templateOffscreenSliderPath @@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks unformattedMessage: "This slider goes offscreen here on a 4:3 aspect ratio." ); - public override IEnumerable Run(IBeatmap beatmap) + public IEnumerable Run(IBeatmap beatmap) { foreach (var hitobject in beatmap.HitObjects) { @@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks case HitCircle circle: { if (isOffscreen(circle.StackedPosition, circle.Radius)) - yield return new Issue(circle, templateOffscreen); + yield return new Issue(this, circle, templateOffscreen); break; } @@ -89,7 +89,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks // `SpanDuration` ensures we don't include reverses. double time = slider.StartTime + progress * slider.SpanDuration; - yield return new Issue(slider, templateOffscreenSliderPath) { Time = time }; + yield return new Issue(this, slider, templateOffscreenSliderPath) { Time = time }; yield break; } @@ -98,7 +98,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks if (!isOffscreen(slider.StackedEndPosition, slider.Radius)) yield break; - yield return new Issue(slider, templateOffscreenSliderPath) { Time = slider.EndTime }; + yield return new Issue(this, slider, templateOffscreenSliderPath) { Time = slider.EndTime }; } private bool isOffscreen(Vector2 position, double radius) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs b/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs index 272612dbaf..2faa239720 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Edit { public class OsuBeatmapVerifier : BeatmapVerifier { - private readonly List checks = new List + private readonly List checks = new List { new CheckOffscreenObjects() }; diff --git a/osu.Game/Rulesets/Edit/BeatmapVerifier.cs b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs index f67a068525..1d0508705a 100644 --- a/osu.Game/Rulesets/Edit/BeatmapVerifier.cs +++ b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Edit /// Checks which are performed regardless of ruleset. /// These handle things like beatmap metadata, timing, and other ruleset agnostic elements. /// - private readonly IReadOnlyList generalChecks = new List + private readonly IReadOnlyList generalChecks = new List { new CheckBackground() }; diff --git a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs index 22f98b6fd7..c922aa03c0 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs @@ -8,15 +8,15 @@ using osu.Game.Rulesets.Edit.Checks.Components; namespace osu.Game.Rulesets.Edit.Checks { - public class CheckBackground : Check + public class CheckBackground : ICheck { - public override CheckMetadata Metadata { get; } = new CheckMetadata + public CheckMetadata Metadata { get; } = new CheckMetadata ( category: CheckCategory.Resources, description: "Missing background." ); - public override IEnumerable PossibleTemplates => new[] + public IEnumerable PossibleTemplates => new[] { templateNoneSet, templateDoesNotExist @@ -34,11 +34,11 @@ namespace osu.Game.Rulesets.Edit.Checks unformattedMessage: "The background file \"{0}\" is does not exist." ); - public override IEnumerable Run(IBeatmap beatmap) + public IEnumerable Run(IBeatmap beatmap) { if (beatmap.Metadata.BackgroundFile == null) { - yield return new Issue(templateNoneSet); + yield return new Issue(this, templateNoneSet); yield break; } @@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Edit.Checks if (file != null) yield break; - yield return new Issue(templateDoesNotExist, beatmap.Metadata.BackgroundFile); + yield return new Issue(this, templateDoesNotExist, beatmap.Metadata.BackgroundFile); } } } diff --git a/osu.Game/Rulesets/Edit/Checks/Components/Check.cs b/osu.Game/Rulesets/Edit/Checks/Components/Check.cs index 7c039d1572..f355ae734e 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/Check.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/Check.cs @@ -6,28 +6,22 @@ using osu.Game.Beatmaps; namespace osu.Game.Rulesets.Edit.Checks.Components { - public abstract class Check + public interface ICheck { /// /// The metadata for this check. /// - public abstract CheckMetadata Metadata { get; } + public CheckMetadata Metadata { get; } /// /// All possible templates for issues that this check may return. /// - public abstract IEnumerable PossibleTemplates { get; } + public IEnumerable PossibleTemplates { get; } /// /// Runs this check and returns any issues detected for the provided beatmap. /// /// The beatmap to run the check on. - public abstract IEnumerable Run(IBeatmap beatmap); - - protected Check() - { - foreach (var template in PossibleTemplates) - template.Origin = this; - } + public IEnumerable Run(IBeatmap beatmap); } } diff --git a/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs b/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs index 7241fabf5b..d0f7df857b 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs @@ -26,38 +26,41 @@ namespace osu.Game.Rulesets.Edit.Checks.Components /// public IssueTemplate Template; + /// + /// The check that this issue originates from. + /// + public ICheck Check; + /// /// The arguments that give this issue its context, based on the . These are then substituted into the . /// This could for instance include timestamps, which diff is being compared to, what some volume is, etc. /// public object[] Arguments; - public Issue(IssueTemplate template, params object[] args) + public Issue(ICheck check, IssueTemplate template, params object[] args) { + Check = check; Time = null; HitObjects = 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) + public Issue(ICheck check, double? time, IssueTemplate template, params object[] args) + : this(check, template, args) { Time = time; } - public Issue(HitObject hitObject, IssueTemplate template, params object[] args) - : this(template, args) + public Issue(ICheck check, HitObject hitObject, IssueTemplate template, params object[] args) + : this(check, template, args) { Time = hitObject.StartTime; HitObjects = new[] { hitObject }; } - public Issue(IEnumerable hitObjects, IssueTemplate template, params object[] args) - : this(template, args) + public Issue(ICheck check, IEnumerable hitObjects, IssueTemplate template, params object[] args) + : this(check, template, args) { var hitObjectList = hitObjects.ToList(); diff --git a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs index a1156c7c72..4a5f98ca5f 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs @@ -14,11 +14,6 @@ namespace osu.Game.Rulesets.Edit.Checks.Components private static readonly Color4 negligible_green = new Colour4(0.33f, 0.8f, 0.5f, 1.0f); private static readonly Color4 error_gray = new Colour4(0.5f, 0.5f, 0.5f, 1.0f); - /// - /// The check that this template originates from. - /// - public Check Origin; - /// /// The type of the issue. /// diff --git a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs index 0c4aa04ff3..c0c4a040f6 100644 --- a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs +++ b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs @@ -130,7 +130,7 @@ namespace osu.Game.Screens.Edit.Verify { table.Issues = beatmapVerifier.Run(Beatmap) .OrderByDescending(issue => issue.Template.Type) - .ThenByDescending(issue => issue.Template.Origin.Metadata.Category); + .ThenByDescending(issue => issue.Check.Metadata.Category); } } } From a2fc9c398fc731e36c3472bc37a5c153af7d5378 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Mon, 12 Apr 2021 10:08:30 +0200 Subject: [PATCH 044/150] Rename `CreateChecker` -> `CreateBeatmapVerifier` --- osu.Game.Rulesets.Osu/OsuRuleset.cs | 2 +- osu.Game/Rulesets/Ruleset.cs | 2 +- osu.Game/Screens/Edit/Verify/VerifyScreen.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 1658846e98..63da100a04 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -206,7 +206,7 @@ namespace osu.Game.Rulesets.Osu public override HitObjectComposer CreateHitObjectComposer() => new OsuHitObjectComposer(this); - public override BeatmapVerifier CreateChecker() => new OsuBeatmapVerifier(); + public override BeatmapVerifier CreateBeatmapVerifier() => new OsuBeatmapVerifier(); public override string Description => "osu!"; diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 088bcc7712..2a29d88c89 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -202,7 +202,7 @@ namespace osu.Game.Rulesets public virtual HitObjectComposer CreateHitObjectComposer() => null; - public virtual BeatmapVerifier CreateChecker() => null; + public virtual BeatmapVerifier CreateBeatmapVerifier() => null; public virtual Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.Solid.QuestionCircle }; diff --git a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs index c0c4a040f6..806029df4d 100644 --- a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs +++ b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs @@ -36,7 +36,7 @@ namespace osu.Game.Screens.Edit.Verify var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); ruleset = parent.Get>().Value.BeatmapInfo.Ruleset?.CreateInstance(); - beatmapVerifier = ruleset?.CreateChecker(); + beatmapVerifier = ruleset?.CreateBeatmapVerifier(); return dependencies; } From f1b8171e389e5df4eb67b567d4e48586a3001c73 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 12 Apr 2021 17:13:48 +0900 Subject: [PATCH 045/150] Remove `#nullable true` for now to suppress inspector --- osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs index f527c0e105..45bcd0bc19 100644 --- a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs +++ b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable enable - using System; using System.Collections.Generic; using JetBrains.Annotations; From 6d18b3db00a5f3f045dc35cdc6a740bae7156f90 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 12 Apr 2021 18:49:38 +0900 Subject: [PATCH 046/150] Avoid empty list allocation --- osu.Game/Rulesets/UI/RulesetInputManager.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index fb56a5d93d..1c0d820a3d 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -102,10 +102,13 @@ namespace osu.Game.Rulesets.UI #endregion + // to avoid allocation + private readonly List emptyInputList = new List(); + protected override List GetPendingInputs() { if (replayInputHandler != null && !replayInputHandler.IsActive) - return new List(); + return emptyInputList; return base.GetPendingInputs(); } From 359fae895f843ff7b8775917d443a89556705d07 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 12 Apr 2021 18:50:25 +0900 Subject: [PATCH 047/150] Rename property --- osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs | 6 +++--- osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs | 4 ++-- osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs index 64af4b6db6..954871595e 100644 --- a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs +++ b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs @@ -220,7 +220,7 @@ namespace osu.Game.Tests.NonVisual // no frames are arrived yet setTime(0, null); setTime(1000, null); - Assert.IsTrue(handler.WaitingNextFrame, "Should be waiting for the first frame"); + Assert.IsTrue(handler.WaitingForFrame, "Should be waiting for the first frame"); replay.Frames.Add(new TestReplayFrame(0)); replay.Frames.Add(new TestReplayFrame(1000)); @@ -228,11 +228,11 @@ namespace osu.Game.Tests.NonVisual // should always play from beginning setTime(1000, 0); confirmCurrentFrame(0); - Assert.IsFalse(handler.WaitingNextFrame, "Should not be waiting yet"); + Assert.IsFalse(handler.WaitingForFrame, "Should not be waiting yet"); setTime(1000, 1000); confirmCurrentFrame(1); confirmNextFrame(1); - Assert.IsTrue(handler.WaitingNextFrame, "Should be waiting"); + Assert.IsTrue(handler.WaitingForFrame, "Should be waiting"); // cannot seek beyond the last frame setTime(1500, null); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs index 9f1faa8e26..397b37718d 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs @@ -83,7 +83,7 @@ namespace osu.Game.Tests.Visual.Gameplay waitForPlayer(); AddAssert("ensure frames arrived", () => replayHandler.HasFrames); - AddUntilStep("wait for frame starvation", () => replayHandler.WaitingNextFrame); + AddUntilStep("wait for frame starvation", () => replayHandler.WaitingForFrame); checkPaused(true); double? pausedTime = null; @@ -92,7 +92,7 @@ namespace osu.Game.Tests.Visual.Gameplay sendFrames(); - AddUntilStep("wait for frame starvation", () => replayHandler.WaitingNextFrame); + AddUntilStep("wait for frame starvation", () => replayHandler.WaitingForFrame); checkPaused(true); AddAssert("time advanced", () => currentFrameStableTime > pausedTime); diff --git a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs index 45bcd0bc19..23cc311d79 100644 --- a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs +++ b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Replays /// /// Whether we are waiting for new frames to be received. /// - public bool WaitingNextFrame => !replay.HasReceivedAllFrames && currentFrameIndex == Frames.Count - 1; + public bool WaitingForFrame => !replay.HasReceivedAllFrames && currentFrameIndex == Frames.Count - 1; /// /// The current frame of the replay. @@ -140,7 +140,7 @@ namespace osu.Game.Rulesets.Replays frameEnd = getFrameTime(currentFrameIndex + 1); // Pause until more frames are arrived. - if (WaitingNextFrame && frameStart < time) + if (WaitingForFrame && frameStart < time) { CurrentTime = frameStart; return null; From 31d36071052bf72f9a82256242cbb6fa7c2b0e38 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 12 Apr 2021 18:50:54 +0900 Subject: [PATCH 048/150] Add TODO comment --- osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs index 23cc311d79..c3cd957f0d 100644 --- a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs +++ b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs @@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Replays /// /// The next frame of the replay. - /// The start time is always greater or equals to the start time of regardless of the seeking direction. + /// The start time is always greater or equal to the start time of regardless of the seeking direction. /// If it is before the first frame of the replay or the after the last frame of the replay, and agree. /// /// The replay is empty. @@ -83,7 +83,8 @@ namespace osu.Game.Rulesets.Replays protected FramedReplayInputHandler(Replay replay) { - // This replay frame ordering should be enforced on the Replay type + // TODO: This replay frame ordering should be enforced on the Replay type. + // Currently, the ordering can be broken if the frames are added after this construction. replay.Frames.Sort((x, y) => x.Time.CompareTo(y.Time)); this.replay = replay; From cc2acf5e548fb11873f1d20d480b27e35ced57fb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Apr 2021 19:05:23 +0900 Subject: [PATCH 049/150] Fix ctrl-dragging on an existing selection causing deselection of the hovered object --- .../Compose/Components/BlueprintContainer.cs | 38 ++++++++++++++----- .../Compose/Components/SelectionHandler.cs | 37 +++++++++++++++--- 2 files changed, 60 insertions(+), 15 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 5699be4560..64cf0e7512 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -135,11 +135,12 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override bool OnMouseDown(MouseDownEvent e) { - if (!beginClickSelection(e)) return true; + bool selectionPerformed = beginClickSelection(e); + // even if a selection didn't occur, a drag event may still move the selection. prepareSelectionMovement(); - return e.Button == MouseButton.Left; + return selectionPerformed || e.Button == MouseButton.Left; } private SelectionBlueprint clickedBlueprint; @@ -154,7 +155,7 @@ namespace osu.Game.Screens.Edit.Compose.Components // Deselection should only occur if no selected blueprints are hovered // A special case for when a blueprint was selected via this click is added since OnClick() may occur outside the hitobject and should not trigger deselection - if (endClickSelection() || clickedBlueprint != null) + if (endClickSelection(e) || clickedBlueprint != null) return true; deselectAll(); @@ -177,7 +178,12 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override void OnMouseUp(MouseUpEvent e) { // Special case for when a drag happened instead of a click - Schedule(() => endClickSelection()); + Schedule(() => + { + endClickSelection(e); + clickSelectionBegan = false; + isDraggingBlueprint = false; + }); finishSelectionMovement(); } @@ -226,7 +232,6 @@ namespace osu.Game.Screens.Edit.Compose.Components Beatmap.Update(obj); changeHandler?.EndChange(); - isDraggingBlueprint = false; } if (DragBox.State == Visibility.Visible) @@ -355,13 +360,28 @@ namespace osu.Game.Screens.Edit.Compose.Components /// /// Finishes the current blueprint selection. /// + /// The mouse event which triggered end of selection. /// Whether a click selection was active. - private bool endClickSelection() + private bool endClickSelection(MouseButtonEvent e) { - if (!clickSelectionBegan) - return false; + if (!clickSelectionBegan && !isDraggingBlueprint) + { + // if a selection didn't occur, we may want to trigger a deselection. + if (e.ControlPressed && e.Button == MouseButton.Left) + { + // Iterate from the top of the input stack (blueprints closest to the front of the screen first). + // Priority is given to already-selected blueprints. + foreach (SelectionBlueprint blueprint in SelectionBlueprints.AliveChildren.Reverse().OrderByDescending(b => b.IsSelected)) + { + if (!blueprint.IsHovered) continue; + + return clickSelectionBegan = SelectionHandler.HandleDeselectionRequested(blueprint, e); + } + } + + return false; + } - clickSelectionBegan = false; return true; } diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index 018d4d081c..e5e1100797 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -228,12 +228,31 @@ namespace osu.Game.Screens.Edit.Compose.Components return false; } - if (e.ControlPressed && e.Button == MouseButton.Left) + // while holding control, we only want to add to selection, not replace an existing selection. + if (e.ControlPressed && e.Button == MouseButton.Left && !blueprint.IsSelected) + { blueprint.ToggleSelection(); - else - ensureSelected(blueprint); + return true; + } - return true; + return ensureSelected(blueprint); + } + + /// + /// Handle a blueprint requesting selection. + /// + /// The blueprint. + /// The mouse event responsible for deselection. + /// Whether a deselection was performed. + internal bool HandleDeselectionRequested(SelectionBlueprint blueprint, MouseButtonEvent e) + { + if (blueprint.IsSelected) + { + blueprint.ToggleSelection(); + return true; + } + + return false; } private void handleQuickDeletion(SelectionBlueprint blueprint) @@ -247,13 +266,19 @@ namespace osu.Game.Screens.Edit.Compose.Components deleteSelected(); } - private void ensureSelected(SelectionBlueprint blueprint) + /// + /// Ensure the blueprint is in a selected state. + /// + /// The blueprint to select. + /// Whether selection state was changed. + private bool ensureSelected(SelectionBlueprint blueprint) { if (blueprint.IsSelected) - return; + return false; DeselectAll?.Invoke(); blueprint.Select(); + return true; } private void deleteSelected() From b4c75ba3c6eb81a8cf88f36edc8738ee7e2e09df Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Apr 2021 19:19:25 +0900 Subject: [PATCH 050/150] Fix TestQuickDeleteRemovesObject failing on second run --- .../Visual/Editing/TestSceneEditorQuickDelete.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorQuickDelete.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorQuickDelete.cs index 9efd299fba..8a0f27b851 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorQuickDelete.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorQuickDelete.cs @@ -51,7 +51,7 @@ namespace osu.Game.Tests.Visual.Editing [Test] public void TestQuickDeleteRemovesSliderControlPoint() { - Slider slider = new Slider { StartTime = 1000 }; + Slider slider = null; PathControlPoint[] points = { @@ -62,7 +62,12 @@ namespace osu.Game.Tests.Visual.Editing AddStep("add slider", () => { - slider.Path = new SliderPath(points); + slider = new Slider + { + StartTime = 1000, + Path = new SliderPath(points) + }; + EditorBeatmap.Add(slider); }); From 7c4f6d2b62635ef38c95754484f7bb82b2d2122d Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Mon, 12 Apr 2021 15:47:26 +0200 Subject: [PATCH 051/150] Rework template usage Includes moving the origin check back to templates, constructing nested template classes in each check, and making parameterized template usage. --- .../Edit/Checks/CheckOffscreenObjects.cs | 57 ++++++++++++------- .../Rulesets/Edit/Checks/CheckBackground.cs | 55 +++++++++++------- .../Rulesets/Edit/Checks/Components/Issue.cs | 17 +++--- .../Edit/Checks/Components/IssueTemplate.cs | 8 ++- 4 files changed, 88 insertions(+), 49 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs index b34c9966a4..c4f38e6d09 100644 --- a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs @@ -22,29 +22,46 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks // (higher = more performant, but higher false-negative chance). private const int path_step_size = 5; + private readonly IssueTemplateOffscreenCircle templateOffscreenCircle; + private readonly IssueTemplateOffscreenSlider templateOffscreenSlider; + private readonly IssueTemplate[] templates; + + private class IssueTemplateOffscreenCircle : IssueTemplate + { + public IssueTemplateOffscreenCircle(ICheck checkOrigin) + : base(checkOrigin, IssueType.Problem, "This circle goes offscreen on a 4:3 aspect ratio.") + { + } + + public Issue Create(HitCircle circle) => new Issue(circle, this); + } + + private class IssueTemplateOffscreenSlider : IssueTemplate + { + public IssueTemplateOffscreenSlider(ICheck checkOrigin) + : base(checkOrigin, IssueType.Problem, "This slider goes offscreen here on a 4:3 aspect ratio.") + { + } + + public Issue Create(Slider slider, double offscreenTime) => new Issue(slider, this) { Time = offscreenTime }; + } + + public CheckOffscreenObjects() + { + templates = new IssueTemplate[] + { + templateOffscreenCircle = new IssueTemplateOffscreenCircle(this), + templateOffscreenSlider = new IssueTemplateOffscreenSlider(this) + }; + } + public CheckMetadata Metadata { get; } = new CheckMetadata ( category: CheckCategory.Compose, description: "Offscreen hitobjects." ); - public IEnumerable PossibleTemplates => new[] - { - templateOffscreen, - templateOffscreenSliderPath - }; - - private readonly IssueTemplate templateOffscreen = new IssueTemplate - ( - type: IssueType.Problem, - unformattedMessage: "This object goes offscreen on a 4:3 aspect ratio." - ); - - private readonly IssueTemplate templateOffscreenSliderPath = new IssueTemplate - ( - type: IssueType.Problem, - unformattedMessage: "This slider goes offscreen here on a 4:3 aspect ratio." - ); + public IEnumerable PossibleTemplates => templates; public IEnumerable Run(IBeatmap beatmap) { @@ -63,7 +80,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks case HitCircle circle: { if (isOffscreen(circle.StackedPosition, circle.Radius)) - yield return new Issue(this, circle, templateOffscreen); + yield return templateOffscreenCircle.Create(circle); break; } @@ -89,7 +106,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks // `SpanDuration` ensures we don't include reverses. double time = slider.StartTime + progress * slider.SpanDuration; - yield return new Issue(this, slider, templateOffscreenSliderPath) { Time = time }; + yield return templateOffscreenSlider.Create(slider, time); yield break; } @@ -98,7 +115,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks if (!isOffscreen(slider.StackedEndPosition, slider.Radius)) yield break; - yield return new Issue(this, slider, templateOffscreenSliderPath) { Time = slider.EndTime }; + yield return templateOffscreenSlider.Create(slider, slider.EndTime); } private bool isOffscreen(Vector2 position, double radius) diff --git a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs index c922aa03c0..1e45ea261c 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs @@ -10,35 +10,52 @@ namespace osu.Game.Rulesets.Edit.Checks { public class CheckBackground : ICheck { + private readonly IssueTemplateNoneSet templateNoneSet; + private readonly IssueTemplateDoesNotExist templateDoesNotExist; + private readonly IssueTemplate[] templates; + + private class IssueTemplateNoneSet : IssueTemplate + { + public IssueTemplateNoneSet(ICheck checkOrigin) + : base(checkOrigin, IssueType.Problem, "No background has been set") + { + } + + public Issue Create() => new Issue(this); + } + + private class IssueTemplateDoesNotExist : IssueTemplate + { + public IssueTemplateDoesNotExist(ICheck checkOrigin) + : base(checkOrigin, IssueType.Problem, "The background file \"{0}\" does not exist.") + { + } + + public Issue Create(string filename) => new Issue(this, filename); + } + + public CheckBackground() + { + templates = new IssueTemplate[] + { + templateNoneSet = new IssueTemplateNoneSet(this), + templateDoesNotExist = new IssueTemplateDoesNotExist(this) + }; + } + public CheckMetadata Metadata { get; } = new CheckMetadata ( category: CheckCategory.Resources, description: "Missing background." ); - public IEnumerable PossibleTemplates => new[] - { - templateNoneSet, - templateDoesNotExist - }; - - private readonly IssueTemplate templateNoneSet = new IssueTemplate - ( - type: IssueType.Problem, - unformattedMessage: "No background has been set." - ); - - private readonly IssueTemplate templateDoesNotExist = new IssueTemplate - ( - type: IssueType.Problem, - unformattedMessage: "The background file \"{0}\" is does not exist." - ); + public IEnumerable PossibleTemplates => templates; public IEnumerable Run(IBeatmap beatmap) { if (beatmap.Metadata.BackgroundFile == null) { - yield return new Issue(this, templateNoneSet); + yield return templateNoneSet.Create(); yield break; } @@ -51,7 +68,7 @@ namespace osu.Game.Rulesets.Edit.Checks if (file != null) yield break; - yield return new Issue(this, templateDoesNotExist, beatmap.Metadata.BackgroundFile); + yield return templateDoesNotExist.Create(beatmap.Metadata.BackgroundFile); } } } diff --git a/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs b/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs index d0f7df857b..2bc9930e8f 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Edit.Checks.Components /// /// The check that this issue originates from. /// - public ICheck Check; + public ICheck Check => Template.Check; /// /// The arguments that give this issue its context, based on the . These are then substituted into the . @@ -37,30 +37,29 @@ namespace osu.Game.Rulesets.Edit.Checks.Components /// public object[] Arguments; - public Issue(ICheck check, IssueTemplate template, params object[] args) + public Issue(IssueTemplate template, params object[] args) { - Check = check; Time = null; HitObjects = Array.Empty(); Template = template; Arguments = args; } - public Issue(ICheck check, double? time, IssueTemplate template, params object[] args) - : this(check, template, args) + public Issue(double? time, IssueTemplate template, params object[] args) + : this(template, args) { Time = time; } - public Issue(ICheck check, HitObject hitObject, IssueTemplate template, params object[] args) - : this(check, template, args) + public Issue(HitObject hitObject, IssueTemplate template, params object[] args) + : this(template, args) { Time = hitObject.StartTime; HitObjects = new[] { hitObject }; } - public Issue(ICheck check, IEnumerable hitObjects, IssueTemplate template, params object[] args) - : this(check, template, args) + public Issue(IEnumerable hitObjects, IssueTemplate template, params object[] args) + : this(template, args) { var hitObjectList = hitObjects.ToList(); diff --git a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs index 4a5f98ca5f..e746844879 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs @@ -14,6 +14,11 @@ namespace osu.Game.Rulesets.Edit.Checks.Components private static readonly Color4 negligible_green = new Colour4(0.33f, 0.8f, 0.5f, 1.0f); private static readonly Color4 error_gray = new Colour4(0.5f, 0.5f, 0.5f, 1.0f); + /// + /// The check that this template originates from. + /// + public ICheck Check; + /// /// The type of the issue. /// @@ -26,8 +31,9 @@ namespace osu.Game.Rulesets.Edit.Checks.Components /// public readonly string UnformattedMessage; - public IssueTemplate(IssueType type, string unformattedMessage) + public IssueTemplate(ICheck check, IssueType type, string unformattedMessage) { + Check = check; Type = type; UnformattedMessage = unformattedMessage; } From 1c69829ad488edf10b2aaaa347c5b3be28aeabc0 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Mon, 12 Apr 2021 15:47:58 +0200 Subject: [PATCH 052/150] Fix `Template.Origin` -> `Check` --- osu.Game/Screens/Edit/Verify/IssueTable.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Verify/IssueTable.cs b/osu.Game/Screens/Edit/Verify/IssueTable.cs index 84dbabf300..516e1adf44 100644 --- a/osu.Game/Screens/Edit/Verify/IssueTable.cs +++ b/osu.Game/Screens/Edit/Verify/IssueTable.cs @@ -112,7 +112,7 @@ namespace osu.Game.Screens.Edit.Verify }, new OsuSpriteText { - Text = issue.Template.Origin.Metadata.Category.ToString(), + Text = issue.Check.Metadata.Category.ToString(), Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold), Margin = new MarginPadding(10) } From 008dbc7dd6f841ea245ec8b732696ebbbb2a73b4 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Mon, 12 Apr 2021 15:49:13 +0200 Subject: [PATCH 053/150] Reverse `IssueType` ordering Reversed both in the enum and where it's displayed, so ends up the same in the end. --- osu.Game/Rulesets/Edit/Checks/Components/IssueType.cs | 10 +++++----- osu.Game/Screens/Edit/Verify/VerifyScreen.cs | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game/Rulesets/Edit/Checks/Components/IssueType.cs b/osu.Game/Rulesets/Edit/Checks/Components/IssueType.cs index be43060cfc..1241e058ad 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/IssueType.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/IssueType.cs @@ -4,22 +4,22 @@ namespace osu.Game.Rulesets.Edit.Checks.Components { /// - /// The type, or severity, of an issue. This decides its priority. + /// The type, or severity, of an issue. /// public enum IssueType { /// A must-fix in the vast majority of cases. - Problem = 3, + Problem, /// A possible mistake. Often requires critical thinking. - Warning = 2, + Warning, // 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, + Error, // TODO: Negligible issues should be hidden by default. /// A possible mistake so minor/unlikely that it can often be safely ignored. - Negligible = 0, + Negligible, } } diff --git a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs index 806029df4d..a3d808ce62 100644 --- a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs +++ b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs @@ -129,8 +129,8 @@ namespace osu.Game.Screens.Edit.Verify private void refresh() { table.Issues = beatmapVerifier.Run(Beatmap) - .OrderByDescending(issue => issue.Template.Type) - .ThenByDescending(issue => issue.Check.Metadata.Category); + .OrderBy(issue => issue.Template.Type) + .ThenBy(issue => issue.Check.Metadata.Category); } } } From caaaba59505f19f999a593a3c35f460c598c9065 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Mon, 12 Apr 2021 16:20:53 +0200 Subject: [PATCH 054/150] Rename `Check` -> `ICheck` --- osu.Game/Rulesets/Edit/Checks/Components/{Check.cs => ICheck.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename osu.Game/Rulesets/Edit/Checks/Components/{Check.cs => ICheck.cs} (100%) diff --git a/osu.Game/Rulesets/Edit/Checks/Components/Check.cs b/osu.Game/Rulesets/Edit/Checks/Components/ICheck.cs similarity index 100% rename from osu.Game/Rulesets/Edit/Checks/Components/Check.cs rename to osu.Game/Rulesets/Edit/Checks/Components/ICheck.cs From 6d50d01186f24c342b0fc406803b2486cebbbf4b Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Mon, 12 Apr 2021 16:23:05 +0200 Subject: [PATCH 055/150] Make `IssueTemplate.Check` readonly --- osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs index e746844879..97df79ecd8 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Edit.Checks.Components /// /// The check that this template originates from. /// - public ICheck Check; + public readonly ICheck Check; /// /// The type of the issue. From 36bd235021d08202dd0c489b1bc664b91a31aca1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Apr 2021 23:36:10 +0900 Subject: [PATCH 056/150] Move nested classes to bottom of file --- .../Edit/Checks/CheckOffscreenObjects.cs | 40 +++++++++---------- .../Rulesets/Edit/Checks/CheckBackground.cs | 40 +++++++++---------- 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs index c4f38e6d09..f2a062b6d7 100644 --- a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs @@ -26,26 +26,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks private readonly IssueTemplateOffscreenSlider templateOffscreenSlider; private readonly IssueTemplate[] templates; - private class IssueTemplateOffscreenCircle : IssueTemplate - { - public IssueTemplateOffscreenCircle(ICheck checkOrigin) - : base(checkOrigin, IssueType.Problem, "This circle goes offscreen on a 4:3 aspect ratio.") - { - } - - public Issue Create(HitCircle circle) => new Issue(circle, this); - } - - private class IssueTemplateOffscreenSlider : IssueTemplate - { - public IssueTemplateOffscreenSlider(ICheck checkOrigin) - : base(checkOrigin, IssueType.Problem, "This slider goes offscreen here on a 4:3 aspect ratio.") - { - } - - public Issue Create(Slider slider, double offscreenTime) => new Issue(slider, this) { Time = offscreenTime }; - } - public CheckOffscreenObjects() { templates = new IssueTemplate[] @@ -123,5 +103,25 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks return position.X - radius < min_x || position.X + radius > max_x || position.Y - radius < min_y || position.Y + radius > max_y; } + + private class IssueTemplateOffscreenCircle : IssueTemplate + { + public IssueTemplateOffscreenCircle(ICheck checkOrigin) + : base(checkOrigin, IssueType.Problem, "This circle goes offscreen on a 4:3 aspect ratio.") + { + } + + public Issue Create(HitCircle circle) => new Issue(circle, this); + } + + private class IssueTemplateOffscreenSlider : IssueTemplate + { + public IssueTemplateOffscreenSlider(ICheck checkOrigin) + : base(checkOrigin, IssueType.Problem, "This slider goes offscreen here on a 4:3 aspect ratio.") + { + } + + public Issue Create(Slider slider, double offscreenTime) => new Issue(slider, this) { Time = offscreenTime }; + } } } diff --git a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs index 1e45ea261c..b48d19ae29 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs @@ -14,26 +14,6 @@ namespace osu.Game.Rulesets.Edit.Checks private readonly IssueTemplateDoesNotExist templateDoesNotExist; private readonly IssueTemplate[] templates; - private class IssueTemplateNoneSet : IssueTemplate - { - public IssueTemplateNoneSet(ICheck checkOrigin) - : base(checkOrigin, IssueType.Problem, "No background has been set") - { - } - - public Issue Create() => new Issue(this); - } - - private class IssueTemplateDoesNotExist : IssueTemplate - { - public IssueTemplateDoesNotExist(ICheck checkOrigin) - : base(checkOrigin, IssueType.Problem, "The background file \"{0}\" does not exist.") - { - } - - public Issue Create(string filename) => new Issue(this, filename); - } - public CheckBackground() { templates = new IssueTemplate[] @@ -70,5 +50,25 @@ namespace osu.Game.Rulesets.Edit.Checks yield return templateDoesNotExist.Create(beatmap.Metadata.BackgroundFile); } + + private class IssueTemplateNoneSet : IssueTemplate + { + public IssueTemplateNoneSet(ICheck checkOrigin) + : base(checkOrigin, IssueType.Problem, "No background has been set") + { + } + + public Issue Create() => new Issue(this); + } + + private class IssueTemplateDoesNotExist : IssueTemplate + { + public IssueTemplateDoesNotExist(ICheck checkOrigin) + : base(checkOrigin, IssueType.Problem, "The background file \"{0}\" does not exist.") + { + } + + public Issue Create(string filename) => new Issue(this, filename); + } } } From 62c181228284f034168b87c27879fa72b87369b9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Apr 2021 23:37:47 +0900 Subject: [PATCH 057/150] Remove redundant parameter naming --- osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs | 6 +----- osu.Game/Rulesets/Edit/Checks/CheckBackground.cs | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs index f2a062b6d7..8d4cf2f4f0 100644 --- a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs @@ -35,11 +35,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks }; } - public CheckMetadata Metadata { get; } = new CheckMetadata - ( - category: CheckCategory.Compose, - description: "Offscreen hitobjects." - ); + public CheckMetadata Metadata { get; } = new CheckMetadata(CheckCategory.Compose, "Offscreen hitobjects"); public IEnumerable PossibleTemplates => templates; diff --git a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs index b48d19ae29..1a766798cb 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs @@ -23,11 +23,7 @@ namespace osu.Game.Rulesets.Edit.Checks }; } - public CheckMetadata Metadata { get; } = new CheckMetadata - ( - category: CheckCategory.Resources, - description: "Missing background." - ); + public CheckMetadata Metadata { get; } = new CheckMetadata(CheckCategory.Resources, "Missing background"); public IEnumerable PossibleTemplates => templates; From 43b97fe0ad738854f1ef795bfd386db615c75458 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Mon, 12 Apr 2021 10:52:12 -0400 Subject: [PATCH 058/150] Refactor PowerStatus (now called BatteryInfo) --- osu.Android/OsuGameAndroid.cs | 6 ++---- osu.Android/osu.Android.csproj | 1 - .../Visual/Gameplay/TestScenePlayerLoader.cs | 14 ++++++-------- osu.Game/OsuGameBase.cs | 4 ++-- osu.Game/Screens/Play/PlayerLoader.cs | 6 +++--- osu.Game/Utils/{PowerStatus.cs => BatteryInfo.cs} | 12 +++--------- osu.iOS/OsuGameIOS.cs | 6 ++---- osu.iOS/osu.iOS.csproj | 1 - 8 files changed, 18 insertions(+), 32 deletions(-) rename osu.Game/Utils/{PowerStatus.cs => BatteryInfo.cs} (68%) diff --git a/osu.Android/OsuGameAndroid.cs b/osu.Android/OsuGameAndroid.cs index 02f4fa1ad6..050bf2b787 100644 --- a/osu.Android/OsuGameAndroid.cs +++ b/osu.Android/OsuGameAndroid.cs @@ -75,12 +75,10 @@ namespace osu.Android protected override UpdateManager CreateUpdateManager() => new SimpleUpdateManager(); - protected override PowerStatus CreatePowerStatus() => new AndroidPowerStatus(); + protected override BatteryInfo CreateBatteryInfo() => new AndroidBatteryInfo(); - private class AndroidPowerStatus : PowerStatus + private class AndroidBatteryInfo : BatteryInfo { - public override double BatteryCutoff => 0.20; - public override double ChargeLevel => Battery.ChargeLevel; public override bool IsCharging => Battery.PowerSource != BatteryPowerSource.Battery; diff --git a/osu.Android/osu.Android.csproj b/osu.Android/osu.Android.csproj index 64d5e5b1c8..582c856a47 100644 --- a/osu.Android/osu.Android.csproj +++ b/osu.Android/osu.Android.csproj @@ -64,7 +64,6 @@ - diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index 657c1dd47e..311448a284 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -49,8 +49,8 @@ namespace osu.Game.Tests.Visual.Gameplay [Cached] private readonly VolumeOverlay volumeOverlay; - [Cached(typeof(PowerStatus))] - private readonly LocalPowerStatus powerStatus = new LocalPowerStatus(); + [Cached(typeof(BatteryInfo))] + private readonly LocalBatteryInfo batteryInfo = new LocalBatteryInfo(); private readonly ChangelogOverlay changelogOverlay; @@ -302,8 +302,8 @@ namespace osu.Game.Tests.Visual.Gameplay // set charge status and level AddStep("load player", () => resetPlayer(false, () => { - powerStatus.SetCharging(isCharging); - powerStatus.SetChargeLevel(chargeLevel); + batteryInfo.SetCharging(isCharging); + batteryInfo.SetChargeLevel(chargeLevel); })); AddUntilStep("wait for player", () => player?.LoadState == LoadState.Ready); AddAssert($"notification {(shouldWarn ? "triggered" : "not triggered")}", () => notificationOverlay.UnreadCount.Value == (shouldWarn ? 1 : 0)); @@ -381,16 +381,14 @@ namespace osu.Game.Tests.Visual.Gameplay } /// - /// Mutable dummy PowerStatus class for + /// Mutable dummy BatteryInfo class for /// /// - private class LocalPowerStatus : PowerStatus + private class LocalBatteryInfo : BatteryInfo { private bool isCharging = true; private double chargeLevel = 1; - public override double BatteryCutoff => 0.2; - public override bool IsCharging => isCharging; public override double ChargeLevel => chargeLevel; diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 96aabf0024..de8ba93106 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -157,7 +157,7 @@ namespace osu.Game protected override UserInputManager CreateUserInputManager() => new OsuUserInputManager(); - protected virtual PowerStatus CreatePowerStatus() => null; + protected virtual BatteryInfo CreateBatteryInfo() => null; /// /// The maximum volume at which audio tracks should playback. This can be set lower than 1 to create some head-room for sound effects. @@ -285,7 +285,7 @@ namespace osu.Game dependencies.Cache(SettingsStore = new SettingsStore(contextFactory)); dependencies.Cache(RulesetConfigCache = new RulesetConfigCache(SettingsStore)); - var powerStatus = CreatePowerStatus(); + var powerStatus = CreateBatteryInfo(); if (powerStatus != null) dependencies.CacheAs(powerStatus); diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index d342bf8273..964410f838 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -114,7 +114,7 @@ namespace osu.Game.Screens.Play private AudioManager audioManager { get; set; } [Resolved(CanBeNull = true)] - private PowerStatus powerStatus { get; set; } + private BatteryInfo batteryInfo { get; set; } public PlayerLoader(Func createPlayer) { @@ -483,11 +483,11 @@ namespace osu.Game.Screens.Play private void showBatteryWarningIfNeeded() { - if (powerStatus == null) return; + if (batteryInfo == null) return; if (!batteryWarningShownOnce.Value) { - if (powerStatus.IsLowBattery) + if (batteryInfo.IsLowBattery) { notificationOverlay?.Post(new BatteryWarningNotification()); batteryWarningShownOnce.Value = true; diff --git a/osu.Game/Utils/PowerStatus.cs b/osu.Game/Utils/BatteryInfo.cs similarity index 68% rename from osu.Game/Utils/PowerStatus.cs rename to osu.Game/Utils/BatteryInfo.cs index 46f7e32b9e..1a64213d8e 100644 --- a/osu.Game/Utils/PowerStatus.cs +++ b/osu.Game/Utils/BatteryInfo.cs @@ -5,15 +5,9 @@ namespace osu.Game.Utils { /// /// Provides access to the system's power status. - /// Currently implemented on iOS and Android only. /// - public abstract class PowerStatus + public abstract class BatteryInfo { - /// - /// The maximum battery level considered as low, from 0 to 1. - /// - public abstract double BatteryCutoff { get; } - /// /// The charge level of the battery, from 0 to 1. /// @@ -23,8 +17,8 @@ namespace osu.Game.Utils /// /// Whether the battery is currently low in charge. - /// Returns true if not charging and current charge level is lower than or equal to . + /// Returns true if not charging and current charge level is lower than or equal to 25%. /// - public bool IsLowBattery => !IsCharging && ChargeLevel <= BatteryCutoff; + public bool IsLowBattery => !IsCharging && ChargeLevel <= 0.25; } } diff --git a/osu.iOS/OsuGameIOS.cs b/osu.iOS/OsuGameIOS.cs index b53b594eae..702aef45f5 100644 --- a/osu.iOS/OsuGameIOS.cs +++ b/osu.iOS/OsuGameIOS.cs @@ -16,12 +16,10 @@ namespace osu.iOS protected override UpdateManager CreateUpdateManager() => new SimpleUpdateManager(); - protected override PowerStatus CreatePowerStatus() => new IOSPowerStatus(); + protected override BatteryInfo CreateBatteryInfo() => new IOSBatteryInfo(); - private class IOSPowerStatus : PowerStatus + private class IOSBatteryInfo : BatteryInfo { - public override double BatteryCutoff => 0.25; - public override double ChargeLevel => Battery.ChargeLevel; public override bool IsCharging => Battery.PowerSource != BatteryPowerSource.Battery; diff --git a/osu.iOS/osu.iOS.csproj b/osu.iOS/osu.iOS.csproj index ed6f52c60e..1cbe4422cc 100644 --- a/osu.iOS/osu.iOS.csproj +++ b/osu.iOS/osu.iOS.csproj @@ -117,7 +117,6 @@ - From bb720c23a01e6f27b376491abf29e578f4db5bc6 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Mon, 12 Apr 2021 17:12:37 +0200 Subject: [PATCH 059/150] Remove check ctors and locals --- .../Edit/Checks/CheckOffscreenObjects.cs | 25 ++++++------------- .../Rulesets/Edit/Checks/CheckBackground.cs | 23 ++++++----------- 2 files changed, 15 insertions(+), 33 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs index 8d4cf2f4f0..adaabc0a1d 100644 --- a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs @@ -22,22 +22,13 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks // (higher = more performant, but higher false-negative chance). private const int path_step_size = 5; - private readonly IssueTemplateOffscreenCircle templateOffscreenCircle; - private readonly IssueTemplateOffscreenSlider templateOffscreenSlider; - private readonly IssueTemplate[] templates; - - public CheckOffscreenObjects() - { - templates = new IssueTemplate[] - { - templateOffscreenCircle = new IssueTemplateOffscreenCircle(this), - templateOffscreenSlider = new IssueTemplateOffscreenSlider(this) - }; - } - public CheckMetadata Metadata { get; } = new CheckMetadata(CheckCategory.Compose, "Offscreen hitobjects"); - public IEnumerable PossibleTemplates => templates; + public IEnumerable PossibleTemplates => new IssueTemplate[] + { + new IssueTemplateOffscreenCircle(this), + new IssueTemplateOffscreenSlider(this) + }; public IEnumerable Run(IBeatmap beatmap) { @@ -56,7 +47,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks case HitCircle circle: { if (isOffscreen(circle.StackedPosition, circle.Radius)) - yield return templateOffscreenCircle.Create(circle); + yield return new IssueTemplateOffscreenCircle(this).Create(circle); break; } @@ -82,7 +73,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks // `SpanDuration` ensures we don't include reverses. double time = slider.StartTime + progress * slider.SpanDuration; - yield return templateOffscreenSlider.Create(slider, time); + yield return new IssueTemplateOffscreenSlider(this).Create(slider, time); yield break; } @@ -91,7 +82,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks if (!isOffscreen(slider.StackedEndPosition, slider.Radius)) yield break; - yield return templateOffscreenSlider.Create(slider, slider.EndTime); + yield return new IssueTemplateOffscreenSlider(this).Create(slider, slider.EndTime); } private bool isOffscreen(Vector2 position, double radius) diff --git a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs index 1a766798cb..b0c1d6eb4b 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs @@ -10,28 +10,19 @@ namespace osu.Game.Rulesets.Edit.Checks { public class CheckBackground : ICheck { - private readonly IssueTemplateNoneSet templateNoneSet; - private readonly IssueTemplateDoesNotExist templateDoesNotExist; - private readonly IssueTemplate[] templates; - - public CheckBackground() - { - templates = new IssueTemplate[] - { - templateNoneSet = new IssueTemplateNoneSet(this), - templateDoesNotExist = new IssueTemplateDoesNotExist(this) - }; - } - public CheckMetadata Metadata { get; } = new CheckMetadata(CheckCategory.Resources, "Missing background"); - public IEnumerable PossibleTemplates => templates; + public IEnumerable PossibleTemplates => new IssueTemplate[] + { + new IssueTemplateNoneSet(this), + new IssueTemplateDoesNotExist(this) + }; public IEnumerable Run(IBeatmap beatmap) { if (beatmap.Metadata.BackgroundFile == null) { - yield return templateNoneSet.Create(); + yield return new IssueTemplateNoneSet(this).Create(); yield break; } @@ -44,7 +35,7 @@ namespace osu.Game.Rulesets.Edit.Checks if (file != null) yield break; - yield return templateDoesNotExist.Create(beatmap.Metadata.BackgroundFile); + yield return new IssueTemplateDoesNotExist(this).Create(beatmap.Metadata.BackgroundFile); } private class IssueTemplateNoneSet : IssueTemplate From f66306a81ad70868c29bc2a56d471a08f6bebbe5 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Mon, 12 Apr 2021 11:11:22 -0400 Subject: [PATCH 060/150] Remove IsLowBattery --- osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs | 2 +- osu.Game/Screens/Play/PlayerLoader.cs | 2 +- osu.Game/Utils/BatteryInfo.cs | 6 ------ 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index 311448a284..c56f2db0d0 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -294,7 +294,7 @@ namespace osu.Game.Tests.Visual.Gameplay [TestCase(false, 1.0, false)] // not charging, above cutoff --> no warning [TestCase(true, 0.1, false)] // charging, below cutoff --> no warning - [TestCase(false, 0.2, true)] // not charging, at cutoff --> warning + [TestCase(false, 0.25, true)] // not charging, at cutoff --> warning public void TestLowBatteryNotification(bool isCharging, double chargeLevel, bool shouldWarn) { AddStep("reset notification lock", () => sessionStatics.GetBindable(Static.LowBatteryNotificationShownOnce).Value = false); diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 964410f838..fc4659da97 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -487,7 +487,7 @@ namespace osu.Game.Screens.Play if (!batteryWarningShownOnce.Value) { - if (batteryInfo.IsLowBattery) + if (!batteryInfo.IsCharging && batteryInfo.ChargeLevel <= 0.25) { notificationOverlay?.Post(new BatteryWarningNotification()); batteryWarningShownOnce.Value = true; diff --git a/osu.Game/Utils/BatteryInfo.cs b/osu.Game/Utils/BatteryInfo.cs index 1a64213d8e..dd9b695e1f 100644 --- a/osu.Game/Utils/BatteryInfo.cs +++ b/osu.Game/Utils/BatteryInfo.cs @@ -14,11 +14,5 @@ namespace osu.Game.Utils public abstract double ChargeLevel { get; } public abstract bool IsCharging { get; } - - /// - /// Whether the battery is currently low in charge. - /// Returns true if not charging and current charge level is lower than or equal to 25%. - /// - public bool IsLowBattery => !IsCharging && ChargeLevel <= 0.25; } } From 19a154ddf15684f8a3429a642e3f71e555e0d27f Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Mon, 12 Apr 2021 17:28:12 +0200 Subject: [PATCH 061/150] Rename `checkOrigin` -> `check` More consistent with `Issue.ctor`'s "template". --- .../Edit/Checks/CheckOffscreenObjects.cs | 8 ++++---- osu.Game/Rulesets/Edit/Checks/CheckBackground.cs | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs index adaabc0a1d..0a682c4a83 100644 --- a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs @@ -93,8 +93,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks private class IssueTemplateOffscreenCircle : IssueTemplate { - public IssueTemplateOffscreenCircle(ICheck checkOrigin) - : base(checkOrigin, IssueType.Problem, "This circle goes offscreen on a 4:3 aspect ratio.") + public IssueTemplateOffscreenCircle(ICheck check) + : base(check, IssueType.Problem, "This circle goes offscreen on a 4:3 aspect ratio.") { } @@ -103,8 +103,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks private class IssueTemplateOffscreenSlider : IssueTemplate { - public IssueTemplateOffscreenSlider(ICheck checkOrigin) - : base(checkOrigin, IssueType.Problem, "This slider goes offscreen here on a 4:3 aspect ratio.") + public IssueTemplateOffscreenSlider(ICheck check) + : base(check, IssueType.Problem, "This slider goes offscreen here on a 4:3 aspect ratio.") { } diff --git a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs index b0c1d6eb4b..463b596120 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs @@ -40,8 +40,8 @@ namespace osu.Game.Rulesets.Edit.Checks private class IssueTemplateNoneSet : IssueTemplate { - public IssueTemplateNoneSet(ICheck checkOrigin) - : base(checkOrigin, IssueType.Problem, "No background has been set") + public IssueTemplateNoneSet(ICheck check) + : base(check, IssueType.Problem, "No background has been set") { } @@ -50,8 +50,8 @@ namespace osu.Game.Rulesets.Edit.Checks private class IssueTemplateDoesNotExist : IssueTemplate { - public IssueTemplateDoesNotExist(ICheck checkOrigin) - : base(checkOrigin, IssueType.Problem, "The background file \"{0}\" does not exist.") + public IssueTemplateDoesNotExist(ICheck check) + : base(check, IssueType.Problem, "The background file \"{0}\" does not exist.") { } From d9e3276d0edb2f163efd5dcd5894a2881b89ab78 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Mon, 12 Apr 2021 19:18:22 +0200 Subject: [PATCH 062/150] Don't update path type once immediately --- .../Blueprints/Sliders/Components/PathControlPointPiece.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs index 6b78cff33e..7686043c43 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -58,12 +58,13 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components { this.slider = slider; ControlPoint = controlPoint; + PointsInSegment = slider.Path.PointsInSegment(ControlPoint); slider.Path.Version.BindValueChanged(_ => { PointsInSegment = slider.Path.PointsInSegment(ControlPoint); updatePathType(); - }, runOnceImmediately: true); + }); controlPoint.Type.BindValueChanged(_ => updateMarkerDisplay()); From d8088777ea0a1177fb3e205530fdab7e04165458 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Tue, 13 Apr 2021 01:21:34 +0200 Subject: [PATCH 063/150] Add `Equals` method to `IssueTemplate` This will be useful in tests. --- osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs index 97df79ecd8..e21f14f4bc 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs @@ -70,5 +70,7 @@ namespace osu.Game.Rulesets.Edit.Checks.Components } } } + + public bool Equals(IssueTemplate other) => other.Type == Type && other.UnformattedMessage == UnformattedMessage; } } From 47cf4bcf2595c672c0f70a85da4c55a36beea182 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Tue, 13 Apr 2021 01:22:24 +0200 Subject: [PATCH 064/150] Add `CheckBackground` tests --- .../Editing/Checks/CheckBackgroundTest.cs | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 osu.Game.Tests/Editing/Checks/CheckBackgroundTest.cs diff --git a/osu.Game.Tests/Editing/Checks/CheckBackgroundTest.cs b/osu.Game.Tests/Editing/Checks/CheckBackgroundTest.cs new file mode 100644 index 0000000000..54d1a116c7 --- /dev/null +++ b/osu.Game.Tests/Editing/Checks/CheckBackgroundTest.cs @@ -0,0 +1,73 @@ +// 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 NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Edit.Checks; +using osu.Game.Rulesets.Objects; + +namespace osu.Game.Tests.Editing.Checks +{ + [TestFixture] + public class CheckBackgroundTest + { + private CheckBackground check; + private IBeatmap beatmap; + + [SetUp] + public void Setup() + { + check = new CheckBackground(); + beatmap = new Beatmap + { + BeatmapInfo = new BeatmapInfo + { + Metadata = new BeatmapMetadata { BackgroundFile = "abc123.jpg" }, + BeatmapSet = new BeatmapSetInfo + { + Files = new List(new [] + { + new BeatmapSetFileInfo { Filename = "abc123.jpg" } + }) + } + } + }; + } + + [Test] + public void TestBackgroundSetAndInFiles() + { + var issues = check.Run(beatmap); + + Assert.That(!issues.Any()); + } + + [Test] + public void TestBackgroundSetAndNotInFiles() + { + beatmap.BeatmapInfo.BeatmapSet.Files.Clear(); + + var issues = check.Run(beatmap).ToList(); + var issue = issues.FirstOrDefault(); + + Assert.That(issues.Count == 1); + Assert.That(issue != null); + Assert.That(issue.Template.Equals(check.PossibleTemplates.ElementAt(1))); + } + + [Test] + public void TestBackgroundNotSet() + { + beatmap.Metadata.BackgroundFile = null; + + var issues = check.Run(beatmap).ToList(); + var issue = issues.FirstOrDefault(); + + Assert.That(issues.Count == 1); + Assert.That(issue != null); + Assert.That(issue.Template.Equals(check.PossibleTemplates.ElementAt(0))); + } + } +} From 8a6dfcfae1cb9c8b50e04777648098ef271a8d56 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Tue, 13 Apr 2021 01:22:36 +0200 Subject: [PATCH 065/150] Add `CheckOffscreenObjects` tests --- .../Checks/CheckOffscreenObjectsTest.cs | 263 ++++++++++++++++++ 1 file changed, 263 insertions(+) create mode 100644 osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs new file mode 100644 index 0000000000..fa7854765f --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs @@ -0,0 +1,263 @@ +// 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 NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Osu.Edit.Checks; +using osu.Game.Rulesets.Osu.Objects; +using osuTK; + +namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks +{ + [TestFixture] + public class CheckOffscreenObjectsTest + { + private CheckOffscreenObjects check; + + [SetUp] + public void Setup() + { + check = new CheckOffscreenObjects(); + } + + [Test] + public void TestCircleInCenter() + { + var beatmap = new Beatmap + { + HitObjects = new List + { + new HitCircle + { + StartTime = 3000, + Position = new Vector2(320, 240) // Playfield is 640 x 480. + } + } + }; + + var issues = check.Run(beatmap); + + Assert.That(!issues.Any()); + } + + [Test] + public void TestCircleNearEdge() + { + var beatmap = new Beatmap + { + HitObjects = new List + { + new HitCircle + { + StartTime = 3000, + Position = new Vector2(5, 5) + } + } + }; + + var issues = check.Run(beatmap); + + Assert.That(!issues.Any()); + } + + [Test] + public void TestCircleNearEdgeStackedOffscreen() + { + var beatmap = new Beatmap + { + HitObjects = new List + { + new HitCircle + { + StartTime = 3000, + Position = new Vector2(5, 5), + StackHeight = 5 + } + } + }; + + assertOffscreenCircle(beatmap); + } + + [Test] + public void TestCircleOffscreen() + { + var beatmap = new Beatmap + { + HitObjects = new List + { + new HitCircle + { + StartTime = 3000, + Position = new Vector2(0, 0) + } + } + }; + + assertOffscreenCircle(beatmap); + } + + [Test] + public void TestSliderInCenter() + { + var beatmap = new Beatmap + { + HitObjects = new List + { + new Slider + { + StartTime = 3000, + Position = new Vector2(420, 240), + Path = new SliderPath(new[] + { + new PathControlPoint(new Vector2(0, 0), PathType.Linear), + new PathControlPoint(new Vector2(-100, 0)) + }), + } + } + }; + + var issues = check.Run(beatmap); + + Assert.That(!issues.Any()); + } + + [Test] + public void TestSliderNearEdge() + { + var beatmap = new Beatmap + { + HitObjects = new List + { + new Slider + { + StartTime = 3000, + Position = new Vector2(320, 240), + Path = new SliderPath(new[] + { + new PathControlPoint(new Vector2(0, 0), PathType.Linear), + new PathControlPoint(new Vector2(0, -235)) + }), + } + } + }; + + var issues = check.Run(beatmap); + + Assert.That(!issues.Any()); + } + + [Test] + public void TestSliderNearEdgeStackedOffscreen() + { + var beatmap = new Beatmap + { + HitObjects = new List + { + new Slider + { + StartTime = 3000, + Position = new Vector2(320, 240), + Path = new SliderPath(new[] + { + new PathControlPoint(new Vector2(0, 0), PathType.Linear), + new PathControlPoint(new Vector2(0, -235)) + }), + StackHeight = 5 + } + } + }; + + assertOffscreenSlider(beatmap); + } + + [Test] + public void TestSliderOffscreenStart() + { + var beatmap = new Beatmap + { + HitObjects = new List + { + new Slider + { + StartTime = 3000, + Position = new Vector2(0, 0), + Path = new SliderPath(new[] + { + new PathControlPoint(new Vector2(0, 0), PathType.Linear), + new PathControlPoint(new Vector2(320, 240)) + }), + } + } + }; + + assertOffscreenSlider(beatmap); + } + + [Test] + public void TestSliderOffscreenEnd() + { + var beatmap = new Beatmap + { + HitObjects = new List + { + new Slider + { + StartTime = 3000, + Position = new Vector2(320, 240), + Path = new SliderPath(new[] + { + new PathControlPoint(new Vector2(0, 0), PathType.Linear), + new PathControlPoint(new Vector2(-320, -240)) + }), + } + } + }; + + assertOffscreenSlider(beatmap); + } + + [Test] + public void TestSliderOffscreenPath() + { + var beatmap = new Beatmap + { + HitObjects = new List + { + new Slider + { + StartTime = 3000, + Position = new Vector2(320, 240), + Path = new SliderPath(new[] + { + // Circular arc shoots over the top of the screen. + new PathControlPoint(new Vector2(0, 0), PathType.PerfectCurve), + new PathControlPoint(new Vector2(-100, -200)), + new PathControlPoint(new Vector2(100, -200)) + }), + } + } + }; + + assertOffscreenSlider(beatmap); + } + + private void assertOneIssue(IBeatmap beatmap, int templateIndex) + { + var issues = check.Run(beatmap).ToList(); + var issue = issues.FirstOrDefault(); + + Assert.That(issues.Count == 1); + Assert.That(issue != null); + Assert.That(issue.Template.Equals(check.PossibleTemplates.ElementAt(templateIndex))); + } + + private void assertOffscreenCircle(IBeatmap beatmap) => assertOneIssue(beatmap, 0); + + private void assertOffscreenSlider(IBeatmap beatmap) => assertOneIssue(beatmap, 1); + } +} From 0bcc39bd36a9a5893f57fcaa8b94f9d3e31356e9 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Tue, 13 Apr 2021 02:17:35 +0200 Subject: [PATCH 066/150] Remove redundant space --- osu.Game.Tests/Editing/Checks/CheckBackgroundTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Editing/Checks/CheckBackgroundTest.cs b/osu.Game.Tests/Editing/Checks/CheckBackgroundTest.cs index 54d1a116c7..2f309afb6c 100644 --- a/osu.Game.Tests/Editing/Checks/CheckBackgroundTest.cs +++ b/osu.Game.Tests/Editing/Checks/CheckBackgroundTest.cs @@ -27,7 +27,7 @@ namespace osu.Game.Tests.Editing.Checks Metadata = new BeatmapMetadata { BackgroundFile = "abc123.jpg" }, BeatmapSet = new BeatmapSetInfo { - Files = new List(new [] + Files = new List(new[] { new BeatmapSetFileInfo { Filename = "abc123.jpg" } }) From 6d3f9fa9cefdd776d937e0c6b809df8624457e50 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Tue, 13 Apr 2021 02:29:25 +0200 Subject: [PATCH 067/150] Use `is` class instead of `Equals` with template index Ensures ordering of `PossibleTemplates` does not affect tests. --- .../Editor/Checks/CheckOffscreenObjectsTest.cs | 14 ++++++++++---- .../Edit/Checks/CheckOffscreenObjects.cs | 4 ++-- .../Editing/Checks/CheckBackgroundTest.cs | 4 ++-- osu.Game/Rulesets/Edit/Checks/CheckBackground.cs | 4 ++-- .../Edit/Checks/Components/IssueTemplate.cs | 2 -- 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs index fa7854765f..33b839650f 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs @@ -246,18 +246,24 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks assertOffscreenSlider(beatmap); } - private void assertOneIssue(IBeatmap beatmap, int templateIndex) + private void assertOffscreenCircle(IBeatmap beatmap) { var issues = check.Run(beatmap).ToList(); var issue = issues.FirstOrDefault(); Assert.That(issues.Count == 1); Assert.That(issue != null); - Assert.That(issue.Template.Equals(check.PossibleTemplates.ElementAt(templateIndex))); + Assert.That(issue.Template is CheckOffscreenObjects.IssueTemplateOffscreenCircle); } - private void assertOffscreenCircle(IBeatmap beatmap) => assertOneIssue(beatmap, 0); + private void assertOffscreenSlider(IBeatmap beatmap) + { + var issues = check.Run(beatmap).ToList(); + var issue = issues.FirstOrDefault(); - private void assertOffscreenSlider(IBeatmap beatmap) => assertOneIssue(beatmap, 1); + Assert.That(issues.Count == 1); + Assert.That(issue != null); + Assert.That(issue.Template is CheckOffscreenObjects.IssueTemplateOffscreenSlider); + } } } diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs index 0a682c4a83..c241db7e06 100644 --- a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs @@ -91,7 +91,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks position.Y - radius < min_y || position.Y + radius > max_y; } - private class IssueTemplateOffscreenCircle : IssueTemplate + public class IssueTemplateOffscreenCircle : IssueTemplate { public IssueTemplateOffscreenCircle(ICheck check) : base(check, IssueType.Problem, "This circle goes offscreen on a 4:3 aspect ratio.") @@ -101,7 +101,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks public Issue Create(HitCircle circle) => new Issue(circle, this); } - private class IssueTemplateOffscreenSlider : IssueTemplate + public class IssueTemplateOffscreenSlider : IssueTemplate { public IssueTemplateOffscreenSlider(ICheck check) : base(check, IssueType.Problem, "This slider goes offscreen here on a 4:3 aspect ratio.") diff --git a/osu.Game.Tests/Editing/Checks/CheckBackgroundTest.cs b/osu.Game.Tests/Editing/Checks/CheckBackgroundTest.cs index 2f309afb6c..327abcab6c 100644 --- a/osu.Game.Tests/Editing/Checks/CheckBackgroundTest.cs +++ b/osu.Game.Tests/Editing/Checks/CheckBackgroundTest.cs @@ -54,7 +54,7 @@ namespace osu.Game.Tests.Editing.Checks Assert.That(issues.Count == 1); Assert.That(issue != null); - Assert.That(issue.Template.Equals(check.PossibleTemplates.ElementAt(1))); + Assert.That(issue.Template is CheckBackground.IssueTemplateDoesNotExist); } [Test] @@ -67,7 +67,7 @@ namespace osu.Game.Tests.Editing.Checks Assert.That(issues.Count == 1); Assert.That(issue != null); - Assert.That(issue.Template.Equals(check.PossibleTemplates.ElementAt(0))); + Assert.That(issue.Template is CheckBackground.IssueTemplateNoneSet); } } } diff --git a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs index 463b596120..4d5069f446 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Edit.Checks yield return new IssueTemplateDoesNotExist(this).Create(beatmap.Metadata.BackgroundFile); } - private class IssueTemplateNoneSet : IssueTemplate + public class IssueTemplateNoneSet : IssueTemplate { public IssueTemplateNoneSet(ICheck check) : base(check, IssueType.Problem, "No background has been set") @@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Edit.Checks public Issue Create() => new Issue(this); } - private class IssueTemplateDoesNotExist : IssueTemplate + public class IssueTemplateDoesNotExist : IssueTemplate { public IssueTemplateDoesNotExist(ICheck check) : base(check, IssueType.Problem, "The background file \"{0}\" does not exist.") diff --git a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs index e21f14f4bc..97df79ecd8 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs @@ -70,7 +70,5 @@ namespace osu.Game.Rulesets.Edit.Checks.Components } } } - - public bool Equals(IssueTemplate other) => other.Type == Type && other.UnformattedMessage == UnformattedMessage; } } From 66e74da2b74a10ed695b073bf1a92c5c1b7b7d32 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 13:03:14 +0900 Subject: [PATCH 068/150] Fix regression in quick delete mouse action blocking --- .../Visual/Editing/TestSceneEditorQuickDelete.cs | 3 ++- .../Screens/Edit/Compose/Components/BlueprintContainer.cs | 8 ++++---- .../Screens/Edit/Compose/Components/SelectionHandler.cs | 6 +++--- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorQuickDelete.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorQuickDelete.cs index 8a0f27b851..25e12b7a88 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorQuickDelete.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorQuickDelete.cs @@ -83,8 +83,9 @@ namespace osu.Game.Tests.Visual.Editing AddStep("right click", () => InputManager.Click(MouseButton.Right)); AddAssert("slider has 2 points", () => slider.Path.ControlPoints.Count == 2); - // second click should nuke the object completely. AddStep("right click", () => InputManager.Click(MouseButton.Right)); + + // second click should nuke the object completely. AddAssert("no hitobjects in beatmap", () => EditorBeatmap.HitObjects.Count == 0); AddStep("release shift", () => InputManager.ReleaseKey(Key.ShiftLeft)); diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 64cf0e7512..b5a28dc022 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -135,7 +135,7 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override bool OnMouseDown(MouseDownEvent e) { - bool selectionPerformed = beginClickSelection(e); + bool selectionPerformed = performMouseDownActions(e); // even if a selection didn't occur, a drag event may still move the selection. prepareSelectionMovement(); @@ -343,7 +343,7 @@ namespace osu.Game.Screens.Edit.Compose.Components /// /// The input event that triggered this selection. /// Whether a selection was performed. - private bool beginClickSelection(MouseButtonEvent e) + private bool performMouseDownActions(MouseButtonEvent e) { // Iterate from the top of the input stack (blueprints closest to the front of the screen first). // Priority is given to already-selected blueprints. @@ -351,7 +351,7 @@ namespace osu.Game.Screens.Edit.Compose.Components { if (!blueprint.IsHovered) continue; - return clickSelectionBegan = SelectionHandler.HandleSelectionRequested(blueprint, e); + return clickSelectionBegan = SelectionHandler.MouseDownSelectionRequested(blueprint, e); } return false; @@ -375,7 +375,7 @@ namespace osu.Game.Screens.Edit.Compose.Components { if (!blueprint.IsHovered) continue; - return clickSelectionBegan = SelectionHandler.HandleDeselectionRequested(blueprint, e); + return clickSelectionBegan = SelectionHandler.MouseUpSelectionRequested(blueprint, e); } } diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index e5e1100797..389ef78ed5 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -220,12 +220,12 @@ namespace osu.Game.Screens.Edit.Compose.Components /// The blueprint. /// The mouse event responsible for selection. /// Whether a selection was performed. - internal bool HandleSelectionRequested(SelectionBlueprint blueprint, MouseButtonEvent e) + internal bool MouseDownSelectionRequested(SelectionBlueprint blueprint, MouseButtonEvent e) { if (e.ShiftPressed && e.Button == MouseButton.Right) { handleQuickDeletion(blueprint); - return false; + return true; } // while holding control, we only want to add to selection, not replace an existing selection. @@ -244,7 +244,7 @@ namespace osu.Game.Screens.Edit.Compose.Components /// The blueprint. /// The mouse event responsible for deselection. /// Whether a deselection was performed. - internal bool HandleDeselectionRequested(SelectionBlueprint blueprint, MouseButtonEvent e) + internal bool MouseUpSelectionRequested(SelectionBlueprint blueprint, MouseButtonEvent e) { if (blueprint.IsSelected) { From 05d7fe289f3e6a48b855edc629e163fe7ea26fff Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 13:09:18 +0900 Subject: [PATCH 069/150] Rename test scene in preparation for increasing scope --- ...estSceneEditorQuickDelete.cs => TestSceneEditorSelection.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename osu.Game.Tests/Visual/Editing/{TestSceneEditorQuickDelete.cs => TestSceneEditorSelection.cs} (98%) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorQuickDelete.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorSelection.cs similarity index 98% rename from osu.Game.Tests/Visual/Editing/TestSceneEditorQuickDelete.cs rename to osu.Game.Tests/Visual/Editing/TestSceneEditorSelection.cs index 25e12b7a88..36c4357432 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorQuickDelete.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorSelection.cs @@ -18,7 +18,7 @@ using osuTK.Input; namespace osu.Game.Tests.Visual.Editing { - public class TestSceneEditorQuickDelete : EditorTestScene + public class TestSceneEditorSelection : EditorTestScene { protected override Ruleset CreateEditorRuleset() => new OsuRuleset(); From 7c975359d9b6b06a797a7121c38700e09c2f08b8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 13:29:37 +0900 Subject: [PATCH 070/150] Add basic select/deselect tests --- .../Editing/TestSceneEditorSelection.cs | 98 ++++++++++++++++++- 1 file changed, 93 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorSelection.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorSelection.cs index 36c4357432..4f386972fa 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorSelection.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorSelection.cs @@ -27,6 +27,97 @@ namespace osu.Game.Tests.Visual.Editing private BlueprintContainer blueprintContainer => Editor.ChildrenOfType().First(); + private void moveMouseToObject(HitObject obj) + { + AddStep("move mouse to object", () => + { + var pos = blueprintContainer.SelectionBlueprints + .First(s => s.HitObject == obj) + .ChildrenOfType() + .First().ScreenSpaceDrawQuad.Centre; + + InputManager.MoveMouseTo(pos); + }); + } + + [Test] + public void TestBasicSelect() + { + var addedObject = new HitCircle { StartTime = 100 }; + AddStep("add hitobject", () => EditorBeatmap.Add(addedObject)); + + moveMouseToObject(addedObject); + AddStep("left click", () => InputManager.Click(MouseButton.Left)); + + AddAssert("hitobject selected", () => EditorBeatmap.SelectedHitObjects.Single() == addedObject); + + var addedObject2 = new HitCircle + { + StartTime = 100, + Position = new Vector2(100), + }; + + AddStep("add one more hitobject", () => EditorBeatmap.Add(addedObject2)); + AddAssert("selection unchanged", () => EditorBeatmap.SelectedHitObjects.Single() == addedObject); + + moveMouseToObject(addedObject2); + AddStep("left click", () => InputManager.Click(MouseButton.Left)); + AddAssert("hitobject selected", () => EditorBeatmap.SelectedHitObjects.Single() == addedObject2); + } + + [Test] + public void TestMultiSelect() + { + var addedObjects = new[] + { + new HitCircle { StartTime = 100 }, + new HitCircle { StartTime = 200, Position = new Vector2(50) }, + new HitCircle { StartTime = 300, Position = new Vector2(100) }, + new HitCircle { StartTime = 400, Position = new Vector2(150) }, + }; + + AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects)); + + moveMouseToObject(addedObjects[0]); + AddStep("click first", () => InputManager.Click(MouseButton.Left)); + + AddAssert("hitobject selected", () => EditorBeatmap.SelectedHitObjects.Single() == addedObjects[0]); + + AddStep("hold control", () => InputManager.PressKey(Key.ControlLeft)); + + moveMouseToObject(addedObjects[1]); + AddStep("click second", () => InputManager.Click(MouseButton.Left)); + AddAssert("2 hitobjects selected", () => EditorBeatmap.SelectedHitObjects.Count == 2 && EditorBeatmap.SelectedHitObjects.Contains(addedObjects[1])); + + moveMouseToObject(addedObjects[2]); + AddStep("click third", () => InputManager.Click(MouseButton.Left)); + AddAssert("3 hitobjects selected", () => EditorBeatmap.SelectedHitObjects.Count == 3 && EditorBeatmap.SelectedHitObjects.Contains(addedObjects[2])); + + moveMouseToObject(addedObjects[1]); + AddStep("click second", () => InputManager.Click(MouseButton.Left)); + AddAssert("2 hitobjects selected", () => EditorBeatmap.SelectedHitObjects.Count == 2 && !EditorBeatmap.SelectedHitObjects.Contains(addedObjects[1])); + } + + [Test] + public void TestBasicDeselect() + { + var addedObject = new HitCircle { StartTime = 100 }; + AddStep("add hitobject", () => EditorBeatmap.Add(addedObject)); + + moveMouseToObject(addedObject); + AddStep("left click", () => InputManager.Click(MouseButton.Left)); + + AddAssert("hitobject selected", () => EditorBeatmap.SelectedHitObjects.Single() == addedObject); + + AddStep("click away", () => + { + InputManager.MoveMouseTo(blueprintContainer.ScreenSpaceDrawQuad.Centre); + InputManager.Click(MouseButton.Left); + }); + + AddAssert("selection lost", () => EditorBeatmap.SelectedHitObjects.Count == 0); + } + [Test] public void TestQuickDeleteRemovesObject() { @@ -36,11 +127,8 @@ namespace osu.Game.Tests.Visual.Editing AddStep("select added object", () => EditorBeatmap.SelectedHitObjects.Add(addedObject)); - AddStep("move mouse to object", () => - { - var pos = blueprintContainer.ChildrenOfType().First().ScreenSpaceDrawQuad.Centre; - InputManager.MoveMouseTo(pos); - }); + moveMouseToObject(addedObject); + AddStep("hold shift", () => InputManager.PressKey(Key.ShiftLeft)); AddStep("right click", () => InputManager.Click(MouseButton.Right)); AddStep("release shift", () => InputManager.ReleaseKey(Key.ShiftLeft)); From 516bd138e32ba1bd3e4ae35a150d4ae1e9b0d8ab Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 13:46:38 +0900 Subject: [PATCH 071/150] Add (previously failing) test coverage of drag from selection --- .../Editing/TestSceneEditorSelection.cs | 57 +++++++++++++++---- 1 file changed, 47 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorSelection.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorSelection.cs index 4f386972fa..99f31b0c2a 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorSelection.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorSelection.cs @@ -1,6 +1,7 @@ // 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.Linq; using NUnit.Framework; using osu.Framework.Testing; @@ -27,12 +28,12 @@ namespace osu.Game.Tests.Visual.Editing private BlueprintContainer blueprintContainer => Editor.ChildrenOfType().First(); - private void moveMouseToObject(HitObject obj) + private void moveMouseToObject(Func targetFunc) { AddStep("move mouse to object", () => { var pos = blueprintContainer.SelectionBlueprints - .First(s => s.HitObject == obj) + .First(s => s.HitObject == targetFunc()) .ChildrenOfType() .First().ScreenSpaceDrawQuad.Centre; @@ -46,7 +47,7 @@ namespace osu.Game.Tests.Visual.Editing var addedObject = new HitCircle { StartTime = 100 }; AddStep("add hitobject", () => EditorBeatmap.Add(addedObject)); - moveMouseToObject(addedObject); + moveMouseToObject(() => addedObject); AddStep("left click", () => InputManager.Click(MouseButton.Left)); AddAssert("hitobject selected", () => EditorBeatmap.SelectedHitObjects.Single() == addedObject); @@ -60,7 +61,7 @@ namespace osu.Game.Tests.Visual.Editing AddStep("add one more hitobject", () => EditorBeatmap.Add(addedObject2)); AddAssert("selection unchanged", () => EditorBeatmap.SelectedHitObjects.Single() == addedObject); - moveMouseToObject(addedObject2); + moveMouseToObject(() => addedObject2); AddStep("left click", () => InputManager.Click(MouseButton.Left)); AddAssert("hitobject selected", () => EditorBeatmap.SelectedHitObjects.Single() == addedObject2); } @@ -78,33 +79,69 @@ namespace osu.Game.Tests.Visual.Editing AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects)); - moveMouseToObject(addedObjects[0]); + moveMouseToObject(() => addedObjects[0]); AddStep("click first", () => InputManager.Click(MouseButton.Left)); AddAssert("hitobject selected", () => EditorBeatmap.SelectedHitObjects.Single() == addedObjects[0]); AddStep("hold control", () => InputManager.PressKey(Key.ControlLeft)); - moveMouseToObject(addedObjects[1]); + moveMouseToObject(() => addedObjects[1]); AddStep("click second", () => InputManager.Click(MouseButton.Left)); AddAssert("2 hitobjects selected", () => EditorBeatmap.SelectedHitObjects.Count == 2 && EditorBeatmap.SelectedHitObjects.Contains(addedObjects[1])); - moveMouseToObject(addedObjects[2]); + moveMouseToObject(() => addedObjects[2]); AddStep("click third", () => InputManager.Click(MouseButton.Left)); AddAssert("3 hitobjects selected", () => EditorBeatmap.SelectedHitObjects.Count == 3 && EditorBeatmap.SelectedHitObjects.Contains(addedObjects[2])); - moveMouseToObject(addedObjects[1]); + moveMouseToObject(() => addedObjects[1]); AddStep("click second", () => InputManager.Click(MouseButton.Left)); AddAssert("2 hitobjects selected", () => EditorBeatmap.SelectedHitObjects.Count == 2 && !EditorBeatmap.SelectedHitObjects.Contains(addedObjects[1])); } + [TestCase(false)] + [TestCase(true)] + public void TestMultiSelectFromDrag(bool alreadySelectedBeforeDrag) + { + HitCircle[] addedObjects = null; + + AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects = new[] + { + new HitCircle { StartTime = 100 }, + new HitCircle { StartTime = 200, Position = new Vector2(50) }, + new HitCircle { StartTime = 300, Position = new Vector2(100) }, + new HitCircle { StartTime = 400, Position = new Vector2(150) }, + })); + + moveMouseToObject(() => addedObjects[0]); + AddStep("click first", () => InputManager.Click(MouseButton.Left)); + + AddStep("hold control", () => InputManager.PressKey(Key.ControlLeft)); + + moveMouseToObject(() => addedObjects[1]); + + if (alreadySelectedBeforeDrag) + AddStep("click second", () => InputManager.Click(MouseButton.Left)); + + AddStep("mouse down on second", () => InputManager.PressButton(MouseButton.Left)); + + AddAssert("2 hitobjects selected", () => EditorBeatmap.SelectedHitObjects.Count == 2 && EditorBeatmap.SelectedHitObjects.Contains(addedObjects[1])); + + AddStep("drag to centre", () => InputManager.MoveMouseTo(blueprintContainer.ScreenSpaceDrawQuad.Centre)); + + AddAssert("positions changed", () => addedObjects[0].Position != Vector2.Zero && addedObjects[1].Position != new Vector2(50)); + + AddStep("release control", () => InputManager.ReleaseKey(Key.ControlLeft)); + AddStep("mouse up", () => InputManager.ReleaseButton(MouseButton.Left)); + } + [Test] public void TestBasicDeselect() { var addedObject = new HitCircle { StartTime = 100 }; AddStep("add hitobject", () => EditorBeatmap.Add(addedObject)); - moveMouseToObject(addedObject); + moveMouseToObject(() => addedObject); AddStep("left click", () => InputManager.Click(MouseButton.Left)); AddAssert("hitobject selected", () => EditorBeatmap.SelectedHitObjects.Single() == addedObject); @@ -127,7 +164,7 @@ namespace osu.Game.Tests.Visual.Editing AddStep("select added object", () => EditorBeatmap.SelectedHitObjects.Add(addedObject)); - moveMouseToObject(addedObject); + moveMouseToObject(() => addedObject); AddStep("hold shift", () => InputManager.PressKey(Key.ShiftLeft)); AddStep("right click", () => InputManager.Click(MouseButton.Right)); From d0f30b7b422afe385fd1fc0de7a28684bf0ab7ef Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 13 Apr 2021 14:29:47 +0900 Subject: [PATCH 072/150] Delay map completion one frame after the last judgment This is a workaround of a timing issue. KeyCounter is disabled while break time (`HasCompleted == true`). When the last keypress is exactly at the same time the map ends, the last frame was considered in a break time while forward play but considered not in a break time while rewinding. This inconsistency made the last keypress not decremented in the key counter when a replay is rewound. The situation regularly happens in osu!standard because the map ends right after the player hits the last hit circle. It was caught by `TestSceneGameplayRewinding`. This commit makes the update of the map completion delayed one frame. The problematic keypress frame is now processed strictly before the map completion, and the map completion status is correctly rewound before the keypress frame. --- osu.Game/Rulesets/Scoring/JudgementProcessor.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/JudgementProcessor.cs b/osu.Game/Rulesets/Scoring/JudgementProcessor.cs index 8aef615b5f..201a05e569 100644 --- a/osu.Game/Rulesets/Scoring/JudgementProcessor.cs +++ b/osu.Game/Rulesets/Scoring/JudgementProcessor.cs @@ -28,6 +28,8 @@ namespace osu.Game.Rulesets.Scoring /// public int JudgedHits { get; private set; } + private JudgementResult lastAppliedResult; + private readonly BindableBool hasCompleted = new BindableBool(); /// @@ -53,12 +55,11 @@ namespace osu.Game.Rulesets.Scoring public void ApplyResult(JudgementResult result) { JudgedHits++; + lastAppliedResult = result; ApplyResultInternal(result); NewJudgement?.Invoke(result); - - updateHasCompleted(); } /// @@ -69,8 +70,6 @@ namespace osu.Game.Rulesets.Scoring { JudgedHits--; - updateHasCompleted(); - RevertResultInternal(result); } @@ -134,6 +133,10 @@ namespace osu.Game.Rulesets.Scoring } } - private void updateHasCompleted() => hasCompleted.Value = JudgedHits == MaxHits; + protected override void Update() + { + base.Update(); + hasCompleted.Value = JudgedHits == MaxHits && (JudgedHits == 0 || lastAppliedResult.TimeAbsolute < Clock.CurrentTime); + } } } From 4837cef095558c2ed301881471f3eaf76d6eb209 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 14:44:52 +0900 Subject: [PATCH 073/150] Use static for playfield centre positioning --- .../Checks/CheckOffscreenObjectsTest.cs | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs index 33b839650f..4a5e4e18c0 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs @@ -9,6 +9,7 @@ using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Edit.Checks; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.UI; using osuTK; namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks @@ -16,6 +17,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks [TestFixture] public class CheckOffscreenObjectsTest { + private static readonly Vector2 playfield_centre = OsuPlayfield.BASE_SIZE * 0.5f; + private CheckOffscreenObjects check; [SetUp] @@ -34,7 +37,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks new HitCircle { StartTime = 3000, - Position = new Vector2(320, 240) // Playfield is 640 x 480. + Position = playfield_centre // Playfield is 640 x 480. } } }; @@ -136,11 +139,11 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks new Slider { StartTime = 3000, - Position = new Vector2(320, 240), + Position = playfield_centre, Path = new SliderPath(new[] { new PathControlPoint(new Vector2(0, 0), PathType.Linear), - new PathControlPoint(new Vector2(0, -235)) + new PathControlPoint(new Vector2(0, -playfield_centre.Y + 5)) }), } } @@ -161,11 +164,11 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks new Slider { StartTime = 3000, - Position = new Vector2(320, 240), + Position = playfield_centre, Path = new SliderPath(new[] { new PathControlPoint(new Vector2(0, 0), PathType.Linear), - new PathControlPoint(new Vector2(0, -235)) + new PathControlPoint(new Vector2(0, -playfield_centre.Y + 5)) }), StackHeight = 5 } @@ -189,7 +192,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks Path = new SliderPath(new[] { new PathControlPoint(new Vector2(0, 0), PathType.Linear), - new PathControlPoint(new Vector2(320, 240)) + new PathControlPoint(playfield_centre) }), } } @@ -208,11 +211,11 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks new Slider { StartTime = 3000, - Position = new Vector2(320, 240), + Position = playfield_centre, Path = new SliderPath(new[] { new PathControlPoint(new Vector2(0, 0), PathType.Linear), - new PathControlPoint(new Vector2(-320, -240)) + new PathControlPoint(-playfield_centre) }), } } @@ -231,7 +234,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks new Slider { StartTime = 3000, - Position = new Vector2(320, 240), + Position = playfield_centre, Path = new SliderPath(new[] { // Circular arc shoots over the top of the screen. From b45d7de4eccbd9aecbf621afefc6661ca986fa9e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 15:04:01 +0900 Subject: [PATCH 074/150] Update asserts to use better nunit specifications --- .../Checks/CheckOffscreenObjectsTest.cs | 29 ++++++------------- .../Editing/Checks/CheckBackgroundTest.cs | 16 ++++------ 2 files changed, 14 insertions(+), 31 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs index 4a5e4e18c0..9f9ba0e8db 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Game.Beatmaps; +using osu.Game.Rulesets.Edit.Checks.Components; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Edit.Checks; @@ -42,9 +43,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks } }; - var issues = check.Run(beatmap); - - Assert.That(!issues.Any()); + Assert.That(check.Run(beatmap), Is.Empty); } [Test] @@ -62,9 +61,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks } }; - var issues = check.Run(beatmap); - - Assert.That(!issues.Any()); + Assert.That(check.Run(beatmap), Is.Empty); } [Test] @@ -124,9 +121,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks } }; - var issues = check.Run(beatmap); - - Assert.That(!issues.Any()); + Assert.That(check.Run(beatmap), Is.Empty); } [Test] @@ -149,9 +144,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks } }; - var issues = check.Run(beatmap); - - Assert.That(!issues.Any()); + Assert.That(check.Run(beatmap), Is.Empty); } [Test] @@ -252,21 +245,17 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks private void assertOffscreenCircle(IBeatmap beatmap) { var issues = check.Run(beatmap).ToList(); - var issue = issues.FirstOrDefault(); - Assert.That(issues.Count == 1); - Assert.That(issue != null); - Assert.That(issue.Template is CheckOffscreenObjects.IssueTemplateOffscreenCircle); + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckOffscreenObjects.IssueTemplateOffscreenCircle); } private void assertOffscreenSlider(IBeatmap beatmap) { var issues = check.Run(beatmap).ToList(); - var issue = issues.FirstOrDefault(); - Assert.That(issues.Count == 1); - Assert.That(issue != null); - Assert.That(issue.Template is CheckOffscreenObjects.IssueTemplateOffscreenSlider); + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckOffscreenObjects.IssueTemplateOffscreenSlider); } } } diff --git a/osu.Game.Tests/Editing/Checks/CheckBackgroundTest.cs b/osu.Game.Tests/Editing/Checks/CheckBackgroundTest.cs index 327abcab6c..635e3bb0f3 100644 --- a/osu.Game.Tests/Editing/Checks/CheckBackgroundTest.cs +++ b/osu.Game.Tests/Editing/Checks/CheckBackgroundTest.cs @@ -39,9 +39,7 @@ namespace osu.Game.Tests.Editing.Checks [Test] public void TestBackgroundSetAndInFiles() { - var issues = check.Run(beatmap); - - Assert.That(!issues.Any()); + Assert.That(check.Run(beatmap), Is.Empty); } [Test] @@ -50,11 +48,9 @@ namespace osu.Game.Tests.Editing.Checks beatmap.BeatmapInfo.BeatmapSet.Files.Clear(); var issues = check.Run(beatmap).ToList(); - var issue = issues.FirstOrDefault(); - Assert.That(issues.Count == 1); - Assert.That(issue != null); - Assert.That(issue.Template is CheckBackground.IssueTemplateDoesNotExist); + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckBackground.IssueTemplateDoesNotExist); } [Test] @@ -63,11 +59,9 @@ namespace osu.Game.Tests.Editing.Checks beatmap.Metadata.BackgroundFile = null; var issues = check.Run(beatmap).ToList(); - var issue = issues.FirstOrDefault(); - Assert.That(issues.Count == 1); - Assert.That(issue != null); - Assert.That(issue.Template is CheckBackground.IssueTemplateNoneSet); + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckBackground.IssueTemplateNoneSet); } } } From fbc6fb8fc55ad431173ad61072a84e26629469e7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 15:35:57 +0900 Subject: [PATCH 075/150] Split out common logic into private method and add inline comment for future visitors --- .../Sliders/Components/PathControlPointPiece.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs index 7686043c43..ce9580d0f4 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -58,11 +58,13 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components { this.slider = slider; ControlPoint = controlPoint; - PointsInSegment = slider.Path.PointsInSegment(ControlPoint); + + // we don't want to run the path type update on construction as it may inadvertently change the slider. + cachePoints(slider); slider.Path.Version.BindValueChanged(_ => { - PointsInSegment = slider.Path.PointsInSegment(ControlPoint); + cachePoints(slider); updatePathType(); }); @@ -206,6 +208,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components protected override void OnDragEnd(DragEndEvent e) => changeHandler?.EndChange(); + private void cachePoints(Slider slider) => PointsInSegment = slider.Path.PointsInSegment(ControlPoint); + /// /// Handles correction of invalid path types. /// From 57ba7b7cbbbd93870978d047b6edb957cd49d8cf Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 13 Apr 2021 15:55:23 +0900 Subject: [PATCH 076/150] Partially revert the changes of `CurrentFrame` and `NextFrame` for compatibility Making those always non-null is postponed as when a replay's frame contains keypress the behavior is changed. Previously, the key is pressed at the time of the first frame. But using non-null frames means the key is pressed at negative infinity. However, I think the new way of always using non-null frames makes the client code so I plan to bundle the change to more breaking changes. --- .../NonVisual/FramedReplayInputHandlerTest.cs | 22 +++++++++---------- .../Replays/FramedReplayInputHandler.cs | 9 ++++---- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs index 954871595e..a42b7d54ee 100644 --- a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs +++ b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs @@ -84,11 +84,11 @@ namespace osu.Game.Tests.NonVisual // exited important section setTime(8200, 8000); confirmCurrentFrame(7); - confirmNextFrame(7); + confirmNextFrame(null); setTime(8200, 8200); confirmCurrentFrame(7); - confirmNextFrame(7); + confirmNextFrame(null); } [Test] @@ -97,11 +97,11 @@ namespace osu.Game.Tests.NonVisual setReplayFrames(); setTime(-1000, -1000); - confirmCurrentFrame(0); + confirmCurrentFrame(null); confirmNextFrame(0); setTime(-500, -500); - confirmCurrentFrame(0); + confirmCurrentFrame(null); confirmNextFrame(0); setTime(0, 0); @@ -145,7 +145,7 @@ namespace osu.Game.Tests.NonVisual confirmNextFrame(1); setTime(-500, -500); - confirmCurrentFrame(0); + confirmCurrentFrame(null); confirmNextFrame(0); } @@ -231,7 +231,7 @@ namespace osu.Game.Tests.NonVisual Assert.IsFalse(handler.WaitingForFrame, "Should not be waiting yet"); setTime(1000, 1000); confirmCurrentFrame(1); - confirmNextFrame(1); + confirmNextFrame(null); Assert.IsTrue(handler.WaitingForFrame, "Should be waiting"); // cannot seek beyond the last frame @@ -243,7 +243,7 @@ namespace osu.Game.Tests.NonVisual // can seek to the point before the first frame, however setTime(-100, -100); - confirmCurrentFrame(0); + confirmCurrentFrame(null); confirmNextFrame(0); fastForwardToPoint(1000); @@ -311,14 +311,14 @@ namespace osu.Game.Tests.NonVisual Assert.AreEqual(expect, handler.SetFrameFromTime(set), "Unexpected return value"); } - private void confirmCurrentFrame(int frame) + private void confirmCurrentFrame(int? frame) { - Assert.AreEqual(replay.Frames[frame].Time, handler.CurrentFrame.Time, "Unexpected current frame"); + Assert.AreEqual(frame is int x ? replay.Frames[x].Time : (double?)null, handler.CurrentFrame?.Time, "Unexpected current frame"); } - private void confirmNextFrame(int frame) + private void confirmNextFrame(int? frame) { - Assert.AreEqual(replay.Frames[frame].Time, handler.NextFrame.Time, "Unexpected next frame"); + Assert.AreEqual(frame is int x ? replay.Frames[x].Time : (double?)null, handler.NextFrame?.Time, "Unexpected next frame"); } private class TestReplayFrame : ReplayFrame diff --git a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs index c3cd957f0d..a7f11b1e6f 100644 --- a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs +++ b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs @@ -30,6 +30,7 @@ namespace osu.Game.Rulesets.Replays /// The current frame of the replay. /// The current time is always between the start and the end time of the current frame. /// + /// Returns null if the current time is strictly before the first frame. /// The replay is empty. public TFrame CurrentFrame { @@ -38,15 +39,15 @@ namespace osu.Game.Rulesets.Replays if (!HasFrames) throw new InvalidOperationException($"Attempted to get {nameof(CurrentFrame)} of an empty replay"); - return (TFrame)Frames[Math.Max(0, currentFrameIndex)]; + return currentFrameIndex == -1 ? null : (TFrame)Frames[currentFrameIndex]; } } /// /// The next frame of the replay. /// The start time is always greater or equal to the start time of regardless of the seeking direction. - /// If it is before the first frame of the replay or the after the last frame of the replay, and agree. /// + /// Returns null if the current frame is the last frame. /// The replay is empty. public TFrame NextFrame { @@ -55,7 +56,7 @@ namespace osu.Game.Rulesets.Replays if (!HasFrames) throw new InvalidOperationException($"Attempted to get {nameof(NextFrame)} of an empty replay"); - return (TFrame)Frames[Math.Min(currentFrameIndex + 1, Frames.Count - 1)]; + return currentFrameIndex == Frames.Count - 1 ? null : (TFrame)Frames[currentFrameIndex + 1]; } } @@ -96,7 +97,7 @@ namespace osu.Game.Rulesets.Replays { get { - if (!HasFrames || !FrameAccuratePlayback) + if (!HasFrames || !FrameAccuratePlayback || CurrentFrame == null) return false; return IsImportant(CurrentFrame) && // a button is in a pressed state From 36510309d10105ddcdbea2b45f36e941f6be5415 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 13 Apr 2021 09:24:35 +0300 Subject: [PATCH 077/150] Merge `EnableUserDim` and `IgnoreUserSettings` to one bindable --- .../Visual/Background/TestSceneUserDimBackgrounds.cs | 12 ++++++------ osu.Game/Graphics/Containers/UserDimContainer.cs | 8 +------- osu.Game/Rulesets/Mods/ModCinema.cs | 3 +-- .../Screens/Backgrounds/BackgroundScreenBeatmap.cs | 8 ++++---- osu.Game/Screens/Edit/Editor.cs | 2 +- osu.Game/Screens/Play/Player.cs | 4 ++-- osu.Game/Screens/Play/PlayerLoader.cs | 6 +++--- 7 files changed, 18 insertions(+), 25 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs index ba4d12b19f..a4c28651df 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs @@ -142,9 +142,9 @@ namespace osu.Game.Tests.Visual.Background { performFullSetup(); AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); - AddStep("Enable user dim", () => songSelect.DimEnabled.Value = false); + AddStep("Disable user dim", () => songSelect.IgnoreUserSettings.Value = true); AddUntilStep("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && songSelect.IsUserBlurDisabled()); - AddStep("Disable user dim", () => songSelect.DimEnabled.Value = true); + AddStep("Enable user dim", () => songSelect.IgnoreUserSettings.Value = false); AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); } @@ -161,10 +161,10 @@ namespace osu.Game.Tests.Visual.Background player.ReplacesBackground.Value = true; player.StoryboardEnabled.Value = true; }); - AddStep("Enable user dim", () => player.DimmableStoryboard.EnableUserDim.Value = true); + AddStep("Enable user dim", () => player.DimmableStoryboard.IgnoreUserSettings.Value = false); AddStep("Set dim level to 1", () => songSelect.DimLevel.Value = 1f); AddUntilStep("Storyboard is invisible", () => !player.IsStoryboardVisible); - AddStep("Disable user dim", () => player.DimmableStoryboard.EnableUserDim.Value = false); + AddStep("Disable user dim", () => player.DimmableStoryboard.IgnoreUserSettings.Value = true); AddUntilStep("Storyboard is visible", () => player.IsStoryboardVisible); } @@ -281,11 +281,11 @@ namespace osu.Game.Tests.Visual.Background protected override BackgroundScreen CreateBackground() { background = new FadeAccessibleBackground(Beatmap.Value); - DimEnabled.BindTo(background.EnableUserDim); + IgnoreUserSettings.BindTo(background.IgnoreUserSettings); return background; } - public readonly Bindable DimEnabled = new Bindable(); + public readonly Bindable IgnoreUserSettings = new Bindable(); public readonly Bindable DimLevel = new BindableDouble(); public readonly Bindable BlurLevel = new BindableDouble(); diff --git a/osu.Game/Graphics/Containers/UserDimContainer.cs b/osu.Game/Graphics/Containers/UserDimContainer.cs index 39c1fdad52..4e555ac1eb 100644 --- a/osu.Game/Graphics/Containers/UserDimContainer.cs +++ b/osu.Game/Graphics/Containers/UserDimContainer.cs @@ -23,11 +23,6 @@ namespace osu.Game.Graphics.Containers protected const double BACKGROUND_FADE_DURATION = 800; - /// - /// Whether or not user-configured dim levels should be applied to the container. - /// - public readonly Bindable EnableUserDim = new Bindable(true); - /// /// Whether or not user-configured settings relating to brightness of elements should be ignored /// @@ -57,7 +52,7 @@ namespace osu.Game.Graphics.Containers private float breakLightening => LightenDuringBreaks.Value && IsBreakTime.Value ? BREAK_LIGHTEN_AMOUNT : 0; - protected float DimLevel => Math.Max(EnableUserDim.Value && !IgnoreUserSettings.Value ? (float)UserDimLevel.Value - breakLightening : 0, 0); + protected float DimLevel => Math.Max(!IgnoreUserSettings.Value ? (float)UserDimLevel.Value - breakLightening : 0, 0); protected override Container Content => dimContent; @@ -78,7 +73,6 @@ namespace osu.Game.Graphics.Containers LightenDuringBreaks = config.GetBindable(OsuSetting.LightenDuringBreaks); ShowStoryboard = config.GetBindable(OsuSetting.ShowStoryboard); - EnableUserDim.ValueChanged += _ => UpdateVisuals(); UserDimLevel.ValueChanged += _ => UpdateVisuals(); LightenDuringBreaks.ValueChanged += _ => UpdateVisuals(); IsBreakTime.ValueChanged += _ => UpdateVisuals(); diff --git a/osu.Game/Rulesets/Mods/ModCinema.cs b/osu.Game/Rulesets/Mods/ModCinema.cs index eb0473016a..c78088ba2d 100644 --- a/osu.Game/Rulesets/Mods/ModCinema.cs +++ b/osu.Game/Rulesets/Mods/ModCinema.cs @@ -37,8 +37,7 @@ namespace osu.Game.Rulesets.Mods public void ApplyToPlayer(Player player) { - player.ApplyToBackground(b => b.EnableUserDim.Value = false); - + player.ApplyToBackground(b => b.IgnoreUserSettings.Value = true); player.DimmableStoryboard.IgnoreUserSettings.Value = true; player.BreakOverlay.Hide(); diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs index b08455be95..d27211144e 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs @@ -27,9 +27,9 @@ namespace osu.Game.Screens.Backgrounds private WorkingBeatmap beatmap; /// - /// Whether or not user dim settings should be applied to this Background. + /// Whether or not user-configured settings relating to brightness of elements should be ignored /// - public readonly Bindable EnableUserDim = new Bindable(); + public readonly Bindable IgnoreUserSettings = new Bindable(); public readonly Bindable StoryboardReplacesBackground = new Bindable(); @@ -50,7 +50,7 @@ namespace osu.Game.Screens.Backgrounds InternalChild = dimmable = CreateFadeContainer(); - dimmable.EnableUserDim.BindTo(EnableUserDim); + dimmable.IgnoreUserSettings.BindTo(IgnoreUserSettings); dimmable.IsBreakTime.BindTo(IsBreakTime); dimmable.BlurAmount.BindTo(BlurAmount); @@ -148,7 +148,7 @@ namespace osu.Game.Screens.Backgrounds /// /// As an optimisation, we add the two blur portions to be applied rather than actually applying two separate blurs. /// - private Vector2 blurTarget => EnableUserDim.Value + private Vector2 blurTarget => !IgnoreUserSettings.Value ? new Vector2(BlurAmount.Value + (float)userBlurLevel.Value * USER_BLUR_FACTOR) : new Vector2(BlurAmount.Value); diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 0759e21382..64350fb56e 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -462,7 +462,7 @@ namespace osu.Game.Screens.Edit // todo: temporary. we want to be applying dim using the UserDimContainer eventually. b.FadeColour(Color4.DarkGray, 500); - b.EnableUserDim.Value = false; + b.IgnoreUserSettings.Value = true; b.BlurAmount.Value = 0; }); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index efe5d26409..dd3f58439b 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -764,7 +764,7 @@ namespace osu.Game.Screens.Play ApplyToBackground(b => { - b.EnableUserDim.Value = true; + b.IgnoreUserSettings.Value = false; b.BlurAmount.Value = 0; // bind component bindables. @@ -913,7 +913,7 @@ namespace osu.Game.Screens.Play float fadeOutDuration = instant ? 0 : 250; this.FadeOut(fadeOutDuration); - ApplyToBackground(b => b.EnableUserDim.Value = false); + ApplyToBackground(b => b.IgnoreUserSettings.Value = true); storyboardReplacesBackground.Value = false; } diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 679b3c7313..cf15104809 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -229,7 +229,7 @@ namespace osu.Game.Screens.Play content.ScaleTo(0.7f, 150, Easing.InQuint); this.FadeOut(150); - ApplyToBackground(b => b.EnableUserDim.Value = false); + ApplyToBackground(b => b.IgnoreUserSettings.Value = true); BackgroundBrightnessReduction = false; Beatmap.Value.Track.RemoveAdjustment(AdjustableProperty.Volume, volumeAdjustment); @@ -277,7 +277,7 @@ namespace osu.Game.Screens.Play // Preview user-defined background dim and blur when hovered on the visual settings panel. ApplyToBackground(b => { - b.EnableUserDim.Value = true; + b.IgnoreUserSettings.Value = false; b.BlurAmount.Value = 0; }); @@ -288,7 +288,7 @@ namespace osu.Game.Screens.Play ApplyToBackground(b => { // Returns background dim and blur to the values specified by PlayerLoader. - b.EnableUserDim.Value = false; + b.IgnoreUserSettings.Value = true; b.BlurAmount.Value = BACKGROUND_BLUR; }); From 98c25b2e71c1aef8ad2973c0cb7ce96b33faff42 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Tue, 13 Apr 2021 10:33:08 +0200 Subject: [PATCH 078/150] Remove unused import --- .../Editor/Checks/CheckOffscreenObjectsTest.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs index 9f9ba0e8db..f9445a9a96 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Game.Beatmaps; -using osu.Game.Rulesets.Edit.Checks.Components; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Edit.Checks; From c8cb4286f6e61eeba14b4b0256d5a17d781f6bd9 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Tue, 13 Apr 2021 10:35:06 +0200 Subject: [PATCH 079/150] Add reference for screen bounding box numbers --- osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs index c241db7e06..27cae2ecc1 100644 --- a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs @@ -11,8 +11,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks { public class CheckOffscreenObjects : ICheck { - // These are close approximates to the edges of the screen - // in gameplay on a 4:3 aspect ratio for osu!stable. + // A close approximation for the bounding box of the screen in gameplay on 4:3 aspect ratio. + // Uses gameplay space coordinates (512 x 384 playfield / 640 x 480 screen area). + // See https://github.com/ppy/osu/pull/12361#discussion_r612199777 for reference. private const int min_x = -67; private const int min_y = -60; private const int max_x = 579; From 60c2494b316827c691327a55793750871753a190 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Tue, 13 Apr 2021 10:40:56 +0200 Subject: [PATCH 080/150] Make `BeatmapVerifier` an interface --- .../Edit/OsuBeatmapVerifier.cs | 12 ++---------- osu.Game.Rulesets.Osu/OsuRuleset.cs | 2 +- osu.Game/Rulesets/Edit/BeatmapVerifier.cs | 18 ++---------------- osu.Game/Rulesets/Ruleset.cs | 2 +- osu.Game/Screens/Edit/Verify/VerifyScreen.cs | 2 +- 5 files changed, 7 insertions(+), 29 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs b/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs index 2faa239720..1c7ab00bbb 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs @@ -10,21 +10,13 @@ using osu.Game.Rulesets.Osu.Edit.Checks; namespace osu.Game.Rulesets.Osu.Edit { - public class OsuBeatmapVerifier : BeatmapVerifier + public class OsuBeatmapVerifier : IBeatmapVerifier { private readonly List checks = new List { new CheckOffscreenObjects() }; - public override IEnumerable Run(IBeatmap beatmap) - { - // Also run mode-invariant checks. - foreach (var issue in base.Run(beatmap)) - yield return issue; - - foreach (var issue in checks.SelectMany(check => check.Run(beatmap))) - yield return issue; - } + public IEnumerable Run(IBeatmap beatmap) => checks.SelectMany(check => check.Run(beatmap)); } } diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 63da100a04..d6375fa6e3 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -206,7 +206,7 @@ namespace osu.Game.Rulesets.Osu public override HitObjectComposer CreateHitObjectComposer() => new OsuHitObjectComposer(this); - public override BeatmapVerifier CreateBeatmapVerifier() => new OsuBeatmapVerifier(); + public override IBeatmapVerifier CreateBeatmapVerifier() => new OsuBeatmapVerifier(); public override string Description => "osu!"; diff --git a/osu.Game/Rulesets/Edit/BeatmapVerifier.cs b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs index 1d0508705a..2bafacefa3 100644 --- a/osu.Game/Rulesets/Edit/BeatmapVerifier.cs +++ b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs @@ -2,27 +2,13 @@ // 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; using osu.Game.Rulesets.Edit.Checks.Components; namespace osu.Game.Rulesets.Edit { - public abstract class BeatmapVerifier + public interface IBeatmapVerifier { - /// - /// Checks which are performed regardless of ruleset. - /// These handle things like beatmap metadata, timing, and other ruleset agnostic elements. - /// - private readonly IReadOnlyList generalChecks = new List - { - new CheckBackground() - }; - - public virtual IEnumerable Run(IBeatmap beatmap) - { - return generalChecks.SelectMany(check => check.Run(beatmap)); - } + public IEnumerable Run(IBeatmap beatmap); } } diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 2a29d88c89..b501c55aef 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -202,7 +202,7 @@ namespace osu.Game.Rulesets public virtual HitObjectComposer CreateHitObjectComposer() => null; - public virtual BeatmapVerifier CreateBeatmapVerifier() => null; + public virtual IBeatmapVerifier CreateBeatmapVerifier() => null; public virtual Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.Solid.QuestionCircle }; diff --git a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs index a3d808ce62..a733c9c176 100644 --- a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs +++ b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs @@ -21,7 +21,7 @@ namespace osu.Game.Screens.Edit.Verify public class VerifyScreen : EditorScreen { private Ruleset ruleset; - private static BeatmapVerifier beatmapVerifier; + private static IBeatmapVerifier beatmapVerifier; [Cached] private Bindable selectedIssue = new Bindable(); From 304fe5cd341027b19da18b82f394e5fc444a2883 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Tue, 13 Apr 2021 10:41:02 +0200 Subject: [PATCH 081/150] Add `CheckBackground` to `OsuBeatmapVerifier` --- osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs b/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs index 1c7ab00bbb..66ef74ab08 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Checks; using osu.Game.Rulesets.Edit.Checks.Components; using osu.Game.Rulesets.Osu.Edit.Checks; @@ -14,6 +15,10 @@ namespace osu.Game.Rulesets.Osu.Edit { private readonly List checks = new List { + // General checks + new CheckBackground(), + + // Ruleset-specific checks new CheckOffscreenObjects() }; From 15658eda554f0574dd85f8d9e4b557ace3229c2d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 13 Apr 2021 09:56:43 +0300 Subject: [PATCH 082/150] Add failing test case --- .../Background/TestSceneUserDimBackgrounds.cs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs index a4c28651df..655b426e43 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs @@ -168,6 +168,29 @@ namespace osu.Game.Tests.Visual.Background AddUntilStep("Storyboard is visible", () => player.IsStoryboardVisible); } + [Test] + public void TestStoryboardIgnoreUserSettings() + { + performFullSetup(); + createFakeStoryboard(); + AddStep("Enable replacing background", () => player.ReplacesBackground.Value = true); + + AddUntilStep("Storyboard is invisible", () => !player.IsStoryboardVisible); + AddUntilStep("Background is visible", () => songSelect.IsBackgroundVisible()); + + AddStep("Ignore user settings", () => + { + player.ApplyToBackground(b => b.IgnoreUserSettings.Value = true); + player.DimmableStoryboard.IgnoreUserSettings.Value = true; + }); + AddUntilStep("Storyboard is visible", () => player.IsStoryboardVisible); + AddUntilStep("Background is invisible", () => songSelect.IsBackgroundInvisible()); + + AddStep("Disable background replacement", () => player.ReplacesBackground.Value = false); + AddUntilStep("Storyboard is visible", () => player.IsStoryboardVisible); + AddUntilStep("Background is visible", () => songSelect.IsBackgroundVisible()); + } + /// /// Check if the visual settings container retains dim and blur when pausing /// From 7c53bebfd4a5e1ac99b62ec12f0521616b6994dd Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 13 Apr 2021 09:25:08 +0300 Subject: [PATCH 083/150] Fix beatmap background not hiding when user settings ignored and storyboard replaces background --- osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs index d27211144e..10d381b8b7 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs @@ -166,7 +166,9 @@ namespace osu.Game.Screens.Backgrounds BlurAmount.ValueChanged += _ => UpdateVisuals(); } - protected override bool ShowDimContent => !ShowStoryboard.Value || !StoryboardReplacesBackground.Value; // The background needs to be hidden in the case of it being replaced by the storyboard + protected override bool ShowDimContent + // The background needs to be hidden in the case of it being replaced by the storyboard + => (!ShowStoryboard.Value && !IgnoreUserSettings.Value) || !StoryboardReplacesBackground.Value; protected override void UpdateVisuals() { From aa5fe2e9fcdbe3f2dd6ac1d63328c4f5f4af477d Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Tue, 13 Apr 2021 11:02:01 +0200 Subject: [PATCH 084/150] Rename `BeatmapVerifier` -> `IBeatmapVerifier` --- .../Rulesets/Edit/{BeatmapVerifier.cs => IBeatmapVerifier.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename osu.Game/Rulesets/Edit/{BeatmapVerifier.cs => IBeatmapVerifier.cs} (100%) diff --git a/osu.Game/Rulesets/Edit/BeatmapVerifier.cs b/osu.Game/Rulesets/Edit/IBeatmapVerifier.cs similarity index 100% rename from osu.Game/Rulesets/Edit/BeatmapVerifier.cs rename to osu.Game/Rulesets/Edit/IBeatmapVerifier.cs From 4618728bf016fc3253d0cce337c230a2ed0f2fac Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Tue, 13 Apr 2021 11:35:12 +0200 Subject: [PATCH 085/150] Add test case --- .../TestSceneOsuEditorSelectInvalidPath.cs | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorSelectInvalidPath.cs diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorSelectInvalidPath.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorSelectInvalidPath.cs new file mode 100644 index 0000000000..d0348c1b6b --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorSelectInvalidPath.cs @@ -0,0 +1,46 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Tests.Beatmaps; +using osu.Game.Tests.Visual; +using osuTK; + +namespace osu.Game.Rulesets.Osu.Tests.Editor +{ + public class TestSceneOsuEditorSelectInvalidPath : EditorTestScene + { + protected override Ruleset CreateEditorRuleset() => new OsuRuleset(); + + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset, false); + + [Test] + public void TestSelectDoesNotModify() + { + Slider slider = new Slider { StartTime = 0, Position = new Vector2(320, 40) }; + + PathControlPoint[] points = + { + new PathControlPoint(new Vector2(0), PathType.PerfectCurve), + new PathControlPoint(new Vector2(-100, 0)), + new PathControlPoint(new Vector2(100, 20)) + }; + + int preSelectVersion = -1; + AddStep("add slider", () => + { + slider.Path = new SliderPath(points); + EditorBeatmap.Add(slider); + preSelectVersion = slider.Path.Version.Value; + }); + + AddStep("select added slider", () => EditorBeatmap.SelectedHitObjects.Add(slider)); + + AddAssert("slider same path", () => slider.Path.Version.Value == preSelectVersion); + } + } +} From fca9c70c1b47bd986fe089e675f85ea44d9b6690 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 17:39:12 +0900 Subject: [PATCH 086/150] Move timeline hit object test to immediately viewable area --- .../Visual/Editing/TestSceneTimelineHitObjectBlueprint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneTimelineHitObjectBlueprint.cs b/osu.Game.Tests/Visual/Editing/TestSceneTimelineHitObjectBlueprint.cs index 35f394fe1d..88246381bf 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneTimelineHitObjectBlueprint.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneTimelineHitObjectBlueprint.cs @@ -29,7 +29,7 @@ namespace osu.Game.Tests.Visual.Editing EditorBeatmap.Add(new Spinner { Position = new Vector2(256, 256), - StartTime = 150, + StartTime = 2700, Duration = 500 }); }); From 00f235760d237e6902162c4f1675cbedbc0063ba Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 18:12:46 +0900 Subject: [PATCH 087/150] Update visual appearance of timeline blueprints to close match new designs --- .../TestSceneTimelineHitObjectBlueprint.cs | 6 +- .../Timeline/TimelineBlueprintContainer.cs | 3 +- .../Timeline/TimelineHitObjectBlueprint.cs | 166 +++++++++--------- 3 files changed, 92 insertions(+), 83 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneTimelineHitObjectBlueprint.cs b/osu.Game.Tests/Visual/Editing/TestSceneTimelineHitObjectBlueprint.cs index 88246381bf..e6fad33a51 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneTimelineHitObjectBlueprint.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneTimelineHitObjectBlueprint.cs @@ -21,7 +21,7 @@ namespace osu.Game.Tests.Visual.Editing [Test] public void TestDisallowZeroDurationObjects() { - DragBar dragBar; + DragArea dragArea; AddStep("add spinner", () => { @@ -37,8 +37,8 @@ namespace osu.Game.Tests.Visual.Editing AddStep("hold down drag bar", () => { // distinguishes between the actual drag bar and its "underlay shadow". - dragBar = this.ChildrenOfType().Single(bar => bar.HandlePositionalInput); - InputManager.MoveMouseTo(dragBar); + dragArea = this.ChildrenOfType().Single(bar => bar.HandlePositionalInput); + InputManager.MoveMouseTo(dragArea); InputManager.PressButton(MouseButton.Left); }); diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index be34c8d57e..7427473a35 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -63,7 +63,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { AddInternal(backgroundBox = new SelectableAreaBackground { - Colour = Color4.Black + Colour = Color4.Black, + Depth = float.MaxValue, }); } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index d24614299c..179abd0a26 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; @@ -28,9 +29,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { public class TimelineHitObjectBlueprint : SelectionBlueprint { - private const float thickness = 5; private const float shadow_radius = 5; - private const float circle_size = 34; + private const float circle_size = 38; public Action OnDragHandled; @@ -40,8 +40,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private Bindable indexInCurrentComboBindable; private Bindable comboIndexBindable; - private readonly Circle circle; - private readonly DragBar dragBar; + private readonly Container circle; + private readonly DragArea dragArea; private readonly List shadowComponents = new List(); private readonly Container mainComponents; private readonly OsuSpriteText comboIndexText; @@ -76,71 +76,27 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { Anchor = Anchor.CentreLeft, Origin = Anchor.Centre, - Font = OsuFont.Numeric.With(size: circle_size / 2, weight: FontWeight.Black), + Y = -1, + Font = OsuFont.Default.With(size: circle_size * 0.5f, weight: FontWeight.Regular), }, }); - circle = new Circle + circle = new ExtendableCircle { - Size = new Vector2(circle_size), + RelativeSizeAxes = Axes.X, + Size = new Vector2(1, circle_size), Anchor = Anchor.CentreLeft, - Origin = Anchor.Centre, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Radius = shadow_radius, - Colour = Color4.Black - }, + Origin = Anchor.CentreLeft, }; - shadowComponents.Add(circle); + mainComponents.Add(circle); if (hitObject is IHasDuration) { - DragBar dragBarUnderlay; - Container extensionBar; - - mainComponents.AddRange(new Drawable[] + mainComponents.Add(dragArea = new DragArea(hitObject) { - extensionBar = new Container - { - Masking = true, - Size = new Vector2(1, thickness), - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - RelativePositionAxes = Axes.X, - RelativeSizeAxes = Axes.X, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Radius = shadow_radius, - Colour = Color4.Black - }, - Child = new Box - { - RelativeSizeAxes = Axes.Both, - } - }, - circle, - // only used for drawing the shadow - dragBarUnderlay = new DragBar(null), - // cover up the shadow on the join - new Box - { - Height = thickness, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - RelativeSizeAxes = Axes.X, - }, - dragBar = new DragBar(hitObject) { OnDragHandled = e => OnDragHandled?.Invoke(e) }, + OnDragHandled = e => OnDragHandled?.Invoke(e) }); - - shadowComponents.Add(dragBarUnderlay); - shadowComponents.Add(extensionBar); - } - else - { - mainComponents.Add(circle); } updateShadows(); @@ -173,7 +129,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline var comboColour = combo.GetComboColour(comboColours); if (HitObject is IHasDuration) - mainComponents.Colour = ColourInfo.GradientHorizontal(comboColour, Color4.White); + mainComponents.Colour = ColourInfo.GradientHorizontal(comboColour, comboColour.Lighten(0.4f)); else mainComponents.Colour = comboColour; @@ -227,10 +183,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline protected override bool ShouldBeConsideredForInput(Drawable child) => true; - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => - base.ReceivePositionalInputAt(screenSpacePos) || - circle.ReceivePositionalInputAt(screenSpacePos) || - dragBar?.ReceivePositionalInputAt(screenSpacePos) == true; + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => circle.Contains(screenSpacePos); protected override void OnSelected() { @@ -256,7 +209,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { Type = EdgeEffectType.Shadow, Radius = shadow_radius, - Colour = State == SelectionState.Selected ? Color4.Orange : Color4.Black + Colour = Color4.Black.Opacity(0.4f) }; } } @@ -267,22 +220,11 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline updateShadows(); } - public override Quad SelectionQuad - { - get - { - // correctly include the circle in the selection quad region, as it is usually outside the blueprint itself. - var leftQuad = circle.ScreenSpaceDrawQuad; - var rightQuad = dragBar?.ScreenSpaceDrawQuad ?? ScreenSpaceDrawQuad; - - return new Quad(leftQuad.TopLeft, Vector2.ComponentMax(rightQuad.TopRight, leftQuad.TopRight), - leftQuad.BottomLeft, Vector2.ComponentMax(rightQuad.BottomRight, leftQuad.BottomRight)); - } - } + public override Quad SelectionQuad => circle.ScreenSpaceDrawQuad; public override Vector2 ScreenSpaceSelectionPoint => ScreenSpaceDrawQuad.TopLeft; - public class DragBar : Container + public class DragArea : Container { private readonly HitObject hitObject; @@ -293,13 +235,13 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline public override bool HandlePositionalInput => hitObject != null; - public DragBar(HitObject hitObject) + public DragArea(HitObject hitObject) { this.hitObject = hitObject; - CornerRadius = 2; + CornerRadius = circle_size / 2; Masking = true; - Size = new Vector2(5, 1); + Size = new Vector2(circle_size, 1); Anchor = Anchor.CentreRight; Origin = Anchor.Centre; RelativePositionAxes = Axes.X; @@ -406,5 +348,71 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline changeHandler?.EndChange(); } } + + /// + /// A circle with externalised end caps so it can take up the full width of a relative width area. + /// + public class ExtendableCircle : Container + { + private readonly Circle rightCircle; + private readonly Circle leftCircle; + + public override Quad ScreenSpaceDrawQuad + { + get + { + var leftQuad = leftCircle.ScreenSpaceDrawQuad; + + if (Width == 0) + { + return leftQuad; + } + + var rightQuad = rightCircle.ScreenSpaceDrawQuad; + + return new Quad(leftQuad.TopLeft, rightQuad.TopRight, leftQuad.BottomLeft, rightQuad.BottomRight); + } + } + + public ExtendableCircle() + { + var effect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Radius = shadow_radius, + Colour = Color4.Black.Opacity(0.4f) + }; + + InternalChildren = new Drawable[] + { + new Container + { + Height = circle_size, + RelativeSizeAxes = Axes.X, + Masking = true, + AlwaysPresent = true, + EdgeEffect = effect, + }, + leftCircle = new Circle + { + EdgeEffect = effect, + Origin = Anchor.TopCentre, + Size = new Vector2(circle_size) + }, + rightCircle = new Circle + { + EdgeEffect = effect, + Anchor = Anchor.TopRight, + Origin = Anchor.TopCentre, + Size = new Vector2(circle_size) + }, + new Box + { + Height = circle_size, + RelativeSizeAxes = Axes.X, + }, + }; + } + } } } From 109ee395bf397fc73b13e10f70bf8dedae67b20f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 18:47:07 +0900 Subject: [PATCH 088/150] Fix input and remove outdated hover logic --- .../Timeline/TimelineHitObjectBlueprint.cs | 42 +++++++------------ 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index 179abd0a26..89a9095d22 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -17,7 +17,6 @@ using osu.Framework.Input.Events; using osu.Framework.Utils; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; @@ -40,9 +39,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private Bindable indexInCurrentComboBindable; private Bindable comboIndexBindable; - private readonly Container circle; - private readonly DragArea dragArea; - private readonly List shadowComponents = new List(); + private readonly Drawable circle; + private readonly Container mainComponents; private readonly OsuSpriteText comboIndexText; @@ -93,7 +91,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline if (hitObject is IHasDuration) { - mainComponents.Add(dragArea = new DragArea(hitObject) + mainComponents.Add(new DragArea(hitObject) { OnDragHandled = e => OnDragHandled?.Invoke(e) }); @@ -183,8 +181,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline protected override bool ShouldBeConsideredForInput(Drawable child) => true; - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => circle.Contains(screenSpacePos); - protected override void OnSelected() { updateShadows(); @@ -192,27 +188,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private void updateShadows() { - foreach (var s in shadowComponents) - { - if (State == SelectionState.Selected) - { - s.EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Radius = shadow_radius / 2, - Colour = Color4.Orange, - }; - } - else - { - s.EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Radius = shadow_radius, - Colour = Color4.Black.Opacity(0.4f) - }; - } - } } protected override void OnDeselected() @@ -220,6 +195,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline updateShadows(); } + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => + circle.ReceivePositionalInputAt(screenSpacePos); + public override Quad SelectionQuad => circle.ScreenSpaceDrawQuad; public override Vector2 ScreenSpaceSelectionPoint => ScreenSpaceDrawQuad.TopLeft; @@ -351,12 +329,20 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline /// /// A circle with externalised end caps so it can take up the full width of a relative width area. + /// TODO: figure how to do this with a single circle to avoid pixel-misaligned edges. /// public class ExtendableCircle : Container { private readonly Circle rightCircle; private readonly Circle leftCircle; + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) + { + return base.ReceivePositionalInputAt(screenSpacePos) + || leftCircle.ReceivePositionalInputAt(screenSpacePos) + || rightCircle.ReceivePositionalInputAt(screenSpacePos); + } + public override Quad ScreenSpaceDrawQuad { get From 495fdd8d65d99d7818726e29829fcd6ff8fed660 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 18:58:51 +0900 Subject: [PATCH 089/150] Update drag area display to match new design logic --- .../Timeline/TimelineHitObjectBlueprint.cs | 40 ++++++++++++++++--- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index 89a9095d22..493f62921b 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -202,7 +202,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline public override Vector2 ScreenSpaceSelectionPoint => ScreenSpaceDrawQuad.TopLeft; - public class DragArea : Container + public class DragArea : Circle { private readonly HitObject hitObject; @@ -224,6 +224,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline Origin = Anchor.Centre; RelativePositionAxes = Axes.X; RelativeSizeAxes = Axes.Y; + Colour = OsuColour.Gray(0.2f); InternalChildren = new Drawable[] { @@ -234,6 +235,14 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline }; } + protected override void LoadComplete() + { + base.LoadComplete(); + + updateState(); + FinishTransforms(); + } + protected override bool OnHover(HoverEvent e) { updateState(); @@ -265,7 +274,20 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private void updateState() { - Colour = IsHovered || hasMouseDown ? Color4.OrangeRed : Color4.White; + if (hasMouseDown) + { + this.ScaleTo(0.7f, 200, Easing.OutQuint); + } + else if (IsHovered) + { + this.ScaleTo(0.8f, 200, Easing.OutQuint); + } + else + { + this.ScaleTo(0.6f, 200, Easing.OutQuint); + } + + this.FadeTo(IsHovered || hasMouseDown ? 0.8f : 0.2f, 200, Easing.OutQuint); } [Resolved] @@ -369,12 +391,16 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline Colour = Color4.Black.Opacity(0.4f) }; + const float fudge = 0.97f; + InternalChildren = new Drawable[] { new Container { - Height = circle_size, - RelativeSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Height = fudge, Masking = true, AlwaysPresent = true, EdgeEffect = effect, @@ -394,8 +420,10 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline }, new Box { - Height = circle_size, - RelativeSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Height = fudge, }, }; } From b2c17979defca0bd018244129c9b9e1d029fdc84 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 19:24:20 +0900 Subject: [PATCH 090/150] Update colours of all overlay components in one swoop (based off combo colour) --- .../Timeline/TimelineHitObjectBlueprint.cs | 79 ++++++++++--------- 1 file changed, 41 insertions(+), 38 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index 493f62921b..6463f1a200 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -31,6 +31,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private const float shadow_radius = 5; private const float circle_size = 38; + private Container repeatsContainer; + public Action OnDragHandled; [UsedImplicitly] @@ -41,7 +43,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private readonly Drawable circle; - private readonly Container mainComponents; + private readonly Container colouredComponents; private readonly OsuSpriteText comboIndexText; [Resolved] @@ -59,39 +61,37 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline RelativePositionAxes = Axes.X; RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; + Height = circle_size; - AddRangeInternal(new Drawable[] + AddRangeInternal(new[] { - mainComponents = new Container + circle = new ExtendableCircle + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, + colouredComponents = new Container { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - }, - comboIndexText = new OsuSpriteText - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.Centre, - Y = -1, - Font = OsuFont.Default.With(size: circle_size * 0.5f, weight: FontWeight.Regular), + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + comboIndexText = new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.Centre, + Y = -1, + Font = OsuFont.Default.With(size: circle_size * 0.5f, weight: FontWeight.Regular), + }, + } }, }); - circle = new ExtendableCircle - { - RelativeSizeAxes = Axes.X, - Size = new Vector2(1, circle_size), - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - }; - - mainComponents.Add(circle); - if (hitObject is IHasDuration) { - mainComponents.Add(new DragArea(hitObject) + colouredComponents.Add(new DragArea(hitObject) { OnDragHandled = e => OnDragHandled?.Invoke(e) }); @@ -127,15 +127,15 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline var comboColour = combo.GetComboColour(comboColours); if (HitObject is IHasDuration) - mainComponents.Colour = ColourInfo.GradientHorizontal(comboColour, comboColour.Lighten(0.4f)); + circle.Colour = ColourInfo.GradientHorizontal(comboColour, comboColour.Lighten(0.4f)); else - mainComponents.Colour = comboColour; + circle.Colour = comboColour; - var col = mainComponents.Colour.TopLeft.Linear; + var col = circle.Colour.TopLeft.Linear; float brightness = col.R + col.G + col.B; // decide the combo index colour based on brightness? - comboIndexText.Colour = brightness > 0.5f ? Color4.Black : Color4.White; + colouredComponents.Colour = OsuColour.Gray(brightness > 0.5f ? 0.2f : 0.9f); } protected override void Update() @@ -155,13 +155,11 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline } } - private Container repeatsContainer; - private void updateRepeats(IHasRepeats repeats) { repeatsContainer?.Expire(); - mainComponents.Add(repeatsContainer = new Container + colouredComponents.Add(repeatsContainer = new Container { RelativeSizeAxes = Axes.Both, }); @@ -170,7 +168,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { repeatsContainer.Add(new Circle { - Size = new Vector2(circle_size / 2), + Size = new Vector2(circle_size / 3), + Alpha = 0.2f, Anchor = Anchor.CentreLeft, Origin = Anchor.Centre, RelativePositionAxes = Axes.X, @@ -224,7 +223,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline Origin = Anchor.Centre; RelativePositionAxes = Axes.X; RelativeSizeAxes = Axes.Y; - Colour = OsuColour.Gray(0.2f); InternalChildren = new Drawable[] { @@ -351,7 +349,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline /// /// A circle with externalised end caps so it can take up the full width of a relative width area. - /// TODO: figure how to do this with a single circle to avoid pixel-misaligned edges. /// public class ExtendableCircle : Container { @@ -391,7 +388,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline Colour = Color4.Black.Opacity(0.4f) }; - const float fudge = 0.97f; + // TODO: figure how to do this whole thing with a single circle to avoid pixel-misaligned edges. + // just working with what i can make work for the time being.. + const float fudge = 0.4f; InternalChildren = new Drawable[] { @@ -400,7 +399,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline RelativeSizeAxes = Axes.Both, Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Height = fudge, + Padding = new MarginPadding { Vertical = fudge }, Masking = true, AlwaysPresent = true, EdgeEffect = effect, @@ -418,12 +417,16 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline Origin = Anchor.TopCentre, Size = new Vector2(circle_size) }, - new Box + new Container { RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Vertical = fudge }, Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Height = fudge, + Children = new Drawable[] + { + new Box { RelativeSizeAxes = Axes.Both, } + } }, }; } From e7b0042a608d09e1f6a01e53860a01595905177f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 19:25:03 +0900 Subject: [PATCH 091/150] Remove unnecessary hover / shadow logic --- .../Timeline/TimelineHitObjectBlueprint.cs | 29 +++++++------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index 6463f1a200..67be298567 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -28,7 +28,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { public class TimelineHitObjectBlueprint : SelectionBlueprint { - private const float shadow_radius = 5; private const float circle_size = 38; private Container repeatsContainer; @@ -96,8 +95,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline OnDragHandled = e => OnDragHandled?.Invoke(e) }); } - - updateShadows(); } protected override void LoadComplete() @@ -116,6 +113,16 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline } } + protected override void OnSelected() + { + // base logic hides selected blueprints when not selected, but timeline doesn't do that. + } + + protected override void OnDeselected() + { + // base logic hides selected blueprints when not selected, but timeline doesn't do that. + } + private void updateComboIndex() => comboIndexText.Text = (indexInCurrentComboBindable.Value + 1).ToString(); private void updateComboColour() @@ -180,20 +187,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline protected override bool ShouldBeConsideredForInput(Drawable child) => true; - protected override void OnSelected() - { - updateShadows(); - } - - private void updateShadows() - { - } - - protected override void OnDeselected() - { - updateShadows(); - } - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => circle.ReceivePositionalInputAt(screenSpacePos); @@ -384,7 +377,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline var effect = new EdgeEffectParameters { Type = EdgeEffectType.Shadow, - Radius = shadow_radius, + Radius = 5, Colour = Color4.Black.Opacity(0.4f) }; From bcd41417b3cb7701985d8db8614ded82cc9c7034 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 19:36:29 +0900 Subject: [PATCH 092/150] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index c78dfb6a55..b5315c3616 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 92e05cb4a6..45b3d5c161 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -29,7 +29,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 11124730c9..105a6e59c2 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 03ba04e8cec2b335355916d477712e84305ce00e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 19:50:22 +0900 Subject: [PATCH 093/150] Split out general checks into its own verifier class (and remove `static` usage) --- .../Edit/OsuBeatmapVerifier.cs | 5 --- osu.Game/Rulesets/Edit/BeatmapVerifier.cs | 24 ++++++++++++++ osu.Game/Screens/Edit/Verify/VerifyScreen.cs | 32 ++++++++----------- 3 files changed, 38 insertions(+), 23 deletions(-) create mode 100644 osu.Game/Rulesets/Edit/BeatmapVerifier.cs diff --git a/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs b/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs index 66ef74ab08..1c7ab00bbb 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Edit.Checks; using osu.Game.Rulesets.Edit.Checks.Components; using osu.Game.Rulesets.Osu.Edit.Checks; @@ -15,10 +14,6 @@ namespace osu.Game.Rulesets.Osu.Edit { private readonly List checks = new List { - // General checks - new CheckBackground(), - - // Ruleset-specific checks new CheckOffscreenObjects() }; diff --git a/osu.Game/Rulesets/Edit/BeatmapVerifier.cs b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs new file mode 100644 index 0000000000..b1d538bf04 --- /dev/null +++ b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs @@ -0,0 +1,24 @@ +// 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; +using osu.Game.Rulesets.Edit.Checks.Components; + +namespace osu.Game.Rulesets.Edit +{ + /// + /// A ruleset-agnostic beatmap converter that identifies issues in common metadata or mapping standards. + /// + public class BeatmapVerifier : IBeatmapVerifier + { + private readonly List checks = new List + { + new CheckBackground(), + }; + + public IEnumerable Run(IBeatmap beatmap) => checks.SelectMany(check => check.Run(beatmap)); + } +} diff --git a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs index a733c9c176..550fbe2950 100644 --- a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs +++ b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs @@ -7,11 +7,9 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; -using osu.Game.Rulesets; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Checks.Components; using osuTK; @@ -20,9 +18,6 @@ namespace osu.Game.Screens.Edit.Verify { public class VerifyScreen : EditorScreen { - private Ruleset ruleset; - private static IBeatmapVerifier beatmapVerifier; - [Cached] private Bindable selectedIssue = new Bindable(); @@ -31,16 +26,6 @@ namespace osu.Game.Screens.Edit.Verify { } - protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) - { - var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - - ruleset = parent.Get>().Value.BeatmapInfo.Ruleset?.CreateInstance(); - beatmapVerifier = ruleset?.CreateBeatmapVerifier(); - - return dependencies; - } - [BackgroundDependencyLoader] private void load() { @@ -81,9 +66,15 @@ namespace osu.Game.Screens.Edit.Verify [Resolved] private Bindable selectedIssue { get; set; } + private IBeatmapVerifier rulesetVerifier; + private BeatmapVerifier generalVerifier; + [BackgroundDependencyLoader] private void load(OsuColour colours) { + generalVerifier = new BeatmapVerifier(); + rulesetVerifier = Beatmap.BeatmapInfo.Ruleset?.CreateInstance()?.CreateBeatmapVerifier(); + RelativeSizeAxes = Axes.Both; InternalChildren = new Drawable[] @@ -128,9 +119,14 @@ namespace osu.Game.Screens.Edit.Verify private void refresh() { - table.Issues = beatmapVerifier.Run(Beatmap) - .OrderBy(issue => issue.Template.Type) - .ThenBy(issue => issue.Check.Metadata.Category); + var issues = generalVerifier.Run(Beatmap); + + if (rulesetVerifier != null) + issues = issues.Concat(rulesetVerifier.Run(Beatmap)); + + table.Issues = issues + .OrderBy(issue => issue.Template.Type) + .ThenBy(issue => issue.Check.Metadata.Category); } } } From 464fc02875f067aef4bd55f3244aa1df97cc774b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 19:55:17 +0900 Subject: [PATCH 094/150] Fix some styling issues with the verify screen layout --- osu.Game/Screens/Edit/Verify/IssueTable.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Verify/IssueTable.cs b/osu.Game/Screens/Edit/Verify/IssueTable.cs index 516e1adf44..042d6d84e3 100644 --- a/osu.Game/Screens/Edit/Verify/IssueTable.cs +++ b/osu.Game/Screens/Edit/Verify/IssueTable.cs @@ -38,9 +38,6 @@ namespace osu.Game.Screens.Edit.Verify Padding = new MarginPadding { Horizontal = horizontal_inset }; RowSize = new Dimension(GridSizeMode.Absolute, row_height); - Masking = true; - CornerRadius = 6; - AddInternal(backgroundFlow = new FillFlowContainer { RelativeSizeAxes = Axes.Both, @@ -118,6 +115,17 @@ namespace osu.Game.Screens.Edit.Verify } }; + protected override Drawable CreateHeader(int index, TableColumn column) => new HeaderText(column?.Header ?? string.Empty); + + private class HeaderText : OsuSpriteText + { + public HeaderText(string text) + { + Text = text.ToUpper(); + Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold); + } + } + public class RowBackground : OsuClickableContainer { private readonly Issue issue; From 0d6890243fc7b6308ffdd87e7f87c1551d4ae2fd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 20:18:18 +0900 Subject: [PATCH 095/150] Fix typo in xmldoc --- osu.Game/Rulesets/Edit/BeatmapVerifier.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/BeatmapVerifier.cs b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs index b1d538bf04..f9bced7beb 100644 --- a/osu.Game/Rulesets/Edit/BeatmapVerifier.cs +++ b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs @@ -10,7 +10,7 @@ using osu.Game.Rulesets.Edit.Checks.Components; namespace osu.Game.Rulesets.Edit { /// - /// A ruleset-agnostic beatmap converter that identifies issues in common metadata or mapping standards. + /// A ruleset-agnostic beatmap verifier that identifies issues in common metadata or mapping standards. /// public class BeatmapVerifier : IBeatmapVerifier { From e601141be2a70e1a1f004c205b360212a9fe2605 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 13 Apr 2021 14:57:02 +0300 Subject: [PATCH 096/150] Simplify ExtendableCircle component --- .../Timeline/TimelineHitObjectBlueprint.cs | 83 ++++--------------- 1 file changed, 15 insertions(+), 68 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index 67be298567..1c0d6e5ab6 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -343,84 +343,31 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline /// /// A circle with externalised end caps so it can take up the full width of a relative width area. /// - public class ExtendableCircle : Container + public class ExtendableCircle : CompositeDrawable { - private readonly Circle rightCircle; - private readonly Circle leftCircle; + private readonly CircularContainer content; - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) - { - return base.ReceivePositionalInputAt(screenSpacePos) - || leftCircle.ReceivePositionalInputAt(screenSpacePos) - || rightCircle.ReceivePositionalInputAt(screenSpacePos); - } + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => content.ReceivePositionalInputAt(screenSpacePos); - public override Quad ScreenSpaceDrawQuad - { - get - { - var leftQuad = leftCircle.ScreenSpaceDrawQuad; - - if (Width == 0) - { - return leftQuad; - } - - var rightQuad = rightCircle.ScreenSpaceDrawQuad; - - return new Quad(leftQuad.TopLeft, rightQuad.TopRight, leftQuad.BottomLeft, rightQuad.BottomRight); - } - } + public override Quad ScreenSpaceDrawQuad => content.ScreenSpaceDrawQuad; public ExtendableCircle() { - var effect = new EdgeEffectParameters + Padding = new MarginPadding { Horizontal = -circle_size / 2f }; + InternalChild = content = new CircularContainer { - Type = EdgeEffectType.Shadow, - Radius = 5, - Colour = Color4.Black.Opacity(0.4f) - }; - - // TODO: figure how to do this whole thing with a single circle to avoid pixel-misaligned edges. - // just working with what i can make work for the time being.. - const float fudge = 0.4f; - - InternalChildren = new Drawable[] - { - new Container + RelativeSizeAxes = Axes.Both, + Masking = true, + EdgeEffect = new EdgeEffectParameters { - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Padding = new MarginPadding { Vertical = fudge }, - Masking = true, - AlwaysPresent = true, - EdgeEffect = effect, + Type = EdgeEffectType.Shadow, + Radius = 5, + Colour = Color4.Black.Opacity(0.4f) }, - leftCircle = new Circle + Child = new Box { - EdgeEffect = effect, - Origin = Anchor.TopCentre, - Size = new Vector2(circle_size) - }, - rightCircle = new Circle - { - EdgeEffect = effect, - Anchor = Anchor.TopRight, - Origin = Anchor.TopCentre, - Size = new Vector2(circle_size) - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Vertical = fudge }, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Children = new Drawable[] - { - new Box { RelativeSizeAxes = Axes.Both, } - } - }, + RelativeSizeAxes = Axes.Both + } }; } } From 69da804f817dac304b7bc36587070d5f129b5eae Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Tue, 13 Apr 2021 13:57:56 +0200 Subject: [PATCH 097/150] Add missing period --- osu.Game/Rulesets/Edit/Checks/CheckBackground.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs index 4d5069f446..93da42425c 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Edit.Checks public class IssueTemplateNoneSet : IssueTemplate { public IssueTemplateNoneSet(ICheck check) - : base(check, IssueType.Problem, "No background has been set") + : base(check, IssueType.Problem, "No background has been set.") { } From 0edc1a850d95c1c8bd94c873f12be8b40bcb33d5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 23:05:58 +0900 Subject: [PATCH 098/150] Split out common EditorTable base class --- osu.Game/Screens/Edit/EditorTable.cs | 49 +++++++++++++++++ .../Screens/Edit/Timing/ControlPointTable.cs | 44 ++-------------- osu.Game/Screens/Edit/Verify/IssueTable.cs | 52 ++++--------------- 3 files changed, 63 insertions(+), 82 deletions(-) create mode 100644 osu.Game/Screens/Edit/EditorTable.cs diff --git a/osu.Game/Screens/Edit/EditorTable.cs b/osu.Game/Screens/Edit/EditorTable.cs new file mode 100644 index 0000000000..e5e2add384 --- /dev/null +++ b/osu.Game/Screens/Edit/EditorTable.cs @@ -0,0 +1,49 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Screens.Edit +{ + public abstract class EditorTable : TableContainer + { + private const float horizontal_inset = 20; + + protected const float ROW_HEIGHT = 25; + + protected const int TEXT_SIZE = 14; + + protected readonly FillFlowContainer BackgroundFlow; + + protected EditorTable() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + Padding = new MarginPadding { Horizontal = horizontal_inset }; + RowSize = new Dimension(GridSizeMode.Absolute, ROW_HEIGHT); + + AddInternal(BackgroundFlow = new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Depth = 1f, + Padding = new MarginPadding { Horizontal = -horizontal_inset }, + Margin = new MarginPadding { Top = ROW_HEIGHT } + }); + } + + protected override Drawable CreateHeader(int index, TableColumn column) => new HeaderText(column?.Header ?? string.Empty); + + private class HeaderText : OsuSpriteText + { + public HeaderText(string text) + { + Text = text.ToUpper(); + Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold); + } + } + } +} diff --git a/osu.Game/Screens/Edit/Timing/ControlPointTable.cs b/osu.Game/Screens/Edit/Timing/ControlPointTable.cs index a17b431fcc..75e2bb1f5c 100644 --- a/osu.Game/Screens/Edit/Timing/ControlPointTable.cs +++ b/osu.Game/Screens/Edit/Timing/ControlPointTable.cs @@ -20,47 +20,24 @@ using osuTK.Graphics; namespace osu.Game.Screens.Edit.Timing { - public class ControlPointTable : TableContainer + public class ControlPointTable : EditorTable { - private const float horizontal_inset = 20; - private const float row_height = 25; - private const int text_size = 14; - - private readonly FillFlowContainer backgroundFlow; - [Resolved] private Bindable selectedGroup { get; set; } - public ControlPointTable() - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - - Padding = new MarginPadding { Horizontal = horizontal_inset }; - RowSize = new Dimension(GridSizeMode.Absolute, row_height); - - AddInternal(backgroundFlow = new FillFlowContainer - { - RelativeSizeAxes = Axes.Both, - Depth = 1f, - Padding = new MarginPadding { Horizontal = -horizontal_inset }, - Margin = new MarginPadding { Top = row_height } - }); - } - public IEnumerable ControlGroups { set { Content = null; - backgroundFlow.Clear(); + BackgroundFlow.Clear(); if (value?.Any() != true) return; foreach (var group in value) { - backgroundFlow.Add(new RowBackground(group)); + BackgroundFlow.Add(new RowBackground(group)); } Columns = createHeaders(); @@ -86,13 +63,13 @@ namespace osu.Game.Screens.Edit.Timing new OsuSpriteText { Text = $"#{index + 1}", - Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold), + Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Bold), Margin = new MarginPadding(10) }, new OsuSpriteText { Text = group.Time.ToEditorFormattedString(), - Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold) + Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Bold) }, null, new ControlGroupAttributes(group), @@ -164,17 +141,6 @@ namespace osu.Game.Screens.Edit.Timing } } - protected override Drawable CreateHeader(int index, TableColumn column) => new HeaderText(column?.Header ?? string.Empty); - - private class HeaderText : OsuSpriteText - { - public HeaderText(string text) - { - Text = text.ToUpper(); - Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold); - } - } - public class RowBackground : OsuClickableContainer { private readonly ControlPointGroup controlGroup; diff --git a/osu.Game/Screens/Edit/Verify/IssueTable.cs b/osu.Game/Screens/Edit/Verify/IssueTable.cs index 042d6d84e3..00570d363b 100644 --- a/osu.Game/Screens/Edit/Verify/IssueTable.cs +++ b/osu.Game/Screens/Edit/Verify/IssueTable.cs @@ -19,47 +19,24 @@ using osuTK.Graphics; namespace osu.Game.Screens.Edit.Verify { - public class IssueTable : TableContainer + public class IssueTable : EditorTable { - private const float horizontal_inset = 20; - private const float row_height = 25; - private const int text_size = 14; - - private readonly FillFlowContainer backgroundFlow; - [Resolved] private Bindable selectedIssue { get; set; } - public IssueTable() - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - - Padding = new MarginPadding { Horizontal = horizontal_inset }; - RowSize = new Dimension(GridSizeMode.Absolute, row_height); - - AddInternal(backgroundFlow = new FillFlowContainer - { - RelativeSizeAxes = Axes.Both, - Depth = 1f, - Padding = new MarginPadding { Horizontal = -horizontal_inset }, - Margin = new MarginPadding { Top = row_height } - }); - } - public IEnumerable Issues { set { Content = null; - backgroundFlow.Clear(); + BackgroundFlow.Clear(); if (value == null) return; foreach (var issue in value) { - backgroundFlow.Add(new RowBackground(issue)); + BackgroundFlow.Add(new RowBackground(issue)); } Columns = createHeaders(); @@ -86,46 +63,35 @@ namespace osu.Game.Screens.Edit.Verify new OsuSpriteText { Text = $"#{index + 1}", - Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Medium), + Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Medium), Margin = new MarginPadding { Right = 10 } }, new OsuSpriteText { Text = issue.Template.Type.ToString(), - Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold), + Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Bold), Margin = new MarginPadding { Right = 10 }, Colour = issue.Template.Colour }, new OsuSpriteText { Text = issue.GetEditorTimestamp(), - Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold), + Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Bold), Margin = new MarginPadding { Right = 10 }, }, new OsuSpriteText { Text = issue.ToString(), - Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Medium) + Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Medium) }, new OsuSpriteText { Text = issue.Check.Metadata.Category.ToString(), - Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold), + Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Bold), Margin = new MarginPadding(10) } }; - protected override Drawable CreateHeader(int index, TableColumn column) => new HeaderText(column?.Header ?? string.Empty); - - private class HeaderText : OsuSpriteText - { - public HeaderText(string text) - { - Text = text.ToUpper(); - Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold); - } - } - public class RowBackground : OsuClickableContainer { private readonly Issue issue; @@ -150,7 +116,7 @@ namespace osu.Game.Screens.Edit.Verify this.issue = issue; RelativeSizeAxes = Axes.X; - Height = row_height; + Height = ROW_HEIGHT; AlwaysPresent = true; CornerRadius = 3; Masking = true; From 21e8e5fbcacf799b0485ab0203e66473a5d6e881 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 23:26:19 +0900 Subject: [PATCH 099/150] Move common table layout logic into `EditorTable` abstract class --- osu.Game/Screens/Edit/EditorTable.cs | 95 ++++++++++- .../Screens/Edit/Timing/ControlPointTable.cs | 120 +++----------- osu.Game/Screens/Edit/Verify/IssueTable.cs | 154 +++++------------- 3 files changed, 152 insertions(+), 217 deletions(-) diff --git a/osu.Game/Screens/Edit/EditorTable.cs b/osu.Game/Screens/Edit/EditorTable.cs index e5e2add384..ef1c88db9a 100644 --- a/osu.Game/Screens/Edit/EditorTable.cs +++ b/osu.Game/Screens/Edit/EditorTable.cs @@ -1,10 +1,15 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osuTK.Graphics; namespace osu.Game.Screens.Edit { @@ -16,7 +21,7 @@ namespace osu.Game.Screens.Edit protected const int TEXT_SIZE = 14; - protected readonly FillFlowContainer BackgroundFlow; + protected readonly FillFlowContainer BackgroundFlow; protected EditorTable() { @@ -26,7 +31,7 @@ namespace osu.Game.Screens.Edit Padding = new MarginPadding { Horizontal = horizontal_inset }; RowSize = new Dimension(GridSizeMode.Absolute, ROW_HEIGHT); - AddInternal(BackgroundFlow = new FillFlowContainer + AddInternal(BackgroundFlow = new FillFlowContainer { RelativeSizeAxes = Axes.Both, Depth = 1f, @@ -45,5 +50,91 @@ namespace osu.Game.Screens.Edit Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold); } } + + public class RowBackground : OsuClickableContainer + { + public readonly object Item; + + private const int fade_duration = 100; + + private readonly Box hoveredBackground; + + [Resolved] + private EditorClock clock { get; set; } + + public RowBackground(object item) + { + Item = item; + + RelativeSizeAxes = Axes.X; + Height = 25; + + AlwaysPresent = true; + + CornerRadius = 3; + Masking = true; + + Children = new Drawable[] + { + hoveredBackground = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + }, + }; + + // todo delete + Action = () => + { + }; + } + + private Color4 colourHover; + private Color4 colourSelected; + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + hoveredBackground.Colour = colourHover = colours.BlueDarker; + colourSelected = colours.YellowDarker; + } + + private bool selected; + + public bool Selected + { + get => selected; + set + { + if (value == selected) + return; + + selected = value; + updateState(); + } + } + + protected override bool OnHover(HoverEvent e) + { + updateState(); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + updateState(); + base.OnHoverLost(e); + } + + private void updateState() + { + hoveredBackground.FadeColour(selected ? colourSelected : colourHover, 450, Easing.OutQuint); + + if (selected || IsHovered) + hoveredBackground.FadeIn(fade_duration, Easing.OutQuint); + else + hoveredBackground.FadeOut(fade_duration, Easing.OutQuint); + } + } } } diff --git a/osu.Game/Screens/Edit/Timing/ControlPointTable.cs b/osu.Game/Screens/Edit/Timing/ControlPointTable.cs index 75e2bb1f5c..dd51056bf1 100644 --- a/osu.Game/Screens/Edit/Timing/ControlPointTable.cs +++ b/osu.Game/Screens/Edit/Timing/ControlPointTable.cs @@ -8,12 +8,9 @@ using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.Events; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Extensions; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osuTK; using osuTK.Graphics; @@ -25,6 +22,9 @@ namespace osu.Game.Screens.Edit.Timing [Resolved] private Bindable selectedGroup { get; set; } + [Resolved] + private EditorClock clock { get; set; } + public IEnumerable ControlGroups { set @@ -37,7 +37,14 @@ namespace osu.Game.Screens.Edit.Timing foreach (var group in value) { - BackgroundFlow.Add(new RowBackground(group)); + BackgroundFlow.Add(new RowBackground(group) + { + Action = () => + { + selectedGroup.Value = group; + clock.SeekSmoothlyTo(group.Time); + } + }); } Columns = createHeaders(); @@ -45,6 +52,16 @@ namespace osu.Game.Screens.Edit.Timing } } + protected override void LoadComplete() + { + base.LoadComplete(); + + selectedGroup.BindValueChanged(group => + { + foreach (var b in BackgroundFlow) b.Selected = b.Item == group.NewValue; + }, true); + } + private TableColumn[] createHeaders() { var columns = new List @@ -140,100 +157,5 @@ namespace osu.Game.Screens.Edit.Timing return null; } } - - public class RowBackground : OsuClickableContainer - { - private readonly ControlPointGroup controlGroup; - private const int fade_duration = 100; - - private readonly Box hoveredBackground; - - [Resolved] - private EditorClock clock { get; set; } - - [Resolved] - private Bindable selectedGroup { get; set; } - - public RowBackground(ControlPointGroup controlGroup) - { - this.controlGroup = controlGroup; - RelativeSizeAxes = Axes.X; - Height = 25; - - AlwaysPresent = true; - - CornerRadius = 3; - Masking = true; - - Children = new Drawable[] - { - hoveredBackground = new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - }, - }; - - Action = () => - { - selectedGroup.Value = controlGroup; - clock.SeekSmoothlyTo(controlGroup.Time); - }; - } - - private Color4 colourHover; - private Color4 colourSelected; - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - hoveredBackground.Colour = colourHover = colours.BlueDarker; - colourSelected = colours.YellowDarker; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - selectedGroup.BindValueChanged(group => { Selected = controlGroup == group.NewValue; }, true); - } - - private bool selected; - - protected bool Selected - { - get => selected; - set - { - if (value == selected) - return; - - selected = value; - updateState(); - } - } - - protected override bool OnHover(HoverEvent e) - { - updateState(); - return base.OnHover(e); - } - - protected override void OnHoverLost(HoverLostEvent e) - { - updateState(); - base.OnHoverLost(e); - } - - private void updateState() - { - hoveredBackground.FadeColour(selected ? colourSelected : colourHover, 450, Easing.OutQuint); - - if (selected || IsHovered) - hoveredBackground.FadeIn(fade_duration, Easing.OutQuint); - else - hoveredBackground.FadeOut(fade_duration, Easing.OutQuint); - } - } } } diff --git a/osu.Game/Screens/Edit/Verify/IssueTable.cs b/osu.Game/Screens/Edit/Verify/IssueTable.cs index 00570d363b..44244028c9 100644 --- a/osu.Game/Screens/Edit/Verify/IssueTable.cs +++ b/osu.Game/Screens/Edit/Verify/IssueTable.cs @@ -8,14 +8,10 @@ using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.Events; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Input.Bindings; using osu.Game.Rulesets.Edit.Checks.Components; -using osuTK.Graphics; namespace osu.Game.Screens.Edit.Verify { @@ -24,6 +20,15 @@ namespace osu.Game.Screens.Edit.Verify [Resolved] private Bindable selectedIssue { get; set; } + [Resolved] + private EditorClock clock { get; set; } + + [Resolved] + private EditorBeatmap editorBeatmap { get; set; } + + [Resolved] + private Editor editor { get; set; } + public IEnumerable Issues { set @@ -36,7 +41,25 @@ namespace osu.Game.Screens.Edit.Verify foreach (var issue in value) { - BackgroundFlow.Add(new RowBackground(issue)); + BackgroundFlow.Add(new RowBackground(issue) + { + Action = () => + { + selectedIssue.Value = issue; + + if (issue.Time != null) + { + clock.Seek(issue.Time.Value); + editor.OnPressed(GlobalAction.EditorComposeMode); + } + + if (!issue.HitObjects.Any()) + return; + + editorBeatmap.SelectedHitObjects.Clear(); + editorBeatmap.SelectedHitObjects.AddRange(issue.HitObjects); + }, + }); } Columns = createHeaders(); @@ -44,6 +67,16 @@ namespace osu.Game.Screens.Edit.Verify } } + protected override void LoadComplete() + { + base.LoadComplete(); + + selectedIssue.BindValueChanged(issue => + { + foreach (var b in BackgroundFlow) b.Selected = b.Item == issue.NewValue; + }, true); + } + private TableColumn[] createHeaders() { var columns = new List @@ -91,116 +124,5 @@ namespace osu.Game.Screens.Edit.Verify Margin = new MarginPadding(10) } }; - - public class RowBackground : OsuClickableContainer - { - private readonly Issue issue; - private const int fade_duration = 100; - - private readonly Box hoveredBackground; - - [Resolved] - private EditorClock clock { get; set; } - - [Resolved] - private Editor editor { get; set; } - - [Resolved] - private EditorBeatmap editorBeatmap { get; set; } - - [Resolved] - private Bindable selectedIssue { get; set; } - - public RowBackground(Issue issue) - { - this.issue = issue; - - RelativeSizeAxes = Axes.X; - Height = ROW_HEIGHT; - AlwaysPresent = true; - CornerRadius = 3; - Masking = true; - - Children = new Drawable[] - { - hoveredBackground = new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - }, - }; - - Action = () => - { - selectedIssue.Value = issue; - - if (issue.Time != null) - { - clock.Seek(issue.Time.Value); - editor.OnPressed(GlobalAction.EditorComposeMode); - } - - if (!issue.HitObjects.Any()) - return; - - editorBeatmap.SelectedHitObjects.Clear(); - editorBeatmap.SelectedHitObjects.AddRange(issue.HitObjects); - }; - } - - private Color4 colourHover; - private Color4 colourSelected; - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - hoveredBackground.Colour = colourHover = colours.BlueDarker; - colourSelected = colours.YellowDarker; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - selectedIssue.BindValueChanged(change => { Selected = issue == change.NewValue; }, true); - } - - private bool selected; - - protected bool Selected - { - get => selected; - set - { - if (value == selected) - return; - - selected = value; - updateState(); - } - } - - protected override bool OnHover(HoverEvent e) - { - updateState(); - return base.OnHover(e); - } - - protected override void OnHoverLost(HoverLostEvent e) - { - updateState(); - base.OnHoverLost(e); - } - - private void updateState() - { - hoveredBackground.FadeColour(selected ? colourSelected : colourHover, 450, Easing.OutQuint); - - if (selected || IsHovered) - hoveredBackground.FadeIn(fade_duration, Easing.OutQuint); - else - hoveredBackground.FadeOut(fade_duration, Easing.OutQuint); - } - } } } From cb4f64133eedde15134e64b9b0a0c584256df3aa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 23:30:20 +0900 Subject: [PATCH 100/150] Add xmldoc to interfaces --- osu.Game/Rulesets/Edit/Checks/Components/ICheck.cs | 3 +++ osu.Game/Rulesets/Edit/IBeatmapVerifier.cs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/osu.Game/Rulesets/Edit/Checks/Components/ICheck.cs b/osu.Game/Rulesets/Edit/Checks/Components/ICheck.cs index f355ae734e..f284240092 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/ICheck.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/ICheck.cs @@ -6,6 +6,9 @@ using osu.Game.Beatmaps; namespace osu.Game.Rulesets.Edit.Checks.Components { + /// + /// A specific check that can be run on a beatmap to verify or find issues. + /// public interface ICheck { /// diff --git a/osu.Game/Rulesets/Edit/IBeatmapVerifier.cs b/osu.Game/Rulesets/Edit/IBeatmapVerifier.cs index 2bafacefa3..61d8119635 100644 --- a/osu.Game/Rulesets/Edit/IBeatmapVerifier.cs +++ b/osu.Game/Rulesets/Edit/IBeatmapVerifier.cs @@ -7,6 +7,9 @@ using osu.Game.Rulesets.Edit.Checks.Components; namespace osu.Game.Rulesets.Edit { + /// + /// A class which can run against a beatmap and surface issues to the user which could go against known criteria or hinder gameplay. + /// public interface IBeatmapVerifier { public IEnumerable Run(IBeatmap beatmap); From 8282f38eb708884e765e14148aa254b21db5f4fd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Apr 2021 13:10:45 +0900 Subject: [PATCH 101/150] Fix volume controls not supporting key repeat --- osu.Game/Extensions/DrawableExtensions.cs | 8 +++-- .../Overlays/Volume/VolumeControlReceptor.cs | 32 +++++++++++++++---- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/osu.Game/Extensions/DrawableExtensions.cs b/osu.Game/Extensions/DrawableExtensions.cs index 1790eb608e..67b9e727a5 100644 --- a/osu.Game/Extensions/DrawableExtensions.cs +++ b/osu.Game/Extensions/DrawableExtensions.cs @@ -9,6 +9,9 @@ namespace osu.Game.Extensions { public static class DrawableExtensions { + public const double REPEAT_INTERVAL = 70; + public const double INITIAL_DELAY = 250; + /// /// Helper method that is used while doesn't support repetitions of . /// Simulates repetitions by continually invoking a delegate according to the default key repeat rate. @@ -19,12 +22,13 @@ namespace osu.Game.Extensions /// The which is handling the repeat. /// The to schedule repetitions on. /// The to be invoked once immediately and with every repetition. + /// The delay imposed on the first repeat. Defaults to . /// A which can be cancelled to stop the repeat events from firing. - public static ScheduledDelegate BeginKeyRepeat(this IKeyBindingHandler handler, Scheduler scheduler, Action action) + public static ScheduledDelegate BeginKeyRepeat(this IKeyBindingHandler handler, Scheduler scheduler, Action action, double initialRepeatDelay = INITIAL_DELAY) { action(); - ScheduledDelegate repeatDelegate = new ScheduledDelegate(action, handler.Time.Current + 250, 70); + ScheduledDelegate repeatDelegate = new ScheduledDelegate(action, handler.Time.Current + initialRepeatDelay, REPEAT_INTERVAL); scheduler.Add(repeatDelegate); return repeatDelegate; } diff --git a/osu.Game/Overlays/Volume/VolumeControlReceptor.cs b/osu.Game/Overlays/Volume/VolumeControlReceptor.cs index 3b39b74e00..34b86b2f81 100644 --- a/osu.Game/Overlays/Volume/VolumeControlReceptor.cs +++ b/osu.Game/Overlays/Volume/VolumeControlReceptor.cs @@ -6,6 +6,8 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Framework.Threading; +using osu.Game.Extensions; using osu.Game.Input.Bindings; namespace osu.Game.Overlays.Volume @@ -15,8 +17,30 @@ namespace osu.Game.Overlays.Volume public Func ActionRequested; public Func ScrollActionRequested; - public bool OnPressed(GlobalAction action) => - ActionRequested?.Invoke(action) ?? false; + private ScheduledDelegate keyRepeat; + + public bool OnPressed(GlobalAction action) + { + switch (action) + { + case GlobalAction.DecreaseVolume: + case GlobalAction.IncreaseVolume: + keyRepeat?.Cancel(); + keyRepeat = this.BeginKeyRepeat(Scheduler, () => ActionRequested?.Invoke(action), 150); + return true; + + case GlobalAction.ToggleMute: + ActionRequested?.Invoke(action); + return true; + } + + return false; + } + + public void OnReleased(GlobalAction action) + { + keyRepeat?.Cancel(); + } protected override bool OnScroll(ScrollEvent e) { @@ -27,9 +51,5 @@ namespace osu.Game.Overlays.Volume public bool OnScroll(GlobalAction action, float amount, bool isPrecise) => ScrollActionRequested?.Invoke(action, amount, isPrecise) ?? false; - - public void OnReleased(GlobalAction action) - { - } } } From b7d2821b5514f0b3e4b76064696354a0f56ae48a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Apr 2021 14:51:52 +0900 Subject: [PATCH 102/150] Display the centre marker above the waveform Gives it a bit more visibility. This is where it was meant to sit, but didn't consider using a proxy drawable to make it work previously. --- .../Edit/Compose/Components/Timeline/Timeline.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index 86a30b7e2d..aa5cc46e37 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -74,13 +74,18 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline [BackgroundDependencyLoader] private void load(IBindable beatmap, OsuColour colours, OsuConfigManager config) { + CentreMarker centreMarker; + + // We don't want the centre marker to scroll + AddInternal(centreMarker = new CentreMarker()); + AddRange(new Drawable[] { new Container { RelativeSizeAxes = Axes.Both, Depth = float.MaxValue, - Children = new Drawable[] + Children = new[] { waveform = new WaveformGraph { @@ -90,6 +95,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline MidColour = colours.BlueDark, HighColour = colours.BlueDarker, }, + centreMarker.CreateProxy(), ticks = new TimelineTickDisplay(), controlPoints = new TimelineControlPointDisplay(), new Box @@ -104,9 +110,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline }, }); - // We don't want the centre marker to scroll - AddInternal(new CentreMarker { Depth = float.MaxValue }); - waveformOpacity = config.GetBindable(OsuSetting.EditorWaveformOpacity); waveformOpacity.BindValueChanged(_ => updateWaveformOpacity(), true); From e543db9bee68f8d733b62f24a4c4ad8f1c046e8f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Apr 2021 14:56:27 +0900 Subject: [PATCH 103/150] Use additive blending for background box Doesn't make a huge difference but this was intended. --- .../Compose/Components/Timeline/TimelineBlueprintContainer.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index 7427473a35..7a3781a981 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -65,6 +65,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { Colour = Color4.Black, Depth = float.MaxValue, + Blending = BlendingParameters.Additive, }); } From a2094159421cc98d47b4af23a53ae8c4358091fe Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Apr 2021 16:52:29 +0900 Subject: [PATCH 104/150] Add "Barrel Roll" mod --- .../Mods/OsuModBarrelRoll.cs | 30 +++++++++++++++++++ osu.Game.Rulesets.Osu/OsuRuleset.cs | 1 + osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 3 ++ 3 files changed, 34 insertions(+) create mode 100644 osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs new file mode 100644 index 0000000000..1ba6c47f4c --- /dev/null +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs @@ -0,0 +1,30 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Bindables; +using osu.Game.Configuration; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Rulesets.Osu.Mods +{ + public class OsuModBarrelRoll : Mod, IUpdatableByPlayfield + { + [SettingSource("Roll speed", "Speed at which things rotate")] + public BindableNumber SpinSpeed { get; } = new BindableDouble(1) + { + MinValue = 0.1, + MaxValue = 20, + Precision = 0.1, + }; + + public override string Name => "Barrel Roll"; + public override string Acronym => "BR"; + public override double ScoreMultiplier => 1; + + public void Update(Playfield playfield) + { + playfield.Rotation = (float)(playfield.Time.Current / 1000 * SpinSpeed.Value); + } + } +} diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index d6375fa6e3..465d6d7155 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -185,6 +185,7 @@ namespace osu.Game.Rulesets.Osu new MultiMod(new OsuModGrow(), new OsuModDeflate()), new MultiMod(new ModWindUp(), new ModWindDown()), new OsuModTraceable(), + new OsuModBarrelRoll(), }; case ModType.System: diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index b1069149f3..ea3eb5eb5c 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -42,6 +42,9 @@ namespace osu.Game.Rulesets.Osu.UI public OsuPlayfield() { + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + InternalChildren = new Drawable[] { playfieldBorder = new PlayfieldBorder { RelativeSizeAxes = Axes.Both }, From a314f90d3732a0852edcd89fc8c1970012a6071f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Apr 2021 16:00:49 +0900 Subject: [PATCH 105/150] Allow timeline to govern the size of the rest of the editor content --- .../Compose/Components/Timeline/Timeline.cs | 5 + .../Components/Timeline/TimelineArea.cs | 11 +- .../Screens/Edit/EditorScreenWithTimeline.cs | 117 +++++++++++------- 3 files changed, 82 insertions(+), 51 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index aa5cc46e37..d06f977fc9 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -56,8 +56,13 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private Track track; + private const float timeline_height = 90; + public Timeline() { + RelativeSizeAxes = Axes.X; + Height = timeline_height; + ZoomDuration = 200; ZoomEasing = Easing.OutQuint; ScrollbarVisible = false; diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs index 0ec48e04c6..f144fd3a65 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs @@ -14,7 +14,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { public class TimelineArea : Container { - public readonly Timeline Timeline = new Timeline { RelativeSizeAxes = Axes.Both }; + public readonly Timeline Timeline = new Timeline(); protected override Container Content => Timeline; @@ -37,7 +37,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline }, new GridContainer { - RelativeSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, Content = new[] { new Drawable[] @@ -126,11 +127,15 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline Timeline }, }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + }, ColumnDimensions = new[] { new Dimension(GridSizeMode.AutoSize), new Dimension(GridSizeMode.AutoSize), - new Dimension(GridSizeMode.Distributed), + new Dimension(), } } }; diff --git a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs index 2d623a200c..b4b3aafc68 100644 --- a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs +++ b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs @@ -19,8 +19,6 @@ namespace osu.Game.Screens.Edit private const float vertical_margins = 10; private const float horizontal_margins = 20; - private const float timeline_height = 110; - private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor(); private Container timelineContainer; @@ -40,64 +38,86 @@ namespace osu.Game.Screens.Edit if (beatDivisor != null) this.beatDivisor.BindTo(beatDivisor); - Children = new Drawable[] + Child = new GridContainer { - mainContent = new Container + RelativeSizeAxes = Axes.Both, + RowDimensions = new[] { - Name = "Main content", - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding - { - Horizontal = horizontal_margins, - Top = vertical_margins + timeline_height, - Bottom = vertical_margins - }, - Child = spinner = new LoadingSpinner(true) - { - State = { Value = Visibility.Visible }, - }, + new Dimension(GridSizeMode.AutoSize), + new Dimension(), }, - new Container + Content = new[] { - Name = "Timeline", - RelativeSizeAxes = Axes.X, - Height = timeline_height, - Children = new Drawable[] + new Drawable[] { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black.Opacity(0.5f) - }, new Container { - Name = "Timeline content", - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Horizontal = horizontal_margins, Vertical = vertical_margins }, - Child = new GridContainer + Name = "Timeline", + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Content = new[] + new Box { - new Drawable[] - { - timelineContainer = new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Right = 5 }, - }, - new BeatDivisorControl(beatDivisor) { RelativeSizeAxes = Axes.Both } - }, + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black.Opacity(0.5f) }, - ColumnDimensions = new[] + new Container { - new Dimension(), - new Dimension(GridSizeMode.Absolute, 90), + Name = "Timeline content", + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Horizontal = horizontal_margins, Vertical = vertical_margins }, + Child = new GridContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Content = new[] + { + new Drawable[] + { + timelineContainer = new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Right = 5 }, + }, + new BeatDivisorControl(beatDivisor) { RelativeSizeAxes = Axes.Both } + }, + }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + }, + ColumnDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.Absolute, 90), + } + }, } + } + }, + }, + new Drawable[] + { + mainContent = new Container + { + Name = "Main content", + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding + { + Horizontal = horizontal_margins, + Top = vertical_margins, + Bottom = vertical_margins }, - } - } - }, + Child = spinner = new LoadingSpinner(true) + { + State = { Value = Visibility.Visible }, + }, + }, + }, + } }; } @@ -114,7 +134,8 @@ namespace osu.Game.Screens.Edit LoadComponentAsync(new TimelineArea { - RelativeSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, Children = new[] { CreateTimelineContent(), From 26110cd777e3c4880adc2a10c2bb49e1b64c7947 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Apr 2021 18:15:11 +0900 Subject: [PATCH 106/150] Fix timeline not receiving input (being eaten by composer) --- osu.Game/Screens/Edit/EditorScreenWithTimeline.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs index b4b3aafc68..26083e6a82 100644 --- a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs +++ b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs @@ -105,6 +105,7 @@ namespace osu.Game.Screens.Edit { Name = "Main content", RelativeSizeAxes = Axes.Both, + Depth = float.MaxValue, Padding = new MarginPadding { Horizontal = horizontal_margins, From ff2a37b7f492b16f135c026865e410b55c1095c7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Apr 2021 19:38:25 +0900 Subject: [PATCH 107/150] Add new colours for editor designs --- osu.Game/Graphics/OsuColour.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index 466d59b08b..c3b9b6006c 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -186,6 +186,13 @@ namespace osu.Game.Graphics public readonly Color4 GrayE = Color4Extensions.FromHex(@"eee"); public readonly Color4 GrayF = Color4Extensions.FromHex(@"fff"); + // in latest editor design logic, need to figure out where these sit... + public readonly Color4 Lime1 = Color4Extensions.FromHex(@"b2ff66"); + public readonly Color4 Orange1 = Color4Extensions.FromHex(@"ffd966"); + + // Content Background + public readonly Color4 B5 = Color4Extensions.FromHex(@"222a28"); + public readonly Color4 RedLighter = Color4Extensions.FromHex(@"ffeded"); public readonly Color4 RedLight = Color4Extensions.FromHex(@"ed7787"); public readonly Color4 Red = Color4Extensions.FromHex(@"ed1121"); From 1209c9fa32e0c0c98497748bb54f39760007408c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Apr 2021 19:39:12 +0900 Subject: [PATCH 108/150] Allow timeline to expand in height when control points are to be displayed --- .../Compose/Components/Timeline/Timeline.cs | 39 ++++++++++++++++--- .../Timeline/TimelineControlPointDisplay.cs | 6 --- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index d06f977fc9..bd7b426044 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -57,11 +57,11 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private Track track; private const float timeline_height = 90; + private const float timeline_expanded_height = 180; public Timeline() { RelativeSizeAxes = Axes.X; - Height = timeline_height; ZoomDuration = 200; ZoomEasing = Easing.OutQuint; @@ -86,9 +86,17 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline AddRange(new Drawable[] { + controlPoints = new TimelineControlPointDisplay + { + RelativeSizeAxes = Axes.X, + Height = timeline_expanded_height, + }, new Container { - RelativeSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.X, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Height = timeline_height, Depth = float.MaxValue, Children = new[] { @@ -102,7 +110,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline }, centreMarker.CreateProxy(), ticks = new TimelineTickDisplay(), - controlPoints = new TimelineControlPointDisplay(), new Box { Name = "zero marker", @@ -116,13 +123,35 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline }); waveformOpacity = config.GetBindable(OsuSetting.EditorWaveformOpacity); + Beatmap.BindTo(beatmap); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + waveformOpacity.BindValueChanged(_ => updateWaveformOpacity(), true); WaveformVisible.ValueChanged += _ => updateWaveformOpacity(); - ControlPointsVisible.ValueChanged += visible => controlPoints.FadeTo(visible.NewValue ? 1 : 0, 200, Easing.OutQuint); TicksVisible.ValueChanged += visible => ticks.FadeTo(visible.NewValue ? 1 : 0, 200, Easing.OutQuint); + ControlPointsVisible.BindValueChanged(visible => + { + if (visible.NewValue) + { + this.ResizeHeightTo(timeline_expanded_height, 200, Easing.OutQuint); + + // delay the fade in else masking looks weird. + controlPoints.Delay(180).FadeIn(400, Easing.OutQuint); + } + else + { + controlPoints.FadeOut(200, Easing.OutQuint); + + // likewise, delay the resize until the fade is complete. + this.Delay(180).ResizeHeightTo(timeline_height, 200, Easing.OutQuint); + } + }, true); - Beatmap.BindTo(beatmap); Beatmap.BindValueChanged(b => { waveform.Waveform = b.NewValue.Waveform; diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineControlPointDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineControlPointDisplay.cs index 18600bcdee..8520567fa9 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineControlPointDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineControlPointDisplay.cs @@ -4,7 +4,6 @@ using System.Collections.Specialized; using System.Linq; using osu.Framework.Bindables; -using osu.Framework.Graphics; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts; @@ -17,11 +16,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { private readonly IBindableList controlPointGroups = new BindableList(); - public TimelineControlPointDisplay() - { - RelativeSizeAxes = Axes.Both; - } - protected override void LoadBeatmap(EditorBeatmap beatmap) { base.LoadBeatmap(beatmap); From a8df2388eb184c8abb47f4e841a954587d4fef25 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Apr 2021 19:39:27 +0900 Subject: [PATCH 109/150] Update design for TimingControlPoint --- .../ControlPoints/TimingControlPoint.cs | 2 +- .../Timeline/TimelineControlPointGroup.cs | 2 ++ .../Components/Timeline/TimingPointPiece.cs | 20 +++++++++---------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs index 580642f593..ec20328fab 100644 --- a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs @@ -20,7 +20,7 @@ namespace osu.Game.Beatmaps.ControlPoints /// private const double default_beat_length = 60000.0 / 60.0; - public override Color4 GetRepresentingColour(OsuColour colours) => colours.YellowDark; + public override Color4 GetRepresentingColour(OsuColour colours) => colours.Orange1; public static readonly TimingControlPoint DEFAULT = new TimingControlPoint { diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineControlPointGroup.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineControlPointGroup.cs index fb69f16792..c4beb40f92 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineControlPointGroup.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineControlPointGroup.cs @@ -27,6 +27,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline RelativeSizeAxes = Axes.Y; AutoSizeAxes = Axes.X; + Origin = Anchor.TopCentre; + X = (float)group.Time; } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimingPointPiece.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimingPointPiece.cs index ba94916458..cd1470aa0a 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimingPointPiece.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimingPointPiece.cs @@ -3,15 +3,12 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osuTK.Graphics; namespace osu.Game.Screens.Edit.Compose.Components.Timeline { @@ -31,26 +28,27 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline [BackgroundDependencyLoader] private void load(OsuColour colours) { - Origin = Anchor.CentreLeft; - Anchor = Anchor.CentreLeft; + Margin = new MarginPadding { Vertical = 10 }; + + const float corner_radius = 5; AutoSizeAxes = Axes.Both; - - Color4 colour = point.GetRepresentingColour(colours); + Masking = true; + CornerRadius = corner_radius; InternalChildren = new Drawable[] { new Box { - Alpha = 0.9f, - Colour = ColourInfo.GradientHorizontal(colour, colour.Opacity(0.5f)), + Colour = point.GetRepresentingColour(colours), RelativeSizeAxes = Axes.Both, }, bpmText = new OsuSpriteText { Alpha = 0.9f, - Padding = new MarginPadding(3), - Font = OsuFont.Default.With(size: 40) + Padding = new MarginPadding { Vertical = 3, Horizontal = 6 }, + Font = OsuFont.Default.With(size: 20, weight: FontWeight.SemiBold), + Colour = colours.B5, } }; From f9b1b7fe255370fb502aebaea0fb5c5327e569b1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Apr 2021 20:10:58 +0900 Subject: [PATCH 110/150] Update SamplePointPiece design --- .../Components/Timeline/SamplePointPiece.cs | 61 ++++++++++--------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs index 0f11fb1126..9461f5e885 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs @@ -3,9 +3,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps.ControlPoints; @@ -23,7 +21,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private readonly BindableNumber volume; private OsuSpriteText text; - private Box volumeBox; + private Container volumeBox; + + private const int max_volume_height = 22; public SamplePointPiece(SampleControlPoint samplePoint) { @@ -35,8 +35,10 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline [BackgroundDependencyLoader] private void load(OsuColour colours) { - Origin = Anchor.TopLeft; - Anchor = Anchor.TopLeft; + Margin = new MarginPadding { Vertical = 5 }; + + Origin = Anchor.BottomCentre; + Anchor = Anchor.BottomCentre; AutoSizeAxes = Axes.X; RelativeSizeAxes = Axes.Y; @@ -45,40 +47,43 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline InternalChildren = new Drawable[] { + volumeBox = new Circle + { + CornerRadius = 5, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Y = -20, + Width = 10, + Colour = colour, + }, new Container { - RelativeSizeAxes = Axes.Y, - Width = 20, + AutoSizeAxes = Axes.X, + Height = 16, + Masking = true, + CornerRadius = 8, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, Children = new Drawable[] { - volumeBox = new Box - { - X = 2, - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Colour = ColourInfo.GradientVertical(colour, Color4.Black), - RelativeSizeAxes = Axes.Both, - }, new Box { - Colour = colour.Lighten(0.2f), - Width = 2, - RelativeSizeAxes = Axes.Y, + Colour = colour, + RelativeSizeAxes = Axes.Both, }, + text = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Padding = new MarginPadding(5), + Font = OsuFont.Default.With(size: 12, weight: FontWeight.SemiBold), + Colour = colours.B5, + } } }, - text = new OsuSpriteText - { - X = 2, - Y = -5, - Anchor = Anchor.BottomLeft, - Alpha = 0.9f, - Rotation = -90, - Font = OsuFont.Default.With(weight: FontWeight.SemiBold) - } }; - volume.BindValueChanged(volume => volumeBox.Height = volume.NewValue / 100f, true); + volume.BindValueChanged(volume => volumeBox.Height = max_volume_height * volume.NewValue / 100f, true); bank.BindValueChanged(bank => text.Text = bank.NewValue, true); } } From 99f05253fd2f0cc7634e06571666af28b24b3e2a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Apr 2021 20:11:16 +0900 Subject: [PATCH 111/150] Adjust timeline sizing to closer match designs (but not 1:1 yet) --- .../Edit/Compose/Components/Timeline/Timeline.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index bd7b426044..d688ad511f 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -56,8 +56,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private Track track; - private const float timeline_height = 90; - private const float timeline_expanded_height = 180; + private const float timeline_height = 72; + private const float timeline_expanded_height = 150; public Timeline() { @@ -74,6 +74,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private TimelineControlPointDisplay controlPoints; + private Container mainContent; + private Bindable waveformOpacity; [BackgroundDependencyLoader] @@ -91,11 +93,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline RelativeSizeAxes = Axes.X, Height = timeline_expanded_height, }, - new Container + mainContent = new Container { RelativeSizeAxes = Axes.X, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, Height = timeline_height, Depth = float.MaxValue, Children = new[] @@ -139,6 +139,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline if (visible.NewValue) { this.ResizeHeightTo(timeline_expanded_height, 200, Easing.OutQuint); + mainContent.MoveToY(36, 200, Easing.OutQuint); // delay the fade in else masking looks weird. controlPoints.Delay(180).FadeIn(400, Easing.OutQuint); @@ -149,6 +150,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline // likewise, delay the resize until the fade is complete. this.Delay(180).ResizeHeightTo(timeline_height, 200, Easing.OutQuint); + mainContent.Delay(180).MoveToY(0, 200, Easing.OutQuint); } }, true); From afbb674e526a12148e4c328e5b5a5618720fe081 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Apr 2021 20:11:32 +0900 Subject: [PATCH 112/150] TopLeft align check buttons so they don't move while interacting with them --- .../Screens/Edit/Compose/Components/Timeline/TimelineArea.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs index f144fd3a65..ee3543354f 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs @@ -56,11 +56,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline }, new FillFlowContainer { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, AutoSizeAxes = Axes.Y, Width = 160, - Padding = new MarginPadding { Horizontal = 10 }, + Padding = new MarginPadding(10), Direction = FillDirection.Vertical, Spacing = new Vector2(0, 4), Children = new[] From be08b9d1eff63dd7ec47c9e26323b90c3e63d208 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Apr 2021 20:54:29 +0900 Subject: [PATCH 113/150] Combine logic of Difficulty and Timing pieces where feasible --- .../ControlPoints/DifficultyControlPoint.cs | 2 +- .../Timeline/DifficultyPointPiece.cs | 58 +++---------------- .../Components/Timeline/TimingPointPiece.cs | 37 +----------- .../Components/Timeline/TopPointPiece.cs | 55 ++++++++++++++++++ 4 files changed, 68 insertions(+), 84 deletions(-) create mode 100644 osu.Game/Screens/Edit/Compose/Components/Timeline/TopPointPiece.cs diff --git a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs index 73337ab6f5..8a6cfaf688 100644 --- a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs @@ -25,7 +25,7 @@ namespace osu.Game.Beatmaps.ControlPoints MaxValue = 10 }; - public override Color4 GetRepresentingColour(OsuColour colours) => colours.GreenDark; + public override Color4 GetRepresentingColour(OsuColour colours) => colours.Lime1; /// /// The speed multiplier at this control point. diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs index 510ba8c094..3248936765 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs @@ -1,67 +1,27 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osuTK.Graphics; namespace osu.Game.Screens.Edit.Compose.Components.Timeline { - public class DifficultyPointPiece : CompositeDrawable + public class DifficultyPointPiece : TopPointPiece { - private readonly DifficultyControlPoint difficultyPoint; - - private OsuSpriteText speedMultiplierText; private readonly BindableNumber speedMultiplier; - public DifficultyPointPiece(DifficultyControlPoint difficultyPoint) + public DifficultyPointPiece(DifficultyControlPoint point) + : base(point) { - this.difficultyPoint = difficultyPoint; - speedMultiplier = difficultyPoint.SpeedMultiplierBindable.GetBoundCopy(); + speedMultiplier = point.SpeedMultiplierBindable.GetBoundCopy(); + + Y = Height; } - [BackgroundDependencyLoader] - private void load(OsuColour colours) + protected override void LoadComplete() { - RelativeSizeAxes = Axes.Y; - AutoSizeAxes = Axes.X; - - Color4 colour = difficultyPoint.GetRepresentingColour(colours); - - InternalChildren = new Drawable[] - { - new Box - { - Colour = colour, - Width = 2, - RelativeSizeAxes = Axes.Y, - }, - new Container - { - AutoSizeAxes = Axes.Both, - Children = new Drawable[] - { - new Box - { - Colour = colour, - RelativeSizeAxes = Axes.Both, - }, - speedMultiplierText = new OsuSpriteText - { - Font = OsuFont.Default.With(weight: FontWeight.Bold), - Colour = Color4.White, - } - } - }, - }; - - speedMultiplier.BindValueChanged(multiplier => speedMultiplierText.Text = $"{multiplier.NewValue:n2}x", true); + base.LoadComplete(); + speedMultiplier.BindValueChanged(multiplier => Label.Text = $"{multiplier.NewValue:n2}x", true); } } } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimingPointPiece.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimingPointPiece.cs index cd1470aa0a..fa51281c55 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimingPointPiece.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimingPointPiece.cs @@ -3,58 +3,27 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; namespace osu.Game.Screens.Edit.Compose.Components.Timeline { - public class TimingPointPiece : CompositeDrawable + public class TimingPointPiece : TopPointPiece { - private readonly TimingControlPoint point; - private readonly BindableNumber beatLength; - private OsuSpriteText bpmText; public TimingPointPiece(TimingControlPoint point) + : base(point) { - this.point = point; beatLength = point.BeatLengthBindable.GetBoundCopy(); } [BackgroundDependencyLoader] private void load(OsuColour colours) { - Margin = new MarginPadding { Vertical = 10 }; - - const float corner_radius = 5; - - AutoSizeAxes = Axes.Both; - Masking = true; - CornerRadius = corner_radius; - - InternalChildren = new Drawable[] - { - new Box - { - Colour = point.GetRepresentingColour(colours), - RelativeSizeAxes = Axes.Both, - }, - bpmText = new OsuSpriteText - { - Alpha = 0.9f, - Padding = new MarginPadding { Vertical = 3, Horizontal = 6 }, - Font = OsuFont.Default.With(size: 20, weight: FontWeight.SemiBold), - Colour = colours.B5, - } - }; - beatLength.BindValueChanged(beatLength => { - bpmText.Text = $"{60000 / beatLength.NewValue:n1} BPM"; + Label.Text = $"{60000 / beatLength.NewValue:n1} BPM"; }, true); } } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TopPointPiece.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TopPointPiece.cs new file mode 100644 index 0000000000..60a9e1ed66 --- /dev/null +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TopPointPiece.cs @@ -0,0 +1,55 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Screens.Edit.Compose.Components.Timeline +{ + public class TopPointPiece : CompositeDrawable + { + private readonly ControlPoint point; + + protected OsuSpriteText Label { get; private set; } + + public TopPointPiece(ControlPoint point) + { + this.point = point; + AutoSizeAxes = Axes.X; + Height = 16; + Margin = new MarginPadding(4); + + Masking = true; + CornerRadius = Height / 2; + + Origin = Anchor.TopCentre; + Anchor = Anchor.TopCentre; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + InternalChildren = new Drawable[] + { + new Box + { + Colour = point.GetRepresentingColour(colours), + RelativeSizeAxes = Axes.Both, + }, + Label = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Padding = new MarginPadding(3), + Font = OsuFont.Default.With(size: 14, weight: FontWeight.SemiBold), + Colour = colours.B5, + } + }; + } + } +} From d1c68cb92b0efe025fcaadd28f9c61ea4f4a17bf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Apr 2021 20:55:12 +0900 Subject: [PATCH 114/150] Simplify content creation of Timeline / TimelineArea --- .../Edit/Compose/Components/Timeline/Timeline.cs | 8 ++++++-- .../Compose/Components/Timeline/TimelineArea.cs | 16 ++++++++++++---- .../Screens/Edit/EditorScreenWithTimeline.cs | 10 +--------- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index d688ad511f..55fb557474 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -23,6 +23,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline [Cached] public class Timeline : ZoomableScrollContainer, IPositionSnapProvider { + private readonly Drawable userContent; public readonly Bindable WaveformVisible = new Bindable(); public readonly Bindable ControlPointsVisible = new Bindable(); @@ -57,10 +58,12 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private Track track; private const float timeline_height = 72; - private const float timeline_expanded_height = 150; + private const float timeline_expanded_height = 156; - public Timeline() + public Timeline(Drawable userContent) { + this.userContent = userContent; + RelativeSizeAxes = Axes.X; ZoomDuration = 200; @@ -118,6 +121,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline Origin = Anchor.TopCentre, Colour = colours.YellowDarker, }, + userContent, } }, }); diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs index ee3543354f..1541ceade5 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs @@ -12,11 +12,19 @@ using osuTK; namespace osu.Game.Screens.Edit.Compose.Components.Timeline { - public class TimelineArea : Container + public class TimelineArea : CompositeDrawable { - public readonly Timeline Timeline = new Timeline(); + public Timeline Timeline; - protected override Container Content => Timeline; + private readonly Drawable userContent; + + public TimelineArea(Drawable content = null) + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + userContent = content ?? Drawable.Empty(); + } [BackgroundDependencyLoader] private void load() @@ -122,7 +130,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline } } }, - Timeline + Timeline = new Timeline(userContent), }, }, RowDimensions = new[] diff --git a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs index 26083e6a82..0d59a7a1a8 100644 --- a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs +++ b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs @@ -133,15 +133,7 @@ namespace osu.Game.Screens.Edit mainContent.Add(content); content.FadeInFromZero(300, Easing.OutQuint); - LoadComponentAsync(new TimelineArea - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new[] - { - CreateTimelineContent(), - } - }, t => + LoadComponentAsync(new TimelineArea(CreateTimelineContent()), t => { timelineContainer.Add(t); OnTimelineLoaded(t); From 9dea0ae935ac4d4464bfc31a49ac42a4fe86f817 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Apr 2021 20:55:23 +0900 Subject: [PATCH 115/150] Update test scene to be able to see a bit more --- osu.Game.Tests/Visual/Editing/TimelineTestScene.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs b/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs index 88b4614791..4aed445d9d 100644 --- a/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs +++ b/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs @@ -53,13 +53,10 @@ namespace osu.Game.Tests.Visual.Editing new AudioVisualiser(), } }, - TimelineArea = new TimelineArea + TimelineArea = new TimelineArea(CreateTestComponent()) { - Child = CreateTestComponent(), Anchor = Anchor.Centre, Origin = Anchor.Centre, - RelativeSizeAxes = Axes.X, - Size = new Vector2(0.8f, 100), } }); } From 505dc15e0389efb9586ac655f682b727e991dd30 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 14 Apr 2021 23:22:38 +0300 Subject: [PATCH 116/150] Add failing test case --- .../Background/TestSceneUserDimBackgrounds.cs | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs index 655b426e43..c0aab1b7ef 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs @@ -65,6 +65,21 @@ namespace osu.Game.Tests.Visual.Background stack.Push(songSelect = new DummySongSelect()); }); + /// + /// User settings should always be ignored on song select screen. + /// + [Test] + public void TestUserSettingsIgnoredOnSongSelect() + { + setupUserSettings(); + AddAssert("Screen is undimmed", () => songSelect.IsBackgroundUndimmed()); + AddAssert("Screen using background blur", () => songSelect.IsBackgroundBlur()); + performFullSetup(); + AddStep("Exit to song select", () => player.Exit()); + AddUntilStep("Screen is undimmed", () => songSelect.IsBackgroundUndimmed()); + AddUntilStep("Screen using background blur", () => songSelect.IsBackgroundBlur()); + } + /// /// Check if properly triggers the visual settings preview when a user hovers over the visual settings panel. /// @@ -227,17 +242,6 @@ namespace osu.Game.Tests.Visual.Background songSelect.IsBackgroundUndimmed() && songSelect.IsBackgroundCurrent() && songSelect.CheckBackgroundBlur(results.ExpectedBackgroundBlur)); } - /// - /// Check if background gets undimmed and unblurred when leaving for - /// - [Test] - public void TestTransitionOut() - { - performFullSetup(); - AddStep("Exit to song select", () => player.Exit()); - AddUntilStep("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && songSelect.IsBlurCorrect()); - } - /// /// Check if hovering on the visual settings dialogue after resuming from player still previews the background dim. /// @@ -333,7 +337,7 @@ namespace osu.Game.Tests.Visual.Background public bool IsBackgroundVisible() => background.CurrentAlpha == 1; - public bool IsBlurCorrect() => background.CurrentBlur == new Vector2(BACKGROUND_BLUR); + public bool IsBackgroundBlur() => background.CurrentBlur == new Vector2(BACKGROUND_BLUR); public bool CheckBackgroundBlur(Vector2 expected) => background.CurrentBlur == expected; From a5fa14ac4ae010b77a66edd79fe394aeddbad022 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 14 Apr 2021 23:17:06 +0300 Subject: [PATCH 117/150] Ignore user settings on background screen beatmap by default --- osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs index 10d381b8b7..02166644ab 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs @@ -50,6 +50,9 @@ namespace osu.Game.Screens.Backgrounds InternalChild = dimmable = CreateFadeContainer(); + // Beatmap background screens should not apply user settings by default. + IgnoreUserSettings.Value = true; + dimmable.IgnoreUserSettings.BindTo(IgnoreUserSettings); dimmable.IsBreakTime.BindTo(IsBreakTime); dimmable.BlurAmount.BindTo(BlurAmount); From 7a9ff2ab380030871d434a1af6b0ae5c34ad9436 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 15 Apr 2021 00:48:15 +0300 Subject: [PATCH 118/150] Use until steps instead Ah.. --- .../Visual/Background/TestSceneUserDimBackgrounds.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs index c0aab1b7ef..f89988cd1a 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs @@ -72,8 +72,8 @@ namespace osu.Game.Tests.Visual.Background public void TestUserSettingsIgnoredOnSongSelect() { setupUserSettings(); - AddAssert("Screen is undimmed", () => songSelect.IsBackgroundUndimmed()); - AddAssert("Screen using background blur", () => songSelect.IsBackgroundBlur()); + AddUntilStep("Screen is undimmed", () => songSelect.IsBackgroundUndimmed()); + AddUntilStep("Screen using background blur", () => songSelect.IsBackgroundBlur()); performFullSetup(); AddStep("Exit to song select", () => player.Exit()); AddUntilStep("Screen is undimmed", () => songSelect.IsBackgroundUndimmed()); From 362a5a39d0068d3a143fd245ef80a053995c6a8c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 13:06:52 +0900 Subject: [PATCH 119/150] Scale the playfield to avoid off-screen objects --- osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs index 1ba6c47f4c..d646f41588 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs @@ -4,11 +4,14 @@ using osu.Framework.Bindables; using osu.Game.Configuration; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.UI; +using osuTK; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModBarrelRoll : Mod, IUpdatableByPlayfield + public class OsuModBarrelRoll : Mod, IUpdatableByPlayfield, IApplicableToDrawableRuleset { [SettingSource("Roll speed", "Speed at which things rotate")] public BindableNumber SpinSpeed { get; } = new BindableDouble(1) @@ -26,5 +29,11 @@ namespace osu.Game.Rulesets.Osu.Mods { playfield.Rotation = (float)(playfield.Time.Current / 1000 * SpinSpeed.Value); } + + public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) + { + // scale the playfield to allow all hitobjects to stay within the visible region. + drawableRuleset.Playfield.Scale = new Vector2(OsuPlayfield.BASE_SIZE.Y / OsuPlayfield.BASE_SIZE.X); + } } } From 0d32290cd529cceb63e0fed31b3d46aeb4b2bca4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 13:15:52 +0900 Subject: [PATCH 120/150] Show roll speed in rotations-per-minute --- osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs index d646f41588..7cdfdb3c4a 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs @@ -13,12 +13,12 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModBarrelRoll : Mod, IUpdatableByPlayfield, IApplicableToDrawableRuleset { - [SettingSource("Roll speed", "Speed at which things rotate")] - public BindableNumber SpinSpeed { get; } = new BindableDouble(1) + [SettingSource("Roll speed", "Rotations per minute")] + public BindableNumber SpinSpeed { get; } = new BindableDouble(0.5) { - MinValue = 0.1, - MaxValue = 20, - Precision = 0.1, + MinValue = 0.02, + MaxValue = 4, + Precision = 0.01, }; public override string Name => "Barrel Roll"; @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Osu.Mods public void Update(Playfield playfield) { - playfield.Rotation = (float)(playfield.Time.Current / 1000 * SpinSpeed.Value); + playfield.Rotation = 360 * (float)(playfield.Time.Current / 60000 * SpinSpeed.Value); } public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) From 175b8da2b27002c262d53cfa5c8f44d46034abbf Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 15 Apr 2021 00:04:38 +0300 Subject: [PATCH 121/150] Inverse ignore user settings bindable to "apply user settings" instead --- .../Background/TestSceneUserDimBackgrounds.cs | 16 ++++++++-------- .../Background/TestSceneUserDimContainer.cs | 2 +- osu.Game/Graphics/Containers/UserDimContainer.cs | 8 ++++---- osu.Game/Rulesets/Mods/ModCinema.cs | 4 ++-- .../Backgrounds/BackgroundScreenBeatmap.cs | 15 ++++++--------- osu.Game/Screens/Edit/Editor.cs | 2 +- osu.Game/Screens/Play/DimmableStoryboard.cs | 4 ++-- osu.Game/Screens/Play/Player.cs | 4 ++-- osu.Game/Screens/Play/PlayerLoader.cs | 6 +++--- 9 files changed, 29 insertions(+), 32 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs index f89988cd1a..d915442570 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs @@ -157,9 +157,9 @@ namespace osu.Game.Tests.Visual.Background { performFullSetup(); AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); - AddStep("Disable user dim", () => songSelect.IgnoreUserSettings.Value = true); + AddStep("Disable user dim", () => songSelect.ApplyUserSettings.Value = false); AddUntilStep("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && songSelect.IsUserBlurDisabled()); - AddStep("Enable user dim", () => songSelect.IgnoreUserSettings.Value = false); + AddStep("Enable user dim", () => songSelect.ApplyUserSettings.Value = true); AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); } @@ -176,10 +176,10 @@ namespace osu.Game.Tests.Visual.Background player.ReplacesBackground.Value = true; player.StoryboardEnabled.Value = true; }); - AddStep("Enable user dim", () => player.DimmableStoryboard.IgnoreUserSettings.Value = false); + AddStep("Enable user dim", () => player.DimmableStoryboard.ApplyUserSettings.Value = true); AddStep("Set dim level to 1", () => songSelect.DimLevel.Value = 1f); AddUntilStep("Storyboard is invisible", () => !player.IsStoryboardVisible); - AddStep("Disable user dim", () => player.DimmableStoryboard.IgnoreUserSettings.Value = true); + AddStep("Disable user dim", () => player.DimmableStoryboard.ApplyUserSettings.Value = false); AddUntilStep("Storyboard is visible", () => player.IsStoryboardVisible); } @@ -195,8 +195,8 @@ namespace osu.Game.Tests.Visual.Background AddStep("Ignore user settings", () => { - player.ApplyToBackground(b => b.IgnoreUserSettings.Value = true); - player.DimmableStoryboard.IgnoreUserSettings.Value = true; + player.ApplyToBackground(b => b.ApplyUserSettings.Value = false); + player.DimmableStoryboard.ApplyUserSettings.Value = false; }); AddUntilStep("Storyboard is visible", () => player.IsStoryboardVisible); AddUntilStep("Background is invisible", () => songSelect.IsBackgroundInvisible()); @@ -308,11 +308,11 @@ namespace osu.Game.Tests.Visual.Background protected override BackgroundScreen CreateBackground() { background = new FadeAccessibleBackground(Beatmap.Value); - IgnoreUserSettings.BindTo(background.IgnoreUserSettings); + ApplyUserSettings.BindTo(background.ApplyUserSettings); return background; } - public readonly Bindable IgnoreUserSettings = new Bindable(); + public readonly Bindable ApplyUserSettings = new Bindable(); public readonly Bindable DimLevel = new BindableDouble(); public readonly Bindable BlurLevel = new BindableDouble(); diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs index fede99f450..9c3a044f18 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs @@ -94,7 +94,7 @@ namespace osu.Game.Tests.Visual.Background AddStep("set dim level 0.6", () => userDimContainer.UserDimLevel.Value = test_user_dim); AddUntilStep("dim reached", () => userDimContainer.DimEqual(test_user_dim)); - AddStep("ignore settings", () => userDimContainer.IgnoreUserSettings.Value = true); + AddStep("ignore settings", () => userDimContainer.ApplyUserSettings.Value = false); AddUntilStep("no dim", () => userDimContainer.DimEqual(0)); AddStep("set break", () => isBreakTime.Value = true); AddAssert("no dim", () => userDimContainer.DimEqual(0)); diff --git a/osu.Game/Graphics/Containers/UserDimContainer.cs b/osu.Game/Graphics/Containers/UserDimContainer.cs index 4e555ac1eb..a3d59da961 100644 --- a/osu.Game/Graphics/Containers/UserDimContainer.cs +++ b/osu.Game/Graphics/Containers/UserDimContainer.cs @@ -24,9 +24,9 @@ namespace osu.Game.Graphics.Containers protected const double BACKGROUND_FADE_DURATION = 800; /// - /// Whether or not user-configured settings relating to brightness of elements should be ignored + /// Whether or not user-configured effect settings should be applied to this container. /// - public readonly Bindable IgnoreUserSettings = new Bindable(); + public readonly Bindable ApplyUserSettings = new Bindable(true); /// /// Whether or not the storyboard loaded should completely hide the background behind it. @@ -52,7 +52,7 @@ namespace osu.Game.Graphics.Containers private float breakLightening => LightenDuringBreaks.Value && IsBreakTime.Value ? BREAK_LIGHTEN_AMOUNT : 0; - protected float DimLevel => Math.Max(!IgnoreUserSettings.Value ? (float)UserDimLevel.Value - breakLightening : 0, 0); + protected float DimLevel => Math.Max(ApplyUserSettings.Value ? (float)UserDimLevel.Value - breakLightening : 0, 0); protected override Container Content => dimContent; @@ -78,7 +78,7 @@ namespace osu.Game.Graphics.Containers IsBreakTime.ValueChanged += _ => UpdateVisuals(); ShowStoryboard.ValueChanged += _ => UpdateVisuals(); StoryboardReplacesBackground.ValueChanged += _ => UpdateVisuals(); - IgnoreUserSettings.ValueChanged += _ => UpdateVisuals(); + ApplyUserSettings.ValueChanged += _ => UpdateVisuals(); } protected override void LoadComplete() diff --git a/osu.Game/Rulesets/Mods/ModCinema.cs b/osu.Game/Rulesets/Mods/ModCinema.cs index c78088ba2d..93062218fe 100644 --- a/osu.Game/Rulesets/Mods/ModCinema.cs +++ b/osu.Game/Rulesets/Mods/ModCinema.cs @@ -37,8 +37,8 @@ namespace osu.Game.Rulesets.Mods public void ApplyToPlayer(Player player) { - player.ApplyToBackground(b => b.IgnoreUserSettings.Value = true); - player.DimmableStoryboard.IgnoreUserSettings.Value = true; + player.ApplyToBackground(b => b.ApplyUserSettings.Value = false); + player.DimmableStoryboard.ApplyUserSettings.Value = false; player.BreakOverlay.Hide(); } diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs index 02166644ab..553a8f3689 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs @@ -27,9 +27,9 @@ namespace osu.Game.Screens.Backgrounds private WorkingBeatmap beatmap; /// - /// Whether or not user-configured settings relating to brightness of elements should be ignored + /// Whether or not user-configured effect settings should be applied to this background screen. /// - public readonly Bindable IgnoreUserSettings = new Bindable(); + public readonly Bindable ApplyUserSettings = new Bindable(); public readonly Bindable StoryboardReplacesBackground = new Bindable(); @@ -50,10 +50,7 @@ namespace osu.Game.Screens.Backgrounds InternalChild = dimmable = CreateFadeContainer(); - // Beatmap background screens should not apply user settings by default. - IgnoreUserSettings.Value = true; - - dimmable.IgnoreUserSettings.BindTo(IgnoreUserSettings); + dimmable.ApplyUserSettings.BindTo(ApplyUserSettings); dimmable.IsBreakTime.BindTo(IsBreakTime); dimmable.BlurAmount.BindTo(BlurAmount); @@ -151,7 +148,7 @@ namespace osu.Game.Screens.Backgrounds /// /// As an optimisation, we add the two blur portions to be applied rather than actually applying two separate blurs. /// - private Vector2 blurTarget => !IgnoreUserSettings.Value + private Vector2 blurTarget => ApplyUserSettings.Value ? new Vector2(BlurAmount.Value + (float)userBlurLevel.Value * USER_BLUR_FACTOR) : new Vector2(BlurAmount.Value); @@ -170,8 +167,8 @@ namespace osu.Game.Screens.Backgrounds } protected override bool ShowDimContent - // The background needs to be hidden in the case of it being replaced by the storyboard - => (!ShowStoryboard.Value && !IgnoreUserSettings.Value) || !StoryboardReplacesBackground.Value; + // The background needs to be hidden in the case of it being replaced by the storyboard. + => (ApplyUserSettings.Value && !ShowStoryboard.Value) || !StoryboardReplacesBackground.Value; protected override void UpdateVisuals() { diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index fffea65456..99c6cce26f 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -467,7 +467,7 @@ namespace osu.Game.Screens.Edit // todo: temporary. we want to be applying dim using the UserDimContainer eventually. b.FadeColour(Color4.DarkGray, 500); - b.IgnoreUserSettings.Value = true; + b.ApplyUserSettings.Value = false; b.BlurAmount.Value = 0; }); diff --git a/osu.Game/Screens/Play/DimmableStoryboard.cs b/osu.Game/Screens/Play/DimmableStoryboard.cs index 58eb95b7c6..105f672847 100644 --- a/osu.Game/Screens/Play/DimmableStoryboard.cs +++ b/osu.Game/Screens/Play/DimmableStoryboard.cs @@ -38,14 +38,14 @@ namespace osu.Game.Screens.Play base.LoadComplete(); } - protected override bool ShowDimContent => IgnoreUserSettings.Value || (ShowStoryboard.Value && DimLevel < 1); + protected override bool ShowDimContent => !ApplyUserSettings.Value || (ShowStoryboard.Value && DimLevel < 1); private void initializeStoryboard(bool async) { if (drawableStoryboard != null) return; - if (!ShowStoryboard.Value && !IgnoreUserSettings.Value) + if (ApplyUserSettings.Value && !ShowStoryboard.Value) return; drawableStoryboard = storyboard.CreateDrawable(); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index dd3f58439b..1c71305baf 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -764,7 +764,7 @@ namespace osu.Game.Screens.Play ApplyToBackground(b => { - b.IgnoreUserSettings.Value = false; + b.ApplyUserSettings.Value = true; b.BlurAmount.Value = 0; // bind component bindables. @@ -913,7 +913,7 @@ namespace osu.Game.Screens.Play float fadeOutDuration = instant ? 0 : 250; this.FadeOut(fadeOutDuration); - ApplyToBackground(b => b.IgnoreUserSettings.Value = true); + ApplyToBackground(b => b.ApplyUserSettings.Value = false); storyboardReplacesBackground.Value = false; } diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index cf15104809..8627432c11 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -229,7 +229,7 @@ namespace osu.Game.Screens.Play content.ScaleTo(0.7f, 150, Easing.InQuint); this.FadeOut(150); - ApplyToBackground(b => b.IgnoreUserSettings.Value = true); + ApplyToBackground(b => b.ApplyUserSettings.Value = false); BackgroundBrightnessReduction = false; Beatmap.Value.Track.RemoveAdjustment(AdjustableProperty.Volume, volumeAdjustment); @@ -277,7 +277,7 @@ namespace osu.Game.Screens.Play // Preview user-defined background dim and blur when hovered on the visual settings panel. ApplyToBackground(b => { - b.IgnoreUserSettings.Value = false; + b.ApplyUserSettings.Value = true; b.BlurAmount.Value = 0; }); @@ -288,7 +288,7 @@ namespace osu.Game.Screens.Play ApplyToBackground(b => { // Returns background dim and blur to the values specified by PlayerLoader. - b.IgnoreUserSettings.Value = true; + b.ApplyUserSettings.Value = false; b.BlurAmount.Value = BACKGROUND_BLUR; }); From 92fd34cea99c659155089ef828f54a65e620989b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 15 Apr 2021 08:02:12 +0300 Subject: [PATCH 122/150] Revert "Inverse ignore user settings bindable to "apply user settings" instead" This reverts commit 175b8da2b27002c262d53cfa5c8f44d46034abbf. --- .../Background/TestSceneUserDimBackgrounds.cs | 16 ++++++++-------- .../Background/TestSceneUserDimContainer.cs | 2 +- osu.Game/Graphics/Containers/UserDimContainer.cs | 8 ++++---- osu.Game/Rulesets/Mods/ModCinema.cs | 4 ++-- .../Backgrounds/BackgroundScreenBeatmap.cs | 15 +++++++++------ osu.Game/Screens/Edit/Editor.cs | 2 +- osu.Game/Screens/Play/DimmableStoryboard.cs | 4 ++-- osu.Game/Screens/Play/Player.cs | 4 ++-- osu.Game/Screens/Play/PlayerLoader.cs | 6 +++--- 9 files changed, 32 insertions(+), 29 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs index d915442570..f89988cd1a 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs @@ -157,9 +157,9 @@ namespace osu.Game.Tests.Visual.Background { performFullSetup(); AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); - AddStep("Disable user dim", () => songSelect.ApplyUserSettings.Value = false); + AddStep("Disable user dim", () => songSelect.IgnoreUserSettings.Value = true); AddUntilStep("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && songSelect.IsUserBlurDisabled()); - AddStep("Enable user dim", () => songSelect.ApplyUserSettings.Value = true); + AddStep("Enable user dim", () => songSelect.IgnoreUserSettings.Value = false); AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); } @@ -176,10 +176,10 @@ namespace osu.Game.Tests.Visual.Background player.ReplacesBackground.Value = true; player.StoryboardEnabled.Value = true; }); - AddStep("Enable user dim", () => player.DimmableStoryboard.ApplyUserSettings.Value = true); + AddStep("Enable user dim", () => player.DimmableStoryboard.IgnoreUserSettings.Value = false); AddStep("Set dim level to 1", () => songSelect.DimLevel.Value = 1f); AddUntilStep("Storyboard is invisible", () => !player.IsStoryboardVisible); - AddStep("Disable user dim", () => player.DimmableStoryboard.ApplyUserSettings.Value = false); + AddStep("Disable user dim", () => player.DimmableStoryboard.IgnoreUserSettings.Value = true); AddUntilStep("Storyboard is visible", () => player.IsStoryboardVisible); } @@ -195,8 +195,8 @@ namespace osu.Game.Tests.Visual.Background AddStep("Ignore user settings", () => { - player.ApplyToBackground(b => b.ApplyUserSettings.Value = false); - player.DimmableStoryboard.ApplyUserSettings.Value = false; + player.ApplyToBackground(b => b.IgnoreUserSettings.Value = true); + player.DimmableStoryboard.IgnoreUserSettings.Value = true; }); AddUntilStep("Storyboard is visible", () => player.IsStoryboardVisible); AddUntilStep("Background is invisible", () => songSelect.IsBackgroundInvisible()); @@ -308,11 +308,11 @@ namespace osu.Game.Tests.Visual.Background protected override BackgroundScreen CreateBackground() { background = new FadeAccessibleBackground(Beatmap.Value); - ApplyUserSettings.BindTo(background.ApplyUserSettings); + IgnoreUserSettings.BindTo(background.IgnoreUserSettings); return background; } - public readonly Bindable ApplyUserSettings = new Bindable(); + public readonly Bindable IgnoreUserSettings = new Bindable(); public readonly Bindable DimLevel = new BindableDouble(); public readonly Bindable BlurLevel = new BindableDouble(); diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs index 9c3a044f18..fede99f450 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs @@ -94,7 +94,7 @@ namespace osu.Game.Tests.Visual.Background AddStep("set dim level 0.6", () => userDimContainer.UserDimLevel.Value = test_user_dim); AddUntilStep("dim reached", () => userDimContainer.DimEqual(test_user_dim)); - AddStep("ignore settings", () => userDimContainer.ApplyUserSettings.Value = false); + AddStep("ignore settings", () => userDimContainer.IgnoreUserSettings.Value = true); AddUntilStep("no dim", () => userDimContainer.DimEqual(0)); AddStep("set break", () => isBreakTime.Value = true); AddAssert("no dim", () => userDimContainer.DimEqual(0)); diff --git a/osu.Game/Graphics/Containers/UserDimContainer.cs b/osu.Game/Graphics/Containers/UserDimContainer.cs index a3d59da961..4e555ac1eb 100644 --- a/osu.Game/Graphics/Containers/UserDimContainer.cs +++ b/osu.Game/Graphics/Containers/UserDimContainer.cs @@ -24,9 +24,9 @@ namespace osu.Game.Graphics.Containers protected const double BACKGROUND_FADE_DURATION = 800; /// - /// Whether or not user-configured effect settings should be applied to this container. + /// Whether or not user-configured settings relating to brightness of elements should be ignored /// - public readonly Bindable ApplyUserSettings = new Bindable(true); + public readonly Bindable IgnoreUserSettings = new Bindable(); /// /// Whether or not the storyboard loaded should completely hide the background behind it. @@ -52,7 +52,7 @@ namespace osu.Game.Graphics.Containers private float breakLightening => LightenDuringBreaks.Value && IsBreakTime.Value ? BREAK_LIGHTEN_AMOUNT : 0; - protected float DimLevel => Math.Max(ApplyUserSettings.Value ? (float)UserDimLevel.Value - breakLightening : 0, 0); + protected float DimLevel => Math.Max(!IgnoreUserSettings.Value ? (float)UserDimLevel.Value - breakLightening : 0, 0); protected override Container Content => dimContent; @@ -78,7 +78,7 @@ namespace osu.Game.Graphics.Containers IsBreakTime.ValueChanged += _ => UpdateVisuals(); ShowStoryboard.ValueChanged += _ => UpdateVisuals(); StoryboardReplacesBackground.ValueChanged += _ => UpdateVisuals(); - ApplyUserSettings.ValueChanged += _ => UpdateVisuals(); + IgnoreUserSettings.ValueChanged += _ => UpdateVisuals(); } protected override void LoadComplete() diff --git a/osu.Game/Rulesets/Mods/ModCinema.cs b/osu.Game/Rulesets/Mods/ModCinema.cs index 93062218fe..c78088ba2d 100644 --- a/osu.Game/Rulesets/Mods/ModCinema.cs +++ b/osu.Game/Rulesets/Mods/ModCinema.cs @@ -37,8 +37,8 @@ namespace osu.Game.Rulesets.Mods public void ApplyToPlayer(Player player) { - player.ApplyToBackground(b => b.ApplyUserSettings.Value = false); - player.DimmableStoryboard.ApplyUserSettings.Value = false; + player.ApplyToBackground(b => b.IgnoreUserSettings.Value = true); + player.DimmableStoryboard.IgnoreUserSettings.Value = true; player.BreakOverlay.Hide(); } diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs index 553a8f3689..02166644ab 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs @@ -27,9 +27,9 @@ namespace osu.Game.Screens.Backgrounds private WorkingBeatmap beatmap; /// - /// Whether or not user-configured effect settings should be applied to this background screen. + /// Whether or not user-configured settings relating to brightness of elements should be ignored /// - public readonly Bindable ApplyUserSettings = new Bindable(); + public readonly Bindable IgnoreUserSettings = new Bindable(); public readonly Bindable StoryboardReplacesBackground = new Bindable(); @@ -50,7 +50,10 @@ namespace osu.Game.Screens.Backgrounds InternalChild = dimmable = CreateFadeContainer(); - dimmable.ApplyUserSettings.BindTo(ApplyUserSettings); + // Beatmap background screens should not apply user settings by default. + IgnoreUserSettings.Value = true; + + dimmable.IgnoreUserSettings.BindTo(IgnoreUserSettings); dimmable.IsBreakTime.BindTo(IsBreakTime); dimmable.BlurAmount.BindTo(BlurAmount); @@ -148,7 +151,7 @@ namespace osu.Game.Screens.Backgrounds /// /// As an optimisation, we add the two blur portions to be applied rather than actually applying two separate blurs. /// - private Vector2 blurTarget => ApplyUserSettings.Value + private Vector2 blurTarget => !IgnoreUserSettings.Value ? new Vector2(BlurAmount.Value + (float)userBlurLevel.Value * USER_BLUR_FACTOR) : new Vector2(BlurAmount.Value); @@ -167,8 +170,8 @@ namespace osu.Game.Screens.Backgrounds } protected override bool ShowDimContent - // The background needs to be hidden in the case of it being replaced by the storyboard. - => (ApplyUserSettings.Value && !ShowStoryboard.Value) || !StoryboardReplacesBackground.Value; + // The background needs to be hidden in the case of it being replaced by the storyboard + => (!ShowStoryboard.Value && !IgnoreUserSettings.Value) || !StoryboardReplacesBackground.Value; protected override void UpdateVisuals() { diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 99c6cce26f..fffea65456 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -467,7 +467,7 @@ namespace osu.Game.Screens.Edit // todo: temporary. we want to be applying dim using the UserDimContainer eventually. b.FadeColour(Color4.DarkGray, 500); - b.ApplyUserSettings.Value = false; + b.IgnoreUserSettings.Value = true; b.BlurAmount.Value = 0; }); diff --git a/osu.Game/Screens/Play/DimmableStoryboard.cs b/osu.Game/Screens/Play/DimmableStoryboard.cs index 105f672847..58eb95b7c6 100644 --- a/osu.Game/Screens/Play/DimmableStoryboard.cs +++ b/osu.Game/Screens/Play/DimmableStoryboard.cs @@ -38,14 +38,14 @@ namespace osu.Game.Screens.Play base.LoadComplete(); } - protected override bool ShowDimContent => !ApplyUserSettings.Value || (ShowStoryboard.Value && DimLevel < 1); + protected override bool ShowDimContent => IgnoreUserSettings.Value || (ShowStoryboard.Value && DimLevel < 1); private void initializeStoryboard(bool async) { if (drawableStoryboard != null) return; - if (ApplyUserSettings.Value && !ShowStoryboard.Value) + if (!ShowStoryboard.Value && !IgnoreUserSettings.Value) return; drawableStoryboard = storyboard.CreateDrawable(); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 1c71305baf..dd3f58439b 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -764,7 +764,7 @@ namespace osu.Game.Screens.Play ApplyToBackground(b => { - b.ApplyUserSettings.Value = true; + b.IgnoreUserSettings.Value = false; b.BlurAmount.Value = 0; // bind component bindables. @@ -913,7 +913,7 @@ namespace osu.Game.Screens.Play float fadeOutDuration = instant ? 0 : 250; this.FadeOut(fadeOutDuration); - ApplyToBackground(b => b.ApplyUserSettings.Value = false); + ApplyToBackground(b => b.IgnoreUserSettings.Value = true); storyboardReplacesBackground.Value = false; } diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 8627432c11..cf15104809 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -229,7 +229,7 @@ namespace osu.Game.Screens.Play content.ScaleTo(0.7f, 150, Easing.InQuint); this.FadeOut(150); - ApplyToBackground(b => b.ApplyUserSettings.Value = false); + ApplyToBackground(b => b.IgnoreUserSettings.Value = true); BackgroundBrightnessReduction = false; Beatmap.Value.Track.RemoveAdjustment(AdjustableProperty.Volume, volumeAdjustment); @@ -277,7 +277,7 @@ namespace osu.Game.Screens.Play // Preview user-defined background dim and blur when hovered on the visual settings panel. ApplyToBackground(b => { - b.ApplyUserSettings.Value = true; + b.IgnoreUserSettings.Value = false; b.BlurAmount.Value = 0; }); @@ -288,7 +288,7 @@ namespace osu.Game.Screens.Play ApplyToBackground(b => { // Returns background dim and blur to the values specified by PlayerLoader. - b.ApplyUserSettings.Value = false; + b.IgnoreUserSettings.Value = true; b.BlurAmount.Value = BACKGROUND_BLUR; }); From 6c5234f8daa605ffb936b492edd0e247ee71351a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 15 Apr 2021 08:04:03 +0300 Subject: [PATCH 123/150] Move default `IgnoreUserSettings` value to construction --- .../Screens/Backgrounds/BackgroundScreenBeatmap.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs index 02166644ab..65bc9cfaea 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs @@ -27,9 +27,12 @@ namespace osu.Game.Screens.Backgrounds private WorkingBeatmap beatmap; /// - /// Whether or not user-configured settings relating to brightness of elements should be ignored + /// Whether or not user-configured settings relating to brightness of elements should be ignored. /// - public readonly Bindable IgnoreUserSettings = new Bindable(); + /// + /// Beatmap background screens should not apply user settings by default. + /// + public readonly Bindable IgnoreUserSettings = new Bindable(true); public readonly Bindable StoryboardReplacesBackground = new Bindable(); @@ -50,9 +53,6 @@ namespace osu.Game.Screens.Backgrounds InternalChild = dimmable = CreateFadeContainer(); - // Beatmap background screens should not apply user settings by default. - IgnoreUserSettings.Value = true; - dimmable.IgnoreUserSettings.BindTo(IgnoreUserSettings); dimmable.IsBreakTime.BindTo(IsBreakTime); dimmable.BlurAmount.BindTo(BlurAmount); From 5eaf3ea5765e21a0137b983fa586651d0228ffc5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 14:19:06 +0900 Subject: [PATCH 124/150] Reorganise and reword comments to make time override behaviour a bit clearer --- osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs index a7f11b1e6f..279087ead9 100644 --- a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs +++ b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs @@ -128,13 +128,13 @@ namespace osu.Game.Rulesets.Replays double frameStart = getFrameTime(currentFrameIndex); double frameEnd = getFrameTime(currentFrameIndex + 1); - // If the proposed time is after the current frame end time, we progress forwards. - // If the proposed time is before the current frame start time, and we are at the frame boundary, we progress backwards. + // If the proposed time is after the current frame end time, we progress forwards to precisely the new frame's time (regardless of incoming time). if (frameEnd <= time) { time = frameEnd; currentFrameIndex++; } + // If the proposed time is before the current frame start time, and we are at the frame boundary, we progress backwards. else if (time < frameStart && CurrentTime == frameStart) currentFrameIndex--; From ba325de5959bb83828fc222b359fcb9f9c846236 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 14:19:59 +0900 Subject: [PATCH 125/150] Merge conditionals for readability --- osu.Game/Rulesets/UI/RulesetInputManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index 1c0d820a3d..5ab09f9516 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -107,7 +107,7 @@ namespace osu.Game.Rulesets.UI protected override List GetPendingInputs() { - if (replayInputHandler != null && !replayInputHandler.IsActive) + if (replayInputHandler?.IsActive == false) return emptyInputList; return base.GetPendingInputs(); From 346e36d32a09ebdafb1c902f4eb0b03e45fddf42 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 14:32:01 +0900 Subject: [PATCH 126/150] Make `Mod.Description` abstract and add missing descriptions --- osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs | 1 + osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs | 1 + .../NonVisual/DifficultyAdjustmentModCombinationsTest.cs | 5 +++++ osu.Game.Tests/Online/TestAPIModJsonSerialization.cs | 2 ++ osu.Game.Tests/Online/TestAPIModMessagePackSerialization.cs | 3 +++ osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs | 1 + osu.Game.Tests/Visual/UserInterface/TestSceneModButton.cs | 2 ++ osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs | 2 ++ osu.Game/Rulesets/Mods/Mod.cs | 2 +- osu.Game/Rulesets/Mods/ModNoMod.cs | 1 + 10 files changed, 19 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs index 485595cea9..fa1eb10f5e 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs @@ -15,6 +15,7 @@ namespace osu.Game.Rulesets.Mania.Mods public override string Name => "Mirror"; public override string Acronym => "MR"; public override ModType Type => ModType.Conversion; + public override string Description => "Notes are flipped horizontally"; public override double ScoreMultiplier => 1; public override bool Ranked => true; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs index f0db548e74..3b16e9d2b7 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs @@ -9,6 +9,7 @@ namespace osu.Game.Rulesets.Osu.Mods { public override string Name => "Touch Device"; public override string Acronym => "TD"; + public override string Description => "Automatically applied to plays on devices with a touchscreen."; public override double ScoreMultiplier => 1; public override ModType Type => ModType.System; diff --git a/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs b/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs index 1c0bfd56dd..16c1004f37 100644 --- a/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs +++ b/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs @@ -144,6 +144,7 @@ namespace osu.Game.Tests.NonVisual { public override string Name => nameof(ModA); public override string Acronym => nameof(ModA); + public override string Description => string.Empty; public override double ScoreMultiplier => 1; public override Type[] IncompatibleMods => new[] { typeof(ModIncompatibleWithA), typeof(ModIncompatibleWithAAndB) }; @@ -152,6 +153,7 @@ namespace osu.Game.Tests.NonVisual private class ModB : Mod { public override string Name => nameof(ModB); + public override string Description => string.Empty; public override string Acronym => nameof(ModB); public override double ScoreMultiplier => 1; @@ -162,6 +164,7 @@ namespace osu.Game.Tests.NonVisual { public override string Name => nameof(ModC); public override string Acronym => nameof(ModC); + public override string Description => string.Empty; public override double ScoreMultiplier => 1; } @@ -169,6 +172,7 @@ namespace osu.Game.Tests.NonVisual { public override string Name => $"Incompatible With {nameof(ModA)}"; public override string Acronym => $"Incompatible With {nameof(ModA)}"; + public override string Description => string.Empty; public override double ScoreMultiplier => 1; public override Type[] IncompatibleMods => new[] { typeof(ModA) }; @@ -187,6 +191,7 @@ namespace osu.Game.Tests.NonVisual { public override string Name => $"Incompatible With {nameof(ModA)} and {nameof(ModB)}"; public override string Acronym => $"Incompatible With {nameof(ModA)} and {nameof(ModB)}"; + public override string Description => string.Empty; public override double ScoreMultiplier => 1; public override Type[] IncompatibleMods => new[] { typeof(ModA), typeof(ModB) }; diff --git a/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs b/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs index 3afb7481b1..ad2007f202 100644 --- a/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs +++ b/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs @@ -140,6 +140,7 @@ namespace osu.Game.Tests.Online { public override string Name => "Test Mod"; public override string Acronym => "TM"; + public override string Description => "This is a test mod."; public override double ScoreMultiplier => 1; [SettingSource("Test")] @@ -156,6 +157,7 @@ namespace osu.Game.Tests.Online { public override string Name => "Test Mod"; public override string Acronym => "TMTR"; + public override string Description => "This is a test mod."; public override double ScoreMultiplier => 1; [SettingSource("Initial rate", "The starting speed of the track")] diff --git a/osu.Game.Tests/Online/TestAPIModMessagePackSerialization.cs b/osu.Game.Tests/Online/TestAPIModMessagePackSerialization.cs index 74db477cfc..0462e9feb5 100644 --- a/osu.Game.Tests/Online/TestAPIModMessagePackSerialization.cs +++ b/osu.Game.Tests/Online/TestAPIModMessagePackSerialization.cs @@ -100,6 +100,7 @@ namespace osu.Game.Tests.Online { public override string Name => "Test Mod"; public override string Acronym => "TM"; + public override string Description => "This is a test mod."; public override double ScoreMultiplier => 1; [SettingSource("Test")] @@ -116,6 +117,7 @@ namespace osu.Game.Tests.Online { public override string Name => "Test Mod"; public override string Acronym => "TMTR"; + public override string Description => "This is a test mod."; public override double ScoreMultiplier => 1; [SettingSource("Initial rate", "The starting speed of the track")] @@ -150,6 +152,7 @@ namespace osu.Game.Tests.Online { public override string Name => "Test Mod"; public override string Acronym => "TM"; + public override string Description => "This is a test mod."; public override double ScoreMultiplier => 1; [SettingSource("Test")] diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index 88fbf09ef4..280c182259 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -321,6 +321,7 @@ namespace osu.Game.Tests.Visual.Gameplay public override string Name => string.Empty; public override string Acronym => string.Empty; public override double ScoreMultiplier => 1; + public override string Description => string.Empty; public bool Applied { get; private set; } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModButton.cs index 443cf59003..fdc21d80ff 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModButton.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModButton.cs @@ -57,6 +57,8 @@ namespace osu.Game.Tests.Visual.UserInterface private abstract class TestMod : Mod, IApplicableMod { public override double ScoreMultiplier => 1.0; + + public override string Description => "This is a test mod."; } } } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs index 89f9b7381b..2158cf77e5 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs @@ -226,6 +226,8 @@ namespace osu.Game.Tests.Visual.UserInterface { public override double ScoreMultiplier => 1.0; + public override string Description => "This is a customisable test mod."; + public override ModType Type => ModType.Conversion; [SettingSource("Sample float", "Change something for a mod")] diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index 832a14ee1e..4879590e24 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Mods /// The user readable description of this mod. /// [JsonIgnore] - public virtual string Description => string.Empty; + public abstract string Description { get; } /// /// The tooltip to display for this mod when used in a . diff --git a/osu.Game/Rulesets/Mods/ModNoMod.cs b/osu.Game/Rulesets/Mods/ModNoMod.cs index 379a2122f2..1009c5bc42 100644 --- a/osu.Game/Rulesets/Mods/ModNoMod.cs +++ b/osu.Game/Rulesets/Mods/ModNoMod.cs @@ -12,6 +12,7 @@ namespace osu.Game.Rulesets.Mods { public override string Name => "No Mod"; public override string Acronym => "NM"; + public override string Description => "No mods applied."; public override double ScoreMultiplier => 1; public override IconUsage? Icon => FontAwesome.Solid.Ban; public override ModType Type => ModType.System; From 23eb1c655ce3b2dd5f6bea7e5f68bdc4d111fe8e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 14:37:47 +0900 Subject: [PATCH 127/150] Add missing description --- osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs index 7cdfdb3c4a..3a61968075 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs @@ -23,6 +23,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override string Name => "Barrel Roll"; public override string Acronym => "BR"; + public override string Description => "The whole playfield is on a wheel!"; public override double ScoreMultiplier => 1; public void Update(Playfield playfield) From 698a9d3feddddbb80fc9a483f02160446b985993 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 14:40:03 +0900 Subject: [PATCH 128/150] Add rotation direction setting --- osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs index 3a61968075..b6cfa514a1 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Bindables; +using osu.Framework.Graphics; using osu.Game.Configuration; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Objects; @@ -21,6 +22,9 @@ namespace osu.Game.Rulesets.Osu.Mods Precision = 0.01, }; + [SettingSource("Direction", "The direction of rotation")] + public Bindable Direction { get; } = new Bindable(RotationDirection.Clockwise); + public override string Name => "Barrel Roll"; public override string Acronym => "BR"; public override string Description => "The whole playfield is on a wheel!"; @@ -28,7 +32,7 @@ namespace osu.Game.Rulesets.Osu.Mods public void Update(Playfield playfield) { - playfield.Rotation = 360 * (float)(playfield.Time.Current / 60000 * SpinSpeed.Value); + playfield.Rotation = (Direction.Value == RotationDirection.CounterClockwise ? -1 : 1) * 360 * (float)(playfield.Time.Current / 60000 * SpinSpeed.Value); } public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) From 55421b0065a8e22b66e260ae594463a04d1bdbf5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 14:41:10 +0900 Subject: [PATCH 129/150] Add missing full stop Co-authored-by: Salman Ahmed --- osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs index fa1eb10f5e..12f379bddb 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mania.Mods public override string Name => "Mirror"; public override string Acronym => "MR"; public override ModType Type => ModType.Conversion; - public override string Description => "Notes are flipped horizontally"; + public override string Description => "Notes are flipped horizontally."; public override double ScoreMultiplier => 1; public override bool Ranked => true; From bc3b2af39d5d391e3bf0c95a5514193e6853faf2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 15:29:22 +0900 Subject: [PATCH 130/150] Add rounded corners to timeline ticks display --- .../Timelines/Summary/Visualisations/PointVisualisation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/PointVisualisation.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/PointVisualisation.cs index 53a1f94731..d647c6bfe8 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/PointVisualisation.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/PointVisualisation.cs @@ -9,7 +9,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations /// /// Represents a singular point on a timeline part. /// - public class PointVisualisation : Box + public class PointVisualisation : Circle { public const float MAX_WIDTH = 4; From 66bb5766b9486bd06b6059007cde79680e4fcc2c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 14:32:01 +0900 Subject: [PATCH 131/150] Make `Mod.Description` abstract and add missing descriptions --- osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs | 1 + osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs | 1 + .../NonVisual/DifficultyAdjustmentModCombinationsTest.cs | 5 +++++ osu.Game.Tests/Online/TestAPIModJsonSerialization.cs | 2 ++ osu.Game.Tests/Online/TestAPIModMessagePackSerialization.cs | 3 +++ osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs | 1 + osu.Game.Tests/Visual/UserInterface/TestSceneModButton.cs | 2 ++ osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs | 2 ++ osu.Game/Rulesets/Mods/Mod.cs | 2 +- osu.Game/Rulesets/Mods/ModNoMod.cs | 1 + 10 files changed, 19 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs index 485595cea9..fa1eb10f5e 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs @@ -15,6 +15,7 @@ namespace osu.Game.Rulesets.Mania.Mods public override string Name => "Mirror"; public override string Acronym => "MR"; public override ModType Type => ModType.Conversion; + public override string Description => "Notes are flipped horizontally"; public override double ScoreMultiplier => 1; public override bool Ranked => true; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs index f0db548e74..3b16e9d2b7 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs @@ -9,6 +9,7 @@ namespace osu.Game.Rulesets.Osu.Mods { public override string Name => "Touch Device"; public override string Acronym => "TD"; + public override string Description => "Automatically applied to plays on devices with a touchscreen."; public override double ScoreMultiplier => 1; public override ModType Type => ModType.System; diff --git a/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs b/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs index 1c0bfd56dd..16c1004f37 100644 --- a/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs +++ b/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs @@ -144,6 +144,7 @@ namespace osu.Game.Tests.NonVisual { public override string Name => nameof(ModA); public override string Acronym => nameof(ModA); + public override string Description => string.Empty; public override double ScoreMultiplier => 1; public override Type[] IncompatibleMods => new[] { typeof(ModIncompatibleWithA), typeof(ModIncompatibleWithAAndB) }; @@ -152,6 +153,7 @@ namespace osu.Game.Tests.NonVisual private class ModB : Mod { public override string Name => nameof(ModB); + public override string Description => string.Empty; public override string Acronym => nameof(ModB); public override double ScoreMultiplier => 1; @@ -162,6 +164,7 @@ namespace osu.Game.Tests.NonVisual { public override string Name => nameof(ModC); public override string Acronym => nameof(ModC); + public override string Description => string.Empty; public override double ScoreMultiplier => 1; } @@ -169,6 +172,7 @@ namespace osu.Game.Tests.NonVisual { public override string Name => $"Incompatible With {nameof(ModA)}"; public override string Acronym => $"Incompatible With {nameof(ModA)}"; + public override string Description => string.Empty; public override double ScoreMultiplier => 1; public override Type[] IncompatibleMods => new[] { typeof(ModA) }; @@ -187,6 +191,7 @@ namespace osu.Game.Tests.NonVisual { public override string Name => $"Incompatible With {nameof(ModA)} and {nameof(ModB)}"; public override string Acronym => $"Incompatible With {nameof(ModA)} and {nameof(ModB)}"; + public override string Description => string.Empty; public override double ScoreMultiplier => 1; public override Type[] IncompatibleMods => new[] { typeof(ModA), typeof(ModB) }; diff --git a/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs b/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs index 3afb7481b1..ad2007f202 100644 --- a/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs +++ b/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs @@ -140,6 +140,7 @@ namespace osu.Game.Tests.Online { public override string Name => "Test Mod"; public override string Acronym => "TM"; + public override string Description => "This is a test mod."; public override double ScoreMultiplier => 1; [SettingSource("Test")] @@ -156,6 +157,7 @@ namespace osu.Game.Tests.Online { public override string Name => "Test Mod"; public override string Acronym => "TMTR"; + public override string Description => "This is a test mod."; public override double ScoreMultiplier => 1; [SettingSource("Initial rate", "The starting speed of the track")] diff --git a/osu.Game.Tests/Online/TestAPIModMessagePackSerialization.cs b/osu.Game.Tests/Online/TestAPIModMessagePackSerialization.cs index 74db477cfc..0462e9feb5 100644 --- a/osu.Game.Tests/Online/TestAPIModMessagePackSerialization.cs +++ b/osu.Game.Tests/Online/TestAPIModMessagePackSerialization.cs @@ -100,6 +100,7 @@ namespace osu.Game.Tests.Online { public override string Name => "Test Mod"; public override string Acronym => "TM"; + public override string Description => "This is a test mod."; public override double ScoreMultiplier => 1; [SettingSource("Test")] @@ -116,6 +117,7 @@ namespace osu.Game.Tests.Online { public override string Name => "Test Mod"; public override string Acronym => "TMTR"; + public override string Description => "This is a test mod."; public override double ScoreMultiplier => 1; [SettingSource("Initial rate", "The starting speed of the track")] @@ -150,6 +152,7 @@ namespace osu.Game.Tests.Online { public override string Name => "Test Mod"; public override string Acronym => "TM"; + public override string Description => "This is a test mod."; public override double ScoreMultiplier => 1; [SettingSource("Test")] diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index 88fbf09ef4..280c182259 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -321,6 +321,7 @@ namespace osu.Game.Tests.Visual.Gameplay public override string Name => string.Empty; public override string Acronym => string.Empty; public override double ScoreMultiplier => 1; + public override string Description => string.Empty; public bool Applied { get; private set; } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModButton.cs index 443cf59003..fdc21d80ff 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModButton.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModButton.cs @@ -57,6 +57,8 @@ namespace osu.Game.Tests.Visual.UserInterface private abstract class TestMod : Mod, IApplicableMod { public override double ScoreMultiplier => 1.0; + + public override string Description => "This is a test mod."; } } } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs index 89f9b7381b..2158cf77e5 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs @@ -226,6 +226,8 @@ namespace osu.Game.Tests.Visual.UserInterface { public override double ScoreMultiplier => 1.0; + public override string Description => "This is a customisable test mod."; + public override ModType Type => ModType.Conversion; [SettingSource("Sample float", "Change something for a mod")] diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index 832a14ee1e..4879590e24 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Mods /// The user readable description of this mod. /// [JsonIgnore] - public virtual string Description => string.Empty; + public abstract string Description { get; } /// /// The tooltip to display for this mod when used in a . diff --git a/osu.Game/Rulesets/Mods/ModNoMod.cs b/osu.Game/Rulesets/Mods/ModNoMod.cs index 379a2122f2..1009c5bc42 100644 --- a/osu.Game/Rulesets/Mods/ModNoMod.cs +++ b/osu.Game/Rulesets/Mods/ModNoMod.cs @@ -12,6 +12,7 @@ namespace osu.Game.Rulesets.Mods { public override string Name => "No Mod"; public override string Acronym => "NM"; + public override string Description => "No mods applied."; public override double ScoreMultiplier => 1; public override IconUsage? Icon => FontAwesome.Solid.Ban; public override ModType Type => ModType.System; From ed14e014015042a2527210fab3af73c060677f88 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 14:41:10 +0900 Subject: [PATCH 132/150] Add missing full stop Co-authored-by: Salman Ahmed --- osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs index fa1eb10f5e..12f379bddb 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mania.Mods public override string Name => "Mirror"; public override string Acronym => "MR"; public override ModType Type => ModType.Conversion; - public override string Description => "Notes are flipped horizontally"; + public override string Description => "Notes are flipped horizontally."; public override double ScoreMultiplier => 1; public override bool Ranked => true; From dd9a142e899030c6a53d0fd0275af6a57efd34f6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 16:30:02 +0900 Subject: [PATCH 133/150] Fix `TestSceneEditorSummaryTimeline` not displaying actual beatmap content --- .../Editing/TestSceneEditorSummaryTimeline.cs | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorSummaryTimeline.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorSummaryTimeline.cs index 94a9fd7b35..ba57eeacbe 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorSummaryTimeline.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorSummaryTimeline.cs @@ -4,6 +4,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Game.Beatmaps; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Screens.Edit; @@ -16,18 +17,28 @@ namespace osu.Game.Tests.Visual.Editing public class TestSceneEditorSummaryTimeline : EditorClockTestScene { [Cached(typeof(EditorBeatmap))] - private readonly EditorBeatmap editorBeatmap = new EditorBeatmap(new OsuBeatmap()); + private readonly EditorBeatmap editorBeatmap; - [BackgroundDependencyLoader] - private void load() + public TestSceneEditorSummaryTimeline() { - Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); + editorBeatmap = new EditorBeatmap(CreateBeatmap(new OsuRuleset().RulesetInfo)); + } - Add(new SummaryTimeline + protected override void LoadComplete() + { + base.LoadComplete(); + + AddStep("create timeline", () => { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(500, 50) + // required for track + Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap); + + Add(new SummaryTimeline + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(500, 50) + }); }); } } From 73821beb1d8d127be59f77c9fa980259b3081a5d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 16:30:20 +0900 Subject: [PATCH 134/150] Fix break display looking bad on very long beatmaps due to fixed corner radius --- .../Edit/Components/Timelines/Summary/SummaryTimeline.cs | 2 +- .../Summary/Visualisations/DurationVisualisation.cs | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs index 02cd4bccb4..e1a1eff0cb 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs @@ -69,7 +69,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, - Height = 0.25f + Height = 0.10f } }; } diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/DurationVisualisation.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/DurationVisualisation.cs index de63df5463..86e6446555 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/DurationVisualisation.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/DurationVisualisation.cs @@ -10,19 +10,15 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations /// /// Represents a spanning point on a timeline part. /// - public class DurationVisualisation : Container + public class DurationVisualisation : Circle { protected DurationVisualisation(double startTime, double endTime) { - Masking = true; - CornerRadius = 5; - RelativePositionAxes = Axes.X; RelativeSizeAxes = Axes.Both; + X = (float)startTime; Width = (float)(endTime - startTime); - - AddInternal(new Box { RelativeSizeAxes = Axes.Both }); } } } From da6f9060fa2eb98c982367ec2a0ef00fcf1edf26 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 16:30:56 +0900 Subject: [PATCH 135/150] Centre end circles to avoid visual gaps --- .../Edit/Components/Timelines/Summary/SummaryTimeline.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs index e1a1eff0cb..ada7810599 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs @@ -45,7 +45,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary new Circle { Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreRight, + Origin = Anchor.Centre, Size = new Vector2(5) }, new Box @@ -59,7 +59,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary new Circle { Anchor = Anchor.CentreRight, - Origin = Anchor.CentreLeft, + Origin = Anchor.Centre, Size = new Vector2(5) }, } From 757475e6d4c3f300684d46f5f3c860f73ecd66e5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 16:33:29 +0900 Subject: [PATCH 136/150] Use correct representation colours --- .../Components/Timelines/Summary/Parts/GroupVisualisation.cs | 2 +- .../Edit/Components/Timelines/Summary/SummaryTimeline.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/GroupVisualisation.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/GroupVisualisation.cs index 93fe6f9989..8bc8618479 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/GroupVisualisation.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/GroupVisualisation.cs @@ -39,7 +39,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts return; } - Colour = controlPoints.Any(c => c is TimingControlPoint) ? colours.YellowDark : colours.Green; + Colour = Group.ControlPoints.First().GetRepresentingColour(colours); }, true); } } diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs index ada7810599..ae60cd4dd3 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs @@ -38,6 +38,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary }, new Container { + Name = "centre line", RelativeSizeAxes = Axes.Both, Colour = colours.Gray5, Children = new Drawable[] From 18e8682f391ea78fa40487af2d813fe6b6fe77b2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 17:01:25 +0900 Subject: [PATCH 137/150] Remove unused using statements --- osu.Game.Tests/Visual/Editing/TestSceneEditorSummaryTimeline.cs | 2 -- .../Timelines/Summary/Visualisations/DurationVisualisation.cs | 1 - 2 files changed, 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorSummaryTimeline.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorSummaryTimeline.cs index ba57eeacbe..da0c83bb11 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorSummaryTimeline.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorSummaryTimeline.cs @@ -4,9 +4,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Game.Beatmaps; using osu.Game.Rulesets.Osu; -using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Components.Timelines.Summary; using osuTK; diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/DurationVisualisation.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/DurationVisualisation.cs index 86e6446555..ec68bf9c00 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/DurationVisualisation.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/DurationVisualisation.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations From bf5af3310aa1d643e08a0135531fff7b9aff9416 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 17:04:11 +0900 Subject: [PATCH 138/150] Update break colour to not look like kiai time --- .../Edit/Components/Timelines/Summary/Parts/BreakPart.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/BreakPart.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/BreakPart.cs index e8a4b5c8c7..3d535ec915 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/BreakPart.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/BreakPart.cs @@ -28,7 +28,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts } [BackgroundDependencyLoader] - private void load(OsuColour colours) => Colour = colours.Yellow; + private void load(OsuColour colours) => Colour = colours.GreyCarmineLight; } } } From 50fad47ebc45d744dcd2f0005a9a66aa80639e63 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 15 Apr 2021 18:06:45 +0900 Subject: [PATCH 139/150] Remove usage of Lazy> for NestedHitObjects --- .../Objects/Drawables/DrawableHitObject.cs | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index d95b246c96..669e4cecbe 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -56,8 +56,8 @@ namespace osu.Game.Rulesets.Objects.Drawables public virtual IEnumerable GetSamples() => HitObject.Samples; - private readonly Lazy> nestedHitObjects = new Lazy>(); - public IReadOnlyList NestedHitObjects => nestedHitObjects.IsValueCreated ? nestedHitObjects.Value : (IReadOnlyList)Array.Empty(); + private readonly List nestedHitObjects = new List(); + public IReadOnlyList NestedHitObjects => nestedHitObjects; /// /// Whether this object should handle any user input events. @@ -249,7 +249,7 @@ namespace osu.Game.Rulesets.Objects.Drawables // Must be done before the nested DHO is added to occur before the nested Apply()! drawableNested.ParentHitObject = this; - nestedHitObjects.Value.Add(drawableNested); + nestedHitObjects.Add(drawableNested); AddNestedHitObject(drawableNested); } @@ -305,19 +305,16 @@ namespace osu.Game.Rulesets.Objects.Drawables if (Samples != null) Samples.Samples = null; - if (nestedHitObjects.IsValueCreated) + foreach (var obj in nestedHitObjects) { - foreach (var obj in nestedHitObjects.Value) - { - obj.OnNewResult -= onNewResult; - obj.OnRevertResult -= onRevertResult; - obj.ApplyCustomUpdateState -= onApplyCustomUpdateState; - } - - nestedHitObjects.Value.Clear(); - ClearNestedHitObjects(); + obj.OnNewResult -= onNewResult; + obj.OnRevertResult -= onRevertResult; + obj.ApplyCustomUpdateState -= onApplyCustomUpdateState; } + nestedHitObjects.Clear(); + ClearNestedHitObjects(); + HitObject.DefaultsApplied -= onDefaultsApplied; OnFree(); From d8aa436e81a1f70160983c882cf2b94d83063677 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 15 Apr 2021 18:11:47 +0900 Subject: [PATCH 140/150] Remove usage of Lazy> in NestedPlayfields --- osu.Game/Rulesets/UI/Playfield.cs | 35 ++++++++----------------------- 1 file changed, 9 insertions(+), 26 deletions(-) diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs index c40ab4bd94..d55005363c 100644 --- a/osu.Game/Rulesets/UI/Playfield.cs +++ b/osu.Game/Rulesets/UI/Playfield.cs @@ -66,7 +66,7 @@ namespace osu.Game.Rulesets.UI var enumerable = HitObjectContainer.Objects; - if (nestedPlayfields.IsValueCreated) + if (nestedPlayfields.Count != 0) enumerable = enumerable.Concat(NestedPlayfields.SelectMany(p => p.AllHitObjects)); return enumerable; @@ -76,9 +76,9 @@ namespace osu.Game.Rulesets.UI /// /// All s nested inside this . /// - public IEnumerable NestedPlayfields => nestedPlayfields.IsValueCreated ? nestedPlayfields.Value : Enumerable.Empty(); + public IEnumerable NestedPlayfields => nestedPlayfields; - private readonly Lazy> nestedPlayfields = new Lazy>(); + private readonly List nestedPlayfields = new List(); /// /// Whether judgements should be displayed by this and and all nested s. @@ -217,7 +217,7 @@ namespace osu.Game.Rulesets.UI otherPlayfield.HitObjectUsageBegan += h => HitObjectUsageBegan?.Invoke(h); otherPlayfield.HitObjectUsageFinished += h => HitObjectUsageFinished?.Invoke(h); - nestedPlayfields.Value.Add(otherPlayfield); + nestedPlayfields.Add(otherPlayfield); } protected override void LoadComplete() @@ -279,12 +279,7 @@ namespace osu.Game.Rulesets.UI return true; } - bool removedFromNested = false; - - if (nestedPlayfields.IsValueCreated) - removedFromNested = nestedPlayfields.Value.Any(p => p.Remove(hitObject)); - - return removedFromNested; + return nestedPlayfields.Any(p => p.Remove(hitObject)); } /// @@ -429,10 +424,7 @@ namespace osu.Game.Rulesets.UI return; } - if (!nestedPlayfields.IsValueCreated) - return; - - foreach (var p in nestedPlayfields.Value) + foreach (var p in nestedPlayfields) p.SetKeepAlive(hitObject, keepAlive); } @@ -444,10 +436,7 @@ namespace osu.Game.Rulesets.UI foreach (var (_, entry) in lifetimeEntryMap) entry.KeepAlive = true; - if (!nestedPlayfields.IsValueCreated) - return; - - foreach (var p in nestedPlayfields.Value) + foreach (var p in nestedPlayfields) p.KeepAllAlive(); } @@ -461,10 +450,7 @@ namespace osu.Game.Rulesets.UI { HitObjectContainer.PastLifetimeExtension = value; - if (!nestedPlayfields.IsValueCreated) - return; - - foreach (var nested in nestedPlayfields.Value) + foreach (var nested in nestedPlayfields) nested.PastLifetimeExtension = value; } } @@ -479,10 +465,7 @@ namespace osu.Game.Rulesets.UI { HitObjectContainer.FutureLifetimeExtension = value; - if (!nestedPlayfields.IsValueCreated) - return; - - foreach (var nested in nestedPlayfields.Value) + foreach (var nested in nestedPlayfields) nested.FutureLifetimeExtension = value; } } From 153ee2551048cbbd25bf1c83856adb3adaa154f1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 18:42:07 +0900 Subject: [PATCH 141/150] Update base specifications to a more sane default --- .../Timelines/Summary/Visualisations/PointVisualisation.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/PointVisualisation.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/PointVisualisation.cs index d647c6bfe8..a4b6b0c392 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/PointVisualisation.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/PointVisualisation.cs @@ -21,9 +21,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations public PointVisualisation() { - Origin = Anchor.TopCentre; - - RelativePositionAxes = Axes.X; + RelativePositionAxes = Axes.Both; RelativeSizeAxes = Axes.Y; Anchor = Anchor.CentreLeft; From 0dc1577f6804a0b7c3863abfe3db39411ab2b980 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 18:42:30 +0900 Subject: [PATCH 142/150] Split out control point visualisation logic and add special kiai duration handling --- .../Parts/ControlPointVisualisation.cs | 30 ++++++++ .../Summary/Parts/EffectPointVisualisation.cs | 72 +++++++++++++++++++ .../Summary/Parts/GroupVisualisation.cs | 51 +++++++++---- .../Timelines/Summary/SummaryTimeline.cs | 1 + 4 files changed, 140 insertions(+), 14 deletions(-) create mode 100644 osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/ControlPointVisualisation.cs create mode 100644 osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/EffectPointVisualisation.cs diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/ControlPointVisualisation.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/ControlPointVisualisation.cs new file mode 100644 index 0000000000..a8e41d220a --- /dev/null +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/ControlPointVisualisation.cs @@ -0,0 +1,30 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Graphics; +using osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations; + +namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts +{ + public class ControlPointVisualisation : PointVisualisation + { + protected readonly ControlPoint Point; + + public ControlPointVisualisation(ControlPoint point) + { + Point = point; + + Height = 0.25f; + Origin = Anchor.TopCentre; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Colour = Point.GetRepresentingColour(colours); + } + } +} diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/EffectPointVisualisation.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/EffectPointVisualisation.cs new file mode 100644 index 0000000000..801372305b --- /dev/null +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/EffectPointVisualisation.cs @@ -0,0 +1,72 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Graphics; +using osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations; + +namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts +{ + public class EffectPointVisualisation : CompositeDrawable + { + private readonly EffectControlPoint effect; + private Bindable kiai; + + [Resolved] + private EditorBeatmap beatmap { get; set; } + + [Resolved] + private OsuColour colours { get; set; } + + public EffectPointVisualisation(EffectControlPoint point) + { + RelativePositionAxes = Axes.Both; + RelativeSizeAxes = Axes.Y; + + effect = point; + } + + [BackgroundDependencyLoader] + private void load() + { + kiai = effect.KiaiModeBindable.GetBoundCopy(); + kiai.BindValueChanged(_ => + { + ClearInternal(); + + AddInternal(new ControlPointVisualisation(effect)); + + if (!kiai.Value) + return; + + var endControlPoint = beatmap.ControlPointInfo.EffectPoints.FirstOrDefault(c => c.Time > effect.Time && !c.KiaiMode); + + // handle kiai duration + // eventually this will be simpler when we have control points with durations. + if (endControlPoint != null) + { + RelativeSizeAxes = Axes.Both; + Origin = Anchor.TopLeft; + + Width = (float)(endControlPoint.Time - effect.Time); + + AddInternal(new PointVisualisation + { + RelativeSizeAxes = Axes.Both, + Origin = Anchor.TopLeft, + Width = 1, + Height = 0.25f, + Depth = float.MaxValue, + Colour = effect.GetRepresentingColour(colours).Darken(0.5f), + }); + } + }, true); + } + } +} diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/GroupVisualisation.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/GroupVisualisation.cs index 8bc8618479..4629f9b540 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/GroupVisualisation.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/GroupVisualisation.cs @@ -1,29 +1,33 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; -using osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations; -using osuTK.Graphics; namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts { - public class GroupVisualisation : PointVisualisation + public class GroupVisualisation : CompositeDrawable { + [Resolved] + private OsuColour colours { get; set; } + public readonly ControlPointGroup Group; private readonly IBindableList controlPoints = new BindableList(); - [Resolved] - private OsuColour colours { get; set; } - public GroupVisualisation(ControlPointGroup group) - : base(group.Time) { + RelativePositionAxes = Axes.X; + + RelativeSizeAxes = Axes.Both; + Origin = Anchor.TopLeft; + Group = group; + X = (float)group.Time; } protected override void LoadComplete() @@ -33,13 +37,32 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts controlPoints.BindTo(Group.ControlPoints); controlPoints.BindCollectionChanged((_, __) => { - if (controlPoints.Count == 0) - { - Colour = Color4.Transparent; - return; - } + ClearInternal(); - Colour = Group.ControlPoints.First().GetRepresentingColour(colours); + if (controlPoints.Count == 0) + return; + + foreach (var point in Group.ControlPoints) + { + switch (point) + { + case TimingControlPoint _: + AddInternal(new ControlPointVisualisation(point) { Y = 0, }); + break; + + case DifficultyControlPoint _: + AddInternal(new ControlPointVisualisation(point) { Y = 0.25f, }); + break; + + case SampleControlPoint _: + AddInternal(new ControlPointVisualisation(point) { Y = 0.5f, }); + break; + + case EffectControlPoint effect: + AddInternal(new EffectPointVisualisation(effect) { Y = 0.75f }); + break; + } + } }, true); } } diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs index ae60cd4dd3..e90ae411de 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs @@ -27,6 +27,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary Anchor = Anchor.Centre, Origin = Anchor.BottomCentre, RelativeSizeAxes = Axes.Both, + Y = -10, Height = 0.35f }, new BookmarkPart From 17e021c549f424e9151eefe2351395925e08d365 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 18:45:52 +0900 Subject: [PATCH 143/150] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index b5315c3616..32e236ccd5 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 45b3d5c161..954cf511b6 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -29,7 +29,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 105a6e59c2..09f6033bfe 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 1a987dfbc0712b5ee54ba35de6a88a54fb0aa20e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 15 Apr 2021 21:16:38 +0900 Subject: [PATCH 144/150] Fix gameplay cursor showing offscreen --- osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs b/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs index ec7751d2b4..44ca5e850f 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs @@ -33,7 +33,6 @@ namespace osu.Game.Rulesets.Osu.UI { Add(cursorScaleContainer = new Container { - RelativePositionAxes = Axes.Both, Child = clickToResumeCursor = new OsuClickToResumeCursor { ResumeRequested = Resume } }); } From 71b06d7e61024503a7e983860ecfd07412d8585f Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 15 Apr 2021 15:53:21 +0300 Subject: [PATCH 145/150] Simplify ExtendableCircle even more --- .../Components/Timeline/TimelineHitObjectBlueprint.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index 1c0d6e5ab6..23069f6079 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -345,7 +345,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline /// public class ExtendableCircle : CompositeDrawable { - private readonly CircularContainer content; + private readonly Circle content; public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => content.ReceivePositionalInputAt(screenSpacePos); @@ -354,19 +354,14 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline public ExtendableCircle() { Padding = new MarginPadding { Horizontal = -circle_size / 2f }; - InternalChild = content = new CircularContainer + InternalChild = content = new Circle { RelativeSizeAxes = Axes.Both, - Masking = true, EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Shadow, Radius = 5, Colour = Color4.Black.Opacity(0.4f) - }, - Child = new Box - { - RelativeSizeAxes = Axes.Both } }; } From 5652490d61c4ee2c5b046e8baf7e5524303a1a31 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 16 Apr 2021 14:11:55 +0900 Subject: [PATCH 146/150] Fix OnUserBeganPlaying not being invoked if already watching --- .../Visual/Gameplay/TestSceneSpectator.cs | 24 ++++++++++++++++ .../Spectator/SpectatorStreamingClient.cs | 28 +++++++++++++++++-- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs index 397b37718d..ea66144b21 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs @@ -1,6 +1,7 @@ // 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 System.Threading; @@ -204,6 +205,29 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("screen didn't change", () => Stack.CurrentScreen is SoloSpectator); } + [Test] + public void OnUserBeganPlayingCallbackInvokedOnNewAdd() + { + bool callbackInvoked = false; + Action callbackAction = (_, __) => callbackInvoked = true; + + AddStep("bind first event", () => testSpectatorStreamingClient.OnUserBeganPlaying += callbackAction); + start(); + AddAssert("callback invoked", () => callbackInvoked); + + AddStep("reset", () => + { + testSpectatorStreamingClient.OnUserBeganPlaying -= callbackAction; + callbackInvoked = false; + }); + + AddStep("bind event again", () => testSpectatorStreamingClient.OnUserBeganPlaying += callbackAction); + AddAssert("callback invoked", () => callbackInvoked); + + // Don't leave the event bound if test run succeeded. + AddStep("reset", () => testSpectatorStreamingClient.OnUserBeganPlaying -= callbackAction); + } + private OsuFramedReplayInputHandler replayHandler => (OsuFramedReplayInputHandler)Stack.ChildrenOfType().First().ReplayInputHandler; diff --git a/osu.Game/Online/Spectator/SpectatorStreamingClient.cs b/osu.Game/Online/Spectator/SpectatorStreamingClient.cs index 3a586874fe..7bea49e102 100644 --- a/osu.Game/Online/Spectator/SpectatorStreamingClient.cs +++ b/osu.Game/Online/Spectator/SpectatorStreamingClient.cs @@ -60,6 +60,7 @@ namespace osu.Game.Online.Spectator private IBindable> currentMods { get; set; } private readonly SpectatorState currentState = new SpectatorState(); + private readonly Dictionary currentUserStates = new Dictionary(); private bool isPlaying; @@ -68,10 +69,25 @@ namespace osu.Game.Online.Spectator /// public event Action OnNewFrames; + private event Action onUserBeganPlaying; + /// - /// Called whenever a user starts a play session. + /// Called whenever a user starts a play session, or immediately if the user is being watched and currently in a play session. /// - public event Action OnUserBeganPlaying; + public event Action OnUserBeganPlaying + { + add + { + onUserBeganPlaying += value; + + lock (userLock) + { + foreach (var (userId, state) in currentUserStates) + value?.Invoke(userId, state); + } + } + remove => onUserBeganPlaying -= value; + } /// /// Called whenever a user finishes a play session. @@ -134,7 +150,10 @@ namespace osu.Game.Online.Spectator if (!playingUsers.Contains(userId)) playingUsers.Add(userId); - OnUserBeganPlaying?.Invoke(userId, state); + lock (userLock) + currentUserStates[userId] = state; + + onUserBeganPlaying?.Invoke(userId, state); return Task.CompletedTask; } @@ -143,6 +162,9 @@ namespace osu.Game.Online.Spectator { playingUsers.Remove(userId); + lock (userLock) + currentUserStates.Remove(userId); + OnUserFinishedPlaying?.Invoke(userId, state); return Task.CompletedTask; From ca74f413cd10ccaee2dd1e51c5190019262b76c8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 16 Apr 2021 17:29:42 +0900 Subject: [PATCH 147/150] Change to explicit method instead --- .../Visual/Gameplay/TestSceneSpectator.cs | 2 +- .../Spectator/SpectatorStreamingClient.cs | 38 ++++++++++--------- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs index ea66144b21..def662d3ea 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs @@ -221,7 +221,7 @@ namespace osu.Game.Tests.Visual.Gameplay callbackInvoked = false; }); - AddStep("bind event again", () => testSpectatorStreamingClient.OnUserBeganPlaying += callbackAction); + AddStep("bind event with run once immediately", () => testSpectatorStreamingClient.BindUserBeganPlaying(callbackAction, true)); AddAssert("callback invoked", () => callbackInvoked); // Don't leave the event bound if test run succeeded. diff --git a/osu.Game/Online/Spectator/SpectatorStreamingClient.cs b/osu.Game/Online/Spectator/SpectatorStreamingClient.cs index 7bea49e102..4bbc420223 100644 --- a/osu.Game/Online/Spectator/SpectatorStreamingClient.cs +++ b/osu.Game/Online/Spectator/SpectatorStreamingClient.cs @@ -69,25 +69,10 @@ namespace osu.Game.Online.Spectator /// public event Action OnNewFrames; - private event Action onUserBeganPlaying; - /// /// Called whenever a user starts a play session, or immediately if the user is being watched and currently in a play session. /// - public event Action OnUserBeganPlaying - { - add - { - onUserBeganPlaying += value; - - lock (userLock) - { - foreach (var (userId, state) in currentUserStates) - value?.Invoke(userId, state); - } - } - remove => onUserBeganPlaying -= value; - } + public event Action OnUserBeganPlaying; /// /// Called whenever a user finishes a play session. @@ -153,7 +138,7 @@ namespace osu.Game.Online.Spectator lock (userLock) currentUserStates[userId] = state; - onUserBeganPlaying?.Invoke(userId, state); + OnUserBeganPlaying?.Invoke(userId, state); return Task.CompletedTask; } @@ -290,5 +275,24 @@ namespace osu.Game.Online.Spectator lastSendTime = Time.Current; } + + /// + /// Bind an action to with the option of running the bound action once immediately. + /// + /// The action to perform when a user begins playing. + /// Whether the action provided in should be run once immediately for all users currently playing. + public void BindUserBeganPlaying(Action callback, bool runOnceImmediately = false) + { + OnUserBeganPlaying += callback; + + if (!runOnceImmediately) + return; + + lock (userLock) + { + foreach (var (userId, state) in currentUserStates) + callback(userId, state); + } + } } } From 377e5ce6b396c585a0652cf63253966c59c06948 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 16 Apr 2021 18:21:35 +0900 Subject: [PATCH 148/150] Fix test incorrect sending state too often --- osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs index def662d3ea..392419649b 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs @@ -312,7 +312,7 @@ namespace osu.Game.Tests.Visual.Gameplay public override void WatchUser(int userId) { - if (sentState) + if (!PlayingUsers.Contains(userId) && sentState) { // usually the server would do this. sendState(beatmapId); From 46d2181d42930ac61f6da22f8146ba2b74357929 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 16 Apr 2021 18:21:56 +0900 Subject: [PATCH 149/150] Remove now unnecessary (duplicating) test --- .../Visual/Gameplay/TestSceneSpectator.cs | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs index 392419649b..74ce66096e 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs @@ -1,7 +1,6 @@ // 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 System.Threading; @@ -205,29 +204,6 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("screen didn't change", () => Stack.CurrentScreen is SoloSpectator); } - [Test] - public void OnUserBeganPlayingCallbackInvokedOnNewAdd() - { - bool callbackInvoked = false; - Action callbackAction = (_, __) => callbackInvoked = true; - - AddStep("bind first event", () => testSpectatorStreamingClient.OnUserBeganPlaying += callbackAction); - start(); - AddAssert("callback invoked", () => callbackInvoked); - - AddStep("reset", () => - { - testSpectatorStreamingClient.OnUserBeganPlaying -= callbackAction; - callbackInvoked = false; - }); - - AddStep("bind event with run once immediately", () => testSpectatorStreamingClient.BindUserBeganPlaying(callbackAction, true)); - AddAssert("callback invoked", () => callbackInvoked); - - // Don't leave the event bound if test run succeeded. - AddStep("reset", () => testSpectatorStreamingClient.OnUserBeganPlaying -= callbackAction); - } - private OsuFramedReplayInputHandler replayHandler => (OsuFramedReplayInputHandler)Stack.ChildrenOfType().First().ReplayInputHandler; From 274e33184b8afb9d10ef8b2d380edc8b742ee2ab Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 16 Apr 2021 18:22:22 +0900 Subject: [PATCH 150/150] Fix SpectatorScreen potentially missing user playing callbacks --- osu.Game/Screens/Spectate/SpectatorScreen.cs | 42 ++++++++++++++------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/Spectate/SpectatorScreen.cs b/osu.Game/Screens/Spectate/SpectatorScreen.cs index 6dd3144fc8..7be6c6183b 100644 --- a/osu.Game/Screens/Spectate/SpectatorScreen.cs +++ b/osu.Game/Screens/Spectate/SpectatorScreen.cs @@ -62,26 +62,42 @@ namespace osu.Game.Screens.Spectate { base.LoadComplete(); - spectatorClient.OnUserBeganPlaying += userBeganPlaying; - spectatorClient.OnUserFinishedPlaying += userFinishedPlaying; - spectatorClient.OnNewFrames += userSentFrames; - - foreach (var id in userIds) + populateAllUsers().ContinueWith(_ => Schedule(() => { - userLookupCache.GetUserAsync(id).ContinueWith(u => Schedule(() => + spectatorClient.BindUserBeganPlaying(userBeganPlaying, true); + spectatorClient.OnUserFinishedPlaying += userFinishedPlaying; + spectatorClient.OnNewFrames += userSentFrames; + + managerUpdated = beatmaps.ItemUpdated.GetBoundCopy(); + managerUpdated.BindValueChanged(beatmapUpdated); + + lock (stateLock) { - if (u.Result == null) + foreach (var (id, _) in userMap) + spectatorClient.WatchUser(id); + } + })); + } + + private Task populateAllUsers() + { + var userLookupTasks = new Task[userIds.Length]; + + for (int i = 0; i < userIds.Length; i++) + { + var userId = userIds[i]; + + userLookupTasks[i] = userLookupCache.GetUserAsync(userId).ContinueWith(task => + { + if (!task.IsCompletedSuccessfully) return; lock (stateLock) - userMap[id] = u.Result; - - spectatorClient.WatchUser(id); - }), TaskContinuationOptions.OnlyOnRanToCompletion); + userMap[userId] = task.Result; + }); } - managerUpdated = beatmaps.ItemUpdated.GetBoundCopy(); - managerUpdated.BindValueChanged(beatmapUpdated); + return Task.WhenAll(userLookupTasks); } private void beatmapUpdated(ValueChangedEvent> e)