From 970388d4e22ca71b05021f7ee506a29313cfcc14 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Feb 2023 17:35:12 +0900 Subject: [PATCH 1/8] Move `Overlays` container to accept input and be frame-stable --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 71b452c309..c49f2d003f 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -184,11 +184,13 @@ private void load(CancellationToken? cancellationToken) { RelativeSizeAxes = Axes.Both, Child = KeyBindingInputManager - .WithChild(CreatePlayfieldAdjustmentContainer() - .WithChild(Playfield) - ), + .WithChildren(new Drawable[] + { + CreatePlayfieldAdjustmentContainer() + .WithChild(Playfield), + Overlays + }), }, - Overlays, } }; From b42b5f97cfc5944b3a3d84d008c88e000d939cad Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Feb 2023 17:36:01 +0900 Subject: [PATCH 2/8] Use `Overlays` container rather than `KeyBindingInputManager` for flashlight --- osu.Game/Rulesets/Mods/ModFlashlight.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/ModFlashlight.cs b/osu.Game/Rulesets/Mods/ModFlashlight.cs index 45fa55c7f2..9fe0864c7a 100644 --- a/osu.Game/Rulesets/Mods/ModFlashlight.cs +++ b/osu.Game/Rulesets/Mods/ModFlashlight.cs @@ -82,7 +82,7 @@ public virtual void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) flashlight.Colour = Color4.Black; flashlight.Combo.BindTo(Combo); - drawableRuleset.KeyBindingInputManager.Add(flashlight); + drawableRuleset.Overlays.Add(flashlight); } protected abstract Flashlight CreateFlashlight(); From 5ec5222d8acd2f902689e0b0e0eeffd697e46d2a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Feb 2023 17:35:41 +0900 Subject: [PATCH 3/8] Expose and consume `OsuInputManager` explicitly --- osu.Game.Rulesets.Osu/Mods/InputBlockingMod.cs | 7 ++++--- osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs | 3 ++- osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs | 3 ++- osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs | 4 +++- osu.Game/Rulesets/UI/DrawableRuleset.cs | 2 +- 5 files changed, 12 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/InputBlockingMod.cs b/osu.Game.Rulesets.Osu/Mods/InputBlockingMod.cs index 7db4e2625b..b76b8d8cf5 100644 --- a/osu.Game.Rulesets.Osu/Mods/InputBlockingMod.cs +++ b/osu.Game.Rulesets.Osu/Mods/InputBlockingMod.cs @@ -11,6 +11,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Screens.Play; @@ -26,7 +27,7 @@ public abstract partial class InputBlockingMod : Mod, IApplicableToDrawableRules private const double flash_duration = 1000; - private DrawableRuleset ruleset = null!; + private DrawableOsuRuleset ruleset = null!; protected OsuAction? LastAcceptedAction { get; private set; } @@ -42,8 +43,8 @@ public abstract partial class InputBlockingMod : Mod, IApplicableToDrawableRules public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { - ruleset = drawableRuleset; - drawableRuleset.KeyBindingInputManager.Add(new InputInterceptor(this)); + ruleset = (DrawableOsuRuleset)drawableRuleset; + ruleset.InputManager.Add(new InputInterceptor(this)); var periods = new List(); diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs index 6772cfe0be..909238954a 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs @@ -11,6 +11,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Replays; +using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Osu.Mods @@ -55,7 +56,7 @@ public void Update(Playfield playfield) public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { // Grab the input manager to disable the user's cursor, and for future use - inputManager = (OsuInputManager)drawableRuleset.KeyBindingInputManager; + inputManager = ((DrawableOsuRuleset)drawableRuleset).InputManager; inputManager.AllowUserCursorMovement = false; // Generate the replay frames the cursor should follow diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs index 753de6231a..4feb0f9537 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs @@ -10,6 +10,7 @@ using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.UI; using osu.Game.Screens.Play; @@ -42,7 +43,7 @@ public class OsuModRelax : ModRelax, IUpdatableByPlayfield, IApplicableToDrawabl public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { // grab the input manager for future use. - osuInputManager = (OsuInputManager)drawableRuleset.KeyBindingInputManager; + osuInputManager = ((DrawableOsuRuleset)drawableRuleset).InputManager; } public void ApplyToPlayer(Player player) diff --git a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs index d1fa0d09cc..4ad95a8012 100644 --- a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs @@ -26,6 +26,8 @@ public partial class DrawableOsuRuleset : DrawableRuleset { protected new OsuRulesetConfigManager Config => (OsuRulesetConfigManager)base.Config; + public OsuInputManager InputManager { get; private set; } + public new OsuPlayfield Playfield => (OsuPlayfield)base.Playfield; public DrawableOsuRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) @@ -39,7 +41,7 @@ public DrawableOsuRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList protected override Playfield CreatePlayfield() => new OsuPlayfield(); - protected override PassThroughInputManager CreateInputManager() => new OsuInputManager(Ruleset.RulesetInfo); + protected override PassThroughInputManager CreateInputManager() => InputManager = new OsuInputManager(Ruleset.RulesetInfo); public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new OsuPlayfieldAdjustmentContainer { AlignWithStoryboard = true }; diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index c49f2d003f..11b238480a 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -53,7 +53,7 @@ public abstract partial class DrawableRuleset : DrawableRuleset, IProvi /// /// The key conversion input manager for this DrawableRuleset. /// - public PassThroughInputManager KeyBindingInputManager; + protected PassThroughInputManager KeyBindingInputManager; public override double GameplayStartTime => Objects.FirstOrDefault()?.StartTime - 2000 ?? 0; From c540d78fbc05065d859a298ebb2c4d717e3fb944 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Feb 2023 18:10:25 +0900 Subject: [PATCH 4/8] Expose the actual `KeyBindingInputManager` Turns out that `CreateInputManager` is called more than once, and some mods (ie. `InputBlockingMod`) rely on consuming the "main" one. So let's go back to accessing and exposing in `DrawableOsuRuleset` rather than storing out own reference. --- osu.Game.Rulesets.Osu/Mods/InputBlockingMod.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs | 2 +- osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/InputBlockingMod.cs b/osu.Game.Rulesets.Osu/Mods/InputBlockingMod.cs index b76b8d8cf5..b56fdbdf74 100644 --- a/osu.Game.Rulesets.Osu/Mods/InputBlockingMod.cs +++ b/osu.Game.Rulesets.Osu/Mods/InputBlockingMod.cs @@ -44,7 +44,7 @@ public abstract partial class InputBlockingMod : Mod, IApplicableToDrawableRules public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { ruleset = (DrawableOsuRuleset)drawableRuleset; - ruleset.InputManager.Add(new InputInterceptor(this)); + ruleset.KeyBindingInputManager.Add(new InputInterceptor(this)); var periods = new List(); diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs index 909238954a..d39ca06da3 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs @@ -56,7 +56,7 @@ public void Update(Playfield playfield) public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { // Grab the input manager to disable the user's cursor, and for future use - inputManager = ((DrawableOsuRuleset)drawableRuleset).InputManager; + inputManager = ((DrawableOsuRuleset)drawableRuleset).KeyBindingInputManager; inputManager.AllowUserCursorMovement = false; // Generate the replay frames the cursor should follow diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs index 4feb0f9537..32ffb545e0 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs @@ -43,7 +43,7 @@ public class OsuModRelax : ModRelax, IUpdatableByPlayfield, IApplicableToDrawabl public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { // grab the input manager for future use. - osuInputManager = ((DrawableOsuRuleset)drawableRuleset).InputManager; + osuInputManager = ((DrawableOsuRuleset)drawableRuleset).KeyBindingInputManager; } public void ApplyToPlayer(Player player) diff --git a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs index 4ad95a8012..c3efd48053 100644 --- a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs @@ -26,7 +26,7 @@ public partial class DrawableOsuRuleset : DrawableRuleset { protected new OsuRulesetConfigManager Config => (OsuRulesetConfigManager)base.Config; - public OsuInputManager InputManager { get; private set; } + public new OsuInputManager KeyBindingInputManager => (OsuInputManager)base.KeyBindingInputManager; public new OsuPlayfield Playfield => (OsuPlayfield)base.Playfield; @@ -41,7 +41,7 @@ public DrawableOsuRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList protected override Playfield CreatePlayfield() => new OsuPlayfield(); - protected override PassThroughInputManager CreateInputManager() => InputManager = new OsuInputManager(Ruleset.RulesetInfo); + protected override PassThroughInputManager CreateInputManager() => new OsuInputManager(Ruleset.RulesetInfo); public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new OsuPlayfieldAdjustmentContainer { AlignWithStoryboard = true }; From 1acc5362480b5eec0f8f150d2f44a6755b47f76a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Feb 2023 19:03:52 +0900 Subject: [PATCH 5/8] Move `DrawableRuleset.Audio` to a less generic level --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 44 +++++++++++-------------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 11b238480a..7d7361b9b6 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -66,6 +66,10 @@ public abstract partial class DrawableRuleset : DrawableRuleset, IProvi public override Container Overlays { get; } = new Container { RelativeSizeAxes = Axes.Both }; + public override IAdjustableAudioComponent Audio => audioContainer; + + private readonly AudioContainer audioContainer = new AudioContainer { RelativeSizeAxes = Axes.Both }; + public override Container FrameStableComponents { get; } = new Container { RelativeSizeAxes = Axes.Both }; public override IFrameStableClock FrameStableClock => frameStabilityContainer; @@ -102,14 +106,6 @@ internal override bool FrameStablePlayback private DrawableRulesetDependencies dependencies; - /// - /// Audio adjustments which are applied to the playfield. - /// - /// - /// Does not affect . - /// - public IAdjustableAudioComponent Audio { get; private set; } - /// /// Creates a ruleset visualisation for the provided ruleset and beatmap. /// @@ -172,30 +168,22 @@ protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnl [BackgroundDependencyLoader] private void load(CancellationToken? cancellationToken) { - AudioContainer audioContainer; - InternalChild = frameStabilityContainer = new FrameStabilityContainer(GameplayStartTime) { FrameStablePlayback = FrameStablePlayback, Children = new Drawable[] { FrameStableComponents, - audioContainer = new AudioContainer - { - RelativeSizeAxes = Axes.Both, - Child = KeyBindingInputManager - .WithChildren(new Drawable[] - { - CreatePlayfieldAdjustmentContainer() - .WithChild(Playfield), - Overlays - }), - }, + audioContainer.WithChild(KeyBindingInputManager + .WithChildren(new Drawable[] + { + CreatePlayfieldAdjustmentContainer() + .WithChild(Playfield), + Overlays + })), } }; - Audio = audioContainer; - if ((ResumeOverlay = CreateResumeOverlay()) != null) { AddInternal(CreateInputManager() @@ -438,13 +426,21 @@ public abstract partial class DrawableRuleset : CompositeDrawable /// public readonly BindableBool IsPaused = new BindableBool(); + /// + /// Audio adjustments which are applied to the playfield. + /// + /// + /// Does not affect . + /// + public abstract IAdjustableAudioComponent Audio { get; } + /// /// The playfield. /// public abstract Playfield Playfield { get; } /// - /// Content to be placed above hitobjects. Will be affected by frame stability. + /// Content to be placed above hitobjects. Will be affected by frame stability and adjustments applied to . /// public abstract Container Overlays { get; } From 9384687d6d38f02e98af67975b008abfde406167 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Feb 2023 19:04:06 +0900 Subject: [PATCH 6/8] Switch `ModMuted` to add its metronome to components rather than overlays --- osu.Game/Rulesets/Mods/ModMuted.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/ModMuted.cs b/osu.Game/Rulesets/Mods/ModMuted.cs index 367ceeb446..131f501630 100644 --- a/osu.Game/Rulesets/Mods/ModMuted.cs +++ b/osu.Game/Rulesets/Mods/ModMuted.cs @@ -67,7 +67,8 @@ public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { MetronomeBeat metronomeBeat; - drawableRuleset.Overlays.Add(metronomeBeat = new MetronomeBeat(drawableRuleset.Beatmap.HitObjects.First().StartTime)); + // Importantly, this is added to FrameStableComponents and not Overlays as the latter would cause it to be self-muted by the mod's volume adjustment. + drawableRuleset.FrameStableComponents.Add(metronomeBeat = new MetronomeBeat(drawableRuleset.Beatmap.HitObjects.First().StartTime)); metronomeBeat.AddAdjustment(AdjustableProperty.Volume, metronomeVolumeAdjust); } From d59d153654f6543e2f51556454964c51bb9c8f1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 21 Feb 2023 21:03:00 +0100 Subject: [PATCH 7/8] Fix test compile failures from `Audio` hoisting --- osu.Game.Tests/NonVisual/FirstAvailableHitWindowsTest.cs | 2 ++ osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/osu.Game.Tests/NonVisual/FirstAvailableHitWindowsTest.cs b/osu.Game.Tests/NonVisual/FirstAvailableHitWindowsTest.cs index 9a32b8e894..0bdd0ceae6 100644 --- a/osu.Game.Tests/NonVisual/FirstAvailableHitWindowsTest.cs +++ b/osu.Game.Tests/NonVisual/FirstAvailableHitWindowsTest.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using NUnit.Framework; +using osu.Framework.Audio; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mods; @@ -93,6 +94,7 @@ public override event Action RevertResult remove => throw new InvalidOperationException($"{nameof(RevertResult)} operations not supported in test context"); } + public override IAdjustableAudioComponent Audio { get; } public override Playfield Playfield { get; } public override Container Overlays { get; } public override Container FrameStableComponents { get; } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs index e2ff2780e0..56900a0549 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs @@ -9,6 +9,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Audio; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -281,6 +282,7 @@ public override event Action RevertResult remove => throw new InvalidOperationException($"{nameof(RevertResult)} operations not supported in test context"); } + public override IAdjustableAudioComponent Audio { get; } public override Playfield Playfield { get; } public override Container Overlays { get; } public override Container FrameStableComponents { get; } From ab97b022355abdc2bfd33473d0f102af9d8baa62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 21 Feb 2023 21:05:46 +0100 Subject: [PATCH 8/8] Remove contradictory remark from xmldoc --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 7d7361b9b6..df482a6459 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -429,9 +429,6 @@ public abstract partial class DrawableRuleset : CompositeDrawable /// /// Audio adjustments which are applied to the playfield. /// - /// - /// Does not affect . - /// public abstract IAdjustableAudioComponent Audio { get; } ///