From dc75d55f72af11bdf81b081dad8f5e84be3ed2e2 Mon Sep 17 00:00:00 2001 From: Gabe Livengood <47010459+ggliv@users.noreply.github.com> Date: Wed, 8 Jun 2022 14:02:15 -0400 Subject: [PATCH 001/476] allow modfailcondition to arbitrarily trigger fail --- osu.Game/Rulesets/Mods/ModFailCondition.cs | 19 +++++++++++++++++++ osu.Game/Rulesets/Scoring/HealthProcessor.cs | 14 ++++++++++---- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModFailCondition.cs b/osu.Game/Rulesets/Mods/ModFailCondition.cs index 4425ece513..1aab0ab880 100644 --- a/osu.Game/Rulesets/Mods/ModFailCondition.cs +++ b/osu.Game/Rulesets/Mods/ModFailCondition.cs @@ -19,12 +19,31 @@ namespace osu.Game.Rulesets.Mods public virtual bool PerformFail() => true; public virtual bool RestartOnFail => Restart.Value; + private HealthProcessor healthProcessorInternal; public void ApplyToHealthProcessor(HealthProcessor healthProcessor) { + healthProcessorInternal = healthProcessor; healthProcessor.FailConditions += FailCondition; } + /// + /// Immediately triggers a failure on the loaded . + /// + protected void TriggerArbitraryFailure() => healthProcessorInternal.TriggerFailure(); + + /// + /// Determines whether should trigger a failure. Called every time a + /// judgement is applied to . + /// + /// The loaded . + /// The latest . + /// Whether the fail condition has been met. + /// + /// This method should only be used to trigger failures based on . + /// Using outside values to evaluate failure may introduce event ordering discrepancies, use + /// an with instead. + /// protected abstract bool FailCondition(HealthProcessor healthProcessor, JudgementResult result); } } diff --git a/osu.Game/Rulesets/Scoring/HealthProcessor.cs b/osu.Game/Rulesets/Scoring/HealthProcessor.cs index 0f51560476..4f5ff95477 100644 --- a/osu.Game/Rulesets/Scoring/HealthProcessor.cs +++ b/osu.Game/Rulesets/Scoring/HealthProcessor.cs @@ -33,6 +33,15 @@ namespace osu.Game.Rulesets.Scoring /// public bool HasFailed { get; private set; } + /// + /// Immediately triggers a failure for this HealthProcessor. + /// + public void TriggerFailure() + { + if (Failed?.Invoke() != false) + HasFailed = true; + } + protected override void ApplyResultInternal(JudgementResult result) { result.HealthAtJudgement = Health.Value; @@ -44,10 +53,7 @@ namespace osu.Game.Rulesets.Scoring Health.Value += GetHealthIncreaseFor(result); if (meetsAnyFailCondition(result)) - { - if (Failed?.Invoke() != false) - HasFailed = true; - } + TriggerFailure(); } protected override void RevertResultInternal(JudgementResult result) From 21c5499da16eb88f12d6f5bee8d28b1557559b2f Mon Sep 17 00:00:00 2001 From: Gabe Livengood <47010459+ggliv@users.noreply.github.com> Date: Fri, 10 Jun 2022 13:11:17 -0400 Subject: [PATCH 002/476] remove arbitrary from method name --- osu.Game/Rulesets/Mods/ModFailCondition.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModFailCondition.cs b/osu.Game/Rulesets/Mods/ModFailCondition.cs index 1aab0ab880..9500734408 100644 --- a/osu.Game/Rulesets/Mods/ModFailCondition.cs +++ b/osu.Game/Rulesets/Mods/ModFailCondition.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mods /// /// Immediately triggers a failure on the loaded . /// - protected void TriggerArbitraryFailure() => healthProcessorInternal.TriggerFailure(); + protected void TriggerFailure() => healthProcessorInternal.TriggerFailure(); /// /// Determines whether should trigger a failure. Called every time a @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Mods /// /// This method should only be used to trigger failures based on . /// Using outside values to evaluate failure may introduce event ordering discrepancies, use - /// an with instead. + /// an with instead. /// protected abstract bool FailCondition(HealthProcessor healthProcessor, JudgementResult result); } From 6e64a8f55ef5838a5d58625668138fb3c85e34db Mon Sep 17 00:00:00 2001 From: Gabe Livengood <47010459+ggliv@users.noreply.github.com> Date: Fri, 10 Jun 2022 13:13:35 -0400 Subject: [PATCH 003/476] use event to trigger failure --- osu.Game/Rulesets/Mods/ModFailCondition.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModFailCondition.cs b/osu.Game/Rulesets/Mods/ModFailCondition.cs index 9500734408..63cebc9747 100644 --- a/osu.Game/Rulesets/Mods/ModFailCondition.cs +++ b/osu.Game/Rulesets/Mods/ModFailCondition.cs @@ -19,18 +19,18 @@ namespace osu.Game.Rulesets.Mods public virtual bool PerformFail() => true; public virtual bool RestartOnFail => Restart.Value; - private HealthProcessor healthProcessorInternal; + private event Action failureTriggered; public void ApplyToHealthProcessor(HealthProcessor healthProcessor) { - healthProcessorInternal = healthProcessor; + failureTriggered = healthProcessor.TriggerFailure; healthProcessor.FailConditions += FailCondition; } /// /// Immediately triggers a failure on the loaded . /// - protected void TriggerFailure() => healthProcessorInternal.TriggerFailure(); + protected void TriggerFailure() => failureTriggered?.Invoke(); /// /// Determines whether should trigger a failure. Called every time a From c1077d909ca5e41270522714e8457733a36043f2 Mon Sep 17 00:00:00 2001 From: Ryuki Date: Sat, 17 Sep 2022 21:09:34 +0200 Subject: [PATCH 004/476] Basic avatar HUD implementation --- osu.Game/Screens/Play/HUD/SkinnableAvatar.cs | 50 ++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 osu.Game/Screens/Play/HUD/SkinnableAvatar.cs diff --git a/osu.Game/Screens/Play/HUD/SkinnableAvatar.cs b/osu.Game/Screens/Play/HUD/SkinnableAvatar.cs new file mode 100644 index 0000000000..abec4402a7 --- /dev/null +++ b/osu.Game/Screens/Play/HUD/SkinnableAvatar.cs @@ -0,0 +1,50 @@ +// 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.Configuration; +using osu.Game.Skinning; +using osu.Game.Users.Drawables; +using osuTK; + +namespace osu.Game.Screens.Play.HUD +{ + public class SkinnableAvatar : CompositeDrawable, ISkinnableDrawable + { + [SettingSource("Corner radius", "How much the edges should be rounded.")] + public new BindableFloat CornerRadius { get; set; } = new BindableFloat(0) + { + MinValue = 0, + MaxValue = 100, + Precision = 0.01f + }; + + [Resolved] + private GameplayState gameplayState { get; set; } = null!; + + private readonly UpdateableAvatar avatar; + + public SkinnableAvatar() + { + Size = new Vector2(128f); + InternalChild = avatar = new UpdateableAvatar(isInteractive: false) + { + RelativeSizeAxes = Axes.Both, + Masking = true + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + avatar.User = gameplayState.Score.ScoreInfo.User; + CornerRadius.BindValueChanged(e => avatar.CornerRadius = e.NewValue); + } + + public bool UsesFixedAnchor { get; set; } + } +} From ecf71df8a23bfca5cbe5a78daef23908135d92c2 Mon Sep 17 00:00:00 2001 From: Ryuki Date: Sun, 18 Sep 2022 00:04:56 +0200 Subject: [PATCH 005/476] Change CornerRadius Max Value --- osu.Game/Screens/Play/HUD/SkinnableAvatar.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/SkinnableAvatar.cs b/osu.Game/Screens/Play/HUD/SkinnableAvatar.cs index abec4402a7..d675176a0a 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableAvatar.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableAvatar.cs @@ -15,10 +15,10 @@ namespace osu.Game.Screens.Play.HUD public class SkinnableAvatar : CompositeDrawable, ISkinnableDrawable { [SettingSource("Corner radius", "How much the edges should be rounded.")] - public new BindableFloat CornerRadius { get; set; } = new BindableFloat(0) + public new BindableFloat CornerRadius { get; set; } = new BindableFloat { MinValue = 0, - MaxValue = 100, + MaxValue = 63, Precision = 0.01f }; From bbb22479a8aefd1b040d236dde395c0220ed4bc4 Mon Sep 17 00:00:00 2001 From: mk56-spn Date: Sun, 25 Dec 2022 21:32:47 +0100 Subject: [PATCH 006/476] Add "ModBubbles" for the osu ruleset. --- .../Mods/TestSceneOsuModBubbles.cs | 19 ++ osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 228 ++++++++++++++++++ osu.Game.Rulesets.Osu/OsuRuleset.cs | 3 +- osu.Game/Rulesets/Mods/ModFlashlight.cs | 3 + 4 files changed, 252 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModBubbles.cs create mode 100644 osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModBubbles.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModBubbles.cs new file mode 100644 index 0000000000..e72a1f79f5 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModBubbles.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 NUnit.Framework; +using osu.Game.Rulesets.Osu.Mods; + +namespace osu.Game.Rulesets.Osu.Tests.Mods +{ + public partial class TestSceneOsuModBubbles : OsuModTestScene + { + [Test] + public void TestOsuModBubbles() => CreateModTest(new ModTestData + { + Mod = new OsuModBubbles(), + Autoplay = true, + PassCondition = () => true + }); + } +} diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs new file mode 100644 index 0000000000..c51ebde383 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs @@ -0,0 +1,228 @@ +// 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 osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; +using osu.Framework.Graphics.Performance; +using osu.Framework.Graphics.Pooling; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Localisation; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Objects.Pooling; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; +using osu.Game.Scoring; +using osuTK; + +namespace osu.Game.Rulesets.Osu.Mods +{ + public partial class OsuModBubbles : ModWithVisibilityAdjustment, IApplicableToDrawableRuleset, IApplicableToScoreProcessor + { + public override string Name => "Bubbles"; + + public override string Acronym => "BB"; + + public override LocalisableString Description => "Dont let their popping distract you!"; + + public override double ScoreMultiplier => 1; + + public override ModType Type => ModType.Fun; + + // Compatibility with these seems potentially feasible in the future, blocked for now because they dont work as one would expect + public override Type[] IncompatibleMods => new[] { typeof(OsuModBarrelRoll), typeof(OsuModMagnetised), typeof(OsuModRepel) }; + + private PlayfieldAdjustmentContainer adjustmentContainer = null!; + private BubbleContainer bubbleContainer = null!; + + private readonly Bindable currentCombo = new BindableInt(); + + private float maxSize; + private float bubbleRadius; + private double bubbleFade; + + public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank; + + public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) + { + currentCombo.BindTo(scoreProcessor.Combo); + currentCombo.BindValueChanged(combo => + maxSize = Math.Min(1.75f, (float)(1.25 + 0.005 * combo.NewValue)), true); + } + + public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) + { + // Multiplying by 2 results in an initial size that is too large, hence 1.85 has been chosen + bubbleRadius = (float)(drawableRuleset.Beatmap.HitObjects.OfType().First().Radius * 1.85f); + bubbleFade = drawableRuleset.Beatmap.HitObjects.OfType().First().TimeFadeIn * 2; + + // We want to hide the judgements since they are obscured by the BubbleDrawable (due to layering) + drawableRuleset.Playfield.DisplayJudgements.Value = false; + + adjustmentContainer = drawableRuleset.CreatePlayfieldAdjustmentContainer(); + + adjustmentContainer.Add(bubbleContainer = new BubbleContainer()); + drawableRuleset.KeyBindingInputManager.Add(adjustmentContainer); + } + + protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) => applyBubbleState(hitObject); + + protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) => applyBubbleState(hitObject); + + private void applyBubbleState(DrawableHitObject drawableObject) + { + if (drawableObject is not DrawableOsuHitObject drawableOsuObject || !drawableObject.Judged) return; + + OsuHitObject hitObject = drawableOsuObject.HitObject; + + switch (drawableOsuObject) + { + //Needs to be done explicitly to avoid being handled by DrawableHitCircle below + case DrawableSliderHead: + addBubbleContainer(hitObject.Position); + break; + + //Stack leniency causes placement issues if this isn't handled as such. + case DrawableHitCircle hitCircle: + addBubbleContainer(hitCircle.Position); + break; + + case DrawableSpinnerTick: + case DrawableSlider: + return; + + default: + addBubbleContainer(hitObject.Position); + break; + } + + void addBubbleContainer(Vector2 position) => bubbleContainer.Add(new BubbleLifeTimeEntry + { + LifetimeStart = bubbleContainer.Time.Current, + Colour = drawableOsuObject.AccentColour.Value, + Position = position, + InitialSize = new Vector2(bubbleRadius), + MaxSize = maxSize, + FadeTime = bubbleFade, + IsHit = drawableOsuObject.IsHit + } + ); + } + + #region Pooled Bubble drawable + + //LifetimeEntry flow is necessary to allow for correct rewind behaviour, can probably be made generic later if more mods are made requiring it + //Todo: find solution to bubbles rewinding in "groups" + private sealed partial class BubbleContainer : PooledDrawableWithLifetimeContainer + { + protected override bool RemoveRewoundEntry => true; + + private readonly DrawablePool pool; + + public BubbleContainer() + { + RelativeSizeAxes = Axes.Both; + AddInternal(pool = new DrawablePool(10, 1000)); + } + + protected override BubbleObject GetDrawable(BubbleLifeTimeEntry entry) => pool.Get(d => d.Apply(entry)); + } + + private sealed partial class BubbleObject : PoolableDrawableWithLifetime + { + private readonly BubbleDrawable bubbleDrawable; + + public BubbleObject() + { + InternalChild = bubbleDrawable = new BubbleDrawable(); + } + + protected override void OnApply(BubbleLifeTimeEntry entry) + { + base.OnApply(entry); + if (IsLoaded) + apply(entry); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + apply(Entry); + } + + private void apply(BubbleLifeTimeEntry? entry) + { + if (entry == null) + return; + + ApplyTransformsAt(float.MinValue, true); + ClearTransforms(true); + + Position = entry.Position; + + bubbleDrawable.Animate(entry); + + LifetimeEnd = bubbleDrawable.LatestTransformEndTime; + } + } + + private partial class BubbleDrawable : CompositeDrawable + { + [BackgroundDependencyLoader] + private void load() + { + InternalChild = new Circle + { + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Masking = true, + EdgeEffect = new EdgeEffectParameters + { + Colour = Colour4.Black.Opacity(0.05f), + Type = EdgeEffectType.Shadow, + Radius = 5 + } + }; + } + + public void Animate(BubbleLifeTimeEntry entry) + { + Size = entry.InitialSize; + this + .ScaleTo(entry.MaxSize, entry.FadeTime * 6, Easing.OutSine) + .FadeColour(entry.IsHit ? entry.Colour : Colour4.Black, entry.FadeTime, Easing.OutSine) + .Delay(entry.FadeTime) + .FadeColour(entry.IsHit ? entry.Colour.Darken(.4f) : Colour4.Black, entry.FadeTime * 5, Easing.OutSine) + .Then() + .ScaleTo(entry.MaxSize * 1.5f, entry.FadeTime, Easing.OutSine) + .FadeTo(0, entry.FadeTime, Easing.OutQuint); + } + } + + private class BubbleLifeTimeEntry : LifetimeEntry + { + public Vector2 InitialSize { get; set; } + + public float MaxSize { get; set; } + + public Vector2 Position { get; set; } + + public Colour4 Colour { get; set; } + + // FadeTime is based on the approach rate of the beatmap. + public double FadeTime { get; set; } + + // Whether the corresponding HitObject was hit + public bool IsHit { get; set; } + } + + #endregion + } +} diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 79a566e33c..0df1e4dfca 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -202,7 +202,8 @@ namespace osu.Game.Rulesets.Osu new OsuModNoScope(), new MultiMod(new OsuModMagnetised(), new OsuModRepel()), new ModAdaptiveSpeed(), - new OsuModFreezeFrame() + new OsuModFreezeFrame(), + new OsuModBubbles() }; case ModType.System: diff --git a/osu.Game/Rulesets/Mods/ModFlashlight.cs b/osu.Game/Rulesets/Mods/ModFlashlight.cs index 45fa55c7f2..2c9ef357b5 100644 --- a/osu.Game/Rulesets/Mods/ModFlashlight.cs +++ b/osu.Game/Rulesets/Mods/ModFlashlight.cs @@ -83,6 +83,9 @@ namespace osu.Game.Rulesets.Mods flashlight.Combo.BindTo(Combo); drawableRuleset.KeyBindingInputManager.Add(flashlight); + + // Stop flashlight from being drawn underneath other mods that generate HitObjects. + drawableRuleset.KeyBindingInputManager.ChangeChildDepth(flashlight, -1); } protected abstract Flashlight CreateFlashlight(); From 8a108b143e10bf80162eb0109aad7a68ae9692fc Mon Sep 17 00:00:00 2001 From: mk56-spn Date: Sun, 25 Dec 2022 21:33:10 +0100 Subject: [PATCH 007/476] Address mod incompatibilities --- osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs | 3 +++ osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 17 +++++++++++------ osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModRepel.cs | 2 +- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs index 9e71f657ce..2394cf92fc 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.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 osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects; @@ -10,6 +11,8 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModBarrelRoll : ModBarrelRoll, IApplicableToDrawableHitObject { + public override Type[] IncompatibleMods => new[] { typeof(OsuModBubbles) }; + public void ApplyToDrawableHitObject(DrawableHitObject d) { d.OnUpdate += _ => diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs index c51ebde383..2e4d574148 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs @@ -86,12 +86,12 @@ namespace osu.Game.Rulesets.Osu.Mods { //Needs to be done explicitly to avoid being handled by DrawableHitCircle below case DrawableSliderHead: - addBubbleContainer(hitObject.Position); + addBubbleContainer(hitObject.Position, drawableOsuObject); break; //Stack leniency causes placement issues if this isn't handled as such. case DrawableHitCircle hitCircle: - addBubbleContainer(hitCircle.Position); + addBubbleContainer(hitCircle.Position, drawableOsuObject); break; case DrawableSpinnerTick: @@ -99,19 +99,24 @@ namespace osu.Game.Rulesets.Osu.Mods return; default: - addBubbleContainer(hitObject.Position); + addBubbleContainer(hitObject.Position, drawableOsuObject); break; } + } - void addBubbleContainer(Vector2 position) => bubbleContainer.Add(new BubbleLifeTimeEntry + private void addBubbleContainer(Vector2 position, DrawableHitObject hitObject) + { + bubbleContainer.Add + ( + new BubbleLifeTimeEntry { LifetimeStart = bubbleContainer.Time.Current, - Colour = drawableOsuObject.AccentColour.Value, + Colour = hitObject.AccentColour.Value, Position = position, InitialSize = new Vector2(bubbleRadius), MaxSize = maxSize, FadeTime = bubbleFade, - IsHit = drawableOsuObject.IsHit + IsHit = hitObject.IsHit } ); } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs b/osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs index 38d90eb121..c8c4cd6a14 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override ModType Type => ModType.Fun; public override LocalisableString Description => "No need to chase the circles – your cursor is a magnet!"; public override double ScoreMultiplier => 0.5; - public override Type[] IncompatibleMods => new[] { typeof(OsuModAutopilot), typeof(OsuModWiggle), typeof(OsuModTransform), typeof(ModAutoplay), typeof(OsuModRelax), typeof(OsuModRepel) }; + public override Type[] IncompatibleMods => new[] { typeof(OsuModAutopilot), typeof(OsuModWiggle), typeof(OsuModTransform), typeof(ModAutoplay), typeof(OsuModRelax), typeof(OsuModRepel), typeof(OsuModBubbles) }; [SettingSource("Attraction strength", "How strong the pull is.", 0)] public BindableFloat AttractionStrength { get; } = new BindableFloat(0.5f) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRepel.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRepel.cs index 31a6b69d6b..28d459cedb 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRepel.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRepel.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override ModType Type => ModType.Fun; public override LocalisableString Description => "Hit objects run away!"; public override double ScoreMultiplier => 1; - public override Type[] IncompatibleMods => new[] { typeof(OsuModAutopilot), typeof(OsuModWiggle), typeof(OsuModTransform), typeof(ModAutoplay), typeof(OsuModMagnetised) }; + public override Type[] IncompatibleMods => new[] { typeof(OsuModAutopilot), typeof(OsuModWiggle), typeof(OsuModTransform), typeof(ModAutoplay), typeof(OsuModMagnetised), typeof(OsuModBubbles) }; [SettingSource("Repulsion strength", "How strong the repulsion is.", 0)] public BindableFloat RepulsionStrength { get; } = new BindableFloat(0.5f) From ca84b885dcc6695cb7da3549757f7e6338bc37de Mon Sep 17 00:00:00 2001 From: mk56-spn Date: Wed, 11 Jan 2023 17:51:41 +0100 Subject: [PATCH 008/476] Add more detail to bubbles --- osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 58 ++++++++++++++++----- 1 file changed, 45 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs index 2e4d574148..f5e7e035b2 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics.Performance; using osu.Framework.Graphics.Pooling; using osu.Framework.Graphics.Shapes; using osu.Framework.Localisation; +using osu.Game.Graphics; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Pooling; @@ -21,6 +22,7 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Scoring; using osuTK; +using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Mods { @@ -178,21 +180,38 @@ namespace osu.Game.Rulesets.Osu.Mods } } - private partial class BubbleDrawable : CompositeDrawable + private partial class BubbleDrawable : CompositeDrawable, IHasAccentColour { + public Color4 AccentColour { get; set; } + + private Circle outerCircle = null!; + private Circle innerCircle = null!; + [BackgroundDependencyLoader] private void load() { - InternalChild = new Circle + InternalChildren = new Drawable[] { - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Masking = true, - EdgeEffect = new EdgeEffectParameters + outerCircle = new Circle { - Colour = Colour4.Black.Opacity(0.05f), - Type = EdgeEffectType.Shadow, - Radius = 5 + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Masking = true, + MaskingSmoothness = 2, + BorderThickness = 0, + BorderColour = Colour4.Transparent, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Radius = 3, + Colour = Colour4.Black.Opacity(0.05f) + } + }, + innerCircle = new Circle + { + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Scale = new Vector2(0.5f) } }; } @@ -202,12 +221,25 @@ namespace osu.Game.Rulesets.Osu.Mods Size = entry.InitialSize; this .ScaleTo(entry.MaxSize, entry.FadeTime * 6, Easing.OutSine) - .FadeColour(entry.IsHit ? entry.Colour : Colour4.Black, entry.FadeTime, Easing.OutSine) - .Delay(entry.FadeTime) - .FadeColour(entry.IsHit ? entry.Colour.Darken(.4f) : Colour4.Black, entry.FadeTime * 5, Easing.OutSine) .Then() .ScaleTo(entry.MaxSize * 1.5f, entry.FadeTime, Easing.OutSine) - .FadeTo(0, entry.FadeTime, Easing.OutQuint); + .FadeTo(0, entry.FadeTime, Easing.OutExpo); + + animateCircles(entry); + } + + private void animateCircles(BubbleLifeTimeEntry entry) + { + innerCircle.FadeColour(entry.IsHit ? entry.Colour.Darken(0.2f) : Colour4.Black) + .FadeColour(entry.IsHit ? entry.Colour.Lighten(0.2f) : Colour4.Black, entry.FadeTime * 7); + + innerCircle.FadeTo(0.5f, entry.FadeTime * 7, Easing.InExpo) + .ScaleTo(1.1f, entry.FadeTime * 7, Easing.InSine); + + outerCircle + .FadeColour(entry.IsHit ? entry.Colour : Colour4.Black, entry.FadeTime, Easing.OutSine) + .Delay(entry.FadeTime) + .FadeColour(entry.IsHit ? entry.Colour.Darken(.4f) : Colour4.Black, entry.FadeTime * 5, Easing.OutSine); } } From 7c81f1e75bb40981a55f16a3544d9521280bf49c Mon Sep 17 00:00:00 2001 From: mk56-spn Date: Fri, 27 Jan 2023 11:21:11 +0100 Subject: [PATCH 009/476] Remove unnecessary BDL from bubble drawable Improve animation duration formula --- osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 85 +++++++++------------ 1 file changed, 38 insertions(+), 47 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs index f5e7e035b2..6613d84e0e 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs @@ -3,7 +3,6 @@ using System; using System.Linq; -using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -12,7 +11,6 @@ using osu.Framework.Graphics.Performance; using osu.Framework.Graphics.Pooling; using osu.Framework.Graphics.Shapes; using osu.Framework.Localisation; -using osu.Game.Graphics; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Pooling; @@ -22,7 +20,6 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Scoring; using osuTK; -using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Mods { @@ -63,7 +60,7 @@ namespace osu.Game.Rulesets.Osu.Mods { // Multiplying by 2 results in an initial size that is too large, hence 1.85 has been chosen bubbleRadius = (float)(drawableRuleset.Beatmap.HitObjects.OfType().First().Radius * 1.85f); - bubbleFade = drawableRuleset.Beatmap.HitObjects.OfType().First().TimeFadeIn * 2; + bubbleFade = drawableRuleset.Beatmap.HitObjects.OfType().First().TimePreempt * 2; // We want to hide the judgements since they are obscured by the BubbleDrawable (due to layering) drawableRuleset.Playfield.DisplayJudgements.Value = false; @@ -125,8 +122,8 @@ namespace osu.Game.Rulesets.Osu.Mods #region Pooled Bubble drawable - //LifetimeEntry flow is necessary to allow for correct rewind behaviour, can probably be made generic later if more mods are made requiring it - //Todo: find solution to bubbles rewinding in "groups" + // LifetimeEntry flow is necessary to allow for correct rewind behaviour, can probably be made generic later if more mods are made requiring it + // Todo: find solution to bubbles rewinding in "groups" private sealed partial class BubbleContainer : PooledDrawableWithLifetimeContainer { protected override bool RemoveRewoundEntry => true; @@ -166,8 +163,7 @@ namespace osu.Game.Rulesets.Osu.Mods private void apply(BubbleLifeTimeEntry? entry) { - if (entry == null) - return; + if (entry == null) return; ApplyTransformsAt(float.MinValue, true); ClearTransforms(true); @@ -180,38 +176,36 @@ namespace osu.Game.Rulesets.Osu.Mods } } - private partial class BubbleDrawable : CompositeDrawable, IHasAccentColour + private partial class BubbleDrawable : CircularContainer { - public Color4 AccentColour { get; set; } + private readonly Circle innerCircle; + private readonly Box colourBox; - private Circle outerCircle = null!; - private Circle innerCircle = null!; - - [BackgroundDependencyLoader] - private void load() + public BubbleDrawable() { - InternalChildren = new Drawable[] + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + Masking = true; + MaskingSmoothness = 2; + BorderThickness = 0; + BorderColour = Colour4.Transparent; + EdgeEffect = new EdgeEffectParameters { - outerCircle = new Circle - { - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Masking = true, - MaskingSmoothness = 2, - BorderThickness = 0, - BorderColour = Colour4.Transparent, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Radius = 3, - Colour = Colour4.Black.Opacity(0.05f) - } - }, + Type = EdgeEffectType.Shadow, + Radius = 3, + Colour = Colour4.Black.Opacity(0.05f) + }; + + Children = new Drawable[] + { + colourBox = new Box { RelativeSizeAxes = Axes.Both, }, innerCircle = new Circle { + Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, - Scale = new Vector2(0.5f) + Size = new Vector2(0.5f), } }; } @@ -219,27 +213,24 @@ namespace osu.Game.Rulesets.Osu.Mods public void Animate(BubbleLifeTimeEntry entry) { Size = entry.InitialSize; - this - .ScaleTo(entry.MaxSize, entry.FadeTime * 6, Easing.OutSine) + + this.ScaleTo(entry.MaxSize, getAnimationTime() * 0.8f, Easing.OutSine) .Then() - .ScaleTo(entry.MaxSize * 1.5f, entry.FadeTime, Easing.OutSine) - .FadeTo(0, entry.FadeTime, Easing.OutExpo); + .ScaleTo(entry.MaxSize * 1.5f, getAnimationTime() * 0.2f, Easing.OutSine) + .FadeTo(0, getAnimationTime() * 0.2f, Easing.OutExpo); - animateCircles(entry); - } + colourBox.FadeColour(entry.IsHit ? entry.Colour : Colour4.Black, getAnimationTime() * 0.1f, Easing.OutSine) + .Then() + .FadeColour(entry.IsHit ? entry.Colour.Darken(0.4f) : Colour4.Black, getAnimationTime() * 0.9f, Easing.OutSine); - private void animateCircles(BubbleLifeTimeEntry entry) - { innerCircle.FadeColour(entry.IsHit ? entry.Colour.Darken(0.2f) : Colour4.Black) - .FadeColour(entry.IsHit ? entry.Colour.Lighten(0.2f) : Colour4.Black, entry.FadeTime * 7); + .FadeColour(entry.IsHit ? entry.Colour.Lighten(0.2f) : Colour4.Black, getAnimationTime()); - innerCircle.FadeTo(0.5f, entry.FadeTime * 7, Easing.InExpo) - .ScaleTo(1.1f, entry.FadeTime * 7, Easing.InSine); + innerCircle.FadeTo(0.5f, getAnimationTime(), Easing.InExpo) + .ScaleTo(2.2f, getAnimationTime(), Easing.InSine); - outerCircle - .FadeColour(entry.IsHit ? entry.Colour : Colour4.Black, entry.FadeTime, Easing.OutSine) - .Delay(entry.FadeTime) - .FadeColour(entry.IsHit ? entry.Colour.Darken(.4f) : Colour4.Black, entry.FadeTime * 5, Easing.OutSine); + // The absolute length of the bubble's animation, can be used in fractions for animations of partial length + double getAnimationTime() => 2000 + 450 / (450 / entry.FadeTime); } } From c3090dea5f7bea51e2662cd82fc292d65c56d7ca Mon Sep 17 00:00:00 2001 From: mk56-spn Date: Sat, 28 Jan 2023 00:30:30 +0100 Subject: [PATCH 010/476] Simplify animations --- osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 28 +++++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs index 6613d84e0e..479741b5b9 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs @@ -214,23 +214,29 @@ namespace osu.Game.Rulesets.Osu.Mods { Size = entry.InitialSize; - this.ScaleTo(entry.MaxSize, getAnimationTime() * 0.8f, Easing.OutSine) + //We want to fade to a darker colour to avoid colours such as white hiding the "ripple" effect. + var colourDarker = entry.Colour.Darken(0.1f); + + this.ScaleTo(entry.MaxSize, getAnimationDuration() * 0.8f, Easing.OutSine) .Then() - .ScaleTo(entry.MaxSize * 1.5f, getAnimationTime() * 0.2f, Easing.OutSine) - .FadeTo(0, getAnimationTime() * 0.2f, Easing.OutExpo); + .ScaleTo(entry.MaxSize * 1.5f, getAnimationDuration() * 0.2f, Easing.OutSine) + .FadeTo(0, getAnimationDuration() * 0.2f, Easing.OutExpo); - colourBox.FadeColour(entry.IsHit ? entry.Colour : Colour4.Black, getAnimationTime() * 0.1f, Easing.OutSine) - .Then() - .FadeColour(entry.IsHit ? entry.Colour.Darken(0.4f) : Colour4.Black, getAnimationTime() * 0.9f, Easing.OutSine); + innerCircle.ScaleTo(2f, getAnimationDuration() * 0.8f, Easing.OutCubic); - innerCircle.FadeColour(entry.IsHit ? entry.Colour.Darken(0.2f) : Colour4.Black) - .FadeColour(entry.IsHit ? entry.Colour.Lighten(0.2f) : Colour4.Black, getAnimationTime()); + if (!entry.IsHit) + { + colourBox.Colour = Colour4.Black; + innerCircle.Colour = Colour4.Black; - innerCircle.FadeTo(0.5f, getAnimationTime(), Easing.InExpo) - .ScaleTo(2.2f, getAnimationTime(), Easing.InSine); + return; + } + + colourBox.FadeColour(colourDarker, getAnimationDuration() * 0.2f, Easing.OutQuint); + innerCircle.FadeColour(colourDarker); // The absolute length of the bubble's animation, can be used in fractions for animations of partial length - double getAnimationTime() => 2000 + 450 / (450 / entry.FadeTime); + double getAnimationDuration() => 1700 + Math.Pow(entry.FadeTime, 1.15f); } } From 66da4c0288fd63d87fa7b4ea3547da39796e74e7 Mon Sep 17 00:00:00 2001 From: mk56-spn Date: Sat, 28 Jan 2023 17:38:24 +0100 Subject: [PATCH 011/476] Add colouration to the sliders to better match the vibrancy of the mod --- osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 27 +++++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs index 479741b5b9..0101427f7a 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs @@ -16,6 +16,7 @@ using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Pooling; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Rulesets.Osu.Skinning.Default; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Scoring; @@ -77,6 +78,12 @@ namespace osu.Game.Rulesets.Osu.Mods private void applyBubbleState(DrawableHitObject drawableObject) { + if (drawableObject is DrawableSlider slider) + { + slider.Body.OnSkinChanged += () => applySliderState(slider); + applySliderState(slider); + } + if (drawableObject is not DrawableOsuHitObject drawableOsuObject || !drawableObject.Judged) return; OsuHitObject hitObject = drawableOsuObject.HitObject; @@ -93,9 +100,9 @@ namespace osu.Game.Rulesets.Osu.Mods addBubbleContainer(hitCircle.Position, drawableOsuObject); break; - case DrawableSpinnerTick: case DrawableSlider: - return; + case DrawableSpinnerTick: + break; default: addBubbleContainer(hitObject.Position, drawableOsuObject); @@ -103,6 +110,9 @@ namespace osu.Game.Rulesets.Osu.Mods } } + private void applySliderState(DrawableSlider slider) => + ((PlaySliderBody)slider.Body.Drawable).BorderColour = slider.AccentColour.Value; + private void addBubbleContainer(Vector2 position, DrawableHitObject hitObject) { bubbleContainer.Add @@ -217,12 +227,12 @@ namespace osu.Game.Rulesets.Osu.Mods //We want to fade to a darker colour to avoid colours such as white hiding the "ripple" effect. var colourDarker = entry.Colour.Darken(0.1f); - this.ScaleTo(entry.MaxSize, getAnimationDuration() * 0.8f, Easing.OutSine) + this.ScaleTo(entry.MaxSize, getAnimationDuration() * 0.8f) .Then() - .ScaleTo(entry.MaxSize * 1.5f, getAnimationDuration() * 0.2f, Easing.OutSine) - .FadeTo(0, getAnimationDuration() * 0.2f, Easing.OutExpo); + .ScaleTo(entry.MaxSize * 1.5f, getAnimationDuration() * 0.2f, Easing.OutQuint) + .FadeTo(0, getAnimationDuration() * 0.2f, Easing.OutQuint); - innerCircle.ScaleTo(2f, getAnimationDuration() * 0.8f, Easing.OutCubic); + innerCircle.ScaleTo(2f, getAnimationDuration() * 0.8f, Easing.OutQuint); if (!entry.IsHit) { @@ -232,11 +242,12 @@ namespace osu.Game.Rulesets.Osu.Mods return; } - colourBox.FadeColour(colourDarker, getAnimationDuration() * 0.2f, Easing.OutQuint); + colourBox.FadeColour(colourDarker, getAnimationDuration() * 0.2f, Easing.OutQuint + ); innerCircle.FadeColour(colourDarker); // The absolute length of the bubble's animation, can be used in fractions for animations of partial length - double getAnimationDuration() => 1700 + Math.Pow(entry.FadeTime, 1.15f); + double getAnimationDuration() => 1700 + Math.Pow(entry.FadeTime, 1.07f); } } From 3bdf83bf44e63000d2a4c23c7467a1aa24b87724 Mon Sep 17 00:00:00 2001 From: mk56-spn Date: Sat, 28 Jan 2023 22:44:57 +0100 Subject: [PATCH 012/476] Redo the drawable structure of bubbledrawable to run and look better --- osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 50 ++++++++++----------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs index 0101427f7a..2c90bfa399 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs @@ -5,6 +5,7 @@ using System; using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Performance; @@ -93,7 +94,7 @@ namespace osu.Game.Rulesets.Osu.Mods //Needs to be done explicitly to avoid being handled by DrawableHitCircle below case DrawableSliderHead: addBubbleContainer(hitObject.Position, drawableOsuObject); - break; + return; //Stack leniency causes placement issues if this isn't handled as such. case DrawableHitCircle hitCircle: @@ -188,7 +189,6 @@ namespace osu.Game.Rulesets.Osu.Mods private partial class BubbleDrawable : CircularContainer { - private readonly Circle innerCircle; private readonly Box colourBox; public BubbleDrawable() @@ -196,55 +196,55 @@ namespace osu.Game.Rulesets.Osu.Mods Anchor = Anchor.Centre; Origin = Anchor.Centre; - Masking = true; MaskingSmoothness = 2; BorderThickness = 0; - BorderColour = Colour4.Transparent; + BorderColour = Colour4.White; + Masking = true; EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Shadow, Radius = 3, Colour = Colour4.Black.Opacity(0.05f) }; - - Children = new Drawable[] - { - colourBox = new Box { RelativeSizeAxes = Axes.Both, }, - innerCircle = new Circle - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.5f), - } - }; + Child = colourBox = new Box { RelativeSizeAxes = Axes.Both, }; } public void Animate(BubbleLifeTimeEntry entry) { Size = entry.InitialSize; + BorderThickness = Width / 3.5f; //We want to fade to a darker colour to avoid colours such as white hiding the "ripple" effect. - var colourDarker = entry.Colour.Darken(0.1f); + ColourInfo colourDarker = entry.Colour.Darken(0.1f); + // Main bubble scaling based on combo this.ScaleTo(entry.MaxSize, getAnimationDuration() * 0.8f) .Then() + // Pop at the end of the bubbles life time .ScaleTo(entry.MaxSize * 1.5f, getAnimationDuration() * 0.2f, Easing.OutQuint) - .FadeTo(0, getAnimationDuration() * 0.2f, Easing.OutQuint); - - innerCircle.ScaleTo(2f, getAnimationDuration() * 0.8f, Easing.OutQuint); + .FadeTo(0, getAnimationDuration() * 0.2f, Easing.OutCirc); if (!entry.IsHit) { - colourBox.Colour = Colour4.Black; - innerCircle.Colour = Colour4.Black; + Colour = Colour4.Black; + BorderColour = Colour4.Black; return; } - colourBox.FadeColour(colourDarker, getAnimationDuration() * 0.2f, Easing.OutQuint - ); - innerCircle.FadeColour(colourDarker); + colourBox.FadeColour(colourDarker); + + this.TransformTo(nameof(BorderColour), colourDarker, getAnimationDuration() * 0.3f, Easing.OutQuint); + + // Ripple effect utilises the border to reduce drawable count + this.TransformTo(nameof(BorderThickness), 2f, getAnimationDuration() * 0.3f, Easing.OutQuint) + + // Avoids transparency overlap issues during the bubble "pop" + .Then().Schedule(() => + { + BorderThickness = 0; + BorderColour = Colour4.Transparent; + }); // The absolute length of the bubble's animation, can be used in fractions for animations of partial length double getAnimationDuration() => 1700 + Math.Pow(entry.FadeTime, 1.07f); From f58534f60c76e4b0f9874eb91be336a49cb62035 Mon Sep 17 00:00:00 2001 From: Terochi Date: Sun, 5 Feb 2023 16:35:11 +0100 Subject: [PATCH 013/476] Extended the length of replay at the end of map --- osu.Game/Screens/Play/Player.cs | 88 +++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 36 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index bf7f38cdd3..fda6bdaa8f 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -613,6 +613,8 @@ namespace osu.Game.Screens.Play // if an exit has been requested, cancel any pending completion (the user has shown intention to exit). resultsDisplayDelegate?.Cancel(); + beginScoreImport(); + // The actual exit is performed if // - the pause / fail dialog was not requested // - the pause / fail dialog was requested but is already displayed (user showing intention to exit). @@ -735,14 +737,9 @@ namespace osu.Game.Screens.Play // is no chance that a user could return to the (already completed) Player instance from a child screen. ValidForResume = false; - // Ensure we are not writing to the replay any more, as we are about to consume and store the score. - DrawableRuleset.SetRecordTarget(null); - if (!Configuration.ShowResults) return; - prepareScoreForDisplayTask ??= Task.Run(prepareAndImportScore); - bool storyboardHasOutro = DimmableStoryboard.ContentDisplayed && !DimmableStoryboard.HasStoryboardEnded.Value; if (storyboardHasOutro) @@ -756,6 +753,56 @@ namespace osu.Game.Screens.Play progressToResults(true); } + /// + /// Queue the results screen for display. + /// + /// + /// A final display will only occur once all work is completed in . This means that even after calling this method, the results screen will never be shown until ScoreProcessor.HasCompleted becomes . + /// + /// Whether a minimum delay () should be added before the screen is displayed. + private void progressToResults(bool withDelay) + { + resultsDisplayDelegate?.Cancel(); + + double delay = withDelay ? RESULTS_DISPLAY_DELAY : 0; + + resultsDisplayDelegate = new ScheduledDelegate(() => + { + if (prepareScoreForDisplayTask == null) + { + beginScoreImport(); + return; + } + + if (!prepareScoreForDisplayTask.IsCompleted) + // If the asynchronous preparation has not completed, keep repeating this delegate. + return; + + resultsDisplayDelegate?.Cancel(); + + if (!this.IsCurrentScreen()) + // This player instance may already be in the process of exiting. + return; + + this.Push(CreateResults(prepareScoreForDisplayTask.GetResultSafely())); + }, Time.Current + delay, 50); + + Scheduler.Add(resultsDisplayDelegate); + } + + private void beginScoreImport() + { + // We do not want to import the score in cases where we don't show results + bool canShowResults = Configuration.ShowResults && ScoreProcessor.HasCompleted.Value && GameplayState.HasPassed; + if (!canShowResults) + return; + + // Ensure we are not writing to the replay any more, as we are about to consume and store the score. + DrawableRuleset.SetRecordTarget(null); + + prepareScoreForDisplayTask ??= Task.Run(prepareAndImportScore); + } + /// /// Asynchronously run score preparation operations (database import, online submission etc.). /// @@ -785,37 +832,6 @@ namespace osu.Game.Screens.Play return scoreCopy.ScoreInfo; } - /// - /// Queue the results screen for display. - /// - /// - /// A final display will only occur once all work is completed in . This means that even after calling this method, the results screen will never be shown until ScoreProcessor.HasCompleted becomes . - /// - /// Whether a minimum delay () should be added before the screen is displayed. - private void progressToResults(bool withDelay) - { - resultsDisplayDelegate?.Cancel(); - - double delay = withDelay ? RESULTS_DISPLAY_DELAY : 0; - - resultsDisplayDelegate = new ScheduledDelegate(() => - { - if (prepareScoreForDisplayTask?.IsCompleted != true) - // If the asynchronous preparation has not completed, keep repeating this delegate. - return; - - resultsDisplayDelegate?.Cancel(); - - if (!this.IsCurrentScreen()) - // This player instance may already be in the process of exiting. - return; - - this.Push(CreateResults(prepareScoreForDisplayTask.GetResultSafely())); - }, Time.Current + delay, 50); - - Scheduler.Add(resultsDisplayDelegate); - } - protected override bool OnScroll(ScrollEvent e) { // During pause, allow global volume adjust regardless of settings. From 45981125860b331d5f5dbaa59966e6d354f2fd4c Mon Sep 17 00:00:00 2001 From: Cootz Date: Sun, 5 Feb 2023 21:46:38 +0300 Subject: [PATCH 014/476] Add OriginalBeatmapHash to ScoreInfo. Update db schema_version, migration --- osu.Game/Beatmaps/BeatmapManager.cs | 8 ++++++++ osu.Game/Database/RealmAccess.cs | 15 +++++++++++++++ osu.Game/Scoring/ScoreInfo.cs | 2 ++ 3 files changed, 25 insertions(+) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index ad56bbbc3a..e972f067ca 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -13,6 +13,7 @@ using System.Threading.Tasks; using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Extensions; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.IO.Stores; using osu.Framework.Platform; using osu.Framework.Testing; @@ -25,6 +26,7 @@ using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.Notifications; using osu.Game.Rulesets; +using osu.Game.Scoring; using osu.Game.Skinning; using osu.Game.Utils; @@ -454,6 +456,12 @@ namespace osu.Game.Beatmaps if (transferCollections) beatmapInfo.TransferCollectionReferences(r, oldMd5Hash); + //Unlinking all scores from this beatmap + r.All().Where(s => s.BeatmapInfoID == beatmapInfo.ID).ForEach(s => s.BeatmapInfo = new BeatmapInfo()); + + //Linking all the previos scores + r.All().Where(s => s.OriginalBeatmapHash == beatmapInfo.Hash).ForEach(s => s.BeatmapInfo = beatmapInfo); + ProcessBeatmap?.Invoke((liveBeatmapSet, false)); }); } diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index 177c671bca..422ceb8af3 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -70,6 +70,7 @@ namespace osu.Game.Database /// 23 2022-08-01 Added LastLocalUpdate to BeatmapInfo. /// 24 2022-08-22 Added MaximumStatistics to ScoreInfo. /// 25 2022-09-18 Remove skins to add with new naming. + /// 26 2023-02-05 Added OriginalBeatmapHash to ScoreInfo. /// private const int schema_version = 25; @@ -865,6 +866,20 @@ namespace osu.Game.Database case 25: // Remove the default skins so they can be added back by SkinManager with updated naming. migration.NewRealm.RemoveRange(migration.NewRealm.All().Where(s => s.Protected)); + break; + case 26: + // Adding origin beatmap hash property to ensure the score corresponds to the version of beatmap it should + // See: https://github.com/ppy/osu/issues/22062 + string ScoreInfoName = getMappedOrOriginalName(typeof(ScoreInfo)); + + var oldScoreInfos = migration.OldRealm.DynamicApi.All(ScoreInfoName); + var newScoreInfos = migration.NewRealm.All(); + + for (int i = 0; i < newScoreInfos.Count(); i++) + { + newScoreInfos.ElementAt(i).OriginalBeatmapHash = oldScoreInfos.ElementAt(i).BeatmapInfo.Hash; + } + break; } } diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 1009474d89..2c029bbe68 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -66,6 +66,8 @@ namespace osu.Game.Scoring [MapTo("MaximumStatistics")] public string MaximumStatisticsJson { get; set; } = string.Empty; + public string OriginalBeatmapHash { get; set; } = string.Empty; + public ScoreInfo(BeatmapInfo? beatmap = null, RulesetInfo? ruleset = null, RealmUser? realmUser = null) { Ruleset = ruleset ?? new RulesetInfo(); From d23e787bc1f341ae41c3fa5a21467d0c9f320973 Mon Sep 17 00:00:00 2001 From: Cootz Date: Sun, 5 Feb 2023 21:55:50 +0300 Subject: [PATCH 015/476] Update `schema_version` --- osu.Game/Database/RealmAccess.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index 422ceb8af3..6f85b3f1be 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -72,7 +72,7 @@ namespace osu.Game.Database /// 25 2022-09-18 Remove skins to add with new naming. /// 26 2023-02-05 Added OriginalBeatmapHash to ScoreInfo. /// - private const int schema_version = 25; + private const int schema_version = 26; /// /// Lock object which is held during sections, blocking realm retrieval during blocking periods. From 4f23e096d76e9ab33140b77472dd4fa6247d9b0c Mon Sep 17 00:00:00 2001 From: Terochi Date: Mon, 6 Feb 2023 07:59:37 +0100 Subject: [PATCH 016/476] Improved readability --- osu.Game/Screens/Play/Player.cs | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index fda6bdaa8f..443108cf34 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -613,6 +613,7 @@ namespace osu.Game.Screens.Play // if an exit has been requested, cancel any pending completion (the user has shown intention to exit). resultsDisplayDelegate?.Cancel(); + // import current score if possible. beginScoreImport(); // The actual exit is performed if @@ -770,7 +771,11 @@ namespace osu.Game.Screens.Play { if (prepareScoreForDisplayTask == null) { - beginScoreImport(); + // Try importing score since the task hasn't been invoked yet. + if (!beginScoreImport()) + // If the task hasn't started, the score will never be imported. + resultsDisplayDelegate?.Cancel(); + return; } @@ -790,17 +795,25 @@ namespace osu.Game.Screens.Play Scheduler.Add(resultsDisplayDelegate); } - private void beginScoreImport() + /// + /// Ends replay recording and runs only when results can be shown + /// + /// + /// Whether the task has been invoked + /// + private bool beginScoreImport() { - // We do not want to import the score in cases where we don't show results - bool canShowResults = Configuration.ShowResults && ScoreProcessor.HasCompleted.Value && GameplayState.HasPassed; - if (!canShowResults) - return; - // Ensure we are not writing to the replay any more, as we are about to consume and store the score. DrawableRuleset.SetRecordTarget(null); + // We do not want to import the score in cases where we don't show results + bool canShowResults = Configuration.ShowResults && ScoreProcessor.HasCompleted.Value && GameplayState.HasPassed; + if (!canShowResults) + return false; + prepareScoreForDisplayTask ??= Task.Run(prepareAndImportScore); + + return true; } /// From abcb564a74efeb3bb9e43ee1686f402297d416b5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 6 Feb 2023 17:32:17 +0900 Subject: [PATCH 017/476] Code quality pass of `OsuModBubbles` --- osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 24 ++++++--------------- 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs index 2c90bfa399..3606434042 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs @@ -29,15 +29,15 @@ namespace osu.Game.Rulesets.Osu.Mods { public override string Name => "Bubbles"; - public override string Acronym => "BB"; + public override string Acronym => "BU"; - public override LocalisableString Description => "Dont let their popping distract you!"; + public override LocalisableString Description => "Don't let their popping distract you!"; public override double ScoreMultiplier => 1; public override ModType Type => ModType.Fun; - // Compatibility with these seems potentially feasible in the future, blocked for now because they dont work as one would expect + // Compatibility with these seems potentially feasible in the future, blocked for now because they don't work as one would expect public override Type[] IncompatibleMods => new[] { typeof(OsuModBarrelRoll), typeof(OsuModMagnetised), typeof(OsuModRepel) }; private PlayfieldAdjustmentContainer adjustmentContainer = null!; @@ -87,26 +87,14 @@ namespace osu.Game.Rulesets.Osu.Mods if (drawableObject is not DrawableOsuHitObject drawableOsuObject || !drawableObject.Judged) return; - OsuHitObject hitObject = drawableOsuObject.HitObject; - switch (drawableOsuObject) { - //Needs to be done explicitly to avoid being handled by DrawableHitCircle below - case DrawableSliderHead: - addBubbleContainer(hitObject.Position, drawableOsuObject); - return; - - //Stack leniency causes placement issues if this isn't handled as such. - case DrawableHitCircle hitCircle: - addBubbleContainer(hitCircle.Position, drawableOsuObject); - break; - case DrawableSlider: case DrawableSpinnerTick: break; default: - addBubbleContainer(hitObject.Position, drawableOsuObject); + addBubbleForObject(drawableOsuObject); break; } } @@ -114,7 +102,7 @@ namespace osu.Game.Rulesets.Osu.Mods private void applySliderState(DrawableSlider slider) => ((PlaySliderBody)slider.Body.Drawable).BorderColour = slider.AccentColour.Value; - private void addBubbleContainer(Vector2 position, DrawableHitObject hitObject) + private void addBubbleForObject(DrawableOsuHitObject hitObject) { bubbleContainer.Add ( @@ -122,7 +110,7 @@ namespace osu.Game.Rulesets.Osu.Mods { LifetimeStart = bubbleContainer.Time.Current, Colour = hitObject.AccentColour.Value, - Position = position, + Position = hitObject.HitObject.Position, InitialSize = new Vector2(bubbleRadius), MaxSize = maxSize, FadeTime = bubbleFade, From 43f7665c9eabe7105bb1a7c1c2ace3784214d28f Mon Sep 17 00:00:00 2001 From: Terochi Date: Mon, 6 Feb 2023 09:49:42 +0100 Subject: [PATCH 018/476] Improved readability again --- osu.Game/Screens/Play/Player.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 443108cf34..2f81b7154a 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -614,7 +614,7 @@ namespace osu.Game.Screens.Play resultsDisplayDelegate?.Cancel(); // import current score if possible. - beginScoreImport(); + attemptScoreImport(); // The actual exit is performed if // - the pause / fail dialog was not requested @@ -772,8 +772,8 @@ namespace osu.Game.Screens.Play if (prepareScoreForDisplayTask == null) { // Try importing score since the task hasn't been invoked yet. - if (!beginScoreImport()) - // If the task hasn't started, the score will never be imported. + if (!attemptScoreImport()) + // If attempt failed, trying again is unnecessary resultsDisplayDelegate?.Cancel(); return; @@ -796,12 +796,12 @@ namespace osu.Game.Screens.Play } /// - /// Ends replay recording and runs only when results can be shown + /// Attempts to run /// /// - /// Whether the task has been invoked + /// Whether the attempt was successful /// - private bool beginScoreImport() + private bool attemptScoreImport() { // Ensure we are not writing to the replay any more, as we are about to consume and store the score. DrawableRuleset.SetRecordTarget(null); From b00848e742d48ef08849d5115690909109c88de9 Mon Sep 17 00:00:00 2001 From: Cootz Date: Mon, 6 Feb 2023 13:58:41 +0300 Subject: [PATCH 019/476] Fix realm error. Apply `OriginalBeatmapHash` on import --- osu.Game/Beatmaps/BeatmapManager.cs | 6 ------ osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs | 1 + 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index e972f067ca..b46859cc59 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -456,12 +456,6 @@ namespace osu.Game.Beatmaps if (transferCollections) beatmapInfo.TransferCollectionReferences(r, oldMd5Hash); - //Unlinking all scores from this beatmap - r.All().Where(s => s.BeatmapInfoID == beatmapInfo.ID).ForEach(s => s.BeatmapInfo = new BeatmapInfo()); - - //Linking all the previos scores - r.All().Where(s => s.OriginalBeatmapHash == beatmapInfo.Hash).ForEach(s => s.BeatmapInfo = beatmapInfo); - ProcessBeatmap?.Invoke((liveBeatmapSet, false)); }); } diff --git a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs index 6f0b0c62f8..4bd068ca0f 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs @@ -123,6 +123,7 @@ namespace osu.Game.Scoring.Legacy // before returning for database import, we must restore the database-sourced BeatmapInfo. // if not, the clone operation in GetPlayableBeatmap will cause a dereference and subsequent database exception. score.ScoreInfo.BeatmapInfo = workingBeatmap.BeatmapInfo; + score.ScoreInfo.OriginalBeatmapHash = workingBeatmap.BeatmapInfo.Hash; return score; } From 2c7386db39d894cfa8ef335e7c58659399ed2e19 Mon Sep 17 00:00:00 2001 From: Cootz Date: Mon, 6 Feb 2023 15:14:14 +0300 Subject: [PATCH 020/476] FIx score appearing on `BeatmapLeaderboard` and `TopLocalRank` --- osu.Game/Screens/Play/Player.cs | 1 + osu.Game/Screens/Select/Carousel/TopLocalRank.cs | 1 + osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs | 1 + 3 files changed, 3 insertions(+) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 0d208e6d9b..7bd020db93 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -248,6 +248,7 @@ namespace osu.Game.Screens.Play // ensure the score is in a consistent state with the current player. Score.ScoreInfo.BeatmapInfo = Beatmap.Value.BeatmapInfo; + Score.ScoreInfo.OriginalBeatmapHash = Beatmap.Value.BeatmapInfo.Hash; Score.ScoreInfo.Ruleset = ruleset.RulesetInfo; Score.ScoreInfo.Mods = gameplayMods; diff --git a/osu.Game/Screens/Select/Carousel/TopLocalRank.cs b/osu.Game/Screens/Select/Carousel/TopLocalRank.cs index f1b773c831..3df72f7d3b 100644 --- a/osu.Game/Screens/Select/Carousel/TopLocalRank.cs +++ b/osu.Game/Screens/Select/Carousel/TopLocalRank.cs @@ -65,6 +65,7 @@ namespace osu.Game.Screens.Select.Carousel r.All() .Filter($"{nameof(ScoreInfo.User)}.{nameof(RealmUser.OnlineID)} == $0" + $" && {nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.ID)} == $1" + + $" && {nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.Hash)} == {nameof(ScoreInfo.OriginalBeatmapHash)}" + $" && {nameof(ScoreInfo.Ruleset)}.{nameof(RulesetInfo.ShortName)} == $2" + $" && {nameof(ScoreInfo.DeletePending)} == false", api.LocalUser.Value.Id, beatmapInfo.ID, ruleset.Value.ShortName), localScoresChanged); diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index 5c720c8491..3e90d2465b 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -191,6 +191,7 @@ namespace osu.Game.Screens.Select.Leaderboards scoreSubscription = realm.RegisterForNotifications(r => r.All().Filter($"{nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.ID)} == $0" + + $" AND {nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.Hash)} == {nameof(ScoreInfo.OriginalBeatmapHash)}" + $" AND {nameof(ScoreInfo.Ruleset)}.{nameof(RulesetInfo.ShortName)} == $1" + $" AND {nameof(ScoreInfo.DeletePending)} == false" , beatmapInfo.ID, ruleset.Value.ShortName), localScoresChanged); From f0d4b9f0ca339c79c74fba39f1d9a97de37f5f6e Mon Sep 17 00:00:00 2001 From: mk56-spn Date: Mon, 6 Feb 2023 17:00:47 +0100 Subject: [PATCH 021/476] Add inline comment for colour border override --- osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs index 3606434042..41430bb323 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs @@ -99,6 +99,7 @@ namespace osu.Game.Rulesets.Osu.Mods } } + // Makes the slider border coloured on all skins private void applySliderState(DrawableSlider slider) => ((PlaySliderBody)slider.Body.Drawable).BorderColour = slider.AccentColour.Value; From cb26601cb4be90a62d898d230a14ac9cb667ecc3 Mon Sep 17 00:00:00 2001 From: Cootz Date: Tue, 7 Feb 2023 02:29:28 +0300 Subject: [PATCH 022/476] Fix test's score generation --- osu.Game.Tests/Resources/TestResources.cs | 1 + .../Visual/SongSelect/TestSceneBeatmapLeaderboard.cs | 10 ++++++++++ .../Visual/UserInterface/TestSceneDeleteLocalScore.cs | 1 + 3 files changed, 12 insertions(+) diff --git a/osu.Game.Tests/Resources/TestResources.cs b/osu.Game.Tests/Resources/TestResources.cs index adf28afc8e..f65d427649 100644 --- a/osu.Game.Tests/Resources/TestResources.cs +++ b/osu.Game.Tests/Resources/TestResources.cs @@ -176,6 +176,7 @@ namespace osu.Game.Tests.Resources CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", }, BeatmapInfo = beatmap, + OriginalBeatmapHash = beatmap.Hash, Ruleset = beatmap.Ruleset, Mods = new Mod[] { new TestModHardRock(), new TestModDoubleTime() }, TotalScore = 2845370, diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs index ef0ad6c25c..7a578230d9 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs @@ -210,6 +210,7 @@ namespace osu.Game.Tests.Visual.SongSelect }, Ruleset = new OsuRuleset().RulesetInfo, BeatmapInfo = beatmapInfo, + OriginalBeatmapHash = beatmapInfo.Hash, User = new APIUser { Id = 6602580, @@ -226,6 +227,7 @@ namespace osu.Game.Tests.Visual.SongSelect Date = DateTime.Now.AddSeconds(-30), Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, BeatmapInfo = beatmapInfo, + OriginalBeatmapHash = beatmapInfo.Hash, Ruleset = new OsuRuleset().RulesetInfo, User = new APIUser { @@ -243,6 +245,7 @@ namespace osu.Game.Tests.Visual.SongSelect Date = DateTime.Now.AddSeconds(-70), Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, BeatmapInfo = beatmapInfo, + OriginalBeatmapHash = beatmapInfo.Hash, Ruleset = new OsuRuleset().RulesetInfo, User = new APIUser @@ -261,6 +264,7 @@ namespace osu.Game.Tests.Visual.SongSelect Date = DateTime.Now.AddMinutes(-40), Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, BeatmapInfo = beatmapInfo, + OriginalBeatmapHash = beatmapInfo.Hash, Ruleset = new OsuRuleset().RulesetInfo, User = new APIUser @@ -279,6 +283,7 @@ namespace osu.Game.Tests.Visual.SongSelect Date = DateTime.Now.AddHours(-2), Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, BeatmapInfo = beatmapInfo, + OriginalBeatmapHash = beatmapInfo.Hash, Ruleset = new OsuRuleset().RulesetInfo, User = new APIUser @@ -297,6 +302,7 @@ namespace osu.Game.Tests.Visual.SongSelect Date = DateTime.Now.AddHours(-25), Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, BeatmapInfo = beatmapInfo, + OriginalBeatmapHash = beatmapInfo.Hash, Ruleset = new OsuRuleset().RulesetInfo, User = new APIUser @@ -315,6 +321,7 @@ namespace osu.Game.Tests.Visual.SongSelect Date = DateTime.Now.AddHours(-50), Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, BeatmapInfo = beatmapInfo, + OriginalBeatmapHash = beatmapInfo.Hash, Ruleset = new OsuRuleset().RulesetInfo, User = new APIUser @@ -333,6 +340,7 @@ namespace osu.Game.Tests.Visual.SongSelect Date = DateTime.Now.AddHours(-72), Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, BeatmapInfo = beatmapInfo, + OriginalBeatmapHash = beatmapInfo.Hash, Ruleset = new OsuRuleset().RulesetInfo, User = new APIUser @@ -351,6 +359,7 @@ namespace osu.Game.Tests.Visual.SongSelect Date = DateTime.Now.AddMonths(-3), Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, BeatmapInfo = beatmapInfo, + OriginalBeatmapHash = beatmapInfo.Hash, Ruleset = new OsuRuleset().RulesetInfo, User = new APIUser @@ -369,6 +378,7 @@ namespace osu.Game.Tests.Visual.SongSelect Date = DateTime.Now.AddYears(-2), Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, BeatmapInfo = beatmapInfo, + OriginalBeatmapHash = beatmapInfo.Hash, Ruleset = new OsuRuleset().RulesetInfo, User = new APIUser diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs index 7635c61867..14193b1ac8 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs @@ -94,6 +94,7 @@ namespace osu.Game.Tests.Visual.UserInterface { OnlineID = i, BeatmapInfo = beatmapInfo, + OriginalBeatmapHash = beatmapInfo.Hash, Accuracy = RNG.NextDouble(), TotalScore = RNG.Next(1, 1000000), MaxCombo = RNG.Next(1, 1000), From 723f13af259da2e022a5c25b61d84b67c8ad75a9 Mon Sep 17 00:00:00 2001 From: Cootz Date: Tue, 7 Feb 2023 02:43:27 +0300 Subject: [PATCH 023/476] Add summary for `OriginalBeatmapHash` --- osu.Game/Scoring/ScoreInfo.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 2c029bbe68..7ad2d9203a 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -66,6 +66,9 @@ namespace osu.Game.Scoring [MapTo("MaximumStatistics")] public string MaximumStatisticsJson { get; set; } = string.Empty; + /// + /// Hash of the beatmap where it scored + /// public string OriginalBeatmapHash { get; set; } = string.Empty; public ScoreInfo(BeatmapInfo? beatmap = null, RulesetInfo? ruleset = null, RealmUser? realmUser = null) From 1470ea0a311bec07ba3ba6525025939e51322a68 Mon Sep 17 00:00:00 2001 From: Cootz Date: Tue, 7 Feb 2023 03:07:53 +0300 Subject: [PATCH 024/476] Remove unnecessary using directives --- osu.Game/Beatmaps/BeatmapManager.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index b46859cc59..ad56bbbc3a 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -13,7 +13,6 @@ using System.Threading.Tasks; using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Extensions; -using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.IO.Stores; using osu.Framework.Platform; using osu.Framework.Testing; @@ -26,7 +25,6 @@ using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.Notifications; using osu.Game.Rulesets; -using osu.Game.Scoring; using osu.Game.Skinning; using osu.Game.Utils; From a1ee3df453f46a271d7ba7450f791340119cc539 Mon Sep 17 00:00:00 2001 From: Cootz Date: Tue, 7 Feb 2023 03:16:25 +0300 Subject: [PATCH 025/476] Improve local variable naming --- osu.Game/Database/RealmAccess.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index 6f85b3f1be..1ef904fbc3 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -867,12 +867,13 @@ namespace osu.Game.Database // Remove the default skins so they can be added back by SkinManager with updated naming. migration.NewRealm.RemoveRange(migration.NewRealm.All().Where(s => s.Protected)); break; + case 26: // Adding origin beatmap hash property to ensure the score corresponds to the version of beatmap it should // See: https://github.com/ppy/osu/issues/22062 - string ScoreInfoName = getMappedOrOriginalName(typeof(ScoreInfo)); + string scoreInfoName = getMappedOrOriginalName(typeof(ScoreInfo)); - var oldScoreInfos = migration.OldRealm.DynamicApi.All(ScoreInfoName); + var oldScoreInfos = migration.OldRealm.DynamicApi.All(scoreInfoName); var newScoreInfos = migration.NewRealm.All(); for (int i = 0; i < newScoreInfos.Count(); i++) From 957c9e7e276037c9c0db101f5e77cbaf3ceda5da Mon Sep 17 00:00:00 2001 From: Cootz <50776304+Cootz@users.noreply.github.com> Date: Tue, 7 Feb 2023 11:23:39 +0300 Subject: [PATCH 026/476] Update osu.Game/Scoring/ScoreInfo.cs Co-authored-by: Dean Herbert --- osu.Game/Scoring/ScoreInfo.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 7ad2d9203a..2ea40df44d 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -67,9 +67,9 @@ namespace osu.Game.Scoring public string MaximumStatisticsJson { get; set; } = string.Empty; /// - /// Hash of the beatmap where it scored + /// The beatmap's at the point in time when the score was set. /// - public string OriginalBeatmapHash { get; set; } = string.Empty; + public string BeatmapHash { get; set; } = string.Empty; public ScoreInfo(BeatmapInfo? beatmap = null, RulesetInfo? ruleset = null, RealmUser? realmUser = null) { From 7e127dafe21a89f5059f59de5cff7904f34e9783 Mon Sep 17 00:00:00 2001 From: PC Date: Tue, 7 Feb 2023 11:52:47 +0300 Subject: [PATCH 027/476] Update reference --- osu.Game.Tests/Resources/TestResources.cs | 2 +- .../SongSelect/TestSceneBeatmapLeaderboard.cs | 20 +++++++++---------- .../TestSceneDeleteLocalScore.cs | 2 +- osu.Game/Database/RealmAccess.cs | 4 ++-- osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs | 2 +- osu.Game/Scoring/ScoreInfo.cs | 2 +- osu.Game/Screens/Play/Player.cs | 2 +- .../Screens/Select/Carousel/TopLocalRank.cs | 2 +- .../Select/Leaderboards/BeatmapLeaderboard.cs | 2 +- 9 files changed, 19 insertions(+), 19 deletions(-) diff --git a/osu.Game.Tests/Resources/TestResources.cs b/osu.Game.Tests/Resources/TestResources.cs index f65d427649..a2d81c0a75 100644 --- a/osu.Game.Tests/Resources/TestResources.cs +++ b/osu.Game.Tests/Resources/TestResources.cs @@ -176,7 +176,7 @@ namespace osu.Game.Tests.Resources CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", }, BeatmapInfo = beatmap, - OriginalBeatmapHash = beatmap.Hash, + BeatmapHash = beatmap.Hash, Ruleset = beatmap.Ruleset, Mods = new Mod[] { new TestModHardRock(), new TestModDoubleTime() }, TotalScore = 2845370, diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs index 7a578230d9..c4bca79480 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs @@ -210,7 +210,7 @@ namespace osu.Game.Tests.Visual.SongSelect }, Ruleset = new OsuRuleset().RulesetInfo, BeatmapInfo = beatmapInfo, - OriginalBeatmapHash = beatmapInfo.Hash, + BeatmapHash = beatmapInfo.Hash, User = new APIUser { Id = 6602580, @@ -227,7 +227,7 @@ namespace osu.Game.Tests.Visual.SongSelect Date = DateTime.Now.AddSeconds(-30), Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, BeatmapInfo = beatmapInfo, - OriginalBeatmapHash = beatmapInfo.Hash, + BeatmapHash = beatmapInfo.Hash, Ruleset = new OsuRuleset().RulesetInfo, User = new APIUser { @@ -245,7 +245,7 @@ namespace osu.Game.Tests.Visual.SongSelect Date = DateTime.Now.AddSeconds(-70), Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, BeatmapInfo = beatmapInfo, - OriginalBeatmapHash = beatmapInfo.Hash, + BeatmapHash = beatmapInfo.Hash, Ruleset = new OsuRuleset().RulesetInfo, User = new APIUser @@ -264,7 +264,7 @@ namespace osu.Game.Tests.Visual.SongSelect Date = DateTime.Now.AddMinutes(-40), Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, BeatmapInfo = beatmapInfo, - OriginalBeatmapHash = beatmapInfo.Hash, + BeatmapHash = beatmapInfo.Hash, Ruleset = new OsuRuleset().RulesetInfo, User = new APIUser @@ -283,7 +283,7 @@ namespace osu.Game.Tests.Visual.SongSelect Date = DateTime.Now.AddHours(-2), Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, BeatmapInfo = beatmapInfo, - OriginalBeatmapHash = beatmapInfo.Hash, + BeatmapHash = beatmapInfo.Hash, Ruleset = new OsuRuleset().RulesetInfo, User = new APIUser @@ -302,7 +302,7 @@ namespace osu.Game.Tests.Visual.SongSelect Date = DateTime.Now.AddHours(-25), Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, BeatmapInfo = beatmapInfo, - OriginalBeatmapHash = beatmapInfo.Hash, + BeatmapHash = beatmapInfo.Hash, Ruleset = new OsuRuleset().RulesetInfo, User = new APIUser @@ -321,7 +321,7 @@ namespace osu.Game.Tests.Visual.SongSelect Date = DateTime.Now.AddHours(-50), Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, BeatmapInfo = beatmapInfo, - OriginalBeatmapHash = beatmapInfo.Hash, + BeatmapHash = beatmapInfo.Hash, Ruleset = new OsuRuleset().RulesetInfo, User = new APIUser @@ -340,7 +340,7 @@ namespace osu.Game.Tests.Visual.SongSelect Date = DateTime.Now.AddHours(-72), Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, BeatmapInfo = beatmapInfo, - OriginalBeatmapHash = beatmapInfo.Hash, + BeatmapHash = beatmapInfo.Hash, Ruleset = new OsuRuleset().RulesetInfo, User = new APIUser @@ -359,7 +359,7 @@ namespace osu.Game.Tests.Visual.SongSelect Date = DateTime.Now.AddMonths(-3), Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, BeatmapInfo = beatmapInfo, - OriginalBeatmapHash = beatmapInfo.Hash, + BeatmapHash = beatmapInfo.Hash, Ruleset = new OsuRuleset().RulesetInfo, User = new APIUser @@ -378,7 +378,7 @@ namespace osu.Game.Tests.Visual.SongSelect Date = DateTime.Now.AddYears(-2), Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, BeatmapInfo = beatmapInfo, - OriginalBeatmapHash = beatmapInfo.Hash, + BeatmapHash = beatmapInfo.Hash, Ruleset = new OsuRuleset().RulesetInfo, User = new APIUser diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs index 14193b1ac8..529874b71e 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs @@ -94,7 +94,7 @@ namespace osu.Game.Tests.Visual.UserInterface { OnlineID = i, BeatmapInfo = beatmapInfo, - OriginalBeatmapHash = beatmapInfo.Hash, + BeatmapHash = beatmapInfo.Hash, Accuracy = RNG.NextDouble(), TotalScore = RNG.Next(1, 1000000), MaxCombo = RNG.Next(1, 1000), diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index 1ef904fbc3..b151fc6474 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -70,7 +70,7 @@ namespace osu.Game.Database /// 23 2022-08-01 Added LastLocalUpdate to BeatmapInfo. /// 24 2022-08-22 Added MaximumStatistics to ScoreInfo. /// 25 2022-09-18 Remove skins to add with new naming. - /// 26 2023-02-05 Added OriginalBeatmapHash to ScoreInfo. + /// 26 2023-02-05 Added BeatmapHash to ScoreInfo. /// private const int schema_version = 26; @@ -878,7 +878,7 @@ namespace osu.Game.Database for (int i = 0; i < newScoreInfos.Count(); i++) { - newScoreInfos.ElementAt(i).OriginalBeatmapHash = oldScoreInfos.ElementAt(i).BeatmapInfo.Hash; + newScoreInfos.ElementAt(i).BeatmapHash = oldScoreInfos.ElementAt(i).BeatmapInfo.Hash; } break; diff --git a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs index 4bd068ca0f..9b145ad56e 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs @@ -123,7 +123,7 @@ namespace osu.Game.Scoring.Legacy // before returning for database import, we must restore the database-sourced BeatmapInfo. // if not, the clone operation in GetPlayableBeatmap will cause a dereference and subsequent database exception. score.ScoreInfo.BeatmapInfo = workingBeatmap.BeatmapInfo; - score.ScoreInfo.OriginalBeatmapHash = workingBeatmap.BeatmapInfo.Hash; + score.ScoreInfo.BeatmapHash = workingBeatmap.BeatmapInfo.Hash; return score; } diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 2ea40df44d..62adcb9f94 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -67,7 +67,7 @@ namespace osu.Game.Scoring public string MaximumStatisticsJson { get; set; } = string.Empty; /// - /// The beatmap's at the point in time when the score was set. + /// The at the point in time when the score was set. /// public string BeatmapHash { get; set; } = string.Empty; diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 7bd020db93..a3d8d3237c 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -248,7 +248,7 @@ namespace osu.Game.Screens.Play // ensure the score is in a consistent state with the current player. Score.ScoreInfo.BeatmapInfo = Beatmap.Value.BeatmapInfo; - Score.ScoreInfo.OriginalBeatmapHash = Beatmap.Value.BeatmapInfo.Hash; + Score.ScoreInfo.BeatmapHash = Beatmap.Value.BeatmapInfo.Hash; Score.ScoreInfo.Ruleset = ruleset.RulesetInfo; Score.ScoreInfo.Mods = gameplayMods; diff --git a/osu.Game/Screens/Select/Carousel/TopLocalRank.cs b/osu.Game/Screens/Select/Carousel/TopLocalRank.cs index 3df72f7d3b..a57a8b0f27 100644 --- a/osu.Game/Screens/Select/Carousel/TopLocalRank.cs +++ b/osu.Game/Screens/Select/Carousel/TopLocalRank.cs @@ -65,7 +65,7 @@ namespace osu.Game.Screens.Select.Carousel r.All() .Filter($"{nameof(ScoreInfo.User)}.{nameof(RealmUser.OnlineID)} == $0" + $" && {nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.ID)} == $1" - + $" && {nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.Hash)} == {nameof(ScoreInfo.OriginalBeatmapHash)}" + + $" && {nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.Hash)} == {nameof(ScoreInfo.BeatmapHash)}" + $" && {nameof(ScoreInfo.Ruleset)}.{nameof(RulesetInfo.ShortName)} == $2" + $" && {nameof(ScoreInfo.DeletePending)} == false", api.LocalUser.Value.Id, beatmapInfo.ID, ruleset.Value.ShortName), localScoresChanged); diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index 3e90d2465b..2b40b9faf8 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -191,7 +191,7 @@ namespace osu.Game.Screens.Select.Leaderboards scoreSubscription = realm.RegisterForNotifications(r => r.All().Filter($"{nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.ID)} == $0" - + $" AND {nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.Hash)} == {nameof(ScoreInfo.OriginalBeatmapHash)}" + + $" AND {nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.Hash)} == {nameof(ScoreInfo.BeatmapHash)}" + $" AND {nameof(ScoreInfo.Ruleset)}.{nameof(RulesetInfo.ShortName)} == $1" + $" AND {nameof(ScoreInfo.DeletePending)} == false" , beatmapInfo.ID, ruleset.Value.ShortName), localScoresChanged); From 338d96534a2ad7b27d82eeeafb2c778fb67d0b67 Mon Sep 17 00:00:00 2001 From: Cootz Date: Wed, 8 Feb 2023 05:01:54 +0300 Subject: [PATCH 028/476] Add leaderboard test on beatmap update --- .../SongSelect/TestSceneBeatmapLeaderboard.cs | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs index c4bca79480..13a24cd490 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs @@ -96,6 +96,37 @@ namespace osu.Game.Tests.Visual.SongSelect checkCount(0); } + [Test] + public void TestLocalScoresDisplayOnBeatmapEdit() + { + BeatmapInfo beatmapInfo = null!; + + AddStep(@"Set scope", () => leaderboard.Scope = BeatmapLeaderboardScope.Local); + + AddStep(@"Set beatmap", () => + { + beatmapManager.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely(); + beatmapInfo = beatmapManager.GetAllUsableBeatmapSets().First().Beatmaps.First(); + + leaderboard.BeatmapInfo = beatmapInfo; + }); + + clearScores(); + checkCount(0); + + loadMoreScores(() => beatmapInfo); + checkCount(10); + + beatmapEdit(() => beatmapInfo); + checkCount(0); + + loadMoreScores(() => beatmapInfo); + checkCount(10); + + clearScores(); + checkCount(0); + } + [Test] public void TestGlobalScoresDisplay() { @@ -123,6 +154,21 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep(@"None selected", () => leaderboard.SetErrorState(LeaderboardState.NoneSelected)); } + private void beatmapEdit(Func beatmapInfo) + { + AddStep(@"Update beatmap via BeatmapManager", () => + { + BeatmapInfo info = beatmapInfo(); + IBeatmap beatmap = beatmapManager.GetWorkingBeatmap(info).Beatmap; + + beatmap.Difficulty.ApproachRate = 11; + beatmap.Difficulty.DrainRate = 11; + beatmap.Difficulty.OverallDifficulty = 11; + + beatmapManager.Save(info, beatmap); + }); + } + private void showPersonalBestWithNullPosition() { leaderboard.SetScores(leaderboard.Scores, new ScoreInfo From 391af2791b875a9928bba1d92dbcbae2128c0dd0 Mon Sep 17 00:00:00 2001 From: Cootz Date: Wed, 8 Feb 2023 05:23:42 +0300 Subject: [PATCH 029/476] Fix CSharpWarnings::CS1574,CS1584,CS1581,CS1580 --- osu.Game/Scoring/ScoreInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 62adcb9f94..8c912ef32a 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -67,7 +67,7 @@ namespace osu.Game.Scoring public string MaximumStatisticsJson { get; set; } = string.Empty; /// - /// The at the point in time when the score was set. + /// The .Hash at the point in time when the score was set. /// public string BeatmapHash { get; set; } = string.Empty; From 6bf56aff73e07b7cd92241b251ae0ea0d9787b99 Mon Sep 17 00:00:00 2001 From: Cootz Date: Wed, 8 Feb 2023 05:40:20 +0300 Subject: [PATCH 030/476] Add warning for `ScoreInfo` --- osu.Game/Scoring/ScoreInfo.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 8c912ef32a..c57e06bee5 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -22,6 +22,13 @@ using Realms; namespace osu.Game.Scoring { + + /// + /// Store information about the score + /// + /// + /// If you work on inporting/adding score please ensure you provide both BeatmapInfo and BeatmapHash + /// [ExcludeFromDynamicCompile] [MapTo("Score")] public class ScoreInfo : RealmObject, IHasGuidPrimaryKey, IHasRealmFiles, ISoftDelete, IEquatable, IScoreInfo From ab7c9a200bdcc7804927ca68b9c59da58d22d49f Mon Sep 17 00:00:00 2001 From: Cootz Date: Wed, 8 Feb 2023 05:42:06 +0300 Subject: [PATCH 031/476] Fix a typo --- osu.Game/Scoring/ScoreInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index c57e06bee5..85452ede17 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -27,7 +27,7 @@ namespace osu.Game.Scoring /// Store information about the score /// /// - /// If you work on inporting/adding score please ensure you provide both BeatmapInfo and BeatmapHash + /// Warning: If you work on importing/adding score please ensure you provide both BeatmapInfo and BeatmapHash /// [ExcludeFromDynamicCompile] [MapTo("Score")] From 4ba915268c5e0a34824d2a03cb501a12574707a4 Mon Sep 17 00:00:00 2001 From: Cootz Date: Wed, 8 Feb 2023 05:46:47 +0300 Subject: [PATCH 032/476] Change a comment into `RealmAccess` --- osu.Game/Database/RealmAccess.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index b151fc6474..861a74e163 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -869,7 +869,7 @@ namespace osu.Game.Database break; case 26: - // Adding origin beatmap hash property to ensure the score corresponds to the version of beatmap it should + // Add ScoreInfo.BeatmapHash property to ensure the score corresponds to the version of beatmap it should // See: https://github.com/ppy/osu/issues/22062 string scoreInfoName = getMappedOrOriginalName(typeof(ScoreInfo)); From 086b3eb542348048da062fde8234239e8d7d3ef6 Mon Sep 17 00:00:00 2001 From: Cootz Date: Wed, 8 Feb 2023 05:50:52 +0300 Subject: [PATCH 033/476] Fix minor formating issues --- osu.Game/Scoring/ScoreInfo.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 85452ede17..d2ec4ebd5f 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -22,10 +22,9 @@ using Realms; namespace osu.Game.Scoring { - /// /// Store information about the score - /// + /// /// /// Warning: If you work on importing/adding score please ensure you provide both BeatmapInfo and BeatmapHash /// From 5c113ddb030445c5099fdd2334d6b2608f5851ef Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 8 Feb 2023 14:20:58 +0900 Subject: [PATCH 034/476] Reword xmldoc to read better --- osu.Game/Scoring/ScoreInfo.cs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index d2ec4ebd5f..6213c65c75 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -23,11 +23,8 @@ using Realms; namespace osu.Game.Scoring { /// - /// Store information about the score + /// A realm model containing metadata for a single score. /// - /// - /// Warning: If you work on importing/adding score please ensure you provide both BeatmapInfo and BeatmapHash - /// [ExcludeFromDynamicCompile] [MapTo("Score")] public class ScoreInfo : RealmObject, IHasGuidPrimaryKey, IHasRealmFiles, ISoftDelete, IEquatable, IScoreInfo @@ -35,8 +32,19 @@ namespace osu.Game.Scoring [PrimaryKey] public Guid ID { get; set; } + /// + /// The this score was made against. + /// + /// + /// When setting this, make sure to also set to allow relational consistency when a beatmap is potentially changed. + /// public BeatmapInfo BeatmapInfo { get; set; } = null!; + /// + /// The at the point in time when the score was set. + /// + public string BeatmapHash { get; set; } = string.Empty; + public RulesetInfo Ruleset { get; set; } = null!; public IList Files { get; } = null!; @@ -72,11 +80,6 @@ namespace osu.Game.Scoring [MapTo("MaximumStatistics")] public string MaximumStatisticsJson { get; set; } = string.Empty; - /// - /// The .Hash at the point in time when the score was set. - /// - public string BeatmapHash { get; set; } = string.Empty; - public ScoreInfo(BeatmapInfo? beatmap = null, RulesetInfo? ruleset = null, RealmUser? realmUser = null) { Ruleset = ruleset ?? new RulesetInfo(); From c50ea89bc99f3dc142e85ecec3bf2504d3fd5f32 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 8 Feb 2023 14:24:06 +0900 Subject: [PATCH 035/476] Simplify migration to not rely on old/dynamic schema --- osu.Game/Database/RealmAccess.cs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index 861a74e163..831e328439 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -869,17 +869,11 @@ namespace osu.Game.Database break; case 26: - // Add ScoreInfo.BeatmapHash property to ensure the score corresponds to the version of beatmap it should - // See: https://github.com/ppy/osu/issues/22062 - string scoreInfoName = getMappedOrOriginalName(typeof(ScoreInfo)); + // Add ScoreInfo.BeatmapHash property to ensure scores correspond to the correct version of beatmap. + var scores = migration.NewRealm.All(); - var oldScoreInfos = migration.OldRealm.DynamicApi.All(scoreInfoName); - var newScoreInfos = migration.NewRealm.All(); - - for (int i = 0; i < newScoreInfos.Count(); i++) - { - newScoreInfos.ElementAt(i).BeatmapHash = oldScoreInfos.ElementAt(i).BeatmapInfo.Hash; - } + foreach (var score in scores) + score.BeatmapHash = score.BeatmapInfo.Hash; break; } From c7eec371f52dd403a0bfaeea6f645d4a56dd3857 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 8 Feb 2023 15:22:17 +0900 Subject: [PATCH 036/476] Clean up tests somewhat --- .../SongSelect/TestSceneBeatmapLeaderboard.cs | 41 ++++++++----------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs index 13a24cd490..83bb58804d 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs @@ -86,10 +86,10 @@ namespace osu.Game.Tests.Visual.SongSelect clearScores(); checkCount(0); - loadMoreScores(() => beatmapInfo); + importMoreScores(() => beatmapInfo); checkCount(10); - loadMoreScores(() => beatmapInfo); + importMoreScores(() => beatmapInfo); checkCount(20); clearScores(); @@ -114,13 +114,23 @@ namespace osu.Game.Tests.Visual.SongSelect clearScores(); checkCount(0); - loadMoreScores(() => beatmapInfo); + importMoreScores(() => beatmapInfo); checkCount(10); - beatmapEdit(() => beatmapInfo); + AddStep(@"Save beatmap with changes", () => + { + IBeatmap beatmap = beatmapManager.GetWorkingBeatmap(beatmapInfo).Beatmap; + + beatmap.Difficulty.ApproachRate = 11; + beatmap.Difficulty.DrainRate = 11; + beatmap.Difficulty.OverallDifficulty = 11; + + beatmapManager.Save(beatmapInfo, beatmap); + }); + checkCount(0); - loadMoreScores(() => beatmapInfo); + importMoreScores(() => beatmapInfo); checkCount(10); clearScores(); @@ -154,21 +164,6 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep(@"None selected", () => leaderboard.SetErrorState(LeaderboardState.NoneSelected)); } - private void beatmapEdit(Func beatmapInfo) - { - AddStep(@"Update beatmap via BeatmapManager", () => - { - BeatmapInfo info = beatmapInfo(); - IBeatmap beatmap = beatmapManager.GetWorkingBeatmap(info).Beatmap; - - beatmap.Difficulty.ApproachRate = 11; - beatmap.Difficulty.DrainRate = 11; - beatmap.Difficulty.OverallDifficulty = 11; - - beatmapManager.Save(info, beatmap); - }); - } - private void showPersonalBestWithNullPosition() { leaderboard.SetScores(leaderboard.Scores, new ScoreInfo @@ -208,9 +203,9 @@ namespace osu.Game.Tests.Visual.SongSelect }); } - private void loadMoreScores(Func beatmapInfo) + private void importMoreScores(Func beatmapInfo) { - AddStep(@"Load new scores via manager", () => + AddStep(@"Import new scores", () => { foreach (var score in generateSampleScores(beatmapInfo())) scoreManager.Import(score); @@ -223,7 +218,7 @@ namespace osu.Game.Tests.Visual.SongSelect } private void checkCount(int expected) => - AddUntilStep("Correct count displayed", () => leaderboard.ChildrenOfType().Count() == expected); + AddUntilStep($"{expected} scores displayed", () => leaderboard.ChildrenOfType().Count(), () => Is.EqualTo(expected)); private static ScoreInfo[] generateSampleScores(BeatmapInfo beatmapInfo) { From d4d985ba0f0378d3c93a71b17e5cfcb793a7b41b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 8 Feb 2023 15:34:46 +0900 Subject: [PATCH 037/476] Improve test to also check that reverting hash restores old scores --- .../SongSelect/TestSceneBeatmapLeaderboard.cs | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs index 83bb58804d..01397563fd 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs @@ -100,10 +100,11 @@ namespace osu.Game.Tests.Visual.SongSelect public void TestLocalScoresDisplayOnBeatmapEdit() { BeatmapInfo beatmapInfo = null!; + string originalHash = string.Empty; AddStep(@"Set scope", () => leaderboard.Scope = BeatmapLeaderboardScope.Local); - AddStep(@"Set beatmap", () => + AddStep(@"Import beatmap", () => { beatmapManager.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely(); beatmapInfo = beatmapManager.GetAllUsableBeatmapSets().First().Beatmaps.First(); @@ -114,23 +115,39 @@ namespace osu.Game.Tests.Visual.SongSelect clearScores(); checkCount(0); + AddStep(@"Perform initial save to guarantee stable hash", () => + { + IBeatmap beatmap = beatmapManager.GetWorkingBeatmap(beatmapInfo).Beatmap; + beatmapManager.Save(beatmapInfo, beatmap); + + originalHash = beatmapInfo.Hash; + }); + importMoreScores(() => beatmapInfo); checkCount(10); - AddStep(@"Save beatmap with changes", () => + AddStep(@"Save with changes", () => { IBeatmap beatmap = beatmapManager.GetWorkingBeatmap(beatmapInfo).Beatmap; - - beatmap.Difficulty.ApproachRate = 11; - beatmap.Difficulty.DrainRate = 11; - beatmap.Difficulty.OverallDifficulty = 11; - + beatmap.Difficulty.ApproachRate = 12; beatmapManager.Save(beatmapInfo, beatmap); }); + AddAssert("Hash changed", () => beatmapInfo.Hash, () => Is.Not.EqualTo(originalHash)); checkCount(0); importMoreScores(() => beatmapInfo); + importMoreScores(() => beatmapInfo); + checkCount(20); + + AddStep(@"Revert changes", () => + { + IBeatmap beatmap = beatmapManager.GetWorkingBeatmap(beatmapInfo).Beatmap; + beatmap.Difficulty.ApproachRate = 8; + beatmapManager.Save(beatmapInfo, beatmap); + }); + + AddAssert("Hash restored", () => beatmapInfo.Hash, () => Is.EqualTo(originalHash)); checkCount(10); clearScores(); From 38031fdf2327cb50705cd14fa55c4cb1cb058a9d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 8 Feb 2023 15:38:07 +0900 Subject: [PATCH 038/476] Add test coverage of stores stored in database as well --- .../SongSelect/TestSceneBeatmapLeaderboard.cs | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs index 01397563fd..c234cc8a9c 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs @@ -84,16 +84,16 @@ namespace osu.Game.Tests.Visual.SongSelect }); clearScores(); - checkCount(0); + checkDisplayedCount(0); importMoreScores(() => beatmapInfo); - checkCount(10); + checkDisplayedCount(10); importMoreScores(() => beatmapInfo); - checkCount(20); + checkDisplayedCount(20); clearScores(); - checkCount(0); + checkDisplayedCount(0); } [Test] @@ -113,7 +113,7 @@ namespace osu.Game.Tests.Visual.SongSelect }); clearScores(); - checkCount(0); + checkDisplayedCount(0); AddStep(@"Perform initial save to guarantee stable hash", () => { @@ -124,7 +124,9 @@ namespace osu.Game.Tests.Visual.SongSelect }); importMoreScores(() => beatmapInfo); - checkCount(10); + + checkDisplayedCount(10); + checkStoredCount(10); AddStep(@"Save with changes", () => { @@ -134,11 +136,13 @@ namespace osu.Game.Tests.Visual.SongSelect }); AddAssert("Hash changed", () => beatmapInfo.Hash, () => Is.Not.EqualTo(originalHash)); - checkCount(0); + checkDisplayedCount(0); + checkStoredCount(10); importMoreScores(() => beatmapInfo); importMoreScores(() => beatmapInfo); - checkCount(20); + checkDisplayedCount(20); + checkStoredCount(30); AddStep(@"Revert changes", () => { @@ -148,10 +152,12 @@ namespace osu.Game.Tests.Visual.SongSelect }); AddAssert("Hash restored", () => beatmapInfo.Hash, () => Is.EqualTo(originalHash)); - checkCount(10); + checkDisplayedCount(10); + checkStoredCount(30); clearScores(); - checkCount(0); + checkDisplayedCount(0); + checkStoredCount(0); } [Test] @@ -234,9 +240,12 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("Clear all scores", () => scoreManager.Delete()); } - private void checkCount(int expected) => + private void checkDisplayedCount(int expected) => AddUntilStep($"{expected} scores displayed", () => leaderboard.ChildrenOfType().Count(), () => Is.EqualTo(expected)); + private void checkStoredCount(int expected) => + AddUntilStep($"Total scores stored is {expected}", () => Realm.Run(r => r.All().Count(s => !s.DeletePending)), () => Is.EqualTo(expected)); + private static ScoreInfo[] generateSampleScores(BeatmapInfo beatmapInfo) { return new[] From 4fdba880b1efc2dcb49a2f3c64585f2e896a64ba Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 8 Feb 2023 15:39:18 +0900 Subject: [PATCH 039/476] Fix xmldoc reference fail at CI --- osu.Game/Scoring/ScoreInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 6213c65c75..02c7acf350 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -41,7 +41,7 @@ namespace osu.Game.Scoring public BeatmapInfo BeatmapInfo { get; set; } = null!; /// - /// The at the point in time when the score was set. + /// The at the point in time when the score was set. /// public string BeatmapHash { get; set; } = string.Empty; From 5e0c4aa904f28435bb2ffad3967f2f4fe2b08802 Mon Sep 17 00:00:00 2001 From: mk56-spn Date: Wed, 8 Feb 2023 11:12:14 +0100 Subject: [PATCH 040/476] Refactor pooling for bubbles, tweak the animations a tad, add some clarifying comments --- osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 231 ++++++++++---------- 1 file changed, 114 insertions(+), 117 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs index 41430bb323..8cf9c619d7 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs @@ -8,13 +8,11 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; -using osu.Framework.Graphics.Performance; using osu.Framework.Graphics.Pooling; using osu.Framework.Graphics.Shapes; using osu.Framework.Localisation; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Objects.Pooling; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Skinning.Default; @@ -41,7 +39,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override Type[] IncompatibleMods => new[] { typeof(OsuModBarrelRoll), typeof(OsuModMagnetised), typeof(OsuModRepel) }; private PlayfieldAdjustmentContainer adjustmentContainer = null!; - private BubbleContainer bubbleContainer = null!; + private Container bubbleContainer = null!; private readonly Bindable currentCombo = new BindableInt(); @@ -49,6 +47,10 @@ namespace osu.Game.Rulesets.Osu.Mods private float bubbleRadius; private double bubbleFade; + private readonly DrawablePool bubblePool = new DrawablePool(100); + + private DrawableOsuHitObject lastJudgedHitobject = null!; + public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank; public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) @@ -56,6 +58,63 @@ namespace osu.Game.Rulesets.Osu.Mods currentCombo.BindTo(scoreProcessor.Combo); currentCombo.BindValueChanged(combo => maxSize = Math.Min(1.75f, (float)(1.25 + 0.005 * combo.NewValue)), true); + + scoreProcessor.NewJudgement += result => + { + if (result.HitObject is not OsuHitObject osuHitObject) return; + + DrawableOsuHitObject drawableOsuHitObject = lastJudgedHitobject; + + switch (result.HitObject) + { + case Slider: + case SpinnerTick: + break; + + default: + addBubble(); + break; + } + + void addBubble() + { + BubbleDrawable bubble = bubblePool.Get(); + bubble.Info = new BubbleInfo + { + InitialSize = new Vector2(bubbleRadius), + MaxSize = maxSize, + Position = getPosition(), + FadeTime = bubbleFade, + Colour = drawableOsuHitObject.AccentColour.Value, + IsHit = drawableOsuHitObject.IsHit, + }; + bubbleContainer.Add(bubble); + } + + Vector2 getPosition() + { + switch (drawableOsuHitObject) + { + // SliderHeads are derived from HitCircles, + // so we must handle them before to avoid them using the wrong positioning logic + case DrawableSliderHead: + return osuHitObject.Position; + + // Using hitobject position will cause issues with HitCircle placement due to stack leniency. + case DrawableHitCircle: + return drawableOsuHitObject.Position; + + default: + return osuHitObject.Position; + } + } + }; + + scoreProcessor.JudgementReverted += _ => + { + bubbleContainer.LastOrDefault()?.FinishTransforms(); + bubbleContainer.LastOrDefault()?.Expire(); + }; } public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) @@ -69,178 +128,116 @@ namespace osu.Game.Rulesets.Osu.Mods adjustmentContainer = drawableRuleset.CreatePlayfieldAdjustmentContainer(); - adjustmentContainer.Add(bubbleContainer = new BubbleContainer()); + adjustmentContainer.Add(bubbleContainer = new Container + { + RelativeSizeAxes = Axes.Both, + }); drawableRuleset.KeyBindingInputManager.Add(adjustmentContainer); } protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) => applyBubbleState(hitObject); - protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) => applyBubbleState(hitObject); private void applyBubbleState(DrawableHitObject drawableObject) { + DrawableOsuHitObject osuHitObject = (DrawableOsuHitObject)drawableObject; + if (drawableObject is DrawableSlider slider) { slider.Body.OnSkinChanged += () => applySliderState(slider); applySliderState(slider); } - if (drawableObject is not DrawableOsuHitObject drawableOsuObject || !drawableObject.Judged) return; + if (osuHitObject == lastJudgedHitobject || !osuHitObject.Judged) return; - switch (drawableOsuObject) + switch (osuHitObject) { case DrawableSlider: case DrawableSpinnerTick: break; default: - addBubbleForObject(drawableOsuObject); + lastJudgedHitobject = osuHitObject; break; } } - // Makes the slider border coloured on all skins + // Makes the slider border coloured on all skins (for aesthetics) private void applySliderState(DrawableSlider slider) => ((PlaySliderBody)slider.Body.Drawable).BorderColour = slider.AccentColour.Value; - private void addBubbleForObject(DrawableOsuHitObject hitObject) - { - bubbleContainer.Add - ( - new BubbleLifeTimeEntry - { - LifetimeStart = bubbleContainer.Time.Current, - Colour = hitObject.AccentColour.Value, - Position = hitObject.HitObject.Position, - InitialSize = new Vector2(bubbleRadius), - MaxSize = maxSize, - FadeTime = bubbleFade, - IsHit = hitObject.IsHit - } - ); - } - #region Pooled Bubble drawable - // LifetimeEntry flow is necessary to allow for correct rewind behaviour, can probably be made generic later if more mods are made requiring it - // Todo: find solution to bubbles rewinding in "groups" - private sealed partial class BubbleContainer : PooledDrawableWithLifetimeContainer - { - protected override bool RemoveRewoundEntry => true; - - private readonly DrawablePool pool; - - public BubbleContainer() - { - RelativeSizeAxes = Axes.Both; - AddInternal(pool = new DrawablePool(10, 1000)); - } - - protected override BubbleObject GetDrawable(BubbleLifeTimeEntry entry) => pool.Get(d => d.Apply(entry)); - } - - private sealed partial class BubbleObject : PoolableDrawableWithLifetime - { - private readonly BubbleDrawable bubbleDrawable; - - public BubbleObject() - { - InternalChild = bubbleDrawable = new BubbleDrawable(); - } - - protected override void OnApply(BubbleLifeTimeEntry entry) - { - base.OnApply(entry); - if (IsLoaded) - apply(entry); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - apply(Entry); - } - - private void apply(BubbleLifeTimeEntry? entry) - { - if (entry == null) return; - - ApplyTransformsAt(float.MinValue, true); - ClearTransforms(true); - - Position = entry.Position; - - bubbleDrawable.Animate(entry); - - LifetimeEnd = bubbleDrawable.LatestTransformEndTime; - } - } - - private partial class BubbleDrawable : CircularContainer + private partial class BubbleDrawable : PoolableDrawable { private readonly Box colourBox; + private readonly CircularContainer content; + + public BubbleInfo Info { get; set; } public BubbleDrawable() { - Anchor = Anchor.Centre; Origin = Anchor.Centre; - - MaskingSmoothness = 2; - BorderThickness = 0; - BorderColour = Colour4.White; - Masking = true; - EdgeEffect = new EdgeEffectParameters + InternalChild = content = new CircularContainer { - Type = EdgeEffectType.Shadow, - Radius = 3, - Colour = Colour4.Black.Opacity(0.05f) + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + MaskingSmoothness = 2, + BorderThickness = 0, + BorderColour = Colour4.White, + Masking = true, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Radius = 3, + Colour = Colour4.Black.Opacity(0.05f), + }, + Child = colourBox = new Box { RelativeSizeAxes = Axes.Both, } }; - Child = colourBox = new Box { RelativeSizeAxes = Axes.Both, }; } - public void Animate(BubbleLifeTimeEntry entry) + protected override void PrepareForUse() { - Size = entry.InitialSize; - BorderThickness = Width / 3.5f; + Alpha = 1; + Colour = Colour4.White; + Scale = new Vector2(1); + Position = Info.Position; + Size = Info.InitialSize; + content.BorderThickness = Info.InitialSize.X / 3.5f; + content.BorderColour = Colour4.White; //We want to fade to a darker colour to avoid colours such as white hiding the "ripple" effect. - ColourInfo colourDarker = entry.Colour.Darken(0.1f); + ColourInfo colourDarker = Info.Colour.Darken(0.1f); // Main bubble scaling based on combo - this.ScaleTo(entry.MaxSize, getAnimationDuration() * 0.8f) + this.ScaleTo(Info.MaxSize, getAnimationDuration() * 0.8f) .Then() // Pop at the end of the bubbles life time - .ScaleTo(entry.MaxSize * 1.5f, getAnimationDuration() * 0.2f, Easing.OutQuint) - .FadeTo(0, getAnimationDuration() * 0.2f, Easing.OutCirc); + .ScaleTo(Info.MaxSize * 1.5f, getAnimationDuration() * 0.2f, Easing.OutQuint) + .FadeOutFromOne(getAnimationDuration() * 0.2f, Easing.OutCirc).Expire(); - if (!entry.IsHit) + if (Info.IsHit) { - Colour = Colour4.Black; - BorderColour = Colour4.Black; + colourBox.FadeColour(colourDarker); + + content.TransformTo(nameof(BorderColour), colourDarker, getAnimationDuration() * 0.3f, Easing.OutQuint); + // Ripple effect utilises the border to reduce drawable count + content.TransformTo(nameof(BorderThickness), 2f, getAnimationDuration() * 0.3f, Easing.OutQuint) + // Avoids transparency overlap issues during the bubble "pop" + .Then().Schedule(() => content.BorderThickness = 0); return; } - colourBox.FadeColour(colourDarker); - - this.TransformTo(nameof(BorderColour), colourDarker, getAnimationDuration() * 0.3f, Easing.OutQuint); - - // Ripple effect utilises the border to reduce drawable count - this.TransformTo(nameof(BorderThickness), 2f, getAnimationDuration() * 0.3f, Easing.OutQuint) - - // Avoids transparency overlap issues during the bubble "pop" - .Then().Schedule(() => - { - BorderThickness = 0; - BorderColour = Colour4.Transparent; - }); + Colour = Colour4.Black; // The absolute length of the bubble's animation, can be used in fractions for animations of partial length - double getAnimationDuration() => 1700 + Math.Pow(entry.FadeTime, 1.07f); + double getAnimationDuration() => 1700 + Math.Pow(Info.FadeTime, 1.07f); } } - private class BubbleLifeTimeEntry : LifetimeEntry + private struct BubbleInfo { public Vector2 InitialSize { get; set; } From 6ff6e06a69256987d7a409a154c021df9988df30 Mon Sep 17 00:00:00 2001 From: mk56-spn Date: Sun, 12 Feb 2023 11:37:07 +0100 Subject: [PATCH 041/476] Simplify bubble container structure, modify some comments --- osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs index 8cf9c619d7..3e3fce5c27 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs @@ -38,8 +38,7 @@ namespace osu.Game.Rulesets.Osu.Mods // Compatibility with these seems potentially feasible in the future, blocked for now because they don't work as one would expect public override Type[] IncompatibleMods => new[] { typeof(OsuModBarrelRoll), typeof(OsuModMagnetised), typeof(OsuModRepel) }; - private PlayfieldAdjustmentContainer adjustmentContainer = null!; - private Container bubbleContainer = null!; + private PlayfieldAdjustmentContainer bubbleContainer = null!; private readonly Bindable currentCombo = new BindableInt(); @@ -119,20 +118,17 @@ namespace osu.Game.Rulesets.Osu.Mods public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { - // Multiplying by 2 results in an initial size that is too large, hence 1.85 has been chosen - bubbleRadius = (float)(drawableRuleset.Beatmap.HitObjects.OfType().First().Radius * 1.85f); + // Multiplying by 2 results in an initial size that is too large, hence 1.90 has been chosen + // Also avoids the HitObject bleeding around the edges of the bubble drawable at minimum size + bubbleRadius = (float)(drawableRuleset.Beatmap.HitObjects.OfType().First().Radius * 1.90f); bubbleFade = drawableRuleset.Beatmap.HitObjects.OfType().First().TimePreempt * 2; // We want to hide the judgements since they are obscured by the BubbleDrawable (due to layering) drawableRuleset.Playfield.DisplayJudgements.Value = false; - adjustmentContainer = drawableRuleset.CreatePlayfieldAdjustmentContainer(); + bubbleContainer = drawableRuleset.CreatePlayfieldAdjustmentContainer(); - adjustmentContainer.Add(bubbleContainer = new Container - { - RelativeSizeAxes = Axes.Both, - }); - drawableRuleset.KeyBindingInputManager.Add(adjustmentContainer); + drawableRuleset.KeyBindingInputManager.Add(bubbleContainer); } protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) => applyBubbleState(hitObject); From 74a58fb674945a5ec82f20ca2bffff91b6de776c Mon Sep 17 00:00:00 2001 From: tsrk Date: Mon, 13 Feb 2023 01:24:27 +0000 Subject: [PATCH 042/476] refactor: separate things in KeyCounter To implement different different sources of input for KeyCounter, it is now possible to create a Trigger class (to inherit) instead of inheriting KeyCounter. This eases the creation of more input sources (like for tests) while allowing to implement different UI variants. That way, if another variant of the key counter needs to implemented (for whathever reason), this can be done by only inheriting KeyCounter and changing how things are arranged visually. --- osu.Game/Rulesets/UI/RulesetInputManager.cs | 9 +- osu.Game/Screens/Play/DefaultKeyCounter.cs | 105 +++++++++++++ osu.Game/Screens/Play/KeyCounter.cs | 157 +++++++------------- osu.Game/Screens/Play/KeyCounterAction.cs | 10 +- osu.Game/Screens/Play/KeyCounterKeyboard.cs | 11 +- osu.Game/Screens/Play/KeyCounterMouse.cs | 11 +- 6 files changed, 177 insertions(+), 126 deletions(-) create mode 100644 osu.Game/Screens/Play/DefaultKeyCounter.cs diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index a5e442b7de..0fa1f0b332 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -166,7 +166,7 @@ namespace osu.Game.Rulesets.UI .Select(b => b.GetAction()) .Distinct() .OrderBy(action => action) - .Select(action => new KeyCounterAction(action))); + .Select(action => keyCounter.CreateKeyCounter(new KeyCounterAction(action)))); } private partial class ActionReceptor : KeyCounterDisplay.Receptor, IKeyBindingHandler @@ -176,11 +176,14 @@ namespace osu.Game.Rulesets.UI { } - public bool OnPressed(KeyBindingPressEvent e) => Target.Children.OfType>().Any(c => c.OnPressed(e.Action, Clock.Rate >= 0)); + public bool OnPressed(KeyBindingPressEvent e) => Target.Children.Where(c => c.CounterTrigger is KeyCounterAction) + .Select(c => (KeyCounterAction)c.CounterTrigger) + .Any(c => c.OnPressed(e.Action, Clock.Rate >= 0)); public void OnReleased(KeyBindingReleaseEvent e) { - foreach (var c in Target.Children.OfType>()) + foreach (var c + in Target.Children.Where(c => c.CounterTrigger is KeyCounterAction).Select(c => (KeyCounterAction)c.CounterTrigger)) c.OnReleased(e.Action, Clock.Rate >= 0); } } diff --git a/osu.Game/Screens/Play/DefaultKeyCounter.cs b/osu.Game/Screens/Play/DefaultKeyCounter.cs new file mode 100644 index 0000000000..dcb425ae1d --- /dev/null +++ b/osu.Game/Screens/Play/DefaultKeyCounter.cs @@ -0,0 +1,105 @@ +// 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.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Screens.Play +{ + public partial class DefaultKeyCounter : KeyCounter + { + private Sprite buttonSprite = null!; + private Sprite glowSprite = null!; + private Container textLayer = null!; + private SpriteText countSpriteText = null!; + + //further: change default values here and in KeyCounterCollection if needed, instead of passing them in every constructor + public Color4 KeyDownTextColor { get; set; } = Color4.DarkGray; + public Color4 KeyUpTextColor { get; set; } = Color4.White; + public double FadeTime { get; set; } + + public DefaultKeyCounter(Trigger trigger) + : base(trigger) + { + } + + [BackgroundDependencyLoader(true)] + private void load(TextureStore textures) + { + Children = new Drawable[] + { + buttonSprite = new Sprite + { + Texture = textures.Get(@"KeyCounter/key-up"), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + glowSprite = new Sprite + { + Texture = textures.Get(@"KeyCounter/key-glow"), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Alpha = 0 + }, + textLayer = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new OsuSpriteText + { + Text = Name, + Font = OsuFont.Numeric.With(size: 12), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativePositionAxes = Axes.Both, + Position = new Vector2(0, -0.25f), + Colour = KeyUpTextColor + }, + countSpriteText = new OsuSpriteText + { + Text = CountPresses.ToString(@"#,0"), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativePositionAxes = Axes.Both, + Position = new Vector2(0, 0.25f), + Colour = KeyUpTextColor + } + } + } + }; + // Set this manually because an element with Alpha=0 won't take it size to AutoSizeContainer, + // so the size can be changing between buttonSprite and glowSprite. + Height = buttonSprite.DrawHeight; + Width = buttonSprite.DrawWidth; + + IsLit.BindValueChanged(e => updateGlowSprite(e.NewValue), true); + PressesCount.BindValueChanged(e => countSpriteText.Text = e.NewValue.ToString(@"#,0"), true); + } + + private void updateGlowSprite(bool show) + { + if (show) + { + double remainingFadeTime = FadeTime * (1 - glowSprite.Alpha); + glowSprite.FadeIn(remainingFadeTime, Easing.OutQuint); + textLayer.FadeColour(KeyDownTextColor, remainingFadeTime, Easing.OutQuint); + } + else + { + double remainingFadeTime = 8 * FadeTime * glowSprite.Alpha; + glowSprite.FadeOut(remainingFadeTime, Easing.OutQuint); + textLayer.FadeColour(KeyUpTextColor, remainingFadeTime, Easing.OutQuint); + } + } + } +} diff --git a/osu.Game/Screens/Play/KeyCounter.cs b/osu.Game/Screens/Play/KeyCounter.cs index 4405542b3b..a612edbace 100644 --- a/osu.Game/Screens/Play/KeyCounter.cs +++ b/osu.Game/Screens/Play/KeyCounter.cs @@ -1,57 +1,37 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - -using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osuTK; -using osuTK.Graphics; +using osu.Framework.Input.Events; namespace osu.Game.Screens.Play { public abstract partial class KeyCounter : Container { - private Sprite buttonSprite; - private Sprite glowSprite; - private Container textLayer; - private SpriteText countSpriteText; + public readonly Trigger CounterTrigger; - public bool IsCounting { get; set; } = true; - private int countPresses; + protected Bindable IsCountingBindable = new BindableBool(true); + + protected Bindable PressesCount = new BindableInt + { + MinValue = 0 + }; + + public bool IsCounting + { + get => IsCountingBindable.Value; + set => IsCountingBindable.Value = value; + } public int CountPresses { - get => countPresses; - private set - { - if (countPresses != value) - { - countPresses = value; - countSpriteText.Text = value.ToString(@"#,0"); - } - } + get => PressesCount.Value; + private set => PressesCount.Value = value; } - private bool isLit; - - public bool IsLit - { - get => isLit; - protected set - { - if (isLit != value) - { - isLit = value; - updateGlowSprite(value); - } - } - } + protected Bindable IsLit = new BindableBool(); public void Increment() { @@ -69,82 +49,51 @@ namespace osu.Game.Screens.Play CountPresses--; } - //further: change default values here and in KeyCounterCollection if needed, instead of passing them in every constructor - public Color4 KeyDownTextColor { get; set; } = Color4.DarkGray; - public Color4 KeyUpTextColor { get; set; } = Color4.White; - public double FadeTime { get; set; } - - protected KeyCounter(string name) + protected override void LoadComplete() { - Name = name; + Add(CounterTrigger); + base.LoadComplete(); } - [BackgroundDependencyLoader(true)] - private void load(TextureStore textures) + protected override bool Handle(UIEvent e) => CounterTrigger.TriggerEvent(e); + + protected KeyCounter(Trigger trigger) { - Children = new Drawable[] - { - buttonSprite = new Sprite - { - Texture = textures.Get(@"KeyCounter/key-up"), - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, - glowSprite = new Sprite - { - Texture = textures.Get(@"KeyCounter/key-glow"), - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Alpha = 0 - }, - textLayer = new Container - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - new OsuSpriteText - { - Text = Name, - Font = OsuFont.Numeric.With(size: 12), - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativePositionAxes = Axes.Both, - Position = new Vector2(0, -0.25f), - Colour = KeyUpTextColor - }, - countSpriteText = new OsuSpriteText - { - Text = CountPresses.ToString(@"#,0"), - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativePositionAxes = Axes.Both, - Position = new Vector2(0, 0.25f), - Colour = KeyUpTextColor - } - } - } - }; - // Set this manually because an element with Alpha=0 won't take it size to AutoSizeContainer, - // so the size can be changing between buttonSprite and glowSprite. - Height = buttonSprite.DrawHeight; - Width = buttonSprite.DrawWidth; + CounterTrigger = trigger; + trigger.Target = this; + Name = trigger.Name; } - private void updateGlowSprite(bool show) + public abstract partial class Trigger : Component { - if (show) + private KeyCounter? target; + + public KeyCounter Target { - double remainingFadeTime = FadeTime * (1 - glowSprite.Alpha); - glowSprite.FadeIn(remainingFadeTime, Easing.OutQuint); - textLayer.FadeColour(KeyDownTextColor, remainingFadeTime, Easing.OutQuint); + set => target = value; } - else + + protected Trigger(string name) { - double remainingFadeTime = 8 * FadeTime * glowSprite.Alpha; - glowSprite.FadeOut(remainingFadeTime, Easing.OutQuint); - textLayer.FadeColour(KeyUpTextColor, remainingFadeTime, Easing.OutQuint); + Name = name; + } + + protected void Lit(bool increment = true) + { + if (target == null) return; + + target.IsLit.Value = true; + if (increment) + target.Increment(); + } + + protected void Unlit(bool preserve = true) + { + if (target == null) return; + + target.IsLit.Value = false; + if (!preserve) + target.Decrement(); } } } diff --git a/osu.Game/Screens/Play/KeyCounterAction.cs b/osu.Game/Screens/Play/KeyCounterAction.cs index 900d9bcd0e..058dbb1480 100644 --- a/osu.Game/Screens/Play/KeyCounterAction.cs +++ b/osu.Game/Screens/Play/KeyCounterAction.cs @@ -7,7 +7,7 @@ using System.Collections.Generic; namespace osu.Game.Screens.Play { - public partial class KeyCounterAction : KeyCounter + public partial class KeyCounterAction : KeyCounter.Trigger where T : struct { public T Action { get; } @@ -23,9 +23,7 @@ namespace osu.Game.Screens.Play if (!EqualityComparer.Default.Equals(action, Action)) return false; - IsLit = true; - if (forwards) - Increment(); + Lit(forwards); return false; } @@ -34,9 +32,7 @@ namespace osu.Game.Screens.Play if (!EqualityComparer.Default.Equals(action, Action)) return; - IsLit = false; - if (!forwards) - Decrement(); + Unlit(forwards); } } } diff --git a/osu.Game/Screens/Play/KeyCounterKeyboard.cs b/osu.Game/Screens/Play/KeyCounterKeyboard.cs index c5c8b7eeae..4306efd360 100644 --- a/osu.Game/Screens/Play/KeyCounterKeyboard.cs +++ b/osu.Game/Screens/Play/KeyCounterKeyboard.cs @@ -8,7 +8,7 @@ using osuTK.Input; namespace osu.Game.Screens.Play { - public partial class KeyCounterKeyboard : KeyCounter + public partial class KeyCounterKeyboard : KeyCounter.Trigger { public Key Key { get; } @@ -21,17 +21,16 @@ namespace osu.Game.Screens.Play protected override bool OnKeyDown(KeyDownEvent e) { if (e.Key == Key) - { - IsLit = true; - Increment(); - } + Lit(); return base.OnKeyDown(e); } protected override void OnKeyUp(KeyUpEvent e) { - if (e.Key == Key) IsLit = false; + if (e.Key == Key) + Unlit(); + base.OnKeyUp(e); } } diff --git a/osu.Game/Screens/Play/KeyCounterMouse.cs b/osu.Game/Screens/Play/KeyCounterMouse.cs index cf9c7c029f..00fca47ba2 100644 --- a/osu.Game/Screens/Play/KeyCounterMouse.cs +++ b/osu.Game/Screens/Play/KeyCounterMouse.cs @@ -9,7 +9,7 @@ using osuTK; namespace osu.Game.Screens.Play { - public partial class KeyCounterMouse : KeyCounter + public partial class KeyCounterMouse : KeyCounter.Trigger { public MouseButton Button { get; } @@ -39,17 +39,16 @@ namespace osu.Game.Screens.Play protected override bool OnMouseDown(MouseDownEvent e) { if (e.Button == Button) - { - IsLit = true; - Increment(); - } + Lit(); return base.OnMouseDown(e); } protected override void OnMouseUp(MouseUpEvent e) { - if (e.Button == Button) IsLit = false; + if (e.Button == Button) + Unlit(); + base.OnMouseUp(e); } } From 11d0e185b8188d0986f4520131a14ba95ab2322f Mon Sep 17 00:00:00 2001 From: tsrk Date: Mon, 13 Feb 2023 01:33:09 +0000 Subject: [PATCH 043/476] refactor: separate impl of KeyCounterDisplay This allows for different layouts of display. Idk, maybe someone would want to mix both variants? (don't do this please). This commit is mostly prep for further changes. --- .../TestSceneOsuTouchInput.cs | 16 ++-- .../Visual/Gameplay/TestSceneHUDOverlay.cs | 2 +- .../Visual/Gameplay/TestSceneKeyCounter.cs | 18 ++-- .../TestSceneSkinEditorMultipleSkins.cs | 2 +- .../Gameplay/TestSceneSkinnableHUDOverlay.cs | 2 +- .../Screens/Play/DefaultKeyCounterDisplay.cs | 91 +++++++++++++++++++ osu.Game/Screens/Play/HUDOverlay.cs | 2 +- osu.Game/Screens/Play/KeyCounterDisplay.cs | 76 ++-------------- 8 files changed, 121 insertions(+), 88 deletions(-) create mode 100644 osu.Game/Screens/Play/DefaultKeyCounterDisplay.cs diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs index 72bcec6045..cd30d8df83 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs @@ -34,9 +34,9 @@ namespace osu.Game.Rulesets.Osu.Tests [Resolved] private OsuConfigManager config { get; set; } = null!; - private TestActionKeyCounter leftKeyCounter = null!; + private DefaultKeyCounter leftKeyCounter = null!; - private TestActionKeyCounter rightKeyCounter = null!; + private DefaultKeyCounter rightKeyCounter = null!; private OsuInputManager osuInputManager = null!; @@ -59,14 +59,14 @@ namespace osu.Game.Rulesets.Osu.Tests Origin = Anchor.Centre, Children = new Drawable[] { - leftKeyCounter = new TestActionKeyCounter(OsuAction.LeftButton) + leftKeyCounter = new DefaultKeyCounter(new TestActionKeyCounter(OsuAction.LeftButton)) { Anchor = Anchor.Centre, Origin = Anchor.CentreRight, Depth = float.MinValue, X = -100, }, - rightKeyCounter = new TestActionKeyCounter(OsuAction.RightButton) + rightKeyCounter = new DefaultKeyCounter(new TestActionKeyCounter(OsuAction.RightButton)) { Anchor = Anchor.Centre, Origin = Anchor.CentreLeft, @@ -579,7 +579,7 @@ namespace osu.Game.Rulesets.Osu.Tests private void checkNotPressed(OsuAction action) => AddAssert($"Not pressing {action}", () => !osuInputManager.PressedActions.Contains(action)); private void checkPressed(OsuAction action) => AddAssert($"Is pressing {action}", () => osuInputManager.PressedActions.Contains(action)); - public partial class TestActionKeyCounter : KeyCounter, IKeyBindingHandler + public partial class TestActionKeyCounter : KeyCounter.Trigger, IKeyBindingHandler { public OsuAction Action { get; } @@ -593,8 +593,7 @@ namespace osu.Game.Rulesets.Osu.Tests { if (e.Action == Action) { - IsLit = true; - Increment(); + Lit(); } return false; @@ -602,7 +601,8 @@ namespace osu.Game.Rulesets.Osu.Tests public void OnReleased(KeyBindingReleaseEvent e) { - if (e.Action == Action) IsLit = false; + if (e.Action == Action) + Unlit(); } } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs index 5e1412d79b..4055ef9d3a 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs @@ -267,7 +267,7 @@ namespace osu.Game.Tests.Visual.Gameplay hudOverlay = new HUDOverlay(null, Array.Empty()); // Add any key just to display the key counter visually. - hudOverlay.KeyCounter.Add(new KeyCounterKeyboard(Key.Space)); + hudOverlay.KeyCounter.Add(hudOverlay.KeyCounter.CreateKeyCounter(new KeyCounterKeyboard(Key.Space))); scoreProcessor.Combo.Value = 1; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs index 890ac21b40..60ebce4f52 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs @@ -17,28 +17,28 @@ namespace osu.Game.Tests.Visual.Gameplay { public TestSceneKeyCounter() { - KeyCounterKeyboard testCounter; + DefaultKeyCounter testCounter; - KeyCounterDisplay kc = new KeyCounterDisplay + KeyCounterDisplay kc = new DefaultKeyCounterDisplay { Origin = Anchor.Centre, Anchor = Anchor.Centre, - Children = new KeyCounter[] + Children = new[] { - testCounter = new KeyCounterKeyboard(Key.X), - new KeyCounterKeyboard(Key.X), - new KeyCounterMouse(MouseButton.Left), - new KeyCounterMouse(MouseButton.Right), + testCounter = new DefaultKeyCounter(new KeyCounterKeyboard(Key.X)), + new DefaultKeyCounter(new KeyCounterKeyboard(Key.X)), + new DefaultKeyCounter(new KeyCounterMouse(MouseButton.Left)), + new DefaultKeyCounter(new KeyCounterMouse(MouseButton.Right)), }, }; AddStep("Add random", () => { Key key = (Key)((int)Key.A + RNG.Next(26)); - kc.Add(new KeyCounterKeyboard(key)); + kc.Add(kc.CreateKeyCounter(new KeyCounterKeyboard(key))); }); - Key testKey = ((KeyCounterKeyboard)kc.Children.First()).Key; + Key testKey = ((KeyCounterKeyboard)kc.Children.First().CounterTrigger).Key; void addPressKeyStep() { diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs index d5b6ac38cb..56cf56efd9 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs @@ -53,7 +53,7 @@ namespace osu.Game.Tests.Visual.Gameplay }; // Add any key just to display the key counter visually. - hudOverlay.KeyCounter.Add(new KeyCounterKeyboard(Key.Space)); + hudOverlay.KeyCounter.Add(hudOverlay.KeyCounter.CreateKeyCounter(new KeyCounterKeyboard(Key.Space))); scoreProcessor.Combo.Value = 1; return new Container diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs index 1f2329af4a..f713bca081 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs @@ -88,7 +88,7 @@ namespace osu.Game.Tests.Visual.Gameplay hudOverlay = new HUDOverlay(null, Array.Empty()); // Add any key just to display the key counter visually. - hudOverlay.KeyCounter.Add(new KeyCounterKeyboard(Key.Space)); + hudOverlay.KeyCounter.Add(hudOverlay.KeyCounter.CreateKeyCounter(new KeyCounterKeyboard(Key.Space))); action?.Invoke(hudOverlay); diff --git a/osu.Game/Screens/Play/DefaultKeyCounterDisplay.cs b/osu.Game/Screens/Play/DefaultKeyCounterDisplay.cs new file mode 100644 index 0000000000..d643070e06 --- /dev/null +++ b/osu.Game/Screens/Play/DefaultKeyCounterDisplay.cs @@ -0,0 +1,91 @@ +// 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 osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osuTK.Graphics; + +namespace osu.Game.Screens.Play +{ + public partial class DefaultKeyCounterDisplay : KeyCounterDisplay + { + private const int duration = 100; + private const double key_fade_time = 80; + + protected override Container Content => KeyFlow; + + public new IReadOnlyList Children + { + get => (IReadOnlyList)base.Children; + set => base.Children = value; + } + + public DefaultKeyCounterDisplay() + { + InternalChild = KeyFlow = new FillFlowContainer + { + Direction = FillDirection.Horizontal, + AutoSizeAxes = Axes.Both, + Alpha = 0, + }; + } + + protected override void Update() + { + base.Update(); + + // Don't use autosize as it will shrink to zero when KeyFlow is hidden. + // In turn this can cause the display to be masked off screen and never become visible again. + Size = KeyFlow.Size; + } + + public override void Add(KeyCounter key) + { + base.Add(key); + if (key is not DefaultKeyCounter defaultKey) + throw new ArgumentException($"{key.GetType()} is not a supported {nameof(KeyCounter)}.", nameof(key)); + + defaultKey.FadeTime = key_fade_time; + defaultKey.KeyDownTextColor = KeyDownTextColor; + defaultKey.KeyUpTextColor = KeyUpTextColor; + } + + protected override void UpdateVisibility() => + // Isolate changing visibility of the key counters from fading this component. + KeyFlow.FadeTo(AlwaysVisible.Value || ConfigVisibility.Value ? 1 : 0, duration); + + private Color4 keyDownTextColor = Color4.DarkGray; + + public Color4 KeyDownTextColor + { + get => keyDownTextColor; + set + { + if (value != keyDownTextColor) + { + keyDownTextColor = value; + foreach (var child in Children) + child.KeyDownTextColor = value; + } + } + } + + private Color4 keyUpTextColor = Color4.White; + + public Color4 KeyUpTextColor + { + get => keyUpTextColor; + set + { + if (value != keyUpTextColor) + { + keyUpTextColor = value; + foreach (var child in Children) + child.KeyUpTextColor = value; + } + } + } + } +} diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 4d1f0b96b6..a09da14132 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -327,7 +327,7 @@ namespace osu.Game.Screens.Play ShowHealth = { BindTarget = ShowHealthBar } }; - protected KeyCounterDisplay CreateKeyCounter() => new KeyCounterDisplay + protected KeyCounterDisplay CreateKeyCounter() => new DefaultKeyCounterDisplay { Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, diff --git a/osu.Game/Screens/Play/KeyCounterDisplay.cs b/osu.Game/Screens/Play/KeyCounterDisplay.cs index bb50d4a539..b06d1adfa0 100644 --- a/osu.Game/Screens/Play/KeyCounterDisplay.cs +++ b/osu.Game/Screens/Play/KeyCounterDisplay.cs @@ -12,18 +12,14 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; using osu.Game.Configuration; using osuTK; -using osuTK.Graphics; namespace osu.Game.Screens.Play { - public partial class KeyCounterDisplay : Container + public abstract partial class KeyCounterDisplay : Container { - private const int duration = 100; - private const double key_fade_time = 80; + protected readonly Bindable ConfigVisibility = new Bindable(); - private readonly Bindable configVisibility = new Bindable(); - - protected readonly FillFlowContainer KeyFlow; + protected FillFlowContainer KeyFlow; protected override Container Content => KeyFlow; @@ -33,48 +29,26 @@ namespace osu.Game.Screens.Play /// public readonly Bindable AlwaysVisible = new Bindable(true); - public KeyCounterDisplay() - { - InternalChild = KeyFlow = new FillFlowContainer - { - Direction = FillDirection.Horizontal, - AutoSizeAxes = Axes.Both, - Alpha = 0, - }; - } - - protected override void Update() - { - base.Update(); - - // Don't use autosize as it will shrink to zero when KeyFlow is hidden. - // In turn this can cause the display to be masked off screen and never become visible again. - Size = KeyFlow.Size; - } - public override void Add(KeyCounter key) { ArgumentNullException.ThrowIfNull(key); base.Add(key); key.IsCounting = IsCounting; - key.FadeTime = key_fade_time; - key.KeyDownTextColor = KeyDownTextColor; - key.KeyUpTextColor = KeyUpTextColor; } [BackgroundDependencyLoader] private void load(OsuConfigManager config) { - config.BindWith(OsuSetting.KeyOverlay, configVisibility); + config.BindWith(OsuSetting.KeyOverlay, ConfigVisibility); } protected override void LoadComplete() { base.LoadComplete(); - AlwaysVisible.BindValueChanged(_ => updateVisibility()); - configVisibility.BindValueChanged(_ => updateVisibility(), true); + AlwaysVisible.BindValueChanged(_ => UpdateVisibility()); + ConfigVisibility.BindValueChanged(_ => UpdateVisibility(), true); } private bool isCounting = true; @@ -92,41 +66,7 @@ namespace osu.Game.Screens.Play } } - private Color4 keyDownTextColor = Color4.DarkGray; - - public Color4 KeyDownTextColor - { - get => keyDownTextColor; - set - { - if (value != keyDownTextColor) - { - keyDownTextColor = value; - foreach (var child in Children) - child.KeyDownTextColor = value; - } - } - } - - private Color4 keyUpTextColor = Color4.White; - - public Color4 KeyUpTextColor - { - get => keyUpTextColor; - set - { - if (value != keyUpTextColor) - { - keyUpTextColor = value; - foreach (var child in Children) - child.KeyUpTextColor = value; - } - } - } - - private void updateVisibility() => - // Isolate changing visibility of the key counters from fading this component. - KeyFlow.FadeTo(AlwaysVisible.Value || configVisibility.Value ? 1 : 0, duration); + protected abstract void UpdateVisibility(); public override bool HandleNonPositionalInput => receptor == null; public override bool HandlePositionalInput => receptor == null; @@ -141,6 +81,8 @@ namespace osu.Game.Screens.Play this.receptor = receptor; } + public virtual KeyCounter CreateKeyCounter(KeyCounter.Trigger trigger) => new DefaultKeyCounter(trigger); + public partial class Receptor : Drawable { protected readonly KeyCounterDisplay Target; From aa2e0028ab3c20cb4e0afe412e12670e3b14f96f Mon Sep 17 00:00:00 2001 From: tsrk Date: Mon, 13 Feb 2023 10:59:10 +0000 Subject: [PATCH 044/476] refactor: hide trigger presence from content --- osu.Game/Screens/Play/KeyCounter.cs | 32 +++++++++++++++++------------ 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/Play/KeyCounter.cs b/osu.Game/Screens/Play/KeyCounter.cs index a612edbace..b111305b22 100644 --- a/osu.Game/Screens/Play/KeyCounter.cs +++ b/osu.Game/Screens/Play/KeyCounter.cs @@ -14,6 +14,10 @@ namespace osu.Game.Screens.Play protected Bindable IsCountingBindable = new BindableBool(true); + private readonly Container content; + + protected override Container Content => content; + protected Bindable PressesCount = new BindableInt { MinValue = 0 @@ -31,6 +35,21 @@ namespace osu.Game.Screens.Play private set => PressesCount.Value = value; } + protected KeyCounter(Trigger trigger) + { + InternalChildren = new Drawable[] + { + content = new Container + { + RelativeSizeAxes = Axes.Both + }, + CounterTrigger = trigger, + }; + + CounterTrigger.Target = this; + Name = trigger.Name; + } + protected Bindable IsLit = new BindableBool(); public void Increment() @@ -49,21 +68,8 @@ namespace osu.Game.Screens.Play CountPresses--; } - protected override void LoadComplete() - { - Add(CounterTrigger); - base.LoadComplete(); - } - protected override bool Handle(UIEvent e) => CounterTrigger.TriggerEvent(e); - protected KeyCounter(Trigger trigger) - { - CounterTrigger = trigger; - trigger.Target = this; - Name = trigger.Name; - } - public abstract partial class Trigger : Component { private KeyCounter? target; From 7aaaf7fca2d598998d7c407a3b259db985484b06 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Feb 2023 16:55:35 +0900 Subject: [PATCH 045/476] Combine and attempt to simplify the score import / preparation process further --- osu.Game/Rulesets/UI/RulesetInputManager.cs | 3 + osu.Game/Screens/Play/Player.cs | 82 ++++++++++----------- 2 files changed, 43 insertions(+), 42 deletions(-) diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index a5e442b7de..7bf0482673 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -39,6 +39,9 @@ namespace osu.Game.Rulesets.UI { set { + if (value == recorder) + return; + if (value != null && recorder != null) throw new InvalidOperationException("Cannot attach more than one recorder"); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 0825f36a0a..bc453d2151 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -276,7 +276,7 @@ namespace osu.Game.Screens.Play }, FailOverlay = new FailOverlay { - SaveReplay = prepareAndImportScore, + SaveReplay = async () => await prepareAndImportScoreAsync(true).ConfigureAwait(false), OnRetry = () => Restart(), OnQuit = () => PerformExit(true), }, @@ -614,7 +614,7 @@ namespace osu.Game.Screens.Play resultsDisplayDelegate?.Cancel(); // import current score if possible. - attemptScoreImport(); + prepareAndImportScoreAsync(); // The actual exit is performed if // - the pause / fail dialog was not requested @@ -772,10 +772,7 @@ namespace osu.Game.Screens.Play if (prepareScoreForDisplayTask == null) { // Try importing score since the task hasn't been invoked yet. - if (!attemptScoreImport()) - // If attempt failed, trying again is unnecessary - resultsDisplayDelegate?.Cancel(); - + prepareAndImportScoreAsync(); return; } @@ -785,6 +782,12 @@ namespace osu.Game.Screens.Play resultsDisplayDelegate?.Cancel(); + if (prepareScoreForDisplayTask.GetResultSafely() == null) + { + // If score import did not occur, we do not want to show the results screen. + return; + } + if (!this.IsCurrentScreen()) // This player instance may already be in the process of exiting. return; @@ -796,53 +799,48 @@ namespace osu.Game.Screens.Play } /// - /// Attempts to run + /// Asynchronously run score preparation operations (database import, online submission etc.). /// - /// - /// Whether the attempt was successful - /// - private bool attemptScoreImport() + /// Whether the score should be imported even if non-passing (or the current configuration doesn't allow for it). + /// The final score. + [ItemCanBeNull] + private Task prepareAndImportScoreAsync(bool forceImport = false) { // Ensure we are not writing to the replay any more, as we are about to consume and store the score. DrawableRuleset.SetRecordTarget(null); + if (prepareScoreForDisplayTask != null) + return prepareScoreForDisplayTask; + // We do not want to import the score in cases where we don't show results bool canShowResults = Configuration.ShowResults && ScoreProcessor.HasCompleted.Value && GameplayState.HasPassed; - if (!canShowResults) - return false; + if (!canShowResults && !forceImport) + return Task.FromResult(null); - prepareScoreForDisplayTask ??= Task.Run(prepareAndImportScore); - - return true; - } - - /// - /// Asynchronously run score preparation operations (database import, online submission etc.). - /// - /// The final score. - private async Task prepareAndImportScore() - { - var scoreCopy = Score.DeepClone(); - - try + return prepareScoreForDisplayTask = Task.Run(async () => { - await PrepareScoreForResultsAsync(scoreCopy).ConfigureAwait(false); - } - catch (Exception ex) - { - Logger.Error(ex, @"Score preparation failed!"); - } + var scoreCopy = Score.DeepClone(); - try - { - await ImportScore(scoreCopy).ConfigureAwait(false); - } - catch (Exception ex) - { - Logger.Error(ex, @"Score import failed!"); - } + try + { + await PrepareScoreForResultsAsync(scoreCopy).ConfigureAwait(false); + } + catch (Exception ex) + { + Logger.Error(ex, @"Score preparation failed!"); + } - return scoreCopy.ScoreInfo; + try + { + await ImportScore(scoreCopy).ConfigureAwait(false); + } + catch (Exception ex) + { + Logger.Error(ex, @"Score import failed!"); + } + + return scoreCopy.ScoreInfo; + }); } protected override bool OnScroll(ScrollEvent e) From d100a4a4915b3c67c48cf790822e93da90dc02be Mon Sep 17 00:00:00 2001 From: mk56-spn Date: Tue, 14 Feb 2023 10:12:37 +0100 Subject: [PATCH 046/476] Make `lastJudgedHitObject` nullable, and fix typo in name. --- osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 23 ++++++++------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs index 3e3fce5c27..40c235911c 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs @@ -2,8 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Diagnostics; using System.Linq; using osu.Framework.Bindables; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; @@ -48,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.Mods private readonly DrawablePool bubblePool = new DrawablePool(100); - private DrawableOsuHitObject lastJudgedHitobject = null!; + private DrawableOsuHitObject? lastJudgedHitObject; public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank; @@ -60,9 +62,11 @@ namespace osu.Game.Rulesets.Osu.Mods scoreProcessor.NewJudgement += result => { - if (result.HitObject is not OsuHitObject osuHitObject) return; + if (result.HitObject is not OsuHitObject osuHitObject || lastJudgedHitObject.IsNull()) return; - DrawableOsuHitObject drawableOsuHitObject = lastJudgedHitobject; + Debug.Assert(result.HitObject == lastJudgedHitObject.HitObject); + + DrawableOsuHitObject drawableOsuHitObject = lastJudgedHitObject; switch (result.HitObject) { @@ -144,18 +148,9 @@ namespace osu.Game.Rulesets.Osu.Mods applySliderState(slider); } - if (osuHitObject == lastJudgedHitobject || !osuHitObject.Judged) return; + if (osuHitObject == lastJudgedHitObject || !osuHitObject.Judged) return; - switch (osuHitObject) - { - case DrawableSlider: - case DrawableSpinnerTick: - break; - - default: - lastJudgedHitobject = osuHitObject; - break; - } + lastJudgedHitObject = osuHitObject; } // Makes the slider border coloured on all skins (for aesthetics) From 2d49b5f9d66150598260451250c11736bdad87bc Mon Sep 17 00:00:00 2001 From: mk56-spn Date: Tue, 14 Feb 2023 14:03:48 +0100 Subject: [PATCH 047/476] Move bubbles to ruleset overlays container --- osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs index 40c235911c..732626b177 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs @@ -132,7 +132,7 @@ namespace osu.Game.Rulesets.Osu.Mods bubbleContainer = drawableRuleset.CreatePlayfieldAdjustmentContainer(); - drawableRuleset.KeyBindingInputManager.Add(bubbleContainer); + drawableRuleset.Overlays.Add(bubbleContainer); } protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) => applyBubbleState(hitObject); From 92c61c73396939363b3c6bd7ffffb083e3c74116 Mon Sep 17 00:00:00 2001 From: mk56-spn Date: Tue, 14 Feb 2023 16:31:34 +0100 Subject: [PATCH 048/476] move logic for bubble invoking to `ApplyToDrawableHitobject()`` method --- osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 85 +++++++++------------ 1 file changed, 34 insertions(+), 51 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs index 732626b177..4a8c11e7ff 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs @@ -2,10 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Diagnostics; using System.Linq; using osu.Framework.Bindables; -using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; @@ -25,7 +23,7 @@ using osuTK; namespace osu.Game.Rulesets.Osu.Mods { - public partial class OsuModBubbles : ModWithVisibilityAdjustment, IApplicableToDrawableRuleset, IApplicableToScoreProcessor + public partial class OsuModBubbles : Mod, IApplicableToDrawableRuleset, IApplicableToDrawableHitObject, IApplicableToScoreProcessor { public override string Name => "Bubbles"; @@ -50,8 +48,6 @@ namespace osu.Game.Rulesets.Osu.Mods private readonly DrawablePool bubblePool = new DrawablePool(100); - private DrawableOsuHitObject? lastJudgedHitObject; - public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank; public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) @@ -60,15 +56,41 @@ namespace osu.Game.Rulesets.Osu.Mods currentCombo.BindValueChanged(combo => maxSize = Math.Min(1.75f, (float)(1.25 + 0.005 * combo.NewValue)), true); - scoreProcessor.NewJudgement += result => + scoreProcessor.JudgementReverted += _ => { - if (result.HitObject is not OsuHitObject osuHitObject || lastJudgedHitObject.IsNull()) return; + bubbleContainer.LastOrDefault()?.ClearTransforms(); + bubbleContainer.LastOrDefault()?.Expire(); + }; + } - Debug.Assert(result.HitObject == lastJudgedHitObject.HitObject); + public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) + { + // Multiplying by 2 results in an initial size that is too large, hence 1.90 has been chosen + // Also avoids the HitObject bleeding around the edges of the bubble drawable at minimum size + bubbleRadius = (float)(drawableRuleset.Beatmap.HitObjects.OfType().First().Radius * 1.90f); + bubbleFade = drawableRuleset.Beatmap.HitObjects.OfType().First().TimePreempt * 2; - DrawableOsuHitObject drawableOsuHitObject = lastJudgedHitObject; + // We want to hide the judgements since they are obscured by the BubbleDrawable (due to layering) + drawableRuleset.Playfield.DisplayJudgements.Value = false; - switch (result.HitObject) + bubbleContainer = drawableRuleset.CreatePlayfieldAdjustmentContainer(); + + drawableRuleset.Overlays.Add(bubbleContainer); + } + + public void ApplyToDrawableHitObject(DrawableHitObject drawableObject) + { + if (drawableObject is DrawableSlider slider) + { + applySliderState(slider); + slider.Body.OnSkinChanged += () => applySliderState(slider); + } + + drawableObject.OnNewResult += (drawable, _) => + { + if (drawable is not DrawableOsuHitObject drawableOsuHitObject) return; + + switch (drawableOsuHitObject.HitObject) { case Slider: case SpinnerTick: @@ -101,56 +123,17 @@ namespace osu.Game.Rulesets.Osu.Mods // SliderHeads are derived from HitCircles, // so we must handle them before to avoid them using the wrong positioning logic case DrawableSliderHead: - return osuHitObject.Position; + return drawableOsuHitObject.HitObject.Position; // Using hitobject position will cause issues with HitCircle placement due to stack leniency. case DrawableHitCircle: return drawableOsuHitObject.Position; default: - return osuHitObject.Position; + return drawableOsuHitObject.HitObject.Position; } } }; - - scoreProcessor.JudgementReverted += _ => - { - bubbleContainer.LastOrDefault()?.FinishTransforms(); - bubbleContainer.LastOrDefault()?.Expire(); - }; - } - - public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) - { - // Multiplying by 2 results in an initial size that is too large, hence 1.90 has been chosen - // Also avoids the HitObject bleeding around the edges of the bubble drawable at minimum size - bubbleRadius = (float)(drawableRuleset.Beatmap.HitObjects.OfType().First().Radius * 1.90f); - bubbleFade = drawableRuleset.Beatmap.HitObjects.OfType().First().TimePreempt * 2; - - // We want to hide the judgements since they are obscured by the BubbleDrawable (due to layering) - drawableRuleset.Playfield.DisplayJudgements.Value = false; - - bubbleContainer = drawableRuleset.CreatePlayfieldAdjustmentContainer(); - - drawableRuleset.Overlays.Add(bubbleContainer); - } - - protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) => applyBubbleState(hitObject); - protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) => applyBubbleState(hitObject); - - private void applyBubbleState(DrawableHitObject drawableObject) - { - DrawableOsuHitObject osuHitObject = (DrawableOsuHitObject)drawableObject; - - if (drawableObject is DrawableSlider slider) - { - slider.Body.OnSkinChanged += () => applySliderState(slider); - applySliderState(slider); - } - - if (osuHitObject == lastJudgedHitObject || !osuHitObject.Judged) return; - - lastJudgedHitObject = osuHitObject; } // Makes the slider border coloured on all skins (for aesthetics) From 5db624159b1230e7ca522e9ab6cdec8194e8a0fa Mon Sep 17 00:00:00 2001 From: mk56-spn Date: Tue, 14 Feb 2023 18:06:43 +0100 Subject: [PATCH 049/476] Change bubble rewind removal to be in `ApplyToDrawableHitObject` method. --- osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs index 4a8c11e7ff..f521dfb1f8 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs @@ -55,12 +55,6 @@ namespace osu.Game.Rulesets.Osu.Mods currentCombo.BindTo(scoreProcessor.Combo); currentCombo.BindValueChanged(combo => maxSize = Math.Min(1.75f, (float)(1.25 + 0.005 * combo.NewValue)), true); - - scoreProcessor.JudgementReverted += _ => - { - bubbleContainer.LastOrDefault()?.ClearTransforms(); - bubbleContainer.LastOrDefault()?.Expire(); - }; } public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) @@ -134,6 +128,16 @@ namespace osu.Game.Rulesets.Osu.Mods } } }; + + drawableObject.OnRevertResult += (drawable, _) => + { + if (drawable.HitObject is SpinnerTick or Slider) return; + + BubbleDrawable? lastBubble = bubbleContainer.OfType().LastOrDefault(); + + lastBubble?.ClearTransforms(); + lastBubble?.Expire(); + }; } // Makes the slider border coloured on all skins (for aesthetics) From 82292d61621e49a158c576879ae56f47a9e52a84 Mon Sep 17 00:00:00 2001 From: mk56-spn Date: Wed, 15 Feb 2023 09:30:12 +0100 Subject: [PATCH 050/476] Make colouring for bubble more intuitive and remove unnecessary alpha assignment --- osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs index f521dfb1f8..2a4208065d 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs @@ -177,8 +177,7 @@ namespace osu.Game.Rulesets.Osu.Mods protected override void PrepareForUse() { - Alpha = 1; - Colour = Colour4.White; + Colour = Info.IsHit ? Colour4.White : Colour4.Black; Scale = new Vector2(1); Position = Info.Position; Size = Info.InitialSize; @@ -204,12 +203,8 @@ namespace osu.Game.Rulesets.Osu.Mods content.TransformTo(nameof(BorderThickness), 2f, getAnimationDuration() * 0.3f, Easing.OutQuint) // Avoids transparency overlap issues during the bubble "pop" .Then().Schedule(() => content.BorderThickness = 0); - - return; } - Colour = Colour4.Black; - // The absolute length of the bubble's animation, can be used in fractions for animations of partial length double getAnimationDuration() => 1700 + Math.Pow(Info.FadeTime, 1.07f); } From e9a7d90273c57dbd7dfb30ff32b9ca21dc9b6d39 Mon Sep 17 00:00:00 2001 From: mk56-spn Date: Wed, 15 Feb 2023 09:33:18 +0100 Subject: [PATCH 051/476] make transform duration for bubble a method instead of a variable --- osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs index 2a4208065d..a88bf6b813 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs @@ -187,26 +187,26 @@ namespace osu.Game.Rulesets.Osu.Mods //We want to fade to a darker colour to avoid colours such as white hiding the "ripple" effect. ColourInfo colourDarker = Info.Colour.Darken(0.1f); + // The absolute length of the bubble's animation, can be used in fractions for animations of partial length + double getAnimationDuration = 1700 + Math.Pow(Info.FadeTime, 1.07f); + // Main bubble scaling based on combo - this.ScaleTo(Info.MaxSize, getAnimationDuration() * 0.8f) + this.ScaleTo(Info.MaxSize, getAnimationDuration * 0.8f) .Then() // Pop at the end of the bubbles life time - .ScaleTo(Info.MaxSize * 1.5f, getAnimationDuration() * 0.2f, Easing.OutQuint) - .FadeOutFromOne(getAnimationDuration() * 0.2f, Easing.OutCirc).Expire(); + .ScaleTo(Info.MaxSize * 1.5f, getAnimationDuration * 0.2f, Easing.OutQuint) + .FadeOutFromOne(getAnimationDuration * 0.2f, Easing.OutCirc).Expire(); if (Info.IsHit) { colourBox.FadeColour(colourDarker); - content.TransformTo(nameof(BorderColour), colourDarker, getAnimationDuration() * 0.3f, Easing.OutQuint); + content.TransformTo(nameof(BorderColour), colourDarker, getAnimationDuration * 0.3f, Easing.OutQuint); // Ripple effect utilises the border to reduce drawable count - content.TransformTo(nameof(BorderThickness), 2f, getAnimationDuration() * 0.3f, Easing.OutQuint) + content.TransformTo(nameof(BorderThickness), 2f, getAnimationDuration * 0.3f, Easing.OutQuint) // Avoids transparency overlap issues during the bubble "pop" .Then().Schedule(() => content.BorderThickness = 0); } - - // The absolute length of the bubble's animation, can be used in fractions for animations of partial length - double getAnimationDuration() => 1700 + Math.Pow(Info.FadeTime, 1.07f); } } From 1d1c794ccfde23743574d8579da648cf42f4e4d4 Mon Sep 17 00:00:00 2001 From: mk56-spn Date: Wed, 15 Feb 2023 09:37:47 +0100 Subject: [PATCH 052/476] Invert pointless nested `if` statement --- osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs index a88bf6b813..d75c82dc85 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs @@ -197,16 +197,15 @@ namespace osu.Game.Rulesets.Osu.Mods .ScaleTo(Info.MaxSize * 1.5f, getAnimationDuration * 0.2f, Easing.OutQuint) .FadeOutFromOne(getAnimationDuration * 0.2f, Easing.OutCirc).Expire(); - if (Info.IsHit) - { - colourBox.FadeColour(colourDarker); + if (!Info.IsHit) return; - content.TransformTo(nameof(BorderColour), colourDarker, getAnimationDuration * 0.3f, Easing.OutQuint); - // Ripple effect utilises the border to reduce drawable count - content.TransformTo(nameof(BorderThickness), 2f, getAnimationDuration * 0.3f, Easing.OutQuint) - // Avoids transparency overlap issues during the bubble "pop" - .Then().Schedule(() => content.BorderThickness = 0); - } + colourBox.FadeColour(colourDarker); + + content.TransformTo(nameof(BorderColour), colourDarker, getAnimationDuration * 0.3f, Easing.OutQuint); + // Ripple effect utilises the border to reduce drawable count + content.TransformTo(nameof(BorderThickness), 2f, getAnimationDuration * 0.3f, Easing.OutQuint) + // Avoids transparency overlap issues during the bubble "pop" + .Then().Schedule(() => content.BorderThickness = 0); } } From 297963b461cfdfd6083784ab0c4561c43c1d4a93 Mon Sep 17 00:00:00 2001 From: mk56-spn Date: Wed, 15 Feb 2023 10:00:46 +0100 Subject: [PATCH 053/476] Remove BubbleInfo struct and consume `DrawableOsuHitObject`s directly --- osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 102 +++++++++----------- 1 file changed, 46 insertions(+), 56 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs index d75c82dc85..981932c580 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs @@ -2,8 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Diagnostics; using System.Linq; using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; @@ -98,35 +101,14 @@ namespace osu.Game.Rulesets.Osu.Mods void addBubble() { BubbleDrawable bubble = bubblePool.Get(); - bubble.Info = new BubbleInfo - { - InitialSize = new Vector2(bubbleRadius), - MaxSize = maxSize, - Position = getPosition(), - FadeTime = bubbleFade, - Colour = drawableOsuHitObject.AccentColour.Value, - IsHit = drawableOsuHitObject.IsHit, - }; + + bubble.DrawableOsuHitObject = drawableOsuHitObject; + bubble.InitialSize = new Vector2(bubbleRadius); + bubble.FadeTime = bubbleFade; + bubble.MaxSize = maxSize; + bubbleContainer.Add(bubble); } - - Vector2 getPosition() - { - switch (drawableOsuHitObject) - { - // SliderHeads are derived from HitCircles, - // so we must handle them before to avoid them using the wrong positioning logic - case DrawableSliderHead: - return drawableOsuHitObject.HitObject.Position; - - // Using hitobject position will cause issues with HitCircle placement due to stack leniency. - case DrawableHitCircle: - return drawableOsuHitObject.Position; - - default: - return drawableOsuHitObject.HitObject.Position; - } - } }; drawableObject.OnRevertResult += (drawable, _) => @@ -148,11 +130,15 @@ namespace osu.Game.Rulesets.Osu.Mods private partial class BubbleDrawable : PoolableDrawable { + public DrawableOsuHitObject? DrawableOsuHitObject { get; set; } + + public Vector2 InitialSize { get; set; } + public double FadeTime { get; set; } + public float MaxSize { get; set; } + private readonly Box colourBox; private readonly CircularContainer content; - public BubbleInfo Info { get; set; } - public BubbleDrawable() { Origin = Anchor.Centre; @@ -177,27 +163,30 @@ namespace osu.Game.Rulesets.Osu.Mods protected override void PrepareForUse() { - Colour = Info.IsHit ? Colour4.White : Colour4.Black; + Debug.Assert(DrawableOsuHitObject.IsNotNull()); + + Colour = DrawableOsuHitObject.IsHit ? Colour4.White : Colour4.Black; + Alpha = 1; Scale = new Vector2(1); - Position = Info.Position; - Size = Info.InitialSize; - content.BorderThickness = Info.InitialSize.X / 3.5f; + Position = getPosition(); + Size = InitialSize; + content.BorderThickness = InitialSize.X / 3.5f; content.BorderColour = Colour4.White; //We want to fade to a darker colour to avoid colours such as white hiding the "ripple" effect. - ColourInfo colourDarker = Info.Colour.Darken(0.1f); + ColourInfo colourDarker = DrawableOsuHitObject.AccentColour.Value.Darken(0.1f); // The absolute length of the bubble's animation, can be used in fractions for animations of partial length - double getAnimationDuration = 1700 + Math.Pow(Info.FadeTime, 1.07f); + double getAnimationDuration = 1700 + Math.Pow(FadeTime, 1.07f); // Main bubble scaling based on combo - this.ScaleTo(Info.MaxSize, getAnimationDuration * 0.8f) + this.ScaleTo(MaxSize, getAnimationDuration * 0.8f) .Then() // Pop at the end of the bubbles life time - .ScaleTo(Info.MaxSize * 1.5f, getAnimationDuration * 0.2f, Easing.OutQuint) - .FadeOutFromOne(getAnimationDuration * 0.2f, Easing.OutCirc).Expire(); + .ScaleTo(MaxSize * 1.5f, getAnimationDuration * 0.2f, Easing.OutQuint) + .FadeOut(getAnimationDuration * 0.2f, Easing.OutCirc).Expire(); - if (!Info.IsHit) return; + if (!DrawableOsuHitObject.IsHit) return; colourBox.FadeColour(colourDarker); @@ -206,26 +195,27 @@ namespace osu.Game.Rulesets.Osu.Mods content.TransformTo(nameof(BorderThickness), 2f, getAnimationDuration * 0.3f, Easing.OutQuint) // Avoids transparency overlap issues during the bubble "pop" .Then().Schedule(() => content.BorderThickness = 0); + + Vector2 getPosition() + { + switch (DrawableOsuHitObject) + { + // SliderHeads are derived from HitCircles, + // so we must handle them before to avoid them using the wrong positioning logic + case DrawableSliderHead: + return DrawableOsuHitObject.HitObject.Position; + + // Using hitobject position will cause issues with HitCircle placement due to stack leniency. + case DrawableHitCircle: + return DrawableOsuHitObject.Position; + + default: + return DrawableOsuHitObject.HitObject.Position; + } + } } } - private struct BubbleInfo - { - public Vector2 InitialSize { get; set; } - - public float MaxSize { get; set; } - - public Vector2 Position { get; set; } - - public Colour4 Colour { get; set; } - - // FadeTime is based on the approach rate of the beatmap. - public double FadeTime { get; set; } - - // Whether the corresponding HitObject was hit - public bool IsHit { get; set; } - } - #endregion } } From 8fc35b159f87a10a087c79c469d981ff9750bc95 Mon Sep 17 00:00:00 2001 From: mk56-spn Date: Wed, 15 Feb 2023 10:04:50 +0100 Subject: [PATCH 054/476] Remove dysfunctional slider colouring --- osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs index 981932c580..4edf726f26 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs @@ -18,7 +18,6 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; -using osu.Game.Rulesets.Osu.Skinning.Default; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Scoring; @@ -77,12 +76,6 @@ namespace osu.Game.Rulesets.Osu.Mods public void ApplyToDrawableHitObject(DrawableHitObject drawableObject) { - if (drawableObject is DrawableSlider slider) - { - applySliderState(slider); - slider.Body.OnSkinChanged += () => applySliderState(slider); - } - drawableObject.OnNewResult += (drawable, _) => { if (drawable is not DrawableOsuHitObject drawableOsuHitObject) return; @@ -122,10 +115,6 @@ namespace osu.Game.Rulesets.Osu.Mods }; } - // Makes the slider border coloured on all skins (for aesthetics) - private void applySliderState(DrawableSlider slider) => - ((PlaySliderBody)slider.Body.Drawable).BorderColour = slider.AccentColour.Value; - #region Pooled Bubble drawable private partial class BubbleDrawable : PoolableDrawable From 1f586c129c1b27fee148db26846e69eb6febc1d8 Mon Sep 17 00:00:00 2001 From: Cootz Date: Wed, 15 Feb 2023 22:15:44 +0300 Subject: [PATCH 055/476] fix applied --- osu.Game/Database/LegacyExporter.cs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/osu.Game/Database/LegacyExporter.cs b/osu.Game/Database/LegacyExporter.cs index 09d6913dd9..c56c17222d 100644 --- a/osu.Game/Database/LegacyExporter.cs +++ b/osu.Game/Database/LegacyExporter.cs @@ -3,6 +3,7 @@ #nullable disable +using System; using System.Collections.Generic; using System.IO; using osu.Framework.Platform; @@ -18,6 +19,14 @@ namespace osu.Game.Database public abstract class LegacyExporter where TModel : class, IHasNamedFiles { + /// + /// Max length of filename (including extension) + /// + /// + /// This constant is smaller 256 because adds additional "_" to the end of the path + /// + private const int max_path = 255 - (32 + 4 + 2); //max path - (Guid + Guid "D" format chars + Storage.CreateFileSafely chars) + /// /// The file extension for exports (including the leading '.'). /// @@ -33,7 +42,16 @@ namespace osu.Game.Database UserFileStorage = storage.GetStorageForDirectory(@"files"); } - protected virtual string GetFilename(TModel item) => item.GetDisplayString(); + protected virtual string GetFilename(TModel item) + { + string fileName = item.GetDisplayString(); + + int fileNameLength = fileName.Length - FileExtension.Length; + if (fileNameLength > max_path) + fileName = fileName.Remove(max_path - FileExtension.Length); //Truncating the name to fit the path limit + + return fileName; + } /// /// Exports an item to a legacy (.zip based) package. From 387a6f1330b3e22360565dd07a4a7048eae3e4d8 Mon Sep 17 00:00:00 2001 From: Cootz Date: Wed, 15 Feb 2023 22:43:43 +0300 Subject: [PATCH 056/476] Move logic to `Export` method --- osu.Game/Database/LegacyExporter.cs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/osu.Game/Database/LegacyExporter.cs b/osu.Game/Database/LegacyExporter.cs index cd0b50c109..483c3cbd5c 100644 --- a/osu.Game/Database/LegacyExporter.cs +++ b/osu.Game/Database/LegacyExporter.cs @@ -43,16 +43,7 @@ namespace osu.Game.Database UserFileStorage = storage.GetStorageForDirectory(@"files"); } - protected virtual string GetFilename(TModel item) - { - string fileName = item.GetDisplayString(); - - int fileNameLength = fileName.Length - FileExtension.Length; - if (fileNameLength > max_path) - fileName = fileName.Remove(max_path - FileExtension.Length); //Truncating the name to fit the path limit - - return fileName; - } + protected virtual string GetFilename(TModel item) => item.GetDisplayString(); /// /// Exports an item to a legacy (.zip based) package. @@ -68,6 +59,15 @@ namespace osu.Game.Database .Concat(exportStorage.GetDirectories(string.Empty)); string filename = NamingUtils.GetNextBestFilename(existingExports, $"{itemFilename}{FileExtension}"); + + if (filename.Length > max_path) + { + string filenameWithoutExtension = Path.GetFileNameWithoutExtension(filename); + + filenameWithoutExtension = filenameWithoutExtension.Remove(max_path - FileExtension.Length); //Truncating the name to fit the path limit + filename = $"{filenameWithoutExtension}{FileExtension}"; + } + using (var stream = exportStorage.CreateFileSafely(filename)) ExportModelTo(item, stream); From 157bba78305b3474fecc5a529b95c954b994e9e9 Mon Sep 17 00:00:00 2001 From: tsrk Date: Mon, 13 Feb 2023 21:59:17 +0000 Subject: [PATCH 057/476] refactor: rename `Trigger` class to `InputTrigger` --- .../TestSceneOsuTouchInput.cs | 2 +- .../Visual/Gameplay/TestSceneKeyCounter.cs | 2 +- osu.Game/Rulesets/UI/RulesetInputManager.cs | 6 +++--- osu.Game/Screens/Play/DefaultKeyCounter.cs | 2 +- osu.Game/Screens/Play/KeyCounter.cs | 14 +++++++------- osu.Game/Screens/Play/KeyCounterAction.cs | 2 +- osu.Game/Screens/Play/KeyCounterDisplay.cs | 2 +- osu.Game/Screens/Play/KeyCounterKeyboard.cs | 2 +- osu.Game/Screens/Play/KeyCounterMouse.cs | 2 +- 9 files changed, 17 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs index cd30d8df83..1a273153bd 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs @@ -579,7 +579,7 @@ namespace osu.Game.Rulesets.Osu.Tests private void checkNotPressed(OsuAction action) => AddAssert($"Not pressing {action}", () => !osuInputManager.PressedActions.Contains(action)); private void checkPressed(OsuAction action) => AddAssert($"Is pressing {action}", () => osuInputManager.PressedActions.Contains(action)); - public partial class TestActionKeyCounter : KeyCounter.Trigger, IKeyBindingHandler + public partial class TestActionKeyCounter : KeyCounter.InputTrigger, IKeyBindingHandler { public OsuAction Action { get; } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs index 60ebce4f52..f652a62489 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs @@ -38,7 +38,7 @@ namespace osu.Game.Tests.Visual.Gameplay kc.Add(kc.CreateKeyCounter(new KeyCounterKeyboard(key))); }); - Key testKey = ((KeyCounterKeyboard)kc.Children.First().CounterTrigger).Key; + Key testKey = ((KeyCounterKeyboard)kc.Children.First().Trigger).Key; void addPressKeyStep() { diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index 0fa1f0b332..22dc6567eb 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -176,14 +176,14 @@ namespace osu.Game.Rulesets.UI { } - public bool OnPressed(KeyBindingPressEvent e) => Target.Children.Where(c => c.CounterTrigger is KeyCounterAction) - .Select(c => (KeyCounterAction)c.CounterTrigger) + public bool OnPressed(KeyBindingPressEvent e) => Target.Children.Where(c => c.Trigger is KeyCounterAction) + .Select(c => (KeyCounterAction)c.Trigger) .Any(c => c.OnPressed(e.Action, Clock.Rate >= 0)); public void OnReleased(KeyBindingReleaseEvent e) { foreach (var c - in Target.Children.Where(c => c.CounterTrigger is KeyCounterAction).Select(c => (KeyCounterAction)c.CounterTrigger)) + in Target.Children.Where(c => c.Trigger is KeyCounterAction).Select(c => (KeyCounterAction)c.Trigger)) c.OnReleased(e.Action, Clock.Rate >= 0); } } diff --git a/osu.Game/Screens/Play/DefaultKeyCounter.cs b/osu.Game/Screens/Play/DefaultKeyCounter.cs index dcb425ae1d..93dc4abcb5 100644 --- a/osu.Game/Screens/Play/DefaultKeyCounter.cs +++ b/osu.Game/Screens/Play/DefaultKeyCounter.cs @@ -25,7 +25,7 @@ namespace osu.Game.Screens.Play public Color4 KeyUpTextColor { get; set; } = Color4.White; public double FadeTime { get; set; } - public DefaultKeyCounter(Trigger trigger) + public DefaultKeyCounter(InputTrigger trigger) : base(trigger) { } diff --git a/osu.Game/Screens/Play/KeyCounter.cs b/osu.Game/Screens/Play/KeyCounter.cs index b111305b22..a1950a49f4 100644 --- a/osu.Game/Screens/Play/KeyCounter.cs +++ b/osu.Game/Screens/Play/KeyCounter.cs @@ -10,7 +10,7 @@ namespace osu.Game.Screens.Play { public abstract partial class KeyCounter : Container { - public readonly Trigger CounterTrigger; + public readonly InputTrigger Trigger; protected Bindable IsCountingBindable = new BindableBool(true); @@ -35,7 +35,7 @@ namespace osu.Game.Screens.Play private set => PressesCount.Value = value; } - protected KeyCounter(Trigger trigger) + protected KeyCounter(InputTrigger trigger) { InternalChildren = new Drawable[] { @@ -43,10 +43,10 @@ namespace osu.Game.Screens.Play { RelativeSizeAxes = Axes.Both }, - CounterTrigger = trigger, + Trigger = trigger, }; - CounterTrigger.Target = this; + Trigger.Target = this; Name = trigger.Name; } @@ -68,9 +68,9 @@ namespace osu.Game.Screens.Play CountPresses--; } - protected override bool Handle(UIEvent e) => CounterTrigger.TriggerEvent(e); + protected override bool Handle(UIEvent e) => Trigger.TriggerEvent(e); - public abstract partial class Trigger : Component + public abstract partial class InputTrigger : Component { private KeyCounter? target; @@ -79,7 +79,7 @@ namespace osu.Game.Screens.Play set => target = value; } - protected Trigger(string name) + protected InputTrigger(string name) { Name = name; } diff --git a/osu.Game/Screens/Play/KeyCounterAction.cs b/osu.Game/Screens/Play/KeyCounterAction.cs index 058dbb1480..4926970960 100644 --- a/osu.Game/Screens/Play/KeyCounterAction.cs +++ b/osu.Game/Screens/Play/KeyCounterAction.cs @@ -7,7 +7,7 @@ using System.Collections.Generic; namespace osu.Game.Screens.Play { - public partial class KeyCounterAction : KeyCounter.Trigger + public partial class KeyCounterAction : KeyCounter.InputTrigger where T : struct { public T Action { get; } diff --git a/osu.Game/Screens/Play/KeyCounterDisplay.cs b/osu.Game/Screens/Play/KeyCounterDisplay.cs index b06d1adfa0..fc6fa12f10 100644 --- a/osu.Game/Screens/Play/KeyCounterDisplay.cs +++ b/osu.Game/Screens/Play/KeyCounterDisplay.cs @@ -81,7 +81,7 @@ namespace osu.Game.Screens.Play this.receptor = receptor; } - public virtual KeyCounter CreateKeyCounter(KeyCounter.Trigger trigger) => new DefaultKeyCounter(trigger); + public virtual KeyCounter CreateKeyCounter(KeyCounter.InputTrigger trigger) => new DefaultKeyCounter(trigger); public partial class Receptor : Drawable { diff --git a/osu.Game/Screens/Play/KeyCounterKeyboard.cs b/osu.Game/Screens/Play/KeyCounterKeyboard.cs index 4306efd360..6ae1a2c5bc 100644 --- a/osu.Game/Screens/Play/KeyCounterKeyboard.cs +++ b/osu.Game/Screens/Play/KeyCounterKeyboard.cs @@ -8,7 +8,7 @@ using osuTK.Input; namespace osu.Game.Screens.Play { - public partial class KeyCounterKeyboard : KeyCounter.Trigger + public partial class KeyCounterKeyboard : KeyCounter.InputTrigger { public Key Key { get; } diff --git a/osu.Game/Screens/Play/KeyCounterMouse.cs b/osu.Game/Screens/Play/KeyCounterMouse.cs index 00fca47ba2..40674cdbcd 100644 --- a/osu.Game/Screens/Play/KeyCounterMouse.cs +++ b/osu.Game/Screens/Play/KeyCounterMouse.cs @@ -9,7 +9,7 @@ using osuTK; namespace osu.Game.Screens.Play { - public partial class KeyCounterMouse : KeyCounter.Trigger + public partial class KeyCounterMouse : KeyCounter.InputTrigger { public MouseButton Button { get; } From df0633858cb9aa0734a95e5f67fc284313571485 Mon Sep 17 00:00:00 2001 From: tsrk Date: Mon, 13 Feb 2023 23:20:23 +0000 Subject: [PATCH 058/476] fix(KeyCounter): don't override Handle This caused the Keyboard inputs to register twice, which is not what we want. --- osu.Game/Screens/Play/KeyCounter.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Screens/Play/KeyCounter.cs b/osu.Game/Screens/Play/KeyCounter.cs index a1950a49f4..cd306dfb9b 100644 --- a/osu.Game/Screens/Play/KeyCounter.cs +++ b/osu.Game/Screens/Play/KeyCounter.cs @@ -4,7 +4,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Input.Events; namespace osu.Game.Screens.Play { @@ -68,8 +67,6 @@ namespace osu.Game.Screens.Play CountPresses--; } - protected override bool Handle(UIEvent e) => Trigger.TriggerEvent(e); - public abstract partial class InputTrigger : Component { private KeyCounter? target; From a644fae3649f29eacf612b2bd920fc4ad0a8ec48 Mon Sep 17 00:00:00 2001 From: tsrk Date: Mon, 13 Feb 2023 23:22:50 +0000 Subject: [PATCH 059/476] style(KeyCounter): rename `(Un)lit` methods to `(Un)light` --- osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs | 4 ++-- osu.Game/Screens/Play/KeyCounter.cs | 4 ++-- osu.Game/Screens/Play/KeyCounterAction.cs | 4 ++-- osu.Game/Screens/Play/KeyCounterKeyboard.cs | 6 ++++-- osu.Game/Screens/Play/KeyCounterMouse.cs | 4 ++-- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs index 1a273153bd..6068cf50b6 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs @@ -593,7 +593,7 @@ namespace osu.Game.Rulesets.Osu.Tests { if (e.Action == Action) { - Lit(); + Light(); } return false; @@ -602,7 +602,7 @@ namespace osu.Game.Rulesets.Osu.Tests public void OnReleased(KeyBindingReleaseEvent e) { if (e.Action == Action) - Unlit(); + Unlight(); } } diff --git a/osu.Game/Screens/Play/KeyCounter.cs b/osu.Game/Screens/Play/KeyCounter.cs index cd306dfb9b..4a7203870c 100644 --- a/osu.Game/Screens/Play/KeyCounter.cs +++ b/osu.Game/Screens/Play/KeyCounter.cs @@ -81,7 +81,7 @@ namespace osu.Game.Screens.Play Name = name; } - protected void Lit(bool increment = true) + protected void Light(bool increment = true) { if (target == null) return; @@ -90,7 +90,7 @@ namespace osu.Game.Screens.Play target.Increment(); } - protected void Unlit(bool preserve = true) + protected void Unlight(bool preserve = true) { if (target == null) return; diff --git a/osu.Game/Screens/Play/KeyCounterAction.cs b/osu.Game/Screens/Play/KeyCounterAction.cs index 4926970960..65a0bc2ca7 100644 --- a/osu.Game/Screens/Play/KeyCounterAction.cs +++ b/osu.Game/Screens/Play/KeyCounterAction.cs @@ -23,7 +23,7 @@ namespace osu.Game.Screens.Play if (!EqualityComparer.Default.Equals(action, Action)) return false; - Lit(forwards); + Light(forwards); return false; } @@ -32,7 +32,7 @@ namespace osu.Game.Screens.Play if (!EqualityComparer.Default.Equals(action, Action)) return; - Unlit(forwards); + Unlight(forwards); } } } diff --git a/osu.Game/Screens/Play/KeyCounterKeyboard.cs b/osu.Game/Screens/Play/KeyCounterKeyboard.cs index 6ae1a2c5bc..ef1f207556 100644 --- a/osu.Game/Screens/Play/KeyCounterKeyboard.cs +++ b/osu.Game/Screens/Play/KeyCounterKeyboard.cs @@ -21,7 +21,9 @@ namespace osu.Game.Screens.Play protected override bool OnKeyDown(KeyDownEvent e) { if (e.Key == Key) - Lit(); + { + Light(); + } return base.OnKeyDown(e); } @@ -29,7 +31,7 @@ namespace osu.Game.Screens.Play protected override void OnKeyUp(KeyUpEvent e) { if (e.Key == Key) - Unlit(); + Unlight(); base.OnKeyUp(e); } diff --git a/osu.Game/Screens/Play/KeyCounterMouse.cs b/osu.Game/Screens/Play/KeyCounterMouse.cs index 40674cdbcd..cf0e0a394f 100644 --- a/osu.Game/Screens/Play/KeyCounterMouse.cs +++ b/osu.Game/Screens/Play/KeyCounterMouse.cs @@ -39,7 +39,7 @@ namespace osu.Game.Screens.Play protected override bool OnMouseDown(MouseDownEvent e) { if (e.Button == Button) - Lit(); + Light(); return base.OnMouseDown(e); } @@ -47,7 +47,7 @@ namespace osu.Game.Screens.Play protected override void OnMouseUp(MouseUpEvent e) { if (e.Button == Button) - Unlit(); + Unlight(); base.OnMouseUp(e); } From 076eb81b212caaa61546ad3d3d0605b5e072eb46 Mon Sep 17 00:00:00 2001 From: tsrk Date: Mon, 13 Feb 2023 23:49:57 +0000 Subject: [PATCH 060/476] refactor: rename trigger classes Makes it better to understand their purpose --- .../TestSceneOsuTouchInput.cs | 8 ++++---- .../Visual/Gameplay/TestSceneHUDOverlay.cs | 2 +- .../Visual/Gameplay/TestSceneKeyCounter.cs | 12 ++++++------ .../Gameplay/TestSceneSkinEditorMultipleSkins.cs | 2 +- .../Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs | 2 +- osu.Game/Rulesets/UI/RulesetInputManager.cs | 8 ++++---- ...eyCounterAction.cs => KeyCounterActionTrigger.cs} | 4 ++-- ...unterKeyboard.cs => KeyCounterKeyboardTrigger.cs} | 4 ++-- ...{KeyCounterMouse.cs => KeyCounterMouseTrigger.cs} | 4 ++-- 9 files changed, 23 insertions(+), 23 deletions(-) rename osu.Game/Screens/Play/{KeyCounterAction.cs => KeyCounterActionTrigger.cs} (86%) rename osu.Game/Screens/Play/{KeyCounterKeyboard.cs => KeyCounterKeyboardTrigger.cs} (85%) rename osu.Game/Screens/Play/{KeyCounterMouse.cs => KeyCounterMouseTrigger.cs} (90%) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs index 6068cf50b6..950e034d8f 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs @@ -59,14 +59,14 @@ namespace osu.Game.Rulesets.Osu.Tests Origin = Anchor.Centre, Children = new Drawable[] { - leftKeyCounter = new DefaultKeyCounter(new TestActionKeyCounter(OsuAction.LeftButton)) + leftKeyCounter = new DefaultKeyCounter(new TestActionKeyCounterTrigger(OsuAction.LeftButton)) { Anchor = Anchor.Centre, Origin = Anchor.CentreRight, Depth = float.MinValue, X = -100, }, - rightKeyCounter = new DefaultKeyCounter(new TestActionKeyCounter(OsuAction.RightButton)) + rightKeyCounter = new DefaultKeyCounter(new TestActionKeyCounterTrigger(OsuAction.RightButton)) { Anchor = Anchor.Centre, Origin = Anchor.CentreLeft, @@ -579,11 +579,11 @@ namespace osu.Game.Rulesets.Osu.Tests private void checkNotPressed(OsuAction action) => AddAssert($"Not pressing {action}", () => !osuInputManager.PressedActions.Contains(action)); private void checkPressed(OsuAction action) => AddAssert($"Is pressing {action}", () => osuInputManager.PressedActions.Contains(action)); - public partial class TestActionKeyCounter : KeyCounter.InputTrigger, IKeyBindingHandler + public partial class TestActionKeyCounterTrigger : KeyCounter.InputTrigger, IKeyBindingHandler { public OsuAction Action { get; } - public TestActionKeyCounter(OsuAction action) + public TestActionKeyCounterTrigger(OsuAction action) : base(action.ToString()) { Action = action; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs index 4055ef9d3a..af79650d29 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs @@ -267,7 +267,7 @@ namespace osu.Game.Tests.Visual.Gameplay hudOverlay = new HUDOverlay(null, Array.Empty()); // Add any key just to display the key counter visually. - hudOverlay.KeyCounter.Add(hudOverlay.KeyCounter.CreateKeyCounter(new KeyCounterKeyboard(Key.Space))); + hudOverlay.KeyCounter.Add(hudOverlay.KeyCounter.CreateKeyCounter(new KeyCounterKeyboardTrigger(Key.Space))); scoreProcessor.Combo.Value = 1; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs index f652a62489..975a5c9465 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs @@ -25,20 +25,20 @@ namespace osu.Game.Tests.Visual.Gameplay Anchor = Anchor.Centre, Children = new[] { - testCounter = new DefaultKeyCounter(new KeyCounterKeyboard(Key.X)), - new DefaultKeyCounter(new KeyCounterKeyboard(Key.X)), - new DefaultKeyCounter(new KeyCounterMouse(MouseButton.Left)), - new DefaultKeyCounter(new KeyCounterMouse(MouseButton.Right)), + testCounter = new DefaultKeyCounter(new KeyCounterKeyboardTrigger(Key.X)), + new DefaultKeyCounter(new KeyCounterKeyboardTrigger(Key.X)), + new DefaultKeyCounter(new KeyCounterMouseTrigger(MouseButton.Left)), + new DefaultKeyCounter(new KeyCounterMouseTrigger(MouseButton.Right)), }, }; AddStep("Add random", () => { Key key = (Key)((int)Key.A + RNG.Next(26)); - kc.Add(kc.CreateKeyCounter(new KeyCounterKeyboard(key))); + kc.Add(kc.CreateKeyCounter(new KeyCounterKeyboardTrigger(key))); }); - Key testKey = ((KeyCounterKeyboard)kc.Children.First().Trigger).Key; + Key testKey = ((KeyCounterKeyboardTrigger)kc.Children.First().Trigger).Key; void addPressKeyStep() { diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs index 56cf56efd9..432ff2fc7e 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs @@ -53,7 +53,7 @@ namespace osu.Game.Tests.Visual.Gameplay }; // Add any key just to display the key counter visually. - hudOverlay.KeyCounter.Add(hudOverlay.KeyCounter.CreateKeyCounter(new KeyCounterKeyboard(Key.Space))); + hudOverlay.KeyCounter.Add(hudOverlay.KeyCounter.CreateKeyCounter(new KeyCounterKeyboardTrigger(Key.Space))); scoreProcessor.Combo.Value = 1; return new Container diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs index f713bca081..24de29fa03 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs @@ -88,7 +88,7 @@ namespace osu.Game.Tests.Visual.Gameplay hudOverlay = new HUDOverlay(null, Array.Empty()); // Add any key just to display the key counter visually. - hudOverlay.KeyCounter.Add(hudOverlay.KeyCounter.CreateKeyCounter(new KeyCounterKeyboard(Key.Space))); + hudOverlay.KeyCounter.Add(hudOverlay.KeyCounter.CreateKeyCounter(new KeyCounterKeyboardTrigger(Key.Space))); action?.Invoke(hudOverlay); diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index 22dc6567eb..6a38fa4824 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -166,7 +166,7 @@ namespace osu.Game.Rulesets.UI .Select(b => b.GetAction()) .Distinct() .OrderBy(action => action) - .Select(action => keyCounter.CreateKeyCounter(new KeyCounterAction(action)))); + .Select(action => keyCounter.CreateKeyCounter(new KeyCounterActionTrigger(action)))); } private partial class ActionReceptor : KeyCounterDisplay.Receptor, IKeyBindingHandler @@ -176,14 +176,14 @@ namespace osu.Game.Rulesets.UI { } - public bool OnPressed(KeyBindingPressEvent e) => Target.Children.Where(c => c.Trigger is KeyCounterAction) - .Select(c => (KeyCounterAction)c.Trigger) + public bool OnPressed(KeyBindingPressEvent e) => Target.Children.Where(c => c.Trigger is KeyCounterActionTrigger) + .Select(c => (KeyCounterActionTrigger)c.Trigger) .Any(c => c.OnPressed(e.Action, Clock.Rate >= 0)); public void OnReleased(KeyBindingReleaseEvent e) { foreach (var c - in Target.Children.Where(c => c.Trigger is KeyCounterAction).Select(c => (KeyCounterAction)c.Trigger)) + in Target.Children.Where(c => c.Trigger is KeyCounterActionTrigger).Select(c => (KeyCounterActionTrigger)c.Trigger)) c.OnReleased(e.Action, Clock.Rate >= 0); } } diff --git a/osu.Game/Screens/Play/KeyCounterAction.cs b/osu.Game/Screens/Play/KeyCounterActionTrigger.cs similarity index 86% rename from osu.Game/Screens/Play/KeyCounterAction.cs rename to osu.Game/Screens/Play/KeyCounterActionTrigger.cs index 65a0bc2ca7..51b82ac5e5 100644 --- a/osu.Game/Screens/Play/KeyCounterAction.cs +++ b/osu.Game/Screens/Play/KeyCounterActionTrigger.cs @@ -7,12 +7,12 @@ using System.Collections.Generic; namespace osu.Game.Screens.Play { - public partial class KeyCounterAction : KeyCounter.InputTrigger + public partial class KeyCounterActionTrigger : KeyCounter.InputTrigger where T : struct { public T Action { get; } - public KeyCounterAction(T action) + public KeyCounterActionTrigger(T action) : base($"B{(int)(object)action + 1}") { Action = action; diff --git a/osu.Game/Screens/Play/KeyCounterKeyboard.cs b/osu.Game/Screens/Play/KeyCounterKeyboardTrigger.cs similarity index 85% rename from osu.Game/Screens/Play/KeyCounterKeyboard.cs rename to osu.Game/Screens/Play/KeyCounterKeyboardTrigger.cs index ef1f207556..fee716abf4 100644 --- a/osu.Game/Screens/Play/KeyCounterKeyboard.cs +++ b/osu.Game/Screens/Play/KeyCounterKeyboardTrigger.cs @@ -8,11 +8,11 @@ using osuTK.Input; namespace osu.Game.Screens.Play { - public partial class KeyCounterKeyboard : KeyCounter.InputTrigger + public partial class KeyCounterKeyboardTrigger : KeyCounter.InputTrigger { public Key Key { get; } - public KeyCounterKeyboard(Key key) + public KeyCounterKeyboardTrigger(Key key) : base(key.ToString()) { Key = key; diff --git a/osu.Game/Screens/Play/KeyCounterMouse.cs b/osu.Game/Screens/Play/KeyCounterMouseTrigger.cs similarity index 90% rename from osu.Game/Screens/Play/KeyCounterMouse.cs rename to osu.Game/Screens/Play/KeyCounterMouseTrigger.cs index cf0e0a394f..a693db9b19 100644 --- a/osu.Game/Screens/Play/KeyCounterMouse.cs +++ b/osu.Game/Screens/Play/KeyCounterMouseTrigger.cs @@ -9,11 +9,11 @@ using osuTK; namespace osu.Game.Screens.Play { - public partial class KeyCounterMouse : KeyCounter.InputTrigger + public partial class KeyCounterMouseTrigger : KeyCounter.InputTrigger { public MouseButton Button { get; } - public KeyCounterMouse(MouseButton button) + public KeyCounterMouseTrigger(MouseButton button) : base(getStringRepresentation(button)) { Button = button; From b0a2e69f951910907559d16fc2392a4d9867cd99 Mon Sep 17 00:00:00 2001 From: tsrk Date: Wed, 15 Feb 2023 22:06:10 +0000 Subject: [PATCH 061/476] style: nullable pass on `KeyCounterDisplay` --- osu.Game/Screens/Play/DefaultKeyCounterDisplay.cs | 11 +++++------ osu.Game/Screens/Play/KeyCounterDisplay.cs | 6 ++---- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Play/DefaultKeyCounterDisplay.cs b/osu.Game/Screens/Play/DefaultKeyCounterDisplay.cs index d643070e06..332474a517 100644 --- a/osu.Game/Screens/Play/DefaultKeyCounterDisplay.cs +++ b/osu.Game/Screens/Play/DefaultKeyCounterDisplay.cs @@ -24,12 +24,11 @@ namespace osu.Game.Screens.Play public DefaultKeyCounterDisplay() { - InternalChild = KeyFlow = new FillFlowContainer - { - Direction = FillDirection.Horizontal, - AutoSizeAxes = Axes.Both, - Alpha = 0, - }; + KeyFlow.Direction = FillDirection.Horizontal; + KeyFlow.AutoSizeAxes = Axes.Both; + KeyFlow.Alpha = 0; + + InternalChild = KeyFlow; } protected override void Update() diff --git a/osu.Game/Screens/Play/KeyCounterDisplay.cs b/osu.Game/Screens/Play/KeyCounterDisplay.cs index fc6fa12f10..f5af67caea 100644 --- a/osu.Game/Screens/Play/KeyCounterDisplay.cs +++ b/osu.Game/Screens/Play/KeyCounterDisplay.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 disable - using System; using System.Linq; using osu.Framework.Allocation; @@ -19,7 +17,7 @@ namespace osu.Game.Screens.Play { protected readonly Bindable ConfigVisibility = new Bindable(); - protected FillFlowContainer KeyFlow; + protected FillFlowContainer KeyFlow = new FillFlowContainer(); protected override Container Content => KeyFlow; @@ -71,7 +69,7 @@ namespace osu.Game.Screens.Play public override bool HandleNonPositionalInput => receptor == null; public override bool HandlePositionalInput => receptor == null; - private Receptor receptor; + private Receptor? receptor; public void SetReceptor(Receptor receptor) { From e9dcc257b48ae26302c598df7d433e09007a40ba Mon Sep 17 00:00:00 2001 From: tsrk Date: Wed, 15 Feb 2023 22:06:35 +0000 Subject: [PATCH 062/476] reafactor: simplify type checking --- osu.Game/Screens/Play/DefaultKeyCounterDisplay.cs | 6 +++--- osu.Game/Screens/Play/KeyCounterDisplay.cs | 5 ++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/DefaultKeyCounterDisplay.cs b/osu.Game/Screens/Play/DefaultKeyCounterDisplay.cs index 332474a517..b69ecfd7ae 100644 --- a/osu.Game/Screens/Play/DefaultKeyCounterDisplay.cs +++ b/osu.Game/Screens/Play/DefaultKeyCounterDisplay.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 osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -43,14 +42,15 @@ namespace osu.Game.Screens.Play public override void Add(KeyCounter key) { base.Add(key); - if (key is not DefaultKeyCounter defaultKey) - throw new ArgumentException($"{key.GetType()} is not a supported {nameof(KeyCounter)}.", nameof(key)); + DefaultKeyCounter defaultKey = (DefaultKeyCounter)key; defaultKey.FadeTime = key_fade_time; defaultKey.KeyDownTextColor = KeyDownTextColor; defaultKey.KeyUpTextColor = KeyUpTextColor; } + protected override bool CheckType(KeyCounter key) => key is DefaultKeyCounter; + protected override void UpdateVisibility() => // Isolate changing visibility of the key counters from fading this component. KeyFlow.FadeTo(AlwaysVisible.Value || ConfigVisibility.Value ? 1 : 0, duration); diff --git a/osu.Game/Screens/Play/KeyCounterDisplay.cs b/osu.Game/Screens/Play/KeyCounterDisplay.cs index f5af67caea..ed47af11a3 100644 --- a/osu.Game/Screens/Play/KeyCounterDisplay.cs +++ b/osu.Game/Screens/Play/KeyCounterDisplay.cs @@ -29,12 +29,15 @@ namespace osu.Game.Screens.Play public override void Add(KeyCounter key) { - ArgumentNullException.ThrowIfNull(key); + if (!CheckType(key)) + throw new ArgumentException($"{key.GetType()} is not a supported {nameof(KeyCounter)}.", nameof(key)); base.Add(key); key.IsCounting = IsCounting; } + protected virtual bool CheckType(KeyCounter key) => true; + [BackgroundDependencyLoader] private void load(OsuConfigManager config) { From 74e7cc205601bc4edb9d88e12afb8c63f8e967d2 Mon Sep 17 00:00:00 2001 From: tsrk Date: Wed, 15 Feb 2023 22:18:02 +0000 Subject: [PATCH 063/476] feat: implement new design of key counter --- .../Visual/Gameplay/TestSceneKeyCounter.cs | 39 +++++++--- osu.Game/Screens/Play/ArgonKeyCounter.cs | 76 +++++++++++++++++++ .../Screens/Play/ArgonKeyCounterDisplay.cs | 42 ++++++++++ 3 files changed, 147 insertions(+), 10 deletions(-) create mode 100644 osu.Game/Screens/Play/ArgonKeyCounter.cs create mode 100644 osu.Game/Screens/Play/ArgonKeyCounterDisplay.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs index 975a5c9465..41add82245 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs @@ -8,6 +8,7 @@ using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Utils; using osu.Game.Screens.Play; +using osuTK; using osuTK.Input; namespace osu.Game.Tests.Visual.Gameplay @@ -18,24 +19,44 @@ namespace osu.Game.Tests.Visual.Gameplay public TestSceneKeyCounter() { DefaultKeyCounter testCounter; + KeyCounterDisplay kc; + KeyCounterDisplay argonKc; - KeyCounterDisplay kc = new DefaultKeyCounterDisplay + Children = new Drawable[] { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Children = new[] + kc = new DefaultKeyCounterDisplay { - testCounter = new DefaultKeyCounter(new KeyCounterKeyboardTrigger(Key.X)), - new DefaultKeyCounter(new KeyCounterKeyboardTrigger(Key.X)), - new DefaultKeyCounter(new KeyCounterMouseTrigger(MouseButton.Left)), - new DefaultKeyCounter(new KeyCounterMouseTrigger(MouseButton.Right)), + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Position = new Vector2(0, -50), + Children = new[] + { + testCounter = new DefaultKeyCounter(new KeyCounterKeyboardTrigger(Key.X)), + new DefaultKeyCounter(new KeyCounterKeyboardTrigger(Key.X)), + new DefaultKeyCounter(new KeyCounterMouseTrigger(MouseButton.Left)), + new DefaultKeyCounter(new KeyCounterMouseTrigger(MouseButton.Right)), + }, }, + argonKc = new ArgonKeyCounterDisplay + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Position = new Vector2(0, 50), + Children = new[] + { + new ArgonKeyCounter(new KeyCounterKeyboardTrigger(Key.X)), + new ArgonKeyCounter(new KeyCounterKeyboardTrigger(Key.X)), + new ArgonKeyCounter(new KeyCounterMouseTrigger(MouseButton.Left)), + new ArgonKeyCounter(new KeyCounterMouseTrigger(MouseButton.Right)), + }, + } }; AddStep("Add random", () => { Key key = (Key)((int)Key.A + RNG.Next(26)); kc.Add(kc.CreateKeyCounter(new KeyCounterKeyboardTrigger(key))); + argonKc.Add(argonKc.CreateKeyCounter(new KeyCounterKeyboardTrigger(key))); }); Key testKey = ((KeyCounterKeyboardTrigger)kc.Children.First().Trigger).Key; @@ -52,8 +73,6 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("Disable counting", () => testCounter.IsCounting = false); addPressKeyStep(); AddAssert($"Check {testKey} count has not changed", () => testCounter.CountPresses == 2); - - Add(kc); } } } diff --git a/osu.Game/Screens/Play/ArgonKeyCounter.cs b/osu.Game/Screens/Play/ArgonKeyCounter.cs new file mode 100644 index 0000000000..a275a7e017 --- /dev/null +++ b/osu.Game/Screens/Play/ArgonKeyCounter.cs @@ -0,0 +1,76 @@ +// 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.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osuTK; + +namespace osu.Game.Screens.Play +{ + public partial class ArgonKeyCounter : KeyCounter + { + private Circle inputIndicator = null!; + private OsuSpriteText countText = null!; + + // These values were taken from Figma + private const float line_height = 3; + private const float name_font_size = 10; + private const float count_font_size = 14; + + // Make things look bigger without using Scale + private const float scale_factor = 1.5f; + + public ArgonKeyCounter(InputTrigger trigger) + : base(trigger) + { + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Children = new Drawable[] + { + inputIndicator = new Circle + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.X, + Height = line_height * scale_factor, + Alpha = 0.5f + }, + new OsuSpriteText + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Position = new Vector2(0, -13) * scale_factor, + Font = OsuFont.Torus.With(size: name_font_size * scale_factor, weight: FontWeight.Bold), + Colour = colours.Blue0, + Text = Name + }, + countText = new OsuSpriteText + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Font = OsuFont.Torus.With(size: count_font_size * scale_factor, weight: FontWeight.Bold), + Text = "0" + }, + }; + + // Values from Figma didn't match visually + // So these were just eyeballed + Height = 30 * scale_factor; + Width = 35 * scale_factor; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + IsLit.BindValueChanged(e => inputIndicator.Alpha = e.NewValue ? 1 : 0.5f, true); + PressesCount.BindValueChanged(e => countText.Text = e.NewValue.ToString(@"#,0"), true); + } + } +} diff --git a/osu.Game/Screens/Play/ArgonKeyCounterDisplay.cs b/osu.Game/Screens/Play/ArgonKeyCounterDisplay.cs new file mode 100644 index 0000000000..da34a64a4c --- /dev/null +++ b/osu.Game/Screens/Play/ArgonKeyCounterDisplay.cs @@ -0,0 +1,42 @@ +// 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 osuTK; + +namespace osu.Game.Screens.Play +{ + public partial class ArgonKeyCounterDisplay : KeyCounterDisplay + { + private const int duration = 100; + + public new IReadOnlyList Children + { + get => (IReadOnlyList)base.Children; + set => base.Children = value; + } + + [BackgroundDependencyLoader] + private void load() + { + KeyFlow.Direction = FillDirection.Horizontal; + KeyFlow.AutoSizeAxes = Axes.Both; + KeyFlow.Spacing = new Vector2(2); + + InternalChildren = new[] + { + KeyFlow + }; + } + + protected override bool CheckType(KeyCounter key) => key is ArgonKeyCounter; + + protected override void UpdateVisibility() + => KeyFlow.FadeTo(AlwaysVisible.Value || ConfigVisibility.Value ? 1 : 0, duration); + + public override KeyCounter CreateKeyCounter(KeyCounter.InputTrigger trigger) => new ArgonKeyCounter(trigger); + } +} From d5bc8e2941fc0a6d948fda24546cc976b12165fa Mon Sep 17 00:00:00 2001 From: mk56-spn Date: Thu, 16 Feb 2023 11:12:30 +0100 Subject: [PATCH 064/476] Code cleanup pass: Make bubble transform logic more sane. Extract bubble `getPosition()` method. Address poorly named variables. --- osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 47 +++++++++++---------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs index 4edf726f26..0fc27c8f1d 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs @@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Osu.Mods private readonly Bindable currentCombo = new BindableInt(); private float maxSize; - private float bubbleRadius; + private float bubbleSize; private double bubbleFade; private readonly DrawablePool bubblePool = new DrawablePool(100); @@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Osu.Mods { // Multiplying by 2 results in an initial size that is too large, hence 1.90 has been chosen // Also avoids the HitObject bleeding around the edges of the bubble drawable at minimum size - bubbleRadius = (float)(drawableRuleset.Beatmap.HitObjects.OfType().First().Radius * 1.90f); + bubbleSize = (float)(drawableRuleset.Beatmap.HitObjects.OfType().First().Radius * 1.90f); bubbleFade = drawableRuleset.Beatmap.HitObjects.OfType().First().TimePreempt * 2; // We want to hide the judgements since they are obscured by the BubbleDrawable (due to layering) @@ -96,7 +96,7 @@ namespace osu.Game.Rulesets.Osu.Mods BubbleDrawable bubble = bubblePool.Get(); bubble.DrawableOsuHitObject = drawableOsuHitObject; - bubble.InitialSize = new Vector2(bubbleRadius); + bubble.InitialSize = new Vector2(bubbleSize); bubble.FadeTime = bubbleFade; bubble.MaxSize = maxSize; @@ -122,9 +122,11 @@ namespace osu.Game.Rulesets.Osu.Mods public DrawableOsuHitObject? DrawableOsuHitObject { get; set; } public Vector2 InitialSize { get; set; } - public double FadeTime { get; set; } + public float MaxSize { get; set; } + public double FadeTime { get; set; } + private readonly Box colourBox; private readonly CircularContainer content; @@ -155,12 +157,9 @@ namespace osu.Game.Rulesets.Osu.Mods Debug.Assert(DrawableOsuHitObject.IsNotNull()); Colour = DrawableOsuHitObject.IsHit ? Colour4.White : Colour4.Black; - Alpha = 1; Scale = new Vector2(1); - Position = getPosition(); + Position = getPosition(DrawableOsuHitObject); Size = InitialSize; - content.BorderThickness = InitialSize.X / 3.5f; - content.BorderColour = Colour4.White; //We want to fade to a darker colour to avoid colours such as white hiding the "ripple" effect. ColourInfo colourDarker = DrawableOsuHitObject.AccentColour.Value.Darken(0.1f); @@ -169,7 +168,8 @@ namespace osu.Game.Rulesets.Osu.Mods double getAnimationDuration = 1700 + Math.Pow(FadeTime, 1.07f); // Main bubble scaling based on combo - this.ScaleTo(MaxSize, getAnimationDuration * 0.8f) + this.FadeTo(1) + .ScaleTo(MaxSize, getAnimationDuration * 0.8f) .Then() // Pop at the end of the bubbles life time .ScaleTo(MaxSize * 1.5f, getAnimationDuration * 0.2f, Easing.OutQuint) @@ -177,6 +177,9 @@ namespace osu.Game.Rulesets.Osu.Mods if (!DrawableOsuHitObject.IsHit) return; + content.BorderThickness = InitialSize.X / 3.5f; + content.BorderColour = Colour4.White; + colourBox.FadeColour(colourDarker); content.TransformTo(nameof(BorderColour), colourDarker, getAnimationDuration * 0.3f, Easing.OutQuint); @@ -184,23 +187,23 @@ namespace osu.Game.Rulesets.Osu.Mods content.TransformTo(nameof(BorderThickness), 2f, getAnimationDuration * 0.3f, Easing.OutQuint) // Avoids transparency overlap issues during the bubble "pop" .Then().Schedule(() => content.BorderThickness = 0); + } - Vector2 getPosition() + private Vector2 getPosition(DrawableOsuHitObject drawableOsuHitObject) + { + switch (drawableOsuHitObject) { - switch (DrawableOsuHitObject) - { - // SliderHeads are derived from HitCircles, - // so we must handle them before to avoid them using the wrong positioning logic - case DrawableSliderHead: - return DrawableOsuHitObject.HitObject.Position; + // SliderHeads are derived from HitCircles, + // so we must handle them before to avoid them using the wrong positioning logic + case DrawableSliderHead: + return drawableOsuHitObject.HitObject.Position; - // Using hitobject position will cause issues with HitCircle placement due to stack leniency. - case DrawableHitCircle: - return DrawableOsuHitObject.Position; + // Using hitobject position will cause issues with HitCircle placement due to stack leniency. + case DrawableHitCircle: + return drawableOsuHitObject.Position; - default: - return DrawableOsuHitObject.HitObject.Position; - } + default: + return drawableOsuHitObject.HitObject.Position; } } } From f1da213beaaaba85b82c785def1c43f6cb1f62ec Mon Sep 17 00:00:00 2001 From: Cootz Date: Thu, 16 Feb 2023 16:26:57 +0300 Subject: [PATCH 065/476] Add tests --- osu.Game.Tests/Database/LegacyExporterTest.cs | 85 +++++++++++++++++++ osu.Game/Database/LegacyExporter.cs | 6 +- 2 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 osu.Game.Tests/Database/LegacyExporterTest.cs diff --git a/osu.Game.Tests/Database/LegacyExporterTest.cs b/osu.Game.Tests/Database/LegacyExporterTest.cs new file mode 100644 index 0000000000..5f07e6c917 --- /dev/null +++ b/osu.Game.Tests/Database/LegacyExporterTest.cs @@ -0,0 +1,85 @@ +// 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.Diagnostics; +using System.IO; +using System.Text; +using NUnit.Framework; +using osu.Framework.Platform; +using osu.Framework.Testing; +using osu.Game.Database; + +namespace osu.Game.Tests.Database +{ + public class LegacyExporterTest + { + private TestLegacyExporter? legacyExporter; + private TemporaryNativeStorage? storage; + + [SetUp] + public void SetupLegacyExporter() + { + storage = new TemporaryNativeStorage("export-storage"); + legacyExporter = new TestLegacyExporter(storage); + } + + [Test] + public void ExportFileWithNormalName() + { + var exportStorage = storage?.GetStorageForDirectory(@"exports"); + + string filename = "normal file name"; + var item = new TestPathInfo(filename); + + Assert.That(item.FileName.Length < TestLegacyExporter.GetMaxPath(), Is.True); + Assert.DoesNotThrow(() => legacyExporter?.Export(item)); + Assert.That(exportStorage?.Exists($"{filename}{legacyExporter?.GetExtension()}"), Is.True); + } + + [Test] + public void ExportFileWithSuperLongName() + { + var exportStorage = storage?.GetStorageForDirectory(@"exports"); + + string fullname = "some file with super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name"; + + int capacity = TestLegacyExporter.GetMaxPath() - (legacyExporter?.GetExtension().Length ?? 0); + string expectedName = fullname.Remove(capacity); + + var item = new TestPathInfo(fullname); + + Assert.That(item.FileName.Length > TestLegacyExporter.GetMaxPath(), Is.True); + Assert.DoesNotThrow(() => legacyExporter?.Export(item)); + Assert.That(exportStorage?.Exists($"{expectedName}{legacyExporter?.GetExtension()}"), Is.True); + } + + [TearDown] + public void CleanupAfterTest() + { + storage?.Dispose(); + } + + private class TestPathInfo : IHasNamedFiles + { + public string FileName { get; set; } = string.Empty; + + public TestPathInfo(string fileName) => FileName = fileName; + + public IEnumerable Files { get; set; } = new List(); + + public override string ToString() => FileName; + } + + private class TestLegacyExporter : LegacyExporter + { + public TestLegacyExporter(Storage storage) : base(storage) { } + + public static int GetMaxPath() => MAX_PATH; + + public string GetExtension() => FileExtension; + + protected override string FileExtension => ".ots"; + } + } +} diff --git a/osu.Game/Database/LegacyExporter.cs b/osu.Game/Database/LegacyExporter.cs index 483c3cbd5c..9041f58368 100644 --- a/osu.Game/Database/LegacyExporter.cs +++ b/osu.Game/Database/LegacyExporter.cs @@ -26,7 +26,7 @@ namespace osu.Game.Database /// /// This constant is smaller 256 because adds additional "_" to the end of the path /// - private const int max_path = 255 - (32 + 4 + 2); //max path - (Guid + Guid "D" format chars + Storage.CreateFileSafely chars) + protected const int MAX_PATH = 255 - (32 + 4 + 2); //max path - (Guid + Guid "D" format chars + Storage.CreateFileSafely chars) /// /// The file extension for exports (including the leading '.'). @@ -60,11 +60,11 @@ namespace osu.Game.Database string filename = NamingUtils.GetNextBestFilename(existingExports, $"{itemFilename}{FileExtension}"); - if (filename.Length > max_path) + if (filename.Length > MAX_PATH) { string filenameWithoutExtension = Path.GetFileNameWithoutExtension(filename); - filenameWithoutExtension = filenameWithoutExtension.Remove(max_path - FileExtension.Length); //Truncating the name to fit the path limit + filenameWithoutExtension = filenameWithoutExtension.Remove(MAX_PATH - FileExtension.Length); //Truncating the name to fit the path limit filename = $"{filenameWithoutExtension}{FileExtension}"; } From 6819a45a1bdba3fdd2b28a6d7983b73116ddbded Mon Sep 17 00:00:00 2001 From: Cootz Date: Thu, 16 Feb 2023 16:42:07 +0300 Subject: [PATCH 066/476] Improve code slightly --- osu.Game.Tests/Database/LegacyExporterTest.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Database/LegacyExporterTest.cs b/osu.Game.Tests/Database/LegacyExporterTest.cs index 5f07e6c917..c464eb5c28 100644 --- a/osu.Game.Tests/Database/LegacyExporterTest.cs +++ b/osu.Game.Tests/Database/LegacyExporterTest.cs @@ -2,9 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Text; using NUnit.Framework; using osu.Framework.Platform; using osu.Framework.Testing; @@ -29,7 +26,7 @@ namespace osu.Game.Tests.Database { var exportStorage = storage?.GetStorageForDirectory(@"exports"); - string filename = "normal file name"; + const string filename = "normal file name"; var item = new TestPathInfo(filename); Assert.That(item.FileName.Length < TestLegacyExporter.GetMaxPath(), Is.True); @@ -42,7 +39,7 @@ namespace osu.Game.Tests.Database { var exportStorage = storage?.GetStorageForDirectory(@"exports"); - string fullname = "some file with super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name"; + const string fullname = "some file with super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name"; int capacity = TestLegacyExporter.GetMaxPath() - (legacyExporter?.GetExtension().Length ?? 0); string expectedName = fullname.Remove(capacity); @@ -62,7 +59,7 @@ namespace osu.Game.Tests.Database private class TestPathInfo : IHasNamedFiles { - public string FileName { get; set; } = string.Empty; + public string FileName { get; set; } public TestPathInfo(string fileName) => FileName = fileName; @@ -73,7 +70,10 @@ namespace osu.Game.Tests.Database private class TestLegacyExporter : LegacyExporter { - public TestLegacyExporter(Storage storage) : base(storage) { } + public TestLegacyExporter(Storage storage) + : base(storage) + { + } public static int GetMaxPath() => MAX_PATH; From 6340730427908b839aaa3d00c82497818cec93e1 Mon Sep 17 00:00:00 2001 From: tsrk Date: Thu, 16 Feb 2023 21:59:39 +0000 Subject: [PATCH 067/476] refactor(KeyCounter): remove circularity --- .../TestSceneOsuTouchInput.cs | 2 +- osu.Game/Screens/Play/KeyCounter.cs | 52 +++++++++++-------- .../Screens/Play/KeyCounterActionTrigger.cs | 2 +- .../Screens/Play/KeyCounterKeyboardTrigger.cs | 2 +- .../Screens/Play/KeyCounterMouseTrigger.cs | 2 +- 5 files changed, 33 insertions(+), 27 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs index 950e034d8f..c73025ebb9 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs @@ -593,7 +593,7 @@ namespace osu.Game.Rulesets.Osu.Tests { if (e.Action == Action) { - Light(); + LightUp(); } return false; diff --git a/osu.Game/Screens/Play/KeyCounter.cs b/osu.Game/Screens/Play/KeyCounter.cs index 4a7203870c..3748792383 100644 --- a/osu.Game/Screens/Play/KeyCounter.cs +++ b/osu.Game/Screens/Play/KeyCounter.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 osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -45,7 +46,9 @@ namespace osu.Game.Screens.Play Trigger = trigger, }; - Trigger.Target = this; + Trigger.OnLightUp += LightUp; + Trigger.OnUnlight += Unlight; + Name = trigger.Name; } @@ -67,37 +70,40 @@ namespace osu.Game.Screens.Play CountPresses--; } + protected virtual void LightUp(bool increment = true) + { + IsLit.Value = true; + if (increment) + Increment(); + } + + protected virtual void Unlight(bool preserve = true) + { + IsLit.Value = false; + if (!preserve) + Decrement(); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + Trigger.OnLightUp -= LightUp; + Trigger.OnUnlight -= Unlight; + } + public abstract partial class InputTrigger : Component { - private KeyCounter? target; - - public KeyCounter Target - { - set => target = value; - } + public event Action? OnLightUp; + public event Action? OnUnlight; protected InputTrigger(string name) { Name = name; } - protected void Light(bool increment = true) - { - if (target == null) return; + protected void LightUp(bool increment = true) => OnLightUp?.Invoke(increment); - target.IsLit.Value = true; - if (increment) - target.Increment(); - } - - protected void Unlight(bool preserve = true) - { - if (target == null) return; - - target.IsLit.Value = false; - if (!preserve) - target.Decrement(); - } + protected void Unlight(bool preserve = true) => OnUnlight?.Invoke(preserve); } } } diff --git a/osu.Game/Screens/Play/KeyCounterActionTrigger.cs b/osu.Game/Screens/Play/KeyCounterActionTrigger.cs index 51b82ac5e5..c6acb3f95f 100644 --- a/osu.Game/Screens/Play/KeyCounterActionTrigger.cs +++ b/osu.Game/Screens/Play/KeyCounterActionTrigger.cs @@ -23,7 +23,7 @@ namespace osu.Game.Screens.Play if (!EqualityComparer.Default.Equals(action, Action)) return false; - Light(forwards); + LightUp(forwards); return false; } diff --git a/osu.Game/Screens/Play/KeyCounterKeyboardTrigger.cs b/osu.Game/Screens/Play/KeyCounterKeyboardTrigger.cs index fee716abf4..18eb6b7612 100644 --- a/osu.Game/Screens/Play/KeyCounterKeyboardTrigger.cs +++ b/osu.Game/Screens/Play/KeyCounterKeyboardTrigger.cs @@ -22,7 +22,7 @@ namespace osu.Game.Screens.Play { if (e.Key == Key) { - Light(); + LightUp(); } return base.OnKeyDown(e); diff --git a/osu.Game/Screens/Play/KeyCounterMouseTrigger.cs b/osu.Game/Screens/Play/KeyCounterMouseTrigger.cs index a693db9b19..1446494b5b 100644 --- a/osu.Game/Screens/Play/KeyCounterMouseTrigger.cs +++ b/osu.Game/Screens/Play/KeyCounterMouseTrigger.cs @@ -39,7 +39,7 @@ namespace osu.Game.Screens.Play protected override bool OnMouseDown(MouseDownEvent e) { if (e.Button == Button) - Light(); + LightUp(); return base.OnMouseDown(e); } From ddd6c1a1c671c24f33479eceff34f7acf05b5cc7 Mon Sep 17 00:00:00 2001 From: tsrk Date: Thu, 16 Feb 2023 22:20:34 +0000 Subject: [PATCH 068/476] refactor(KeyCounter): address bindables issues `IsCounting` is back being an auto-property. `countPresses` is now encapsulated and being exposed as an `IBindable` via `CountPresses` --- .../Visual/Gameplay/TestSceneAutoplay.cs | 4 ++-- .../Gameplay/TestSceneGameplayRewinding.cs | 4 ++-- .../Visual/Gameplay/TestSceneKeyCounter.cs | 6 +++--- .../Visual/Gameplay/TestSceneReplay.cs | 2 +- .../TestSceneChangeAndUseGameplayBindings.cs | 4 ++-- osu.Game/Screens/Play/DefaultKeyCounter.cs | 4 ++-- osu.Game/Screens/Play/KeyCounter.cs | 20 +++++-------------- 7 files changed, 17 insertions(+), 27 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs index 5442b3bfef..4b6e1f089f 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs @@ -35,14 +35,14 @@ namespace osu.Game.Tests.Visual.Gameplay var referenceBeatmap = CreateBeatmap(new OsuRuleset().RulesetInfo); AddUntilStep("score above zero", () => Player.ScoreProcessor.TotalScore.Value > 0); - AddUntilStep("key counter counted keys", () => Player.HUDOverlay.KeyCounter.Children.Any(kc => kc.CountPresses > 2)); + AddUntilStep("key counter counted keys", () => Player.HUDOverlay.KeyCounter.Children.Any(kc => kc.CountPresses.Value > 2)); seekTo(referenceBeatmap.Breaks[0].StartTime); AddAssert("keys not counting", () => !Player.HUDOverlay.KeyCounter.IsCounting); AddAssert("overlay displays 100% accuracy", () => Player.BreakOverlay.ChildrenOfType().Single().AccuracyDisplay.Current.Value == 1); AddStep("rewind", () => Player.GameplayClockContainer.Seek(-80000)); - AddUntilStep("key counter reset", () => Player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses == 0)); + AddUntilStep("key counter reset", () => Player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses.Value == 0)); seekTo(referenceBeatmap.HitObjects[^1].GetEndTime()); AddUntilStep("results displayed", () => getResultsScreen()?.IsLoaded == true); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs index 1dffeed01b..9f485cd7bf 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs @@ -31,11 +31,11 @@ namespace osu.Game.Tests.Visual.Gameplay AddUntilStep("wait for track to start running", () => Beatmap.Value.Track.IsRunning); addSeekStep(3000); AddAssert("all judged", () => Player.DrawableRuleset.Playfield.AllHitObjects.All(h => h.Judged)); - AddUntilStep("key counter counted keys", () => Player.HUDOverlay.KeyCounter.Children.Select(kc => kc.CountPresses).Sum() == 15); + AddUntilStep("key counter counted keys", () => Player.HUDOverlay.KeyCounter.Children.Select(kc => kc.CountPresses.Value).Sum() == 15); AddStep("clear results", () => Player.Results.Clear()); addSeekStep(0); AddAssert("none judged", () => Player.DrawableRuleset.Playfield.AllHitObjects.All(h => !h.Judged)); - AddUntilStep("key counters reset", () => Player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses == 0)); + AddUntilStep("key counters reset", () => Player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses.Value == 0)); AddAssert("no results triggered", () => Player.Results.Count == 0); } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs index 975a5c9465..9eeee800d9 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs @@ -46,12 +46,12 @@ namespace osu.Game.Tests.Visual.Gameplay } addPressKeyStep(); - AddAssert($"Check {testKey} counter after keypress", () => testCounter.CountPresses == 1); + AddAssert($"Check {testKey} counter after keypress", () => testCounter.CountPresses.Value == 1); addPressKeyStep(); - AddAssert($"Check {testKey} counter after keypress", () => testCounter.CountPresses == 2); + AddAssert($"Check {testKey} counter after keypress", () => testCounter.CountPresses.Value == 2); AddStep("Disable counting", () => testCounter.IsCounting = false); addPressKeyStep(); - AddAssert($"Check {testKey} count has not changed", () => testCounter.CountPresses == 2); + AddAssert($"Check {testKey} count has not changed", () => testCounter.CountPresses.Value == 2); Add(kc); } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplay.cs index c476aae202..542686f0cd 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplay.cs @@ -27,7 +27,7 @@ namespace osu.Game.Tests.Visual.Gameplay protected override void AddCheckSteps() { AddUntilStep("score above zero", () => ((ScoreAccessibleReplayPlayer)Player).ScoreProcessor.TotalScore.Value > 0); - AddUntilStep("key counter counted keys", () => ((ScoreAccessibleReplayPlayer)Player).HUDOverlay.KeyCounter.Children.Any(kc => kc.CountPresses > 0)); + AddUntilStep("key counter counted keys", () => ((ScoreAccessibleReplayPlayer)Player).HUDOverlay.KeyCounter.Children.Any(kc => kc.CountPresses.Value > 0)); AddAssert("cannot fail", () => !((ScoreAccessibleReplayPlayer)Player).AllowFail); } diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneChangeAndUseGameplayBindings.cs b/osu.Game.Tests/Visual/Navigation/TestSceneChangeAndUseGameplayBindings.cs index d937b9e6d7..59a0f9cea8 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneChangeAndUseGameplayBindings.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneChangeAndUseGameplayBindings.cs @@ -69,10 +69,10 @@ namespace osu.Game.Tests.Visual.Navigation AddUntilStep("wait for gameplay", () => player?.IsBreakTime.Value == false); AddStep("press 'z'", () => InputManager.Key(Key.Z)); - AddAssert("key counter didn't increase", () => keyCounter.CountPresses == 0); + AddAssert("key counter didn't increase", () => keyCounter.CountPresses.Value == 0); AddStep("press 's'", () => InputManager.Key(Key.S)); - AddAssert("key counter did increase", () => keyCounter.CountPresses == 1); + AddAssert("key counter did increase", () => keyCounter.CountPresses.Value == 1); } private KeyBindingsSubsection osuBindingSubsection => keyBindingPanel diff --git a/osu.Game/Screens/Play/DefaultKeyCounter.cs b/osu.Game/Screens/Play/DefaultKeyCounter.cs index 93dc4abcb5..52d54b9d4a 100644 --- a/osu.Game/Screens/Play/DefaultKeyCounter.cs +++ b/osu.Game/Screens/Play/DefaultKeyCounter.cs @@ -67,7 +67,7 @@ namespace osu.Game.Screens.Play }, countSpriteText = new OsuSpriteText { - Text = CountPresses.ToString(@"#,0"), + Text = CountPresses.Value.ToString(@"#,0"), Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativePositionAxes = Axes.Both, @@ -83,7 +83,7 @@ namespace osu.Game.Screens.Play Width = buttonSprite.DrawWidth; IsLit.BindValueChanged(e => updateGlowSprite(e.NewValue), true); - PressesCount.BindValueChanged(e => countSpriteText.Text = e.NewValue.ToString(@"#,0"), true); + CountPresses.BindValueChanged(e => countSpriteText.Text = e.NewValue.ToString(@"#,0"), true); } private void updateGlowSprite(bool show) diff --git a/osu.Game/Screens/Play/KeyCounter.cs b/osu.Game/Screens/Play/KeyCounter.cs index 3748792383..23afa97597 100644 --- a/osu.Game/Screens/Play/KeyCounter.cs +++ b/osu.Game/Screens/Play/KeyCounter.cs @@ -12,28 +12,18 @@ namespace osu.Game.Screens.Play { public readonly InputTrigger Trigger; - protected Bindable IsCountingBindable = new BindableBool(true); - private readonly Container content; protected override Container Content => content; - protected Bindable PressesCount = new BindableInt + private readonly Bindable countPresses = new BindableInt { MinValue = 0 }; - public bool IsCounting - { - get => IsCountingBindable.Value; - set => IsCountingBindable.Value = value; - } + public bool IsCounting { get; set; } = true; - public int CountPresses - { - get => PressesCount.Value; - private set => PressesCount.Value = value; - } + public IBindable CountPresses => countPresses; protected KeyCounter(InputTrigger trigger) { @@ -59,7 +49,7 @@ namespace osu.Game.Screens.Play if (!IsCounting) return; - CountPresses++; + countPresses.Value++; } public void Decrement() @@ -67,7 +57,7 @@ namespace osu.Game.Screens.Play if (!IsCounting) return; - CountPresses--; + countPresses.Value--; } protected virtual void LightUp(bool increment = true) From 6cb00cd42f22b7324df66a2863a0fe333875d2f8 Mon Sep 17 00:00:00 2001 From: Cootz Date: Fri, 17 Feb 2023 01:44:45 +0300 Subject: [PATCH 069/476] Add more test cases --- osu.Game.Tests/Database/LegacyExporterTest.cs | 54 ++++++++++++++++--- 1 file changed, 48 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Database/LegacyExporterTest.cs b/osu.Game.Tests/Database/LegacyExporterTest.cs index c464eb5c28..c67ec75e92 100644 --- a/osu.Game.Tests/Database/LegacyExporterTest.cs +++ b/osu.Game.Tests/Database/LegacyExporterTest.cs @@ -22,7 +22,7 @@ namespace osu.Game.Tests.Database } [Test] - public void ExportFileWithNormalName() + public void ExportFileWithNormalNameTest() { var exportStorage = storage?.GetStorageForDirectory(@"exports"); @@ -30,23 +30,65 @@ namespace osu.Game.Tests.Database var item = new TestPathInfo(filename); Assert.That(item.FileName.Length < TestLegacyExporter.GetMaxPath(), Is.True); - Assert.DoesNotThrow(() => legacyExporter?.Export(item)); - Assert.That(exportStorage?.Exists($"{filename}{legacyExporter?.GetExtension()}"), Is.True); + exportItemWithLongNameAndAssert(item, exportStorage, filename); } [Test] - public void ExportFileWithSuperLongName() + public void ExportFileWithNormalNameMultipleTimesTest() + { + var exportStorage = storage?.GetStorageForDirectory(@"exports"); + + const string filename = "normal file name"; + var item = new TestPathInfo(filename); + + Assert.That(item.FileName.Length < TestLegacyExporter.GetMaxPath(), Is.True); + + //Export multiple times + string expectedFileName; + for (int i = 0; i < 10; i++) + { + expectedFileName = i == 0 ? filename : $"{filename} ({i})"; + exportItemWithLongNameAndAssert(item, exportStorage, expectedFileName); + } + } + + [Test] + public void ExportFileWithSuperLongNameTest() { var exportStorage = storage?.GetStorageForDirectory(@"exports"); const string fullname = "some file with super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name"; - int capacity = TestLegacyExporter.GetMaxPath() - (legacyExporter?.GetExtension().Length ?? 0); - string expectedName = fullname.Remove(capacity); + int expectedLength = TestLegacyExporter.GetMaxPath() - (legacyExporter?.GetExtension().Length ?? 0); + string expectedName = fullname.Remove(expectedLength); var item = new TestPathInfo(fullname); Assert.That(item.FileName.Length > TestLegacyExporter.GetMaxPath(), Is.True); + exportItemWithLongNameAndAssert(item, exportStorage, expectedName); + } + + [Test] + public void ExportFileWithSuperLongNameMultipleTimesTest() + { + var exportStorage = storage?.GetStorageForDirectory(@"exports"); + + const string fullname = "some file with super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name"; + + int expectedLength = TestLegacyExporter.GetMaxPath() - (legacyExporter?.GetExtension().Length ?? 0); + string expectedName = fullname.Remove(expectedLength); + + var item = new TestPathInfo(fullname); + + Assert.That(item.FileName.Length > TestLegacyExporter.GetMaxPath(), Is.True); + + //Export multiple times + for (int i = 0; i < 10; i++) + exportItemWithLongNameAndAssert(item, exportStorage, expectedName); + } + + private void exportItemWithLongNameAndAssert(IHasNamedFiles item, Storage? exportStorage, string expectedName) + { Assert.DoesNotThrow(() => legacyExporter?.Export(item)); Assert.That(exportStorage?.Exists($"{expectedName}{legacyExporter?.GetExtension()}"), Is.True); } From 1d8b348e4c72d29d44ce0f56d9d6e71f98000187 Mon Sep 17 00:00:00 2001 From: Cootz Date: Fri, 17 Feb 2023 01:46:15 +0300 Subject: [PATCH 070/476] Improve naming --- osu.Game.Tests/Database/LegacyExporterTest.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Database/LegacyExporterTest.cs b/osu.Game.Tests/Database/LegacyExporterTest.cs index c67ec75e92..97b32b0c51 100644 --- a/osu.Game.Tests/Database/LegacyExporterTest.cs +++ b/osu.Game.Tests/Database/LegacyExporterTest.cs @@ -30,7 +30,7 @@ namespace osu.Game.Tests.Database var item = new TestPathInfo(filename); Assert.That(item.FileName.Length < TestLegacyExporter.GetMaxPath(), Is.True); - exportItemWithLongNameAndAssert(item, exportStorage, filename); + exportItemAndAssert(item, exportStorage, filename); } [Test] @@ -48,7 +48,7 @@ namespace osu.Game.Tests.Database for (int i = 0; i < 10; i++) { expectedFileName = i == 0 ? filename : $"{filename} ({i})"; - exportItemWithLongNameAndAssert(item, exportStorage, expectedFileName); + exportItemAndAssert(item, exportStorage, expectedFileName); } } @@ -65,7 +65,7 @@ namespace osu.Game.Tests.Database var item = new TestPathInfo(fullname); Assert.That(item.FileName.Length > TestLegacyExporter.GetMaxPath(), Is.True); - exportItemWithLongNameAndAssert(item, exportStorage, expectedName); + exportItemAndAssert(item, exportStorage, expectedName); } [Test] @@ -84,10 +84,10 @@ namespace osu.Game.Tests.Database //Export multiple times for (int i = 0; i < 10; i++) - exportItemWithLongNameAndAssert(item, exportStorage, expectedName); + exportItemAndAssert(item, exportStorage, expectedName); } - private void exportItemWithLongNameAndAssert(IHasNamedFiles item, Storage? exportStorage, string expectedName) + private void exportItemAndAssert(IHasNamedFiles item, Storage? exportStorage, string expectedName) { Assert.DoesNotThrow(() => legacyExporter?.Export(item)); Assert.That(exportStorage?.Exists($"{expectedName}{legacyExporter?.GetExtension()}"), Is.True); From f4038a49a17f8670f3b3ed8613e12d197d4e96b6 Mon Sep 17 00:00:00 2001 From: Cootz Date: Fri, 17 Feb 2023 01:50:24 +0300 Subject: [PATCH 071/476] Fix inspectCode issues --- osu.Game.Tests/Database/LegacyExporterTest.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Tests/Database/LegacyExporterTest.cs b/osu.Game.Tests/Database/LegacyExporterTest.cs index 97b32b0c51..d67cb8abf1 100644 --- a/osu.Game.Tests/Database/LegacyExporterTest.cs +++ b/osu.Game.Tests/Database/LegacyExporterTest.cs @@ -44,10 +44,9 @@ namespace osu.Game.Tests.Database Assert.That(item.FileName.Length < TestLegacyExporter.GetMaxPath(), Is.True); //Export multiple times - string expectedFileName; for (int i = 0; i < 10; i++) { - expectedFileName = i == 0 ? filename : $"{filename} ({i})"; + string expectedFileName = i == 0 ? filename : $"{filename} ({i})"; exportItemAndAssert(item, exportStorage, expectedFileName); } } From c61fac578ca53fa4dba22ac7ec85aa0cc335c762 Mon Sep 17 00:00:00 2001 From: tsrk Date: Thu, 16 Feb 2023 23:15:03 +0000 Subject: [PATCH 072/476] style(KeyCounter): rename methods and arguments As for the second suggestion in https://github.com/ppy/osu/pull/22654#discussion_r1109047998, I went with the first one as only one Trigger actually uses this argument for rewinding. --- .../TestSceneOsuTouchInput.cs | 4 ++-- osu.Game/Screens/Play/KeyCounter.cs | 20 +++++++++---------- .../Screens/Play/KeyCounterActionTrigger.cs | 4 ++-- .../Screens/Play/KeyCounterKeyboardTrigger.cs | 4 ++-- .../Screens/Play/KeyCounterMouseTrigger.cs | 4 ++-- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs index c73025ebb9..8a933c6b24 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs @@ -593,7 +593,7 @@ namespace osu.Game.Rulesets.Osu.Tests { if (e.Action == Action) { - LightUp(); + Activate(); } return false; @@ -602,7 +602,7 @@ namespace osu.Game.Rulesets.Osu.Tests public void OnReleased(KeyBindingReleaseEvent e) { if (e.Action == Action) - Unlight(); + Deactivate(); } } diff --git a/osu.Game/Screens/Play/KeyCounter.cs b/osu.Game/Screens/Play/KeyCounter.cs index 23afa97597..a276c9d59e 100644 --- a/osu.Game/Screens/Play/KeyCounter.cs +++ b/osu.Game/Screens/Play/KeyCounter.cs @@ -36,8 +36,8 @@ namespace osu.Game.Screens.Play Trigger = trigger, }; - Trigger.OnLightUp += LightUp; - Trigger.OnUnlight += Unlight; + Trigger.OnActivate += Activate; + Trigger.OnDeactivate += Deactivate; Name = trigger.Name; } @@ -60,14 +60,14 @@ namespace osu.Game.Screens.Play countPresses.Value--; } - protected virtual void LightUp(bool increment = true) + protected virtual void Activate(bool increment = true) { IsLit.Value = true; if (increment) Increment(); } - protected virtual void Unlight(bool preserve = true) + protected virtual void Deactivate(bool preserve = true) { IsLit.Value = false; if (!preserve) @@ -77,23 +77,23 @@ namespace osu.Game.Screens.Play protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); - Trigger.OnLightUp -= LightUp; - Trigger.OnUnlight -= Unlight; + Trigger.OnActivate -= Activate; + Trigger.OnDeactivate -= Deactivate; } public abstract partial class InputTrigger : Component { - public event Action? OnLightUp; - public event Action? OnUnlight; + public event Action? OnActivate; + public event Action? OnDeactivate; protected InputTrigger(string name) { Name = name; } - protected void LightUp(bool increment = true) => OnLightUp?.Invoke(increment); + protected void Activate(bool forwardPlayback = true) => OnActivate?.Invoke(forwardPlayback); - protected void Unlight(bool preserve = true) => OnUnlight?.Invoke(preserve); + protected void Deactivate(bool forwardPlayback = true) => OnDeactivate?.Invoke(forwardPlayback); } } } diff --git a/osu.Game/Screens/Play/KeyCounterActionTrigger.cs b/osu.Game/Screens/Play/KeyCounterActionTrigger.cs index c6acb3f95f..8bb9bdc886 100644 --- a/osu.Game/Screens/Play/KeyCounterActionTrigger.cs +++ b/osu.Game/Screens/Play/KeyCounterActionTrigger.cs @@ -23,7 +23,7 @@ namespace osu.Game.Screens.Play if (!EqualityComparer.Default.Equals(action, Action)) return false; - LightUp(forwards); + Activate(forwards); return false; } @@ -32,7 +32,7 @@ namespace osu.Game.Screens.Play if (!EqualityComparer.Default.Equals(action, Action)) return; - Unlight(forwards); + Deactivate(forwards); } } } diff --git a/osu.Game/Screens/Play/KeyCounterKeyboardTrigger.cs b/osu.Game/Screens/Play/KeyCounterKeyboardTrigger.cs index 18eb6b7612..56c5ab0083 100644 --- a/osu.Game/Screens/Play/KeyCounterKeyboardTrigger.cs +++ b/osu.Game/Screens/Play/KeyCounterKeyboardTrigger.cs @@ -22,7 +22,7 @@ namespace osu.Game.Screens.Play { if (e.Key == Key) { - LightUp(); + Activate(); } return base.OnKeyDown(e); @@ -31,7 +31,7 @@ namespace osu.Game.Screens.Play protected override void OnKeyUp(KeyUpEvent e) { if (e.Key == Key) - Unlight(); + Deactivate(); base.OnKeyUp(e); } diff --git a/osu.Game/Screens/Play/KeyCounterMouseTrigger.cs b/osu.Game/Screens/Play/KeyCounterMouseTrigger.cs index 1446494b5b..66890073a8 100644 --- a/osu.Game/Screens/Play/KeyCounterMouseTrigger.cs +++ b/osu.Game/Screens/Play/KeyCounterMouseTrigger.cs @@ -39,7 +39,7 @@ namespace osu.Game.Screens.Play protected override bool OnMouseDown(MouseDownEvent e) { if (e.Button == Button) - LightUp(); + Activate(); return base.OnMouseDown(e); } @@ -47,7 +47,7 @@ namespace osu.Game.Screens.Play protected override void OnMouseUp(MouseUpEvent e) { if (e.Button == Button) - Unlight(); + Deactivate(); base.OnMouseUp(e); } From e3ca751027af079ac74e73ad7e88d59c8dc82d24 Mon Sep 17 00:00:00 2001 From: tsrk Date: Thu, 16 Feb 2023 23:17:47 +0000 Subject: [PATCH 073/476] refactor: make `FillFlowContainer` read-only --- osu.Game/Screens/Play/KeyCounterDisplay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/KeyCounterDisplay.cs b/osu.Game/Screens/Play/KeyCounterDisplay.cs index ed47af11a3..0f2f8e43c9 100644 --- a/osu.Game/Screens/Play/KeyCounterDisplay.cs +++ b/osu.Game/Screens/Play/KeyCounterDisplay.cs @@ -17,7 +17,7 @@ namespace osu.Game.Screens.Play { protected readonly Bindable ConfigVisibility = new Bindable(); - protected FillFlowContainer KeyFlow = new FillFlowContainer(); + protected readonly FillFlowContainer KeyFlow = new FillFlowContainer(); protected override Container Content => KeyFlow; From 6193aeed120f2bc85768a47efafa21b95695119e Mon Sep 17 00:00:00 2001 From: tsrk Date: Fri, 17 Feb 2023 00:13:45 +0000 Subject: [PATCH 074/476] fix(TestSceneOsuTouchInput): missing Value call --- osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs index 8a933c6b24..4cb017cc56 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs @@ -562,8 +562,8 @@ namespace osu.Game.Rulesets.Osu.Tests private void assertKeyCounter(int left, int right) { - AddAssert($"The left key was pressed {left} times", () => leftKeyCounter.CountPresses, () => Is.EqualTo(left)); - AddAssert($"The right key was pressed {right} times", () => rightKeyCounter.CountPresses, () => Is.EqualTo(right)); + AddAssert($"The left key was pressed {left} times", () => leftKeyCounter.CountPresses.Value, () => Is.EqualTo(left)); + AddAssert($"The right key was pressed {right} times", () => rightKeyCounter.CountPresses.Value, () => Is.EqualTo(right)); } private void releaseAllTouches() From d0e8d65766df488c98bf99ccdef7c05df3f694d8 Mon Sep 17 00:00:00 2001 From: tsrk Date: Fri, 17 Feb 2023 00:40:01 +0000 Subject: [PATCH 075/476] style(KeyCounter): rename `IsLit` to `IsActive` --- osu.Game/Screens/Play/DefaultKeyCounter.cs | 2 +- osu.Game/Screens/Play/KeyCounter.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/DefaultKeyCounter.cs b/osu.Game/Screens/Play/DefaultKeyCounter.cs index 52d54b9d4a..3673281577 100644 --- a/osu.Game/Screens/Play/DefaultKeyCounter.cs +++ b/osu.Game/Screens/Play/DefaultKeyCounter.cs @@ -82,7 +82,7 @@ namespace osu.Game.Screens.Play Height = buttonSprite.DrawHeight; Width = buttonSprite.DrawWidth; - IsLit.BindValueChanged(e => updateGlowSprite(e.NewValue), true); + IsActive.BindValueChanged(e => updateGlowSprite(e.NewValue), true); CountPresses.BindValueChanged(e => countSpriteText.Text = e.NewValue.ToString(@"#,0"), true); } diff --git a/osu.Game/Screens/Play/KeyCounter.cs b/osu.Game/Screens/Play/KeyCounter.cs index a276c9d59e..212843cbe9 100644 --- a/osu.Game/Screens/Play/KeyCounter.cs +++ b/osu.Game/Screens/Play/KeyCounter.cs @@ -42,7 +42,7 @@ namespace osu.Game.Screens.Play Name = trigger.Name; } - protected Bindable IsLit = new BindableBool(); + protected Bindable IsActive = new BindableBool(); public void Increment() { @@ -62,14 +62,14 @@ namespace osu.Game.Screens.Play protected virtual void Activate(bool increment = true) { - IsLit.Value = true; + IsActive.Value = true; if (increment) Increment(); } protected virtual void Deactivate(bool preserve = true) { - IsLit.Value = false; + IsActive.Value = false; if (!preserve) Decrement(); } From 415220a44769fa8a7027ec8d56eb38b63fac6e04 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Feb 2023 13:30:00 +0900 Subject: [PATCH 076/476] Tidy up new test method code quality --- osu.Game.Tests/Database/LegacyExporterTest.cs | 55 +++++++++++-------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/osu.Game.Tests/Database/LegacyExporterTest.cs b/osu.Game.Tests/Database/LegacyExporterTest.cs index d67cb8abf1..803cd40132 100644 --- a/osu.Game.Tests/Database/LegacyExporterTest.cs +++ b/osu.Game.Tests/Database/LegacyExporterTest.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using NUnit.Framework; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Platform; using osu.Framework.Testing; using osu.Game.Database; @@ -11,11 +12,11 @@ namespace osu.Game.Tests.Database { public class LegacyExporterTest { - private TestLegacyExporter? legacyExporter; - private TemporaryNativeStorage? storage; + private TestLegacyExporter legacyExporter = null!; + private TemporaryNativeStorage storage = null!; [SetUp] - public void SetupLegacyExporter() + public void SetUp() { storage = new TemporaryNativeStorage("export-storage"); legacyExporter = new TestLegacyExporter(storage); @@ -24,24 +25,24 @@ namespace osu.Game.Tests.Database [Test] public void ExportFileWithNormalNameTest() { - var exportStorage = storage?.GetStorageForDirectory(@"exports"); + var exportStorage = storage.GetStorageForDirectory(@"exports"); const string filename = "normal file name"; var item = new TestPathInfo(filename); - Assert.That(item.FileName.Length < TestLegacyExporter.GetMaxPath(), Is.True); + Assert.That(item.Filename.Length < TestLegacyExporter.GetMaxPath(), Is.True); exportItemAndAssert(item, exportStorage, filename); } [Test] public void ExportFileWithNormalNameMultipleTimesTest() { - var exportStorage = storage?.GetStorageForDirectory(@"exports"); + var exportStorage = storage.GetStorageForDirectory(@"exports"); const string filename = "normal file name"; var item = new TestPathInfo(filename); - Assert.That(item.FileName.Length < TestLegacyExporter.GetMaxPath(), Is.True); + Assert.That(item.Filename.Length < TestLegacyExporter.GetMaxPath(), Is.True); //Export multiple times for (int i = 0; i < 10; i++) @@ -54,59 +55,65 @@ namespace osu.Game.Tests.Database [Test] public void ExportFileWithSuperLongNameTest() { - var exportStorage = storage?.GetStorageForDirectory(@"exports"); + var exportStorage = storage.GetStorageForDirectory(@"exports"); - const string fullname = "some file with super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name"; + const string fullname = + "some file with super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name"; - int expectedLength = TestLegacyExporter.GetMaxPath() - (legacyExporter?.GetExtension().Length ?? 0); + int expectedLength = TestLegacyExporter.GetMaxPath() - (legacyExporter.GetExtension().Length); string expectedName = fullname.Remove(expectedLength); var item = new TestPathInfo(fullname); - Assert.That(item.FileName.Length > TestLegacyExporter.GetMaxPath(), Is.True); + Assert.That(item.Filename.Length > TestLegacyExporter.GetMaxPath(), Is.True); exportItemAndAssert(item, exportStorage, expectedName); } [Test] public void ExportFileWithSuperLongNameMultipleTimesTest() { - var exportStorage = storage?.GetStorageForDirectory(@"exports"); + var exportStorage = storage.GetStorageForDirectory(@"exports"); - const string fullname = "some file with super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name"; + const string fullname = + "some file with super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name"; - int expectedLength = TestLegacyExporter.GetMaxPath() - (legacyExporter?.GetExtension().Length ?? 0); + int expectedLength = TestLegacyExporter.GetMaxPath() - (legacyExporter.GetExtension().Length); string expectedName = fullname.Remove(expectedLength); var item = new TestPathInfo(fullname); - Assert.That(item.FileName.Length > TestLegacyExporter.GetMaxPath(), Is.True); + Assert.That(item.Filename.Length > TestLegacyExporter.GetMaxPath(), Is.True); //Export multiple times for (int i = 0; i < 10; i++) exportItemAndAssert(item, exportStorage, expectedName); } - private void exportItemAndAssert(IHasNamedFiles item, Storage? exportStorage, string expectedName) + private void exportItemAndAssert(IHasNamedFiles item, Storage exportStorage, string expectedName) { - Assert.DoesNotThrow(() => legacyExporter?.Export(item)); - Assert.That(exportStorage?.Exists($"{expectedName}{legacyExporter?.GetExtension()}"), Is.True); + Assert.DoesNotThrow(() => legacyExporter.Export(item)); + Assert.That(exportStorage.Exists($"{expectedName}{legacyExporter.GetExtension()}"), Is.True); } [TearDown] - public void CleanupAfterTest() + public void TearDown() { - storage?.Dispose(); + if (storage.IsNotNull()) + storage.Dispose(); } private class TestPathInfo : IHasNamedFiles { - public string FileName { get; set; } + public string Filename { get; } - public TestPathInfo(string fileName) => FileName = fileName; + public IEnumerable Files { get; } = new List(); - public IEnumerable Files { get; set; } = new List(); + public TestPathInfo(string filename) + { + Filename = filename; + } - public override string ToString() => FileName; + public override string ToString() => Filename; } private class TestLegacyExporter : LegacyExporter From 96b1498932c4b93126a477cf1acc1b93d11c1b57 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Feb 2023 13:33:22 +0900 Subject: [PATCH 077/476] Rename max length variable to make sense (it's a filename limit, not path) --- osu.Game.Tests/Database/LegacyExporterTest.cs | 14 +++++++------- osu.Game/Database/LegacyExporter.cs | 12 +++++++----- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/osu.Game.Tests/Database/LegacyExporterTest.cs b/osu.Game.Tests/Database/LegacyExporterTest.cs index 803cd40132..6f144ee833 100644 --- a/osu.Game.Tests/Database/LegacyExporterTest.cs +++ b/osu.Game.Tests/Database/LegacyExporterTest.cs @@ -30,7 +30,7 @@ namespace osu.Game.Tests.Database const string filename = "normal file name"; var item = new TestPathInfo(filename); - Assert.That(item.Filename.Length < TestLegacyExporter.GetMaxPath(), Is.True); + Assert.That(item.Filename.Length, Is.LessThan(TestLegacyExporter.GetMaxPathLength())); exportItemAndAssert(item, exportStorage, filename); } @@ -42,7 +42,7 @@ namespace osu.Game.Tests.Database const string filename = "normal file name"; var item = new TestPathInfo(filename); - Assert.That(item.Filename.Length < TestLegacyExporter.GetMaxPath(), Is.True); + Assert.That(item.Filename.Length < TestLegacyExporter.GetMaxPathLength(), Is.True); //Export multiple times for (int i = 0; i < 10; i++) @@ -60,12 +60,12 @@ namespace osu.Game.Tests.Database const string fullname = "some file with super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name"; - int expectedLength = TestLegacyExporter.GetMaxPath() - (legacyExporter.GetExtension().Length); + int expectedLength = TestLegacyExporter.GetMaxPathLength() - (legacyExporter.GetExtension().Length); string expectedName = fullname.Remove(expectedLength); var item = new TestPathInfo(fullname); - Assert.That(item.Filename.Length > TestLegacyExporter.GetMaxPath(), Is.True); + Assert.That(item.Filename.Length > TestLegacyExporter.GetMaxPathLength(), Is.True); exportItemAndAssert(item, exportStorage, expectedName); } @@ -77,12 +77,12 @@ namespace osu.Game.Tests.Database const string fullname = "some file with super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name"; - int expectedLength = TestLegacyExporter.GetMaxPath() - (legacyExporter.GetExtension().Length); + int expectedLength = TestLegacyExporter.GetMaxPathLength() - (legacyExporter.GetExtension().Length); string expectedName = fullname.Remove(expectedLength); var item = new TestPathInfo(fullname); - Assert.That(item.Filename.Length > TestLegacyExporter.GetMaxPath(), Is.True); + Assert.That(item.Filename.Length > TestLegacyExporter.GetMaxPathLength(), Is.True); //Export multiple times for (int i = 0; i < 10; i++) @@ -123,7 +123,7 @@ namespace osu.Game.Tests.Database { } - public static int GetMaxPath() => MAX_PATH; + public static int GetMaxPathLength() => MAX_FILENAME_LENGTH; public string GetExtension() => FileExtension; diff --git a/osu.Game/Database/LegacyExporter.cs b/osu.Game/Database/LegacyExporter.cs index 9041f58368..1b74454027 100644 --- a/osu.Game/Database/LegacyExporter.cs +++ b/osu.Game/Database/LegacyExporter.cs @@ -21,12 +21,14 @@ namespace osu.Game.Database where TModel : class, IHasNamedFiles { /// - /// Max length of filename (including extension) + /// Max length of filename (including extension). /// /// - /// This constant is smaller 256 because adds additional "_" to the end of the path + /// TODO: WHY? DOCUMENTATION PER PLATFORM LINKS HERE. + /// + /// This actual usable length is smaller 256 because adds additional "_" to the end of the path /// - protected const int MAX_PATH = 255 - (32 + 4 + 2); //max path - (Guid + Guid "D" format chars + Storage.CreateFileSafely chars) + protected const int MAX_FILENAME_LENGTH = 255 - (32 + 4 + 2); //max path - (Guid + Guid "D" format chars + Storage.CreateFileSafely chars) /// /// The file extension for exports (including the leading '.'). @@ -60,11 +62,11 @@ namespace osu.Game.Database string filename = NamingUtils.GetNextBestFilename(existingExports, $"{itemFilename}{FileExtension}"); - if (filename.Length > MAX_PATH) + if (filename.Length > MAX_FILENAME_LENGTH) { string filenameWithoutExtension = Path.GetFileNameWithoutExtension(filename); - filenameWithoutExtension = filenameWithoutExtension.Remove(MAX_PATH - FileExtension.Length); //Truncating the name to fit the path limit + filenameWithoutExtension = filenameWithoutExtension.Remove(MAX_FILENAME_LENGTH - FileExtension.Length); //Truncating the name to fit the path limit filename = $"{filenameWithoutExtension}{FileExtension}"; } From 8c772a723f0f8ae52cf3141fab286c64c2eba198 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Feb 2023 13:34:19 +0900 Subject: [PATCH 078/476] Expose constant `public`ly rather than reexposing business --- osu.Game/Database/LegacyExporter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/LegacyExporter.cs b/osu.Game/Database/LegacyExporter.cs index 1b74454027..7434a46602 100644 --- a/osu.Game/Database/LegacyExporter.cs +++ b/osu.Game/Database/LegacyExporter.cs @@ -28,7 +28,7 @@ namespace osu.Game.Database /// /// This actual usable length is smaller 256 because adds additional "_" to the end of the path /// - protected const int MAX_FILENAME_LENGTH = 255 - (32 + 4 + 2); //max path - (Guid + Guid "D" format chars + Storage.CreateFileSafely chars) + public const int MAX_FILENAME_LENGTH = 255 - (32 + 4 + 2); //max path - (Guid + Guid "D" format chars + Storage.CreateFileSafely chars) /// /// The file extension for exports (including the leading '.'). From 99236f0ae80399d9fade78c7331dff63d59301e4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Feb 2023 13:39:24 +0900 Subject: [PATCH 079/476] Move long filename to fixture level --- osu.Game.Tests/Database/LegacyExporterTest.cs | 31 ++++++++----------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/osu.Game.Tests/Database/LegacyExporterTest.cs b/osu.Game.Tests/Database/LegacyExporterTest.cs index 6f144ee833..114872bcf8 100644 --- a/osu.Game.Tests/Database/LegacyExporterTest.cs +++ b/osu.Game.Tests/Database/LegacyExporterTest.cs @@ -15,6 +15,9 @@ namespace osu.Game.Tests.Database private TestLegacyExporter legacyExporter = null!; private TemporaryNativeStorage storage = null!; + private const string long_filename = + "some file with super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name"; + [SetUp] public void SetUp() { @@ -30,7 +33,7 @@ namespace osu.Game.Tests.Database const string filename = "normal file name"; var item = new TestPathInfo(filename); - Assert.That(item.Filename.Length, Is.LessThan(TestLegacyExporter.GetMaxPathLength())); + Assert.That(item.Filename.Length, Is.LessThan(TestLegacyExporter.MAX_FILENAME_LENGTH)); exportItemAndAssert(item, exportStorage, filename); } @@ -42,7 +45,7 @@ namespace osu.Game.Tests.Database const string filename = "normal file name"; var item = new TestPathInfo(filename); - Assert.That(item.Filename.Length < TestLegacyExporter.GetMaxPathLength(), Is.True); + Assert.That(item.Filename.Length < TestLegacyExporter.MAX_FILENAME_LENGTH, Is.True); //Export multiple times for (int i = 0; i < 10; i++) @@ -57,15 +60,12 @@ namespace osu.Game.Tests.Database { var exportStorage = storage.GetStorageForDirectory(@"exports"); - const string fullname = - "some file with super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name"; + int expectedLength = TestLegacyExporter.MAX_FILENAME_LENGTH - (legacyExporter.GetExtension().Length); + string expectedName = long_filename.Remove(expectedLength); - int expectedLength = TestLegacyExporter.GetMaxPathLength() - (legacyExporter.GetExtension().Length); - string expectedName = fullname.Remove(expectedLength); + var item = new TestPathInfo(long_filename); - var item = new TestPathInfo(fullname); - - Assert.That(item.Filename.Length > TestLegacyExporter.GetMaxPathLength(), Is.True); + Assert.That(item.Filename.Length > TestLegacyExporter.MAX_FILENAME_LENGTH, Is.True); exportItemAndAssert(item, exportStorage, expectedName); } @@ -74,15 +74,12 @@ namespace osu.Game.Tests.Database { var exportStorage = storage.GetStorageForDirectory(@"exports"); - const string fullname = - "some file with super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name"; + int expectedLength = TestLegacyExporter.MAX_FILENAME_LENGTH - (legacyExporter.GetExtension().Length); + string expectedName = long_filename.Remove(expectedLength); - int expectedLength = TestLegacyExporter.GetMaxPathLength() - (legacyExporter.GetExtension().Length); - string expectedName = fullname.Remove(expectedLength); + var item = new TestPathInfo(long_filename); - var item = new TestPathInfo(fullname); - - Assert.That(item.Filename.Length > TestLegacyExporter.GetMaxPathLength(), Is.True); + Assert.That(item.Filename.Length > TestLegacyExporter.MAX_FILENAME_LENGTH, Is.True); //Export multiple times for (int i = 0; i < 10; i++) @@ -123,8 +120,6 @@ namespace osu.Game.Tests.Database { } - public static int GetMaxPathLength() => MAX_FILENAME_LENGTH; - public string GetExtension() => FileExtension; protected override string FileExtension => ".ots"; From 4560ae6b026bd1ce575713ccab7d9052f73af40b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Feb 2023 13:39:31 +0900 Subject: [PATCH 080/476] Mark test as fixture --- osu.Game.Tests/Database/LegacyExporterTest.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Database/LegacyExporterTest.cs b/osu.Game.Tests/Database/LegacyExporterTest.cs index 114872bcf8..ec20028a2c 100644 --- a/osu.Game.Tests/Database/LegacyExporterTest.cs +++ b/osu.Game.Tests/Database/LegacyExporterTest.cs @@ -10,6 +10,7 @@ using osu.Game.Database; namespace osu.Game.Tests.Database { + [TestFixture] public class LegacyExporterTest { private TestLegacyExporter legacyExporter = null!; From 86d110e893cd2572377c575fc67e5260a1199e27 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Feb 2023 13:43:42 +0900 Subject: [PATCH 081/476] Simplify test storage by removing nested storage --- osu.Game.Tests/Database/LegacyExporterTest.cs | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Database/LegacyExporterTest.cs b/osu.Game.Tests/Database/LegacyExporterTest.cs index ec20028a2c..f38ffb7db6 100644 --- a/osu.Game.Tests/Database/LegacyExporterTest.cs +++ b/osu.Game.Tests/Database/LegacyExporterTest.cs @@ -29,20 +29,16 @@ namespace osu.Game.Tests.Database [Test] public void ExportFileWithNormalNameTest() { - var exportStorage = storage.GetStorageForDirectory(@"exports"); - const string filename = "normal file name"; var item = new TestPathInfo(filename); Assert.That(item.Filename.Length, Is.LessThan(TestLegacyExporter.MAX_FILENAME_LENGTH)); - exportItemAndAssert(item, exportStorage, filename); + exportItemAndAssert(item, filename); } [Test] public void ExportFileWithNormalNameMultipleTimesTest() { - var exportStorage = storage.GetStorageForDirectory(@"exports"); - const string filename = "normal file name"; var item = new TestPathInfo(filename); @@ -52,29 +48,25 @@ namespace osu.Game.Tests.Database for (int i = 0; i < 10; i++) { string expectedFileName = i == 0 ? filename : $"{filename} ({i})"; - exportItemAndAssert(item, exportStorage, expectedFileName); + exportItemAndAssert(item, expectedFileName); } } [Test] public void ExportFileWithSuperLongNameTest() { - var exportStorage = storage.GetStorageForDirectory(@"exports"); - int expectedLength = TestLegacyExporter.MAX_FILENAME_LENGTH - (legacyExporter.GetExtension().Length); string expectedName = long_filename.Remove(expectedLength); var item = new TestPathInfo(long_filename); Assert.That(item.Filename.Length > TestLegacyExporter.MAX_FILENAME_LENGTH, Is.True); - exportItemAndAssert(item, exportStorage, expectedName); + exportItemAndAssert(item, expectedName); } [Test] public void ExportFileWithSuperLongNameMultipleTimesTest() { - var exportStorage = storage.GetStorageForDirectory(@"exports"); - int expectedLength = TestLegacyExporter.MAX_FILENAME_LENGTH - (legacyExporter.GetExtension().Length); string expectedName = long_filename.Remove(expectedLength); @@ -84,13 +76,13 @@ namespace osu.Game.Tests.Database //Export multiple times for (int i = 0; i < 10; i++) - exportItemAndAssert(item, exportStorage, expectedName); + exportItemAndAssert(item, expectedName); } - private void exportItemAndAssert(IHasNamedFiles item, Storage exportStorage, string expectedName) + private void exportItemAndAssert(IHasNamedFiles item, string expectedName) { Assert.DoesNotThrow(() => legacyExporter.Export(item)); - Assert.That(exportStorage.Exists($"{expectedName}{legacyExporter.GetExtension()}"), Is.True); + Assert.That(storage.Exists($"exports/{expectedName}{legacyExporter.GetExtension()}"), Is.True); } [TearDown] From 8ef3fb26e056b2c271a690be5539983f6d9d09b0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Feb 2023 13:45:27 +0900 Subject: [PATCH 082/476] More constants and assert fixes --- osu.Game.Tests/Database/LegacyExporterTest.cs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Database/LegacyExporterTest.cs b/osu.Game.Tests/Database/LegacyExporterTest.cs index f38ffb7db6..b8dca6dc7b 100644 --- a/osu.Game.Tests/Database/LegacyExporterTest.cs +++ b/osu.Game.Tests/Database/LegacyExporterTest.cs @@ -16,6 +16,8 @@ namespace osu.Game.Tests.Database private TestLegacyExporter legacyExporter = null!; private TemporaryNativeStorage storage = null!; + private const string short_filename = "normal file name"; + private const string long_filename = "some file with super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name super long name"; @@ -29,25 +31,24 @@ namespace osu.Game.Tests.Database [Test] public void ExportFileWithNormalNameTest() { - const string filename = "normal file name"; - var item = new TestPathInfo(filename); + var item = new TestPathInfo(short_filename); Assert.That(item.Filename.Length, Is.LessThan(TestLegacyExporter.MAX_FILENAME_LENGTH)); - exportItemAndAssert(item, filename); + + exportItemAndAssert(item, short_filename); } [Test] public void ExportFileWithNormalNameMultipleTimesTest() { - const string filename = "normal file name"; - var item = new TestPathInfo(filename); + var item = new TestPathInfo(short_filename); - Assert.That(item.Filename.Length < TestLegacyExporter.MAX_FILENAME_LENGTH, Is.True); + Assert.That(item.Filename.Length, Is.LessThan(TestLegacyExporter.MAX_FILENAME_LENGTH)); //Export multiple times for (int i = 0; i < 10; i++) { - string expectedFileName = i == 0 ? filename : $"{filename} ({i})"; + string expectedFileName = i == 0 ? short_filename : $"{short_filename} ({i})"; exportItemAndAssert(item, expectedFileName); } } @@ -60,7 +61,7 @@ namespace osu.Game.Tests.Database var item = new TestPathInfo(long_filename); - Assert.That(item.Filename.Length > TestLegacyExporter.MAX_FILENAME_LENGTH, Is.True); + Assert.That(item.Filename.Length, Is.GreaterThan(TestLegacyExporter.MAX_FILENAME_LENGTH)); exportItemAndAssert(item, expectedName); } @@ -72,7 +73,7 @@ namespace osu.Game.Tests.Database var item = new TestPathInfo(long_filename); - Assert.That(item.Filename.Length > TestLegacyExporter.MAX_FILENAME_LENGTH, Is.True); + Assert.That(item.Filename.Length, Is.GreaterThan(TestLegacyExporter.MAX_FILENAME_LENGTH)); //Export multiple times for (int i = 0; i < 10; i++) From 372b6b794cae69dc16a882f0caa3b91dcfef0491 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Feb 2023 13:45:43 +0900 Subject: [PATCH 083/476] I don't know what .ots is but let's not use random file extension that make no sense --- osu.Game.Tests/Database/LegacyExporterTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Database/LegacyExporterTest.cs b/osu.Game.Tests/Database/LegacyExporterTest.cs index b8dca6dc7b..c9aea80585 100644 --- a/osu.Game.Tests/Database/LegacyExporterTest.cs +++ b/osu.Game.Tests/Database/LegacyExporterTest.cs @@ -116,7 +116,7 @@ namespace osu.Game.Tests.Database public string GetExtension() => FileExtension; - protected override string FileExtension => ".ots"; + protected override string FileExtension => ".test"; } } } From c94e647e21902db2fc98bc3a42ad1b2a75246842 Mon Sep 17 00:00:00 2001 From: tsrk Date: Fri, 17 Feb 2023 09:09:56 +0000 Subject: [PATCH 084/476] style(KeyCounterDisplay): remove type check --- osu.Game/Screens/Play/KeyCounterDisplay.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/osu.Game/Screens/Play/KeyCounterDisplay.cs b/osu.Game/Screens/Play/KeyCounterDisplay.cs index 0f2f8e43c9..d2b50ff73d 100644 --- a/osu.Game/Screens/Play/KeyCounterDisplay.cs +++ b/osu.Game/Screens/Play/KeyCounterDisplay.cs @@ -29,15 +29,10 @@ namespace osu.Game.Screens.Play public override void Add(KeyCounter key) { - if (!CheckType(key)) - throw new ArgumentException($"{key.GetType()} is not a supported {nameof(KeyCounter)}.", nameof(key)); - base.Add(key); key.IsCounting = IsCounting; } - protected virtual bool CheckType(KeyCounter key) => true; - [BackgroundDependencyLoader] private void load(OsuConfigManager config) { From 8830e0658834095dd87e3d9b82b425528021b93a Mon Sep 17 00:00:00 2001 From: tsrk Date: Fri, 17 Feb 2023 09:17:11 +0000 Subject: [PATCH 085/476] fix: compilation --- osu.Game/Screens/Play/DefaultKeyCounterDisplay.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Screens/Play/DefaultKeyCounterDisplay.cs b/osu.Game/Screens/Play/DefaultKeyCounterDisplay.cs index b69ecfd7ae..fbf1b87395 100644 --- a/osu.Game/Screens/Play/DefaultKeyCounterDisplay.cs +++ b/osu.Game/Screens/Play/DefaultKeyCounterDisplay.cs @@ -49,8 +49,6 @@ namespace osu.Game.Screens.Play defaultKey.KeyUpTextColor = KeyUpTextColor; } - protected override bool CheckType(KeyCounter key) => key is DefaultKeyCounter; - protected override void UpdateVisibility() => // Isolate changing visibility of the key counters from fading this component. KeyFlow.FadeTo(AlwaysVisible.Value || ConfigVisibility.Value ? 1 : 0, duration); From ceed3606cd7a3b0666c345ea768219ac5e65b91d Mon Sep 17 00:00:00 2001 From: Cootz <50776304+Cootz@users.noreply.github.com> Date: Fri, 17 Feb 2023 13:46:06 +0300 Subject: [PATCH 086/476] Remove redundant comment Co-authored-by: Dean Herbert --- osu.Game/Database/LegacyExporter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/LegacyExporter.cs b/osu.Game/Database/LegacyExporter.cs index 7434a46602..7fe0428db3 100644 --- a/osu.Game/Database/LegacyExporter.cs +++ b/osu.Game/Database/LegacyExporter.cs @@ -66,7 +66,7 @@ namespace osu.Game.Database { string filenameWithoutExtension = Path.GetFileNameWithoutExtension(filename); - filenameWithoutExtension = filenameWithoutExtension.Remove(MAX_FILENAME_LENGTH - FileExtension.Length); //Truncating the name to fit the path limit + filenameWithoutExtension = filenameWithoutExtension.Remove(MAX_FILENAME_LENGTH - FileExtension.Length); filename = $"{filenameWithoutExtension}{FileExtension}"; } From a3b440493aeae5fc3fbea99e36be28758c8fe2ba Mon Sep 17 00:00:00 2001 From: Cootz Date: Fri, 17 Feb 2023 15:23:43 +0300 Subject: [PATCH 087/476] Update xml doc --- osu.Game/Database/LegacyExporter.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/osu.Game/Database/LegacyExporter.cs b/osu.Game/Database/LegacyExporter.cs index 7fe0428db3..2d0a1d3528 100644 --- a/osu.Game/Database/LegacyExporter.cs +++ b/osu.Game/Database/LegacyExporter.cs @@ -24,9 +24,18 @@ namespace osu.Game.Database /// Max length of filename (including extension). /// /// - /// TODO: WHY? DOCUMENTATION PER PLATFORM LINKS HERE. - /// - /// This actual usable length is smaller 256 because adds additional "_" to the end of the path + /// + /// Filename limit for most OSs is 255, this actual usable length is smaller because adds additional "_" to the end of the path. + /// + /// + /// For more information see: + ///
+ /// File specification syntax + ///
+ ///
+ /// File systems limitations + ///
+ ///
///
public const int MAX_FILENAME_LENGTH = 255 - (32 + 4 + 2); //max path - (Guid + Guid "D" format chars + Storage.CreateFileSafely chars) From fd1beaef87ef2156b5f5e56bf6d1656ece326a16 Mon Sep 17 00:00:00 2001 From: Cootz Date: Fri, 17 Feb 2023 15:24:27 +0300 Subject: [PATCH 088/476] Fix typo --- osu.Game/Database/LegacyExporter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/LegacyExporter.cs b/osu.Game/Database/LegacyExporter.cs index 2d0a1d3528..04d5a3aebc 100644 --- a/osu.Game/Database/LegacyExporter.cs +++ b/osu.Game/Database/LegacyExporter.cs @@ -25,7 +25,7 @@ namespace osu.Game.Database ///
/// /// - /// Filename limit for most OSs is 255, this actual usable length is smaller because adds additional "_" to the end of the path. + /// The filename limit for most OSs is 255. This actual usable length is smaller because adds an additional "_" to the end of the path. /// /// /// For more information see: From e3bdb3d852a85748032ea468d1763849aee74249 Mon Sep 17 00:00:00 2001 From: Cootz Date: Fri, 17 Feb 2023 15:32:36 +0300 Subject: [PATCH 089/476] Align links in one line --- osu.Game/Database/LegacyExporter.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/osu.Game/Database/LegacyExporter.cs b/osu.Game/Database/LegacyExporter.cs index 04d5a3aebc..cf88efb151 100644 --- a/osu.Game/Database/LegacyExporter.cs +++ b/osu.Game/Database/LegacyExporter.cs @@ -28,13 +28,7 @@ namespace osu.Game.Database /// The filename limit for most OSs is 255. This actual usable length is smaller because adds an additional "_" to the end of the path. /// /// - /// For more information see: - ///
- /// File specification syntax - ///
- ///
- /// File systems limitations - ///
+ /// For more information see file specification syntax, file systems limitations ///
///
public const int MAX_FILENAME_LENGTH = 255 - (32 + 4 + 2); //max path - (Guid + Guid "D" format chars + Storage.CreateFileSafely chars) From 90aa4288d0a9a265d666c8e66545108bce4d8edd Mon Sep 17 00:00:00 2001 From: Cootz Date: Tue, 21 Feb 2023 18:35:53 +0300 Subject: [PATCH 090/476] Reduce the allowed length by 5 to account for (99) suffix. Move truncating logic to `GetFilename`. Update tests. --- osu.Game.Tests/Database/LegacyExporterTest.cs | 9 ++++++--- osu.Game/Database/LegacyExporter.cs | 20 +++++++++---------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Database/LegacyExporterTest.cs b/osu.Game.Tests/Database/LegacyExporterTest.cs index c9aea80585..d41b3a5017 100644 --- a/osu.Game.Tests/Database/LegacyExporterTest.cs +++ b/osu.Game.Tests/Database/LegacyExporterTest.cs @@ -46,7 +46,7 @@ namespace osu.Game.Tests.Database Assert.That(item.Filename.Length, Is.LessThan(TestLegacyExporter.MAX_FILENAME_LENGTH)); //Export multiple times - for (int i = 0; i < 10; i++) + for (int i = 0; i < 100; i++) { string expectedFileName = i == 0 ? short_filename : $"{short_filename} ({i})"; exportItemAndAssert(item, expectedFileName); @@ -76,8 +76,11 @@ namespace osu.Game.Tests.Database Assert.That(item.Filename.Length, Is.GreaterThan(TestLegacyExporter.MAX_FILENAME_LENGTH)); //Export multiple times - for (int i = 0; i < 10; i++) - exportItemAndAssert(item, expectedName); + for (int i = 0; i < 100; i++) + { + string expectedFilename = i == 0 ? expectedName : $"{expectedName} ({i})"; + exportItemAndAssert(item, expectedFilename); + } } private void exportItemAndAssert(IHasNamedFiles item, string expectedName) diff --git a/osu.Game/Database/LegacyExporter.cs b/osu.Game/Database/LegacyExporter.cs index cf88efb151..0fa7b9e03c 100644 --- a/osu.Game/Database/LegacyExporter.cs +++ b/osu.Game/Database/LegacyExporter.cs @@ -31,7 +31,7 @@ namespace osu.Game.Database /// For more information see file specification syntax, file systems limitations /// /// - public const int MAX_FILENAME_LENGTH = 255 - (32 + 4 + 2); //max path - (Guid + Guid "D" format chars + Storage.CreateFileSafely chars) + public const int MAX_FILENAME_LENGTH = 255 - (32 + 4 + 2 + 5); //max path - (Guid + Guid "D" format chars + Storage.CreateFileSafely chars + account for ' (99)' suffix) /// /// The file extension for exports (including the leading '.'). @@ -48,7 +48,15 @@ namespace osu.Game.Database UserFileStorage = storage.GetStorageForDirectory(@"files"); } - protected virtual string GetFilename(TModel item) => item.GetDisplayString(); + protected virtual string GetFilename(TModel item) + { + string filename = item.GetDisplayString(); + + if (filename.Length > MAX_FILENAME_LENGTH - FileExtension.Length) + return filename.Remove(MAX_FILENAME_LENGTH - FileExtension.Length); + + return filename; + } /// /// Exports an item to a legacy (.zip based) package. @@ -65,14 +73,6 @@ namespace osu.Game.Database string filename = NamingUtils.GetNextBestFilename(existingExports, $"{itemFilename}{FileExtension}"); - if (filename.Length > MAX_FILENAME_LENGTH) - { - string filenameWithoutExtension = Path.GetFileNameWithoutExtension(filename); - - filenameWithoutExtension = filenameWithoutExtension.Remove(MAX_FILENAME_LENGTH - FileExtension.Length); - filename = $"{filenameWithoutExtension}{FileExtension}"; - } - using (var stream = exportStorage.CreateFileSafely(filename)) ExportModelTo(item, stream); From fc3d74472cc52b6e06cd1c0791f7277d7ed77744 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 21 Feb 2023 15:55:46 +0300 Subject: [PATCH 091/476] Add mobile local framework reference support --- UseLocalFramework.ps1 | 48 ++++++++++++++++++++++++++++++++++++++----- UseLocalFramework.sh | 39 +++++++++++++++++++++++++++++------ 2 files changed, 76 insertions(+), 11 deletions(-) diff --git a/UseLocalFramework.ps1 b/UseLocalFramework.ps1 index 837685f310..9f4547d980 100644 --- a/UseLocalFramework.ps1 +++ b/UseLocalFramework.ps1 @@ -3,15 +3,53 @@ # # https://github.com/ppy/osu-framework/wiki/Testing-local-framework-checkout-with-other-projects -$CSPROJ="osu.Game/osu.Game.csproj" +$GAME_CSPROJ="osu.Game/osu.Game.csproj" +$ANDROID_PROPS="osu.Android.props" +$IOS_PROPS="osu.iOS.props" $SLN="osu.sln" -dotnet remove $CSPROJ package ppy.osu.Framework; -dotnet sln $SLN add ../osu-framework/osu.Framework/osu.Framework.csproj ../osu-framework/osu.Framework.NativeLibs/osu.Framework.NativeLibs.csproj; -dotnet add $CSPROJ reference ../osu-framework/osu.Framework/osu.Framework.csproj +dotnet remove $GAME_CSPROJ reference ppy.osu.Framework; +dotnet remove $ANDROID_PROPS reference ppy.osu.Framework.Android; +dotnet remove $IOS_PROPS reference ppy.osu.Framework.iOS; + +dotnet sln $SLN add ../osu-framework/osu.Framework/osu.Framework.csproj ` + ../osu-framework/osu.Framework.NativeLibs/osu.Framework.NativeLibs.csproj ` + ../osu-framework/osu.Framework.Android/osu.Framework.Android.csproj ` + ../osu-framework/osu.Framework.iOS/osu.Framework.iOS.csproj; + +dotnet add $GAME_CSPROJ reference ../osu-framework/osu.Framework/osu.Framework.csproj; +dotnet add $ANDROID_PROPS reference ../osu-framework/osu.Framework.Android/osu.Framework.Android.csproj; +dotnet add $IOS_PROPS reference ../osu-framework/osu.Framework.iOS/osu.Framework.iOS.csproj; + +# workaround for dotnet add not inserting $(MSBuildThisFileDirectory) on props files +(Get-Content "osu.Android.props") -replace "`"..\\osu-framework", "`"`$(MSBuildThisFileDirectory)..\osu-framework" | Set-Content "osu.Android.props" +(Get-Content "osu.iOS.props") -replace "`"..\\osu-framework", "`"`$(MSBuildThisFileDirectory)..\osu-framework" | Set-Content "osu.iOS.props" + +# needed because iOS framework nupkg includes a set of properties to work around certain issues during building, +# and those get ignored when referencing framework via project, threfore we have to manually include it via props reference. +(Get-Content "osu.iOS.props") | + Foreach-Object { + if ($_ -match "") + { + " " + } + + $_ + } | Set-Content "osu.iOS.props" + +$TMP=New-TemporaryFile $SLNF=Get-Content "osu.Desktop.slnf" | ConvertFrom-Json -$TMP=New-TemporaryFile $SLNF.solution.projects += ("../osu-framework/osu.Framework/osu.Framework.csproj", "../osu-framework/osu.Framework.NativeLibs/osu.Framework.NativeLibs.csproj") ConvertTo-Json $SLNF | Out-File $TMP -Encoding UTF8 Move-Item -Path $TMP -Destination "osu.Desktop.slnf" -Force + +$SLNF=Get-Content "osu.Android.slnf" | ConvertFrom-Json +$SLNF.solution.projects += ("../osu-framework/osu.Framework/osu.Framework.csproj", "../osu-framework/osu.Framework.NativeLibs/osu.Framework.NativeLibs.csproj", "../osu-framework/osu.Framework.Android/osu.Framework.Android.csproj") +ConvertTo-Json $SLNF | Out-File $TMP -Encoding UTF8 +Move-Item -Path $TMP -Destination "osu.Android.slnf" -Force + +$SLNF=Get-Content "osu.iOS.slnf" | ConvertFrom-Json +$SLNF.solution.projects += ("../osu-framework/osu.Framework/osu.Framework.csproj", "../osu-framework/osu.Framework.NativeLibs/osu.Framework.NativeLibs.csproj", "../osu-framework/osu.Framework.iOS/osu.Framework.iOS.csproj") +ConvertTo-Json $SLNF | Out-File $TMP -Encoding UTF8 +Move-Item -Path $TMP -Destination "osu.iOS.slnf" -Force diff --git a/UseLocalFramework.sh b/UseLocalFramework.sh index 4fd1fdfd1b..c12b388e96 100755 --- a/UseLocalFramework.sh +++ b/UseLocalFramework.sh @@ -5,14 +5,41 @@ # # https://github.com/ppy/osu-framework/wiki/Testing-local-framework-checkout-with-other-projects -CSPROJ="osu.Game/osu.Game.csproj" +GAME_CSPROJ="osu.Game/osu.Game.csproj" +ANDROID_PROPS="osu.Android.props" +IOS_PROPS="osu.iOS.props" SLN="osu.sln" -dotnet remove $CSPROJ package ppy.osu.Framework -dotnet sln $SLN add ../osu-framework/osu.Framework/osu.Framework.csproj ../osu-framework/osu.Framework.NativeLibs/osu.Framework.NativeLibs.csproj -dotnet add $CSPROJ reference ../osu-framework/osu.Framework/osu.Framework.csproj +dotnet remove $GAME_CSPROJ reference ppy.osu.Framework +dotnet remove $ANDROID_PROPS reference ppy.osu.Framework.Android +dotnet remove $IOS_PROPS reference ppy.osu.Framework.iOS + +dotnet sln $SLN add ../osu-framework/osu.Framework/osu.Framework.csproj \ + ../osu-framework/osu.Framework.NativeLibs/osu.Framework.NativeLibs.csproj \ + ../osu-framework/osu.Framework.Android/osu.Framework.Android.csproj \ + ../osu-framework/osu.Framework.iOS/osu.Framework.iOS.csproj + +dotnet add $GAME_CSPROJ reference ../osu-framework/osu.Framework/osu.Framework.csproj +dotnet add $ANDROID_PROPS reference ../osu-framework/osu.Framework.Android/osu.Framework.Android.csproj +dotnet add $IOS_PROPS reference ../osu-framework/osu.Framework.iOS/osu.Framework.iOS.csproj + +# workaround for dotnet add not inserting $(MSBuildThisFileDirectory) on props files +sed -i.bak 's:"..\\osu-framework:"$(MSBuildThisFileDirectory)..\\osu-framework:g' ./osu.Android.props && rm osu.Android.props.bak +sed -i.bak 's:"..\\osu-framework:"$(MSBuildThisFileDirectory)..\\osu-framework:g' ./osu.iOS.props && rm osu.iOS.props.bak + +# needed because iOS framework nupkg includes a set of properties to work around certain issues during building, +# and those get ignored when referencing framework via project, threfore we have to manually include it via props reference. +sed -i.bak '/<\/Project>/i\ + \ +' ./osu.iOS.props && rm osu.iOS.props.bak -SLNF="osu.Desktop.slnf" tmp=$(mktemp) + jq '.solution.projects += ["../osu-framework/osu.Framework/osu.Framework.csproj", "../osu-framework/osu.Framework.NativeLibs/osu.Framework.NativeLibs.csproj"]' osu.Desktop.slnf > $tmp -mv -f $tmp $SLNF +mv -f $tmp osu.Desktop.slnf + +jq '.solution.projects += ["../osu-framework/osu.Framework/osu.Framework.csproj", "../osu-framework/osu.Framework.NativeLibs/osu.Framework.NativeLibs.csproj", "../osu-framework/osu.Framework.Android/osu.Framework.Android.csproj"]' osu.Android.slnf > $tmp +mv -f $tmp osu.Android.slnf + +jq '.solution.projects += ["../osu-framework/osu.Framework/osu.Framework.csproj", "../osu-framework/osu.Framework.NativeLibs/osu.Framework.NativeLibs.csproj", "../osu-framework/osu.Framework.iOS/osu.Framework.iOS.csproj"]' osu.iOS.slnf > $tmp +mv -f $tmp osu.iOS.slnf From 5bec2d7c525fac4fd975f0abb3e5fd19a37ef843 Mon Sep 17 00:00:00 2001 From: tsrk Date: Tue, 21 Feb 2023 19:02:56 +0000 Subject: [PATCH 092/476] style(KeyCounter): `forwardPlayback` --- osu.Game/Screens/Play/KeyCounter.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/KeyCounter.cs b/osu.Game/Screens/Play/KeyCounter.cs index 212843cbe9..a07c650736 100644 --- a/osu.Game/Screens/Play/KeyCounter.cs +++ b/osu.Game/Screens/Play/KeyCounter.cs @@ -60,17 +60,17 @@ namespace osu.Game.Screens.Play countPresses.Value--; } - protected virtual void Activate(bool increment = true) + protected virtual void Activate(bool forwardPlayback = true) { IsActive.Value = true; - if (increment) + if (forwardPlayback) Increment(); } - protected virtual void Deactivate(bool preserve = true) + protected virtual void Deactivate(bool forwardPlayback = true) { IsActive.Value = false; - if (!preserve) + if (!forwardPlayback) Decrement(); } From 42a5a06b9d6e8e1416c2aaf828dfe236e9617b6e Mon Sep 17 00:00:00 2001 From: tsrk Date: Tue, 21 Feb 2023 19:10:37 +0000 Subject: [PATCH 093/476] style(KeyCounter): fields and methods visiblity --- osu.Game/Screens/Play/KeyCounter.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/KeyCounter.cs b/osu.Game/Screens/Play/KeyCounter.cs index a07c650736..4bad6920e3 100644 --- a/osu.Game/Screens/Play/KeyCounter.cs +++ b/osu.Game/Screens/Play/KeyCounter.cs @@ -42,9 +42,9 @@ namespace osu.Game.Screens.Play Name = trigger.Name; } - protected Bindable IsActive = new BindableBool(); + protected readonly Bindable IsActive = new BindableBool(); - public void Increment() + private void increment() { if (!IsCounting) return; @@ -52,7 +52,7 @@ namespace osu.Game.Screens.Play countPresses.Value++; } - public void Decrement() + private void decrement() { if (!IsCounting) return; @@ -64,14 +64,14 @@ namespace osu.Game.Screens.Play { IsActive.Value = true; if (forwardPlayback) - Increment(); + increment(); } protected virtual void Deactivate(bool forwardPlayback = true) { IsActive.Value = false; if (!forwardPlayback) - Decrement(); + decrement(); } protected override void Dispose(bool isDisposing) From 1beec7103725ca9ade4b081804d8a7cc83e5c912 Mon Sep 17 00:00:00 2001 From: tsrk Date: Wed, 22 Feb 2023 14:58:27 +0000 Subject: [PATCH 094/476] refactor(KeyCounterDisplay): apply suggestions I also took the freedom to add type checking, as we can't limit the usage of `Add()` since it's a Container. The exception thrown also advises of using the suggested `AddTrigger()` instead. --- .../Visual/Gameplay/TestSceneAutoplay.cs | 2 +- .../Visual/Gameplay/TestSceneHUDOverlay.cs | 2 +- .../Visual/Gameplay/TestSceneKeyCounter.cs | 4 +- .../TestSceneSkinEditorMultipleSkins.cs | 2 +- .../Gameplay/TestSceneSkinnableHUDOverlay.cs | 2 +- osu.Game/Rulesets/UI/RulesetInputManager.cs | 10 +-- .../Screens/Play/DefaultKeyCounterDisplay.cs | 27 ++++-- osu.Game/Screens/Play/KeyCounter.cs | 6 +- osu.Game/Screens/Play/KeyCounterDisplay.cs | 84 +++++++++++-------- osu.Game/Screens/Play/Player.cs | 7 +- 10 files changed, 86 insertions(+), 60 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs index 4b6e1f089f..903cd178b7 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs @@ -38,7 +38,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddUntilStep("key counter counted keys", () => Player.HUDOverlay.KeyCounter.Children.Any(kc => kc.CountPresses.Value > 2)); seekTo(referenceBeatmap.Breaks[0].StartTime); - AddAssert("keys not counting", () => !Player.HUDOverlay.KeyCounter.IsCounting); + AddAssert("keys not counting", () => !Player.HUDOverlay.KeyCounter.IsCounting.Value); AddAssert("overlay displays 100% accuracy", () => Player.BreakOverlay.ChildrenOfType().Single().AccuracyDisplay.Current.Value == 1); AddStep("rewind", () => Player.GameplayClockContainer.Seek(-80000)); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs index af79650d29..a586d798f5 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs @@ -267,7 +267,7 @@ namespace osu.Game.Tests.Visual.Gameplay hudOverlay = new HUDOverlay(null, Array.Empty()); // Add any key just to display the key counter visually. - hudOverlay.KeyCounter.Add(hudOverlay.KeyCounter.CreateKeyCounter(new KeyCounterKeyboardTrigger(Key.Space))); + hudOverlay.KeyCounter.AddTrigger(new KeyCounterKeyboardTrigger(Key.Space)); scoreProcessor.Combo.Value = 1; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs index 9eeee800d9..5405274cd0 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs @@ -35,7 +35,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("Add random", () => { Key key = (Key)((int)Key.A + RNG.Next(26)); - kc.Add(kc.CreateKeyCounter(new KeyCounterKeyboardTrigger(key))); + kc.AddTrigger(new KeyCounterKeyboardTrigger(key)); }); Key testKey = ((KeyCounterKeyboardTrigger)kc.Children.First().Trigger).Key; @@ -49,7 +49,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert($"Check {testKey} counter after keypress", () => testCounter.CountPresses.Value == 1); addPressKeyStep(); AddAssert($"Check {testKey} counter after keypress", () => testCounter.CountPresses.Value == 2); - AddStep("Disable counting", () => testCounter.IsCounting = false); + AddStep("Disable counting", () => testCounter.IsCounting.Value = false); addPressKeyStep(); AddAssert($"Check {testKey} count has not changed", () => testCounter.CountPresses.Value == 2); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs index 432ff2fc7e..e4f257582d 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs @@ -53,7 +53,7 @@ namespace osu.Game.Tests.Visual.Gameplay }; // Add any key just to display the key counter visually. - hudOverlay.KeyCounter.Add(hudOverlay.KeyCounter.CreateKeyCounter(new KeyCounterKeyboardTrigger(Key.Space))); + hudOverlay.KeyCounter.AddTrigger(new KeyCounterKeyboardTrigger(Key.Space)); scoreProcessor.Combo.Value = 1; return new Container diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs index 24de29fa03..9848894f84 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs @@ -88,7 +88,7 @@ namespace osu.Game.Tests.Visual.Gameplay hudOverlay = new HUDOverlay(null, Array.Empty()); // Add any key just to display the key counter visually. - hudOverlay.KeyCounter.Add(hudOverlay.KeyCounter.CreateKeyCounter(new KeyCounterKeyboardTrigger(Key.Space))); + hudOverlay.KeyCounter.AddTrigger(new KeyCounterKeyboardTrigger(Key.Space)); action?.Invoke(hudOverlay); diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index 6a38fa4824..32b2a19e21 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -162,11 +162,11 @@ namespace osu.Game.Rulesets.UI KeyBindingContainer.Add(receptor); keyCounter.SetReceptor(receptor); - keyCounter.AddRange(KeyBindingContainer.DefaultKeyBindings - .Select(b => b.GetAction()) - .Distinct() - .OrderBy(action => action) - .Select(action => keyCounter.CreateKeyCounter(new KeyCounterActionTrigger(action)))); + keyCounter.AddTriggerRange(KeyBindingContainer.DefaultKeyBindings + .Select(b => b.GetAction()) + .Distinct() + .OrderBy(action => action) + .Select(action => new KeyCounterActionTrigger(action))); } private partial class ActionReceptor : KeyCounterDisplay.Receptor, IKeyBindingHandler diff --git a/osu.Game/Screens/Play/DefaultKeyCounterDisplay.cs b/osu.Game/Screens/Play/DefaultKeyCounterDisplay.cs index fbf1b87395..367eb483a0 100644 --- a/osu.Game/Screens/Play/DefaultKeyCounterDisplay.cs +++ b/osu.Game/Screens/Play/DefaultKeyCounterDisplay.cs @@ -13,7 +13,9 @@ namespace osu.Game.Screens.Play private const int duration = 100; private const double key_fade_time = 80; - protected override Container Content => KeyFlow; + private readonly FillFlowContainer keyFlow = new FillFlowContainer(); + + protected override Container Content => keyFlow; public new IReadOnlyList Children { @@ -22,12 +24,13 @@ namespace osu.Game.Screens.Play } public DefaultKeyCounterDisplay() + : base(typeof(DefaultKeyCounter)) { - KeyFlow.Direction = FillDirection.Horizontal; - KeyFlow.AutoSizeAxes = Axes.Both; - KeyFlow.Alpha = 0; + keyFlow.Direction = FillDirection.Horizontal; + keyFlow.AutoSizeAxes = Axes.Both; + keyFlow.Alpha = 0; - InternalChild = KeyFlow; + InternalChild = keyFlow; } protected override void Update() @@ -36,12 +39,22 @@ namespace osu.Game.Screens.Play // Don't use autosize as it will shrink to zero when KeyFlow is hidden. // In turn this can cause the display to be masked off screen and never become visible again. - Size = KeyFlow.Size; + Size = keyFlow.Size; + } + + public override void AddTrigger(KeyCounter.InputTrigger trigger) + { + DefaultKeyCounter key = new DefaultKeyCounter(trigger); + Add(key); + key.FadeTime = key_fade_time; + key.KeyDownTextColor = KeyDownTextColor; + key.KeyUpTextColor = KeyUpTextColor; } public override void Add(KeyCounter key) { base.Add(key); + DefaultKeyCounter defaultKey = (DefaultKeyCounter)key; defaultKey.FadeTime = key_fade_time; @@ -51,7 +64,7 @@ namespace osu.Game.Screens.Play protected override void UpdateVisibility() => // Isolate changing visibility of the key counters from fading this component. - KeyFlow.FadeTo(AlwaysVisible.Value || ConfigVisibility.Value ? 1 : 0, duration); + keyFlow.FadeTo(AlwaysVisible.Value || ConfigVisibility.Value ? 1 : 0, duration); private Color4 keyDownTextColor = Color4.DarkGray; diff --git a/osu.Game/Screens/Play/KeyCounter.cs b/osu.Game/Screens/Play/KeyCounter.cs index 4bad6920e3..26bb6f1a22 100644 --- a/osu.Game/Screens/Play/KeyCounter.cs +++ b/osu.Game/Screens/Play/KeyCounter.cs @@ -21,7 +21,7 @@ namespace osu.Game.Screens.Play MinValue = 0 }; - public bool IsCounting { get; set; } = true; + public Bindable IsCounting { get; } = new BindableBool(true); public IBindable CountPresses => countPresses; @@ -46,7 +46,7 @@ namespace osu.Game.Screens.Play private void increment() { - if (!IsCounting) + if (!IsCounting.Value) return; countPresses.Value++; @@ -54,7 +54,7 @@ namespace osu.Game.Screens.Play private void decrement() { - if (!IsCounting) + if (!IsCounting.Value) return; countPresses.Value--; diff --git a/osu.Game/Screens/Play/KeyCounterDisplay.cs b/osu.Game/Screens/Play/KeyCounterDisplay.cs index d2b50ff73d..d1fbfe166d 100644 --- a/osu.Game/Screens/Play/KeyCounterDisplay.cs +++ b/osu.Game/Screens/Play/KeyCounterDisplay.cs @@ -2,9 +2,11 @@ // 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; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; @@ -15,22 +17,58 @@ namespace osu.Game.Screens.Play { public abstract partial class KeyCounterDisplay : Container { - protected readonly Bindable ConfigVisibility = new Bindable(); - - protected readonly FillFlowContainer KeyFlow = new FillFlowContainer(); - - protected override Container Content => KeyFlow; - /// /// Whether the key counter should be visible regardless of the configuration value. /// This is true by default, but can be changed. /// - public readonly Bindable AlwaysVisible = new Bindable(true); + public Bindable AlwaysVisible { get; } = new Bindable(true); + + public Bindable IsCounting { get; } = new BindableBool(true); + + public IReadOnlyList Triggers + { + get => Children.Select(c => c.Trigger).ToArray(); + set + { + Clear(); + value.ForEach(AddTrigger); + } + } + + protected readonly Bindable ConfigVisibility = new Bindable(); + + protected abstract void UpdateVisibility(); + + private Receptor? receptor; + + private readonly Type[] acceptedTypes; + + protected KeyCounterDisplay(params Type[] acceptedTypes) + { + this.acceptedTypes = acceptedTypes; + } + + public void SetReceptor(Receptor receptor) + { + if (this.receptor != null) + throw new InvalidOperationException("Cannot set a new receptor when one is already active"); + + this.receptor = receptor; + } + + public abstract void AddTrigger(KeyCounter.InputTrigger trigger); + + public void AddTriggerRange(IEnumerable triggers) => triggers.ForEach(AddTrigger); + + private bool checkType(KeyCounter key) => acceptedTypes.Length == 0 || acceptedTypes.Any(t => t.IsInstanceOfType(key)); public override void Add(KeyCounter key) { + if (!checkType(key)) + throw new InvalidOperationException($"{key.GetType()} is not a supported counter type. (hint: you may want to use {nameof(AddTrigger)} instead.)"); + base.Add(key); - key.IsCounting = IsCounting; + key.IsCounting.BindTo(IsCounting); } [BackgroundDependencyLoader] @@ -47,38 +85,10 @@ namespace osu.Game.Screens.Play ConfigVisibility.BindValueChanged(_ => UpdateVisibility(), true); } - private bool isCounting = true; - - public bool IsCounting - { - get => isCounting; - set - { - if (value == isCounting) return; - - isCounting = value; - foreach (var child in Children) - child.IsCounting = value; - } - } - - protected abstract void UpdateVisibility(); - public override bool HandleNonPositionalInput => receptor == null; + public override bool HandlePositionalInput => receptor == null; - private Receptor? receptor; - - public void SetReceptor(Receptor receptor) - { - if (this.receptor != null) - throw new InvalidOperationException("Cannot set a new receptor when one is already active"); - - this.receptor = receptor; - } - - public virtual KeyCounter CreateKeyCounter(KeyCounter.InputTrigger trigger) => new DefaultKeyCounter(trigger); - public partial class Receptor : Drawable { protected readonly KeyCounterDisplay Target; diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 6dc4854e80..b141848a21 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -440,8 +440,11 @@ namespace osu.Game.Screens.Play }, KeyCounter = { + IsCounting = + { + Value = false + }, AlwaysVisible = { BindTarget = DrawableRuleset.HasReplayLoaded }, - IsCounting = false }, Anchor = Anchor.Centre, Origin = Anchor.Centre @@ -481,7 +484,7 @@ namespace osu.Game.Screens.Play { updateGameplayState(); updatePauseOnFocusLostState(); - HUDOverlay.KeyCounter.IsCounting = !isBreakTime.NewValue; + HUDOverlay.KeyCounter.IsCounting.Value = !isBreakTime.NewValue; } private void updateGameplayState() From 8c94b77de18e4ce57edd57aa50db5848f4d8e60b Mon Sep 17 00:00:00 2001 From: tsrk Date: Wed, 22 Feb 2023 15:03:44 +0000 Subject: [PATCH 095/476] refactor(InputTrigger): move out of KCD I love JetBrains Rider. --- .../TestSceneOsuTouchInput.cs | 2 +- .../Screens/Play/DefaultKeyCounterDisplay.cs | 2 +- osu.Game/Screens/Play/InputTrigger.cs | 23 +++++++++++++++++++ osu.Game/Screens/Play/KeyCounter.cs | 16 ------------- .../Screens/Play/KeyCounterActionTrigger.cs | 2 +- osu.Game/Screens/Play/KeyCounterDisplay.cs | 6 ++--- .../Screens/Play/KeyCounterKeyboardTrigger.cs | 2 +- .../Screens/Play/KeyCounterMouseTrigger.cs | 2 +- 8 files changed, 31 insertions(+), 24 deletions(-) create mode 100644 osu.Game/Screens/Play/InputTrigger.cs diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs index 4cb017cc56..ec8fad9bf3 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs @@ -579,7 +579,7 @@ namespace osu.Game.Rulesets.Osu.Tests private void checkNotPressed(OsuAction action) => AddAssert($"Not pressing {action}", () => !osuInputManager.PressedActions.Contains(action)); private void checkPressed(OsuAction action) => AddAssert($"Is pressing {action}", () => osuInputManager.PressedActions.Contains(action)); - public partial class TestActionKeyCounterTrigger : KeyCounter.InputTrigger, IKeyBindingHandler + public partial class TestActionKeyCounterTrigger : InputTrigger, IKeyBindingHandler { public OsuAction Action { get; } diff --git a/osu.Game/Screens/Play/DefaultKeyCounterDisplay.cs b/osu.Game/Screens/Play/DefaultKeyCounterDisplay.cs index 367eb483a0..10f5a3cfe0 100644 --- a/osu.Game/Screens/Play/DefaultKeyCounterDisplay.cs +++ b/osu.Game/Screens/Play/DefaultKeyCounterDisplay.cs @@ -42,7 +42,7 @@ namespace osu.Game.Screens.Play Size = keyFlow.Size; } - public override void AddTrigger(KeyCounter.InputTrigger trigger) + public override void AddTrigger(InputTrigger trigger) { DefaultKeyCounter key = new DefaultKeyCounter(trigger); Add(key); diff --git a/osu.Game/Screens/Play/InputTrigger.cs b/osu.Game/Screens/Play/InputTrigger.cs new file mode 100644 index 0000000000..b8951b0f8e --- /dev/null +++ b/osu.Game/Screens/Play/InputTrigger.cs @@ -0,0 +1,23 @@ +// 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 osu.Framework.Graphics; + +namespace osu.Game.Screens.Play +{ + public abstract partial class InputTrigger : Component + { + public event Action? OnActivate; + public event Action? OnDeactivate; + + protected InputTrigger(string name) + { + Name = name; + } + + protected void Activate(bool forwardPlayback = true) => OnActivate?.Invoke(forwardPlayback); + + protected void Deactivate(bool forwardPlayback = true) => OnDeactivate?.Invoke(forwardPlayback); + } +} diff --git a/osu.Game/Screens/Play/KeyCounter.cs b/osu.Game/Screens/Play/KeyCounter.cs index 26bb6f1a22..7ee9c94f62 100644 --- a/osu.Game/Screens/Play/KeyCounter.cs +++ b/osu.Game/Screens/Play/KeyCounter.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 osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -80,20 +79,5 @@ namespace osu.Game.Screens.Play Trigger.OnActivate -= Activate; Trigger.OnDeactivate -= Deactivate; } - - public abstract partial class InputTrigger : Component - { - public event Action? OnActivate; - public event Action? OnDeactivate; - - protected InputTrigger(string name) - { - Name = name; - } - - protected void Activate(bool forwardPlayback = true) => OnActivate?.Invoke(forwardPlayback); - - protected void Deactivate(bool forwardPlayback = true) => OnDeactivate?.Invoke(forwardPlayback); - } } } diff --git a/osu.Game/Screens/Play/KeyCounterActionTrigger.cs b/osu.Game/Screens/Play/KeyCounterActionTrigger.cs index 8bb9bdc886..be0d259f85 100644 --- a/osu.Game/Screens/Play/KeyCounterActionTrigger.cs +++ b/osu.Game/Screens/Play/KeyCounterActionTrigger.cs @@ -7,7 +7,7 @@ using System.Collections.Generic; namespace osu.Game.Screens.Play { - public partial class KeyCounterActionTrigger : KeyCounter.InputTrigger + public partial class KeyCounterActionTrigger : InputTrigger where T : struct { public T Action { get; } diff --git a/osu.Game/Screens/Play/KeyCounterDisplay.cs b/osu.Game/Screens/Play/KeyCounterDisplay.cs index d1fbfe166d..01686ae6de 100644 --- a/osu.Game/Screens/Play/KeyCounterDisplay.cs +++ b/osu.Game/Screens/Play/KeyCounterDisplay.cs @@ -25,7 +25,7 @@ namespace osu.Game.Screens.Play public Bindable IsCounting { get; } = new BindableBool(true); - public IReadOnlyList Triggers + public IReadOnlyList Triggers { get => Children.Select(c => c.Trigger).ToArray(); set @@ -56,9 +56,9 @@ namespace osu.Game.Screens.Play this.receptor = receptor; } - public abstract void AddTrigger(KeyCounter.InputTrigger trigger); + public abstract void AddTrigger(InputTrigger trigger); - public void AddTriggerRange(IEnumerable triggers) => triggers.ForEach(AddTrigger); + public void AddTriggerRange(IEnumerable triggers) => triggers.ForEach(AddTrigger); private bool checkType(KeyCounter key) => acceptedTypes.Length == 0 || acceptedTypes.Any(t => t.IsInstanceOfType(key)); diff --git a/osu.Game/Screens/Play/KeyCounterKeyboardTrigger.cs b/osu.Game/Screens/Play/KeyCounterKeyboardTrigger.cs index 56c5ab0083..1d89c58fc3 100644 --- a/osu.Game/Screens/Play/KeyCounterKeyboardTrigger.cs +++ b/osu.Game/Screens/Play/KeyCounterKeyboardTrigger.cs @@ -8,7 +8,7 @@ using osuTK.Input; namespace osu.Game.Screens.Play { - public partial class KeyCounterKeyboardTrigger : KeyCounter.InputTrigger + public partial class KeyCounterKeyboardTrigger : InputTrigger { public Key Key { get; } diff --git a/osu.Game/Screens/Play/KeyCounterMouseTrigger.cs b/osu.Game/Screens/Play/KeyCounterMouseTrigger.cs index 66890073a8..e710c6e33f 100644 --- a/osu.Game/Screens/Play/KeyCounterMouseTrigger.cs +++ b/osu.Game/Screens/Play/KeyCounterMouseTrigger.cs @@ -9,7 +9,7 @@ using osuTK; namespace osu.Game.Screens.Play { - public partial class KeyCounterMouseTrigger : KeyCounter.InputTrigger + public partial class KeyCounterMouseTrigger : InputTrigger { public MouseButton Button { get; } From 6307b3948a2ace0140cdf9c43fe4c6b7cbe7f7db Mon Sep 17 00:00:00 2001 From: tsrk Date: Wed, 22 Feb 2023 17:59:39 +0000 Subject: [PATCH 096/476] style: use Trigger initialisation --- .../Visual/Gameplay/TestSceneKeyCounter.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs index 5405274cd0..12cd7e1be9 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs @@ -17,21 +17,21 @@ namespace osu.Game.Tests.Visual.Gameplay { public TestSceneKeyCounter() { - DefaultKeyCounter testCounter; - KeyCounterDisplay kc = new DefaultKeyCounterDisplay { Origin = Anchor.Centre, Anchor = Anchor.Centre, - Children = new[] + Triggers = new InputTrigger[] { - testCounter = new DefaultKeyCounter(new KeyCounterKeyboardTrigger(Key.X)), - new DefaultKeyCounter(new KeyCounterKeyboardTrigger(Key.X)), - new DefaultKeyCounter(new KeyCounterMouseTrigger(MouseButton.Left)), - new DefaultKeyCounter(new KeyCounterMouseTrigger(MouseButton.Right)), - }, + new KeyCounterKeyboardTrigger(Key.X), + new KeyCounterKeyboardTrigger(Key.X), + new KeyCounterMouseTrigger(MouseButton.Left), + new KeyCounterMouseTrigger(MouseButton.Right), + } }; + var testCounter = (DefaultKeyCounter)kc.Children.First(); + AddStep("Add random", () => { Key key = (Key)((int)Key.A + RNG.Next(26)); From dd9748a25cb3d29537b3abf01a382a9f2be2d7e9 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sat, 25 Feb 2023 01:21:37 +0900 Subject: [PATCH 097/476] Adjust DrawNodes to use UBOs --- .../UI/Cursor/CursorTrail.cs | 22 ++++++++++++-- .../TestSceneTriangleBorderShader.cs | 19 ++++++++++-- .../Backgrounds/TriangleBorderData.cs | 16 ++++++++++ osu.Game/Graphics/Backgrounds/Triangles.cs | 17 +++++++---- osu.Game/Graphics/Backgrounds/TrianglesV2.cs | 13 ++++++-- osu.Game/Graphics/Sprites/LogoAnimation.cs | 22 +++++++++++++- osu.Game/Rulesets/Mods/ModFlashlight.cs | 30 +++++++++++++++---- 7 files changed, 121 insertions(+), 18 deletions(-) create mode 100644 osu.Game/Graphics/Backgrounds/TriangleBorderData.cs diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index 9ecabd1d5e..a29faac5a0 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -13,6 +13,7 @@ using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Rendering; using osu.Framework.Graphics.Rendering.Vertices; using osu.Framework.Graphics.Shaders; +using osu.Framework.Graphics.Shaders.Types; using osu.Framework.Graphics.Textures; using osu.Framework.Input; using osu.Framework.Input.Events; @@ -255,15 +256,23 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor Source.parts.CopyTo(parts, 0); } + private IUniformBuffer cursorTrailParameters; + public override void Draw(IRenderer renderer) { base.Draw(renderer); vertexBatch ??= renderer.CreateQuadBatch(max_sprites, 1); + cursorTrailParameters ??= renderer.CreateUniformBuffer(); + cursorTrailParameters.Data = cursorTrailParameters.Data with + { + FadeClock = time, + FadeExponent = fadeExponent + }; + shader.Bind(); - shader.GetUniform("g_FadeClock").UpdateValue(ref time); - shader.GetUniform("g_FadeExponent").UpdateValue(ref fadeExponent); + shader.BindUniformBlock("m_CursorTrailParameters", cursorTrailParameters); texture.Bind(); @@ -323,6 +332,15 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor base.Dispose(isDisposing); vertexBatch?.Dispose(); + cursorTrailParameters?.Dispose(); + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + private record struct CursorTrailParameters + { + public UniformFloat FadeClock; + public UniformFloat FadeExponent; + private readonly UniformPadding8 pad1; } } diff --git a/osu.Game.Tests/Visual/Background/TestSceneTriangleBorderShader.cs b/osu.Game.Tests/Visual/Background/TestSceneTriangleBorderShader.cs index 271642edd1..07427c242f 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneTriangleBorderShader.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneTriangleBorderShader.cs @@ -9,6 +9,7 @@ using osuTK; using osuTK.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Rendering; +using osu.Game.Graphics.Backgrounds; namespace osu.Game.Tests.Visual.Background { @@ -97,15 +98,29 @@ namespace osu.Game.Tests.Visual.Background texelSize = Source.texelSize; } + private IUniformBuffer? borderDataBuffer; + public override void Draw(IRenderer renderer) { - TextureShader.GetUniform("thickness").UpdateValue(ref thickness); - TextureShader.GetUniform("texelSize").UpdateValue(ref texelSize); + borderDataBuffer ??= renderer.CreateUniformBuffer(); + borderDataBuffer.Data = borderDataBuffer.Data with + { + Thickness = thickness, + TexelSize = texelSize + }; + + TextureShader.BindUniformBlock("m_BorderData", borderDataBuffer); base.Draw(renderer); } protected override bool CanDrawOpaqueInterior => false; + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + borderDataBuffer?.Dispose(); + } } } } diff --git a/osu.Game/Graphics/Backgrounds/TriangleBorderData.cs b/osu.Game/Graphics/Backgrounds/TriangleBorderData.cs new file mode 100644 index 0000000000..f4d327dc8e --- /dev/null +++ b/osu.Game/Graphics/Backgrounds/TriangleBorderData.cs @@ -0,0 +1,16 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Runtime.InteropServices; +using osu.Framework.Graphics.Shaders.Types; + +namespace osu.Game.Graphics.Backgrounds +{ + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public record struct TriangleBorderData + { + public UniformFloat Thickness; + public UniformFloat TexelSize; + private readonly UniformPadding8 pad1; + } +} diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index 68ece56d8a..f3ebdd875a 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -284,6 +284,8 @@ namespace osu.Game.Graphics.Backgrounds parts.AddRange(Source.parts); } + private IUniformBuffer borderDataBuffer; + public override void Draw(IRenderer renderer) { base.Draw(renderer); @@ -294,14 +296,16 @@ namespace osu.Game.Graphics.Backgrounds vertexBatch = renderer.CreateQuadBatch(Source.AimCount, 1); } - // Due to triangles having various sizes we would need to set a different "texelSize" value for each of them, which is insanely expensive, thus we should use one single value. - // texelSize computed for an average triangle (size 100) will result in big triangles becoming blurry, so we may just use 0 for all of them. - // But we still need to specify at least something, because otherwise other shader usages will override this value. - float texelSize = 0f; + borderDataBuffer ??= renderer.CreateUniformBuffer(); + borderDataBuffer.Data = borderDataBuffer.Data with + { + Thickness = fill, + // Due to triangles having various sizes we would need to set a different "TexelSize" value for each of them, which is insanely expensive, thus we should use one single value. + // TexelSize computed for an average triangle (size 100) will result in big triangles becoming blurry, so we may just use 0 for all of them. + TexelSize = 0 + }; shader.Bind(); - shader.GetUniform("thickness").UpdateValue(ref fill); - shader.GetUniform("texelSize").UpdateValue(ref texelSize); foreach (TriangleParticle particle in parts) { @@ -352,6 +356,7 @@ namespace osu.Game.Graphics.Backgrounds base.Dispose(isDisposing); vertexBatch?.Dispose(); + borderDataBuffer?.Dispose(); } } diff --git a/osu.Game/Graphics/Backgrounds/TrianglesV2.cs b/osu.Game/Graphics/Backgrounds/TrianglesV2.cs index 3bc8bb6df1..b108649ce4 100644 --- a/osu.Game/Graphics/Backgrounds/TrianglesV2.cs +++ b/osu.Game/Graphics/Backgrounds/TrianglesV2.cs @@ -227,6 +227,8 @@ namespace osu.Game.Graphics.Backgrounds parts.AddRange(Source.parts); } + private IUniformBuffer? borderDataBuffer; + public override void Draw(IRenderer renderer) { base.Draw(renderer); @@ -240,9 +242,15 @@ namespace osu.Game.Graphics.Backgrounds vertexBatch = renderer.CreateQuadBatch(Source.AimCount, 1); } + borderDataBuffer ??= renderer.CreateUniformBuffer(); + borderDataBuffer.Data = borderDataBuffer.Data with + { + Thickness = thickness, + TexelSize = texelSize + }; + shader.Bind(); - shader.GetUniform("thickness").UpdateValue(ref thickness); - shader.GetUniform("texelSize").UpdateValue(ref texelSize); + shader.BindUniformBlock("m_BorderData", borderDataBuffer); Vector2 relativeSize = Vector2.Divide(triangleSize, size); @@ -303,6 +311,7 @@ namespace osu.Game.Graphics.Backgrounds base.Dispose(isDisposing); vertexBatch?.Dispose(); + borderDataBuffer?.Dispose(); } } diff --git a/osu.Game/Graphics/Sprites/LogoAnimation.cs b/osu.Game/Graphics/Sprites/LogoAnimation.cs index e177cc604b..220f57e9fa 100644 --- a/osu.Game/Graphics/Sprites/LogoAnimation.cs +++ b/osu.Game/Graphics/Sprites/LogoAnimation.cs @@ -3,10 +3,12 @@ #nullable disable +using System.Runtime.InteropServices; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Rendering; using osu.Framework.Graphics.Shaders; +using osu.Framework.Graphics.Shaders.Types; using osu.Framework.Graphics.Sprites; namespace osu.Game.Graphics.Sprites @@ -55,14 +57,32 @@ namespace osu.Game.Graphics.Sprites progress = source.animationProgress; } + private IUniformBuffer animationDataBuffer; + protected override void Blit(IRenderer renderer) { - TextureShader.GetUniform("progress").UpdateValue(ref progress); + animationDataBuffer ??= renderer.CreateUniformBuffer(); + animationDataBuffer.Data = animationDataBuffer.Data with { Progress = progress }; + + TextureShader.BindUniformBlock("m_AnimationData", animationDataBuffer); base.Blit(renderer); } protected override bool CanDrawOpaqueInterior => false; + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + animationDataBuffer?.Dispose(); + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + private record struct AnimationData + { + public UniformFloat Progress; + private readonly UniformPadding12 pad1; + } } } } diff --git a/osu.Game/Rulesets/Mods/ModFlashlight.cs b/osu.Game/Rulesets/Mods/ModFlashlight.cs index 45fa55c7f2..d1446444bf 100644 --- a/osu.Game/Rulesets/Mods/ModFlashlight.cs +++ b/osu.Game/Rulesets/Mods/ModFlashlight.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Runtime.InteropServices; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -9,6 +10,7 @@ using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Rendering; using osu.Framework.Graphics.Rendering.Vertices; using osu.Framework.Graphics.Shaders; +using osu.Framework.Graphics.Shaders.Types; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Game.Configuration; @@ -245,6 +247,8 @@ namespace osu.Game.Rulesets.Mods flashlightSmoothness = Source.flashlightSmoothness; } + private IUniformBuffer? flashlightParametersBuffer; + public override void Draw(IRenderer renderer) { base.Draw(renderer); @@ -259,12 +263,17 @@ namespace osu.Game.Rulesets.Mods }); } - shader.Bind(); + flashlightParametersBuffer ??= renderer.CreateUniformBuffer(); + flashlightParametersBuffer.Data = flashlightParametersBuffer.Data with + { + Position = flashlightPosition, + Size = flashlightSize, + Dim = flashlightDim, + Smoothness = flashlightSmoothness + }; - shader.GetUniform("flashlightPos").UpdateValue(ref flashlightPosition); - shader.GetUniform("flashlightSize").UpdateValue(ref flashlightSize); - shader.GetUniform("flashlightDim").UpdateValue(ref flashlightDim); - shader.GetUniform("flashlightSmoothness").UpdateValue(ref flashlightSmoothness); + shader.Bind(); + shader.BindUniformBlock("m_FlashlightParameters", flashlightParametersBuffer); renderer.DrawQuad(renderer.WhitePixel, screenSpaceDrawQuad, DrawColourInfo.Colour, vertexAction: addAction); @@ -275,6 +284,17 @@ namespace osu.Game.Rulesets.Mods { base.Dispose(isDisposing); quadBatch?.Dispose(); + flashlightParametersBuffer?.Dispose(); + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + private record struct FlashlightParameters + { + public UniformVector2 Position; + public UniformVector2 Size; + public UniformFloat Dim; + public UniformFloat Smoothness; + private readonly UniformPadding8 pad1; } } } From 069b77dd23ffc9aaa04ebd1995f9fd37f1a1c076 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sat, 25 Feb 2023 02:15:56 +0900 Subject: [PATCH 098/476] Update language version --- osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj | 1 + osu.Game/osu.Game.csproj | 1 + 2 files changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index bf776fe5dd..75656e2976 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -4,6 +4,7 @@ Library true click the circles. to the beat. + 10 diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 65ea301cbd..c5001cdc93 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -3,6 +3,7 @@ net6.0 Library true + 10 osu! From a44c9d10d77047669db8e67f474c4427f1afe4db Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sat, 25 Feb 2023 02:18:40 +0900 Subject: [PATCH 099/476] Fix buffer not being bound --- osu.Game/Graphics/Backgrounds/Triangles.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index f3ebdd875a..5b7b94ff4c 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -306,6 +306,7 @@ namespace osu.Game.Graphics.Backgrounds }; shader.Bind(); + shader.BindUniformBlock("m_BorderData", borderDataBuffer); foreach (TriangleParticle particle in parts) { From a705698ab64c7cfe63c82f7be048ed03de54aed8 Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Sun, 26 Feb 2023 21:13:05 +0900 Subject: [PATCH 100/476] beatmapset that already deletePending should not be fetched --- osu.Game/Scoring/Legacy/DatabasedLegacyScoreDecoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Scoring/Legacy/DatabasedLegacyScoreDecoder.cs b/osu.Game/Scoring/Legacy/DatabasedLegacyScoreDecoder.cs index 8908163646..8ee2cc6241 100644 --- a/osu.Game/Scoring/Legacy/DatabasedLegacyScoreDecoder.cs +++ b/osu.Game/Scoring/Legacy/DatabasedLegacyScoreDecoder.cs @@ -24,6 +24,6 @@ namespace osu.Game.Scoring.Legacy } protected override Ruleset GetRuleset(int rulesetId) => rulesets.GetRuleset(rulesetId)?.CreateInstance(); - protected override WorkingBeatmap GetBeatmap(string md5Hash) => beatmaps.GetWorkingBeatmap(beatmaps.QueryBeatmap(b => b.MD5Hash == md5Hash)); + protected override WorkingBeatmap GetBeatmap(string md5Hash) => beatmaps.GetWorkingBeatmap(beatmaps.QueryBeatmap(b => b.MD5Hash == md5Hash && !b.BeatmapSet.DeletePending)); } } From ff0d1aa9f704560a96f6a3f1d048d44f70e33af8 Mon Sep 17 00:00:00 2001 From: mk56-spn Date: Sun, 26 Feb 2023 20:38:50 +0100 Subject: [PATCH 101/476] Make reverting changes to a given skin into a "dangerous action" --- osu.Game/Localisation/SkinEditorStrings.cs | 12 +++++++- osu.Game/Overlays/SkinEditor/SkinEditor.cs | 32 +++++++++++++++++++++- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/osu.Game/Localisation/SkinEditorStrings.cs b/osu.Game/Localisation/SkinEditorStrings.cs index 5cf2e5b5c5..029d50df32 100644 --- a/osu.Game/Localisation/SkinEditorStrings.cs +++ b/osu.Game/Localisation/SkinEditorStrings.cs @@ -42,7 +42,17 @@ namespace osu.Game.Localisation /// /// "Currently editing" /// - public static LocalisableString CurrentlyEditing => new TranslatableString(getKey(@"currently_editing"), "Currently editing"); + public static LocalisableString CurrentlyEditing => new TranslatableString(getKey(@"currently_editing"), @"Currently editing"); + + /// + /// "Revert?" + /// + public static LocalisableString Revert => new TranslatableString(getKey(@"revert"), @"Revert?"); + + /// + /// "The skin will return to the state it was in upon import" + /// + public static LocalisableString ResetDialogue => new TranslatableString(getKey(@"the_skin_will_return_to"), @"The skin will return to the state it was in upon import"); private static string getKey(string key) => $@"{prefix}:{key}"; } diff --git a/osu.Game/Overlays/SkinEditor/SkinEditor.cs b/osu.Game/Overlays/SkinEditor/SkinEditor.cs index 9d470f58f1..936dc7fa84 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditor.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditor.cs @@ -12,11 +12,13 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Framework.Localisation; +using web = osu.Game.Resources.Localisation.Web; using osu.Framework.Testing; using osu.Game.Database; using osu.Game.Graphics; @@ -24,6 +26,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Cursor; using osu.Game.Graphics.UserInterface; using osu.Game.Localisation; +using osu.Game.Overlays.Dialog; using osu.Game.Overlays.OSD; using osu.Game.Overlays.Settings; using osu.Game.Screens.Edit; @@ -97,6 +100,9 @@ namespace osu.Game.Overlays.SkinEditor [Resolved] private OnScreenDisplay? onScreenDisplay { get; set; } + [Resolved] + private IDialogOverlay? dialogOverlay { get; set; } + public SkinEditor() { } @@ -148,7 +154,7 @@ namespace osu.Game.Overlays.SkinEditor Items = new[] { new EditorMenuItem(Resources.Localisation.Web.CommonStrings.ButtonsSave, MenuItemType.Standard, () => Save()), - new EditorMenuItem(CommonStrings.RevertToDefault, MenuItemType.Destructive, revert), + new EditorMenuItem(CommonStrings.RevertToDefault, MenuItemType.Destructive, () => dialogOverlay?.Push(new ResetConfirmDialog(revert))), new EditorMenuItemSpacer(), new EditorMenuItem(CommonStrings.Exit, MenuItemType.Standard, () => skinEditorOverlay?.Hide()), }, @@ -625,6 +631,30 @@ namespace osu.Game.Overlays.SkinEditor } } + public partial class ResetConfirmDialog : PopupDialog + { + public ResetConfirmDialog(Action reset) + { + HeaderText = SkinEditorStrings.Revert; + BodyText = SkinEditorStrings.ResetDialogue; + + Icon = FontAwesome.Solid.ExclamationTriangle; + + Buttons = new PopupDialogButton[] + { + new PopupDialogDangerousButton + { + Text = BeatmapOverlayStrings.UserContentConfirmButtonText, + Action = reset + }, + new PopupDialogCancelButton + { + Text = web.CommonStrings.ButtonsCancel, + }, + }; + } + } + #region Delegation of IEditorChangeHandler public event Action? OnStateChange From 2b9d13cfee0e3c7fc46a2bf9d25077e75e01f02d Mon Sep 17 00:00:00 2001 From: EXtremeExploit Date: Sun, 26 Feb 2023 20:58:20 -0300 Subject: [PATCH 102/476] Add group badges to list view --- .../Visual/Online/TestSceneFriendDisplay.cs | 17 +++++++++++++++-- .../Profile/Header/Components/GroupBadgeFlow.cs | 6 ++++++ osu.Game/Users/ExtendedUserPanel.cs | 8 ++++++++ osu.Game/Users/UserListPanel.cs | 5 +++++ 4 files changed, 34 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneFriendDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneFriendDisplay.cs index 7925b252b6..5028a532b7 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneFriendDisplay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneFriendDisplay.cs @@ -56,7 +56,13 @@ namespace osu.Game.Tests.Visual.Online IsOnline = true, Statistics = new UserStatistics { GlobalRank = 1111 }, CountryCode = CountryCode.JP, - CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c6.jpg" + CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c6.jpg", + IsSupporter = true, + SupportLevel = 1, + Groups = new[] + { + new APIUserGroup { Colour = "#EB47D0", ShortName = "DEV", Name = "Developers" } + } }, new APIUser { @@ -68,6 +74,11 @@ namespace osu.Game.Tests.Visual.Online CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", IsSupporter = true, SupportLevel = 3, + Groups = new[] + { + new APIUserGroup { Colour = "#0066FF", ShortName = "PPY", Name = "peppy" }, + new APIUserGroup { Colour = "#EB47D0", ShortName = "DEV", Name = "Developers" } + } }, new APIUser { @@ -76,7 +87,9 @@ namespace osu.Game.Tests.Visual.Online CountryCode = CountryCode.BY, CoverUrl = "https://assets.ppy.sh/user-profile-covers/8195163/4a8e2ad5a02a2642b631438cfa6c6bd7e2f9db289be881cb27df18331f64144c.jpeg", IsOnline = false, - LastVisit = DateTimeOffset.Now + LastVisit = DateTimeOffset.Now, + IsSupporter = true, + SupportLevel = 2 } }; } diff --git a/osu.Game/Overlays/Profile/Header/Components/GroupBadgeFlow.cs b/osu.Game/Overlays/Profile/Header/Components/GroupBadgeFlow.cs index 33b3de94db..2498543b11 100644 --- a/osu.Game/Overlays/Profile/Header/Components/GroupBadgeFlow.cs +++ b/osu.Game/Overlays/Profile/Header/Components/GroupBadgeFlow.cs @@ -25,8 +25,14 @@ namespace osu.Game.Overlays.Profile.Header.Components Clear(true); if (user.NewValue?.Groups != null) + { AddRange(user.NewValue.Groups.Select(g => new GroupBadge(g))); + Show(); + } + else + Hide(); }); + } } } diff --git a/osu.Game/Users/ExtendedUserPanel.cs b/osu.Game/Users/ExtendedUserPanel.cs index 3c1b68f9ef..af4da22906 100644 --- a/osu.Game/Users/ExtendedUserPanel.cs +++ b/osu.Game/Users/ExtendedUserPanel.cs @@ -13,6 +13,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Users.Drawables; using osu.Framework.Input.Events; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Overlays.Profile.Header.Components; namespace osu.Game.Users { @@ -97,6 +98,13 @@ namespace osu.Game.Users return statusContainer; } + protected FillFlowContainer CreateGroupBadges() + { + var groupBadgeFlow = new GroupBadgeFlow(); + groupBadgeFlow.User.Value = User; + return groupBadgeFlow; + } + private void displayStatus(UserStatus status, UserActivity activity = null) { if (status != null) diff --git a/osu.Game/Users/UserListPanel.cs b/osu.Game/Users/UserListPanel.cs index bbd3c60a33..ebe58c2b93 100644 --- a/osu.Game/Users/UserListPanel.cs +++ b/osu.Game/Users/UserListPanel.cs @@ -67,6 +67,11 @@ namespace osu.Game.Users { username.Anchor = Anchor.CentreLeft; username.Origin = Anchor.CentreLeft; + }), + CreateGroupBadges().With(badges => + { + badges.Anchor = Anchor.CentreLeft; + badges.Origin = Anchor.CentreLeft; }) } }, From d722e09b2cc2840edfc540e9b52de00370930fe3 Mon Sep 17 00:00:00 2001 From: EXtremeExploit Date: Sun, 26 Feb 2023 21:05:41 -0300 Subject: [PATCH 103/476] oops --- osu.Game/Overlays/Profile/Header/Components/GroupBadgeFlow.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/GroupBadgeFlow.cs b/osu.Game/Overlays/Profile/Header/Components/GroupBadgeFlow.cs index 2498543b11..4d4b16d7f5 100644 --- a/osu.Game/Overlays/Profile/Header/Components/GroupBadgeFlow.cs +++ b/osu.Game/Overlays/Profile/Header/Components/GroupBadgeFlow.cs @@ -32,7 +32,6 @@ namespace osu.Game.Overlays.Profile.Header.Components else Hide(); }); - } } } From 17a05de107f15a3336b3410ce87c4c276e22f2ac Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Mon, 13 Feb 2023 21:25:52 +0000 Subject: [PATCH 104/476] Initial Taiko single tap implementation (still needs code to allow for Strong hits) --- .../Mods/InputBlockingMod.cs | 131 ++++++++++++++++++ .../Mods/TaikoModSingleTap.cs | 16 +++ osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 1 + 3 files changed, 148 insertions(+) create mode 100644 osu.Game.Rulesets.Taiko/Mods/InputBlockingMod.cs create mode 100644 osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs diff --git a/osu.Game.Rulesets.Taiko/Mods/InputBlockingMod.cs b/osu.Game.Rulesets.Taiko/Mods/InputBlockingMod.cs new file mode 100644 index 0000000000..c815d62336 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Mods/InputBlockingMod.cs @@ -0,0 +1,131 @@ +// 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.Framework.Graphics; +using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; +using osu.Game.Beatmaps.Timing; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; +using osu.Game.Screens.Play; +using osu.Game.Utils; +using osu.Game.Rulesets.Taiko.Mods; +using osu.Game.Rulesets.Taiko; + +namespace osu.Game.Rulesets.Taiko.Mods +{ + public abstract partial class InputBlockingMod : Mod, IApplicableToDrawableRuleset, IUpdatableByPlayfield + { + public override double ScoreMultiplier => 1.0; + public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModRelax), typeof(TaikoModCinema) }; + public override ModType Type => ModType.Conversion; + + private const double flash_duration = 1000; + + private DrawableRuleset ruleset = null!; + + protected TaikoAction? LastAcceptedDonAction { get; private set; } + protected TaikoAction? LastAcceptedKatAction { get; private set; } + + /// + /// A tracker for periods where alternate should not be forced (i.e. non-gameplay periods). + /// + /// + /// This is different from in that the periods here end strictly at the first object after the break, rather than the break's end time. + /// + private PeriodTracker nonGameplayPeriods = null!; + + private IFrameStableClock gameplayClock = null!; + + public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) + { + ruleset = drawableRuleset; + drawableRuleset.KeyBindingInputManager.Add(new InputInterceptor(this)); + + var periods = new List(); + + if (drawableRuleset.Objects.Any()) + { + periods.Add(new Period(int.MinValue, getValidJudgementTime(ruleset.Objects.First()) - 1)); + + foreach (BreakPeriod b in drawableRuleset.Beatmap.Breaks) + periods.Add(new Period(b.StartTime, getValidJudgementTime(ruleset.Objects.First(h => h.StartTime >= b.EndTime)) - 1)); + + static double getValidJudgementTime(HitObject hitObject) => hitObject.StartTime - hitObject.HitWindows.WindowFor(HitResult.Meh); + } + + nonGameplayPeriods = new PeriodTracker(periods); + + gameplayClock = drawableRuleset.FrameStableClock; + } + + public void Update(Playfield playfield) + { + if (nonGameplayPeriods.IsInAny(gameplayClock.CurrentTime)) + { + if (LastAcceptedDonAction != null) + LastAcceptedDonAction = null; + + if (LastAcceptedKatAction != null) + LastAcceptedKatAction = null; + } + } + + protected abstract bool CheckValidNewAction(TaikoAction action); + + private bool checkCorrectAction(TaikoAction action) + { + if (nonGameplayPeriods.IsInAny(gameplayClock.CurrentTime)) + return true; + + switch (action) + { + case TaikoAction.LeftCentre: + case TaikoAction.RightCentre: + case TaikoAction.LeftRim: + case TaikoAction.RightRim: + break; + + // Any action which is not left or right button should be ignored. + default: + return true; + } + + if (CheckValidNewAction(action)) + { + if (action == TaikoAction.LeftCentre || action == TaikoAction.RightCentre) + LastAcceptedDonAction = action; + if (action == TaikoAction.LeftRim || action == TaikoAction.RightRim) + LastAcceptedKatAction = action; + return true; + } + + //ruleset.Cursor.FlashColour(Colour4.Red, flash_duration, Easing.OutQuint); + return false; + } + + private partial class InputInterceptor : Component, IKeyBindingHandler + { + private readonly InputBlockingMod mod; + + public InputInterceptor(InputBlockingMod mod) + { + this.mod = mod; + } + + public bool OnPressed(KeyBindingPressEvent e) + // if the pressed action is incorrect, block it from reaching gameplay. + => !mod.checkCorrectAction(e.Action); + + public void OnReleased(KeyBindingReleaseEvent e) + { + } + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs new file mode 100644 index 0000000000..20c37fcb71 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs @@ -0,0 +1,16 @@ +// 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.Localisation; + +namespace osu.Game.Rulesets.Taiko.Mods +{ + public class TaikoModSingleTap : InputBlockingMod + { + public override string Name => @"Single Tap"; + public override string Acronym => @"SG"; + public override LocalisableString Description => @"You must only use one key!"; + + protected override bool CheckValidNewAction(TaikoAction action) => LastAcceptedDonAction == null || LastAcceptedDonAction == action || LastAcceptedKatAction == null || LastAcceptedKatAction == action; + } +} diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 4f435e73b3..a35fdb890d 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -158,6 +158,7 @@ namespace osu.Game.Rulesets.Taiko new TaikoModDifficultyAdjust(), new TaikoModClassic(), new TaikoModSwap(), + new TaikoModSingleTap(), }; case ModType.Automation: From 715df42a690ca1d33792f8be5b1d0e860c700d77 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Sun, 26 Feb 2023 22:22:45 +0000 Subject: [PATCH 105/476] Strong hits are now allowed to be hit --- osu.Game.Rulesets.Taiko/Mods/InputBlockingMod.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/InputBlockingMod.cs b/osu.Game.Rulesets.Taiko/Mods/InputBlockingMod.cs index c815d62336..7613ce7c67 100644 --- a/osu.Game.Rulesets.Taiko/Mods/InputBlockingMod.cs +++ b/osu.Game.Rulesets.Taiko/Mods/InputBlockingMod.cs @@ -15,8 +15,7 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Screens.Play; using osu.Game.Utils; -using osu.Game.Rulesets.Taiko.Mods; -using osu.Game.Rulesets.Taiko; +using osu.Game.Rulesets.Taiko.UI; namespace osu.Game.Rulesets.Taiko.Mods { @@ -30,6 +29,8 @@ namespace osu.Game.Rulesets.Taiko.Mods private DrawableRuleset ruleset = null!; + private TaikoPlayfield playfield { get; set; } = null!; + protected TaikoAction? LastAcceptedDonAction { get; private set; } protected TaikoAction? LastAcceptedKatAction { get; private set; } @@ -46,6 +47,7 @@ namespace osu.Game.Rulesets.Taiko.Mods public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { ruleset = drawableRuleset; + playfield = (TaikoPlayfield)ruleset.Playfield; drawableRuleset.KeyBindingInputManager.Add(new InputInterceptor(this)); var periods = new List(); @@ -97,6 +99,10 @@ namespace osu.Game.Rulesets.Taiko.Mods return true; } + // If next hit object is strong, allow usage of all actions + if (playfield.HitObjectContainer.AliveObjects.FirstOrDefault(h => h.Result?.HasResult != true)?.HitObject is TaikoStrongableHitObject hitObject && hitObject.IsStrong) + return true; + if (CheckValidNewAction(action)) { if (action == TaikoAction.LeftCentre || action == TaikoAction.RightCentre) @@ -106,7 +112,6 @@ namespace osu.Game.Rulesets.Taiko.Mods return true; } - //ruleset.Cursor.FlashColour(Colour4.Red, flash_duration, Easing.OutQuint); return false; } From b883c6af34a7601023d7fe84c37cd3ae8f96d652 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Mon, 27 Feb 2023 10:30:57 +0000 Subject: [PATCH 106/476] Renamed all instances of Taiko's InputBlockingMod to TaikoInputBlockingMod --- .../Mods/{InputBlockingMod.cs => TaikoInputBlockingMod.cs} | 6 +++--- osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) rename osu.Game.Rulesets.Taiko/Mods/{InputBlockingMod.cs => TaikoInputBlockingMod.cs} (95%) diff --git a/osu.Game.Rulesets.Taiko/Mods/InputBlockingMod.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs similarity index 95% rename from osu.Game.Rulesets.Taiko/Mods/InputBlockingMod.cs rename to osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs index 7613ce7c67..a843e99671 100644 --- a/osu.Game.Rulesets.Taiko/Mods/InputBlockingMod.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs @@ -19,7 +19,7 @@ using osu.Game.Rulesets.Taiko.UI; namespace osu.Game.Rulesets.Taiko.Mods { - public abstract partial class InputBlockingMod : Mod, IApplicableToDrawableRuleset, IUpdatableByPlayfield + public abstract partial class TaikoInputBlockingMod : Mod, IApplicableToDrawableRuleset, IUpdatableByPlayfield { public override double ScoreMultiplier => 1.0; public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModRelax), typeof(TaikoModCinema) }; @@ -117,9 +117,9 @@ namespace osu.Game.Rulesets.Taiko.Mods private partial class InputInterceptor : Component, IKeyBindingHandler { - private readonly InputBlockingMod mod; + private readonly TaikoInputBlockingMod mod; - public InputInterceptor(InputBlockingMod mod) + public InputInterceptor(TaikoInputBlockingMod mod) { this.mod = mod; } diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs index 20c37fcb71..57e041f151 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs @@ -5,7 +5,7 @@ using osu.Framework.Localisation; namespace osu.Game.Rulesets.Taiko.Mods { - public class TaikoModSingleTap : InputBlockingMod + public class TaikoModSingleTap : TaikoInputBlockingMod { public override string Name => @"Single Tap"; public override string Acronym => @"SG"; From f2cada4584db82428ebcee47914084fc4bdad7e7 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Mon, 27 Feb 2023 12:22:38 +0000 Subject: [PATCH 107/476] Strong drumrolls no longer allow all actions to be used --- osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs index a843e99671..fe9f190d91 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs @@ -99,8 +99,10 @@ namespace osu.Game.Rulesets.Taiko.Mods return true; } - // If next hit object is strong, allow usage of all actions - if (playfield.HitObjectContainer.AliveObjects.FirstOrDefault(h => h.Result?.HasResult != true)?.HitObject is TaikoStrongableHitObject hitObject && hitObject.IsStrong) + // If next hit object is strong, allow usage of all actions. Strong drumrolls are ignored in this check. + if (playfield.HitObjectContainer.AliveObjects.FirstOrDefault(h => h.Result?.HasResult != true)?.HitObject is TaikoStrongableHitObject hitObject + && hitObject.IsStrong + && hitObject as DrumRoll == null) return true; if (CheckValidNewAction(action)) From 12b983495c9883ca0fd691f95ec93143a7688096 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Mon, 27 Feb 2023 13:40:55 +0000 Subject: [PATCH 108/476] Fixed issues caused by branch being stale --- osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs | 6 +++--- osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs index fe9f190d91..eecb8c0bbd 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Taiko.Mods private const double flash_duration = 1000; - private DrawableRuleset ruleset = null!; + private DrawableTaikoRuleset ruleset = null!; private TaikoPlayfield playfield { get; set; } = null!; @@ -46,9 +46,9 @@ namespace osu.Game.Rulesets.Taiko.Mods public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { - ruleset = drawableRuleset; + ruleset = (DrawableTaikoRuleset)drawableRuleset; + ruleset.InputManager.Add(new InputInterceptor(this)); playfield = (TaikoPlayfield)ruleset.Playfield; - drawableRuleset.KeyBindingInputManager.Add(new InputInterceptor(this)); var periods = new List(); diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs index 40203440c5..5390d3b138 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs @@ -33,6 +33,8 @@ namespace osu.Game.Rulesets.Taiko.UI public readonly BindableBool LockPlayfieldMaxAspect = new BindableBool(true); + public TaikoInputManager InputManager { get; private set; } + protected override ScrollVisualisationMethod VisualisationMethod => ScrollVisualisationMethod.Overlapping; protected override bool UserScrollSpeedAdjustment => false; @@ -93,7 +95,7 @@ namespace osu.Game.Rulesets.Taiko.UI LockPlayfieldMaxAspect = { BindTarget = LockPlayfieldMaxAspect } }; - protected override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo); + protected override PassThroughInputManager CreateInputManager() => InputManager = new TaikoInputManager(Ruleset.RulesetInfo); protected override Playfield CreatePlayfield() => new TaikoPlayfield(); From dd9194b976089afc985589fa3100338b896e1596 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Mon, 27 Feb 2023 13:58:40 +0000 Subject: [PATCH 109/476] Code formatting fix --- osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs index eecb8c0bbd..63bc156d2d 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs @@ -101,8 +101,8 @@ namespace osu.Game.Rulesets.Taiko.Mods // If next hit object is strong, allow usage of all actions. Strong drumrolls are ignored in this check. if (playfield.HitObjectContainer.AliveObjects.FirstOrDefault(h => h.Result?.HasResult != true)?.HitObject is TaikoStrongableHitObject hitObject - && hitObject.IsStrong - && hitObject as DrumRoll == null) + && hitObject.IsStrong + && hitObject as DrumRoll == null) return true; if (CheckValidNewAction(action)) From d823faed081db916e967747ac0076bb631e8c0d1 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Mon, 27 Feb 2023 14:33:47 +0000 Subject: [PATCH 110/476] Remove unused `flash_duration` const --- osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs index 63bc156d2d..5512a7d775 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs @@ -25,8 +25,6 @@ namespace osu.Game.Rulesets.Taiko.Mods public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModRelax), typeof(TaikoModCinema) }; public override ModType Type => ModType.Conversion; - private const double flash_duration = 1000; - private DrawableTaikoRuleset ruleset = null!; private TaikoPlayfield playfield { get; set; } = null!; From 12a2037086c8878173f85c8af60a14dad0fec538 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Mon, 27 Feb 2023 14:39:12 +0000 Subject: [PATCH 111/476] Removed useless TaikoAction check from TaikoInputBlockingMod --- .../Mods/TaikoInputBlockingMod.cs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs index 5512a7d775..f437d53b93 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs @@ -84,19 +84,6 @@ namespace osu.Game.Rulesets.Taiko.Mods if (nonGameplayPeriods.IsInAny(gameplayClock.CurrentTime)) return true; - switch (action) - { - case TaikoAction.LeftCentre: - case TaikoAction.RightCentre: - case TaikoAction.LeftRim: - case TaikoAction.RightRim: - break; - - // Any action which is not left or right button should be ignored. - default: - return true; - } - // If next hit object is strong, allow usage of all actions. Strong drumrolls are ignored in this check. if (playfield.HitObjectContainer.AliveObjects.FirstOrDefault(h => h.Result?.HasResult != true)?.HitObject is TaikoStrongableHitObject hitObject && hitObject.IsStrong From bf1897a98fc5d215c9c75a1faffba4d853bf4b9e Mon Sep 17 00:00:00 2001 From: mk56-spn Date: Mon, 27 Feb 2023 16:08:14 +0100 Subject: [PATCH 112/476] Adjust warning text slightly. --- osu.Game/Localisation/SkinEditorStrings.cs | 4 ++-- osu.Game/Overlays/SkinEditor/SkinEditor.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Localisation/SkinEditorStrings.cs b/osu.Game/Localisation/SkinEditorStrings.cs index 029d50df32..b83a105c14 100644 --- a/osu.Game/Localisation/SkinEditorStrings.cs +++ b/osu.Game/Localisation/SkinEditorStrings.cs @@ -45,9 +45,9 @@ namespace osu.Game.Localisation public static LocalisableString CurrentlyEditing => new TranslatableString(getKey(@"currently_editing"), @"Currently editing"); /// - /// "Revert?" + /// "Revert to default?" /// - public static LocalisableString Revert => new TranslatableString(getKey(@"revert"), @"Revert?"); + public static LocalisableString RevertToDefault => new TranslatableString(getKey(@"revert_to_default"), @"Revert to default?"); /// /// "The skin will return to the state it was in upon import" diff --git a/osu.Game/Overlays/SkinEditor/SkinEditor.cs b/osu.Game/Overlays/SkinEditor/SkinEditor.cs index 936dc7fa84..7661abc551 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditor.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditor.cs @@ -635,7 +635,7 @@ namespace osu.Game.Overlays.SkinEditor { public ResetConfirmDialog(Action reset) { - HeaderText = SkinEditorStrings.Revert; + HeaderText = SkinEditorStrings.RevertToDefault; BodyText = SkinEditorStrings.ResetDialogue; Icon = FontAwesome.Solid.ExclamationTriangle; From 25ef1f199d64c32760ad694826970e90a74761f3 Mon Sep 17 00:00:00 2001 From: OpenSauce <48618519+OpenSauce04@users.noreply.github.com> Date: Mon, 27 Feb 2023 15:22:06 +0000 Subject: [PATCH 113/476] Update description for Taiko Single Tap mod --- osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs index 57e041f151..666e550c93 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs @@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Taiko.Mods { public override string Name => @"Single Tap"; public override string Acronym => @"SG"; - public override LocalisableString Description => @"You must only use one key!"; + public override LocalisableString Description => @"One key for dons, one key for kats."; protected override bool CheckValidNewAction(TaikoAction action) => LastAcceptedDonAction == null || LastAcceptedDonAction == action || LastAcceptedKatAction == null || LastAcceptedKatAction == action; } From 1239de6f41fd36b0fca8d3e41708e0f8095d30fd Mon Sep 17 00:00:00 2001 From: mk56-spn Date: Mon, 27 Feb 2023 21:34:07 +0100 Subject: [PATCH 114/476] Upper case web `using` alias --- osu.Game/Overlays/SkinEditor/SkinEditor.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinEditor.cs b/osu.Game/Overlays/SkinEditor/SkinEditor.cs index 7661abc551..53669e9f9c 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditor.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditor.cs @@ -18,7 +18,7 @@ using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Framework.Localisation; -using web = osu.Game.Resources.Localisation.Web; +using Web = osu.Game.Resources.Localisation.Web; using osu.Framework.Testing; using osu.Game.Database; using osu.Game.Graphics; @@ -153,7 +153,7 @@ namespace osu.Game.Overlays.SkinEditor { Items = new[] { - new EditorMenuItem(Resources.Localisation.Web.CommonStrings.ButtonsSave, MenuItemType.Standard, () => Save()), + new EditorMenuItem(Web.CommonStrings.ButtonsSave, MenuItemType.Standard, () => Save()), new EditorMenuItem(CommonStrings.RevertToDefault, MenuItemType.Destructive, () => dialogOverlay?.Push(new ResetConfirmDialog(revert))), new EditorMenuItemSpacer(), new EditorMenuItem(CommonStrings.Exit, MenuItemType.Standard, () => skinEditorOverlay?.Hide()), @@ -649,7 +649,7 @@ namespace osu.Game.Overlays.SkinEditor }, new PopupDialogCancelButton { - Text = web.CommonStrings.ButtonsCancel, + Text = Web.CommonStrings.ButtonsCancel, }, }; } From 00a00ead225c8426e83582a88969cb2e39418ebc Mon Sep 17 00:00:00 2001 From: mk56-spn Date: Mon, 27 Feb 2023 21:50:27 +0100 Subject: [PATCH 115/476] Make `ResetConfirmDialog` inherit from `DeleteConfirmationDialog` --- osu.Game/Overlays/SkinEditor/SkinEditor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinEditor.cs b/osu.Game/Overlays/SkinEditor/SkinEditor.cs index 53669e9f9c..bab5344e1e 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditor.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditor.cs @@ -631,7 +631,7 @@ namespace osu.Game.Overlays.SkinEditor } } - public partial class ResetConfirmDialog : PopupDialog + public partial class ResetConfirmDialog : DeleteConfirmationDialog { public ResetConfirmDialog(Action reset) { From 90227a64966383e7a313132d339c84c434ad8be0 Mon Sep 17 00:00:00 2001 From: mk56-spn Date: Mon, 27 Feb 2023 21:57:59 +0100 Subject: [PATCH 116/476] Rename `DeleteConfirmationDialog.cs` into `DangerousActionDialog.cs` --- osu.Game/Collections/DeleteCollectionDialog.cs | 2 +- .../{DeleteConfirmationDialog.cs => DangerousActionDialog.cs} | 4 ++-- osu.Game/Overlays/Mods/DeleteModPresetDialog.cs | 2 +- .../Sections/Maintenance/MassDeleteConfirmationDialog.cs | 2 +- osu.Game/Overlays/SkinEditor/SkinEditor.cs | 2 +- osu.Game/Screens/Edit/DeleteDifficultyConfirmationDialog.cs | 2 +- osu.Game/Screens/Select/BeatmapClearScoresDialog.cs | 2 +- osu.Game/Screens/Select/BeatmapDeleteDialog.cs | 2 +- .../Screens/Select/Carousel/UpdateLocalConfirmationDialog.cs | 2 +- osu.Game/Screens/Select/LocalScoreDeleteDialog.cs | 2 +- osu.Game/Screens/Select/SkinDeleteDialog.cs | 2 +- 11 files changed, 12 insertions(+), 12 deletions(-) rename osu.Game/Overlays/Dialog/{DeleteConfirmationDialog.cs => DangerousActionDialog.cs} (91%) diff --git a/osu.Game/Collections/DeleteCollectionDialog.cs b/osu.Game/Collections/DeleteCollectionDialog.cs index 4b23f661f9..4da2979481 100644 --- a/osu.Game/Collections/DeleteCollectionDialog.cs +++ b/osu.Game/Collections/DeleteCollectionDialog.cs @@ -8,7 +8,7 @@ using osu.Game.Overlays.Dialog; namespace osu.Game.Collections { - public partial class DeleteCollectionDialog : DeleteConfirmationDialog + public partial class DeleteCollectionDialog : DangerousActionDialog { public DeleteCollectionDialog(Live collection, Action deleteAction) { diff --git a/osu.Game/Overlays/Dialog/DeleteConfirmationDialog.cs b/osu.Game/Overlays/Dialog/DangerousActionDialog.cs similarity index 91% rename from osu.Game/Overlays/Dialog/DeleteConfirmationDialog.cs rename to osu.Game/Overlays/Dialog/DangerousActionDialog.cs index ddb59c4c9e..aaec72fc4d 100644 --- a/osu.Game/Overlays/Dialog/DeleteConfirmationDialog.cs +++ b/osu.Game/Overlays/Dialog/DangerousActionDialog.cs @@ -12,14 +12,14 @@ namespace osu.Game.Overlays.Dialog /// Differs from in that the confirmation button is a "dangerous" one /// (requires the confirm button to be held). /// - public abstract partial class DeleteConfirmationDialog : PopupDialog + public abstract partial class DangerousActionDialog : PopupDialog { /// /// The action which performs the deletion. /// protected Action? DeleteAction { get; set; } - protected DeleteConfirmationDialog() + protected DangerousActionDialog() { HeaderText = DeleteConfirmationDialogStrings.HeaderText; diff --git a/osu.Game/Overlays/Mods/DeleteModPresetDialog.cs b/osu.Game/Overlays/Mods/DeleteModPresetDialog.cs index 800ebe8b4e..5b329a32d5 100644 --- a/osu.Game/Overlays/Mods/DeleteModPresetDialog.cs +++ b/osu.Game/Overlays/Mods/DeleteModPresetDialog.cs @@ -7,7 +7,7 @@ using osu.Game.Rulesets.Mods; namespace osu.Game.Overlays.Mods { - public partial class DeleteModPresetDialog : DeleteConfirmationDialog + public partial class DeleteModPresetDialog : DangerousActionDialog { public DeleteModPresetDialog(Live modPreset) { diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/MassDeleteConfirmationDialog.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/MassDeleteConfirmationDialog.cs index 948d646e3d..71226803c8 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/MassDeleteConfirmationDialog.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/MassDeleteConfirmationDialog.cs @@ -6,7 +6,7 @@ using osu.Game.Overlays.Dialog; namespace osu.Game.Overlays.Settings.Sections.Maintenance { - public partial class MassDeleteConfirmationDialog : DeleteConfirmationDialog + public partial class MassDeleteConfirmationDialog : DangerousActionDialog { public MassDeleteConfirmationDialog(Action deleteAction) { diff --git a/osu.Game/Overlays/SkinEditor/SkinEditor.cs b/osu.Game/Overlays/SkinEditor/SkinEditor.cs index bab5344e1e..770a3c5930 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditor.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditor.cs @@ -631,7 +631,7 @@ namespace osu.Game.Overlays.SkinEditor } } - public partial class ResetConfirmDialog : DeleteConfirmationDialog + public partial class ResetConfirmDialog : DangerousActionDialog { public ResetConfirmDialog(Action reset) { diff --git a/osu.Game/Screens/Edit/DeleteDifficultyConfirmationDialog.cs b/osu.Game/Screens/Edit/DeleteDifficultyConfirmationDialog.cs index 68a0ef4250..6db31967d6 100644 --- a/osu.Game/Screens/Edit/DeleteDifficultyConfirmationDialog.cs +++ b/osu.Game/Screens/Edit/DeleteDifficultyConfirmationDialog.cs @@ -7,7 +7,7 @@ using osu.Game.Overlays.Dialog; namespace osu.Game.Screens.Edit { - public partial class DeleteDifficultyConfirmationDialog : DeleteConfirmationDialog + public partial class DeleteDifficultyConfirmationDialog : DangerousActionDialog { public DeleteDifficultyConfirmationDialog(BeatmapInfo beatmapInfo, Action deleteAction) { diff --git a/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs b/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs index c0f97a05e2..2704a1afb4 100644 --- a/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs +++ b/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs @@ -10,7 +10,7 @@ using osu.Game.Scoring; namespace osu.Game.Screens.Select { - public partial class BeatmapClearScoresDialog : DeleteConfirmationDialog + public partial class BeatmapClearScoresDialog : DangerousActionDialog { [Resolved] private ScoreManager scoreManager { get; set; } = null!; diff --git a/osu.Game/Screens/Select/BeatmapDeleteDialog.cs b/osu.Game/Screens/Select/BeatmapDeleteDialog.cs index 4ab23c3a3a..0692cc3745 100644 --- a/osu.Game/Screens/Select/BeatmapDeleteDialog.cs +++ b/osu.Game/Screens/Select/BeatmapDeleteDialog.cs @@ -7,7 +7,7 @@ using osu.Game.Overlays.Dialog; namespace osu.Game.Screens.Select { - public partial class BeatmapDeleteDialog : DeleteConfirmationDialog + public partial class BeatmapDeleteDialog : DangerousActionDialog { private readonly BeatmapSetInfo beatmapSet; diff --git a/osu.Game/Screens/Select/Carousel/UpdateLocalConfirmationDialog.cs b/osu.Game/Screens/Select/Carousel/UpdateLocalConfirmationDialog.cs index e1aa662942..ecb21d4e2c 100644 --- a/osu.Game/Screens/Select/Carousel/UpdateLocalConfirmationDialog.cs +++ b/osu.Game/Screens/Select/Carousel/UpdateLocalConfirmationDialog.cs @@ -8,7 +8,7 @@ using osu.Game.Localisation; namespace osu.Game.Screens.Select.Carousel { - public partial class UpdateLocalConfirmationDialog : DeleteConfirmationDialog + public partial class UpdateLocalConfirmationDialog : DangerousActionDialog { public UpdateLocalConfirmationDialog(Action onConfirm) { diff --git a/osu.Game/Screens/Select/LocalScoreDeleteDialog.cs b/osu.Game/Screens/Select/LocalScoreDeleteDialog.cs index 6349e9e5eb..660122c80e 100644 --- a/osu.Game/Screens/Select/LocalScoreDeleteDialog.cs +++ b/osu.Game/Screens/Select/LocalScoreDeleteDialog.cs @@ -10,7 +10,7 @@ using osu.Game.Beatmaps; namespace osu.Game.Screens.Select { - public partial class LocalScoreDeleteDialog : DeleteConfirmationDialog + public partial class LocalScoreDeleteDialog : DangerousActionDialog { private readonly ScoreInfo score; diff --git a/osu.Game/Screens/Select/SkinDeleteDialog.cs b/osu.Game/Screens/Select/SkinDeleteDialog.cs index 9e11629890..3c95de0620 100644 --- a/osu.Game/Screens/Select/SkinDeleteDialog.cs +++ b/osu.Game/Screens/Select/SkinDeleteDialog.cs @@ -7,7 +7,7 @@ using osu.Game.Overlays.Dialog; namespace osu.Game.Screens.Select { - public partial class SkinDeleteDialog : DeleteConfirmationDialog + public partial class SkinDeleteDialog : DangerousActionDialog { private readonly Skin skin; From beb04379f9a518c78802da385caa92f5f05c3670 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Mon, 27 Feb 2023 22:36:09 +0000 Subject: [PATCH 117/476] Inverted an if statement for code clarity --- .../Mods/TaikoInputBlockingMod.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs index f437d53b93..16d885b5d5 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs @@ -67,14 +67,13 @@ namespace osu.Game.Rulesets.Taiko.Mods public void Update(Playfield playfield) { - if (nonGameplayPeriods.IsInAny(gameplayClock.CurrentTime)) - { - if (LastAcceptedDonAction != null) - LastAcceptedDonAction = null; + if (!nonGameplayPeriods.IsInAny(gameplayClock.CurrentTime)) return; - if (LastAcceptedKatAction != null) - LastAcceptedKatAction = null; - } + if (LastAcceptedDonAction != null) + LastAcceptedDonAction = null; + + if (LastAcceptedKatAction != null) + LastAcceptedKatAction = null; } protected abstract bool CheckValidNewAction(TaikoAction action); From 1250c1f0c1f50bd28ce5b5c11cae68d0b095bf57 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 Feb 2023 19:27:46 +0900 Subject: [PATCH 118/476] Update test assertions to match stable expectations These changes were taken from https://github.com/ppy/osu/pull/22582. Minor adjustments were applied to match stable expectations, which is to say there cannot be an inherited control point with omit barline specification (in the editor the setting is greyed out when inheritance is turned on). --- .../Formats/LegacyBeatmapDecoderTest.cs | 28 +++++++++++++++++-- .../NonVisual/ControlPointInfoTest.cs | 22 +++++++++++---- .../Resources/omit-barline-control-points.osu | 27 ++++++++++++++++++ 3 files changed, 69 insertions(+), 8 deletions(-) create mode 100644 osu.Game.Tests/Resources/omit-barline-control-points.osu diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 5787bd6066..85d304da9c 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -181,16 +181,19 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.AreEqual(956, timingPoint.Time); Assert.AreEqual(329.67032967033, timingPoint.BeatLength); Assert.AreEqual(TimeSignature.SimpleQuadruple, timingPoint.TimeSignature); + Assert.IsFalse(timingPoint.OmitFirstBarLine); timingPoint = controlPoints.TimingPointAt(48428); Assert.AreEqual(956, timingPoint.Time); Assert.AreEqual(329.67032967033d, timingPoint.BeatLength); Assert.AreEqual(TimeSignature.SimpleQuadruple, timingPoint.TimeSignature); + Assert.IsFalse(timingPoint.OmitFirstBarLine); timingPoint = controlPoints.TimingPointAt(119637); Assert.AreEqual(119637, timingPoint.Time); Assert.AreEqual(659.340659340659, timingPoint.BeatLength); Assert.AreEqual(TimeSignature.SimpleQuadruple, timingPoint.TimeSignature); + Assert.IsFalse(timingPoint.OmitFirstBarLine); var difficultyPoint = controlPoints.DifficultyPointAt(0); Assert.AreEqual(0, difficultyPoint.Time); @@ -222,17 +225,14 @@ namespace osu.Game.Tests.Beatmaps.Formats var effectPoint = controlPoints.EffectPointAt(0); Assert.AreEqual(0, effectPoint.Time); Assert.IsFalse(effectPoint.KiaiMode); - Assert.IsFalse(effectPoint.OmitFirstBarLine); effectPoint = controlPoints.EffectPointAt(53703); Assert.AreEqual(53703, effectPoint.Time); Assert.IsTrue(effectPoint.KiaiMode); - Assert.IsFalse(effectPoint.OmitFirstBarLine); effectPoint = controlPoints.EffectPointAt(116637); Assert.AreEqual(95901, effectPoint.Time); Assert.IsFalse(effectPoint.KiaiMode); - Assert.IsFalse(effectPoint.OmitFirstBarLine); } } @@ -273,6 +273,28 @@ namespace osu.Game.Tests.Beatmaps.Formats } } + [Test] + public void TestDecodeOmitBarLineEffect() + { + var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; + + using (var resStream = TestResources.OpenResource("omit-barline-control-points.osu")) + using (var stream = new LineBufferedReader(resStream)) + { + var controlPoints = (LegacyControlPointInfo)decoder.Decode(stream).ControlPointInfo; + + Assert.That(controlPoints.TimingPoints.Count, Is.EqualTo(6)); + Assert.That(controlPoints.EffectPoints.Count, Is.EqualTo(0)); + + Assert.That(controlPoints.TimingPointAt(500).OmitFirstBarLine, Is.False); + Assert.That(controlPoints.TimingPointAt(1500).OmitFirstBarLine, Is.True); + Assert.That(controlPoints.TimingPointAt(2500).OmitFirstBarLine, Is.False); + Assert.That(controlPoints.TimingPointAt(3500).OmitFirstBarLine, Is.False); + Assert.That(controlPoints.TimingPointAt(4500).OmitFirstBarLine, Is.False); + Assert.That(controlPoints.TimingPointAt(5500).OmitFirstBarLine, Is.True); + } + } + [Test] public void TestTimingPointResetsSpeedMultiplier() { diff --git a/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs b/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs index 554473cf77..a2ded643fa 100644 --- a/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs +++ b/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs @@ -43,6 +43,18 @@ namespace osu.Game.Tests.NonVisual Assert.That(cpi.Groups.Count, Is.EqualTo(2)); Assert.That(cpi.TimingPoints.Count, Is.EqualTo(2)); Assert.That(cpi.AllControlPoints.Count(), Is.EqualTo(2)); + + cpi.Add(1200, new TimingControlPoint { OmitFirstBarLine = true }); // is not redundant + + Assert.That(cpi.Groups.Count, Is.EqualTo(3)); + Assert.That(cpi.TimingPoints.Count, Is.EqualTo(3)); + Assert.That(cpi.AllControlPoints.Count(), Is.EqualTo(3)); + + cpi.Add(1500, new TimingControlPoint { OmitFirstBarLine = true }); // is not redundant + + Assert.That(cpi.Groups.Count, Is.EqualTo(4)); + Assert.That(cpi.TimingPoints.Count, Is.EqualTo(4)); + Assert.That(cpi.AllControlPoints.Count(), Is.EqualTo(4)); } [Test] @@ -95,12 +107,12 @@ namespace osu.Game.Tests.NonVisual Assert.That(cpi.EffectPoints.Count, Is.EqualTo(0)); Assert.That(cpi.AllControlPoints.Count(), Is.EqualTo(0)); - cpi.Add(1000, new EffectControlPoint { KiaiMode = true, OmitFirstBarLine = true }); // is not redundant - cpi.Add(1400, new EffectControlPoint { KiaiMode = true, OmitFirstBarLine = true }); // same settings, but is not redundant + cpi.Add(1000, new EffectControlPoint { KiaiMode = true }); // is not redundant + cpi.Add(1400, new EffectControlPoint { KiaiMode = true }); // is redundant - Assert.That(cpi.Groups.Count, Is.EqualTo(2)); - Assert.That(cpi.EffectPoints.Count, Is.EqualTo(2)); - Assert.That(cpi.AllControlPoints.Count(), Is.EqualTo(2)); + Assert.That(cpi.Groups.Count, Is.EqualTo(1)); + Assert.That(cpi.EffectPoints.Count, Is.EqualTo(1)); + Assert.That(cpi.AllControlPoints.Count(), Is.EqualTo(1)); } [Test] diff --git a/osu.Game.Tests/Resources/omit-barline-control-points.osu b/osu.Game.Tests/Resources/omit-barline-control-points.osu new file mode 100644 index 0000000000..839a59215b --- /dev/null +++ b/osu.Game.Tests/Resources/omit-barline-control-points.osu @@ -0,0 +1,27 @@ +osu file format v14 + +[TimingPoints] + +// Uninherited: none, inherited: none +0,500,4,2,0,100,1,0 +0,-50,4,3,0,100,0,0 + +// Uninherited: omit, inherited: none +1000,500,4,2,0,100,1,8 +1000,-50,4,3,0,100,0,0 + +// Uninherited: none, inherited: omit (should be ignored, inheriting cannot omit) +2000,500,4,2,0,100,1,0 +2000,-50,4,3,0,100,0,8 + +// Inherited: none, uninherited: none +3000,-50,4,3,0,100,0,0 +3000,500,4,2,0,100,1,0 + +// Inherited: omit, uninherited: none (should be ignored, inheriting cannot omit) +4000,-50,4,3,0,100,0,8 +4000,500,4,2,0,100,1,0 + +// Inherited: none, uninherited: omit +5000,-50,4,3,0,100,0,0 +5000,500,4,2,0,100,1,8 From 044b0604b260461acde4f89f5ad2719b06252b4f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 Feb 2023 19:29:31 +0900 Subject: [PATCH 119/476] Move `OmitFirstBarLine` to `TimingControlPoint` --- .../TestSceneBarLineGeneration.cs | 5 ++-- .../Beatmaps/TaikoBeatmapConverter.cs | 1 - .../ControlPoints/EffectControlPoint.cs | 23 ++----------------- .../ControlPoints/TimingControlPoint.cs | 19 ++++++++++++++- .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 2 +- .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 5 ++-- .../Containers/BeatSyncedContainer.cs | 2 +- osu.Game/Rulesets/Objects/BarLineGenerator.cs | 3 +-- osu.Game/Screens/Edit/Timing/EffectSection.cs | 5 ---- .../RowAttributes/EffectRowAttribute.cs | 5 ---- .../RowAttributes/TimingRowAttribute.cs | 11 ++++++++- osu.Game/Screens/Edit/Timing/TimingSection.cs | 9 ++++++-- 12 files changed, 45 insertions(+), 45 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneBarLineGeneration.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneBarLineGeneration.cs index 7b8c8926f0..8511ad9f00 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneBarLineGeneration.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneBarLineGeneration.cs @@ -73,11 +73,10 @@ namespace osu.Game.Rulesets.Taiko.Tests beatmap.ControlPointInfo.Add(start_time, new TimingControlPoint { BeatLength = beat_length, - TimeSignature = new TimeSignature(time_signature_numerator) + TimeSignature = new TimeSignature(time_signature_numerator), + OmitFirstBarLine = true }); - beatmap.ControlPointInfo.Add(start_time, new EffectControlPoint { OmitFirstBarLine = true }); - var barlines = new BarLineGenerator(beatmap).BarLines; AddAssert("first barline ommited", () => barlines.All(b => b.StartTime != start_time)); diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 3cc47deed0..6a35e9376b 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -72,7 +72,6 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps converted.ControlPointInfo.Add(hitObject.StartTime, new EffectControlPoint { KiaiMode = currentEffectPoint.KiaiMode, - OmitFirstBarLine = currentEffectPoint.OmitFirstBarLine, ScrollSpeed = lastScrollSpeed = nextScrollSpeed, }); } diff --git a/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs index 7c4313a015..7edf892f35 100644 --- a/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs @@ -13,15 +13,9 @@ namespace osu.Game.Beatmaps.ControlPoints public static readonly EffectControlPoint DEFAULT = new EffectControlPoint { KiaiModeBindable = { Disabled = true }, - OmitFirstBarLineBindable = { Disabled = true }, ScrollSpeedBindable = { Disabled = true } }; - /// - /// Whether the first bar line of this control point is ignored. - /// - public readonly BindableBool OmitFirstBarLineBindable = new BindableBool(); - /// /// The relative scroll speed at this control point. /// @@ -43,15 +37,6 @@ namespace osu.Game.Beatmaps.ControlPoints public override Color4 GetRepresentingColour(OsuColour colours) => colours.Purple; - /// - /// Whether the first bar line of this control point is ignored. - /// - public bool OmitFirstBarLine - { - get => OmitFirstBarLineBindable.Value; - set => OmitFirstBarLineBindable.Value = value; - } - /// /// Whether this control point enables Kiai mode. /// @@ -67,16 +52,13 @@ namespace osu.Game.Beatmaps.ControlPoints } public override bool IsRedundant(ControlPoint? existing) - => !OmitFirstBarLine - && existing is EffectControlPoint existingEffect + => existing is EffectControlPoint existingEffect && KiaiMode == existingEffect.KiaiMode - && OmitFirstBarLine == existingEffect.OmitFirstBarLine && ScrollSpeed == existingEffect.ScrollSpeed; public override void CopyFrom(ControlPoint other) { KiaiMode = ((EffectControlPoint)other).KiaiMode; - OmitFirstBarLine = ((EffectControlPoint)other).OmitFirstBarLine; ScrollSpeed = ((EffectControlPoint)other).ScrollSpeed; base.CopyFrom(other); @@ -88,10 +70,9 @@ namespace osu.Game.Beatmaps.ControlPoints public bool Equals(EffectControlPoint? other) => base.Equals(other) - && OmitFirstBarLine == other.OmitFirstBarLine && ScrollSpeed == other.ScrollSpeed && KiaiMode == other.KiaiMode; - public override int GetHashCode() => HashCode.Combine(base.GetHashCode(), OmitFirstBarLine, ScrollSpeed, KiaiMode); + public override int GetHashCode() => HashCode.Combine(base.GetHashCode(), ScrollSpeed, KiaiMode); } } diff --git a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs index 61cc060594..4e69486e2d 100644 --- a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs @@ -16,6 +16,11 @@ namespace osu.Game.Beatmaps.ControlPoints /// public readonly Bindable TimeSignatureBindable = new Bindable(TimeSignature.SimpleQuadruple); + /// + /// Whether the first bar line of this control point is ignored. + /// + public readonly BindableBool OmitFirstBarLineBindable = new BindableBool(); + /// /// Default length of a beat in milliseconds. Used whenever there is no beatmap or track playing. /// @@ -30,6 +35,7 @@ namespace osu.Game.Beatmaps.ControlPoints Value = default_beat_length, Disabled = true }, + OmitFirstBarLineBindable = { Disabled = true }, TimeSignatureBindable = { Disabled = true } }; @@ -42,6 +48,15 @@ namespace osu.Game.Beatmaps.ControlPoints set => TimeSignatureBindable.Value = value; } + /// + /// Whether the first bar line of this control point is ignored. + /// + public bool OmitFirstBarLine + { + get => OmitFirstBarLineBindable.Value; + set => OmitFirstBarLineBindable.Value = value; + } + public const double DEFAULT_BEAT_LENGTH = 1000; /// @@ -73,6 +88,7 @@ namespace osu.Game.Beatmaps.ControlPoints public override void CopyFrom(ControlPoint other) { TimeSignature = ((TimingControlPoint)other).TimeSignature; + OmitFirstBarLine = ((TimingControlPoint)other).OmitFirstBarLine; BeatLength = ((TimingControlPoint)other).BeatLength; base.CopyFrom(other); @@ -85,8 +101,9 @@ namespace osu.Game.Beatmaps.ControlPoints public bool Equals(TimingControlPoint? other) => base.Equals(other) && TimeSignature.Equals(other.TimeSignature) + && OmitFirstBarLine == other.OmitFirstBarLine && BeatLength.Equals(other.BeatLength); - public override int GetHashCode() => HashCode.Combine(base.GetHashCode(), TimeSignature, BeatLength); + public override int GetHashCode() => HashCode.Combine(base.GetHashCode(), TimeSignature, BeatLength, OmitFirstBarLine); } } diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 9c710b690e..eabc63b341 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -431,6 +431,7 @@ namespace osu.Game.Beatmaps.Formats controlPoint.BeatLength = beatLength; controlPoint.TimeSignature = timeSignature; + controlPoint.OmitFirstBarLine = omitFirstBarSignature; addControlPoint(time, controlPoint, true); } @@ -447,7 +448,6 @@ namespace osu.Game.Beatmaps.Formats var effectPoint = new EffectControlPoint { KiaiMode = kiaiMode, - OmitFirstBarLine = omitFirstBarSignature, }; // osu!taiko and osu!mania use effect points rather than difficulty points for scroll speed adjustments. diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 7e058d755e..072e442dea 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -222,6 +222,7 @@ namespace osu.Game.Beatmaps.Formats { var samplePoint = legacyControlPoints.SamplePointAt(time); var effectPoint = legacyControlPoints.EffectPointAt(time); + var timingPoint = legacyControlPoints.TimingPointAt(time); // Apply the control point to a hit sample to uncover legacy properties (e.g. suffix) HitSampleInfo tempHitSample = samplePoint.ApplyTo(new ConvertHitObjectParser.LegacyHitSampleInfo(string.Empty)); @@ -230,10 +231,10 @@ namespace osu.Game.Beatmaps.Formats LegacyEffectFlags effectFlags = LegacyEffectFlags.None; if (effectPoint.KiaiMode) effectFlags |= LegacyEffectFlags.Kiai; - if (effectPoint.OmitFirstBarLine) + if (timingPoint.OmitFirstBarLine) effectFlags |= LegacyEffectFlags.OmitFirstBarLine; - writer.Write(FormattableString.Invariant($"{legacyControlPoints.TimingPointAt(time).TimeSignature.Numerator},")); + writer.Write(FormattableString.Invariant($"{timingPoint.TimeSignature.Numerator},")); writer.Write(FormattableString.Invariant($"{(int)toLegacySampleBank(tempHitSample.Bank)},")); writer.Write(FormattableString.Invariant($"{toLegacyCustomSampleBank(tempHitSample)},")); writer.Write(FormattableString.Invariant($"{tempHitSample.Volume},")); diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index 724005a0cc..42b30f9d18 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -114,7 +114,7 @@ namespace osu.Game.Graphics.Containers while (beatLength < MinimumBeatLength) beatLength *= 2; - int beatIndex = (int)((currentTrackTime - timingPoint.Time) / beatLength) - (effectPoint.OmitFirstBarLine ? 1 : 0); + int beatIndex = (int)((currentTrackTime - timingPoint.Time) / beatLength) - (timingPoint.OmitFirstBarLine ? 1 : 0); // The beats before the start of the first control point are off by 1, this should do the trick if (currentTrackTime < timingPoint.Time) diff --git a/osu.Game/Rulesets/Objects/BarLineGenerator.cs b/osu.Game/Rulesets/Objects/BarLineGenerator.cs index af32c7def3..affbcbd878 100644 --- a/osu.Game/Rulesets/Objects/BarLineGenerator.cs +++ b/osu.Game/Rulesets/Objects/BarLineGenerator.cs @@ -38,7 +38,6 @@ namespace osu.Game.Rulesets.Objects for (int i = 0; i < timingPoints.Count; i++) { TimingControlPoint currentTimingPoint = timingPoints[i]; - EffectControlPoint currentEffectPoint = beatmap.ControlPointInfo.EffectPointAt(currentTimingPoint.Time); int currentBeat = 0; // Don't generate barlines before the hit object or t=0 (whichever is earliest). Some beatmaps use very unrealistic values here (although none are ranked). @@ -66,7 +65,7 @@ namespace osu.Game.Rulesets.Objects startTime = currentTimingPoint.Time + barCount * barLength; } - if (currentEffectPoint.OmitFirstBarLine) + if (currentTimingPoint.OmitFirstBarLine) { startTime += barLength; } diff --git a/osu.Game/Screens/Edit/Timing/EffectSection.cs b/osu.Game/Screens/Edit/Timing/EffectSection.cs index 051ac97366..7e484433f7 100644 --- a/osu.Game/Screens/Edit/Timing/EffectSection.cs +++ b/osu.Game/Screens/Edit/Timing/EffectSection.cs @@ -14,7 +14,6 @@ namespace osu.Game.Screens.Edit.Timing internal partial class EffectSection : Section { private LabelledSwitchButton kiai = null!; - private LabelledSwitchButton omitBarLine = null!; private SliderWithTextBoxInput scrollSpeedSlider = null!; @@ -24,7 +23,6 @@ namespace osu.Game.Screens.Edit.Timing Flow.AddRange(new Drawable[] { kiai = new LabelledSwitchButton { Label = "Kiai Time" }, - omitBarLine = new LabelledSwitchButton { Label = "Skip Bar Line" }, scrollSpeedSlider = new SliderWithTextBoxInput("Scroll Speed") { Current = new EffectControlPoint().ScrollSpeedBindable, @@ -38,7 +36,6 @@ namespace osu.Game.Screens.Edit.Timing base.LoadComplete(); kiai.Current.BindValueChanged(_ => saveChanges()); - omitBarLine.Current.BindValueChanged(_ => saveChanges()); scrollSpeedSlider.Current.BindValueChanged(_ => saveChanges()); var drawableRuleset = Beatmap.BeatmapInfo.Ruleset.CreateInstance().CreateDrawableRulesetWith(Beatmap.PlayableBeatmap); @@ -60,7 +57,6 @@ namespace osu.Game.Screens.Edit.Timing isRebinding = true; kiai.Current = point.NewValue.KiaiModeBindable; - omitBarLine.Current = point.NewValue.OmitFirstBarLineBindable; scrollSpeedSlider.Current = point.NewValue.ScrollSpeedBindable; isRebinding = false; @@ -74,7 +70,6 @@ namespace osu.Game.Screens.Edit.Timing return new EffectControlPoint { KiaiMode = reference.KiaiMode, - OmitFirstBarLine = reference.OmitFirstBarLine, ScrollSpeed = reference.ScrollSpeed, }; } diff --git a/osu.Game/Screens/Edit/Timing/RowAttributes/EffectRowAttribute.cs b/osu.Game/Screens/Edit/Timing/RowAttributes/EffectRowAttribute.cs index 3b068699ca..ad22aa81fc 100644 --- a/osu.Game/Screens/Edit/Timing/RowAttributes/EffectRowAttribute.cs +++ b/osu.Game/Screens/Edit/Timing/RowAttributes/EffectRowAttribute.cs @@ -11,18 +11,15 @@ namespace osu.Game.Screens.Edit.Timing.RowAttributes public partial class EffectRowAttribute : RowAttribute { private readonly Bindable kiaiMode; - private readonly Bindable omitBarLine; private readonly BindableNumber scrollSpeed; private AttributeText kiaiModeBubble = null!; - private AttributeText omitBarLineBubble = null!; private AttributeText text = null!; public EffectRowAttribute(EffectControlPoint effect) : base(effect, "effect") { kiaiMode = effect.KiaiModeBindable.GetBoundCopy(); - omitBarLine = effect.OmitFirstBarLineBindable.GetBoundCopy(); scrollSpeed = effect.ScrollSpeedBindable.GetBoundCopy(); } @@ -37,11 +34,9 @@ namespace osu.Game.Screens.Edit.Timing.RowAttributes }, text = new AttributeText(Point) { Width = 45 }, kiaiModeBubble = new AttributeText(Point) { Text = "kiai" }, - omitBarLineBubble = new AttributeText(Point) { Text = "no barline" }, }); kiaiMode.BindValueChanged(enabled => kiaiModeBubble.FadeTo(enabled.NewValue ? 1 : 0), true); - omitBarLine.BindValueChanged(enabled => omitBarLineBubble.FadeTo(enabled.NewValue ? 1 : 0), true); scrollSpeed.BindValueChanged(_ => updateText(), true); } diff --git a/osu.Game/Screens/Edit/Timing/RowAttributes/TimingRowAttribute.cs b/osu.Game/Screens/Edit/Timing/RowAttributes/TimingRowAttribute.cs index a6d3816bd4..577e7a3134 100644 --- a/osu.Game/Screens/Edit/Timing/RowAttributes/TimingRowAttribute.cs +++ b/osu.Game/Screens/Edit/Timing/RowAttributes/TimingRowAttribute.cs @@ -4,6 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions; +using osu.Framework.Graphics; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Timing; using osu.Game.Graphics.Sprites; @@ -14,24 +15,32 @@ namespace osu.Game.Screens.Edit.Timing.RowAttributes public partial class TimingRowAttribute : RowAttribute { private readonly BindableNumber beatLength; + private readonly Bindable omitBarLine; private readonly Bindable timeSignature; + private AttributeText omitBarLineBubble = null!; private OsuSpriteText text = null!; public TimingRowAttribute(TimingControlPoint timing) : base(timing, "timing") { timeSignature = timing.TimeSignatureBindable.GetBoundCopy(); + omitBarLine = timing.OmitFirstBarLineBindable.GetBoundCopy(); beatLength = timing.BeatLengthBindable.GetBoundCopy(); } [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider) { - Content.Add(text = new AttributeText(Point)); + Content.AddRange(new[] + { + text = new AttributeText(Point), + omitBarLineBubble = new AttributeText(Point) { Text = "no barline" }, + }); Background.Colour = colourProvider.Background4; timeSignature.BindValueChanged(_ => updateText()); + omitBarLine.BindValueChanged(enabled => omitBarLineBubble.FadeTo(enabled.NewValue ? 1 : 0), true); beatLength.BindValueChanged(_ => updateText(), true); } diff --git a/osu.Game/Screens/Edit/Timing/TimingSection.cs b/osu.Game/Screens/Edit/Timing/TimingSection.cs index d2d524ccbe..2757753b07 100644 --- a/osu.Game/Screens/Edit/Timing/TimingSection.cs +++ b/osu.Game/Screens/Edit/Timing/TimingSection.cs @@ -12,6 +12,7 @@ namespace osu.Game.Screens.Edit.Timing internal partial class TimingSection : Section { private LabelledTimeSignature timeSignature = null!; + private LabelledSwitchButton omitBarLine = null!; private BPMTextBox bpmTextEntry = null!; [BackgroundDependencyLoader] @@ -24,7 +25,8 @@ namespace osu.Game.Screens.Edit.Timing timeSignature = new LabelledTimeSignature { Label = "Time Signature" - } + }, + omitBarLine = new LabelledSwitchButton { Label = "Skip Bar Line" }, }); } @@ -33,6 +35,7 @@ namespace osu.Game.Screens.Edit.Timing base.LoadComplete(); bpmTextEntry.Current.BindValueChanged(_ => saveChanges()); + omitBarLine.Current.BindValueChanged(_ => saveChanges()); timeSignature.Current.BindValueChanged(_ => saveChanges()); void saveChanges() @@ -51,6 +54,7 @@ namespace osu.Game.Screens.Edit.Timing bpmTextEntry.Bindable = point.NewValue.BeatLengthBindable; timeSignature.Current = point.NewValue.TimeSignatureBindable; + omitBarLine.Current = point.NewValue.OmitFirstBarLineBindable; isRebinding = false; } @@ -63,7 +67,8 @@ namespace osu.Game.Screens.Edit.Timing return new TimingControlPoint { BeatLength = reference.BeatLength, - TimeSignature = reference.TimeSignature + TimeSignature = reference.TimeSignature, + OmitFirstBarLine = reference.OmitFirstBarLine, }; } From 7c84a84fbedc90032e29906090b0d7a362341fbc Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Tue, 28 Feb 2023 15:14:03 +0000 Subject: [PATCH 120/476] Removed TaikoInputBlockingMod, and instead moved code into TaikoModSingleTap --- .../Mods/TaikoInputBlockingMod.cs | 122 ------------------ .../Mods/TaikoModSingleTap.cs | 115 ++++++++++++++++- 2 files changed, 113 insertions(+), 124 deletions(-) delete mode 100644 osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs deleted file mode 100644 index 16d885b5d5..0000000000 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoInputBlockingMod.cs +++ /dev/null @@ -1,122 +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 System.Linq; -using osu.Framework.Graphics; -using osu.Framework.Input.Bindings; -using osu.Framework.Input.Events; -using osu.Game.Beatmaps.Timing; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Taiko.Objects; -using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.UI; -using osu.Game.Screens.Play; -using osu.Game.Utils; -using osu.Game.Rulesets.Taiko.UI; - -namespace osu.Game.Rulesets.Taiko.Mods -{ - public abstract partial class TaikoInputBlockingMod : Mod, IApplicableToDrawableRuleset, IUpdatableByPlayfield - { - public override double ScoreMultiplier => 1.0; - public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModRelax), typeof(TaikoModCinema) }; - public override ModType Type => ModType.Conversion; - - private DrawableTaikoRuleset ruleset = null!; - - private TaikoPlayfield playfield { get; set; } = null!; - - protected TaikoAction? LastAcceptedDonAction { get; private set; } - protected TaikoAction? LastAcceptedKatAction { get; private set; } - - /// - /// A tracker for periods where alternate should not be forced (i.e. non-gameplay periods). - /// - /// - /// This is different from in that the periods here end strictly at the first object after the break, rather than the break's end time. - /// - private PeriodTracker nonGameplayPeriods = null!; - - private IFrameStableClock gameplayClock = null!; - - public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) - { - ruleset = (DrawableTaikoRuleset)drawableRuleset; - ruleset.InputManager.Add(new InputInterceptor(this)); - playfield = (TaikoPlayfield)ruleset.Playfield; - - var periods = new List(); - - if (drawableRuleset.Objects.Any()) - { - periods.Add(new Period(int.MinValue, getValidJudgementTime(ruleset.Objects.First()) - 1)); - - foreach (BreakPeriod b in drawableRuleset.Beatmap.Breaks) - periods.Add(new Period(b.StartTime, getValidJudgementTime(ruleset.Objects.First(h => h.StartTime >= b.EndTime)) - 1)); - - static double getValidJudgementTime(HitObject hitObject) => hitObject.StartTime - hitObject.HitWindows.WindowFor(HitResult.Meh); - } - - nonGameplayPeriods = new PeriodTracker(periods); - - gameplayClock = drawableRuleset.FrameStableClock; - } - - public void Update(Playfield playfield) - { - if (!nonGameplayPeriods.IsInAny(gameplayClock.CurrentTime)) return; - - if (LastAcceptedDonAction != null) - LastAcceptedDonAction = null; - - if (LastAcceptedKatAction != null) - LastAcceptedKatAction = null; - } - - protected abstract bool CheckValidNewAction(TaikoAction action); - - private bool checkCorrectAction(TaikoAction action) - { - if (nonGameplayPeriods.IsInAny(gameplayClock.CurrentTime)) - return true; - - // If next hit object is strong, allow usage of all actions. Strong drumrolls are ignored in this check. - if (playfield.HitObjectContainer.AliveObjects.FirstOrDefault(h => h.Result?.HasResult != true)?.HitObject is TaikoStrongableHitObject hitObject - && hitObject.IsStrong - && hitObject as DrumRoll == null) - return true; - - if (CheckValidNewAction(action)) - { - if (action == TaikoAction.LeftCentre || action == TaikoAction.RightCentre) - LastAcceptedDonAction = action; - if (action == TaikoAction.LeftRim || action == TaikoAction.RightRim) - LastAcceptedKatAction = action; - return true; - } - - return false; - } - - private partial class InputInterceptor : Component, IKeyBindingHandler - { - private readonly TaikoInputBlockingMod mod; - - public InputInterceptor(TaikoInputBlockingMod mod) - { - this.mod = mod; - } - - public bool OnPressed(KeyBindingPressEvent e) - // if the pressed action is incorrect, block it from reaching gameplay. - => !mod.checkCorrectAction(e.Action); - - public void OnReleased(KeyBindingReleaseEvent e) - { - } - } - } -} diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs index 666e550c93..fe3b81d1d2 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs @@ -2,15 +2,126 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Localisation; +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Graphics; +using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; +using osu.Game.Beatmaps.Timing; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; +using osu.Game.Screens.Play; +using osu.Game.Utils; +using osu.Game.Rulesets.Taiko.UI; namespace osu.Game.Rulesets.Taiko.Mods { - public class TaikoModSingleTap : TaikoInputBlockingMod + public class TaikoModSingleTap : Mod, IApplicableToDrawableRuleset, IUpdatableByPlayfield { public override string Name => @"Single Tap"; public override string Acronym => @"SG"; public override LocalisableString Description => @"One key for dons, one key for kats."; - protected override bool CheckValidNewAction(TaikoAction action) => LastAcceptedDonAction == null || LastAcceptedDonAction == action || LastAcceptedKatAction == null || LastAcceptedKatAction == action; + protected bool CheckValidNewAction(TaikoAction action) => LastAcceptedDonAction == null || LastAcceptedDonAction == action || LastAcceptedKatAction == null || LastAcceptedKatAction == action; + + public override double ScoreMultiplier => 1.0; + public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModRelax), typeof(TaikoModCinema) }; + public override ModType Type => ModType.Conversion; + + private DrawableTaikoRuleset ruleset = null!; + + private TaikoPlayfield playfield { get; set; } = null!; + + protected TaikoAction? LastAcceptedDonAction { get; private set; } + protected TaikoAction? LastAcceptedKatAction { get; private set; } + + /// + /// A tracker for periods where alternate should not be forced (i.e. non-gameplay periods). + /// + /// + /// This is different from in that the periods here end strictly at the first object after the break, rather than the break's end time. + /// + private PeriodTracker nonGameplayPeriods = null!; + + private IFrameStableClock gameplayClock = null!; + + public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) + { + ruleset = (DrawableTaikoRuleset)drawableRuleset; + ruleset.InputManager.Add(new InputInterceptor(this)); + playfield = (TaikoPlayfield)ruleset.Playfield; + + var periods = new List(); + + if (drawableRuleset.Objects.Any()) + { + periods.Add(new Period(int.MinValue, getValidJudgementTime(ruleset.Objects.First()) - 1)); + + foreach (BreakPeriod b in drawableRuleset.Beatmap.Breaks) + periods.Add(new Period(b.StartTime, getValidJudgementTime(ruleset.Objects.First(h => h.StartTime >= b.EndTime)) - 1)); + + static double getValidJudgementTime(HitObject hitObject) => hitObject.StartTime - hitObject.HitWindows.WindowFor(HitResult.Meh); + } + + nonGameplayPeriods = new PeriodTracker(periods); + + gameplayClock = drawableRuleset.FrameStableClock; + } + + public void Update(Playfield playfield) + { + if (!nonGameplayPeriods.IsInAny(gameplayClock.CurrentTime)) return; + + if (LastAcceptedDonAction != null) + LastAcceptedDonAction = null; + + if (LastAcceptedKatAction != null) + LastAcceptedKatAction = null; + } + + private bool checkCorrectAction(TaikoAction action) + { + if (nonGameplayPeriods.IsInAny(gameplayClock.CurrentTime)) + return true; + + // If next hit object is strong, allow usage of all actions. Strong drumrolls are ignored in this check. + if (playfield.HitObjectContainer.AliveObjects.FirstOrDefault(h => h.Result?.HasResult != true)?.HitObject is TaikoStrongableHitObject hitObject + && hitObject.IsStrong + && hitObject as DrumRoll == null) + return true; + + if (CheckValidNewAction(action)) + { + if (action == TaikoAction.LeftCentre || action == TaikoAction.RightCentre) + LastAcceptedDonAction = action; + if (action == TaikoAction.LeftRim || action == TaikoAction.RightRim) + LastAcceptedKatAction = action; + return true; + } + + return false; + } + + private partial class InputInterceptor : Component, IKeyBindingHandler + { + private readonly TaikoModSingleTap mod; + + public InputInterceptor(TaikoModSingleTap mod) + { + this.mod = mod; + } + + public bool OnPressed(KeyBindingPressEvent e) + // if the pressed action is incorrect, block it from reaching gameplay. + => !mod.checkCorrectAction(e.Action); + + public void OnReleased(KeyBindingReleaseEvent e) + { + } + } } } From 3aa8c81c5d62c7fae355e8a161f8891b13d06ad3 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Tue, 28 Feb 2023 15:20:53 +0000 Subject: [PATCH 121/476] Added missing mod incompatabilities --- osu.Game.Rulesets.Taiko/Mods/TaikoModAutoplay.cs | 4 ++++ osu.Game.Rulesets.Taiko/Mods/TaikoModCinema.cs | 4 ++++ osu.Game.Rulesets.Taiko/Mods/TaikoModRelax.cs | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModAutoplay.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModAutoplay.cs index 4b74b4991e..1e09061859 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModAutoplay.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModAutoplay.cs @@ -1,7 +1,9 @@ // 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.Beatmaps; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Taiko.Replays; @@ -12,5 +14,7 @@ namespace osu.Game.Rulesets.Taiko.Mods { public override ModReplayData CreateReplayData(IBeatmap beatmap, IReadOnlyList mods) => new ModReplayData(new TaikoAutoGenerator(beatmap).Generate(), new ModCreatedUser { Username = "mekkadosu!" }); + + public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(TaikoModSingleTap) }).ToArray(); } } diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModCinema.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModCinema.cs index fee0cb2744..c268087f0a 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModCinema.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModCinema.cs @@ -1,7 +1,9 @@ // 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.Beatmaps; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Taiko.Objects; @@ -13,5 +15,7 @@ namespace osu.Game.Rulesets.Taiko.Mods { public override ModReplayData CreateReplayData(IBeatmap beatmap, IReadOnlyList mods) => new ModReplayData(new TaikoAutoGenerator(beatmap).Generate(), new ModCreatedUser { Username = "mekkadosu!" }); + + public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(TaikoModSingleTap) }).ToArray(); } } diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModRelax.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModRelax.cs index d1e9ab1428..f97a2d2027 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModRelax.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModRelax.cs @@ -1,6 +1,8 @@ // 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 osu.Framework.Localisation; using osu.Game.Rulesets.Mods; @@ -9,5 +11,7 @@ namespace osu.Game.Rulesets.Taiko.Mods public class TaikoModRelax : ModRelax { public override LocalisableString Description => @"No ninja-like spinners, demanding drumrolls or unexpected katu's."; + + public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(TaikoModSingleTap) }).ToArray(); } } From 616f7b3f64a31bd265112c126d6a0844b204ae4a Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Tue, 28 Feb 2023 15:37:46 +0000 Subject: [PATCH 122/476] Simplified a type check --- osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs index fe3b81d1d2..67921fc7a9 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs @@ -91,7 +91,7 @@ namespace osu.Game.Rulesets.Taiko.Mods // If next hit object is strong, allow usage of all actions. Strong drumrolls are ignored in this check. if (playfield.HitObjectContainer.AliveObjects.FirstOrDefault(h => h.Result?.HasResult != true)?.HitObject is TaikoStrongableHitObject hitObject && hitObject.IsStrong - && hitObject as DrumRoll == null) + && !(hitObject is DrumRoll)) return true; if (CheckValidNewAction(action)) From 0a76c8ee7f4fa95f91f03bb585587d2158dcfc6f Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Tue, 28 Feb 2023 15:41:06 +0000 Subject: [PATCH 123/476] Further simplified a type check --- osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs index 67921fc7a9..ff8e4dc298 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs @@ -91,7 +91,7 @@ namespace osu.Game.Rulesets.Taiko.Mods // If next hit object is strong, allow usage of all actions. Strong drumrolls are ignored in this check. if (playfield.HitObjectContainer.AliveObjects.FirstOrDefault(h => h.Result?.HasResult != true)?.HitObject is TaikoStrongableHitObject hitObject && hitObject.IsStrong - && !(hitObject is DrumRoll)) + && hitObject is not DrumRoll) return true; if (CheckValidNewAction(action)) From d4bb08a4408250754acc6866b5e21b359ef7cc69 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Tue, 28 Feb 2023 18:38:09 +0000 Subject: [PATCH 124/476] Fixed CodeInspector complaining --- osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs index ff8e4dc298..f08e83d76c 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs @@ -20,7 +20,7 @@ using osu.Game.Rulesets.Taiko.UI; namespace osu.Game.Rulesets.Taiko.Mods { - public class TaikoModSingleTap : Mod, IApplicableToDrawableRuleset, IUpdatableByPlayfield + public partial class TaikoModSingleTap : Mod, IApplicableToDrawableRuleset, IUpdatableByPlayfield { public override string Name => @"Single Tap"; public override string Acronym => @"SG"; From bb5791a7140ba6a617657238684ebddf898f5f7b Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Wed, 1 Mar 2023 21:41:01 -0800 Subject: [PATCH 125/476] Fix overlay sidebars not scrolling to end due to parent scroll view --- osu.Game/Overlays/OverlaySidebar.cs | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/OverlaySidebar.cs b/osu.Game/Overlays/OverlaySidebar.cs index 87ce1b7e8c..b8c0032e87 100644 --- a/osu.Game/Overlays/OverlaySidebar.cs +++ b/osu.Game/Overlays/OverlaySidebar.cs @@ -8,6 +8,7 @@ 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.Containers; namespace osu.Game.Overlays @@ -39,7 +40,7 @@ namespace osu.Game.Overlays { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Right = -3 }, // Compensate for scrollbar margin - Child = new OsuScrollContainer + Child = new SidebarScrollContainer { RelativeSizeAxes = Axes.Both, Child = new Container @@ -74,5 +75,30 @@ namespace osu.Game.Overlays [NotNull] protected virtual Drawable CreateContent() => Empty(); + + private partial class SidebarScrollContainer : OsuScrollContainer + { + protected override bool OnScroll(ScrollEvent e) + { + if (e.ScrollDelta.Y > 0 && IsScrolledToStart()) + return false; + + if (e.ScrollDelta.Y < 0 && IsScrolledToEnd()) + return false; + + return base.OnScroll(e); + } + + protected override bool OnDragStart(DragStartEvent e) + { + if (e.Delta.Y > 0 && IsScrolledToStart()) + return false; + + if (e.Delta.Y < 0 && IsScrolledToEnd()) + return false; + + return base.OnDragStart(e); + } + } } } From 6ff1fb6b1aac475a9a2d217ad0750d075d275be0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 2 Mar 2023 18:57:54 +0900 Subject: [PATCH 126/476] Adjust sizing to allow for timing point information now being longer than before --- osu.Game/Screens/Edit/Timing/ControlPointTable.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Timing/ControlPointTable.cs b/osu.Game/Screens/Edit/Timing/ControlPointTable.cs index 08b2ce8562..b078e3fa44 100644 --- a/osu.Game/Screens/Edit/Timing/ControlPointTable.cs +++ b/osu.Game/Screens/Edit/Timing/ControlPointTable.cs @@ -26,7 +26,7 @@ namespace osu.Game.Screens.Edit.Timing [Resolved] private EditorClock clock { get; set; } = null!; - public const float TIMING_COLUMN_WIDTH = 230; + public const float TIMING_COLUMN_WIDTH = 300; public IEnumerable ControlGroups { From dc669835e264546a4e6555ae15417c2a17a0d2bb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 3 Mar 2023 15:25:55 +0900 Subject: [PATCH 127/476] Show count of visible beatmaps at song select --- osu.Game/Localisation/SongSelectStrings.cs | 7 +- osu.Game/Screens/Select/BeatmapCarousel.cs | 12 ++ osu.Game/Screens/Select/FilterControl.cs | 126 ++++++++++++--------- osu.Game/Screens/Select/SongSelect.cs | 2 + 4 files changed, 90 insertions(+), 57 deletions(-) diff --git a/osu.Game/Localisation/SongSelectStrings.cs b/osu.Game/Localisation/SongSelectStrings.cs index 12f70cd967..046aec6bcf 100644 --- a/osu.Game/Localisation/SongSelectStrings.cs +++ b/osu.Game/Localisation/SongSelectStrings.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// 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.Localisation; @@ -19,6 +19,11 @@ namespace osu.Game.Localisation /// public static LocalisableString LocallyModifiedTooltip => new TranslatableString(getKey(@"locally_modified_tooltip"), @"Has been locally modified"); + /// + /// "{0} beatmaps displayed" + /// + public static LocalisableString BeatmapsDisplayed(int arg0) => new TranslatableString(getKey(@"beatmaps_displayed"), @"{0:#,0} beatmaps displayed", arg0); + private static string getKey(string key) => $@"{prefix}:{key}"; } } diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 774ecc2c9c..68d3247275 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -49,6 +49,11 @@ namespace osu.Game.Screens.Select /// public Action? BeatmapSetsChanged; + /// + /// Triggered after filter conditions have finished being applied to the model hierarchy. + /// + public Action? FilterApplied; + /// /// The currently selected beatmap. /// @@ -56,6 +61,11 @@ namespace osu.Game.Screens.Select private CarouselBeatmap? selectedBeatmap => selectedBeatmapSet?.Beatmaps.FirstOrDefault(s => s.State.Value == CarouselItemState.Selected); + /// + /// The total count of non-filtered beatmaps displayed. + /// + public int CountDisplayed => beatmapSets.Where(s => !s.Filtered.Value).Sum(s => s.Beatmaps.Count(b => !b.Filtered.Value)); + /// /// The currently selected beatmap set. /// @@ -639,6 +649,8 @@ namespace osu.Game.Screens.Select if (alwaysResetScrollPosition || !Scroll.UserScrolling) ScrollToSelected(true); + + FilterApplied?.Invoke(); } } diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index 2f21ffbe6a..b5469abffe 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; +using osu.Framework.Localisation; using osu.Game.Collections; using osu.Game.Configuration; using osu.Game.Graphics; @@ -27,13 +28,20 @@ namespace osu.Game.Screens.Select { public partial class FilterControl : Container { - public const float HEIGHT = 2 * side_margin + 85; - private const float side_margin = 20; + public const float HEIGHT = 2 * side_margin + 120; + + private const float side_margin = 10; public Action FilterChanged; public Bindable CurrentTextSearch => searchTextBox.Current; + public LocalisableString InformationalText + { + get => filterText.Text; + set => filterText.Text = value; + } + private OsuTabControl sortTabs; private Bindable sortMode; @@ -44,6 +52,8 @@ namespace osu.Game.Screens.Select private CollectionDropdown collectionDropdown; + private OsuSpriteText filterText; + public FilterCriteria CreateCriteria() { string query = searchTextBox.Text; @@ -99,72 +109,76 @@ namespace osu.Game.Screens.Select { RelativeSizeAxes = Axes.Both, Spacing = new Vector2(0, 5), - Children = new[] + Children = new Drawable[] { + searchTextBox = new SeekLimitedSearchTextBox { RelativeSizeAxes = Axes.X }, new Container { RelativeSizeAxes = Axes.X, - Height = 60, + AutoSizeAxes = Axes.Y, + AutoSizeDuration = 200, + AutoSizeEasing = Easing.OutQuint, Children = new Drawable[] { - searchTextBox = new SeekLimitedSearchTextBox { RelativeSizeAxes = Axes.X }, - new Box + filterText = new OsuSpriteText { - RelativeSizeAxes = Axes.X, - Height = 1, - Colour = OsuColour.Gray(80), - Origin = Anchor.BottomLeft, - Anchor = Anchor.BottomLeft, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Font = OsuFont.Default.With(size: 12), }, - new GridContainer + } + }, + new Box + { + RelativeSizeAxes = Axes.X, + Height = 1, + Colour = OsuColour.Gray(80), + }, + new GridContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.Absolute, OsuTabControl.HORIZONTAL_SPACING), + new Dimension(), + new Dimension(GridSizeMode.Absolute, OsuTabControl.HORIZONTAL_SPACING), + new Dimension(GridSizeMode.AutoSize), + }, + RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) }, + Content = new[] + { + new[] { - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - ColumnDimensions = new[] + new OsuSpriteText { - new Dimension(GridSizeMode.AutoSize), - new Dimension(GridSizeMode.Absolute, OsuTabControl.HORIZONTAL_SPACING), - new Dimension(), - new Dimension(GridSizeMode.Absolute, OsuTabControl.HORIZONTAL_SPACING), - new Dimension(GridSizeMode.AutoSize), + Text = SortStrings.Default, + Font = OsuFont.GetFont(size: 14), + Margin = new MarginPadding(5), + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, }, - RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) }, - Content = new[] + Empty(), + sortTabs = new OsuTabControl { - new[] - { - new OsuSpriteText - { - Text = SortStrings.Default, - Font = OsuFont.GetFont(size: 14), - Margin = new MarginPadding(5), - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - }, - Empty(), - sortTabs = new OsuTabControl - { - RelativeSizeAxes = Axes.X, - Height = 24, - AutoSort = true, - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - AccentColour = colours.GreenLight, - Current = { BindTarget = sortMode } - }, - Empty(), - new OsuTabControlCheckbox - { - Text = "Show converted", - Current = config.GetBindable(OsuSetting.ShowConvertedBeatmaps), - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - }, - } - } - }, + RelativeSizeAxes = Axes.X, + Height = 24, + AutoSort = true, + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + AccentColour = colours.GreenLight, + Current = { BindTarget = sortMode } + }, + Empty(), + new OsuTabControlCheckbox + { + Text = "Show converted", + Current = config.GetBindable(OsuSetting.ShowConvertedBeatmaps), + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + }, + } } }, new Container diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 661eec8e97..9f8c3f1a2c 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -40,6 +40,7 @@ using osu.Game.Skinning; using osuTK; using osuTK.Graphics; using osuTK.Input; +using osu.Game.Localisation; namespace osu.Game.Screens.Select { @@ -162,6 +163,7 @@ namespace osu.Game.Screens.Select BleedBottom = Footer.HEIGHT, SelectionChanged = updateSelectedBeatmap, BeatmapSetsChanged = carouselBeatmapsLoaded, + FilterApplied = () => FilterControl.InformationalText = SongSelectStrings.BeatmapsDisplayed(Carousel.CountDisplayed), GetRecommendedBeatmap = s => recommender?.GetRecommendedBeatmap(s), }, c => carouselContainer.Child = c); From 82293c0c868d07b0a37237db787353431c280519 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 3 Mar 2023 16:28:09 +0900 Subject: [PATCH 128/476] Don't filter away results with missing data when using "Date Submitted" or "Date Ranked" sort modes From a user's perspective, changing a sort / order mode shouldn't filter away results, but we were doing this. In terms of UX expectations, in stable this kind of scenario would results in a group being added to the end of son select with "Not ranked" or "Unknown". I think we should aim to match this eventually. --- .../SongSelect/TestSceneBeatmapCarousel.cs | 11 +++++-- .../Select/Carousel/CarouselBeatmapSet.cs | 29 ++++++++----------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs index 65ce0429fc..5285c485ea 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs @@ -532,15 +532,20 @@ namespace osu.Game.Tests.Visual.SongSelect loadBeatmaps(sets); AddStep("Sort by date submitted", () => carousel.Filter(new FilterCriteria { Sort = SortMode.DateSubmitted }, false)); - checkVisibleItemCount(diff: false, count: 8); + checkVisibleItemCount(diff: false, count: 20); checkVisibleItemCount(diff: true, count: 5); + AddStep("Sort by date submitted and string", () => carousel.Filter(new FilterCriteria { Sort = SortMode.DateSubmitted, SearchText = zzz_string }, false)); - checkVisibleItemCount(diff: false, count: 3); + checkVisibleItemCount(diff: false, count: 5); checkVisibleItemCount(diff: true, count: 5); + + AddAssert("missing date submitted are at end", + () => carousel.Items.OfType().TakeLast(2).All(i => i.Item is CarouselBeatmapSet s && s.BeatmapSet.DateSubmitted == null)); + AddAssert("rest are at start", () => carousel.Items.OfType().Take(3).All(i => i.Item is CarouselBeatmapSet s && s.BeatmapSet.DateSubmitted != null)); } [Test] @@ -1129,7 +1134,7 @@ namespace osu.Game.Tests.Visual.SongSelect { // until step required as we are querying against alive items, which are loaded asynchronously inside DrawableCarouselBeatmapSet. AddUntilStep($"{count} {(diff ? "diffs" : "sets")} visible", () => - carousel.Items.Count(s => (diff ? s.Item is CarouselBeatmap : s.Item is CarouselBeatmapSet) && s.Item.Visible) == count); + carousel.Items.Count(s => (diff ? s.Item is CarouselBeatmap : s.Item is CarouselBeatmapSet) && s.Item.Visible), () => Is.EqualTo(count)); } private void checkSelectionIsCentered() diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs index c52b81f915..63493a3b02 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs @@ -61,7 +61,7 @@ namespace osu.Game.Screens.Select.Carousel if (!(other is CarouselBeatmapSet otherSet)) return base.CompareTo(criteria, other); - int comparison = 0; + int comparison; switch (criteria.Sort) { @@ -87,11 +87,7 @@ namespace osu.Game.Screens.Select.Carousel break; case SortMode.DateRanked: - // Beatmaps which have no ranked date should already be filtered away in this mode. - if (BeatmapSet.DateRanked == null || otherSet.BeatmapSet.DateRanked == null) - break; - - comparison = otherSet.BeatmapSet.DateRanked.Value.CompareTo(BeatmapSet.DateRanked.Value); + comparison = compareNullableDateTimeOffset(BeatmapSet.DateRanked, otherSet.BeatmapSet.DateRanked); break; case SortMode.LastPlayed: @@ -111,11 +107,7 @@ namespace osu.Game.Screens.Select.Carousel break; case SortMode.DateSubmitted: - // Beatmaps which have no submitted date should already be filtered away in this mode. - if (BeatmapSet.DateSubmitted == null || otherSet.BeatmapSet.DateSubmitted == null) - break; - - comparison = otherSet.BeatmapSet.DateSubmitted.Value.CompareTo(BeatmapSet.DateSubmitted.Value); + comparison = compareNullableDateTimeOffset(BeatmapSet.DateSubmitted, otherSet.BeatmapSet.DateSubmitted); break; } @@ -132,6 +124,14 @@ namespace osu.Game.Screens.Select.Carousel return otherSet.BeatmapSet.ID.CompareTo(BeatmapSet.ID); } + private static int compareNullableDateTimeOffset(DateTimeOffset? x, DateTimeOffset? y) + { + if (x == null) return 1; + if (y == null) return -1; + + return y.Value.CompareTo(x.Value); + } + /// /// All beatmaps which are not filtered and valid for display. /// @@ -153,12 +153,7 @@ namespace osu.Game.Screens.Select.Carousel { base.Filter(criteria); - bool filtered = Items.All(i => i.Filtered.Value); - - filtered |= criteria.Sort == SortMode.DateRanked && BeatmapSet.DateRanked == null; - filtered |= criteria.Sort == SortMode.DateSubmitted && BeatmapSet.DateSubmitted == null; - - Filtered.Value = filtered; + Filtered.Value = Items.All(i => i.Filtered.Value); } public override string ToString() => BeatmapSet.ToString(); From 7a092ea995436cbd987534a86648af05aa4161c4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 3 Mar 2023 17:18:14 +0900 Subject: [PATCH 129/476] Add better coverage and more comments explaining the structure of test setup --- .../SongSelect/TestSceneBeatmapCarousel.cs | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs index 5285c485ea..deead47770 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs @@ -516,15 +516,20 @@ namespace osu.Game.Tests.Visual.SongSelect { sets.Clear(); - for (int i = 0; i < 20; i++) + for (int i = 0; i < 10; i++) { var set = TestResources.CreateTestBeatmapSetInfo(5); - if (i >= 2 && i < 10) + // A total of 6 sets have date submitted (4 don't) + // A total of 5 sets have artist string (3 of which also have date submitted) + + if (i >= 2 && i < 8) // i = 2, 3, 4, 5, 6, 7 have submitted date set.DateSubmitted = DateTimeOffset.Now.AddMinutes(i); - if (i < 5) + if (i < 5) // i = 0, 1, 2, 3, 4 have matching string set.Beatmaps.ForEach(b => b.Metadata.Artist = zzz_string); + set.Beatmaps.ForEach(b => b.Metadata.Title = $"submitted: {set.DateSubmitted}"); + sets.Add(set); } }); @@ -532,9 +537,14 @@ namespace osu.Game.Tests.Visual.SongSelect loadBeatmaps(sets); AddStep("Sort by date submitted", () => carousel.Filter(new FilterCriteria { Sort = SortMode.DateSubmitted }, false)); - checkVisibleItemCount(diff: false, count: 20); + checkVisibleItemCount(diff: false, count: 10); checkVisibleItemCount(diff: true, count: 5); + AddAssert("missing date are at end", + () => carousel.Items.OfType().Reverse().TakeWhile(i => i.Item is CarouselBeatmapSet s && s.BeatmapSet.DateSubmitted == null).Count(), () => Is.EqualTo(4)); + AddAssert("rest are at start", () => carousel.Items.OfType().TakeWhile(i => i.Item is CarouselBeatmapSet s && s.BeatmapSet.DateSubmitted != null).Count(), + () => Is.EqualTo(6)); + AddStep("Sort by date submitted and string", () => carousel.Filter(new FilterCriteria { Sort = SortMode.DateSubmitted, @@ -543,9 +553,10 @@ namespace osu.Game.Tests.Visual.SongSelect checkVisibleItemCount(diff: false, count: 5); checkVisibleItemCount(diff: true, count: 5); - AddAssert("missing date submitted are at end", - () => carousel.Items.OfType().TakeLast(2).All(i => i.Item is CarouselBeatmapSet s && s.BeatmapSet.DateSubmitted == null)); - AddAssert("rest are at start", () => carousel.Items.OfType().Take(3).All(i => i.Item is CarouselBeatmapSet s && s.BeatmapSet.DateSubmitted != null)); + AddAssert("missing date are at end", + () => carousel.Items.OfType().Reverse().TakeWhile(i => i.Item is CarouselBeatmapSet s && s.BeatmapSet.DateSubmitted == null).Count(), () => Is.EqualTo(2)); + AddAssert("rest are at start", () => carousel.Items.OfType().TakeWhile(i => i.Item is CarouselBeatmapSet s && s.BeatmapSet.DateSubmitted != null).Count(), + () => Is.EqualTo(3)); } [Test] From a14dede0360836429a954f57b4fc7aec1eb7c9f7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 3 Mar 2023 17:18:33 +0900 Subject: [PATCH 130/476] Ensure `TestBeatmapCarousel.Items` only returns actually visible items (and in correct order) Turns out that items could be in an order that isn't the same as how things look on the carousel, so this change ensures that for testing purposes they are sorted by Y position. Also uses the `CarouselItem.Visible` flag to ensure that we don't have to wait for drawable fade transitions after a filter operation. --- osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs index deead47770..61a8322ee3 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs @@ -1206,8 +1206,11 @@ namespace osu.Game.Tests.Visual.SongSelect { get { - foreach (var item in Scroll.Children) + foreach (var item in Scroll.Children.OrderBy(c => c.Y)) { + if (item.Item?.Visible != true) + continue; + yield return item; if (item is DrawableCarouselBeatmapSet set) From 2b7111867c348231a5bfb1a36f427e2cb1c6fc96 Mon Sep 17 00:00:00 2001 From: mk56-spn Date: Fri, 3 Mar 2023 15:12:20 +0100 Subject: [PATCH 131/476] Adjust reversion header text naming. --- osu.Game/Localisation/SkinEditorStrings.cs | 4 ++-- osu.Game/Overlays/SkinEditor/SkinEditor.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Localisation/SkinEditorStrings.cs b/osu.Game/Localisation/SkinEditorStrings.cs index b83a105c14..ea0c90e547 100644 --- a/osu.Game/Localisation/SkinEditorStrings.cs +++ b/osu.Game/Localisation/SkinEditorStrings.cs @@ -45,9 +45,9 @@ namespace osu.Game.Localisation public static LocalisableString CurrentlyEditing => new TranslatableString(getKey(@"currently_editing"), @"Currently editing"); /// - /// "Revert to default?" + /// "Revert to default." /// - public static LocalisableString RevertToDefault => new TranslatableString(getKey(@"revert_to_default"), @"Revert to default?"); + public static LocalisableString RevertToDefaultDescription => new TranslatableString(getKey(@"revert_to_default"), @"Revert to default."); /// /// "The skin will return to the state it was in upon import" diff --git a/osu.Game/Overlays/SkinEditor/SkinEditor.cs b/osu.Game/Overlays/SkinEditor/SkinEditor.cs index 770a3c5930..5ffdaa11d4 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditor.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditor.cs @@ -635,7 +635,7 @@ namespace osu.Game.Overlays.SkinEditor { public ResetConfirmDialog(Action reset) { - HeaderText = SkinEditorStrings.RevertToDefault; + HeaderText = SkinEditorStrings.RevertToDefaultDescription; BodyText = SkinEditorStrings.ResetDialogue; Icon = FontAwesome.Solid.ExclamationTriangle; From 3bff415909ea42d64b145c32bbb8f71375c0ca0d Mon Sep 17 00:00:00 2001 From: mk56-spn Date: Fri, 3 Mar 2023 15:15:14 +0100 Subject: [PATCH 132/476] Change `ResetDialogue` text --- osu.Game/Localisation/SkinEditorStrings.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Localisation/SkinEditorStrings.cs b/osu.Game/Localisation/SkinEditorStrings.cs index ea0c90e547..a6b59418fd 100644 --- a/osu.Game/Localisation/SkinEditorStrings.cs +++ b/osu.Game/Localisation/SkinEditorStrings.cs @@ -50,9 +50,9 @@ namespace osu.Game.Localisation public static LocalisableString RevertToDefaultDescription => new TranslatableString(getKey(@"revert_to_default"), @"Revert to default."); /// - /// "The skin will return to the state it was in upon import" + /// "Return the skin to its default state" /// - public static LocalisableString ResetDialogue => new TranslatableString(getKey(@"the_skin_will_return_to"), @"The skin will return to the state it was in upon import"); + public static LocalisableString ResetDialogue => new TranslatableString(getKey(@"return_the_skin_to_its"), @"Return the skin to its default state"); private static string getKey(string key) => $@"{prefix}:{key}"; } From ec95d0031354d3ff900dcc24fce84a113fe4cf47 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Fri, 3 Mar 2023 16:57:09 +0000 Subject: [PATCH 133/476] Added Taiko Single Tap test scene --- .../Mods/TestSceneTaikoModSingleTap.cs | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs new file mode 100644 index 0000000000..24cad6fee3 --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs @@ -0,0 +1,107 @@ +// 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 NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Replays; +using osu.Game.Rulesets.Taiko.Mods; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Taiko.Replays; + +namespace osu.Game.Rulesets.Taiko.Tests.Mods +{ + public partial class TestSceneTaikoModSingleTap : TaikoModTestScene + { + [Test] + public void TestInputAlternate() => CreateModTest(new ModTestData + { + Mod = new TaikoModSingleTap(), + Autoplay = false, + Beatmap = new Beatmap + { + HitObjects = new List + { + new Hit + { + StartTime = 100, + Type = HitType.Rim + }, + new Hit + { + StartTime = 300, + Type = HitType.Rim + }, + new Hit + { + StartTime = 500, + Type = HitType.Rim + }, + new Hit + { + StartTime = 700, + Type = HitType.Rim + }, + }, + }, + ReplayFrames = new List + { + new TaikoReplayFrame(100, TaikoAction.RightRim), + new TaikoReplayFrame(120), + new TaikoReplayFrame(300, TaikoAction.LeftRim), + new TaikoReplayFrame(320), + new TaikoReplayFrame(500, TaikoAction.RightRim), + new TaikoReplayFrame(520), + new TaikoReplayFrame(700, TaikoAction.LeftRim), + new TaikoReplayFrame(720), + }, + PassCondition = () => Player.ScoreProcessor.Combo.Value == 4 + }); + + [Test] + public void TestInputSameKey() => CreateModTest(new ModTestData + { + Mod = new TaikoModSingleTap(), + Autoplay = false, + Beatmap = new Beatmap + { + HitObjects = new List + { + new Hit + { + StartTime = 100, + Type = HitType.Rim + }, + new Hit + { + StartTime = 300, + Type = HitType.Rim + }, + new Hit + { + StartTime = 500, + Type = HitType.Rim + }, + new Hit + { + StartTime = 700, + Type = HitType.Rim + }, + }, + }, + ReplayFrames = new List + { + new TaikoReplayFrame(100, TaikoAction.RightRim), + new TaikoReplayFrame(120), + new TaikoReplayFrame(300, TaikoAction.RightRim), + new TaikoReplayFrame(320), + new TaikoReplayFrame(500, TaikoAction.RightRim), + new TaikoReplayFrame(520), + new TaikoReplayFrame(700, TaikoAction.RightRim), + new TaikoReplayFrame(720), + }, + PassCondition = () => Player.ScoreProcessor.Combo.Value == 4 + }); + } +} From 865f785f506a69020dce0cb9d8e913fd7f652960 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Fri, 3 Mar 2023 17:13:39 +0000 Subject: [PATCH 134/476] Fixed an issue where Taiko's Single Tap could allow alternation under very specific circumstances --- osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs index f08e83d76c..eed4e8a280 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs @@ -26,7 +26,14 @@ namespace osu.Game.Rulesets.Taiko.Mods public override string Acronym => @"SG"; public override LocalisableString Description => @"One key for dons, one key for kats."; - protected bool CheckValidNewAction(TaikoAction action) => LastAcceptedDonAction == null || LastAcceptedDonAction == action || LastAcceptedKatAction == null || LastAcceptedKatAction == action; + protected bool CheckValidNewAction(TaikoAction action) + { + if (action == TaikoAction.LeftCentre || action == TaikoAction.RightCentre) + return LastAcceptedDonAction == null || LastAcceptedDonAction == action; + if (action == TaikoAction.LeftRim || action == TaikoAction.RightRim) + return LastAcceptedKatAction == null || LastAcceptedKatAction == action; + return true; + } public override double ScoreMultiplier => 1.0; public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModRelax), typeof(TaikoModCinema) }; From 4dbe589de2423749e4b088dc1bd3357db972cda2 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Fri, 3 Mar 2023 17:18:02 +0000 Subject: [PATCH 135/476] Fixed Taiko Single Tap test pass condition --- .../Mods/TestSceneTaikoModSingleTap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs index 24cad6fee3..75d4ef5c7f 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs @@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Mods new TaikoReplayFrame(700, TaikoAction.LeftRim), new TaikoReplayFrame(720), }, - PassCondition = () => Player.ScoreProcessor.Combo.Value == 4 + PassCondition = () => Player.ScoreProcessor.Combo.Value == 0 && Player.ScoreProcessor.HighestCombo.Value == 1 }); [Test] From 50b0fca2643a924344a327282e823f3c19a723e5 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Fri, 3 Mar 2023 18:11:29 +0000 Subject: [PATCH 136/476] Code formatting --- osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs index eed4e8a280..1130338521 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs @@ -32,6 +32,7 @@ namespace osu.Game.Rulesets.Taiko.Mods return LastAcceptedDonAction == null || LastAcceptedDonAction == action; if (action == TaikoAction.LeftRim || action == TaikoAction.RightRim) return LastAcceptedKatAction == null || LastAcceptedKatAction == action; + return true; } @@ -107,6 +108,7 @@ namespace osu.Game.Rulesets.Taiko.Mods LastAcceptedDonAction = action; if (action == TaikoAction.LeftRim || action == TaikoAction.RightRim) LastAcceptedKatAction = action; + return true; } From 91d206e8d2c0553266e5605ab454d0862c3e604b Mon Sep 17 00:00:00 2001 From: OliBomby Date: Fri, 3 Mar 2023 19:21:50 +0100 Subject: [PATCH 137/476] Optimised GetSearchableTerms Reduced memory allocations to 1 --- osu.Game/Beatmaps/BeatmapInfoExtensions.cs | 27 ++++++++++++--- .../Select/Carousel/CarouselBeatmap.cs | 34 ++++++++++++++++--- 2 files changed, 52 insertions(+), 9 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapInfoExtensions.cs b/osu.Game/Beatmaps/BeatmapInfoExtensions.cs index eab66b9857..e14a44ec58 100644 --- a/osu.Game/Beatmaps/BeatmapInfoExtensions.cs +++ b/osu.Game/Beatmaps/BeatmapInfoExtensions.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. -using System.Linq; +using System; using osu.Framework.Localisation; namespace osu.Game.Beatmaps @@ -29,10 +29,29 @@ namespace osu.Game.Beatmaps return new RomanisableString($"{metadata.GetPreferred(true)}".Trim(), $"{metadata.GetPreferred(false)}".Trim()); } - public static string[] GetSearchableTerms(this IBeatmapInfo beatmapInfo) => new[] + public static ReadOnlySpan GetSearchableTerms(this IBeatmapInfo beatmapInfo) { - beatmapInfo.DifficultyName - }.Concat(beatmapInfo.Metadata.GetSearchableTerms()).Where(s => !string.IsNullOrEmpty(s)).ToArray(); + Span terms = new string[8]; + int i = 0; + if (!string.IsNullOrEmpty(beatmapInfo.DifficultyName)) + terms[i++] = beatmapInfo.DifficultyName; + var metadata = beatmapInfo.Metadata; + if (!string.IsNullOrEmpty(metadata.Author.Username)) + terms[i++] = metadata.Author.Username; + if (!string.IsNullOrEmpty(metadata.Artist)) + terms[i++] = metadata.Artist; + if (!string.IsNullOrEmpty(metadata.ArtistUnicode)) + terms[i++] = metadata.ArtistUnicode; + if (!string.IsNullOrEmpty(metadata.Title)) + terms[i++] = metadata.Title; + if (!string.IsNullOrEmpty(metadata.TitleUnicode)) + terms[i++] = metadata.TitleUnicode; + if (!string.IsNullOrEmpty(metadata.Source)) + terms[i++] = metadata.Source; + if (!string.IsNullOrEmpty(metadata.Tags)) + terms[i++] = metadata.Tags; + return terms[..i]; + } private static string getVersionString(IBeatmapInfo beatmapInfo) => string.IsNullOrEmpty(beatmapInfo.DifficultyName) ? string.Empty : $"[{beatmapInfo.DifficultyName}]"; } diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs index 837939716b..75bf8b2bbc 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs @@ -55,12 +55,31 @@ namespace osu.Game.Screens.Select.Carousel match &= !criteria.UserStarDifficulty.HasFilter || criteria.UserStarDifficulty.IsInRange(BeatmapInfo.StarRating); - if (match && criteria.SearchTerms.Length > 0) + if (!match) { - string[] terms = BeatmapInfo.GetSearchableTerms(); + Filtered.Value = !match; + return; + } + + if (criteria.SearchTerms.Length > 0) + { + var terms = BeatmapInfo.GetSearchableTerms(); foreach (string criteriaTerm in criteria.SearchTerms) - match &= terms.Any(term => term.Contains(criteriaTerm, StringComparison.InvariantCultureIgnoreCase)); + { + bool any = false; + + // ReSharper disable once LoopCanBeConvertedToQuery + foreach (string term in terms) + { + if (!term.Contains(criteriaTerm, StringComparison.InvariantCultureIgnoreCase)) continue; + + any = true; + break; + } + + match &= any; + } // if a match wasn't found via text matching of terms, do a second catch-all check matching against online IDs. // this should be done after text matching so we can prioritise matching numbers in metadata. @@ -71,8 +90,13 @@ namespace osu.Game.Screens.Select.Carousel } } - if (match) - match &= criteria.CollectionBeatmapMD5Hashes?.Contains(BeatmapInfo.MD5Hash) ?? true; + if (!match) + { + Filtered.Value = !match; + return; + } + + match &= criteria.CollectionBeatmapMD5Hashes?.Contains(BeatmapInfo.MD5Hash) ?? true; if (match && criteria.RulesetCriteria != null) match &= criteria.RulesetCriteria.Matches(BeatmapInfo); From e23db6238622cc3fbe58ace064ffe5fe941275c5 Mon Sep 17 00:00:00 2001 From: EXtremeExploit Date: Fri, 3 Mar 2023 16:14:19 -0300 Subject: [PATCH 138/476] Do list group'badges in a nicer way --- .../Profile/Header/Components/GroupBadgeFlow.cs | 5 ----- osu.Game/Users/ExtendedUserPanel.cs | 7 ------- osu.Game/Users/UserListPanel.cs | 15 ++++++++++----- 3 files changed, 10 insertions(+), 17 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/GroupBadgeFlow.cs b/osu.Game/Overlays/Profile/Header/Components/GroupBadgeFlow.cs index 4d4b16d7f5..33b3de94db 100644 --- a/osu.Game/Overlays/Profile/Header/Components/GroupBadgeFlow.cs +++ b/osu.Game/Overlays/Profile/Header/Components/GroupBadgeFlow.cs @@ -25,12 +25,7 @@ namespace osu.Game.Overlays.Profile.Header.Components Clear(true); if (user.NewValue?.Groups != null) - { AddRange(user.NewValue.Groups.Select(g => new GroupBadge(g))); - Show(); - } - else - Hide(); }); } } diff --git a/osu.Game/Users/ExtendedUserPanel.cs b/osu.Game/Users/ExtendedUserPanel.cs index af4da22906..c004918d87 100644 --- a/osu.Game/Users/ExtendedUserPanel.cs +++ b/osu.Game/Users/ExtendedUserPanel.cs @@ -98,13 +98,6 @@ namespace osu.Game.Users return statusContainer; } - protected FillFlowContainer CreateGroupBadges() - { - var groupBadgeFlow = new GroupBadgeFlow(); - groupBadgeFlow.User.Value = User; - return groupBadgeFlow; - } - private void displayStatus(UserStatus status, UserActivity activity = null) { if (status != null) diff --git a/osu.Game/Users/UserListPanel.cs b/osu.Game/Users/UserListPanel.cs index ebe58c2b93..a11494e849 100644 --- a/osu.Game/Users/UserListPanel.cs +++ b/osu.Game/Users/UserListPanel.cs @@ -68,11 +68,6 @@ namespace osu.Game.Users username.Anchor = Anchor.CentreLeft; username.Origin = Anchor.CentreLeft; }), - CreateGroupBadges().With(badges => - { - badges.Anchor = Anchor.CentreLeft; - badges.Origin = Anchor.CentreLeft; - }) } }, new FillFlowContainer @@ -100,6 +95,16 @@ namespace osu.Game.Users } }; + if (User.Groups != null) + { + details.Add(new GroupBadgeFlow + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + User = { Value = User } + }); + } + if (User.IsSupporter) { details.Add(new SupporterIcon From ada610b907d8316d3ac7679a3e6a0ac0d94f2e71 Mon Sep 17 00:00:00 2001 From: EXtremeExploit Date: Fri, 3 Mar 2023 16:15:49 -0300 Subject: [PATCH 139/476] Revert my changes on tests --- .../Visual/Online/TestSceneFriendDisplay.cs | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneFriendDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneFriendDisplay.cs index 5028a532b7..7925b252b6 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneFriendDisplay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneFriendDisplay.cs @@ -56,13 +56,7 @@ namespace osu.Game.Tests.Visual.Online IsOnline = true, Statistics = new UserStatistics { GlobalRank = 1111 }, CountryCode = CountryCode.JP, - CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c6.jpg", - IsSupporter = true, - SupportLevel = 1, - Groups = new[] - { - new APIUserGroup { Colour = "#EB47D0", ShortName = "DEV", Name = "Developers" } - } + CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c6.jpg" }, new APIUser { @@ -74,11 +68,6 @@ namespace osu.Game.Tests.Visual.Online CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", IsSupporter = true, SupportLevel = 3, - Groups = new[] - { - new APIUserGroup { Colour = "#0066FF", ShortName = "PPY", Name = "peppy" }, - new APIUserGroup { Colour = "#EB47D0", ShortName = "DEV", Name = "Developers" } - } }, new APIUser { @@ -87,9 +76,7 @@ namespace osu.Game.Tests.Visual.Online CountryCode = CountryCode.BY, CoverUrl = "https://assets.ppy.sh/user-profile-covers/8195163/4a8e2ad5a02a2642b631438cfa6c6bd7e2f9db289be881cb27df18331f64144c.jpeg", IsOnline = false, - LastVisit = DateTimeOffset.Now, - IsSupporter = true, - SupportLevel = 2 + LastVisit = DateTimeOffset.Now } }; } From edd37a9a7c1fc5af60db80718d9135a1e4dd3ec2 Mon Sep 17 00:00:00 2001 From: EXtremeExploit Date: Fri, 3 Mar 2023 16:16:45 -0300 Subject: [PATCH 140/476] cleanup --- osu.Game/Users/ExtendedUserPanel.cs | 1 - osu.Game/Users/UserListPanel.cs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Users/ExtendedUserPanel.cs b/osu.Game/Users/ExtendedUserPanel.cs index c004918d87..3c1b68f9ef 100644 --- a/osu.Game/Users/ExtendedUserPanel.cs +++ b/osu.Game/Users/ExtendedUserPanel.cs @@ -13,7 +13,6 @@ using osu.Game.Graphics.Sprites; using osu.Game.Users.Drawables; using osu.Framework.Input.Events; using osu.Game.Online.API.Requests.Responses; -using osu.Game.Overlays.Profile.Header.Components; namespace osu.Game.Users { diff --git a/osu.Game/Users/UserListPanel.cs b/osu.Game/Users/UserListPanel.cs index a11494e849..9b5b0e9f6d 100644 --- a/osu.Game/Users/UserListPanel.cs +++ b/osu.Game/Users/UserListPanel.cs @@ -67,7 +67,7 @@ namespace osu.Game.Users { username.Anchor = Anchor.CentreLeft; username.Origin = Anchor.CentreLeft; - }), + }) } }, new FillFlowContainer From d39b8c7c23046b13af95c212f7278e4f3abcdf21 Mon Sep 17 00:00:00 2001 From: tsrk Date: Fri, 3 Mar 2023 19:35:45 +0000 Subject: [PATCH 141/476] fix(SkinnableAvatar): update outdated interface --- osu.Game/Screens/Play/HUD/SkinnableAvatar.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/SkinnableAvatar.cs b/osu.Game/Screens/Play/HUD/SkinnableAvatar.cs index d675176a0a..7bb1467c1d 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableAvatar.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableAvatar.cs @@ -12,7 +12,7 @@ using osuTK; namespace osu.Game.Screens.Play.HUD { - public class SkinnableAvatar : CompositeDrawable, ISkinnableDrawable + public partial class SkinnableAvatar : CompositeDrawable, ISerialisableDrawable { [SettingSource("Corner radius", "How much the edges should be rounded.")] public new BindableFloat CornerRadius { get; set; } = new BindableFloat From 93fd9401642147be1a5f98b627259a92813d6e45 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Fri, 3 Mar 2023 20:35:28 +0000 Subject: [PATCH 142/476] Added new tests for Taiko Single Tap mod --- .../Mods/TestSceneTaikoModSingleTap.cs | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs index 75d4ef5c7f..7d544200cd 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using NUnit.Framework; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Taiko.Mods; @@ -103,5 +104,103 @@ namespace osu.Game.Rulesets.Taiko.Tests.Mods }, PassCondition = () => Player.ScoreProcessor.Combo.Value == 4 }); + + [Test] + public void TestInputIntro() => CreateModTest(new ModTestData + { + Mod = new TaikoModSingleTap(), + Autoplay = false, + Beatmap = new Beatmap + { + HitObjects = new List + { + new Hit + { + StartTime = 100, + Type = HitType.Rim + }, + }, + }, + ReplayFrames = new List + { + new TaikoReplayFrame(0, TaikoAction.RightRim), + new TaikoReplayFrame(20), + new TaikoReplayFrame(100, TaikoAction.LeftRim), + new TaikoReplayFrame(120), + }, + PassCondition = () => Player.ScoreProcessor.Combo.Value == 1 + }); + + [Test] + public void TestInputStrong() => CreateModTest(new ModTestData + { + Mod = new TaikoModSingleTap(), + Autoplay = false, + Beatmap = new Beatmap + { + HitObjects = new List + { + new Hit + { + StartTime = 100, + Type = HitType.Rim + }, + new Hit + { + StartTime = 200, + Type = HitType.Rim, + IsStrong = true + }, + }, + }, + ReplayFrames = new List + { + new TaikoReplayFrame(100, TaikoAction.RightRim), + new TaikoReplayFrame(120), + new TaikoReplayFrame(200, TaikoAction.LeftRim), + new TaikoReplayFrame(220), + }, + PassCondition = () => Player.ScoreProcessor.Combo.Value == 2 + }); + + [Test] + public void TestInputBreaks() => CreateModTest(new ModTestData + { + Mod = new TaikoModSingleTap(), + Autoplay = false, + Beatmap = new Beatmap + { + Breaks = new List + { + new BreakPeriod(100, 1600), + }, + HitObjects = new List + { + new Hit + { + StartTime = 100, + Type = HitType.Rim + }, + new Hit + { + StartTime = 2000, + Type = HitType.Rim, + IsStrong = true + }, + }, + }, + ReplayFrames = new List + { + new TaikoReplayFrame(100, TaikoAction.RightRim), + new TaikoReplayFrame(120), + // Press different key after break but before hit object. + new TaikoReplayFrame(1900, TaikoAction.LeftRim), + new TaikoReplayFrame(1820), + // Press original key at second hitobject and ensure it has been hit. + new TaikoReplayFrame(2000, TaikoAction.RightRim), + new TaikoReplayFrame(2020), + }, + PassCondition = () => Player.ScoreProcessor.Combo.Value == 2 + }); } } From 41e6956432cb5c1687ee664666d1ff4cb8b82978 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Fri, 3 Mar 2023 20:37:58 +0000 Subject: [PATCH 143/476] Improved single tap strong hit test --- .../Mods/TestSceneTaikoModSingleTap.cs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs index 7d544200cd..079f061c63 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs @@ -147,20 +147,27 @@ namespace osu.Game.Rulesets.Taiko.Tests.Mods }, new Hit { - StartTime = 200, + StartTime = 300, Type = HitType.Rim, IsStrong = true }, + new Hit + { + StartTime = 500, + Type = HitType.Rim, + }, }, }, ReplayFrames = new List { new TaikoReplayFrame(100, TaikoAction.RightRim), new TaikoReplayFrame(120), - new TaikoReplayFrame(200, TaikoAction.LeftRim), - new TaikoReplayFrame(220), + new TaikoReplayFrame(300, TaikoAction.LeftRim), + new TaikoReplayFrame(320), + new TaikoReplayFrame(500, TaikoAction.LeftRim), + new TaikoReplayFrame(520), }, - PassCondition = () => Player.ScoreProcessor.Combo.Value == 2 + PassCondition = () => Player.ScoreProcessor.Combo.Value == 0 && Player.ScoreProcessor.HighestCombo.Value == 2 }); [Test] From b4ed2db15e5954152660e80fb600f952a7393592 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Fri, 3 Mar 2023 20:40:30 +0000 Subject: [PATCH 144/476] Removed accidental strong hit in single tap break test --- osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs index 079f061c63..0cd3b85f8e 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs @@ -192,7 +192,6 @@ namespace osu.Game.Rulesets.Taiko.Tests.Mods { StartTime = 2000, Type = HitType.Rim, - IsStrong = true }, }, }, From d5ba5eed88d154130802a92de8a33382f0334de8 Mon Sep 17 00:00:00 2001 From: tsrk Date: Fri, 3 Mar 2023 23:47:22 +0000 Subject: [PATCH 145/476] fix(SkinnableAvatar): init `avatar.CornerRadius` --- osu.Game/Screens/Play/HUD/SkinnableAvatar.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Play/HUD/SkinnableAvatar.cs b/osu.Game/Screens/Play/HUD/SkinnableAvatar.cs index 7bb1467c1d..41fe8c6a06 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableAvatar.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableAvatar.cs @@ -42,6 +42,7 @@ namespace osu.Game.Screens.Play.HUD base.LoadComplete(); avatar.User = gameplayState.Score.ScoreInfo.User; + avatar.CornerRadius = CornerRadius.Value; CornerRadius.BindValueChanged(e => avatar.CornerRadius = e.NewValue); } From 31854188348e34fc28ba34c63ba3bc215301ad43 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sat, 4 Mar 2023 15:14:22 +0100 Subject: [PATCH 146/476] Add benchmarks for carousel beatmap filter --- .../BenchmarkCarouselFilter.cs | 123 ++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 osu.Game.Benchmarks/BenchmarkCarouselFilter.cs diff --git a/osu.Game.Benchmarks/BenchmarkCarouselFilter.cs b/osu.Game.Benchmarks/BenchmarkCarouselFilter.cs new file mode 100644 index 0000000000..8f7027da17 --- /dev/null +++ b/osu.Game.Benchmarks/BenchmarkCarouselFilter.cs @@ -0,0 +1,123 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using BenchmarkDotNet.Attributes; +using osu.Game.Beatmaps; +using osu.Game.Rulesets; +using osu.Game.Screens.Select; +using osu.Game.Screens.Select.Carousel; + +namespace osu.Game.Benchmarks +{ + public class BenchmarkCarouselFilter : BenchmarkTest + { + private BeatmapInfo getExampleBeatmap() => new BeatmapInfo + { + Ruleset = new RulesetInfo + { + ShortName = "osu", + OnlineID = 0 + }, + StarRating = 4.0d, + Difficulty = new BeatmapDifficulty + { + ApproachRate = 5.0f, + DrainRate = 3.0f, + CircleSize = 2.0f, + }, + Metadata = new BeatmapMetadata + { + Artist = "The Artist", + ArtistUnicode = "check unicode too", + Title = "Title goes here", + TitleUnicode = "Title goes here", + Author = { Username = "The Author" }, + Source = "unit tests", + Tags = "look for tags too", + }, + DifficultyName = "version as well", + Length = 2500, + BPM = 160, + BeatDivisor = 12, + Status = BeatmapOnlineStatus.Loved + }; + + private CarouselBeatmap carouselBeatmap = null!; + private FilterCriteria criteria1 = null!; + private FilterCriteria criteria2 = null!; + private FilterCriteria criteria3 = null!; + private FilterCriteria criteria4 = null!; + private FilterCriteria criteria5 = null!; + private FilterCriteria criteria6 = null!; + + public override void SetUp() + { + var beatmap = getExampleBeatmap(); + beatmap.OnlineID = 20201010; + beatmap.BeatmapSet = new BeatmapSetInfo { OnlineID = 1535 }; + carouselBeatmap = new CarouselBeatmap(beatmap); + criteria1 = new FilterCriteria(); + criteria2 = new FilterCriteria + { + Ruleset = new RulesetInfo { ShortName = "catch" } + }; + criteria3 = new FilterCriteria + { + Ruleset = new RulesetInfo { OnlineID = 6 }, + AllowConvertedBeatmaps = true, + BPM = new FilterCriteria.OptionalRange + { + IsUpperInclusive = false, + Max = 160d + } + }; + criteria4 = new FilterCriteria + { + Ruleset = new RulesetInfo { OnlineID = 6 }, + AllowConvertedBeatmaps = true, + SearchText = "an artist" + }; + criteria5 = new FilterCriteria + { + Creator = new FilterCriteria.OptionalTextFilter { SearchTerm = "the author AND then something else" } + }; + criteria6 = new FilterCriteria { SearchText = "20201010" }; + } + + [Benchmark] + public void CarouselBeatmapFilter() + { + carouselBeatmap.Filter(criteria1); + } + + [Benchmark] + public void CriteriaMatchingSpecificRuleset() + { + carouselBeatmap.Filter(criteria2); + } + + [Benchmark] + public void CriteriaMatchingRangeMax() + { + carouselBeatmap.Filter(criteria3); + } + + [Benchmark] + public void CriteriaMatchingTerms() + { + carouselBeatmap.Filter(criteria4); + } + + [Benchmark] + public void CriteriaMatchingCreator() + { + carouselBeatmap.Filter(criteria5); + } + + [Benchmark] + public void CriteriaMatchingBeatmapIDs() + { + carouselBeatmap.Filter(criteria6); + } + } +} From b90c389ff04ffa90bce040bbaa3f531173f93867 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sat, 4 Mar 2023 15:42:35 +0100 Subject: [PATCH 147/476] using List instead of Span in GetSearchableTerms --- osu.Game/Beatmaps/BeatmapInfoExtensions.cs | 39 +++++++++---------- .../Select/Carousel/CarouselBeatmap.cs | 2 +- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapInfoExtensions.cs b/osu.Game/Beatmaps/BeatmapInfoExtensions.cs index e14a44ec58..7e336dcde2 100644 --- a/osu.Game/Beatmaps/BeatmapInfoExtensions.cs +++ b/osu.Game/Beatmaps/BeatmapInfoExtensions.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. -using System; +using System.Collections.Generic; using osu.Framework.Localisation; namespace osu.Game.Beatmaps @@ -29,28 +29,25 @@ namespace osu.Game.Beatmaps return new RomanisableString($"{metadata.GetPreferred(true)}".Trim(), $"{metadata.GetPreferred(false)}".Trim()); } - public static ReadOnlySpan GetSearchableTerms(this IBeatmapInfo beatmapInfo) + public static List GetSearchableTerms(this IBeatmapInfo beatmapInfo) { - Span terms = new string[8]; - int i = 0; - if (!string.IsNullOrEmpty(beatmapInfo.DifficultyName)) - terms[i++] = beatmapInfo.DifficultyName; + var terms = new List(8); var metadata = beatmapInfo.Metadata; - if (!string.IsNullOrEmpty(metadata.Author.Username)) - terms[i++] = metadata.Author.Username; - if (!string.IsNullOrEmpty(metadata.Artist)) - terms[i++] = metadata.Artist; - if (!string.IsNullOrEmpty(metadata.ArtistUnicode)) - terms[i++] = metadata.ArtistUnicode; - if (!string.IsNullOrEmpty(metadata.Title)) - terms[i++] = metadata.Title; - if (!string.IsNullOrEmpty(metadata.TitleUnicode)) - terms[i++] = metadata.TitleUnicode; - if (!string.IsNullOrEmpty(metadata.Source)) - terms[i++] = metadata.Source; - if (!string.IsNullOrEmpty(metadata.Tags)) - terms[i++] = metadata.Tags; - return terms[..i]; + addIfNotNull(beatmapInfo.DifficultyName); + addIfNotNull(metadata.Author.Username); + addIfNotNull(metadata.Artist); + addIfNotNull(metadata.ArtistUnicode); + addIfNotNull(metadata.Title); + addIfNotNull(metadata.TitleUnicode); + addIfNotNull(metadata.Source); + addIfNotNull(metadata.Tags); + return terms; + + void addIfNotNull(string? s) + { + if (!string.IsNullOrEmpty(s)) + terms.Add(s); + } } private static string getVersionString(IBeatmapInfo beatmapInfo) => string.IsNullOrEmpty(beatmapInfo.DifficultyName) ? string.Empty : $"[{beatmapInfo.DifficultyName}]"; diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs index 75bf8b2bbc..eb9fd09224 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs @@ -69,7 +69,7 @@ namespace osu.Game.Screens.Select.Carousel { bool any = false; - // ReSharper disable once LoopCanBeConvertedToQuery + // ReSharper disable once ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator foreach (string term in terms) { if (!term.Contains(criteriaTerm, StringComparison.InvariantCultureIgnoreCase)) continue; From 70a925aab178706afc2fedff3c3ed47ec1f72002 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sat, 4 Mar 2023 16:49:33 +0100 Subject: [PATCH 148/476] added extra early-returns --- osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs index eb9fd09224..2cfa163b7a 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs @@ -49,6 +49,12 @@ namespace osu.Game.Screens.Select.Carousel match &= !criteria.BeatDivisor.HasFilter || criteria.BeatDivisor.IsInRange(BeatmapInfo.BeatDivisor); match &= !criteria.OnlineStatus.HasFilter || criteria.OnlineStatus.IsInRange(BeatmapInfo.Status); + if (!match) + { + Filtered.Value = !match; + return; + } + match &= !criteria.Creator.HasFilter || criteria.Creator.Matches(BeatmapInfo.Metadata.Author.Username); match &= !criteria.Artist.HasFilter || criteria.Artist.Matches(BeatmapInfo.Metadata.Artist) || criteria.Artist.Matches(BeatmapInfo.Metadata.ArtistUnicode); @@ -78,7 +84,10 @@ namespace osu.Game.Screens.Select.Carousel break; } - match &= any; + if (any) continue; + + match = false; + break; } // if a match wasn't found via text matching of terms, do a second catch-all check matching against online IDs. From 5146f7c9784b38988b5dbfa2397e91330dd942dc Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sat, 4 Mar 2023 16:49:46 +0100 Subject: [PATCH 149/476] using array again in GetSearchableTerms --- osu.Game/Beatmaps/BeatmapInfoExtensions.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapInfoExtensions.cs b/osu.Game/Beatmaps/BeatmapInfoExtensions.cs index 7e336dcde2..7d6667cdcd 100644 --- a/osu.Game/Beatmaps/BeatmapInfoExtensions.cs +++ b/osu.Game/Beatmaps/BeatmapInfoExtensions.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 osu.Framework.Localisation; @@ -29,9 +30,10 @@ namespace osu.Game.Beatmaps return new RomanisableString($"{metadata.GetPreferred(true)}".Trim(), $"{metadata.GetPreferred(false)}".Trim()); } - public static List GetSearchableTerms(this IBeatmapInfo beatmapInfo) + public static ReadOnlySpan GetSearchableTerms(this IBeatmapInfo beatmapInfo) { - var terms = new List(8); + string[] terms = new string[8]; + int i = 0; var metadata = beatmapInfo.Metadata; addIfNotNull(beatmapInfo.DifficultyName); addIfNotNull(metadata.Author.Username); @@ -41,12 +43,12 @@ namespace osu.Game.Beatmaps addIfNotNull(metadata.TitleUnicode); addIfNotNull(metadata.Source); addIfNotNull(metadata.Tags); - return terms; + return terms.AsSpan(0, i); void addIfNotNull(string? s) { if (!string.IsNullOrEmpty(s)) - terms.Add(s); + terms[i++] = s; } } From b0cd801405c26fe325cd11aad75a6e2734c198a5 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sat, 4 Mar 2023 19:34:22 +0100 Subject: [PATCH 150/476] fix code quality --- osu.Game/Beatmaps/BeatmapInfoExtensions.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapInfoExtensions.cs b/osu.Game/Beatmaps/BeatmapInfoExtensions.cs index 7d6667cdcd..460944d2fa 100644 --- a/osu.Game/Beatmaps/BeatmapInfoExtensions.cs +++ b/osu.Game/Beatmaps/BeatmapInfoExtensions.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using osu.Framework.Localisation; namespace osu.Game.Beatmaps From 908651cc11251cf80e13b8b539abcfc0bd4454a2 Mon Sep 17 00:00:00 2001 From: mk56-spn Date: Sun, 5 Mar 2023 20:57:26 +0100 Subject: [PATCH 151/476] make `ResetConfirmDialog` properly utilise its parent's logic Adjust name of `DeleteAction` to `DangerousAction` --- .../Collections/DeleteCollectionDialog.cs | 2 +- .../Overlays/Dialog/DangerousActionDialog.cs | 4 ++-- .../Overlays/Mods/DeleteModPresetDialog.cs | 2 +- .../MassDeleteConfirmationDialog.cs | 2 +- osu.Game/Overlays/SkinEditor/SkinEditor.cs | 19 ++----------------- .../DeleteDifficultyConfirmationDialog.cs | 2 +- .../Select/BeatmapClearScoresDialog.cs | 2 +- .../Screens/Select/BeatmapDeleteDialog.cs | 2 +- .../Carousel/UpdateLocalConfirmationDialog.cs | 2 +- .../Screens/Select/LocalScoreDeleteDialog.cs | 2 +- osu.Game/Screens/Select/SkinDeleteDialog.cs | 2 +- 11 files changed, 13 insertions(+), 28 deletions(-) diff --git a/osu.Game/Collections/DeleteCollectionDialog.cs b/osu.Game/Collections/DeleteCollectionDialog.cs index 4da2979481..9edc213077 100644 --- a/osu.Game/Collections/DeleteCollectionDialog.cs +++ b/osu.Game/Collections/DeleteCollectionDialog.cs @@ -13,7 +13,7 @@ namespace osu.Game.Collections public DeleteCollectionDialog(Live collection, Action deleteAction) { BodyText = collection.PerformRead(c => $"{c.Name} ({"beatmap".ToQuantity(c.BeatmapMD5Hashes.Count)})"); - DeleteAction = deleteAction; + DangerousAction = deleteAction; } } } diff --git a/osu.Game/Overlays/Dialog/DangerousActionDialog.cs b/osu.Game/Overlays/Dialog/DangerousActionDialog.cs index aaec72fc4d..85cae5e0d2 100644 --- a/osu.Game/Overlays/Dialog/DangerousActionDialog.cs +++ b/osu.Game/Overlays/Dialog/DangerousActionDialog.cs @@ -17,7 +17,7 @@ namespace osu.Game.Overlays.Dialog /// /// The action which performs the deletion. /// - protected Action? DeleteAction { get; set; } + protected Action? DangerousAction { get; set; } protected DangerousActionDialog() { @@ -30,7 +30,7 @@ namespace osu.Game.Overlays.Dialog new PopupDialogDangerousButton { Text = DeleteConfirmationDialogStrings.Confirm, - Action = () => DeleteAction?.Invoke() + Action = () => DangerousAction?.Invoke() }, new PopupDialogCancelButton { diff --git a/osu.Game/Overlays/Mods/DeleteModPresetDialog.cs b/osu.Game/Overlays/Mods/DeleteModPresetDialog.cs index 5b329a32d5..9788764453 100644 --- a/osu.Game/Overlays/Mods/DeleteModPresetDialog.cs +++ b/osu.Game/Overlays/Mods/DeleteModPresetDialog.cs @@ -12,7 +12,7 @@ namespace osu.Game.Overlays.Mods public DeleteModPresetDialog(Live modPreset) { BodyText = modPreset.PerformRead(preset => preset.Name); - DeleteAction = () => modPreset.PerformWrite(preset => preset.DeletePending = true); + DangerousAction = () => modPreset.PerformWrite(preset => preset.DeletePending = true); } } } diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/MassDeleteConfirmationDialog.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/MassDeleteConfirmationDialog.cs index 71226803c8..99ef62d94b 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/MassDeleteConfirmationDialog.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/MassDeleteConfirmationDialog.cs @@ -11,7 +11,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance public MassDeleteConfirmationDialog(Action deleteAction) { BodyText = "Everything?"; - DeleteAction = deleteAction; + DangerousAction = deleteAction; } } } diff --git a/osu.Game/Overlays/SkinEditor/SkinEditor.cs b/osu.Game/Overlays/SkinEditor/SkinEditor.cs index 5ffdaa11d4..a7fe459aa4 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditor.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditor.cs @@ -12,7 +12,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input; using osu.Framework.Input.Bindings; @@ -633,25 +632,11 @@ namespace osu.Game.Overlays.SkinEditor public partial class ResetConfirmDialog : DangerousActionDialog { - public ResetConfirmDialog(Action reset) + public ResetConfirmDialog(Action revert) { HeaderText = SkinEditorStrings.RevertToDefaultDescription; BodyText = SkinEditorStrings.ResetDialogue; - - Icon = FontAwesome.Solid.ExclamationTriangle; - - Buttons = new PopupDialogButton[] - { - new PopupDialogDangerousButton - { - Text = BeatmapOverlayStrings.UserContentConfirmButtonText, - Action = reset - }, - new PopupDialogCancelButton - { - Text = Web.CommonStrings.ButtonsCancel, - }, - }; + DangerousAction = revert; } } diff --git a/osu.Game/Screens/Edit/DeleteDifficultyConfirmationDialog.cs b/osu.Game/Screens/Edit/DeleteDifficultyConfirmationDialog.cs index 6db31967d6..8556949528 100644 --- a/osu.Game/Screens/Edit/DeleteDifficultyConfirmationDialog.cs +++ b/osu.Game/Screens/Edit/DeleteDifficultyConfirmationDialog.cs @@ -12,7 +12,7 @@ namespace osu.Game.Screens.Edit public DeleteDifficultyConfirmationDialog(BeatmapInfo beatmapInfo, Action deleteAction) { BodyText = $"\"{beatmapInfo.DifficultyName}\" difficulty"; - DeleteAction = deleteAction; + DangerousAction = deleteAction; } } } diff --git a/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs b/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs index 2704a1afb4..8efad451df 100644 --- a/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs +++ b/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs @@ -18,7 +18,7 @@ namespace osu.Game.Screens.Select public BeatmapClearScoresDialog(BeatmapInfo beatmapInfo, Action onCompletion) { BodyText = $"All local scores on {beatmapInfo.GetDisplayTitle()}"; - DeleteAction = () => + DangerousAction = () => { Task.Run(() => scoreManager.Delete(beatmapInfo)) .ContinueWith(_ => onCompletion); diff --git a/osu.Game/Screens/Select/BeatmapDeleteDialog.cs b/osu.Game/Screens/Select/BeatmapDeleteDialog.cs index 0692cc3745..e98af8cca2 100644 --- a/osu.Game/Screens/Select/BeatmapDeleteDialog.cs +++ b/osu.Game/Screens/Select/BeatmapDeleteDialog.cs @@ -20,7 +20,7 @@ namespace osu.Game.Screens.Select [BackgroundDependencyLoader] private void load(BeatmapManager beatmapManager) { - DeleteAction = () => beatmapManager.Delete(beatmapSet); + DangerousAction = () => beatmapManager.Delete(beatmapSet); } } } diff --git a/osu.Game/Screens/Select/Carousel/UpdateLocalConfirmationDialog.cs b/osu.Game/Screens/Select/Carousel/UpdateLocalConfirmationDialog.cs index ecb21d4e2c..6157e8f6a5 100644 --- a/osu.Game/Screens/Select/Carousel/UpdateLocalConfirmationDialog.cs +++ b/osu.Game/Screens/Select/Carousel/UpdateLocalConfirmationDialog.cs @@ -15,7 +15,7 @@ namespace osu.Game.Screens.Select.Carousel HeaderText = PopupDialogStrings.UpdateLocallyModifiedText; BodyText = PopupDialogStrings.UpdateLocallyModifiedDescription; Icon = FontAwesome.Solid.ExclamationTriangle; - DeleteAction = onConfirm; + DangerousAction = onConfirm; } } } diff --git a/osu.Game/Screens/Select/LocalScoreDeleteDialog.cs b/osu.Game/Screens/Select/LocalScoreDeleteDialog.cs index 660122c80e..c4add31a4f 100644 --- a/osu.Game/Screens/Select/LocalScoreDeleteDialog.cs +++ b/osu.Game/Screens/Select/LocalScoreDeleteDialog.cs @@ -28,7 +28,7 @@ namespace osu.Game.Screens.Select BodyText = $"{score.User} ({score.DisplayAccuracy}, {score.Rank})"; Icon = FontAwesome.Regular.TrashAlt; - DeleteAction = () => scoreManager.Delete(score); + DangerousAction = () => scoreManager.Delete(score); } } } diff --git a/osu.Game/Screens/Select/SkinDeleteDialog.cs b/osu.Game/Screens/Select/SkinDeleteDialog.cs index 3c95de0620..6612ae837a 100644 --- a/osu.Game/Screens/Select/SkinDeleteDialog.cs +++ b/osu.Game/Screens/Select/SkinDeleteDialog.cs @@ -20,7 +20,7 @@ namespace osu.Game.Screens.Select [BackgroundDependencyLoader] private void load(SkinManager manager) { - DeleteAction = () => + DangerousAction = () => { manager.Delete(skin.SkinInfo.Value); manager.CurrentSkinInfo.SetDefault(); From 082bfe3621f8502b81376f5a92e031cf3c7d4803 Mon Sep 17 00:00:00 2001 From: tsrk Date: Sun, 5 Mar 2023 23:09:02 +0000 Subject: [PATCH 152/476] refactor: `SkinnableAvatar` to `PlayerAvatar` --- .../Screens/Play/HUD/{SkinnableAvatar.cs => PlayerAvatar.cs} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename osu.Game/Screens/Play/HUD/{SkinnableAvatar.cs => PlayerAvatar.cs} (92%) diff --git a/osu.Game/Screens/Play/HUD/SkinnableAvatar.cs b/osu.Game/Screens/Play/HUD/PlayerAvatar.cs similarity index 92% rename from osu.Game/Screens/Play/HUD/SkinnableAvatar.cs rename to osu.Game/Screens/Play/HUD/PlayerAvatar.cs index 41fe8c6a06..d4ab555d04 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableAvatar.cs +++ b/osu.Game/Screens/Play/HUD/PlayerAvatar.cs @@ -12,7 +12,7 @@ using osuTK; namespace osu.Game.Screens.Play.HUD { - public partial class SkinnableAvatar : CompositeDrawable, ISerialisableDrawable + public partial class PlayerAvatar : CompositeDrawable, ISerialisableDrawable { [SettingSource("Corner radius", "How much the edges should be rounded.")] public new BindableFloat CornerRadius { get; set; } = new BindableFloat @@ -27,7 +27,7 @@ namespace osu.Game.Screens.Play.HUD private readonly UpdateableAvatar avatar; - public SkinnableAvatar() + public PlayerAvatar() { Size = new Vector2(128f); InternalChild = avatar = new UpdateableAvatar(isInteractive: false) From 654eacd4496cde2601015abc053d8d081f25869d Mon Sep 17 00:00:00 2001 From: tsrk Date: Sun, 5 Mar 2023 23:10:42 +0000 Subject: [PATCH 153/476] feat(l10n): localise `CornerRadius` --- .../SkinComponents/SkinnableComponentStrings.cs | 10 ++++++++++ osu.Game/Screens/Play/HUD/PlayerAvatar.cs | 3 ++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/osu.Game/Localisation/SkinComponents/SkinnableComponentStrings.cs b/osu.Game/Localisation/SkinComponents/SkinnableComponentStrings.cs index 547df86fc7..5cec9442e4 100644 --- a/osu.Game/Localisation/SkinComponents/SkinnableComponentStrings.cs +++ b/osu.Game/Localisation/SkinComponents/SkinnableComponentStrings.cs @@ -39,6 +39,16 @@ namespace osu.Game.Localisation.SkinComponents /// public static LocalisableString TextElementTextDescription => new TranslatableString(getKey(@"text_element_text_description"), "The text to be displayed."); + /// + /// "Corner Radius" + /// + public static LocalisableString CornerRadius => new TranslatableString(getKey(@"corner_radius"), "Corner Radius"); + + /// + /// "How rounded the corners should be." + /// + public static LocalisableString CornerRadiusDescription => new TranslatableString(getKey(@"corner_radius_description"), "How rounded the corners should be."); + private static string getKey(string key) => $"{prefix}:{key}"; } } diff --git a/osu.Game/Screens/Play/HUD/PlayerAvatar.cs b/osu.Game/Screens/Play/HUD/PlayerAvatar.cs index d4ab555d04..6cea463193 100644 --- a/osu.Game/Screens/Play/HUD/PlayerAvatar.cs +++ b/osu.Game/Screens/Play/HUD/PlayerAvatar.cs @@ -6,6 +6,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Configuration; +using osu.Game.Localisation.SkinComponents; using osu.Game.Skinning; using osu.Game.Users.Drawables; using osuTK; @@ -14,7 +15,7 @@ namespace osu.Game.Screens.Play.HUD { public partial class PlayerAvatar : CompositeDrawable, ISerialisableDrawable { - [SettingSource("Corner radius", "How much the edges should be rounded.")] + [SettingSource(typeof(SkinnableComponentStrings), nameof(SkinnableComponentStrings.CornerRadius), nameof(SkinnableComponentStrings.CornerRadiusDescription))] public new BindableFloat CornerRadius { get; set; } = new BindableFloat { MinValue = 0, From 20a610ea6c4c407cedcdffc8a9ac3ae0fbd1f8b0 Mon Sep 17 00:00:00 2001 From: tsrk Date: Sun, 5 Mar 2023 23:15:58 +0000 Subject: [PATCH 154/476] style: a NRT pass on `UpdateableFlag` --- osu.Game/Users/Drawables/UpdateableFlag.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/osu.Game/Users/Drawables/UpdateableFlag.cs b/osu.Game/Users/Drawables/UpdateableFlag.cs index a208f3c7c4..8f8d7052e5 100644 --- a/osu.Game/Users/Drawables/UpdateableFlag.cs +++ b/osu.Game/Users/Drawables/UpdateableFlag.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 disable - using System; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -30,14 +28,14 @@ namespace osu.Game.Users.Drawables /// Perform an action in addition to showing the country ranking. /// This should be used to perform auxiliary tasks and not as a primary action for clicking a flag (to maintain a consistent UX). ///
- public Action Action; + public Action? Action; public UpdateableFlag(CountryCode countryCode = CountryCode.Unknown) { CountryCode = countryCode; } - protected override Drawable CreateDrawable(CountryCode countryCode) + protected override Drawable? CreateDrawable(CountryCode countryCode) { if (countryCode == CountryCode.Unknown && !ShowPlaceholderOnUnknown) return null; @@ -56,8 +54,8 @@ namespace osu.Game.Users.Drawables }; } - [Resolved(canBeNull: true)] - private RankingsOverlay rankingsOverlay { get; set; } + [Resolved] + private RankingsOverlay? rankingsOverlay { get; set; } protected override bool OnClick(ClickEvent e) { From 07bde4990d50836a64e62f61a60f991165bb6436 Mon Sep 17 00:00:00 2001 From: tsrk Date: Sun, 5 Mar 2023 23:17:30 +0000 Subject: [PATCH 155/476] feat: implement `PlayerAvatar` --- osu.Game/Screens/Play/HUD/PlayerFlag.cs | 38 +++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 osu.Game/Screens/Play/HUD/PlayerFlag.cs diff --git a/osu.Game/Screens/Play/HUD/PlayerFlag.cs b/osu.Game/Screens/Play/HUD/PlayerFlag.cs new file mode 100644 index 0000000000..17188d0009 --- /dev/null +++ b/osu.Game/Screens/Play/HUD/PlayerFlag.cs @@ -0,0 +1,38 @@ +// 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.Game.Skinning; +using osu.Game.Users.Drawables; +using osuTK; + +namespace osu.Game.Screens.Play.HUD +{ + public partial class PlayerFlag : CompositeDrawable, ISerialisableDrawable + { + [Resolved] + private GameplayState gameplayState { get; set; } = null!; + + private readonly UpdateableFlag flag; + + public PlayerFlag() + { + Size = new Vector2(114, 80); + InternalChild = flag = new UpdateableFlag + { + RelativeSizeAxes = Axes.Both, + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + flag.CountryCode = gameplayState.Score.ScoreInfo.User.CountryCode; + } + + public bool UsesFixedAnchor { get; set; } + } +} From ddee6400dfa3c28f476e0c9fea11a17874c82257 Mon Sep 17 00:00:00 2001 From: tsrk Date: Sun, 5 Mar 2023 23:46:51 +0000 Subject: [PATCH 156/476] test: add coverage for `Player{Avatar,Flag}` --- .../Archives/modified-argon-20230305.osk | Bin 0 -> 1897 bytes osu.Game.Tests/Skins/SkinDeserialisationTest.cs | 7 ++++++- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Tests/Resources/Archives/modified-argon-20230305.osk diff --git a/osu.Game.Tests/Resources/Archives/modified-argon-20230305.osk b/osu.Game.Tests/Resources/Archives/modified-argon-20230305.osk new file mode 100644 index 0000000000000000000000000000000000000000..c85f3b6352af3357089a797c287659a0d8e848f7 GIT binary patch literal 1897 zcmZ`)2{ha37XJrDwM>j<6dg)aofm6Nl`=@FlHetRpxP!SwNw*ZQW6}y5s4*KF={EH zN^R8^#bCygmIl?)Djh?_UPTSDj7j_Ey*c&X`@VC}J@-4`@1A?V@7~`N?B_rB4NU|rVlAmS1QZ5D)sBQZsC%Y@)DD`?fvQgW%$c95cG#nwX5eaNBy!>}^x zSMnoc3Ae1r;c-(M3B@4=N}}*4N1bz$rM~DOQvG&rj>c9JM;D3N!SAoqeU77@MDnSzO%AM%6NUOL18A1WCPf&1x=^Hr@-YKH;#HEae+Y?K$)9#Y zp;nQ74leahsHb|mF3#Vh5!whYHwEo9pbyiB4LJ|=_WzS(|IFS21k?HUW@x)deu|SE zay7-W`RrQ#+Um2lqAv@=yCBKoklbWQt{}W1+)sdtTOW(bo;~2>!=EPb*$XDIi@-{# zJdTBE@q7q;9w5sc$)t(`K$Q{zC<+5~!21zxU97E0*8@pJ0x>k?Q>067Xwn-aaFkl@ z9*+0=gl%$GFDSe>pM2y|arf9e_DM01wq8$Pm^!<`W^m8^Y2CufVp;Vdp8Vb2E8{mN zZwtncE^kiH6pF`XhGLyWl%Go~&CPFdp0mECH6>MyEWXe-D>w<=HN&b+n zHvTxQ&#A2@r^Q?FE^O+n-!W*D@YYu?FVAORzPojLrfOT|EO}!VJ9mclb(~0#l>XZb z(c4ukV&`+0>4li7y=#00?T+CDS8+PE_$gE_IC#8$Y^|wb)Tlbg4m)q;`(f9V$V8vB(W^<3RQMb^q4L(#q25!yjaJWPgxI?H|lk0dHv z69fR(A^`A(uv;-Cq7Q};K=2Cv)TZPNB(YN;oVan5Dgj;SmGVPav9<4gD884XAvs>8 zYp)cWU}zI{S3OH~UA5KiwxyKpxwm~0duCDLlo4!vSQCT-xC(8*>Fw(iJ434N=Sk_qq46_?0cwbTrb3fm^Ie z_w9``>IV!Cb~kcIe9W1q@W}E(?v0LzBjo0Ex0GAwih;z|%R|JIw ziO2*0f5ZqM^wjSYAg1##)PG2RKXgCg{tfjKe(e9I-~B%Jm%-mY YWDopTQJ|0q#6MmT6Yf=^#(%Q@1WNchY5)KL literal 0 HcmV?d00001 diff --git a/osu.Game.Tests/Skins/SkinDeserialisationTest.cs b/osu.Game.Tests/Skins/SkinDeserialisationTest.cs index 04b8c6dd4e..eb1df954aa 100644 --- a/osu.Game.Tests/Skins/SkinDeserialisationTest.cs +++ b/osu.Game.Tests/Skins/SkinDeserialisationTest.cs @@ -9,6 +9,7 @@ using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; +using osu.Framework.Logging; using osu.Game.Audio; using osu.Game.IO; using osu.Game.IO.Archives; @@ -48,7 +49,9 @@ namespace osu.Game.Tests.Skins // Covers BPM counter. "Archives/modified-default-20221205.osk", // Covers judgement counter. - "Archives/modified-default-20230117.osk" + "Archives/modified-default-20230117.osk", + // Covers player avatar and flag. + "Archives/modified-argon-20230305.osk", }; /// @@ -61,6 +64,8 @@ namespace osu.Game.Tests.Skins foreach (string oskFile in available_skins) { + Logger.Log($"Testing file {oskFile}..."); + using (var stream = TestResources.OpenResource(oskFile)) using (var storage = new ZipArchiveReader(stream)) { From 87d0bef313fc9e5563dc3d368f3daf59b437c6aa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 6 Mar 2023 15:16:31 +0900 Subject: [PATCH 157/476] Use nullable comparison helper method instead of manual implementation --- .../Screens/Select/Carousel/CarouselBeatmapSet.cs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs index 63493a3b02..67822a27ee 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs @@ -87,7 +87,7 @@ namespace osu.Game.Screens.Select.Carousel break; case SortMode.DateRanked: - comparison = compareNullableDateTimeOffset(BeatmapSet.DateRanked, otherSet.BeatmapSet.DateRanked); + comparison = Nullable.Compare(otherSet.BeatmapSet.DateRanked, BeatmapSet.DateRanked); break; case SortMode.LastPlayed: @@ -107,7 +107,7 @@ namespace osu.Game.Screens.Select.Carousel break; case SortMode.DateSubmitted: - comparison = compareNullableDateTimeOffset(BeatmapSet.DateSubmitted, otherSet.BeatmapSet.DateSubmitted); + comparison = Nullable.Compare(otherSet.BeatmapSet.DateSubmitted, BeatmapSet.DateSubmitted); break; } @@ -124,14 +124,6 @@ namespace osu.Game.Screens.Select.Carousel return otherSet.BeatmapSet.ID.CompareTo(BeatmapSet.ID); } - private static int compareNullableDateTimeOffset(DateTimeOffset? x, DateTimeOffset? y) - { - if (x == null) return 1; - if (y == null) return -1; - - return y.Value.CompareTo(x.Value); - } - /// /// All beatmaps which are not filtered and valid for display. /// From 3a240aaa5fa1e55bcc9b50c26345ebdf0c001d99 Mon Sep 17 00:00:00 2001 From: tsrk Date: Mon, 6 Mar 2023 10:27:59 +0000 Subject: [PATCH 158/476] style: use 64 radius --- osu.Game/Screens/Play/HUD/PlayerAvatar.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/PlayerAvatar.cs b/osu.Game/Screens/Play/HUD/PlayerAvatar.cs index 6cea463193..32ed045df7 100644 --- a/osu.Game/Screens/Play/HUD/PlayerAvatar.cs +++ b/osu.Game/Screens/Play/HUD/PlayerAvatar.cs @@ -19,7 +19,7 @@ namespace osu.Game.Screens.Play.HUD public new BindableFloat CornerRadius { get; set; } = new BindableFloat { MinValue = 0, - MaxValue = 63, + MaxValue = 64, Precision = 0.01f }; From df34bdc825e5496339de30ad3173f93dd28f40df Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 6 Mar 2023 19:31:45 +0900 Subject: [PATCH 159/476] Use lang version 10 --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 3b6b985961..734374c840 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@  - 9.0 + 10.0 true enable From a7be59175d15298d1a7eff2c9e090dca03c8492f Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Mon, 6 Mar 2023 10:57:09 +0000 Subject: [PATCH 160/476] Made private + renamed Taiko Single Tap tracking variables for better readability --- .../Mods/TaikoModSingleTap.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs index 1130338521..30e4946833 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs @@ -29,9 +29,9 @@ namespace osu.Game.Rulesets.Taiko.Mods protected bool CheckValidNewAction(TaikoAction action) { if (action == TaikoAction.LeftCentre || action == TaikoAction.RightCentre) - return LastAcceptedDonAction == null || LastAcceptedDonAction == action; + return lastAcceptedCentreAction == null || lastAcceptedCentreAction == action; if (action == TaikoAction.LeftRim || action == TaikoAction.RightRim) - return LastAcceptedKatAction == null || LastAcceptedKatAction == action; + return lastAcceptedRimAction == null || lastAcceptedRimAction == action; return true; } @@ -44,8 +44,8 @@ namespace osu.Game.Rulesets.Taiko.Mods private TaikoPlayfield playfield { get; set; } = null!; - protected TaikoAction? LastAcceptedDonAction { get; private set; } - protected TaikoAction? LastAcceptedKatAction { get; private set; } + private TaikoAction? lastAcceptedCentreAction { get; set; } + private TaikoAction? lastAcceptedRimAction { get; set; } /// /// A tracker for periods where alternate should not be forced (i.e. non-gameplay periods). @@ -84,11 +84,11 @@ namespace osu.Game.Rulesets.Taiko.Mods { if (!nonGameplayPeriods.IsInAny(gameplayClock.CurrentTime)) return; - if (LastAcceptedDonAction != null) - LastAcceptedDonAction = null; + if (lastAcceptedCentreAction != null) + lastAcceptedCentreAction = null; - if (LastAcceptedKatAction != null) - LastAcceptedKatAction = null; + if (lastAcceptedRimAction != null) + lastAcceptedRimAction = null; } private bool checkCorrectAction(TaikoAction action) @@ -105,9 +105,9 @@ namespace osu.Game.Rulesets.Taiko.Mods if (CheckValidNewAction(action)) { if (action == TaikoAction.LeftCentre || action == TaikoAction.RightCentre) - LastAcceptedDonAction = action; + lastAcceptedCentreAction = action; if (action == TaikoAction.LeftRim || action == TaikoAction.RightRim) - LastAcceptedKatAction = action; + lastAcceptedRimAction = action; return true; } From ac6c8e600a4ef58cedae8f19b8fabf50fa8aa6fc Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Mon, 6 Mar 2023 10:59:47 +0000 Subject: [PATCH 161/476] Updated a comment for correctness --- osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs index 30e4946833..9b411b49d4 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs @@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Taiko.Mods private TaikoAction? lastAcceptedRimAction { get; set; } /// - /// A tracker for periods where alternate should not be forced (i.e. non-gameplay periods). + /// A tracker for periods where single tap should not be enforced (i.e. non-gameplay periods). /// /// /// This is different from in that the periods here end strictly at the first object after the break, rather than the break's end time. From 4ae2661f1bd90181b54917ef55df431c7622ad57 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Mon, 6 Mar 2023 11:00:37 +0000 Subject: [PATCH 162/476] Removed useless null checks --- osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs index 9b411b49d4..92aff90ae0 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs @@ -84,11 +84,8 @@ namespace osu.Game.Rulesets.Taiko.Mods { if (!nonGameplayPeriods.IsInAny(gameplayClock.CurrentTime)) return; - if (lastAcceptedCentreAction != null) - lastAcceptedCentreAction = null; - - if (lastAcceptedRimAction != null) - lastAcceptedRimAction = null; + lastAcceptedCentreAction = null; + lastAcceptedRimAction = null; } private bool checkCorrectAction(TaikoAction action) From 36108ea841603febdc2602e9f4a85ad69c25f34a Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Mon, 6 Mar 2023 11:10:57 +0000 Subject: [PATCH 163/476] Moved `CheckValidNewAction` checks into `checkCorrectAction` --- .../Mods/TaikoModSingleTap.cs | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs index 92aff90ae0..556e31d2c2 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs @@ -26,16 +26,6 @@ namespace osu.Game.Rulesets.Taiko.Mods public override string Acronym => @"SG"; public override LocalisableString Description => @"One key for dons, one key for kats."; - protected bool CheckValidNewAction(TaikoAction action) - { - if (action == TaikoAction.LeftCentre || action == TaikoAction.RightCentre) - return lastAcceptedCentreAction == null || lastAcceptedCentreAction == action; - if (action == TaikoAction.LeftRim || action == TaikoAction.RightRim) - return lastAcceptedRimAction == null || lastAcceptedRimAction == action; - - return true; - } - public override double ScoreMultiplier => 1.0; public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModRelax), typeof(TaikoModCinema) }; public override ModType Type => ModType.Conversion; @@ -99,13 +89,17 @@ namespace osu.Game.Rulesets.Taiko.Mods && hitObject is not DrumRoll) return true; - if (CheckValidNewAction(action)) + if ((action == TaikoAction.LeftCentre || action == TaikoAction.RightCentre) + && (lastAcceptedCentreAction == null || lastAcceptedCentreAction == action)) { - if (action == TaikoAction.LeftCentre || action == TaikoAction.RightCentre) - lastAcceptedCentreAction = action; - if (action == TaikoAction.LeftRim || action == TaikoAction.RightRim) - lastAcceptedRimAction = action; + lastAcceptedCentreAction = action; + return true; + } + if ((action == TaikoAction.LeftRim || action == TaikoAction.RightRim) + && (lastAcceptedRimAction == null || lastAcceptedRimAction == action)) + { + lastAcceptedRimAction = action; return true; } From b2d453fd2edae6478bb0dfbfa860bea39a8251c3 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Mon, 6 Mar 2023 11:44:00 +0000 Subject: [PATCH 164/476] Fixed indentation --- osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs index 556e31d2c2..fa876555e1 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs @@ -90,14 +90,14 @@ namespace osu.Game.Rulesets.Taiko.Mods return true; if ((action == TaikoAction.LeftCentre || action == TaikoAction.RightCentre) - && (lastAcceptedCentreAction == null || lastAcceptedCentreAction == action)) + && (lastAcceptedCentreAction == null || lastAcceptedCentreAction == action)) { lastAcceptedCentreAction = action; return true; } if ((action == TaikoAction.LeftRim || action == TaikoAction.RightRim) - && (lastAcceptedRimAction == null || lastAcceptedRimAction == action)) + && (lastAcceptedRimAction == null || lastAcceptedRimAction == action)) { lastAcceptedRimAction = action; return true; From 1fcf41379d5d5c37d211644fc96e4e99c4710ab0 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Mon, 6 Mar 2023 16:20:36 +0100 Subject: [PATCH 165/476] Added maintainability patch by bdach --- osu.Game/Beatmaps/BeatmapInfoExtensions.cs | 22 +++++------- .../Beatmaps/BeatmapMetadataInfoExtensions.cs | 35 +++++++++++++------ 2 files changed, 33 insertions(+), 24 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapInfoExtensions.cs b/osu.Game/Beatmaps/BeatmapInfoExtensions.cs index 460944d2fa..3aab9a24e1 100644 --- a/osu.Game/Beatmaps/BeatmapInfoExtensions.cs +++ b/osu.Game/Beatmaps/BeatmapInfoExtensions.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. -using System; +using System.Collections.Generic; using osu.Framework.Localisation; namespace osu.Game.Beatmaps @@ -29,25 +29,19 @@ namespace osu.Game.Beatmaps return new RomanisableString($"{metadata.GetPreferred(true)}".Trim(), $"{metadata.GetPreferred(false)}".Trim()); } - public static ReadOnlySpan GetSearchableTerms(this IBeatmapInfo beatmapInfo) + public static List GetSearchableTerms(this IBeatmapInfo beatmapInfo) { - string[] terms = new string[8]; - int i = 0; - var metadata = beatmapInfo.Metadata; + var termsList = new List(BeatmapMetadataInfoExtensions.MAX_SEARCHABLE_TERM_COUNT + 1); + addIfNotNull(beatmapInfo.DifficultyName); - addIfNotNull(metadata.Author.Username); - addIfNotNull(metadata.Artist); - addIfNotNull(metadata.ArtistUnicode); - addIfNotNull(metadata.Title); - addIfNotNull(metadata.TitleUnicode); - addIfNotNull(metadata.Source); - addIfNotNull(metadata.Tags); - return terms.AsSpan(0, i); + + BeatmapMetadataInfoExtensions.CollectSearchableTerms(beatmapInfo.Metadata, termsList); + return termsList; void addIfNotNull(string? s) { if (!string.IsNullOrEmpty(s)) - terms[i++] = s; + termsList.Add(s); } } diff --git a/osu.Game/Beatmaps/BeatmapMetadataInfoExtensions.cs b/osu.Game/Beatmaps/BeatmapMetadataInfoExtensions.cs index 738bdb0839..fe4e815e62 100644 --- a/osu.Game/Beatmaps/BeatmapMetadataInfoExtensions.cs +++ b/osu.Game/Beatmaps/BeatmapMetadataInfoExtensions.cs @@ -3,7 +3,7 @@ #nullable disable -using System.Linq; +using System.Collections.Generic; using osu.Framework.Localisation; namespace osu.Game.Beatmaps @@ -13,16 +13,31 @@ namespace osu.Game.Beatmaps /// /// An array of all searchable terms provided in contained metadata. /// - public static string[] GetSearchableTerms(this IBeatmapMetadataInfo metadataInfo) => new[] + public static string[] GetSearchableTerms(this IBeatmapMetadataInfo metadataInfo) { - metadataInfo.Author.Username, - metadataInfo.Artist, - metadataInfo.ArtistUnicode, - metadataInfo.Title, - metadataInfo.TitleUnicode, - metadataInfo.Source, - metadataInfo.Tags - }.Where(s => !string.IsNullOrEmpty(s)).ToArray(); + var termsList = new List(MAX_SEARCHABLE_TERM_COUNT); + CollectSearchableTerms(metadataInfo, termsList); + return termsList.ToArray(); + } + + internal const int MAX_SEARCHABLE_TERM_COUNT = 7; + + internal static void CollectSearchableTerms(IBeatmapMetadataInfo metadataInfo, IList termsList) + { + addIfNotNull(metadataInfo.Author.Username); + addIfNotNull(metadataInfo.Artist); + addIfNotNull(metadataInfo.ArtistUnicode); + addIfNotNull(metadataInfo.Title); + addIfNotNull(metadataInfo.TitleUnicode); + addIfNotNull(metadataInfo.Source); + addIfNotNull(metadataInfo.Tags); + + void addIfNotNull(string s) + { + if (!string.IsNullOrEmpty(s)) + termsList.Add(s); + } + } /// /// A user-presentable display title representing this metadata. From 952814604ed43ab85b799198268cd222b7f9b9a9 Mon Sep 17 00:00:00 2001 From: tsrk Date: Mon, 6 Mar 2023 20:06:56 +0000 Subject: [PATCH 166/476] style: fix late night oopsies --- osu.Game.Tests/Skins/SkinDeserialisationTest.cs | 3 --- osu.Game/Screens/Play/HUD/PlayerAvatar.cs | 3 +-- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game.Tests/Skins/SkinDeserialisationTest.cs b/osu.Game.Tests/Skins/SkinDeserialisationTest.cs index eb1df954aa..bd8088cfb6 100644 --- a/osu.Game.Tests/Skins/SkinDeserialisationTest.cs +++ b/osu.Game.Tests/Skins/SkinDeserialisationTest.cs @@ -9,7 +9,6 @@ using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; -using osu.Framework.Logging; using osu.Game.Audio; using osu.Game.IO; using osu.Game.IO.Archives; @@ -64,8 +63,6 @@ namespace osu.Game.Tests.Skins foreach (string oskFile in available_skins) { - Logger.Log($"Testing file {oskFile}..."); - using (var stream = TestResources.OpenResource(oskFile)) using (var storage = new ZipArchiveReader(stream)) { diff --git a/osu.Game/Screens/Play/HUD/PlayerAvatar.cs b/osu.Game/Screens/Play/HUD/PlayerAvatar.cs index 32ed045df7..d2f4f0be17 100644 --- a/osu.Game/Screens/Play/HUD/PlayerAvatar.cs +++ b/osu.Game/Screens/Play/HUD/PlayerAvatar.cs @@ -43,8 +43,7 @@ namespace osu.Game.Screens.Play.HUD base.LoadComplete(); avatar.User = gameplayState.Score.ScoreInfo.User; - avatar.CornerRadius = CornerRadius.Value; - CornerRadius.BindValueChanged(e => avatar.CornerRadius = e.NewValue); + CornerRadius.BindValueChanged(e => avatar.CornerRadius = e.NewValue, true); } public bool UsesFixedAnchor { get; set; } From fc0e27fb15e3eb1a143e5924a9adf3f472944a06 Mon Sep 17 00:00:00 2001 From: tsrk Date: Mon, 6 Mar 2023 20:11:52 +0000 Subject: [PATCH 167/476] style: use normalised `CornerRadius` values --- osu.Game/Screens/Play/HUD/PlayerAvatar.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/PlayerAvatar.cs b/osu.Game/Screens/Play/HUD/PlayerAvatar.cs index d2f4f0be17..f293199911 100644 --- a/osu.Game/Screens/Play/HUD/PlayerAvatar.cs +++ b/osu.Game/Screens/Play/HUD/PlayerAvatar.cs @@ -16,10 +16,10 @@ namespace osu.Game.Screens.Play.HUD public partial class PlayerAvatar : CompositeDrawable, ISerialisableDrawable { [SettingSource(typeof(SkinnableComponentStrings), nameof(SkinnableComponentStrings.CornerRadius), nameof(SkinnableComponentStrings.CornerRadiusDescription))] - public new BindableFloat CornerRadius { get; set; } = new BindableFloat + public new BindableFloat CornerRadius { get; set; } = new BindableFloat(0.25f) { MinValue = 0, - MaxValue = 64, + MaxValue = 0.5f, Precision = 0.01f }; @@ -43,7 +43,7 @@ namespace osu.Game.Screens.Play.HUD base.LoadComplete(); avatar.User = gameplayState.Score.ScoreInfo.User; - CornerRadius.BindValueChanged(e => avatar.CornerRadius = e.NewValue, true); + CornerRadius.BindValueChanged(e => avatar.CornerRadius = e.NewValue * 128f, true); } public bool UsesFixedAnchor { get; set; } From 21bdbb20e6b362f96f53863614474dfae32f1726 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 7 Mar 2023 14:22:12 +0900 Subject: [PATCH 168/476] Add optional support for cyclic selection to `BlueprintContainer` --- .../SkinEditor/SkinBlueprintContainer.cs | 2 + .../Compose/Components/BlueprintContainer.cs | 72 +++++++++++++++---- 2 files changed, 61 insertions(+), 13 deletions(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinBlueprintContainer.cs b/osu.Game/Overlays/SkinEditor/SkinBlueprintContainer.cs index 3f8d9f80d4..db27e20010 100644 --- a/osu.Game/Overlays/SkinEditor/SkinBlueprintContainer.cs +++ b/osu.Game/Overlays/SkinEditor/SkinBlueprintContainer.cs @@ -25,6 +25,8 @@ namespace osu.Game.Overlays.SkinEditor [Resolved] private SkinEditor editor { get; set; } = null!; + protected override bool AllowCyclicSelection => true; + public SkinBlueprintContainer(ISerialisableDrawableContainer targetContainer) { this.targetContainer = targetContainer; diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index e4e67d10d7..87cee59d83 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -45,6 +45,15 @@ namespace osu.Game.Screens.Edit.Compose.Components protected readonly BindableList SelectedItems = new BindableList(); + /// + /// Whether to allow cyclic selection on clicking multiple times. + /// + /// + /// Disabled by default as it does not work well with editors that support double-clicking or other advanced interactions. + /// Can probably be made to work with more thought. + /// + protected virtual bool AllowCyclicSelection => false; + protected BlueprintContainer() { RelativeSizeAxes = Axes.Both; @@ -167,7 +176,7 @@ namespace osu.Game.Screens.Edit.Compose.Components Schedule(() => { endClickSelection(e); - clickSelectionBegan = false; + clickSelectionHandled = false; isDraggingBlueprint = false; }); @@ -339,7 +348,12 @@ namespace osu.Game.Screens.Edit.Compose.Components /// /// Whether a blueprint was selected by a previous click event. /// - private bool clickSelectionBegan; + private bool clickSelectionHandled; + + /// + /// Whether the selected blueprint(s) were already selected on mouse down. Generally used to perform selection cycling on mouse up in such a case. + /// + private bool selectedBlueprintAlreadySelectedOnMouseDown; /// /// Attempts to select any hovered blueprints. @@ -354,7 +368,8 @@ namespace osu.Game.Screens.Edit.Compose.Components { if (!blueprint.IsHovered) continue; - return clickSelectionBegan = SelectionHandler.MouseDownSelectionRequested(blueprint, e); + selectedBlueprintAlreadySelectedOnMouseDown = blueprint.State == SelectionState.Selected; + return clickSelectionHandled = SelectionHandler.MouseDownSelectionRequested(blueprint, e); } return false; @@ -367,18 +382,45 @@ namespace osu.Game.Screens.Edit.Compose.Components /// Whether a click selection was active. private bool endClickSelection(MouseButtonEvent e) { - if (!clickSelectionBegan && !isDraggingBlueprint) + if (!clickSelectionHandled && !isDraggingBlueprint) { - // if a selection didn't occur, we may want to trigger a deselection. - if (e.ControlPressed && e.Button == MouseButton.Left) + if (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 (e.ControlPressed) { - if (!blueprint.IsHovered) continue; + // if a selection didn't occur, we may want to trigger a deselection. - return clickSelectionBegan = SelectionHandler.MouseUpSelectionRequested(blueprint, 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. + foreach (SelectionBlueprint blueprint in SelectionBlueprints.AliveChildren.OrderByDescending(b => b.IsSelected)) + { + if (!blueprint.IsHovered) continue; + + return clickSelectionHandled = SelectionHandler.MouseUpSelectionRequested(blueprint, e); + } + } + else if (selectedBlueprintAlreadySelectedOnMouseDown && AllowCyclicSelection) + { + // If a click occurred and was handled by the currently selected blueprint but didn't result in a drag, + // cycle between other blueprints which are also under the cursor. + + // The depth of blueprints is constantly changing (see above where selected blueprints are brought to the front). + // For this logic, we want a stable sort order so we can correctly cycle, thus using the blueprintMap instead. + IEnumerable> cyclingSelectionBlueprints = blueprintMap.Values; + + // If there's already a selection, let's start from the blueprint after the selection. + cyclingSelectionBlueprints = cyclingSelectionBlueprints.SkipWhile(b => !b.IsSelected).Skip(1); + + // Add the blueprints from before the selection to the end of the enumerable to allow for cyclic selection. + cyclingSelectionBlueprints = cyclingSelectionBlueprints.Concat(blueprintMap.Values.TakeWhile(b => !b.IsSelected)); + + foreach (SelectionBlueprint blueprint in cyclingSelectionBlueprints) + { + if (!blueprint.IsHovered) continue; + + // We are performing a mouse up, but selection handlers perform selection on mouse down, so we need to call that instead. + return clickSelectionHandled = SelectionHandler.MouseDownSelectionRequested(blueprint, e); + } } } @@ -441,7 +483,11 @@ namespace osu.Game.Screens.Edit.Compose.Components private Vector2[][] movementBlueprintsOriginalPositions; private SelectionBlueprint[] movementBlueprints; - private bool isDraggingBlueprint; + + /// + /// Whether a blueprint is currently being dragged. + /// + private bool isDraggingBlueprint { get; set; } /// /// Attempts to begin the movement of any selected blueprints. @@ -454,7 +500,7 @@ namespace osu.Game.Screens.Edit.Compose.Components // Any selected blueprint that is hovered can begin the movement of the group, however only the first item (according to SortForMovement) is used for movement. // A special case is added for when a click selection occurred before the drag - if (!clickSelectionBegan && !SelectionHandler.SelectedBlueprints.Any(b => b.IsHovered)) + if (!clickSelectionHandled && !SelectionHandler.SelectedBlueprints.Any(b => b.IsHovered)) return false; // Movement is tracked from the blueprint of the earliest item, since it only makes sense to distance snap from that item From d2fcdf6e0e464ec389cff62ca715411402df6323 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 7 Mar 2023 14:22:19 +0900 Subject: [PATCH 169/476] Add test coverage of cyclic selection in skin editor --- .../Visual/Gameplay/TestSceneSkinEditor.cs | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs index 9690d00d4c..7af2b7d6fe 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs @@ -17,6 +17,8 @@ using osu.Game.Rulesets.Osu; using osu.Game.Screens.Edit; using osu.Game.Screens.Play.HUD.HitErrorMeters; using osu.Game.Skinning; +using osu.Game.Skinning.Components; +using osuTK; using osuTK.Input; namespace osu.Game.Tests.Visual.Gameplay @@ -52,6 +54,44 @@ namespace osu.Game.Tests.Visual.Gameplay AddUntilStep("wait for loaded", () => skinEditor.IsLoaded); } + [Test] + public void TestCyclicSelection() + { + SkinBlueprint[] blueprints = null!; + + AddStep("Add big black boxes", () => + { + InputManager.MoveMouseTo(skinEditor.ChildrenOfType().First()); + InputManager.Click(MouseButton.Left); + InputManager.Click(MouseButton.Left); + InputManager.Click(MouseButton.Left); + }); + + AddAssert("Three black boxes added", () => targetContainer.Components.OfType().Count(), () => Is.EqualTo(3)); + + AddStep("Store black box blueprints", () => + { + blueprints = skinEditor.ChildrenOfType().Where(b => b.Item is BigBlackBox).ToArray(); + }); + + AddAssert("Selection is black box 1", () => skinEditor.SelectedComponents.Single(), () => Is.EqualTo(blueprints[0].Item)); + + AddStep("move cursor to black box", () => + { + // Slightly offset from centre to avoid random failures (see https://github.com/ppy/osu-framework/issues/5669). + InputManager.MoveMouseTo(((Drawable)blueprints[0].Item).ScreenSpaceDrawQuad.Centre + new Vector2(1)); + }); + + AddStep("click on black box stack", () => InputManager.Click(MouseButton.Left)); + AddAssert("Selection is black box 2", () => skinEditor.SelectedComponents.Single(), () => Is.EqualTo(blueprints[1].Item)); + + AddStep("click on black box stack", () => InputManager.Click(MouseButton.Left)); + AddAssert("Selection is black box 3", () => skinEditor.SelectedComponents.Single(), () => Is.EqualTo(blueprints[2].Item)); + + AddStep("click on black box stack", () => InputManager.Click(MouseButton.Left)); + AddAssert("Selection is black box 1", () => skinEditor.SelectedComponents.Single(), () => Is.EqualTo(blueprints[0].Item)); + } + [TestCase(false)] [TestCase(true)] public void TestBringToFront(bool alterSelectionOrder) From 2cce785fa5bb7fd1ab6b77d1a757a76a6fc3f94b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 7 Mar 2023 15:54:33 +0900 Subject: [PATCH 170/476] Fix storyboard videos not fading out on completion Closes https://github.com/ppy/osu/issues/22802. Stable uses a 1,000 ms fade-in / out. Rather than matching that, I've stuck with 500ms (what lazer was already using for the fade-in) because I think it feels better. Tested using the beatmap linked in the issue thread. --- osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs index f4b0692619..eec2cd6a60 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs @@ -58,7 +58,11 @@ namespace osu.Game.Storyboards.Drawables using (drawableVideo.BeginAbsoluteSequence(Video.StartTime)) { Schedule(() => drawableVideo.PlaybackPosition = Time.Current - Video.StartTime); + drawableVideo.FadeIn(500); + + using (drawableVideo.BeginDelayedSequence(drawableVideo.Duration - 500)) + drawableVideo.FadeOut(500); } } } From 15d65059b5403824059b753fd9359280a475afb2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 7 Mar 2023 16:21:57 +0900 Subject: [PATCH 171/476] Tidy up `Add` method logic --- .../Visual/Gameplay/TestSceneKeyCounter.cs | 16 ++++++------ osu.Game/Screens/Play/KeyCounterDisplay.cs | 26 ++++++------------- 2 files changed, 16 insertions(+), 26 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs index 12cd7e1be9..d0c691e23f 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs @@ -3,7 +3,6 @@ #nullable disable -using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Utils; @@ -21,15 +20,16 @@ namespace osu.Game.Tests.Visual.Gameplay { Origin = Anchor.Centre, Anchor = Anchor.Centre, - Triggers = new InputTrigger[] - { - new KeyCounterKeyboardTrigger(Key.X), - new KeyCounterKeyboardTrigger(Key.X), - new KeyCounterMouseTrigger(MouseButton.Left), - new KeyCounterMouseTrigger(MouseButton.Right), - } }; + kc.AddTriggerRange(new InputTrigger[] + { + new KeyCounterKeyboardTrigger(Key.X), + new KeyCounterKeyboardTrigger(Key.X), + new KeyCounterMouseTrigger(MouseButton.Left), + new KeyCounterMouseTrigger(MouseButton.Right), + }); + var testCounter = (DefaultKeyCounter)kc.Children.First(); AddStep("Add random", () => diff --git a/osu.Game/Screens/Play/KeyCounterDisplay.cs b/osu.Game/Screens/Play/KeyCounterDisplay.cs index 01686ae6de..50670fb2fe 100644 --- a/osu.Game/Screens/Play/KeyCounterDisplay.cs +++ b/osu.Game/Screens/Play/KeyCounterDisplay.cs @@ -25,16 +25,6 @@ namespace osu.Game.Screens.Play public Bindable IsCounting { get; } = new BindableBool(true); - public IReadOnlyList Triggers - { - get => Children.Select(c => c.Trigger).ToArray(); - set - { - Clear(); - value.ForEach(AddTrigger); - } - } - protected readonly Bindable ConfigVisibility = new Bindable(); protected abstract void UpdateVisibility(); @@ -60,17 +50,17 @@ namespace osu.Game.Screens.Play public void AddTriggerRange(IEnumerable triggers) => triggers.ForEach(AddTrigger); - private bool checkType(KeyCounter key) => acceptedTypes.Length == 0 || acceptedTypes.Any(t => t.IsInstanceOfType(key)); - - public override void Add(KeyCounter key) + public override void Add(KeyCounter counter) { - if (!checkType(key)) - throw new InvalidOperationException($"{key.GetType()} is not a supported counter type. (hint: you may want to use {nameof(AddTrigger)} instead.)"); + if (!checkType(counter)) + throw new InvalidOperationException($"{counter.GetType()} is not a supported counter type. (hint: you may want to use {nameof(AddTrigger)} instead.)"); - base.Add(key); - key.IsCounting.BindTo(IsCounting); + base.Add(counter); + counter.IsCounting.BindTo(IsCounting); } + private bool checkType(KeyCounter counter) => acceptedTypes.Length == 0 || acceptedTypes.Any(t => t.IsInstanceOfType(counter)); + [BackgroundDependencyLoader] private void load(OsuConfigManager config) { @@ -110,7 +100,7 @@ namespace osu.Game.Screens.Play case KeyUpEvent: case MouseDownEvent: case MouseUpEvent: - return Target.Children.Any(c => c.TriggerEvent(e)); + return Target.InternalChildren.Any(c => c.TriggerEvent(e)); } return base.Handle(e); From 28520414aa5d857724b093b6e6b1999d80c6a21b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 7 Mar 2023 16:28:54 +0900 Subject: [PATCH 172/476] Move `KeyCounter` components to `HUD` namespace --- osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs | 1 + osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs | 3 ++- .../Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs | 1 + .../Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs | 1 + .../Navigation/TestSceneChangeAndUseGameplayBindings.cs | 1 + osu.Game/Rulesets/UI/DrawableRuleset.cs | 1 + osu.Game/Rulesets/UI/RulesetInputManager.cs | 2 +- osu.Game/Screens/Play/{ => HUD}/DefaultKeyCounter.cs | 2 +- osu.Game/Screens/Play/{ => HUD}/DefaultKeyCounterDisplay.cs | 2 +- osu.Game/Screens/Play/{ => HUD}/InputTrigger.cs | 2 +- osu.Game/Screens/Play/{ => HUD}/KeyCounter.cs | 2 +- osu.Game/Screens/Play/{ => HUD}/KeyCounterActionTrigger.cs | 2 +- osu.Game/Screens/Play/{ => HUD}/KeyCounterDisplay.cs | 2 +- osu.Game/Screens/Play/{ => HUD}/KeyCounterKeyboardTrigger.cs | 2 +- osu.Game/Screens/Play/{ => HUD}/KeyCounterMouseTrigger.cs | 4 ++-- 15 files changed, 17 insertions(+), 11 deletions(-) rename osu.Game/Screens/Play/{ => HUD}/DefaultKeyCounter.cs (99%) rename osu.Game/Screens/Play/{ => HUD}/DefaultKeyCounterDisplay.cs (98%) rename osu.Game/Screens/Play/{ => HUD}/InputTrigger.cs (94%) rename osu.Game/Screens/Play/{ => HUD}/KeyCounter.cs (98%) rename osu.Game/Screens/Play/{ => HUD}/KeyCounterActionTrigger.cs (96%) rename osu.Game/Screens/Play/{ => HUD}/KeyCounterDisplay.cs (99%) rename osu.Game/Screens/Play/{ => HUD}/KeyCounterKeyboardTrigger.cs (95%) rename osu.Game/Screens/Play/{ => HUD}/KeyCounterMouseTrigger.cs (97%) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs index ec8fad9bf3..4e13bde755 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs @@ -22,6 +22,7 @@ using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.UI.Cursor; using osu.Game.Screens.Play; +using osu.Game.Screens.Play.HUD; using osu.Game.Tests.Visual; using osuTK; using osuTK.Graphics; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs index d0c691e23f..3260ba8e33 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs @@ -3,10 +3,11 @@ #nullable disable +using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Utils; -using osu.Game.Screens.Play; +using osu.Game.Screens.Play.HUD; using osuTK.Input; namespace osu.Game.Tests.Visual.Gameplay diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs index 01d3bfa23e..aa7119829a 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs @@ -14,6 +14,7 @@ using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Edit; using osu.Game.Screens.Play; +using osu.Game.Screens.Play.HUD; using osu.Game.Tests.Gameplay; using osuTK.Input; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs index 9848894f84..808241729b 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs @@ -18,6 +18,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Play; +using osu.Game.Screens.Play.HUD; using osu.Game.Tests.Gameplay; using osuTK.Input; diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneChangeAndUseGameplayBindings.cs b/osu.Game.Tests/Visual/Navigation/TestSceneChangeAndUseGameplayBindings.cs index 59a0f9cea8..224e7e411e 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneChangeAndUseGameplayBindings.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneChangeAndUseGameplayBindings.cs @@ -14,6 +14,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; using osu.Game.Overlays.Settings.Sections.Input; using osu.Game.Screens.Play; +using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Select; using osu.Game.Tests.Beatmaps.IO; using osuTK.Input; diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 64fe9c8a86..4f22c0c617 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -30,6 +30,7 @@ using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Play; +using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Play.HUD.ClicksPerSecond; using osuTK; diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index 32b2a19e21..c2f0b1a951 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -19,7 +19,7 @@ using osu.Game.Input; using osu.Game.Input.Bindings; using osu.Game.Input.Handlers; using osu.Game.Rulesets.Scoring; -using osu.Game.Screens.Play; +using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Play.HUD.ClicksPerSecond; using static osu.Game.Input.Handlers.ReplayInputHandler; diff --git a/osu.Game/Screens/Play/DefaultKeyCounter.cs b/osu.Game/Screens/Play/HUD/DefaultKeyCounter.cs similarity index 99% rename from osu.Game/Screens/Play/DefaultKeyCounter.cs rename to osu.Game/Screens/Play/HUD/DefaultKeyCounter.cs index 3673281577..69a3e53dfc 100644 --- a/osu.Game/Screens/Play/DefaultKeyCounter.cs +++ b/osu.Game/Screens/Play/HUD/DefaultKeyCounter.cs @@ -11,7 +11,7 @@ using osu.Game.Graphics.Sprites; using osuTK; using osuTK.Graphics; -namespace osu.Game.Screens.Play +namespace osu.Game.Screens.Play.HUD { public partial class DefaultKeyCounter : KeyCounter { diff --git a/osu.Game/Screens/Play/DefaultKeyCounterDisplay.cs b/osu.Game/Screens/Play/HUD/DefaultKeyCounterDisplay.cs similarity index 98% rename from osu.Game/Screens/Play/DefaultKeyCounterDisplay.cs rename to osu.Game/Screens/Play/HUD/DefaultKeyCounterDisplay.cs index 10f5a3cfe0..1d79b2d27a 100644 --- a/osu.Game/Screens/Play/DefaultKeyCounterDisplay.cs +++ b/osu.Game/Screens/Play/HUD/DefaultKeyCounterDisplay.cs @@ -6,7 +6,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osuTK.Graphics; -namespace osu.Game.Screens.Play +namespace osu.Game.Screens.Play.HUD { public partial class DefaultKeyCounterDisplay : KeyCounterDisplay { diff --git a/osu.Game/Screens/Play/InputTrigger.cs b/osu.Game/Screens/Play/HUD/InputTrigger.cs similarity index 94% rename from osu.Game/Screens/Play/InputTrigger.cs rename to osu.Game/Screens/Play/HUD/InputTrigger.cs index b8951b0f8e..34d286d697 100644 --- a/osu.Game/Screens/Play/InputTrigger.cs +++ b/osu.Game/Screens/Play/HUD/InputTrigger.cs @@ -4,7 +4,7 @@ using System; using osu.Framework.Graphics; -namespace osu.Game.Screens.Play +namespace osu.Game.Screens.Play.HUD { public abstract partial class InputTrigger : Component { diff --git a/osu.Game/Screens/Play/KeyCounter.cs b/osu.Game/Screens/Play/HUD/KeyCounter.cs similarity index 98% rename from osu.Game/Screens/Play/KeyCounter.cs rename to osu.Game/Screens/Play/HUD/KeyCounter.cs index 7ee9c94f62..4b3da4f785 100644 --- a/osu.Game/Screens/Play/KeyCounter.cs +++ b/osu.Game/Screens/Play/HUD/KeyCounter.cs @@ -5,7 +5,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -namespace osu.Game.Screens.Play +namespace osu.Game.Screens.Play.HUD { public abstract partial class KeyCounter : Container { diff --git a/osu.Game/Screens/Play/KeyCounterActionTrigger.cs b/osu.Game/Screens/Play/HUD/KeyCounterActionTrigger.cs similarity index 96% rename from osu.Game/Screens/Play/KeyCounterActionTrigger.cs rename to osu.Game/Screens/Play/HUD/KeyCounterActionTrigger.cs index be0d259f85..1af69fdd81 100644 --- a/osu.Game/Screens/Play/KeyCounterActionTrigger.cs +++ b/osu.Game/Screens/Play/HUD/KeyCounterActionTrigger.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; -namespace osu.Game.Screens.Play +namespace osu.Game.Screens.Play.HUD { public partial class KeyCounterActionTrigger : InputTrigger where T : struct diff --git a/osu.Game/Screens/Play/KeyCounterDisplay.cs b/osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs similarity index 99% rename from osu.Game/Screens/Play/KeyCounterDisplay.cs rename to osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs index 50670fb2fe..0dbae50466 100644 --- a/osu.Game/Screens/Play/KeyCounterDisplay.cs +++ b/osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs @@ -13,7 +13,7 @@ using osu.Framework.Input.Events; using osu.Game.Configuration; using osuTK; -namespace osu.Game.Screens.Play +namespace osu.Game.Screens.Play.HUD { public abstract partial class KeyCounterDisplay : Container { diff --git a/osu.Game/Screens/Play/KeyCounterKeyboardTrigger.cs b/osu.Game/Screens/Play/HUD/KeyCounterKeyboardTrigger.cs similarity index 95% rename from osu.Game/Screens/Play/KeyCounterKeyboardTrigger.cs rename to osu.Game/Screens/Play/HUD/KeyCounterKeyboardTrigger.cs index 1d89c58fc3..742cbc1569 100644 --- a/osu.Game/Screens/Play/KeyCounterKeyboardTrigger.cs +++ b/osu.Game/Screens/Play/HUD/KeyCounterKeyboardTrigger.cs @@ -6,7 +6,7 @@ using osu.Framework.Input.Events; using osuTK.Input; -namespace osu.Game.Screens.Play +namespace osu.Game.Screens.Play.HUD { public partial class KeyCounterKeyboardTrigger : InputTrigger { diff --git a/osu.Game/Screens/Play/KeyCounterMouseTrigger.cs b/osu.Game/Screens/Play/HUD/KeyCounterMouseTrigger.cs similarity index 97% rename from osu.Game/Screens/Play/KeyCounterMouseTrigger.cs rename to osu.Game/Screens/Play/HUD/KeyCounterMouseTrigger.cs index e710c6e33f..cd71fef705 100644 --- a/osu.Game/Screens/Play/KeyCounterMouseTrigger.cs +++ b/osu.Game/Screens/Play/HUD/KeyCounterMouseTrigger.cs @@ -4,10 +4,10 @@ #nullable disable using osu.Framework.Input.Events; -using osuTK.Input; using osuTK; +using osuTK.Input; -namespace osu.Game.Screens.Play +namespace osu.Game.Screens.Play.HUD { public partial class KeyCounterMouseTrigger : InputTrigger { From 97ba236eb146fe69eb318aff6dce1db3ba8bf78d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 7 Mar 2023 16:31:36 +0900 Subject: [PATCH 173/476] Add basic xmldoc to `KeyCounter` classes --- osu.Game/Screens/Play/HUD/InputTrigger.cs | 3 +++ osu.Game/Screens/Play/HUD/KeyCounter.cs | 3 +++ osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs | 3 +++ 3 files changed, 9 insertions(+) diff --git a/osu.Game/Screens/Play/HUD/InputTrigger.cs b/osu.Game/Screens/Play/HUD/InputTrigger.cs index 34d286d697..93b45daab3 100644 --- a/osu.Game/Screens/Play/HUD/InputTrigger.cs +++ b/osu.Game/Screens/Play/HUD/InputTrigger.cs @@ -6,6 +6,9 @@ using osu.Framework.Graphics; namespace osu.Game.Screens.Play.HUD { + /// + /// An event trigger which can be used with to create visual tracking of button/key presses. + /// public abstract partial class InputTrigger : Component { public event Action? OnActivate; diff --git a/osu.Game/Screens/Play/HUD/KeyCounter.cs b/osu.Game/Screens/Play/HUD/KeyCounter.cs index 4b3da4f785..93cc4f908a 100644 --- a/osu.Game/Screens/Play/HUD/KeyCounter.cs +++ b/osu.Game/Screens/Play/HUD/KeyCounter.cs @@ -7,6 +7,9 @@ using osu.Framework.Graphics.Containers; namespace osu.Game.Screens.Play.HUD { + /// + /// An individual key display which is intended to be displayed within a . + /// public abstract partial class KeyCounter : Container { public readonly InputTrigger Trigger; diff --git a/osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs b/osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs index 0dbae50466..4080e561cd 100644 --- a/osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs +++ b/osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs @@ -15,6 +15,9 @@ using osuTK; namespace osu.Game.Screens.Play.HUD { + /// + /// A flowing display of all gameplay keys. Individual keys can be added using implementations. + /// public abstract partial class KeyCounterDisplay : Container { /// From 6a7c4d0bf7ffef86abc7eef6f34a09d26238ffc3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 7 Mar 2023 16:32:38 +0900 Subject: [PATCH 174/476] Remove `NRT` disables in new classes --- osu.Game/Screens/Play/HUD/KeyCounterActionTrigger.cs | 2 -- osu.Game/Screens/Play/HUD/KeyCounterKeyboardTrigger.cs | 2 -- osu.Game/Screens/Play/HUD/KeyCounterMouseTrigger.cs | 2 -- 3 files changed, 6 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/KeyCounterActionTrigger.cs b/osu.Game/Screens/Play/HUD/KeyCounterActionTrigger.cs index 1af69fdd81..e5951a8bf4 100644 --- a/osu.Game/Screens/Play/HUD/KeyCounterActionTrigger.cs +++ b/osu.Game/Screens/Play/HUD/KeyCounterActionTrigger.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 disable - using System.Collections.Generic; namespace osu.Game.Screens.Play.HUD diff --git a/osu.Game/Screens/Play/HUD/KeyCounterKeyboardTrigger.cs b/osu.Game/Screens/Play/HUD/KeyCounterKeyboardTrigger.cs index 742cbc1569..3052c1e666 100644 --- a/osu.Game/Screens/Play/HUD/KeyCounterKeyboardTrigger.cs +++ b/osu.Game/Screens/Play/HUD/KeyCounterKeyboardTrigger.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 disable - using osu.Framework.Input.Events; using osuTK.Input; diff --git a/osu.Game/Screens/Play/HUD/KeyCounterMouseTrigger.cs b/osu.Game/Screens/Play/HUD/KeyCounterMouseTrigger.cs index cd71fef705..369aaa9f74 100644 --- a/osu.Game/Screens/Play/HUD/KeyCounterMouseTrigger.cs +++ b/osu.Game/Screens/Play/HUD/KeyCounterMouseTrigger.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 disable - using osu.Framework.Input.Events; using osuTK; using osuTK.Input; From f7f1dff6471784c79a14c27e79f168597ca30a0c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 7 Mar 2023 16:45:33 +0900 Subject: [PATCH 175/476] Fix incorrect case of localisable string for "corner radius" --- .../Localisation/SkinComponents/SkinnableComponentStrings.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Localisation/SkinComponents/SkinnableComponentStrings.cs b/osu.Game/Localisation/SkinComponents/SkinnableComponentStrings.cs index 5cec9442e4..7c11ea6ac6 100644 --- a/osu.Game/Localisation/SkinComponents/SkinnableComponentStrings.cs +++ b/osu.Game/Localisation/SkinComponents/SkinnableComponentStrings.cs @@ -40,9 +40,9 @@ namespace osu.Game.Localisation.SkinComponents public static LocalisableString TextElementTextDescription => new TranslatableString(getKey(@"text_element_text_description"), "The text to be displayed."); /// - /// "Corner Radius" + /// "Corner radius" /// - public static LocalisableString CornerRadius => new TranslatableString(getKey(@"corner_radius"), "Corner Radius"); + public static LocalisableString CornerRadius => new TranslatableString(getKey(@"corner_radius"), "Corner radius"); /// /// "How rounded the corners should be." From d98199961b2d0820a3e4ff12816ac1c8e2190004 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 7 Mar 2023 17:00:10 +0900 Subject: [PATCH 176/476] Adjust default sizing to fit a bit better with existing elements --- osu.Game/Screens/Play/HUD/PlayerAvatar.cs | 7 +++++-- osu.Game/Screens/Play/HUD/PlayerFlag.cs | 4 +++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/PlayerAvatar.cs b/osu.Game/Screens/Play/HUD/PlayerAvatar.cs index f293199911..1c558aaa8a 100644 --- a/osu.Game/Screens/Play/HUD/PlayerAvatar.cs +++ b/osu.Game/Screens/Play/HUD/PlayerAvatar.cs @@ -28,9 +28,12 @@ namespace osu.Game.Screens.Play.HUD private readonly UpdateableAvatar avatar; + private const float default_size = 80f; + public PlayerAvatar() { - Size = new Vector2(128f); + Size = new Vector2(default_size); + InternalChild = avatar = new UpdateableAvatar(isInteractive: false) { RelativeSizeAxes = Axes.Both, @@ -43,7 +46,7 @@ namespace osu.Game.Screens.Play.HUD base.LoadComplete(); avatar.User = gameplayState.Score.ScoreInfo.User; - CornerRadius.BindValueChanged(e => avatar.CornerRadius = e.NewValue * 128f, true); + CornerRadius.BindValueChanged(e => avatar.CornerRadius = e.NewValue * default_size, true); } public bool UsesFixedAnchor { get; set; } diff --git a/osu.Game/Screens/Play/HUD/PlayerFlag.cs b/osu.Game/Screens/Play/HUD/PlayerFlag.cs index 17188d0009..bf65ee23d8 100644 --- a/osu.Game/Screens/Play/HUD/PlayerFlag.cs +++ b/osu.Game/Screens/Play/HUD/PlayerFlag.cs @@ -17,9 +17,11 @@ namespace osu.Game.Screens.Play.HUD private readonly UpdateableFlag flag; + private const float default_size = 40f; + public PlayerFlag() { - Size = new Vector2(114, 80); + Size = new Vector2(default_size, default_size / 1.4f); InternalChild = flag = new UpdateableFlag { RelativeSizeAxes = Axes.Both, From 5e90b67be09b842a9fb4b7114f49e4463afdce52 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 7 Mar 2023 17:01:29 +0900 Subject: [PATCH 177/476] Move `GameplayState` usage to BDL --- osu.Game/Screens/Play/HUD/PlayerFlag.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/PlayerFlag.cs b/osu.Game/Screens/Play/HUD/PlayerFlag.cs index bf65ee23d8..85799c03d3 100644 --- a/osu.Game/Screens/Play/HUD/PlayerFlag.cs +++ b/osu.Game/Screens/Play/HUD/PlayerFlag.cs @@ -12,9 +12,6 @@ namespace osu.Game.Screens.Play.HUD { public partial class PlayerFlag : CompositeDrawable, ISerialisableDrawable { - [Resolved] - private GameplayState gameplayState { get; set; } = null!; - private readonly UpdateableFlag flag; private const float default_size = 40f; @@ -28,10 +25,9 @@ namespace osu.Game.Screens.Play.HUD }; } - protected override void LoadComplete() + [BackgroundDependencyLoader] + private void load(GameplayState gameplayState) { - base.LoadComplete(); - flag.CountryCode = gameplayState.Score.ScoreInfo.User.CountryCode; } From 5af41bb1c81054205c4cb77ccc9b0faf4686288d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 7 Mar 2023 17:24:17 +0900 Subject: [PATCH 178/476] Move filter matching code into own method to simplify early returns --- .../Select/Carousel/CarouselBeatmap.cs | 28 +++++++------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs index 2cfa163b7a..7e48bc5cdd 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs @@ -26,6 +26,11 @@ namespace osu.Game.Screens.Select.Carousel { base.Filter(criteria); + Filtered.Value = !checkMatch(criteria); + } + + private bool checkMatch(FilterCriteria criteria) + { bool match = criteria.Ruleset == null || BeatmapInfo.Ruleset.ShortName == criteria.Ruleset.ShortName || @@ -34,8 +39,7 @@ namespace osu.Game.Screens.Select.Carousel if (BeatmapInfo.BeatmapSet?.Equals(criteria.SelectedBeatmapSet) == true) { // only check ruleset equality or convertability for selected beatmap - Filtered.Value = !match; - return; + return match; } match &= !criteria.StarDifficulty.HasFilter || criteria.StarDifficulty.IsInRange(BeatmapInfo.StarRating); @@ -49,11 +53,7 @@ namespace osu.Game.Screens.Select.Carousel match &= !criteria.BeatDivisor.HasFilter || criteria.BeatDivisor.IsInRange(BeatmapInfo.BeatDivisor); match &= !criteria.OnlineStatus.HasFilter || criteria.OnlineStatus.IsInRange(BeatmapInfo.Status); - if (!match) - { - Filtered.Value = !match; - return; - } + if (!match) return false; match &= !criteria.Creator.HasFilter || criteria.Creator.Matches(BeatmapInfo.Metadata.Author.Username); match &= !criteria.Artist.HasFilter || criteria.Artist.Matches(BeatmapInfo.Metadata.Artist) || @@ -61,11 +61,7 @@ namespace osu.Game.Screens.Select.Carousel match &= !criteria.UserStarDifficulty.HasFilter || criteria.UserStarDifficulty.IsInRange(BeatmapInfo.StarRating); - if (!match) - { - Filtered.Value = !match; - return; - } + if (!match) return false; if (criteria.SearchTerms.Length > 0) { @@ -99,18 +95,14 @@ namespace osu.Game.Screens.Select.Carousel } } - if (!match) - { - Filtered.Value = !match; - return; - } + if (!match) return false; match &= criteria.CollectionBeatmapMD5Hashes?.Contains(BeatmapInfo.MD5Hash) ?? true; if (match && criteria.RulesetCriteria != null) match &= criteria.RulesetCriteria.Matches(BeatmapInfo); - Filtered.Value = !match; + return match; } public override int CompareTo(FilterCriteria criteria, CarouselItem other) From dc2945f4f8635a08c83c84fd57f65c6da4eb8e47 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 7 Mar 2023 17:39:21 +0900 Subject: [PATCH 179/476] Move avatar user loading to BDL also --- osu.Game/Screens/Play/HUD/PlayerAvatar.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/PlayerAvatar.cs b/osu.Game/Screens/Play/HUD/PlayerAvatar.cs index 1c558aaa8a..e3fab499f3 100644 --- a/osu.Game/Screens/Play/HUD/PlayerAvatar.cs +++ b/osu.Game/Screens/Play/HUD/PlayerAvatar.cs @@ -23,9 +23,6 @@ namespace osu.Game.Screens.Play.HUD Precision = 0.01f }; - [Resolved] - private GameplayState gameplayState { get; set; } = null!; - private readonly UpdateableAvatar avatar; private const float default_size = 80f; @@ -41,11 +38,16 @@ namespace osu.Game.Screens.Play.HUD }; } + [BackgroundDependencyLoader] + private void load(GameplayState gameplayState) + { + avatar.User = gameplayState.Score.ScoreInfo.User; + } + protected override void LoadComplete() { base.LoadComplete(); - avatar.User = gameplayState.Score.ScoreInfo.User; CornerRadius.BindValueChanged(e => avatar.CornerRadius = e.NewValue * default_size, true); } From 55467fcbe35cc6c8c7a94740c372b665eaa77519 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 7 Mar 2023 18:00:37 +0900 Subject: [PATCH 180/476] Update xmldoc for `DangerousActionDialog` to match new behaviour --- osu.Game/Overlays/Dialog/DangerousActionDialog.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Dialog/DangerousActionDialog.cs b/osu.Game/Overlays/Dialog/DangerousActionDialog.cs index 85cae5e0d2..c86570386f 100644 --- a/osu.Game/Overlays/Dialog/DangerousActionDialog.cs +++ b/osu.Game/Overlays/Dialog/DangerousActionDialog.cs @@ -8,10 +8,14 @@ using osu.Game.Localisation; namespace osu.Game.Overlays.Dialog { /// - /// Base class for various confirmation dialogs that concern deletion actions. + /// A dialog which provides confirmation for actions which result in permanent consequences. /// Differs from in that the confirmation button is a "dangerous" one /// (requires the confirm button to be held). /// + /// + /// The default implementation comes with text for a generic deletion operation. + /// This can be further customised by specifying custom . + /// public abstract partial class DangerousActionDialog : PopupDialog { /// From 26ef7c2637ddde346c65ff6bd853a48c03d5863e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 7 Mar 2023 18:01:13 +0900 Subject: [PATCH 181/476] Rename confirmation dialog class to mention `revert` not `reset` --- osu.Game/Overlays/SkinEditor/SkinEditor.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinEditor.cs b/osu.Game/Overlays/SkinEditor/SkinEditor.cs index a7fe459aa4..c6d190502b 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditor.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditor.cs @@ -153,7 +153,7 @@ namespace osu.Game.Overlays.SkinEditor Items = new[] { new EditorMenuItem(Web.CommonStrings.ButtonsSave, MenuItemType.Standard, () => Save()), - new EditorMenuItem(CommonStrings.RevertToDefault, MenuItemType.Destructive, () => dialogOverlay?.Push(new ResetConfirmDialog(revert))), + new EditorMenuItem(CommonStrings.RevertToDefault, MenuItemType.Destructive, () => dialogOverlay?.Push(new RevertConfirmDialog(revert))), new EditorMenuItemSpacer(), new EditorMenuItem(CommonStrings.Exit, MenuItemType.Standard, () => skinEditorOverlay?.Hide()), }, @@ -630,9 +630,9 @@ namespace osu.Game.Overlays.SkinEditor } } - public partial class ResetConfirmDialog : DangerousActionDialog + public partial class RevertConfirmDialog : DangerousActionDialog { - public ResetConfirmDialog(Action revert) + public RevertConfirmDialog(Action revert) { HeaderText = SkinEditorStrings.RevertToDefaultDescription; BodyText = SkinEditorStrings.ResetDialogue; From 97bee4db89048419265aeb9a1a6c9ffa478cc68a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 7 Mar 2023 18:07:53 +0900 Subject: [PATCH 182/476] Fix localisations --- osu.Game/Localisation/SkinEditorStrings.cs | 9 ++------- osu.Game/Overlays/SkinEditor/SkinEditor.cs | 4 ++-- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/osu.Game/Localisation/SkinEditorStrings.cs b/osu.Game/Localisation/SkinEditorStrings.cs index a6b59418fd..3c1d1ff40d 100644 --- a/osu.Game/Localisation/SkinEditorStrings.cs +++ b/osu.Game/Localisation/SkinEditorStrings.cs @@ -45,14 +45,9 @@ namespace osu.Game.Localisation public static LocalisableString CurrentlyEditing => new TranslatableString(getKey(@"currently_editing"), @"Currently editing"); /// - /// "Revert to default." + /// "All layout elements for layers in the current screen will be reset to defaults." /// - public static LocalisableString RevertToDefaultDescription => new TranslatableString(getKey(@"revert_to_default"), @"Revert to default."); - - /// - /// "Return the skin to its default state" - /// - public static LocalisableString ResetDialogue => new TranslatableString(getKey(@"return_the_skin_to_its"), @"Return the skin to its default state"); + public static LocalisableString RevertToDefaultDescription => new TranslatableString(getKey(@"revert_to_default_description"), @"All layout elements for layers in the current screen will be reset to defaults."); private static string getKey(string key) => $@"{prefix}:{key}"; } diff --git a/osu.Game/Overlays/SkinEditor/SkinEditor.cs b/osu.Game/Overlays/SkinEditor/SkinEditor.cs index c6d190502b..ac40d1335e 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditor.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditor.cs @@ -634,8 +634,8 @@ namespace osu.Game.Overlays.SkinEditor { public RevertConfirmDialog(Action revert) { - HeaderText = SkinEditorStrings.RevertToDefaultDescription; - BodyText = SkinEditorStrings.ResetDialogue; + HeaderText = CommonStrings.RevertToDefault; + BodyText = SkinEditorStrings.RevertToDefaultDescription; DangerousAction = revert; } } From 5191204569241e0e92c93b51681a9384d711a019 Mon Sep 17 00:00:00 2001 From: Cootz Date: Tue, 7 Mar 2023 15:39:43 +0300 Subject: [PATCH 183/476] Bring truncating logic back to `Export` method --- osu.Game/Database/LegacyExporter.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game/Database/LegacyExporter.cs b/osu.Game/Database/LegacyExporter.cs index 0fa7b9e03c..9981e08442 100644 --- a/osu.Game/Database/LegacyExporter.cs +++ b/osu.Game/Database/LegacyExporter.cs @@ -48,15 +48,7 @@ namespace osu.Game.Database UserFileStorage = storage.GetStorageForDirectory(@"files"); } - protected virtual string GetFilename(TModel item) - { - string filename = item.GetDisplayString(); - - if (filename.Length > MAX_FILENAME_LENGTH - FileExtension.Length) - return filename.Remove(MAX_FILENAME_LENGTH - FileExtension.Length); - - return filename; - } + protected virtual string GetFilename(TModel item) => item.GetDisplayString(); /// /// Exports an item to a legacy (.zip based) package. @@ -73,6 +65,14 @@ namespace osu.Game.Database string filename = NamingUtils.GetNextBestFilename(existingExports, $"{itemFilename}{FileExtension}"); + if (filename.Length > MAX_FILENAME_LENGTH) + { + string filenameWithoutExtension = Path.GetFileNameWithoutExtension(filename); + + filenameWithoutExtension = filenameWithoutExtension.Remove(MAX_FILENAME_LENGTH - FileExtension.Length); + filename = $"{filenameWithoutExtension}{FileExtension}"; + } + using (var stream = exportStorage.CreateFileSafely(filename)) ExportModelTo(item, stream); From 7107834b9ec297062ff29442fa1a550c03ff5a94 Mon Sep 17 00:00:00 2001 From: Cootz Date: Tue, 7 Mar 2023 15:43:03 +0300 Subject: [PATCH 184/476] Fix truncating bug --- osu.Game/Database/LegacyExporter.cs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/osu.Game/Database/LegacyExporter.cs b/osu.Game/Database/LegacyExporter.cs index 9981e08442..8da285daa3 100644 --- a/osu.Game/Database/LegacyExporter.cs +++ b/osu.Game/Database/LegacyExporter.cs @@ -58,6 +58,9 @@ namespace osu.Game.Database { string itemFilename = GetFilename(item).GetValidFilename(); + if (itemFilename.Length > MAX_FILENAME_LENGTH - FileExtension.Length) + itemFilename = itemFilename.Remove(MAX_FILENAME_LENGTH - FileExtension.Length); + IEnumerable existingExports = exportStorage .GetFiles(string.Empty, $"{itemFilename}*{FileExtension}") @@ -65,14 +68,6 @@ namespace osu.Game.Database string filename = NamingUtils.GetNextBestFilename(existingExports, $"{itemFilename}{FileExtension}"); - if (filename.Length > MAX_FILENAME_LENGTH) - { - string filenameWithoutExtension = Path.GetFileNameWithoutExtension(filename); - - filenameWithoutExtension = filenameWithoutExtension.Remove(MAX_FILENAME_LENGTH - FileExtension.Length); - filename = $"{filenameWithoutExtension}{FileExtension}"; - } - using (var stream = exportStorage.CreateFileSafely(filename)) ExportModelTo(item, stream); From 7ade525eef4a3c887de1d50123682140ee9c605a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 7 Mar 2023 19:43:52 +0100 Subject: [PATCH 185/476] Display corner radius setting as percentage --- .../Settings/SettingsPercentageSlider.cs | 20 +++++++++++++++++++ .../Rulesets/Mods/ModAccuracyChallenge.cs | 11 +--------- osu.Game/Screens/Play/HUD/PlayerAvatar.cs | 4 +++- 3 files changed, 24 insertions(+), 11 deletions(-) create mode 100644 osu.Game/Overlays/Settings/SettingsPercentageSlider.cs diff --git a/osu.Game/Overlays/Settings/SettingsPercentageSlider.cs b/osu.Game/Overlays/Settings/SettingsPercentageSlider.cs new file mode 100644 index 0000000000..fa59d18de1 --- /dev/null +++ b/osu.Game/Overlays/Settings/SettingsPercentageSlider.cs @@ -0,0 +1,20 @@ +// 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 osu.Framework.Graphics; +using osu.Game.Configuration; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.Settings +{ + /// + /// A that displays its value as a percentage by default. + /// Mostly provided for convenience of use with . + /// + public partial class SettingsPercentageSlider : SettingsSlider + where TValue : struct, IEquatable, IComparable, IConvertible + { + protected override Drawable CreateControl() => ((RoundedSliderBar)base.CreateControl()).With(sliderBar => sliderBar.DisplayAsPercentage = true); + } +} diff --git a/osu.Game/Rulesets/Mods/ModAccuracyChallenge.cs b/osu.Game/Rulesets/Mods/ModAccuracyChallenge.cs index dc7594f469..d4223a80c2 100644 --- a/osu.Game/Rulesets/Mods/ModAccuracyChallenge.cs +++ b/osu.Game/Rulesets/Mods/ModAccuracyChallenge.cs @@ -7,7 +7,6 @@ using System.Linq; using osu.Framework.Bindables; using osu.Framework.Localisation; using osu.Game.Configuration; -using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Judgements; @@ -33,7 +32,7 @@ namespace osu.Game.Rulesets.Mods public override string SettingDescription => base.SettingDescription.Replace(MinimumAccuracy.ToString(), MinimumAccuracy.Value.ToString("##%", NumberFormatInfo.InvariantInfo)); - [SettingSource("Minimum accuracy", "Trigger a failure if your accuracy goes below this value.", SettingControlType = typeof(SettingsSlider))] + [SettingSource("Minimum accuracy", "Trigger a failure if your accuracy goes below this value.", SettingControlType = typeof(SettingsPercentageSlider))] public BindableNumber MinimumAccuracy { get; } = new BindableDouble { MinValue = 0.60, @@ -69,12 +68,4 @@ namespace osu.Game.Rulesets.Mods return scoreProcessor.ComputeAccuracy(score); } } - - public partial class PercentSlider : RoundedSliderBar - { - public PercentSlider() - { - DisplayAsPercentage = true; - } - } } diff --git a/osu.Game/Screens/Play/HUD/PlayerAvatar.cs b/osu.Game/Screens/Play/HUD/PlayerAvatar.cs index e3fab499f3..1d0331593a 100644 --- a/osu.Game/Screens/Play/HUD/PlayerAvatar.cs +++ b/osu.Game/Screens/Play/HUD/PlayerAvatar.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Configuration; using osu.Game.Localisation.SkinComponents; +using osu.Game.Overlays.Settings; using osu.Game.Skinning; using osu.Game.Users.Drawables; using osuTK; @@ -15,7 +16,8 @@ namespace osu.Game.Screens.Play.HUD { public partial class PlayerAvatar : CompositeDrawable, ISerialisableDrawable { - [SettingSource(typeof(SkinnableComponentStrings), nameof(SkinnableComponentStrings.CornerRadius), nameof(SkinnableComponentStrings.CornerRadiusDescription))] + [SettingSource(typeof(SkinnableComponentStrings), nameof(SkinnableComponentStrings.CornerRadius), nameof(SkinnableComponentStrings.CornerRadiusDescription), + SettingControlType = typeof(SettingsPercentageSlider))] public new BindableFloat CornerRadius { get; set; } = new BindableFloat(0.25f) { MinValue = 0, From f711915e5fd89447a98322b7eb8070f218770a5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 7 Mar 2023 21:16:30 +0100 Subject: [PATCH 186/476] Remove unused using directive --- osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs index 4e13bde755..b90081a29f 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs @@ -21,7 +21,6 @@ using osu.Game.Configuration; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.UI.Cursor; -using osu.Game.Screens.Play; using osu.Game.Screens.Play.HUD; using osu.Game.Tests.Visual; using osuTK; From 98f40b2679bb37f7b15f2ea7a33f6971cda91b32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 7 Mar 2023 21:22:59 +0100 Subject: [PATCH 187/476] Improve documentation of `InputTrigger` --- osu.Game/Screens/Play/HUD/InputTrigger.cs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/InputTrigger.cs b/osu.Game/Screens/Play/HUD/InputTrigger.cs index 93b45daab3..b57f2cdf91 100644 --- a/osu.Game/Screens/Play/HUD/InputTrigger.cs +++ b/osu.Game/Screens/Play/HUD/InputTrigger.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 osu.Framework.Graphics; namespace osu.Game.Screens.Play.HUD @@ -11,8 +10,20 @@ namespace osu.Game.Screens.Play.HUD /// public abstract partial class InputTrigger : Component { - public event Action? OnActivate; - public event Action? OnDeactivate; + /// + /// Callback to invoke when the associated input has been activated. + /// + /// Whether gameplay is progressing in the forward direction time-wise. + public delegate void OnActivateCallback(bool forwardPlayback); + + /// + /// Callback to invoke when the associated input has been deactivated. + /// + /// Whether gameplay is progressing in the forward direction time-wise. + public delegate void OnDeactivateCallback(bool forwardPlayback); + + public event OnActivateCallback? OnActivate; + public event OnDeactivateCallback? OnDeactivate; protected InputTrigger(string name) { From 12af002c4d81dcc98b60e4c5058aeccfdd676f05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 7 Mar 2023 21:28:42 +0100 Subject: [PATCH 188/476] Reorder and add xmldoc to `KeyCounter` members --- osu.Game/Screens/Play/HUD/KeyCounter.cs | 26 ++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/KeyCounter.cs b/osu.Game/Screens/Play/HUD/KeyCounter.cs index 93cc4f908a..2a4ab1993a 100644 --- a/osu.Game/Screens/Play/HUD/KeyCounter.cs +++ b/osu.Game/Screens/Play/HUD/KeyCounter.cs @@ -12,21 +12,35 @@ namespace osu.Game.Screens.Play.HUD /// public abstract partial class KeyCounter : Container { + /// + /// The which activates and deactivates this . + /// public readonly InputTrigger Trigger; - private readonly Container content; - - protected override Container Content => content; + /// + /// Whether the actions reported by should be counted. + /// + public Bindable IsCounting { get; } = new BindableBool(true); private readonly Bindable countPresses = new BindableInt { MinValue = 0 }; - public Bindable IsCounting { get; } = new BindableBool(true); - + /// + /// The current count of registered key presses. + /// public IBindable CountPresses => countPresses; + private readonly Container content; + + protected override Container Content => content; + + /// + /// Whether this is currently in the "activated" state because the associated key is currently pressed. + /// + protected readonly Bindable IsActive = new BindableBool(); + protected KeyCounter(InputTrigger trigger) { InternalChildren = new Drawable[] @@ -44,8 +58,6 @@ namespace osu.Game.Screens.Play.HUD Name = trigger.Name; } - protected readonly Bindable IsActive = new BindableBool(); - private void increment() { if (!IsCounting.Value) From 44297a7d0a01e44f24d2860cdd4ce2584988e341 Mon Sep 17 00:00:00 2001 From: tsrk Date: Wed, 8 Mar 2023 00:47:16 +0000 Subject: [PATCH 189/476] refactor: make KCD a `CompositeDrawable` --- .../Visual/Gameplay/TestSceneAutoplay.cs | 4 +-- .../Gameplay/TestSceneGameplayRewinding.cs | 4 +-- .../Visual/Gameplay/TestSceneKeyCounter.cs | 4 +-- .../Visual/Gameplay/TestSceneReplay.cs | 2 +- osu.Game/Rulesets/UI/RulesetInputManager.cs | 4 +-- .../Play/HUD/DefaultKeyCounterDisplay.cs | 28 ++++--------------- .../Screens/Play/HUD/KeyCounterDisplay.cs | 25 ++++------------- 7 files changed, 20 insertions(+), 51 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs index 903cd178b7..f3f942b74b 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs @@ -35,14 +35,14 @@ namespace osu.Game.Tests.Visual.Gameplay var referenceBeatmap = CreateBeatmap(new OsuRuleset().RulesetInfo); AddUntilStep("score above zero", () => Player.ScoreProcessor.TotalScore.Value > 0); - AddUntilStep("key counter counted keys", () => Player.HUDOverlay.KeyCounter.Children.Any(kc => kc.CountPresses.Value > 2)); + AddUntilStep("key counter counted keys", () => Player.HUDOverlay.KeyCounter.Counters.Any(kc => kc.CountPresses.Value > 2)); seekTo(referenceBeatmap.Breaks[0].StartTime); AddAssert("keys not counting", () => !Player.HUDOverlay.KeyCounter.IsCounting.Value); AddAssert("overlay displays 100% accuracy", () => Player.BreakOverlay.ChildrenOfType().Single().AccuracyDisplay.Current.Value == 1); AddStep("rewind", () => Player.GameplayClockContainer.Seek(-80000)); - AddUntilStep("key counter reset", () => Player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses.Value == 0)); + AddUntilStep("key counter reset", () => Player.HUDOverlay.KeyCounter.Counters.All(kc => kc.CountPresses.Value == 0)); seekTo(referenceBeatmap.HitObjects[^1].GetEndTime()); AddUntilStep("results displayed", () => getResultsScreen()?.IsLoaded == true); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs index 9f485cd7bf..751aeb4e13 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs @@ -31,11 +31,11 @@ namespace osu.Game.Tests.Visual.Gameplay AddUntilStep("wait for track to start running", () => Beatmap.Value.Track.IsRunning); addSeekStep(3000); AddAssert("all judged", () => Player.DrawableRuleset.Playfield.AllHitObjects.All(h => h.Judged)); - AddUntilStep("key counter counted keys", () => Player.HUDOverlay.KeyCounter.Children.Select(kc => kc.CountPresses.Value).Sum() == 15); + AddUntilStep("key counter counted keys", () => Player.HUDOverlay.KeyCounter.Counters.Select(kc => kc.CountPresses.Value).Sum() == 15); AddStep("clear results", () => Player.Results.Clear()); addSeekStep(0); AddAssert("none judged", () => Player.DrawableRuleset.Playfield.AllHitObjects.All(h => !h.Judged)); - AddUntilStep("key counters reset", () => Player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses.Value == 0)); + AddUntilStep("key counters reset", () => Player.HUDOverlay.KeyCounter.Counters.All(kc => kc.CountPresses.Value == 0)); AddAssert("no results triggered", () => Player.Results.Count == 0); } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs index 3260ba8e33..6dc07ca9d3 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs @@ -31,7 +31,7 @@ namespace osu.Game.Tests.Visual.Gameplay new KeyCounterMouseTrigger(MouseButton.Right), }); - var testCounter = (DefaultKeyCounter)kc.Children.First(); + var testCounter = (DefaultKeyCounter)kc.Counters.First(); AddStep("Add random", () => { @@ -39,7 +39,7 @@ namespace osu.Game.Tests.Visual.Gameplay kc.AddTrigger(new KeyCounterKeyboardTrigger(key)); }); - Key testKey = ((KeyCounterKeyboardTrigger)kc.Children.First().Trigger).Key; + Key testKey = ((KeyCounterKeyboardTrigger)kc.Counters.First().Trigger).Key; void addPressKeyStep() { diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplay.cs index 542686f0cd..bf9b13b320 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplay.cs @@ -27,7 +27,7 @@ namespace osu.Game.Tests.Visual.Gameplay protected override void AddCheckSteps() { AddUntilStep("score above zero", () => ((ScoreAccessibleReplayPlayer)Player).ScoreProcessor.TotalScore.Value > 0); - AddUntilStep("key counter counted keys", () => ((ScoreAccessibleReplayPlayer)Player).HUDOverlay.KeyCounter.Children.Any(kc => kc.CountPresses.Value > 0)); + AddUntilStep("key counter counted keys", () => ((ScoreAccessibleReplayPlayer)Player).HUDOverlay.KeyCounter.Counters.Any(kc => kc.CountPresses.Value > 0)); AddAssert("cannot fail", () => !((ScoreAccessibleReplayPlayer)Player).AllowFail); } diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index c2f0b1a951..ea9dc3fb01 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -176,14 +176,14 @@ namespace osu.Game.Rulesets.UI { } - public bool OnPressed(KeyBindingPressEvent e) => Target.Children.Where(c => c.Trigger is KeyCounterActionTrigger) + public bool OnPressed(KeyBindingPressEvent e) => Target.Counters.Where(c => c.Trigger is KeyCounterActionTrigger) .Select(c => (KeyCounterActionTrigger)c.Trigger) .Any(c => c.OnPressed(e.Action, Clock.Rate >= 0)); public void OnReleased(KeyBindingReleaseEvent e) { foreach (var c - in Target.Children.Where(c => c.Trigger is KeyCounterActionTrigger).Select(c => (KeyCounterActionTrigger)c.Trigger)) + in Target.Counters.Where(c => c.Trigger is KeyCounterActionTrigger).Select(c => (KeyCounterActionTrigger)c.Trigger)) c.OnReleased(e.Action, Clock.Rate >= 0); } } diff --git a/osu.Game/Screens/Play/HUD/DefaultKeyCounterDisplay.cs b/osu.Game/Screens/Play/HUD/DefaultKeyCounterDisplay.cs index 1d79b2d27a..9499263474 100644 --- a/osu.Game/Screens/Play/HUD/DefaultKeyCounterDisplay.cs +++ b/osu.Game/Screens/Play/HUD/DefaultKeyCounterDisplay.cs @@ -13,18 +13,11 @@ namespace osu.Game.Screens.Play.HUD private const int duration = 100; private const double key_fade_time = 80; - private readonly FillFlowContainer keyFlow = new FillFlowContainer(); + private readonly FillFlowContainer keyFlow = new FillFlowContainer(); - protected override Container Content => keyFlow; - - public new IReadOnlyList Children - { - get => (IReadOnlyList)base.Children; - set => base.Children = value; - } + public override IEnumerable Counters => keyFlow; public DefaultKeyCounterDisplay() - : base(typeof(DefaultKeyCounter)) { keyFlow.Direction = FillDirection.Horizontal; keyFlow.AutoSizeAxes = Axes.Both; @@ -45,23 +38,12 @@ namespace osu.Game.Screens.Play.HUD public override void AddTrigger(InputTrigger trigger) { DefaultKeyCounter key = new DefaultKeyCounter(trigger); - Add(key); + keyFlow.Add(key); key.FadeTime = key_fade_time; key.KeyDownTextColor = KeyDownTextColor; key.KeyUpTextColor = KeyUpTextColor; } - public override void Add(KeyCounter key) - { - base.Add(key); - - DefaultKeyCounter defaultKey = (DefaultKeyCounter)key; - - defaultKey.FadeTime = key_fade_time; - defaultKey.KeyDownTextColor = KeyDownTextColor; - defaultKey.KeyUpTextColor = KeyUpTextColor; - } - protected override void UpdateVisibility() => // Isolate changing visibility of the key counters from fading this component. keyFlow.FadeTo(AlwaysVisible.Value || ConfigVisibility.Value ? 1 : 0, duration); @@ -76,7 +58,7 @@ namespace osu.Game.Screens.Play.HUD if (value != keyDownTextColor) { keyDownTextColor = value; - foreach (var child in Children) + foreach (var child in keyFlow) child.KeyDownTextColor = value; } } @@ -92,7 +74,7 @@ namespace osu.Game.Screens.Play.HUD if (value != keyUpTextColor) { keyUpTextColor = value; - foreach (var child in Children) + foreach (var child in keyFlow) child.KeyUpTextColor = value; } } diff --git a/osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs b/osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs index 4080e561cd..9a28d40418 100644 --- a/osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs +++ b/osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs @@ -18,7 +18,7 @@ namespace osu.Game.Screens.Play.HUD /// /// A flowing display of all gameplay keys. Individual keys can be added using implementations. /// - public abstract partial class KeyCounterDisplay : Container + public abstract partial class KeyCounterDisplay : CompositeDrawable { /// /// Whether the key counter should be visible regardless of the configuration value. @@ -26,6 +26,11 @@ namespace osu.Game.Screens.Play.HUD /// public Bindable AlwaysVisible { get; } = new Bindable(true); + /// + /// The s contained in this . + /// + public abstract IEnumerable Counters { get; } + public Bindable IsCounting { get; } = new BindableBool(true); protected readonly Bindable ConfigVisibility = new Bindable(); @@ -34,13 +39,6 @@ namespace osu.Game.Screens.Play.HUD private Receptor? receptor; - private readonly Type[] acceptedTypes; - - protected KeyCounterDisplay(params Type[] acceptedTypes) - { - this.acceptedTypes = acceptedTypes; - } - public void SetReceptor(Receptor receptor) { if (this.receptor != null) @@ -53,17 +51,6 @@ namespace osu.Game.Screens.Play.HUD public void AddTriggerRange(IEnumerable triggers) => triggers.ForEach(AddTrigger); - public override void Add(KeyCounter counter) - { - if (!checkType(counter)) - throw new InvalidOperationException($"{counter.GetType()} is not a supported counter type. (hint: you may want to use {nameof(AddTrigger)} instead.)"); - - base.Add(counter); - counter.IsCounting.BindTo(IsCounting); - } - - private bool checkType(KeyCounter counter) => acceptedTypes.Length == 0 || acceptedTypes.Any(t => t.IsInstanceOfType(counter)); - [BackgroundDependencyLoader] private void load(OsuConfigManager config) { From 5b0db94a242a8e5896806a4e76831d399b33010a Mon Sep 17 00:00:00 2001 From: tsrk Date: Wed, 8 Mar 2023 00:58:54 +0000 Subject: [PATCH 190/476] docs: add XMLDoc for methods in KCD --- osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs b/osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs index 9a28d40418..0e0f8a1190 100644 --- a/osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs +++ b/osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs @@ -31,6 +31,9 @@ namespace osu.Game.Screens.Play.HUD /// public abstract IEnumerable Counters { get; } + /// + /// Whether the actions reported by all s within this should be counted. + /// public Bindable IsCounting { get; } = new BindableBool(true); protected readonly Bindable ConfigVisibility = new Bindable(); @@ -47,8 +50,16 @@ namespace osu.Game.Screens.Play.HUD this.receptor = receptor; } + /// + /// Adds a new to this . + /// + /// The the resulting will react to. public abstract void AddTrigger(InputTrigger trigger); + /// + /// Adds a range of new s to this . + /// + /// The s the resulting s will react to. public void AddTriggerRange(IEnumerable triggers) => triggers.ForEach(AddTrigger); [BackgroundDependencyLoader] From 5d15426c275084144d68e8790f0275d985c52269 Mon Sep 17 00:00:00 2001 From: tsrk Date: Wed, 8 Mar 2023 01:52:12 +0000 Subject: [PATCH 191/476] refactor: make `Counters` return a `Container` --- osu.Game/Screens/Play/HUD/DefaultKeyCounterDisplay.cs | 9 ++++----- osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/DefaultKeyCounterDisplay.cs b/osu.Game/Screens/Play/HUD/DefaultKeyCounterDisplay.cs index 9499263474..dce0be9cde 100644 --- a/osu.Game/Screens/Play/HUD/DefaultKeyCounterDisplay.cs +++ b/osu.Game/Screens/Play/HUD/DefaultKeyCounterDisplay.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.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osuTK.Graphics; @@ -13,9 +12,9 @@ namespace osu.Game.Screens.Play.HUD private const int duration = 100; private const double key_fade_time = 80; - private readonly FillFlowContainer keyFlow = new FillFlowContainer(); + private readonly FillFlowContainer keyFlow = new FillFlowContainer(); - public override IEnumerable Counters => keyFlow; + public override Container Counters => keyFlow; public DefaultKeyCounterDisplay() { @@ -59,7 +58,7 @@ namespace osu.Game.Screens.Play.HUD { keyDownTextColor = value; foreach (var child in keyFlow) - child.KeyDownTextColor = value; + ((DefaultKeyCounter)child).KeyDownTextColor = value; } } } @@ -75,7 +74,7 @@ namespace osu.Game.Screens.Play.HUD { keyUpTextColor = value; foreach (var child in keyFlow) - child.KeyUpTextColor = value; + ((DefaultKeyCounter)child).KeyUpTextColor = value; } } } diff --git a/osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs b/osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs index 0e0f8a1190..2c90d1474d 100644 --- a/osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs +++ b/osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs @@ -29,7 +29,7 @@ namespace osu.Game.Screens.Play.HUD /// /// The s contained in this . /// - public abstract IEnumerable Counters { get; } + public abstract Container Counters { get; } /// /// Whether the actions reported by all s within this should be counted. From 245c3c025c9fa2dd4e75c279fa41b2281b3abfdb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 8 Mar 2023 19:23:32 +0900 Subject: [PATCH 192/476] Refactor `endClickSelection` to reduce nesting --- .../Compose/Components/BlueprintContainer.cs | 76 +++++++++---------- 1 file changed, 36 insertions(+), 40 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 87cee59d83..3bf9f42957 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -382,52 +382,48 @@ namespace osu.Game.Screens.Edit.Compose.Components /// Whether a click selection was active. private bool endClickSelection(MouseButtonEvent e) { - if (!clickSelectionHandled && !isDraggingBlueprint) + // If already handled a selection or drag, we don't want to perform a mouse up / click action. + if (clickSelectionHandled || isDraggingBlueprint) return true; + + if (e.Button != MouseButton.Left) return false; + + if (e.ControlPressed) { - if (e.Button == MouseButton.Left) - { - if (e.ControlPressed) - { - // if a selection didn't occur, we may want to trigger a deselection. + // if a selection didn't occur, we may want to trigger a deselection. - // 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.OrderByDescending(b => b.IsSelected)) - { - if (!blueprint.IsHovered) continue; - - return clickSelectionHandled = SelectionHandler.MouseUpSelectionRequested(blueprint, e); - } - } - else if (selectedBlueprintAlreadySelectedOnMouseDown && AllowCyclicSelection) - { - // If a click occurred and was handled by the currently selected blueprint but didn't result in a drag, - // cycle between other blueprints which are also under the cursor. - - // The depth of blueprints is constantly changing (see above where selected blueprints are brought to the front). - // For this logic, we want a stable sort order so we can correctly cycle, thus using the blueprintMap instead. - IEnumerable> cyclingSelectionBlueprints = blueprintMap.Values; - - // If there's already a selection, let's start from the blueprint after the selection. - cyclingSelectionBlueprints = cyclingSelectionBlueprints.SkipWhile(b => !b.IsSelected).Skip(1); - - // Add the blueprints from before the selection to the end of the enumerable to allow for cyclic selection. - cyclingSelectionBlueprints = cyclingSelectionBlueprints.Concat(blueprintMap.Values.TakeWhile(b => !b.IsSelected)); - - foreach (SelectionBlueprint blueprint in cyclingSelectionBlueprints) - { - if (!blueprint.IsHovered) continue; - - // We are performing a mouse up, but selection handlers perform selection on mouse down, so we need to call that instead. - return clickSelectionHandled = SelectionHandler.MouseDownSelectionRequested(blueprint, 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. + foreach (SelectionBlueprint blueprint in SelectionBlueprints.AliveChildren.Where(b => b.IsHovered).OrderByDescending(b => b.IsSelected)) + return clickSelectionHandled = SelectionHandler.MouseUpSelectionRequested(blueprint, e); return false; } - return true; + if (selectedBlueprintAlreadySelectedOnMouseDown && AllowCyclicSelection) + { + // If a click occurred and was handled by the currently selected blueprint but didn't result in a drag, + // cycle between other blueprints which are also under the cursor. + + // The depth of blueprints is constantly changing (see above where selected blueprints are brought to the front). + // For this logic, we want a stable sort order so we can correctly cycle, thus using the blueprintMap instead. + IEnumerable> cyclingSelectionBlueprints = blueprintMap.Values; + + // If there's already a selection, let's start from the blueprint after the selection. + cyclingSelectionBlueprints = cyclingSelectionBlueprints.SkipWhile(b => !b.IsSelected).Skip(1); + + // Add the blueprints from before the selection to the end of the enumerable to allow for cyclic selection. + cyclingSelectionBlueprints = cyclingSelectionBlueprints.Concat(blueprintMap.Values.TakeWhile(b => !b.IsSelected)); + + foreach (SelectionBlueprint blueprint in cyclingSelectionBlueprints) + { + if (!blueprint.IsHovered) continue; + + // We are performing a mouse up, but selection handlers perform selection on mouse down, so we need to call that instead. + return clickSelectionHandled = SelectionHandler.MouseDownSelectionRequested(blueprint, e); + } + } + + return false; } /// From 4f7be332f3736fc14fd02008fee3239fa2beb657 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 8 Mar 2023 19:24:03 +0900 Subject: [PATCH 193/476] Revert `isDraggingBlueprint` to field --- osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 3bf9f42957..3ba71cebe6 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -483,7 +483,7 @@ namespace osu.Game.Screens.Edit.Compose.Components /// /// Whether a blueprint is currently being dragged. /// - private bool isDraggingBlueprint { get; set; } + private bool isDraggingBlueprint; /// /// Attempts to begin the movement of any selected blueprints. From b8e87e3a08d0cc92363a24d7bf32127509a67311 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 8 Mar 2023 19:58:05 +0900 Subject: [PATCH 194/476] Update osu!mania argon colours to match new proposal --- .../Argon/ManiaArgonSkinTransformer.cs | 249 +++++++++++++++--- 1 file changed, 207 insertions(+), 42 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs index 057b7eb0d9..75ecd53cd1 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs @@ -69,6 +69,17 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon return base.GetDrawableComponent(lookup); } + private static readonly Color4 colour_special_column = new Color4(169, 106, 255, 255); + + private const int total_colours = 6; + + private static readonly Color4 colour_yellow = new Color4(255, 197, 40, 255); + private static readonly Color4 colour_orange = new Color4(252, 109, 1, 255); + private static readonly Color4 colour_pink = new Color4(213, 35, 90, 255); + private static readonly Color4 colour_purple = new Color4(203, 60, 236, 255); + private static readonly Color4 colour_cyan = new Color4(72, 198, 255, 255); + private static readonly Color4 colour_green = new Color4(100, 192, 92, 255); + public override IBindable? GetConfig(TLookup lookup) { if (lookup is ManiaSkinConfigurationLookup maniaLookup) @@ -92,48 +103,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon case LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour: - Color4 colour; - - const int total_colours = 7; - - if (stage.IsSpecialColumn(column)) - colour = new Color4(159, 101, 255, 255); - else - { - switch (column % total_colours) - { - case 0: - colour = new Color4(240, 216, 0, 255); - break; - - case 1: - colour = new Color4(240, 101, 0, 255); - break; - - case 2: - colour = new Color4(240, 0, 130, 255); - break; - - case 3: - colour = new Color4(192, 0, 240, 255); - break; - - case 4: - colour = new Color4(0, 96, 240, 255); - break; - - case 5: - colour = new Color4(0, 226, 240, 255); - break; - - case 6: - colour = new Color4(0, 240, 96, 255); - break; - - default: - throw new ArgumentOutOfRangeException(); - } - } + var colour = getColourForLayout(column, stage); return SkinUtils.As(new Bindable(colour)); } @@ -141,5 +111,200 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon return base.GetConfig(lookup); } + + private Color4 getColourForLayout(int column, StageDefinition stage) + { + // For now, these are defined per column count as per https://user-images.githubusercontent.com/50823728/218038463-b450f46c-ef21-4551-b133-f866be59970c.png + // See https://github.com/ppy/osu/discussions/21996 for discussion. + switch (stage.Columns) + { + case 1: + return colour_yellow; + + case 2: + switch (column) + { + case 0: return colour_green; + + case 1: return colour_cyan; + + default: throw new ArgumentOutOfRangeException(); + } + + case 3: + switch (column) + { + case 0: return colour_pink; + + case 1: return colour_purple; + + case 2: return colour_special_column; + + default: throw new ArgumentOutOfRangeException(); + } + + case 4: + switch (column) + { + case 0: return colour_yellow; + + case 1: return colour_orange; + + case 2: return colour_pink; + + case 3: return colour_purple; + + default: throw new ArgumentOutOfRangeException(); + } + + case 5: + switch (column) + { + case 0: return colour_pink; + + case 1: return colour_orange; + + case 2: return colour_yellow; + + case 3: return colour_green; + + case 4: return colour_cyan; + + default: throw new ArgumentOutOfRangeException(); + } + + case 6: + switch (column) + { + case 0: return colour_pink; + + case 1: return colour_orange; + + case 2: return colour_yellow; + + case 3: return colour_cyan; + + case 4: return colour_purple; + + case 5: return colour_pink; + + default: throw new ArgumentOutOfRangeException(); + } + + case 7: + switch (column) + { + case 0: return colour_pink; + + case 1: return colour_cyan; + + case 2: return colour_pink; + + case 3: return colour_special_column; + + case 4: return colour_green; + + case 5: return colour_cyan; + + case 6: return colour_green; + + default: throw new ArgumentOutOfRangeException(); + } + + case 8: + switch (column) + { + case 0: return colour_purple; + + case 1: return colour_pink; + + case 2: return colour_orange; + + case 3: return colour_yellow; + + case 4: return colour_yellow; + + case 5: return colour_orange; + + case 6: return colour_pink; + + case 7: return colour_purple; + + default: throw new ArgumentOutOfRangeException(); + } + + case 9: + switch (column) + { + case 0: return colour_purple; + + case 1: return colour_pink; + + case 2: return colour_orange; + + case 3: return colour_yellow; + + case 4: return colour_special_column; + + case 5: return colour_yellow; + + case 6: return colour_orange; + + case 7: return colour_pink; + + case 8: return colour_purple; + + default: throw new ArgumentOutOfRangeException(); + } + + case 10: + switch (column) + { + case 0: return colour_purple; + + case 1: return colour_pink; + + case 2: return colour_orange; + + case 3: return colour_yellow; + + case 4: return colour_cyan; + + case 5: return colour_green; + + case 6: return colour_yellow; + + case 7: return colour_orange; + + case 8: return colour_pink; + + case 9: return colour_purple; + + default: throw new ArgumentOutOfRangeException(); + } + } + + // fallback for unhandled scenarios + + if (stage.IsSpecialColumn(column)) + return colour_special_column; + + switch (column % total_colours) + { + case 0: return new Color4(255, 197, 40, 255); + + case 1: return new Color4(252, 109, 1, 255); + + case 2: return new Color4(213, 35, 90, 255); + + case 3: return new Color4(203, 60, 236, 255); + + case 4: return new Color4(72, 198, 255, 255); + + case 5: return new Color4(100, 192, 92, 255); + + default: throw new ArgumentOutOfRangeException(); + } + } } } From 686259a33c62aed9ce227001f8cedccdf3031f56 Mon Sep 17 00:00:00 2001 From: EXtremeExploit Date: Wed, 8 Mar 2023 13:57:20 -0300 Subject: [PATCH 195/476] Make support badge in list match groups --- osu.Game/Users/UserListPanel.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Users/UserListPanel.cs b/osu.Game/Users/UserListPanel.cs index 9b5b0e9f6d..3047e70a1a 100644 --- a/osu.Game/Users/UserListPanel.cs +++ b/osu.Game/Users/UserListPanel.cs @@ -67,6 +67,7 @@ namespace osu.Game.Users { username.Anchor = Anchor.CentreLeft; username.Origin = Anchor.CentreLeft; + username.UseFullGlyphHeight = false; }) } }, @@ -111,7 +112,7 @@ namespace osu.Game.Users { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Height = 20, + Height = 16, SupportLevel = User.SupportLevel }); } From 430b09acb211f1b8ab44c7e889d7b2d22cc7ea0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 8 Mar 2023 22:52:06 +0100 Subject: [PATCH 196/476] Expose taiko input manager in same manner as osu! --- osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs | 2 +- osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs index fa876555e1..a5cffca06f 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModSingleTap.cs @@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Taiko.Mods public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { ruleset = (DrawableTaikoRuleset)drawableRuleset; - ruleset.InputManager.Add(new InputInterceptor(this)); + ruleset.KeyBindingInputManager.Add(new InputInterceptor(this)); playfield = (TaikoPlayfield)ruleset.Playfield; var periods = new List(); diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs index 5390d3b138..a08877e2dd 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Taiko.UI public readonly BindableBool LockPlayfieldMaxAspect = new BindableBool(true); - public TaikoInputManager InputManager { get; private set; } + public new TaikoInputManager KeyBindingInputManager => (TaikoInputManager)base.KeyBindingInputManager; protected override ScrollVisualisationMethod VisualisationMethod => ScrollVisualisationMethod.Overlapping; @@ -95,7 +95,7 @@ namespace osu.Game.Rulesets.Taiko.UI LockPlayfieldMaxAspect = { BindTarget = LockPlayfieldMaxAspect } }; - protected override PassThroughInputManager CreateInputManager() => InputManager = new TaikoInputManager(Ruleset.RulesetInfo); + protected override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo); protected override Playfield CreatePlayfield() => new TaikoPlayfield(); From e2467848679538557c64608152dc4ad3872223d0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 Mar 2023 18:25:30 +0900 Subject: [PATCH 197/476] Fix dual stage column colours not being looked up correctly --- .../Argon/ManiaArgonSkinTransformer.cs | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs index 75ecd53cd1..0beca815b2 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs @@ -84,8 +84,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon { if (lookup is ManiaSkinConfigurationLookup maniaLookup) { - int column = maniaLookup.ColumnIndex ?? 0; - var stage = beatmap.GetStageForColumnIndex(column); + int columnIndex = maniaLookup.ColumnIndex ?? 0; + var stage = beatmap.GetStageForColumnIndex(columnIndex); switch (maniaLookup.Lookup) { @@ -98,12 +98,12 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon case LegacyManiaSkinConfigurationLookups.ColumnWidth: return SkinUtils.As(new Bindable( - stage.IsSpecialColumn(column) ? 120 : 60 + stage.IsSpecialColumn(columnIndex) ? 120 : 60 )); case LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour: - var colour = getColourForLayout(column, stage); + var colour = getColourForLayout(columnIndex, stage); return SkinUtils.As(new Bindable(colour)); } @@ -112,8 +112,11 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon return base.GetConfig(lookup); } - private Color4 getColourForLayout(int column, StageDefinition stage) + private Color4 getColourForLayout(int columnIndex, StageDefinition stage) { + // Account for cases like dual-stage (assume that all stages have the same column count for now). + columnIndex %= stage.Columns; + // For now, these are defined per column count as per https://user-images.githubusercontent.com/50823728/218038463-b450f46c-ef21-4551-b133-f866be59970c.png // See https://github.com/ppy/osu/discussions/21996 for discussion. switch (stage.Columns) @@ -122,7 +125,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon return colour_yellow; case 2: - switch (column) + switch (columnIndex) { case 0: return colour_green; @@ -132,7 +135,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon } case 3: - switch (column) + switch (columnIndex) { case 0: return colour_pink; @@ -144,7 +147,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon } case 4: - switch (column) + switch (columnIndex) { case 0: return colour_yellow; @@ -158,7 +161,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon } case 5: - switch (column) + switch (columnIndex) { case 0: return colour_pink; @@ -174,7 +177,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon } case 6: - switch (column) + switch (columnIndex) { case 0: return colour_pink; @@ -192,7 +195,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon } case 7: - switch (column) + switch (columnIndex) { case 0: return colour_pink; @@ -212,7 +215,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon } case 8: - switch (column) + switch (columnIndex) { case 0: return colour_purple; @@ -234,7 +237,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon } case 9: - switch (column) + switch (columnIndex) { case 0: return colour_purple; @@ -258,7 +261,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon } case 10: - switch (column) + switch (columnIndex) { case 0: return colour_purple; @@ -286,10 +289,10 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon // fallback for unhandled scenarios - if (stage.IsSpecialColumn(column)) + if (stage.IsSpecialColumn(columnIndex)) return colour_special_column; - switch (column % total_colours) + switch (columnIndex % total_colours) { case 0: return new Color4(255, 197, 40, 255); From 030742c64820fcaffb4dc58b06946489e165849c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 Mar 2023 19:02:15 +0900 Subject: [PATCH 198/476] Use different icon style on hold note heads --- .../Skinning/Argon/ArgonHoldNoteHeadPiece.cs | 19 +++++++++++++++ .../Skinning/Argon/ArgonNotePiece.cs | 24 +++++++++++-------- .../Argon/ManiaArgonSkinTransformer.cs | 2 ++ 3 files changed, 35 insertions(+), 10 deletions(-) create mode 100644 osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldNoteHeadPiece.cs diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldNoteHeadPiece.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldNoteHeadPiece.cs new file mode 100644 index 0000000000..16381a6dec --- /dev/null +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldNoteHeadPiece.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 osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osuTK; + +namespace osu.Game.Rulesets.Mania.Skinning.Argon +{ + internal partial class ArgonHoldNoteHeadPiece : ArgonNotePiece + { + protected override Drawable CreateIcon() => new Circle + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(20, 5), + }; + } +} diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonNotePiece.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonNotePiece.cs index 2a5bce255c..49d9cebc2e 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonNotePiece.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonNotePiece.cs @@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon CornerRadius = CORNER_RADIUS; Masking = true; - InternalChildren = new Drawable[] + InternalChildren = new[] { shadow = new Box { @@ -65,18 +65,22 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon RelativeSizeAxes = Axes.X, Height = CORNER_RADIUS * 2, }, - new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Y = 4, - Icon = FontAwesome.Solid.AngleDown, - Size = new Vector2(20), - Scale = new Vector2(1, 0.7f) - } + CreateIcon(), }; } + protected virtual Drawable CreateIcon() => new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Y = 4, + // TODO: replace with a non-squashed version. + // The 0.7f height scale should be removed. + Icon = FontAwesome.Solid.AngleDown, + Size = new Vector2(20), + Scale = new Vector2(1, 0.7f) + }; + [BackgroundDependencyLoader(true)] private void load(IScrollingInfo scrollingInfo, DrawableHitObject? drawableObject) { diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs index 057b7eb0d9..faf55c15fe 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs @@ -50,6 +50,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon return new ArgonHoldNoteTailPiece(); case ManiaSkinComponents.HoldNoteHead: + return new ArgonHoldNoteHeadPiece(); + case ManiaSkinComponents.Note: return new ArgonNotePiece(); From d806b85a30ca59d2515b29bc18b7362ae7931902 Mon Sep 17 00:00:00 2001 From: tsrk Date: Wed, 8 Mar 2023 21:10:34 +0000 Subject: [PATCH 199/476] revert: make `counters` an `IEnumerable` again As suggested by bdach as this would make the last two commits useless Refs: 5d15426 --- osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs | 3 +-- .../Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs | 4 +--- osu.Game/Screens/Play/HUD/DefaultKeyCounterDisplay.cs | 9 +++++---- osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs | 2 +- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs index aeb7aa7dbe..42683a3eec 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs @@ -8,7 +8,6 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Framework.Timing; using osu.Game.Configuration; @@ -45,7 +44,7 @@ namespace osu.Game.Tests.Visual.Gameplay // best way to check without exposing. private Drawable hideTarget => hudOverlay.KeyCounter; - private FillFlowContainer keyCounterFlow => hudOverlay.KeyCounter.ChildrenOfType>().First(); + private Drawable keyCounterFlow => (Drawable)hudOverlay.KeyCounter.Counters; [BackgroundDependencyLoader] private void load() diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs index 808241729b..3eda80719a 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs @@ -10,8 +10,6 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Testing; using osu.Framework.Timing; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; @@ -44,7 +42,7 @@ namespace osu.Game.Tests.Visual.Gameplay // best way to check without exposing. private Drawable hideTarget => hudOverlay.KeyCounter; - private FillFlowContainer keyCounterFlow => hudOverlay.KeyCounter.ChildrenOfType>().First(); + private Drawable keyCounterFlow => (Drawable)hudOverlay.KeyCounter.Counters; [Test] public void TestComboCounterIncrementing() diff --git a/osu.Game/Screens/Play/HUD/DefaultKeyCounterDisplay.cs b/osu.Game/Screens/Play/HUD/DefaultKeyCounterDisplay.cs index dce0be9cde..9499263474 100644 --- a/osu.Game/Screens/Play/HUD/DefaultKeyCounterDisplay.cs +++ b/osu.Game/Screens/Play/HUD/DefaultKeyCounterDisplay.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.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osuTK.Graphics; @@ -12,9 +13,9 @@ namespace osu.Game.Screens.Play.HUD private const int duration = 100; private const double key_fade_time = 80; - private readonly FillFlowContainer keyFlow = new FillFlowContainer(); + private readonly FillFlowContainer keyFlow = new FillFlowContainer(); - public override Container Counters => keyFlow; + public override IEnumerable Counters => keyFlow; public DefaultKeyCounterDisplay() { @@ -58,7 +59,7 @@ namespace osu.Game.Screens.Play.HUD { keyDownTextColor = value; foreach (var child in keyFlow) - ((DefaultKeyCounter)child).KeyDownTextColor = value; + child.KeyDownTextColor = value; } } } @@ -74,7 +75,7 @@ namespace osu.Game.Screens.Play.HUD { keyUpTextColor = value; foreach (var child in keyFlow) - ((DefaultKeyCounter)child).KeyUpTextColor = value; + child.KeyUpTextColor = value; } } } diff --git a/osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs b/osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs index 2c90d1474d..0e0f8a1190 100644 --- a/osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs +++ b/osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs @@ -29,7 +29,7 @@ namespace osu.Game.Screens.Play.HUD /// /// The s contained in this . /// - public abstract Container Counters { get; } + public abstract IEnumerable Counters { get; } /// /// Whether the actions reported by all s within this should be counted. From bfc0b946fbbdc0943b1a9c2d52df0382db261f97 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 Mar 2023 20:26:35 +0900 Subject: [PATCH 200/476] Remove additive blending from argon body piece --- osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldBodyPiece.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldBodyPiece.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldBodyPiece.cs index 1f52f5f15f..39a030d621 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldBodyPiece.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldBodyPiece.cs @@ -32,7 +32,6 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon // Without this, the width of the body will be slightly larger than the head/tail. Masking = true; CornerRadius = ArgonNotePiece.CORNER_RADIUS; - Blending = BlendingParameters.Additive; } [BackgroundDependencyLoader(true)] From ed3ff62e4f9b9042fccef2f05dd757cfa5c4e762 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 Mar 2023 20:26:48 +0900 Subject: [PATCH 201/476] Add note about why `bodyPiece` sizing is done as it is I think we're going to have to change this as it's quite limiting in what you can do with osu!mania skin implementation, but for now I want to leave a note as to why this is done, because each time I have to trial and error check what breaks when adjusting it. --- osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index 25d0573a82..6e1c6cf80f 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -236,6 +236,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables }; // Position and resize the body to lie half-way under the head and the tail notes. + // The rationale for this is account for heads/tails with corner radius. bodyPiece.Y = (Direction.Value == ScrollingDirection.Up ? 1 : -1) * Head.Height / 2; bodyPiece.Height = DrawHeight - Head.Height / 2 + Tail.Height / 2; From 526eeedec2d81b10931235fd55637c184a08d108 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 Mar 2023 20:27:43 +0900 Subject: [PATCH 202/476] Adjust explosion and hit target to not include shadow portion in height calculation --- osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitExplosion.cs | 5 +++-- osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitTarget.cs | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitExplosion.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitExplosion.cs index e32de6f3f3..8e27b4abd7 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitExplosion.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitExplosion.cs @@ -43,9 +43,10 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon { largeFaint = new Container { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, RelativeSizeAxes = Axes.Both, + Height = ArgonNotePiece.NOTE_ACCENT_RATIO, Masking = true, CornerRadius = ArgonNotePiece.CORNER_RADIUS, Blending = BlendingParameters.Additive, diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitTarget.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitTarget.cs index 4ffb4a435b..cf5931231c 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitTarget.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitTarget.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon private void load(IScrollingInfo scrollingInfo) { RelativeSizeAxes = Axes.X; - Height = ArgonNotePiece.NOTE_HEIGHT; + Height = ArgonNotePiece.NOTE_HEIGHT * ArgonNotePiece.NOTE_ACCENT_RATIO; Masking = true; CornerRadius = ArgonNotePiece.CORNER_RADIUS; From 2ad531f263ad20386e9ab57724c9a9e39c36d547 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 Mar 2023 20:27:58 +0900 Subject: [PATCH 203/476] Adjust argon note shadows and body to be closer in line with new design proposal --- .../Skinning/Argon/ArgonHoldNoteTailPiece.cs | 45 ++++++++++++------- .../Skinning/Argon/ArgonNotePiece.cs | 3 +- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldNoteTailPiece.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldNoteTailPiece.cs index 428439d52c..abfd9c4dc8 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldNoteTailPiece.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldNoteTailPiece.cs @@ -5,6 +5,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.Rulesets.Objects.Drawables; @@ -19,7 +20,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon private readonly IBindable direction = new Bindable(); private readonly IBindable accentColour = new Bindable(); - private readonly Box shadeBackground; + private readonly Box shadow; private readonly Box shadeForeground; public ArgonHoldNoteTailPiece() @@ -27,30 +28,40 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon RelativeSizeAxes = Axes.X; Height = ArgonNotePiece.NOTE_HEIGHT; - CornerRadius = ArgonNotePiece.CORNER_RADIUS; - Masking = true; - InternalChildren = new Drawable[] { - shadeBackground = new Box - { - RelativeSizeAxes = Axes.Both, - }, new Container { - RelativeSizeAxes = Axes.Both, - Height = ArgonNotePiece.NOTE_ACCENT_RATIO, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, + RelativeSizeAxes = Axes.X, + Height = ArgonNotePiece.NOTE_HEIGHT, CornerRadius = ArgonNotePiece.CORNER_RADIUS, Masking = true, Children = new Drawable[] { - shadeForeground = new Box + shadow = new Box { RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0), Colour4.Black), + // Avoid ugly single pixel overlap. + Height = 0.9f, }, - }, + new Container + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Height = ArgonNotePiece.NOTE_ACCENT_RATIO, + CornerRadius = ArgonNotePiece.CORNER_RADIUS, + Masking = true, + Children = new Drawable[] + { + shadeForeground = new Box + { + RelativeSizeAxes = Axes.Both, + }, + }, + }, + } }, }; } @@ -75,8 +86,10 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon private void onAccentChanged(ValueChangedEvent accent) { - shadeBackground.Colour = accent.NewValue.Darken(1.7f); - shadeForeground.Colour = accent.NewValue.Darken(1.1f); + shadeForeground.Colour = ColourInfo.GradientVertical( + accent.NewValue.Darken(0.2f), + accent.NewValue.Darken(1.2f) // matches body + ); } } } diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonNotePiece.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonNotePiece.cs index 49d9cebc2e..24ce22eccc 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonNotePiece.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonNotePiece.cs @@ -41,6 +41,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon shadow = new Box { RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0), Colour4.Black) }, new Container { @@ -109,8 +110,6 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon accent.NewValue.Lighten(0.1f), accent.NewValue ); - - shadow.Colour = accent.NewValue.Darken(0.5f); } } } From e12ab165b87adc0ed63b72b5de01d02de52cfcaa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 Mar 2023 20:42:59 +0900 Subject: [PATCH 204/476] Adjust colours a bit to make hold note bodies more accented --- .../Skinning/Argon/ArgonHoldBodyPiece.cs | 2 +- .../Skinning/Argon/ArgonHoldNoteTailPiece.cs | 19 ++++++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldBodyPiece.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldBodyPiece.cs index 39a030d621..91eda57c8f 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldBodyPiece.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldBodyPiece.cs @@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon AccentColour.BindValueChanged(colour => { - background.Colour = colour.NewValue.Darken(1.2f); + background.Colour = colour.NewValue.Darken(0.6f); foreground.Colour = colour.NewValue.Opacity(0.2f); }, true); diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldNoteTailPiece.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldNoteTailPiece.cs index abfd9c4dc8..140c3d5ecc 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldNoteTailPiece.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldNoteTailPiece.cs @@ -21,7 +21,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon private readonly IBindable accentColour = new Bindable(); private readonly Box shadow; - private readonly Box shadeForeground; + private readonly Box foreground; + private readonly Box foregroundAdditive; public ArgonHoldNoteTailPiece() { @@ -55,10 +56,16 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon Masking = true, Children = new Drawable[] { - shadeForeground = new Box + foreground = new Box { RelativeSizeAxes = Axes.Both, }, + foregroundAdditive = new Box + { + RelativeSizeAxes = Axes.Both, + Blending = BlendingParameters.Additive, + Height = 0.5f, + }, }, }, } @@ -86,9 +93,11 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon private void onAccentChanged(ValueChangedEvent accent) { - shadeForeground.Colour = ColourInfo.GradientVertical( - accent.NewValue.Darken(0.2f), - accent.NewValue.Darken(1.2f) // matches body + foreground.Colour = accent.NewValue.Darken(0.6f); // matches body + + foregroundAdditive.Colour = ColourInfo.GradientVertical( + accent.NewValue.Opacity(0.4f), + accent.NewValue.Opacity(0) ); } } From 08b88ed6395835de3bede8d4e028aa42a4238b59 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 Mar 2023 20:43:42 +0900 Subject: [PATCH 205/476] Adjust hold note head icon to be more centered --- osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldNoteHeadPiece.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldNoteHeadPiece.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldNoteHeadPiece.cs index 16381a6dec..b9cc73c75c 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldNoteHeadPiece.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldNoteHeadPiece.cs @@ -13,6 +13,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon { Anchor = Anchor.Centre, Origin = Anchor.Centre, + Y = 2, Size = new Vector2(20, 5), }; } From 6b2a70b1126b3fc07a97f02ca35d4c7536ff4e57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 9 Mar 2023 19:14:08 +0100 Subject: [PATCH 206/476] Remove unused fields --- .../Skinning/Argon/ArgonHoldNoteTailPiece.cs | 3 +-- osu.Game.Rulesets.Mania/Skinning/Argon/ArgonNotePiece.cs | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldNoteTailPiece.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldNoteTailPiece.cs index 140c3d5ecc..085e0630fb 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldNoteTailPiece.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldNoteTailPiece.cs @@ -20,7 +20,6 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon private readonly IBindable direction = new Bindable(); private readonly IBindable accentColour = new Bindable(); - private readonly Box shadow; private readonly Box foreground; private readonly Box foregroundAdditive; @@ -39,7 +38,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon Masking = true, Children = new Drawable[] { - shadow = new Box + new Box { RelativeSizeAxes = Axes.Both, Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0), Colour4.Black), diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonNotePiece.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonNotePiece.cs index 24ce22eccc..3a519283f1 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonNotePiece.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonNotePiece.cs @@ -26,7 +26,6 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon private readonly IBindable accentColour = new Bindable(); private readonly Box colouredBox; - private readonly Box shadow; public ArgonNotePiece() { @@ -38,7 +37,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon InternalChildren = new[] { - shadow = new Box + new Box { RelativeSizeAxes = Axes.Both, Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0), Colour4.Black) From febdca45470f34a7cdfc493cddc13f371593099c Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Sun, 12 Mar 2023 02:08:00 +0100 Subject: [PATCH 207/476] Fix argon progress bar fill being oversized --- osu.Game/Screens/Play/HUD/ArgonSongProgressBar.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonSongProgressBar.cs b/osu.Game/Screens/Play/HUD/ArgonSongProgressBar.cs index 6db1072fbb..dd6e10ba5d 100644 --- a/osu.Game/Screens/Play/HUD/ArgonSongProgressBar.cs +++ b/osu.Game/Screens/Play/HUD/ArgonSongProgressBar.cs @@ -242,7 +242,6 @@ namespace osu.Game.Screens.Play.HUD { length = value; mask.Width = value * DrawWidth; - fill.Width = value * DrawWidth; } } From 0cf69a10846b59934a837d3a39b43c812092f848 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sat, 11 Mar 2023 21:35:52 -0800 Subject: [PATCH 208/476] Make yaml line strings verbatim --- osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs b/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs index 5f8bee7558..8dc058bd69 100644 --- a/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs +++ b/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs @@ -30,11 +30,11 @@ namespace osu.Game.Overlays.Wiki.Markdown { switch (line.ToString()) { - case "outdated: true": + case @"outdated: true": isOutdated = true; break; - case "needs_cleanup: true": + case @"needs_cleanup: true": needsCleanup = true; break; } From ea88aee41f27491d4022c5bb38a4661f55661e3c Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sat, 11 Mar 2023 21:36:15 -0800 Subject: [PATCH 209/476] Display stub notice in marked wiki articles --- .../Overlays/Wiki/Markdown/WikiNoticeContainer.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs b/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs index 8dc058bd69..df800f47b1 100644 --- a/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs +++ b/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs @@ -19,6 +19,7 @@ namespace osu.Game.Overlays.Wiki.Markdown { private readonly bool isOutdated; private readonly bool needsCleanup; + private readonly bool isStub; public WikiNoticeContainer(YamlFrontMatterBlock yamlFrontMatterBlock) { @@ -37,6 +38,10 @@ namespace osu.Game.Overlays.Wiki.Markdown case @"needs_cleanup: true": needsCleanup = true; break; + + case @"stub: true": + isStub = true; + break; } } } @@ -60,6 +65,14 @@ namespace osu.Game.Overlays.Wiki.Markdown Text = WikiStrings.ShowNeedsCleanupOrRewrite, }); } + + if (isStub) + { + Add(new NoticeBox + { + Text = WikiStrings.ShowStub, + }); + } } private partial class NoticeBox : Container From 6e5b1280b7261d37e89a494b973f35905099d630 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sat, 11 Mar 2023 21:18:56 -0800 Subject: [PATCH 210/476] Fix multiple notice boxes having no spacing --- osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs b/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs index df800f47b1..a40bd14878 100644 --- a/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs +++ b/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Resources.Localisation.Web; +using osuTK; namespace osu.Game.Overlays.Wiki.Markdown { @@ -26,6 +27,7 @@ namespace osu.Game.Overlays.Wiki.Markdown RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; Direction = FillDirection.Vertical; + Spacing = new Vector2(10); foreach (object line in yamlFrontMatterBlock.Lines) { From c1618a7a16f923cf57f891150d19fd9afde06aa7 Mon Sep 17 00:00:00 2001 From: Rovearix Date: Sun, 12 Mar 2023 10:57:53 -0400 Subject: [PATCH 211/476] Prevent elements that are anchored in the center from blocking loading --- osu.Game/Graphics/UserInterface/LoadingLayer.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Graphics/UserInterface/LoadingLayer.cs b/osu.Game/Graphics/UserInterface/LoadingLayer.cs index 9059b61a33..7cd54304b7 100644 --- a/osu.Game/Graphics/UserInterface/LoadingLayer.cs +++ b/osu.Game/Graphics/UserInterface/LoadingLayer.cs @@ -69,6 +69,10 @@ namespace osu.Game.Graphics.UserInterface // note that this will not work well if touch handling elements are beneath this loading layer (something to consider for the future). case TouchEvent: return false; + + // blocking drag events to prevent unintended ui pausing while loading a beat map (see https://github.com/ppy/osu/issues/22657) + case DragEvent: + return false; } return true; From 12f240e11ad13b2bcc3a348135a78194bb130919 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 13 Mar 2023 18:23:28 +0900 Subject: [PATCH 212/476] Apply simple NRT changes to touched test scenes --- .../Formats/LegacyBeatmapDecoderTest.cs | 33 ++++++++++--------- .../Formats/LegacyStoryboardDecoderTest.cs | 14 ++++---- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 85d304da9c..e2a8062569 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -1,9 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; +using System.Diagnostics; using System.IO; using System.Linq; using NUnit.Framework; @@ -320,6 +319,8 @@ namespace osu.Game.Tests.Beatmaps.Formats { var comboColors = decoder.Decode(stream).ComboColours; + Debug.Assert(comboColors != null); + Color4[] expectedColors = { new Color4(142, 199, 255, 255), @@ -330,7 +331,7 @@ namespace osu.Game.Tests.Beatmaps.Formats new Color4(255, 177, 140, 255), new Color4(100, 100, 100, 255), // alpha is specified as 100, but should be ignored. }; - Assert.AreEqual(expectedColors.Length, comboColors?.Count); + Assert.AreEqual(expectedColors.Length, comboColors.Count); for (int i = 0; i < expectedColors.Length; i++) Assert.AreEqual(expectedColors[i], comboColors[i]); } @@ -415,14 +416,14 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.IsNotNull(positionData); Assert.IsNotNull(curveData); - Assert.AreEqual(new Vector2(192, 168), positionData.Position); + Assert.AreEqual(new Vector2(192, 168), positionData!.Position); Assert.AreEqual(956, hitObjects[0].StartTime); Assert.IsTrue(hitObjects[0].Samples.Any(s => s.Name == HitSampleInfo.HIT_NORMAL)); positionData = hitObjects[1] as IHasPosition; Assert.IsNotNull(positionData); - Assert.AreEqual(new Vector2(304, 56), positionData.Position); + Assert.AreEqual(new Vector2(304, 56), positionData!.Position); Assert.AreEqual(1285, hitObjects[1].StartTime); Assert.IsTrue(hitObjects[1].Samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP)); } @@ -578,8 +579,8 @@ namespace osu.Game.Tests.Beatmaps.Formats [Test] public void TestFallbackDecoderForCorruptedHeader() { - Decoder decoder = null; - Beatmap beatmap = null; + Decoder decoder = null!; + Beatmap beatmap = null!; using (var resStream = TestResources.OpenResource("corrupted-header.osu")) using (var stream = new LineBufferedReader(resStream)) @@ -596,8 +597,8 @@ namespace osu.Game.Tests.Beatmaps.Formats [Test] public void TestFallbackDecoderForMissingHeader() { - Decoder decoder = null; - Beatmap beatmap = null; + Decoder decoder = null!; + Beatmap beatmap = null!; using (var resStream = TestResources.OpenResource("missing-header.osu")) using (var stream = new LineBufferedReader(resStream)) @@ -614,8 +615,8 @@ namespace osu.Game.Tests.Beatmaps.Formats [Test] public void TestDecodeFileWithEmptyLinesAtStart() { - Decoder decoder = null; - Beatmap beatmap = null; + Decoder decoder = null!; + Beatmap beatmap = null!; using (var resStream = TestResources.OpenResource("empty-lines-at-start.osu")) using (var stream = new LineBufferedReader(resStream)) @@ -632,8 +633,8 @@ namespace osu.Game.Tests.Beatmaps.Formats [Test] public void TestDecodeFileWithEmptyLinesAndNoHeader() { - Decoder decoder = null; - Beatmap beatmap = null; + Decoder decoder = null!; + Beatmap beatmap = null!; using (var resStream = TestResources.OpenResource("empty-line-instead-of-header.osu")) using (var stream = new LineBufferedReader(resStream)) @@ -650,8 +651,8 @@ namespace osu.Game.Tests.Beatmaps.Formats [Test] public void TestDecodeFileWithContentImmediatelyAfterHeader() { - Decoder decoder = null; - Beatmap beatmap = null; + Decoder decoder = null!; + Beatmap beatmap = null!; using (var resStream = TestResources.OpenResource("no-empty-line-after-header.osu")) using (var stream = new LineBufferedReader(resStream)) @@ -678,7 +679,7 @@ namespace osu.Game.Tests.Beatmaps.Formats [Test] public void TestAllowFallbackDecoderOverwrite() { - Decoder decoder = null; + Decoder decoder = null!; using (var resStream = TestResources.OpenResource("corrupted-header.osu")) using (var stream = new LineBufferedReader(resStream)) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs index 281ea4e4ff..577ae3fe95 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.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 disable - using System.Linq; using NUnit.Framework; using osuTK; @@ -30,35 +28,35 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.IsTrue(storyboard.HasDrawable); Assert.AreEqual(6, storyboard.Layers.Count()); - StoryboardLayer background = storyboard.Layers.FirstOrDefault(l => l.Depth == 3); + StoryboardLayer background = storyboard.Layers.Single(l => l.Depth == 3); Assert.IsNotNull(background); Assert.AreEqual(16, background.Elements.Count); Assert.IsTrue(background.VisibleWhenFailing); Assert.IsTrue(background.VisibleWhenPassing); Assert.AreEqual("Background", background.Name); - StoryboardLayer fail = storyboard.Layers.FirstOrDefault(l => l.Depth == 2); + StoryboardLayer fail = storyboard.Layers.Single(l => l.Depth == 2); Assert.IsNotNull(fail); Assert.AreEqual(0, fail.Elements.Count); Assert.IsTrue(fail.VisibleWhenFailing); Assert.IsFalse(fail.VisibleWhenPassing); Assert.AreEqual("Fail", fail.Name); - StoryboardLayer pass = storyboard.Layers.FirstOrDefault(l => l.Depth == 1); + StoryboardLayer pass = storyboard.Layers.Single(l => l.Depth == 1); Assert.IsNotNull(pass); Assert.AreEqual(0, pass.Elements.Count); Assert.IsFalse(pass.VisibleWhenFailing); Assert.IsTrue(pass.VisibleWhenPassing); Assert.AreEqual("Pass", pass.Name); - StoryboardLayer foreground = storyboard.Layers.FirstOrDefault(l => l.Depth == 0); + StoryboardLayer foreground = storyboard.Layers.Single(l => l.Depth == 0); Assert.IsNotNull(foreground); Assert.AreEqual(151, foreground.Elements.Count); Assert.IsTrue(foreground.VisibleWhenFailing); Assert.IsTrue(foreground.VisibleWhenPassing); Assert.AreEqual("Foreground", foreground.Name); - StoryboardLayer overlay = storyboard.Layers.FirstOrDefault(l => l.Depth == int.MinValue); + StoryboardLayer overlay = storyboard.Layers.Single(l => l.Depth == int.MinValue); Assert.IsNotNull(overlay); Assert.IsEmpty(overlay.Elements); Assert.IsTrue(overlay.VisibleWhenFailing); @@ -76,7 +74,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var sprite = background.Elements.ElementAt(0) as StoryboardSprite; Assert.NotNull(sprite); - Assert.IsTrue(sprite.HasCommands); + Assert.IsTrue(sprite!.HasCommands); Assert.AreEqual(new Vector2(320, 240), sprite.InitialPosition); Assert.IsTrue(sprite.IsDrawable); Assert.AreEqual(Anchor.Centre, sprite.Origin); From 3aea058c98e07bcc4435dcb94ed49aa0c82aff8f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 13 Mar 2023 18:15:17 +0900 Subject: [PATCH 213/476] Add test coverage ensuring images are not read as videos --- .../Formats/LegacyStoryboardDecoderTest.cs | 15 +++++++++++++++ .../Resources/image-specified-as-video.osb | 4 ++++ 2 files changed, 19 insertions(+) create mode 100644 osu.Game.Tests/Resources/image-specified-as-video.osb diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs index 577ae3fe95..3a776ac225 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs @@ -169,6 +169,21 @@ namespace osu.Game.Tests.Beatmaps.Formats } } + [Test] + public void TestDecodeImageSpecifiedAsVideo() + { + var decoder = new LegacyStoryboardDecoder(); + + using (var resStream = TestResources.OpenResource("image-specified-as-video.osb")) + using (var stream = new LineBufferedReader(resStream)) + { + var storyboard = decoder.Decode(stream); + + StoryboardLayer foreground = storyboard.Layers.Single(l => l.Name == "Video"); + Assert.That(foreground.Elements.Count, Is.Zero); + } + } + [Test] public void TestDecodeOutOfRangeLoopAnimationType() { diff --git a/osu.Game.Tests/Resources/image-specified-as-video.osb b/osu.Game.Tests/Resources/image-specified-as-video.osb new file mode 100644 index 0000000000..9cea7dd4e7 --- /dev/null +++ b/osu.Game.Tests/Resources/image-specified-as-video.osb @@ -0,0 +1,4 @@ +osu file format v14 + +[Events] +Video,0,"BG.jpg",0,0 From c35c81293a149d00d6c497829a15797ef562c489 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 13 Mar 2023 18:19:18 +0900 Subject: [PATCH 214/476] Add test coverage ensuring images specified as videos are used as background image instead --- .../Beatmaps/Formats/LegacyBeatmapDecoderTest.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index e2a8062569..518981980b 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -160,6 +160,21 @@ namespace osu.Game.Tests.Beatmaps.Formats } } + [Test] + public void TestDecodeImageSpecifiedAsVideo() + { + var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; + + using (var resStream = TestResources.OpenResource("image-specified-as-video.osb")) + using (var stream = new LineBufferedReader(resStream)) + { + var beatmap = decoder.Decode(stream); + var metadata = beatmap.Metadata; + + Assert.AreEqual("BG.jpg", metadata.BackgroundFile); + } + } + [Test] public void TestDecodeBeatmapTimingPoints() { From eb37d740b13b3cafb346a7a0dfb13a8371ebe076 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 13 Mar 2023 17:40:23 +0900 Subject: [PATCH 215/476] Update supported video filetypes to match osu-stable --- osu.Game/OsuGameBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index cf58d07b9e..8f27e5dc53 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -71,7 +71,7 @@ namespace osu.Game [Cached(typeof(OsuGameBase))] public partial class OsuGameBase : Framework.Game, ICanAcceptFiles, IBeatSyncProvider { - public static readonly string[] VIDEO_EXTENSIONS = { ".mp4", ".mov", ".avi", ".flv" }; + public static readonly string[] VIDEO_EXTENSIONS = { ".mp4", ".mov", ".avi", ".flv", ".mpg", ".wmv", ".m4v" }; public const string OSU_PROTOCOL = "osu://"; From da947d86613ffe99f19f4d49a6535810ba752c59 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 13 Mar 2023 18:10:16 +0900 Subject: [PATCH 216/476] Gracefully handle beatmaps specifying images using the video storyboard type --- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 13 +++++++++++++ .../Beatmaps/Formats/LegacyStoryboardDecoder.cs | 11 ++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index eabc63b341..a9bdd21b64 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -363,6 +363,19 @@ namespace osu.Game.Beatmaps.Formats beatmap.BeatmapInfo.Metadata.BackgroundFile = CleanFilename(split[3]); break; + case LegacyEventType.Video: + string filename = CleanFilename(split[2]); + + // Some very old beatmaps had incorrect type specifications for their backgrounds (ie. using 1 for VIDEO + // instead of 0 for BACKGROUND). To handle this gracefully, check the file extension against known supported + // video extensions and handle similar to a background if it doesn't match. + if (!OsuGameBase.VIDEO_EXTENSIONS.Contains(Path.GetExtension(filename))) + { + beatmap.BeatmapInfo.Metadata.BackgroundFile = filename; + } + + break; + case LegacyEventType.Background: beatmap.BeatmapInfo.Metadata.BackgroundFile = CleanFilename(split[2]); break; diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index 44dbb3cc9f..f8308fe431 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -109,6 +109,14 @@ namespace osu.Game.Beatmaps.Formats int offset = Parsing.ParseInt(split[1]); string path = CleanFilename(split[2]); + // See handling in LegacyBeatmapDecoder for the special case where a video type is used but + // the file extension is not a valid video. + // + // This avoids potential weird crashes when ffmpeg attempts to parse an image file as a video + // (see https://github.com/ppy/osu/issues/22829#issuecomment-1465552451). + if (!OsuGameBase.VIDEO_EXTENSIONS.Contains(Path.GetExtension(path))) + break; + storyboard.GetLayer("Video").Add(new StoryboardVideo(path, offset)); break; } @@ -276,7 +284,8 @@ namespace osu.Game.Beatmaps.Formats switch (type) { case "A": - timelineGroup?.BlendingParameters.Add(easing, startTime, endTime, BlendingParameters.Additive, startTime == endTime ? BlendingParameters.Additive : BlendingParameters.Inherit); + timelineGroup?.BlendingParameters.Add(easing, startTime, endTime, BlendingParameters.Additive, + startTime == endTime ? BlendingParameters.Additive : BlendingParameters.Inherit); break; case "H": From 1f7721786b02d628d2b2c7c1d16c83b2d1a4a170 Mon Sep 17 00:00:00 2001 From: rozukke Date: Mon, 13 Mar 2023 22:01:26 +1100 Subject: [PATCH 217/476] Perform check to account for non-ASCII characters --- osu.Game/Skinning/SkinImporter.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/SkinImporter.cs b/osu.Game/Skinning/SkinImporter.cs index 9e2e02876d..43760c4a19 100644 --- a/osu.Game/Skinning/SkinImporter.cs +++ b/osu.Game/Skinning/SkinImporter.cs @@ -101,7 +101,8 @@ namespace osu.Game.Skinning // In both of these cases, the expectation from the user is that the filename or folder name is displayed somewhere to identify the skin. if (archiveName != item.Name // lazer exports use this format - && archiveName != item.GetDisplayString()) + // GetValidFilename accounts for skins with non-ASCII characters in the name that have been exported by lazer. + && archiveName != item.GetDisplayString().GetValidFilename()) item.Name = @$"{item.Name} [{archiveName}]"; } From ba728bdab1b4662366e3737fd4f84cf413e2d0e1 Mon Sep 17 00:00:00 2001 From: Rovearix Date: Mon, 13 Mar 2023 07:49:51 -0400 Subject: [PATCH 218/476] Changed the event to be the more correct one to block --- osu.Game/Graphics/UserInterface/LoadingLayer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/LoadingLayer.cs b/osu.Game/Graphics/UserInterface/LoadingLayer.cs index 7cd54304b7..3f9856eddd 100644 --- a/osu.Game/Graphics/UserInterface/LoadingLayer.cs +++ b/osu.Game/Graphics/UserInterface/LoadingLayer.cs @@ -71,7 +71,7 @@ namespace osu.Game.Graphics.UserInterface return false; // blocking drag events to prevent unintended ui pausing while loading a beat map (see https://github.com/ppy/osu/issues/22657) - case DragEvent: + case DragStartEvent: return false; } From 9ac9287dbd89051a685d9821cbbf951bdef37d7d Mon Sep 17 00:00:00 2001 From: Rovearix Date: Mon, 13 Mar 2023 08:07:55 -0400 Subject: [PATCH 219/476] Switched the implementation to set the blockInput flag for the BeatmapMetadataDisplay's LoadingLayer. This prevents the UIEvents from being being handled in this case without modifying the class --- osu.Game/Graphics/UserInterface/LoadingLayer.cs | 4 ---- osu.Game/Screens/Play/BeatmapMetadataDisplay.cs | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/LoadingLayer.cs b/osu.Game/Graphics/UserInterface/LoadingLayer.cs index 3f9856eddd..9059b61a33 100644 --- a/osu.Game/Graphics/UserInterface/LoadingLayer.cs +++ b/osu.Game/Graphics/UserInterface/LoadingLayer.cs @@ -69,10 +69,6 @@ namespace osu.Game.Graphics.UserInterface // note that this will not work well if touch handling elements are beneath this loading layer (something to consider for the future). case TouchEvent: return false; - - // blocking drag events to prevent unintended ui pausing while loading a beat map (see https://github.com/ppy/osu/issues/22657) - case DragStartEvent: - return false; } return true; diff --git a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs index 06509b6465..a152f4be19 100644 --- a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs +++ b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs @@ -114,7 +114,7 @@ namespace osu.Game.Screens.Play Anchor = Anchor.Centre, FillMode = FillMode.Fill, }, - loading = new LoadingLayer(true) + loading = new LoadingLayer(dimBackground: true, blockInput: false) } }, versionFlow = new FillFlowContainer From 4570c0030f1211bc166eff6be86ced372c1e0907 Mon Sep 17 00:00:00 2001 From: rozukke Date: Mon, 13 Mar 2023 23:37:45 +1100 Subject: [PATCH 220/476] Add test to check for import of exported skin with non-ASCII name --- osu.Game.Tests/Skins/IO/ImportSkinTest.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs index 0bd40e9962..81ebc59729 100644 --- a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs +++ b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs @@ -133,6 +133,25 @@ namespace osu.Game.Tests.Skins.IO assertImportedOnce(import1, import2); }); + [Test] + public Task TestImportExportedNonAsciiSkinFilename() => runSkinTest(async osu => + { + MemoryStream exportStream = new MemoryStream(); + + var import1 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni("name 『1』", "author 1"), "custom.osk")); + assertCorrectMetadata(import1, "name 『1』 [custom]", "author 1", osu); + + import1.PerformRead(s => + { + new LegacySkinExporter(osu.Dependencies.Get()).ExportModelTo(s, exportStream); + }); + + string exportFilename = import1.GetDisplayString().GetValidFilename(); + + var import2 = await loadSkinIntoOsu(osu, new ImportTask(exportStream, $"{exportFilename}.osk")); + assertCorrectMetadata(import2, "name 『1』 [custom]", "author 1", osu); + }); + [Test] public Task TestSameMetadataNameSameFolderName([Values] bool batchImport) => runSkinTest(async osu => { From 300d81c46ba58f7fc8867fcfa99396d3ba04d1c0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Mar 2023 16:59:18 +0900 Subject: [PATCH 221/476] Add hitting layer to fix hit lighting not being applied to tail piece Taken from https://github.com/ppy/osu/pull/22820#issuecomment-1462626898. --- .../Objects/Drawables/DrawableHoldNoteTail.cs | 2 +- .../Skinning/Argon/ArgonHoldBodyPiece.cs | 41 ++---------- .../Argon/ArgonHoldNoteHittingLayer.cs | 64 +++++++++++++++++++ .../Skinning/Argon/ArgonHoldNoteTailPiece.cs | 31 ++++++++- 4 files changed, 100 insertions(+), 38 deletions(-) create mode 100644 osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldNoteHittingLayer.cs diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTail.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTail.cs index 20ea962994..e7326df07d 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTail.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTail.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables { protected override ManiaSkinComponents Component => ManiaSkinComponents.HoldNoteTail; - protected DrawableHoldNote HoldNote => (DrawableHoldNote)ParentHitObject; + protected internal DrawableHoldNote HoldNote => (DrawableHoldNote)ParentHitObject; public DrawableHoldNoteTail() : this(null) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldBodyPiece.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldBodyPiece.cs index 91eda57c8f..57fa1c10ae 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldBodyPiece.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldBodyPiece.cs @@ -20,10 +20,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon public partial class ArgonHoldBodyPiece : CompositeDrawable, IHoldNoteBody { protected readonly Bindable AccentColour = new Bindable(); - protected readonly IBindable IsHitting = new Bindable(); private Drawable background = null!; - private Box foreground = null!; + private ArgonHoldNoteHittingLayer hittingLayer = null!; public ArgonHoldBodyPiece() { @@ -40,12 +39,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon InternalChildren = new[] { background = new Box { RelativeSizeAxes = Axes.Both }, - foreground = new Box - { - RelativeSizeAxes = Axes.Both, - Blending = BlendingParameters.Additive, - Alpha = 0, - }, + hittingLayer = new ArgonHoldNoteHittingLayer() }; if (drawableObject != null) @@ -53,44 +47,19 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon var holdNote = (DrawableHoldNote)drawableObject; AccentColour.BindTo(holdNote.AccentColour); - IsHitting.BindTo(holdNote.IsHitting); + hittingLayer.AccentColour.BindTo(holdNote.AccentColour); + ((IBindable)hittingLayer.IsHitting).BindTo(holdNote.IsHitting); } AccentColour.BindValueChanged(colour => { background.Colour = colour.NewValue.Darken(0.6f); - foreground.Colour = colour.NewValue.Opacity(0.2f); }, true); - - IsHitting.BindValueChanged(hitting => - { - const float animation_length = 50; - - foreground.ClearTransforms(); - - if (hitting.NewValue) - { - // wait for the next sync point - double synchronisedOffset = animation_length * 2 - Time.Current % (animation_length * 2); - - using (foreground.BeginDelayedSequence(synchronisedOffset)) - { - foreground.FadeTo(1, animation_length).Then() - .FadeTo(0.5f, animation_length) - .Loop(); - } - } - else - { - foreground.FadeOut(animation_length); - } - }); } public void Recycle() { - foreground.ClearTransforms(); - foreground.Alpha = 0; + hittingLayer.Recycle(); } } } diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldNoteHittingLayer.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldNoteHittingLayer.cs new file mode 100644 index 0000000000..9df7e06a4b --- /dev/null +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldNoteHittingLayer.cs @@ -0,0 +1,64 @@ +// 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.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osuTK.Graphics; +using Box = osu.Framework.Graphics.Shapes.Box; + +namespace osu.Game.Rulesets.Mania.Skinning.Argon +{ + public partial class ArgonHoldNoteHittingLayer : Box + { + public readonly Bindable AccentColour = new Bindable(); + public readonly Bindable IsHitting = new Bindable(); + + public ArgonHoldNoteHittingLayer() + { + RelativeSizeAxes = Axes.Both; + Blending = BlendingParameters.Additive; + Alpha = 0; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + AccentColour.BindValueChanged(colour => + { + Colour = colour.NewValue.Opacity(0.2f); + }, true); + + IsHitting.BindValueChanged(hitting => + { + const float animation_length = 50; + + ClearTransforms(); + + if (hitting.NewValue) + { + // wait for the next sync point + double synchronisedOffset = animation_length * 2 - Time.Current % (animation_length * 2); + + using (BeginDelayedSequence(synchronisedOffset)) + { + this.FadeTo(1, animation_length).Then() + .FadeTo(0.5f, animation_length) + .Loop(); + } + } + else + { + this.FadeOut(animation_length); + } + }); + } + + public void Recycle() + { + ClearTransforms(); + Alpha = 0; + } + } +} diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldNoteTailPiece.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldNoteTailPiece.cs index 085e0630fb..efd7f4f280 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldNoteTailPiece.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldNoteTailPiece.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI.Scrolling; using osuTK; @@ -17,10 +18,14 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon { internal partial class ArgonHoldNoteTailPiece : CompositeDrawable { + [Resolved] + private DrawableHitObject? drawableObject { get; set; } + private readonly IBindable direction = new Bindable(); private readonly IBindable accentColour = new Bindable(); private readonly Box foreground; + private readonly ArgonHoldNoteHittingLayer hittingLayer; private readonly Box foregroundAdditive; public ArgonHoldNoteTailPiece() @@ -59,6 +64,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon { RelativeSizeAxes = Axes.Both, }, + hittingLayer = new ArgonHoldNoteHittingLayer(), foregroundAdditive = new Box { RelativeSizeAxes = Axes.Both, @@ -73,7 +79,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon } [BackgroundDependencyLoader(true)] - private void load(IScrollingInfo scrollingInfo, DrawableHitObject? drawableObject) + private void load(IScrollingInfo scrollingInfo) { direction.BindTo(scrollingInfo.Direction); direction.BindValueChanged(onDirectionChanged, true); @@ -82,9 +88,24 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon { accentColour.BindTo(drawableObject.AccentColour); accentColour.BindValueChanged(onAccentChanged, true); + + drawableObject.HitObjectApplied += hitObjectApplied; } } + private void hitObjectApplied(DrawableHitObject drawableHitObject) + { + var holdNoteTail = (DrawableHoldNoteTail)drawableHitObject; + + hittingLayer.Recycle(); + + hittingLayer.AccentColour.UnbindBindings(); + hittingLayer.AccentColour.BindTo(holdNoteTail.HoldNote.AccentColour); + + hittingLayer.IsHitting.UnbindBindings(); + ((IBindable)hittingLayer.IsHitting).BindTo(holdNoteTail.HoldNote.IsHitting); + } + private void onDirectionChanged(ValueChangedEvent direction) { Scale = new Vector2(1, direction.NewValue == ScrollingDirection.Up ? -1 : 1); @@ -99,5 +120,13 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon accent.NewValue.Opacity(0) ); } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (drawableObject != null) + drawableObject.HitObjectApplied -= hitObjectApplied; + } } } From 48d11610b344ac23dd0ff3628b90ee4b4b40402d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Mar 2023 17:44:51 +0900 Subject: [PATCH 222/476] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 234575fc62..4e580a6919 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -11,7 +11,7 @@ manifestmerger.jar - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 103ac99172..a84b42d9a4 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index fde45817cf..8738979c57 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -16,6 +16,6 @@ iossimulator-x64 - + From 0b23809585d73617942c7f5a0edebfcab2ca02ee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Mar 2023 17:52:17 +0900 Subject: [PATCH 223/476] Update resources --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index a84b42d9a4..c08dc9ed8f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + From f40a4b591fe30341289619ca83fc4e3459ee392a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Mar 2023 17:29:54 +0900 Subject: [PATCH 224/476] Adjust animation length and colouring of hitting layer --- .../Skinning/Argon/ArgonHoldNoteHittingLayer.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldNoteHittingLayer.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldNoteHittingLayer.cs index 9df7e06a4b..9e7afa8b9e 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldNoteHittingLayer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldNoteHittingLayer.cs @@ -27,12 +27,12 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon AccentColour.BindValueChanged(colour => { - Colour = colour.NewValue.Opacity(0.2f); + Colour = colour.NewValue.Lighten(0.2f).Opacity(0.3f); }, true); IsHitting.BindValueChanged(hitting => { - const float animation_length = 50; + const float animation_length = 80; ClearTransforms(); @@ -43,8 +43,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon using (BeginDelayedSequence(synchronisedOffset)) { - this.FadeTo(1, animation_length).Then() - .FadeTo(0.5f, animation_length) + this.FadeTo(1, animation_length, Easing.OutSine).Then() + .FadeTo(0.5f, animation_length, Easing.InSine) .Loop(); } } @@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon { this.FadeOut(animation_length); } - }); + }, true); } public void Recycle() From 4cea29402bfae7a537bd27a11df3b338b5b9f350 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Mar 2023 18:32:24 +0900 Subject: [PATCH 225/476] Don't show epilepsy warning when storyboards are disabled I have more thoughts on this warning in general (which will likely see it removed or moved in the future) but this seems like a quick QOL fix for now. As mentioned in https://github.com/ppy/osu/discussions/22861. --- osu.Game/Screens/Play/PlayerLoader.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 4f7e4add32..be4229ade9 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -67,6 +67,8 @@ namespace osu.Game.Screens.Play private OsuScrollContainer settingsScroll = null!; + private Bindable showStoryboards = null!; + private bool backgroundBrightnessReduction; private readonly BindableDouble volumeAdjustment = new BindableDouble(1); @@ -149,10 +151,11 @@ namespace osu.Game.Screens.Play } [BackgroundDependencyLoader] - private void load(SessionStatics sessionStatics, AudioManager audio) + private void load(SessionStatics sessionStatics, AudioManager audio, OsuConfigManager config) { muteWarningShownOnce = sessionStatics.GetBindable(Static.MutedAudioNotificationShownOnce); batteryWarningShownOnce = sessionStatics.GetBindable(Static.LowBatteryNotificationShownOnce); + showStoryboards = config.GetBindable(OsuSetting.ShowStoryboard); const float padding = 25; @@ -463,7 +466,10 @@ namespace osu.Game.Screens.Play // only show if the warning was created (i.e. the beatmap needs it) // and this is not a restart of the map (the warning expires after first load). - if (epilepsyWarning?.IsAlive == true) + // + // note the late check of storyboard enable as the user may have just changed it + // from the settings on the loader screen. + if (epilepsyWarning?.IsAlive == true && showStoryboards.Value) { const double epilepsy_display_length = 3000; From d65d09e45f9c02186bec88fcb9ab01867ead71b7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Mar 2023 20:24:39 +0900 Subject: [PATCH 226/476] Change field to `const` --- osu.Game/Graphics/Backgrounds/Triangles.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index 5b7b94ff4c..28a715ca0b 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -252,7 +252,7 @@ namespace osu.Game.Graphics.Backgrounds private class TrianglesDrawNode : DrawNode { - private float fill = 1f; + private const float fill = 1f; protected new Triangles Source => (Triangles)base.Source; From b5ea855b6cf8ff9faa5659c1a2e6f4e41b5f5739 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Mar 2023 20:37:50 +0900 Subject: [PATCH 227/476] Fix failing `DrawableRulesetDependencies` test --- .../Rulesets/TestSceneDrawableRulesetDependencies.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Rulesets/TestSceneDrawableRulesetDependencies.cs b/osu.Game.Tests/Rulesets/TestSceneDrawableRulesetDependencies.cs index 5cfcca303f..f8248e88bb 100644 --- a/osu.Game.Tests/Rulesets/TestSceneDrawableRulesetDependencies.cs +++ b/osu.Game.Tests/Rulesets/TestSceneDrawableRulesetDependencies.cs @@ -80,7 +80,7 @@ namespace osu.Game.Tests.Rulesets dependencies.CacheAs(ParentTextureStore = new TestTextureStore(parent.Get().Renderer)); dependencies.CacheAs(ParentSampleStore = new TestSampleStore()); - dependencies.CacheAs(ParentShaderManager = new TestShaderManager(parent.Get().Renderer)); + dependencies.CacheAs(ParentShaderManager = new TestShaderManager(parent.Get().Renderer, parent.Get())); return new DrawableRulesetDependencies(new OsuRuleset(), dependencies); } @@ -156,12 +156,15 @@ namespace osu.Game.Tests.Rulesets private class TestShaderManager : ShaderManager { - public TestShaderManager(IRenderer renderer) + private readonly ShaderManager parentManager; + + public TestShaderManager(IRenderer renderer, ShaderManager parentManager) : base(renderer, new ResourceStore()) { + this.parentManager = parentManager; } - public override byte[] LoadRaw(string name) => null; + public override byte[] LoadRaw(string name) => parentManager.LoadRaw(name); public bool IsDisposed { get; private set; } From 3c4e2d8700163f8647717643ba6e5575dc3401ee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Mar 2023 21:04:51 +0900 Subject: [PATCH 228/476] Add tests covering drag selection --- .../Visual/Gameplay/TestSceneSkinEditor.cs | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs index 7af2b7d6fe..a999a9b4fc 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs @@ -54,6 +54,72 @@ namespace osu.Game.Tests.Visual.Gameplay AddUntilStep("wait for loaded", () => skinEditor.IsLoaded); } + [Test] + public void TestDragSelection() + { + BigBlackBox box1 = null!; + BigBlackBox box2 = null!; + BigBlackBox box3 = null!; + + AddStep("Add big black boxes", () => + { + var target = Player.ChildrenOfType().First(); + target.Add(box1 = new BigBlackBox + { + Position = new Vector2(-90), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }); + target.Add(box2 = new BigBlackBox + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }); + target.Add(box3 = new BigBlackBox + { + Position = new Vector2(90), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }); + }); + + AddStep("Begin drag top left", () => + { + InputManager.MoveMouseTo(box1.ScreenSpaceDrawQuad.TopLeft - new Vector2(box1.ScreenSpaceDrawQuad.Width / 4)); + InputManager.PressButton(MouseButton.Left); + }); + + AddStep("Drag to bottom right", () => + { + InputManager.MoveMouseTo(box2.ScreenSpaceDrawQuad.Centre + new Vector2(box2.ScreenSpaceDrawQuad.Width / 4)); + }); + + AddStep("Release button", () => + { + InputManager.ReleaseButton(MouseButton.Left); + }); + + AddAssert("First two boxes selected", () => skinEditor.SelectedComponents, () => Is.EqualTo(new[] { box1, box2 })); + + AddStep("Begin drag bottom right", () => + { + InputManager.MoveMouseTo(box3.ScreenSpaceDrawQuad.BottomRight + new Vector2(box3.ScreenSpaceDrawQuad.Width / 4)); + InputManager.PressButton(MouseButton.Left); + }); + + AddStep("Drag to top left", () => + { + InputManager.MoveMouseTo(box2.ScreenSpaceDrawQuad.Centre - new Vector2(box2.ScreenSpaceDrawQuad.Width / 4)); + }); + + AddStep("Release button", () => + { + InputManager.ReleaseButton(MouseButton.Left); + }); + + AddAssert("Last two boxes selected", () => skinEditor.SelectedComponents, () => Is.EqualTo(new[] { box2, box3 })); + } + [Test] public void TestCyclicSelection() { From 1d5e5966153e5fbacb206518bdeb7c70b88955b7 Mon Sep 17 00:00:00 2001 From: Terochi Date: Tue, 14 Mar 2023 20:44:30 +0100 Subject: [PATCH 229/476] Update `FailAnimation` to use `SkinnableSound` --- osu.Game/Screens/Play/FailAnimation.cs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Play/FailAnimation.cs b/osu.Game/Screens/Play/FailAnimation.cs index 0214d33549..57bdad079e 100644 --- a/osu.Game/Screens/Play/FailAnimation.cs +++ b/osu.Game/Screens/Play/FailAnimation.cs @@ -1,15 +1,13 @@ // 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.Audio; -using osu.Framework.Bindables; -using osu.Game.Rulesets.UI; using System; using System.Collections.Generic; using ManagedBass.Fx; using osu.Framework.Allocation; -using osu.Framework.Audio.Sample; +using osu.Framework.Audio; using osu.Framework.Audio.Track; +using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -21,6 +19,7 @@ using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.UI; using osu.Game.Skinning; using osuTK; using osuTK.Graphics; @@ -50,8 +49,7 @@ namespace osu.Game.Screens.Play private const float duration = 2500; - private ISample? failSample; - private SampleChannel? failSampleChannel; + private SkinnableSound failSample = null!; [Resolved] private OsuConfigManager config { get; set; } = null!; @@ -76,10 +74,10 @@ namespace osu.Game.Screens.Play } [BackgroundDependencyLoader] - private void load(AudioManager audio, ISkinSource skin, IBindable beatmap) + private void load(AudioManager audio, IBindable beatmap) { track = beatmap.Value.Track; - failSample = skin.GetSample(new SampleInfo(@"Gameplay/failsound")); + AddInternal(failSample = new SkinnableSound(new SampleInfo("Gameplay/failsound"))); AddRangeInternal(new Drawable[] { @@ -126,7 +124,7 @@ namespace osu.Game.Screens.Play failHighPassFilter.CutoffTo(300); failLowPassFilter.CutoffTo(300, duration, Easing.OutCubic); - failSampleChannel = failSample?.Play(); + failSample.Play(); track.AddAdjustment(AdjustableProperty.Frequency, trackFreq); track.AddAdjustment(AdjustableProperty.Volume, volumeAdjustment); @@ -159,7 +157,7 @@ namespace osu.Game.Screens.Play /// public void Stop() { - failSampleChannel?.Stop(); + failSample.Stop(); removeFilters(); } From 390ad335d0aa8c1cb7f5a8bb99af0d905c0de034 Mon Sep 17 00:00:00 2001 From: Terochi Date: Tue, 14 Mar 2023 21:35:52 +0100 Subject: [PATCH 230/476] Reloading samples before playing then when skin change occurs --- osu.Game/Skinning/SkinnableSound.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index 475b79053a..43e16f4930 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -115,6 +115,10 @@ namespace osu.Game.Skinning /// public virtual void Play() { + if (Scheduler.HasPendingTasks) + // update samples queued due to skin change before playing them + UpdateSubTree(); + samplesContainer.ForEach(c => { if (PlayWhenZeroVolume || c.AggregateVolume.Value > 0) From 1cf870d956289393abbdcc2578070b90fe9f8fc9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 15 Mar 2023 15:16:48 +0900 Subject: [PATCH 231/476] Add test coverage and fix fail case where a drag selection ends incorrectly with cyclic selection --- osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs | 10 +++++++++- .../Edit/Compose/Components/BlueprintContainer.cs | 9 ++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs index a999a9b4fc..35ba363f4b 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs @@ -83,6 +83,14 @@ namespace osu.Game.Tests.Visual.Gameplay }); }); + // This step is specifically added to reproduce an edge case which was found during cyclic selection development. + // If everything is working as expected it should not affect the subsequent drag selections. + AddRepeatStep("Select top left", () => + { + InputManager.MoveMouseTo(box1.ScreenSpaceDrawQuad.TopLeft + new Vector2(box1.ScreenSpaceDrawQuad.Width / 8)); + InputManager.Click(MouseButton.Left); + }, 2); + AddStep("Begin drag top left", () => { InputManager.MoveMouseTo(box1.ScreenSpaceDrawQuad.TopLeft - new Vector2(box1.ScreenSpaceDrawQuad.Width / 4)); @@ -91,7 +99,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("Drag to bottom right", () => { - InputManager.MoveMouseTo(box2.ScreenSpaceDrawQuad.Centre + new Vector2(box2.ScreenSpaceDrawQuad.Width / 4)); + InputManager.MoveMouseTo(box3.ScreenSpaceDrawQuad.TopRight + new Vector2(-box3.ScreenSpaceDrawQuad.Width / 8, box3.ScreenSpaceDrawQuad.Height / 4)); }); AddStep("Release button", () => diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 3ba71cebe6..143f343c7d 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -178,6 +178,7 @@ namespace osu.Game.Screens.Edit.Compose.Components endClickSelection(e); clickSelectionHandled = false; isDraggingBlueprint = false; + wasDragStarted = false; }); finishSelectionMovement(); @@ -191,6 +192,7 @@ namespace osu.Game.Screens.Edit.Compose.Components return false; lastDragEvent = e; + wasDragStarted = true; if (movementBlueprints != null) { @@ -399,7 +401,7 @@ namespace osu.Game.Screens.Edit.Compose.Components return false; } - if (selectedBlueprintAlreadySelectedOnMouseDown && AllowCyclicSelection) + if (!wasDragStarted && selectedBlueprintAlreadySelectedOnMouseDown && AllowCyclicSelection) { // If a click occurred and was handled by the currently selected blueprint but didn't result in a drag, // cycle between other blueprints which are also under the cursor. @@ -485,6 +487,11 @@ namespace osu.Game.Screens.Edit.Compose.Components /// private bool isDraggingBlueprint; + /// + /// Whether a drag operation was started at all. + /// + private bool wasDragStarted; + /// /// Attempts to begin the movement of any selected blueprints. /// From 6c4f596a9a1a2c0321f0ba1f231d8dc23ae41836 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 15 Mar 2023 16:26:36 +0900 Subject: [PATCH 232/476] Make osu! touch input aware of the distance travelled of a non-direct touch --- .../TestSceneOsuTouchInput.cs | 28 +++++++++++++++++++ .../UI/OsuTouchInputMapper.cs | 18 ++++++++++-- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs index 72bcec6045..bd32d820d1 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs @@ -150,6 +150,34 @@ namespace osu.Game.Rulesets.Osu.Tests assertKeyCounter(1, 1); } + [Test] + public void TestPositionalTrackingAfterLongDistanceTravelled() + { + // When a single touch has already travelled enough distance on screen, it should remain as the positional + // tracking touch until released (unless a direct touch occurs). + + beginTouch(TouchSource.Touch1); + + assertKeyCounter(1, 0); + checkPressed(OsuAction.LeftButton); + checkPosition(TouchSource.Touch1); + + // cover some distance + beginTouch(TouchSource.Touch1, new Vector2(0)); + beginTouch(TouchSource.Touch1, new Vector2(9999)); + beginTouch(TouchSource.Touch1, new Vector2(0)); + beginTouch(TouchSource.Touch1, new Vector2(9999)); + beginTouch(TouchSource.Touch1); + + beginTouch(TouchSource.Touch2); + + assertKeyCounter(1, 1); + checkPressed(OsuAction.LeftButton); + checkPressed(OsuAction.RightButton); + // in this case, touch 2 should not become the positional tracking touch. + checkPosition(TouchSource.Touch1); + } + [Test] public void TestPositionalInputUpdatesOnlyFromMostRecentTouch() { diff --git a/osu.Game.Rulesets.Osu/UI/OsuTouchInputMapper.cs b/osu.Game.Rulesets.Osu/UI/OsuTouchInputMapper.cs index 8df1c35b5c..76cb388949 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuTouchInputMapper.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuTouchInputMapper.cs @@ -97,8 +97,8 @@ namespace osu.Game.Rulesets.Osu.UI return; } - // ..or if the current position tracking touch was not a direct touch (this one is debatable and may be change in the future, but it's the simplest way to handle) - if (!positionTrackingTouch.DirectTouch) + // ..or if the current position tracking touch was not a direct touch (and didn't travel across the screen too far). + if (!positionTrackingTouch.DirectTouch && positionTrackingTouch.DistanceTravelled < 200) { positionTrackingTouch = newTouch; return; @@ -117,6 +117,12 @@ namespace osu.Game.Rulesets.Osu.UI private void handleTouchMovement(TouchEvent touchEvent) { + if (touchEvent is TouchMoveEvent moveEvent) + { + var trackedTouch = trackedTouches.Single(t => t.Source == touchEvent.Touch.Source); + trackedTouch.DistanceTravelled += moveEvent.Delta.Length; + } + // Movement should only be tracked for the most recent touch. if (touchEvent.Touch.Source != positionTrackingTouch?.Source) return; @@ -148,8 +154,16 @@ namespace osu.Game.Rulesets.Osu.UI public OsuAction? Action; + /// + /// Whether the touch was on a hit circle receptor. + /// public readonly bool DirectTouch; + /// + /// The total distance on screen travelled by this touch. + /// + public double DistanceTravelled; + public TrackedTouch(TouchSource source, OsuAction? action, bool directTouch) { Source = source; From 42359a9754d0884b97f3c0ca459f22d8358494b1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 15 Mar 2023 16:38:26 +0900 Subject: [PATCH 233/476] Fix previous touch action not being released when it's not a direct touch --- osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs | 2 +- osu.Game.Rulesets.Osu/UI/OsuTouchInputMapper.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs index bd32d820d1..d1880d7552 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs @@ -172,7 +172,7 @@ namespace osu.Game.Rulesets.Osu.Tests beginTouch(TouchSource.Touch2); assertKeyCounter(1, 1); - checkPressed(OsuAction.LeftButton); + checkNotPressed(OsuAction.LeftButton); checkPressed(OsuAction.RightButton); // in this case, touch 2 should not become the positional tracking touch. checkPosition(TouchSource.Touch1); diff --git a/osu.Game.Rulesets.Osu/UI/OsuTouchInputMapper.cs b/osu.Game.Rulesets.Osu/UI/OsuTouchInputMapper.cs index 76cb388949..8cec65f515 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuTouchInputMapper.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuTouchInputMapper.cs @@ -105,12 +105,12 @@ namespace osu.Game.Rulesets.Osu.UI } // In the case the new touch was not used for position tracking, we should also check the previous position tracking touch. - // If it was a direct touch and still has its action pressed, that action should be released. + // If it still has its action pressed, that action should be released. // // This is done to allow tracking with the initial touch while still having both Left/Right actions available for alternating with two more touches. - if (positionTrackingTouch.DirectTouch && positionTrackingTouch.Action is OsuAction directTouchAction) + if (positionTrackingTouch.Action is OsuAction touchAction) { - osuInputManager.KeyBindingContainer.TriggerReleased(directTouchAction); + osuInputManager.KeyBindingContainer.TriggerReleased(touchAction); positionTrackingTouch.Action = null; } } From a9c349fa6db682c9c2cdfa961f2821e84bac57d4 Mon Sep 17 00:00:00 2001 From: Terochi Date: Wed, 15 Mar 2023 09:00:34 +0100 Subject: [PATCH 234/476] Cache any skin changes in `SkinReloadableDrawable` to `ScheduledDelegate` --- osu.Game/Skinning/SkinReloadableDrawable.cs | 19 +++++++++++++++++-- osu.Game/Skinning/SkinnableSound.cs | 6 +++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/osu.Game/Skinning/SkinReloadableDrawable.cs b/osu.Game/Skinning/SkinReloadableDrawable.cs index cef1db4bc0..757ca8de83 100644 --- a/osu.Game/Skinning/SkinReloadableDrawable.cs +++ b/osu.Game/Skinning/SkinReloadableDrawable.cs @@ -5,6 +5,7 @@ using System; using osu.Framework.Allocation; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics.Pooling; +using osu.Framework.Threading; namespace osu.Game.Skinning { @@ -14,6 +15,8 @@ namespace osu.Game.Skinning /// public abstract partial class SkinReloadableDrawable : PoolableDrawable { + private ScheduledDelegate? pendingSkinChange; + /// /// Invoked when has changed. /// @@ -31,10 +34,22 @@ namespace osu.Game.Skinning CurrentSkin.SourceChanged += onChange; } - private void onChange() => + private void onChange() + { // schedule required to avoid calls after disposed. // note that this has the side-effect of components only performing a skin change when they are alive. - Scheduler.AddOnce(skinChanged); + pendingSkinChange?.Cancel(); + pendingSkinChange = Scheduler.Add(skinChanged); + } + + public void FlushPendingSkinChanges() + { + if (pendingSkinChange == null) + return; + + pendingSkinChange.RunTask(); + pendingSkinChange = null; + } protected override void LoadAsyncComplete() { diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index 43e16f4930..052b1e5b9f 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -115,12 +115,12 @@ namespace osu.Game.Skinning /// public virtual void Play() { - if (Scheduler.HasPendingTasks) - // update samples queued due to skin change before playing them - UpdateSubTree(); + FlushPendingSkinChanges(); samplesContainer.ForEach(c => { + c.FlushPendingSkinChanges(); + if (PlayWhenZeroVolume || c.AggregateVolume.Value > 0) { c.Stop(); From b0f1a6952393c6035614d791be6a5a6dd494bbbd Mon Sep 17 00:00:00 2001 From: Terochi Date: Wed, 15 Mar 2023 09:05:34 +0100 Subject: [PATCH 235/476] Update the pauseLoop sample instantly on skin change --- osu.Game/Screens/Play/PauseOverlay.cs | 1 + osu.Game/Screens/Play/Player.cs | 5 +++++ osu.Game/Skinning/SkinReloadableDrawable.cs | 2 +- osu.Game/Skinning/SkinnableSound.cs | 9 +++++++-- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/PauseOverlay.cs b/osu.Game/Screens/Play/PauseOverlay.cs index db42998c45..452e170cc6 100644 --- a/osu.Game/Screens/Play/PauseOverlay.cs +++ b/osu.Game/Screens/Play/PauseOverlay.cs @@ -28,6 +28,7 @@ namespace osu.Game.Screens.Play private SkinnableSound pauseLoop; + public void FlushPendingSkinChanges() => pauseLoop.FlushPendingSkinChanges(); protected override Action BackAction => () => InternalButtons.First().TriggerClick(); [BackgroundDependencyLoader] diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index bc453d2151..d4180068d3 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -316,6 +316,11 @@ namespace osu.Game.Screens.Play // we may want to limit this in the future to disallow rulesets from outright replacing elements the user expects to be there. failAnimationLayer.Add(createOverlayComponents(Beatmap.Value)); + rulesetSkinProvider.SourceChanged += () => + { + PauseOverlay.FlushPendingSkinChanges(); + }; + if (!DrawableRuleset.AllowGameplayOverlays) { HUDOverlay.ShowHud.Value = false; diff --git a/osu.Game/Skinning/SkinReloadableDrawable.cs b/osu.Game/Skinning/SkinReloadableDrawable.cs index 757ca8de83..ff51eb6dce 100644 --- a/osu.Game/Skinning/SkinReloadableDrawable.cs +++ b/osu.Game/Skinning/SkinReloadableDrawable.cs @@ -42,7 +42,7 @@ namespace osu.Game.Skinning pendingSkinChange = Scheduler.Add(skinChanged); } - public void FlushPendingSkinChanges() + public virtual void FlushPendingSkinChanges() { if (pendingSkinChange == null) return; diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index 052b1e5b9f..f80b1f52fa 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -110,6 +110,13 @@ namespace osu.Game.Skinning } } + public override void FlushPendingSkinChanges() + { + base.FlushPendingSkinChanges(); + + samplesContainer.ForEach(c => c.FlushPendingSkinChanges()); + } + /// /// Plays the samples. /// @@ -119,8 +126,6 @@ namespace osu.Game.Skinning samplesContainer.ForEach(c => { - c.FlushPendingSkinChanges(); - if (PlayWhenZeroVolume || c.AggregateVolume.Value > 0) { c.Stop(); From d87f0557cef3288a72eb0ba9d21610a2433c2124 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 15 Mar 2023 17:18:45 +0900 Subject: [PATCH 236/476] Update colours for 3k to not use double-purples --- .../Skinning/Argon/ManiaArgonSkinTransformer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs index 0beca815b2..ca9cd83a63 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs @@ -139,9 +139,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon { case 0: return colour_pink; - case 1: return colour_purple; + case 1: return colour_orange; - case 2: return colour_special_column; + case 2: return colour_yellow; default: throw new ArgumentOutOfRangeException(); } From 8908648f97b5397261ffaa8036ec493a96ce36d7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 15 Mar 2023 18:01:22 +0900 Subject: [PATCH 237/476] Fix super-dodgy cast of `IEnumerable` to `Drawable` --- osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs | 2 +- osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs index 42683a3eec..44fa555149 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs @@ -44,7 +44,7 @@ namespace osu.Game.Tests.Visual.Gameplay // best way to check without exposing. private Drawable hideTarget => hudOverlay.KeyCounter; - private Drawable keyCounterFlow => (Drawable)hudOverlay.KeyCounter.Counters; + private Drawable keyCounterFlow => hudOverlay.KeyCounter.ChildrenOfType>().Single(); [BackgroundDependencyLoader] private void load() diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs index 3eda80719a..dd1b37341f 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs @@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual.Gameplay // best way to check without exposing. private Drawable hideTarget => hudOverlay.KeyCounter; - private Drawable keyCounterFlow => (Drawable)hudOverlay.KeyCounter.Counters; + private Drawable keyCounterFlow => hudOverlay.KeyCounter.ChildrenOfType>().Single(); [Test] public void TestComboCounterIncrementing() From 9e444af380ed0d3e96a17c3f83b3cf35d5a7ae4a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 15 Mar 2023 18:02:12 +0900 Subject: [PATCH 238/476] Use object initialisers and fix order of initialisation vs add --- .../Play/HUD/DefaultKeyCounterDisplay.cs | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/DefaultKeyCounterDisplay.cs b/osu.Game/Screens/Play/HUD/DefaultKeyCounterDisplay.cs index 9499263474..8b910d56f6 100644 --- a/osu.Game/Screens/Play/HUD/DefaultKeyCounterDisplay.cs +++ b/osu.Game/Screens/Play/HUD/DefaultKeyCounterDisplay.cs @@ -13,17 +13,18 @@ namespace osu.Game.Screens.Play.HUD private const int duration = 100; private const double key_fade_time = 80; - private readonly FillFlowContainer keyFlow = new FillFlowContainer(); + private readonly FillFlowContainer keyFlow; public override IEnumerable Counters => keyFlow; public DefaultKeyCounterDisplay() { - keyFlow.Direction = FillDirection.Horizontal; - keyFlow.AutoSizeAxes = Axes.Both; - keyFlow.Alpha = 0; - - InternalChild = keyFlow; + InternalChild = keyFlow = new FillFlowContainer + { + Direction = FillDirection.Horizontal, + AutoSizeAxes = Axes.Both, + Alpha = 0, + }; } protected override void Update() @@ -36,13 +37,12 @@ namespace osu.Game.Screens.Play.HUD } public override void AddTrigger(InputTrigger trigger) - { - DefaultKeyCounter key = new DefaultKeyCounter(trigger); - keyFlow.Add(key); - key.FadeTime = key_fade_time; - key.KeyDownTextColor = KeyDownTextColor; - key.KeyUpTextColor = KeyUpTextColor; - } + keyFlow.Add(new DefaultKeyCounter(trigger) + { + FadeTime = key_fade_time, + KeyDownTextColor = KeyDownTextColor, + KeyUpTextColor = KeyUpTextColor, + }); protected override void UpdateVisibility() => // Isolate changing visibility of the key counters from fading this component. From 5f9b13a77513df7797581eb8f758d0b5fc37f2d8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 15 Mar 2023 18:02:41 +0900 Subject: [PATCH 239/476] Rename `Add`/`AddRange` methods as they are no longer conflicting with `Container` --- osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs | 2 +- osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs | 4 ++-- .../Gameplay/TestSceneSkinEditorMultipleSkins.cs | 2 +- .../Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs | 2 +- osu.Game/Rulesets/UI/RulesetInputManager.cs | 2 +- osu.Game/Screens/Play/HUD/DefaultKeyCounterDisplay.cs | 2 +- osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs | 10 ++++------ 7 files changed, 11 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs index 44fa555149..7bc789ecc4 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs @@ -266,7 +266,7 @@ namespace osu.Game.Tests.Visual.Gameplay hudOverlay = new HUDOverlay(null, Array.Empty()); // Add any key just to display the key counter visually. - hudOverlay.KeyCounter.AddTrigger(new KeyCounterKeyboardTrigger(Key.Space)); + hudOverlay.KeyCounter.Add(new KeyCounterKeyboardTrigger(Key.Space)); scoreProcessor.Combo.Value = 1; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs index 6dc07ca9d3..46d5e6c4d2 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs @@ -23,7 +23,7 @@ namespace osu.Game.Tests.Visual.Gameplay Anchor = Anchor.Centre, }; - kc.AddTriggerRange(new InputTrigger[] + kc.AddRange(new InputTrigger[] { new KeyCounterKeyboardTrigger(Key.X), new KeyCounterKeyboardTrigger(Key.X), @@ -36,7 +36,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("Add random", () => { Key key = (Key)((int)Key.A + RNG.Next(26)); - kc.AddTrigger(new KeyCounterKeyboardTrigger(key)); + kc.Add(new KeyCounterKeyboardTrigger(key)); }); Key testKey = ((KeyCounterKeyboardTrigger)kc.Counters.First().Trigger).Key; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs index aa7119829a..93fec60de4 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs @@ -58,7 +58,7 @@ namespace osu.Game.Tests.Visual.Gameplay }; // Add any key just to display the key counter visually. - hudOverlay.KeyCounter.AddTrigger(new KeyCounterKeyboardTrigger(Key.Space)); + hudOverlay.KeyCounter.Add(new KeyCounterKeyboardTrigger(Key.Space)); scoreProcessor.Combo.Value = 1; return new Container diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs index dd1b37341f..9d8d82108d 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs @@ -87,7 +87,7 @@ namespace osu.Game.Tests.Visual.Gameplay hudOverlay = new HUDOverlay(null, Array.Empty()); // Add any key just to display the key counter visually. - hudOverlay.KeyCounter.AddTrigger(new KeyCounterKeyboardTrigger(Key.Space)); + hudOverlay.KeyCounter.Add(new KeyCounterKeyboardTrigger(Key.Space)); action?.Invoke(hudOverlay); diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index ea9dc3fb01..ce3ee8cc7a 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -162,7 +162,7 @@ namespace osu.Game.Rulesets.UI KeyBindingContainer.Add(receptor); keyCounter.SetReceptor(receptor); - keyCounter.AddTriggerRange(KeyBindingContainer.DefaultKeyBindings + keyCounter.AddRange(KeyBindingContainer.DefaultKeyBindings .Select(b => b.GetAction()) .Distinct() .OrderBy(action => action) diff --git a/osu.Game/Screens/Play/HUD/DefaultKeyCounterDisplay.cs b/osu.Game/Screens/Play/HUD/DefaultKeyCounterDisplay.cs index 8b910d56f6..14d7f56093 100644 --- a/osu.Game/Screens/Play/HUD/DefaultKeyCounterDisplay.cs +++ b/osu.Game/Screens/Play/HUD/DefaultKeyCounterDisplay.cs @@ -36,7 +36,7 @@ namespace osu.Game.Screens.Play.HUD Size = keyFlow.Size; } - public override void AddTrigger(InputTrigger trigger) + public override void Add(InputTrigger trigger) => keyFlow.Add(new DefaultKeyCounter(trigger) { FadeTime = key_fade_time, diff --git a/osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs b/osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs index 0e0f8a1190..49c0da6793 100644 --- a/osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs +++ b/osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs @@ -51,16 +51,14 @@ namespace osu.Game.Screens.Play.HUD } /// - /// Adds a new to this . + /// Add a to this display. /// - /// The the resulting will react to. - public abstract void AddTrigger(InputTrigger trigger); + public abstract void Add(InputTrigger trigger); /// - /// Adds a range of new s to this . + /// Add a range of to this display. /// - /// The s the resulting s will react to. - public void AddTriggerRange(IEnumerable triggers) => triggers.ForEach(AddTrigger); + public void AddRange(IEnumerable triggers) => triggers.ForEach(Add); [BackgroundDependencyLoader] private void load(OsuConfigManager config) From edc63146344a81f6ff3c993d11690e99579dbd6b Mon Sep 17 00:00:00 2001 From: Terochi Date: Wed, 15 Mar 2023 10:49:59 +0100 Subject: [PATCH 240/476] Drank some coffee and figured out the fix --- osu.Game/Screens/Play/PauseOverlay.cs | 1 - osu.Game/Screens/Play/Player.cs | 5 ----- osu.Game/Skinning/PoolableSkinnableSample.cs | 2 ++ osu.Game/Skinning/SkinReloadableDrawable.cs | 20 +++++++++++--------- osu.Game/Skinning/SkinnableSound.cs | 7 ------- 5 files changed, 13 insertions(+), 22 deletions(-) diff --git a/osu.Game/Screens/Play/PauseOverlay.cs b/osu.Game/Screens/Play/PauseOverlay.cs index 452e170cc6..db42998c45 100644 --- a/osu.Game/Screens/Play/PauseOverlay.cs +++ b/osu.Game/Screens/Play/PauseOverlay.cs @@ -28,7 +28,6 @@ namespace osu.Game.Screens.Play private SkinnableSound pauseLoop; - public void FlushPendingSkinChanges() => pauseLoop.FlushPendingSkinChanges(); protected override Action BackAction => () => InternalButtons.First().TriggerClick(); [BackgroundDependencyLoader] diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index d4180068d3..bc453d2151 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -316,11 +316,6 @@ namespace osu.Game.Screens.Play // we may want to limit this in the future to disallow rulesets from outright replacing elements the user expects to be there. failAnimationLayer.Add(createOverlayComponents(Beatmap.Value)); - rulesetSkinProvider.SourceChanged += () => - { - PauseOverlay.FlushPendingSkinChanges(); - }; - if (!DrawableRuleset.AllowGameplayOverlays) { HUDOverlay.ShowHud.Value = false; diff --git a/osu.Game/Skinning/PoolableSkinnableSample.cs b/osu.Game/Skinning/PoolableSkinnableSample.cs index 0158c47ea3..c8b0955db2 100644 --- a/osu.Game/Skinning/PoolableSkinnableSample.cs +++ b/osu.Game/Skinning/PoolableSkinnableSample.cs @@ -132,6 +132,8 @@ namespace osu.Game.Skinning if (Sample == null) return; + FlushPendingSkinChanges(); + activeChannel = Sample.GetChannel(); activeChannel.Looping = Looping; activeChannel.Play(); diff --git a/osu.Game/Skinning/SkinReloadableDrawable.cs b/osu.Game/Skinning/SkinReloadableDrawable.cs index ff51eb6dce..b6b9650048 100644 --- a/osu.Game/Skinning/SkinReloadableDrawable.cs +++ b/osu.Game/Skinning/SkinReloadableDrawable.cs @@ -27,6 +27,15 @@ namespace osu.Game.Skinning /// protected ISkinSource CurrentSkin { get; private set; } = null!; + protected void FlushPendingSkinChanges() + { + if (pendingSkinChange == null) + return; + + pendingSkinChange.RunTask(); + pendingSkinChange = null; + } + [BackgroundDependencyLoader] private void load(ISkinSource source) { @@ -42,15 +51,6 @@ namespace osu.Game.Skinning pendingSkinChange = Scheduler.Add(skinChanged); } - public virtual void FlushPendingSkinChanges() - { - if (pendingSkinChange == null) - return; - - pendingSkinChange.RunTask(); - pendingSkinChange = null; - } - protected override void LoadAsyncComplete() { base.LoadAsyncComplete(); @@ -61,6 +61,8 @@ namespace osu.Game.Skinning { SkinChanged(CurrentSkin); OnSkinChanged?.Invoke(); + + pendingSkinChange = null; } /// diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index f80b1f52fa..59b3799e0a 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -110,13 +110,6 @@ namespace osu.Game.Skinning } } - public override void FlushPendingSkinChanges() - { - base.FlushPendingSkinChanges(); - - samplesContainer.ForEach(c => c.FlushPendingSkinChanges()); - } - /// /// Plays the samples. /// From 89b42ddd98f239ca605bf68311b4bc75a42babdc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Mar 2023 15:02:38 +0900 Subject: [PATCH 241/476] Don't localise beatmap count string for now --- osu.Game/Localisation/SongSelectStrings.cs | 5 ----- osu.Game/Screens/Select/SongSelect.cs | 11 +++++++++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/osu.Game/Localisation/SongSelectStrings.cs b/osu.Game/Localisation/SongSelectStrings.cs index 046aec6bcf..e1ac328420 100644 --- a/osu.Game/Localisation/SongSelectStrings.cs +++ b/osu.Game/Localisation/SongSelectStrings.cs @@ -19,11 +19,6 @@ namespace osu.Game.Localisation /// public static LocalisableString LocallyModifiedTooltip => new TranslatableString(getKey(@"locally_modified_tooltip"), @"Has been locally modified"); - /// - /// "{0} beatmaps displayed" - /// - public static LocalisableString BeatmapsDisplayed(int arg0) => new TranslatableString(getKey(@"beatmaps_displayed"), @"{0:#,0} beatmaps displayed", arg0); - private static string getKey(string key) => $@"{prefix}:{key}"; } } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 9f8c3f1a2c..a3521337ee 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -40,7 +40,6 @@ using osu.Game.Skinning; using osuTK; using osuTK.Graphics; using osuTK.Input; -using osu.Game.Localisation; namespace osu.Game.Screens.Select { @@ -163,7 +162,15 @@ namespace osu.Game.Screens.Select BleedBottom = Footer.HEIGHT, SelectionChanged = updateSelectedBeatmap, BeatmapSetsChanged = carouselBeatmapsLoaded, - FilterApplied = () => FilterControl.InformationalText = SongSelectStrings.BeatmapsDisplayed(Carousel.CountDisplayed), + FilterApplied = () => + { + FilterControl.InformationalText = + Carousel.CountDisplayed == 1 + // Intentionally not localised until we have proper support for this (see https://github.com/ppy/osu-framework/pull/4918 + // but also in this case we want support for formatting a number within a string). + ? $"{Carousel.CountDisplayed:#,0} beatmap displayed" + : $"{Carousel.CountDisplayed:#,0} beatmaps displayed"; + }, GetRecommendedBeatmap = s => recommender?.GetRecommendedBeatmap(s), }, c => carouselContainer.Child = c); From 5378cdff20845d4bfa6cea7cea9f02cdf09b8345 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Mar 2023 15:10:35 +0900 Subject: [PATCH 242/476] Apply NRT to `TestSceneSkinnableSound` --- .../Gameplay/TestSceneSkinnableSound.cs | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs index 5c69062e67..40bfe975bc 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.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 disable - using System; using System.Collections.Generic; using System.Linq; @@ -22,8 +20,8 @@ namespace osu.Game.Tests.Visual.Gameplay { public partial class TestSceneSkinnableSound : OsuTestScene { - private TestSkinSourceContainer skinSource; - private PausableSkinnableSound skinnableSound; + private TestSkinSourceContainer skinSource = null!; + private PausableSkinnableSound skinnableSound = null!; [SetUpSteps] public void SetUpSteps() @@ -102,7 +100,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestSkinChangeDoesntPlayOnPause() { - DrawableSample sample = null; + DrawableSample? sample = null; AddStep("start sample", () => { skinnableSound.Play(); @@ -118,7 +116,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("retrieve and ensure current sample is different", () => { - DrawableSample oldSample = sample; + DrawableSample? oldSample = sample; sample = skinnableSound.ChildrenOfType().Single(); return sample != oldSample; }); @@ -134,20 +132,27 @@ namespace osu.Game.Tests.Visual.Gameplay private partial class TestSkinSourceContainer : Container, ISkinSource, ISamplePlaybackDisabler { [Resolved] - private ISkinSource source { get; set; } + private ISkinSource source { get; set; } = null!; - public event Action SourceChanged; + public event Action? SourceChanged; public Bindable SamplePlaybackDisabled { get; } = new Bindable(); IBindable ISamplePlaybackDisabler.SamplePlaybackDisabled => SamplePlaybackDisabled; - public Drawable GetDrawableComponent(ISkinComponentLookup lookup) => source?.GetDrawableComponent(lookup); - public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => source?.GetTexture(componentName, wrapModeS, wrapModeT); - public ISample GetSample(ISampleInfo sampleInfo) => source?.GetSample(sampleInfo); - public IBindable GetConfig(TLookup lookup) => source?.GetConfig(lookup); - public ISkin FindProvider(Func lookupFunction) => lookupFunction(this) ? this : source?.FindProvider(lookupFunction); - public IEnumerable AllSources => new[] { this }.Concat(source?.AllSources ?? Enumerable.Empty()); + public Drawable? GetDrawableComponent(ISkinComponentLookup lookup) => source.GetDrawableComponent(lookup); + public Texture? GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => source.GetTexture(componentName, wrapModeS, wrapModeT); + public ISample? GetSample(ISampleInfo sampleInfo) => source.GetSample(sampleInfo); + + public IBindable? GetConfig(TLookup lookup) + where TLookup : notnull + where TValue : notnull + { + return source.GetConfig(lookup); + } + + public ISkin? FindProvider(Func lookupFunction) => lookupFunction(this) ? this : source.FindProvider(lookupFunction); + public IEnumerable AllSources => new[] { this }.Concat(source.AllSources); public void TriggerSourceChanged() { From 297e7d654239ecc02b6516cf80fafb2a34c23afa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Mar 2023 15:33:30 +0900 Subject: [PATCH 243/476] Fix `Flush` call being run too late in `PoolableSkinnableSample` --- osu.Game/Skinning/PoolableSkinnableSample.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/PoolableSkinnableSample.cs b/osu.Game/Skinning/PoolableSkinnableSample.cs index c8b0955db2..361c8688e7 100644 --- a/osu.Game/Skinning/PoolableSkinnableSample.cs +++ b/osu.Game/Skinning/PoolableSkinnableSample.cs @@ -129,11 +129,11 @@ namespace osu.Game.Skinning /// public void Play() { + FlushPendingSkinChanges(); + if (Sample == null) return; - FlushPendingSkinChanges(); - activeChannel = Sample.GetChannel(); activeChannel.Looping = Looping; activeChannel.Play(); From 159c8833c7e2506feed18ba05d1ec51df3a54860 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Mar 2023 15:41:05 +0900 Subject: [PATCH 244/476] Add test coverage of `SkinnableSound` not updating in time when not present --- .../Gameplay/TestSceneSkinnableSound.cs | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs index 40bfe975bc..cc82ffed2b 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs @@ -23,6 +23,8 @@ namespace osu.Game.Tests.Visual.Gameplay private TestSkinSourceContainer skinSource = null!; private PausableSkinnableSound skinnableSound = null!; + private const string sample_lookup = "Gameplay/normal-sliderslide"; + [SetUpSteps] public void SetUpSteps() { @@ -34,7 +36,7 @@ namespace osu.Game.Tests.Visual.Gameplay }; // has to be added after the hierarchy above else the `ISkinSource` dependency won't be cached. - skinSource.Add(skinnableSound = new PausableSkinnableSound(new SampleInfo("Gameplay/normal-sliderslide"))); + skinSource.Add(skinnableSound = new PausableSkinnableSound(new SampleInfo(sample_lookup))); }); } @@ -97,6 +99,27 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("sample not playing", () => !skinnableSound.IsPlaying); } + [Test] + public void TestSampleUpdatedBeforePlaybackWhenNotPresent() + { + AddStep("make sample non-present", () => skinnableSound.Hide()); + AddUntilStep("ensure not present", () => skinnableSound.IsPresent, () => Is.False); + + AddUntilStep("ensure sample loaded", () => skinnableSound.ChildrenOfType().Single().Name, () => Is.EqualTo(sample_lookup)); + + AddStep("Change source", () => + { + skinSource.OverridingSample = new SampleVirtual("new skin"); + skinSource.TriggerSourceChanged(); + }); + + // Samples are nulled on source change immediately + AddUntilStep("wait for sample null", () => skinnableSound.ChildrenOfType().Count(), () => Is.Zero); + + AddStep("start sample", () => skinnableSound.Play()); + AddUntilStep("sample updated", () => skinnableSound.ChildrenOfType().Single().Name, () => Is.EqualTo("new skin")); + } + [Test] public void TestSkinChangeDoesntPlayOnPause() { @@ -138,11 +161,13 @@ namespace osu.Game.Tests.Visual.Gameplay public Bindable SamplePlaybackDisabled { get; } = new Bindable(); + public ISample? OverridingSample; + IBindable ISamplePlaybackDisabler.SamplePlaybackDisabled => SamplePlaybackDisabled; public Drawable? GetDrawableComponent(ISkinComponentLookup lookup) => source.GetDrawableComponent(lookup); public Texture? GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => source.GetTexture(componentName, wrapModeS, wrapModeT); - public ISample? GetSample(ISampleInfo sampleInfo) => source.GetSample(sampleInfo); + public ISample? GetSample(ISampleInfo sampleInfo) => OverridingSample ?? source.GetSample(sampleInfo); public IBindable? GetConfig(TLookup lookup) where TLookup : notnull From 8e6a4559e3ae8c9892866cf9cf8d4e8d1b72afd0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Mar 2023 15:58:42 +0900 Subject: [PATCH 245/476] Add xmldoc for new method and reorder methods in `SkinReloadableDrawable` --- osu.Game/Skinning/SkinReloadableDrawable.cs | 47 +++++++++++++-------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/osu.Game/Skinning/SkinReloadableDrawable.cs b/osu.Game/Skinning/SkinReloadableDrawable.cs index b6b9650048..c7b33dc539 100644 --- a/osu.Game/Skinning/SkinReloadableDrawable.cs +++ b/osu.Game/Skinning/SkinReloadableDrawable.cs @@ -27,6 +27,30 @@ namespace osu.Game.Skinning /// protected ISkinSource CurrentSkin { get; private set; } = null!; + [BackgroundDependencyLoader] + private void load(ISkinSource source) + { + CurrentSkin = source; + CurrentSkin.SourceChanged += onChange; + } + + protected override void LoadAsyncComplete() + { + base.LoadAsyncComplete(); + skinChanged(); + } + + /// + /// Force any pending calls to be performed immediately. + /// + /// + /// When a skin change occurs, the handling provided by this class is scheduled. + /// In some cases, such a sample playback, this can result in the sample being played + /// just before it is updated to a potentially different sample. + /// + /// Calling this method will ensure any pending update operations are run immediately. + /// It is recommended to call this before consuming the result of skin changes for anything non-drawable. + /// protected void FlushPendingSkinChanges() { if (pendingSkinChange == null) @@ -36,11 +60,12 @@ namespace osu.Game.Skinning pendingSkinChange = null; } - [BackgroundDependencyLoader] - private void load(ISkinSource source) + /// + /// Called when a change is made to the skin. + /// + /// The new skin. + protected virtual void SkinChanged(ISkinSource skin) { - CurrentSkin = source; - CurrentSkin.SourceChanged += onChange; } private void onChange() @@ -51,12 +76,6 @@ namespace osu.Game.Skinning pendingSkinChange = Scheduler.Add(skinChanged); } - protected override void LoadAsyncComplete() - { - base.LoadAsyncComplete(); - skinChanged(); - } - private void skinChanged() { SkinChanged(CurrentSkin); @@ -65,14 +84,6 @@ namespace osu.Game.Skinning pendingSkinChange = null; } - /// - /// Called when a change is made to the skin. - /// - /// The new skin. - protected virtual void SkinChanged(ISkinSource skin) - { - } - protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); From cd102da3af06d06486ea20daa319f143ab13632c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Mar 2023 16:34:31 +0900 Subject: [PATCH 246/476] Move matches string inside text box --- osu.Game/Screens/Select/FilterControl.cs | 50 +++++++++++++++--------- osu.Game/Screens/Select/SongSelect.cs | 4 +- 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index b5469abffe..1bcc042f72 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -38,8 +38,8 @@ namespace osu.Game.Screens.Select public LocalisableString InformationalText { - get => filterText.Text; - set => filterText.Text = value; + get => searchTextBox.FilterText.Text; + set => searchTextBox.FilterText.Text = value; } private OsuTabControl sortTabs; @@ -48,12 +48,10 @@ namespace osu.Game.Screens.Select private Bindable groupMode; - private SeekLimitedSearchTextBox searchTextBox; + private FilterControlTextBox searchTextBox; private CollectionDropdown collectionDropdown; - private OsuSpriteText filterText; - public FilterCriteria CreateCriteria() { string query = searchTextBox.Text; @@ -111,22 +109,9 @@ namespace osu.Game.Screens.Select Spacing = new Vector2(0, 5), Children = new Drawable[] { - searchTextBox = new SeekLimitedSearchTextBox { RelativeSizeAxes = Axes.X }, - new Container + searchTextBox = new FilterControlTextBox { RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - AutoSizeDuration = 200, - AutoSizeEasing = Easing.OutQuint, - Children = new Drawable[] - { - filterText = new OsuSpriteText - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Font = OsuFont.Default.With(size: 12), - }, - } }, new Box { @@ -262,5 +247,32 @@ namespace osu.Game.Screens.Select protected override bool OnClick(ClickEvent e) => true; protected override bool OnHover(HoverEvent e) => true; + + private partial class FilterControlTextBox : SeekLimitedSearchTextBox + { + private const float filter_text_size = 12; + + public OsuSpriteText FilterText; + + public FilterControlTextBox() + { + Height += filter_text_size; + TextContainer.Margin = new MarginPadding { Bottom = filter_text_size }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + TextContainer.Add(FilterText = new OsuSpriteText + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.TopLeft, + Depth = float.MinValue, + Font = OsuFont.Default.With(size: filter_text_size, weight: FontWeight.SemiBold), + Margin = new MarginPadding { Top = 2, Left = 2 }, + Colour = colours.Yellow + }); + } + } } } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index a3521337ee..722119f82e 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -168,8 +168,8 @@ namespace osu.Game.Screens.Select Carousel.CountDisplayed == 1 // Intentionally not localised until we have proper support for this (see https://github.com/ppy/osu-framework/pull/4918 // but also in this case we want support for formatting a number within a string). - ? $"{Carousel.CountDisplayed:#,0} beatmap displayed" - : $"{Carousel.CountDisplayed:#,0} beatmaps displayed"; + ? $"{Carousel.CountDisplayed:#,0} matching beatmap" + : $"{Carousel.CountDisplayed:#,0} matching beatmaps"; }, GetRecommendedBeatmap = s => recommender?.GetRecommendedBeatmap(s), }, c => carouselContainer.Child = c); From a81408ca0626edc77c36060d0bf574dd1a47cdd5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Mar 2023 17:12:46 +0900 Subject: [PATCH 247/476] Add failing test coverage showing that replay will fail on exiting gameplay --- .../Visual/Gameplay/TestSceneReplayPlayer.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayPlayer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayPlayer.cs index 3e415af86e..ae10207de0 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayPlayer.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayPlayer.cs @@ -8,6 +8,7 @@ using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; +using osu.Game.Scoring; using osu.Game.Screens.Play; using osu.Game.Tests.Beatmaps; using osuTK.Input; @@ -45,6 +46,18 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("Time still stopped", () => lastTime == Player.GameplayClockContainer.CurrentTime); } + [Test] + public void TestDoesNotFailOnExit() + { + loadPlayerWithBeatmap(); + + AddUntilStep("wait for first hit", () => Player.ScoreProcessor.TotalScore.Value > 0); + AddAssert("ensure rank is not fail", () => Player.ScoreProcessor.Rank.Value, () => Is.Not.EqualTo(ScoreRank.F)); + AddStep("exit player", () => Player.Exit()); + AddUntilStep("wait for exit", () => Player.Parent == null); + AddAssert("ensure rank is not fail", () => Player.ScoreProcessor.Rank.Value, () => Is.Not.EqualTo(ScoreRank.F)); + } + [Test] public void TestPauseViaSpaceWithSkip() { From 3b62f87b64f966bd277323d9afc7e256e1dea5e0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Mar 2023 17:14:20 +0900 Subject: [PATCH 248/476] Ensure `Player` does not fail a score on exit if a replay is currently loaded --- osu.Game/Screens/Play/Player.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index bc453d2151..8d0da8c44f 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -1111,7 +1111,7 @@ namespace osu.Game.Screens.Play GameplayState.HasQuit = true; // if arriving here and the results screen preparation task hasn't run, it's safe to say the user has not completed the beatmap. - if (prepareScoreForDisplayTask == null) + if (prepareScoreForDisplayTask == null && DrawableRuleset.ReplayScore == null) ScoreProcessor.FailScore(Score.ScoreInfo); } From cb9b14b30fd8c3620d5b45aadb9152a9b29f3617 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Mar 2023 19:48:36 +0900 Subject: [PATCH 249/476] Revert "Merge pull request #22741 from cdwcgt/do-not-fetch-deletePending" This reverts commit 15c44a281725189671c29fb569949f7c8be66cbe, reversing changes made to de2ab05e785dfaf2fb108c68c4c7424c8db417a5. --- osu.Game/Scoring/Legacy/DatabasedLegacyScoreDecoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Scoring/Legacy/DatabasedLegacyScoreDecoder.cs b/osu.Game/Scoring/Legacy/DatabasedLegacyScoreDecoder.cs index 8ee2cc6241..8908163646 100644 --- a/osu.Game/Scoring/Legacy/DatabasedLegacyScoreDecoder.cs +++ b/osu.Game/Scoring/Legacy/DatabasedLegacyScoreDecoder.cs @@ -24,6 +24,6 @@ namespace osu.Game.Scoring.Legacy } protected override Ruleset GetRuleset(int rulesetId) => rulesets.GetRuleset(rulesetId)?.CreateInstance(); - protected override WorkingBeatmap GetBeatmap(string md5Hash) => beatmaps.GetWorkingBeatmap(beatmaps.QueryBeatmap(b => b.MD5Hash == md5Hash && !b.BeatmapSet.DeletePending)); + protected override WorkingBeatmap GetBeatmap(string md5Hash) => beatmaps.GetWorkingBeatmap(beatmaps.QueryBeatmap(b => b.MD5Hash == md5Hash)); } } From 678e8ed736b4b89617988300888997fab85d4373 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 16 Mar 2023 14:06:35 +0300 Subject: [PATCH 250/476] Update UBO usages inline with framework changes --- osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs | 5 +++-- .../Visual/Background/TestSceneTriangleBorderShader.cs | 8 ++++---- osu.Game/Graphics/Backgrounds/Triangles.cs | 2 +- osu.Game/Graphics/Backgrounds/TrianglesV2.cs | 2 +- osu.Game/Graphics/Sprites/LogoAnimation.cs | 8 ++++---- osu.Game/Rulesets/Mods/ModFlashlight.cs | 2 +- 6 files changed, 14 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs b/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs index a824f202ec..9d64c354e2 100644 --- a/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs +++ b/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs @@ -252,13 +252,14 @@ namespace osu.Game.Rulesets.Osu.Skinning renderer.SetBlend(BlendingParameters.Additive); renderer.PushLocalMatrix(DrawInfo.Matrix); - TextureShader.Bind(); + BindTextureShader(renderer); + texture.Bind(); for (int i = 0; i < points.Count; i++) drawPointQuad(points[i], textureRect, i + firstVisiblePointIndex); - TextureShader.Unbind(); + UnbindTextureShader(renderer); renderer.PopLocalMatrix(); } diff --git a/osu.Game.Tests/Visual/Background/TestSceneTriangleBorderShader.cs b/osu.Game.Tests/Visual/Background/TestSceneTriangleBorderShader.cs index 07427c242f..711d9ab5ea 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneTriangleBorderShader.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneTriangleBorderShader.cs @@ -100,8 +100,10 @@ namespace osu.Game.Tests.Visual.Background private IUniformBuffer? borderDataBuffer; - public override void Draw(IRenderer renderer) + protected override void BindUniformResources(IShader shader, IRenderer renderer) { + base.BindUniformResources(shader, renderer); + borderDataBuffer ??= renderer.CreateUniformBuffer(); borderDataBuffer.Data = borderDataBuffer.Data with { @@ -109,9 +111,7 @@ namespace osu.Game.Tests.Visual.Background TexelSize = texelSize }; - TextureShader.BindUniformBlock("m_BorderData", borderDataBuffer); - - base.Draw(renderer); + shader.BindUniformBlock("m_BorderData", borderDataBuffer); } protected override bool CanDrawOpaqueInterior => false; diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index 28a715ca0b..0ee42c69d5 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -306,7 +306,7 @@ namespace osu.Game.Graphics.Backgrounds }; shader.Bind(); - shader.BindUniformBlock("m_BorderData", borderDataBuffer); + shader.BindUniformBlock(@"m_BorderData", borderDataBuffer); foreach (TriangleParticle particle in parts) { diff --git a/osu.Game/Graphics/Backgrounds/TrianglesV2.cs b/osu.Game/Graphics/Backgrounds/TrianglesV2.cs index 6a34fefa3a..750e96440d 100644 --- a/osu.Game/Graphics/Backgrounds/TrianglesV2.cs +++ b/osu.Game/Graphics/Backgrounds/TrianglesV2.cs @@ -249,7 +249,7 @@ namespace osu.Game.Graphics.Backgrounds }; shader.Bind(); - shader.BindUniformBlock("m_BorderData", borderDataBuffer); + shader.BindUniformBlock(@"m_BorderData", borderDataBuffer); Vector2 relativeSize = Vector2.Divide(triangleSize, size); diff --git a/osu.Game/Graphics/Sprites/LogoAnimation.cs b/osu.Game/Graphics/Sprites/LogoAnimation.cs index 220f57e9fa..eb7613a1b2 100644 --- a/osu.Game/Graphics/Sprites/LogoAnimation.cs +++ b/osu.Game/Graphics/Sprites/LogoAnimation.cs @@ -59,14 +59,14 @@ namespace osu.Game.Graphics.Sprites private IUniformBuffer animationDataBuffer; - protected override void Blit(IRenderer renderer) + protected override void BindUniformResources(IShader shader, IRenderer renderer) { + base.BindUniformResources(shader, renderer); + animationDataBuffer ??= renderer.CreateUniformBuffer(); animationDataBuffer.Data = animationDataBuffer.Data with { Progress = progress }; - TextureShader.BindUniformBlock("m_AnimationData", animationDataBuffer); - - base.Blit(renderer); + shader.BindUniformBlock(@"m_AnimationData", animationDataBuffer); } protected override bool CanDrawOpaqueInterior => false; diff --git a/osu.Game/Rulesets/Mods/ModFlashlight.cs b/osu.Game/Rulesets/Mods/ModFlashlight.cs index a7889cb98d..f8c3a730f2 100644 --- a/osu.Game/Rulesets/Mods/ModFlashlight.cs +++ b/osu.Game/Rulesets/Mods/ModFlashlight.cs @@ -273,7 +273,7 @@ namespace osu.Game.Rulesets.Mods }; shader.Bind(); - shader.BindUniformBlock("m_FlashlightParameters", flashlightParametersBuffer); + shader.BindUniformBlock(@"m_FlashlightParameters", flashlightParametersBuffer); renderer.DrawQuad(renderer.WhitePixel, screenSpaceDrawQuad, DrawColourInfo.Colour, vertexAction: addAction); From 1c3b60b9e6c6d31a2d955fcf0dafe41b20774abf Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 16 Mar 2023 23:56:41 +0900 Subject: [PATCH 251/476] Use custom vertex shader for logo animation --- osu.Game/Graphics/Sprites/LogoAnimation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Sprites/LogoAnimation.cs b/osu.Game/Graphics/Sprites/LogoAnimation.cs index 220f57e9fa..619a28ecad 100644 --- a/osu.Game/Graphics/Sprites/LogoAnimation.cs +++ b/osu.Game/Graphics/Sprites/LogoAnimation.cs @@ -18,7 +18,7 @@ namespace osu.Game.Graphics.Sprites [BackgroundDependencyLoader] private void load(ShaderManager shaders) { - TextureShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, @"LogoAnimation"); + TextureShader = shaders.Load(@"LogoAnimation", @"LogoAnimation"); } private float animationProgress; From 8bdb89d05dbc779fda0a6b9f924b6e07eca2b63c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Mar 2023 19:12:43 +0900 Subject: [PATCH 252/476] Update resources --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index c08dc9ed8f..ce9cf37ec3 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + From bcd24873d6361992339ca2ef5e1fdd6053419c33 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 17 Mar 2023 20:37:05 +0900 Subject: [PATCH 253/476] Use custom vertex type --- osu.Game/Graphics/Sprites/LogoAnimation.cs | 37 ++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/osu.Game/Graphics/Sprites/LogoAnimation.cs b/osu.Game/Graphics/Sprites/LogoAnimation.cs index 619a28ecad..3b8b0bfc88 100644 --- a/osu.Game/Graphics/Sprites/LogoAnimation.cs +++ b/osu.Game/Graphics/Sprites/LogoAnimation.cs @@ -3,13 +3,18 @@ #nullable disable +using System; using System.Runtime.InteropServices; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Rendering; +using osu.Framework.Graphics.Rendering.Vertices; using osu.Framework.Graphics.Shaders; using osu.Framework.Graphics.Shaders.Types; using osu.Framework.Graphics.Sprites; +using osuTK; +using osuTK.Graphics; +using osuTK.Graphics.ES30; namespace osu.Game.Graphics.Sprites { @@ -43,11 +48,22 @@ namespace osu.Game.Graphics.Sprites { private LogoAnimation source => (LogoAnimation)Source; + private readonly Action addVertexAction; + private float progress; public LogoAnimationDrawNode(LogoAnimation source) : base(source) { + addVertexAction = v => + { + animationVertexBatch!.Add(new LogoAnimationVertex + { + Position = v.Position, + Colour = v.Colour, + TexturePosition = v.TexturePosition, + }); + }; } public override void ApplyState() @@ -58,10 +74,13 @@ namespace osu.Game.Graphics.Sprites } private IUniformBuffer animationDataBuffer; + private IVertexBatch animationVertexBatch; protected override void Blit(IRenderer renderer) { animationDataBuffer ??= renderer.CreateUniformBuffer(); + animationVertexBatch ??= renderer.CreateQuadBatch(1, 2); + animationDataBuffer.Data = animationDataBuffer.Data with { Progress = progress }; TextureShader.BindUniformBlock("m_AnimationData", animationDataBuffer); @@ -83,6 +102,24 @@ namespace osu.Game.Graphics.Sprites public UniformFloat Progress; private readonly UniformPadding12 pad1; } + + [StructLayout(LayoutKind.Sequential)] + private struct LogoAnimationVertex : IEquatable, IVertex + { + [VertexMember(2, VertexAttribPointerType.Float)] + public Vector2 Position; + + [VertexMember(4, VertexAttribPointerType.Float)] + public Color4 Colour; + + [VertexMember(2, VertexAttribPointerType.Float)] + public Vector2 TexturePosition; + + public readonly bool Equals(LogoAnimationVertex other) => + Position.Equals(other.Position) + && TexturePosition.Equals(other.TexturePosition) + && Colour.Equals(other.Colour); + } } } } From c08513d59049e78c8151a2b2fbaaa4df1921ccef Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 17 Mar 2023 20:47:11 +0900 Subject: [PATCH 254/476] Actually use custom vertex action --- osu.Game/Graphics/Sprites/LogoAnimation.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Sprites/LogoAnimation.cs b/osu.Game/Graphics/Sprites/LogoAnimation.cs index 3b8b0bfc88..fbf8717d3c 100644 --- a/osu.Game/Graphics/Sprites/LogoAnimation.cs +++ b/osu.Game/Graphics/Sprites/LogoAnimation.cs @@ -78,6 +78,9 @@ namespace osu.Game.Graphics.Sprites protected override void Blit(IRenderer renderer) { + if (DrawRectangle.Width == 0 || DrawRectangle.Height == 0) + return; + animationDataBuffer ??= renderer.CreateUniformBuffer(); animationVertexBatch ??= renderer.CreateQuadBatch(1, 2); @@ -85,7 +88,13 @@ namespace osu.Game.Graphics.Sprites TextureShader.BindUniformBlock("m_AnimationData", animationDataBuffer); - base.Blit(renderer); + renderer.DrawQuad( + Texture, + ScreenSpaceDrawQuad, + DrawColourInfo.Colour, + inflationPercentage: new Vector2(InflationAmount.X / DrawRectangle.Width, InflationAmount.Y / DrawRectangle.Height), + textureCoords: TextureCoords, + vertexAction: addVertexAction); } protected override bool CanDrawOpaqueInterior => false; From 970df5d88a09d6d55797739164ce20cb08b9aed4 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sun, 12 Mar 2023 18:46:34 -0700 Subject: [PATCH 255/476] Update profile kudosu section in line with web --- .../Profile/Sections/Kudosu/KudosuInfo.cs | 24 +------------------ 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs index d2f01ef9f7..7b26640e50 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs @@ -2,15 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Bindables; -using osuTK; 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.Framework.Allocation; using osu.Framework.Extensions.LocalisationExtensions; using osu.Game.Resources.Localisation.Web; using osu.Framework.Localisation; @@ -52,7 +49,6 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu { private readonly OsuSpriteText valueText; protected readonly LinkFlowContainer DescriptionText; - private readonly Box lineBackground; public new int Count { @@ -63,25 +59,14 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; - Padding = new MarginPadding { Top = 10, Bottom = 20 }; + Padding = new MarginPadding { Bottom = 20 }; Child = new FillFlowContainer { AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 5), Children = new Drawable[] { - new CircularContainer - { - Masking = true, - RelativeSizeAxes = Axes.X, - Height = 2, - Child = lineBackground = new Box - { - RelativeSizeAxes = Axes.Both, - } - }, new OsuSpriteText { Text = header, @@ -91,7 +76,6 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu { Text = "0", Font = OsuFont.GetFont(size: 40, weight: FontWeight.Light), - UseFullGlyphHeight = false, }, DescriptionText = new LinkFlowContainer(t => t.Font = t.Font.With(size: 14)) { @@ -101,12 +85,6 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu } }; } - - [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) - { - lineBackground.Colour = colourProvider.Highlight1; - } } } } From 48b6e214af56fab60571964a91474c5f8d3ce296 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Sun, 19 Mar 2023 17:12:12 +0100 Subject: [PATCH 256/476] Fix URL handling on macOS --- osu.Desktop/OsuGameDesktop.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index b3f6370ccb..3fe251cb00 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -139,7 +139,17 @@ namespace osu.Desktop desktopWindow.CursorState |= CursorState.Hidden; desktopWindow.Title = Name; - desktopWindow.DragDrop += f => fileDrop(new[] { f }); + desktopWindow.DragDrop += f => + { + // on macOS, URL associations are handled via SDL_DROPFILE events. + if (f.StartsWith(OSU_PROTOCOL, StringComparison.Ordinal)) + { + HandleLink(f); + return; + } + + fileDrop(new[] { f }); + }; } protected override BatteryInfo CreateBatteryInfo() => new SDL2BatteryInfo(); From b254dbd7ca409e502b9d041f9c0c623ea3afd841 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Sun, 19 Mar 2023 17:37:49 +0100 Subject: [PATCH 257/476] Remove arbitrary extension limitation from drag and drop imports `OsuGameBase` already properly handles multiple extensions in the same import. --- osu.Desktop/OsuGameDesktop.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index b3f6370ccb..6b5c5b809b 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -151,10 +151,6 @@ namespace osu.Desktop { lock (importableFiles) { - string firstExtension = Path.GetExtension(filePaths.First()); - - if (filePaths.Any(f => Path.GetExtension(f) != firstExtension)) return; - importableFiles.AddRange(filePaths); Logger.Log($"Adding {filePaths.Length} files for import"); From 11f52d5bf4440d5012dbaff2167e921d7f2fd49c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Mar 2023 14:01:35 +0900 Subject: [PATCH 258/476] Remove unused using statement --- osu.Desktop/OsuGameDesktop.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 6b5c5b809b..f094785a5b 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.Linq; using System.Reflection; using System.Runtime.Versioning; using System.Threading.Tasks; From 8557589a3542c7bc26969c9c8cbdac5f3a418ec5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Mar 2023 15:28:13 +0900 Subject: [PATCH 259/476] Update resources --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index ce9cf37ec3..54d6533713 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + From 13a32b5246442bed162b1b4cefc873aa48750c78 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Mar 2023 15:36:27 +0900 Subject: [PATCH 260/476] Move lock-in variable to `const` and document better --- osu.Game.Rulesets.Osu/UI/OsuTouchInputMapper.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/OsuTouchInputMapper.cs b/osu.Game.Rulesets.Osu/UI/OsuTouchInputMapper.cs index 8cec65f515..4270a4df58 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuTouchInputMapper.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuTouchInputMapper.cs @@ -22,6 +22,13 @@ namespace osu.Game.Rulesets.Osu.UI /// private readonly List trackedTouches = new List(); + /// + /// The distance (in local pixels) that a touch must move before being considered a permanent tracking touch. + /// After this distance is covered, any extra touches on the screen will be considered as button inputs, unless + /// a new touch directly interacts with a hit circle. + /// + private const float distance_before_position_tracking_lock_in = 200; + private TrackedTouch? positionTrackingTouch; private readonly OsuInputManager osuInputManager; @@ -98,7 +105,7 @@ namespace osu.Game.Rulesets.Osu.UI } // ..or if the current position tracking touch was not a direct touch (and didn't travel across the screen too far). - if (!positionTrackingTouch.DirectTouch && positionTrackingTouch.DistanceTravelled < 200) + if (!positionTrackingTouch.DirectTouch && positionTrackingTouch.DistanceTravelled < distance_before_position_tracking_lock_in) { positionTrackingTouch = newTouch; return; @@ -160,9 +167,9 @@ namespace osu.Game.Rulesets.Osu.UI public readonly bool DirectTouch; /// - /// The total distance on screen travelled by this touch. + /// The total distance on screen travelled by this touch (in local pixels). /// - public double DistanceTravelled; + public float DistanceTravelled; public TrackedTouch(TouchSource source, OsuAction? action, bool directTouch) { From c056d5a6fb9f779e958b6eb1731ffa204a166600 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Mar 2023 15:36:58 +0900 Subject: [PATCH 261/476] Reduce distance requirement for lock-in behaviour --- osu.Game.Rulesets.Osu/UI/OsuTouchInputMapper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/UI/OsuTouchInputMapper.cs b/osu.Game.Rulesets.Osu/UI/OsuTouchInputMapper.cs index 4270a4df58..5277a1f7d6 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuTouchInputMapper.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuTouchInputMapper.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Osu.UI /// After this distance is covered, any extra touches on the screen will be considered as button inputs, unless /// a new touch directly interacts with a hit circle. /// - private const float distance_before_position_tracking_lock_in = 200; + private const float distance_before_position_tracking_lock_in = 100; private TrackedTouch? positionTrackingTouch; From fe91f85f6fe9afea0139408fc07c0b21d8dc93d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Mar 2023 18:16:33 +0100 Subject: [PATCH 262/476] Reuse colour constants in fallback path --- .../Skinning/Argon/ManiaArgonSkinTransformer.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs index ca9cd83a63..dbea9c7c88 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs @@ -294,17 +294,17 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon switch (columnIndex % total_colours) { - case 0: return new Color4(255, 197, 40, 255); + case 0: return colour_yellow; - case 1: return new Color4(252, 109, 1, 255); + case 1: return colour_orange; - case 2: return new Color4(213, 35, 90, 255); + case 2: return colour_pink; - case 3: return new Color4(203, 60, 236, 255); + case 3: return colour_purple; - case 4: return new Color4(72, 198, 255, 255); + case 4: return colour_cyan; - case 5: return new Color4(100, 192, 92, 255); + case 5: return colour_green; default: throw new ArgumentOutOfRangeException(); } From 695ee39b87644f4bf62a52c41b80805b7b772dff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Mar 2023 20:30:54 +0100 Subject: [PATCH 263/476] Privatise setter --- osu.Game/Screens/Select/FilterControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index 1bcc042f72..b44ca8ac04 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -252,7 +252,7 @@ namespace osu.Game.Screens.Select { private const float filter_text_size = 12; - public OsuSpriteText FilterText; + public OsuSpriteText FilterText { get; private set; } public FilterControlTextBox() { From ea8da69263ece422d6119dedc1471a8df4654a1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Mar 2023 20:49:47 +0100 Subject: [PATCH 264/476] Fix importing beatmaps not changing count of visible beatmaps Reproduction steps: 1. Go to song select 2. Open beatmap listing 3. Import a beatmap that would fit the current filter criteria 4. The count of visible beatmaps does not change Fixed by updating the count on `BeatmapSetsChanged` too. --- osu.Game/Screens/Select/SongSelect.cs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 722119f82e..c5e914b461 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -162,15 +162,7 @@ namespace osu.Game.Screens.Select BleedBottom = Footer.HEIGHT, SelectionChanged = updateSelectedBeatmap, BeatmapSetsChanged = carouselBeatmapsLoaded, - FilterApplied = () => - { - FilterControl.InformationalText = - Carousel.CountDisplayed == 1 - // Intentionally not localised until we have proper support for this (see https://github.com/ppy/osu-framework/pull/4918 - // but also in this case we want support for formatting a number within a string). - ? $"{Carousel.CountDisplayed:#,0} matching beatmap" - : $"{Carousel.CountDisplayed:#,0} matching beatmaps"; - }, + FilterApplied = updateVisibleBeatmapCount, GetRecommendedBeatmap = s => recommender?.GetRecommendedBeatmap(s), }, c => carouselContainer.Child = c); @@ -837,6 +829,7 @@ namespace osu.Game.Screens.Select private void carouselBeatmapsLoaded() { bindBindables(); + updateVisibleBeatmapCount(); Carousel.AllowSelection = true; @@ -866,6 +859,15 @@ namespace osu.Game.Screens.Select } } + private void updateVisibleBeatmapCount() + { + FilterControl.InformationalText = Carousel.CountDisplayed == 1 + // Intentionally not localised until we have proper support for this (see https://github.com/ppy/osu-framework/pull/4918 + // but also in this case we want support for formatting a number within a string). + ? $"{Carousel.CountDisplayed:#,0} matching beatmap" + : $"{Carousel.CountDisplayed:#,0} matching beatmaps"; + } + private bool boundLocalBindables; private void bindBindables() From cc408470f4bafde7c8571171f0392437cc7d4b92 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Mar 2023 14:47:20 +0900 Subject: [PATCH 265/476] Add test coverage of second touch moving but not resulting in cursor movement --- osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs index d1880d7552..b2e4e07526 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs @@ -176,6 +176,14 @@ namespace osu.Game.Rulesets.Osu.Tests checkPressed(OsuAction.RightButton); // in this case, touch 2 should not become the positional tracking touch. checkPosition(TouchSource.Touch1); + + // even if the second touch moves on the screen, the original tracking touch is retained. + beginTouch(TouchSource.Touch2, new Vector2(0)); + beginTouch(TouchSource.Touch2, new Vector2(9999)); + beginTouch(TouchSource.Touch2, new Vector2(0)); + beginTouch(TouchSource.Touch2, new Vector2(9999)); + + checkPosition(TouchSource.Touch1); } [Test] From fb51221c2bb29e492fc09bb4f549bb3854d61432 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Mar 2023 21:25:50 +0900 Subject: [PATCH 266/476] Add test coverage of cyclic selection triggering when more than one item is selected --- .../Visual/Gameplay/TestSceneSkinEditor.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs index 35ba363f4b..119b753d70 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs @@ -126,6 +126,10 @@ namespace osu.Game.Tests.Visual.Gameplay }); AddAssert("Last two boxes selected", () => skinEditor.SelectedComponents, () => Is.EqualTo(new[] { box2, box3 })); + + // Test cyclic selection doesn't trigger in this state. + AddStep("click on black box stack", () => InputManager.Click(MouseButton.Left)); + AddAssert("Last two boxes still selected", () => skinEditor.SelectedComponents, () => Is.EqualTo(new[] { box2, box3 })); } [Test] @@ -164,6 +168,18 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("click on black box stack", () => InputManager.Click(MouseButton.Left)); AddAssert("Selection is black box 1", () => skinEditor.SelectedComponents.Single(), () => Is.EqualTo(blueprints[0].Item)); + + AddStep("select all boxes", () => + { + skinEditor.SelectedComponents.Clear(); + skinEditor.SelectedComponents.AddRange(targetContainer.Components.OfType().Skip(1)); + }); + + AddAssert("all boxes selected", () => skinEditor.SelectedComponents, () => Has.Count.EqualTo(2)); + AddStep("click on black box stack", () => InputManager.Click(MouseButton.Left)); + AddStep("click on black box stack", () => InputManager.Click(MouseButton.Left)); + AddStep("click on black box stack", () => InputManager.Click(MouseButton.Left)); + AddAssert("all boxes still selected", () => skinEditor.SelectedComponents, () => Has.Count.EqualTo(2)); } [TestCase(false)] From e31a90e0432928e487c25d0973d75555db68c262 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Mar 2023 21:21:43 +0900 Subject: [PATCH 267/476] Don't cycle selection when more than one items are selected --- osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 143f343c7d..cb7c083d87 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -401,7 +401,7 @@ namespace osu.Game.Screens.Edit.Compose.Components return false; } - if (!wasDragStarted && selectedBlueprintAlreadySelectedOnMouseDown && AllowCyclicSelection) + if (!wasDragStarted && selectedBlueprintAlreadySelectedOnMouseDown && SelectedItems.Count == 1 && AllowCyclicSelection) { // If a click occurred and was handled by the currently selected blueprint but didn't result in a drag, // cycle between other blueprints which are also under the cursor. From df3ccdff9fed5b05188af319f19849656ac58975 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Mon, 20 Mar 2023 22:22:28 -0700 Subject: [PATCH 268/476] Add failing beatmap listing sort direction on criteria change test --- .../TestSceneBeatmapListingSortTabControl.cs | 26 ++++++++++++++++++- .../BeatmapListingSortTabControl.cs | 2 +- osu.Game/Overlays/OverlaySortTabControl.cs | 2 +- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSortTabControl.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSortTabControl.cs index 316035275f..dd7bf48791 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSortTabControl.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSortTabControl.cs @@ -14,10 +14,11 @@ using osu.Game.Graphics.Sprites; using osu.Game.Overlays; using osu.Game.Overlays.BeatmapListing; using osuTK; +using osuTK.Input; namespace osu.Game.Tests.Visual.UserInterface { - public partial class TestSceneBeatmapListingSortTabControl : OsuTestScene + public partial class TestSceneBeatmapListingSortTabControl : OsuManualInputManagerTestScene { private readonly BeatmapListingSortTabControl control; @@ -111,6 +112,29 @@ namespace osu.Game.Tests.Visual.UserInterface resetUsesCriteriaOnCategory(SortCriteria.Updated, SearchCategory.Mine); } + [Test] + public void TestSortDirectionOnCriteriaChange() + { + AddStep("set category to leaderboard", () => control.Reset(SearchCategory.Leaderboard, false)); + AddAssert("sort direction is descending", () => control.SortDirection.Value == SortDirection.Descending); + + AddStep("click ranked sort button", () => + { + InputManager.MoveMouseTo(control.TabControl.ChildrenOfType().Single(s => s.Active.Value)); + InputManager.Click(MouseButton.Left); + }); + + AddAssert("sort direction is ascending", () => control.SortDirection.Value == SortDirection.Ascending); + + AddStep("click first inactive sort button", () => + { + InputManager.MoveMouseTo(control.TabControl.ChildrenOfType().First(s => !s.Active.Value)); + InputManager.Click(MouseButton.Left); + }); + + AddAssert("sort direction is descending", () => control.SortDirection.Value == SortDirection.Descending); + } + private void criteriaShowsOnCategory(bool expected, SortCriteria criteria, SearchCategory category) { AddAssert($"{criteria.ToString().ToLowerInvariant()} {(expected ? "shown" : "not shown")} on {category.ToString().ToLowerInvariant()}", () => diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingSortTabControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingSortTabControl.cs index 025738710f..79a2d01208 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingSortTabControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingSortTabControl.cs @@ -102,7 +102,7 @@ namespace osu.Game.Overlays.BeatmapListing }; } - private partial class BeatmapTabButton : TabButton + public partial class BeatmapTabButton : TabButton { public readonly Bindable SortDirection = new Bindable(); diff --git a/osu.Game/Overlays/OverlaySortTabControl.cs b/osu.Game/Overlays/OverlaySortTabControl.cs index 8af2ab3823..5c51f5e4d0 100644 --- a/osu.Game/Overlays/OverlaySortTabControl.cs +++ b/osu.Game/Overlays/OverlaySortTabControl.cs @@ -117,7 +117,7 @@ namespace osu.Game.Overlays } } - protected partial class TabButton : HeaderButton + public partial class TabButton : HeaderButton { public readonly BindableBool Active = new BindableBool(); From 3cd01ee621d6df6eda45374ed3765cfe3d074887 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Tue, 21 Mar 2023 14:49:39 -0700 Subject: [PATCH 269/476] Fix beatmap listing sort direction not resetting when changing criteria --- .../Overlays/BeatmapListing/BeatmapListingSortTabControl.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingSortTabControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingSortTabControl.cs index 79a2d01208..9cc4f8a34b 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingSortTabControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingSortTabControl.cs @@ -25,6 +25,8 @@ namespace osu.Game.Overlays.BeatmapListing if (currentParameters == null) Reset(SearchCategory.Leaderboard, false); + + Current.BindValueChanged(_ => SortDirection.Value = Overlays.SortDirection.Descending); } public void Reset(SearchCategory category, bool hasQuery) From 1478a26cc03beb5f90f53db1d61352de6cea65b4 Mon Sep 17 00:00:00 2001 From: Terochi Date: Tue, 21 Mar 2023 23:15:49 +0100 Subject: [PATCH 270/476] Addressed changes --- .../Gameplay/TestSceneSkinnableSound.cs | 5 +-- osu.Game/Skinning/PoolableSkinnableSample.cs | 45 ++----------------- 2 files changed, 5 insertions(+), 45 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs index cc82ffed2b..3f78dbfd96 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs @@ -107,15 +107,12 @@ namespace osu.Game.Tests.Visual.Gameplay AddUntilStep("ensure sample loaded", () => skinnableSound.ChildrenOfType().Single().Name, () => Is.EqualTo(sample_lookup)); - AddStep("Change source", () => + AddStep("change source", () => { skinSource.OverridingSample = new SampleVirtual("new skin"); skinSource.TriggerSourceChanged(); }); - // Samples are nulled on source change immediately - AddUntilStep("wait for sample null", () => skinnableSound.ChildrenOfType().Count(), () => Is.Zero); - AddStep("start sample", () => skinnableSound.Play()); AddUntilStep("sample updated", () => skinnableSound.ChildrenOfType().Single().Name, () => Is.EqualTo("new skin")); } diff --git a/osu.Game/Skinning/PoolableSkinnableSample.cs b/osu.Game/Skinning/PoolableSkinnableSample.cs index 361c8688e7..eacb33f7d7 100644 --- a/osu.Game/Skinning/PoolableSkinnableSample.cs +++ b/osu.Game/Skinning/PoolableSkinnableSample.cs @@ -4,12 +4,10 @@ #nullable disable using System; -using System.Linq; using JetBrains.Annotations; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; -using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Audio; using osu.Framework.Graphics.Containers; @@ -70,48 +68,21 @@ namespace osu.Game.Skinning updateSample(); } - protected override void LoadComplete() - { - base.LoadComplete(); - - CurrentSkin.SourceChanged += skinChangedImmediate; - } - - private void skinChangedImmediate() - { - // Clean up the previous sample immediately on a source change. - // This avoids a potential call to Play() of an already disposed sample (samples are disposed along with the skin, but SkinChanged is scheduled). - clearPreviousSamples(); - } - protected override void SkinChanged(ISkinSource skin) { base.SkinChanged(skin); updateSample(); } - /// - /// Whether this sample was playing before a skin source change. - /// - private bool wasPlaying; - - private void clearPreviousSamples() - { - // only run if the samples aren't already cleared. - // this ensures the "wasPlaying" state is stored correctly even if multiple clear calls are executed. - if (!sampleContainer.Any()) return; - - wasPlaying = Playing; - - sampleContainer.Clear(); - Sample = null; - } - private void updateSample() { if (sampleInfo == null) return; + bool wasPlaying = Playing; + + sampleContainer.Clear(); + var sample = CurrentSkin.GetSample(sampleInfo); if (sample == null) @@ -174,14 +145,6 @@ namespace osu.Game.Skinning } } - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - if (CurrentSkin.IsNotNull()) - CurrentSkin.SourceChanged -= skinChangedImmediate; - } - #region Re-expose AudioContainer public BindableNumber Volume => sampleContainer.Volume; From e1fb63e1f313943d058c820ca6a660d9342ca15f Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Mon, 20 Mar 2023 22:27:07 -0700 Subject: [PATCH 271/476] Move beatmap listing filter control to header --- .../BeatmapListing/BeatmapListingHeader.cs | 5 +++++ osu.Game/Overlays/BeatmapListingOverlay.cs | 14 +++++++------- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs index 76b6dec65b..3336c383ff 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs @@ -3,6 +3,7 @@ #nullable disable +using osu.Framework.Graphics; using osu.Game.Localisation; using osu.Game.Resources.Localisation.Web; @@ -10,8 +11,12 @@ namespace osu.Game.Overlays.BeatmapListing { public partial class BeatmapListingHeader : OverlayHeader { + public BeatmapListingFilterControl FilterControl { get; private set; } + protected override OverlayTitle CreateTitle() => new BeatmapListingTitle(); + protected override Drawable CreateContent() => FilterControl = new BeatmapListingFilterControl(); + private partial class BeatmapListingTitle : OverlayTitle { public BeatmapListingTitle() diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index 73961487ed..d2f39fde8e 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -43,7 +43,13 @@ namespace osu.Game.Overlays private Container panelTarget; private FillFlowContainer foundContent; - private BeatmapListingFilterControl filterControl; + + private BeatmapListingFilterControl filterControl => Header.FilterControl.With(f => + { + f.TypingStarted = onTypingStarted; + f.SearchStarted = onSearchStarted; + f.SearchFinished = onSearchFinished; + }); public BeatmapListingOverlay() : base(OverlayColourScheme.Blue) @@ -60,12 +66,6 @@ namespace osu.Game.Overlays Direction = FillDirection.Vertical, Children = new Drawable[] { - filterControl = new BeatmapListingFilterControl - { - TypingStarted = onTypingStarted, - SearchStarted = onSearchStarted, - SearchFinished = onSearchFinished, - }, new Container { AutoSizeAxes = Axes.Y, From 74a15d742495d95fbb94ce2793e750802f67feef Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Mon, 20 Mar 2023 22:26:51 -0700 Subject: [PATCH 272/476] Fix overlay headers being blocked by loading layer --- osu.Game/Overlays/OnlineOverlay.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/OnlineOverlay.cs b/osu.Game/Overlays/OnlineOverlay.cs index 4fdf7cb2b6..f29a5719d8 100644 --- a/osu.Game/Overlays/OnlineOverlay.cs +++ b/osu.Game/Overlays/OnlineOverlay.cs @@ -3,6 +3,7 @@ #nullable disable +using System; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -22,6 +23,7 @@ namespace osu.Game.Overlays protected readonly OverlayScrollContainer ScrollFlow; protected readonly LoadingLayer Loading; + private readonly Container loadingContainer; private readonly Container content; protected OnlineOverlay(OverlayColourScheme colourScheme, bool requiresSignIn = true) @@ -65,10 +67,21 @@ namespace osu.Game.Overlays }, } }, - Loading = new LoadingLayer(true) + loadingContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Child = Loading = new LoadingLayer(true), + } }); base.Content.Add(mainContent); } + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + loadingContainer.Padding = new MarginPadding { Top = Math.Max(0, Header.Height - ScrollFlow.Current) }; + } } } From d9571b6fc9700111a5ca3ee1433618e667cca766 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Tue, 21 Mar 2023 21:31:35 -0700 Subject: [PATCH 273/476] Move filter control property setters to `load()` --- osu.Game/Overlays/BeatmapListingOverlay.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index d2f39fde8e..f8784504b8 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -44,12 +44,7 @@ namespace osu.Game.Overlays private Container panelTarget; private FillFlowContainer foundContent; - private BeatmapListingFilterControl filterControl => Header.FilterControl.With(f => - { - f.TypingStarted = onTypingStarted; - f.SearchStarted = onSearchStarted; - f.SearchFinished = onSearchFinished; - }); + private BeatmapListingFilterControl filterControl => Header.FilterControl; public BeatmapListingOverlay() : base(OverlayColourScheme.Blue) @@ -88,6 +83,10 @@ namespace osu.Game.Overlays }, } }; + + filterControl.TypingStarted = onTypingStarted; + filterControl.SearchStarted = onSearchStarted; + filterControl.SearchFinished = onSearchFinished; } protected override void LoadComplete() From 425be20e462f32af5ca4a5103383a664f281315e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Mar 2023 14:13:42 +0900 Subject: [PATCH 274/476] Fix song select search textbox font size incorrectly having increased --- osu.Game/Screens/Select/FilterControl.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index b44ca8ac04..38520a85b7 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -257,6 +257,7 @@ namespace osu.Game.Screens.Select public FilterControlTextBox() { Height += filter_text_size; + TextContainer.Height *= (Height - filter_text_size) / Height; TextContainer.Margin = new MarginPadding { Bottom = filter_text_size }; } From 6f3bb85eaab77c727a64bffc415d56c853796aa7 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Wed, 22 Mar 2023 00:19:23 -0700 Subject: [PATCH 275/476] Always show down arrow on inactive sort buttons --- .../Overlays/BeatmapListing/BeatmapListingSortTabControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingSortTabControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingSortTabControl.cs index 9cc4f8a34b..2f290d05e9 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingSortTabControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingSortTabControl.cs @@ -138,7 +138,7 @@ namespace osu.Game.Overlays.BeatmapListing SortDirection.BindValueChanged(direction => { - icon.Icon = direction.NewValue == Overlays.SortDirection.Ascending ? FontAwesome.Solid.CaretUp : FontAwesome.Solid.CaretDown; + icon.Icon = direction.NewValue == Overlays.SortDirection.Ascending && Active.Value ? FontAwesome.Solid.CaretUp : FontAwesome.Solid.CaretDown; }, true); } From 9bc6b46e4e407704dddfe91ce2b043494825424a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Mar 2023 16:36:05 +0900 Subject: [PATCH 276/476] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 4e580a6919..a62e28dca0 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -11,7 +11,7 @@ manifestmerger.jar - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index c08dc9ed8f..d5ff3fad56 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 8738979c57..25466f5426 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -16,6 +16,6 @@ iossimulator-x64 - + From 13be7097180551ff44c801ed219472d2d999c588 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Mar 2023 19:29:34 +0900 Subject: [PATCH 277/476] Add basic renderer selection --- .../Settings/Sections/Graphics/RendererSettings.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs index a5fdfdc105..08a62ff324 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs @@ -1,9 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Localisation; @@ -23,6 +22,12 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics // NOTE: Compatability mode omitted Children = new Drawable[] { + new SettingsEnumDropdown + { + LabelText = GraphicsSettingsStrings.Renderer, + Current = config.GetBindable(FrameworkSetting.Renderer), + Keywords = new[] { @"compatibility", @"directx" }, + }, // TODO: this needs to be a custom dropdown at some point new SettingsEnumDropdown { From 956fabb445aa22ec931750f20c5c78c7a80bab6c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Mar 2023 20:28:26 +0900 Subject: [PATCH 278/476] Show restart notice when changing renderer --- .../Localisation/GraphicsSettingsStrings.cs | 11 ++++++++++ .../Sections/Graphics/RendererSettings.cs | 20 +++++++++++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/osu.Game/Localisation/GraphicsSettingsStrings.cs b/osu.Game/Localisation/GraphicsSettingsStrings.cs index 6e05929d81..422704514f 100644 --- a/osu.Game/Localisation/GraphicsSettingsStrings.cs +++ b/osu.Game/Localisation/GraphicsSettingsStrings.cs @@ -19,6 +19,11 @@ namespace osu.Game.Localisation ///
public static LocalisableString RendererHeader => new TranslatableString(getKey(@"renderer_header"), @"Renderer"); + /// + /// "Renderer" + /// + public static LocalisableString Renderer => new TranslatableString(getKey(@"renderer"), @"Renderer"); + /// /// "Frame limiter" /// @@ -144,6 +149,12 @@ namespace osu.Game.Localisation ///
public static LocalisableString Png => new TranslatableString(getKey(@"png_lossless"), @"PNG (lossless)"); + /// + /// "In order to change the renderer, the game will close. Please open it again." + /// + public static LocalisableString ChangeRendererConfirmation => + new TranslatableString(getKey(@"change_renderer_configuration"), @"In order to change the renderer, the game will close. Please open it again."); + private static string getKey(string key) => $"{prefix}:{key}"; } } diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs index 08a62ff324..ba2691e41c 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs @@ -2,13 +2,13 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Framework.Platform; using osu.Game.Configuration; using osu.Game.Localisation; +using osu.Game.Overlays.Dialog; namespace osu.Game.Overlays.Settings.Sections.Graphics { @@ -17,15 +17,16 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics protected override LocalisableString Header => GraphicsSettingsStrings.RendererHeader; [BackgroundDependencyLoader] - private void load(FrameworkConfigManager config, OsuConfigManager osuConfig) + private void load(FrameworkConfigManager config, OsuConfigManager osuConfig, IDialogOverlay dialogOverlay, OsuGame game, GameHost host) { - // NOTE: Compatability mode omitted + var renderer = config.GetBindable(FrameworkSetting.Renderer); + Children = new Drawable[] { new SettingsEnumDropdown { LabelText = GraphicsSettingsStrings.Renderer, - Current = config.GetBindable(FrameworkSetting.Renderer), + Current = renderer, Keywords = new[] { @"compatibility", @"directx" }, }, // TODO: this needs to be a custom dropdown at some point @@ -46,6 +47,17 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics Current = osuConfig.GetBindable(OsuSetting.ShowFpsDisplay) }, }; + + renderer.BindValueChanged(r => + { + if (r.NewValue == host.ResolvedRenderer) + return; + + dialogOverlay.Push(new ConfirmDialog(GraphicsSettingsStrings.ChangeRendererConfirmation, game.AttemptExit, () => + { + renderer.SetDefault(); + })); + }); } } } From aabe86dc26c90c90e1d9a5c0bd860bcf6d1b06fb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Mar 2023 20:28:36 +0900 Subject: [PATCH 279/476] Limit renderers to those available for the current platform --- .../Overlays/Settings/Sections/Graphics/RendererSettings.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs index ba2691e41c..80e5dd3843 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.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.Linq; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; @@ -27,6 +28,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics { LabelText = GraphicsSettingsStrings.Renderer, Current = renderer, + Items = host.GetPreferredRenderersForCurrentPlatform().OrderBy(t => t), Keywords = new[] { @"compatibility", @"directx" }, }, // TODO: this needs to be a custom dropdown at some point From 3050a16bf866895a9188e008f397b3e0262cea58 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Mar 2023 16:56:35 +0900 Subject: [PATCH 280/476] Don't require a restart when selecting `Automatic` and startup setting was also automatic --- .../Settings/Sections/Graphics/RendererSettings.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs index 80e5dd3843..4f24898bce 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs @@ -17,10 +17,13 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics { protected override LocalisableString Header => GraphicsSettingsStrings.RendererHeader; + private bool automaticRendererInUse; + [BackgroundDependencyLoader] private void load(FrameworkConfigManager config, OsuConfigManager osuConfig, IDialogOverlay dialogOverlay, OsuGame game, GameHost host) { var renderer = config.GetBindable(FrameworkSetting.Renderer); + automaticRendererInUse = renderer.Value == RendererType.Automatic; Children = new Drawable[] { @@ -55,6 +58,10 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics if (r.NewValue == host.ResolvedRenderer) return; + // Need to check startup renderer for the "automatic" case, as ResolvedRenderer above will track the final resolved renderer instead. + if (r.NewValue == RendererType.Automatic && automaticRendererInUse) + return; + dialogOverlay.Push(new ConfirmDialog(GraphicsSettingsStrings.ChangeRendererConfirmation, game.AttemptExit, () => { renderer.SetDefault(); From ab6cfea5c71dded5fbd88ef4183943e7e25fbc74 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 Mar 2023 11:27:05 +0900 Subject: [PATCH 281/476] Revert old value instead of always using default Co-authored-by: cdwcgt --- .../Overlays/Settings/Sections/Graphics/RendererSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs index 4f24898bce..45a6d35749 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs @@ -64,7 +64,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics dialogOverlay.Push(new ConfirmDialog(GraphicsSettingsStrings.ChangeRendererConfirmation, game.AttemptExit, () => { - renderer.SetDefault(); + renderer.Value = r.OldValue; })); }); } From 540b38dc2191691047b7ccf646c2b82541870233 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 Mar 2023 14:01:19 +0900 Subject: [PATCH 282/476] Fix tournament interface save button not usable after changing match progression/round --- osu.Game.Tournament/TournamentGameBase.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 7e19cb3aa5..634cc87a9f 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -332,13 +332,6 @@ namespace osu.Game.Tournament private void saveChanges() { - foreach (var r in ladder.Rounds) - r.Matches = ladder.Matches.Where(p => p.Round.Value == r).Select(p => p.ID).ToList(); - - ladder.Progressions = ladder.Matches.Where(p => p.Progression.Value != null).Select(p => new TournamentProgression(p.ID, p.Progression.Value.ID)).Concat( - ladder.Matches.Where(p => p.LosersProgression.Value != null).Select(p => new TournamentProgression(p.ID, p.LosersProgression.Value.ID, true))) - .ToList(); - // Serialise before opening stream for writing, so if there's a failure it will leave the file in the previous state. string serialisedLadder = GetSerialisedLadder(); @@ -349,6 +342,13 @@ namespace osu.Game.Tournament public string GetSerialisedLadder() { + foreach (var r in ladder.Rounds) + r.Matches = ladder.Matches.Where(p => p.Round.Value == r).Select(p => p.ID).ToList(); + + ladder.Progressions = ladder.Matches.Where(p => p.Progression.Value != null).Select(p => new TournamentProgression(p.ID, p.Progression.Value.ID)).Concat( + ladder.Matches.Where(p => p.LosersProgression.Value != null).Select(p => new TournamentProgression(p.ID, p.LosersProgression.Value.ID, true))) + .ToList(); + return JsonConvert.SerializeObject(ladder, new JsonSerializerSettings { From e6f1ec57a963cf4bdc16d8c186227dd18230d6d9 Mon Sep 17 00:00:00 2001 From: Terochi Date: Thu, 23 Mar 2023 18:46:48 +0100 Subject: [PATCH 283/476] Bring back and make use of `clearPreviousSamples()` --- osu.Game/Skinning/PoolableSkinnableSample.cs | 26 ++++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/osu.Game/Skinning/PoolableSkinnableSample.cs b/osu.Game/Skinning/PoolableSkinnableSample.cs index eacb33f7d7..76c2c4d7ec 100644 --- a/osu.Game/Skinning/PoolableSkinnableSample.cs +++ b/osu.Game/Skinning/PoolableSkinnableSample.cs @@ -4,6 +4,7 @@ #nullable disable using System; +using System.Linq; using JetBrains.Annotations; using osu.Framework.Audio; using osu.Framework.Audio.Sample; @@ -74,14 +75,29 @@ namespace osu.Game.Skinning updateSample(); } - private void updateSample() - { - if (sampleInfo == null) - return; + /// + /// Whether this sample was playing before a skin source change. + /// + private bool wasPlaying; - bool wasPlaying = Playing; + private void clearPreviousSamples() + { + // only run if the samples aren't already cleared. + // this ensures the "wasPlaying" state is stored correctly even if multiple clear calls are executed. + if (!sampleContainer.Any()) return; + + wasPlaying = Playing; sampleContainer.Clear(); + Sample = null; + } + + private void updateSample() + { + clearPreviousSamples(); + + if (sampleInfo == null) + return; var sample = CurrentSkin.GetSample(sampleInfo); From 450c5cef074b85b6f190b9ca05687937b044d776 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Fri, 24 Mar 2023 18:42:34 -0700 Subject: [PATCH 284/476] Add comment explaining loading container padding --- osu.Game/Overlays/OnlineOverlay.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/OnlineOverlay.cs b/osu.Game/Overlays/OnlineOverlay.cs index f29a5719d8..4d2c6bc9d0 100644 --- a/osu.Game/Overlays/OnlineOverlay.cs +++ b/osu.Game/Overlays/OnlineOverlay.cs @@ -81,6 +81,7 @@ namespace osu.Game.Overlays { base.UpdateAfterChildren(); + // don't block header by applying padding equal to the visible header height loadingContainer.Padding = new MarginPadding { Top = Math.Max(0, Header.Height - ScrollFlow.Current) }; } } From e1906a90eb8f6ea79140490304b06ee3453f1508 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Tue, 21 Mar 2023 13:23:08 -0700 Subject: [PATCH 285/476] Use `image@2x` from tournament banner api --- osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs | 1 + osu.Game/Users/TournamentBanner.cs | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs index a97c8aff66..4278c46d6a 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs @@ -126,6 +126,7 @@ namespace osu.Game.Tests.Visual.Online Id = 13926, TournamentId = 35, ImageLowRes = "https://assets.ppy.sh/tournament-banners/official/owc2022/profile/winner_US.jpg", + Image = "https://assets.ppy.sh/tournament-banners/official/owc2022/profile/winner_US@2x.jpg", }, Badges = new[] { diff --git a/osu.Game/Users/TournamentBanner.cs b/osu.Game/Users/TournamentBanner.cs index 62e1913412..e7fada1eff 100644 --- a/osu.Game/Users/TournamentBanner.cs +++ b/osu.Game/Users/TournamentBanner.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.IO; using Newtonsoft.Json; namespace osu.Game.Users @@ -17,7 +16,7 @@ namespace osu.Game.Users [JsonProperty("image")] public string ImageLowRes = null!; - // TODO: remove when api returns @2x image link: https://github.com/ppy/osu-web/issues/9816 - public string Image => $@"{Path.ChangeExtension(ImageLowRes, null)}@2x{Path.GetExtension(ImageLowRes)}"; + [JsonProperty("image@2x")] + public string Image = null!; } } From eaef5ff2a3e0412036246eb6b4226a156bdbeaed Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sat, 25 Mar 2023 22:13:51 -0700 Subject: [PATCH 286/476] Fix now playing playlist not highlighting selected item on initial open --- osu.Game/Overlays/Music/PlaylistItem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Music/PlaylistItem.cs b/osu.Game/Overlays/Music/PlaylistItem.cs index 00c5ce8002..4032af7651 100644 --- a/osu.Game/Overlays/Music/PlaylistItem.cs +++ b/osu.Game/Overlays/Music/PlaylistItem.cs @@ -75,7 +75,7 @@ namespace osu.Game.Overlays.Music selected = newSelected; updateSelectionState(false); - }); + }, true); updateSelectionState(true); }); From c803eb8e044424c601a585632755bf5f48f47834 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 26 Mar 2023 16:14:15 +0900 Subject: [PATCH 287/476] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index a62e28dca0..bc382d8f97 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -11,7 +11,7 @@ manifestmerger.jar - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index f8fc6ffda6..16f780d034 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 25466f5426..5d90119233 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -16,6 +16,6 @@ iossimulator-x64 - + From 27de3314fdfb89cec20e1d34d9d323354e3922c7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 26 Mar 2023 16:23:07 +0900 Subject: [PATCH 288/476] Remove iOS mouse handler code --- osu.iOS/IOSMouseSettings.cs | 36 ------------------------------------ osu.iOS/OsuGameIOS.cs | 15 --------------- 2 files changed, 51 deletions(-) delete mode 100644 osu.iOS/IOSMouseSettings.cs diff --git a/osu.iOS/IOSMouseSettings.cs b/osu.iOS/IOSMouseSettings.cs deleted file mode 100644 index f464bd93b8..0000000000 --- a/osu.iOS/IOSMouseSettings.cs +++ /dev/null @@ -1,36 +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.Graphics; -using osu.Framework.Localisation; -using osu.Game.Configuration; -using osu.Game.Localisation; -using osu.Game.Overlays.Settings; - -namespace osu.iOS -{ - public partial class IOSMouseSettings : SettingsSubsection - { - protected override LocalisableString Header => MouseSettingsStrings.Mouse; - - [BackgroundDependencyLoader] - private void load(OsuConfigManager osuConfig) - { - Children = new Drawable[] - { - new SettingsCheckbox - { - LabelText = MouseSettingsStrings.DisableMouseWheelVolumeAdjust, - TooltipText = MouseSettingsStrings.DisableMouseWheelVolumeAdjustTooltip, - Current = osuConfig.GetBindable(OsuSetting.MouseDisableWheel), - }, - new SettingsCheckbox - { - LabelText = MouseSettingsStrings.DisableMouseButtons, - Current = osuConfig.GetBindable(OsuSetting.MouseDisableButtons), - }, - }; - } - } -} diff --git a/osu.iOS/OsuGameIOS.cs b/osu.iOS/OsuGameIOS.cs index 3e79bc6ad6..c49e6907ff 100644 --- a/osu.iOS/OsuGameIOS.cs +++ b/osu.iOS/OsuGameIOS.cs @@ -7,10 +7,7 @@ using System; using Foundation; using Microsoft.Maui.Devices; using osu.Framework.Graphics; -using osu.Framework.Input.Handlers; -using osu.Framework.iOS.Input; using osu.Game; -using osu.Game.Overlays.Settings; using osu.Game.Updater; using osu.Game.Utils; @@ -29,18 +26,6 @@ namespace osu.iOS // Because we have the home indicator (mostly) hidden we don't really care about drawing in this region. Edges.Bottom; - public override SettingsSubsection CreateSettingsSubsectionFor(InputHandler handler) - { - switch (handler) - { - case IOSMouseHandler: - return new IOSMouseSettings(); - - default: - return base.CreateSettingsSubsectionFor(handler); - } - } - private class IOSBatteryInfo : BatteryInfo { public override double? ChargeLevel => Battery.ChargeLevel; From 816eff1a87348e1b240dec34df0222868866fdf8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 26 Mar 2023 16:27:44 +0900 Subject: [PATCH 289/476] Update iOS startup code --- .../AppDelegate.cs | 17 ----------- .../Application.cs | 7 ++--- .../AppDelegate.cs | 17 ----------- .../Application.cs | 7 ++--- .../AppDelegate.cs | 17 ----------- .../Application.cs | 7 ++--- .../AppDelegate.cs | 17 ----------- .../Application.cs | 7 ++--- osu.Game.Tests.iOS/AppDelegate.cs | 16 ---------- osu.Game.Tests.iOS/Application.cs | 6 ++-- osu.iOS/AppDelegate.cs | 29 ------------------- osu.iOS/Application.cs | 6 ++-- 12 files changed, 16 insertions(+), 137 deletions(-) delete mode 100644 osu.Game.Rulesets.Catch.Tests.iOS/AppDelegate.cs delete mode 100644 osu.Game.Rulesets.Mania.Tests.iOS/AppDelegate.cs delete mode 100644 osu.Game.Rulesets.Osu.Tests.iOS/AppDelegate.cs delete mode 100644 osu.Game.Rulesets.Taiko.Tests.iOS/AppDelegate.cs delete mode 100644 osu.Game.Tests.iOS/AppDelegate.cs delete mode 100644 osu.iOS/AppDelegate.cs diff --git a/osu.Game.Rulesets.Catch.Tests.iOS/AppDelegate.cs b/osu.Game.Rulesets.Catch.Tests.iOS/AppDelegate.cs deleted file mode 100644 index 64ff3f7151..0000000000 --- a/osu.Game.Rulesets.Catch.Tests.iOS/AppDelegate.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -#nullable disable - -using Foundation; -using osu.Framework.iOS; -using osu.Game.Tests; - -namespace osu.Game.Rulesets.Catch.Tests.iOS -{ - [Register("AppDelegate")] - public class AppDelegate : GameAppDelegate - { - protected override Framework.Game CreateGame() => new OsuTestBrowser(); - } -} diff --git a/osu.Game.Rulesets.Catch.Tests.iOS/Application.cs b/osu.Game.Rulesets.Catch.Tests.iOS/Application.cs index 1fcb0aa427..d097c6a698 100644 --- a/osu.Game.Rulesets.Catch.Tests.iOS/Application.cs +++ b/osu.Game.Rulesets.Catch.Tests.iOS/Application.cs @@ -1,9 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - -using UIKit; +using osu.Framework.iOS; +using osu.Game.Tests; namespace osu.Game.Rulesets.Catch.Tests.iOS { @@ -11,7 +10,7 @@ namespace osu.Game.Rulesets.Catch.Tests.iOS { public static void Main(string[] args) { - UIApplication.Main(args, null, typeof(AppDelegate)); + GameApplication.Main(new OsuTestBrowser()); } } } diff --git a/osu.Game.Rulesets.Mania.Tests.iOS/AppDelegate.cs b/osu.Game.Rulesets.Mania.Tests.iOS/AppDelegate.cs deleted file mode 100644 index a528634f3b..0000000000 --- a/osu.Game.Rulesets.Mania.Tests.iOS/AppDelegate.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -#nullable disable - -using Foundation; -using osu.Framework.iOS; -using osu.Game.Tests; - -namespace osu.Game.Rulesets.Mania.Tests.iOS -{ - [Register("AppDelegate")] - public class AppDelegate : GameAppDelegate - { - protected override Framework.Game CreateGame() => new OsuTestBrowser(); - } -} diff --git a/osu.Game.Rulesets.Mania.Tests.iOS/Application.cs b/osu.Game.Rulesets.Mania.Tests.iOS/Application.cs index a508198f7f..75a5a73058 100644 --- a/osu.Game.Rulesets.Mania.Tests.iOS/Application.cs +++ b/osu.Game.Rulesets.Mania.Tests.iOS/Application.cs @@ -1,9 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - -using UIKit; +using osu.Framework.iOS; +using osu.Game.Tests; namespace osu.Game.Rulesets.Mania.Tests.iOS { @@ -11,7 +10,7 @@ namespace osu.Game.Rulesets.Mania.Tests.iOS { public static void Main(string[] args) { - UIApplication.Main(args, null, typeof(AppDelegate)); + GameApplication.Main(new OsuTestBrowser()); } } } diff --git a/osu.Game.Rulesets.Osu.Tests.iOS/AppDelegate.cs b/osu.Game.Rulesets.Osu.Tests.iOS/AppDelegate.cs deleted file mode 100644 index fa40a8536e..0000000000 --- a/osu.Game.Rulesets.Osu.Tests.iOS/AppDelegate.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -#nullable disable - -using Foundation; -using osu.Framework.iOS; -using osu.Game.Tests; - -namespace osu.Game.Rulesets.Osu.Tests.iOS -{ - [Register("AppDelegate")] - public class AppDelegate : GameAppDelegate - { - protected override Framework.Game CreateGame() => new OsuTestBrowser(); - } -} diff --git a/osu.Game.Rulesets.Osu.Tests.iOS/Application.cs b/osu.Game.Rulesets.Osu.Tests.iOS/Application.cs index 6ef29fa68e..f9059014a5 100644 --- a/osu.Game.Rulesets.Osu.Tests.iOS/Application.cs +++ b/osu.Game.Rulesets.Osu.Tests.iOS/Application.cs @@ -1,9 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - -using UIKit; +using osu.Framework.iOS; +using osu.Game.Tests; namespace osu.Game.Rulesets.Osu.Tests.iOS { @@ -11,7 +10,7 @@ namespace osu.Game.Rulesets.Osu.Tests.iOS { public static void Main(string[] args) { - UIApplication.Main(args, null, typeof(AppDelegate)); + GameApplication.Main(new OsuTestBrowser()); } } } diff --git a/osu.Game.Rulesets.Taiko.Tests.iOS/AppDelegate.cs b/osu.Game.Rulesets.Taiko.Tests.iOS/AppDelegate.cs deleted file mode 100644 index 385ba48707..0000000000 --- a/osu.Game.Rulesets.Taiko.Tests.iOS/AppDelegate.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -#nullable disable - -using Foundation; -using osu.Framework.iOS; -using osu.Game.Tests; - -namespace osu.Game.Rulesets.Taiko.Tests.iOS -{ - [Register("AppDelegate")] - public class AppDelegate : GameAppDelegate - { - protected override Framework.Game CreateGame() => new OsuTestBrowser(); - } -} diff --git a/osu.Game.Rulesets.Taiko.Tests.iOS/Application.cs b/osu.Game.Rulesets.Taiko.Tests.iOS/Application.cs index 0e3a953728..0b6a11d8c2 100644 --- a/osu.Game.Rulesets.Taiko.Tests.iOS/Application.cs +++ b/osu.Game.Rulesets.Taiko.Tests.iOS/Application.cs @@ -1,9 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - -using UIKit; +using osu.Framework.iOS; +using osu.Game.Tests; namespace osu.Game.Rulesets.Taiko.Tests.iOS { @@ -11,7 +10,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.iOS { public static void Main(string[] args) { - UIApplication.Main(args, null, typeof(AppDelegate)); + GameApplication.Main(new OsuTestBrowser()); } } } diff --git a/osu.Game.Tests.iOS/AppDelegate.cs b/osu.Game.Tests.iOS/AppDelegate.cs deleted file mode 100644 index b13027459f..0000000000 --- a/osu.Game.Tests.iOS/AppDelegate.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -#nullable disable - -using Foundation; -using osu.Framework.iOS; - -namespace osu.Game.Tests.iOS -{ - [Register("AppDelegate")] - public class AppDelegate : GameAppDelegate - { - protected override Framework.Game CreateGame() => new OsuTestBrowser(); - } -} diff --git a/osu.Game.Tests.iOS/Application.cs b/osu.Game.Tests.iOS/Application.cs index 4678be4fb8..e5df79f3de 100644 --- a/osu.Game.Tests.iOS/Application.cs +++ b/osu.Game.Tests.iOS/Application.cs @@ -1,9 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - -using UIKit; +using osu.Framework.iOS; namespace osu.Game.Tests.iOS { @@ -11,7 +9,7 @@ namespace osu.Game.Tests.iOS { public static void Main(string[] args) { - UIApplication.Main(args, null, typeof(AppDelegate)); + GameApplication.Main(new OsuTestBrowser()); } } } diff --git a/osu.iOS/AppDelegate.cs b/osu.iOS/AppDelegate.cs deleted file mode 100644 index 1d29d59fff..0000000000 --- a/osu.iOS/AppDelegate.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -#nullable disable - -using System.Threading.Tasks; -using Foundation; -using osu.Framework.iOS; -using UIKit; - -namespace osu.iOS -{ - [Register("AppDelegate")] - public class AppDelegate : GameAppDelegate - { - private OsuGameIOS game; - - protected override Framework.Game CreateGame() => game = new OsuGameIOS(); - - public override bool OpenUrl(UIApplication app, NSUrl url, NSDictionary options) - { - if (url.IsFileUrl) - Task.Run(() => game.Import(url.Path)); - else - Task.Run(() => game.HandleLink(url.AbsoluteString)); - return true; - } - } -} diff --git a/osu.iOS/Application.cs b/osu.iOS/Application.cs index 64eb5c63f5..74bd58acb8 100644 --- a/osu.iOS/Application.cs +++ b/osu.iOS/Application.cs @@ -1,9 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - -using UIKit; +using osu.Framework.iOS; namespace osu.iOS { @@ -11,7 +9,7 @@ namespace osu.iOS { public static void Main(string[] args) { - UIApplication.Main(args, null, typeof(AppDelegate)); + GameApplication.Main(new OsuGameIOS()); } } } From e346b02ebf811f172bd7416db85bc0975406ccd9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 26 Mar 2023 20:09:35 +0900 Subject: [PATCH 290/476] Add display of current renderer --- osu.Game/Localisation/GraphicsSettingsStrings.cs | 5 +++++ .../Settings/Sections/Graphics/RendererSettings.cs | 8 +++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/osu.Game/Localisation/GraphicsSettingsStrings.cs b/osu.Game/Localisation/GraphicsSettingsStrings.cs index 422704514f..d35446af3d 100644 --- a/osu.Game/Localisation/GraphicsSettingsStrings.cs +++ b/osu.Game/Localisation/GraphicsSettingsStrings.cs @@ -24,6 +24,11 @@ namespace osu.Game.Localisation /// public static LocalisableString Renderer => new TranslatableString(getKey(@"renderer"), @"Renderer"); + /// + /// "Current renderer is "{0}"" + /// + public static LocalisableString CurrentRenderer(string arg0) => new TranslatableString(getKey(@"current_renderer"), @"Current renderer is ""{0}""", arg0); + /// /// "Frame limiter" /// diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs index 45a6d35749..2a25939e08 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs @@ -4,6 +4,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Configuration; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Framework.Platform; @@ -25,9 +26,11 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics var renderer = config.GetBindable(FrameworkSetting.Renderer); automaticRendererInUse = renderer.Value == RendererType.Automatic; + SettingsEnumDropdown rendererDropdown; + Children = new Drawable[] { - new SettingsEnumDropdown + rendererDropdown = new SettingsEnumDropdown { LabelText = GraphicsSettingsStrings.Renderer, Current = renderer, @@ -67,6 +70,9 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics renderer.Value = r.OldValue; })); }); + + if (renderer.Value == RendererType.Automatic) + rendererDropdown.SetNoticeText(GraphicsSettingsStrings.CurrentRenderer(host.ResolvedRenderer.GetDescription())); } } } From 764361b3d3ae9e7c5276cb4ec861b98ae0b90fb5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 26 Mar 2023 20:14:33 +0900 Subject: [PATCH 291/476] Add special case to hide definitely non-working renderers on android --- .../Settings/Sections/Graphics/RendererSettings.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs index 2a25939e08..4b5d2f5a7e 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Linq; +using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Extensions; @@ -71,7 +72,13 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics })); }); - if (renderer.Value == RendererType.Automatic) + // TODO: remove this once we support SDL+android. + if (RuntimeInfo.OS == RuntimeInfo.Platform.Android) + { + rendererDropdown.Items = new[] { RendererType.Automatic, RendererType.OpenGLLegacy }; + rendererDropdown.SetNoticeText("New renderer support for android is coming soon!", true); + } + else if (renderer.Value == RendererType.Automatic) rendererDropdown.SetNoticeText(GraphicsSettingsStrings.CurrentRenderer(host.ResolvedRenderer.GetDescription())); } } From ba078e8357ef50d4a737a2e8f7366887e1bbd670 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 26 Mar 2023 20:29:30 +0900 Subject: [PATCH 292/476] Show "automatic" resolved renderer inline in dropdown instead --- .../Localisation/GraphicsSettingsStrings.cs | 5 --- .../Sections/Graphics/RendererSettings.cs | 33 ++++++++++++++++--- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/osu.Game/Localisation/GraphicsSettingsStrings.cs b/osu.Game/Localisation/GraphicsSettingsStrings.cs index d35446af3d..422704514f 100644 --- a/osu.Game/Localisation/GraphicsSettingsStrings.cs +++ b/osu.Game/Localisation/GraphicsSettingsStrings.cs @@ -24,11 +24,6 @@ namespace osu.Game.Localisation /// public static LocalisableString Renderer => new TranslatableString(getKey(@"renderer"), @"Renderer"); - /// - /// "Current renderer is "{0}"" - /// - public static LocalisableString CurrentRenderer(string arg0) => new TranslatableString(getKey(@"current_renderer"), @"Current renderer is ""{0}""", arg0); - /// /// "Frame limiter" /// diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs index 4b5d2f5a7e..f007d45e8f 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs @@ -5,11 +5,11 @@ using System.Linq; using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Configuration; -using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Framework.Platform; using osu.Game.Configuration; +using osu.Game.Graphics.UserInterface; using osu.Game.Localisation; using osu.Game.Overlays.Dialog; @@ -31,7 +31,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics Children = new Drawable[] { - rendererDropdown = new SettingsEnumDropdown + rendererDropdown = new RendererSettingsDropdown { LabelText = GraphicsSettingsStrings.Renderer, Current = renderer, @@ -78,8 +78,33 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics rendererDropdown.Items = new[] { RendererType.Automatic, RendererType.OpenGLLegacy }; rendererDropdown.SetNoticeText("New renderer support for android is coming soon!", true); } - else if (renderer.Value == RendererType.Automatic) - rendererDropdown.SetNoticeText(GraphicsSettingsStrings.CurrentRenderer(host.ResolvedRenderer.GetDescription())); + } + + private partial class RendererSettingsDropdown : SettingsEnumDropdown + { + protected override OsuDropdown CreateDropdown() => new RendererDropdown(); + + protected partial class RendererDropdown : DropdownControl + { + private RendererType hostResolvedRenderer; + private bool automaticRendererInUse; + + [BackgroundDependencyLoader] + private void load(FrameworkConfigManager config, GameHost host) + { + var renderer = config.GetBindable(FrameworkSetting.Renderer); + automaticRendererInUse = renderer.Value == RendererType.Automatic; + hostResolvedRenderer = host.ResolvedRenderer; + } + + protected override LocalisableString GenerateItemText(RendererType item) + { + if (item == RendererType.Automatic && automaticRendererInUse) + return $"{base.GenerateItemText(item)} ({hostResolvedRenderer})"; + + return base.GenerateItemText(item); + } + } } } } From bab93bed177bd85c52f7ea9928e1cda4c8733603 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 26 Mar 2023 20:43:28 +0900 Subject: [PATCH 293/476] Fix string interpolation and use `GetDescription` on renderer value --- .../Overlays/Settings/Sections/Graphics/RendererSettings.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs index f007d45e8f..f9127fcd9a 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs @@ -5,6 +5,7 @@ using System.Linq; using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Configuration; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Framework.Platform; @@ -100,7 +101,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics protected override LocalisableString GenerateItemText(RendererType item) { if (item == RendererType.Automatic && automaticRendererInUse) - return $"{base.GenerateItemText(item)} ({hostResolvedRenderer})"; + return LocalisableString.Interpolate($"{base.GenerateItemText(item)} ({hostResolvedRenderer.GetDescription()})"); return base.GenerateItemText(item); } From f3c174a7f2d5538d38daa4293c86677a1e324272 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 26 Mar 2023 13:52:26 +0200 Subject: [PATCH 294/476] Fix test errors due to missing dependencies --- .../Overlays/Settings/Sections/Graphics/RendererSettings.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs index f9127fcd9a..ca21b15ff7 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs @@ -23,7 +23,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics private bool automaticRendererInUse; [BackgroundDependencyLoader] - private void load(FrameworkConfigManager config, OsuConfigManager osuConfig, IDialogOverlay dialogOverlay, OsuGame game, GameHost host) + private void load(FrameworkConfigManager config, OsuConfigManager osuConfig, IDialogOverlay? dialogOverlay, OsuGame? game, GameHost host) { var renderer = config.GetBindable(FrameworkSetting.Renderer); automaticRendererInUse = renderer.Value == RendererType.Automatic; @@ -67,7 +67,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics if (r.NewValue == RendererType.Automatic && automaticRendererInUse) return; - dialogOverlay.Push(new ConfirmDialog(GraphicsSettingsStrings.ChangeRendererConfirmation, game.AttemptExit, () => + dialogOverlay?.Push(new ConfirmDialog(GraphicsSettingsStrings.ChangeRendererConfirmation, () => game?.AttemptExit(), () => { renderer.Value = r.OldValue; })); From 292486c25ac65f9ef3c7b92a77699bd773fde531 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 26 Mar 2023 14:35:00 +0200 Subject: [PATCH 295/476] Use more resilient restore method --- .../Overlays/Settings/Sections/Graphics/RendererSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs index ca21b15ff7..a4b0feb8bf 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs @@ -69,7 +69,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics dialogOverlay?.Push(new ConfirmDialog(GraphicsSettingsStrings.ChangeRendererConfirmation, () => game?.AttemptExit(), () => { - renderer.Value = r.OldValue; + renderer.Value = automaticRendererInUse ? RendererType.Automatic : host.ResolvedRenderer; })); }); From 898717231b333d744f5adb94263cc1faa466ae45 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 27 Mar 2023 02:00:09 +0900 Subject: [PATCH 296/476] Hide vulkan renderer option for now We'll bring it back when it's more stable. --- .../Overlays/Settings/Sections/Graphics/RendererSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs index a4b0feb8bf..a1f728ca87 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs @@ -36,7 +36,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics { LabelText = GraphicsSettingsStrings.Renderer, Current = renderer, - Items = host.GetPreferredRenderersForCurrentPlatform().OrderBy(t => t), + Items = host.GetPreferredRenderersForCurrentPlatform().OrderBy(t => t).Where(t => t != RendererType.Vulkan), Keywords = new[] { @"compatibility", @"directx" }, }, // TODO: this needs to be a custom dropdown at some point From 6924dc5c5066e761e00822c47a62f9999dbc69b8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 27 Mar 2023 02:16:33 +0900 Subject: [PATCH 297/476] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index bc382d8f97..927d66d93f 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -11,7 +11,7 @@ manifestmerger.jar - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 16f780d034..3de022e88d 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 5d90119233..eb7ba24336 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -16,6 +16,6 @@ iossimulator-x64 - + From 3e0bbb24326ee0d0b4c6dae72ea86d423dc47bed Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sun, 26 Mar 2023 16:03:21 -0700 Subject: [PATCH 298/476] Fix select beatmap button not highlighting when creating a multiplayer room using keyboard --- .../Match/MultiplayerMatchSettingsOverlay.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index 207b9c378b..66acd6d1b0 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -73,11 +73,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match private OsuSpriteText typeLabel = null!; private LoadingLayer loadingLayer = null!; - public void SelectBeatmap() - { - if (matchSubScreen.IsCurrentScreen()) - matchSubScreen.Push(new MultiplayerMatchSongSelect(matchSubScreen.Room)); - } + public void SelectBeatmap() => selectBeatmapButton.TriggerClick(); [Resolved] private MultiplayerMatchSubScreen matchSubScreen { get; set; } = null!; @@ -97,6 +93,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match private IDisposable? applyingSettingsOperation; private Drawable playlistContainer = null!; private DrawableRoomPlaylist drawablePlaylist = null!; + private RoundedButton selectBeatmapButton = null!; public MatchSettings(Room room) { @@ -275,12 +272,16 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match RelativeSizeAxes = Axes.X, Height = DrawableRoomPlaylistItem.HEIGHT }, - new RoundedButton + selectBeatmapButton = new RoundedButton { RelativeSizeAxes = Axes.X, Height = 40, Text = "Select beatmap", - Action = SelectBeatmap + Action = () => + { + if (matchSubScreen.IsCurrentScreen()) + matchSubScreen.Push(new MultiplayerMatchSongSelect(matchSubScreen.Room)); + } } } } From 76a6f97fbb13aeb163818c1baed19422c30620be Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sun, 26 Mar 2023 17:32:03 -0700 Subject: [PATCH 299/476] Fix wrong definition of a beatmap in first run setup --- osu.Game/Localisation/FirstRunSetupBeatmapScreenStrings.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Localisation/FirstRunSetupBeatmapScreenStrings.cs b/osu.Game/Localisation/FirstRunSetupBeatmapScreenStrings.cs index 3a7fe4bb12..d822f4976f 100644 --- a/osu.Game/Localisation/FirstRunSetupBeatmapScreenStrings.cs +++ b/osu.Game/Localisation/FirstRunSetupBeatmapScreenStrings.cs @@ -15,9 +15,9 @@ namespace osu.Game.Localisation public static LocalisableString Header => new TranslatableString(getKey(@"header"), @"Obtaining Beatmaps"); /// - /// ""Beatmaps" are what we call playable levels. osu! doesn't come with any beatmaps pre-loaded. This step will help you get started on your beatmap collection." + /// ""Beatmaps" are what we call a set of playable levels. osu! doesn't come with any beatmaps pre-loaded. This step will help you get started on your beatmap collection." /// - public static LocalisableString Description => new TranslatableString(getKey(@"description"), @"""Beatmaps"" are what we call playable levels. osu! doesn't come with any beatmaps pre-loaded. This step will help you get started on your beatmap collection."); + public static LocalisableString Description => new TranslatableString(getKey(@"description"), @"""Beatmaps"" are what we call a set of playable levels. osu! doesn't come with any beatmaps pre-loaded. This step will help you get started on your beatmap collection."); /// /// "If you are a new player, we recommend playing through the tutorial to get accustomed to the gameplay." From 8b30c67580f34dd8375d473e4fa469b928339088 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sun, 26 Mar 2023 18:01:48 -0700 Subject: [PATCH 300/476] Fix incorrect song select matching label by showing both beatmap and difficulty count for less ambiguity --- osu.Game/Screens/Select/BeatmapCarousel.cs | 7 ++++++- osu.Game/Screens/Select/SongSelect.cs | 9 ++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 68d3247275..dd3f13373d 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -64,7 +64,12 @@ namespace osu.Game.Screens.Select /// /// The total count of non-filtered beatmaps displayed. /// - public int CountDisplayed => beatmapSets.Where(s => !s.Filtered.Value).Sum(s => s.Beatmaps.Count(b => !b.Filtered.Value)); + public int CountDisplayedBeatmaps => beatmapSets.Where(s => !s.Filtered.Value).Sum(s => s.Beatmaps.Count(b => !b.Filtered.Value)); + + /// + /// The total count of non-filtered beatmap sets displayed. + /// + public int CountDisplayedSets => beatmapSets.Count(s => !s.Filtered.Value); /// /// The currently selected beatmap set. diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index c5e914b461..7191ff7c2a 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using Humanizer; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; @@ -861,11 +862,9 @@ namespace osu.Game.Screens.Select private void updateVisibleBeatmapCount() { - FilterControl.InformationalText = Carousel.CountDisplayed == 1 - // Intentionally not localised until we have proper support for this (see https://github.com/ppy/osu-framework/pull/4918 - // but also in this case we want support for formatting a number within a string). - ? $"{Carousel.CountDisplayed:#,0} matching beatmap" - : $"{Carousel.CountDisplayed:#,0} matching beatmaps"; + // Intentionally not localised until we have proper support for this (see https://github.com/ppy/osu-framework/pull/4918 + // but also in this case we want support for formatting a number within a string). + FilterControl.InformationalText = $"{"matching beatmap".ToQuantity(Carousel.CountDisplayedSets, "#,0")} ({"difficulty".ToQuantity(Carousel.CountDisplayedBeatmaps, "#,0")})"; } private bool boundLocalBindables; From 3d032d0024bec4aa11c52725476025c92dbb9452 Mon Sep 17 00:00:00 2001 From: Mohammed Keyvanzadeh Date: Mon, 27 Mar 2023 18:10:32 +0330 Subject: [PATCH 301/476] github: update workflows and make tweaks - Update the GitHub Actions workflows to their latest versions. - Replace the usage of the deprecated `set-output` command with the new recommended way to set the output. - Format the YAML files. --- .github/ISSUE_TEMPLATE/config.yml | 19 +++--- .github/dependabot.yml | 88 ++++++++++++++-------------- .github/workflows/ci.yml | 46 +++++++-------- .github/workflows/diffcalc.yml | 29 +++++---- .github/workflows/report-nunit.yml | 23 ++++---- .github/workflows/sentry-release.yml | 4 +- 6 files changed, 104 insertions(+), 105 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 47a6a4c3d3..3f76831a81 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,12 +1,11 @@ blank_issues_enabled: false contact_links: - - name: Help - url: https://github.com/ppy/osu/discussions/categories/q-a - about: osu! not working as you'd expect? Not sure it's a bug? Check the Q&A section! - - name: Suggestions or feature request - url: https://github.com/ppy/osu/discussions/categories/ideas - about: Got something you think should change or be added? Search for or start a new discussion! - - name: osu!stable issues - url: https://github.com/ppy/osu-stable-issues - about: For osu!(stable) - ie. the current "live" game version, check out the dedicated repository. Note that this is for serious bug reports only, not tech support. - + - name: Help + url: https://github.com/ppy/osu/discussions/categories/q-a + about: osu! not working as you'd expect? Not sure it's a bug? Check the Q&A section! + - name: Suggestions or feature request + url: https://github.com/ppy/osu/discussions/categories/ideas + about: Got something you think should change or be added? Search for or start a new discussion! + - name: osu!stable issues + url: https://github.com/ppy/osu-stable-issues + about: For osu!(stable) - ie. the current "live" game version, check out the dedicated repository. Note that this is for serious bug reports only, not tech support. diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 814fc81f51..ed1c3cf658 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,46 +1,46 @@ version: 2 updates: -- package-ecosystem: nuget - directory: "/" - schedule: - interval: monthly - time: "17:00" - open-pull-requests-limit: 0 # disabled until https://github.com/dependabot/dependabot-core/issues/369 is resolved. - ignore: - - dependency-name: Microsoft.EntityFrameworkCore.Design - versions: - - "> 2.2.6" - - dependency-name: Microsoft.EntityFrameworkCore.Sqlite - versions: - - "> 2.2.6" - - dependency-name: Microsoft.EntityFrameworkCore.Sqlite.Core - versions: - - "> 2.2.6" - - dependency-name: Microsoft.Extensions.DependencyInjection - versions: - - ">= 5.a, < 6" - - dependency-name: NUnit3TestAdapter - versions: - - ">= 3.16.a, < 3.17" - - dependency-name: Microsoft.NET.Test.Sdk - versions: - - 16.9.1 - - dependency-name: Microsoft.Extensions.DependencyInjection - versions: - - 3.1.11 - - 3.1.12 - - dependency-name: Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson - versions: - - 3.1.11 - - dependency-name: Microsoft.NETCore.Targets - versions: - - 5.0.0 - - dependency-name: Microsoft.AspNetCore.SignalR.Protocols.MessagePack - versions: - - 5.0.2 - - dependency-name: NUnit - versions: - - 3.13.1 - - dependency-name: Microsoft.AspNetCore.SignalR.Client - versions: - - 3.1.11 + - package-ecosystem: nuget + directory: "/" + schedule: + interval: monthly + time: "17:00" + open-pull-requests-limit: 0 # disabled until https://github.com/dependabot/dependabot-core/issues/369 is resolved. + ignore: + - dependency-name: Microsoft.EntityFrameworkCore.Design + versions: + - "> 2.2.6" + - dependency-name: Microsoft.EntityFrameworkCore.Sqlite + versions: + - "> 2.2.6" + - dependency-name: Microsoft.EntityFrameworkCore.Sqlite.Core + versions: + - "> 2.2.6" + - dependency-name: Microsoft.Extensions.DependencyInjection + versions: + - ">= 5.a, < 6" + - dependency-name: NUnit3TestAdapter + versions: + - ">= 3.16.a, < 3.17" + - dependency-name: Microsoft.NET.Test.Sdk + versions: + - 16.9.1 + - dependency-name: Microsoft.Extensions.DependencyInjection + versions: + - 3.1.11 + - 3.1.12 + - dependency-name: Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson + versions: + - 3.1.11 + - dependency-name: Microsoft.NETCore.Targets + versions: + - 5.0.0 + - dependency-name: Microsoft.AspNetCore.SignalR.Protocols.MessagePack + versions: + - 5.0.2 + - dependency-name: NUnit + versions: + - 3.13.1 + - dependency-name: Microsoft.AspNetCore.SignalR.Client + versions: + - 3.1.11 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5c11f91994..742b428d1d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,17 +13,17 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 # FIXME: Tools won't run in .NET 6.0 unless you install 3.1.x LTS side by side. # https://itnext.io/how-to-support-multiple-net-sdks-in-github-actions-workflows-b988daa884e - name: Install .NET 3.1.x LTS - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v3 with: dotnet-version: "3.1.x" - name: Install .NET 6.0.x - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v3 with: dotnet-version: "6.0.x" @@ -59,28 +59,28 @@ jobs: run: dotnet jb inspectcode $(pwd)/osu.Desktop.slnf --no-build --output="inspectcodereport.xml" --caches-home="inspectcode" --verbosity=WARN - name: NVika - run: dotnet nvika parsereport "${{github.workspace}}/inspectcodereport.xml" --treatwarningsaserrors + run: dotnet nvika parsereport "${{ github.workspace }}/inspectcodereport.xml" --treatwarningsaserrors test: name: Test - runs-on: ${{matrix.os.fullname}} + runs-on: ${{ matrix.os.fullname }} env: - OSU_EXECUTION_MODE: ${{matrix.threadingMode}} + OSU_EXECUTION_MODE: ${{ matrix.threadingMode }} strategy: - fail-fast: false - matrix: - os: - - { prettyname: Windows, fullname: windows-latest } - - { prettyname: macOS, fullname: macos-latest } - - { prettyname: Linux, fullname: ubuntu-latest } - threadingMode: ['SingleThread', 'MultiThreaded'] + fail-fast: false + matrix: + os: + - { prettyname: Windows, fullname: windows-latest } + - { prettyname: macOS, fullname: macos-latest } + - { prettyname: Linux, fullname: ubuntu-latest } + threadingMode: ["SingleThread", "MultiThreaded"] timeout-minutes: 60 steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Install .NET 6.0.x - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v3 with: dotnet-version: "6.0.x" @@ -88,17 +88,17 @@ jobs: run: dotnet build -c Debug -warnaserror osu.Desktop.slnf - name: Test - run: dotnet test $pwd/**/*.Tests/bin/Debug/*/*.Tests.dll --logger "trx;LogFileName=TestResults-${{matrix.os.prettyname}}-${{matrix.threadingMode}}.trx" -- NUnit.ConsoleOut=0 + run: dotnet test $pwd/**/*.Tests/bin/Debug/*/*.Tests.dll --logger "trx;LogFileName=TestResults-${{ matrix.os.prettyname }}-${{ matrix.threadingMode }}.trx" -- NUnit.ConsoleOut=0 shell: pwsh # Attempt to upload results even if test fails. # https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#always - name: Upload Test Results - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 if: ${{ always() }} with: - name: osu-test-results-${{matrix.os.prettyname}}-${{matrix.threadingMode}} - path: ${{github.workspace}}/TestResults/TestResults-${{matrix.os.prettyname}}-${{matrix.threadingMode}}.trx + name: osu-test-results-${{ matrix.os.prettyname }}-${{ matrix.threadingMode }} + path: ${{ github.workspace }}/TestResults/TestResults-${{ matrix.os.prettyname }}-${{ matrix.threadingMode }}.trx build-only-android: name: Build only (Android) @@ -106,10 +106,10 @@ jobs: timeout-minutes: 60 steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Install .NET 6.0.x - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v3 with: dotnet-version: "6.0.x" @@ -125,10 +125,10 @@ jobs: timeout-minutes: 60 steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Install .NET 6.0.x - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v3 with: dotnet-version: "6.0.x" diff --git a/.github/workflows/diffcalc.yml b/.github/workflows/diffcalc.yml index 9e11ab6663..b213d76794 100644 --- a/.github/workflows/diffcalc.yml +++ b/.github/workflows/diffcalc.yml @@ -2,12 +2,11 @@ # Usage: # !pp check 0 | Runs only the osu! ruleset. # !pp check 0 2 | Runs only the osu! and catch rulesets. -# name: Difficulty Calculation on: issue_comment: - types: [ created ] + types: [created] env: CONCURRENCY: 4 @@ -48,8 +47,8 @@ jobs: CONTINUE="no" fi - echo "::set-output name=continue::${CONTINUE}" - echo "::set-output name=matrix::${MATRIX_JSON}" + echo "continue=${CONTINUE}" >> $GITHUB_OUTPUT + echo "matrix=${MATRIX_JSON}" >> $GITHUB_OUTPUT diffcalc: name: Run runs-on: self-hosted @@ -80,34 +79,34 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - echo "::set-output name=branchname::$(curl -H "Authorization: token ${GITHUB_TOKEN}" ${{ github.event.issue.pull_request.url }} | jq '.head.ref' | sed 's/\"//g')" - echo "::set-output name=repo::$(curl -H "Authorization: token ${GITHUB_TOKEN}" ${{ github.event.issue.pull_request.url }} | jq '.head.repo.full_name' | sed 's/\"//g')" + echo "branchname=$(curl -H "Authorization: token ${GITHUB_TOKEN}" ${{ github.event.issue.pull_request.url }} | jq '.head.ref' | sed 's/\"//g')" >> $GITHUB_OUTPUT + echo "repo=$(curl -H "Authorization: token ${GITHUB_TOKEN}" ${{ github.event.issue.pull_request.url }} | jq '.head.repo.full_name' | sed 's/\"//g')" >> $GITHUB_OUTPUT # Checkout osu - name: Checkout osu (master) - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: - path: 'master/osu' + path: "master/osu" - name: Checkout osu (pr) - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: - path: 'pr/osu' + path: "pr/osu" repository: ${{ steps.upstreambranch.outputs.repo }} ref: ${{ steps.upstreambranch.outputs.branchname }} - name: Checkout osu-difficulty-calculator (master) - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: repository: ppy/osu-difficulty-calculator - path: 'master/osu-difficulty-calculator' + path: "master/osu-difficulty-calculator" - name: Checkout osu-difficulty-calculator (pr) - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: repository: ppy/osu-difficulty-calculator - path: 'pr/osu-difficulty-calculator' + path: "pr/osu-difficulty-calculator" - name: Install .NET 5.0.x - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v3 with: dotnet-version: "5.0.x" diff --git a/.github/workflows/report-nunit.yml b/.github/workflows/report-nunit.yml index 99e39f6f56..fee10aa487 100644 --- a/.github/workflows/report-nunit.yml +++ b/.github/workflows/report-nunit.yml @@ -2,6 +2,7 @@ # See: # * https://github.com/dorny/test-reporter#recommended-setup-for-public-repositories # * https://docs.github.com/en/actions/reference/authentication-in-a-workflow#permissions-for-the-github_token + name: Annotate CI run with test results on: workflow_run: @@ -18,21 +19,21 @@ jobs: runs-on: ubuntu-latest if: ${{ github.event.workflow_run.conclusion != 'cancelled' }} strategy: - fail-fast: false - matrix: - os: - - { prettyname: Windows } - - { prettyname: macOS } - - { prettyname: Linux } - threadingMode: ['SingleThread', 'MultiThreaded'] + fail-fast: false + matrix: + os: + - { prettyname: Windows } + - { prettyname: macOS } + - { prettyname: Linux } + threadingMode: ["SingleThread", "MultiThreaded"] timeout-minutes: 5 steps: - name: Annotate CI run with test results uses: dorny/test-reporter@v1.6.0 with: - artifact: osu-test-results-${{matrix.os.prettyname}}-${{matrix.threadingMode}} - name: Test Results (${{matrix.os.prettyname}}, ${{matrix.threadingMode}}) + artifact: osu-test-results-${{ matrix.os.prettyname }}-${{ matrix.threadingMode }} + name: Test Results (${{ matrix.os.prettyname }}, ${{ matrix.threadingMode }}) path: "*.trx" reporter: dotnet-trx - list-suites: 'failed' - list-tests: 'failed' + list-suites: "failed" + list-tests: "failed" diff --git a/.github/workflows/sentry-release.yml b/.github/workflows/sentry-release.yml index cce3f23e5f..001a63cc64 100644 --- a/.github/workflows/sentry-release.yml +++ b/.github/workflows/sentry-release.yml @@ -3,7 +3,7 @@ name: Add Release to Sentry on: push: tags: - - '*' + - "*" permissions: contents: read # to fetch code (actions/checkout) @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 From 433f9e53679f4595cf1b9b42da5e713981dcc95d Mon Sep 17 00:00:00 2001 From: Mohammed Keyvanzadeh Date: Mon, 27 Mar 2023 18:37:01 +0330 Subject: [PATCH 302/476] fixup! revert formatting changes --- .github/ISSUE_TEMPLATE/config.yml | 19 +++--- .github/dependabot.yml | 88 ++++++++++++++-------------- .github/workflows/ci.yml | 26 ++++---- .github/workflows/diffcalc.yml | 11 ++-- .github/workflows/report-nunit.yml | 23 ++++---- .github/workflows/sentry-release.yml | 2 +- 6 files changed, 85 insertions(+), 84 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 3f76831a81..47a6a4c3d3 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,11 +1,12 @@ blank_issues_enabled: false contact_links: - - name: Help - url: https://github.com/ppy/osu/discussions/categories/q-a - about: osu! not working as you'd expect? Not sure it's a bug? Check the Q&A section! - - name: Suggestions or feature request - url: https://github.com/ppy/osu/discussions/categories/ideas - about: Got something you think should change or be added? Search for or start a new discussion! - - name: osu!stable issues - url: https://github.com/ppy/osu-stable-issues - about: For osu!(stable) - ie. the current "live" game version, check out the dedicated repository. Note that this is for serious bug reports only, not tech support. + - name: Help + url: https://github.com/ppy/osu/discussions/categories/q-a + about: osu! not working as you'd expect? Not sure it's a bug? Check the Q&A section! + - name: Suggestions or feature request + url: https://github.com/ppy/osu/discussions/categories/ideas + about: Got something you think should change or be added? Search for or start a new discussion! + - name: osu!stable issues + url: https://github.com/ppy/osu-stable-issues + about: For osu!(stable) - ie. the current "live" game version, check out the dedicated repository. Note that this is for serious bug reports only, not tech support. + diff --git a/.github/dependabot.yml b/.github/dependabot.yml index ed1c3cf658..814fc81f51 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,46 +1,46 @@ version: 2 updates: - - package-ecosystem: nuget - directory: "/" - schedule: - interval: monthly - time: "17:00" - open-pull-requests-limit: 0 # disabled until https://github.com/dependabot/dependabot-core/issues/369 is resolved. - ignore: - - dependency-name: Microsoft.EntityFrameworkCore.Design - versions: - - "> 2.2.6" - - dependency-name: Microsoft.EntityFrameworkCore.Sqlite - versions: - - "> 2.2.6" - - dependency-name: Microsoft.EntityFrameworkCore.Sqlite.Core - versions: - - "> 2.2.6" - - dependency-name: Microsoft.Extensions.DependencyInjection - versions: - - ">= 5.a, < 6" - - dependency-name: NUnit3TestAdapter - versions: - - ">= 3.16.a, < 3.17" - - dependency-name: Microsoft.NET.Test.Sdk - versions: - - 16.9.1 - - dependency-name: Microsoft.Extensions.DependencyInjection - versions: - - 3.1.11 - - 3.1.12 - - dependency-name: Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson - versions: - - 3.1.11 - - dependency-name: Microsoft.NETCore.Targets - versions: - - 5.0.0 - - dependency-name: Microsoft.AspNetCore.SignalR.Protocols.MessagePack - versions: - - 5.0.2 - - dependency-name: NUnit - versions: - - 3.13.1 - - dependency-name: Microsoft.AspNetCore.SignalR.Client - versions: - - 3.1.11 +- package-ecosystem: nuget + directory: "/" + schedule: + interval: monthly + time: "17:00" + open-pull-requests-limit: 0 # disabled until https://github.com/dependabot/dependabot-core/issues/369 is resolved. + ignore: + - dependency-name: Microsoft.EntityFrameworkCore.Design + versions: + - "> 2.2.6" + - dependency-name: Microsoft.EntityFrameworkCore.Sqlite + versions: + - "> 2.2.6" + - dependency-name: Microsoft.EntityFrameworkCore.Sqlite.Core + versions: + - "> 2.2.6" + - dependency-name: Microsoft.Extensions.DependencyInjection + versions: + - ">= 5.a, < 6" + - dependency-name: NUnit3TestAdapter + versions: + - ">= 3.16.a, < 3.17" + - dependency-name: Microsoft.NET.Test.Sdk + versions: + - 16.9.1 + - dependency-name: Microsoft.Extensions.DependencyInjection + versions: + - 3.1.11 + - 3.1.12 + - dependency-name: Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson + versions: + - 3.1.11 + - dependency-name: Microsoft.NETCore.Targets + versions: + - 5.0.0 + - dependency-name: Microsoft.AspNetCore.SignalR.Protocols.MessagePack + versions: + - 5.0.2 + - dependency-name: NUnit + versions: + - 3.13.1 + - dependency-name: Microsoft.AspNetCore.SignalR.Client + versions: + - 3.1.11 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 742b428d1d..e60e0a39ae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,21 +59,21 @@ jobs: run: dotnet jb inspectcode $(pwd)/osu.Desktop.slnf --no-build --output="inspectcodereport.xml" --caches-home="inspectcode" --verbosity=WARN - name: NVika - run: dotnet nvika parsereport "${{ github.workspace }}/inspectcodereport.xml" --treatwarningsaserrors + run: dotnet nvika parsereport "${{github.workspace}}/inspectcodereport.xml" --treatwarningsaserrors test: name: Test - runs-on: ${{ matrix.os.fullname }} + runs-on: ${{matrix.os.fullname}} env: - OSU_EXECUTION_MODE: ${{ matrix.threadingMode }} + OSU_EXECUTION_MODE: ${{matrix.threadingMode}} strategy: - fail-fast: false - matrix: - os: - - { prettyname: Windows, fullname: windows-latest } - - { prettyname: macOS, fullname: macos-latest } - - { prettyname: Linux, fullname: ubuntu-latest } - threadingMode: ["SingleThread", "MultiThreaded"] + fail-fast: false + matrix: + os: + - { prettyname: Windows, fullname: windows-latest } + - { prettyname: macOS, fullname: macos-latest } + - { prettyname: Linux, fullname: ubuntu-latest } + threadingMode: ['SingleThread', 'MultiThreaded'] timeout-minutes: 60 steps: - name: Checkout @@ -88,7 +88,7 @@ jobs: run: dotnet build -c Debug -warnaserror osu.Desktop.slnf - name: Test - run: dotnet test $pwd/**/*.Tests/bin/Debug/*/*.Tests.dll --logger "trx;LogFileName=TestResults-${{ matrix.os.prettyname }}-${{ matrix.threadingMode }}.trx" -- NUnit.ConsoleOut=0 + run: dotnet test $pwd/**/*.Tests/bin/Debug/*/*.Tests.dll --logger "trx;LogFileName=TestResults-${{matrix.os.prettyname}}-${{matrix.threadingMode}}.trx" -- NUnit.ConsoleOut=0 shell: pwsh # Attempt to upload results even if test fails. @@ -97,8 +97,8 @@ jobs: uses: actions/upload-artifact@v3 if: ${{ always() }} with: - name: osu-test-results-${{ matrix.os.prettyname }}-${{ matrix.threadingMode }} - path: ${{ github.workspace }}/TestResults/TestResults-${{ matrix.os.prettyname }}-${{ matrix.threadingMode }}.trx + name: osu-test-results-${{matrix.os.prettyname}}-${{matrix.threadingMode}} + path: ${{github.workspace}}/TestResults/TestResults-${{matrix.os.prettyname}}-${{matrix.threadingMode}}.trx build-only-android: name: Build only (Android) diff --git a/.github/workflows/diffcalc.yml b/.github/workflows/diffcalc.yml index b213d76794..2c6ec17e18 100644 --- a/.github/workflows/diffcalc.yml +++ b/.github/workflows/diffcalc.yml @@ -2,11 +2,12 @@ # Usage: # !pp check 0 | Runs only the osu! ruleset. # !pp check 0 2 | Runs only the osu! and catch rulesets. +# name: Difficulty Calculation on: issue_comment: - types: [created] + types: [ created ] env: CONCURRENCY: 4 @@ -86,11 +87,11 @@ jobs: - name: Checkout osu (master) uses: actions/checkout@v3 with: - path: "master/osu" + path: 'master/osu' - name: Checkout osu (pr) uses: actions/checkout@v3 with: - path: "pr/osu" + path: 'pr/osu' repository: ${{ steps.upstreambranch.outputs.repo }} ref: ${{ steps.upstreambranch.outputs.branchname }} @@ -98,12 +99,12 @@ jobs: uses: actions/checkout@v3 with: repository: ppy/osu-difficulty-calculator - path: "master/osu-difficulty-calculator" + path: 'master/osu-difficulty-calculator' - name: Checkout osu-difficulty-calculator (pr) uses: actions/checkout@v3 with: repository: ppy/osu-difficulty-calculator - path: "pr/osu-difficulty-calculator" + path: 'pr/osu-difficulty-calculator' - name: Install .NET 5.0.x uses: actions/setup-dotnet@v3 diff --git a/.github/workflows/report-nunit.yml b/.github/workflows/report-nunit.yml index fee10aa487..99e39f6f56 100644 --- a/.github/workflows/report-nunit.yml +++ b/.github/workflows/report-nunit.yml @@ -2,7 +2,6 @@ # See: # * https://github.com/dorny/test-reporter#recommended-setup-for-public-repositories # * https://docs.github.com/en/actions/reference/authentication-in-a-workflow#permissions-for-the-github_token - name: Annotate CI run with test results on: workflow_run: @@ -19,21 +18,21 @@ jobs: runs-on: ubuntu-latest if: ${{ github.event.workflow_run.conclusion != 'cancelled' }} strategy: - fail-fast: false - matrix: - os: - - { prettyname: Windows } - - { prettyname: macOS } - - { prettyname: Linux } - threadingMode: ["SingleThread", "MultiThreaded"] + fail-fast: false + matrix: + os: + - { prettyname: Windows } + - { prettyname: macOS } + - { prettyname: Linux } + threadingMode: ['SingleThread', 'MultiThreaded'] timeout-minutes: 5 steps: - name: Annotate CI run with test results uses: dorny/test-reporter@v1.6.0 with: - artifact: osu-test-results-${{ matrix.os.prettyname }}-${{ matrix.threadingMode }} - name: Test Results (${{ matrix.os.prettyname }}, ${{ matrix.threadingMode }}) + artifact: osu-test-results-${{matrix.os.prettyname}}-${{matrix.threadingMode}} + name: Test Results (${{matrix.os.prettyname}}, ${{matrix.threadingMode}}) path: "*.trx" reporter: dotnet-trx - list-suites: "failed" - list-tests: "failed" + list-suites: 'failed' + list-tests: 'failed' diff --git a/.github/workflows/sentry-release.yml b/.github/workflows/sentry-release.yml index 001a63cc64..ff4165c414 100644 --- a/.github/workflows/sentry-release.yml +++ b/.github/workflows/sentry-release.yml @@ -3,7 +3,7 @@ name: Add Release to Sentry on: push: tags: - - "*" + - '*' permissions: contents: read # to fetch code (actions/checkout) From 9426633a05c04c640666463fde89b20f07e96595 Mon Sep 17 00:00:00 2001 From: rrex971 <75212090+rrex971@users.noreply.github.com> Date: Mon, 27 Mar 2023 20:39:13 +0530 Subject: [PATCH 303/476] Allow AR and CS values below 1.0 for Catch the Beat Difficulty Adjustment mod --- osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs b/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs index e59a0a0431..6efb415880 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Catch.Mods public DifficultyBindable CircleSize { get; } = new DifficultyBindable { Precision = 0.1f, - MinValue = 1, + MinValue = 0, MaxValue = 10, ExtendedMaxValue = 11, ReadCurrentFromDifficulty = diff => diff.CircleSize, @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Catch.Mods public DifficultyBindable ApproachRate { get; } = new DifficultyBindable { Precision = 0.1f, - MinValue = 1, + MinValue = 0, MaxValue = 10, ExtendedMaxValue = 11, ReadCurrentFromDifficulty = diff => diff.ApproachRate, From f31e77dce5317b3beb82838e678c183536deb6d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 27 Mar 2023 18:08:06 +0200 Subject: [PATCH 304/476] Add direction switching to `TestSceneManiaPlayer` To test upscroll easier, and with all parts in conjunction. `ManiaSkinnableTestScene`s already had the capability to switch directions, but they did not show all parts together, which meant regressions were missed. --- .../TestSceneManiaPlayer.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaPlayer.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaPlayer.cs index 98046320cb..4e50fd924c 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaPlayer.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaPlayer.cs @@ -3,6 +3,9 @@ #nullable disable +using osu.Framework.Extensions.ObjectExtensions; +using osu.Game.Rulesets.Mania.Configuration; +using osu.Game.Rulesets.Mania.UI; using osu.Game.Tests.Visual; namespace osu.Game.Rulesets.Mania.Tests @@ -10,5 +13,19 @@ namespace osu.Game.Rulesets.Mania.Tests public partial class TestSceneManiaPlayer : PlayerTestScene { protected override Ruleset CreatePlayerRuleset() => new ManiaRuleset(); + + public override void SetUpSteps() + { + base.SetUpSteps(); + + AddStep("change direction to down", () => changeDirectionTo(ManiaScrollingDirection.Down)); + AddStep("change direction to up", () => changeDirectionTo(ManiaScrollingDirection.Up)); + } + + private void changeDirectionTo(ManiaScrollingDirection direction) + { + var rulesetConfig = (ManiaRulesetConfigManager)RulesetConfigs.GetConfigFor(new ManiaRuleset()).AsNonNull(); + rulesetConfig.SetValue(ManiaRulesetSetting.ScrollDirection, direction); + } } } From c54934cb45f361daaacbac7059b8139c655c6901 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 27 Mar 2023 18:15:01 +0200 Subject: [PATCH 305/476] Fix hit lighting misalignment on argon skin with upscroll --- .../Skinning/Argon/ArgonHitExplosion.cs | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitExplosion.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitExplosion.cs index 8e27b4abd7..fb38b536dc 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitExplosion.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitExplosion.cs @@ -43,8 +43,6 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon { largeFaint = new Container { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, RelativeSizeAxes = Axes.Both, Height = ArgonNotePiece.NOTE_ACCENT_RATIO, Masking = true, @@ -78,16 +76,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon private void onDirectionChanged(ValueChangedEvent direction) { - if (direction.NewValue == ScrollingDirection.Up) - { - Anchor = Anchor.TopCentre; - Y = ArgonNotePiece.NOTE_HEIGHT / 2; - } - else - { - Anchor = Anchor.BottomCentre; - Y = -ArgonNotePiece.NOTE_HEIGHT / 2; - } + Anchor = largeFaint.Anchor = largeFaint.Origin = direction.NewValue == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre; + Y = direction.NewValue == ScrollingDirection.Up ? ArgonNotePiece.NOTE_HEIGHT / 2 : -ArgonNotePiece.NOTE_HEIGHT / 2; } public void Animate(JudgementResult result) From 2b525b626c1e7a46c7402edbb4def3fa6d6a9581 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 28 Mar 2023 06:22:00 +0200 Subject: [PATCH 306/476] Revert to previous conditional style --- .../Skinning/Argon/ArgonHitExplosion.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitExplosion.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitExplosion.cs index fb38b536dc..d490d3f944 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitExplosion.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitExplosion.cs @@ -76,8 +76,20 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon private void onDirectionChanged(ValueChangedEvent direction) { - Anchor = largeFaint.Anchor = largeFaint.Origin = direction.NewValue == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre; - Y = direction.NewValue == ScrollingDirection.Up ? ArgonNotePiece.NOTE_HEIGHT / 2 : -ArgonNotePiece.NOTE_HEIGHT / 2; + if (direction.NewValue == ScrollingDirection.Up) + { + Anchor = Anchor.TopCentre; + largeFaint.Anchor = Anchor.TopCentre; + largeFaint.Origin = Anchor.TopCentre; + Y = ArgonNotePiece.NOTE_HEIGHT / 2; + } + else + { + Anchor = Anchor.BottomCentre; + largeFaint.Anchor = Anchor.BottomCentre; + largeFaint.Origin = Anchor.BottomCentre; + Y = -ArgonNotePiece.NOTE_HEIGHT / 2; + } } public void Animate(JudgementResult result) From c742b3f0a8a10854ba73fc59c0f5a521c445a968 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 Mar 2023 14:15:19 +0900 Subject: [PATCH 307/476] Update `DrawableRulesetDependencies` xmldoc to read more correctly --- osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs b/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs index 96b02ee4dc..5c031c5f08 100644 --- a/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs +++ b/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs @@ -25,21 +25,28 @@ namespace osu.Game.Rulesets.UI /// /// The texture store to be used for the ruleset. /// + /// + /// Reads textures from the "Textures" folder in ruleset resources. + /// If not available locally, lookups will fallback to the global texture store. + /// public TextureStore TextureStore { get; } /// /// The sample store to be used for the ruleset. /// /// - /// This is the local sample store pointing to the ruleset sample resources, - /// the cached sample store () retrieves from - /// this store and falls back to the parent store if this store doesn't have the requested sample. + /// Reads samples from the "Samples" folder in ruleset resources. + /// If not available locally, lookups will fallback to the global sample store. /// public ISampleStore SampleStore { get; } /// /// The shader manager to be used for the ruleset. /// + /// + /// Reads shaders from the "Shaders" folder in ruleset resources. + /// If not available locally, lookups will fallback to the global shader manager. + /// public ShaderManager ShaderManager { get; } /// From 5dfac02b11a3c5c70ee84f6787d5e355d4b1b7ba Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 Mar 2023 14:15:38 +0900 Subject: [PATCH 308/476] Preload triangle shader on startup --- osu.Game/Screens/Loader.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Screens/Loader.cs b/osu.Game/Screens/Loader.cs index b70c1f7ddf..bee6207a35 100644 --- a/osu.Game/Screens/Loader.cs +++ b/osu.Game/Screens/Loader.cs @@ -130,6 +130,10 @@ namespace osu.Game.Screens loadTargets.Add(manager.Load(@"CursorTrail", FragmentShaderDescriptor.TEXTURE)); + loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2, "TriangleBorder")); + + loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_3, FragmentShaderDescriptor.TEXTURE)); + loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_3, FragmentShaderDescriptor.TEXTURE)); } From 0d77ec013a936fd77b852837b7007229028f34bd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 Mar 2023 14:53:08 +0900 Subject: [PATCH 309/476] Fix ruleset-local shader manager not correctly falling back to existing cached shaders --- .../UI/DrawableRulesetDependencies.cs | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs b/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs index 5c031c5f08..e6ee770e19 100644 --- a/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs +++ b/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs @@ -68,8 +68,7 @@ namespace osu.Game.Rulesets.UI SampleStore.PlaybackConcurrency = OsuGameBase.SAMPLE_CONCURRENCY; CacheAs(SampleStore = new FallbackSampleStore(SampleStore, parent.Get())); - ShaderManager = new ShaderManager(host.Renderer, new NamespacedResourceStore(resources, @"Shaders")); - CacheAs(ShaderManager = new FallbackShaderManager(host.Renderer, ShaderManager, parent.Get())); + CacheAs(ShaderManager = new RulesetShaderManager(host.Renderer, new NamespacedResourceStore(resources, @"Shaders"), parent.Get())); RulesetConfigManager = parent.Get().GetConfigFor(ruleset); if (RulesetConfigManager != null) @@ -197,24 +196,27 @@ namespace osu.Game.Rulesets.UI } } - private class FallbackShaderManager : ShaderManager + private class RulesetShaderManager : ShaderManager { - private readonly ShaderManager primary; - private readonly ShaderManager fallback; + private readonly ShaderManager parent; - public FallbackShaderManager(IRenderer renderer, ShaderManager primary, ShaderManager fallback) - : base(renderer, new ResourceStore()) + public RulesetShaderManager(IRenderer renderer, NamespacedResourceStore rulesetResources, ShaderManager parent) + : base(renderer, rulesetResources) { - this.primary = primary; - this.fallback = fallback; + this.parent = parent; } - public override byte[]? LoadRaw(string name) => primary.LoadRaw(name) ?? fallback.LoadRaw(name); - - protected override void Dispose(bool disposing) + public override IShader Load(string vertex, string fragment) { - base.Dispose(disposing); - if (primary.IsNotNull()) primary.Dispose(); + try + { + return base.Load(vertex, fragment); + } + catch + { + // Shader lookup is very non-standard. Rather than returning null on missing shaders, exceptions are thrown. + return parent.Load(vertex, fragment); + } } } } From 8c1df3c8d9d6c875d151f086035dc6b7d13ceb04 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 Mar 2023 15:38:19 +0900 Subject: [PATCH 310/476] Fix web account registration not bypassing the URL warning --- osu.Game/Overlays/AccountCreation/ScreenEntry.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs index 2e20f83e9e..219cbe7eef 100644 --- a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs +++ b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs @@ -209,7 +209,7 @@ namespace osu.Game.Overlays.AccountCreation if (!string.IsNullOrEmpty(errors.Message)) passwordDescription.AddErrors(new[] { errors.Message }); - game.OpenUrlExternally($"{errors.Redirect}?username={usernameTextBox.Text}&email={emailTextBox.Text}"); + game.OpenUrlExternally($"{errors.Redirect}?username={usernameTextBox.Text}&email={emailTextBox.Text}", true); } } else From 45e3e3623b70c206a9889dd602186d895506d448 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 Mar 2023 18:48:55 +0900 Subject: [PATCH 311/476] Fix spinners being selectable for too long after they fade in the editor The actual visual extension is only applied to `HitCircle`s (which does include slider start / end), and should not be applied to spinners in the first place. Addresses https://github.com/ppy/osu/discussions/22949. --- osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs index 3e161089cd..d6409279a4 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints protected override bool AlwaysShowWhenSelected => true; protected override bool ShouldBeAlive => base.ShouldBeAlive - || (ShowHitMarkers.Value && editorClock.CurrentTime >= Item.StartTime && editorClock.CurrentTime - Item.GetEndTime() < HitCircleOverlapMarker.FADE_OUT_EXTENSION); + || (DrawableObject is not DrawableSpinner && ShowHitMarkers.Value && editorClock.CurrentTime >= Item.StartTime && editorClock.CurrentTime - Item.GetEndTime() < HitCircleOverlapMarker.FADE_OUT_EXTENSION); protected OsuSelectionBlueprint(T hitObject) : base(hitObject) From 46ede27869efca77ec226081454cf682ad907a77 Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Tue, 28 Mar 2023 22:24:05 +0900 Subject: [PATCH 312/476] add feature to adjust `ScalingContainer` background dim --- osu.Game/Configuration/OsuConfigManager.cs | 2 ++ .../Graphics/Containers/ScalingContainer.cs | 11 +++++++++-- .../Sections/Graphics/LayoutSettings.cs | 18 ++++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 70ad6bfc96..a06bfcc5f0 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -157,6 +157,7 @@ namespace osu.Game.Configuration SetDefault(OsuSetting.Scaling, ScalingMode.Off); SetDefault(OsuSetting.SafeAreaConsiderations, true); + SetDefault(OsuSetting.ScalingMenuBackgroundDim, 0.9f, 0.5f, 1f); SetDefault(OsuSetting.ScalingSizeX, 0.8f, 0.2f, 1f); SetDefault(OsuSetting.ScalingSizeY, 0.8f, 0.2f, 1f); @@ -364,6 +365,7 @@ namespace osu.Game.Configuration ScalingPositionY, ScalingSizeX, ScalingSizeY, + ScalingMenuBackgroundDim, UIScale, IntroSequence, NotifyOnUsernameMentioned, diff --git a/osu.Game/Graphics/Containers/ScalingContainer.cs b/osu.Game/Graphics/Containers/ScalingContainer.cs index fb5c3e3b60..9c6830ce05 100644 --- a/osu.Game/Graphics/Containers/ScalingContainer.cs +++ b/osu.Game/Graphics/Containers/ScalingContainer.cs @@ -46,6 +46,8 @@ namespace osu.Game.Graphics.Containers private BackgroundScreenStack backgroundStack; + private Bindable scalingMenuBackgroundDim; + private RectangleF? customRect; private bool customRectIsRelativePosition; @@ -138,6 +140,9 @@ namespace osu.Game.Graphics.Containers safeAreaPadding = safeArea.SafeAreaPadding.GetBoundCopy(); safeAreaPadding.BindValueChanged(_ => Scheduler.AddOnce(updateSize)); + + scalingMenuBackgroundDim = config.GetBindable(OsuSetting.ScalingMenuBackgroundDim); + scalingMenuBackgroundDim.ValueChanged += _ => Scheduler.AddOnce(updateSize); } protected override void LoadComplete() @@ -148,7 +153,9 @@ namespace osu.Game.Graphics.Containers sizableContainer.FinishTransforms(); } - private bool requiresBackgroundVisible => (scalingMode.Value == ScalingMode.Everything || scalingMode.Value == ScalingMode.ExcludeOverlays) && (sizeX.Value != 1 || sizeY.Value != 1); + private bool requiresBackgroundVisible => (scalingMode.Value == ScalingMode.Everything || scalingMode.Value == ScalingMode.ExcludeOverlays) + && (sizeX.Value != 1 || sizeY.Value != 1) + && scalingMenuBackgroundDim.Value != 1f; private void updateSize() { @@ -161,7 +168,6 @@ namespace osu.Game.Graphics.Containers { AddInternal(backgroundStack = new BackgroundScreenStack { - Colour = OsuColour.Gray(0.1f), Alpha = 0, Depth = float.MaxValue }); @@ -170,6 +176,7 @@ namespace osu.Game.Graphics.Containers } backgroundStack.FadeIn(TRANSITION_DURATION); + backgroundStack.FadeColour(OsuColour.Gray(1.0f - scalingMenuBackgroundDim.Value), 800, Easing.OutQuint); } else backgroundStack?.FadeOut(TRANSITION_DURATION); diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 6465d62ef0..6dbaf27afc 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -30,6 +30,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics protected override LocalisableString Header => GraphicsSettingsStrings.LayoutHeader; private FillFlowContainer> scalingSettings = null!; + private SettingsSlider dimSlider = null!; private readonly Bindable currentDisplay = new Bindable(); private readonly IBindableList windowModes = new BindableList(); @@ -58,6 +59,8 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics private Bindable scalingSizeX = null!; private Bindable scalingSizeY = null!; + private Bindable scalingBackgroundDim = null!; + private const int transition_duration = 400; [BackgroundDependencyLoader] @@ -71,6 +74,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics scalingSizeY = osuConfig.GetBindable(OsuSetting.ScalingSizeY); scalingPositionX = osuConfig.GetBindable(OsuSetting.ScalingPositionX); scalingPositionY = osuConfig.GetBindable(OsuSetting.ScalingPositionY); + scalingBackgroundDim = osuConfig.GetBindable(OsuSetting.ScalingMenuBackgroundDim); if (window != null) { @@ -162,6 +166,13 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics KeyboardStep = 0.01f, DisplayAsPercentage = true }, + dimSlider = new SettingsSlider + { + LabelText = GameplaySettingsStrings.BackgroundDim, + Current = scalingBackgroundDim, + KeyboardStep = 0.01f, + DisplayAsPercentage = true + }, } }, }; @@ -219,6 +230,13 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics scalingSettings.AutoSizeAxes = scalingMode.Value != ScalingMode.Off ? Axes.Y : Axes.None; scalingSettings.ForEach(s => { + if (s == dimSlider) + { + s.TransferValueOnCommit = false; + s.CanBeShown.Value = scalingMode.Value == ScalingMode.Everything || scalingMode.Value == ScalingMode.ExcludeOverlays; + return; + } + s.TransferValueOnCommit = scalingMode.Value == ScalingMode.Everything; s.CanBeShown.Value = scalingMode.Value != ScalingMode.Off; }); From aad540629f4818f10310b56d5878c828196e7a80 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Mar 2023 12:36:56 +0900 Subject: [PATCH 313/476] Remove duplicate load rule from `ShaderPrecompiler` --- osu.Game/Screens/Loader.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Screens/Loader.cs b/osu.Game/Screens/Loader.cs index bee6207a35..372cfe748e 100644 --- a/osu.Game/Screens/Loader.cs +++ b/osu.Game/Screens/Loader.cs @@ -133,8 +133,6 @@ namespace osu.Game.Screens loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2, "TriangleBorder")); loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_3, FragmentShaderDescriptor.TEXTURE)); - - loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_3, FragmentShaderDescriptor.TEXTURE)); } protected virtual bool AllLoaded => loadTargets.All(s => s.IsLoaded); From 9b45591c2f92ae4a2cd45329557187b043751b1b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Mar 2023 14:00:07 +0900 Subject: [PATCH 314/476] Add failing test coverage of saving failed replay causing progression to results --- .../Gameplay/TestSceneStoryboardWithOutro.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs index 0d88fb01a8..283866bef2 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs @@ -13,6 +13,7 @@ using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Configuration; +using osu.Game.Graphics.Containers; using osu.Game.Rulesets; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Osu; @@ -106,6 +107,26 @@ namespace osu.Game.Tests.Visual.Gameplay AddUntilStep("wait for fail overlay", () => Player.FailOverlay.State.Value == Visibility.Visible); } + [Test] + public void TestSaveFailedReplayWithStoryboardEndedDoesNotProgress() + { + CreateTest(() => + { + AddStep("fail on first judgement", () => currentFailConditions = (_, _) => true); + AddStep("set storyboard duration to 0s", () => currentStoryboardDuration = 0); + }); + AddUntilStep("storyboard ends", () => Player.GameplayClockContainer.CurrentTime >= currentStoryboardDuration); + AddUntilStep("wait for fail", () => Player.GameplayState.HasFailed); + + AddUntilStep("wait for fail overlay", () => Player.FailOverlay.State.Value == Visibility.Visible); + AddUntilStep("wait for button clickable", () => Player.ChildrenOfType().First().ChildrenOfType().First().Enabled.Value); + AddStep("click save button", () => Player.ChildrenOfType().First().ChildrenOfType().First().TriggerClick()); + + // Test a regression where importing the fail replay would cause progression to results screen in a failed state. + AddWaitStep("wait some", 10); + AddAssert("player is still current screen", () => Player.IsCurrentScreen()); + } + [Test] public void TestShowResultsFalse() { From a8bb2e33ac669b4c47b1c5b1e6b42c94f3a7bc32 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Mar 2023 13:30:13 +0900 Subject: [PATCH 315/476] Ensure all preconditions are checked before progressing to results screen after storyboard ends --- osu.Game/Screens/Play/Player.cs | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 45a671fb89..3976e5059b 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -358,14 +358,10 @@ namespace osu.Game.Screens.Play ScoreProcessor.RevertResult(r); }; - DimmableStoryboard.HasStoryboardEnded.ValueChanged += storyboardEnded => - { - if (storyboardEnded.NewValue) - progressToResults(true); - }; + DimmableStoryboard.HasStoryboardEnded.ValueChanged += _ => scoreCompleted(); // Bind the judgement processors to ourselves - ScoreProcessor.HasCompleted.BindValueChanged(scoreCompletionChanged); + ScoreProcessor.HasCompleted.BindValueChanged(_ => scoreCompleted()); HealthProcessor.Failed += onFail; // Provide judgement processors to mods after they're loaded so that they're on the gameplay clock, @@ -706,8 +702,7 @@ namespace osu.Game.Screens.Play /// /// Handles changes in player state which may progress the completion of gameplay / this screen's lifetime. /// - /// Thrown if this method is called more than once without changing state. - private void scoreCompletionChanged(ValueChangedEvent completed) + private void scoreCompleted() { // If this player instance is in the middle of an exit, don't attempt any kind of state update. if (!this.IsCurrentScreen()) @@ -718,7 +713,7 @@ namespace osu.Game.Screens.Play // Currently, even if this scenario is hit, prepareScoreForDisplay has already been queued (and potentially run). // In scenarios where rewinding is possible (replay, spectating) this is a non-issue as no submission/import work is done, // but it still doesn't feel right that this exists here. - if (!completed.NewValue) + if (!ScoreProcessor.HasCompleted.Value) { resultsDisplayDelegate?.Cancel(); resultsDisplayDelegate = null; @@ -742,12 +737,12 @@ namespace osu.Game.Screens.Play if (!Configuration.ShowResults) return; - bool storyboardHasOutro = DimmableStoryboard.ContentDisplayed && !DimmableStoryboard.HasStoryboardEnded.Value; + bool storyboardStillRunning = DimmableStoryboard.ContentDisplayed && !DimmableStoryboard.HasStoryboardEnded.Value; - if (storyboardHasOutro) + // If the current beatmap has a storyboard, this method will be called again on storyboard completion. + // Alternatively, the user may press the outro skip button, forcing immediate display of the results screen. + if (storyboardStillRunning) { - // if the current beatmap has a storyboard, the progression to results will be handled by the storyboard ending - // or the user pressing the skip outro button. skipOutroOverlay.Show(); return; } From 4dd0c2c7a5eb3b0d57fbed9bdf4acf1bacb439e1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Mar 2023 14:11:56 +0900 Subject: [PATCH 316/476] Add assert ensuring we don't ever get to the results screen with an F rank Intentionally an assertion as I want tests to fail, but I don't want this to cause crashes for an end user if it does happen to occur. --- osu.Game/Screens/Play/Player.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 3976e5059b..b8b3a8b5c0 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -788,6 +788,8 @@ namespace osu.Game.Screens.Play // This player instance may already be in the process of exiting. return; + Debug.Assert(ScoreProcessor.Rank.Value != ScoreRank.F); + this.Push(CreateResults(prepareScoreForDisplayTask.GetResultSafely())); }, Time.Current + delay, 50); From c7003434b2f1640e88443a536fac5b716f04bcbf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Mar 2023 14:33:10 +0900 Subject: [PATCH 317/476] Fix localisation for audio device error containing incorrect newline escaping --- osu.Game/Localisation/NotificationsStrings.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game/Localisation/NotificationsStrings.cs b/osu.Game/Localisation/NotificationsStrings.cs index 6a9793b20c..5e2600bc50 100644 --- a/osu.Game/Localisation/NotificationsStrings.cs +++ b/osu.Game/Localisation/NotificationsStrings.cs @@ -50,16 +50,18 @@ namespace osu.Game.Localisation public static LocalisableString NoAutoplayMod => new TranslatableString(getKey(@"no_autoplay_mod"), @"The current ruleset doesn't have an autoplay mod available!"); /// - /// "osu! doesn't seem to be able to play audio correctly.\n\nPlease try changing your audio device to a working setting." + /// "osu! doesn't seem to be able to play audio correctly. + /// + /// Please try changing your audio device to a working setting." /// - public static LocalisableString AudioPlaybackIssue => new TranslatableString(getKey(@"audio_playback_issue"), - @"osu! doesn't seem to be able to play audio correctly.\n\nPlease try changing your audio device to a working setting."); + public static LocalisableString AudioPlaybackIssue => new TranslatableString(getKey(@"audio_playback_issue"), @"osu! doesn't seem to be able to play audio correctly. + +Please try changing your audio device to a working setting."); /// /// "The score overlay is currently disabled. You can toggle this by pressing {0}." /// - public static LocalisableString ScoreOverlayDisabled(LocalisableString arg0) => new TranslatableString(getKey(@"score_overlay_disabled"), - @"The score overlay is currently disabled. You can toggle this by pressing {0}.", arg0); + public static LocalisableString ScoreOverlayDisabled(LocalisableString arg0) => new TranslatableString(getKey(@"score_overlay_disabled"), @"The score overlay is currently disabled. You can toggle this by pressing {0}.", arg0); private static string getKey(string key) => $@"{prefix}:{key}"; } From 72c5c9848f8761c719ca1fa22ecc6f784019d0ff Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Mar 2023 14:44:42 +0900 Subject: [PATCH 318/476] Always use `LocalisableString` fallbacks when deploying debug and viewing english This allows changes to `xxxStrings.cs` files to immediately reflect in the UI, which is (at least for me) an expectation. --- osu.Game/Localisation/ResourceManagerLocalisationStore.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Localisation/ResourceManagerLocalisationStore.cs b/osu.Game/Localisation/ResourceManagerLocalisationStore.cs index d2ff783413..50a450c101 100644 --- a/osu.Game/Localisation/ResourceManagerLocalisationStore.cs +++ b/osu.Game/Localisation/ResourceManagerLocalisationStore.cs @@ -11,6 +11,7 @@ using System.Linq; using System.Resources; using System.Threading; using System.Threading.Tasks; +using osu.Framework.Development; using osu.Framework.Localisation; namespace osu.Game.Localisation @@ -65,6 +66,11 @@ namespace osu.Game.Localisation if (manager == null) return null; + // When running a debug build and in viewing english culture, use the fallbacks rather than osu-resources baked strings. + // This is what a developer expects to see when making changes to `xxxStrings.cs` files. + if (DebugUtils.IsDebugBuild && EffectiveCulture.Name == @"en") + return null; + try { return manager.GetString(key, EffectiveCulture); From 836c884aaed7c97925a7c4f8279192a053ebb8fa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Mar 2023 18:03:21 +0900 Subject: [PATCH 319/476] Fix circle-size based scale being applied twice to caught fruit Closes #22968. --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 1c52c092ec..ab754e51f7 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -136,6 +136,7 @@ namespace osu.Game.Rulesets.Catch.UI Origin = Anchor.TopCentre; Size = new Vector2(BASE_SIZE); + if (difficulty != null) Scale = calculateScale(difficulty); @@ -333,8 +334,11 @@ namespace osu.Game.Rulesets.Catch.UI base.Update(); var scaleFromDirection = new Vector2((int)VisualDirection, 1); + body.Scale = scaleFromDirection; - caughtObjectContainer.Scale = hitExplosionContainer.Scale = flipCatcherPlate ? scaleFromDirection : Vector2.One; + // Inverse of catcher scale is applied here, as catcher gets scaled by circle size and so do the incoming fruit. + caughtObjectContainer.Scale = (1 / Scale.X) * (flipCatcherPlate ? scaleFromDirection : Vector2.One); + hitExplosionContainer.Scale = flipCatcherPlate ? scaleFromDirection : Vector2.One; // Correct overshooting. if ((hyperDashDirection > 0 && hyperDashTargetPosition < X) || From e85c28031e2348f0f4e71eafe3d1fe13ba4305fc Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Wed, 29 Mar 2023 22:55:25 +0900 Subject: [PATCH 320/476] change weird name --- osu.Game/Configuration/OsuConfigManager.cs | 4 ++-- osu.Game/Graphics/Containers/ScalingContainer.cs | 2 +- .../Overlays/Settings/Sections/Graphics/LayoutSettings.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index a06bfcc5f0..1e7d3cf84f 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -157,7 +157,7 @@ namespace osu.Game.Configuration SetDefault(OsuSetting.Scaling, ScalingMode.Off); SetDefault(OsuSetting.SafeAreaConsiderations, true); - SetDefault(OsuSetting.ScalingMenuBackgroundDim, 0.9f, 0.5f, 1f); + SetDefault(OsuSetting.ScalingBackgroundDim, 0.9f, 0.5f, 1f); SetDefault(OsuSetting.ScalingSizeX, 0.8f, 0.2f, 1f); SetDefault(OsuSetting.ScalingSizeY, 0.8f, 0.2f, 1f); @@ -365,7 +365,7 @@ namespace osu.Game.Configuration ScalingPositionY, ScalingSizeX, ScalingSizeY, - ScalingMenuBackgroundDim, + ScalingBackgroundDim, UIScale, IntroSequence, NotifyOnUsernameMentioned, diff --git a/osu.Game/Graphics/Containers/ScalingContainer.cs b/osu.Game/Graphics/Containers/ScalingContainer.cs index 9c6830ce05..8b0450b71c 100644 --- a/osu.Game/Graphics/Containers/ScalingContainer.cs +++ b/osu.Game/Graphics/Containers/ScalingContainer.cs @@ -141,7 +141,7 @@ namespace osu.Game.Graphics.Containers safeAreaPadding = safeArea.SafeAreaPadding.GetBoundCopy(); safeAreaPadding.BindValueChanged(_ => Scheduler.AddOnce(updateSize)); - scalingMenuBackgroundDim = config.GetBindable(OsuSetting.ScalingMenuBackgroundDim); + scalingMenuBackgroundDim = config.GetBindable(OsuSetting.ScalingBackgroundDim); scalingMenuBackgroundDim.ValueChanged += _ => Scheduler.AddOnce(updateSize); } diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 6dbaf27afc..895ae1ed89 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -74,7 +74,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics scalingSizeY = osuConfig.GetBindable(OsuSetting.ScalingSizeY); scalingPositionX = osuConfig.GetBindable(OsuSetting.ScalingPositionX); scalingPositionY = osuConfig.GetBindable(OsuSetting.ScalingPositionY); - scalingBackgroundDim = osuConfig.GetBindable(OsuSetting.ScalingMenuBackgroundDim); + scalingBackgroundDim = osuConfig.GetBindable(OsuSetting.ScalingBackgroundDim); if (window != null) { From 19b7036b95eeac979f75850feb19b2a5a1a97ce2 Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Wed, 29 Mar 2023 22:56:35 +0900 Subject: [PATCH 321/476] use same same duration negligence in migrating code --- osu.Game/Graphics/Containers/ScalingContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/ScalingContainer.cs b/osu.Game/Graphics/Containers/ScalingContainer.cs index 8b0450b71c..bd52c8bb32 100644 --- a/osu.Game/Graphics/Containers/ScalingContainer.cs +++ b/osu.Game/Graphics/Containers/ScalingContainer.cs @@ -176,7 +176,7 @@ namespace osu.Game.Graphics.Containers } backgroundStack.FadeIn(TRANSITION_DURATION); - backgroundStack.FadeColour(OsuColour.Gray(1.0f - scalingMenuBackgroundDim.Value), 800, Easing.OutQuint); + backgroundStack.FadeColour(OsuColour.Gray(1.0f - scalingMenuBackgroundDim.Value), TRANSITION_DURATION, Easing.OutQuint); } else backgroundStack?.FadeOut(TRANSITION_DURATION); From 5d395e6d37f6247430e57c3ede359cad4c729b64 Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Wed, 29 Mar 2023 22:59:54 +0900 Subject: [PATCH 322/476] move to ctor --- .../Overlays/Settings/Sections/Graphics/LayoutSettings.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 895ae1ed89..523b1237fa 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -171,7 +171,8 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics LabelText = GameplaySettingsStrings.BackgroundDim, Current = scalingBackgroundDim, KeyboardStep = 0.01f, - DisplayAsPercentage = true + DisplayAsPercentage = true, + TransferValueOnCommit = false }, } }, @@ -232,7 +233,6 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics { if (s == dimSlider) { - s.TransferValueOnCommit = false; s.CanBeShown.Value = scalingMode.Value == ScalingMode.Everything || scalingMode.Value == ScalingMode.ExcludeOverlays; return; } From b25a59fd14ddac89f688e2bed48b6aef944b416d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 31 Mar 2023 02:30:04 +0900 Subject: [PATCH 323/476] Rename `scoreCompleted` -> `checkScoreCompleted` to reflect the fact it doesn't always succeed --- osu.Game/Screens/Play/Player.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index b8b3a8b5c0..c4b52136b9 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -358,10 +358,10 @@ namespace osu.Game.Screens.Play ScoreProcessor.RevertResult(r); }; - DimmableStoryboard.HasStoryboardEnded.ValueChanged += _ => scoreCompleted(); + DimmableStoryboard.HasStoryboardEnded.ValueChanged += _ => checkScoreCompleted(); // Bind the judgement processors to ourselves - ScoreProcessor.HasCompleted.BindValueChanged(_ => scoreCompleted()); + ScoreProcessor.HasCompleted.BindValueChanged(_ => checkScoreCompleted()); HealthProcessor.Failed += onFail; // Provide judgement processors to mods after they're loaded so that they're on the gameplay clock, @@ -702,7 +702,7 @@ namespace osu.Game.Screens.Play /// /// Handles changes in player state which may progress the completion of gameplay / this screen's lifetime. /// - private void scoreCompleted() + private void checkScoreCompleted() { // If this player instance is in the middle of an exit, don't attempt any kind of state update. if (!this.IsCurrentScreen()) From 796cd9c916d8c9643b569986e106afa554584a6a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 31 Mar 2023 02:36:17 +0900 Subject: [PATCH 324/476] Rewrite comment explaining early return on `checkScoreCompleted` given new usages --- osu.Game/Screens/Play/Player.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index c4b52136b9..eb33bf43d6 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -708,11 +708,13 @@ namespace osu.Game.Screens.Play if (!this.IsCurrentScreen()) return; - // Special case to handle rewinding post-completion. This is the only way already queued forward progress can be cancelled. - // TODO: Investigate whether this can be moved to a RewindablePlayer subclass or similar. - // Currently, even if this scenario is hit, prepareScoreForDisplay has already been queued (and potentially run). - // In scenarios where rewinding is possible (replay, spectating) this is a non-issue as no submission/import work is done, - // but it still doesn't feel right that this exists here. + // Handle cases of arriving at this method when not in a completed state. + // - When a storyboard completion triggered this call earlier than gameplay finishes. + // - When a replay has been rewound before a queued resultsDisplayDelegate has run. + // + // Currently, even if this scenario is hit, prepareAndImportScoreAsync has already been queued (and potentially run). + // In the scenarios above, this is a non-issue, but it still feels a bit convoluted to have to cancel in this method. + // Maybe this can be improved with further refactoring. if (!ScoreProcessor.HasCompleted.Value) { resultsDisplayDelegate?.Cancel(); From db71db84915aa219e96aa50f67fcae9599a132b4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 31 Mar 2023 13:07:14 +0900 Subject: [PATCH 325/476] Update README bounty/compensation section in line with changes applied to osu-web --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f3f025fa10..eb2fe6d0eb 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,7 @@ When it comes to contributing to the project, the two main things you can do to If you wish to help with localisation efforts, head over to [crowdin](https://crowdin.com/project/osu-web). -For those interested, we love to reward quality contributions via [bounties](https://docs.google.com/spreadsheets/d/1jNXfj_S3Pb5PErA-czDdC9DUu4IgUbe1Lt8E7CYUJuE/view?&rm=minimal#gid=523803337), paid out via PayPal or osu!supporter tags. Don't hesitate to [request a bounty](https://docs.google.com/forms/d/e/1FAIpQLSet_8iFAgPMG526pBZ2Kic6HSh7XPM3fE8xPcnWNkMzINDdYg/viewform) for your work on this project. +We love to reward quality contributions. If you have made a large contribution, or are a regular contributor, you are welcome to [submit an expense via opencollective](https://opencollective.com/ppy/expenses/new). If you have any questions, feel free to [reach out to peppy](mailto:pe@ppy.sh) before doing so. ## Licence From d5b8a45541703f3a469cf866f92c75755334183b Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 31 Mar 2023 16:20:16 +0900 Subject: [PATCH 326/476] Always use fallback strings for English MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Localisation/ResourceManagerLocalisationStore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Localisation/ResourceManagerLocalisationStore.cs b/osu.Game/Localisation/ResourceManagerLocalisationStore.cs index 50a450c101..1a05bec41a 100644 --- a/osu.Game/Localisation/ResourceManagerLocalisationStore.cs +++ b/osu.Game/Localisation/ResourceManagerLocalisationStore.cs @@ -68,7 +68,7 @@ namespace osu.Game.Localisation // When running a debug build and in viewing english culture, use the fallbacks rather than osu-resources baked strings. // This is what a developer expects to see when making changes to `xxxStrings.cs` files. - if (DebugUtils.IsDebugBuild && EffectiveCulture.Name == @"en") + if (EffectiveCulture.Name == @"en") return null; try From 28f31ef37968134a61a59255f7ea2037ae413644 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 31 Mar 2023 16:23:37 +0900 Subject: [PATCH 327/476] Adjust comment slightly --- osu.Game/Localisation/ResourceManagerLocalisationStore.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Localisation/ResourceManagerLocalisationStore.cs b/osu.Game/Localisation/ResourceManagerLocalisationStore.cs index 1a05bec41a..8551a140bd 100644 --- a/osu.Game/Localisation/ResourceManagerLocalisationStore.cs +++ b/osu.Game/Localisation/ResourceManagerLocalisationStore.cs @@ -66,8 +66,8 @@ namespace osu.Game.Localisation if (manager == null) return null; - // When running a debug build and in viewing english culture, use the fallbacks rather than osu-resources baked strings. - // This is what a developer expects to see when making changes to `xxxStrings.cs` files. + // When using the English culture, prefer the fallbacks rather than osu-resources baked strings. + // They are guaranteed to be up-to-date, and is also what a developer expects to see when making changes to `xxxStrings.cs` files. if (EffectiveCulture.Name == @"en") return null; From 4b7d44c329dd12fd97651492edf0b1b0d58d0fcf Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 31 Mar 2023 16:23:56 +0900 Subject: [PATCH 328/476] Remove unused using --- osu.Game/Localisation/ResourceManagerLocalisationStore.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Localisation/ResourceManagerLocalisationStore.cs b/osu.Game/Localisation/ResourceManagerLocalisationStore.cs index 8551a140bd..3fa86c188c 100644 --- a/osu.Game/Localisation/ResourceManagerLocalisationStore.cs +++ b/osu.Game/Localisation/ResourceManagerLocalisationStore.cs @@ -11,7 +11,6 @@ using System.Linq; using System.Resources; using System.Threading; using System.Threading.Tasks; -using osu.Framework.Development; using osu.Framework.Localisation; namespace osu.Game.Localisation From 7f9bf09e0396f7b76f78b55ebaaf995c21d245c4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 2 Apr 2023 20:07:31 +0900 Subject: [PATCH 329/476] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 927d66d93f..9b26526b9a 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -11,7 +11,7 @@ manifestmerger.jar - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 3de022e88d..6c10137a92 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index eb7ba24336..25fad5eaa3 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -16,6 +16,6 @@ iossimulator-x64 - + From 0f0dd9f2dcdb731ac3e33d4c9a416ce3a3952a95 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 2 Apr 2023 20:07:33 +0900 Subject: [PATCH 330/476] Update resources --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 6c10137a92..640f72c7da 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + From cf5acbf66eb5b0ff872eef50ff78f5409d4111a8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 2 Apr 2023 20:16:14 +0900 Subject: [PATCH 331/476] Update usage of `SupportedWindowModes` --- osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 6465d62ef0..a40156cf6d 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -75,7 +75,6 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics if (window != null) { currentDisplay.BindTo(window.CurrentDisplayBindable); - windowModes.BindTo(window.SupportedWindowModes); window.DisplaysChanged += onDisplaysChanged; } @@ -87,7 +86,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics windowModeDropdown = new SettingsDropdown { LabelText = GraphicsSettingsStrings.ScreenMode, - ItemSource = windowModes, + Items = window?.SupportedWindowModes, Current = config.GetBindable(FrameworkSetting.WindowMode), }, displayDropdown = new DisplaySettingsDropdown From e66569b394848348d62dd654af716afd69eae1b9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 2 Apr 2023 21:04:44 +0900 Subject: [PATCH 332/476] Update ruleset dependency tests in line with nullable changes --- .../Testing/TestSceneRulesetDependencies.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Testing/TestSceneRulesetDependencies.cs b/osu.Game.Tests/Testing/TestSceneRulesetDependencies.cs index f1533a32b9..a5a83d7231 100644 --- a/osu.Game.Tests/Testing/TestSceneRulesetDependencies.cs +++ b/osu.Game.Tests/Testing/TestSceneRulesetDependencies.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 disable - using System; using System.Collections.Generic; using NUnit.Framework; @@ -51,9 +49,11 @@ namespace osu.Game.Tests.Testing [Test] public void TestRetrieveShader() { - AddAssert("ruleset shaders retrieved", () => - Dependencies.Get().LoadRaw(@"sh_TestVertex.vs") != null && - Dependencies.Get().LoadRaw(@"sh_TestFragment.fs") != null); + AddStep("ruleset shaders retrieved without error", () => + { + Dependencies.Get().LoadRaw(@"sh_TestVertex.vs"); + Dependencies.Get().LoadRaw(@"sh_TestFragment.fs"); + }); } [Test] @@ -76,12 +76,12 @@ namespace osu.Game.Tests.Testing } public override IResourceStore CreateResourceStore() => new NamespacedResourceStore(TestResources.GetStore(), @"Resources"); - public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new TestRulesetConfigManager(); + public override IRulesetConfigManager CreateConfig(SettingsStore? settings) => new TestRulesetConfigManager(); public override IEnumerable GetModsFor(ModType type) => Array.Empty(); - public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null) => null; - public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => null; - public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => null; + public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList? mods = null) => null!; + public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => null!; + public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => null!; } private class TestRulesetConfigManager : IRulesetConfigManager From 4c2b7e7788823978d8f82f9f006395ce3d06892c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 2 Apr 2023 21:05:20 +0900 Subject: [PATCH 333/476] Fix random inspection showing up only in CI --- osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs index 7b26640e50..1a44262ef8 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs @@ -55,7 +55,7 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu set => valueText.Text = value.ToLocalisableString("N0"); } - public CountSection(LocalisableString header) + protected CountSection(LocalisableString header) { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; From 2cf863636632887cc7c6029b81dd45b3cad6b10a Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Sun, 2 Apr 2023 22:25:58 +0900 Subject: [PATCH 334/476] show guest diff author in `BeatmapPicker` --- .../Online/TestSceneBeatmapSetOverlay.cs | 73 ++++++++++++++++++- osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs | 30 +++++++- 2 files changed, 101 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs index 5d13421195..3090ff6c49 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs @@ -14,6 +14,7 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Testing; using osu.Game.Beatmaps.Drawables; +using osu.Game.Graphics.Containers; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; @@ -26,7 +27,7 @@ using APIUser = osu.Game.Online.API.Requests.Responses.APIUser; namespace osu.Game.Tests.Visual.Online { - public partial class TestSceneBeatmapSetOverlay : OsuTestScene + public partial class TestSceneBeatmapSetOverlay : OsuManualInputManagerTestScene { private readonly TestBeatmapSetOverlay overlay; @@ -281,6 +282,22 @@ namespace osu.Game.Tests.Visual.Online AddAssert(@"type is correct", () => type == lookupType.ToString()); } + [Test] + public void TestBeatmapSetWithGuestDIff() + { + AddStep("show map", () => overlay.ShowBeatmapSet(createBeatmapSetWithGuestDiff())); + AddStep("Move mouse to host diff", () => + { + InputManager.MoveMouseTo(overlay.ChildrenOfType().ElementAt(0)); + }); + AddAssert("Guset mapper information not show", () => !overlay.ChildrenOfType().Single().ChildrenOfType().Any()); + AddStep("move mouse to guest diff", () => + { + InputManager.MoveMouseTo(overlay.ChildrenOfType().ElementAt(1)); + }); + AddAssert("Guset mapper information show", () => overlay.ChildrenOfType().Single().ChildrenOfType().Any()); + } + private APIBeatmapSet createManyDifficultiesBeatmapSet() { var set = getBeatmapSet(); @@ -320,6 +337,60 @@ namespace osu.Game.Tests.Visual.Online return beatmapSet; } + private APIBeatmapSet createBeatmapSetWithGuestDiff() + { + var set = getBeatmapSet(); + + var beatmaps = new List(); + + var guestUser = new APIUser + { + Username = @"BanchoBot", + Id = 3, + }; + + set.RelatedUsers = new[] + { + set.Author, guestUser + }; + + beatmaps.Add(new APIBeatmap + { + OnlineID = 1145, + DifficultyName = "Host Diff", + RulesetID = Ruleset.Value.OnlineID, + StarRating = 1.4, + OverallDifficulty = 3.5f, + AuthorID = set.AuthorID, + FailTimes = new APIFailTimes + { + Fails = Enumerable.Range(1, 100).Select(j => j % 12 - 6).ToArray(), + Retries = Enumerable.Range(-2, 100).Select(j => j % 12 - 6).ToArray(), + }, + Status = BeatmapOnlineStatus.Graveyard + }); + + beatmaps.Add(new APIBeatmap + { + OnlineID = 1919, + DifficultyName = "Guest Diff", + RulesetID = Ruleset.Value.OnlineID, + StarRating = 8.1, + OverallDifficulty = 3.5f, + AuthorID = 3, + FailTimes = new APIFailTimes + { + Fails = Enumerable.Range(1, 100).Select(j => j % 12 - 6).ToArray(), + Retries = Enumerable.Range(-2, 100).Select(j => j % 12 - 6).ToArray(), + }, + Status = BeatmapOnlineStatus.Graveyard + }); + + set.Beatmaps = beatmaps.ToArray(); + + return set; + } + private void downloadAssert(bool shown) { AddAssert($"is download button {(shown ? "shown" : "hidden")}", () => overlay.Header.HeaderContent.DownloadButtonsVisible == shown); diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs index 585e0dd1a2..7dc3fc665f 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs @@ -31,6 +31,7 @@ namespace osu.Game.Overlays.BeatmapSet private const float tile_spacing = 2; private readonly OsuSpriteText version, starRating, starRatingText; + public readonly FillFlowContainer GuestMapperContainer; private readonly FillFlowContainer starRatingContainer; private readonly Statistic plays, favourites; @@ -88,6 +89,12 @@ namespace osu.Game.Overlays.BeatmapSet Origin = Anchor.BottomLeft, Font = OsuFont.GetFont(size: 17, weight: FontWeight.Bold) }, + GuestMapperContainer = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + }, starRatingContainer = new FillFlowContainer { Anchor = Anchor.BottomLeft, @@ -198,11 +205,32 @@ namespace osu.Game.Overlays.BeatmapSet updateDifficultyButtons(); } - private void showBeatmap(IBeatmapInfo? beatmapInfo) + private void showBeatmap(APIBeatmap? beatmapInfo) { + GuestMapperContainer.Clear(); + + if (beatmapInfo != null && beatmapSet?.Author.OnlineID != beatmapInfo.AuthorID) + { + if (BeatmapSet?.RelatedUsers?.Single(u => u.OnlineID == beatmapInfo.AuthorID) is APIUser user) + GuestMapperContainer.Child = getGueatMapper(user); + } + version.Text = beatmapInfo?.DifficultyName ?? string.Empty; } + private Drawable getGueatMapper(APIUser user) + { + return new LinkFlowContainer(s => + { + s.Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 15); + }).With(d => + { + d.AutoSizeAxes = Axes.Both; + d.AddText("mapped by "); + d.AddUserLink(user); + }); + } + private void updateDifficultyButtons() { Difficulties.Children.ToList().ForEach(diff => diff.State = diff.Beatmap == Beatmap.Value ? DifficultySelectorState.Selected : DifficultySelectorState.NotSelected); From d949ef3ca4cf0891f6dff2bc02b9a38c9ee9687c Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Sun, 2 Apr 2023 22:53:15 +0900 Subject: [PATCH 335/476] make `guestMapperContainer` private tests don't use it --- osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs index 7dc3fc665f..61bfd97f3c 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs @@ -31,7 +31,7 @@ namespace osu.Game.Overlays.BeatmapSet private const float tile_spacing = 2; private readonly OsuSpriteText version, starRating, starRatingText; - public readonly FillFlowContainer GuestMapperContainer; + private readonly FillFlowContainer guestMapperContainer; private readonly FillFlowContainer starRatingContainer; private readonly Statistic plays, favourites; @@ -89,7 +89,7 @@ namespace osu.Game.Overlays.BeatmapSet Origin = Anchor.BottomLeft, Font = OsuFont.GetFont(size: 17, weight: FontWeight.Bold) }, - GuestMapperContainer = new FillFlowContainer + guestMapperContainer = new FillFlowContainer { AutoSizeAxes = Axes.Both, Anchor = Anchor.BottomLeft, @@ -207,12 +207,12 @@ namespace osu.Game.Overlays.BeatmapSet private void showBeatmap(APIBeatmap? beatmapInfo) { - GuestMapperContainer.Clear(); + guestMapperContainer.Clear(); if (beatmapInfo != null && beatmapSet?.Author.OnlineID != beatmapInfo.AuthorID) { if (BeatmapSet?.RelatedUsers?.Single(u => u.OnlineID == beatmapInfo.AuthorID) is APIUser user) - GuestMapperContainer.Child = getGueatMapper(user); + guestMapperContainer.Child = getGueatMapper(user); } version.Text = beatmapInfo?.DifficultyName ?? string.Empty; From 1e0b64c9e8c408e27f039c47c67327d33a79bd38 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 2 Apr 2023 23:07:39 +0900 Subject: [PATCH 336/476] Update framework (again) --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 9b26526b9a..eb9a7e60df 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -11,7 +11,7 @@ manifestmerger.jar - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 640f72c7da..ec33eff5eb 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 25fad5eaa3..096c4d52ab 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -16,6 +16,6 @@ iossimulator-x64 - + From c3a6a581693efda04f913462f216960b19bedd5e Mon Sep 17 00:00:00 2001 From: Susko3 Date: Sun, 2 Apr 2023 19:23:18 +0200 Subject: [PATCH 337/476] Fix window mode dropdown not showing --- .../Overlays/Settings/Sections/Graphics/LayoutSettings.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index a40156cf6d..2765d2b437 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -32,7 +32,6 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics private FillFlowContainer> scalingSettings = null!; private readonly Bindable currentDisplay = new Bindable(); - private readonly IBindableList windowModes = new BindableList(); private Bindable scalingMode = null!; private Bindable sizeFullscreen = null!; @@ -87,6 +86,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics { LabelText = GraphicsSettingsStrings.ScreenMode, Items = window?.SupportedWindowModes, + CanBeShown = { Value = window?.SupportedWindowModes.Count() > 1 }, Current = config.GetBindable(FrameworkSetting.WindowMode), }, displayDropdown = new DisplaySettingsDropdown @@ -180,8 +180,6 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics updateScreenModeWarning(); }, true); - windowModes.BindCollectionChanged((_, _) => updateDisplaySettingsVisibility()); - currentDisplay.BindValueChanged(display => Schedule(() => { resolutions.RemoveRange(1, resolutions.Count - 1); @@ -235,7 +233,6 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics private void updateDisplaySettingsVisibility() { - windowModeDropdown.CanBeShown.Value = windowModes.Count > 1; resolutionDropdown.CanBeShown.Value = resolutions.Count > 1 && windowModeDropdown.Current.Value == WindowMode.Fullscreen; displayDropdown.CanBeShown.Value = displayDropdown.Items.Count() > 1; safeAreaConsiderationsCheckbox.CanBeShown.Value = host.Window?.SafeAreaPadding.Value.Total != Vector2.Zero; From 63ea17f10e7661bafe2c2c978ab20ac9fbd9e789 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sun, 2 Apr 2023 18:15:00 -0700 Subject: [PATCH 338/476] Update comment vote pill in line with web --- osu.Game/Overlays/Comments/VotePill.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Comments/VotePill.cs b/osu.Game/Overlays/Comments/VotePill.cs index 6cfa5cb9e8..dd418a9e58 100644 --- a/osu.Game/Overlays/Comments/VotePill.cs +++ b/osu.Game/Overlays/Comments/VotePill.cs @@ -132,11 +132,10 @@ namespace osu.Game.Overlays.Comments }, sideNumber = new OsuSpriteText { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreRight, + Anchor = Anchor.TopCentre, + Origin = Anchor.BottomCentre, Text = "+1", Font = OsuFont.GetFont(size: 14), - Margin = new MarginPadding { Right = 3 }, Alpha = 0, }, votesCounter = new OsuSpriteText @@ -189,7 +188,7 @@ namespace osu.Game.Overlays.Comments else sideNumber.FadeTo(IsHovered ? 1 : 0); - borderContainer.BorderThickness = IsHovered ? 3 : 0; + borderContainer.BorderThickness = IsHovered ? 2 : 0; } private void onHoverAction() From 8932668f77c46d358da083a8f5d2dce2d9a9c3af Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 Apr 2023 10:31:47 +0900 Subject: [PATCH 339/476] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index eb9a7e60df..4b89e82729 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -11,7 +11,7 @@ manifestmerger.jar - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index ec33eff5eb..b9c6c1df9d 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 096c4d52ab..083d8192ea 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -16,6 +16,6 @@ iossimulator-x64 - + From 7a0edabd5dc557680b06b5e51f4628da9c08879f Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sun, 2 Apr 2023 17:11:19 -0700 Subject: [PATCH 340/476] Normalise overlay horizontal padding const --- osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs | 6 +++--- osu.Game/Overlays/BeatmapSet/Info.cs | 2 +- osu.Game/Overlays/BeatmapSetOverlay.cs | 1 - osu.Game/Overlays/Changelog/ChangelogBuild.cs | 4 +--- osu.Game/Overlays/Changelog/ChangelogListing.cs | 2 +- osu.Game/Overlays/Profile/Header/BadgeHeaderContainer.cs | 2 +- osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs | 2 +- osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs | 4 ++-- osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs | 2 +- osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs | 2 +- osu.Game/Overlays/Profile/ProfileHeader.cs | 2 +- osu.Game/Overlays/Profile/ProfileSection.cs | 4 ++-- osu.Game/Overlays/Rankings/CountryFilter.cs | 2 +- osu.Game/Overlays/Rankings/SpotlightSelector.cs | 2 +- osu.Game/Overlays/Rankings/Tables/RankingsTable.cs | 3 +-- osu.Game/Overlays/UserProfileOverlay.cs | 4 +--- osu.Game/Overlays/WaveOverlayContainer.cs | 2 ++ 17 files changed, 21 insertions(+), 25 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs index 26e6b1f158..7ff8352054 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs @@ -97,8 +97,8 @@ namespace osu.Game.Overlays.BeatmapSet Padding = new MarginPadding { Vertical = BeatmapSetOverlay.Y_PADDING, - Left = BeatmapSetOverlay.X_PADDING, - Right = BeatmapSetOverlay.X_PADDING + BeatmapSetOverlay.RIGHT_WIDTH, + Left = WaveOverlayContainer.HORIZONTAL_PADDING, + Right = WaveOverlayContainer.HORIZONTAL_PADDING + BeatmapSetOverlay.RIGHT_WIDTH, }, Children = new Drawable[] { @@ -170,7 +170,7 @@ namespace osu.Game.Overlays.BeatmapSet Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, AutoSizeAxes = Axes.Both, - Margin = new MarginPadding { Top = BeatmapSetOverlay.Y_PADDING, Right = BeatmapSetOverlay.X_PADDING }, + Margin = new MarginPadding { Top = BeatmapSetOverlay.Y_PADDING, Right = WaveOverlayContainer.HORIZONTAL_PADDING }, Direction = FillDirection.Vertical, Spacing = new Vector2(10), Children = new Drawable[] diff --git a/osu.Game/Overlays/BeatmapSet/Info.cs b/osu.Game/Overlays/BeatmapSet/Info.cs index 58739eb471..8758b9c5cf 100644 --- a/osu.Game/Overlays/BeatmapSet/Info.cs +++ b/osu.Game/Overlays/BeatmapSet/Info.cs @@ -55,7 +55,7 @@ namespace osu.Game.Overlays.BeatmapSet new Container { RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Top = 15, Horizontal = BeatmapSetOverlay.X_PADDING }, + Padding = new MarginPadding { Top = 15, Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING }, Children = new Drawable[] { new Container diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index 237ce22767..873336bb6e 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -25,7 +25,6 @@ namespace osu.Game.Overlays { public partial class BeatmapSetOverlay : OnlineOverlay { - public const float X_PADDING = 40; public const float Y_PADDING = 25; public const float RIGHT_WIDTH = 275; diff --git a/osu.Game/Overlays/Changelog/ChangelogBuild.cs b/osu.Game/Overlays/Changelog/ChangelogBuild.cs index 96d5203d14..08978ac2ab 100644 --- a/osu.Game/Overlays/Changelog/ChangelogBuild.cs +++ b/osu.Game/Overlays/Changelog/ChangelogBuild.cs @@ -18,8 +18,6 @@ namespace osu.Game.Overlays.Changelog { public partial class ChangelogBuild : FillFlowContainer { - public const float HORIZONTAL_PADDING = 70; - public Action SelectBuild; protected readonly APIChangelogBuild Build; @@ -33,7 +31,7 @@ namespace osu.Game.Overlays.Changelog RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; Direction = FillDirection.Vertical; - Padding = new MarginPadding { Horizontal = HORIZONTAL_PADDING }; + Padding = new MarginPadding { Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING }; Children = new Drawable[] { diff --git a/osu.Game/Overlays/Changelog/ChangelogListing.cs b/osu.Game/Overlays/Changelog/ChangelogListing.cs index d7c9ff67fe..4b784c7a28 100644 --- a/osu.Game/Overlays/Changelog/ChangelogListing.cs +++ b/osu.Game/Overlays/Changelog/ChangelogListing.cs @@ -64,7 +64,7 @@ namespace osu.Game.Overlays.Changelog { RelativeSizeAxes = Axes.X, Height = 1, - Padding = new MarginPadding { Horizontal = ChangelogBuild.HORIZONTAL_PADDING }, + Padding = new MarginPadding { Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING }, Margin = new MarginPadding { Top = 30 }, Child = new Box { diff --git a/osu.Game/Overlays/Profile/Header/BadgeHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/BadgeHeaderContainer.cs index 508041eb76..24be6ce2f5 100644 --- a/osu.Game/Overlays/Profile/Header/BadgeHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/BadgeHeaderContainer.cs @@ -50,7 +50,7 @@ namespace osu.Game.Overlays.Profile.Header RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Spacing = new Vector2(10, 10), - Padding = new MarginPadding { Horizontal = UserProfileOverlay.CONTENT_X_MARGIN, Top = 10 }, + Padding = new MarginPadding { Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING, Top = 10 }, } }; } diff --git a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs index 1e80257a57..08a816930e 100644 --- a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs @@ -55,7 +55,7 @@ namespace osu.Game.Overlays.Profile.Header RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Direction = FillDirection.Vertical, - Padding = new MarginPadding { Horizontal = UserProfileOverlay.CONTENT_X_MARGIN, Vertical = 10 }, + Padding = new MarginPadding { Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING, Vertical = 10 }, Spacing = new Vector2(0, 10), Children = new Drawable[] { diff --git a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs index 0dab4d582d..cafee7ea85 100644 --- a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs @@ -39,7 +39,7 @@ namespace osu.Game.Overlays.Profile.Header RelativeSizeAxes = Axes.Y, Direction = FillDirection.Horizontal, Padding = new MarginPadding { Vertical = 10 }, - Margin = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN }, + Margin = new MarginPadding { Left = WaveOverlayContainer.HORIZONTAL_PADDING }, Spacing = new Vector2(10, 0), Children = new Drawable[] { @@ -62,7 +62,7 @@ namespace osu.Game.Overlays.Profile.Header Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, AutoSizeAxes = Axes.Both, - Margin = new MarginPadding { Right = UserProfileOverlay.CONTENT_X_MARGIN }, + Margin = new MarginPadding { Right = WaveOverlayContainer.HORIZONTAL_PADDING }, Children = new Drawable[] { levelBadge = new LevelBadge diff --git a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs index 1cc3aae735..1f35f39b49 100644 --- a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs @@ -30,7 +30,7 @@ namespace osu.Game.Overlays.Profile.Header { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Horizontal = UserProfileOverlay.CONTENT_X_MARGIN, Vertical = 10 }, + Padding = new MarginPadding { Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING, Vertical = 10 }, Child = new GridContainer { RelativeSizeAxes = Axes.X, diff --git a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs index 2f4f49788f..d04329430b 100644 --- a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs @@ -83,7 +83,7 @@ namespace osu.Game.Overlays.Profile.Header Direction = FillDirection.Horizontal, Padding = new MarginPadding { - Left = UserProfileOverlay.CONTENT_X_MARGIN, + Left = WaveOverlayContainer.HORIZONTAL_PADDING, Vertical = vertical_padding }, Height = content_height + 2 * vertical_padding, diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 363eb5d58e..80d48ae09e 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Profile public ProfileHeader() { - ContentSidePadding = UserProfileOverlay.CONTENT_X_MARGIN; + ContentSidePadding = WaveOverlayContainer.HORIZONTAL_PADDING; TabControl.AddItem(LayoutStrings.HeaderUsersShow); diff --git a/osu.Game/Overlays/Profile/ProfileSection.cs b/osu.Game/Overlays/Profile/ProfileSection.cs index 4ac86924f8..a8a240ddde 100644 --- a/osu.Game/Overlays/Profile/ProfileSection.cs +++ b/osu.Game/Overlays/Profile/ProfileSection.cs @@ -67,7 +67,7 @@ namespace osu.Game.Overlays.Profile AutoSizeAxes = Axes.Both, Margin = new MarginPadding { - Horizontal = UserProfileOverlay.CONTENT_X_MARGIN - outer_gutter_width, + Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING - outer_gutter_width, Top = 20, Bottom = 20, }, @@ -97,7 +97,7 @@ namespace osu.Game.Overlays.Profile RelativeSizeAxes = Axes.X, Padding = new MarginPadding { - Horizontal = UserProfileOverlay.CONTENT_X_MARGIN - outer_gutter_width, + Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING - outer_gutter_width, Bottom = 20 } }, diff --git a/osu.Game/Overlays/Rankings/CountryFilter.cs b/osu.Game/Overlays/Rankings/CountryFilter.cs index e27fa7c7bd..525816f8fd 100644 --- a/osu.Game/Overlays/Rankings/CountryFilter.cs +++ b/osu.Game/Overlays/Rankings/CountryFilter.cs @@ -54,7 +54,7 @@ namespace osu.Game.Overlays.Rankings Origin = Anchor.CentreLeft, Direction = FillDirection.Horizontal, Spacing = new Vector2(10, 0), - Margin = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN }, + Margin = new MarginPadding { Left = WaveOverlayContainer.HORIZONTAL_PADDING }, Children = new Drawable[] { new OsuSpriteText diff --git a/osu.Game/Overlays/Rankings/SpotlightSelector.cs b/osu.Game/Overlays/Rankings/SpotlightSelector.cs index 31273e3b01..190da04a5d 100644 --- a/osu.Game/Overlays/Rankings/SpotlightSelector.cs +++ b/osu.Game/Overlays/Rankings/SpotlightSelector.cs @@ -63,7 +63,7 @@ namespace osu.Game.Overlays.Rankings { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Horizontal = UserProfileOverlay.CONTENT_X_MARGIN }, + Padding = new MarginPadding { Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING }, Child = new FillFlowContainer { RelativeSizeAxes = Axes.X, diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs index affd9a2c44..27d894cdc2 100644 --- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs @@ -23,7 +23,6 @@ namespace osu.Game.Overlays.Rankings.Tables public abstract partial class RankingsTable : TableContainer { protected const int TEXT_SIZE = 12; - private const float horizontal_inset = 20; private const float row_height = 32; private const float row_spacing = 3; private const int items_per_page = 50; @@ -39,7 +38,7 @@ namespace osu.Game.Overlays.Rankings.Tables RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; - Padding = new MarginPadding { Horizontal = horizontal_inset }; + Padding = new MarginPadding { Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING }; RowSize = new Dimension(GridSizeMode.Absolute, row_height + row_spacing); } diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index c5f8a820ea..d1fe877e55 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -45,8 +45,6 @@ namespace osu.Game.Overlays [Resolved] private RulesetStore rulesets { get; set; } = null!; - public const float CONTENT_X_MARGIN = 50; - public UserProfileOverlay() : base(OverlayColourScheme.Pink) { @@ -184,7 +182,7 @@ namespace osu.Game.Overlays public ProfileSectionTabControl() { Height = 40; - Padding = new MarginPadding { Horizontal = CONTENT_X_MARGIN }; + Padding = new MarginPadding { Horizontal = HORIZONTAL_PADDING }; TabContainer.Spacing = new Vector2(20); } diff --git a/osu.Game/Overlays/WaveOverlayContainer.cs b/osu.Game/Overlays/WaveOverlayContainer.cs index d25f6a9ae5..00474cc0d8 100644 --- a/osu.Game/Overlays/WaveOverlayContainer.cs +++ b/osu.Game/Overlays/WaveOverlayContainer.cs @@ -22,6 +22,8 @@ namespace osu.Game.Overlays protected override string PopInSampleName => "UI/wave-pop-in"; + public const float HORIZONTAL_PADDING = 50; + protected WaveOverlayContainer() { AddInternal(Waves = new WaveContainer From af389b1107367207fd42cbef0b30d0f23175d541 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sun, 2 Apr 2023 17:12:27 -0700 Subject: [PATCH 341/476] Replace all hardcoded 50 horizontal padding with const --- osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs | 2 +- osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs | 4 ++-- osu.Game/Overlays/Comments/CommentsContainer.cs | 4 ++-- osu.Game/Overlays/Comments/CommentsHeader.cs | 2 +- osu.Game/Overlays/Comments/TotalCommentsCounter.cs | 2 +- osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs | 2 +- osu.Game/Overlays/News/Displays/ArticleListing.cs | 2 +- osu.Game/Overlays/OverlayHeader.cs | 2 +- osu.Game/Overlays/OverlaySidebar.cs | 2 +- osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs | 2 +- osu.Game/Overlays/Wiki/WikiArticlePage.cs | 2 +- osu.Game/Overlays/WikiOverlay.cs | 2 +- 12 files changed, 14 insertions(+), 14 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index 9eb04d9cc5..6d89313979 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -116,7 +116,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Direction = FillDirection.Vertical, - Padding = new MarginPadding { Horizontal = 50 }, + Padding = new MarginPadding { Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING }, Margin = new MarginPadding { Vertical = 20 }, Children = new Drawable[] { diff --git a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs index 04526eb7ba..4aded1dd59 100644 --- a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs +++ b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs @@ -34,7 +34,7 @@ namespace osu.Game.Overlays.Changelog Padding = new MarginPadding { Vertical = 20, - Horizontal = 50, + Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING, }; } @@ -79,7 +79,7 @@ namespace osu.Game.Overlays.Changelog Direction = FillDirection.Vertical, Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Padding = new MarginPadding { Right = 50 + image_container_width }, + Padding = new MarginPadding { Right = WaveOverlayContainer.HORIZONTAL_PADDING + image_container_width }, Children = new Drawable[] { new OsuSpriteText diff --git a/osu.Game/Overlays/Comments/CommentsContainer.cs b/osu.Game/Overlays/Comments/CommentsContainer.cs index c4e4700674..2a873690a7 100644 --- a/osu.Game/Overlays/Comments/CommentsContainer.cs +++ b/osu.Game/Overlays/Comments/CommentsContainer.cs @@ -99,7 +99,7 @@ namespace osu.Game.Overlays.Comments { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Horizontal = 50, Vertical = 20 }, + Padding = new MarginPadding { Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING, Vertical = 20 }, Children = new Drawable[] { avatar = new UpdateableAvatar(api.LocalUser.Value) @@ -393,7 +393,7 @@ namespace osu.Game.Overlays.Comments { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Margin = new MarginPadding { Left = 50 }, + Margin = new MarginPadding { Left = WaveOverlayContainer.HORIZONTAL_PADDING }, Text = CommentsStrings.Empty } }); diff --git a/osu.Game/Overlays/Comments/CommentsHeader.cs b/osu.Game/Overlays/Comments/CommentsHeader.cs index e6d44e618b..0ae1f839a1 100644 --- a/osu.Game/Overlays/Comments/CommentsHeader.cs +++ b/osu.Game/Overlays/Comments/CommentsHeader.cs @@ -41,7 +41,7 @@ namespace osu.Game.Overlays.Comments new Container { RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Horizontal = 50 }, + Padding = new MarginPadding { Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING }, Children = new Drawable[] { new OverlaySortTabControl diff --git a/osu.Game/Overlays/Comments/TotalCommentsCounter.cs b/osu.Game/Overlays/Comments/TotalCommentsCounter.cs index 38928f6f3d..2065f7a76b 100644 --- a/osu.Game/Overlays/Comments/TotalCommentsCounter.cs +++ b/osu.Game/Overlays/Comments/TotalCommentsCounter.cs @@ -32,7 +32,7 @@ namespace osu.Game.Overlays.Comments Origin = Anchor.CentreLeft, AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, - Margin = new MarginPadding { Left = 50 }, + Margin = new MarginPadding { Left = WaveOverlayContainer.HORIZONTAL_PADDING }, Spacing = new Vector2(5, 0), Children = new Drawable[] { diff --git a/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs b/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs index 73fab6d62b..a40993ae05 100644 --- a/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs +++ b/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs @@ -129,7 +129,7 @@ namespace osu.Game.Overlays.Dashboard.Friends { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Horizontal = 50 } + Padding = new MarginPadding { Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING } }, loading = new LoadingLayer(true) } diff --git a/osu.Game/Overlays/News/Displays/ArticleListing.cs b/osu.Game/Overlays/News/Displays/ArticleListing.cs index b6ce16ae7d..4fc9dde156 100644 --- a/osu.Game/Overlays/News/Displays/ArticleListing.cs +++ b/osu.Game/Overlays/News/Displays/ArticleListing.cs @@ -43,7 +43,7 @@ namespace osu.Game.Overlays.News.Displays { Vertical = 20, Left = 30, - Right = 50 + Right = WaveOverlayContainer.HORIZONTAL_PADDING }; InternalChild = new FillFlowContainer diff --git a/osu.Game/Overlays/OverlayHeader.cs b/osu.Game/Overlays/OverlayHeader.cs index f28d40c429..93de463204 100644 --- a/osu.Game/Overlays/OverlayHeader.cs +++ b/osu.Game/Overlays/OverlayHeader.cs @@ -89,7 +89,7 @@ namespace osu.Game.Overlays } }); - ContentSidePadding = 50; + ContentSidePadding = WaveOverlayContainer.HORIZONTAL_PADDING; } [BackgroundDependencyLoader] diff --git a/osu.Game/Overlays/OverlaySidebar.cs b/osu.Game/Overlays/OverlaySidebar.cs index b8c0032e87..93e5e83ffc 100644 --- a/osu.Game/Overlays/OverlaySidebar.cs +++ b/osu.Game/Overlays/OverlaySidebar.cs @@ -55,7 +55,7 @@ namespace osu.Game.Overlays Padding = new MarginPadding { Vertical = 20, - Left = 50, + Left = WaveOverlayContainer.HORIZONTAL_PADDING, Right = 30 }, Child = CreateContent() diff --git a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs index cafee7ea85..d964364510 100644 --- a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs @@ -77,7 +77,7 @@ namespace osu.Game.Overlays.Profile.Header Origin = Anchor.CentreRight, Width = 200, Height = 6, - Margin = new MarginPadding { Right = 50 }, + Margin = new MarginPadding { Right = WaveOverlayContainer.HORIZONTAL_PADDING }, Child = new LevelProgressBar { RelativeSizeAxes = Axes.Both, diff --git a/osu.Game/Overlays/Wiki/WikiArticlePage.cs b/osu.Game/Overlays/Wiki/WikiArticlePage.cs index 6c1dbe3181..342a395871 100644 --- a/osu.Game/Overlays/Wiki/WikiArticlePage.cs +++ b/osu.Game/Overlays/Wiki/WikiArticlePage.cs @@ -56,7 +56,7 @@ namespace osu.Game.Overlays.Wiki { Vertical = 20, Left = 30, - Right = 50, + Right = WaveOverlayContainer.HORIZONTAL_PADDING, }, OnAddHeading = sidebar.AddEntry, } diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs index 88dc2cd7a4..2444aa4fa2 100644 --- a/osu.Game/Overlays/WikiOverlay.cs +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -145,7 +145,7 @@ namespace osu.Game.Overlays Padding = new MarginPadding { Vertical = 20, - Horizontal = 50, + Horizontal = HORIZONTAL_PADDING, }, }); } From 436f1e4ae40fb980447672a5f2ad69a41ce61243 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sun, 2 Apr 2023 20:45:09 -0700 Subject: [PATCH 342/476] Replace other hardcoded horizontal padding with const - Also add overlay stream item padding const and account for it --- .../Overlays/BeatmapListing/BeatmapListingSearchControl.cs | 2 +- osu.Game/Overlays/Changelog/ChangelogHeader.cs | 2 +- osu.Game/Overlays/Comments/CommentsContainer.cs | 2 +- osu.Game/Overlays/Comments/DrawableComment.cs | 2 +- osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs | 2 +- osu.Game/Overlays/OverlayStreamItem.cs | 4 +++- 6 files changed, 8 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs index 23de1cf76d..3fa0fc7a77 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs @@ -107,7 +107,7 @@ namespace osu.Game.Overlays.BeatmapListing Padding = new MarginPadding { Vertical = 20, - Horizontal = 40, + Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING, }, Child = new FillFlowContainer { diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs index 54ada24987..e9be67e977 100644 --- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -93,7 +93,7 @@ namespace osu.Game.Overlays.Changelog AutoSizeAxes = Axes.Y, Padding = new MarginPadding { - Horizontal = 65, + Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING - ChangelogUpdateStreamItem.PADDING, Vertical = 20 }, Child = Streams = new ChangelogUpdateStreamControl { Current = currentStream }, diff --git a/osu.Game/Overlays/Comments/CommentsContainer.cs b/osu.Game/Overlays/Comments/CommentsContainer.cs index 2a873690a7..24536fe460 100644 --- a/osu.Game/Overlays/Comments/CommentsContainer.cs +++ b/osu.Game/Overlays/Comments/CommentsContainer.cs @@ -152,7 +152,7 @@ namespace osu.Game.Overlays.Comments ShowDeleted = { BindTarget = ShowDeleted }, Margin = new MarginPadding { - Horizontal = 70, + Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING, Vertical = 10 } }, diff --git a/osu.Game/Overlays/Comments/DrawableComment.cs b/osu.Game/Overlays/Comments/DrawableComment.cs index 397dd46cdc..a710406548 100644 --- a/osu.Game/Overlays/Comments/DrawableComment.cs +++ b/osu.Game/Overlays/Comments/DrawableComment.cs @@ -537,7 +537,7 @@ namespace osu.Game.Overlays.Comments { return new MarginPadding { - Horizontal = 70, + Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING, Vertical = 15 }; } diff --git a/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs b/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs index a40993ae05..e3accfd2ad 100644 --- a/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs +++ b/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs @@ -79,7 +79,7 @@ namespace osu.Game.Overlays.Dashboard.Friends Padding = new MarginPadding { Top = 20, - Horizontal = 45 + Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING - FriendsOnlineStatusItem.PADDING }, Child = onlineStreamControl = new FriendOnlineStreamControl(), } diff --git a/osu.Game/Overlays/OverlayStreamItem.cs b/osu.Game/Overlays/OverlayStreamItem.cs index 9b18e5cccf..45181c13e4 100644 --- a/osu.Game/Overlays/OverlayStreamItem.cs +++ b/osu.Game/Overlays/OverlayStreamItem.cs @@ -39,12 +39,14 @@ namespace osu.Game.Overlays private FillFlowContainer text; private ExpandingBar expandingBar; + public const float PADDING = 5; + protected OverlayStreamItem(T value) : base(value) { Height = 50; Width = 90; - Margin = new MarginPadding(5); + Margin = new MarginPadding(PADDING); } [BackgroundDependencyLoader] From 247d426c8a61cebe24693bf0124798483bea7239 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sun, 2 Apr 2023 17:14:31 -0700 Subject: [PATCH 343/476] Add horizontal padding to currently playing search textbox --- osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs index 1540aa8fbb..5047992c8b 100644 --- a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs +++ b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs @@ -60,7 +60,7 @@ namespace osu.Game.Overlays.Dashboard new Container { RelativeSizeAxes = Axes.X, - Padding = new MarginPadding(padding), + Padding = new MarginPadding { Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING, Vertical = padding }, Child = searchTextBox = new BasicSearchTextBox { RelativeSizeAxes = Axes.X, From a097433cb121f6315166cd8e9914efb9dc0347ea Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 Apr 2023 14:56:29 +0900 Subject: [PATCH 344/476] Fix overlay toggle keys working during disabled activation modes Closes #23104. --- osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs | 13 +++++++++++++ osu.Game/Overlays/Toolbar/Toolbar.cs | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs b/osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs index aef6f9ade0..22c7bb64b2 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs @@ -114,6 +114,19 @@ namespace osu.Game.Tests.Visual.Menus } } + [TestCase(OverlayActivation.All)] + [TestCase(OverlayActivation.Disabled)] + public void TestButtonKeyboardInputRespectsOverlayActivation(OverlayActivation mode) + { + AddStep($"set activation mode to {mode}", () => toolbar.OverlayActivationMode.Value = mode); + AddStep("hide toolbar", () => toolbar.Hide()); + + if (mode == OverlayActivation.Disabled) + AddAssert("check buttons not accepting input", () => InputManager.NonPositionalInputQueue.OfType().Count(), () => Is.Zero); + else + AddAssert("check buttons accepting input", () => InputManager.NonPositionalInputQueue.OfType().Count(), () => Is.Not.Zero); + } + [TestCase(OverlayActivation.All)] [TestCase(OverlayActivation.Disabled)] public void TestRespectsOverlayActivation(OverlayActivation mode) diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index f21ef0ee98..93294a9d30 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -42,7 +42,7 @@ namespace osu.Game.Overlays.Toolbar protected readonly IBindable OverlayActivationMode = new Bindable(OverlayActivation.All); // Toolbar and its components need keyboard input even when hidden. - public override bool PropagateNonPositionalInputSubTree => true; + public override bool PropagateNonPositionalInputSubTree => OverlayActivationMode.Value != OverlayActivation.Disabled; public Toolbar() { From 6239789188c94561aaae96b6932fd67ee9ad5a4c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 Apr 2023 15:37:10 +0900 Subject: [PATCH 345/476] Fix missing using statements in multiple test scenes --- osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs | 1 + osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs index 7bc789ecc4..eecead5415 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs @@ -8,6 +8,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Framework.Timing; using osu.Game.Configuration; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs index 9d8d82108d..7bbfc6a62b 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs @@ -10,6 +10,8 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; using osu.Framework.Timing; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; From 7011928d86e7ed83f3e7d8839d8e1acc0a878267 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 Apr 2023 18:04:33 +0900 Subject: [PATCH 346/476] Fix abysmal debug performance due to try-catch logic in `DrawableRulesetDependencies` --- .../UI/DrawableRulesetDependencies.cs | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs b/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs index e6ee770e19..1d5fcc634e 100644 --- a/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs +++ b/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs @@ -206,17 +206,26 @@ namespace osu.Game.Rulesets.UI this.parent = parent; } + // When the debugger is attached, exceptions are expensive. + // Manually work around this by caching failed lookups and falling back straight to parent. + private readonly HashSet<(string, string)> failedLookups = new HashSet<(string, string)>(); + public override IShader Load(string vertex, string fragment) { - try + if (!failedLookups.Contains((vertex, fragment))) { - return base.Load(vertex, fragment); - } - catch - { - // Shader lookup is very non-standard. Rather than returning null on missing shaders, exceptions are thrown. - return parent.Load(vertex, fragment); + try + { + return base.Load(vertex, fragment); + } + catch + { + // Shader lookup is very non-standard. Rather than returning null on missing shaders, exceptions are thrown. + failedLookups.Add((vertex, fragment)); + } } + + return parent.Load(vertex, fragment); } } } From be79ea1c10eacfe4e759e2fd2fcdeeaf92280c6c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 Apr 2023 18:14:58 +0900 Subject: [PATCH 347/476] Remove left/right click based placement in taiko editor and respect sample selection --- .../Edit/Blueprints/HitPlacementBlueprint.cs | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.cs b/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.cs index 0a1f5380b5..d47a50b94d 100644 --- a/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.cs @@ -35,20 +35,8 @@ namespace osu.Game.Rulesets.Taiko.Edit.Blueprints protected override bool OnMouseDown(MouseDownEvent e) { - switch (e.Button) - { - case MouseButton.Left: - HitObject.Type = HitType.Centre; - EndPlacement(true); - return true; - - case MouseButton.Right: - HitObject.Type = HitType.Rim; - EndPlacement(true); - return true; - } - - return false; + EndPlacement(true); + return true; } public override void UpdateTimeAndPosition(SnapResult result) From 51240ed46b0fbec450b74c7513cf3737a997cad9 Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Mon, 3 Apr 2023 19:51:22 +0900 Subject: [PATCH 348/476] typo --- osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs index 61bfd97f3c..59be9414fd 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs @@ -212,13 +212,13 @@ namespace osu.Game.Overlays.BeatmapSet if (beatmapInfo != null && beatmapSet?.Author.OnlineID != beatmapInfo.AuthorID) { if (BeatmapSet?.RelatedUsers?.Single(u => u.OnlineID == beatmapInfo.AuthorID) is APIUser user) - guestMapperContainer.Child = getGueatMapper(user); + guestMapperContainer.Child = getGuestMapper(user); } version.Text = beatmapInfo?.DifficultyName ?? string.Empty; } - private Drawable getGueatMapper(APIUser user) + private Drawable getGuestMapper(APIUser user) { return new LinkFlowContainer(s => { From 41c01d3929279865a1e542c7aa556bc3dad1d44d Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Mon, 3 Apr 2023 20:07:21 +0900 Subject: [PATCH 349/476] nullable condition --- osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs index 59be9414fd..67348959a6 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs @@ -211,21 +211,18 @@ namespace osu.Game.Overlays.BeatmapSet if (beatmapInfo != null && beatmapSet?.Author.OnlineID != beatmapInfo.AuthorID) { - if (BeatmapSet?.RelatedUsers?.Single(u => u.OnlineID == beatmapInfo.AuthorID) is APIUser user) guestMapperContainer.Child = getGuestMapper(user); + APIUser? user = BeatmapSet?.RelatedUsers?.Single(u => u.OnlineID == beatmapInfo?.AuthorID); + if (user != null) } version.Text = beatmapInfo?.DifficultyName ?? string.Empty; } - private Drawable getGuestMapper(APIUser user) + private void getGuestMapper(APIUser user) { - return new LinkFlowContainer(s => + guestMapperContainer.With(d => { - s.Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 15); - }).With(d => - { - d.AutoSizeAxes = Axes.Both; d.AddText("mapped by "); d.AddUserLink(user); }); From 735b48679e2d4cd025590d84e7bd84a6712b08ab Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Mon, 3 Apr 2023 20:09:49 +0900 Subject: [PATCH 350/476] use `LinkFlowContainer` directly --- .../Visual/Online/TestSceneBeatmapSetOverlay.cs | 5 +++-- osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs | 11 ++++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs index 3090ff6c49..37f7b7623b 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs @@ -15,6 +15,7 @@ using System.Linq; using osu.Framework.Testing; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; @@ -290,12 +291,12 @@ namespace osu.Game.Tests.Visual.Online { InputManager.MoveMouseTo(overlay.ChildrenOfType().ElementAt(0)); }); - AddAssert("Guset mapper information not show", () => !overlay.ChildrenOfType().Single().ChildrenOfType().Any()); + AddAssert("Guset mapper information not show", () => overlay.ChildrenOfType().Single().ChildrenOfType().All(s => s.Text != "BanchoBot")); AddStep("move mouse to guest diff", () => { InputManager.MoveMouseTo(overlay.ChildrenOfType().ElementAt(1)); }); - AddAssert("Guset mapper information show", () => overlay.ChildrenOfType().Single().ChildrenOfType().Any()); + AddAssert("Guset mapper information show", () => overlay.ChildrenOfType().Single().ChildrenOfType().Any(s => s.Text == "BanchoBot")); } private APIBeatmapSet createManyDifficultiesBeatmapSet() diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs index 67348959a6..3689599a6f 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs @@ -31,7 +31,7 @@ namespace osu.Game.Overlays.BeatmapSet private const float tile_spacing = 2; private readonly OsuSpriteText version, starRating, starRatingText; - private readonly FillFlowContainer guestMapperContainer; + private readonly LinkFlowContainer guestMapperContainer; private readonly FillFlowContainer starRatingContainer; private readonly Statistic plays, favourites; @@ -89,7 +89,8 @@ namespace osu.Game.Overlays.BeatmapSet Origin = Anchor.BottomLeft, Font = OsuFont.GetFont(size: 17, weight: FontWeight.Bold) }, - guestMapperContainer = new FillFlowContainer + guestMapperContainer = new LinkFlowContainer(s => + s.Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 15)) { AutoSizeAxes = Axes.Both, Anchor = Anchor.BottomLeft, @@ -209,11 +210,11 @@ namespace osu.Game.Overlays.BeatmapSet { guestMapperContainer.Clear(); - if (beatmapInfo != null && beatmapSet?.Author.OnlineID != beatmapInfo.AuthorID) + if (beatmapInfo != null && beatmapInfo.AuthorID != beatmapSet?.AuthorID) { - guestMapperContainer.Child = getGuestMapper(user); - APIUser? user = BeatmapSet?.RelatedUsers?.Single(u => u.OnlineID == beatmapInfo?.AuthorID); + APIUser? user = BeatmapSet?.RelatedUsers?.Single(u => u.OnlineID == beatmapInfo.AuthorID); if (user != null) + getGuestMapper(user); } version.Text = beatmapInfo?.DifficultyName ?? string.Empty; From 9dd30e4b4cb87a783c3e3f121b0238716137e252 Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Mon, 3 Apr 2023 20:24:38 +0900 Subject: [PATCH 351/476] condition fix --- osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs index 3689599a6f..63bb122cb6 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs @@ -210,9 +210,9 @@ namespace osu.Game.Overlays.BeatmapSet { guestMapperContainer.Clear(); - if (beatmapInfo != null && beatmapInfo.AuthorID != beatmapSet?.AuthorID) + if (beatmapInfo?.AuthorID != beatmapSet?.AuthorID) { - APIUser? user = BeatmapSet?.RelatedUsers?.Single(u => u.OnlineID == beatmapInfo.AuthorID); + APIUser? user = BeatmapSet?.RelatedUsers?.SingleOrDefault(u => u.OnlineID == beatmapInfo?.AuthorID); if (user != null) getGuestMapper(user); } From 9e0277b2fd85c825cb35e9cf47a1f4313db12656 Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Tue, 4 Apr 2023 00:19:14 +0900 Subject: [PATCH 352/476] useless using --- osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs index 37f7b7623b..9e46738305 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs @@ -14,7 +14,6 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Testing; using osu.Game.Beatmaps.Drawables; -using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Online.API; using osu.Game.Online.API.Requests; From ed07c0640b4edca4424103fdadd45e9044dc8976 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 3 Apr 2023 20:13:59 +0200 Subject: [PATCH 353/476] Remove unused using directive --- osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.cs b/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.cs index d47a50b94d..67206fcd8f 100644 --- a/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.cs @@ -8,7 +8,6 @@ using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.UI; using osuTK; -using osuTK.Input; namespace osu.Game.Rulesets.Taiko.Edit.Blueprints { From f42a463479d94480aefaf0189adddeafe0df3965 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 3 Apr 2023 20:14:17 +0200 Subject: [PATCH 354/476] Remove `#nullable disable` May as well that we're here... --- .../Edit/Blueprints/HitPlacementBlueprint.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.cs b/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.cs index 67206fcd8f..f152c98a2e 100644 --- a/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.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 disable - using osu.Framework.Input.Events; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Taiko.Objects; From f9ebdadfe8da725e41f3fd4cd86a21a832457c7e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 Apr 2023 18:53:25 +0900 Subject: [PATCH 355/476] Move right-side editor toolbox to base `HitObjectComposer` Move right-side editor toolbox to base `HitObjectComposer` --- .../Edit/CatchHitObjectComposer.cs | 1 - .../Edit/DistancedHitObjectComposer.cs | 53 ++++++------------- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 22 +++++++- 3 files changed, 37 insertions(+), 39 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs index ea5f54a775..cd8894753f 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs @@ -48,7 +48,6 @@ namespace osu.Game.Rulesets.Catch.Edit private void load() { // todo: enable distance spacing once catch supports applying it to its existing distance snap grid implementation. - RightSideToolboxContainer.Alpha = 0; DistanceSpacingMultiplier.Disabled = true; LayerBelowRuleset.Add(new PlayfieldBorder diff --git a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs index 0df481737e..aa47b4f424 100644 --- a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs @@ -11,8 +11,6 @@ using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; @@ -47,8 +45,6 @@ namespace osu.Game.Rulesets.Edit IBindable IDistanceSnapProvider.DistanceSpacingMultiplier => DistanceSpacingMultiplier; - protected ExpandingToolboxContainer RightSideToolboxContainer { get; private set; } - private ExpandableSlider> distanceSpacingSlider; private ExpandableButton currentDistanceSpacingButton; @@ -67,47 +63,29 @@ namespace osu.Game.Rulesets.Edit [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider) { - AddInternal(new Container + RightToolbox.Add(new EditorToolboxGroup("snapping") { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, + Alpha = DistanceSpacingMultiplier.Disabled ? 0 : 1, Children = new Drawable[] { - new Box + distanceSpacingSlider = new ExpandableSlider> { - Colour = colourProvider.Background5, - RelativeSizeAxes = Axes.Both, + KeyboardStep = adjust_step, + // Manual binding in LoadComplete to handle one-way event flow. + Current = DistanceSpacingMultiplier.GetUnboundCopy(), }, - RightSideToolboxContainer = new ExpandingToolboxContainer(130, 250) + currentDistanceSpacingButton = new ExpandableButton { - Alpha = DistanceSpacingMultiplier.Disabled ? 0 : 1, - Child = new EditorToolboxGroup("snapping") + Action = () => { - Children = new Drawable[] - { - distanceSpacingSlider = new ExpandableSlider> - { - KeyboardStep = adjust_step, - // Manual binding in LoadComplete to handle one-way event flow. - Current = DistanceSpacingMultiplier.GetUnboundCopy(), - }, - currentDistanceSpacingButton = new ExpandableButton - { - Action = () => - { - (HitObject before, HitObject after)? objects = getObjectsOnEitherSideOfCurrentTime(); + (HitObject before, HitObject after)? objects = getObjectsOnEitherSideOfCurrentTime(); - Debug.Assert(objects != null); + Debug.Assert(objects != null); - DistanceSpacingMultiplier.Value = ReadCurrentDistanceSnap(objects.Value.before, objects.Value.after); - DistanceSnapToggle.Value = TernaryState.True; - }, - RelativeSizeAxes = Axes.X, - } - } - } + DistanceSpacingMultiplier.Value = ReadCurrentDistanceSnap(objects.Value.before, objects.Value.after); + DistanceSnapToggle.Value = TernaryState.True; + }, + RelativeSizeAxes = Axes.X, } } }); @@ -261,7 +239,8 @@ namespace osu.Game.Rulesets.Edit public virtual float GetBeatSnapDistanceAt(HitObject referenceObject, bool useReferenceSliderVelocity = true) { - return (float)(100 * (useReferenceSliderVelocity ? referenceObject.DifficultyControlPoint.SliderVelocity : 1) * EditorBeatmap.Difficulty.SliderMultiplier * 1 / BeatSnapProvider.BeatDivisor); + return (float)(100 * (useReferenceSliderVelocity ? referenceObject.DifficultyControlPoint.SliderVelocity : 1) * EditorBeatmap.Difficulty.SliderMultiplier * 1 + / BeatSnapProvider.BeatDivisor); } public virtual float DurationToDistance(HitObject referenceObject, double duration) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index aee86fd942..c2250b1cd5 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -60,6 +60,10 @@ namespace osu.Game.Rulesets.Edit protected ComposeBlueprintContainer BlueprintContainer { get; private set; } + protected ExpandingToolboxContainer LeftToolbox { get; private set; } + + protected ExpandingToolboxContainer RightToolbox { get; private set; } + private DrawableEditorRulesetWrapper drawableRulesetWrapper; protected readonly Container LayerBelowRuleset = new Container { RelativeSizeAxes = Axes.Both }; @@ -131,7 +135,7 @@ namespace osu.Game.Rulesets.Edit Colour = colourProvider.Background5, RelativeSizeAxes = Axes.Both, }, - new ExpandingToolboxContainer(60, 200) + LeftToolbox = new ExpandingToolboxContainer(60, 200) { Children = new Drawable[] { @@ -153,6 +157,22 @@ namespace osu.Game.Rulesets.Edit }, } }, + new Container + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Children = new Drawable[] + { + new Box + { + Colour = colourProvider.Background5, + RelativeSizeAxes = Axes.Both, + }, + RightToolbox = new ExpandingToolboxContainer(130, 250) + } + } }; toolboxCollection.Items = CompositionTools From c356c163fa3b86597090751e1e4dcc3bb9598d86 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 Apr 2023 19:03:45 +0900 Subject: [PATCH 356/476] Add hit object inspector view --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 98 +++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index c2250b1cd5..9f5663747c 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -18,12 +18,15 @@ using osu.Framework.Input.Events; using osu.Framework.Logging; using osu.Game.Beatmaps; using osu.Game.Configuration; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Overlays; using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Screens.Edit; @@ -33,6 +36,7 @@ using osu.Game.Screens.Edit.Compose; using osu.Game.Screens.Edit.Compose.Components; using osuTK; using osuTK.Input; +using FontWeight = osu.Game.Graphics.FontWeight; namespace osu.Game.Rulesets.Edit { @@ -77,6 +81,14 @@ namespace osu.Game.Rulesets.Edit private IBindable hasTiming; private Bindable autoSeekOnPlacement; + [Resolved] + private EditorBeatmap beatmap { get; set; } + + [Resolved] + private OverlayColourProvider colours { get; set; } + + private OsuTextFlowContainer inspectorText; + protected HitObjectComposer(Ruleset ruleset) : base(ruleset) { @@ -171,6 +183,16 @@ namespace osu.Game.Rulesets.Edit RelativeSizeAxes = Axes.Both, }, RightToolbox = new ExpandingToolboxContainer(130, 250) + { + Child = new EditorToolboxGroup("inspector") + { + Child = inspectorText = new OsuTextFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + } + }, + } } } }; @@ -212,6 +234,13 @@ namespace osu.Game.Rulesets.Edit }); } + protected override void Update() + { + base.Update(); + + updateInspectorText(); + } + public override Playfield Playfield => drawableRulesetWrapper.Playfield; public override IEnumerable HitObjects => drawableRulesetWrapper.Playfield.AllHitObjects; @@ -289,6 +318,75 @@ namespace osu.Game.Rulesets.Edit return base.OnKeyDown(e); } + private void updateInspectorText() + { + if (beatmap.SelectedHitObjects.Count != 1) + { + inspectorText.Clear(); + return; + } + + var selected = beatmap.SelectedHitObjects.Single(); + + inspectorText.Clear(); + + addHeader("Time"); + addValue($"{selected.StartTime:#,0.##}ms"); + + switch (selected) + { + case IHasPosition pos: + addHeader("Position"); + addValue($"x:{pos.X:#,0.##} y:{pos.Y:#,0.##}"); + break; + + case IHasXPosition x: + addHeader("Position"); + + addValue($"x:{x.X:#,0.##} "); + break; + + case IHasYPosition y: + addHeader("Position"); + + addValue($"y:{y.Y:#,0.##}"); + break; + } + + if (selected is IHasDistance distance) + { + addHeader("Distance"); + addValue($"{distance.Distance:#,0.##}px"); + } + + if (selected is IHasRepeats repeats) + { + addHeader("Repeats"); + addValue($"{repeats.RepeatCount:#,0.##}"); + } + + if (selected is IHasDuration duration) + { + addHeader("End Time"); + addValue($"{duration.EndTime:#,0.##}ms"); + addHeader("Duration"); + addValue($"{duration.Duration:#,0.##}ms"); + } + + void addHeader(string header) => inspectorText.AddParagraph($"{header}: ", s => + { + s.Padding = new MarginPadding { Top = 2 }; + s.Font = s.Font.With(size: 12); + s.Colour = colours.Content2; + }); + + void addValue(string value) => inspectorText.AddParagraph(value, s => + { + s.Font = s.Font.With(weight: FontWeight.SemiBold); + s.Colour = colours.Content1; + }); + } + private bool checkLeftToggleFromKey(Key key, out int index) { if (key < Key.Number1 || key > Key.Number9) From b0d57616679c01c8d6ac73000ceb034331ff2cc1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 Apr 2023 19:05:50 +0900 Subject: [PATCH 357/476] Add object type --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 9f5663747c..753667eb4a 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -10,6 +10,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.EnumExtensions; +using osu.Framework.Extensions.TypeExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -330,6 +331,9 @@ namespace osu.Game.Rulesets.Edit inspectorText.Clear(); + addHeader("Type"); + addValue($"{selected.GetType().ReadableName()}"); + addHeader("Time"); addValue($"{selected.StartTime:#,0.##}ms"); From 195b5fc3f182adfb7ffa68a98102571f635eda0b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 Apr 2023 19:11:48 +0900 Subject: [PATCH 358/476] Add view for selections of size != 1 --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 103 +++++++++++--------- 1 file changed, 59 insertions(+), 44 deletions(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 753667eb4a..b4c8c7798e 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -321,62 +321,77 @@ namespace osu.Game.Rulesets.Edit private void updateInspectorText() { - if (beatmap.SelectedHitObjects.Count != 1) - { - inspectorText.Clear(); - return; - } - - var selected = beatmap.SelectedHitObjects.Single(); - inspectorText.Clear(); - addHeader("Type"); - addValue($"{selected.GetType().ReadableName()}"); - - addHeader("Time"); - addValue($"{selected.StartTime:#,0.##}ms"); - - switch (selected) + switch (beatmap.SelectedHitObjects.Count) { - case IHasPosition pos: - addHeader("Position"); - addValue($"x:{pos.X:#,0.##} y:{pos.Y:#,0.##}"); + case 0: + addHeader("No selection"); break; - case IHasXPosition x: - addHeader("Position"); + case 1: + var selected = beatmap.SelectedHitObjects.Single(); + + addHeader("Type"); + addValue($"{selected.GetType().ReadableName()}"); + + addHeader("Time"); + addValue($"{selected.StartTime:#,0.##}ms"); + + switch (selected) + { + case IHasPosition pos: + addHeader("Position"); + addValue($"x:{pos.X:#,0.##} y:{pos.Y:#,0.##}"); + break; + + case IHasXPosition x: + addHeader("Position"); + + addValue($"x:{x.X:#,0.##} "); + break; + + case IHasYPosition y: + addHeader("Position"); + + addValue($"y:{y.Y:#,0.##}"); + break; + } + + if (selected is IHasDistance distance) + { + addHeader("Distance"); + addValue($"{distance.Distance:#,0.##}px"); + } + + if (selected is IHasRepeats repeats) + { + addHeader("Repeats"); + addValue($"{repeats.RepeatCount:#,0.##}"); + } + + if (selected is IHasDuration duration) + { + addHeader("End Time"); + addValue($"{duration.EndTime:#,0.##}ms"); + addHeader("Duration"); + addValue($"{duration.Duration:#,0.##}ms"); + } - addValue($"x:{x.X:#,0.##} "); break; - case IHasYPosition y: - addHeader("Position"); + default: + addHeader("Selected Objects"); + addValue($"{beatmap.SelectedHitObjects.Count:#,0.##}"); - addValue($"y:{y.Y:#,0.##}"); + addHeader("Start Time"); + addValue($"{beatmap.SelectedHitObjects.Min(o => o.StartTime):#,0.##}"); + + addHeader("End Time"); + addValue($"{beatmap.SelectedHitObjects.Max(o => o.GetEndTime()):#,0.##}"); break; } - if (selected is IHasDistance distance) - { - addHeader("Distance"); - addValue($"{distance.Distance:#,0.##}px"); - } - - if (selected is IHasRepeats repeats) - { - addHeader("Repeats"); - addValue($"{repeats.RepeatCount:#,0.##}"); - } - - if (selected is IHasDuration duration) - { - addHeader("End Time"); - addValue($"{duration.EndTime:#,0.##}ms"); - addHeader("Duration"); - addValue($"{duration.Duration:#,0.##}ms"); - } - void addHeader(string header) => inspectorText.AddParagraph($"{header}: ", s => { s.Padding = new MarginPadding { Top = 2 }; From 4aed483005abf9eaf9ad3f73197cdfad6fa2f051 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 Apr 2023 19:14:30 +0900 Subject: [PATCH 359/476] Tidy up dependency resolution --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 25 +++++++++------------ 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index b4c8c7798e..b28dfdf3f0 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -63,6 +63,9 @@ namespace osu.Game.Rulesets.Edit [Resolved] protected IBeatSnapProvider BeatSnapProvider { get; private set; } + [Resolved] + private OverlayColourProvider colourProvider { get; set; } + protected ComposeBlueprintContainer BlueprintContainer { get; private set; } protected ExpandingToolboxContainer LeftToolbox { get; private set; } @@ -82,12 +85,6 @@ namespace osu.Game.Rulesets.Edit private IBindable hasTiming; private Bindable autoSeekOnPlacement; - [Resolved] - private EditorBeatmap beatmap { get; set; } - - [Resolved] - private OverlayColourProvider colours { get; set; } - private OsuTextFlowContainer inspectorText; protected HitObjectComposer(Ruleset ruleset) @@ -99,7 +96,7 @@ namespace osu.Game.Rulesets.Edit dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider, OsuConfigManager config) + private void load(OsuConfigManager config) { autoSeekOnPlacement = config.GetBindable(OsuSetting.EditorAutoSeekOnPlacement); @@ -323,14 +320,14 @@ namespace osu.Game.Rulesets.Edit { inspectorText.Clear(); - switch (beatmap.SelectedHitObjects.Count) + switch (EditorBeatmap.SelectedHitObjects.Count) { case 0: addHeader("No selection"); break; case 1: - var selected = beatmap.SelectedHitObjects.Single(); + var selected = EditorBeatmap.SelectedHitObjects.Single(); addHeader("Type"); addValue($"{selected.GetType().ReadableName()}"); @@ -382,13 +379,13 @@ namespace osu.Game.Rulesets.Edit default: addHeader("Selected Objects"); - addValue($"{beatmap.SelectedHitObjects.Count:#,0.##}"); + addValue($"{EditorBeatmap.SelectedHitObjects.Count:#,0.##}"); addHeader("Start Time"); - addValue($"{beatmap.SelectedHitObjects.Min(o => o.StartTime):#,0.##}"); + addValue($"{EditorBeatmap.SelectedHitObjects.Min(o => o.StartTime):#,0.##}"); addHeader("End Time"); - addValue($"{beatmap.SelectedHitObjects.Max(o => o.GetEndTime()):#,0.##}"); + addValue($"{EditorBeatmap.SelectedHitObjects.Max(o => o.GetEndTime()):#,0.##}"); break; } @@ -396,13 +393,13 @@ namespace osu.Game.Rulesets.Edit { s.Padding = new MarginPadding { Top = 2 }; s.Font = s.Font.With(size: 12); - s.Colour = colours.Content2; + s.Colour = colourProvider.Content2; }); void addValue(string value) => inspectorText.AddParagraph(value, s => { s.Font = s.Font.With(weight: FontWeight.SemiBold); - s.Colour = colours.Content1; + s.Colour = colourProvider.Content1; }); } From 3209b092702aaa78c376422de632d48c86792d65 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 Apr 2023 19:17:14 +0900 Subject: [PATCH 360/476] Move inspector into own file --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 107 +-------------- osu.Game/Rulesets/Edit/HitObjectInspector.cs | 132 +++++++++++++++++++ 2 files changed, 133 insertions(+), 106 deletions(-) create mode 100644 osu.Game/Rulesets/Edit/HitObjectInspector.cs diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index b28dfdf3f0..653861c11c 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -10,7 +10,6 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.EnumExtensions; -using osu.Framework.Extensions.TypeExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -19,15 +18,12 @@ using osu.Framework.Input.Events; using osu.Framework.Logging; using osu.Game.Beatmaps; using osu.Game.Configuration; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; using osu.Game.Overlays; using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Screens.Edit; @@ -37,7 +33,6 @@ using osu.Game.Screens.Edit.Compose; using osu.Game.Screens.Edit.Compose.Components; using osuTK; using osuTK.Input; -using FontWeight = osu.Game.Graphics.FontWeight; namespace osu.Game.Rulesets.Edit { @@ -85,8 +80,6 @@ namespace osu.Game.Rulesets.Edit private IBindable hasTiming; private Bindable autoSeekOnPlacement; - private OsuTextFlowContainer inspectorText; - protected HitObjectComposer(Ruleset ruleset) : base(ruleset) { @@ -184,11 +177,7 @@ namespace osu.Game.Rulesets.Edit { Child = new EditorToolboxGroup("inspector") { - Child = inspectorText = new OsuTextFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - } + Child = new HitObjectInspector() }, } } @@ -232,13 +221,6 @@ namespace osu.Game.Rulesets.Edit }); } - protected override void Update() - { - base.Update(); - - updateInspectorText(); - } - public override Playfield Playfield => drawableRulesetWrapper.Playfield; public override IEnumerable HitObjects => drawableRulesetWrapper.Playfield.AllHitObjects; @@ -316,93 +298,6 @@ namespace osu.Game.Rulesets.Edit return base.OnKeyDown(e); } - private void updateInspectorText() - { - inspectorText.Clear(); - - switch (EditorBeatmap.SelectedHitObjects.Count) - { - case 0: - addHeader("No selection"); - break; - - case 1: - var selected = EditorBeatmap.SelectedHitObjects.Single(); - - addHeader("Type"); - addValue($"{selected.GetType().ReadableName()}"); - - addHeader("Time"); - addValue($"{selected.StartTime:#,0.##}ms"); - - switch (selected) - { - case IHasPosition pos: - addHeader("Position"); - addValue($"x:{pos.X:#,0.##} y:{pos.Y:#,0.##}"); - break; - - case IHasXPosition x: - addHeader("Position"); - - addValue($"x:{x.X:#,0.##} "); - break; - - case IHasYPosition y: - addHeader("Position"); - - addValue($"y:{y.Y:#,0.##}"); - break; - } - - if (selected is IHasDistance distance) - { - addHeader("Distance"); - addValue($"{distance.Distance:#,0.##}px"); - } - - if (selected is IHasRepeats repeats) - { - addHeader("Repeats"); - addValue($"{repeats.RepeatCount:#,0.##}"); - } - - if (selected is IHasDuration duration) - { - addHeader("End Time"); - addValue($"{duration.EndTime:#,0.##}ms"); - addHeader("Duration"); - addValue($"{duration.Duration:#,0.##}ms"); - } - - break; - - default: - addHeader("Selected Objects"); - addValue($"{EditorBeatmap.SelectedHitObjects.Count:#,0.##}"); - - addHeader("Start Time"); - addValue($"{EditorBeatmap.SelectedHitObjects.Min(o => o.StartTime):#,0.##}"); - - addHeader("End Time"); - addValue($"{EditorBeatmap.SelectedHitObjects.Max(o => o.GetEndTime()):#,0.##}"); - break; - } - - void addHeader(string header) => inspectorText.AddParagraph($"{header}: ", s => - { - s.Padding = new MarginPadding { Top = 2 }; - s.Font = s.Font.With(size: 12); - s.Colour = colourProvider.Content2; - }); - - void addValue(string value) => inspectorText.AddParagraph(value, s => - { - s.Font = s.Font.With(weight: FontWeight.SemiBold); - s.Colour = colourProvider.Content1; - }); - } - private bool checkLeftToggleFromKey(Key key, out int index) { if (key < Key.Number1 || key > Key.Number9) diff --git a/osu.Game/Rulesets/Edit/HitObjectInspector.cs b/osu.Game/Rulesets/Edit/HitObjectInspector.cs new file mode 100644 index 0000000000..a6837b24f2 --- /dev/null +++ b/osu.Game/Rulesets/Edit/HitObjectInspector.cs @@ -0,0 +1,132 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable disable +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Extensions.TypeExtensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Overlays; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Screens.Edit; + +namespace osu.Game.Rulesets.Edit +{ + internal partial class HitObjectInspector : CompositeDrawable + { + private OsuTextFlowContainer inspectorText; + + [Resolved] + protected EditorBeatmap EditorBeatmap { get; private set; } + + [Resolved] + private OverlayColourProvider colourProvider { get; set; } + + [BackgroundDependencyLoader] + private void load() + { + InternalChild = inspectorText = new OsuTextFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + }; + } + + protected override void Update() + { + base.Update(); + updateInspectorText(); + } + + private void updateInspectorText() + { + inspectorText.Clear(); + + switch (EditorBeatmap.SelectedHitObjects.Count) + { + case 0: + addHeader("No selection"); + break; + + case 1: + var selected = EditorBeatmap.SelectedHitObjects.Single(); + + addHeader("Type"); + addValue($"{selected.GetType().ReadableName()}"); + + addHeader("Time"); + addValue($"{selected.StartTime:#,0.##}ms"); + + switch (selected) + { + case IHasPosition pos: + addHeader("Position"); + addValue($"x:{pos.X:#,0.##} y:{pos.Y:#,0.##}"); + break; + + case IHasXPosition x: + addHeader("Position"); + + addValue($"x:{x.X:#,0.##} "); + break; + + case IHasYPosition y: + addHeader("Position"); + + addValue($"y:{y.Y:#,0.##}"); + break; + } + + if (selected is IHasDistance distance) + { + addHeader("Distance"); + addValue($"{distance.Distance:#,0.##}px"); + } + + if (selected is IHasRepeats repeats) + { + addHeader("Repeats"); + addValue($"{repeats.RepeatCount:#,0.##}"); + } + + if (selected is IHasDuration duration) + { + addHeader("End Time"); + addValue($"{duration.EndTime:#,0.##}ms"); + addHeader("Duration"); + addValue($"{duration.Duration:#,0.##}ms"); + } + + break; + + default: + addHeader("Selected Objects"); + addValue($"{EditorBeatmap.SelectedHitObjects.Count:#,0.##}"); + + addHeader("Start Time"); + addValue($"{EditorBeatmap.SelectedHitObjects.Min(o => o.StartTime):#,0.##}"); + + addHeader("End Time"); + addValue($"{EditorBeatmap.SelectedHitObjects.Max(o => o.GetEndTime()):#,0.##}"); + break; + } + + void addHeader(string header) => inspectorText.AddParagraph($"{header}: ", s => + { + s.Padding = new MarginPadding { Top = 2 }; + s.Font = s.Font.With(size: 12); + s.Colour = colourProvider.Content2; + }); + + void addValue(string value) => inspectorText.AddParagraph(value, s => + { + s.Font = s.Font.With(weight: FontWeight.SemiBold); + s.Colour = colourProvider.Content1; + }); + } + } +} From f07d859532cc8906c298fddc59e187be1b3b2fd7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 Apr 2023 19:31:33 +0900 Subject: [PATCH 361/476] Optimise how often we update the display --- osu.Game/Rulesets/Edit/HitObjectInspector.cs | 24 +++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectInspector.cs b/osu.Game/Rulesets/Edit/HitObjectInspector.cs index a6837b24f2..71a3202f8d 100644 --- a/osu.Game/Rulesets/Edit/HitObjectInspector.cs +++ b/osu.Game/Rulesets/Edit/HitObjectInspector.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. -#nullable disable using System.Linq; using osu.Framework.Allocation; using osu.Framework.Extensions.TypeExtensions; @@ -18,17 +17,20 @@ namespace osu.Game.Rulesets.Edit { internal partial class HitObjectInspector : CompositeDrawable { - private OsuTextFlowContainer inspectorText; + private OsuTextFlowContainer inspectorText = null!; [Resolved] - protected EditorBeatmap EditorBeatmap { get; private set; } + protected EditorBeatmap EditorBeatmap { get; private set; } = null!; [Resolved] - private OverlayColourProvider colourProvider { get; set; } + private OverlayColourProvider colourProvider { get; set; } = null!; [BackgroundDependencyLoader] private void load() { + AutoSizeAxes = Axes.Y; + RelativeSizeAxes = Axes.X; + InternalChild = inspectorText = new OsuTextFlowContainer { RelativeSizeAxes = Axes.X, @@ -36,10 +38,13 @@ namespace osu.Game.Rulesets.Edit }; } - protected override void Update() + protected override void LoadComplete() { - base.Update(); - updateInspectorText(); + base.LoadComplete(); + + EditorBeatmap.SelectedHitObjects.CollectionChanged += (_, _) => updateInspectorText(); + EditorBeatmap.TransactionBegan += updateInspectorText; + EditorBeatmap.TransactionEnded += updateInspectorText; } private void updateInspectorText() @@ -49,7 +54,7 @@ namespace osu.Game.Rulesets.Edit switch (EditorBeatmap.SelectedHitObjects.Count) { case 0: - addHeader("No selection"); + addValue("No selection"); break; case 1: @@ -115,6 +120,9 @@ namespace osu.Game.Rulesets.Edit break; } + if (EditorBeatmap.TransactionActive) + Scheduler.AddDelayed(updateInspectorText, 100); + void addHeader(string header) => inspectorText.AddParagraph($"{header}: ", s => { s.Padding = new MarginPadding { Top = 2 }; From 7d9327f3e209665dd13e08020dcb053dc05671ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 4 Apr 2023 18:40:10 +0200 Subject: [PATCH 362/476] Only allow hit placement when left mouse is clicked --- .../Edit/Blueprints/HitPlacementBlueprint.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.cs b/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.cs index f152c98a2e..8b1a4f688c 100644 --- a/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.cs @@ -6,6 +6,7 @@ using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.UI; using osuTK; +using osuTK.Input; namespace osu.Game.Rulesets.Taiko.Edit.Blueprints { @@ -32,6 +33,9 @@ namespace osu.Game.Rulesets.Taiko.Edit.Blueprints protected override bool OnMouseDown(MouseDownEvent e) { + if (e.Button != MouseButton.Left) + return false; + EndPlacement(true); return true; } From 9c8b25e0348cc2b9be66c22b0d4f4b46f6b880a8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 Apr 2023 13:45:24 +0900 Subject: [PATCH 363/476] Fix display not always updating when expected by updating on a schedule --- osu.Game/Rulesets/Edit/HitObjectInspector.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectInspector.cs b/osu.Game/Rulesets/Edit/HitObjectInspector.cs index 71a3202f8d..1870476ca0 100644 --- a/osu.Game/Rulesets/Edit/HitObjectInspector.cs +++ b/osu.Game/Rulesets/Edit/HitObjectInspector.cs @@ -106,6 +106,9 @@ namespace osu.Game.Rulesets.Edit addValue($"{duration.Duration:#,0.##}ms"); } + // I'd hope there's a better way to do this, but I don't want to bind to each and every property above to watch for changes. + // This is a good middle-ground for the time being. + Scheduler.AddDelayed(updateInspectorText, 250); break; default: @@ -120,9 +123,6 @@ namespace osu.Game.Rulesets.Edit break; } - if (EditorBeatmap.TransactionActive) - Scheduler.AddDelayed(updateInspectorText, 100); - void addHeader(string header) => inspectorText.AddParagraph($"{header}: ", s => { s.Padding = new MarginPadding { Top = 2 }; From e5d57a65c9d651419e750fb4f7a9038366651ea7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 5 Apr 2023 19:47:25 +0200 Subject: [PATCH 364/476] Fix incorrect indent --- osu.Game/Rulesets/UI/RulesetInputManager.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index 8b3d62034a..2ae54a3afe 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -166,10 +166,10 @@ namespace osu.Game.Rulesets.UI keyCounter.SetReceptor(receptor); keyCounter.AddRange(KeyBindingContainer.DefaultKeyBindings - .Select(b => b.GetAction()) - .Distinct() - .OrderBy(action => action) - .Select(action => new KeyCounterActionTrigger(action))); + .Select(b => b.GetAction()) + .Distinct() + .OrderBy(action => action) + .Select(action => new KeyCounterActionTrigger(action))); } private partial class ActionReceptor : KeyCounterDisplay.Receptor, IKeyBindingHandler From 02c6126be7807833ba9583006ae6eba54f21d68a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 5 Apr 2023 20:53:54 +0200 Subject: [PATCH 365/476] Ensure storyboards are enabled in existing epilepsy warning tests --- osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index 2ea27c2fef..0c7991e0ca 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -45,6 +45,9 @@ namespace osu.Game.Tests.Visual.Gameplay [Resolved] private SessionStatics sessionStatics { get; set; } + [Resolved] + private OsuConfigManager config { get; set; } + [Cached(typeof(INotificationOverlay))] private readonly NotificationOverlay notificationOverlay; @@ -317,6 +320,7 @@ namespace osu.Game.Tests.Visual.Gameplay saveVolumes(); setFullVolume(); + AddStep("enable storyboards", () => config.SetValue(OsuSetting.ShowStoryboard, true)); AddStep("change epilepsy warning", () => epilepsyWarning = warning); AddStep("load dummy beatmap", () => resetPlayer(false)); @@ -339,6 +343,7 @@ namespace osu.Game.Tests.Visual.Gameplay saveVolumes(); setFullVolume(); + AddStep("enable storyboards", () => config.SetValue(OsuSetting.ShowStoryboard, true)); AddStep("set epilepsy warning", () => epilepsyWarning = true); AddStep("load dummy beatmap", () => resetPlayer(false)); From 6df7614b9df0ed26479d147b6c193d1ea7f6a0b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 5 Apr 2023 20:56:50 +0200 Subject: [PATCH 366/476] Add tests for suppressing epilepsy warning when storyboard disabled --- .../Visual/Gameplay/TestScenePlayerLoader.cs | 19 ++++++++++++++++++- osu.Game/Screens/Play/PlayerLoader.cs | 1 + 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index 0c7991e0ca..dbd1ce1f6e 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -337,6 +337,23 @@ namespace osu.Game.Tests.Visual.Gameplay restoreVolumes(); } + [Test] + public void TestEpilepsyWarningWithDisabledStoryboard() + { + saveVolumes(); + setFullVolume(); + + AddStep("disable storyboards", () => config.SetValue(OsuSetting.ShowStoryboard, false)); + AddStep("change epilepsy warning", () => epilepsyWarning = true); + AddStep("load dummy beatmap", () => resetPlayer(false)); + + AddUntilStep("wait for current", () => loader.IsCurrentScreen()); + + AddUntilStep("epilepsy warning absent", () => getWarning() == null); + + restoreVolumes(); + } + [Test] public void TestEpilepsyWarningEarlyExit() { @@ -454,7 +471,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("click notification", () => notification.TriggerClick()); } - private EpilepsyWarning getWarning() => loader.ChildrenOfType().SingleOrDefault(); + private EpilepsyWarning getWarning() => loader.ChildrenOfType().SingleOrDefault(w => w.IsAlive); private partial class TestPlayerLoader : PlayerLoader { diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index be4229ade9..30ae5ee5aa 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -489,6 +489,7 @@ namespace osu.Game.Screens.Play { // This goes hand-in-hand with the restoration of low pass filter in contentOut(). this.TransformBindableTo(volumeAdjustment, 0, CONTENT_OUT_DURATION, Easing.OutCubic); + epilepsyWarning?.Expire(); } pushSequence.Schedule(() => From ed565b1e5905c47e3ab512c50f1894626999289c Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 6 Apr 2023 11:40:04 +0300 Subject: [PATCH 367/476] Fix SampleStore isn't being disposed --- osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs b/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs index 1d5fcc634e..76d84000f1 100644 --- a/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs +++ b/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs @@ -98,7 +98,7 @@ namespace osu.Game.Rulesets.UI isDisposed = true; - if (ShaderManager.IsNotNull()) SampleStore.Dispose(); + if (SampleStore.IsNotNull()) SampleStore.Dispose(); if (TextureStore.IsNotNull()) TextureStore.Dispose(); if (ShaderManager.IsNotNull()) ShaderManager.Dispose(); } From ad717d2368270b7cb04f7d4d3b70c2cc5d860fea Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Apr 2023 23:39:36 +0900 Subject: [PATCH 368/476] Fix scheduled calls piling up during transactions --- osu.Game/Rulesets/Edit/HitObjectInspector.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectInspector.cs b/osu.Game/Rulesets/Edit/HitObjectInspector.cs index 1870476ca0..02270d4662 100644 --- a/osu.Game/Rulesets/Edit/HitObjectInspector.cs +++ b/osu.Game/Rulesets/Edit/HitObjectInspector.cs @@ -6,6 +6,7 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.TypeExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Threading; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Overlays; @@ -47,9 +48,13 @@ namespace osu.Game.Rulesets.Edit EditorBeatmap.TransactionEnded += updateInspectorText; } + private ScheduledDelegate? rollingTextUpdate; + private void updateInspectorText() { inspectorText.Clear(); + rollingTextUpdate?.Cancel(); + rollingTextUpdate = null; switch (EditorBeatmap.SelectedHitObjects.Count) { @@ -108,7 +113,7 @@ namespace osu.Game.Rulesets.Edit // I'd hope there's a better way to do this, but I don't want to bind to each and every property above to watch for changes. // This is a good middle-ground for the time being. - Scheduler.AddDelayed(updateInspectorText, 250); + rollingTextUpdate ??= Scheduler.AddDelayed(updateInspectorText, 250); break; default: From f1de560d5717ad17588640f5745c7e801f7dfd72 Mon Sep 17 00:00:00 2001 From: Micha Lehmann Date: Sat, 8 Apr 2023 00:50:31 +0200 Subject: [PATCH 369/476] Snap editor selection rotation when holding shift --- .../Components/SelectionBoxRotationHandle.cs | 62 ++++++++++++++++--- 1 file changed, 55 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs index 0f702e1c49..04eaf6c491 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs @@ -16,6 +16,8 @@ using osu.Framework.Localisation; using osuTK; using osuTK.Graphics; +using Key = osuTK.Input.Key; + namespace osu.Game.Screens.Edit.Compose.Components { public partial class SelectionBoxRotationHandle : SelectionBoxDragHandle, IHasTooltip @@ -26,6 +28,8 @@ namespace osu.Game.Screens.Edit.Compose.Components private SpriteIcon icon; + private const float snapStep = 15; + private float rawCumulativeRotation = 0; private readonly Bindable cumulativeRotation = new Bindable(); [Resolved] @@ -74,21 +78,38 @@ namespace osu.Game.Screens.Edit.Compose.Components { base.OnDrag(e); - float instantaneousAngle = convertDragEventToAngleOfRotation(e); - cumulativeRotation.Value += instantaneousAngle; + rawCumulativeRotation += convertDragEventToAngleOfRotation(e); - if (cumulativeRotation.Value < -180) - cumulativeRotation.Value += 360; - else if (cumulativeRotation.Value > 180) - cumulativeRotation.Value -= 360; + applyRotation(shouldSnap: e.ShiftPressed); + } - HandleRotate?.Invoke(instantaneousAngle); + protected override bool OnKeyDown(KeyDownEvent e) + { + base.OnKeyDown(e); + + if (cumulativeRotation.Value != null && (e.Key == Key.ShiftLeft || e.Key == Key.ShiftRight)) + { + applyRotation(shouldSnap: true); + } + + return true; + } + + protected override void OnKeyUp(KeyUpEvent e) + { + base.OnKeyUp(e); + + if (cumulativeRotation.Value != null && (e.Key == Key.ShiftLeft || e.Key == Key.ShiftRight)) + { + applyRotation(shouldSnap: false); + } } protected override void OnDragEnd(DragEndEvent e) { base.OnDragEnd(e); cumulativeRotation.Value = null; + rawCumulativeRotation = 0; } private float convertDragEventToAngleOfRotation(DragEvent e) @@ -100,6 +121,33 @@ namespace osu.Game.Screens.Edit.Compose.Components return (endAngle - startAngle) * 180 / MathF.PI; } + private void applyRotation(bool shouldSnap) + { + float oldRotation = cumulativeRotation.Value ?? 0; + + if (shouldSnap) + { + cumulativeRotation.Value = snap(rawCumulativeRotation, snapStep); + } + else + { + cumulativeRotation.Value = rawCumulativeRotation; + } + + if (cumulativeRotation.Value < -180) + cumulativeRotation.Value += 360; + else if (cumulativeRotation.Value > 180) + cumulativeRotation.Value -= 360; + + HandleRotate?.Invoke((float)cumulativeRotation.Value - oldRotation); + } + + private float snap(float value, float step) + { + float floor = MathF.Floor(value / step) * step; + return value - floor < step / 2f ? floor : floor + step; + } + private void updateTooltipText() { TooltipText = cumulativeRotation.Value?.ToLocalisableString("0.0°") ?? default; From c827c2810b99cae4a725023f69e85652dbd8fde3 Mon Sep 17 00:00:00 2001 From: Micha Lehmann Date: Sat, 8 Apr 2023 01:28:28 +0200 Subject: [PATCH 370/476] Improve editor selection rotation value wrapping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes two issues the previous algorithm had: 1. A half-turn rotation used to show up as -180°. 2. Rotating more than 180° in one drag event would overwhelm it and cause the value to go outside its range. This comes at the cost of a negligible performance hit, since a division (modulo) is performed instead of just addition/subtraction. --- .../Edit/Compose/Components/SelectionBoxRotationHandle.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs index 04eaf6c491..4990a522f8 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs @@ -134,10 +134,7 @@ namespace osu.Game.Screens.Edit.Compose.Components cumulativeRotation.Value = rawCumulativeRotation; } - if (cumulativeRotation.Value < -180) - cumulativeRotation.Value += 360; - else if (cumulativeRotation.Value > 180) - cumulativeRotation.Value -= 360; + cumulativeRotation.Value = (cumulativeRotation.Value - 180) % 360 + 180; HandleRotate?.Invoke((float)cumulativeRotation.Value - oldRotation); } From f72dd86b4251123400048ed2b9474ba69d25ef57 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 8 Apr 2023 10:40:36 +0900 Subject: [PATCH 371/476] Fix code quality issues and avoid updating bindable twice per operation --- .../Components/SelectionBoxRotationHandle.cs | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs index 4990a522f8..5a1587eea6 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs @@ -15,7 +15,6 @@ using osu.Framework.Input.Events; using osu.Framework.Localisation; using osuTK; using osuTK.Graphics; - using Key = osuTK.Input.Key; namespace osu.Game.Screens.Edit.Compose.Components @@ -28,8 +27,8 @@ namespace osu.Game.Screens.Edit.Compose.Components private SpriteIcon icon; - private const float snapStep = 15; - private float rawCumulativeRotation = 0; + private const float snap_step = 15; + private readonly Bindable cumulativeRotation = new Bindable(); [Resolved] @@ -66,6 +65,8 @@ namespace osu.Game.Screens.Edit.Compose.Components icon.FadeColour(!IsHeld && IsHovered ? Color4.White : Color4.Black, TRANSFORM_DURATION, Easing.OutQuint); } + private float rawCumulativeRotation; + protected override bool OnDragStart(DragStartEvent e) { bool handle = base.OnDragStart(e); @@ -125,17 +126,10 @@ namespace osu.Game.Screens.Edit.Compose.Components { float oldRotation = cumulativeRotation.Value ?? 0; - if (shouldSnap) - { - cumulativeRotation.Value = snap(rawCumulativeRotation, snapStep); - } - else - { - cumulativeRotation.Value = rawCumulativeRotation; - } - - cumulativeRotation.Value = (cumulativeRotation.Value - 180) % 360 + 180; + float newRotation = shouldSnap ? snap(rawCumulativeRotation, snap_step) : rawCumulativeRotation; + newRotation = (newRotation - 180) % 360 + 180; + cumulativeRotation.Value = newRotation; HandleRotate?.Invoke((float)cumulativeRotation.Value - oldRotation); } From ed208ef12739ff03fa18fefc15036a7594601739 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Fri, 7 Apr 2023 21:10:37 -0700 Subject: [PATCH 372/476] Fix more typos and adjust font size to match web --- osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs | 6 +++--- osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs index 9e46738305..4838f42043 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs @@ -286,16 +286,16 @@ namespace osu.Game.Tests.Visual.Online public void TestBeatmapSetWithGuestDIff() { AddStep("show map", () => overlay.ShowBeatmapSet(createBeatmapSetWithGuestDiff())); - AddStep("Move mouse to host diff", () => + AddStep("move mouse to host diff", () => { InputManager.MoveMouseTo(overlay.ChildrenOfType().ElementAt(0)); }); - AddAssert("Guset mapper information not show", () => overlay.ChildrenOfType().Single().ChildrenOfType().All(s => s.Text != "BanchoBot")); + AddAssert("guest mapper information not shown", () => overlay.ChildrenOfType().Single().ChildrenOfType().All(s => s.Text != "BanchoBot")); AddStep("move mouse to guest diff", () => { InputManager.MoveMouseTo(overlay.ChildrenOfType().ElementAt(1)); }); - AddAssert("Guset mapper information show", () => overlay.ChildrenOfType().Single().ChildrenOfType().Any(s => s.Text == "BanchoBot")); + AddAssert("guest mapper information shown", () => overlay.ChildrenOfType().Single().ChildrenOfType().Any(s => s.Text == "BanchoBot")); } private APIBeatmapSet createManyDifficultiesBeatmapSet() diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs index 63bb122cb6..d527d4cc9a 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs @@ -90,11 +90,12 @@ namespace osu.Game.Overlays.BeatmapSet Font = OsuFont.GetFont(size: 17, weight: FontWeight.Bold) }, guestMapperContainer = new LinkFlowContainer(s => - s.Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 15)) + s.Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 11)) { AutoSizeAxes = Axes.Both, Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, + Margin = new MarginPadding { Bottom = 1 }, }, starRatingContainer = new FillFlowContainer { From a86a968faca0168128a0ddb432e8dfd590e93c17 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Fri, 7 Apr 2023 21:16:36 -0700 Subject: [PATCH 373/476] Use public `BeatmapSet` to match other usages --- osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs index d527d4cc9a..27e671ded0 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs @@ -211,7 +211,7 @@ namespace osu.Game.Overlays.BeatmapSet { guestMapperContainer.Clear(); - if (beatmapInfo?.AuthorID != beatmapSet?.AuthorID) + if (beatmapInfo?.AuthorID != BeatmapSet?.AuthorID) { APIUser? user = BeatmapSet?.RelatedUsers?.SingleOrDefault(u => u.OnlineID == beatmapInfo?.AuthorID); if (user != null) From 580d5745c0197dbd73c3103ba78b7485ea19fcd6 Mon Sep 17 00:00:00 2001 From: Micha Lehmann Date: Sat, 8 Apr 2023 14:15:49 +0200 Subject: [PATCH 374/476] Add "(snapped)" to the tooltip when snap-rotating in the editor --- osu.Game/Localisation/EditorStrings.cs | 10 ++++++++++ .../Compose/Components/SelectionBoxRotationHandle.cs | 12 +++++------- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/osu.Game/Localisation/EditorStrings.cs b/osu.Game/Localisation/EditorStrings.cs index f4e23ae7cb..beddcfd44e 100644 --- a/osu.Game/Localisation/EditorStrings.cs +++ b/osu.Game/Localisation/EditorStrings.cs @@ -99,6 +99,16 @@ namespace osu.Game.Localisation /// public static LocalisableString TimelineTicks => new TranslatableString(getKey(@"timeline_ticks"), @"Ticks"); + /// + /// "0.0°" + /// + public static LocalisableString RotationFormatUnsnapped => new TranslatableString(getKey(@"rotation_format_unsnapped"), @"0.0°"); + + /// + /// "0.0° (snapped)" + /// + public static LocalisableString RotationFormatSnapped => new TranslatableString(getKey(@"rotation_format_snapped"), @"0.0° (snapped)"); + private static string getKey(string key) => $@"{prefix}:{key}"; } } diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs index 5a1587eea6..8d0e20e4ac 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs @@ -13,6 +13,7 @@ using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Framework.Localisation; +using osu.Game.Localisation; using osuTK; using osuTK.Graphics; using Key = osuTK.Input.Key; @@ -56,7 +57,6 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override void LoadComplete() { base.LoadComplete(); - cumulativeRotation.BindValueChanged(_ => updateTooltipText(), true); } protected override void UpdateHoverState() @@ -130,7 +130,10 @@ namespace osu.Game.Screens.Edit.Compose.Components newRotation = (newRotation - 180) % 360 + 180; cumulativeRotation.Value = newRotation; - HandleRotate?.Invoke((float)cumulativeRotation.Value - oldRotation); + + HandleRotate?.Invoke(newRotation - oldRotation); + string tooltipFormat = shouldSnap ? EditorStrings.RotationFormatSnapped.ToString() : EditorStrings.RotationFormatUnsnapped.ToString(); + TooltipText = newRotation.ToLocalisableString(tooltipFormat); } private float snap(float value, float step) @@ -138,10 +141,5 @@ namespace osu.Game.Screens.Edit.Compose.Components float floor = MathF.Floor(value / step) * step; return value - floor < step / 2f ? floor : floor + step; } - - private void updateTooltipText() - { - TooltipText = cumulativeRotation.Value?.ToLocalisableString("0.0°") ?? default; - } } } From 3c4a25e53f3459aa6ef3e1a9182769b02dd3b966 Mon Sep 17 00:00:00 2001 From: Micha Lehmann Date: Sat, 8 Apr 2023 14:28:52 +0200 Subject: [PATCH 375/476] Fix tooltip text not resetting when ending an editor rotation --- .../Edit/Compose/Components/SelectionBoxRotationHandle.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs index 8d0e20e4ac..09898f1f2f 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs @@ -111,6 +111,7 @@ namespace osu.Game.Screens.Edit.Compose.Components base.OnDragEnd(e); cumulativeRotation.Value = null; rawCumulativeRotation = 0; + TooltipText = default; } private float convertDragEventToAngleOfRotation(DragEvent e) From a1fc4def1dca5791a6cbe0dd44a3badb244718f6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 8 Apr 2023 22:18:07 +0900 Subject: [PATCH 376/476] Remove redundant method override --- .../Edit/Compose/Components/SelectionBoxRotationHandle.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs index 09898f1f2f..fb86cb4a01 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs @@ -54,11 +54,6 @@ namespace osu.Game.Screens.Edit.Compose.Components }); } - protected override void LoadComplete() - { - base.LoadComplete(); - } - protected override void UpdateHoverState() { base.UpdateHoverState(); From 8d2e852ffd2bf8b8d76b4b4132ce3b4aa067e468 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 8 Apr 2023 22:18:29 +0900 Subject: [PATCH 377/476] Fix overbearing key down handling --- .../Compose/Components/SelectionBoxRotationHandle.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs index fb86cb4a01..73da837d03 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs @@ -81,24 +81,21 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override bool OnKeyDown(KeyDownEvent e) { - base.OnKeyDown(e); - - if (cumulativeRotation.Value != null && (e.Key == Key.ShiftLeft || e.Key == Key.ShiftRight)) + if (IsDragged && (e.Key == Key.ShiftLeft || e.Key == Key.ShiftRight)) { applyRotation(shouldSnap: true); + return true; } - return true; + return base.OnKeyDown(e); } protected override void OnKeyUp(KeyUpEvent e) { base.OnKeyUp(e); - if (cumulativeRotation.Value != null && (e.Key == Key.ShiftLeft || e.Key == Key.ShiftRight)) - { + if (IsDragged && (e.Key == Key.ShiftLeft || e.Key == Key.ShiftRight)) applyRotation(shouldSnap: false); - } } protected override void OnDragEnd(DragEndEvent e) From c9234829762667d18272bf187885b0804ee51c1d Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 10 Apr 2023 13:31:48 +0900 Subject: [PATCH 378/476] Add progressive score multiplier for DT --- .../Mods/CatchModDoubleTime.cs | 1 - .../Mods/ManiaModDoubleTime.cs | 1 - osu.Game.Rulesets.Osu/Mods/OsuModDoubleTime.cs | 1 - .../Mods/TaikoModDoubleTime.cs | 1 - osu.Game/Rulesets/Mods/ModDoubleTime.cs | 17 +++++++++++++++++ 5 files changed, 17 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModDoubleTime.cs b/osu.Game.Rulesets.Catch/Mods/CatchModDoubleTime.cs index 57c06e1cd1..83db9f665b 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModDoubleTime.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModDoubleTime.cs @@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Catch.Mods { public class CatchModDoubleTime : ModDoubleTime { - public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.06 : 1; } } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModDoubleTime.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModDoubleTime.cs index a302f95966..f4b9cf3b88 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModDoubleTime.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModDoubleTime.cs @@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Mania.Mods { public class ManiaModDoubleTime : ModDoubleTime { - public override double ScoreMultiplier => 1; } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDoubleTime.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDoubleTime.cs index 700a3f44bc..5569df8d95 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDoubleTime.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDoubleTime.cs @@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModDoubleTime : ModDoubleTime { - public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.12 : 1; } } diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModDoubleTime.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModDoubleTime.cs index 89581c57bd..e517439ba4 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModDoubleTime.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModDoubleTime.cs @@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Taiko.Mods { public class TaikoModDoubleTime : ModDoubleTime { - public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.12 : 1; } } diff --git a/osu.Game/Rulesets/Mods/ModDoubleTime.cs b/osu.Game/Rulesets/Mods/ModDoubleTime.cs index 9e4469bf25..733610c040 100644 --- a/osu.Game/Rulesets/Mods/ModDoubleTime.cs +++ b/osu.Game/Rulesets/Mods/ModDoubleTime.cs @@ -24,5 +24,22 @@ namespace osu.Game.Rulesets.Mods MaxValue = 2, Precision = 0.01, }; + + public override double ScoreMultiplier + { + get + { + // Round to the nearest multiple of 0.1. + double value = (int)(SpeedChange.Value * 10) / 10.0; + + // Offset back to 0. + value -= 1; + + // Each 0.1 multiple changes score multiplier by 0.02. + value /= 5; + + return 1 + value; + } + } } } From 15f6bc155eb49f1fca344e6901d5ec12b8480201 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 10 Apr 2023 13:35:48 +0900 Subject: [PATCH 379/476] Add progressive score multiplier for HT --- osu.Game.Rulesets.Catch/Mods/CatchModHalfTime.cs | 1 - osu.Game.Rulesets.Mania/Mods/ManiaModHalfTime.cs | 1 - osu.Game.Rulesets.Osu/Mods/OsuModHalfTime.cs | 1 - osu.Game.Rulesets.Taiko/Mods/TaikoModHalfTime.cs | 1 - osu.Game/Rulesets/Mods/ModHalfTime.cs | 14 ++++++++++++++ 5 files changed, 14 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModHalfTime.cs b/osu.Game.Rulesets.Catch/Mods/CatchModHalfTime.cs index ce06b841aa..3afb8c3d89 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModHalfTime.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModHalfTime.cs @@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Catch.Mods { public class CatchModHalfTime : ModHalfTime { - public override double ScoreMultiplier => 0.3; } } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModHalfTime.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModHalfTime.cs index 014954dd60..8d48e3acde 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModHalfTime.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModHalfTime.cs @@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Mania.Mods { public class ManiaModHalfTime : ModHalfTime { - public override double ScoreMultiplier => 0.5; } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHalfTime.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHalfTime.cs index 4769e7660b..bf65a6c9d3 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHalfTime.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHalfTime.cs @@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModHalfTime : ModHalfTime { - public override double ScoreMultiplier => 0.3; } } diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModHalfTime.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModHalfTime.cs index 68d6305fbf..9ef6fe8649 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModHalfTime.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModHalfTime.cs @@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Taiko.Mods { public class TaikoModHalfTime : ModHalfTime { - public override double ScoreMultiplier => 0.3; } } diff --git a/osu.Game/Rulesets/Mods/ModHalfTime.cs b/osu.Game/Rulesets/Mods/ModHalfTime.cs index 7d858dca6f..06c7750035 100644 --- a/osu.Game/Rulesets/Mods/ModHalfTime.cs +++ b/osu.Game/Rulesets/Mods/ModHalfTime.cs @@ -24,5 +24,19 @@ namespace osu.Game.Rulesets.Mods MaxValue = 0.99, Precision = 0.01, }; + + public override double ScoreMultiplier + { + get + { + // Round to the nearest multiple of 0.1. + double value = (int)(SpeedChange.Value * 10) / 10.0; + + // Offset back to 0. + value -= 1; + + return 1 + value; + } + } } } From bfb7ead6898424220bc63c73db34397462eba685 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sun, 9 Apr 2023 22:04:07 -0700 Subject: [PATCH 380/476] Add failing text box beatmap difficulty count test --- .../Visual/SongSelect/TestScenePlaySongSelect.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index feab86d3ee..a103cf759f 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -1068,6 +1068,21 @@ namespace osu.Game.Tests.Visual.SongSelect AddAssert("options disabled", () => !songSelect.ChildrenOfType().Single().Enabled.Value); } + [Test] + public void TestTextBoxBeatmapDifficultyCount() + { + createSongSelect(); + + AddAssert("0 matching shown", () => songSelect.ChildrenOfType().Single().InformationalText == "0 matching beatmaps"); + + addRulesetImportStep(0); + + AddAssert("3 matching shown", () => songSelect.ChildrenOfType().Single().InformationalText == "3 matching beatmaps"); + AddStep("delete all beatmaps", () => manager.Delete()); + AddUntilStep("wait for no beatmap", () => Beatmap.IsDefault); + AddAssert("0 matching shown", () => songSelect.ChildrenOfType().Single().InformationalText == "0 matching beatmaps"); + } + private void waitForInitialSelection() { AddUntilStep("wait for initial selection", () => !Beatmap.IsDefault); From 7f5b99c91b3dad9985bb6e0aaca0d8ad8d1ba84e Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Fri, 7 Apr 2023 22:32:12 -0700 Subject: [PATCH 381/476] Fix song select beatmap difficulty count not updating when deleting --- osu.Game/Screens/Select/BeatmapCarousel.cs | 7 +++++++ osu.Game/Screens/Select/SongSelect.cs | 1 + 2 files changed, 8 insertions(+) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 68d3247275..36fc01613e 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -49,6 +49,11 @@ namespace osu.Game.Screens.Select /// public Action? BeatmapSetsChanged; + /// + /// Triggered when the deleted change. + /// + public Action? DeletedBeatmapSetsChanged; + /// /// Triggered after filter conditions have finished being applied to the model hierarchy. /// @@ -353,6 +358,8 @@ namespace osu.Game.Screens.Select if (!Scroll.UserScrolling) ScrollToSelected(true); + + DeletedBeatmapSetsChanged?.Invoke(); }); public void UpdateBeatmapSet(BeatmapSetInfo beatmapSet) => Schedule(() => diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index c5e914b461..41cbfea0fc 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -162,6 +162,7 @@ namespace osu.Game.Screens.Select BleedBottom = Footer.HEIGHT, SelectionChanged = updateSelectedBeatmap, BeatmapSetsChanged = carouselBeatmapsLoaded, + DeletedBeatmapSetsChanged = updateVisibleBeatmapCount, FilterApplied = updateVisibleBeatmapCount, GetRecommendedBeatmap = s => recommender?.GetRecommendedBeatmap(s), }, c => carouselContainer.Child = c); From ad51f880e04a39cfddfb70f08ab87e08448a9096 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 10 Apr 2023 17:49:29 +0900 Subject: [PATCH 382/476] Remove overrides on DC/NC mods --- osu.Game.Rulesets.Catch/Mods/CatchModDaycore.cs | 1 - osu.Game.Rulesets.Catch/Mods/CatchModNightcore.cs | 1 - osu.Game.Rulesets.Mania/Mods/ManiaModDaycore.cs | 1 - osu.Game.Rulesets.Mania/Mods/ManiaModNightcore.cs | 1 - osu.Game.Rulesets.Osu/Mods/OsuModDaycore.cs | 1 - osu.Game.Rulesets.Osu/Mods/OsuModNightcore.cs | 1 - osu.Game.Rulesets.Taiko/Mods/TaikoModDaycore.cs | 1 - osu.Game.Rulesets.Taiko/Mods/TaikoModNightcore.cs | 1 - 8 files changed, 8 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModDaycore.cs b/osu.Game.Rulesets.Catch/Mods/CatchModDaycore.cs index cae19e9468..180cb98ed7 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModDaycore.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModDaycore.cs @@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Catch.Mods { public class CatchModDaycore : ModDaycore { - public override double ScoreMultiplier => 0.3; } } diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModNightcore.cs b/osu.Game.Rulesets.Catch/Mods/CatchModNightcore.cs index 9e38913be7..c537897439 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModNightcore.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModNightcore.cs @@ -8,6 +8,5 @@ namespace osu.Game.Rulesets.Catch.Mods { public class CatchModNightcore : ModNightcore { - public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.06 : 1; } } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModDaycore.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModDaycore.cs index bec0a6a1d3..309393b664 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModDaycore.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModDaycore.cs @@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Mania.Mods { public class ManiaModDaycore : ModDaycore { - public override double ScoreMultiplier => 0.5; } } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModNightcore.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModNightcore.cs index 4cc712060c..748725af9f 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModNightcore.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModNightcore.cs @@ -8,6 +8,5 @@ namespace osu.Game.Rulesets.Mania.Mods { public class ManiaModNightcore : ModNightcore { - public override double ScoreMultiplier => 1; } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDaycore.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDaycore.cs index 371dfe6a1a..1de6b9ce55 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDaycore.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDaycore.cs @@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModDaycore : ModDaycore { - public override double ScoreMultiplier => 0.3; } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModNightcore.cs b/osu.Game.Rulesets.Osu/Mods/OsuModNightcore.cs index b7838ebaa7..661cc948c5 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModNightcore.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModNightcore.cs @@ -8,6 +8,5 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModNightcore : ModNightcore { - public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.12 : 1; } } diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModDaycore.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModDaycore.cs index 84aa5e6bba..f442435d9c 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModDaycore.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModDaycore.cs @@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Taiko.Mods { public class TaikoModDaycore : ModDaycore { - public override double ScoreMultiplier => 0.3; } } diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModNightcore.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModNightcore.cs index 7cb14635ff..ad5da3d601 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModNightcore.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModNightcore.cs @@ -8,6 +8,5 @@ namespace osu.Game.Rulesets.Taiko.Mods { public class TaikoModNightcore : ModNightcore { - public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.12 : 1; } } From 641415ca3263fdff295402f965a5562c309ea8f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 10 Apr 2023 14:05:32 +0200 Subject: [PATCH 383/476] Unify displayed duration format for single/multiple selection --- osu.Game/Rulesets/Edit/HitObjectInspector.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectInspector.cs b/osu.Game/Rulesets/Edit/HitObjectInspector.cs index 02270d4662..d0a022744f 100644 --- a/osu.Game/Rulesets/Edit/HitObjectInspector.cs +++ b/osu.Game/Rulesets/Edit/HitObjectInspector.cs @@ -121,10 +121,10 @@ namespace osu.Game.Rulesets.Edit addValue($"{EditorBeatmap.SelectedHitObjects.Count:#,0.##}"); addHeader("Start Time"); - addValue($"{EditorBeatmap.SelectedHitObjects.Min(o => o.StartTime):#,0.##}"); + addValue($"{EditorBeatmap.SelectedHitObjects.Min(o => o.StartTime):#,0.##}ms"); addHeader("End Time"); - addValue($"{EditorBeatmap.SelectedHitObjects.Max(o => o.GetEndTime()):#,0.##}"); + addValue($"{EditorBeatmap.SelectedHitObjects.Max(o => o.GetEndTime()):#,0.##}ms"); break; } From 60358c720392264ab4b1c51bbca89aa72710300d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 10 Apr 2023 14:13:53 +0200 Subject: [PATCH 384/476] Perform first inspector text update immediately Provides better and more consistent initial state for the inspector. --- osu.Game/Rulesets/Edit/HitObjectInspector.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Rulesets/Edit/HitObjectInspector.cs b/osu.Game/Rulesets/Edit/HitObjectInspector.cs index d0a022744f..977d00ede2 100644 --- a/osu.Game/Rulesets/Edit/HitObjectInspector.cs +++ b/osu.Game/Rulesets/Edit/HitObjectInspector.cs @@ -46,6 +46,7 @@ namespace osu.Game.Rulesets.Edit EditorBeatmap.SelectedHitObjects.CollectionChanged += (_, _) => updateInspectorText(); EditorBeatmap.TransactionBegan += updateInspectorText; EditorBeatmap.TransactionEnded += updateInspectorText; + updateInspectorText(); } private ScheduledDelegate? rollingTextUpdate; From 6fec476147b04d30be33bcba642c262fd6a8c811 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 10 Apr 2023 14:55:30 +0200 Subject: [PATCH 385/476] Simplify snap implementation --- .../Edit/Compose/Components/SelectionBoxRotationHandle.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs index 73da837d03..bee768ad50 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs @@ -129,10 +129,6 @@ namespace osu.Game.Screens.Edit.Compose.Components TooltipText = newRotation.ToLocalisableString(tooltipFormat); } - private float snap(float value, float step) - { - float floor = MathF.Floor(value / step) * step; - return value - floor < step / 2f ? floor : floor + step; - } + private float snap(float value, float step) => MathF.Round(value / step) * step; } } From 73bd0feef58ed190df4317b507a9a4cae882fa53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 10 Apr 2023 15:03:33 +0200 Subject: [PATCH 386/476] Fix incorrectly implemented localisation --- osu.Game/Localisation/EditorStrings.cs | 8 ++++---- .../Edit/Compose/Components/SelectionBoxRotationHandle.cs | 4 +--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/osu.Game/Localisation/EditorStrings.cs b/osu.Game/Localisation/EditorStrings.cs index beddcfd44e..7c9b52275d 100644 --- a/osu.Game/Localisation/EditorStrings.cs +++ b/osu.Game/Localisation/EditorStrings.cs @@ -100,14 +100,14 @@ namespace osu.Game.Localisation public static LocalisableString TimelineTicks => new TranslatableString(getKey(@"timeline_ticks"), @"Ticks"); /// - /// "0.0°" + /// "{0:0.0}°" /// - public static LocalisableString RotationFormatUnsnapped => new TranslatableString(getKey(@"rotation_format_unsnapped"), @"0.0°"); + public static LocalisableString RotationUnsnapped(float newRotation) => new TranslatableString(getKey(@"rotation_unsnapped"), @"{0:0.0}°", newRotation); /// - /// "0.0° (snapped)" + /// "{0:0.0}° (snapped)" /// - public static LocalisableString RotationFormatSnapped => new TranslatableString(getKey(@"rotation_format_snapped"), @"0.0° (snapped)"); + public static LocalisableString RotationSnapped(float newRotation) => new TranslatableString(getKey(@"rotation_snapped"), @"{0:0.0}° (snapped)", newRotation); private static string getKey(string key) => $@"{prefix}:{key}"; } diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs index bee768ad50..305f5bf3c4 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs @@ -7,7 +7,6 @@ using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.EnumExtensions; -using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Sprites; @@ -125,8 +124,7 @@ namespace osu.Game.Screens.Edit.Compose.Components cumulativeRotation.Value = newRotation; HandleRotate?.Invoke(newRotation - oldRotation); - string tooltipFormat = shouldSnap ? EditorStrings.RotationFormatSnapped.ToString() : EditorStrings.RotationFormatUnsnapped.ToString(); - TooltipText = newRotation.ToLocalisableString(tooltipFormat); + TooltipText = shouldSnap ? EditorStrings.RotationSnapped(newRotation) : EditorStrings.RotationUnsnapped(newRotation); } private float snap(float value, float step) => MathF.Round(value / step) * step; From 11fd93a2baec751af641692664cbf3387871839d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 10 Apr 2023 15:19:54 +0200 Subject: [PATCH 387/476] Inline disturbing `getGuestMapper()` method Disturbing because name suggests pure method, but it is in fact `void` and also performs side effects by about the most confusing means possible. --- osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs index 27e671ded0..104f861df7 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs @@ -214,22 +214,17 @@ namespace osu.Game.Overlays.BeatmapSet if (beatmapInfo?.AuthorID != BeatmapSet?.AuthorID) { APIUser? user = BeatmapSet?.RelatedUsers?.SingleOrDefault(u => u.OnlineID == beatmapInfo?.AuthorID); + if (user != null) - getGuestMapper(user); + { + guestMapperContainer.AddText("mapped by "); + guestMapperContainer.AddUserLink(user); + } } version.Text = beatmapInfo?.DifficultyName ?? string.Empty; } - private void getGuestMapper(APIUser user) - { - guestMapperContainer.With(d => - { - d.AddText("mapped by "); - d.AddUserLink(user); - }); - } - private void updateDifficultyButtons() { Difficulties.Children.ToList().ForEach(diff => diff.State = diff.Beatmap == Beatmap.Value ? DifficultySelectorState.Selected : DifficultySelectorState.NotSelected); From 6e08105e2c37656801dda1d0583525a549f049e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 10 Apr 2023 15:27:10 +0200 Subject: [PATCH 388/476] Remove usage of "diff" vernacular --- .../Visual/Online/TestSceneBeatmapSetOverlay.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs index 4838f42043..a27c4ddad2 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs @@ -283,15 +283,15 @@ namespace osu.Game.Tests.Visual.Online } [Test] - public void TestBeatmapSetWithGuestDIff() + public void TestBeatmapSetWithGuestDifficulty() { - AddStep("show map", () => overlay.ShowBeatmapSet(createBeatmapSetWithGuestDiff())); - AddStep("move mouse to host diff", () => + AddStep("show map", () => overlay.ShowBeatmapSet(createBeatmapSetWithGuestDifficulty())); + AddStep("move mouse to host difficulty", () => { InputManager.MoveMouseTo(overlay.ChildrenOfType().ElementAt(0)); }); AddAssert("guest mapper information not shown", () => overlay.ChildrenOfType().Single().ChildrenOfType().All(s => s.Text != "BanchoBot")); - AddStep("move mouse to guest diff", () => + AddStep("move mouse to guest difficulty", () => { InputManager.MoveMouseTo(overlay.ChildrenOfType().ElementAt(1)); }); @@ -337,7 +337,7 @@ namespace osu.Game.Tests.Visual.Online return beatmapSet; } - private APIBeatmapSet createBeatmapSetWithGuestDiff() + private APIBeatmapSet createBeatmapSetWithGuestDifficulty() { var set = getBeatmapSet(); From c7dea717931631e02ef80c5222b6eed2b96e458c Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Mon, 10 Apr 2023 11:26:18 -0700 Subject: [PATCH 389/476] Use existing `BeatmapSetsChanged` action --- osu.Game/Screens/Select/BeatmapCarousel.cs | 7 +------ osu.Game/Screens/Select/SongSelect.cs | 1 - 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 36fc01613e..d6359dd62e 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -49,11 +49,6 @@ namespace osu.Game.Screens.Select /// public Action? BeatmapSetsChanged; - /// - /// Triggered when the deleted change. - /// - public Action? DeletedBeatmapSetsChanged; - /// /// Triggered after filter conditions have finished being applied to the model hierarchy. /// @@ -359,7 +354,7 @@ namespace osu.Game.Screens.Select if (!Scroll.UserScrolling) ScrollToSelected(true); - DeletedBeatmapSetsChanged?.Invoke(); + BeatmapSetsChanged?.Invoke(); }); public void UpdateBeatmapSet(BeatmapSetInfo beatmapSet) => Schedule(() => diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 41cbfea0fc..c5e914b461 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -162,7 +162,6 @@ namespace osu.Game.Screens.Select BleedBottom = Footer.HEIGHT, SelectionChanged = updateSelectedBeatmap, BeatmapSetsChanged = carouselBeatmapsLoaded, - DeletedBeatmapSetsChanged = updateVisibleBeatmapCount, FilterApplied = updateVisibleBeatmapCount, GetRecommendedBeatmap = s => recommender?.GetRecommendedBeatmap(s), }, c => carouselContainer.Child = c); From f80de08f24e5176bc1d3bb79b994e1e1717d059d Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Mon, 10 Apr 2023 11:28:23 -0700 Subject: [PATCH 390/476] Adjust `BeatmapSetsChanged` xmldoc Co-Authored-By: Dean Herbert --- osu.Game/Screens/Select/BeatmapCarousel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index d6359dd62e..6ba9843f7b 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -45,7 +45,7 @@ namespace osu.Game.Screens.Select public float BleedBottom { get; set; } /// - /// Triggered when the loaded change and are completely loaded. + /// Triggered when finish loading, or are subsequently changed. /// public Action? BeatmapSetsChanged; From d0cbe206a949452c6bb4a0116470babc95aa9b36 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Mon, 10 Apr 2023 23:37:29 -0700 Subject: [PATCH 391/476] Revert back to one number with "matching beatmap difficulties" label --- .../Visual/SongSelect/TestScenePlaySongSelect.cs | 6 +++--- osu.Game/Screens/Select/BeatmapCarousel.cs | 7 +------ osu.Game/Screens/Select/SongSelect.cs | 2 +- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index a103cf759f..c9a7cf37a8 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -1073,14 +1073,14 @@ namespace osu.Game.Tests.Visual.SongSelect { createSongSelect(); - AddAssert("0 matching shown", () => songSelect.ChildrenOfType().Single().InformationalText == "0 matching beatmaps"); + AddAssert("0 matching shown", () => songSelect.ChildrenOfType().Single().InformationalText == "0 matching beatmap difficulties"); addRulesetImportStep(0); - AddAssert("3 matching shown", () => songSelect.ChildrenOfType().Single().InformationalText == "3 matching beatmaps"); + AddAssert("3 matching shown", () => songSelect.ChildrenOfType().Single().InformationalText == "3 matching beatmap difficulties"); AddStep("delete all beatmaps", () => manager.Delete()); AddUntilStep("wait for no beatmap", () => Beatmap.IsDefault); - AddAssert("0 matching shown", () => songSelect.ChildrenOfType().Single().InformationalText == "0 matching beatmaps"); + AddAssert("0 matching shown", () => songSelect.ChildrenOfType().Single().InformationalText == "0 matching beatmap difficulties"); } private void waitForInitialSelection() diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index d5ddc375b7..6ba9843f7b 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -64,12 +64,7 @@ namespace osu.Game.Screens.Select /// /// The total count of non-filtered beatmaps displayed. /// - public int CountDisplayedBeatmaps => beatmapSets.Where(s => !s.Filtered.Value).Sum(s => s.Beatmaps.Count(b => !b.Filtered.Value)); - - /// - /// The total count of non-filtered beatmap sets displayed. - /// - public int CountDisplayedSets => beatmapSets.Count(s => !s.Filtered.Value); + public int CountDisplayed => beatmapSets.Where(s => !s.Filtered.Value).Sum(s => s.Beatmaps.Count(b => !b.Filtered.Value)); /// /// The currently selected beatmap set. diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 7191ff7c2a..09ccee3717 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -864,7 +864,7 @@ namespace osu.Game.Screens.Select { // Intentionally not localised until we have proper support for this (see https://github.com/ppy/osu-framework/pull/4918 // but also in this case we want support for formatting a number within a string). - FilterControl.InformationalText = $"{"matching beatmap".ToQuantity(Carousel.CountDisplayedSets, "#,0")} ({"difficulty".ToQuantity(Carousel.CountDisplayedBeatmaps, "#,0")})"; + FilterControl.InformationalText = $"{"matching beatmap difficulty".ToQuantity(Carousel.CountDisplayed, "#,0")}"; } private bool boundLocalBindables; From 0cc92ce5f926d91978c8903d3883fa70c61cf4c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 11 Apr 2023 17:51:01 +0200 Subject: [PATCH 392/476] Add failing test case Covering nested object reverts not firing the parent's `RevertResult` event in accordance with what the xmldoc of the event states. --- .../Gameplay/TestScenePoolingRuleset.cs | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePoolingRuleset.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePoolingRuleset.cs index 0469df1de3..d16f51f36e 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePoolingRuleset.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePoolingRuleset.cs @@ -164,6 +164,36 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("all results reverted", () => playfield.JudgedObjects.Count, () => Is.EqualTo(0)); } + [Test] + public void TestRevertNestedObjects() + { + ManualClock clock = null; + + var beatmap = new Beatmap(); + beatmap.HitObjects.Add(new TestHitObjectWithNested { Duration = 40 }); + + createTest(beatmap, 10, () => new FramedClock(clock = new ManualClock())); + + AddStep("skip to middle of object", () => clock.CurrentTime = (beatmap.HitObjects[0].StartTime + beatmap.HitObjects[0].GetEndTime()) / 2); + AddAssert("2 objects judged", () => playfield.JudgedObjects.Count, () => Is.EqualTo(2)); + + AddStep("skip to before end of object", () => clock.CurrentTime = beatmap.HitObjects[0].GetEndTime() - 1); + AddAssert("3 objects judged", () => playfield.JudgedObjects.Count, () => Is.EqualTo(3)); + + DrawableHitObject drawableHitObject = null; + HashSet revertedHitObjects = new HashSet(); + + AddStep("retrieve drawable hit object", () => drawableHitObject = playfield.ChildrenOfType().Single()); + AddStep("set up revert tracking", () => + { + revertedHitObjects.Clear(); + drawableHitObject.OnRevertResult += (ho, _) => revertedHitObjects.Add(ho.HitObject); + }); + AddStep("skip back to object start", () => clock.CurrentTime = beatmap.HitObjects[0].StartTime); + AddAssert("3 reverts fired", () => revertedHitObjects, () => Has.Count.EqualTo(3)); + AddAssert("no objects judged", () => playfield.JudgedObjects.Count, () => Is.EqualTo(0)); + } + [Test] public void TestApplyHitResultOnKilled() { @@ -258,6 +288,8 @@ namespace osu.Game.Tests.Visual.Gameplay { RegisterPool(poolSize); RegisterPool(poolSize); + RegisterPool(poolSize); + RegisterPool(poolSize); } protected override HitObjectLifetimeEntry CreateLifetimeEntry(HitObject hitObject) => new TestHitObjectLifetimeEntry(hitObject); @@ -388,6 +420,120 @@ namespace osu.Game.Tests.Visual.Gameplay } } + private class TestHitObjectWithNested : TestHitObject + { + protected override void CreateNestedHitObjects(CancellationToken cancellationToken) + { + base.CreateNestedHitObjects(cancellationToken); + + for (int i = 0; i < 3; ++i) + AddNested(new NestedHitObject { StartTime = (float)Duration * (i + 1) / 4 }); + } + } + + private class NestedHitObject : ConvertHitObject + { + } + + private partial class DrawableTestHitObjectWithNested : DrawableHitObject + { + private Container nestedContainer; + + public DrawableTestHitObjectWithNested() + : base(null) + { + } + + [BackgroundDependencyLoader] + private void load() + { + AddRangeInternal(new Drawable[] + { + new Circle + { + RelativeSizeAxes = Axes.Both, + Colour = Colour4.Red + }, + nestedContainer = new Container + { + RelativeSizeAxes = Axes.Both + } + }); + } + + protected override void OnApply() + { + base.OnApply(); + + Size = new Vector2(200, 50); + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + } + + protected override void AddNestedHitObject(DrawableHitObject hitObject) + { + base.AddNestedHitObject(hitObject); + nestedContainer.Add(hitObject); + } + + protected override void ClearNestedHitObjects() + { + base.ClearNestedHitObjects(); + nestedContainer.Clear(false); + } + + protected override void CheckForResult(bool userTriggered, double timeOffset) + { + base.CheckForResult(userTriggered, timeOffset); + if (timeOffset >= 0) + ApplyResult(r => r.Type = r.Judgement.MaxResult); + } + } + + private partial class DrawableNestedHitObject : DrawableHitObject + { + public DrawableNestedHitObject() + : this(null) + { + } + + public DrawableNestedHitObject(NestedHitObject hitObject) + : base(hitObject) + { + Size = new Vector2(15); + Colour = Colour4.White; + RelativePositionAxes = Axes.Both; + Origin = Anchor.Centre; + } + + [BackgroundDependencyLoader] + private void load() + { + AddInternal(new Circle + { + RelativeSizeAxes = Axes.Both, + }); + } + + protected override void OnApply() + { + base.OnApply(); + + X = (float)((HitObject.StartTime - ParentHitObject!.HitObject.StartTime) / (ParentHitObject.HitObject.GetEndTime() - ParentHitObject.HitObject.StartTime)); + Y = 0.5f; + + LifetimeStart = ParentHitObject.LifetimeStart; + LifetimeEnd = ParentHitObject.LifetimeEnd; + } + + protected override void CheckForResult(bool userTriggered, double timeOffset) + { + base.CheckForResult(userTriggered, timeOffset); + if (timeOffset >= 0) + ApplyResult(r => r.Type = r.Judgement.MaxResult); + } + } + #endregion } } From db86ced4b4077f042af4e865c8bad859c3cfff23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 11 Apr 2023 17:59:33 +0200 Subject: [PATCH 393/476] Invoke `RevertResult` on parent DHO when nested DHO is reverted The behaviour described above was removed in 812a4b412a2c1b13a1e1215a00f863ef6fd83e45, thus henceforth contradicting `RevertResult`'s xmldoc. As it is relied on by some external rulesets, bring it back to unbreak them. --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 0c50f8341a..f6c3452e48 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -239,6 +239,7 @@ namespace osu.Game.Rulesets.Objects.Drawables OnNestedDrawableCreated?.Invoke(drawableNested); drawableNested.OnNewResult += onNewResult; + drawableNested.OnRevertResult += onNestedRevertResult; drawableNested.ApplyCustomUpdateState += onApplyCustomUpdateState; // This is only necessary for non-pooled DHOs. For pooled DHOs, this is handled inside GetPooledDrawableRepresentation(). @@ -312,6 +313,7 @@ namespace osu.Game.Rulesets.Objects.Drawables foreach (var obj in nestedHitObjects) { obj.OnNewResult -= onNewResult; + obj.OnRevertResult -= onNestedRevertResult; obj.ApplyCustomUpdateState -= onApplyCustomUpdateState; } @@ -376,6 +378,8 @@ namespace osu.Game.Rulesets.Objects.Drawables OnRevertResult?.Invoke(this, Result); } + private void onNestedRevertResult(DrawableHitObject drawableHitObject, JudgementResult result) => OnRevertResult?.Invoke(drawableHitObject, result); + private void onApplyCustomUpdateState(DrawableHitObject drawableHitObject, ArmedState state) => ApplyCustomUpdateState?.Invoke(drawableHitObject, state); private void onDefaultsApplied(HitObject hitObject) From e72f103c1759e61c3afa7080f962c639265996c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 11 Apr 2023 21:42:55 +0200 Subject: [PATCH 394/476] Do not look up metadata for locally-modified beatmaps on save --- osu.Game/Beatmaps/BeatmapImporter.cs | 6 ++--- osu.Game/Beatmaps/BeatmapManager.cs | 15 ++++++++--- .../Beatmaps/BeatmapOnlineChangeIngest.cs | 2 +- osu.Game/Beatmaps/BeatmapUpdater.cs | 13 +++++----- osu.Game/Beatmaps/MetadataLookupScope.cs | 26 +++++++++++++++++++ osu.Game/OsuGameBase.cs | 2 +- 6 files changed, 50 insertions(+), 14 deletions(-) create mode 100644 osu.Game/Beatmaps/MetadataLookupScope.cs diff --git a/osu.Game/Beatmaps/BeatmapImporter.cs b/osu.Game/Beatmaps/BeatmapImporter.cs index 4752a88199..4731a70753 100644 --- a/osu.Game/Beatmaps/BeatmapImporter.cs +++ b/osu.Game/Beatmaps/BeatmapImporter.cs @@ -35,7 +35,7 @@ namespace osu.Game.Beatmaps protected override string[] HashableFileTypes => new[] { ".osu" }; - public Action<(BeatmapSetInfo beatmapSet, bool isBatch)>? ProcessBeatmap { private get; set; } + public ProcessBeatmapDelegate? ProcessBeatmap { private get; set; } public BeatmapImporter(Storage storage, RealmAccess realm) : base(storage, realm) @@ -59,7 +59,7 @@ namespace osu.Game.Beatmaps first.PerformRead(s => { // Re-run processing even in this case. We might have outdated metadata. - ProcessBeatmap?.Invoke((s, false)); + ProcessBeatmap?.Invoke(s, MetadataLookupScope.OnlineFirst); }); return first; } @@ -206,7 +206,7 @@ namespace osu.Game.Beatmaps protected override void PostImport(BeatmapSetInfo model, Realm realm, ImportParameters parameters) { base.PostImport(model, realm, parameters); - ProcessBeatmap?.Invoke((model, parameters.Batch)); + ProcessBeatmap?.Invoke(model, parameters.Batch ? MetadataLookupScope.LocalCacheFirst : MetadataLookupScope.OnlineFirst); } private void validateOnlineIds(BeatmapSetInfo beatmapSet, Realm realm) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index ad56bbbc3a..cab49b7d69 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -42,7 +42,7 @@ namespace osu.Game.Beatmaps private readonly WorkingBeatmapCache workingBeatmapCache; - public Action<(BeatmapSetInfo beatmapSet, bool isBatch)>? ProcessBeatmap { private get; set; } + public ProcessBeatmapDelegate? ProcessBeatmap { private get; set; } public override bool PauseImports { @@ -72,7 +72,7 @@ namespace osu.Game.Beatmaps BeatmapTrackStore = audioManager.GetTrackStore(userResources); beatmapImporter = CreateBeatmapImporter(storage, realm); - beatmapImporter.ProcessBeatmap = args => ProcessBeatmap?.Invoke(args); + beatmapImporter.ProcessBeatmap = (beatmapSet, scope) => ProcessBeatmap?.Invoke(beatmapSet, scope); beatmapImporter.PostNotification = obj => PostNotification?.Invoke(obj); workingBeatmapCache = CreateWorkingBeatmapCache(audioManager, gameResources, userResources, defaultBeatmap, host); @@ -454,7 +454,9 @@ namespace osu.Game.Beatmaps if (transferCollections) beatmapInfo.TransferCollectionReferences(r, oldMd5Hash); - ProcessBeatmap?.Invoke((liveBeatmapSet, false)); + // do not look up metadata. + // this is a locally-modified set now, so looking up metadata is busy work at best and harmful at worst. + ProcessBeatmap?.Invoke(liveBeatmapSet, MetadataLookupScope.None); }); } @@ -542,4 +544,11 @@ namespace osu.Game.Beatmaps public override string HumanisedModelName => "beatmap"; } + + /// + /// Delegate type for beatmap processing callbacks. + /// + /// The beatmap set to be processed. + /// The scope to use when looking up metadata. + public delegate void ProcessBeatmapDelegate(BeatmapSetInfo beatmapSet, MetadataLookupScope lookupScope); } diff --git a/osu.Game/Beatmaps/BeatmapOnlineChangeIngest.cs b/osu.Game/Beatmaps/BeatmapOnlineChangeIngest.cs index 98aefd75d3..b160043820 100644 --- a/osu.Game/Beatmaps/BeatmapOnlineChangeIngest.cs +++ b/osu.Game/Beatmaps/BeatmapOnlineChangeIngest.cs @@ -36,7 +36,7 @@ namespace osu.Game.Beatmaps var matchingSet = r.All().FirstOrDefault(s => s.OnlineID == id); if (matchingSet != null) - beatmapUpdater.Queue(matchingSet.ToLive(realm), true); + beatmapUpdater.Queue(matchingSet.ToLive(realm), MetadataLookupScope.OnlineFirst); } }); } diff --git a/osu.Game/Beatmaps/BeatmapUpdater.cs b/osu.Game/Beatmaps/BeatmapUpdater.cs index d7b1fac7b3..af9f32f834 100644 --- a/osu.Game/Beatmaps/BeatmapUpdater.cs +++ b/osu.Game/Beatmaps/BeatmapUpdater.cs @@ -42,24 +42,25 @@ namespace osu.Game.Beatmaps /// Queue a beatmap for background processing. /// /// The managed beatmap set to update. A transaction will be opened to apply changes. - /// Whether metadata from an online source should be preferred. If true, the local cache will be skipped to ensure the freshest data state possible. - public void Queue(Live beatmapSet, bool preferOnlineFetch = false) + /// The preferred scope to use for metadata lookup. + public void Queue(Live beatmapSet, MetadataLookupScope lookupScope = MetadataLookupScope.LocalCacheFirst) { Logger.Log($"Queueing change for local beatmap {beatmapSet}"); - Task.Factory.StartNew(() => beatmapSet.PerformRead(b => Process(b, preferOnlineFetch)), default, TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously, updateScheduler); + Task.Factory.StartNew(() => beatmapSet.PerformRead(b => Process(b, lookupScope)), default, TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously, updateScheduler); } /// /// Run all processing on a beatmap immediately. /// /// The managed beatmap set to update. A transaction will be opened to apply changes. - /// Whether metadata from an online source should be preferred. If true, the local cache will be skipped to ensure the freshest data state possible. - public void Process(BeatmapSetInfo beatmapSet, bool preferOnlineFetch = false) => beatmapSet.Realm.Write(r => + /// The preferred scope to use for metadata lookup. + public void Process(BeatmapSetInfo beatmapSet, MetadataLookupScope lookupScope = MetadataLookupScope.LocalCacheFirst) => beatmapSet.Realm.Write(r => { // Before we use below, we want to invalidate. workingBeatmapCache.Invalidate(beatmapSet); - metadataLookup.Update(beatmapSet, preferOnlineFetch); + if (lookupScope != MetadataLookupScope.None) + metadataLookup.Update(beatmapSet, lookupScope == MetadataLookupScope.OnlineFirst); foreach (var beatmap in beatmapSet.Beatmaps) { diff --git a/osu.Game/Beatmaps/MetadataLookupScope.cs b/osu.Game/Beatmaps/MetadataLookupScope.cs new file mode 100644 index 0000000000..e1fbedc26a --- /dev/null +++ b/osu.Game/Beatmaps/MetadataLookupScope.cs @@ -0,0 +1,26 @@ +// 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.Beatmaps +{ + /// + /// Determines which sources (if any at all) should be queried in which order for a beatmap's metadata. + /// + public enum MetadataLookupScope + { + /// + /// Do not attempt to look up the beatmap metadata either in the local cache or online. + /// + None, + + /// + /// Try the local metadata cache first before querying online sources. + /// + LocalCacheFirst, + + /// + /// Query online sources immediately. + /// + OnlineFirst + } +} diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 8f27e5dc53..34e31b0d61 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -310,7 +310,7 @@ namespace osu.Game base.Content.Add(new BeatmapOnlineChangeIngest(beatmapUpdater, realm, metadataClient)); - BeatmapManager.ProcessBeatmap = args => beatmapUpdater.Process(args.beatmapSet, !args.isBatch); + BeatmapManager.ProcessBeatmap = (beatmapSet, scope) => beatmapUpdater.Process(beatmapSet, scope); dependencies.Cache(userCache = new UserLookupCache()); base.Content.Add(userCache); From fbb15fff26ad91ceeccf9facc030fd6e6b503b1c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 17 Apr 2023 23:03:16 +0900 Subject: [PATCH 395/476] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 4b89e82729..a93b450ebb 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -11,7 +11,7 @@ manifestmerger.jar - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index b9c6c1df9d..0ec65f4daf 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 083d8192ea..3862ddacdb 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -16,6 +16,6 @@ iossimulator-x64 - + From c3f3b8db7c8c6e32d10066650eb89f4933d3d94f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 17 Apr 2023 23:09:46 +0900 Subject: [PATCH 396/476] Update resources --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 0ec65f4daf..578b8512e7 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + From c0a25144cf458a979348ac8637087eb33428d3a4 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 18 Apr 2023 11:31:56 +0900 Subject: [PATCH 397/476] Apply changes to custom ShaderManager --- .../TestSceneDrawableRulesetDependencies.cs | 2 +- .../Testing/TestSceneRulesetDependencies.cs | 4 ++-- .../UI/DrawableRulesetDependencies.cs | 22 +++---------------- 3 files changed, 6 insertions(+), 22 deletions(-) diff --git a/osu.Game.Tests/Rulesets/TestSceneDrawableRulesetDependencies.cs b/osu.Game.Tests/Rulesets/TestSceneDrawableRulesetDependencies.cs index f8248e88bb..6639b6dd68 100644 --- a/osu.Game.Tests/Rulesets/TestSceneDrawableRulesetDependencies.cs +++ b/osu.Game.Tests/Rulesets/TestSceneDrawableRulesetDependencies.cs @@ -164,7 +164,7 @@ namespace osu.Game.Tests.Rulesets this.parentManager = parentManager; } - public override byte[] LoadRaw(string name) => parentManager.LoadRaw(name); + public override byte[] GetRawData(string fileName) => parentManager.GetRawData(fileName); public bool IsDisposed { get; private set; } diff --git a/osu.Game.Tests/Testing/TestSceneRulesetDependencies.cs b/osu.Game.Tests/Testing/TestSceneRulesetDependencies.cs index a5a83d7231..585a3f95e7 100644 --- a/osu.Game.Tests/Testing/TestSceneRulesetDependencies.cs +++ b/osu.Game.Tests/Testing/TestSceneRulesetDependencies.cs @@ -51,8 +51,8 @@ namespace osu.Game.Tests.Testing { AddStep("ruleset shaders retrieved without error", () => { - Dependencies.Get().LoadRaw(@"sh_TestVertex.vs"); - Dependencies.Get().LoadRaw(@"sh_TestFragment.fs"); + Dependencies.Get().GetRawData(@"sh_TestVertex.vs"); + Dependencies.Get().GetRawData(@"sh_TestFragment.fs"); }); } diff --git a/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs b/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs index 76d84000f1..e34289c968 100644 --- a/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs +++ b/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs @@ -206,27 +206,11 @@ namespace osu.Game.Rulesets.UI this.parent = parent; } - // When the debugger is attached, exceptions are expensive. - // Manually work around this by caching failed lookups and falling back straight to parent. - private readonly HashSet<(string, string)> failedLookups = new HashSet<(string, string)>(); + public override IShader? GetCachedShader(string vertex, string fragment) => base.GetCachedShader(vertex, fragment) ?? parent.GetCachedShader(vertex, fragment); - public override IShader Load(string vertex, string fragment) - { - if (!failedLookups.Contains((vertex, fragment))) - { - try - { - return base.Load(vertex, fragment); - } - catch - { - // Shader lookup is very non-standard. Rather than returning null on missing shaders, exceptions are thrown. - failedLookups.Add((vertex, fragment)); - } - } + public override IShaderPart? GetCachedShaderPart(string name) => base.GetCachedShaderPart(name) ?? parent.GetCachedShaderPart(name); - return parent.Load(vertex, fragment); - } + public override byte[]? GetRawData(string fileName) => base.GetRawData(fileName) ?? parent.GetRawData(fileName); } } } From 16df92f405cf466c21459189b9c90689d3b23ca7 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Mon, 17 Apr 2023 20:59:02 -0700 Subject: [PATCH 398/476] Fix sets not being plural --- osu.Game/Localisation/FirstRunSetupBeatmapScreenStrings.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Localisation/FirstRunSetupBeatmapScreenStrings.cs b/osu.Game/Localisation/FirstRunSetupBeatmapScreenStrings.cs index d822f4976f..a77ee066e4 100644 --- a/osu.Game/Localisation/FirstRunSetupBeatmapScreenStrings.cs +++ b/osu.Game/Localisation/FirstRunSetupBeatmapScreenStrings.cs @@ -15,9 +15,9 @@ namespace osu.Game.Localisation public static LocalisableString Header => new TranslatableString(getKey(@"header"), @"Obtaining Beatmaps"); /// - /// ""Beatmaps" are what we call a set of playable levels. osu! doesn't come with any beatmaps pre-loaded. This step will help you get started on your beatmap collection." + /// ""Beatmaps" are what we call sets of playable levels. osu! doesn't come with any beatmaps pre-loaded. This step will help you get started on your beatmap collection." /// - public static LocalisableString Description => new TranslatableString(getKey(@"description"), @"""Beatmaps"" are what we call a set of playable levels. osu! doesn't come with any beatmaps pre-loaded. This step will help you get started on your beatmap collection."); + public static LocalisableString Description => new TranslatableString(getKey(@"description"), @"""Beatmaps"" are what we call sets of playable levels. osu! doesn't come with any beatmaps pre-loaded. This step will help you get started on your beatmap collection."); /// /// "If you are a new player, we recommend playing through the tutorial to get accustomed to the gameplay." From c80a25328d2d5dc0fb8b1d7f710ee275db247e33 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Mon, 17 Apr 2023 20:59:41 -0700 Subject: [PATCH 399/476] Shorten label to just "matches" --- osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs | 6 +++--- osu.Game/Screens/Select/SongSelect.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index c9a7cf37a8..f094d40caa 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -1073,14 +1073,14 @@ namespace osu.Game.Tests.Visual.SongSelect { createSongSelect(); - AddAssert("0 matching shown", () => songSelect.ChildrenOfType().Single().InformationalText == "0 matching beatmap difficulties"); + AddAssert("0 matching shown", () => songSelect.ChildrenOfType().Single().InformationalText == "0 matches"); addRulesetImportStep(0); - AddAssert("3 matching shown", () => songSelect.ChildrenOfType().Single().InformationalText == "3 matching beatmap difficulties"); + AddAssert("3 matching shown", () => songSelect.ChildrenOfType().Single().InformationalText == "3 matches"); AddStep("delete all beatmaps", () => manager.Delete()); AddUntilStep("wait for no beatmap", () => Beatmap.IsDefault); - AddAssert("0 matching shown", () => songSelect.ChildrenOfType().Single().InformationalText == "0 matching beatmap difficulties"); + AddAssert("0 matching shown", () => songSelect.ChildrenOfType().Single().InformationalText == "0 matches"); } private void waitForInitialSelection() diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 09ccee3717..4d6a5398c5 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -864,7 +864,7 @@ namespace osu.Game.Screens.Select { // Intentionally not localised until we have proper support for this (see https://github.com/ppy/osu-framework/pull/4918 // but also in this case we want support for formatting a number within a string). - FilterControl.InformationalText = $"{"matching beatmap difficulty".ToQuantity(Carousel.CountDisplayed, "#,0")}"; + FilterControl.InformationalText = $"{"match".ToQuantity(Carousel.CountDisplayed, "#,0")}"; } private bool boundLocalBindables; From 760ef1014587638fb2b3eb3a022136cb0d07f396 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 Apr 2023 00:04:21 +0900 Subject: [PATCH 400/476] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 4b89e82729..3ede0b85da 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -11,7 +11,7 @@ manifestmerger.jar - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index b9c6c1df9d..6e8b642abf 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 083d8192ea..127994c670 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -16,6 +16,6 @@ iossimulator-x64 - + From 776c4cfaad00d1f9f99cfa70792022f68002c5b0 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Wed, 19 Apr 2023 18:34:35 -0700 Subject: [PATCH 401/476] Ensure `selected` field is correct in `updateSelectionState()` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Bartłomiej Dach --- osu.Game/Overlays/Music/PlaylistItem.cs | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/osu.Game/Overlays/Music/PlaylistItem.cs b/osu.Game/Overlays/Music/PlaylistItem.cs index 4032af7651..4a39cc06c8 100644 --- a/osu.Game/Overlays/Music/PlaylistItem.cs +++ b/osu.Game/Overlays/Music/PlaylistItem.cs @@ -56,7 +56,7 @@ namespace osu.Game.Overlays.Music var artist = new RomanisableString(metadata.ArtistUnicode, metadata.Artist); titlePart = text.AddText(title, sprite => sprite.Font = OsuFont.GetFont(weight: FontWeight.Regular)); - titlePart.DrawablePartsRecreated += _ => updateSelectionState(true); + titlePart.DrawablePartsRecreated += _ => updateSelectionState(SelectedSet.Value, instant: true); text.AddText(@" "); // to separate the title from the artist. text.AddText(artist, sprite => @@ -66,25 +66,22 @@ namespace osu.Game.Overlays.Music sprite.Padding = new MarginPadding { Top = 1 }; }); - SelectedSet.BindValueChanged(set => - { - bool newSelected = set.NewValue?.Equals(Model) == true; - - if (newSelected == selected) - return; - - selected = newSelected; - updateSelectionState(false); - }, true); - - updateSelectionState(true); + SelectedSet.BindValueChanged(set => updateSelectionState(set.NewValue, instant: false)); + updateSelectionState(SelectedSet.Value, instant: true); }); } private bool selected; - private void updateSelectionState(bool instant) + private void updateSelectionState(Live selectedSet, bool instant) { + bool newSelected = selectedSet?.Equals(Model) == true; + + if (newSelected == selected && !instant) // instant updates should forcibly set correct state regardless of previous state. + return; + + selected = newSelected; + foreach (Drawable s in titlePart.Drawables) s.FadeColour(selected ? colours.Yellow : Color4.White, instant ? 0 : FADE_DURATION); } From 9f4b1f0f242a95ab2df2e77e2f2670ae5f846364 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 Apr 2023 14:30:01 +0900 Subject: [PATCH 402/476] Always round editor rotation to integer values --- .../Edit/Compose/Components/SelectionBoxRotationHandle.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs index 305f5bf3c4..c2a3f12efd 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs @@ -118,7 +118,7 @@ namespace osu.Game.Screens.Edit.Compose.Components { float oldRotation = cumulativeRotation.Value ?? 0; - float newRotation = shouldSnap ? snap(rawCumulativeRotation, snap_step) : rawCumulativeRotation; + float newRotation = shouldSnap ? snap(rawCumulativeRotation, snap_step) : MathF.Round(rawCumulativeRotation); newRotation = (newRotation - 180) % 360 + 180; cumulativeRotation.Value = newRotation; From 1066dfcbf6bddb1378652b22d1d9922f6d4e5eb7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 Apr 2023 14:35:12 +0900 Subject: [PATCH 403/476] Change default beat divisor to 4 This matches stable, and is a much saner default value. Will apply to new beatmaps and also beatmaps which don't specify a snap value in the `.osu` file. --- osu.Game/Beatmaps/BeatmapInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 3208598f56..63e878b80d 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -167,7 +167,7 @@ namespace osu.Game.Beatmaps /// public double DistanceSpacing { get; set; } = 1.0; - public int BeatDivisor { get; set; } + public int BeatDivisor { get; set; } = 4; public int GridSize { get; set; } From b62de5514c532a3c2df5ef4c311eb5b273578050 Mon Sep 17 00:00:00 2001 From: Haspamelodica Date: Fri, 21 Apr 2023 02:10:24 +0200 Subject: [PATCH 404/476] Fixed video importing bug #23259 --- osu.Game/Beatmaps/BeatmapManager.cs | 2 +- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 2 +- osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs | 2 +- osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs | 2 +- osu.Game/OsuGameBase.cs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index cab49b7d69..6af6a25579 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -368,7 +368,7 @@ namespace osu.Game.Beatmaps // user requested abort return; - var video = b.Files.FirstOrDefault(f => OsuGameBase.VIDEO_EXTENSIONS.Any(ex => f.Filename.EndsWith(ex, StringComparison.Ordinal))); + var video = b.Files.FirstOrDefault(f => OsuGameBase.VIDEO_EXTENSIONS.Any(ex => f.Filename.EndsWith(ex, StringComparison.OrdinalIgnoreCase))); if (video != null) { diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index a9bdd21b64..0e3a62e8fa 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -369,7 +369,7 @@ namespace osu.Game.Beatmaps.Formats // Some very old beatmaps had incorrect type specifications for their backgrounds (ie. using 1 for VIDEO // instead of 0 for BACKGROUND). To handle this gracefully, check the file extension against known supported // video extensions and handle similar to a background if it doesn't match. - if (!OsuGameBase.VIDEO_EXTENSIONS.Contains(Path.GetExtension(filename))) + if (!OsuGameBase.VIDEO_EXTENSIONS.Contains(Path.GetExtension(filename).ToUpperInvariant())) { beatmap.BeatmapInfo.Metadata.BackgroundFile = filename; } diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index f8308fe431..2eb496a798 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -114,7 +114,7 @@ namespace osu.Game.Beatmaps.Formats // // This avoids potential weird crashes when ffmpeg attempts to parse an image file as a video // (see https://github.com/ppy/osu/issues/22829#issuecomment-1465552451). - if (!OsuGameBase.VIDEO_EXTENSIONS.Contains(Path.GetExtension(path))) + if (!OsuGameBase.VIDEO_EXTENSIONS.Contains(Path.GetExtension(path).ToUpperInvariant())) break; storyboard.GetLayer("Video").Add(new StoryboardVideo(path, offset)); diff --git a/osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs b/osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs index 37e15c6127..825b1a5636 100644 --- a/osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs +++ b/osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs @@ -70,7 +70,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 { get { - if (OsuGameBase.VIDEO_EXTENSIONS.Contains(File.Extension)) + if (OsuGameBase.VIDEO_EXTENSIONS.Contains(File.Extension.ToUpperInvariant())) return FontAwesome.Regular.FileVideo; switch (File.Extension) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 34e31b0d61..2ab6c24af7 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -71,7 +71,7 @@ namespace osu.Game [Cached(typeof(OsuGameBase))] public partial class OsuGameBase : Framework.Game, ICanAcceptFiles, IBeatSyncProvider { - public static readonly string[] VIDEO_EXTENSIONS = { ".mp4", ".mov", ".avi", ".flv", ".mpg", ".wmv", ".m4v" }; + public static readonly string[] VIDEO_EXTENSIONS = { ".MP4", ".MOV", ".AVI", ".FLV", ".MPG", ".WMV", ".M4V" }; public const string OSU_PROTOCOL = "osu://"; From e90660c1a48c7eb46447348756ca2b976ca1bbf6 Mon Sep 17 00:00:00 2001 From: Haspamelodica Date: Fri, 21 Apr 2023 02:35:28 +0200 Subject: [PATCH 405/476] Switched to lowercase --- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 2 +- osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs | 2 +- osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs | 2 +- osu.Game/OsuGameBase.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 0e3a62e8fa..ef1dbc0488 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -369,7 +369,7 @@ namespace osu.Game.Beatmaps.Formats // Some very old beatmaps had incorrect type specifications for their backgrounds (ie. using 1 for VIDEO // instead of 0 for BACKGROUND). To handle this gracefully, check the file extension against known supported // video extensions and handle similar to a background if it doesn't match. - if (!OsuGameBase.VIDEO_EXTENSIONS.Contains(Path.GetExtension(filename).ToUpperInvariant())) + if (!OsuGameBase.VIDEO_EXTENSIONS.Contains(Path.GetExtension(filename).ToLowerInvariant())) { beatmap.BeatmapInfo.Metadata.BackgroundFile = filename; } diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index 2eb496a798..df5d3edb55 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -114,7 +114,7 @@ namespace osu.Game.Beatmaps.Formats // // This avoids potential weird crashes when ffmpeg attempts to parse an image file as a video // (see https://github.com/ppy/osu/issues/22829#issuecomment-1465552451). - if (!OsuGameBase.VIDEO_EXTENSIONS.Contains(Path.GetExtension(path).ToUpperInvariant())) + if (!OsuGameBase.VIDEO_EXTENSIONS.Contains(Path.GetExtension(path).ToLowerInvariant())) break; storyboard.GetLayer("Video").Add(new StoryboardVideo(path, offset)); diff --git a/osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs b/osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs index 825b1a5636..7097102335 100644 --- a/osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs +++ b/osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs @@ -70,7 +70,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 { get { - if (OsuGameBase.VIDEO_EXTENSIONS.Contains(File.Extension.ToUpperInvariant())) + if (OsuGameBase.VIDEO_EXTENSIONS.Contains(File.Extension.ToLowerInvariant())) return FontAwesome.Regular.FileVideo; switch (File.Extension) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 2ab6c24af7..34e31b0d61 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -71,7 +71,7 @@ namespace osu.Game [Cached(typeof(OsuGameBase))] public partial class OsuGameBase : Framework.Game, ICanAcceptFiles, IBeatSyncProvider { - public static readonly string[] VIDEO_EXTENSIONS = { ".MP4", ".MOV", ".AVI", ".FLV", ".MPG", ".WMV", ".M4V" }; + public static readonly string[] VIDEO_EXTENSIONS = { ".mp4", ".mov", ".avi", ".flv", ".mpg", ".wmv", ".m4v" }; public const string OSU_PROTOCOL = "osu://"; From e9fb836e9ccfbd6690ffcf5c6974e2d125a5fb80 Mon Sep 17 00:00:00 2001 From: Haspamelodica Date: Fri, 21 Apr 2023 03:24:11 +0200 Subject: [PATCH 406/476] Added tests for video backgrounds --- .../Formats/LegacyBeatmapDecoderTest.cs | 31 +++++++++++++++ .../Formats/LegacyStoryboardDecoderTest.cs | 38 ++++++++++++++++++- .../video-with-lowercase-extension.osb | 5 +++ .../video-with-uppercase-extension.osb | 5 +++ 4 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 osu.Game.Tests/Resources/video-with-lowercase-extension.osb create mode 100644 osu.Game.Tests/Resources/video-with-uppercase-extension.osb diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 518981980b..5979f6785e 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -21,6 +21,7 @@ using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Skinning; +using osu.Game.Storyboards; using osu.Game.Tests.Resources; using osuTK; using osuTK.Graphics; @@ -160,6 +161,36 @@ namespace osu.Game.Tests.Beatmaps.Formats } } + [Test] + public void TestDecodeVideoWithLowercaseExtension() + { + var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; + + using (var resStream = TestResources.OpenResource("video-with-lowercase-extension.osb")) + using (var stream = new LineBufferedReader(resStream)) + { + var beatmap = decoder.Decode(stream); + var metadata = beatmap.Metadata; + + Assert.AreEqual("BG.jpg", metadata.BackgroundFile); + } + } + + [Test] + public void TestDecodeVideoWithUppercaseExtension() + { + var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; + + using (var resStream = TestResources.OpenResource("video-with-uppercase-extension.osb")) + using (var stream = new LineBufferedReader(resStream)) + { + var beatmap = decoder.Decode(stream); + var metadata = beatmap.Metadata; + + Assert.AreEqual("BG.jpg", metadata.BackgroundFile); + } + } + [Test] public void TestDecodeImageSpecifiedAsVideo() { diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs index 3a776ac225..1bfe134610 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs @@ -169,6 +169,40 @@ namespace osu.Game.Tests.Beatmaps.Formats } } + [Test] + public void TestDecodeVideoWithLowercaseExtension() + { + var decoder = new LegacyStoryboardDecoder(); + + using (var resStream = TestResources.OpenResource("video-with-lowercase-extension.osb")) + using (var stream = new LineBufferedReader(resStream)) + { + var storyboard = decoder.Decode(stream); + + StoryboardLayer video = storyboard.Layers.Single(l => l.Name == "Video"); + Assert.That(video.Elements.Count, Is.EqualTo(1)); + + Assert.AreEqual("Video.avi", ((StoryboardVideo)video.Elements[0]).Path); + } + } + + [Test] + public void TestDecodeVideoWithUppercaseExtension() + { + var decoder = new LegacyStoryboardDecoder(); + + using (var resStream = TestResources.OpenResource("video-with-uppercase-extension.osb")) + using (var stream = new LineBufferedReader(resStream)) + { + var storyboard = decoder.Decode(stream); + + StoryboardLayer video = storyboard.Layers.Single(l => l.Name == "Video"); + Assert.That(video.Elements.Count, Is.EqualTo(1)); + + Assert.AreEqual("Video.AVI", ((StoryboardVideo)video.Elements[0]).Path); + } + } + [Test] public void TestDecodeImageSpecifiedAsVideo() { @@ -179,8 +213,8 @@ namespace osu.Game.Tests.Beatmaps.Formats { var storyboard = decoder.Decode(stream); - StoryboardLayer foreground = storyboard.Layers.Single(l => l.Name == "Video"); - Assert.That(foreground.Elements.Count, Is.Zero); + StoryboardLayer video = storyboard.Layers.Single(l => l.Name == "Video"); + Assert.That(video.Elements.Count, Is.Zero); } } diff --git a/osu.Game.Tests/Resources/video-with-lowercase-extension.osb b/osu.Game.Tests/Resources/video-with-lowercase-extension.osb new file mode 100644 index 0000000000..eec09722ed --- /dev/null +++ b/osu.Game.Tests/Resources/video-with-lowercase-extension.osb @@ -0,0 +1,5 @@ +osu file format v14 + +[Events] +0,0,"BG.jpg",0,0 +Video,0,"Video.avi",0,0 diff --git a/osu.Game.Tests/Resources/video-with-uppercase-extension.osb b/osu.Game.Tests/Resources/video-with-uppercase-extension.osb new file mode 100644 index 0000000000..3834a547f2 --- /dev/null +++ b/osu.Game.Tests/Resources/video-with-uppercase-extension.osb @@ -0,0 +1,5 @@ +osu file format v14 + +[Events] +0,0,"BG.jpg",0,0 +Video,0,"Video.AVI",0,0 From 3166f88c17a291724f03b2bb53ca96413864a443 Mon Sep 17 00:00:00 2001 From: Haspamelodica Date: Fri, 21 Apr 2023 10:11:47 +0200 Subject: [PATCH 407/476] Removed unneccessary using directive --- osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 5979f6785e..d898650b66 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -21,7 +21,6 @@ using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Skinning; -using osu.Game.Storyboards; using osu.Game.Tests.Resources; using osuTK; using osuTK.Graphics; From 847b63066b9e08255026c3c530ebb5acab9ab5a4 Mon Sep 17 00:00:00 2001 From: Terochi Date: Fri, 21 Apr 2023 21:46:03 +0200 Subject: [PATCH 408/476] fix --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index ab754e51f7..7787dd80c1 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -418,10 +418,13 @@ namespace osu.Game.Rulesets.Catch.UI private void clearPlate(DroppedObjectAnimation animation) { - var droppedObjects = caughtObjectContainer.Children.Select(getDroppedObject).ToArray(); + var caughtObjects = caughtObjectContainer.Children.ToArray(); caughtObjectContainer.Clear(false); + //use the already returned PoolableDrawables for new objects + var droppedObjects = caughtObjects.Select(getDroppedObject).ToArray(); + droppedObjectTarget.AddRange(droppedObjects); foreach (var droppedObject in droppedObjects) @@ -430,13 +433,11 @@ namespace osu.Game.Rulesets.Catch.UI private void removeFromPlate(CaughtObject caughtObject, DroppedObjectAnimation animation) { - var droppedObject = getDroppedObject(caughtObject); - caughtObjectContainer.Remove(caughtObject, false); - droppedObjectTarget.Add(droppedObject); + droppedObjectTarget.Add(getDroppedObject(caughtObject)); - applyDropAnimation(droppedObject, animation); + applyDropAnimation(caughtObject, animation); } private void applyDropAnimation(Drawable d, DroppedObjectAnimation animation) @@ -456,6 +457,8 @@ namespace osu.Game.Rulesets.Catch.UI break; } + //define lifetime start for dropped objects to be disposed correctly when rewinding replay + d.LifetimeStart = Clock.CurrentTime; d.Expire(); } From bb1ed387ef29958bcc8f8006cebfad2ead9dc8c8 Mon Sep 17 00:00:00 2001 From: Terochi Date: Sat, 22 Apr 2023 10:54:50 +0200 Subject: [PATCH 409/476] fixed missed bit and comments --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 7787dd80c1..f77dab56c8 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -422,7 +422,7 @@ namespace osu.Game.Rulesets.Catch.UI caughtObjectContainer.Clear(false); - //use the already returned PoolableDrawables for new objects + // Use the already returned PoolableDrawables for new objects var droppedObjects = caughtObjects.Select(getDroppedObject).ToArray(); droppedObjectTarget.AddRange(droppedObjects); @@ -435,9 +435,11 @@ namespace osu.Game.Rulesets.Catch.UI { caughtObjectContainer.Remove(caughtObject, false); - droppedObjectTarget.Add(getDroppedObject(caughtObject)); + var droppedObject = getDroppedObject(caughtObject); - applyDropAnimation(caughtObject, animation); + droppedObjectTarget.Add(droppedObject); + + applyDropAnimation(droppedObject, animation); } private void applyDropAnimation(Drawable d, DroppedObjectAnimation animation) @@ -457,7 +459,7 @@ namespace osu.Game.Rulesets.Catch.UI break; } - //define lifetime start for dropped objects to be disposed correctly when rewinding replay + // Define lifetime start for dropped objects to be disposed correctly when rewinding replay d.LifetimeStart = Clock.CurrentTime; d.Expire(); } From 56ab029a58eeff34f1f976fb956b7f042d8c79df Mon Sep 17 00:00:00 2001 From: Hy0tic Date: Sat, 22 Apr 2023 13:30:08 -0400 Subject: [PATCH 410/476] fix issue where multipler does not update when adjusting speed for preset mod --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 16602db4be..d611fd3c0b 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -252,7 +252,7 @@ namespace osu.Game.Overlays.Mods if (AllowCustomisation) { - modSettingChangeTracker = new ModSettingChangeTracker(val.NewValue); + modSettingChangeTracker = new ModSettingChangeTracker(SelectedMods.Value); modSettingChangeTracker.SettingChanged += _ => updateMultiplier(); } }, true); From 3919400be2c0d2dddc287ca8bda36677dfb029fe Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Apr 2023 14:28:56 +0900 Subject: [PATCH 411/476] Apply NRT to some storyboard classes --- osu.Game/Storyboards/StoryboardAnimation.cs | 2 -- osu.Game/Storyboards/StoryboardSprite.cs | 13 +++++-------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/osu.Game/Storyboards/StoryboardAnimation.cs b/osu.Game/Storyboards/StoryboardAnimation.cs index 16deac8e9e..1a4b6bb923 100644 --- a/osu.Game/Storyboards/StoryboardAnimation.cs +++ b/osu.Game/Storyboards/StoryboardAnimation.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 disable - using osuTK; using osu.Framework.Graphics; using osu.Game.Storyboards.Drawables; diff --git a/osu.Game/Storyboards/StoryboardSprite.cs b/osu.Game/Storyboards/StoryboardSprite.cs index 5b7b194be7..0c28d7bce1 100644 --- a/osu.Game/Storyboards/StoryboardSprite.cs +++ b/osu.Game/Storyboards/StoryboardSprite.cs @@ -1,12 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Collections.Generic; using System.Linq; -using JetBrains.Annotations; using osu.Framework.Graphics; using osu.Game.Storyboards.Drawables; using osuTK; @@ -114,7 +111,7 @@ namespace osu.Game.Storyboards public virtual Drawable CreateDrawable() => new DrawableStoryboardSprite(this); - public void ApplyTransforms(Drawable drawable, IEnumerable> triggeredGroups = null) + public void ApplyTransforms(Drawable drawable, IEnumerable>? triggeredGroups = null) { // For performance reasons, we need to apply the commands in order by start time. Not doing so will cause many functions to be interleaved, resulting in O(n^2) complexity. // To achieve this, commands are "generated" as pairs of (command, initFunc, transformFunc) and batched into a contiguous list @@ -156,7 +153,7 @@ namespace osu.Game.Storyboards foreach (var command in commands) { - DrawablePropertyInitializer initFunc = null; + DrawablePropertyInitializer? initFunc = null; if (!initialized) { @@ -169,7 +166,7 @@ namespace osu.Game.Storyboards } } - private IEnumerable.TypedCommand> getCommands(CommandTimelineSelector timelineSelector, IEnumerable> triggeredGroups) + private IEnumerable.TypedCommand> getCommands(CommandTimelineSelector timelineSelector, IEnumerable>? triggeredGroups) { var commands = TimelineGroup.GetCommands(timelineSelector); foreach (var loop in loops) @@ -198,11 +195,11 @@ namespace osu.Game.Storyboards { public double StartTime => command.StartTime; - private readonly DrawablePropertyInitializer initializeProperty; + private readonly DrawablePropertyInitializer? initializeProperty; private readonly DrawableTransformer transform; private readonly CommandTimeline.TypedCommand command; - public GeneratedCommand([NotNull] CommandTimeline.TypedCommand command, [CanBeNull] DrawablePropertyInitializer initializeProperty, [NotNull] DrawableTransformer transform) + public GeneratedCommand(CommandTimeline.TypedCommand command, DrawablePropertyInitializer? initializeProperty, DrawableTransformer transform) { this.command = command; this.initializeProperty = initializeProperty; From dce0c5fac815b4334a921b81989baf2d06e3c57c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Apr 2023 15:14:32 +0900 Subject: [PATCH 412/476] Add test coverage of expected behaviour for playback of loops with no explicit end time --- .../Formats/LegacyStoryboardDecoderTest.cs | 19 +++++++++++++++++++ .../animation-loop-no-explicit-end-time.osb | 6 ++++++ 2 files changed, 25 insertions(+) create mode 100644 osu.Game.Tests/Resources/animation-loop-no-explicit-end-time.osb diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs index 3a776ac225..17e94a8b5f 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs @@ -95,6 +95,25 @@ namespace osu.Game.Tests.Beatmaps.Formats } } + [Test] + public void TestLoopWithoutExplicitFadeOut() + { + var decoder = new LegacyStoryboardDecoder(); + + using (var resStream = TestResources.OpenResource("animation-loop-no-explicit-end-time.osb")) + using (var stream = new LineBufferedReader(resStream)) + { + var storyboard = decoder.Decode(stream); + + StoryboardLayer background = storyboard.Layers.Single(l => l.Depth == 3); + Assert.AreEqual(1, background.Elements.Count); + + Assert.AreEqual(2000, background.Elements[0].StartTime); + Assert.AreEqual(2000, (background.Elements[0] as StoryboardAnimation)?.EarliestTransformTime); + Assert.AreEqual(12000, (background.Elements[0] as StoryboardAnimation)?.GetEndTime()); + } + } + [Test] public void TestCorrectAnimationStartTime() { diff --git a/osu.Game.Tests/Resources/animation-loop-no-explicit-end-time.osb b/osu.Game.Tests/Resources/animation-loop-no-explicit-end-time.osb new file mode 100644 index 0000000000..7afaa445df --- /dev/null +++ b/osu.Game.Tests/Resources/animation-loop-no-explicit-end-time.osb @@ -0,0 +1,6 @@ +[Events] +//Storyboard Layer 0 (Background) +Animation,Background,Centre,"img.jpg",320,240,2,150,LoopForever + F,0,2000,,0,1 + L,2000,10 + F,18,0,1000,1,0 From e330052852a8692c769ed2152a5980a16679b576 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Apr 2023 15:22:11 +0900 Subject: [PATCH 413/476] Add second definition of `EndTime` for storyboard elements to account for loops in lifetime --- .../Beatmaps/Formats/LegacyStoryboardDecoderTest.cs | 4 +++- .../Background/TestSceneBackgroundScreenDefault.cs | 1 + .../Storyboards/IStoryboardElementWithDuration.cs | 8 ++++++++ osu.Game/Storyboards/StoryboardSprite.cs | 13 +++++++++++++ 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs index 17e94a8b5f..f78825ffb1 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs @@ -110,7 +110,9 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.AreEqual(2000, background.Elements[0].StartTime); Assert.AreEqual(2000, (background.Elements[0] as StoryboardAnimation)?.EarliestTransformTime); - Assert.AreEqual(12000, (background.Elements[0] as StoryboardAnimation)?.GetEndTime()); + + Assert.AreEqual(3000, (background.Elements[0] as StoryboardAnimation)?.GetEndTime()); + Assert.AreEqual(12000, (background.Elements[0] as StoryboardAnimation)?.EndTimeForDisplay); } } diff --git a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs index fbdaad1cd8..8f4250799e 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs @@ -311,6 +311,7 @@ namespace osu.Game.Tests.Visual.Background public bool IsDrawable => true; public double StartTime => double.MinValue; public double EndTime => double.MaxValue; + public double EndTimeForDisplay => double.MaxValue; public Drawable CreateDrawable() => new DrawableTestStoryboardElement(); } diff --git a/osu.Game/Storyboards/IStoryboardElementWithDuration.cs b/osu.Game/Storyboards/IStoryboardElementWithDuration.cs index c8daeb3b3d..9eed139ad4 100644 --- a/osu.Game/Storyboards/IStoryboardElementWithDuration.cs +++ b/osu.Game/Storyboards/IStoryboardElementWithDuration.cs @@ -12,9 +12,17 @@ namespace osu.Game.Storyboards { /// /// The time at which the ends. + /// This is consumed to extend the length of a storyboard to ensure all visuals are played to completion. /// double EndTime { get; } + /// + /// The time this element displays until. + /// This is used for lifetime purposes, and includes long playing animations which don't necessarily extend + /// a storyboard's play time. + /// + double EndTimeForDisplay { get; } + /// /// The duration of the StoryboardElement. /// diff --git a/osu.Game/Storyboards/StoryboardSprite.cs b/osu.Game/Storyboards/StoryboardSprite.cs index 0c28d7bce1..982185d51b 100644 --- a/osu.Game/Storyboards/StoryboardSprite.cs +++ b/osu.Game/Storyboards/StoryboardSprite.cs @@ -81,6 +81,19 @@ namespace osu.Game.Storyboards } } + public double EndTimeForDisplay + { + get + { + double latestEndTime = TimelineGroup.EndTime; + + foreach (var l in loops) + latestEndTime = Math.Max(latestEndTime, l.StartTime + l.CommandsDuration * l.TotalIterations); + + return latestEndTime; + } + } + public bool HasCommands => TimelineGroup.HasCommands || loops.Any(l => l.HasCommands); private delegate void DrawablePropertyInitializer(Drawable drawable, T value); From dd2c289ce96ed4c874f901476bef18e0c4657a6c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Apr 2023 20:22:12 +0900 Subject: [PATCH 414/476] Remove pointless default value --- osu.Game/Screens/Play/ArgonKeyCounter.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Play/ArgonKeyCounter.cs b/osu.Game/Screens/Play/ArgonKeyCounter.cs index 70fe2e3a65..a978f001d5 100644 --- a/osu.Game/Screens/Play/ArgonKeyCounter.cs +++ b/osu.Game/Screens/Play/ArgonKeyCounter.cs @@ -56,7 +56,6 @@ namespace osu.Game.Screens.Play Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, Font = OsuFont.Torus.With(size: count_font_size * scale_factor, weight: FontWeight.Bold), - Text = "0" }, }; From 76309586330690aee88bfc4b7f6ac6b70cdbf751 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Apr 2023 20:24:36 +0900 Subject: [PATCH 415/476] Stop using `Drawable.Name` to convey actual UI information --- osu.Game/Screens/Play/ArgonKeyCounter.cs | 2 +- osu.Game/Screens/Play/HUD/DefaultKeyCounter.cs | 2 +- osu.Game/Screens/Play/HUD/KeyCounter.cs | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/ArgonKeyCounter.cs b/osu.Game/Screens/Play/ArgonKeyCounter.cs index a978f001d5..6818b30823 100644 --- a/osu.Game/Screens/Play/ArgonKeyCounter.cs +++ b/osu.Game/Screens/Play/ArgonKeyCounter.cs @@ -49,7 +49,7 @@ namespace osu.Game.Screens.Play Position = new Vector2(0, -13) * scale_factor, Font = OsuFont.Torus.With(size: name_font_size * scale_factor, weight: FontWeight.Bold), Colour = colours.Blue0, - Text = Name + Text = Trigger.Name }, countText = new OsuSpriteText { diff --git a/osu.Game/Screens/Play/HUD/DefaultKeyCounter.cs b/osu.Game/Screens/Play/HUD/DefaultKeyCounter.cs index 69a3e53dfc..f7ac72035f 100644 --- a/osu.Game/Screens/Play/HUD/DefaultKeyCounter.cs +++ b/osu.Game/Screens/Play/HUD/DefaultKeyCounter.cs @@ -57,7 +57,7 @@ namespace osu.Game.Screens.Play.HUD { new OsuSpriteText { - Text = Name, + Text = Trigger.Name, Font = OsuFont.Numeric.With(size: 12), Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Screens/Play/HUD/KeyCounter.cs b/osu.Game/Screens/Play/HUD/KeyCounter.cs index 2a4ab1993a..7cdd6b025f 100644 --- a/osu.Game/Screens/Play/HUD/KeyCounter.cs +++ b/osu.Game/Screens/Play/HUD/KeyCounter.cs @@ -54,8 +54,6 @@ namespace osu.Game.Screens.Play.HUD Trigger.OnActivate += Activate; Trigger.OnDeactivate += Deactivate; - - Name = trigger.Name; } private void increment() From 0c3a01595362d1e995dfe46d7fbd0d1c4bd8c814 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Apr 2023 21:37:17 +0900 Subject: [PATCH 416/476] Fix key counter test not testing the full binding of `IsCounting` --- .../Visual/Gameplay/TestSceneKeyCounter.cs | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs index 1e35c24e97..aabc25d660 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs @@ -19,21 +19,21 @@ namespace osu.Game.Tests.Visual.Gameplay { public TestSceneKeyCounter() { - KeyCounterDisplay kc = new DefaultKeyCounterDisplay + KeyCounterDisplay defaultDisplay = new DefaultKeyCounterDisplay { Origin = Anchor.Centre, Anchor = Anchor.Centre, Position = new Vector2(0, 72.7f) }; - KeyCounterDisplay argonKc = new ArgonKeyCounterDisplay + KeyCounterDisplay argonDisplay = new ArgonKeyCounterDisplay { Origin = Anchor.Centre, Anchor = Anchor.Centre, Position = new Vector2(0, -72.7f) }; - kc.AddRange(new InputTrigger[] + defaultDisplay.AddRange(new InputTrigger[] { new KeyCounterKeyboardTrigger(Key.X), new KeyCounterKeyboardTrigger(Key.X), @@ -41,7 +41,7 @@ namespace osu.Game.Tests.Visual.Gameplay new KeyCounterMouseTrigger(MouseButton.Right), }); - argonKc.AddRange(new InputTrigger[] + argonDisplay.AddRange(new InputTrigger[] { new KeyCounterKeyboardTrigger(Key.X), new KeyCounterKeyboardTrigger(Key.X), @@ -49,32 +49,29 @@ namespace osu.Game.Tests.Visual.Gameplay new KeyCounterMouseTrigger(MouseButton.Right), }); - var testCounter = (DefaultKeyCounter)kc.Counters.First(); + var testCounter = (DefaultKeyCounter)defaultDisplay.Counters.First(); AddStep("Add random", () => { Key key = (Key)((int)Key.A + RNG.Next(26)); - kc.Add(new KeyCounterKeyboardTrigger(key)); - argonKc.Add(new KeyCounterKeyboardTrigger(key)); + defaultDisplay.Add(new KeyCounterKeyboardTrigger(key)); + argonDisplay.Add(new KeyCounterKeyboardTrigger(key)); }); - Key testKey = ((KeyCounterKeyboardTrigger)kc.Counters.First().Trigger).Key; - - void addPressKeyStep() - { - AddStep($"Press {testKey} key", () => InputManager.Key(testKey)); - } + Key testKey = ((KeyCounterKeyboardTrigger)defaultDisplay.Counters.First().Trigger).Key; addPressKeyStep(); AddAssert($"Check {testKey} counter after keypress", () => testCounter.CountPresses.Value == 1); addPressKeyStep(); AddAssert($"Check {testKey} counter after keypress", () => testCounter.CountPresses.Value == 2); - AddStep("Disable counting", () => testCounter.IsCounting.Value = false); + AddStep("Disable counting", () => defaultDisplay.IsCounting.Value = false); addPressKeyStep(); AddAssert($"Check {testKey} count has not changed", () => testCounter.CountPresses.Value == 2); - Add(kc); - Add(argonKc); + Add(defaultDisplay); + Add(argonDisplay); + + void addPressKeyStep() => AddStep($"Press {testKey} key", () => InputManager.Key(testKey)); } } } From 0a861ffcee21bae0b235b88b2455e1f2d3bfe2d6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Apr 2023 21:22:55 +0900 Subject: [PATCH 417/476] Restructure key counters to use a common flow --- .../Screens/Play/ArgonKeyCounterDisplay.cs | 14 ++++----- .../Play/HUD/DefaultKeyCounterDisplay.cs | 29 +++++++++---------- .../Screens/Play/HUD/KeyCounterDisplay.cs | 8 +++-- 3 files changed, 24 insertions(+), 27 deletions(-) diff --git a/osu.Game/Screens/Play/ArgonKeyCounterDisplay.cs b/osu.Game/Screens/Play/ArgonKeyCounterDisplay.cs index a62ac3d39a..984c2a7287 100644 --- a/osu.Game/Screens/Play/ArgonKeyCounterDisplay.cs +++ b/osu.Game/Screens/Play/ArgonKeyCounterDisplay.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.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Screens.Play.HUD; @@ -13,13 +12,11 @@ namespace osu.Game.Screens.Play { private const int duration = 100; - private readonly FillFlowContainer keyFlow; - - public override IEnumerable Counters => keyFlow; + protected override FillFlowContainer KeyFlow { get; } public ArgonKeyCounterDisplay() { - InternalChild = keyFlow = new FillFlowContainer + InternalChild = KeyFlow = new FillFlowContainer { Direction = FillDirection.Horizontal, AutoSizeAxes = Axes.Both, @@ -32,13 +29,12 @@ namespace osu.Game.Screens.Play { base.Update(); - Size = keyFlow.Size; + Size = KeyFlow.Size; } - public override void Add(InputTrigger trigger) => - keyFlow.Add(new ArgonKeyCounter(trigger)); + protected override KeyCounter CreateCounter(InputTrigger trigger) => new ArgonKeyCounter(trigger); protected override void UpdateVisibility() - => keyFlow.FadeTo(AlwaysVisible.Value || ConfigVisibility.Value ? 1 : 0, duration); + => KeyFlow.FadeTo(AlwaysVisible.Value || ConfigVisibility.Value ? 1 : 0, duration); } } diff --git a/osu.Game/Screens/Play/HUD/DefaultKeyCounterDisplay.cs b/osu.Game/Screens/Play/HUD/DefaultKeyCounterDisplay.cs index 14d7f56093..e459574243 100644 --- a/osu.Game/Screens/Play/HUD/DefaultKeyCounterDisplay.cs +++ b/osu.Game/Screens/Play/HUD/DefaultKeyCounterDisplay.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. -using System.Collections.Generic; +using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osuTK.Graphics; @@ -13,13 +13,11 @@ namespace osu.Game.Screens.Play.HUD private const int duration = 100; private const double key_fade_time = 80; - private readonly FillFlowContainer keyFlow; - - public override IEnumerable Counters => keyFlow; + protected override FillFlowContainer KeyFlow { get; } public DefaultKeyCounterDisplay() { - InternalChild = keyFlow = new FillFlowContainer + InternalChild = KeyFlow = new FillFlowContainer { Direction = FillDirection.Horizontal, AutoSizeAxes = Axes.Both, @@ -33,20 +31,19 @@ namespace osu.Game.Screens.Play.HUD // Don't use autosize as it will shrink to zero when KeyFlow is hidden. // In turn this can cause the display to be masked off screen and never become visible again. - Size = keyFlow.Size; + Size = KeyFlow.Size; } - public override void Add(InputTrigger trigger) => - keyFlow.Add(new DefaultKeyCounter(trigger) - { - FadeTime = key_fade_time, - KeyDownTextColor = KeyDownTextColor, - KeyUpTextColor = KeyUpTextColor, - }); + protected override KeyCounter CreateCounter(InputTrigger trigger) => new DefaultKeyCounter(trigger) + { + FadeTime = key_fade_time, + KeyDownTextColor = KeyDownTextColor, + KeyUpTextColor = KeyUpTextColor, + }; protected override void UpdateVisibility() => // Isolate changing visibility of the key counters from fading this component. - keyFlow.FadeTo(AlwaysVisible.Value || ConfigVisibility.Value ? 1 : 0, duration); + KeyFlow.FadeTo(AlwaysVisible.Value || ConfigVisibility.Value ? 1 : 0, duration); private Color4 keyDownTextColor = Color4.DarkGray; @@ -58,7 +55,7 @@ namespace osu.Game.Screens.Play.HUD if (value != keyDownTextColor) { keyDownTextColor = value; - foreach (var child in keyFlow) + foreach (var child in KeyFlow.Cast()) child.KeyDownTextColor = value; } } @@ -74,7 +71,7 @@ namespace osu.Game.Screens.Play.HUD if (value != keyUpTextColor) { keyUpTextColor = value; - foreach (var child in keyFlow) + foreach (var child in KeyFlow.Cast()) child.KeyUpTextColor = value; } } diff --git a/osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs b/osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs index 49c0da6793..e218155af2 100644 --- a/osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs +++ b/osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs @@ -29,7 +29,9 @@ namespace osu.Game.Screens.Play.HUD /// /// The s contained in this . /// - public abstract IEnumerable Counters { get; } + public IEnumerable Counters => KeyFlow; + + protected abstract FillFlowContainer KeyFlow { get; } /// /// Whether the actions reported by all s within this should be counted. @@ -53,13 +55,15 @@ namespace osu.Game.Screens.Play.HUD /// /// Add a to this display. /// - public abstract void Add(InputTrigger trigger); + public void Add(InputTrigger trigger) => KeyFlow.Add(CreateCounter(trigger)); /// /// Add a range of to this display. /// public void AddRange(IEnumerable triggers) => triggers.ForEach(Add); + protected abstract KeyCounter CreateCounter(InputTrigger trigger); + [BackgroundDependencyLoader] private void load(OsuConfigManager config) { From 6b4032e34b57fba897a4f8906a9097c6543acb30 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Apr 2023 21:31:39 +0900 Subject: [PATCH 418/476] Add missing binding of `IsCounting` with contained counters --- osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs b/osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs index e218155af2..05427d3a32 100644 --- a/osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs +++ b/osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs @@ -55,7 +55,14 @@ namespace osu.Game.Screens.Play.HUD /// /// Add a to this display. /// - public void Add(InputTrigger trigger) => KeyFlow.Add(CreateCounter(trigger)); + public void Add(InputTrigger trigger) + { + var keyCounter = CreateCounter(trigger); + + KeyFlow.Add(keyCounter); + + IsCounting.BindTo(keyCounter.IsCounting); + } /// /// Add a range of to this display. From e08d7daffd7c5f582f98a43cad3f2fd2d64829bc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Apr 2023 21:43:14 +0900 Subject: [PATCH 419/476] Don't show decimal point in tooltip --- osu.Game/Localisation/EditorStrings.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Localisation/EditorStrings.cs b/osu.Game/Localisation/EditorStrings.cs index 7c9b52275d..20258b9c35 100644 --- a/osu.Game/Localisation/EditorStrings.cs +++ b/osu.Game/Localisation/EditorStrings.cs @@ -100,14 +100,14 @@ namespace osu.Game.Localisation public static LocalisableString TimelineTicks => new TranslatableString(getKey(@"timeline_ticks"), @"Ticks"); /// - /// "{0:0.0}°" + /// "{0:0}°" /// - public static LocalisableString RotationUnsnapped(float newRotation) => new TranslatableString(getKey(@"rotation_unsnapped"), @"{0:0.0}°", newRotation); + public static LocalisableString RotationUnsnapped(float newRotation) => new TranslatableString(getKey(@"rotation_unsnapped"), @"{0:0}°", newRotation); /// - /// "{0:0.0}° (snapped)" + /// "{0:0}° (snapped)" /// - public static LocalisableString RotationSnapped(float newRotation) => new TranslatableString(getKey(@"rotation_snapped"), @"{0:0.0}° (snapped)", newRotation); + public static LocalisableString RotationSnapped(float newRotation) => new TranslatableString(getKey(@"rotation_snapped"), @"{0:0}° (snapped)", newRotation); private static string getKey(string key) => $@"{prefix}:{key}"; } From 753fa09356a2864060d0c8eb2dd95a29a7041785 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 25 Apr 2023 20:10:11 +0200 Subject: [PATCH 420/476] Fix test failures due to type mismatch --- osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs | 2 +- osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs index eecead5415..ae46dda750 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs @@ -45,7 +45,7 @@ namespace osu.Game.Tests.Visual.Gameplay // best way to check without exposing. private Drawable hideTarget => hudOverlay.KeyCounter; - private Drawable keyCounterFlow => hudOverlay.KeyCounter.ChildrenOfType>().Single(); + private Drawable keyCounterFlow => hudOverlay.KeyCounter.ChildrenOfType>().Single(); [BackgroundDependencyLoader] private void load() diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs index 7bbfc6a62b..0439656aae 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs @@ -44,7 +44,7 @@ namespace osu.Game.Tests.Visual.Gameplay // best way to check without exposing. private Drawable hideTarget => hudOverlay.KeyCounter; - private Drawable keyCounterFlow => hudOverlay.KeyCounter.ChildrenOfType>().Single(); + private Drawable keyCounterFlow => hudOverlay.KeyCounter.ChildrenOfType>().Single(); [Test] public void TestComboCounterIncrementing() From 196b5b41eb07f7e0377c84b0d8ca381386dbc541 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 25 Apr 2023 20:17:52 +0200 Subject: [PATCH 421/476] Also disable counting on argon display in test Mostly for my own peace of mind. --- osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs index aabc25d660..22f7111f68 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs @@ -64,7 +64,11 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert($"Check {testKey} counter after keypress", () => testCounter.CountPresses.Value == 1); addPressKeyStep(); AddAssert($"Check {testKey} counter after keypress", () => testCounter.CountPresses.Value == 2); - AddStep("Disable counting", () => defaultDisplay.IsCounting.Value = false); + AddStep("Disable counting", () => + { + argonDisplay.IsCounting.Value = false; + defaultDisplay.IsCounting.Value = false; + }); addPressKeyStep(); AddAssert($"Check {testKey} count has not changed", () => testCounter.CountPresses.Value == 2); From 1efc78c0f8a7c3b17062c9a90318da3ef41a986c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 Apr 2023 13:28:51 +0900 Subject: [PATCH 422/476] Actually use new end time property when setting lifetimes --- osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs | 2 +- osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs index e598c79b08..be77c9a98e 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs @@ -85,7 +85,7 @@ namespace osu.Game.Storyboards.Drawables Loop = animation.LoopType == AnimationLoopType.LoopForever; LifetimeStart = animation.StartTime; - LifetimeEnd = animation.EndTime; + LifetimeEnd = animation.EndTimeForDisplay; } [Resolved] diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs index f9b09ed57c..400d33481c 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs @@ -82,7 +82,7 @@ namespace osu.Game.Storyboards.Drawables Position = sprite.InitialPosition; LifetimeStart = sprite.StartTime; - LifetimeEnd = sprite.EndTime; + LifetimeEnd = sprite.EndTimeForDisplay; } [Resolved] From cb7b246e3352c08b73119b88fb0966e118720e2c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 Apr 2023 15:27:58 +0900 Subject: [PATCH 423/476] Fix naming and update in line with nullability --- osu.Game/Rulesets/Mods/ModFailCondition.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModFailCondition.cs b/osu.Game/Rulesets/Mods/ModFailCondition.cs index 63cebc9747..97789b7f5a 100644 --- a/osu.Game/Rulesets/Mods/ModFailCondition.cs +++ b/osu.Game/Rulesets/Mods/ModFailCondition.cs @@ -19,18 +19,19 @@ namespace osu.Game.Rulesets.Mods public virtual bool PerformFail() => true; public virtual bool RestartOnFail => Restart.Value; - private event Action failureTriggered; + + private Action? triggerFailureDelegate; public void ApplyToHealthProcessor(HealthProcessor healthProcessor) { - failureTriggered = healthProcessor.TriggerFailure; + triggerFailureDelegate = healthProcessor.TriggerFailure; healthProcessor.FailConditions += FailCondition; } /// /// Immediately triggers a failure on the loaded . /// - protected void TriggerFailure() => failureTriggered?.Invoke(); + protected void TriggerFailure() => triggerFailureDelegate?.Invoke(); /// /// Determines whether should trigger a failure. Called every time a From 76df5fd3e27ddb2149182b5586ab1eff1634f7c4 Mon Sep 17 00:00:00 2001 From: sw1tchbl4d3 Date: Wed, 26 Apr 2023 18:05:47 +0200 Subject: [PATCH 424/476] Limit taiko playfield aspect ratio to 5:4 - 16:9 --- .../Mods/TaikoModClassic.cs | 2 +- .../UI/DrawableTaikoRuleset.cs | 8 ++++--- .../UI/TaikoPlayfieldAdjustmentContainer.cs | 22 ++++++++++++++----- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs index d0361b1c8d..cdeaafde10 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Taiko.Mods public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { var drawableTaikoRuleset = (DrawableTaikoRuleset)drawableRuleset; - drawableTaikoRuleset.LockPlayfieldMaxAspect.Value = false; + drawableTaikoRuleset.LockPlayfieldAspectRange.Value = false; var playfield = (TaikoPlayfield)drawableRuleset.Playfield; playfield.ClassicHitTargetPosition.Value = true; diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs index a08877e2dd..64d406a308 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Taiko.UI { public new BindableDouble TimeRange => base.TimeRange; - public readonly BindableBool LockPlayfieldMaxAspect = new BindableBool(true); + public readonly BindableBool LockPlayfieldAspectRange = new BindableBool(true); public new TaikoInputManager KeyBindingInputManager => (TaikoInputManager)base.KeyBindingInputManager; @@ -69,7 +69,9 @@ namespace osu.Game.Rulesets.Taiko.UI const float scroll_rate = 10; // Since the time range will depend on a positional value, it is referenced to the x480 pixel space. - float ratio = DrawHeight / 480; + // Width is used because it defines how many notes fit on the playfield. + // We clamp the ratio to the maximum aspect ratio to keep scroll speed consistent on widths lower than the default. + float ratio = Math.Max(DrawSize.X / 768f, TaikoPlayfieldAdjustmentContainer.MAXIMUM_ASPECT); TimeRange.Value = (Playfield.HitObjectContainer.DrawWidth / ratio) * scroll_rate; } @@ -92,7 +94,7 @@ namespace osu.Game.Rulesets.Taiko.UI public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new TaikoPlayfieldAdjustmentContainer { - LockPlayfieldMaxAspect = { BindTarget = LockPlayfieldMaxAspect } + LockPlayfieldAspectRange = { BindTarget = LockPlayfieldAspectRange } }; protected override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo); diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs index 42732d90e4..3587783104 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs @@ -11,9 +11,11 @@ namespace osu.Game.Rulesets.Taiko.UI public partial class TaikoPlayfieldAdjustmentContainer : PlayfieldAdjustmentContainer { private const float default_relative_height = TaikoPlayfield.DEFAULT_HEIGHT / 768; - private const float default_aspect = 16f / 9f; - public readonly IBindable LockPlayfieldMaxAspect = new BindableBool(true); + public const float MAXIMUM_ASPECT = 16f / 9f; + public const float MINIMUM_ASPECT = 5f / 4f; + + public readonly IBindable LockPlayfieldAspectRange = new BindableBool(true); protected override void Update() { @@ -26,12 +28,22 @@ namespace osu.Game.Rulesets.Taiko.UI // // As a middle-ground, the aspect ratio can still be adjusted in the downwards direction but has a maximum limit. // This is still a bit weird, because readability changes with window size, but it is what it is. - if (LockPlayfieldMaxAspect.Value && Parent.ChildSize.X / Parent.ChildSize.Y > default_aspect) - height *= Math.Clamp(Parent.ChildSize.X / Parent.ChildSize.Y, 0.4f, 4) / default_aspect; + if (LockPlayfieldAspectRange.Value) + { + float currentAspect = Parent.ChildSize.X / Parent.ChildSize.Y; + if (currentAspect > MAXIMUM_ASPECT) + height *= currentAspect / MAXIMUM_ASPECT; + else if (currentAspect < MINIMUM_ASPECT) + height *= currentAspect / MINIMUM_ASPECT; + } + + // Limit the maximum relative height of the playfield to one-third of available area to avoid it masking out on extreme resolutions. + height = Math.Min(height, 1f / 3f); Height = height; - // Position the taiko playfield exactly one playfield from the top of the screen. + // Position the taiko playfield exactly one playfield from the top of the screen, if there is enough space for it. + // Note that the relative height cannot exceed one-third - if that limit is hit, the playfield will be exactly centered. RelativePositionAxes = Axes.Y; Y = height; } From f5c652325a55402d71dfb3182dea9769906f5d1b Mon Sep 17 00:00:00 2001 From: _ltn <46729135+rltn@users.noreply.github.com> Date: Thu, 27 Apr 2023 23:44:18 +0300 Subject: [PATCH 425/476] Create UpdateableCountryText.cs --- .../Users/Drawables/UpdateableCountryText.cs | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 osu.Game/Users/Drawables/UpdateableCountryText.cs diff --git a/osu.Game/Users/Drawables/UpdateableCountryText.cs b/osu.Game/Users/Drawables/UpdateableCountryText.cs new file mode 100644 index 0000000000..60174505a5 --- /dev/null +++ b/osu.Game/Users/Drawables/UpdateableCountryText.cs @@ -0,0 +1,67 @@ +// 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 osu.Framework.Allocation; +using osu.Framework.Input.Events; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays; +using osu.Framework.Extensions; + +namespace osu.Game.Users.Drawables +{ + public partial class UpdateableCountryText : ModelBackedDrawable + { + public CountryCode CountryCode + { + get => Model; + set => Model = value; + } + + public bool ShowPlaceholderOnUnknown = true; + + public Action? Action; + + protected override Drawable? CreateDrawable(CountryCode countryCode) + { + if (countryCode == CountryCode.Unknown && !ShowPlaceholderOnUnknown) + return null; + + return new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new OsuSpriteText + { + Font = OsuFont.GetFont(size: 14f, weight: FontWeight.Regular), + Margin = new MarginPadding { Left = 5 }, + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + Text = countryCode.GetDescription(), + }, + new HoverClickSounds() + } + }; + } + + [Resolved] + private RankingsOverlay? rankingsOverlay { get; set; } + + public UpdateableCountryText(CountryCode countryCode = CountryCode.Unknown) + { + CountryCode = countryCode; + } + protected override bool OnClick(ClickEvent e) + { + Action?.Invoke(); + rankingsOverlay?.ShowCountry(CountryCode); + return true; + } + } + +} \ No newline at end of file From b13201fb792801e87b5a9148abd2a9ec6a011976 Mon Sep 17 00:00:00 2001 From: _ltn <46729135+rltn@users.noreply.github.com> Date: Fri, 28 Apr 2023 01:14:42 +0300 Subject: [PATCH 426/476] UpdateableCountryText rewrite --- .../Users/Drawables/UpdateableCountryText.cs | 55 ++++++------------- 1 file changed, 17 insertions(+), 38 deletions(-) diff --git a/osu.Game/Users/Drawables/UpdateableCountryText.cs b/osu.Game/Users/Drawables/UpdateableCountryText.cs index 60174505a5..fd749da707 100644 --- a/osu.Game/Users/Drawables/UpdateableCountryText.cs +++ b/osu.Game/Users/Drawables/UpdateableCountryText.cs @@ -7,6 +7,7 @@ using osu.Framework.Input.Events; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; @@ -14,53 +15,31 @@ using osu.Framework.Extensions; namespace osu.Game.Users.Drawables { - public partial class UpdateableCountryText : ModelBackedDrawable + public partial class UpdateableCountryText : OsuHoverContainer { - public CountryCode CountryCode - { - get => Model; - set => Model = value; - } public bool ShowPlaceholderOnUnknown = true; - public Action? Action; - - protected override Drawable? CreateDrawable(CountryCode countryCode) - { - if (countryCode == CountryCode.Unknown && !ShowPlaceholderOnUnknown) - return null; - - return new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - new OsuSpriteText - { - Font = OsuFont.GetFont(size: 14f, weight: FontWeight.Regular), - Margin = new MarginPadding { Left = 5 }, - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - Text = countryCode.GetDescription(), - }, - new HoverClickSounds() - } - }; - } - [Resolved] private RankingsOverlay? rankingsOverlay { get; set; } - - public UpdateableCountryText(CountryCode countryCode = CountryCode.Unknown) + public UpdateableCountryText() { - CountryCode = countryCode; + AutoSizeAxes = Axes.Both; } - protected override bool OnClick(ClickEvent e) + + // [BackgroundDependencyLoader] + public void load(CountryCode countryCode) { - Action?.Invoke(); - rankingsOverlay?.ShowCountry(CountryCode); - return true; + Action = () => + { + rankingsOverlay?.ShowCountry(countryCode); + }; + + Child = new OsuSpriteText + { + Font = OsuFont.GetFont(size: 14f, weight: FontWeight.Regular), + Text = countryCode.GetDescription(), + }; } } From e9843f20665a00da524cc240a1ea307d00d548d2 Mon Sep 17 00:00:00 2001 From: _ltn <46729135+rltn@users.noreply.github.com> Date: Fri, 28 Apr 2023 01:16:50 +0300 Subject: [PATCH 427/476] replace country text object --- osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs index d04329430b..811628c3c1 100644 --- a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs @@ -38,7 +38,7 @@ namespace osu.Game.Overlays.Profile.Header private ExternalLinkButton openUserExternally = null!; private OsuSpriteText titleText = null!; private UpdateableFlag userFlag = null!; - private OsuSpriteText userCountryText = null!; + private UpdateableCountryText userCountryText = null!; private GroupBadgeFlow groupBadgeFlow = null!; private ToggleCoverButton coverToggle = null!; @@ -156,9 +156,8 @@ namespace osu.Game.Overlays.Profile.Header Size = new Vector2(28, 20), ShowPlaceholderOnUnknown = false, }, - userCountryText = new OsuSpriteText + userCountryText = new UpdateableCountryText { - Font = OsuFont.GetFont(size: 14f, weight: FontWeight.Regular), Margin = new MarginPadding { Left = 5 }, Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, @@ -201,7 +200,7 @@ namespace osu.Game.Overlays.Profile.Header usernameText.Text = user?.Username ?? string.Empty; openUserExternally.Link = $@"{api.WebsiteRootUrl}/users/{user?.Id ?? 0}"; userFlag.CountryCode = user?.CountryCode ?? default; - userCountryText.Text = (user?.CountryCode ?? default).GetDescription(); + userCountryText.load(user?.CountryCode ?? default); supporterTag.SupportLevel = user?.SupportLevel ?? 0; titleText.Text = user?.Title ?? string.Empty; titleText.Colour = Color4Extensions.FromHex(user?.Colour ?? "fff"); From 092377fdaa234738a79eeb409cea0e5722c154a4 Mon Sep 17 00:00:00 2001 From: _ltn <46729135+rltn@users.noreply.github.com> Date: Fri, 28 Apr 2023 01:47:14 +0300 Subject: [PATCH 428/476] moving UpdateableCountryText --- .../Profile/Header/TopHeaderContainer.cs | 41 +++++++++++++++++ .../Users/Drawables/UpdateableCountryText.cs | 46 ------------------- 2 files changed, 41 insertions(+), 46 deletions(-) delete mode 100644 osu.Game/Users/Drawables/UpdateableCountryText.cs diff --git a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs index 811628c3c1..54c84e08b8 100644 --- a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs @@ -19,6 +19,20 @@ using osu.Game.Users; using osu.Game.Users.Drawables; using osuTK; + +using System; +// using osu.Framework.Allocation; +using osu.Framework.Input.Events; +// using osu.Framework.Graphics; +// using osu.Framework.Graphics.Containers; +// using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +// using osu.Game.Graphics.Sprites; +// using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays; +using osu.Framework.Graphics.Sprites; +// using osu.Framework.Extensions; + namespace osu.Game.Overlays.Profile.Header { public partial class TopHeaderContainer : CompositeDrawable @@ -158,6 +172,7 @@ namespace osu.Game.Overlays.Profile.Header }, userCountryText = new UpdateableCountryText { + Font = OsuFont.GetFont(size: 14f, weight: FontWeight.Regular), Margin = new MarginPadding { Left = 5 }, Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, @@ -228,5 +243,31 @@ namespace osu.Game.Overlays.Profile.Header Masking = true; } } + + private partial class UpdateableCountryText : OsuHoverContainer + { + public bool ShowPlaceholderOnUnknown = true; + public FontUsage Font = default; + [Resolved] + private RankingsOverlay? rankingsOverlay { get; set; } + public UpdateableCountryText() + { + AutoSizeAxes = Axes.Both; + } + + public void load(CountryCode countryCode) + { + Action = () => + { + rankingsOverlay?.ShowCountry(countryCode); + }; + + Child = new OsuSpriteText + { + Font = Font, + Text = countryCode.GetDescription(), + }; + } + } } } diff --git a/osu.Game/Users/Drawables/UpdateableCountryText.cs b/osu.Game/Users/Drawables/UpdateableCountryText.cs deleted file mode 100644 index fd749da707..0000000000 --- a/osu.Game/Users/Drawables/UpdateableCountryText.cs +++ /dev/null @@ -1,46 +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 osu.Framework.Allocation; -using osu.Framework.Input.Events; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; -using osu.Game.Overlays; -using osu.Framework.Extensions; - -namespace osu.Game.Users.Drawables -{ - public partial class UpdateableCountryText : OsuHoverContainer - { - - public bool ShowPlaceholderOnUnknown = true; - - [Resolved] - private RankingsOverlay? rankingsOverlay { get; set; } - public UpdateableCountryText() - { - AutoSizeAxes = Axes.Both; - } - - // [BackgroundDependencyLoader] - public void load(CountryCode countryCode) - { - Action = () => - { - rankingsOverlay?.ShowCountry(countryCode); - }; - - Child = new OsuSpriteText - { - Font = OsuFont.GetFont(size: 14f, weight: FontWeight.Regular), - Text = countryCode.GetDescription(), - }; - } - } - -} \ No newline at end of file From 4d144cd5b5fb3e787fd51b75cef459bfab56fbf4 Mon Sep 17 00:00:00 2001 From: _ltn <46729135+rltn@users.noreply.github.com> Date: Fri, 28 Apr 2023 02:06:13 +0300 Subject: [PATCH 429/476] clearing the code --- .../Profile/Header/TopHeaderContainer.cs | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs index 54c84e08b8..3485037925 100644 --- a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs @@ -9,8 +9,10 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; using osu.Game.Configuration; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; @@ -20,19 +22,6 @@ using osu.Game.Users.Drawables; using osuTK; -using System; -// using osu.Framework.Allocation; -using osu.Framework.Input.Events; -// using osu.Framework.Graphics; -// using osu.Framework.Graphics.Containers; -// using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -// using osu.Game.Graphics.Sprites; -// using osu.Game.Graphics.UserInterface; -using osu.Game.Overlays; -using osu.Framework.Graphics.Sprites; -// using osu.Framework.Extensions; - namespace osu.Game.Overlays.Profile.Header { public partial class TopHeaderContainer : CompositeDrawable @@ -246,10 +235,12 @@ namespace osu.Game.Overlays.Profile.Header private partial class UpdateableCountryText : OsuHoverContainer { - public bool ShowPlaceholderOnUnknown = true; + public FontUsage Font = default; + [Resolved] private RankingsOverlay? rankingsOverlay { get; set; } + public UpdateableCountryText() { AutoSizeAxes = Axes.Both; From 17730f05bcf42c6af0c8876c46ae218adf71bb32 Mon Sep 17 00:00:00 2001 From: _ltn <46729135+rltn@users.noreply.github.com> Date: Fri, 28 Apr 2023 05:47:05 +0300 Subject: [PATCH 430/476] remove UpdateableCountryText --- .../Profile/Header/TopHeaderContainer.cs | 31 ------------------- 1 file changed, 31 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs index 3485037925..01fbf137d8 100644 --- a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs @@ -21,7 +21,6 @@ using osu.Game.Users; using osu.Game.Users.Drawables; using osuTK; - namespace osu.Game.Overlays.Profile.Header { public partial class TopHeaderContainer : CompositeDrawable @@ -41,7 +40,6 @@ namespace osu.Game.Overlays.Profile.Header private ExternalLinkButton openUserExternally = null!; private OsuSpriteText titleText = null!; private UpdateableFlag userFlag = null!; - private UpdateableCountryText userCountryText = null!; private GroupBadgeFlow groupBadgeFlow = null!; private ToggleCoverButton coverToggle = null!; @@ -204,7 +202,6 @@ namespace osu.Game.Overlays.Profile.Header usernameText.Text = user?.Username ?? string.Empty; openUserExternally.Link = $@"{api.WebsiteRootUrl}/users/{user?.Id ?? 0}"; userFlag.CountryCode = user?.CountryCode ?? default; - userCountryText.load(user?.CountryCode ?? default); supporterTag.SupportLevel = user?.SupportLevel ?? 0; titleText.Text = user?.Title ?? string.Empty; titleText.Colour = Color4Extensions.FromHex(user?.Colour ?? "fff"); @@ -232,33 +229,5 @@ namespace osu.Game.Overlays.Profile.Header Masking = true; } } - - private partial class UpdateableCountryText : OsuHoverContainer - { - - public FontUsage Font = default; - - [Resolved] - private RankingsOverlay? rankingsOverlay { get; set; } - - public UpdateableCountryText() - { - AutoSizeAxes = Axes.Both; - } - - public void load(CountryCode countryCode) - { - Action = () => - { - rankingsOverlay?.ShowCountry(countryCode); - }; - - Child = new OsuSpriteText - { - Font = Font, - Text = countryCode.GetDescription(), - }; - } - } } } From 4b0ee392f6d5bb6ec29a0c014fdc199543475e40 Mon Sep 17 00:00:00 2001 From: _ltn <46729135+rltn@users.noreply.github.com> Date: Fri, 28 Apr 2023 05:48:54 +0300 Subject: [PATCH 431/476] add OsuHoverContainer --- .../Profile/Header/TopHeaderContainer.cs | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs index 01fbf137d8..618e487dc3 100644 --- a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs @@ -33,6 +33,9 @@ namespace osu.Game.Overlays.Profile.Header [Resolved] private IAPIProvider api { get; set; } = null!; + [Resolved] + private RankingsOverlay? rankingsOverlay { get; set; } + private UserCoverBackground cover = null!; private SupporterIcon supporterTag = null!; private UpdateableAvatar avatar = null!; @@ -40,6 +43,8 @@ namespace osu.Game.Overlays.Profile.Header private ExternalLinkButton openUserExternally = null!; private OsuSpriteText titleText = null!; private UpdateableFlag userFlag = null!; + private OsuHoverContainer userCountryContainer = null!; + private OsuSpriteText userCountryText = null!; private GroupBadgeFlow groupBadgeFlow = null!; private ToggleCoverButton coverToggle = null!; @@ -157,13 +162,20 @@ namespace osu.Game.Overlays.Profile.Header Size = new Vector2(28, 20), ShowPlaceholderOnUnknown = false, }, - userCountryText = new UpdateableCountryText + userCountryContainer = new OsuHoverContainer { - Font = OsuFont.GetFont(size: 14f, weight: FontWeight.Regular), Margin = new MarginPadding { Left = 5 }, Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, - } + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + userCountryText = new OsuSpriteText + { + Font = OsuFont.GetFont(size: 14f, weight: FontWeight.Regular), + }, + }, + }, } }, } @@ -202,6 +214,8 @@ namespace osu.Game.Overlays.Profile.Header usernameText.Text = user?.Username ?? string.Empty; openUserExternally.Link = $@"{api.WebsiteRootUrl}/users/{user?.Id ?? 0}"; userFlag.CountryCode = user?.CountryCode ?? default; + userCountryText.Text = (user?.CountryCode ?? default).GetDescription(); + userCountryContainer.Action = () => rankingsOverlay?.ShowCountry(user?.CountryCode ?? default); supporterTag.SupportLevel = user?.SupportLevel ?? 0; titleText.Text = user?.Title ?? string.Empty; titleText.Colour = Color4Extensions.FromHex(user?.Colour ?? "fff"); From 6929be49b707568074899d822c4df0d3e053cc2d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 Apr 2023 22:35:55 +0900 Subject: [PATCH 432/476] Change condition for exclusive fullscreen notice to only show when using the correct renderer This avoids the notice showing when running on windows, but using the newer renderers (where the underlying logic hasn't been tested properly and can result in false-positives). Supersedes https://github.com/ppy/osu-framework/pull/5759 as a more correct implementation. --- osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 2765d2b437..a46205d40d 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -256,7 +256,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics return; } - if (host.Window is WindowsWindow) + if (host.Renderer is IWindowsRenderer) { switch (fullscreenCapability.Value) { From 2d6c0d2900be5779bc1ceea643458372c403daeb Mon Sep 17 00:00:00 2001 From: _ltn <46729135+rltn@users.noreply.github.com> Date: Fri, 28 Apr 2023 19:24:07 +0300 Subject: [PATCH 433/476] use of Child instead of Children --- .../Overlays/Profile/Header/TopHeaderContainer.cs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs index 618e487dc3..de678cb5d1 100644 --- a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs @@ -9,7 +9,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Containers; @@ -164,16 +163,13 @@ namespace osu.Game.Overlays.Profile.Header }, userCountryContainer = new OsuHoverContainer { - Margin = new MarginPadding { Left = 5 }, - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, AutoSizeAxes = Axes.Both, - Children = new Drawable[] + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Margin = new MarginPadding { Left = 5 }, + Child = userCountryText = new OsuSpriteText { - userCountryText = new OsuSpriteText - { - Font = OsuFont.GetFont(size: 14f, weight: FontWeight.Regular), - }, + Font = OsuFont.GetFont(size: 14f, weight: FontWeight.Regular), }, }, } From 607a04ae7319b823da095c948be9897767ff84bd Mon Sep 17 00:00:00 2001 From: Cootz Date: Fri, 28 Apr 2023 17:45:00 +0300 Subject: [PATCH 434/476] Fix the issue --- osu.Game/Screens/Edit/Editor.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index d89392f757..b5d304a031 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -210,7 +210,10 @@ namespace osu.Game.Screens.Edit // this is a bit haphazard, but guards against setting the lease Beatmap bindable if // the editor has already been exited. if (!ValidForPush) + { + beatmapManager.Delete(loadableBeatmap.BeatmapSetInfo); return; + } } try From c5357d30ab0c545a7eb4dd652eeefcb828c1f433 Mon Sep 17 00:00:00 2001 From: Cootz Date: Fri, 28 Apr 2023 20:36:31 +0300 Subject: [PATCH 435/476] Add test --- .../Navigation/TestSceneBeatmapEditor.cs | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditor.cs diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditor.cs b/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditor.cs new file mode 100644 index 0000000000..67835ed0f5 --- /dev/null +++ b/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditor.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; +using System.Linq; +using System.Threading.Tasks; +using DeepEqual.Syntax; +using NUnit.Framework; +using osu.Framework.Screens; +using osu.Game.Beatmaps; +using osu.Game.Screens.Edit; +using osu.Game.Screens.Menu; + +namespace osu.Game.Tests.Visual.Navigation +{ + public partial class TestSceneBeatmapEditor : OsuGameTestScene + { + [Test] + public void TestCancelNavigationToEditor() + { + BeatmapSetInfo[] beatmapSets = Array.Empty(); + + AddStep("Timestamp current beatmapsets", () => + { + Game.Realm.Run(realm => + { + beatmapSets = realm.All().Where(x => !x.DeletePending).ToArray(); + }); + }); + + AddStep("Open editor and close it while loading", () => + { + var task = Task.Run(async () => + { + await Task.Delay(100); + Game.ScreenStack.CurrentScreen.Exit(); + }); + + Game.ScreenStack.Push(new EditorLoader()); + }); + + AddUntilStep("wait for editor", () => Game.ScreenStack.CurrentScreen is MainMenu); + + BeatmapSetInfo[] currentSetInfos = Array.Empty(); + + AddStep("Get current beatmaps", () => + { + Game.Realm.Run(realm => + { + currentSetInfos = realm.All().Where(x => !x.DeletePending).ToArray(); + }); + }); + + AddAssert("dummy beatmap didn't appear", () => currentSetInfos.IsDeepEqual(beatmapSets)); + } + } +} From d9b3c97179d2f9fd5114909fcb323208ff630f22 Mon Sep 17 00:00:00 2001 From: Cootz Date: Fri, 28 Apr 2023 21:23:00 +0300 Subject: [PATCH 436/476] Fix testing --- .../Navigation/TestSceneBeatmapEditor.cs | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditor.cs b/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditor.cs index 67835ed0f5..9760fc2c97 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditor.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditor.cs @@ -1,9 +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 System.Threading.Tasks; using DeepEqual.Syntax; using NUnit.Framework; using osu.Framework.Screens; @@ -18,7 +16,7 @@ namespace osu.Game.Tests.Visual.Navigation [Test] public void TestCancelNavigationToEditor() { - BeatmapSetInfo[] beatmapSets = Array.Empty(); + BeatmapSetInfo[]? beatmapSets = null; AddStep("Timestamp current beatmapsets", () => { @@ -28,20 +26,26 @@ namespace osu.Game.Tests.Visual.Navigation }); }); - AddStep("Open editor and close it while loading", () => + AddStep("Set current beatmap to default", () => { - var task = Task.Run(async () => - { - await Task.Delay(100); - Game.ScreenStack.CurrentScreen.Exit(); - }); + Game.Beatmap.SetDefault(); + }); + AddStep("Open editor loader", () => + { Game.ScreenStack.Push(new EditorLoader()); }); + AddUntilStep("wait for editor", () => Game.ScreenStack.CurrentScreen is EditorLoader); + + AddStep("close editor while loading", () => + { + Game.ScreenStack.CurrentScreen.Exit(); + }); + AddUntilStep("wait for editor", () => Game.ScreenStack.CurrentScreen is MainMenu); - BeatmapSetInfo[] currentSetInfos = Array.Empty(); + BeatmapSetInfo[]? currentSetInfos = null; AddStep("Get current beatmaps", () => { @@ -51,7 +55,7 @@ namespace osu.Game.Tests.Visual.Navigation }); }); - AddAssert("dummy beatmap didn't appear", () => currentSetInfos.IsDeepEqual(beatmapSets)); + AddAssert("dummy beatmap didn't appear", () => currentSetInfos.IsDeepEqual(beatmapSets) && currentSetInfos is not null); } } } From 428b5fad3c45a545820d3fec8e06e9ce05803020 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 29 Apr 2023 10:34:50 +0900 Subject: [PATCH 437/476] Rename test scene to explicitly mention navigation testing --- ...ceneBeatmapEditor.cs => TestSceneBeatmapEditorNavigation.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename osu.Game.Tests/Visual/Navigation/{TestSceneBeatmapEditor.cs => TestSceneBeatmapEditorNavigation.cs} (96%) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditor.cs b/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs similarity index 96% rename from osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditor.cs rename to osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs index 9760fc2c97..e1fba1d630 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditor.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs @@ -11,7 +11,7 @@ using osu.Game.Screens.Menu; namespace osu.Game.Tests.Visual.Navigation { - public partial class TestSceneBeatmapEditor : OsuGameTestScene + public partial class TestSceneBeatmapEditorNavigation : OsuGameTestScene { [Test] public void TestCancelNavigationToEditor() From a6f01861124b3ba119c830c43178af95945864ad Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 29 Apr 2023 10:49:25 +0900 Subject: [PATCH 438/476] Improve legibility and code quality of new test --- .../TestSceneBeatmapEditorNavigation.cs | 48 ++++++------------- 1 file changed, 14 insertions(+), 34 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs index e1fba1d630..c76758e6c6 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs @@ -16,46 +16,26 @@ namespace osu.Game.Tests.Visual.Navigation [Test] public void TestCancelNavigationToEditor() { - BeatmapSetInfo[]? beatmapSets = null; + BeatmapSetInfo[] beatmapSets = null!; - AddStep("Timestamp current beatmapsets", () => + AddStep("Fetch initial beatmaps", () => { - Game.Realm.Run(realm => - { - beatmapSets = realm.All().Where(x => !x.DeletePending).ToArray(); - }); + beatmapSets = Game.Realm.Run(realm => realm.All().Where(x => !x.DeletePending).ToArray()); }); - AddStep("Set current beatmap to default", () => + AddStep("Set current beatmap to default", () => Game.Beatmap.SetDefault()); + + AddStep("Push editor loader", () => Game.ScreenStack.Push(new EditorLoader())); + AddUntilStep("Wait for loader current", () => Game.ScreenStack.CurrentScreen is EditorLoader); + AddStep("Close editor while loading", () => Game.ScreenStack.CurrentScreen.Exit()); + + AddUntilStep("Wait for menu", () => Game.ScreenStack.CurrentScreen is MainMenu); + + AddAssert("Check no new beatmaps were made", () => { - Game.Beatmap.SetDefault(); + var beatmapSetsAfter = Game.Realm.Run(realm => realm.All().Where(x => !x.DeletePending).ToArray()); + return beatmapSetsAfter.SequenceEqual(beatmapSets); }); - - AddStep("Open editor loader", () => - { - Game.ScreenStack.Push(new EditorLoader()); - }); - - AddUntilStep("wait for editor", () => Game.ScreenStack.CurrentScreen is EditorLoader); - - AddStep("close editor while loading", () => - { - Game.ScreenStack.CurrentScreen.Exit(); - }); - - AddUntilStep("wait for editor", () => Game.ScreenStack.CurrentScreen is MainMenu); - - BeatmapSetInfo[]? currentSetInfos = null; - - AddStep("Get current beatmaps", () => - { - Game.Realm.Run(realm => - { - currentSetInfos = realm.All().Where(x => !x.DeletePending).ToArray(); - }); - }); - - AddAssert("dummy beatmap didn't appear", () => currentSetInfos.IsDeepEqual(beatmapSets) && currentSetInfos is not null); } } } From 32f8c674f4d751937db57082cc872bc38a1c7bed Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 29 Apr 2023 11:01:29 +0900 Subject: [PATCH 439/476] Extract beatmap retrieval method for more legibility --- .../Navigation/TestSceneBeatmapEditorNavigation.cs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs index c76758e6c6..554da36cc4 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System.Linq; -using DeepEqual.Syntax; using NUnit.Framework; using osu.Framework.Screens; using osu.Game.Beatmaps; @@ -18,10 +17,7 @@ namespace osu.Game.Tests.Visual.Navigation { BeatmapSetInfo[] beatmapSets = null!; - AddStep("Fetch initial beatmaps", () => - { - beatmapSets = Game.Realm.Run(realm => realm.All().Where(x => !x.DeletePending).ToArray()); - }); + AddStep("Fetch initial beatmaps", () => beatmapSets = allBeatmapSets()); AddStep("Set current beatmap to default", () => Game.Beatmap.SetDefault()); @@ -30,12 +26,9 @@ namespace osu.Game.Tests.Visual.Navigation AddStep("Close editor while loading", () => Game.ScreenStack.CurrentScreen.Exit()); AddUntilStep("Wait for menu", () => Game.ScreenStack.CurrentScreen is MainMenu); + AddAssert("Check no new beatmaps were made", () => allBeatmapSets().SequenceEqual(beatmapSets)); - AddAssert("Check no new beatmaps were made", () => - { - var beatmapSetsAfter = Game.Realm.Run(realm => realm.All().Where(x => !x.DeletePending).ToArray()); - return beatmapSetsAfter.SequenceEqual(beatmapSets); - }); + BeatmapSetInfo[] allBeatmapSets() => Game.Realm.Run(realm => realm.All().Where(x => !x.DeletePending).ToArray()); } } } From 26431006448c9d192ac5931fc1fc3cf10f76c2a9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 29 Apr 2023 11:05:10 +0900 Subject: [PATCH 440/476] Add xmldoc to new test mentioning failure rate and general purpose --- .../Visual/Navigation/TestSceneBeatmapEditorNavigation.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs index 554da36cc4..603573058e 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs @@ -12,6 +12,14 @@ namespace osu.Game.Tests.Visual.Navigation { public partial class TestSceneBeatmapEditorNavigation : OsuGameTestScene { + /// + /// When entering the editor, a new beatmap is created as part of the asynchronous load process. + /// This test ensures that in the case of an early exit from the editor (ie. while it's still loading) + /// doesn't leave a dangling beatmap behind. + /// + /// This may not fail 100% due to timing, but has a pretty high chance of hitting a failure so works well enough + /// as a test. + /// [Test] public void TestCancelNavigationToEditor() { From b39a9d816e1881bd40e471aa738fe8d455828871 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 29 Apr 2023 16:05:45 +0900 Subject: [PATCH 441/476] Add basic structural requirements for cursor ripples --- .../Configuration/OsuRulesetConfigManager.cs | 2 ++ .../Legacy/OsuLegacySkinTransformer.cs | 5 +++++ .../UI/Cursor/CursorRippleVisualiser.cs | 21 +++++++++++++++++++ .../UI/Cursor/OsuCursorContainer.cs | 1 + .../UI/OsuSettingsSubsection.cs | 5 +++++ .../Localisation/RulesetSettingsStrings.cs | 5 +++++ 6 files changed, 39 insertions(+) create mode 100644 osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs diff --git a/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs b/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs index b8ad61e6dd..2056a50eda 100644 --- a/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs +++ b/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs @@ -22,6 +22,7 @@ namespace osu.Game.Rulesets.Osu.Configuration SetDefault(OsuRulesetSetting.SnakingInSliders, true); SetDefault(OsuRulesetSetting.SnakingOutSliders, true); SetDefault(OsuRulesetSetting.ShowCursorTrail, true); + SetDefault(OsuRulesetSetting.ShowCursorRipples, false); SetDefault(OsuRulesetSetting.PlayfieldBorderStyle, PlayfieldBorderStyle.None); } } @@ -31,6 +32,7 @@ namespace osu.Game.Rulesets.Osu.Configuration SnakingInSliders, SnakingOutSliders, ShowCursorTrail, + ShowCursorRipples, PlayfieldBorderStyle, } } diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs index 620540b8ef..279835747f 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs @@ -100,6 +100,11 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy return null; + case OsuSkinComponents.CursorRipple: + // TODO: resize texture to 0.5?? but that might break skins.. + if (GetTexture("cursor-ripple") != null) + return this.GetAnimation("cursor-ripple", false, false); + case OsuSkinComponents.CursorParticles: if (GetTexture("star2") != null) return new LegacyCursorParticles(); diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs new file mode 100644 index 0000000000..ef0cae7de8 --- /dev/null +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs @@ -0,0 +1,21 @@ +// 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.Containers; +using osu.Game.Rulesets.Osu.Configuration; + +namespace osu.Game.Rulesets.Osu.UI.Cursor +{ + public partial class CursorRippleVisualiser : CompositeDrawable + { + private readonly Bindable showRipples = new Bindable(true); + + [BackgroundDependencyLoader(true)] + private void load(OsuRulesetConfigManager rulesetConfig) + { + rulesetConfig?.BindWith(OsuRulesetSetting.ShowCursorTrail, showRipples); + } + } +} diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs index 5d7648b073..2b541bd345 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs @@ -48,6 +48,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor Children = new[] { cursorTrail = new SkinnableDrawable(new OsuSkinComponentLookup(OsuSkinComponents.CursorTrail), _ => new DefaultCursorTrail(), confineMode: ConfineMode.NoScaling), + new SkinnableDrawable(new OsuSkinComponentLookup(OsuSkinComponents.CursorRipples), confineMode: ConfineMode.NoScaling), new SkinnableDrawable(new OsuSkinComponentLookup(OsuSkinComponents.CursorParticles), confineMode: ConfineMode.NoScaling), } }; diff --git a/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs b/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs index 64c4e7eef6..0e410dbf57 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs @@ -43,6 +43,11 @@ namespace osu.Game.Rulesets.Osu.UI LabelText = RulesetSettingsStrings.CursorTrail, Current = config.GetBindable(OsuRulesetSetting.ShowCursorTrail) }, + new SettingsCheckbox + { + LabelText = RulesetSettingsStrings.CursorRipples, + Current = config.GetBindable(OsuRulesetSetting.ShowCursorRipples) + }, new SettingsEnumDropdown { LabelText = RulesetSettingsStrings.PlayfieldBorderStyle, diff --git a/osu.Game/Localisation/RulesetSettingsStrings.cs b/osu.Game/Localisation/RulesetSettingsStrings.cs index 1b0df6ecf6..52e6a5eaac 100644 --- a/osu.Game/Localisation/RulesetSettingsStrings.cs +++ b/osu.Game/Localisation/RulesetSettingsStrings.cs @@ -29,6 +29,11 @@ namespace osu.Game.Localisation /// public static LocalisableString CursorTrail => new TranslatableString(getKey(@"cursor_trail"), @"Cursor trail"); + /// + /// "Cursor ripples" + /// + public static LocalisableString CursorRipples => new TranslatableString(getKey(@"cursor_ripples"), @"Cursor ripples"); + /// /// "Playfield border style" /// From a4ae9e409bf1cb5e9c2f614e2967dc3bf82210b7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 29 Apr 2023 16:06:08 +0900 Subject: [PATCH 442/476] Implement ripples (legacy and default) --- .../TestSceneGameplayCursor.cs | 13 +++++ osu.Game.Rulesets.Osu/OsuSkinComponents.cs | 1 + .../Legacy/OsuLegacySkinTransformer.cs | 2 + .../UI/Cursor/CursorRippleVisualiser.cs | 53 +++++++++++++++++-- .../UI/Cursor/OsuCursorContainer.cs | 2 +- 5 files changed, 67 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs index 907422858e..c84a6ab70f 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs @@ -8,6 +8,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -18,6 +19,7 @@ using osu.Framework.Testing.Input; using osu.Framework.Utils; using osu.Game.Audio; using osu.Game.Configuration; +using osu.Game.Rulesets.Osu.Configuration; using osu.Game.Rulesets.Osu.Skinning; using osu.Game.Rulesets.Osu.UI.Cursor; using osu.Game.Screens.Play; @@ -40,6 +42,8 @@ namespace osu.Game.Rulesets.Osu.Tests private Drawable background; + private readonly Bindable ripples = new Bindable(); + public TestSceneGameplayCursor() { var ruleset = new OsuRuleset(); @@ -57,6 +61,8 @@ namespace osu.Game.Rulesets.Osu.Tests }); }); + AddToggleStep("ripples", v => ripples.Value = v); + AddSliderStep("circle size", 0f, 10f, 0f, val => { config.SetValue(OsuSetting.AutoCursorSize, true); @@ -67,6 +73,13 @@ namespace osu.Game.Rulesets.Osu.Tests AddStep("test cursor container", () => loadContent(false)); } + [BackgroundDependencyLoader] + private void load() + { + var rulesetConfig = (OsuRulesetConfigManager)RulesetConfigs.GetConfigFor(Ruleset.Value.CreateInstance()).AsNonNull(); + rulesetConfig.BindWith(OsuRulesetSetting.ShowCursorRipples, ripples); + } + [TestCase(1, 1)] [TestCase(5, 1)] [TestCase(10, 1)] diff --git a/osu.Game.Rulesets.Osu/OsuSkinComponents.cs b/osu.Game.Rulesets.Osu/OsuSkinComponents.cs index 8fdf3821fa..52fdfea95f 100644 --- a/osu.Game.Rulesets.Osu/OsuSkinComponents.cs +++ b/osu.Game.Rulesets.Osu/OsuSkinComponents.cs @@ -10,6 +10,7 @@ namespace osu.Game.Rulesets.Osu Cursor, CursorTrail, CursorParticles, + CursorRipple, SliderScorePoint, ReverseArrow, HitCircleText, diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs index 279835747f..bf817eda29 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs @@ -105,6 +105,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy if (GetTexture("cursor-ripple") != null) return this.GetAnimation("cursor-ripple", false, false); + return null; + case OsuSkinComponents.CursorParticles: if (GetTexture("star2") != null) return new LegacyCursorParticles(); diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs index ef0cae7de8..401525efcd 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs @@ -3,19 +3,66 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Game.Rulesets.Osu.Configuration; +using osu.Game.Rulesets.Osu.Skinning.Default; +using osu.Game.Skinning; +using osuTK; namespace osu.Game.Rulesets.Osu.UI.Cursor { - public partial class CursorRippleVisualiser : CompositeDrawable + public partial class CursorRippleVisualiser : CompositeDrawable, IKeyBindingHandler { private readonly Bindable showRipples = new Bindable(true); [BackgroundDependencyLoader(true)] - private void load(OsuRulesetConfigManager rulesetConfig) + private void load(OsuRulesetConfigManager? rulesetConfig) { - rulesetConfig?.BindWith(OsuRulesetSetting.ShowCursorTrail, showRipples); + rulesetConfig?.BindWith(OsuRulesetSetting.ShowCursorRipples, showRipples); + } + + public bool OnPressed(KeyBindingPressEvent e) + { + if (showRipples.Value) + { + var ripple = new SkinnableDrawable(new OsuSkinComponentLookup(OsuSkinComponents.CursorRipple), _ => new DefaultCursorRipple()) + { + Blending = BlendingParameters.Additive, + Position = e.MousePosition + }; + + AddInternal(ripple); + + ripple.ScaleTo(0.05f) + .ScaleTo(0.5f, 700, Easing.Out) + .FadeTo(0.2f) + .FadeOut(700) + .Expire(); + } + + return false; + } + + public void OnReleased(KeyBindingReleaseEvent e) + { + } + + public partial class DefaultCursorRipple : CompositeDrawable + { + [BackgroundDependencyLoader] + private void load() + { + InternalChildren = new Drawable[] + { + new RingPiece(3) + { + Size = new Vector2(512), + } + }; + } } } } diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs index 2b541bd345..35fb8e67d8 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs @@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor Children = new[] { cursorTrail = new SkinnableDrawable(new OsuSkinComponentLookup(OsuSkinComponents.CursorTrail), _ => new DefaultCursorTrail(), confineMode: ConfineMode.NoScaling), - new SkinnableDrawable(new OsuSkinComponentLookup(OsuSkinComponents.CursorRipples), confineMode: ConfineMode.NoScaling), + new CursorRippleVisualiser(), new SkinnableDrawable(new OsuSkinComponentLookup(OsuSkinComponents.CursorParticles), confineMode: ConfineMode.NoScaling), } }; From c994adfc22a3c7a25985988bd2b9f48ac47c3b58 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 29 Apr 2023 22:00:17 +0900 Subject: [PATCH 443/476] Add pooling support for ripples --- .../UI/Cursor/CursorRippleVisualiser.cs | 50 +++++++++++++------ 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs index 401525efcd..e13e0d5dfb 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Pooling; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Game.Rulesets.Osu.Configuration; @@ -18,6 +19,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor { private readonly Bindable showRipples = new Bindable(true); + private readonly DrawablePool ripplePool = new DrawablePool(20); + [BackgroundDependencyLoader(true)] private void load(OsuRulesetConfigManager? rulesetConfig) { @@ -27,21 +30,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor public bool OnPressed(KeyBindingPressEvent e) { if (showRipples.Value) - { - var ripple = new SkinnableDrawable(new OsuSkinComponentLookup(OsuSkinComponents.CursorRipple), _ => new DefaultCursorRipple()) - { - Blending = BlendingParameters.Additive, - Position = e.MousePosition - }; - - AddInternal(ripple); - - ripple.ScaleTo(0.05f) - .ScaleTo(0.5f, 700, Easing.Out) - .FadeTo(0.2f) - .FadeOut(700) - .Expire(); - } + AddInternal(ripplePool.Get()); return false; } @@ -50,6 +39,37 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor { } + private partial class CursorRipple : PoolableDrawable + { + [BackgroundDependencyLoader] + private void load() + { + AutoSizeAxes = Axes.Both; + Origin = Anchor.Centre; + + InternalChildren = new Drawable[] + { + new SkinnableDrawable(new OsuSkinComponentLookup(OsuSkinComponents.CursorRipple), _ => new DefaultCursorRipple()) + { + Blending = BlendingParameters.Additive, + } + }; + } + + protected override void PrepareForUse() + { + base.PrepareForUse(); + + ClearTransforms(true); + + this.ScaleTo(0.05f) + .ScaleTo(0.5f, 700, Easing.Out) + .FadeTo(0.2f) + .FadeOut(700) + .Expire(); + } + } + public partial class DefaultCursorRipple : CompositeDrawable { [BackgroundDependencyLoader] From 6a62949fcd36e4ee62fdfb8de528901784733cf6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 30 Apr 2023 10:38:21 +0900 Subject: [PATCH 444/476] Fix positioning and rewinding support for ripples --- .../UI/Cursor/CursorRippleVisualiser.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs index e13e0d5dfb..27e9d1d347 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs @@ -21,6 +21,11 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor private readonly DrawablePool ripplePool = new DrawablePool(20); + public CursorRippleVisualiser() + { + RelativeSizeAxes = Axes.Both; + } + [BackgroundDependencyLoader(true)] private void load(OsuRulesetConfigManager? rulesetConfig) { @@ -30,7 +35,12 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor public bool OnPressed(KeyBindingPressEvent e) { if (showRipples.Value) - AddInternal(ripplePool.Get()); + { + AddInternal(ripplePool.Get(r => + { + r.Position = e.MousePosition; + })); + } return false; } @@ -66,7 +76,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor .ScaleTo(0.5f, 700, Easing.Out) .FadeTo(0.2f) .FadeOut(700) - .Expire(); + .Expire(true); } } @@ -75,6 +85,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor [BackgroundDependencyLoader] private void load() { + AutoSizeAxes = Axes.Both; + InternalChildren = new Drawable[] { new RingPiece(3) From 72b472a75604a97d1f15ee211550bf28e2b4204c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 30 Apr 2023 11:19:03 +0900 Subject: [PATCH 445/476] Change default scaling and add note about legacy `cursor-ripple` scale --- .../Legacy/OsuLegacySkinTransformer.cs | 19 +++++++++++++++++-- .../UI/Cursor/CursorRippleVisualiser.cs | 8 ++++---- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs index bf817eda29..f049aa088f 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs @@ -101,9 +101,24 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy return null; case OsuSkinComponents.CursorRipple: - // TODO: resize texture to 0.5?? but that might break skins.. if (GetTexture("cursor-ripple") != null) - return this.GetAnimation("cursor-ripple", false, false); + { + var ripple = this.GetAnimation("cursor-ripple", false, false); + + // In stable this element was scaled down to 50% and opacity 20%, but this makes the elements WAY too big and inflexible. + // If anyone complains about these not being applied, this can be uncommented. + // + // But if no one complains I'd rather fix this in lazer. Wiki documentation doesn't mention size, + // so we might be okay. + // + // if (ripple != null) + // { + // ripple.Scale = new Vector2(0.5f); + // ripple.Alpha = 0.2f; + // } + + return ripple; + } return null; diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs index 27e9d1d347..465b708b5a 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs @@ -73,9 +73,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor ClearTransforms(true); this.ScaleTo(0.05f) - .ScaleTo(0.5f, 700, Easing.Out) - .FadeTo(0.2f) - .FadeOut(700) + .ScaleTo(1, 700, Easing.Out) + .FadeOutFromOne(700) .Expire(true); } } @@ -91,7 +90,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor { new RingPiece(3) { - Size = new Vector2(512), + Size = new Vector2(256), + Alpha = 0.2f, } }; } From d35355970f2aa40dbddb54dee6eacac225ad5f84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 30 Apr 2023 17:23:45 +0200 Subject: [PATCH 446/476] Add test case covering failure scenario --- .../TestSceneModSelectOverlay.cs | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 5ccaebd721..f99fe1d8d4 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -14,6 +14,7 @@ using osu.Framework.Localisation; using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays; using osu.Game.Overlays.Mods; using osu.Game.Overlays.Settings; using osu.Game.Rulesets; @@ -67,6 +68,19 @@ namespace osu.Game.Tests.Visual.UserInterface } } }); + r.Add(new ModPreset + { + Name = "Half Time 0.5x", + Description = "Very slow", + Ruleset = r.Find(OsuRuleset.SHORT_NAME), + Mods = new[] + { + new OsuModHalfTime + { + SpeedChange = { Value = 0.5 } + } + } + }); }); }); } @@ -566,6 +580,28 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("5 columns visible", () => this.ChildrenOfType().Count(col => col.IsPresent) == 5); } + [Test] + public void TestModMultiplierUpdates() + { + createScreen(); + + AddStep("select mod preset with half time", () => + { + InputManager.MoveMouseTo(this.ChildrenOfType().Single(preset => preset.Preset.Value.Name == "Half Time 0.5x")); + InputManager.Click(MouseButton.Left); + }); + AddAssert("difficulty multiplier display shows correct value", () => modSelectOverlay.ChildrenOfType().Single().Current.Value, () => Is.EqualTo(0.5)); + + // this is highly unorthodox in a test, but because the `ModSettingChangeTracker` machinery heavily leans on events and object disposal and re-creation, + // it is instrumental in the reproduction of the failure scenario that this test is supposed to cover. + AddStep("force collection", GC.Collect); + + AddStep("open customisation area", () => modSelectOverlay.CustomisationButton!.TriggerClick()); + AddStep("reset half time speed to default", () => modSelectOverlay.ChildrenOfType().Single() + .ChildrenOfType>().Single().TriggerClick()); + AddUntilStep("difficulty multiplier display shows correct value", () => modSelectOverlay.ChildrenOfType().Single().Current.Value, () => Is.EqualTo(0.7)); + } + private void waitForColumnLoad() => AddUntilStep("all column content loaded", () => modSelectOverlay.ChildrenOfType().Any() && modSelectOverlay.ChildrenOfType().All(column => column.IsLoaded && column.ItemsLoaded)); From 2e3daf0a541e14fb518292fabcc2ae8dde319e12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 30 Apr 2023 17:24:07 +0200 Subject: [PATCH 447/476] Fix leak of `ModSettingChangeTracker` instances The `SelectedMods.BindValueChanged()` callback in `ModSelectOverlay` can in some instances run recursively. This is most heavily leaned on in scenarios where `SelectedMods` is updated by an external component. In such cases, the mod select overlay needs to replace the mod instances received externally with mod instances which it owns, so that the changes made on the overlay can propagate outwards. This in particular means that prior to this commit, it was possible to encounter the following scenario: modSettingChangeTracker?.Dispose(); updateFromExternalSelection(); // mutates SelectedMods to perform the replacement // therefore causing a recursive call modSettingChangeTracker?.Dispose(); // inner call continues modSettingChangeTracker = new ModSettingChangeTracker(SelectedMods.Value); // outer call continues modSettingChangeTracker = new ModSettingChangeTracker(SelectedMods.Value); This leaks one `modSettingChangeTracker` instance from the inner call, which is never disposed. To avoid this, move the disposal to the same side of the recursion that the creation happens on, changing the call pattern to: updateFromExternalSelection(); // mutates SelectedMods to perform the replacement // therefore causing a recursive call modSettingChangeTracker?.Dispose(); // inner call continues modSettingChangeTracker = new ModSettingChangeTracker(SelectedMods.Value); modSettingChangeTracker?.Dispose(); // outer call continues modSettingChangeTracker = new ModSettingChangeTracker(SelectedMods.Value); which, while slightly wasteful, does not cause any leaks. The solution is definitely suboptimal, but addressing this properly would entail a major rewrite of the mod instance management in the mods overlay, which is probably not the wisest move to make right now. --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index d611fd3c0b..46d3620c9c 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -244,12 +244,12 @@ namespace osu.Game.Overlays.Mods SelectedMods.BindValueChanged(val => { - modSettingChangeTracker?.Dispose(); - updateMultiplier(); updateFromExternalSelection(); updateCustomisation(); + modSettingChangeTracker?.Dispose(); + if (AllowCustomisation) { modSettingChangeTracker = new ModSettingChangeTracker(SelectedMods.Value); From b8ae508639421a89dbfe0fadb1b433bdaa46d05f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 May 2023 13:09:00 +0900 Subject: [PATCH 448/476] Fix incorrect starting scale for ripples --- osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs index 465b708b5a..663c1f54fc 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs @@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor ClearTransforms(true); - this.ScaleTo(0.05f) + this.ScaleTo(0.1f) .ScaleTo(1, 700, Easing.Out) .FadeOutFromOne(700) .Expire(true); From 5cbfefbcb4d6177a4012527d28d53d56c13ffab0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 May 2023 13:18:04 +0900 Subject: [PATCH 449/476] Adjust metrics of default ripple to match stable default better --- osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs index 663c1f54fc..cee5574f06 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Pooling; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Game.Rulesets.Osu.Configuration; +using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Skinning.Default; using osu.Game.Skinning; using osuTK; @@ -90,8 +91,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor { new RingPiece(3) { - Size = new Vector2(256), - Alpha = 0.2f, + Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2), + Alpha = 0.1f, } }; } From 0a70734331f2ba319c74c9b909ddcca3f338a6a6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 May 2023 14:40:48 +0900 Subject: [PATCH 450/476] Adjust ripple size with cursor scale (including CS) --- .../UI/Cursor/CursorRippleVisualiser.cs | 18 +++++++++++------- .../UI/Cursor/OsuCursorContainer.cs | 5 ++++- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs index cee5574f06..076d97d06a 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs @@ -27,6 +27,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor RelativeSizeAxes = Axes.Both; } + public Vector2 CursorScale { get; set; } = Vector2.One; + [BackgroundDependencyLoader(true)] private void load(OsuRulesetConfigManager? rulesetConfig) { @@ -40,6 +42,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor AddInternal(ripplePool.Get(r => { r.Position = e.MousePosition; + r.Scale = CursorScale; })); } @@ -52,18 +55,17 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor private partial class CursorRipple : PoolableDrawable { + private Drawable ripple = null!; + [BackgroundDependencyLoader] private void load() { AutoSizeAxes = Axes.Both; Origin = Anchor.Centre; - InternalChildren = new Drawable[] + InternalChild = ripple = new SkinnableDrawable(new OsuSkinComponentLookup(OsuSkinComponents.CursorRipple), _ => new DefaultCursorRipple()) { - new SkinnableDrawable(new OsuSkinComponentLookup(OsuSkinComponents.CursorRipple), _ => new DefaultCursorRipple()) - { - Blending = BlendingParameters.Additive, - } + Blending = BlendingParameters.Additive, }; } @@ -73,8 +75,10 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor ClearTransforms(true); - this.ScaleTo(0.1f) - .ScaleTo(1, 700, Easing.Out) + ripple.ScaleTo(0.1f) + .ScaleTo(1, 700, Easing.Out); + + this .FadeOutFromOne(700) .Expire(true); } diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs index 35fb8e67d8..bf1ff872dd 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs @@ -40,6 +40,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor private Bindable userCursorScale; private Bindable autoCursorScale; + private readonly CursorRippleVisualiser rippleVisualiser; + public OsuCursorContainer() { InternalChild = fadeContainer = new Container @@ -48,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor Children = new[] { cursorTrail = new SkinnableDrawable(new OsuSkinComponentLookup(OsuSkinComponents.CursorTrail), _ => new DefaultCursorTrail(), confineMode: ConfineMode.NoScaling), - new CursorRippleVisualiser(), + rippleVisualiser = new CursorRippleVisualiser(), new SkinnableDrawable(new OsuSkinComponentLookup(OsuSkinComponents.CursorParticles), confineMode: ConfineMode.NoScaling), } }; @@ -83,6 +85,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor var newScale = new Vector2(e.NewValue); ActiveCursor.Scale = newScale; + rippleVisualiser.CursorScale = newScale; cursorTrail.Scale = newScale; }, true); From ff29189e8951945c6ef701159cc7636a75032d51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 1 May 2023 09:33:37 +0200 Subject: [PATCH 451/476] Add custom `cursor-ripple` to cover skinnability in test --- .../Resources/special-skin/cursor-ripple@2x.png | Bin 0 -> 2149 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/special-skin/cursor-ripple@2x.png diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/cursor-ripple@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/cursor-ripple@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..258162c4864ddc469ba21697770b2fc8e1297eb5 GIT binary patch literal 2149 zcmcgsX;4#V6us|}kS8dFF-#Ic5|_5rT2^f>iv$Bgv@GHZ1A?;2AVv@s6p|2B6e7xW zlxhVxtS~NA5Em3f6%wnCsC65ug_3cob&Xbu8uEHSrtPn`e>!7ka^Ac5o_o&u^4?@q zY;?p3hfxjyFhUU-76(A#Ed`7{UJ~=_Gq6~fC6~u4$>GtPQ{e0-z%ys3EC>aJhf-of7Z^qsf-otCubWW*_atCL+XG%{l}KK3qE zJJPQx;zpf&wKAq{&gx;c4|)bNUo~(mD5|PpyZGCvv!Ow4S#G-qHl-e(c&MYlBlW|i zt(K}s227|wKV7@3M&Kw8hQ>S7shd&{f=X2*z(Bu6#$MS$(MqiyF}RJ(ZDDev_IHcKYigqyWfn0mafQXJTG&9a-P zZ+ju;oVC05gOMGuHLv2Fd!j!x5Dnm|}@n!fi!HEkh|KDMDWe-yn9#<6jxP$&`zJdSWLeZrcAtD)9n?HxowT38fCiXBKhX zNTaMwKn(T~D?S%ZOFaLT+P>07WU6jubmo~!9E0V67oBR26PEa@8YfHiobTSJam)=<3+kH)j3YO6AWl_5pq4je%{>Id;MBdWWTQYP3+T7>3@7Dgwi+~={G(>p5BOWJ`rCSF zl>EZLb|b5G(9F7Ed)Zp6ny|Y^vuE%x`{8Fsb{QGBnf0HLakllD6B&zHd_iz*U>6Y& znOo~tC6A+%fRn=z4j)1%f_#m}YzH*oM+GvV4bH&G+AS1N zj3@-8K$&>Jai21TK?KvR_@KzzR0V#bM(d$o4wcb}jV@rKJ(;Bb} O@S_Ni4m&z6Mf(Thel05i literal 0 HcmV?d00001 From 1dc34ee25de83fd4c2bc73151bfd3fde607519d0 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Mon, 1 May 2023 15:48:54 +0200 Subject: [PATCH 452/476] fix infinite repeat count when adjusting length of 0 length slider --- .../Compose/Components/Timeline/TimelineHitObjectBlueprint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index 4e5087c004..50f941a1e5 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -414,7 +414,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline double lengthOfOneRepeat = repeatHitObject.Duration / (repeatHitObject.RepeatCount + 1); int proposedCount = Math.Max(0, (int)Math.Round(proposedDuration / lengthOfOneRepeat) - 1); - if (proposedCount == repeatHitObject.RepeatCount) + if (proposedCount == repeatHitObject.RepeatCount || lengthOfOneRepeat == 0) return; repeatHitObject.RepeatCount = proposedCount; From dbb2a8980b001c3ca97f31d3b7b9431ea1d30f7c Mon Sep 17 00:00:00 2001 From: OliBomby Date: Mon, 1 May 2023 15:56:23 +0200 Subject: [PATCH 453/476] add test --- .../TestSceneTimelineHitObjectBlueprint.cs | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneTimelineHitObjectBlueprint.cs b/osu.Game.Tests/Visual/Editing/TestSceneTimelineHitObjectBlueprint.cs index 709d796e97..932e45b1a6 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneTimelineHitObjectBlueprint.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneTimelineHitObjectBlueprint.cs @@ -77,5 +77,38 @@ namespace osu.Game.Tests.Visual.Editing AddAssert("object has non-zero duration", () => EditorBeatmap.HitObjects.OfType().Single().Duration > 0); } + + [Test] + public void TestDisallowRepeatsOnZeroDurationObjects() + { + DragArea dragArea; + + AddStep("add zero length slider", () => + { + EditorBeatmap.Clear(); + EditorBeatmap.Add(new Slider + { + Position = new Vector2(256, 256), + StartTime = 2700 + }); + }); + + AddStep("hold down drag bar", () => + { + // distinguishes between the actual drag bar and its "underlay shadow". + dragArea = this.ChildrenOfType().Single(bar => bar.HandlePositionalInput); + InputManager.MoveMouseTo(dragArea); + InputManager.PressButton(MouseButton.Left); + }); + + AddStep("try to extend drag bar", () => + { + var blueprint = this.ChildrenOfType().Single(); + InputManager.MoveMouseTo(blueprint.SelectionQuad.TopLeft + new Vector2(100, 0)); + InputManager.ReleaseButton(MouseButton.Left); + }); + + AddAssert("object has zero repeats", () => EditorBeatmap.HitObjects.OfType().Single().RepeatCount == 0); + } } } From 8ab3a87b13b7a7d0efce52d8db67b2e8af9f6a81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 1 May 2023 17:13:09 +0200 Subject: [PATCH 454/476] Add failing test case covering online ID reset on save This test scene passes at e58e1151f3c610d8019e1f1e408a1cb55d204c24 and fails at current master, due to an inadvertent regression caused by e72f103c1759e61c3afa7080f962c639265996c3. As it turns out, the online lookup flow that was causing UI thread freezes when saving beatmaps in the editor, was also responsible for resetting the online ID of locally-modified beatmaps if online lookup failed. --- ...TestSceneLocallyModifyingOnlineBeatmaps.cs | 56 +++++++++++++++++++ .../Tests/Visual/EditorSavingTestScene.cs | 27 ++++++++- 2 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 osu.Game.Tests/Visual/Editing/TestSceneLocallyModifyingOnlineBeatmaps.cs diff --git a/osu.Game.Tests/Visual/Editing/TestSceneLocallyModifyingOnlineBeatmaps.cs b/osu.Game.Tests/Visual/Editing/TestSceneLocallyModifyingOnlineBeatmaps.cs new file mode 100644 index 0000000000..be9bbcfdd8 --- /dev/null +++ b/osu.Game.Tests/Visual/Editing/TestSceneLocallyModifyingOnlineBeatmaps.cs @@ -0,0 +1,56 @@ +// 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 System.Net; +using NUnit.Framework; +using osu.Framework.Extensions; +using osu.Game.Database; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Tests.Resources; + +namespace osu.Game.Tests.Visual.Editing +{ + public partial class TestSceneLocallyModifyingOnlineBeatmaps : EditorSavingTestScene + { + private DummyAPIAccess dummyAPI => (DummyAPIAccess)API; + + public override void SetUpSteps() + { + CreateInitialBeatmap = () => + { + var importedSet = Game.BeatmapManager.Import(new ImportTask(TestResources.GetTestBeatmapForImport())).GetResultSafely(); + return Game.BeatmapManager.GetWorkingBeatmap(importedSet!.Value.Beatmaps.First()); + }; + + base.SetUpSteps(); + } + + [Test] + public void TestLocallyModifyingOnlineBeatmap() + { + AddAssert("editor beatmap has online ID", () => EditorBeatmap.BeatmapInfo.OnlineID, () => Is.GreaterThan(0)); + + AddStep("delete first hitobject", () => EditorBeatmap.RemoveAt(0)); + + AddStep("mock online lookup failure", () => + { + dummyAPI.HandleRequest = req => + { + if (req is GetBeatmapRequest) + { + req.TriggerFailure(new APIException("Beatmap not found", new WebException("NotFound"))); + return true; + } + + return false; + }; + }); + SaveEditor(); + + ReloadEditorToSameBeatmap(); + AddAssert("editor beatmap online ID reset", () => EditorBeatmap.BeatmapInfo.OnlineID, () => Is.EqualTo(-1)); + } + } +} diff --git a/osu.Game/Tests/Visual/EditorSavingTestScene.cs b/osu.Game/Tests/Visual/EditorSavingTestScene.cs index cd9e9e1d52..78188d7cf7 100644 --- a/osu.Game/Tests/Visual/EditorSavingTestScene.cs +++ b/osu.Game/Tests/Visual/EditorSavingTestScene.cs @@ -3,9 +3,12 @@ #nullable disable +using System; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Input; using osu.Framework.Testing; +using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit; using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Setup; @@ -24,18 +27,27 @@ namespace osu.Game.Tests.Visual protected EditorBeatmap EditorBeatmap => (EditorBeatmap)Editor.Dependencies.Get(typeof(EditorBeatmap)); + [CanBeNull] + protected Func CreateInitialBeatmap { get; set; } + [SetUpSteps] public override void SetUpSteps() { base.SetUpSteps(); - AddStep("set default beatmap", () => Game.Beatmap.SetDefault()); + if (CreateInitialBeatmap == null) + AddStep("set default beatmap", () => Game.Beatmap.SetDefault()); + else + { + AddStep("set test beatmap", () => Game.Beatmap.Value = CreateInitialBeatmap?.Invoke()); + } PushAndConfirm(() => new EditorLoader()); AddUntilStep("wait for editor load", () => Editor?.IsLoaded == true); - AddUntilStep("wait for metadata screen load", () => Editor.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); + if (CreateInitialBeatmap == null) + AddUntilStep("wait for metadata screen load", () => Editor.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); // We intentionally switch away from the metadata screen, else there is a feedback loop with the textbox handling which causes metadata changes below to get overwritten. @@ -50,6 +62,14 @@ namespace osu.Game.Tests.Visual protected void ReloadEditorToSameBeatmap() { + Guid beatmapSetGuid = Guid.Empty; + Guid beatmapGuid = Guid.Empty; + + AddStep("Store beatmap GUIDs", () => + { + beatmapSetGuid = EditorBeatmap.BeatmapInfo.BeatmapSet!.ID; + beatmapGuid = EditorBeatmap.BeatmapInfo.ID; + }); AddStep("Exit", () => InputManager.Key(Key.Escape)); AddUntilStep("Wait for main menu", () => Game.ScreenStack.CurrentScreen is MainMenu); @@ -59,7 +79,8 @@ namespace osu.Game.Tests.Visual PushAndConfirm(() => songSelect = new PlaySongSelect()); AddUntilStep("wait for carousel load", () => songSelect.BeatmapSetsLoaded); - AddUntilStep("Wait for beatmap selected", () => !Game.Beatmap.IsDefault); + AddStep("Present same beatmap", () => Game.PresentBeatmap(Game.BeatmapManager.QueryBeatmapSet(set => set.ID == beatmapSetGuid)!.Value, beatmap => beatmap.ID == beatmapGuid)); + AddUntilStep("Wait for beatmap selected", () => Game.Beatmap.Value.BeatmapInfo.ID == beatmapGuid); AddStep("Open options", () => InputManager.Key(Key.F3)); AddStep("Enter editor", () => InputManager.Key(Key.Number5)); From f470b2c9cc379b24be498b4865f0f82f6fa41a7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 1 May 2023 17:24:58 +0200 Subject: [PATCH 455/476] Always reset online info when saving local beatmap --- osu.Game/Beatmaps/BeatmapManager.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 6af6a25579..3f2ab9b391 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -440,6 +440,7 @@ namespace osu.Game.Beatmaps beatmapInfo.LastLocalUpdate = DateTimeOffset.Now; beatmapInfo.Status = BeatmapOnlineStatus.LocallyModified; + beatmapInfo.ResetOnlineInfo(); AddFile(setInfo, stream, createBeatmapFilenameFromMetadata(beatmapInfo)); From 1fb4c814f4446eefa768e9f2f0f73af0334c64f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 1 May 2023 17:31:55 +0200 Subject: [PATCH 456/476] Remove no longer needed API call mocking The online ID will be reset unconditionally after any local change is made to any beatmap. That behaviour no longer depends on online lookups succeeding or failing. This may change at a later date when beatmap submission is integrated into lazer - the idea is that online IDs would get re-populated on local beatmaps once they are submitted to web. --- ...TestSceneLocallyModifyingOnlineBeatmaps.cs | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneLocallyModifyingOnlineBeatmaps.cs b/osu.Game.Tests/Visual/Editing/TestSceneLocallyModifyingOnlineBeatmaps.cs index be9bbcfdd8..7f9a69833c 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneLocallyModifyingOnlineBeatmaps.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneLocallyModifyingOnlineBeatmaps.cs @@ -2,20 +2,15 @@ // See the LICENCE file in the repository root for full licence text. using System.Linq; -using System.Net; using NUnit.Framework; using osu.Framework.Extensions; using osu.Game.Database; -using osu.Game.Online.API; -using osu.Game.Online.API.Requests; using osu.Game.Tests.Resources; namespace osu.Game.Tests.Visual.Editing { public partial class TestSceneLocallyModifyingOnlineBeatmaps : EditorSavingTestScene { - private DummyAPIAccess dummyAPI => (DummyAPIAccess)API; - public override void SetUpSteps() { CreateInitialBeatmap = () => @@ -33,20 +28,6 @@ namespace osu.Game.Tests.Visual.Editing AddAssert("editor beatmap has online ID", () => EditorBeatmap.BeatmapInfo.OnlineID, () => Is.GreaterThan(0)); AddStep("delete first hitobject", () => EditorBeatmap.RemoveAt(0)); - - AddStep("mock online lookup failure", () => - { - dummyAPI.HandleRequest = req => - { - if (req is GetBeatmapRequest) - { - req.TriggerFailure(new APIException("Beatmap not found", new WebException("NotFound"))); - return true; - } - - return false; - }; - }); SaveEditor(); ReloadEditorToSameBeatmap(); From cf5211aec922852673c77dcedae081c89ca005e1 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Mon, 1 May 2023 19:22:52 +0200 Subject: [PATCH 457/476] Enable current distance snap when exactly on a hit object --- osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs index aa47b4f424..09c6af3820 100644 --- a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs @@ -93,7 +93,7 @@ namespace osu.Game.Rulesets.Edit private (HitObject before, HitObject after)? getObjectsOnEitherSideOfCurrentTime() { - HitObject lastBefore = Playfield.HitObjectContainer.AliveObjects.LastOrDefault(h => h.HitObject.StartTime <= EditorClock.CurrentTime)?.HitObject; + HitObject lastBefore = Playfield.HitObjectContainer.AliveObjects.LastOrDefault(h => h.HitObject.StartTime < EditorClock.CurrentTime)?.HitObject; if (lastBefore == null) return null; From 436ebdcfcb1d556c6bad05dea472aa9f4f41959a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 1 May 2023 19:06:25 +0200 Subject: [PATCH 458/476] Fix beatmap leaderboard test failure Because the online info reset (which includes online ID reset) was happening after encoding, `TestSceneBeatmapLeaderboard.TestLocalScoresDisplayOnBeatmapEdit()` started failing, as the hash no longer matched expectations after the first save of the map. --- osu.Game/Beatmaps/BeatmapManager.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 3f2ab9b391..ae62564b0d 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -415,6 +415,13 @@ namespace osu.Game.Beatmaps // All changes to metadata are made in the provided beatmapInfo, so this should be copied to the `IBeatmap` before encoding. beatmapContent.BeatmapInfo = beatmapInfo; + // Since now this is a locally-modified beatmap, we also set all relevant flags to indicate this. + // Importantly, the `ResetOnlineInfo()` call must happen before encoding, as online ID is encoded into the `.osu` file, + // which influences the beatmap checksums. + beatmapInfo.LastLocalUpdate = DateTimeOffset.Now; + beatmapInfo.Status = BeatmapOnlineStatus.LocallyModified; + beatmapInfo.ResetOnlineInfo(); + using (var stream = new MemoryStream()) { using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) @@ -438,10 +445,6 @@ namespace osu.Game.Beatmaps beatmapInfo.MD5Hash = stream.ComputeMD5Hash(); beatmapInfo.Hash = stream.ComputeSHA2Hash(); - beatmapInfo.LastLocalUpdate = DateTimeOffset.Now; - beatmapInfo.Status = BeatmapOnlineStatus.LocallyModified; - beatmapInfo.ResetOnlineInfo(); - AddFile(setInfo, stream, createBeatmapFilenameFromMetadata(beatmapInfo)); updateHashAndMarkDirty(setInfo); From 87db89114368d55a4029ff45109a0a9b9002c268 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 May 2023 13:12:08 +0900 Subject: [PATCH 459/476] Adjust test to reliabily fail --- .../Visual/Editing/TestSceneTimelineHitObjectBlueprint.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneTimelineHitObjectBlueprint.cs b/osu.Game.Tests/Visual/Editing/TestSceneTimelineHitObjectBlueprint.cs index 932e45b1a6..08e036248b 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneTimelineHitObjectBlueprint.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneTimelineHitObjectBlueprint.cs @@ -105,9 +105,10 @@ namespace osu.Game.Tests.Visual.Editing { var blueprint = this.ChildrenOfType().Single(); InputManager.MoveMouseTo(blueprint.SelectionQuad.TopLeft + new Vector2(100, 0)); - InputManager.ReleaseButton(MouseButton.Left); }); + AddStep("release button", () => InputManager.PressButton(MouseButton.Left)); + AddAssert("object has zero repeats", () => EditorBeatmap.HitObjects.OfType().Single().RepeatCount == 0); } } From 63890ef6feb9b8fffd28152545aaf88d0aefe9ee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 May 2023 13:24:19 +0900 Subject: [PATCH 460/476] Fix audio offset tooltip potentially showing "-0 ms" Closes https://github.com/ppy/osu/issues/23339. --- osu.Game/Graphics/UserInterface/TimeSlider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/TimeSlider.cs b/osu.Game/Graphics/UserInterface/TimeSlider.cs index e4058827f3..e6e7ae9305 100644 --- a/osu.Game/Graphics/UserInterface/TimeSlider.cs +++ b/osu.Game/Graphics/UserInterface/TimeSlider.cs @@ -12,6 +12,6 @@ namespace osu.Game.Graphics.UserInterface /// public partial class TimeSlider : RoundedSliderBar { - public override LocalisableString TooltipText => $"{Current.Value:N0} ms"; + public override LocalisableString TooltipText => $"{base.TooltipText} ms"; } } From e808e7316b320c3b9f4105ed2f79081bf8203283 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 May 2023 13:29:30 +0900 Subject: [PATCH 461/476] Mark delegate value unused and add comment to avoid future regression --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 46d3620c9c..38ae8c68cb 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -242,7 +242,7 @@ namespace osu.Game.Overlays.Mods if (AllowCustomisation) ((IBindable>)modSettingsArea.SelectedMods).BindTo(SelectedMods); - SelectedMods.BindValueChanged(val => + SelectedMods.BindValueChanged(_ => { updateMultiplier(); updateFromExternalSelection(); @@ -252,6 +252,10 @@ namespace osu.Game.Overlays.Mods if (AllowCustomisation) { + // Importantly, use SelectedMods.Value here (and not the ValueChanged NewValue) as the latter can + // potentially be stale, due to complexities in the way change trackers work. + // + // See https://github.com/ppy/osu/pull/23284#issuecomment-1529056988 modSettingChangeTracker = new ModSettingChangeTracker(SelectedMods.Value); modSettingChangeTracker.SettingChanged += _ => updateMultiplier(); } From 37a5dde8592c48edebdc3ffaa36da96da75d226c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 May 2023 13:47:49 +0900 Subject: [PATCH 462/476] Fix `BeatmapAttributeText` not supporting unicode artist/title --- osu.Game/Skinning/Components/BeatmapAttributeText.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/Components/BeatmapAttributeText.cs b/osu.Game/Skinning/Components/BeatmapAttributeText.cs index 2c16a67cac..6523039a3f 100644 --- a/osu.Game/Skinning/Components/BeatmapAttributeText.cs +++ b/osu.Game/Skinning/Components/BeatmapAttributeText.cs @@ -84,8 +84,8 @@ namespace osu.Game.Skinning.Components private void updateBeatmapContent(WorkingBeatmap workingBeatmap) { - valueDictionary[BeatmapAttribute.Title] = workingBeatmap.BeatmapInfo.Metadata.Title; - valueDictionary[BeatmapAttribute.Artist] = workingBeatmap.BeatmapInfo.Metadata.Artist; + valueDictionary[BeatmapAttribute.Title] = new RomanisableString(workingBeatmap.BeatmapInfo.Metadata.TitleUnicode, workingBeatmap.BeatmapInfo.Metadata.Title); + valueDictionary[BeatmapAttribute.Artist] = new RomanisableString(workingBeatmap.BeatmapInfo.Metadata.ArtistUnicode, workingBeatmap.BeatmapInfo.Metadata.Artist); valueDictionary[BeatmapAttribute.DifficultyName] = workingBeatmap.BeatmapInfo.DifficultyName; valueDictionary[BeatmapAttribute.Creator] = workingBeatmap.BeatmapInfo.Metadata.Author.Username; valueDictionary[BeatmapAttribute.Length] = TimeSpan.FromMilliseconds(workingBeatmap.BeatmapInfo.Length).ToFormattedDuration(); From ad40099e3280d0929ce13b2adca488354bef9f81 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 May 2023 14:00:52 +0900 Subject: [PATCH 463/476] Ensure negative sign is only applied when the post-rounded result is negative --- osu.Game/Graphics/UserInterface/OsuSliderBar.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs index 0c36d73085..0e26029ffa 100644 --- a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs +++ b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs @@ -97,7 +97,9 @@ namespace osu.Game.Graphics.UserInterface // Find the number of significant digits (we could have less than 5 after normalize()) int significantDigits = FormatUtils.FindPrecision(decimalPrecision); - return floatValue.ToString($"N{significantDigits}"); + string negativeSign = Math.Round(floatValue, significantDigits) < 0 ? "-" : string.Empty; + + return $"{negativeSign}{Math.Abs(floatValue).ToString($"N{significantDigits}")}"; } /// From 736be6a73ba2c9ff5332b39f828f0310a8d76851 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 May 2023 14:11:16 +0900 Subject: [PATCH 464/476] Refactor slightly for readability --- osu.Game/Overlays/Music/PlaylistItem.cs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/osu.Game/Overlays/Music/PlaylistItem.cs b/osu.Game/Overlays/Music/PlaylistItem.cs index 4a39cc06c8..90fdfd0491 100644 --- a/osu.Game/Overlays/Music/PlaylistItem.cs +++ b/osu.Game/Overlays/Music/PlaylistItem.cs @@ -56,7 +56,7 @@ namespace osu.Game.Overlays.Music var artist = new RomanisableString(metadata.ArtistUnicode, metadata.Artist); titlePart = text.AddText(title, sprite => sprite.Font = OsuFont.GetFont(weight: FontWeight.Regular)); - titlePart.DrawablePartsRecreated += _ => updateSelectionState(SelectedSet.Value, instant: true); + titlePart.DrawablePartsRecreated += _ => updateSelectionState(SelectedSet.Value, applyImmediately: true); text.AddText(@" "); // to separate the title from the artist. text.AddText(artist, sprite => @@ -66,24 +66,25 @@ namespace osu.Game.Overlays.Music sprite.Padding = new MarginPadding { Top = 1 }; }); - SelectedSet.BindValueChanged(set => updateSelectionState(set.NewValue, instant: false)); - updateSelectionState(SelectedSet.Value, instant: true); + SelectedSet.BindValueChanged(set => updateSelectionState(set.NewValue)); + updateSelectionState(SelectedSet.Value, applyImmediately: true); }); } private bool selected; - private void updateSelectionState(Live selectedSet, bool instant) + private void updateSelectionState(Live selectedSet, bool applyImmediately = false) { - bool newSelected = selectedSet?.Equals(Model) == true; + bool wasSelected = selected; + selected = selectedSet?.Equals(Model) == true; - if (newSelected == selected && !instant) // instant updates should forcibly set correct state regardless of previous state. + // Immediate updates should forcibly set correct state regardless of previous state. + // This ensures that the initial state is correctly applied. + if (wasSelected == selected && !applyImmediately) return; - selected = newSelected; - foreach (Drawable s in titlePart.Drawables) - s.FadeColour(selected ? colours.Yellow : Color4.White, instant ? 0 : FADE_DURATION); + s.FadeColour(selected ? colours.Yellow : Color4.White, applyImmediately ? 0 : FADE_DURATION); } protected override Drawable CreateContent() => new DelayedLoadWrapper(text = new OsuTextFlowContainer From 7a840e1d8227a3e2dd5073afd27305c47dc7f494 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 May 2023 14:29:26 +0900 Subject: [PATCH 465/476] Remove unnecessary `TransferValue` spec --- osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 0515e8dc97..ad47039579 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -171,7 +171,6 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics Current = scalingBackgroundDim, KeyboardStep = 0.01f, DisplayAsPercentage = true, - TransferValueOnCommit = false }, } }, From ab4f66fad331aa7d048f8f1365ec12f25071098b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 May 2023 14:31:39 +0900 Subject: [PATCH 466/476] Minor readability refactors --- osu.Game/Graphics/Containers/ScalingContainer.cs | 2 +- .../Settings/Sections/Graphics/LayoutSettings.cs | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/osu.Game/Graphics/Containers/ScalingContainer.cs b/osu.Game/Graphics/Containers/ScalingContainer.cs index bd52c8bb32..84eb382d34 100644 --- a/osu.Game/Graphics/Containers/ScalingContainer.cs +++ b/osu.Game/Graphics/Containers/ScalingContainer.cs @@ -155,7 +155,7 @@ namespace osu.Game.Graphics.Containers private bool requiresBackgroundVisible => (scalingMode.Value == ScalingMode.Everything || scalingMode.Value == ScalingMode.ExcludeOverlays) && (sizeX.Value != 1 || sizeY.Value != 1) - && scalingMenuBackgroundDim.Value != 1f; + && scalingMenuBackgroundDim.Value < 1; private void updateSize() { diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index ad47039579..2e26d15105 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -230,11 +230,12 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics if (s == dimSlider) { s.CanBeShown.Value = scalingMode.Value == ScalingMode.Everything || scalingMode.Value == ScalingMode.ExcludeOverlays; - return; } - - s.TransferValueOnCommit = scalingMode.Value == ScalingMode.Everything; - s.CanBeShown.Value = scalingMode.Value != ScalingMode.Off; + else + { + s.TransferValueOnCommit = scalingMode.Value == ScalingMode.Everything; + s.CanBeShown.Value = scalingMode.Value != ScalingMode.Off; + } }); } } From 57f48e070359f65100e33838091ee72f28d6cc68 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 May 2023 14:33:57 +0900 Subject: [PATCH 467/476] Start colour black to ensure initial appear transition doesn't look silly --- osu.Game/Graphics/Containers/ScalingContainer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Graphics/Containers/ScalingContainer.cs b/osu.Game/Graphics/Containers/ScalingContainer.cs index 84eb382d34..c47aba2f0c 100644 --- a/osu.Game/Graphics/Containers/ScalingContainer.cs +++ b/osu.Game/Graphics/Containers/ScalingContainer.cs @@ -15,6 +15,7 @@ using osu.Game.Configuration; using osu.Game.Screens; using osu.Game.Screens.Backgrounds; using osuTK; +using osuTK.Graphics; namespace osu.Game.Graphics.Containers { @@ -169,6 +170,7 @@ namespace osu.Game.Graphics.Containers AddInternal(backgroundStack = new BackgroundScreenStack { Alpha = 0, + Colour = Color4.Black, Depth = float.MaxValue }); From 67f83f246b1c65c62416a24375ef580139d046dc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 May 2023 15:36:01 +0900 Subject: [PATCH 468/476] Add more padding around playfield in editor to avoid overlap with tool areas Closes #23130. --- osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs | 9 +++++++-- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 12 ++++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 7a70257f3a..ff1e208186 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -13,8 +13,8 @@ using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Framework.Utils; using osu.Framework.Input.Events; +using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; @@ -62,7 +62,12 @@ namespace osu.Game.Rulesets.Osu.Edit private void load() { // Give a bit of breathing room around the playfield content. - PlayfieldContentContainer.Padding = new MarginPadding(10); + PlayfieldContentContainer.Padding = new MarginPadding + { + Vertical = 10, + Left = TOOLBOX_CONTRACTED_SIZE_LEFT + 10, + Right = TOOLBOX_CONTRACTED_SIZE_RIGHT + 10, + }; LayerBelowRuleset.AddRange(new Drawable[] { diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 653861c11c..e2dbd2acdc 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -116,6 +116,11 @@ namespace osu.Game.Rulesets.Edit PlayfieldContentContainer = new Container { Name = "Content", + Padding = new MarginPadding + { + Left = TOOLBOX_CONTRACTED_SIZE_LEFT, + Right = TOOLBOX_CONTRACTED_SIZE_RIGHT, + }, RelativeSizeAxes = Axes.Both, Children = new Drawable[] { @@ -138,7 +143,7 @@ namespace osu.Game.Rulesets.Edit Colour = colourProvider.Background5, RelativeSizeAxes = Axes.Both, }, - LeftToolbox = new ExpandingToolboxContainer(60, 200) + LeftToolbox = new ExpandingToolboxContainer(TOOLBOX_CONTRACTED_SIZE_LEFT, 200) { Children = new Drawable[] { @@ -173,7 +178,7 @@ namespace osu.Game.Rulesets.Edit Colour = colourProvider.Background5, RelativeSizeAxes = Axes.Both, }, - RightToolbox = new ExpandingToolboxContainer(130, 250) + RightToolbox = new ExpandingToolboxContainer(TOOLBOX_CONTRACTED_SIZE_RIGHT, 250) { Child = new EditorToolboxGroup("inspector") { @@ -450,6 +455,9 @@ namespace osu.Game.Rulesets.Edit [Cached] public abstract partial class HitObjectComposer : CompositeDrawable, IPositionSnapProvider { + public const float TOOLBOX_CONTRACTED_SIZE_LEFT = 60; + public const float TOOLBOX_CONTRACTED_SIZE_RIGHT = 130; + public readonly Ruleset Ruleset; protected HitObjectComposer(Ruleset ruleset) From bd72c67d68aa3bc4bc18f70cd0fd6a4c8c9eb9d2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 May 2023 15:47:35 +0900 Subject: [PATCH 469/476] Increase the rate of slider ball fade on argon skins to match other implementations --- osu.Game.Rulesets.Osu/Skinning/Argon/ArgonSliderBall.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonSliderBall.cs b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonSliderBall.cs index d6ce793c7e..461b4a3b45 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonSliderBall.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonSliderBall.cs @@ -98,7 +98,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime)) { - this.FadeOut(duration, Easing.OutQuint); + // intentionally pile on an extra FadeOut to make it happen much faster + this.FadeOut(duration / 4, Easing.OutQuint); icon.ScaleTo(defaultIconScale * icon_scale, duration, Easing.OutQuint); } } From 414b80d44e78ee5495aa18ae3afd7661b9fe32a7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 May 2023 17:00:54 +0900 Subject: [PATCH 470/476] Change flashlight depth in a more standard way --- osu.Game/Rulesets/Mods/ModFlashlight.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModFlashlight.cs b/osu.Game/Rulesets/Mods/ModFlashlight.cs index 9527e8ab5d..215fc877dc 100644 --- a/osu.Game/Rulesets/Mods/ModFlashlight.cs +++ b/osu.Game/Rulesets/Mods/ModFlashlight.cs @@ -82,12 +82,12 @@ namespace osu.Game.Rulesets.Mods flashlight.RelativeSizeAxes = Axes.Both; flashlight.Colour = Color4.Black; + // Flashlight mods should always draw above any other mod adding overlays. + flashlight.Depth = float.MinValue; flashlight.Combo.BindTo(Combo); drawableRuleset.Overlays.Add(flashlight); - // Stop flashlight from being drawn underneath other mods that generate HitObjects. - drawableRuleset.Overlays.ChangeChildDepth(flashlight, float.MinValue); } protected abstract Flashlight CreateFlashlight(); From fb0e90913db89e8ffc80fc17056d8ed1f4cbfecc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 May 2023 17:07:12 +0900 Subject: [PATCH 471/476] Ensure lifetime start is also updated when reverting judgements --- osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs index 0fc27c8f1d..bfc6c24fa5 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs @@ -111,7 +111,7 @@ namespace osu.Game.Rulesets.Osu.Mods BubbleDrawable? lastBubble = bubbleContainer.OfType().LastOrDefault(); lastBubble?.ClearTransforms(); - lastBubble?.Expire(); + lastBubble?.Expire(true); }; } From 7830711c8e97b83852f705dbad25d50cc71a1604 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 May 2023 17:07:22 +0900 Subject: [PATCH 472/476] Tidy up various code quality issues in `OsuModBubbles` --- osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 22 ++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs index bfc6c24fa5..9ae38de7c9 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs @@ -165,15 +165,15 @@ namespace osu.Game.Rulesets.Osu.Mods ColourInfo colourDarker = DrawableOsuHitObject.AccentColour.Value.Darken(0.1f); // The absolute length of the bubble's animation, can be used in fractions for animations of partial length - double getAnimationDuration = 1700 + Math.Pow(FadeTime, 1.07f); + double duration = 1700 + Math.Pow(FadeTime, 1.07f); // Main bubble scaling based on combo this.FadeTo(1) - .ScaleTo(MaxSize, getAnimationDuration * 0.8f) + .ScaleTo(MaxSize, duration * 0.8f) .Then() // Pop at the end of the bubbles life time - .ScaleTo(MaxSize * 1.5f, getAnimationDuration * 0.2f, Easing.OutQuint) - .FadeOut(getAnimationDuration * 0.2f, Easing.OutCirc).Expire(); + .ScaleTo(MaxSize * 1.5f, duration * 0.2f, Easing.OutQuint) + .FadeOut(duration * 0.2f, Easing.OutCirc).Expire(); if (!DrawableOsuHitObject.IsHit) return; @@ -182,28 +182,28 @@ namespace osu.Game.Rulesets.Osu.Mods colourBox.FadeColour(colourDarker); - content.TransformTo(nameof(BorderColour), colourDarker, getAnimationDuration * 0.3f, Easing.OutQuint); + content.TransformTo(nameof(BorderColour), colourDarker, duration * 0.3f, Easing.OutQuint); // Ripple effect utilises the border to reduce drawable count - content.TransformTo(nameof(BorderThickness), 2f, getAnimationDuration * 0.3f, Easing.OutQuint) + content.TransformTo(nameof(BorderThickness), 2f, duration * 0.3f, Easing.OutQuint) // Avoids transparency overlap issues during the bubble "pop" .Then().Schedule(() => content.BorderThickness = 0); } - private Vector2 getPosition(DrawableOsuHitObject drawableOsuHitObject) + private Vector2 getPosition(DrawableOsuHitObject drawableObject) { - switch (drawableOsuHitObject) + switch (drawableObject) { // SliderHeads are derived from HitCircles, // so we must handle them before to avoid them using the wrong positioning logic case DrawableSliderHead: - return drawableOsuHitObject.HitObject.Position; + return drawableObject.HitObject.Position; // Using hitobject position will cause issues with HitCircle placement due to stack leniency. case DrawableHitCircle: - return drawableOsuHitObject.Position; + return drawableObject.Position; default: - return drawableOsuHitObject.HitObject.Position; + return drawableObject.HitObject.Position; } } } From e44672bdd5f4f11ae0d309c20df29271d4203f05 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 May 2023 17:08:49 +0900 Subject: [PATCH 473/476] Avoid using `Schedule` in transforms (doesn't handle rewind well) --- osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs index 9ae38de7c9..12e2090f89 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs @@ -185,8 +185,9 @@ namespace osu.Game.Rulesets.Osu.Mods content.TransformTo(nameof(BorderColour), colourDarker, duration * 0.3f, Easing.OutQuint); // Ripple effect utilises the border to reduce drawable count content.TransformTo(nameof(BorderThickness), 2f, duration * 0.3f, Easing.OutQuint) + .Then() // Avoids transparency overlap issues during the bubble "pop" - .Then().Schedule(() => content.BorderThickness = 0); + .TransformTo(nameof(BorderThickness), 0f); } private Vector2 getPosition(DrawableOsuHitObject drawableObject) From 8160d562640f4c3f40d8d4b01a68306f8177e31c Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 2 May 2023 11:51:05 +0300 Subject: [PATCH 474/476] Update test shaders --- .../Resources/Shaders/sh_TestFragment.fs | 15 ++++--- .../Resources/Shaders/sh_TestVertex.vs | 42 ++++++++----------- 2 files changed, 27 insertions(+), 30 deletions(-) diff --git a/osu.Game.Tests/Resources/Shaders/sh_TestFragment.fs b/osu.Game.Tests/Resources/Shaders/sh_TestFragment.fs index c70ad751be..99b85d0502 100644 --- a/osu.Game.Tests/Resources/Shaders/sh_TestFragment.fs +++ b/osu.Game.Tests/Resources/Shaders/sh_TestFragment.fs @@ -1,11 +1,14 @@ -#include "sh_Utils.h" +#define HIGH_PRECISION_VERTEX -varying mediump vec2 v_TexCoord; -varying mediump vec4 v_TexRect; +#include "sh_Utils.h" +#include "sh_Masking.h" + +layout(location = 2) in highp vec2 v_TexCoord; + +layout(location = 0) out vec4 o_Colour; void main(void) { - float hueValue = v_TexCoord.x / (v_TexRect[2] - v_TexRect[0]); - gl_FragColor = hsv2rgb(vec4(hueValue, 1, 1, 1)); + highp float hueValue = v_TexCoord.x / (v_TexRect[2] - v_TexRect[0]); + o_Colour = getRoundedColor(hsv2rgb(vec4(hueValue, 1, 1, 1)), v_TexCoord); } - diff --git a/osu.Game.Tests/Resources/Shaders/sh_TestVertex.vs b/osu.Game.Tests/Resources/Shaders/sh_TestVertex.vs index 4485356fa4..505554bb33 100644 --- a/osu.Game.Tests/Resources/Shaders/sh_TestVertex.vs +++ b/osu.Game.Tests/Resources/Shaders/sh_TestVertex.vs @@ -1,31 +1,25 @@ -#include "sh_Utils.h" +layout(location = 0) in highp vec2 m_Position; +layout(location = 1) in lowp vec4 m_Colour; +layout(location = 2) in highp vec2 m_TexCoord; +layout(location = 3) in highp vec4 m_TexRect; +layout(location = 4) in mediump vec2 m_BlendRange; -attribute highp vec2 m_Position; -attribute lowp vec4 m_Colour; -attribute mediump vec2 m_TexCoord; -attribute mediump vec4 m_TexRect; -attribute mediump vec2 m_BlendRange; - -varying highp vec2 v_MaskingPosition; -varying lowp vec4 v_Colour; -varying mediump vec2 v_TexCoord; -varying mediump vec4 v_TexRect; -varying mediump vec2 v_BlendRange; - -uniform highp mat4 g_ProjMatrix; -uniform highp mat3 g_ToMaskingSpace; +layout(location = 0) out highp vec2 v_MaskingPosition; +layout(location = 1) out lowp vec4 v_Colour; +layout(location = 2) out highp vec2 v_TexCoord; +layout(location = 3) out highp vec4 v_TexRect; +layout(location = 4) out mediump vec2 v_BlendRange; void main(void) { - // Transform from screen space to masking space. - highp vec3 maskingPos = g_ToMaskingSpace * vec3(m_Position, 1.0); - v_MaskingPosition = maskingPos.xy / maskingPos.z; + // Transform from screen space to masking space. + highp vec3 maskingPos = g_ToMaskingSpace * vec3(m_Position, 1.0); + v_MaskingPosition = maskingPos.xy / maskingPos.z; - v_Colour = m_Colour; - v_TexCoord = m_TexCoord; - v_TexRect = m_TexRect; - v_BlendRange = m_BlendRange; + v_Colour = m_Colour; + v_TexCoord = m_TexCoord; + v_TexRect = m_TexRect; + v_BlendRange = m_BlendRange; - gl_Position = gProjMatrix * vec4(m_Position, 1.0, 1.0); + gl_Position = g_ProjMatrix * vec4(m_Position, 1.0, 1.0); } - From ba5088f71a57bf7d26eac4f6d319b9c0eb08d438 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 2 May 2023 11:55:05 +0300 Subject: [PATCH 475/476] Add missing ruleset shader tests --- osu.Game.Tests/Testing/TestSceneRulesetDependencies.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Testing/TestSceneRulesetDependencies.cs b/osu.Game.Tests/Testing/TestSceneRulesetDependencies.cs index 585a3f95e7..f0a9ce7beb 100644 --- a/osu.Game.Tests/Testing/TestSceneRulesetDependencies.cs +++ b/osu.Game.Tests/Testing/TestSceneRulesetDependencies.cs @@ -53,6 +53,8 @@ namespace osu.Game.Tests.Testing { Dependencies.Get().GetRawData(@"sh_TestVertex.vs"); Dependencies.Get().GetRawData(@"sh_TestFragment.fs"); + Dependencies.Get().Load(@"TestVertex", @"TestFragment"); + Dependencies.Get().Load(VertexShaderDescriptor.TEXTURE_2, @"TestFragment"); }); } From 95badb9455218c2c2d1d755eb9bf1668c7624984 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 2 May 2023 18:35:06 +0200 Subject: [PATCH 476/476] Adjust composer tests to new screen layout `TestSceneHitObjectComposer.TestPlacementFailsWhenClickingButton()` was attempting to cover the case of the user clicking a toolbox button which was in front of the playfield, and ensure that the click did not result in a placement. However, since the paddings in 67f83f246b1c65c62416a24375ef580139d046dc were added, it is impossible for a toolbox button to be in front of the playfield in the collapsed state, which the test was relying on. The test scenario is still however relevant in the case of the toolbox being expanded, as in that state the toolbux buttons may very well end up being in front of the playfield, and they still should not result in a hitobject being placed. To ensure that this is the case, add a few extra test steps ensuring that the toolbox is expanded first before trying to retrieve an overlapping button. --- .../Visual/Editing/TestSceneHitObjectComposer.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs index 7ab0188114..9bdb9a513c 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs @@ -108,12 +108,16 @@ namespace osu.Game.Tests.Visual.Editing AddStep("Change to hitcircle", () => hitObjectComposer.ChildrenOfType().First(d => d.Button.Label == "HitCircle").TriggerClick()); + ExpandingToolboxContainer toolboxContainer = null!; + + AddStep("move mouse to toolbox", () => InputManager.MoveMouseTo(toolboxContainer = hitObjectComposer.ChildrenOfType().First())); + AddUntilStep("toolbox is expanded", () => toolboxContainer.Expanded.Value); + AddUntilStep("wait for toolbox to expand", () => toolboxContainer.LatestTransformEndTime, () => Is.EqualTo(Time.Current)); + AddStep("move mouse to overlapping toggle button", () => { var playfield = hitObjectComposer.Playfield.ScreenSpaceDrawQuad; - var button = hitObjectComposer - .ChildrenOfType().First() - .ChildrenOfType().First(b => playfield.Contains(b.ScreenSpaceDrawQuad.Centre)); + var button = toolboxContainer.ChildrenOfType().First(b => playfield.Contains(b.ScreenSpaceDrawQuad.Centre)); InputManager.MoveMouseTo(button); });