From 7bb984eb8da4e0390ad9671330a0bb61d2a8920f Mon Sep 17 00:00:00 2001 From: mcendu Date: Sat, 21 Dec 2019 20:47:34 +0800 Subject: [PATCH 001/839] Basic kiai flash implementation --- .../Objects/Drawables/Pieces/CirclePiece.cs | 4 ++ .../Objects/Drawables/Pieces/KiaiFlash.cs | 40 +++++++++++++++++++ .../Skinning/LegacyMainCirclePiece.cs | 12 ++++++ 3 files changed, 56 insertions(+) create mode 100644 osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/KiaiFlash.cs diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs index aab01f45d4..84470d7f30 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs @@ -35,6 +35,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces Origin = Anchor.Centre, Texture = textures.Get(@"Gameplay/osu/disc"), }, + new KiaiFlash + { + RelativeSizeAxes = Axes.Both, + }, new TrianglesPiece { RelativeSizeAxes = Axes.Both, diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/KiaiFlash.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/KiaiFlash.cs new file mode 100644 index 0000000000..9f9ca42d69 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/KiaiFlash.cs @@ -0,0 +1,40 @@ +// 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 osu.Game.Graphics; +using osu.Game.Graphics.Containers; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces +{ + public class KiaiFlash : BeatSyncedContainer + { + public Drawable FlashComponent { get; set; } + + public KiaiFlash() + { + Blending = BlendingParameters.Additive; + + Child = FlashComponent = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.Gray(1f), + Alpha = 0f, + }; + } + + protected new double EarlyActivationMilliseconds = 80; + + protected override void OnNewBeat(int beatIndex, Game.Beatmaps.ControlPoints.TimingControlPoint timingPoint, Game.Beatmaps.ControlPoints.EffectControlPoint effectPoint, Framework.Audio.Track.TrackAmplitudes amplitudes) + { + if (effectPoint.KiaiMode) + { + FlashComponent + .FadeTo(0.25f, EarlyActivationMilliseconds, Easing.OutQuint) + .Then() + .FadeOut(timingPoint.BeatLength - 80, Easing.OutSine); + } + } + } +} diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs index 93ae0371df..97541d171b 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs @@ -10,6 +10,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using osu.Game.Skinning; using osuTK; using osuTK.Graphics; @@ -44,6 +45,17 @@ namespace osu.Game.Rulesets.Osu.Skinning Anchor = Anchor.Centre, Origin = Anchor.Centre, }, + new KiaiFlash + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(0.921875f), + + Masking = true, + CornerRadius = Size.X / 2, + CornerExponent = 2, + }, hitCircleText = new SkinnableSpriteText(new OsuSkinComponent(OsuSkinComponents.HitCircleText), _ => new OsuSpriteText { Font = OsuFont.Numeric.With(size: 40), From e4eccb86ba62985bbae398a5b5697269f01df72f Mon Sep 17 00:00:00 2001 From: mcendu Date: Sat, 21 Dec 2019 20:53:02 +0800 Subject: [PATCH 002/839] Add property Intensity --- osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs | 1 + osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/KiaiFlash.cs | 4 +++- osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs index 84470d7f30..e6b193b00f 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs @@ -38,6 +38,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces new KiaiFlash { RelativeSizeAxes = Axes.Both, + Intensity = 0.25f, }, new TrianglesPiece { diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/KiaiFlash.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/KiaiFlash.cs index 9f9ca42d69..e7c880c3c1 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/KiaiFlash.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/KiaiFlash.cs @@ -12,6 +12,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { public Drawable FlashComponent { get; set; } + public float Intensity { get; set; } + public KiaiFlash() { Blending = BlendingParameters.Additive; @@ -31,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces if (effectPoint.KiaiMode) { FlashComponent - .FadeTo(0.25f, EarlyActivationMilliseconds, Easing.OutQuint) + .FadeTo(Intensity, EarlyActivationMilliseconds, Easing.OutQuint) .Then() .FadeOut(timingPoint.BeatLength - 80, Easing.OutSine); } diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs index 97541d171b..ce89fac345 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs @@ -55,6 +55,8 @@ namespace osu.Game.Rulesets.Osu.Skinning Masking = true, CornerRadius = Size.X / 2, CornerExponent = 2, + + Intensity = 0.1f }, hitCircleText = new SkinnableSpriteText(new OsuSkinComponent(OsuSkinComponents.HitCircleText), _ => new OsuSpriteText { From 76a895f34830a8389920cedb1d7db47458c7c2df Mon Sep 17 00:00:00 2001 From: mcendu Date: Sat, 21 Dec 2019 22:44:52 +0800 Subject: [PATCH 003/839] improve code quality --- .../Objects/Drawables/Pieces/CirclePiece.cs | 8 ++++++++ .../Objects/Drawables/Pieces/KiaiFlash.cs | 14 ++------------ .../Skinning/LegacyMainCirclePiece.cs | 11 +++++++++-- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs index e6b193b00f..eba3a02376 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs @@ -6,7 +6,9 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; +using osu.Framework.Graphics.Shapes; using osuTK; +using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { @@ -39,6 +41,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { RelativeSizeAxes = Axes.Both, Intensity = 0.25f, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.White, + Alpha = 0f, + }, }, new TrianglesPiece { diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/KiaiFlash.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/KiaiFlash.cs index e7c880c3c1..379deff8bf 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/KiaiFlash.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/KiaiFlash.cs @@ -10,29 +10,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { public class KiaiFlash : BeatSyncedContainer { - public Drawable FlashComponent { get; set; } - public float Intensity { get; set; } public KiaiFlash() { + EarlyActivationMilliseconds = 80; Blending = BlendingParameters.Additive; - - Child = FlashComponent = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.Gray(1f), - Alpha = 0f, - }; } - protected new double EarlyActivationMilliseconds = 80; - protected override void OnNewBeat(int beatIndex, Game.Beatmaps.ControlPoints.TimingControlPoint timingPoint, Game.Beatmaps.ControlPoints.EffectControlPoint effectPoint, Framework.Audio.Track.TrackAmplitudes amplitudes) { if (effectPoint.KiaiMode) { - FlashComponent + Child .FadeTo(Intensity, EarlyActivationMilliseconds, Easing.OutQuint) .Then() .FadeOut(timingPoint.BeatLength - 80, Easing.OutSine); diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs index ce89fac345..27f9e008ed 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs @@ -6,6 +6,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Objects.Drawables; @@ -50,13 +51,19 @@ namespace osu.Game.Rulesets.Osu.Skinning RelativeSizeAxes = Axes.Both, Anchor = Anchor.Centre, Origin = Anchor.Centre, - Size = new Vector2(0.921875f), + Size = new Vector2(OsuLegacySkinTransformer.LEGACY_CIRCLE_RADIUS / 64), Masking = true, CornerRadius = Size.X / 2, CornerExponent = 2, - Intensity = 0.1f + Intensity = 0.1f, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.White, + Alpha = 0f, + } }, hitCircleText = new SkinnableSpriteText(new OsuSkinComponent(OsuSkinComponents.HitCircleText), _ => new OsuSpriteText { From 4b3cfe3baec4d44d7a5f04d5398199ca1d95eb00 Mon Sep 17 00:00:00 2001 From: mcendu Date: Sun, 22 Dec 2019 20:01:58 +0800 Subject: [PATCH 004/839] temporarily remove kiai flash for legacy circles --- .../Objects/Drawables/Pieces/CirclePiece.cs | 10 +------- .../Objects/Drawables/Pieces/KiaiFlash.cs | 23 +++++++++++++------ .../Skinning/LegacyMainCirclePiece.cs | 21 ----------------- 3 files changed, 17 insertions(+), 37 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs index eba3a02376..8d68714f9a 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs @@ -6,9 +6,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; -using osu.Framework.Graphics.Shapes; using osuTK; -using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { @@ -40,13 +38,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces new KiaiFlash { RelativeSizeAxes = Axes.Both, - Intensity = 0.25f, - Child = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.White, - Alpha = 0f, - }, + FlashOpacity = 0.25f, }, new TrianglesPiece { diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/KiaiFlash.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/KiaiFlash.cs index 379deff8bf..62ea028b11 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/KiaiFlash.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/KiaiFlash.cs @@ -3,30 +3,39 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { public class KiaiFlash : BeatSyncedContainer { - public float Intensity { get; set; } + public float FlashOpacity = 1f; public KiaiFlash() { EarlyActivationMilliseconds = 80; Blending = BlendingParameters.Additive; + + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.White, + Alpha = 0f, + }; } protected override void OnNewBeat(int beatIndex, Game.Beatmaps.ControlPoints.TimingControlPoint timingPoint, Game.Beatmaps.ControlPoints.EffectControlPoint effectPoint, Framework.Audio.Track.TrackAmplitudes amplitudes) { - if (effectPoint.KiaiMode) + if (!effectPoint.KiaiMode) { - Child - .FadeTo(Intensity, EarlyActivationMilliseconds, Easing.OutQuint) - .Then() - .FadeOut(timingPoint.BeatLength - 80, Easing.OutSine); + return; } + + Child + .FadeTo(FlashOpacity, EarlyActivationMilliseconds, Easing.OutQuint) + .Then() + .FadeOut(timingPoint.BeatLength - 80, Easing.OutSine); } } } diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs index 27f9e008ed..93ae0371df 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs @@ -6,12 +6,10 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using osu.Game.Skinning; using osuTK; using osuTK.Graphics; @@ -46,25 +44,6 @@ namespace osu.Game.Rulesets.Osu.Skinning Anchor = Anchor.Centre, Origin = Anchor.Centre, }, - new KiaiFlash - { - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(OsuLegacySkinTransformer.LEGACY_CIRCLE_RADIUS / 64), - - Masking = true, - CornerRadius = Size.X / 2, - CornerExponent = 2, - - Intensity = 0.1f, - Child = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.White, - Alpha = 0f, - } - }, hitCircleText = new SkinnableSpriteText(new OsuSkinComponent(OsuSkinComponents.HitCircleText), _ => new OsuSpriteText { Font = OsuFont.Numeric.With(size: 40), From 92cab12eb7425179fb2277d3428cf99fc3617332 Mon Sep 17 00:00:00 2001 From: Jess Meng Date: Fri, 11 Dec 2020 21:52:36 -0800 Subject: [PATCH 005/839] Added ctrl + w and ctrl + t keyboard shortcuts --- osu.Game/Overlays/ChatOverlay.cs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 8bc7e21047..5162b6aab0 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -25,6 +25,9 @@ using osuTK.Input; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; +using System; +using osu.Game.Input.Bindings; + namespace osu.Game.Overlays { public class ChatOverlay : OsuFocusedOverlayContainer, INamedOverlayComponent @@ -333,6 +336,16 @@ namespace osu.Game.Overlays ChannelTabControl.Current.Value = channel; } + private void selectChannelSelectorTab() + { + int i = 0; + var channel = ChannelTabControl.Items + .Where(tab => (tab is ChannelSelectorTabItem.ChannelSelectorTabChannel)) + .ElementAtOrDefault(i); + if (channel != null) + ChannelTabControl.Current.Value = channel; + } + protected override bool OnKeyDown(KeyDownEvent e) { if (e.AltPressed) @@ -356,6 +369,18 @@ namespace osu.Game.Overlays return true; } } + if (e.ControlPressed) + { + switch (e.Key) + { + case Key.W: + channelManager.LeaveChannel(channelManager.CurrentChannel.Value); + return true; + case Key.T: + selectChannelSelectorTab(); + return true; + } + } return base.OnKeyDown(e); } @@ -392,6 +417,7 @@ namespace osu.Game.Overlays private void joinedChannelsChanged(object sender, NotifyCollectionChangedEventArgs args) { + switch (args.Action) { case NotifyCollectionChangedAction.Add: From 3dd6589d6e2d516ec5790ce06c24bb5d98447b31 Mon Sep 17 00:00:00 2001 From: Jess Meng Date: Sat, 12 Dec 2020 15:53:08 -0800 Subject: [PATCH 006/839] Fixed style changes and cleaned up ctrl + t implementation --- .../Overlays/Chat/Tabs/ChannelTabControl.cs | 5 +++++ osu.Game/Overlays/ChatOverlay.cs | 19 +++---------------- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs index 19c6f437b6..bc68a9f32a 100644 --- a/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs +++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs @@ -96,6 +96,11 @@ namespace osu.Game.Overlays.Chat.Tabs selectorTab.Active.Value = false; } + public void SelectChannelSelectorTab() + { + SelectTab(selectorTab); + } + protected override TabFillFlowContainer CreateTabFlow() => new ChannelTabFillFlowContainer { Direction = FillDirection.Full, diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 5162b6aab0..7ff63595c7 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -25,9 +25,6 @@ using osuTK.Input; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; -using System; -using osu.Game.Input.Bindings; - namespace osu.Game.Overlays { public class ChatOverlay : OsuFocusedOverlayContainer, INamedOverlayComponent @@ -335,17 +332,6 @@ namespace osu.Game.Overlays if (channel != null) ChannelTabControl.Current.Value = channel; } - - private void selectChannelSelectorTab() - { - int i = 0; - var channel = ChannelTabControl.Items - .Where(tab => (tab is ChannelSelectorTabItem.ChannelSelectorTabChannel)) - .ElementAtOrDefault(i); - if (channel != null) - ChannelTabControl.Current.Value = channel; - } - protected override bool OnKeyDown(KeyDownEvent e) { if (e.AltPressed) @@ -369,6 +355,7 @@ namespace osu.Game.Overlays return true; } } + if (e.ControlPressed) { switch (e.Key) @@ -376,8 +363,9 @@ namespace osu.Game.Overlays case Key.W: channelManager.LeaveChannel(channelManager.CurrentChannel.Value); return true; + case Key.T: - selectChannelSelectorTab(); + ChannelTabControl.SelectChannelSelectorTab(); return true; } } @@ -417,7 +405,6 @@ namespace osu.Game.Overlays private void joinedChannelsChanged(object sender, NotifyCollectionChangedEventArgs args) { - switch (args.Action) { case NotifyCollectionChangedAction.Add: From 4c1e75f1017b3b5a5aa01f4f07f9ebcd44bfed5e Mon Sep 17 00:00:00 2001 From: Angela Zhang <43687061+angelaz1@users.noreply.github.com> Date: Sat, 12 Dec 2020 18:02:08 -0600 Subject: [PATCH 007/839] Small whitespace fix --- osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs index bc68a9f32a..c314e10aff 100644 --- a/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs +++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs @@ -96,7 +96,7 @@ namespace osu.Game.Overlays.Chat.Tabs selectorTab.Active.Value = false; } - public void SelectChannelSelectorTab() + public void SelectChannelSelectorTab() { SelectTab(selectorTab); } From b9f687d7f9605f97d8494e9e6a3e6545aa5a9c24 Mon Sep 17 00:00:00 2001 From: Angela Zhang <43687061+angelaz1@users.noreply.github.com> Date: Sat, 12 Dec 2020 18:55:17 -0600 Subject: [PATCH 008/839] Adding in extra blank line --- osu.Game/Overlays/ChatOverlay.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 7ff63595c7..1b1b7ec6e8 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -332,6 +332,7 @@ namespace osu.Game.Overlays if (channel != null) ChannelTabControl.Current.Value = channel; } + protected override bool OnKeyDown(KeyDownEvent e) { if (e.AltPressed) From 084e4ce50bb4265c5fd020c47e9a41a08fc9dc0f Mon Sep 17 00:00:00 2001 From: Angela Zhang <43687061+angelaz1@users.noreply.github.com> Date: Sat, 12 Dec 2020 20:11:57 -0600 Subject: [PATCH 009/839] Removing whitespace --- osu.Game/Overlays/ChatOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 1b1b7ec6e8..b310e3ff4c 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -332,7 +332,7 @@ namespace osu.Game.Overlays if (channel != null) ChannelTabControl.Current.Value = channel; } - + protected override bool OnKeyDown(KeyDownEvent e) { if (e.AltPressed) From 2d98da0d61374a2abedb3c1daae108df0e77d905 Mon Sep 17 00:00:00 2001 From: Joseph-Ramos-CMU Date: Sun, 13 Dec 2020 13:21:50 -0500 Subject: [PATCH 010/839] Untested Ctrl+Shift+T shortcut prototype Added a list to the ChannelManager class that tracks which tabs I closed. Works like a stack, where it adds to the end every time I close a tab. Then added a function that uses this list to open the last closed channel, and added a shortcut inside of ChatOverlay, similar to how jmeng implemented shortcuts. Code is currently untested. --- osu.Game/Online/Chat/ChannelManager.cs | 30 ++++++++++++++++++++++++++ osu.Game/Overlays/ChatOverlay.cs | 26 ++++++++++++++++------ 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 16f46581c5..9c47c94677 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -33,6 +33,19 @@ namespace osu.Game.Online.Chat private readonly BindableList availableChannels = new BindableList(); private readonly BindableList joinedChannels = new BindableList(); + // Prototype for keeping a list of closed channels in an + // order so we can figure out the reverse order of how channels + // were close + // Bindable list supports an insert at indexc function and a + // remove function. If the list re-indexes after each remove (I can + // check the behaviour of the C# List System.Collections.Generic library to confirm this, since that + // library appears to be what is used underneath), then I can just always add at the end + // of the list and always remove index 0 (if size > 0) + // A stack exchange post indicates that List remove will decrement all the + // indeces after the node we removed + + private readonly BindableList closedChannels = new BindableList(); + /// /// The currently opened channel /// @@ -48,6 +61,11 @@ namespace osu.Game.Online.Chat /// public IBindableList AvailableChannels => availableChannels; + /// + /// The channels available for the player to join + /// + public IBindableList ClosedChannels => ClosedChannels; + [Resolved] private IAPIProvider api { get; set; } @@ -407,6 +425,8 @@ namespace osu.Game.Online.Chat CurrentChannel.Value = null; joinedChannels.Remove(channel); + // insert at the end of the list + closedChannels.Insert(closedChannels.Count, channel); if (channel.Joined.Value) { @@ -415,6 +435,16 @@ namespace osu.Game.Online.Chat } } + + public void JoinLastClosedChannel() + { + if(joinedChannels.Count == 0) + return; + Channel lastClosedChannel = joinedChannels[joinedChannels.Count - 1]; + JoinChannel(lastClosedChannel); + joinedChannels.Remove(lastClosedChannel); + } + private long lastMessageId; private bool channelsInitialised; diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index b310e3ff4c..0adf3cb2c5 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -359,15 +359,27 @@ namespace osu.Game.Overlays if (e.ControlPressed) { - switch (e.Key) + if(e.ShiftPressed) { - case Key.W: - channelManager.LeaveChannel(channelManager.CurrentChannel.Value); - return true; + switch(e.Key) + { + case Key.T: + channelManager.JoinLastClosedChannel(); + return true; + } + } + else + { + switch (e.Key) + { + case Key.W: + channelManager.LeaveChannel(channelManager.CurrentChannel.Value); + return true; - case Key.T: - ChannelTabControl.SelectChannelSelectorTab(); - return true; + case Key.T: + ChannelTabControl.SelectChannelSelectorTab(); + return true; + } } } From 5481ba43c7875e6bb8df292813c8de23dabe7738 Mon Sep 17 00:00:00 2001 From: Joseph-Ramos-CMU Date: Sun, 13 Dec 2020 14:14:57 -0500 Subject: [PATCH 011/839] Fixed a bug where Ctrl+Shift+t shortcut was using the wrong list The JoinLastClosedChannel code was using the joinedChannels list instead of the closedChannels list. Fixing this bug made the Ctrl+Shift+t shortuct work as expected. --- osu.Game/Online/Chat/ChannelManager.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 9c47c94677..b472f521d7 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -435,14 +435,13 @@ namespace osu.Game.Online.Chat } } - public void JoinLastClosedChannel() { - if(joinedChannels.Count == 0) + if(closedChannels.Count == 0) return; - Channel lastClosedChannel = joinedChannels[joinedChannels.Count - 1]; + Channel lastClosedChannel = closedChannels[closedChannels.Count - 1]; JoinChannel(lastClosedChannel); - joinedChannels.Remove(lastClosedChannel); + closedChannels.Remove(lastClosedChannel); } private long lastMessageId; From 2863187b16013c2db36c04d8512355d128cc525d Mon Sep 17 00:00:00 2001 From: Joseph-Ramos-CMU Date: Sun, 13 Dec 2020 20:46:02 -0500 Subject: [PATCH 012/839] Changing behvaior for channels that have already been reopened Ctrl+Shift+t will now skip trying to reopen any channels that are already open, and will instead attempt to open the next still-closed channel. --- osu.Game/Online/Chat/ChannelManager.cs | 54 +++++++++++++++++--------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index b472f521d7..cf7f5604d9 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -33,19 +33,15 @@ namespace osu.Game.Online.Chat private readonly BindableList availableChannels = new BindableList(); private readonly BindableList joinedChannels = new BindableList(); - // Prototype for keeping a list of closed channels in an - // order so we can figure out the reverse order of how channels - // were close - // Bindable list supports an insert at indexc function and a - // remove function. If the list re-indexes after each remove (I can - // check the behaviour of the C# List System.Collections.Generic library to confirm this, since that - // library appears to be what is used underneath), then I can just always add at the end - // of the list and always remove index 0 (if size > 0) - // A stack exchange post indicates that List remove will decrement all the - // indeces after the node we removed + // Keeps a list of closed channels. More recently closed channels appear at higher indeces private readonly BindableList closedChannels = new BindableList(); + // For efficiency purposes, this constant bounds the number of closed channels we store. + // This number is somewhat arbitrary; future developers are free to modify it. + // Must be a positive number. + private const int closedChannelsMaxSize = 50; + /// /// The currently opened channel /// @@ -61,11 +57,6 @@ namespace osu.Game.Online.Chat /// public IBindableList AvailableChannels => availableChannels; - /// - /// The channels available for the player to join - /// - public IBindableList ClosedChannels => ClosedChannels; - [Resolved] private IAPIProvider api { get; set; } @@ -425,7 +416,15 @@ namespace osu.Game.Online.Chat CurrentChannel.Value = null; joinedChannels.Remove(channel); - // insert at the end of the list + + // Prevent the closedChannel list from exceeding the max size + // by removing the oldest element + if(closedChannels.Count >= closedChannelsMaxSize) + { + closedChannels.Remove(closedChannels[0]); + } + + // insert at the end of the closedChannels list closedChannels.Insert(closedChannels.Count, channel); if (channel.Joined.Value) @@ -435,13 +434,32 @@ namespace osu.Game.Online.Chat } } + + + /// + /// Opens the most recently closed channel that has not + /// already been reopened + /// Works similarly to reopening last closed tab on a web browser. + /// public void JoinLastClosedChannel() { - if(closedChannels.Count == 0) + if(closedChannels.Count <= 0) + { return; + } + Channel lastClosedChannel = closedChannels[closedChannels.Count - 1]; - JoinChannel(lastClosedChannel); closedChannels.Remove(lastClosedChannel); + // If the user already joined the channel, try the next + // channel in the list + if(joinedChannels.IndexOf(lastClosedChannel) >= 0) + { + JoinLastClosedChannel(); + } + else + { + JoinChannel(lastClosedChannel); + } } private long lastMessageId; From 08a2cdaf8d43b7efe9a44d4c9833242abd261c60 Mon Sep 17 00:00:00 2001 From: Joseph-Ramos-CMU Date: Sun, 13 Dec 2020 21:02:35 -0500 Subject: [PATCH 013/839] Minor formatting changes --- osu.Game/Online/Chat/ChannelManager.cs | 10 +++++----- osu.Game/Overlays/ChatOverlay.cs | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index cf7f5604d9..e1f4d82e52 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -419,7 +419,7 @@ namespace osu.Game.Online.Chat // Prevent the closedChannel list from exceeding the max size // by removing the oldest element - if(closedChannels.Count >= closedChannelsMaxSize) + if (closedChannels.Count >= closedChannelsMaxSize) { closedChannels.Remove(closedChannels[0]); } @@ -435,15 +435,15 @@ namespace osu.Game.Online.Chat } - + /// /// Opens the most recently closed channel that has not /// already been reopened - /// Works similarly to reopening last closed tab on a web browser. + /// Works similarly to reopening the last closed tab on a web browser. /// public void JoinLastClosedChannel() { - if(closedChannels.Count <= 0) + if (closedChannels.Count <= 0) { return; } @@ -452,7 +452,7 @@ namespace osu.Game.Online.Chat closedChannels.Remove(lastClosedChannel); // If the user already joined the channel, try the next // channel in the list - if(joinedChannels.IndexOf(lastClosedChannel) >= 0) + if (joinedChannels.IndexOf(lastClosedChannel) >= 0) { JoinLastClosedChannel(); } diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 0adf3cb2c5..12bd7e895e 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -359,9 +359,9 @@ namespace osu.Game.Overlays if (e.ControlPressed) { - if(e.ShiftPressed) + if (e.ShiftPressed) { - switch(e.Key) + switch (e.Key) { case Key.T: channelManager.JoinLastClosedChannel(); From 337309ec1332766fd314c8bf29f3d2790933dd28 Mon Sep 17 00:00:00 2001 From: Joseph-Ramos-CMU Date: Sun, 13 Dec 2020 22:06:59 -0500 Subject: [PATCH 014/839] Added Tests for the Ctrl+Shift+T shortcut Tests have not actually been run yet. --- .../Visual/Online/TestSceneChatOverlay.cs | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index fca642ad6c..1770c34d2f 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -208,6 +208,84 @@ namespace osu.Game.Tests.Visual.Online AddAssert("All channels closed", () => !channelManager.JoinedChannels.Any()); } + // Control-Shift-T should reopen the most recently closed tab that has not + // already been reopened + private void pressControlShiftT() + { + InputManager.PressKey(Key.ControlLeft); + InputManager.PressKey(Key.ShiftLeft); + InputManager.Key(Key.T); + InputManager.ReleaseKey(Key.ControlLeft); + InputManager.ReleaseKey(Key.ShiftLeft); + } + + public void TestCtrlShiftTShortcut1() + { + AddStep("Join 2 channels", () => + { + channelManager.JoinChannel(channel1); + channelManager.JoinChannel(channel2); + }); + + // Close channel 2 + AddStep("Select channel 2", () => clickDrawable(chatOverlay.TabMap[channel2])); + AddStep("Click channel 2 close button", () => clickDrawable(((TestPrivateChannelTabItem)chatOverlay.TabMap[channel2]).CloseButton.Child)); + AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); + AddAssert("channel 2 closed", () => !channelManager.JoinedChannels.Contains(channel2)); + + AddStep("Press Control-Shift-T", () => pressControlShiftT()); + + // Channel 2 should now be open again + AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); + AddAssert("channel 2 open", () => channelManager.JoinedChannels.Contains(channel2)); + } + + public void TestCtrlShiftTShortcut2() + { + AddStep("Join 2 channels", () => + { + channelManager.JoinChannel(channel1); + channelManager.JoinChannel(channel2); + }); + + // Close channels 1 and channel 2 + AddStep("Select channel 2", () => clickDrawable(chatOverlay.TabMap[channel2])); + AddStep("Click channel 2 close button", () => clickDrawable(((TestPrivateChannelTabItem)chatOverlay.TabMap[channel2]).CloseButton.Child)); + + AddStep("Select channel 1", () => clickDrawable(chatOverlay.TabMap[channel1])); + AddStep("Click channel 1 close button", () => clickDrawable(((TestPrivateChannelTabItem)chatOverlay.TabMap[channel1]).CloseButton.Child)); + + AddAssert("channel 1 closed", () => !channelManager.JoinedChannels.Contains(channel1)); + AddAssert("channel 2 closed", () => !channelManager.JoinedChannels.Contains(channel2)); + + AddStep("Press Control-Shift-T", () => pressControlShiftT()); + // Channel 1 should now be open again. Channel 2 should not + AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); + AddAssert("channel 2 closed", () => !channelManager.JoinedChannels.Contains(channel2)); + + AddStep("Press Control-Shift-T", () => pressControlShiftT()); + + // Both Channel 1 and Channel 2 should be open + AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); + AddAssert("channel 2 open", () => channelManager.JoinedChannels.Contains(channel2)); + + // Processing this again should have not effect + AddStep("Press Control-Shift-T", () => pressControlShiftT()); + AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); + AddAssert("channel 2 open", () => channelManager.JoinedChannels.Contains(channel2)); + + // Close channel 2 again + AddStep("Select channel 2", () => clickDrawable(chatOverlay.TabMap[channel2])); + AddStep("Click channel 2 close button", () => clickDrawable(((TestPrivateChannelTabItem)chatOverlay.TabMap[channel2]).CloseButton.Child)); + + // Both channel 1 and channel 2 should be open + AddStep("Press Control-Shift-T", () => pressControlShiftT()); + AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); + AddAssert("channel 2 open", () => channelManager.JoinedChannels.Contains(channel2)); + } + + + private void pressChannelHotkey(int number) { var channelKey = Key.Number0 + number; From 3301f532eed41ef457b3be009e9c9ca2c44773ca Mon Sep 17 00:00:00 2001 From: Joseph-Ramos-CMU Date: Sun, 13 Dec 2020 23:27:48 -0500 Subject: [PATCH 015/839] Additional formatting Ran the dotnet format as well as the .\InspectCode.ps1 tools, and fixed the style issues they found. --- .../Visual/Online/TestSceneChatOverlay.cs | 22 +++++++++---------- osu.Game/Online/Chat/ChannelManager.cs | 12 +++++----- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index 1770c34d2f..fddaaa8215 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -212,11 +212,11 @@ namespace osu.Game.Tests.Visual.Online // already been reopened private void pressControlShiftT() { - InputManager.PressKey(Key.ControlLeft); - InputManager.PressKey(Key.ShiftLeft); - InputManager.Key(Key.T); - InputManager.ReleaseKey(Key.ControlLeft); - InputManager.ReleaseKey(Key.ShiftLeft); + AddStep("Press and hold Control", () => InputManager.PressKey(Key.ControlLeft)); + AddStep("Press and hold Shift", () => InputManager.PressKey(Key.ShiftLeft)); + AddStep("Press T", () => InputManager.Key(Key.T)); + AddStep("Release Control", () => InputManager.ReleaseKey(Key.ControlLeft)); + AddStep("Release Shift", () => InputManager.ReleaseKey(Key.ShiftLeft)); } public void TestCtrlShiftTShortcut1() @@ -233,7 +233,7 @@ namespace osu.Game.Tests.Visual.Online AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); AddAssert("channel 2 closed", () => !channelManager.JoinedChannels.Contains(channel2)); - AddStep("Press Control-Shift-T", () => pressControlShiftT()); + pressControlShiftT(); // Channel 2 should now be open again AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); @@ -258,19 +258,19 @@ namespace osu.Game.Tests.Visual.Online AddAssert("channel 1 closed", () => !channelManager.JoinedChannels.Contains(channel1)); AddAssert("channel 2 closed", () => !channelManager.JoinedChannels.Contains(channel2)); - AddStep("Press Control-Shift-T", () => pressControlShiftT()); + pressControlShiftT(); // Channel 1 should now be open again. Channel 2 should not AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); AddAssert("channel 2 closed", () => !channelManager.JoinedChannels.Contains(channel2)); - AddStep("Press Control-Shift-T", () => pressControlShiftT()); + pressControlShiftT(); // Both Channel 1 and Channel 2 should be open AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); AddAssert("channel 2 open", () => channelManager.JoinedChannels.Contains(channel2)); // Processing this again should have not effect - AddStep("Press Control-Shift-T", () => pressControlShiftT()); + pressControlShiftT(); AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); AddAssert("channel 2 open", () => channelManager.JoinedChannels.Contains(channel2)); @@ -279,13 +279,11 @@ namespace osu.Game.Tests.Visual.Online AddStep("Click channel 2 close button", () => clickDrawable(((TestPrivateChannelTabItem)chatOverlay.TabMap[channel2]).CloseButton.Child)); // Both channel 1 and channel 2 should be open - AddStep("Press Control-Shift-T", () => pressControlShiftT()); + pressControlShiftT(); AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); AddAssert("channel 2 open", () => channelManager.JoinedChannels.Contains(channel2)); } - - private void pressChannelHotkey(int number) { var channelKey = Key.Number0 + number; diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index e1f4d82e52..63eea0b190 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -33,14 +33,13 @@ namespace osu.Game.Online.Chat private readonly BindableList availableChannels = new BindableList(); private readonly BindableList joinedChannels = new BindableList(); - // Keeps a list of closed channels. More recently closed channels appear at higher indeces private readonly BindableList closedChannels = new BindableList(); // For efficiency purposes, this constant bounds the number of closed channels we store. // This number is somewhat arbitrary; future developers are free to modify it. // Must be a positive number. - private const int closedChannelsMaxSize = 50; + private const int closed_channels_max_size = 50; /// /// The currently opened channel @@ -419,7 +418,7 @@ namespace osu.Game.Online.Chat // Prevent the closedChannel list from exceeding the max size // by removing the oldest element - if (closedChannels.Count >= closedChannelsMaxSize) + if (closedChannels.Count >= closed_channels_max_size) { closedChannels.Remove(closedChannels[0]); } @@ -434,8 +433,6 @@ namespace osu.Game.Online.Chat } } - - /// /// Opens the most recently closed channel that has not /// already been reopened @@ -448,8 +445,11 @@ namespace osu.Game.Online.Chat return; } - Channel lastClosedChannel = closedChannels[closedChannels.Count - 1]; + // equivalent to Channel lastClosedChannel = closedChannels[closedChannels.Count - 1]; + Channel lastClosedChannel = closedChannels[^1]; + closedChannels.Remove(lastClosedChannel); + // If the user already joined the channel, try the next // channel in the list if (joinedChannels.IndexOf(lastClosedChannel) >= 0) From 35f403dacd6b8999a7d51c90d9d176eb2a4fdc88 Mon Sep 17 00:00:00 2001 From: Jess Meng Date: Sun, 13 Dec 2020 22:05:54 -0800 Subject: [PATCH 016/839] Added tests for ctrl + w and ctrl + t --- .../Visual/Online/TestSceneChatOverlay.cs | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index fca642ad6c..e80bab407e 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -207,6 +207,62 @@ namespace osu.Game.Tests.Visual.Online AddStep("Click normal close button", () => clickDrawable(((TestChannelTabItem)chatOverlay.TabMap[channel1]).CloseButton.Child)); AddAssert("All channels closed", () => !channelManager.JoinedChannels.Any()); } + + private void pressControlW() + { + AddStep("Press and hold Control", () => InputManager.PressKey(Key.ControlLeft)); + AddStep("Press W", () => InputManager.Key(Key.W)); + AddStep("Release Control", () => InputManager.ReleaseKey(Key.ControlLeft)); + } + + + public void TestCtrlWShortcut() + { + AddStep("Join 2 channels", () => + { + channelManager.JoinChannel(channel1); + channelManager.JoinChannel(channel2); + }); + + // Want to close channel 2 + AddStep("Select channel 2", () => clickDrawable(chatOverlay.TabMap[channel2])); + + pressControlW(); + + // Channel 2 should be closed + AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); + AddAssert("channel 2 closed", () => !channelManager.JoinedChannels.Contains(channel2)); + + // Want to close channel 1 + AddStep("Select channel 1", () => clickDrawable(chatOverlay.TabMap[channel1])); + + pressControlW(); + + // Channel 1 and channel 2 should be closed + AddAssert("channel 1 closed", () => !channelManager.JoinedChannels.Contains(channel1)); + AddAssert("channel 2 closed", () => !channelManager.JoinedChannels.Contains(channel2)); + } + + private void pressControlT() + { + AddStep("Press and hold Control", () => InputManager.PressKey(Key.ControlLeft)); + AddStep("Press T", () => InputManager.Key(Key.T)); + AddStep("Release Control", () => InputManager.ReleaseKey(Key.ControlLeft)); + } + + public void TestCtrlTShortcut() + { + AddStep("Join 2 channels", () => + { + channelManager.JoinChannel(channel1); + channelManager.JoinChannel(channel2); + }); + + // Want to join another channel + pressControlT(); + // Selector should be visible + AddAssert("Selector is visible", () => chatOverlay.SelectionOverlayState == Visibility.Visible); + } private void pressChannelHotkey(int number) { From ccbf6db810be69444c87c9ffa91d139730e2bea3 Mon Sep 17 00:00:00 2001 From: Jess Meng Date: Sun, 13 Dec 2020 22:11:52 -0800 Subject: [PATCH 017/839] Fixed some formatting and style --- osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index e80bab407e..44901992e7 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -214,8 +214,8 @@ namespace osu.Game.Tests.Visual.Online AddStep("Press W", () => InputManager.Key(Key.W)); AddStep("Release Control", () => InputManager.ReleaseKey(Key.ControlLeft)); } - + [Test] public void TestCtrlWShortcut() { AddStep("Join 2 channels", () => @@ -250,6 +250,7 @@ namespace osu.Game.Tests.Visual.Online AddStep("Release Control", () => InputManager.ReleaseKey(Key.ControlLeft)); } + [Test] public void TestCtrlTShortcut() { AddStep("Join 2 channels", () => From 5903569386a7bd35f6abb075c5bf5331965c5414 Mon Sep 17 00:00:00 2001 From: Joseph-Ramos-CMU Date: Mon, 14 Dec 2020 13:00:02 -0500 Subject: [PATCH 018/839] Changing the Tests --- osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index fddaaa8215..d4cc60ba97 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -255,8 +255,7 @@ namespace osu.Game.Tests.Visual.Online AddStep("Select channel 1", () => clickDrawable(chatOverlay.TabMap[channel1])); AddStep("Click channel 1 close button", () => clickDrawable(((TestPrivateChannelTabItem)chatOverlay.TabMap[channel1]).CloseButton.Child)); - AddAssert("channel 1 closed", () => !channelManager.JoinedChannels.Contains(channel1)); - AddAssert("channel 2 closed", () => !channelManager.JoinedChannels.Contains(channel2)); + AddAssert("All channels closed", () => !channelManager.JoinedChannels.Any(); pressControlShiftT(); // Channel 1 should now be open again. Channel 2 should not From b26946ba8e38f50ec64cc10943d505d087e879cd Mon Sep 17 00:00:00 2001 From: Joseph-Ramos-CMU Date: Mon, 14 Dec 2020 13:09:34 -0500 Subject: [PATCH 019/839] Temporarily removed test cases I did this to try to figure out what was failing on pull requests --- .../Visual/Online/TestSceneChatOverlay.cs | 64 ------------------- 1 file changed, 64 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index d4cc60ba97..d444c2e9f1 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -219,70 +219,6 @@ namespace osu.Game.Tests.Visual.Online AddStep("Release Shift", () => InputManager.ReleaseKey(Key.ShiftLeft)); } - public void TestCtrlShiftTShortcut1() - { - AddStep("Join 2 channels", () => - { - channelManager.JoinChannel(channel1); - channelManager.JoinChannel(channel2); - }); - - // Close channel 2 - AddStep("Select channel 2", () => clickDrawable(chatOverlay.TabMap[channel2])); - AddStep("Click channel 2 close button", () => clickDrawable(((TestPrivateChannelTabItem)chatOverlay.TabMap[channel2]).CloseButton.Child)); - AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); - AddAssert("channel 2 closed", () => !channelManager.JoinedChannels.Contains(channel2)); - - pressControlShiftT(); - - // Channel 2 should now be open again - AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); - AddAssert("channel 2 open", () => channelManager.JoinedChannels.Contains(channel2)); - } - - public void TestCtrlShiftTShortcut2() - { - AddStep("Join 2 channels", () => - { - channelManager.JoinChannel(channel1); - channelManager.JoinChannel(channel2); - }); - - // Close channels 1 and channel 2 - AddStep("Select channel 2", () => clickDrawable(chatOverlay.TabMap[channel2])); - AddStep("Click channel 2 close button", () => clickDrawable(((TestPrivateChannelTabItem)chatOverlay.TabMap[channel2]).CloseButton.Child)); - - AddStep("Select channel 1", () => clickDrawable(chatOverlay.TabMap[channel1])); - AddStep("Click channel 1 close button", () => clickDrawable(((TestPrivateChannelTabItem)chatOverlay.TabMap[channel1]).CloseButton.Child)); - - AddAssert("All channels closed", () => !channelManager.JoinedChannels.Any(); - - pressControlShiftT(); - // Channel 1 should now be open again. Channel 2 should not - AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); - AddAssert("channel 2 closed", () => !channelManager.JoinedChannels.Contains(channel2)); - - pressControlShiftT(); - - // Both Channel 1 and Channel 2 should be open - AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); - AddAssert("channel 2 open", () => channelManager.JoinedChannels.Contains(channel2)); - - // Processing this again should have not effect - pressControlShiftT(); - AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); - AddAssert("channel 2 open", () => channelManager.JoinedChannels.Contains(channel2)); - - // Close channel 2 again - AddStep("Select channel 2", () => clickDrawable(chatOverlay.TabMap[channel2])); - AddStep("Click channel 2 close button", () => clickDrawable(((TestPrivateChannelTabItem)chatOverlay.TabMap[channel2]).CloseButton.Child)); - - // Both channel 1 and channel 2 should be open - pressControlShiftT(); - AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); - AddAssert("channel 2 open", () => channelManager.JoinedChannels.Contains(channel2)); - } - private void pressChannelHotkey(int number) { var channelKey = Key.Number0 + number; From bd2765ecc4fe8900ccf60a693b1b30e33635cdb4 Mon Sep 17 00:00:00 2001 From: Joseph-Ramos-CMU Date: Mon, 14 Dec 2020 13:23:43 -0500 Subject: [PATCH 020/839] Fixed style concerns --- osu.Game/Online/Chat/ChannelManager.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 63eea0b190..607c9a1223 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -420,7 +420,7 @@ namespace osu.Game.Online.Chat // by removing the oldest element if (closedChannels.Count >= closed_channels_max_size) { - closedChannels.Remove(closedChannels[0]); + closedChannels.RemoveAt(0); } // insert at the end of the closedChannels list @@ -445,8 +445,7 @@ namespace osu.Game.Online.Chat return; } - // equivalent to Channel lastClosedChannel = closedChannels[closedChannels.Count - 1]; - Channel lastClosedChannel = closedChannels[^1]; + Channel lastClosedChannel = closedChannels.Last(); closedChannels.Remove(lastClosedChannel); From 9cb0ed3b8f42716db57540e78c11f5326faabfc3 Mon Sep 17 00:00:00 2001 From: Jess Meng Date: Mon, 14 Dec 2020 12:38:59 -0800 Subject: [PATCH 021/839] Fixed formatting for tests --- osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index 44901992e7..53577f41ee 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -207,18 +207,16 @@ namespace osu.Game.Tests.Visual.Online AddStep("Click normal close button", () => clickDrawable(((TestChannelTabItem)chatOverlay.TabMap[channel1]).CloseButton.Child)); AddAssert("All channels closed", () => !channelManager.JoinedChannels.Any()); } - private void pressControlW() { AddStep("Press and hold Control", () => InputManager.PressKey(Key.ControlLeft)); AddStep("Press W", () => InputManager.Key(Key.W)); AddStep("Release Control", () => InputManager.ReleaseKey(Key.ControlLeft)); } - [Test] public void TestCtrlWShortcut() { - AddStep("Join 2 channels", () => + AddStep("Join 2 channels", () => { channelManager.JoinChannel(channel1); channelManager.JoinChannel(channel2); @@ -228,7 +226,6 @@ namespace osu.Game.Tests.Visual.Online AddStep("Select channel 2", () => clickDrawable(chatOverlay.TabMap[channel2])); pressControlW(); - // Channel 2 should be closed AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); AddAssert("channel 2 closed", () => !channelManager.JoinedChannels.Contains(channel2)); @@ -237,10 +234,8 @@ namespace osu.Game.Tests.Visual.Online AddStep("Select channel 1", () => clickDrawable(chatOverlay.TabMap[channel1])); pressControlW(); - // Channel 1 and channel 2 should be closed - AddAssert("channel 1 closed", () => !channelManager.JoinedChannels.Contains(channel1)); - AddAssert("channel 2 closed", () => !channelManager.JoinedChannels.Contains(channel2)); + AddAssert("All channels closed", () => !channelManager.JoinedChannels.Any()); } private void pressControlT() @@ -253,7 +248,7 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestCtrlTShortcut() { - AddStep("Join 2 channels", () => + AddStep("Join 2 channels", () => { channelManager.JoinChannel(channel1); channelManager.JoinChannel(channel2); From 61fe6ca84e91ddccfc80b5d20baf4b6f233cd6a9 Mon Sep 17 00:00:00 2001 From: Joseph-Ramos-CMU Date: Mon, 14 Dec 2020 15:47:48 -0500 Subject: [PATCH 022/839] Added back CI tests --- .../Visual/Online/TestSceneChatOverlay.cs | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index d444c2e9f1..adeeae8652 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -219,6 +219,70 @@ namespace osu.Game.Tests.Visual.Online AddStep("Release Shift", () => InputManager.ReleaseKey(Key.ShiftLeft)); } + public void TestCtrlShiftTShortcut1() + { + AddStep("Join 2 channels", () => + { + channelManager.JoinChannel(channel1); + channelManager.JoinChannel(channel2); + }); + + // Close channel 2 + AddStep("Select channel 2", () => clickDrawable(chatOverlay.TabMap[channel2])); + AddStep("Click channel 2 close button", () => clickDrawable(((TestPrivateChannelTabItem)chatOverlay.TabMap[channel2]).CloseButton.Child)); + AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); + AddAssert("channel 2 closed", () => !channelManager.JoinedChannels.Contains(channel2)); + + pressControlShiftT(); + + // Channel 2 should now be open again + AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); + AddAssert("channel 2 open", () => channelManager.JoinedChannels.Contains(channel2)); + } + + public void TestCtrlShiftTShortcut2() + { + AddStep("Join 2 channels", () => + { + channelManager.JoinChannel(channel1); + channelManager.JoinChannel(channel2); + }); + + // Close channels 1 and channel 2 + AddStep("Select channel 2", () => clickDrawable(chatOverlay.TabMap[channel2])); + AddStep("Click channel 2 close button", () => clickDrawable(((TestPrivateChannelTabItem)chatOverlay.TabMap[channel2]).CloseButton.Child)); + + AddStep("Select channel 1", () => clickDrawable(chatOverlay.TabMap[channel1])); + AddStep("Click channel 1 close button", () => clickDrawable(((TestPrivateChannelTabItem)chatOverlay.TabMap[channel1]).CloseButton.Child)); + + AddAssert("All channels closed", () => !channelManager.JoinedChannels.Any()); + + pressControlShiftT(); + // Channel 1 should now be open again. Channel 2 should not + AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); + AddAssert("channel 2 closed", () => !channelManager.JoinedChannels.Contains(channel2)); + + pressControlShiftT(); + + // Both Channel 1 and Channel 2 should be open + AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); + AddAssert("channel 2 open", () => channelManager.JoinedChannels.Contains(channel2)); + + // Processing this again should have not effect + pressControlShiftT(); + AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); + AddAssert("channel 2 open", () => channelManager.JoinedChannels.Contains(channel2)); + + // Close channel 2 again + AddStep("Select channel 2", () => clickDrawable(chatOverlay.TabMap[channel2])); + AddStep("Click channel 2 close button", () => clickDrawable(((TestPrivateChannelTabItem)chatOverlay.TabMap[channel2]).CloseButton.Child)); + + // Both channel 1 and channel 2 should be open + pressControlShiftT(); + AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); + AddAssert("channel 2 open", () => channelManager.JoinedChannels.Contains(channel2)); + } + private void pressChannelHotkey(int number) { var channelKey = Key.Number0 + number; From 1af0f79ddf4b1af252539164198fd1947982d072 Mon Sep 17 00:00:00 2001 From: Angela Zhang <43687061+angelaz1@users.noreply.github.com> Date: Mon, 14 Dec 2020 16:58:43 -0600 Subject: [PATCH 023/839] Adding newline style fixes --- osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index 53577f41ee..d39f0678c8 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -207,12 +207,14 @@ namespace osu.Game.Tests.Visual.Online AddStep("Click normal close button", () => clickDrawable(((TestChannelTabItem)chatOverlay.TabMap[channel1]).CloseButton.Child)); AddAssert("All channels closed", () => !channelManager.JoinedChannels.Any()); } + private void pressControlW() { AddStep("Press and hold Control", () => InputManager.PressKey(Key.ControlLeft)); AddStep("Press W", () => InputManager.Key(Key.W)); AddStep("Release Control", () => InputManager.ReleaseKey(Key.ControlLeft)); } + [Test] public void TestCtrlWShortcut() { From 206312d4c2feafe29b1a8c0bdf927126f40bbd2e Mon Sep 17 00:00:00 2001 From: Joseph-Ramos-CMU Date: Mon, 14 Dec 2020 18:26:57 -0500 Subject: [PATCH 024/839] Changed the CI tests I'm trying to make changes because something is going wrong with theGitHub CI tests. --- .../Visual/Online/TestSceneChatOverlay.cs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index adeeae8652..556b42390e 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -228,8 +228,7 @@ namespace osu.Game.Tests.Visual.Online }); // Close channel 2 - AddStep("Select channel 2", () => clickDrawable(chatOverlay.TabMap[channel2])); - AddStep("Click channel 2 close button", () => clickDrawable(((TestPrivateChannelTabItem)chatOverlay.TabMap[channel2]).CloseButton.Child)); + AddStep("Leave channel 2", () => channelManager.LeaveChannel(channel2)); AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); AddAssert("channel 2 closed", () => !channelManager.JoinedChannels.Contains(channel2)); @@ -249,12 +248,8 @@ namespace osu.Game.Tests.Visual.Online }); // Close channels 1 and channel 2 - AddStep("Select channel 2", () => clickDrawable(chatOverlay.TabMap[channel2])); - AddStep("Click channel 2 close button", () => clickDrawable(((TestPrivateChannelTabItem)chatOverlay.TabMap[channel2]).CloseButton.Child)); - - AddStep("Select channel 1", () => clickDrawable(chatOverlay.TabMap[channel1])); - AddStep("Click channel 1 close button", () => clickDrawable(((TestPrivateChannelTabItem)chatOverlay.TabMap[channel1]).CloseButton.Child)); - + AddStep("Leave channel 2", () => channelManager.LeaveChannel(channel2)); + AddStep("Leave channel 1", () => channelManager.LeaveChannel(channel1)); AddAssert("All channels closed", () => !channelManager.JoinedChannels.Any()); pressControlShiftT(); @@ -274,8 +269,7 @@ namespace osu.Game.Tests.Visual.Online AddAssert("channel 2 open", () => channelManager.JoinedChannels.Contains(channel2)); // Close channel 2 again - AddStep("Select channel 2", () => clickDrawable(chatOverlay.TabMap[channel2])); - AddStep("Click channel 2 close button", () => clickDrawable(((TestPrivateChannelTabItem)chatOverlay.TabMap[channel2]).CloseButton.Child)); + AddStep("Leave channel 2", () => channelManager.LeaveChannel(channel2)); // Both channel 1 and channel 2 should be open pressControlShiftT(); From 017e00eb268eeaa176d18b128f20a9f53a39ee9c Mon Sep 17 00:00:00 2001 From: Jess Meng Date: Mon, 14 Dec 2020 15:27:57 -0800 Subject: [PATCH 025/839] More style fixes --- osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index d39f0678c8..3dfa5d09be 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -207,14 +207,12 @@ namespace osu.Game.Tests.Visual.Online AddStep("Click normal close button", () => clickDrawable(((TestChannelTabItem)chatOverlay.TabMap[channel1]).CloseButton.Child)); AddAssert("All channels closed", () => !channelManager.JoinedChannels.Any()); } - private void pressControlW() { AddStep("Press and hold Control", () => InputManager.PressKey(Key.ControlLeft)); AddStep("Press W", () => InputManager.Key(Key.W)); AddStep("Release Control", () => InputManager.ReleaseKey(Key.ControlLeft)); - } - + } [Test] public void TestCtrlWShortcut() { From 37a601088786d3dcdccc85adc04195acf4f74edd Mon Sep 17 00:00:00 2001 From: Joseph-Ramos-CMU Date: Mon, 14 Dec 2020 18:33:51 -0500 Subject: [PATCH 026/839] Changed test formatting Tests should be prefixed with [Test]. Previously my tests do not have this. --- osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index 556b42390e..3eaba131ad 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -219,6 +219,7 @@ namespace osu.Game.Tests.Visual.Online AddStep("Release Shift", () => InputManager.ReleaseKey(Key.ShiftLeft)); } + [Test] public void TestCtrlShiftTShortcut1() { AddStep("Join 2 channels", () => @@ -239,6 +240,7 @@ namespace osu.Game.Tests.Visual.Online AddAssert("channel 2 open", () => channelManager.JoinedChannels.Contains(channel2)); } + [Test] public void TestCtrlShiftTShortcut2() { AddStep("Join 2 channels", () => From 7fe7c24ce15cd1ccd0c19020b9d38d26960e4ca8 Mon Sep 17 00:00:00 2001 From: Jess Meng Date: Mon, 14 Dec 2020 15:44:40 -0800 Subject: [PATCH 027/839] Style fixes for whitespaces --- osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index 3dfa5d09be..68fc8ac8c0 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -207,12 +207,14 @@ namespace osu.Game.Tests.Visual.Online AddStep("Click normal close button", () => clickDrawable(((TestChannelTabItem)chatOverlay.TabMap[channel1]).CloseButton.Child)); AddAssert("All channels closed", () => !channelManager.JoinedChannels.Any()); } + private void pressControlW() { AddStep("Press and hold Control", () => InputManager.PressKey(Key.ControlLeft)); AddStep("Press W", () => InputManager.Key(Key.W)); AddStep("Release Control", () => InputManager.ReleaseKey(Key.ControlLeft)); - } + } + [Test] public void TestCtrlWShortcut() { From bd2e42e7867153fb88851e7defb5fe7ec3828665 Mon Sep 17 00:00:00 2001 From: Joseph-Ramos-CMU Date: Mon, 14 Dec 2020 19:10:26 -0500 Subject: [PATCH 028/839] Toggling out CI tests I'm still trying to track donwn why GitHub won't accept my CI. I'm toggling tests to see what is the problem. --- .../Visual/Online/TestSceneChatOverlay.cs | 63 +++++++++---------- 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index 3eaba131ad..fd83f761a6 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -240,45 +240,44 @@ namespace osu.Game.Tests.Visual.Online AddAssert("channel 2 open", () => channelManager.JoinedChannels.Contains(channel2)); } - [Test] - public void TestCtrlShiftTShortcut2() - { - AddStep("Join 2 channels", () => - { - channelManager.JoinChannel(channel1); - channelManager.JoinChannel(channel2); - }); + // [Test] + // public void TestCtrlShiftTShortcut2() + // { + // AddStep("Join 2 channels", () => + // { + // channelManager.JoinChannel(channel1); + // channelManager.JoinChannel(channel2); + // }); - // Close channels 1 and channel 2 - AddStep("Leave channel 2", () => channelManager.LeaveChannel(channel2)); - AddStep("Leave channel 1", () => channelManager.LeaveChannel(channel1)); - AddAssert("All channels closed", () => !channelManager.JoinedChannels.Any()); + // // Close channels 1 and channel 2 + // AddStep("Leave channel 2", () => channelManager.LeaveChannel(channel2)); + // AddStep("Leave channel 1", () => channelManager.LeaveChannel(channel1)); + // AddAssert("All channels closed", () => !channelManager.JoinedChannels.Any()); - pressControlShiftT(); - // Channel 1 should now be open again. Channel 2 should not - AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); - AddAssert("channel 2 closed", () => !channelManager.JoinedChannels.Contains(channel2)); + // pressControlShiftT(); + // // Channel 1 should now be open again. Channel 2 should not + // AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); + // AddAssert("channel 2 closed", () => !channelManager.JoinedChannels.Contains(channel2)); - pressControlShiftT(); + // pressControlShiftT(); - // Both Channel 1 and Channel 2 should be open - AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); - AddAssert("channel 2 open", () => channelManager.JoinedChannels.Contains(channel2)); + // // Both Channel 1 and Channel 2 should be open + // AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); + // AddAssert("channel 2 open", () => channelManager.JoinedChannels.Contains(channel2)); - // Processing this again should have not effect - pressControlShiftT(); - AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); - AddAssert("channel 2 open", () => channelManager.JoinedChannels.Contains(channel2)); + // // Processing this again should have not effect + // pressControlShiftT(); + // AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); + // AddAssert("channel 2 open", () => channelManager.JoinedChannels.Contains(channel2)); - // Close channel 2 again - AddStep("Leave channel 2", () => channelManager.LeaveChannel(channel2)); - - // Both channel 1 and channel 2 should be open - pressControlShiftT(); - AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); - AddAssert("channel 2 open", () => channelManager.JoinedChannels.Contains(channel2)); - } + // // Close channel 2 again + // AddStep("Leave channel 2", () => channelManager.LeaveChannel(channel2)); + // // Both channel 1 and channel 2 should be open + // pressControlShiftT(); + // AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); + // AddAssert("channel 2 open", () => channelManager.JoinedChannels.Contains(channel2)); + // } private void pressChannelHotkey(int number) { var channelKey = Key.Number0 + number; From f17df1fdf59c71ad3d3e32f2175bbd5e8d3bb575 Mon Sep 17 00:00:00 2001 From: Joseph-Ramos-CMU Date: Mon, 14 Dec 2020 19:27:04 -0500 Subject: [PATCH 029/839] Commented out all CI tests Part of my efforts to narrow down GitHub CI issues --- .../Visual/Online/TestSceneChatOverlay.cs | 51 +++++++++---------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index fd83f761a6..43ff5dc78c 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -210,36 +210,35 @@ namespace osu.Game.Tests.Visual.Online // Control-Shift-T should reopen the most recently closed tab that has not // already been reopened - private void pressControlShiftT() - { - AddStep("Press and hold Control", () => InputManager.PressKey(Key.ControlLeft)); - AddStep("Press and hold Shift", () => InputManager.PressKey(Key.ShiftLeft)); - AddStep("Press T", () => InputManager.Key(Key.T)); - AddStep("Release Control", () => InputManager.ReleaseKey(Key.ControlLeft)); - AddStep("Release Shift", () => InputManager.ReleaseKey(Key.ShiftLeft)); - } + // private void pressControlShiftT() + // { + // AddStep("Press and hold Control", () => InputManager.PressKey(Key.ControlLeft)); + // AddStep("Press and hold Shift", () => InputManager.PressKey(Key.ShiftLeft)); + // AddStep("Press T", () => InputManager.Key(Key.T)); + // AddStep("Release Control", () => InputManager.ReleaseKey(Key.ControlLeft)); + // AddStep("Release Shift", () => InputManager.ReleaseKey(Key.ShiftLeft)); + // } - [Test] - public void TestCtrlShiftTShortcut1() - { - AddStep("Join 2 channels", () => - { - channelManager.JoinChannel(channel1); - channelManager.JoinChannel(channel2); - }); + // [Test] + // public void TestCtrlShiftTShortcut1() + // { + // AddStep("Join 2 channels", () => + // { + // channelManager.JoinChannel(channel1); + // channelManager.JoinChannel(channel2); + // }); - // Close channel 2 - AddStep("Leave channel 2", () => channelManager.LeaveChannel(channel2)); - AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); - AddAssert("channel 2 closed", () => !channelManager.JoinedChannels.Contains(channel2)); + // // Close channel 2 + // AddStep("Leave channel 2", () => channelManager.LeaveChannel(channel2)); + // AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); + // AddAssert("channel 2 closed", () => !channelManager.JoinedChannels.Contains(channel2)); - pressControlShiftT(); - - // Channel 2 should now be open again - AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); - AddAssert("channel 2 open", () => channelManager.JoinedChannels.Contains(channel2)); - } + // pressControlShiftT(); + // // Channel 2 should now be open again + // AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); + // AddAssert("channel 2 open", () => channelManager.JoinedChannels.Contains(channel2)); + // } // [Test] // public void TestCtrlShiftTShortcut2() // { From 9af448d559f465b50921f1f5ed5b5d8cc0abd8de Mon Sep 17 00:00:00 2001 From: Joseph-Ramos-CMU Date: Mon, 14 Dec 2020 22:02:34 -0500 Subject: [PATCH 030/839] Testing first CI test --- .../Visual/Online/TestSceneChatOverlay.cs | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index 43ff5dc78c..ce590839c2 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -210,35 +210,36 @@ namespace osu.Game.Tests.Visual.Online // Control-Shift-T should reopen the most recently closed tab that has not // already been reopened - // private void pressControlShiftT() - // { - // AddStep("Press and hold Control", () => InputManager.PressKey(Key.ControlLeft)); - // AddStep("Press and hold Shift", () => InputManager.PressKey(Key.ShiftLeft)); - // AddStep("Press T", () => InputManager.Key(Key.T)); - // AddStep("Release Control", () => InputManager.ReleaseKey(Key.ControlLeft)); - // AddStep("Release Shift", () => InputManager.ReleaseKey(Key.ShiftLeft)); - // } + private void pressControlShiftT() + { + AddStep("Press and hold Control", () => InputManager.PressKey(Key.ControlLeft)); + AddStep("Press and hold Shift", () => InputManager.PressKey(Key.ShiftLeft)); + AddStep("Press T", () => InputManager.Key(Key.T)); + AddStep("Release Control", () => InputManager.ReleaseKey(Key.ControlLeft)); + AddStep("Release Shift", () => InputManager.ReleaseKey(Key.ShiftLeft)); + } - // [Test] - // public void TestCtrlShiftTShortcut1() - // { - // AddStep("Join 2 channels", () => - // { - // channelManager.JoinChannel(channel1); - // channelManager.JoinChannel(channel2); - // }); + [Test] + public void TestCtrlShiftTShortcut1() + { + AddStep("Join 2 channels", () => + { + channelManager.JoinChannel(channel1); + channelManager.JoinChannel(channel2); + }); - // // Close channel 2 - // AddStep("Leave channel 2", () => channelManager.LeaveChannel(channel2)); - // AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); - // AddAssert("channel 2 closed", () => !channelManager.JoinedChannels.Contains(channel2)); + // Close channel 2 + AddStep("Leave channel 2", () => channelManager.LeaveChannel(channel2)); + AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); + AddAssert("channel 2 closed", () => !channelManager.JoinedChannels.Contains(channel2)); - // pressControlShiftT(); + pressControlShiftT(); + + // Channel 2 should now be open again + AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); + AddAssert("channel 2 open", () => channelManager.JoinedChannels.Contains(channel2)); + } - // // Channel 2 should now be open again - // AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); - // AddAssert("channel 2 open", () => channelManager.JoinedChannels.Contains(channel2)); - // } // [Test] // public void TestCtrlShiftTShortcut2() // { @@ -247,31 +248,30 @@ namespace osu.Game.Tests.Visual.Online // channelManager.JoinChannel(channel1); // channelManager.JoinChannel(channel2); // }); - + // // // Close channels 1 and channel 2 // AddStep("Leave channel 2", () => channelManager.LeaveChannel(channel2)); // AddStep("Leave channel 1", () => channelManager.LeaveChannel(channel1)); // AddAssert("All channels closed", () => !channelManager.JoinedChannels.Any()); - + // // pressControlShiftT(); // // Channel 1 should now be open again. Channel 2 should not // AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); // AddAssert("channel 2 closed", () => !channelManager.JoinedChannels.Contains(channel2)); - + // // pressControlShiftT(); - + // // // Both Channel 1 and Channel 2 should be open // AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); // AddAssert("channel 2 open", () => channelManager.JoinedChannels.Contains(channel2)); - + // // // Processing this again should have not effect // pressControlShiftT(); // AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); // AddAssert("channel 2 open", () => channelManager.JoinedChannels.Contains(channel2)); - + // // // Close channel 2 again // AddStep("Leave channel 2", () => channelManager.LeaveChannel(channel2)); - // // Both channel 1 and channel 2 should be open // pressControlShiftT(); // AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); From 5dc917c1081c52a7bf2978d69eb11fecd0022604 Mon Sep 17 00:00:00 2001 From: Joseph-Ramos-CMU Date: Mon, 14 Dec 2020 22:30:16 -0500 Subject: [PATCH 031/839] Modified test case I believe the tests are failing because the shortcut only works when you select a channel on the channel dropdown. So I updated the testcases to do this. --- osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index ce590839c2..2db6bf6220 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -233,6 +233,8 @@ namespace osu.Game.Tests.Visual.Online AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); AddAssert("channel 2 closed", () => !channelManager.JoinedChannels.Contains(channel2)); + AddStep("Select channel 1", () => clickDrawable(chatOverlay.TabMap[channel1])); + pressControlShiftT(); // Channel 2 should now be open again From c000d6d622512eead83e310ae9e0a116a4f9b288 Mon Sep 17 00:00:00 2001 From: Joseph-Ramos-CMU Date: Mon, 14 Dec 2020 23:30:57 -0500 Subject: [PATCH 032/839] Minor formatting change Still trying to fix the CI problems --- .../Visual/Online/TestSceneChatOverlay.cs | 37 ------------------- 1 file changed, 37 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index 2db6bf6220..15fb12df6a 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -242,43 +242,6 @@ namespace osu.Game.Tests.Visual.Online AddAssert("channel 2 open", () => channelManager.JoinedChannels.Contains(channel2)); } - // [Test] - // public void TestCtrlShiftTShortcut2() - // { - // AddStep("Join 2 channels", () => - // { - // channelManager.JoinChannel(channel1); - // channelManager.JoinChannel(channel2); - // }); - // - // // Close channels 1 and channel 2 - // AddStep("Leave channel 2", () => channelManager.LeaveChannel(channel2)); - // AddStep("Leave channel 1", () => channelManager.LeaveChannel(channel1)); - // AddAssert("All channels closed", () => !channelManager.JoinedChannels.Any()); - // - // pressControlShiftT(); - // // Channel 1 should now be open again. Channel 2 should not - // AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); - // AddAssert("channel 2 closed", () => !channelManager.JoinedChannels.Contains(channel2)); - // - // pressControlShiftT(); - // - // // Both Channel 1 and Channel 2 should be open - // AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); - // AddAssert("channel 2 open", () => channelManager.JoinedChannels.Contains(channel2)); - // - // // Processing this again should have not effect - // pressControlShiftT(); - // AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); - // AddAssert("channel 2 open", () => channelManager.JoinedChannels.Contains(channel2)); - // - // // Close channel 2 again - // AddStep("Leave channel 2", () => channelManager.LeaveChannel(channel2)); - // // Both channel 1 and channel 2 should be open - // pressControlShiftT(); - // AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); - // AddAssert("channel 2 open", () => channelManager.JoinedChannels.Contains(channel2)); - // } private void pressChannelHotkey(int number) { var channelKey = Key.Number0 + number; From 16a103952282b574cf2893cf9c028f31c1ee8e55 Mon Sep 17 00:00:00 2001 From: Joseph-Ramos-CMU Date: Tue, 15 Dec 2020 00:00:43 -0500 Subject: [PATCH 033/839] Completely removed all CI tests THe GitHub CI is still failing for unknown reasons. I tried to remove them all to see what the problem really is --- .../Visual/Online/TestSceneChatOverlay.cs | 34 ------------------- 1 file changed, 34 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index 15fb12df6a..fca642ad6c 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -208,40 +208,6 @@ namespace osu.Game.Tests.Visual.Online AddAssert("All channels closed", () => !channelManager.JoinedChannels.Any()); } - // Control-Shift-T should reopen the most recently closed tab that has not - // already been reopened - private void pressControlShiftT() - { - AddStep("Press and hold Control", () => InputManager.PressKey(Key.ControlLeft)); - AddStep("Press and hold Shift", () => InputManager.PressKey(Key.ShiftLeft)); - AddStep("Press T", () => InputManager.Key(Key.T)); - AddStep("Release Control", () => InputManager.ReleaseKey(Key.ControlLeft)); - AddStep("Release Shift", () => InputManager.ReleaseKey(Key.ShiftLeft)); - } - - [Test] - public void TestCtrlShiftTShortcut1() - { - AddStep("Join 2 channels", () => - { - channelManager.JoinChannel(channel1); - channelManager.JoinChannel(channel2); - }); - - // Close channel 2 - AddStep("Leave channel 2", () => channelManager.LeaveChannel(channel2)); - AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); - AddAssert("channel 2 closed", () => !channelManager.JoinedChannels.Contains(channel2)); - - AddStep("Select channel 1", () => clickDrawable(chatOverlay.TabMap[channel1])); - - pressControlShiftT(); - - // Channel 2 should now be open again - AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); - AddAssert("channel 2 open", () => channelManager.JoinedChannels.Contains(channel2)); - } - private void pressChannelHotkey(int number) { var channelKey = Key.Number0 + number; From bd32dbdc2f6a9491ba931356140799d687ad3b8a Mon Sep 17 00:00:00 2001 From: Joseph-Ramos-CMU Date: Tue, 15 Dec 2020 00:10:11 -0500 Subject: [PATCH 034/839] Added back a modified test case Added a simple test for Ctrl+Shift+T. --- .../Visual/Online/TestSceneChatOverlay.cs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index fca642ad6c..8795e50645 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -208,6 +208,41 @@ namespace osu.Game.Tests.Visual.Online AddAssert("All channels closed", () => !channelManager.JoinedChannels.Any()); } + // Control-Shift-T should reopen the most recently closed tab that has not + // already been reopened + private void pressControlShiftT() + { + AddStep("Press and hold Control", () => InputManager.PressKey(Key.ControlLeft)); + AddStep("Press and hold Shift", () => InputManager.PressKey(Key.ShiftLeft)); + AddStep("Press T", () => InputManager.Key(Key.T)); + AddStep("Release Control", () => InputManager.ReleaseKey(Key.ControlLeft)); + AddStep("Release Shift", () => InputManager.ReleaseKey(Key.ShiftLeft)); + } + + [Test] + public void TestCtrlShiftTShortcut1() + { + AddStep("Join 2 channels", () => + { + channelManager.JoinChannel(channel1); + channelManager.JoinChannel(channel2); + }); + + // Close channel 2 + AddStep("Select channel 2", () => clickDrawable(chatOverlay.TabMap[channel2])); + AddStep("Close channel 2", () => clickDrawable(((TestPrivateChannelTabItem)chatOverlay.TabMap[channel2]).CloseButton.Child)); + AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); + AddAssert("channel 2 closed", () => !channelManager.JoinedChannels.Contains(channel2)); + + AddStep("Select channel 1", () => clickDrawable(chatOverlay.TabMap[channel1])); + + pressControlShiftT(); + + // Channel 2 should now be open again + AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); + AddAssert("channel 2 open", () => channelManager.JoinedChannels.Contains(channel2)); + } + private void pressChannelHotkey(int number) { var channelKey = Key.Number0 + number; From 1b702b2b1f0341f1336351f8ebded3874caa1030 Mon Sep 17 00:00:00 2001 From: Joseph-Ramos-CMU Date: Tue, 15 Dec 2020 11:57:51 -0500 Subject: [PATCH 035/839] Removed all CI tests Just trying to get a commit that passes all CI tests --- .../Visual/Online/TestSceneChatOverlay.cs | 35 ------------------- 1 file changed, 35 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index 8795e50645..fca642ad6c 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -208,41 +208,6 @@ namespace osu.Game.Tests.Visual.Online AddAssert("All channels closed", () => !channelManager.JoinedChannels.Any()); } - // Control-Shift-T should reopen the most recently closed tab that has not - // already been reopened - private void pressControlShiftT() - { - AddStep("Press and hold Control", () => InputManager.PressKey(Key.ControlLeft)); - AddStep("Press and hold Shift", () => InputManager.PressKey(Key.ShiftLeft)); - AddStep("Press T", () => InputManager.Key(Key.T)); - AddStep("Release Control", () => InputManager.ReleaseKey(Key.ControlLeft)); - AddStep("Release Shift", () => InputManager.ReleaseKey(Key.ShiftLeft)); - } - - [Test] - public void TestCtrlShiftTShortcut1() - { - AddStep("Join 2 channels", () => - { - channelManager.JoinChannel(channel1); - channelManager.JoinChannel(channel2); - }); - - // Close channel 2 - AddStep("Select channel 2", () => clickDrawable(chatOverlay.TabMap[channel2])); - AddStep("Close channel 2", () => clickDrawable(((TestPrivateChannelTabItem)chatOverlay.TabMap[channel2]).CloseButton.Child)); - AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); - AddAssert("channel 2 closed", () => !channelManager.JoinedChannels.Contains(channel2)); - - AddStep("Select channel 1", () => clickDrawable(chatOverlay.TabMap[channel1])); - - pressControlShiftT(); - - // Channel 2 should now be open again - AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); - AddAssert("channel 2 open", () => channelManager.JoinedChannels.Contains(channel2)); - } - private void pressChannelHotkey(int number) { var channelKey = Key.Number0 + number; From 8bd787dce976fb4564d5ebb26f283f1cbfa67e86 Mon Sep 17 00:00:00 2001 From: Jess Meng Date: Tue, 15 Dec 2020 20:50:23 -0800 Subject: [PATCH 036/839] Moved private methods beneath public ones --- .../Visual/Online/TestSceneChatOverlay.cs | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index 68fc8ac8c0..9d0be85a3a 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -208,13 +208,6 @@ namespace osu.Game.Tests.Visual.Online AddAssert("All channels closed", () => !channelManager.JoinedChannels.Any()); } - private void pressControlW() - { - AddStep("Press and hold Control", () => InputManager.PressKey(Key.ControlLeft)); - AddStep("Press W", () => InputManager.Key(Key.W)); - AddStep("Release Control", () => InputManager.ReleaseKey(Key.ControlLeft)); - } - [Test] public void TestCtrlWShortcut() { @@ -240,13 +233,6 @@ namespace osu.Game.Tests.Visual.Online AddAssert("All channels closed", () => !channelManager.JoinedChannels.Any()); } - private void pressControlT() - { - AddStep("Press and hold Control", () => InputManager.PressKey(Key.ControlLeft)); - AddStep("Press T", () => InputManager.Key(Key.T)); - AddStep("Release Control", () => InputManager.ReleaseKey(Key.ControlLeft)); - } - [Test] public void TestCtrlTShortcut() { @@ -270,6 +256,20 @@ namespace osu.Game.Tests.Visual.Online InputManager.ReleaseKey(Key.AltLeft); } + private void pressControlW() + { + AddStep("Press and hold Control", () => InputManager.PressKey(Key.ControlLeft)); + AddStep("Press W", () => InputManager.Key(Key.W)); + AddStep("Release Control", () => InputManager.ReleaseKey(Key.ControlLeft)); + } + + private void pressControlT() + { + AddStep("Press and hold Control", () => InputManager.PressKey(Key.ControlLeft)); + AddStep("Press T", () => InputManager.Key(Key.T)); + AddStep("Release Control", () => InputManager.ReleaseKey(Key.ControlLeft)); + } + private void clickDrawable(Drawable d) { InputManager.MoveMouseTo(d); From 7b169c4f62428c69fbaee874eb22ca278bb72f78 Mon Sep 17 00:00:00 2001 From: Joseph-Ramos-CMU Date: Wed, 16 Dec 2020 10:13:50 -0500 Subject: [PATCH 037/839] Changed internal closedChannel list to store channels names instead of channel objects This is for efficiency purposes; it's more efficient to just store the names than store the whole object. --- osu.Game/Online/Chat/ChannelManager.cs | 27 ++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 607c9a1223..303696c294 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -33,8 +33,8 @@ namespace osu.Game.Online.Chat private readonly BindableList availableChannels = new BindableList(); private readonly BindableList joinedChannels = new BindableList(); - // Keeps a list of closed channels. More recently closed channels appear at higher indeces - private readonly BindableList closedChannels = new BindableList(); + // Keeps a list of closed channel identifiers + private readonly BindableList closedChannels = new BindableList(); // For efficiency purposes, this constant bounds the number of closed channels we store. // This number is somewhat arbitrary; future developers are free to modify it. @@ -424,7 +424,7 @@ namespace osu.Game.Online.Chat } // insert at the end of the closedChannels list - closedChannels.Insert(closedChannels.Count, channel); + closedChannels.Insert(closedChannels.Count, channel.Name); if (channel.Joined.Value) { @@ -445,13 +445,24 @@ namespace osu.Game.Online.Chat return; } - Channel lastClosedChannel = closedChannels.Last(); + string lastClosedChannelName = closedChannels.Last(); + closedChannels.RemoveAt(closedChannels.Count - 1); - closedChannels.Remove(lastClosedChannel); + // types did not work with existing enumerable interfaces funcitons like Contains + for (int i = 0; i < joinedChannels.Count; i++) + { + // If the user already joined the channel, try the next + // channel in the list + if (joinedChannels[i].Name == lastClosedChannelName) + { + JoinLastClosedChannel(); + return; + } + } - // If the user already joined the channel, try the next - // channel in the list - if (joinedChannels.IndexOf(lastClosedChannel) >= 0) + Channel lastClosedChannel = AvailableChannels.FirstOrDefault(c => c.Name == lastClosedChannelName); + + if (lastClosedChannel == null) { JoinLastClosedChannel(); } From 2f8a085adfb89ce696f6eb1212a63d9287da46fc Mon Sep 17 00:00:00 2001 From: Joseph-Ramos-CMU Date: Wed, 16 Dec 2020 12:04:07 -0500 Subject: [PATCH 038/839] Reworked reopening last tab to no longer use recursion A reviewer of the pull request was concerned about recursion. I changed the code to be iterative. --- osu.Game/Online/Chat/ChannelManager.cs | 56 ++++++++++++++++---------- 1 file changed, 35 insertions(+), 21 deletions(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 303696c294..1bbd2c3471 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -445,33 +445,47 @@ namespace osu.Game.Online.Chat return; } - string lastClosedChannelName = closedChannels.Last(); - closedChannels.RemoveAt(closedChannels.Count - 1); - - // types did not work with existing enumerable interfaces funcitons like Contains - for (int i = 0; i < joinedChannels.Count; i++) + // This nested loop could be eliminated if a check was added so that + // when the code opens a channel it removes from the closedChannel list. + // However, this would require adding an O(|closeChannels|) work operation + // every time the user joins a channel, which would make joining a channel + // slower. I wanted to centralize all major slowdowns so they + // can only occur if the user actually decides to use this feature. + for (int i = closedChannels.Count - 1; i >= 0; i--) { - // If the user already joined the channel, try the next - // channel in the list - if (joinedChannels[i].Name == lastClosedChannelName) + string lastClosedChannelName = closedChannels.Last(); + closedChannels.RemoveAt(closedChannels.Count - 1); + bool alreadyJoined = false; + // If the user already joined the channel, do not + // try to join it + for (int j = 0; j < joinedChannels.Count; j++) { - JoinLastClosedChannel(); - return; + if (joinedChannels[j].Name == lastClosedChannelName) + { + alreadyJoined = true; + break; + } + } + + if (alreadyJoined == false) + { + Channel lastClosedChannel = AvailableChannels.FirstOrDefault(c => c.Name == lastClosedChannelName); + + if (lastClosedChannel == null) + { + continue; + } + else + { + JoinChannel(lastClosedChannel); + return; + } } } - - Channel lastClosedChannel = AvailableChannels.FirstOrDefault(c => c.Name == lastClosedChannelName); - - if (lastClosedChannel == null) - { - JoinLastClosedChannel(); - } - else - { - JoinChannel(lastClosedChannel); - } } + + private long lastMessageId; private bool channelsInitialised; From b37a983fbf6397d2acc8a244dd408f62b8305f18 Mon Sep 17 00:00:00 2001 From: Joseph-Ramos-CMU Date: Wed, 16 Dec 2020 12:56:36 -0500 Subject: [PATCH 039/839] Formatting fixes Fixed some formatting issues to comply with InspectCode. --- osu.Game/Online/Chat/ChannelManager.cs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 1bbd2c3471..51bbe6ca45 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -447,7 +447,7 @@ namespace osu.Game.Online.Chat // This nested loop could be eliminated if a check was added so that // when the code opens a channel it removes from the closedChannel list. - // However, this would require adding an O(|closeChannels|) work operation + // However, this would require adding an O(|closeChannels|) work operation // every time the user joins a channel, which would make joining a channel // slower. I wanted to centralize all major slowdowns so they // can only occur if the user actually decides to use this feature. @@ -456,6 +456,7 @@ namespace osu.Game.Online.Chat string lastClosedChannelName = closedChannels.Last(); closedChannels.RemoveAt(closedChannels.Count - 1); bool alreadyJoined = false; + // If the user already joined the channel, do not // try to join it for (int j = 0; j < joinedChannels.Count; j++) @@ -471,11 +472,7 @@ namespace osu.Game.Online.Chat { Channel lastClosedChannel = AvailableChannels.FirstOrDefault(c => c.Name == lastClosedChannelName); - if (lastClosedChannel == null) - { - continue; - } - else + if (lastClosedChannel != null) { JoinChannel(lastClosedChannel); return; @@ -484,8 +481,6 @@ namespace osu.Game.Online.Chat } } - - private long lastMessageId; private bool channelsInitialised; From 71a082110abe74015dfbaf62b7e6b1dca7b2a726 Mon Sep 17 00:00:00 2001 From: Angela Zhang Date: Thu, 17 Dec 2020 16:56:34 -0600 Subject: [PATCH 040/839] Making style changes + supports reopening PM chats --- osu.Game/Online/Chat/ChannelManager.cs | 54 ++++++++++++++------------ 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 51bbe6ca45..54ef3c6c30 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -33,8 +33,12 @@ namespace osu.Game.Online.Chat private readonly BindableList availableChannels = new BindableList(); private readonly BindableList joinedChannels = new BindableList(); - // Keeps a list of closed channel identifiers - private readonly BindableList closedChannels = new BindableList(); + + /// + /// Keeps a list of closed channel identifiers. Stores the channel ID for normal + /// channels, or the user ID for PM channels + /// + private readonly BindableList closedChannelIds = new BindableList(); // For efficiency purposes, this constant bounds the number of closed channels we store. // This number is somewhat arbitrary; future developers are free to modify it. @@ -418,13 +422,13 @@ namespace osu.Game.Online.Chat // Prevent the closedChannel list from exceeding the max size // by removing the oldest element - if (closedChannels.Count >= closed_channels_max_size) + if (closedChannelIds.Count >= closed_channels_max_size) { - closedChannels.RemoveAt(0); + closedChannelIds.RemoveAt(0); } - // insert at the end of the closedChannels list - closedChannels.Insert(closedChannels.Count, channel.Name); + // For PM channels, we store the user ID; else, we store the channel id + closedChannelIds.Add(channel.Type == ChannelType.PM ? channel.Users.First().Id : channel.Id); if (channel.Joined.Value) { @@ -440,7 +444,7 @@ namespace osu.Game.Online.Chat /// public void JoinLastClosedChannel() { - if (closedChannels.Count <= 0) + if (closedChannelIds.Count <= 0) { return; } @@ -451,32 +455,32 @@ namespace osu.Game.Online.Chat // every time the user joins a channel, which would make joining a channel // slower. I wanted to centralize all major slowdowns so they // can only occur if the user actually decides to use this feature. - for (int i = closedChannels.Count - 1; i >= 0; i--) + while (closedChannelIds.Count != 0) { - string lastClosedChannelName = closedChannels.Last(); - closedChannels.RemoveAt(closedChannels.Count - 1); - bool alreadyJoined = false; + long lastClosedChannelId = closedChannelIds.Last(); + closedChannelIds.RemoveAt(closedChannelIds.Count - 1); - // If the user already joined the channel, do not - // try to join it - for (int j = 0; j < joinedChannels.Count; j++) - { - if (joinedChannels[j].Name == lastClosedChannelName) - { - alreadyJoined = true; - break; - } - } + bool lookupCondition(Channel ch) => + ch.Type == ChannelType.PM ? ch.Users.Any(u => u.Id == lastClosedChannelId) + : ch.Id == lastClosedChannelId; - if (alreadyJoined == false) + // If the user hasn't already joined the channel, try to join it + if (joinedChannels.FirstOrDefault(lookupCondition) == null) { - Channel lastClosedChannel = AvailableChannels.FirstOrDefault(c => c.Name == lastClosedChannelName); + Channel lastClosedChannel = AvailableChannels.FirstOrDefault(lookupCondition); if (lastClosedChannel != null) { - JoinChannel(lastClosedChannel); - return; + CurrentChannel.Value = JoinChannel(lastClosedChannel); } + else + { + // Try to get User to open PM chat + var req = new GetUserRequest(lastClosedChannelId); + req.Success += user => CurrentChannel.Value = JoinChannel(new Channel(user)); + api.Queue(req); + } + return; } } } From cbfa292c05d0d67e57c60066cc3181a692c4d6ef Mon Sep 17 00:00:00 2001 From: Angela Zhang Date: Thu, 17 Dec 2020 16:59:19 -0600 Subject: [PATCH 041/839] Minor test style fixes --- .../Visual/Online/TestSceneChatOverlay.cs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index 9d0be85a3a..12a24963c4 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -220,15 +220,15 @@ namespace osu.Game.Tests.Visual.Online // Want to close channel 2 AddStep("Select channel 2", () => clickDrawable(chatOverlay.TabMap[channel2])); - pressControlW(); + AddStep("Press Ctrl + W", () => pressControlW()); // Channel 2 should be closed - AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); - AddAssert("channel 2 closed", () => !channelManager.JoinedChannels.Contains(channel2)); + AddAssert("Channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); + AddAssert("Channel 2 closed", () => !channelManager.JoinedChannels.Contains(channel2)); // Want to close channel 1 AddStep("Select channel 1", () => clickDrawable(chatOverlay.TabMap[channel1])); - pressControlW(); + AddStep("Press Ctrl + W", () => pressControlW()); // Channel 1 and channel 2 should be closed AddAssert("All channels closed", () => !channelManager.JoinedChannels.Any()); } @@ -243,7 +243,7 @@ namespace osu.Game.Tests.Visual.Online }); // Want to join another channel - pressControlT(); + AddStep("Press Ctrl + T", () => pressControlT()); // Selector should be visible AddAssert("Selector is visible", () => chatOverlay.SelectionOverlayState == Visibility.Visible); } @@ -258,16 +258,16 @@ namespace osu.Game.Tests.Visual.Online private void pressControlW() { - AddStep("Press and hold Control", () => InputManager.PressKey(Key.ControlLeft)); - AddStep("Press W", () => InputManager.Key(Key.W)); - AddStep("Release Control", () => InputManager.ReleaseKey(Key.ControlLeft)); + InputManager.PressKey(Key.ControlLeft); + InputManager.Key(Key.W); + InputManager.ReleaseKey(Key.ControlLeft); } private void pressControlT() { - AddStep("Press and hold Control", () => InputManager.PressKey(Key.ControlLeft)); - AddStep("Press T", () => InputManager.Key(Key.T)); - AddStep("Release Control", () => InputManager.ReleaseKey(Key.ControlLeft)); + InputManager.PressKey(Key.ControlLeft); + InputManager.Key(Key.T); + InputManager.ReleaseKey(Key.ControlLeft); } private void clickDrawable(Drawable d) From 454c7538c0dcc38fc823ae1d59d43475d9ccd8c9 Mon Sep 17 00:00:00 2001 From: Angela Zhang Date: Thu, 17 Dec 2020 17:59:36 -0600 Subject: [PATCH 042/839] CI Style Fixes --- osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs | 7 ++++--- osu.Game/Online/Chat/ChannelManager.cs | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index 12a24963c4..52dafbcfb2 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -219,8 +219,8 @@ namespace osu.Game.Tests.Visual.Online // Want to close channel 2 AddStep("Select channel 2", () => clickDrawable(chatOverlay.TabMap[channel2])); + AddStep("Press Ctrl + W", pressControlW); - AddStep("Press Ctrl + W", () => pressControlW()); // Channel 2 should be closed AddAssert("Channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); AddAssert("Channel 2 closed", () => !channelManager.JoinedChannels.Contains(channel2)); @@ -228,7 +228,7 @@ namespace osu.Game.Tests.Visual.Online // Want to close channel 1 AddStep("Select channel 1", () => clickDrawable(chatOverlay.TabMap[channel1])); - AddStep("Press Ctrl + W", () => pressControlW()); + AddStep("Press Ctrl + W", pressControlW); // Channel 1 and channel 2 should be closed AddAssert("All channels closed", () => !channelManager.JoinedChannels.Any()); } @@ -243,7 +243,8 @@ namespace osu.Game.Tests.Visual.Online }); // Want to join another channel - AddStep("Press Ctrl + T", () => pressControlT()); + AddStep("Press Ctrl + T", pressControlT); + // Selector should be visible AddAssert("Selector is visible", () => chatOverlay.SelectionOverlayState == Visibility.Visible); } diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 54ef3c6c30..5936c4e580 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -33,7 +33,6 @@ namespace osu.Game.Online.Chat private readonly BindableList availableChannels = new BindableList(); private readonly BindableList joinedChannels = new BindableList(); - /// /// Keeps a list of closed channel identifiers. Stores the channel ID for normal /// channels, or the user ID for PM channels @@ -461,8 +460,9 @@ namespace osu.Game.Online.Chat closedChannelIds.RemoveAt(closedChannelIds.Count - 1); bool lookupCondition(Channel ch) => - ch.Type == ChannelType.PM ? ch.Users.Any(u => u.Id == lastClosedChannelId) - : ch.Id == lastClosedChannelId; + ch.Type == ChannelType.PM + ? ch.Users.Any(u => u.Id == lastClosedChannelId) + : ch.Id == lastClosedChannelId; // If the user hasn't already joined the channel, try to join it if (joinedChannels.FirstOrDefault(lookupCondition) == null) @@ -480,6 +480,7 @@ namespace osu.Game.Online.Chat req.Success += user => CurrentChannel.Value = JoinChannel(new Channel(user)); api.Queue(req); } + return; } } From 45482e8709d50527aa9a292cb0c0599390bff50d Mon Sep 17 00:00:00 2001 From: Angela Zhang Date: Thu, 17 Dec 2020 18:43:39 -0600 Subject: [PATCH 043/839] Whitespace fixes --- osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs | 2 +- osu.Game/Online/Chat/ChannelManager.cs | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index 52dafbcfb2..8cb2d3df95 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -244,7 +244,7 @@ namespace osu.Game.Tests.Visual.Online // Want to join another channel AddStep("Press Ctrl + T", pressControlT); - + // Selector should be visible AddAssert("Selector is visible", () => chatOverlay.SelectionOverlayState == Visibility.Visible); } diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 5936c4e580..164330e78b 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -459,10 +459,9 @@ namespace osu.Game.Online.Chat long lastClosedChannelId = closedChannelIds.Last(); closedChannelIds.RemoveAt(closedChannelIds.Count - 1); - bool lookupCondition(Channel ch) => - ch.Type == ChannelType.PM - ? ch.Users.Any(u => u.Id == lastClosedChannelId) - : ch.Id == lastClosedChannelId; + bool lookupCondition(Channel ch) => ch.Type == ChannelType.PM + ? ch.Users.Any(u => u.Id == lastClosedChannelId) + : ch.Id == lastClosedChannelId; // If the user hasn't already joined the channel, try to join it if (joinedChannels.FirstOrDefault(lookupCondition) == null) @@ -480,7 +479,7 @@ namespace osu.Game.Online.Chat req.Success += user => CurrentChannel.Value = JoinChannel(new Channel(user)); api.Queue(req); } - + return; } } From 7d326c7f2462d72f1d87d7e1d4e6ddf90fc0cafc Mon Sep 17 00:00:00 2001 From: Angela Zhang Date: Sun, 20 Dec 2020 13:18:00 -0600 Subject: [PATCH 044/839] Review changes + added tests --- .../Visual/Online/TestSceneChatOverlay.cs | 58 ++++++++++++- osu.Game/Online/Chat/ChannelManager.cs | 81 ++++++++++++------- 2 files changed, 110 insertions(+), 29 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index 8cb2d3df95..beb069d2a2 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -34,6 +34,7 @@ namespace osu.Game.Tests.Visual.Online private Channel previousChannel => joinedChannels.ElementAt(joinedChannels.ToList().IndexOf(currentChannel) - 1); private Channel channel1 => channels[0]; private Channel channel2 => channels[1]; + private Channel channel3 => channels[2]; public TestSceneChatOverlay() { @@ -42,7 +43,8 @@ namespace osu.Game.Tests.Visual.Online { Name = $"Channel no. {index}", Topic = index == 3 ? null : $"We talk about the number {index} here", - Type = index % 2 == 0 ? ChannelType.PM : ChannelType.Temporary + Type = index % 2 == 0 ? ChannelType.PM : ChannelType.Temporary, + Id = index }) .ToList(); } @@ -249,6 +251,51 @@ namespace osu.Game.Tests.Visual.Online AddAssert("Selector is visible", () => chatOverlay.SelectionOverlayState == Visibility.Visible); } + [Test] + public void TestCtrlShiftTShortcut() + { + AddStep("Join 3 channels", () => + { + channelManager.JoinChannel(channel1); + channelManager.JoinChannel(channel2); + channelManager.JoinChannel(channel3); + }); + + // Should do nothing + AddStep("Press Ctrl + Shift + T", pressControlShiftT); + AddAssert("All channels still open", () => channelManager.JoinedChannels.Count == 3); + + // Close channel 1 + AddStep("Select channel 1", () => clickDrawable(chatOverlay.TabMap[channel1])); + AddStep("Click normal close button", () => clickDrawable(((TestChannelTabItem)chatOverlay.TabMap[channel1]).CloseButton.Child)); + AddAssert("Channel 1 closed", () => !channelManager.JoinedChannels.Contains(channel1)); + AddAssert("Other channels still open", () => channelManager.JoinedChannels.Count == 2); + + // Reopen channel 1 + AddStep("Press Ctrl + Shift + T", pressControlShiftT); + AddAssert("All channels now open", () => channelManager.JoinedChannels.Count == 3); + AddAssert("Current channel is channel 1", () => currentChannel == channel1); + + // Close two channels + AddStep("Select channel 1", () => clickDrawable(chatOverlay.TabMap[channel1])); + AddStep("Close channel 1", () => clickDrawable(((TestChannelTabItem)chatOverlay.TabMap[channel1]).CloseButton.Child)); + AddStep("Select channel 2", () => clickDrawable(chatOverlay.TabMap[channel2])); + AddStep("Close channel 2", () => clickDrawable(((TestPrivateChannelTabItem)chatOverlay.TabMap[channel2]).CloseButton.Child)); + AddAssert("Only one channel open", () => channelManager.JoinedChannels.Count == 1); + AddAssert("Current channel is channel 3", () => currentChannel == channel3); + + // Should first re-open channel 2 + AddStep("Press Ctrl + Shift + T", pressControlShiftT); + AddAssert("Channel 1 still closed", () => !channelManager.JoinedChannels.Contains(channel1)); + AddAssert("Channel 2 now open", () => channelManager.JoinedChannels.Contains(channel2)); + AddAssert("Current channel is channel 2", () => currentChannel == channel2); + + // Should then re-open channel 1 + AddStep("Press Ctrl + Shift + T", pressControlShiftT); + AddAssert("All channels now open", () => channelManager.JoinedChannels.Count == 3); + AddAssert("Current channel is channel 1", () => currentChannel == channel1); + } + private void pressChannelHotkey(int number) { var channelKey = Key.Number0 + number; @@ -271,6 +318,15 @@ namespace osu.Game.Tests.Visual.Online InputManager.ReleaseKey(Key.ControlLeft); } + private void pressControlShiftT() + { + InputManager.PressKey(Key.ControlLeft); + InputManager.PressKey(Key.ShiftLeft); + InputManager.Key(Key.T); + InputManager.ReleaseKey(Key.ShiftLeft); + InputManager.ReleaseKey(Key.ControlLeft); + } + private void clickDrawable(Drawable d) { InputManager.MoveMouseTo(d); diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 164330e78b..ecf5c6e245 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Logging; +using osu.Game.Database; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Overlays.Chat.Tabs; @@ -34,10 +35,9 @@ namespace osu.Game.Online.Chat private readonly BindableList joinedChannels = new BindableList(); /// - /// Keeps a list of closed channel identifiers. Stores the channel ID for normal - /// channels, or the user ID for PM channels + /// Keeps a stack of recently closed channels /// - private readonly BindableList closedChannelIds = new BindableList(); + private readonly BindableList closedChannels = new BindableList(); // For efficiency purposes, this constant bounds the number of closed channels we store. // This number is somewhat arbitrary; future developers are free to modify it. @@ -62,6 +62,10 @@ namespace osu.Game.Online.Chat [Resolved] private IAPIProvider api { get; set; } + + [Resolved] + private UserLookupCache users { get; set; } + public readonly BindableBool HighPollRate = new BindableBool(); public ChannelManager() @@ -421,13 +425,15 @@ namespace osu.Game.Online.Chat // Prevent the closedChannel list from exceeding the max size // by removing the oldest element - if (closedChannelIds.Count >= closed_channels_max_size) + if (closedChannels.Count >= closed_channels_max_size) { - closedChannelIds.RemoveAt(0); + closedChannels.RemoveAt(0); } // For PM channels, we store the user ID; else, we store the channel id - closedChannelIds.Add(channel.Type == ChannelType.PM ? channel.Users.First().Id : channel.Id); + closedChannels.Add(channel.Type == ChannelType.PM + ? new ClosedChannel(ChannelType.PM, channel.Users.Single().Id) + : new ClosedChannel(channel.Type, channel.Id)); if (channel.Joined.Value) { @@ -443,41 +449,36 @@ namespace osu.Game.Online.Chat /// public void JoinLastClosedChannel() { - if (closedChannelIds.Count <= 0) - { - return; - } - - // This nested loop could be eliminated if a check was added so that + // This loop could be eliminated if a check was added so that // when the code opens a channel it removes from the closedChannel list. // However, this would require adding an O(|closeChannels|) work operation // every time the user joins a channel, which would make joining a channel - // slower. I wanted to centralize all major slowdowns so they + // slower. We wanted to centralize all major slowdowns so they // can only occur if the user actually decides to use this feature. - while (closedChannelIds.Count != 0) + while (closedChannels.Count > 0) { - long lastClosedChannelId = closedChannelIds.Last(); - closedChannelIds.RemoveAt(closedChannelIds.Count - 1); - - bool lookupCondition(Channel ch) => ch.Type == ChannelType.PM - ? ch.Users.Any(u => u.Id == lastClosedChannelId) - : ch.Id == lastClosedChannelId; + ClosedChannel lastClosedChannel = closedChannels.Last(); + closedChannels.RemoveAt(closedChannels.Count - 1); // If the user hasn't already joined the channel, try to join it - if (joinedChannels.FirstOrDefault(lookupCondition) == null) + if (joinedChannels.FirstOrDefault(lastClosedChannel.IsEqual) == null) { - Channel lastClosedChannel = AvailableChannels.FirstOrDefault(lookupCondition); + Channel lastChannel = AvailableChannels.FirstOrDefault(lastClosedChannel.IsEqual); - if (lastClosedChannel != null) + if (lastChannel != null) { - CurrentChannel.Value = JoinChannel(lastClosedChannel); + // Channel exists as an availaable channel, directly join it + CurrentChannel.Value = JoinChannel(lastChannel); } - else + else if (lastClosedChannel.Type == ChannelType.PM) { // Try to get User to open PM chat - var req = new GetUserRequest(lastClosedChannelId); - req.Success += user => CurrentChannel.Value = JoinChannel(new Channel(user)); - api.Queue(req); + users.GetUserAsync((int)lastClosedChannel.Id).ContinueWith(u => + { + if (u.Result == null) return; + + Schedule(() => CurrentChannel.Value = JoinChannel(new Channel(u.Result))); + }); } return; @@ -569,4 +570,28 @@ namespace osu.Game.Online.Chat { } } + + /// + /// Class that stores information about a closed channel + /// + public class ClosedChannel + { + public ChannelType Type; + public long Id; + + public ClosedChannel(ChannelType type, long id) + { + Type = type; + Id = id; + } + + public bool IsEqual(Channel channel) + { + if (channel.Type != this.Type) return false; + + return this.Type == ChannelType.PM + ? channel.Users.Single().Id == this.Id + : channel.Id == this.Id; + } + } } From 74bd2f41e60b35553f2bad451c1c225a224eebea Mon Sep 17 00:00:00 2001 From: Angela Zhang Date: Sun, 20 Dec 2020 13:51:39 -0600 Subject: [PATCH 045/839] Style fixes --- osu.Game/Online/Chat/ChannelManager.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index ecf5c6e245..f4d1aeae84 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -62,7 +62,6 @@ namespace osu.Game.Online.Chat [Resolved] private IAPIProvider api { get; set; } - [Resolved] private UserLookupCache users { get; set; } @@ -587,11 +586,11 @@ namespace osu.Game.Online.Chat public bool IsEqual(Channel channel) { - if (channel.Type != this.Type) return false; + if (channel.Type != Type) return false; - return this.Type == ChannelType.PM - ? channel.Users.Single().Id == this.Id - : channel.Id == this.Id; + return Type == ChannelType.PM + ? channel.Users.Single().Id == Id + : channel.Id == Id; } } } From d83abfa7d2cbcfcdb2922f1756a3b2b8fac18ff5 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 23 Jan 2021 23:29:32 +0300 Subject: [PATCH 046/839] Add test scene with failing test case --- .../TestSceneUpdateableBeatmapSetCover.cs | 130 ++++++++++++++++++ .../Drawables/UpdateableBeatmapSetCover.cs | 14 +- 2 files changed, 137 insertions(+), 7 deletions(-) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapSetCover.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapSetCover.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapSetCover.cs new file mode 100644 index 0000000000..7daaae45a4 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapSetCover.cs @@ -0,0 +1,130 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Drawables; +using osu.Game.Graphics.Containers; +using osuTK; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneUpdateableBeatmapSetCover : OsuTestScene + { + [Test] + public void TestLocal([Values] BeatmapSetCoverType coverType) + { + AddStep("setup cover", () => Child = new UpdateableBeatmapSetCover + { + CoverType = coverType, + BeatmapSet = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet, + RelativeSizeAxes = Axes.Both, + Masking = true, + }); + + AddUntilStep("wait for load", () => this.ChildrenOfType().SingleOrDefault()?.IsLoaded ?? false); + } + + [Test] + public void TestUnloadAndReload() + { + OsuScrollContainer scroll = null; + List covers = new List(); + + AddStep("setup covers", () => + { + BeatmapSetInfo setInfo = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet; + + FillFlowContainer fillFlow; + + Child = scroll = new OsuScrollContainer + { + Size = new Vector2(500f), + Child = fillFlow = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(10), + Padding = new MarginPadding { Bottom = 550 } + } + }; + + var coverTypes = Enum.GetValues(); + + for (int i = 0; i < 25; i++) + { + var coverType = coverTypes[i % coverTypes.Length]; + + var cover = new UpdateableBeatmapSetCover + { + CoverType = coverType, + BeatmapSet = setInfo, + Height = 100, + Masking = true, + }; + + if (coverType == BeatmapSetCoverType.Cover) + cover.Width = 500; + else if (coverType == BeatmapSetCoverType.Card) + cover.Width = 400; + else if (coverType == BeatmapSetCoverType.List) + cover.Size = new Vector2(100, 50); + + fillFlow.Add(cover); + covers.Add(cover); + } + }); + + var loadedCovers = covers.Where(c => c.ChildrenOfType().SingleOrDefault()?.IsLoaded ?? false); + + AddUntilStep("some loaded", () => loadedCovers.Any()); + AddStep("scroll to end", () => scroll.ScrollToEnd()); + AddUntilStep("all unloaded", () => !loadedCovers.Any()); + } + + [Test] + public void TestSetNullBeatmapWhileLoading() + { + TestUpdateableBeatmapSetCover updateableCover = null; + + AddStep("setup cover", () => Child = updateableCover = new TestUpdateableBeatmapSetCover + { + BeatmapSet = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet, + RelativeSizeAxes = Axes.Both, + Masking = true, + }); + + AddStep("change model", () => updateableCover.BeatmapSet = null); + AddWaitStep("wait some", 5); + AddAssert("no cover added", () => !updateableCover.ChildrenOfType().Any()); + } + + private class TestUpdateableBeatmapSetCover : UpdateableBeatmapSetCover + { + protected override BeatmapSetCover CreateBeatmapSetCover(BeatmapSetInfo beatmapSet, BeatmapSetCoverType coverType) => new TestBeatmapSetCover(beatmapSet, coverType); + } + + private class TestBeatmapSetCover : BeatmapSetCover + { + public TestBeatmapSetCover(BeatmapSetInfo set, BeatmapSetCoverType type = BeatmapSetCoverType.Cover) + : base(set, type) + { + } + + [BackgroundDependencyLoader] + private void load() + { + Thread.Sleep(10000); + } + } + } +} diff --git a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs index 6c229755e7..4b0430381b 100644 --- a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs +++ b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs @@ -59,6 +59,8 @@ namespace osu.Game.Beatmaps.Drawables updateCover(); } + protected virtual BeatmapSetCover CreateBeatmapSetCover(BeatmapSetInfo beatmapSet, BeatmapSetCoverType coverType) => new BeatmapSetCover(beatmapSet, coverType); + private void updateCover() { displayedCover?.FadeOut(400); @@ -69,13 +71,11 @@ namespace osu.Game.Beatmaps.Drawables { Add(displayedCover = new DelayedLoadUnloadWrapper(() => { - var cover = new BeatmapSetCover(beatmapSet, coverType) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - FillMode = FillMode.Fill, - }; + var cover = CreateBeatmapSetCover(beatmapSet, coverType); + cover.Anchor = Anchor.Centre; + cover.Origin = Anchor.Centre; + cover.RelativeSizeAxes = Axes.Both; + cover.FillMode = FillMode.Fill; cover.OnLoadComplete += d => d.FadeInFromZero(400, Easing.Out); return cover; })); From acfb2d2980a99d7f88657db43712a3ed88caaac6 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 23 Jan 2021 23:28:46 +0300 Subject: [PATCH 047/839] Refactor beatmap set covers into using `ModelBackedDrawable` --- .../TestSceneUpdateableBeatmapSetCover.cs | 24 ++++-- .../Drawables/UpdateableBeatmapSetCover.cs | 79 ++++++------------- .../Historical/DrawableMostPlayedBeatmap.cs | 3 +- 3 files changed, 44 insertions(+), 62 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapSetCover.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapSetCover.cs index 7daaae45a4..2ffa7e2668 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapSetCover.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapSetCover.cs @@ -22,9 +22,8 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestLocal([Values] BeatmapSetCoverType coverType) { - AddStep("setup cover", () => Child = new UpdateableBeatmapSetCover + AddStep("setup cover", () => Child = new UpdateableBeatmapSetCover(coverType) { - CoverType = coverType, BeatmapSet = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet, RelativeSizeAxes = Axes.Both, Masking = true, @@ -64,9 +63,8 @@ namespace osu.Game.Tests.Visual.UserInterface { var coverType = coverTypes[i % coverTypes.Length]; - var cover = new UpdateableBeatmapSetCover + var cover = new UpdateableBeatmapSetCover(coverType) { - CoverType = coverType, BeatmapSet = setInfo, Height = 100, Masking = true, @@ -110,13 +108,25 @@ namespace osu.Game.Tests.Visual.UserInterface private class TestUpdateableBeatmapSetCover : UpdateableBeatmapSetCover { - protected override BeatmapSetCover CreateBeatmapSetCover(BeatmapSetInfo beatmapSet, BeatmapSetCoverType coverType) => new TestBeatmapSetCover(beatmapSet, coverType); + protected override Drawable CreateDrawable(BeatmapSetInfo model) + { + if (model == null) + return null; + + return new TestBeatmapSetCover(model) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fill, + }; + } } private class TestBeatmapSetCover : BeatmapSetCover { - public TestBeatmapSetCover(BeatmapSetInfo set, BeatmapSetCoverType type = BeatmapSetCoverType.Cover) - : base(set, type) + public TestBeatmapSetCover(BeatmapSetInfo set) + : base(set) { } diff --git a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs index 4b0430381b..3a4423e42e 100644 --- a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs +++ b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.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.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -8,78 +9,50 @@ using osu.Game.Graphics; namespace osu.Game.Beatmaps.Drawables { - public class UpdateableBeatmapSetCover : Container + public class UpdateableBeatmapSetCover : ModelBackedDrawable { - private Drawable displayedCover; - - private BeatmapSetInfo beatmapSet; + private readonly BeatmapSetCoverType coverType; public BeatmapSetInfo BeatmapSet { - get => beatmapSet; - set - { - if (value == beatmapSet) return; - - beatmapSet = value; - - if (IsLoaded) - updateCover(); - } + get => Model; + set => Model = value; } - private BeatmapSetCoverType coverType = BeatmapSetCoverType.Cover; - - public BeatmapSetCoverType CoverType + public new bool Masking { - get => coverType; - set - { - if (value == coverType) return; - - coverType = value; - - if (IsLoaded) - updateCover(); - } + get => base.Masking; + set => base.Masking = value; } - public UpdateableBeatmapSetCover() + public UpdateableBeatmapSetCover(BeatmapSetCoverType coverType = BeatmapSetCoverType.Cover) { - Child = new Box + this.coverType = coverType; + + InternalChild = new Box { RelativeSizeAxes = Axes.Both, Colour = OsuColour.Gray(0.2f), }; } - protected override void LoadComplete() + protected override double TransformDuration => 400.0; + + protected override DelayedLoadWrapper CreateDelayedLoadWrapper(Func createContentFunc, double timeBeforeLoad) + => new DelayedLoadUnloadWrapper(createContentFunc, timeBeforeLoad); + + protected override Drawable CreateDrawable(BeatmapSetInfo model) { - base.LoadComplete(); - updateCover(); - } + if (model == null) + return null; - protected virtual BeatmapSetCover CreateBeatmapSetCover(BeatmapSetInfo beatmapSet, BeatmapSetCoverType coverType) => new BeatmapSetCover(beatmapSet, coverType); - - private void updateCover() - { - displayedCover?.FadeOut(400); - displayedCover?.Expire(); - displayedCover = null; - - if (beatmapSet != null) + return new BeatmapSetCover(model, coverType) { - Add(displayedCover = new DelayedLoadUnloadWrapper(() => - { - var cover = CreateBeatmapSetCover(beatmapSet, coverType); - cover.Anchor = Anchor.Centre; - cover.Origin = Anchor.Centre; - cover.RelativeSizeAxes = Axes.Both; - cover.FillMode = FillMode.Fill; - cover.OnLoadComplete += d => d.FadeInFromZero(400, Easing.Out); - return cover; - })); - } + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + FillMode = FillMode.Fill, + }; } } } diff --git a/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs b/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs index 5b7c5efbe2..f409411953 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs @@ -41,12 +41,11 @@ namespace osu.Game.Overlays.Profile.Sections.Historical { AddRangeInternal(new Drawable[] { - new UpdateableBeatmapSetCover + new UpdateableBeatmapSetCover(BeatmapSetCoverType.List) { RelativeSizeAxes = Axes.Y, Width = cover_width, BeatmapSet = beatmap.BeatmapSet, - CoverType = BeatmapSetCoverType.List, }, new Container { From faa221027444b36fd3089888119c39964bbdbe4a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 24 Jan 2021 00:55:45 +0300 Subject: [PATCH 048/839] Use old-style Enum.GetValues method for older than .NET 5 --- .../UserInterface/TestSceneUpdateableBeatmapSetCover.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapSetCover.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapSetCover.cs index 2ffa7e2668..2f60fbcda8 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapSetCover.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapSetCover.cs @@ -57,11 +57,15 @@ namespace osu.Game.Tests.Visual.UserInterface } }; +#if NET5_0 var coverTypes = Enum.GetValues(); +#else + var coverTypes = Enum.GetValues(typeof(BeatmapSetCoverType)).Cast(); +#endif for (int i = 0; i < 25; i++) { - var coverType = coverTypes[i % coverTypes.Length]; + var coverType = coverTypes.ElementAt(i); var cover = new UpdateableBeatmapSetCover(coverType) { From 85b8b00b8c10ff23ca10169d5ca9940ea67eb1bb Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 24 Jan 2021 00:59:16 +0300 Subject: [PATCH 049/839] Fix forgotten modulo --- .../Visual/UserInterface/TestSceneUpdateableBeatmapSetCover.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapSetCover.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapSetCover.cs index 2f60fbcda8..0b9bcad9fe 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapSetCover.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapSetCover.cs @@ -65,7 +65,7 @@ namespace osu.Game.Tests.Visual.UserInterface for (int i = 0; i < 25; i++) { - var coverType = coverTypes.ElementAt(i); + var coverType = coverTypes.ElementAt(i % coverTypes.Count()); var cover = new UpdateableBeatmapSetCover(coverType) { From d034728443f395cccc3b0764158ed679a738fb93 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 24 Jan 2021 01:05:45 +0300 Subject: [PATCH 050/839] Remove conditional compilation symbols for such case --- .../UserInterface/TestSceneUpdateableBeatmapSetCover.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapSetCover.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapSetCover.cs index 0b9bcad9fe..ecb076d356 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapSetCover.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapSetCover.cs @@ -57,11 +57,7 @@ namespace osu.Game.Tests.Visual.UserInterface } }; -#if NET5_0 - var coverTypes = Enum.GetValues(); -#else var coverTypes = Enum.GetValues(typeof(BeatmapSetCoverType)).Cast(); -#endif for (int i = 0; i < 25; i++) { From 323e4ac26b8172865bc6fbede9c39fb05ee5da40 Mon Sep 17 00:00:00 2001 From: Samuel Cattini-Schultz Date: Sun, 21 Feb 2021 18:24:27 +1100 Subject: [PATCH 051/839] Refactor catch Movement skill to not require explicit clockrate usage In catch, rate adjustment mods do not only affect the timings of hitobjects, but also the speed of the player's catcher. This catcher speed change has an impact on difficulty which is currently accounted for by using the clockrate directly in calculations. Semantically this is a bad idea because clockrate adjustments are supposed to be fully accounted for in DifficultyHitObjects, but passing clockrate here for the purpose of being used as catcher speed doesn't make much sense, especially since it is copied in every DifficultyHitObject despite being the same value. It makes more sense to account for this catch specific impact by handling rate adjustment mods in a catch specific way, or more specifically in a Movement skill specific way. --- .../Preprocessing/CatchDifficultyHitObject.cs | 3 --- .../Difficulty/Skills/Movement.cs | 15 +++++++++++++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs b/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs index d936ef97ac..e19098c580 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs @@ -24,8 +24,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing /// public readonly double StrainTime; - public readonly double ClockRate; - public CatchDifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate, float halfCatcherWidth) : base(hitObject, lastObject, clockRate) { @@ -37,7 +35,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing // Every strain interval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure StrainTime = Math.Max(40, DeltaTime); - ClockRate = clockRate; } } } diff --git a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs index 9ad719be1a..7d61be7bb1 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Linq; using osu.Game.Rulesets.Catch.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; @@ -26,10 +27,20 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills private float lastDistanceMoved; private double lastStrainTime; + /// + /// The speed multiplier applied to the player's catcher. + /// + private readonly double catcherSpeedMultiplier; + public Movement(Mod[] mods, float halfCatcherWidth) : base(mods) { HalfCatcherWidth = halfCatcherWidth; + + // In catch, rate adjustment mods do not only affect the timings of hitobjects, + // but also the speed of the player's catcher, which has an impact on difficulty + var rateAdjustMod = mods.FirstOrDefault(m => m is ModRateAdjust); + catcherSpeedMultiplier = (rateAdjustMod as ModRateAdjust)?.SpeedChange.Value ?? 1; } protected override double StrainValueOf(DifficultyHitObject current) @@ -46,7 +57,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills float distanceMoved = playerPosition - lastPlayerPosition.Value; - double weightedStrainTime = catchCurrent.StrainTime + 13 + (3 / catchCurrent.ClockRate); + double weightedStrainTime = catchCurrent.StrainTime + 13 + (3 / catcherSpeedMultiplier); double distanceAddition = (Math.Pow(Math.Abs(distanceMoved), 1.3) / 510); double sqrtStrain = Math.Sqrt(weightedStrainTime); @@ -79,7 +90,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills playerPosition = catchCurrent.NormalizedPosition; } - distanceAddition *= 1.0 + edgeDashBonus * ((20 - catchCurrent.LastObject.DistanceToHyperDash) / 20) * Math.Pow((Math.Min(catchCurrent.StrainTime * catchCurrent.ClockRate, 265) / 265), 1.5); // Edge Dashes are easier at lower ms values + distanceAddition *= 1.0 + edgeDashBonus * ((20 - catchCurrent.LastObject.DistanceToHyperDash) / 20) * Math.Pow((Math.Min(catchCurrent.StrainTime * catcherSpeedMultiplier, 265) / 265), 1.5); // Edge Dashes are easier at lower ms values } lastPlayerPosition = playerPosition; From 34429a02e7fac260f7c247d5b28b6744cefff5ec Mon Sep 17 00:00:00 2001 From: ilsubyeega Date: Sun, 28 Mar 2021 03:13:05 +0900 Subject: [PATCH 052/839] Use avatar_url from user first instead of a.ppy.sh in DrawableAvatar --- osu.Game/Users/Drawables/DrawableAvatar.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game/Users/Drawables/DrawableAvatar.cs b/osu.Game/Users/Drawables/DrawableAvatar.cs index 3dae3afe3f..15ef7d2df0 100644 --- a/osu.Game/Users/Drawables/DrawableAvatar.cs +++ b/osu.Game/Users/Drawables/DrawableAvatar.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; +using osu.Game.Online.API; namespace osu.Game.Users.Drawables { @@ -13,6 +14,9 @@ namespace osu.Game.Users.Drawables { private readonly User user; + [Resolved(CanBeNull=true)] + private IAPIProvider api { get; set; } + /// /// A simple, non-interactable avatar sprite for the specified user. /// @@ -30,9 +34,11 @@ namespace osu.Game.Users.Drawables [BackgroundDependencyLoader] private void load(LargeTextureStore textures) { - if (user != null && user.Id > 1) + if (api != null && user?.AvatarUrl != null) + Texture = textures.Get(user.AvatarUrl.StartsWith('/') ? $"{api.WebsiteRootUrl}{user.AvatarUrl}" : user.AvatarUrl); + else if (user != null && user.Id > 1) Texture = textures.Get($@"https://a.ppy.sh/{user.Id}"); - + Texture ??= textures.Get(@"Online/avatar-guest"); } From be08460bea4c10391dd980b2d437a5ee4364e248 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 27 Mar 2021 19:59:33 +0100 Subject: [PATCH 053/839] Fix formatting issues --- osu.Game/Users/Drawables/DrawableAvatar.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Users/Drawables/DrawableAvatar.cs b/osu.Game/Users/Drawables/DrawableAvatar.cs index 15ef7d2df0..079d4a932c 100644 --- a/osu.Game/Users/Drawables/DrawableAvatar.cs +++ b/osu.Game/Users/Drawables/DrawableAvatar.cs @@ -14,7 +14,7 @@ namespace osu.Game.Users.Drawables { private readonly User user; - [Resolved(CanBeNull=true)] + [Resolved(CanBeNull = true)] private IAPIProvider api { get; set; } /// @@ -38,7 +38,7 @@ namespace osu.Game.Users.Drawables Texture = textures.Get(user.AvatarUrl.StartsWith('/') ? $"{api.WebsiteRootUrl}{user.AvatarUrl}" : user.AvatarUrl); else if (user != null && user.Id > 1) Texture = textures.Get($@"https://a.ppy.sh/{user.Id}"); - + Texture ??= textures.Get(@"Online/avatar-guest"); } From 32df02084db91ecb7ab419a04df4f60d61f30ca4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 27 Mar 2021 20:00:49 +0100 Subject: [PATCH 054/839] Extract variable to avoid redundant accesses --- osu.Game/Users/Drawables/DrawableAvatar.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Users/Drawables/DrawableAvatar.cs b/osu.Game/Users/Drawables/DrawableAvatar.cs index 079d4a932c..c672c6fa1d 100644 --- a/osu.Game/Users/Drawables/DrawableAvatar.cs +++ b/osu.Game/Users/Drawables/DrawableAvatar.cs @@ -34,8 +34,9 @@ namespace osu.Game.Users.Drawables [BackgroundDependencyLoader] private void load(LargeTextureStore textures) { - if (api != null && user?.AvatarUrl != null) - Texture = textures.Get(user.AvatarUrl.StartsWith('/') ? $"{api.WebsiteRootUrl}{user.AvatarUrl}" : user.AvatarUrl); + string avatarUrl = user?.AvatarUrl; + if (api != null && avatarUrl != null) + Texture = textures.Get(avatarUrl.StartsWith('/') ? $"{api.WebsiteRootUrl}{avatarUrl}" : avatarUrl); else if (user != null && user.Id > 1) Texture = textures.Get($@"https://a.ppy.sh/{user.Id}"); From b3d3c7ecacf7762c22f5c2f556164c134ce9e8cc Mon Sep 17 00:00:00 2001 From: ilsubyeega Date: Mon, 29 Mar 2021 20:01:20 +0900 Subject: [PATCH 055/839] Revert relative url checking to AvatarUrl --- osu.Game/Users/Drawables/DrawableAvatar.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Users/Drawables/DrawableAvatar.cs b/osu.Game/Users/Drawables/DrawableAvatar.cs index c672c6fa1d..98fc10d7ed 100644 --- a/osu.Game/Users/Drawables/DrawableAvatar.cs +++ b/osu.Game/Users/Drawables/DrawableAvatar.cs @@ -36,7 +36,7 @@ namespace osu.Game.Users.Drawables { string avatarUrl = user?.AvatarUrl; if (api != null && avatarUrl != null) - Texture = textures.Get(avatarUrl.StartsWith('/') ? $"{api.WebsiteRootUrl}{avatarUrl}" : avatarUrl); + Texture = textures.Get(avatarUrl); else if (user != null && user.Id > 1) Texture = textures.Get($@"https://a.ppy.sh/{user.Id}"); From dc3af1e0f7634b373c43957188ec1bfec7f0a3f3 Mon Sep 17 00:00:00 2001 From: ilsubyeega Date: Mon, 29 Mar 2021 20:05:24 +0900 Subject: [PATCH 056/839] Remove IAPIProvider since its not required at DrawableAvatar --- osu.Game/Users/Drawables/DrawableAvatar.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Users/Drawables/DrawableAvatar.cs b/osu.Game/Users/Drawables/DrawableAvatar.cs index 98fc10d7ed..930f1c07a1 100644 --- a/osu.Game/Users/Drawables/DrawableAvatar.cs +++ b/osu.Game/Users/Drawables/DrawableAvatar.cs @@ -5,7 +5,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; -using osu.Game.Online.API; namespace osu.Game.Users.Drawables { @@ -14,9 +13,6 @@ namespace osu.Game.Users.Drawables { private readonly User user; - [Resolved(CanBeNull = true)] - private IAPIProvider api { get; set; } - /// /// A simple, non-interactable avatar sprite for the specified user. /// @@ -35,7 +31,7 @@ namespace osu.Game.Users.Drawables private void load(LargeTextureStore textures) { string avatarUrl = user?.AvatarUrl; - if (api != null && avatarUrl != null) + if (avatarUrl != null) Texture = textures.Get(avatarUrl); else if (user != null && user.Id > 1) Texture = textures.Get($@"https://a.ppy.sh/{user.Id}"); From 0c1f624b56f662bedaee8b530fa9584e9753b59b Mon Sep 17 00:00:00 2001 From: ilsubyeega Date: Tue, 30 Mar 2021 17:03:01 +0900 Subject: [PATCH 057/839] Simply code under assuming that avatarUrl always non-null --- osu.Game/Users/Drawables/DrawableAvatar.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game/Users/Drawables/DrawableAvatar.cs b/osu.Game/Users/Drawables/DrawableAvatar.cs index 930f1c07a1..ef074813a5 100644 --- a/osu.Game/Users/Drawables/DrawableAvatar.cs +++ b/osu.Game/Users/Drawables/DrawableAvatar.cs @@ -30,11 +30,8 @@ namespace osu.Game.Users.Drawables [BackgroundDependencyLoader] private void load(LargeTextureStore textures) { - string avatarUrl = user?.AvatarUrl; - if (avatarUrl != null) - Texture = textures.Get(avatarUrl); - else if (user != null && user.Id > 1) - Texture = textures.Get($@"https://a.ppy.sh/{user.Id}"); + if (user != null && user.Id > 1) + Texture = textures.Get(user.AvatarUrl); Texture ??= textures.Get(@"Online/avatar-guest"); } From 5117c51105419a3d48774b86d4a04b893e0cc47f Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Mon, 19 Apr 2021 16:24:47 +0700 Subject: [PATCH 058/839] initial wiki header --- .../Visual/Online/TestSceneWikiHeader.cs | 25 +++++++++++++++ osu.Game/Overlays/Wiki/WikiHeader.cs | 31 +++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs create mode 100644 osu.Game/Overlays/Wiki/WikiHeader.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs new file mode 100644 index 0000000000..51d8abc516 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs @@ -0,0 +1,25 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Overlays; +using osu.Game.Overlays.Wiki; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneWikiHeader : OsuTestScene + { + [Cached] + private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Orange); + + public TestSceneWikiHeader() + { + Child = new WikiHeader + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre + }; + } + } +} diff --git a/osu.Game/Overlays/Wiki/WikiHeader.cs b/osu.Game/Overlays/Wiki/WikiHeader.cs new file mode 100644 index 0000000000..91377c63da --- /dev/null +++ b/osu.Game/Overlays/Wiki/WikiHeader.cs @@ -0,0 +1,31 @@ +// 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; + +namespace osu.Game.Overlays.Wiki +{ + public class WikiHeader : BreadcrumbControlOverlayHeader + { + private const string index_page_string = "index"; + + public WikiHeader() + { + TabControl.AddItem(index_page_string); + } + + protected override Drawable CreateBackground() => new OverlayHeaderBackground(@"Headers/wiki"); + + protected override OverlayTitle CreateTitle() => new WikiHeaderTitle(); + + private class WikiHeaderTitle : OverlayTitle + { + public WikiHeaderTitle() + { + Title = "wiki"; + Description = "knowledge base"; + IconTexture = "Icons/Hexacons/wiki"; + } + } + } +} From 623eae15762f4cf796dfbd2fbab26b64a3aaa142 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Apr 2021 17:06:01 +0900 Subject: [PATCH 059/839] Add basic language switching ability --- .../ResourceManagerLocalisationStore.cs | 57 +++++++++++++++++++ osu.Game/OsuGame.cs | 7 +++ .../Sections/General/LanguageSettings.cs | 18 ++++++ 3 files changed, 82 insertions(+) create mode 100644 osu.Game/Localisation/ResourceManagerLocalisationStore.cs diff --git a/osu.Game/Localisation/ResourceManagerLocalisationStore.cs b/osu.Game/Localisation/ResourceManagerLocalisationStore.cs new file mode 100644 index 0000000000..dd84eff55f --- /dev/null +++ b/osu.Game/Localisation/ResourceManagerLocalisationStore.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.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Resources; +using System.Threading.Tasks; +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public class ResourceManagerLocalisationStore : ILocalisationStore + { + private readonly Dictionary resourceManagers = new Dictionary(); + + public ResourceManagerLocalisationStore(string cultureCode) + { + EffectiveCulture = new CultureInfo(cultureCode); + } + + public void Dispose() + { + } + + public string Get(string lookup) + { + var split = lookup.Split(':'); + + string ns = split[0]; + string key = split[1]; + + if (!resourceManagers.TryGetValue(ns, out var manager)) + resourceManagers[ns] = manager = new ResourceManager(ns, GetType().Assembly); + + return manager.GetString(key, EffectiveCulture); + } + + public Task GetAsync(string lookup) + { + return Task.FromResult(Get(lookup)); + } + + public Stream GetStream(string name) + { + throw new NotImplementedException(); + } + + public IEnumerable GetAvailableResources() + { + throw new NotImplementedException(); + } + + public CultureInfo EffectiveCulture { get; } + } +} diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 28f32ba455..9af9a34b46 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -51,6 +51,7 @@ using osu.Game.Utils; using LogLevel = osu.Framework.Logging.LogLevel; using osu.Game.Database; using osu.Game.IO; +using osu.Game.Localisation; namespace osu.Game { @@ -541,6 +542,12 @@ namespace osu.Game { base.LoadComplete(); + foreach (var language in Enum.GetValues(typeof(Language)).OfType()) + { + var cultureCode = language.ToString(); + Localisation.AddLanguage(cultureCode, new ResourceManagerLocalisationStore(cultureCode)); + } + // The next time this is updated is in UpdateAfterChildren, which occurs too late and results // in the cursor being shown for a few frames during the intro. // This prevents the cursor from showing until we have a screen with CursorVisible = true diff --git a/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs b/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs index 44e42ecbfe..c2767f61b4 100644 --- a/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs @@ -1,27 +1,45 @@ // 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.Bindables; using osu.Framework.Configuration; using osu.Framework.Graphics; +using osu.Game.Localisation; namespace osu.Game.Overlays.Settings.Sections.General { public class LanguageSettings : SettingsSubsection { + private SettingsDropdown languageSelection; + private Bindable frameworkLocale; + protected override string Header => "Language"; [BackgroundDependencyLoader] private void load(FrameworkConfigManager frameworkConfig) { + frameworkLocale = frameworkConfig.GetBindable(FrameworkSetting.Locale); + Children = new Drawable[] { + languageSelection = new SettingsEnumDropdown + { + LabelText = "Language", + }, new SettingsCheckbox { LabelText = "Prefer metadata in original language", Current = frameworkConfig.GetBindable(FrameworkSetting.ShowUnicode) }, }; + + if (!Enum.TryParse(frameworkLocale.Value, out var locale)) + locale = Language.en; + languageSelection.Current.Value = locale; + + languageSelection.Current.BindValueChanged(val => frameworkLocale.Value = val.NewValue.ToString()); } } } From db524e2395b375e6882eaef9b5424380f70eaa23 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Apr 2021 17:06:12 +0900 Subject: [PATCH 060/839] Add localisation support to DialogButton's text --- osu.Game/Graphics/UserInterface/DialogButton.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/DialogButton.cs b/osu.Game/Graphics/UserInterface/DialogButton.cs index 9b53ee7b2d..1047aa4255 100644 --- a/osu.Game/Graphics/UserInterface/DialogButton.cs +++ b/osu.Game/Graphics/UserInterface/DialogButton.cs @@ -15,6 +15,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics.Effects; using osu.Game.Graphics.Containers; using osu.Framework.Input.Events; +using osu.Framework.Localisation; namespace osu.Game.Graphics.UserInterface { @@ -180,9 +181,9 @@ namespace osu.Game.Graphics.UserInterface } } - private string text; + private LocalisableString text; - public string Text + public LocalisableString Text { get => text; set From 60acd824cbc0c1ce6d7d3ff95bd9759e943f2dd3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Apr 2021 17:06:26 +0900 Subject: [PATCH 061/839] Add two sample implementations --- osu.Game/Localisation/Common.ja.resx | 17 ++++++++++++++++ osu.Game/Localisation/Common.resx | 17 ++++++++++++++++ osu.Game/Localisation/CommonStrings.cs | 19 ++++++++++++++++++ osu.Game/Localisation/Language.cs | 16 +++++++++++++++ osu.Game/Localisation/MainMenu.ja.resx | 17 ++++++++++++++++ osu.Game/Localisation/MainMenu.resx | 17 ++++++++++++++++ osu.Game/Localisation/MainMenuStrings.cs | 24 +++++++++++++++++++++++ osu.Game/Overlays/Dialog/ConfirmDialog.cs | 2 +- osu.Game/Screens/Menu/Button.cs | 3 ++- osu.Game/Screens/Menu/ButtonSystem.cs | 8 +++++--- 10 files changed, 135 insertions(+), 5 deletions(-) create mode 100644 osu.Game/Localisation/Common.ja.resx create mode 100644 osu.Game/Localisation/Common.resx create mode 100644 osu.Game/Localisation/CommonStrings.cs create mode 100644 osu.Game/Localisation/Language.cs create mode 100644 osu.Game/Localisation/MainMenu.ja.resx create mode 100644 osu.Game/Localisation/MainMenu.resx create mode 100644 osu.Game/Localisation/MainMenuStrings.cs diff --git a/osu.Game/Localisation/Common.ja.resx b/osu.Game/Localisation/Common.ja.resx new file mode 100644 index 0000000000..174751c455 --- /dev/null +++ b/osu.Game/Localisation/Common.ja.resx @@ -0,0 +1,17 @@ + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + やめとくわ + + diff --git a/osu.Game/Localisation/Common.resx b/osu.Game/Localisation/Common.resx new file mode 100644 index 0000000000..f63fb90086 --- /dev/null +++ b/osu.Game/Localisation/Common.resx @@ -0,0 +1,17 @@ + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Cancel + + diff --git a/osu.Game/Localisation/CommonStrings.cs b/osu.Game/Localisation/CommonStrings.cs new file mode 100644 index 0000000000..f448158191 --- /dev/null +++ b/osu.Game/Localisation/CommonStrings.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.Localisation; + +namespace osu.Game.Localisation +{ + public static class CommonStrings + { + private const string prefix = "osu.Game.Localisation.Common"; + + /// + /// "Cancel" + /// + public static LocalisableString Cancel => new TranslatableString(getKey("cancel"), "Cancel"); + + private static string getKey(string key) => $"{prefix}:{key}"; + } +} diff --git a/osu.Game/Localisation/Language.cs b/osu.Game/Localisation/Language.cs new file mode 100644 index 0000000000..edcf264c7f --- /dev/null +++ b/osu.Game/Localisation/Language.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.ComponentModel; + +namespace osu.Game.Localisation +{ + public enum Language + { + [Description("English")] + en, + + [Description("日本語")] + ja + } +} diff --git a/osu.Game/Localisation/MainMenu.ja.resx b/osu.Game/Localisation/MainMenu.ja.resx new file mode 100644 index 0000000000..20c85110ad --- /dev/null +++ b/osu.Game/Localisation/MainMenu.ja.resx @@ -0,0 +1,17 @@ + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ソロ + + \ No newline at end of file diff --git a/osu.Game/Localisation/MainMenu.resx b/osu.Game/Localisation/MainMenu.resx new file mode 100644 index 0000000000..845b412d88 --- /dev/null +++ b/osu.Game/Localisation/MainMenu.resx @@ -0,0 +1,17 @@ + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + solo + + diff --git a/osu.Game/Localisation/MainMenuStrings.cs b/osu.Game/Localisation/MainMenuStrings.cs new file mode 100644 index 0000000000..fd9647467a --- /dev/null +++ b/osu.Game/Localisation/MainMenuStrings.cs @@ -0,0 +1,24 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class MainMenuStrings + { + private const string prefix = "osu.Game.Localisation.MainMenu"; + + /// + /// "solo" + /// + public static LocalisableString Solo => new TranslatableString(getKey("solo"), "solo"); + + /// + /// "multi" + /// + public static LocalisableString Multi => new TranslatableString(getKey("multi"), "multi"); + + private static string getKey(string key) => $"{prefix}:{key}"; + } +} diff --git a/osu.Game/Overlays/Dialog/ConfirmDialog.cs b/osu.Game/Overlays/Dialog/ConfirmDialog.cs index a87c06ffdf..d1c0d746d1 100644 --- a/osu.Game/Overlays/Dialog/ConfirmDialog.cs +++ b/osu.Game/Overlays/Dialog/ConfirmDialog.cs @@ -33,7 +33,7 @@ namespace osu.Game.Overlays.Dialog }, new PopupDialogCancelButton { - Text = @"Cancel", + Text = Localisation.CommonStrings.Cancel, Action = onCancel }, }; diff --git a/osu.Game/Screens/Menu/Button.cs b/osu.Game/Screens/Menu/Button.cs index d956394ebb..26f26d1304 100644 --- a/osu.Game/Screens/Menu/Button.cs +++ b/osu.Game/Screens/Menu/Button.cs @@ -19,6 +19,7 @@ using osu.Game.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; +using osu.Framework.Localisation; using osu.Game.Beatmaps.ControlPoints; namespace osu.Game.Screens.Menu @@ -50,7 +51,7 @@ namespace osu.Game.Screens.Menu public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => box.ReceivePositionalInputAt(screenSpacePos); - public Button(string text, string sampleName, IconUsage symbol, Color4 colour, Action clickAction = null, float extraWidth = 0, Key triggerKey = Key.Unknown) + public Button(LocalisableString text, string sampleName, IconUsage symbol, Color4 colour, Action clickAction = null, float extraWidth = 0, Key triggerKey = Key.Unknown) { this.sampleName = sampleName; this.clickAction = clickAction; diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 81b1cb0bf1..ff5ad37b9d 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -15,6 +15,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Framework.Localisation; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Framework.Threading; @@ -22,6 +23,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Input; using osu.Game.Input.Bindings; +using osu.Game.Localisation; using osu.Game.Online.API; using osu.Game.Overlays; using osu.Game.Overlays.Notifications; @@ -121,10 +123,10 @@ namespace osu.Game.Screens.Menu private LoginOverlay loginOverlay { get; set; } [BackgroundDependencyLoader(true)] - private void load(AudioManager audio, IdleTracker idleTracker, GameHost host) + private void load(AudioManager audio, IdleTracker idleTracker, GameHost host, LocalisationManager strings) { - buttonsPlay.Add(new Button(@"solo", @"button-solo-select", FontAwesome.Solid.User, new Color4(102, 68, 204, 255), () => OnSolo?.Invoke(), WEDGE_WIDTH, Key.P)); - buttonsPlay.Add(new Button(@"multi", @"button-generic-select", FontAwesome.Solid.Users, new Color4(94, 63, 186, 255), onMultiplayer, 0, Key.M)); + buttonsPlay.Add(new Button(MainMenuStrings.Solo, @"button-solo-select", FontAwesome.Solid.User, new Color4(102, 68, 204, 255), () => OnSolo?.Invoke(), WEDGE_WIDTH, Key.P)); + buttonsPlay.Add(new Button(MainMenuStrings.Multi, @"button-generic-select", FontAwesome.Solid.Users, new Color4(94, 63, 186, 255), onMultiplayer, 0, Key.M)); buttonsPlay.Add(new Button(@"playlists", @"button-generic-select", OsuIcon.Charts, new Color4(94, 63, 186, 255), onPlaylists, 0, Key.L)); buttonsPlay.ForEach(b => b.VisibleState = ButtonSystemState.Play); From 460d656a0e504ec85b2da9be90d4b89fbc050e15 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Wed, 21 Apr 2021 16:21:07 +0700 Subject: [PATCH 062/839] initial wiki overlay --- .../Visual/Online/TestSceneWikiOverlay.cs | 22 +++++++++++++++++++ osu.Game/Overlays/WikiOverlay.cs | 17 ++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs create mode 100644 osu.Game/Overlays/WikiOverlay.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs new file mode 100644 index 0000000000..737c97c0bd --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs @@ -0,0 +1,22 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Overlays; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneWikiOverlay : OsuTestScene + { + private WikiOverlay wiki; + + [SetUp] + public void SetUp() => Schedule(() => Child = wiki = new WikiOverlay()); + + [Test] + public void TestOverlay() + { + AddStep("Show", () => wiki.Show()); + } + } +} diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs new file mode 100644 index 0000000000..7105fbf953 --- /dev/null +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -0,0 +1,17 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Overlays.Wiki; + +namespace osu.Game.Overlays +{ + public class WikiOverlay : OnlineOverlay + { + public WikiOverlay() + : base(OverlayColourScheme.Orange, false) + { + } + + protected override WikiHeader CreateHeader() => new WikiHeader(); + } +} From f3555ad08cfdc71a61efeb88fde64104484bf8e5 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 20 Apr 2021 16:12:32 +0700 Subject: [PATCH 063/839] add APIWikiPage response --- .../API/Requests/Responses/APIWikiPage.cs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 osu.Game/Online/API/Requests/Responses/APIWikiPage.cs diff --git a/osu.Game/Online/API/Requests/Responses/APIWikiPage.cs b/osu.Game/Online/API/Requests/Responses/APIWikiPage.cs new file mode 100644 index 0000000000..957396b17a --- /dev/null +++ b/osu.Game/Online/API/Requests/Responses/APIWikiPage.cs @@ -0,0 +1,32 @@ +// 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 Newtonsoft.Json; + +namespace osu.Game.Online.API.Requests.Responses +{ + public class APIWikiPage + { + [JsonProperty("layout")] + public string Layout { get; set; } + + [JsonProperty("locale")] + public string Locale { get; set; } + + [JsonProperty("markdown")] + public string Markdown { get; set; } + + [JsonProperty("path")] + public string Path { get; set; } + + [JsonProperty("subtitle")] + public string Subtitle { get; set; } + + [JsonProperty("tags")] + public List Tags { get; set; } + + [JsonProperty("title")] + public string Title { get; set; } + } +} From d4013bd8852aaaa833004969037089477279cfd6 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 20 Apr 2021 16:40:30 +0700 Subject: [PATCH 064/839] add GetWikiRequest --- .../Online/API/Requests/GetWikiRequest.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 osu.Game/Online/API/Requests/GetWikiRequest.cs diff --git a/osu.Game/Online/API/Requests/GetWikiRequest.cs b/osu.Game/Online/API/Requests/GetWikiRequest.cs new file mode 100644 index 0000000000..248fcc03e3 --- /dev/null +++ b/osu.Game/Online/API/Requests/GetWikiRequest.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.Game.Online.API.Requests.Responses; + +namespace osu.Game.Online.API.Requests +{ + public class GetWikiRequest : APIRequest + { + private readonly string path; + private readonly string locale; + + public GetWikiRequest(string path, string locale = "en") + { + this.path = path; + this.locale = locale; + } + + protected override string Target => $"wiki/{locale}/{path}"; + } +} From 5c8f5624724095c7c98961cb9a857be778aee3c8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Apr 2021 14:37:28 +0900 Subject: [PATCH 065/839] Don't bail if the underlying localisation resourced is not embedded --- .../Localisation/ResourceManagerLocalisationStore.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game/Localisation/ResourceManagerLocalisationStore.cs b/osu.Game/Localisation/ResourceManagerLocalisationStore.cs index dd84eff55f..e0f110aba9 100644 --- a/osu.Game/Localisation/ResourceManagerLocalisationStore.cs +++ b/osu.Game/Localisation/ResourceManagerLocalisationStore.cs @@ -34,7 +34,16 @@ namespace osu.Game.Localisation if (!resourceManagers.TryGetValue(ns, out var manager)) resourceManagers[ns] = manager = new ResourceManager(ns, GetType().Assembly); - return manager.GetString(key, EffectiveCulture); + try + { + return manager.GetString(key, EffectiveCulture); + } + catch (MissingManifestResourceException) + { + // in the case the manifest is missing, it is likely that the user is adding code-first implementations of new localisation namespaces. + // it's fine to ignore this as localisation will fallback to default values. + return null; + } } public Task GetAsync(string lookup) From 31c8586dacb5b5234c61d8e83f5546937df903cd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Apr 2021 14:37:11 +0900 Subject: [PATCH 066/839] Add localisation support to overlay header title/description --- osu.Game/Localisation/ChatStrings.cs | 24 +++++++++++++++++++ osu.Game/Localisation/NotificationsStrings.cs | 24 +++++++++++++++++++ osu.Game/Localisation/NowPlayingStrings.cs | 24 +++++++++++++++++++ osu.Game/Localisation/SettingsStrings.cs | 24 +++++++++++++++++++ osu.Game/Overlays/ChatOverlay.cs | 6 +++-- osu.Game/Overlays/FullscreenOverlay.cs | 5 ++-- osu.Game/Overlays/INamedOverlayComponent.cs | 6 +++-- osu.Game/Overlays/NotificationOverlay.cs | 6 +++-- osu.Game/Overlays/NowPlayingOverlay.cs | 5 ++-- osu.Game/Overlays/OverlayTitle.cs | 7 +++--- osu.Game/Overlays/Settings/SettingsHeader.cs | 7 +++--- osu.Game/Overlays/SettingsOverlay.cs | 6 +++-- 12 files changed, 126 insertions(+), 18 deletions(-) create mode 100644 osu.Game/Localisation/ChatStrings.cs create mode 100644 osu.Game/Localisation/NotificationsStrings.cs create mode 100644 osu.Game/Localisation/NowPlayingStrings.cs create mode 100644 osu.Game/Localisation/SettingsStrings.cs diff --git a/osu.Game/Localisation/ChatStrings.cs b/osu.Game/Localisation/ChatStrings.cs new file mode 100644 index 0000000000..daddb602ad --- /dev/null +++ b/osu.Game/Localisation/ChatStrings.cs @@ -0,0 +1,24 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class ChatStrings + { + private const string prefix = "osu.Game.Localisation.Chat"; + + /// + /// "chat" + /// + public static LocalisableString HeaderTitle => new TranslatableString(getKey("header_title"), "chat"); + + /// + /// "join the real-time discussion" + /// + public static LocalisableString HeaderDescription => new TranslatableString(getKey("header_description"), "join the real-time discussion"); + + private static string getKey(string key) => $"{prefix}:{key}"; + } +} diff --git a/osu.Game/Localisation/NotificationsStrings.cs b/osu.Game/Localisation/NotificationsStrings.cs new file mode 100644 index 0000000000..092eec3a6b --- /dev/null +++ b/osu.Game/Localisation/NotificationsStrings.cs @@ -0,0 +1,24 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class NotificationsStrings + { + private const string prefix = "osu.Game.Localisation.Notifications"; + + /// + /// "notifications" + /// + public static LocalisableString HeaderTitle => new TranslatableString(getKey("header_title"), "notifications"); + + /// + /// "waiting for 'ya" + /// + public static LocalisableString HeaderDescription => new TranslatableString(getKey("header_description"), "waiting for 'ya"); + + private static string getKey(string key) => $"{prefix}:{key}"; + } +} diff --git a/osu.Game/Localisation/NowPlayingStrings.cs b/osu.Game/Localisation/NowPlayingStrings.cs new file mode 100644 index 0000000000..d742a56895 --- /dev/null +++ b/osu.Game/Localisation/NowPlayingStrings.cs @@ -0,0 +1,24 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class NowPlayingStrings + { + private const string prefix = "osu.Game.Localisation.NowPlaying"; + + /// + /// "now playing" + /// + public static LocalisableString HeaderTitle => new TranslatableString(getKey("header_title"), "now playing"); + + /// + /// "manage the currently playing track" + /// + public static LocalisableString HeaderDescription => new TranslatableString(getKey("header_description"), "manage the currently playing track"); + + private static string getKey(string key) => $"{prefix}:{key}"; + } +} diff --git a/osu.Game/Localisation/SettingsStrings.cs b/osu.Game/Localisation/SettingsStrings.cs new file mode 100644 index 0000000000..cfbd392691 --- /dev/null +++ b/osu.Game/Localisation/SettingsStrings.cs @@ -0,0 +1,24 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class SettingsStrings + { + private const string prefix = "osu.Game.Localisation.Settings"; + + /// + /// "settings" + /// + public static LocalisableString HeaderTitle => new TranslatableString(getKey("header_title"), "settings"); + + /// + /// "change the way osu! behaves" + /// + public static LocalisableString HeaderDescription => new TranslatableString(getKey("header_description"), "change the way osu! behaves"); + + private static string getKey(string key) => $"{prefix}:{key}"; + } +} diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 28f2287514..285041800a 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -24,6 +24,8 @@ using osu.Game.Overlays.Chat.Tabs; using osuTK.Input; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; +using osu.Framework.Localisation; +using osu.Game.Localisation; using osu.Game.Online; namespace osu.Game.Overlays @@ -31,8 +33,8 @@ namespace osu.Game.Overlays public class ChatOverlay : OsuFocusedOverlayContainer, INamedOverlayComponent { public string IconTexture => "Icons/Hexacons/messaging"; - public string Title => "chat"; - public string Description => "join the real-time discussion"; + public LocalisableString Title => ChatStrings.HeaderTitle; + public LocalisableString Description => ChatStrings.HeaderDescription; private const float textbox_height = 60; private const float channel_selection_min_height = 0.3f; diff --git a/osu.Game/Overlays/FullscreenOverlay.cs b/osu.Game/Overlays/FullscreenOverlay.cs index 735f0bcbd4..58c41c4a4b 100644 --- a/osu.Game/Overlays/FullscreenOverlay.cs +++ b/osu.Game/Overlays/FullscreenOverlay.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; +using osu.Framework.Localisation; using osu.Game.Graphics.Containers; using osu.Game.Online.API; using osuTK.Graphics; @@ -18,8 +19,8 @@ namespace osu.Game.Overlays where T : OverlayHeader { public virtual string IconTexture => Header.Title.IconTexture ?? string.Empty; - public virtual string Title => Header.Title.Title ?? string.Empty; - public virtual string Description => Header.Title.Description ?? string.Empty; + public virtual LocalisableString Title => Header.Title.Title; + public virtual LocalisableString Description => Header.Title.Description; public T Header { get; } diff --git a/osu.Game/Overlays/INamedOverlayComponent.cs b/osu.Game/Overlays/INamedOverlayComponent.cs index 38fb8679a0..ca0aea041e 100644 --- a/osu.Game/Overlays/INamedOverlayComponent.cs +++ b/osu.Game/Overlays/INamedOverlayComponent.cs @@ -1,14 +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.Overlays { public interface INamedOverlayComponent { string IconTexture { get; } - string Title { get; } + LocalisableString Title { get; } - string Description { get; } + LocalisableString Description { get; } } } diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index d51d964fc4..b26e17b34c 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -11,16 +11,18 @@ using osu.Game.Graphics.Containers; using System; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Localisation; using osu.Framework.Threading; using osu.Game.Graphics; +using osu.Game.Localisation; namespace osu.Game.Overlays { public class NotificationOverlay : OsuFocusedOverlayContainer, INamedOverlayComponent { public string IconTexture => "Icons/Hexacons/notification"; - public string Title => "notifications"; - public string Description => "waiting for 'ya"; + public LocalisableString Title => NotificationsStrings.HeaderTitle; + public LocalisableString Description => NotificationsStrings.HeaderDescription; private const float width = 320; diff --git a/osu.Game/Overlays/NowPlayingOverlay.cs b/osu.Game/Overlays/NowPlayingOverlay.cs index 81bf71cdec..f88be91c01 100644 --- a/osu.Game/Overlays/NowPlayingOverlay.cs +++ b/osu.Game/Overlays/NowPlayingOverlay.cs @@ -19,6 +19,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Localisation; using osu.Game.Overlays.Music; using osuTK; using osuTK.Graphics; @@ -28,8 +29,8 @@ namespace osu.Game.Overlays public class NowPlayingOverlay : OsuFocusedOverlayContainer, INamedOverlayComponent { public string IconTexture => "Icons/Hexacons/music"; - public string Title => "now playing"; - public string Description => "manage the currently playing track"; + public LocalisableString Title => NowPlayingStrings.HeaderTitle; + public LocalisableString Description => NowPlayingStrings.HeaderDescription; private const float player_height = 130; private const float transition_length = 800; diff --git a/osu.Game/Overlays/OverlayTitle.cs b/osu.Game/Overlays/OverlayTitle.cs index c3ea35adfc..d92979e8d4 100644 --- a/osu.Game/Overlays/OverlayTitle.cs +++ b/osu.Game/Overlays/OverlayTitle.cs @@ -6,6 +6,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osuTK; @@ -19,15 +20,15 @@ namespace osu.Game.Overlays private readonly OsuSpriteText titleText; private readonly Container icon; - private string title; + private LocalisableString title; - public string Title + public LocalisableString Title { get => title; protected set => titleText.Text = title = value; } - public string Description { get; protected set; } + public LocalisableString Description { get; protected set; } private string iconTexture; diff --git a/osu.Game/Overlays/Settings/SettingsHeader.cs b/osu.Game/Overlays/Settings/SettingsHeader.cs index d8ec00bd99..a7f1cef74c 100644 --- a/osu.Game/Overlays/Settings/SettingsHeader.cs +++ b/osu.Game/Overlays/Settings/SettingsHeader.cs @@ -4,6 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; @@ -11,10 +12,10 @@ namespace osu.Game.Overlays.Settings { public class SettingsHeader : Container { - private readonly string heading; - private readonly string subheading; + private readonly LocalisableString heading; + private readonly LocalisableString subheading; - public SettingsHeader(string heading, string subheading) + public SettingsHeader(LocalisableString heading, LocalisableString subheading) { this.heading = heading; this.subheading = subheading; diff --git a/osu.Game/Overlays/SettingsOverlay.cs b/osu.Game/Overlays/SettingsOverlay.cs index 7bd84dbc6c..8c21880cc6 100644 --- a/osu.Game/Overlays/SettingsOverlay.cs +++ b/osu.Game/Overlays/SettingsOverlay.cs @@ -10,14 +10,16 @@ using osuTK.Graphics; using System.Collections.Generic; using System.Linq; using osu.Framework.Bindables; +using osu.Framework.Localisation; +using osu.Game.Localisation; namespace osu.Game.Overlays { public class SettingsOverlay : SettingsPanel, INamedOverlayComponent { public string IconTexture => "Icons/Hexacons/settings"; - public string Title => "settings"; - public string Description => "change the way osu! behaves"; + public LocalisableString Title => SettingsStrings.HeaderTitle; + public LocalisableString Description => SettingsStrings.HeaderDescription; protected override IEnumerable CreateSections() => new SettingsSection[] { From e536f1ad6d3bd1d00968eeddba4997ebda85218c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Apr 2021 14:39:36 +0900 Subject: [PATCH 067/839] Add simple locking of resourceManagers dictionary for thread safety --- .../ResourceManagerLocalisationStore.cs | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/osu.Game/Localisation/ResourceManagerLocalisationStore.cs b/osu.Game/Localisation/ResourceManagerLocalisationStore.cs index e0f110aba9..7b21e1af42 100644 --- a/osu.Game/Localisation/ResourceManagerLocalisationStore.cs +++ b/osu.Game/Localisation/ResourceManagerLocalisationStore.cs @@ -31,18 +31,21 @@ namespace osu.Game.Localisation string ns = split[0]; string key = split[1]; - if (!resourceManagers.TryGetValue(ns, out var manager)) - resourceManagers[ns] = manager = new ResourceManager(ns, GetType().Assembly); + lock (resourceManagers) + { + if (!resourceManagers.TryGetValue(ns, out var manager)) + resourceManagers[ns] = manager = new ResourceManager(ns, GetType().Assembly); - try - { - return manager.GetString(key, EffectiveCulture); - } - catch (MissingManifestResourceException) - { - // in the case the manifest is missing, it is likely that the user is adding code-first implementations of new localisation namespaces. - // it's fine to ignore this as localisation will fallback to default values. - return null; + try + { + return manager.GetString(key, EffectiveCulture); + } + catch (MissingManifestResourceException) + { + // in the case the manifest is missing, it is likely that the user is adding code-first implementations of new localisation namespaces. + // it's fine to ignore this as localisation will fallback to default values. + return null; + } } } From f6a088e699854eb449047410e6ef6835ea79451a Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Wed, 19 May 2021 19:09:03 +0700 Subject: [PATCH 068/839] add request logic in wiki overlay --- osu.Game/Overlays/WikiOverlay.cs | 90 ++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs index 7105fbf953..964daa3368 100644 --- a/osu.Game/Overlays/WikiOverlay.cs +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -1,17 +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.Threading; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.Wiki; +using osu.Game.Overlays.Wiki.Markdown; namespace osu.Game.Overlays { public class WikiOverlay : OnlineOverlay { + private const string index_path = "Main_Page"; + + private readonly Bindable path = new Bindable(index_path); + + [Cached] + private readonly Bindable wikiData = new Bindable(); + + [Resolved] + private IAPIProvider api { get; set; } + + private GetWikiRequest request; + + private CancellationTokenSource cancellationToken; + + private bool displayUpdateRequired = true; + public WikiOverlay() : base(OverlayColourScheme.Orange, false) { } + private void onPathChanged(ValueChangedEvent e) + { + cancellationToken?.Cancel(); + request?.Cancel(); + + request = new GetWikiRequest(e.NewValue); + + Loading.Show(); + + request.Success += response => Schedule(() => onSuccess(response)); + + api.PerformAsync(request); + } + + private void onSuccess(APIWikiPage response) + { + wikiData.Value = response; + LoadDisplay(new WikiMarkdownContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + CurrentPath = $"{path.Value}/", + Text = response.Markdown, + }); + } + protected override WikiHeader CreateHeader() => new WikiHeader(); + + protected override void LoadComplete() + { + base.LoadComplete(); + path.BindValueChanged(onPathChanged); + } + + protected override void PopIn() + { + base.PopIn(); + + if (displayUpdateRequired) + { + path.TriggerChange(); + displayUpdateRequired = false; + } + } + + protected override void PopOutComplete() + { + base.PopOutComplete(); + displayUpdateRequired = true; + } + + protected void LoadDisplay(Drawable display) + { + ScrollFlow.ScrollToStart(); + LoadComponentAsync(display, loaded => + { + Child = loaded; + Loading.Hide(); + }, (cancellationToken = new CancellationTokenSource()).Token); + } + + protected override void Dispose(bool isDisposing) + { + cancellationToken?.Cancel(); + request?.Cancel(); + base.Dispose(isDisposing); + } } } From 416e08ae7af79700be96fdb5e1070cd0f5d6b9db Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Wed, 21 Apr 2021 17:25:13 +0700 Subject: [PATCH 069/839] add dummy response API in TestSceneWikiOverlay `TestSceneNewsOverlay` is used as example for this test. --- .../Visual/Online/TestSceneWikiOverlay.cs | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs index 737c97c0bd..371be8a003 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs @@ -2,21 +2,51 @@ // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; namespace osu.Game.Tests.Visual.Online { public class TestSceneWikiOverlay : OsuTestScene { + private DummyAPIAccess dummyAPI => (DummyAPIAccess)API; + private WikiOverlay wiki; [SetUp] public void SetUp() => Schedule(() => Child = wiki = new WikiOverlay()); [Test] - public void TestOverlay() + public void TestMainPage() { - AddStep("Show", () => wiki.Show()); + setUpNewsResponse(responseExample); + AddStep("Show Main Page", () => wiki.Show()); } + + private void setUpNewsResponse(APIWikiPage r) + => AddStep("set up response", () => + { + dummyAPI.HandleRequest = request => + { + if (!(request is GetWikiRequest getWikiRequest)) + return false; + + getWikiRequest.TriggerSuccess(r); + return true; + }; + }); + + // From https://osu.ppy.sh/api/v2/wiki/en/Main_Page + private APIWikiPage responseExample => new APIWikiPage + { + Title = "Main Page", + Layout = "main_page", + Path = "Main_Page", + Locale = "en", + Subtitle = null, + Markdown = "---\nlayout: main_page\n---\n\n\n\n
\nWelcome to the osu! wiki, a project containing a wide range of osu! related information.\n
\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# Game client\n\n[Interface](/wiki/Interface) • [Options](/wiki/Options) • [Visual settings](/wiki/Visual_Settings) • [Shortcut key reference](/wiki/Shortcut_key_reference) • [Configuration file](/wiki/osu!_Program_Files/User_Configuration_File) • [Program files](/wiki/osu!_Program_Files)\n\n[File formats](/wiki/osu!_File_Formats): [.osz](/wiki/osu!_File_Formats/Osz_(file_format)) • [.osk](/wiki/osu!_File_Formats/Osk_(file_format)) • [.osr](/wiki/osu!_File_Formats/Osr_(file_format)) • [.osu](/wiki/osu!_File_Formats/Osu_(file_format)) • [.osb](/wiki/osu!_File_Formats/Osb_(file_format)) • [.db](/wiki/osu!_File_Formats/Db_(file_format))\n\n
\n
\n\n# Gameplay\n\n[Game modes](/wiki/Game_mode): [osu!](/wiki/Game_mode/osu!) • [osu!taiko](/wiki/Game_mode/osu!taiko) • [osu!catch](/wiki/Game_mode/osu!catch) • [osu!mania](/wiki/Game_mode/osu!mania)\n\n[Beatmap](/wiki/Beatmap) • [Hit object](/wiki/Hit_object) • [Mods](/wiki/Game_modifier) • [Score](/wiki/Score) • [Replay](/wiki/Replay) • [Multi](/wiki/Multi)\n\n
\n
\n\n# [Beatmap editor](/wiki/Beatmap_Editor)\n\nSections: [Compose](/wiki/Beatmap_Editor/Compose) • [Design](/wiki/Beatmap_Editor/Design) • [Timing](/wiki/Beatmap_Editor/Timing) • [Song setup](/wiki/Beatmap_Editor/Song_Setup)\n\nComponents: [AiMod](/wiki/Beatmap_Editor/AiMod) • [Beat snap divisor](/wiki/Beatmap_Editor/Beat_Snap_Divisor) • [Distance snap](/wiki/Beatmap_Editor/Distance_Snap) • [Menu](/wiki/Beatmap_Editor/Menu) • [SB load](/wiki/Beatmap_Editor/SB_Load) • [Timelines](/wiki/Beatmap_Editor/Timelines)\n\n[Beatmapping](/wiki/Beatmapping) • [Difficulty](/wiki/Beatmap/Difficulty) • [Mapping techniques](/wiki/Mapping_Techniques) • [Storyboarding](/wiki/Storyboarding)\n\n
\n
\n\n# Beatmap submission and ranking\n\n[Submission](/wiki/Submission) • [Modding](/wiki/Modding) • [Ranking procedure](/wiki/Beatmap_ranking_procedure) • [Mappers' Guild](/wiki/Mappers_Guild) • [Project Loved](/wiki/Project_Loved)\n\n[Ranking criteria](/wiki/Ranking_Criteria): [osu!](/wiki/Ranking_Criteria/osu!) • [osu!taiko](/wiki/Ranking_Criteria/osu!taiko) • [osu!catch](/wiki/Ranking_Criteria/osu!catch) • [osu!mania](/wiki/Ranking_Criteria/osu!mania)\n\n
\n
\n\n# Community\n\n[Tournaments](/wiki/Tournaments) • [Skinning](/wiki/Skinning) • [Projects](/wiki/Projects) • [Guides](/wiki/Guides) • [osu!dev Discord server](/wiki/osu!dev_Discord_server) • [How you can help](/wiki/How_You_Can_Help!) • [Glossary](/wiki/Glossary)\n\n
\n
\n\n# People\n\n[The Team](/wiki/People/The_Team): [Developers](/wiki/People/The_Team/Developers) • [Global Moderation Team](/wiki/People/The_Team/Global_Moderation_Team) • [Support Team](/wiki/People/The_Team/Support_Team) • [Nomination Assessment Team](/wiki/People/The_Team/Nomination_Assessment_Team) • [Beatmap Nominators](/wiki/People/The_Team/Beatmap_Nominators) • [osu! Alumni](/wiki/People/The_Team/osu!_Alumni) • [Project Loved Team](/wiki/People/The_Team/Project_Loved_Team)\n\nOrganisations: [osu! UCI](/wiki/Organisations/osu!_UCI)\n\n[Community Contributors](/wiki/People/Community_Contributors) • [Users with unique titles](/wiki/People/Users_with_unique_titles)\n\n
\n
\n\n# For developers\n\n[API](/wiki/osu!api) • [Bot account](/wiki/Bot_account) • [Brand identity guidelines](/wiki/Brand_identity_guidelines)\n\n
\n
\n\n# About the wiki\n\n[Sitemap](/wiki/Sitemap) • [Contribution guide](/wiki/osu!_wiki_Contribution_Guide) • [Article styling criteria](/wiki/Article_Styling_Criteria) • [News styling criteria](/wiki/News_Styling_Criteria)\n\n
\n
\n", + }; } } From 25f2c582e7f6ab9d54fba269c26d656bef1dcb97 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 22 Apr 2021 16:15:20 +0700 Subject: [PATCH 070/839] add ToolbarWikiButton --- .../Overlays/Toolbar/ToolbarWikiButton.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 osu.Game/Overlays/Toolbar/ToolbarWikiButton.cs diff --git a/osu.Game/Overlays/Toolbar/ToolbarWikiButton.cs b/osu.Game/Overlays/Toolbar/ToolbarWikiButton.cs new file mode 100644 index 0000000000..a521219b4f --- /dev/null +++ b/osu.Game/Overlays/Toolbar/ToolbarWikiButton.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.Allocation; +using osu.Framework.Graphics; + +namespace osu.Game.Overlays.Toolbar +{ + public class ToolbarWikiButton : ToolbarOverlayToggleButton + { + protected override Anchor TooltipAnchor => Anchor.TopRight; + + [BackgroundDependencyLoader(true)] + private void load(WikiOverlay wiki) + { + StateContainer = wiki; + } + } +} From 004cd7c8344162cae74380a12605b3d54a7b6a9a Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 22 Apr 2021 16:16:12 +0700 Subject: [PATCH 071/839] add wiki button in main toolbar --- osu.Game/OsuGame.cs | 5 ++++- osu.Game/Overlays/Toolbar/Toolbar.cs | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index b7946f1c2f..42a49aa65e 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -81,6 +81,8 @@ namespace osu.Game private BeatmapSetOverlay beatmapSetOverlay; + private WikiOverlay wikiOverlay; + private SkinEditorOverlay skinEditor; private Container overlayContent; @@ -719,6 +721,7 @@ namespace osu.Game var changelogOverlay = loadComponentSingleFile(new ChangelogOverlay(), overlayContent.Add, true); loadComponentSingleFile(userProfile = new UserProfileOverlay(), overlayContent.Add, true); loadComponentSingleFile(beatmapSetOverlay = new BeatmapSetOverlay(), overlayContent.Add, true); + loadComponentSingleFile(wikiOverlay = new WikiOverlay(), overlayContent.Add, true); loadComponentSingleFile(skinEditor = new SkinEditorOverlay(screenContainer), overlayContent.Add); loadComponentSingleFile(new LoginOverlay @@ -769,7 +772,7 @@ namespace osu.Game } // ensure only one of these overlays are open at once. - var singleDisplayOverlays = new OverlayContainer[] { chatOverlay, news, dashboard, beatmapListing, changelogOverlay, rankingsOverlay }; + var singleDisplayOverlays = new OverlayContainer[] { chatOverlay, news, dashboard, beatmapListing, changelogOverlay, rankingsOverlay, wikiOverlay }; foreach (var overlay in singleDisplayOverlays) { diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index d049c2d3ec..3d88171ba7 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -92,6 +92,7 @@ namespace osu.Game.Overlays.Toolbar new ToolbarBeatmapListingButton(), new ToolbarChatButton(), new ToolbarSocialButton(), + new ToolbarWikiButton(), new ToolbarMusicButton(), //new ToolbarButton //{ From 961bd1177c9bfbeb3203aab22a4602fee3d3f6b2 Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Sun, 25 Apr 2021 00:39:36 +0200 Subject: [PATCH 072/839] Add mod "Random" for ruleset "osu!" --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 77 ++++++++++++++++++++++ osu.Game.Rulesets.Osu/OsuRuleset.cs | 1 + osu.Game/Rulesets/Mods/ModRandomOsu.cs | 47 +++++++++++++ 3 files changed, 125 insertions(+) create mode 100644 osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs create mode 100644 osu.Game/Rulesets/Mods/ModRandomOsu.cs diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs new file mode 100644 index 0000000000..f3c9040b1c --- /dev/null +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -0,0 +1,77 @@ +// 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.Game.Beatmaps; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.UI; +using osuTK; + +namespace osu.Game.Rulesets.Osu.Mods +{ + public class OsuModRandom : ModRandomOsu + { + protected override void RandomiseHitObjectPositions(IBeatmap beatmap) + { + var rng = new Random(); + + foreach (var hitObject in beatmap.HitObjects) + { + if (RandomiseCirclePositions.Value && hitObject is HitCircle circle) + { + circle.Position = new Vector2( + (float)rng.NextDouble() * OsuPlayfield.BASE_SIZE.X, + (float)rng.NextDouble() * OsuPlayfield.BASE_SIZE.Y + ); + } + else if (RandomiseSpinnerPositions.Value && hitObject is Spinner spinner) + { + spinner.Position = new Vector2( + (float)rng.NextDouble() * OsuPlayfield.BASE_SIZE.X, + (float)rng.NextDouble() * OsuPlayfield.BASE_SIZE.Y + ); + } + else if (RandomiseSliderPositions.Value && hitObject is Slider slider) + { + // Min. distances from the slider's position to the border to prevent the slider from being partially out of the screen + float minLeft = 0, minRight = 0, minTop = 0, minBottom = 0; + + var controlPointPositions = (from position + in slider.Path.ControlPoints + select position.Position.Value).ToList(); + + controlPointPositions.Add(slider.EndPosition); + controlPointPositions.RemoveAt(controlPointPositions.Count - 1); + + foreach (var position in controlPointPositions) + { + if (position.X > minRight) + { + minRight = position.X; + } + else if (-position.X > minLeft) + { + minLeft = -position.X; + } + + if (position.Y > minBottom) + { + minBottom = position.Y; + } + else if (-position.Y > minTop) + { + minTop = -position.Y; + } + } + + slider.Position = new Vector2( + (float)rng.NextDouble() * (OsuPlayfield.BASE_SIZE.X - minLeft - minRight) + minLeft, + (float)rng.NextDouble() * (OsuPlayfield.BASE_SIZE.Y - minTop - minBottom) + minTop + ); + } + } + } + } +} diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 465d6d7155..6a04c4ca5c 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -186,6 +186,7 @@ namespace osu.Game.Rulesets.Osu new MultiMod(new ModWindUp(), new ModWindDown()), new OsuModTraceable(), new OsuModBarrelRoll(), + new OsuModRandom(), }; case ModType.System: diff --git a/osu.Game/Rulesets/Mods/ModRandomOsu.cs b/osu.Game/Rulesets/Mods/ModRandomOsu.cs new file mode 100644 index 0000000000..9fb2c07d82 --- /dev/null +++ b/osu.Game/Rulesets/Mods/ModRandomOsu.cs @@ -0,0 +1,47 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Bindables; +using osu.Framework.Graphics.Sprites; +using osu.Game.Beatmaps; +using osu.Game.Configuration; +using osu.Game.Graphics; + +namespace osu.Game.Rulesets.Mods +{ + public abstract class ModRandomOsu : Mod, IApplicableToBeatmap + { + public override string Name => "Random"; + public override string Acronym => "RD"; + public override IconUsage? Icon => OsuIcon.Dice; + public override ModType Type => ModType.Fun; + public override string Description => "Hit objects appear at random positions"; + public override double ScoreMultiplier => 1; + public override bool Ranked => false; + + [SettingSource("Randomise circle positions", "Hit circles appear at random positions")] + public Bindable RandomiseCirclePositions { get; } = new BindableBool + { + Default = true, + Value = true, + }; + + [SettingSource("Randomise slider positions", "Sliders appear at random positions")] + public Bindable RandomiseSliderPositions { get; } = new BindableBool + { + Default = true, + Value = true, + }; + + [SettingSource("Randomise spinner positions", "Spinners appear at random positions")] + public Bindable RandomiseSpinnerPositions { get; } = new BindableBool + { + Default = true, + Value = true, + }; + + public void ApplyToBeatmap(IBeatmap beatmap) => RandomiseHitObjectPositions(beatmap); + + protected abstract void RandomiseHitObjectPositions(IBeatmap beatmap); + } +} From 817bb5213c48d0b4cf5f94a375c74e04c1a16ff5 Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Sun, 25 Apr 2021 00:46:35 +0200 Subject: [PATCH 073/839] Make OsuAutoGenerator spin the cursor around the position of the spinner instead of a set value This is to make Autoplay work with randomised spinner positions --- .../Replays/OsuAutoGenerator.cs | 18 +++++++++--------- .../Replays/OsuAutoGeneratorBase.cs | 3 --- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs index 7b0cf651c8..609799dc54 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs @@ -155,9 +155,9 @@ namespace osu.Game.Rulesets.Osu.Replays if (spinner.SpinsRequired == 0) return; - calcSpinnerStartPosAndDirection(((OsuReplayFrame)Frames[^1]).Position, out startPosition, out spinnerDirection); + calcSpinnerStartPosAndDirection(spinner, ((OsuReplayFrame)Frames[^1]).Position, out startPosition, out spinnerDirection); - Vector2 spinCentreOffset = SPINNER_CENTRE - ((OsuReplayFrame)Frames[^1]).Position; + Vector2 spinCentreOffset = spinner.Position - ((OsuReplayFrame)Frames[^1]).Position; if (spinCentreOffset.Length > SPIN_RADIUS) { @@ -180,9 +180,9 @@ namespace osu.Game.Rulesets.Osu.Replays #region Helper subroutines - private static void calcSpinnerStartPosAndDirection(Vector2 prevPos, out Vector2 startPosition, out float spinnerDirection) + private static void calcSpinnerStartPosAndDirection(Spinner spinner, Vector2 prevPos, out Vector2 startPosition, out float spinnerDirection) { - Vector2 spinCentreOffset = SPINNER_CENTRE - prevPos; + Vector2 spinCentreOffset = spinner.Position - prevPos; float distFromCentre = spinCentreOffset.Length; float distToTangentPoint = MathF.Sqrt(distFromCentre * distFromCentre - SPIN_RADIUS * SPIN_RADIUS); @@ -216,13 +216,13 @@ namespace osu.Game.Rulesets.Osu.Replays else if (spinCentreOffset.Length > 0) { // Previous cursor position was inside spin circle, set startPosition to the nearest point on spin circle. - startPosition = SPINNER_CENTRE - spinCentreOffset * (SPIN_RADIUS / spinCentreOffset.Length); + startPosition = spinner.Position - spinCentreOffset * (SPIN_RADIUS / spinCentreOffset.Length); spinnerDirection = 1; } else { // Degenerate case where cursor position is exactly at the centre of the spin circle. - startPosition = SPINNER_CENTRE + new Vector2(0, -SPIN_RADIUS); + startPosition = spinner.Position + new Vector2(0, -SPIN_RADIUS); spinnerDirection = 1; } } @@ -335,7 +335,7 @@ namespace osu.Game.Rulesets.Osu.Replays { // We add intermediate frames for spinning / following a slider here. case Spinner spinner: - Vector2 difference = startPosition - SPINNER_CENTRE; + Vector2 difference = startPosition - spinner.Position; float radius = difference.Length; float angle = radius == 0 ? 0 : MathF.Atan2(difference.Y, difference.X); @@ -348,7 +348,7 @@ namespace osu.Game.Rulesets.Osu.Replays t = ApplyModsToTimeDelta(previousFrame, nextFrame) * spinnerDirection; angle += (float)t / 20; - Vector2 pos = SPINNER_CENTRE + CirclePosition(angle, SPIN_RADIUS); + Vector2 pos = spinner.Position + CirclePosition(angle, SPIN_RADIUS); AddFrameToReplay(new OsuReplayFrame((int)nextFrame, new Vector2(pos.X, pos.Y), action)); previousFrame = nextFrame; @@ -357,7 +357,7 @@ namespace osu.Game.Rulesets.Osu.Replays t = ApplyModsToTimeDelta(previousFrame, spinner.EndTime) * spinnerDirection; angle += (float)t / 20; - Vector2 endPosition = SPINNER_CENTRE + CirclePosition(angle, SPIN_RADIUS); + Vector2 endPosition = spinner.Position + CirclePosition(angle, SPIN_RADIUS); AddFrameToReplay(new OsuReplayFrame(spinner.EndTime, new Vector2(endPosition.X, endPosition.Y), action)); diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs index 1cb3208c30..69eb669a8e 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs @@ -8,7 +8,6 @@ using System.Collections.Generic; using System.Linq; using osu.Game.Replays; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Replays; namespace osu.Game.Rulesets.Osu.Replays @@ -20,8 +19,6 @@ namespace osu.Game.Rulesets.Osu.Replays /// /// Constants (for spinners). /// - protected static readonly Vector2 SPINNER_CENTRE = OsuPlayfield.BASE_SIZE / 2; - public const float SPIN_RADIUS = 50; #endregion From 8a3fa53c2661662272a62699eed7d7a1f04407ea Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Sun, 25 Apr 2021 01:02:03 +0200 Subject: [PATCH 074/839] Change mod description and settings labels --- osu.Game/Rulesets/Mods/ModRandomOsu.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModRandomOsu.cs b/osu.Game/Rulesets/Mods/ModRandomOsu.cs index 9fb2c07d82..6b86da357a 100644 --- a/osu.Game/Rulesets/Mods/ModRandomOsu.cs +++ b/osu.Game/Rulesets/Mods/ModRandomOsu.cs @@ -15,25 +15,25 @@ namespace osu.Game.Rulesets.Mods public override string Acronym => "RD"; public override IconUsage? Icon => OsuIcon.Dice; public override ModType Type => ModType.Fun; - public override string Description => "Hit objects appear at random positions"; + public override string Description => "Practice your reaction time!"; public override double ScoreMultiplier => 1; public override bool Ranked => false; - [SettingSource("Randomise circle positions", "Hit circles appear at random positions")] + [SettingSource("Circles", "Hit circles appear at random positions")] public Bindable RandomiseCirclePositions { get; } = new BindableBool { Default = true, Value = true, }; - [SettingSource("Randomise slider positions", "Sliders appear at random positions")] + [SettingSource("Sliders", "Sliders appear at random positions")] public Bindable RandomiseSliderPositions { get; } = new BindableBool { Default = true, Value = true, }; - [SettingSource("Randomise spinner positions", "Spinners appear at random positions")] + [SettingSource("Spinners", "Spinners appear at random positions")] public Bindable RandomiseSpinnerPositions { get; } = new BindableBool { Default = true, From 92f765b9588e6de084b4d57d7e00a75a210c07ec Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Sun, 25 Apr 2021 01:19:06 +0200 Subject: [PATCH 075/839] Change ModType from Fun to Conversion --- osu.Game.Rulesets.Osu/OsuRuleset.cs | 4 ++-- osu.Game/Rulesets/Mods/ModRandomOsu.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 6a04c4ca5c..b50d3ad2b4 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -164,7 +164,8 @@ namespace osu.Game.Rulesets.Osu { new OsuModTarget(), new OsuModDifficultyAdjust(), - new OsuModClassic() + new OsuModClassic(), + new OsuModRandom(), }; case ModType.Automation: @@ -186,7 +187,6 @@ namespace osu.Game.Rulesets.Osu new MultiMod(new ModWindUp(), new ModWindDown()), new OsuModTraceable(), new OsuModBarrelRoll(), - new OsuModRandom(), }; case ModType.System: diff --git a/osu.Game/Rulesets/Mods/ModRandomOsu.cs b/osu.Game/Rulesets/Mods/ModRandomOsu.cs index 6b86da357a..1581065c01 100644 --- a/osu.Game/Rulesets/Mods/ModRandomOsu.cs +++ b/osu.Game/Rulesets/Mods/ModRandomOsu.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mods public override string Name => "Random"; public override string Acronym => "RD"; public override IconUsage? Icon => OsuIcon.Dice; - public override ModType Type => ModType.Fun; + public override ModType Type => ModType.Conversion; public override string Description => "Practice your reaction time!"; public override double ScoreMultiplier => 1; public override bool Ranked => false; From f33f1b2bed13faebb9ad8d041077f172178e60fc Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Sun, 25 Apr 2021 01:34:39 +0200 Subject: [PATCH 076/839] Remove class "ModRandomOsu" and adjust code Add documentation comment for OsuModRandom --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 34 +++++++++++++++- osu.Game/Rulesets/Mods/ModRandomOsu.cs | 47 ---------------------- 2 files changed, 32 insertions(+), 49 deletions(-) delete mode 100644 osu.Game/Rulesets/Mods/ModRandomOsu.cs diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index f3c9040b1c..c87628b0e7 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -3,17 +3,47 @@ using System; using System.Linq; +using osu.Framework.Bindables; using osu.Game.Beatmaps; +using osu.Game.Configuration; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.UI; using osuTK; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModRandom : ModRandomOsu + /// + /// Mod that randomises the positions of the s + /// + public class OsuModRandom : ModRandom, IApplicableToBeatmap { - protected override void RandomiseHitObjectPositions(IBeatmap beatmap) + public override string Description => "Practice your reaction time!"; + public override bool Ranked => false; + + [SettingSource("Circles", "Hit circles appear at random positions")] + public Bindable RandomiseCirclePositions { get; } = new BindableBool + { + Default = true, + Value = true, + }; + + [SettingSource("Sliders", "Sliders appear at random positions")] + public Bindable RandomiseSliderPositions { get; } = new BindableBool + { + Default = true, + Value = true, + }; + + [SettingSource("Spinners", "Spinners appear at random positions")] + public Bindable RandomiseSpinnerPositions { get; } = new BindableBool + { + Default = true, + Value = true, + }; + + public void ApplyToBeatmap(IBeatmap beatmap) { var rng = new Random(); diff --git a/osu.Game/Rulesets/Mods/ModRandomOsu.cs b/osu.Game/Rulesets/Mods/ModRandomOsu.cs deleted file mode 100644 index 1581065c01..0000000000 --- a/osu.Game/Rulesets/Mods/ModRandomOsu.cs +++ /dev/null @@ -1,47 +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.Bindables; -using osu.Framework.Graphics.Sprites; -using osu.Game.Beatmaps; -using osu.Game.Configuration; -using osu.Game.Graphics; - -namespace osu.Game.Rulesets.Mods -{ - public abstract class ModRandomOsu : Mod, IApplicableToBeatmap - { - public override string Name => "Random"; - public override string Acronym => "RD"; - public override IconUsage? Icon => OsuIcon.Dice; - public override ModType Type => ModType.Conversion; - public override string Description => "Practice your reaction time!"; - public override double ScoreMultiplier => 1; - public override bool Ranked => false; - - [SettingSource("Circles", "Hit circles appear at random positions")] - public Bindable RandomiseCirclePositions { get; } = new BindableBool - { - Default = true, - Value = true, - }; - - [SettingSource("Sliders", "Sliders appear at random positions")] - public Bindable RandomiseSliderPositions { get; } = new BindableBool - { - Default = true, - Value = true, - }; - - [SettingSource("Spinners", "Spinners appear at random positions")] - public Bindable RandomiseSpinnerPositions { get; } = new BindableBool - { - Default = true, - Value = true, - }; - - public void ApplyToBeatmap(IBeatmap beatmap) => RandomiseHitObjectPositions(beatmap); - - protected abstract void RandomiseHitObjectPositions(IBeatmap beatmap); - } -} From 08821da954d3927d776534fa1550d23838b3855a Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Sun, 25 Apr 2021 01:43:32 +0200 Subject: [PATCH 077/839] Change mod description --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index c87628b0e7..40e966a686 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Mods ///
public class OsuModRandom : ModRandom, IApplicableToBeatmap { - public override string Description => "Practice your reaction time!"; + public override string Description => "It never gets boring!"; public override bool Ranked => false; [SettingSource("Circles", "Hit circles appear at random positions")] From 6e85c4e0699e85df8ebf841404cfd9e1dd652bae Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Sun, 25 Apr 2021 23:57:01 +0200 Subject: [PATCH 078/839] Change randomisation process to keep distances between objects Remove now unnecessary settings; Remove spinners; Sliders are not implemented yet --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 254 +++++++++++++++------ 1 file changed, 183 insertions(+), 71 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 40e966a686..1d430ba711 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -2,10 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Linq; -using osu.Framework.Bindables; using osu.Game.Beatmaps; -using osu.Game.Configuration; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects; @@ -22,86 +19,201 @@ namespace osu.Game.Rulesets.Osu.Mods public override string Description => "It never gets boring!"; public override bool Ranked => false; - [SettingSource("Circles", "Hit circles appear at random positions")] - public Bindable RandomiseCirclePositions { get; } = new BindableBool - { - Default = true, - Value = true, - }; - - [SettingSource("Sliders", "Sliders appear at random positions")] - public Bindable RandomiseSliderPositions { get; } = new BindableBool - { - Default = true, - Value = true, - }; - - [SettingSource("Spinners", "Spinners appear at random positions")] - public Bindable RandomiseSpinnerPositions { get; } = new BindableBool - { - Default = true, - Value = true, - }; + // The distances from the hit objects to the borders of the playfield they start to "turn around" and curve towards the middle. + // The closer the hit objects draw to the border, the sharper the turn + private const byte border_distance_x = 128; + private const byte border_distance_y = 96; public void ApplyToBeatmap(IBeatmap beatmap) { var rng = new Random(); - foreach (var hitObject in beatmap.HitObjects) + // Absolute angle + float prevAngleRad = 0; + + // Absolute positions + Vector2 prevPosUnchanged = ((OsuHitObject)beatmap.HitObjects[0]).Position; + Vector2 prevPosChanged = ((OsuHitObject)beatmap.HitObjects[0]).Position; + + // rateOfChangeMultiplier changes every i iterations to prevent shaky-line-shaped streams + byte i = 5; + float rateOfChangeMultiplier = 0; + + foreach (var beatmapHitObject in beatmap.HitObjects) { - if (RandomiseCirclePositions.Value && hitObject is HitCircle circle) + if (!(beatmapHitObject is OsuHitObject hitObject)) + return; + + // posUnchanged: position from the original beatmap (not randomised) + var posUnchanged = hitObject.EndPosition; + var posChanged = Vector2.Zero; + + // Angle of the vector pointing from the last to the current hit object + float angleRad = 0; + + if (i >= 5) { - circle.Position = new Vector2( - (float)rng.NextDouble() * OsuPlayfield.BASE_SIZE.X, - (float)rng.NextDouble() * OsuPlayfield.BASE_SIZE.Y + i = 0; + rateOfChangeMultiplier = (float)rng.NextDouble() * 2 - 1; + } + + if (hitObject is HitCircle circle) + { + var distanceToPrev = Vector2.Distance(posUnchanged, prevPosUnchanged); + + circle.Position = posChanged = getRandomisedPosition( + rateOfChangeMultiplier, + prevPosChanged, + prevAngleRad, + distanceToPrev, + out angleRad ); } - else if (RandomiseSpinnerPositions.Value && hitObject is Spinner spinner) - { - spinner.Position = new Vector2( - (float)rng.NextDouble() * OsuPlayfield.BASE_SIZE.X, - (float)rng.NextDouble() * OsuPlayfield.BASE_SIZE.Y - ); - } - else if (RandomiseSliderPositions.Value && hitObject is Slider slider) - { - // Min. distances from the slider's position to the border to prevent the slider from being partially out of the screen - float minLeft = 0, minRight = 0, minTop = 0, minBottom = 0; - var controlPointPositions = (from position - in slider.Path.ControlPoints - select position.Position.Value).ToList(); + // TODO: Implement slider position randomisation - controlPointPositions.Add(slider.EndPosition); - controlPointPositions.RemoveAt(controlPointPositions.Count - 1); - - foreach (var position in controlPointPositions) - { - if (position.X > minRight) - { - minRight = position.X; - } - else if (-position.X > minLeft) - { - minLeft = -position.X; - } - - if (position.Y > minBottom) - { - minBottom = position.Y; - } - else if (-position.Y > minTop) - { - minTop = -position.Y; - } - } - - slider.Position = new Vector2( - (float)rng.NextDouble() * (OsuPlayfield.BASE_SIZE.X - minLeft - minRight) + minLeft, - (float)rng.NextDouble() * (OsuPlayfield.BASE_SIZE.Y - minTop - minBottom) + minTop - ); - } + prevAngleRad = angleRad; + prevPosUnchanged = posUnchanged; + prevPosChanged = posChanged; + i++; } } + + /// + /// Returns the final position of the hit object + /// + /// Final position of the hit object + private Vector2 getRandomisedPosition( + float rateOfChangeMultiplier, + Vector2 prevPosChanged, + float prevAngleRad, + float distanceToPrev, + out float newAngle) + { + // The max. angle (relative to the angle of the vector pointing from the 2nd last to the last hit object) + // is proportional to the distance between the last and the current hit object + // to allow jumps and prevent too sharp turns during streams. + var maxDistance = OsuPlayfield.BASE_SIZE.LengthFast; + var randomAngleRad = rateOfChangeMultiplier * 2 * Math.PI * distanceToPrev / maxDistance; + + newAngle = (float)randomAngleRad + prevAngleRad; + if (newAngle < 0) + newAngle += 2 * (float)Math.PI; + + var posRelativeToPrev = new Vector2( + distanceToPrev * (float)Math.Cos(newAngle), + distanceToPrev * (float)Math.Sin(newAngle) + ); + + posRelativeToPrev = getRotatedVector(prevPosChanged, posRelativeToPrev); + + newAngle = (float)Math.Atan2(posRelativeToPrev.Y, posRelativeToPrev.X); + var position = Vector2.Add(prevPosChanged, posRelativeToPrev); + + // Move hit objects back into the playfield if they are outside of it, + // which would sometimes happen during big jumps otherwise. + if (position.X < 0) + position.X = 0; + else if (position.X > OsuPlayfield.BASE_SIZE.X) + position.X = OsuPlayfield.BASE_SIZE.X; + + if (position.Y < 0) + position.Y = 0; + else if (position.Y > OsuPlayfield.BASE_SIZE.Y) + position.Y = OsuPlayfield.BASE_SIZE.Y; + + return position; + } + + /// + /// Determines the position of the current hit object relative to the previous one. + /// + /// The position of the current hit object relative to the previous one + private Vector2 getRotatedVector(Vector2 prevPosChanged, Vector2 posRelativeToPrev) + { + var relativeRotationDistance = 0f; + var playfieldMiddle = Vector2.Divide(OsuPlayfield.BASE_SIZE, 2); + + if (prevPosChanged.X < playfieldMiddle.X) + { + relativeRotationDistance = Math.Max( + (border_distance_x - prevPosChanged.X) / border_distance_x, + relativeRotationDistance + ); + } + else + { + relativeRotationDistance = Math.Max( + (prevPosChanged.X - (OsuPlayfield.BASE_SIZE.X - border_distance_x)) / border_distance_x, + relativeRotationDistance + ); + } + + if (prevPosChanged.Y < playfieldMiddle.Y) + { + relativeRotationDistance = Math.Max( + (border_distance_y - prevPosChanged.Y) / border_distance_y, + relativeRotationDistance + ); + } + else + { + relativeRotationDistance = Math.Max( + (prevPosChanged.Y - (OsuPlayfield.BASE_SIZE.Y - border_distance_y)) / border_distance_y, + relativeRotationDistance + ); + } + + return rotateVectorTowardsVector( + posRelativeToPrev, + Vector2.Subtract(playfieldMiddle, prevPosChanged), + relativeRotationDistance + ); + } + + /// + /// Rotates vector "initial" towards vector "destinantion" + /// + /// Vector to rotate to "destination" + /// Vector "initial" should be rotated to + /// The angle the vector should be rotated relative to the difference between the angles of the the two vectors. + /// Resulting vector + private Vector2 rotateVectorTowardsVector(Vector2 initial, Vector2 destination, float relativeDistance) + { + var initialAngleRad = Math.Atan2(initial.Y, initial.X); + var destAngleRad = Math.Atan2(destination.Y, destination.X); + + // Divide by 2 to limit the max. angle to 90° + // (90° is enough to prevent the hit objects from leaving the playfield) + relativeDistance /= 2; + + var diff = destAngleRad - initialAngleRad; + + while (diff < -Math.PI) + { + diff += 2 * Math.PI; + } + + while (diff > Math.PI) + { + diff -= 2 * Math.PI; + } + + var finalAngle = 0d; + + if (diff > 0) + { + finalAngle = initialAngleRad + relativeDistance * diff; + } + else if (diff < 0) + { + finalAngle = initialAngleRad + relativeDistance * diff; + } + + return new Vector2( + initial.Length * (float)Math.Cos(finalAngle), + initial.Length * (float)Math.Sin(finalAngle) + ); + } } } From 19fc2243489235598d523104296ec51448d6d897 Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Mon, 26 Apr 2021 11:52:46 +0200 Subject: [PATCH 079/839] Revert "Make OsuAutoGenerator spin the cursor around the position of the spinner instead of a set value" This reverts commit 817bb5213c48d0b4cf5f94a375c74e04c1a16ff5. --- .../Replays/OsuAutoGenerator.cs | 18 +++++++++--------- .../Replays/OsuAutoGeneratorBase.cs | 3 +++ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs index 609799dc54..7b0cf651c8 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs @@ -155,9 +155,9 @@ namespace osu.Game.Rulesets.Osu.Replays if (spinner.SpinsRequired == 0) return; - calcSpinnerStartPosAndDirection(spinner, ((OsuReplayFrame)Frames[^1]).Position, out startPosition, out spinnerDirection); + calcSpinnerStartPosAndDirection(((OsuReplayFrame)Frames[^1]).Position, out startPosition, out spinnerDirection); - Vector2 spinCentreOffset = spinner.Position - ((OsuReplayFrame)Frames[^1]).Position; + Vector2 spinCentreOffset = SPINNER_CENTRE - ((OsuReplayFrame)Frames[^1]).Position; if (spinCentreOffset.Length > SPIN_RADIUS) { @@ -180,9 +180,9 @@ namespace osu.Game.Rulesets.Osu.Replays #region Helper subroutines - private static void calcSpinnerStartPosAndDirection(Spinner spinner, Vector2 prevPos, out Vector2 startPosition, out float spinnerDirection) + private static void calcSpinnerStartPosAndDirection(Vector2 prevPos, out Vector2 startPosition, out float spinnerDirection) { - Vector2 spinCentreOffset = spinner.Position - prevPos; + Vector2 spinCentreOffset = SPINNER_CENTRE - prevPos; float distFromCentre = spinCentreOffset.Length; float distToTangentPoint = MathF.Sqrt(distFromCentre * distFromCentre - SPIN_RADIUS * SPIN_RADIUS); @@ -216,13 +216,13 @@ namespace osu.Game.Rulesets.Osu.Replays else if (spinCentreOffset.Length > 0) { // Previous cursor position was inside spin circle, set startPosition to the nearest point on spin circle. - startPosition = spinner.Position - spinCentreOffset * (SPIN_RADIUS / spinCentreOffset.Length); + startPosition = SPINNER_CENTRE - spinCentreOffset * (SPIN_RADIUS / spinCentreOffset.Length); spinnerDirection = 1; } else { // Degenerate case where cursor position is exactly at the centre of the spin circle. - startPosition = spinner.Position + new Vector2(0, -SPIN_RADIUS); + startPosition = SPINNER_CENTRE + new Vector2(0, -SPIN_RADIUS); spinnerDirection = 1; } } @@ -335,7 +335,7 @@ namespace osu.Game.Rulesets.Osu.Replays { // We add intermediate frames for spinning / following a slider here. case Spinner spinner: - Vector2 difference = startPosition - spinner.Position; + Vector2 difference = startPosition - SPINNER_CENTRE; float radius = difference.Length; float angle = radius == 0 ? 0 : MathF.Atan2(difference.Y, difference.X); @@ -348,7 +348,7 @@ namespace osu.Game.Rulesets.Osu.Replays t = ApplyModsToTimeDelta(previousFrame, nextFrame) * spinnerDirection; angle += (float)t / 20; - Vector2 pos = spinner.Position + CirclePosition(angle, SPIN_RADIUS); + Vector2 pos = SPINNER_CENTRE + CirclePosition(angle, SPIN_RADIUS); AddFrameToReplay(new OsuReplayFrame((int)nextFrame, new Vector2(pos.X, pos.Y), action)); previousFrame = nextFrame; @@ -357,7 +357,7 @@ namespace osu.Game.Rulesets.Osu.Replays t = ApplyModsToTimeDelta(previousFrame, spinner.EndTime) * spinnerDirection; angle += (float)t / 20; - Vector2 endPosition = spinner.Position + CirclePosition(angle, SPIN_RADIUS); + Vector2 endPosition = SPINNER_CENTRE + CirclePosition(angle, SPIN_RADIUS); AddFrameToReplay(new OsuReplayFrame(spinner.EndTime, new Vector2(endPosition.X, endPosition.Y), action)); diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs index 69eb669a8e..1cb3208c30 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.Linq; using osu.Game.Replays; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Replays; namespace osu.Game.Rulesets.Osu.Replays @@ -19,6 +20,8 @@ namespace osu.Game.Rulesets.Osu.Replays /// /// Constants (for spinners). /// + protected static readonly Vector2 SPINNER_CENTRE = OsuPlayfield.BASE_SIZE / 2; + public const float SPIN_RADIUS = 50; #endregion From 1dfe028c0209d4ef4cac6c3c2462af22797d81a9 Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Mon, 26 Apr 2021 22:26:13 +0200 Subject: [PATCH 080/839] Fix bug causing the star rating to change when Random is enabled --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 2 +- osu.Game/Beatmaps/WorkingBeatmap.cs | 10 ++++++++++ .../Mods/IApplicableToBeatmapKeepStarRating.cs | 14 ++++++++++++++ osu.Game/Screens/Play/Player.cs | 2 ++ 4 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Rulesets/Mods/IApplicableToBeatmapKeepStarRating.cs diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 1d430ba711..3a4b5073b5 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Osu.Mods /// /// Mod that randomises the positions of the s /// - public class OsuModRandom : ModRandom, IApplicableToBeatmap + public class OsuModRandom : ModRandom, IApplicableToBeatmapKeepStarRating { public override string Description => "It never gets boring!"; public override bool Ranked => false; diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index e0eeaf6db0..d49f6ed50b 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -32,6 +32,11 @@ namespace osu.Game.Beatmaps public readonly BeatmapMetadata Metadata; + /// + /// Only if this is set to true, changes made by mods that implement will be applied. + /// + public bool ApplyChangesToBeatmap; + protected AudioManager AudioManager { get; } protected WorkingBeatmap(BeatmapInfo beatmapInfo, AudioManager audioManager) @@ -166,10 +171,15 @@ namespace osu.Game.Beatmaps foreach (var mod in mods.OfType()) { + if (mod is IApplicableToBeatmapKeepStarRating && !ApplyChangesToBeatmap) + continue; + cancellationSource.Token.ThrowIfCancellationRequested(); mod.ApplyToBeatmap(converted); } + ApplyChangesToBeatmap = false; + return converted; } } diff --git a/osu.Game/Rulesets/Mods/IApplicableToBeatmapKeepStarRating.cs b/osu.Game/Rulesets/Mods/IApplicableToBeatmapKeepStarRating.cs new file mode 100644 index 0000000000..34db7e1be3 --- /dev/null +++ b/osu.Game/Rulesets/Mods/IApplicableToBeatmapKeepStarRating.cs @@ -0,0 +1,14 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Beatmaps; + +namespace osu.Game.Rulesets.Mods +{ + /// + /// Interface for a that applies changes to a after conversion and post-processing has completed without changing its difficulty + /// + public interface IApplicableToBeatmapKeepStarRating : IApplicableToBeatmap + { + } +} diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 27a4fcc291..80eec64884 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -455,6 +455,8 @@ namespace osu.Game.Screens.Play rulesetInfo = Ruleset.Value ?? Beatmap.Value.BeatmapInfo.Ruleset; ruleset = rulesetInfo.CreateInstance(); + Beatmap.Value.ApplyChangesToBeatmap = true; + try { playable = Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo, Mods.Value); From 4b05568d2d0159e98eb0b7664e87bad48fe8200a Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Tue, 27 Apr 2021 19:39:58 +0200 Subject: [PATCH 081/839] Revert "Fix bug causing the star rating to change when Random is enabled" This reverts commit 1dfe028c0209d4ef4cac6c3c2462af22797d81a9. --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 2 +- osu.Game/Beatmaps/WorkingBeatmap.cs | 10 ---------- .../Mods/IApplicableToBeatmapKeepStarRating.cs | 14 -------------- osu.Game/Screens/Play/Player.cs | 2 -- 4 files changed, 1 insertion(+), 27 deletions(-) delete mode 100644 osu.Game/Rulesets/Mods/IApplicableToBeatmapKeepStarRating.cs diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 3a4b5073b5..1d430ba711 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Osu.Mods /// /// Mod that randomises the positions of the s /// - public class OsuModRandom : ModRandom, IApplicableToBeatmapKeepStarRating + public class OsuModRandom : ModRandom, IApplicableToBeatmap { public override string Description => "It never gets boring!"; public override bool Ranked => false; diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index d49f6ed50b..e0eeaf6db0 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -32,11 +32,6 @@ namespace osu.Game.Beatmaps public readonly BeatmapMetadata Metadata; - /// - /// Only if this is set to true, changes made by mods that implement will be applied. - /// - public bool ApplyChangesToBeatmap; - protected AudioManager AudioManager { get; } protected WorkingBeatmap(BeatmapInfo beatmapInfo, AudioManager audioManager) @@ -171,15 +166,10 @@ namespace osu.Game.Beatmaps foreach (var mod in mods.OfType()) { - if (mod is IApplicableToBeatmapKeepStarRating && !ApplyChangesToBeatmap) - continue; - cancellationSource.Token.ThrowIfCancellationRequested(); mod.ApplyToBeatmap(converted); } - ApplyChangesToBeatmap = false; - return converted; } } diff --git a/osu.Game/Rulesets/Mods/IApplicableToBeatmapKeepStarRating.cs b/osu.Game/Rulesets/Mods/IApplicableToBeatmapKeepStarRating.cs deleted file mode 100644 index 34db7e1be3..0000000000 --- a/osu.Game/Rulesets/Mods/IApplicableToBeatmapKeepStarRating.cs +++ /dev/null @@ -1,14 +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.Game.Beatmaps; - -namespace osu.Game.Rulesets.Mods -{ - /// - /// Interface for a that applies changes to a after conversion and post-processing has completed without changing its difficulty - /// - public interface IApplicableToBeatmapKeepStarRating : IApplicableToBeatmap - { - } -} diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 80eec64884..27a4fcc291 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -455,8 +455,6 @@ namespace osu.Game.Screens.Play rulesetInfo = Ruleset.Value ?? Beatmap.Value.BeatmapInfo.Ruleset; ruleset = rulesetInfo.CreateInstance(); - Beatmap.Value.ApplyChangesToBeatmap = true; - try { playable = Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo, Mods.Value); From a141a4e9e6641e322740240258179d5b06718b0b Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Tue, 27 Apr 2021 20:44:36 +0200 Subject: [PATCH 082/839] Add setting "Seed" Random numbers are now generated with the seed specified in the mod settings. --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 1d430ba711..675aa4a0b3 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -2,7 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Bindables; +using osu.Framework.Logging; using osu.Game.Beatmaps; +using osu.Game.Configuration; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects; @@ -24,9 +27,23 @@ namespace osu.Game.Rulesets.Osu.Mods private const byte border_distance_x = 128; private const byte border_distance_y = 96; + [SettingSource("Seed", "Seed for the random number generator")] + public Bindable Seed { get; } = new Bindable + { + Value = "0" + }; + public void ApplyToBeatmap(IBeatmap beatmap) { - var rng = new Random(); + if (!int.TryParse(Seed.Value, out var seed)) + { + var e = new FormatException("Seed must be an integer"); + Logger.Error(e, "Could not load beatmap: RNG seed must be an integer."); + + return; + } + + var rng = new Random(seed); // Absolute angle float prevAngleRad = 0; From 95040f7edc42262fd43cfddac8a4315bc0c402a6 Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Tue, 27 Apr 2021 22:19:04 +0200 Subject: [PATCH 083/839] Change initial seed to a random number --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 675aa4a0b3..0124a3c28e 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -4,6 +4,7 @@ using System; using osu.Framework.Bindables; using osu.Framework.Logging; +using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Rulesets.Mods; @@ -30,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Mods [SettingSource("Seed", "Seed for the random number generator")] public Bindable Seed { get; } = new Bindable { - Value = "0" + Value = RNG.Next().ToString() }; public void ApplyToBeatmap(IBeatmap beatmap) From d2629561469e4dfd0efba561b757d4da10aac481 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 28 Apr 2021 18:27:40 +0900 Subject: [PATCH 084/839] Always use LifetimeEntry to manage hit objects in HitObjectContainer Previously, non-pooled DHOs were immediately added as children of the HOC when Add is called. Also, non-pooled DHOs were always attached to the HOC as children. New behavior is that non-pooled DHOs are only added after CheckChildLifetime, and only attached to the HOC while the DHOs are alive. - LifetimeManagementContainer inheritance of HOC is removed, as it is now all DHOs are "unmanaged" (previously `AddInternal(false)`). - The signature of `Clear` is changed, and it is now always not disposing the children immediately. --- .../Edit/ManiaBeatSnapGrid.cs | 2 +- .../HitObjectApplicationTestScene.cs | 2 +- .../Pooling/PoolableDrawableWithLifetime.cs | 2 +- osu.Game/Rulesets/UI/HitObjectContainer.cs | 66 +++++++++---------- .../Scrolling/ScrollingHitObjectContainer.cs | 4 +- 5 files changed, 38 insertions(+), 38 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs index afc08dcc96..9d1f5429a1 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs @@ -101,7 +101,7 @@ namespace osu.Game.Rulesets.Mania.Edit foreach (var line in grid.Objects.OfType()) availableLines.Push(line); - grid.Clear(false); + grid.Clear(); } if (selectionTimeRange == null) diff --git a/osu.Game.Rulesets.Taiko.Tests/HitObjectApplicationTestScene.cs b/osu.Game.Rulesets.Taiko.Tests/HitObjectApplicationTestScene.cs index a1d000386f..ac01508081 100644 --- a/osu.Game.Rulesets.Taiko.Tests/HitObjectApplicationTestScene.cs +++ b/osu.Game.Rulesets.Taiko.Tests/HitObjectApplicationTestScene.cs @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Taiko.Tests [SetUpSteps] public void SetUp() - => AddStep("clear SHOC", () => hitObjectContainer.Clear(false)); + => AddStep("clear SHOC", () => hitObjectContainer.Clear()); protected void AddHitObject(DrawableHitObject hitObject) => AddStep("add to SHOC", () => hitObjectContainer.Add(hitObject)); diff --git a/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs b/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs index 93e476be76..31f1768044 100644 --- a/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs +++ b/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Objects.Pooling /// /// The entry holding essential state of this . /// - protected TEntry? Entry { get; private set; } + public TEntry? Entry { get; private set; } /// /// Whether is applied to this . diff --git a/osu.Game/Rulesets/UI/HitObjectContainer.cs b/osu.Game/Rulesets/UI/HitObjectContainer.cs index 11312a46df..3d74cdedd6 100644 --- a/osu.Game/Rulesets/UI/HitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/HitObjectContainer.cs @@ -17,7 +17,7 @@ using osu.Game.Rulesets.Objects.Drawables; namespace osu.Game.Rulesets.UI { - public class HitObjectContainer : LifetimeManagementContainer, IHitObjectContainer + public class HitObjectContainer : CompositeDrawable, IHitObjectContainer { public IEnumerable Objects => InternalChildren.Cast().OrderBy(h => h.HitObject.StartTime); @@ -62,6 +62,7 @@ namespace osu.Game.Rulesets.UI private readonly Dictionary startTimeMap = new Dictionary(); private readonly Dictionary drawableMap = new Dictionary(); private readonly LifetimeEntryManager lifetimeManager = new LifetimeEntryManager(); + private readonly Dictionary nonPooledDrawableMap = new Dictionary(); [Resolved(CanBeNull = true)] private IPooledHitObjectProvider pooledObjectProvider { get; set; } @@ -72,6 +73,7 @@ namespace osu.Game.Rulesets.UI lifetimeManager.EntryBecameAlive += entryBecameAlive; lifetimeManager.EntryBecameDead += entryBecameDead; + lifetimeManager.EntryCrossedBoundary += entryCrossedBoundary; } protected override void LoadAsyncComplete() @@ -86,7 +88,13 @@ namespace osu.Game.Rulesets.UI public void Add(HitObjectLifetimeEntry entry) => lifetimeManager.AddEntry(entry); - public bool Remove(HitObjectLifetimeEntry entry) => lifetimeManager.RemoveEntry(entry); + public bool Remove(HitObjectLifetimeEntry entry) + { + if (!lifetimeManager.RemoveEntry(entry)) return false; + // It has to be done here because non-pooled entry may be removed by specifying its entry. + nonPooledDrawableMap.Remove(entry); + return true; + } private void entryBecameAlive(LifetimeEntry entry) => addDrawable((HitObjectLifetimeEntry)entry); @@ -96,7 +104,8 @@ namespace osu.Game.Rulesets.UI { Debug.Assert(!drawableMap.ContainsKey(entry)); - var drawable = pooledObjectProvider?.GetPooledDrawableRepresentation(entry.HitObject, null); + nonPooledDrawableMap.TryGetValue(entry, out var drawable); + drawable ??= pooledObjectProvider?.GetPooledDrawableRepresentation(entry.HitObject, null); if (drawable == null) throw new InvalidOperationException($"A drawable representation could not be retrieved for hitobject type: {entry.HitObject.GetType().ReadableName()}."); @@ -104,7 +113,7 @@ namespace osu.Game.Rulesets.UI drawable.OnRevertResult += onRevertResult; bindStartTime(drawable); - AddInternal(drawableMap[entry] = drawable, false); + AddInternal(drawableMap[entry] = drawable); OnAdd(drawable); HitObjectUsageBegan?.Invoke(entry.HitObject); @@ -127,50 +136,42 @@ namespace osu.Game.Rulesets.UI unbindStartTime(drawable); RemoveInternal(drawable); - HitObjectUsageFinished?.Invoke(entry.HitObject); + // The hit object is not freed when the DHO was not pooled. + if (!nonPooledDrawableMap.ContainsKey(entry)) + HitObjectUsageFinished?.Invoke(entry.HitObject); } #endregion #region Non-pooling support - public virtual void Add(DrawableHitObject hitObject) + public virtual void Add(DrawableHitObject drawable) { - bindStartTime(hitObject); + if (drawable.Entry == null) + throw new InvalidOperationException($"May not add a {nameof(DrawableHitObject)} without {nameof(HitObject)} associated"); - hitObject.OnNewResult += onNewResult; - hitObject.OnRevertResult += onRevertResult; - - AddInternal(hitObject); - OnAdd(hitObject); + nonPooledDrawableMap.Add(drawable.Entry, drawable); + Add(drawable.Entry); } - public virtual bool Remove(DrawableHitObject hitObject) + public virtual bool Remove(DrawableHitObject drawable) { - OnRemove(hitObject); - if (!RemoveInternal(hitObject)) + if (drawable.Entry == null) return false; - hitObject.OnNewResult -= onNewResult; - hitObject.OnRevertResult -= onRevertResult; - - unbindStartTime(hitObject); - - return true; + return Remove(drawable.Entry); } public int IndexOf(DrawableHitObject hitObject) => IndexOfInternal(hitObject); - protected override void OnChildLifetimeBoundaryCrossed(LifetimeBoundaryCrossedEvent e) + private void entryCrossedBoundary(LifetimeEntry entry, LifetimeBoundaryKind kind, LifetimeBoundaryCrossingDirection direction) { - if (!(e.Child is DrawableHitObject hitObject)) - return; + if (nonPooledDrawableMap.TryGetValue((HitObjectLifetimeEntry)entry, out var drawable)) + OnChildLifetimeBoundaryCrossed(new LifetimeBoundaryCrossedEvent(drawable, kind, direction)); + } - if ((e.Kind == LifetimeBoundaryKind.End && e.Direction == LifetimeBoundaryCrossingDirection.Forward) - || (e.Kind == LifetimeBoundaryKind.Start && e.Direction == LifetimeBoundaryCrossingDirection.Backward)) - { - hitObject.OnKilled(); - } + protected virtual void OnChildLifetimeBoundaryCrossed(LifetimeBoundaryCrossedEvent e) + { } #endregion @@ -195,12 +196,11 @@ namespace osu.Game.Rulesets.UI { } - public virtual void Clear(bool disposeChildren = true) + public virtual void Clear() { lifetimeManager.ClearEntries(); - - ClearInternal(disposeChildren); - unbindAllStartTimes(); + nonPooledDrawableMap.Clear(); + Debug.Assert(InternalChildren.Count == 0 && startTimeMap.Count == 0 && drawableMap.Count == 0, "All hit objects should have been removed"); } protected override bool CheckChildrenLife() diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index 289578f3d8..a9eaf3da68 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -50,9 +50,9 @@ namespace osu.Game.Rulesets.UI.Scrolling timeRange.ValueChanged += _ => layoutCache.Invalidate(); } - public override void Clear(bool disposeChildren = true) + public override void Clear() { - base.Clear(disposeChildren); + base.Clear(); toComputeLifetime.Clear(); layoutComputed.Clear(); From f55aa016bec4acd26e5525754a50661398da52b5 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 26 Apr 2021 15:53:07 +0900 Subject: [PATCH 085/839] Adopt HitObjectContainer change in a test Non-pooled objects are attached as children only while alive --- osu.Game.Tests/Gameplay/TestSceneHitObjectContainer.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Gameplay/TestSceneHitObjectContainer.cs b/osu.Game.Tests/Gameplay/TestSceneHitObjectContainer.cs index f2bfccb6de..8f3d3f1276 100644 --- a/osu.Game.Tests/Gameplay/TestSceneHitObjectContainer.cs +++ b/osu.Game.Tests/Gameplay/TestSceneHitObjectContainer.cs @@ -4,6 +4,7 @@ using JetBrains.Annotations; using NUnit.Framework; using osu.Framework.Testing; +using osu.Framework.Timing; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI; @@ -19,7 +20,14 @@ namespace osu.Game.Tests.Gameplay [SetUp] public void Setup() => Schedule(() => { - Child = container = new HitObjectContainer(); + Child = container = new HitObjectContainer + { + Clock = new FramedClock(new ManualClock + { + // Make sure hit objects with `StartTime == 0` are alive + CurrentTime = -1 + }) + }; }); [Test] From 799d2a3300dce4b833a2ca7e9e176d1079aa6038 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 21 Apr 2021 13:00:46 +0900 Subject: [PATCH 086/839] Replace failed mania test (pooling not accounted) with a more robust test Also fix null reference in Playfield --- .../TestSceneDrawableNote.cs | 67 +++++++++++++++++++ .../TestSceneHoldNoteInput.cs | 15 +---- osu.Game/Rulesets/UI/Playfield.cs | 7 +- 3 files changed, 73 insertions(+), 16 deletions(-) create mode 100644 osu.Game.Rulesets.Mania.Tests/TestSceneDrawableNote.cs diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneDrawableNote.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneDrawableNote.cs new file mode 100644 index 0000000000..4a6c59e297 --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneDrawableNote.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 NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Timing; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Mania.Objects.Drawables; +using osu.Game.Rulesets.Mania.UI; +using osu.Game.Rulesets.UI.Scrolling; +using osu.Game.Tests.Visual; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Mania.Tests +{ + public class TestSceneDrawableManiaHitObject : OsuTestScene + { + private readonly ManualClock clock = new ManualClock(); + + private Column column; + + [SetUp] + public void SetUp() => Schedule(() => + { + Child = new ScrollingTestContainer(ScrollingDirection.Down) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + TimeRange = 2000, + Clock = new FramedClock(clock), + Child = column = new Column(0) + { + Action = { Value = ManiaAction.Key1 }, + Height = 0.85f, + AccentColour = Color4.Gray + }, + }; + }); + + [Test] + public void TestHoldNoteHeadVisibility() + { + DrawableHoldNote note = null; + AddStep("Add hold note", () => + { + var h = new HoldNote + { + StartTime = 0, + Duration = 1000 + }; + h.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + column.Add(note = new DrawableHoldNote(h)); + }); + AddStep("Hold key", () => + { + clock.CurrentTime = 0; + note.OnPressed(ManiaAction.Key1); + }); + AddStep("progress time", () => clock.CurrentTime = 500); + AddAssert("head is visible", () => note.Head.Alpha == 1); + } + } +} diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs index 668487f673..387c5f4195 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs @@ -4,14 +4,11 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; -using osu.Framework.Screens; -using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Replays; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mania.Objects; -using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Replays; using osu.Game.Rulesets.Mania.Scoring; using osu.Game.Rulesets.Objects; @@ -411,17 +408,7 @@ namespace osu.Game.Rulesets.Mania.Tests judgementResults = new List(); }); - AddUntilStep("Beatmap at 0", () => Beatmap.Value.Track.CurrentTime == 0); - AddUntilStep("Wait until player is loaded", () => currentPlayer.IsCurrentScreen()); - - AddUntilStep("wait for head", () => currentPlayer.GameplayClockContainer.GameplayClock.CurrentTime >= time_head); - AddAssert("head is visible", - () => currentPlayer.ChildrenOfType() - .Single(note => note.HitObject == beatmap.HitObjects[0]) - .Head - .Alpha == 1); - - AddUntilStep("Wait for completion", () => currentPlayer.ScoreProcessor.HasCompleted.Value); + AddUntilStep("Wait for completion", () => currentPlayer.ScoreProcessor?.HasCompleted.Value == true); } private class ScoreAccessibleReplayPlayer : ReplayPlayer diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs index 17d3cf01a4..b154288dba 100644 --- a/osu.Game/Rulesets/UI/Playfield.cs +++ b/osu.Game/Rulesets/UI/Playfield.cs @@ -354,8 +354,11 @@ namespace osu.Game.Rulesets.UI // If this is the first time this DHO is being used, then apply the DHO mods. // This is done before Apply() so that the state is updated once when the hitobject is applied. - foreach (var m in mods.OfType()) - m.ApplyToDrawableHitObjects(dho.Yield()); + if (mods != null) + { + foreach (var m in mods.OfType()) + m.ApplyToDrawableHitObjects(dho.Yield()); + } } if (!lifetimeEntryMap.TryGetValue(hitObject, out var entry)) From 971ca398260275c52fb16be82bfb38993777c504 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Sun, 18 Apr 2021 21:46:30 +0900 Subject: [PATCH 087/839] Fix failing taiko tests Non-pooled DHO is now not eagerly loaded --- .../TestSceneFlyingHits.cs | 20 +++++++++---------- .../TestSceneHits.cs | 8 ++++++-- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneFlyingHits.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneFlyingHits.cs index 63854e7ead..5738be05d7 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneFlyingHits.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneFlyingHits.cs @@ -20,20 +20,19 @@ namespace osu.Game.Rulesets.Taiko.Tests [TestCase(HitType.Rim)] public void TestFlyingHits(HitType hitType) { - DrawableFlyingHit flyingHit = null; - AddStep("add flying hit", () => { addFlyingHit(hitType); - - // flying hits all land in one common scrolling container (and stay there for rewind purposes), - // so we need to manually get the latest one. - flyingHit = this.ChildrenOfType() - .OrderByDescending(h => h.HitObject.StartTime) - .FirstOrDefault(); }); - AddAssert("hit type is correct", () => flyingHit.HitObject.Type == hitType); + AddAssert("hit type is correct", () => + { + // flying hits all land in one common scrolling container (and stay there for rewind purposes), + // so we need to manually get the latest one. + return this.ChildrenOfType() + .OrderByDescending(h => h.HitObject.StartTime) + .FirstOrDefault()?.HitObject.Type == hitType; + }); } private void addFlyingHit(HitType hitType) @@ -42,7 +41,8 @@ namespace osu.Game.Rulesets.Taiko.Tests DrawableDrumRollTick h; DrawableRuleset.Playfield.Add(h = new DrawableDrumRollTick(tick) { JudgementType = hitType }); - ((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h, new JudgementResult(tick, new TaikoDrumRollTickJudgement()) { Type = HitResult.Great }); + h.OnLoadComplete += _ => + ((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h, new JudgementResult(tick, new TaikoDrumRollTickJudgement()) { Type = HitResult.Great }); } } } diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs index 87c936d386..06acdad3d6 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs @@ -129,8 +129,12 @@ namespace osu.Game.Rulesets.Taiko.Tests DrawableRuleset.Playfield.Add(h); - ((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h, new JudgementResult(hit, new TaikoJudgement()) { Type = hitResult }); - ((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h.NestedHitObjects.Single(), new JudgementResult(hit.NestedHitObjects.Single(), new TaikoStrongJudgement()) { Type = HitResult.Great }); + h.OnLoadComplete += _ => + { + ((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h, new JudgementResult(hit, new TaikoJudgement()) { Type = hitResult }); + ((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h.NestedHitObjects.Single(), + new JudgementResult(hit.NestedHitObjects.Single(), new TaikoStrongJudgement()) { Type = HitResult.Great }); + }; } private void addMissJudgement() From b88e5a31ea838406b25dfb2e57e992fbb14f89f8 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 28 Apr 2021 12:55:04 +0900 Subject: [PATCH 088/839] Add failing test showing lifetime not recomputed with pooled objects --- .../Gameplay/TestSceneDrawableScrollingRuleset.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs index 9931ee4a45..75a5eec6f7 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs @@ -90,6 +90,20 @@ namespace osu.Game.Tests.Visual.Gameplay assertChildPosition(5); } + [TestCase("pooled")] + [TestCase("non-pooled")] + public void TestLifetimeRecomputedWhenTimeRangeChanges(string pooled) + { + var beatmap = createBeatmap(_ => pooled == "pooled" ? new TestPooledHitObject() : new TestHitObject()); + beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = time_range }); + createTest(beatmap); + + assertDead(3); + + AddStep("increase time range", () => drawableRuleset.TimeRange.Value = 3 * time_range); + assertPosition(3, 1); + } + [Test] public void TestRelativeBeatLengthScaleSingleTimingPoint() { From 5aa522b1c28cb500ac1985595234b8cd0acacb69 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 28 Apr 2021 20:23:52 +0900 Subject: [PATCH 089/839] Completely delegate DHO lifetime to Entry lifetime A downside is lifetime update is not caught by LifetimeManagementContainer if used. --- .../Gameplay/TestSceneDrawableHitObject.cs | 13 ++++---- .../Pooling/PoolableDrawableWithLifetime.cs | 32 ++++++------------- 2 files changed, 17 insertions(+), 28 deletions(-) diff --git a/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs b/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs index 2e3f192f1b..0bec02c488 100644 --- a/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs +++ b/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs @@ -45,15 +45,16 @@ namespace osu.Game.Tests.Gameplay AddStep("Create DHO", () => { dho = new TestDrawableHitObject(null); - dho.Apply(entry = new TestLifetimeEntry(new HitObject()) - { - LifetimeStart = 0, - LifetimeEnd = 1000, - }); + dho.Apply(entry = new TestLifetimeEntry(new HitObject())); Child = dho; }); - AddStep("KeepAlive = true", () => entry.KeepAlive = true); + AddStep("KeepAlive = true", () => + { + entry.LifetimeStart = 0; + entry.LifetimeEnd = 1000; + entry.KeepAlive = true; + }); AddAssert("Lifetime is overriden", () => entry.LifetimeStart == double.MinValue && entry.LifetimeEnd == double.MaxValue); AddStep("Set LifetimeStart", () => dho.LifetimeStart = 500); diff --git a/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs b/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs index 31f1768044..e94b6dca9d 100644 --- a/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs +++ b/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs @@ -28,14 +28,20 @@ namespace osu.Game.Rulesets.Objects.Pooling public override double LifetimeStart { - get => base.LifetimeStart; - set => setLifetime(value, LifetimeEnd); + get => Entry?.LifetimeStart ?? double.MinValue; + set + { + if (Entry != null) Entry.LifetimeStart = value; + } } public override double LifetimeEnd { - get => base.LifetimeEnd; - set => setLifetime(LifetimeStart, value); + get => Entry?.LifetimeEnd ?? double.MaxValue; + set + { + if (Entry != null) Entry.LifetimeEnd = value; + } } public override bool RemoveWhenNotAlive => false; @@ -64,11 +70,8 @@ namespace osu.Game.Rulesets.Objects.Pooling if (HasEntryApplied) free(); - setLifetime(entry.LifetimeStart, entry.LifetimeEnd); Entry = entry; - OnApply(entry); - HasEntryApplied = true; } @@ -95,27 +98,12 @@ namespace osu.Game.Rulesets.Objects.Pooling { } - private void setLifetime(double start, double end) - { - base.LifetimeStart = start; - base.LifetimeEnd = end; - - if (Entry != null) - { - Entry.LifetimeStart = start; - Entry.LifetimeEnd = end; - } - } - private void free() { Debug.Assert(Entry != null && HasEntryApplied); OnFree(Entry); - Entry = null; - setLifetime(double.MaxValue, double.MaxValue); - HasEntryApplied = false; } } From 1d023dcedbb64fcac9d4956c73de22407e51b035 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 26 Apr 2021 19:53:38 +0900 Subject: [PATCH 090/839] Fix mania editor null reference --- .../Edit/Blueprints/ManiaSelectionBlueprint.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs index 384f49d9b2..8f8f45c0dd 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs @@ -18,6 +18,9 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints [Resolved] private IScrollingInfo scrollingInfo { get; set; } + // Overriding the base because this method is called right after `Column` is changed and `DrawableObject` is not yet loaded and Parent is not set. + public override Vector2 GetInstantDelta(Vector2 screenSpacePosition) => Parent.ToLocalSpace(screenSpacePosition) - Position; + protected ManiaSelectionBlueprint(DrawableHitObject drawableObject) : base(drawableObject) { From 4cc94efb06258cc7cf5c445e6318f3ac5cf6d852 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 28 Apr 2021 20:38:44 +0900 Subject: [PATCH 091/839] Fix failing mania test --- .../Editor/TestSceneNotePlacementBlueprint.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneNotePlacementBlueprint.cs b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneNotePlacementBlueprint.cs index 36c34a8fb9..a162c5ec44 100644 --- a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneNotePlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneNotePlacementBlueprint.cs @@ -15,7 +15,6 @@ using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Tests.Visual; -using osuTK; using osuTK.Input; namespace osu.Game.Rulesets.Mania.Tests.Editor @@ -35,7 +34,11 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor [Test] public void TestPlaceBeforeCurrentTimeDownwards() { - AddStep("move mouse before current time", () => InputManager.MoveMouseTo(this.ChildrenOfType().Single().ScreenSpaceDrawQuad.BottomLeft - new Vector2(0, 10))); + AddStep("move mouse before current time", () => + { + var column = this.ChildrenOfType().Single(); + InputManager.MoveMouseTo(column.ScreenSpacePositionAtTime(-100)); + }); AddStep("click", () => InputManager.Click(MouseButton.Left)); @@ -45,7 +48,11 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor [Test] public void TestPlaceAfterCurrentTimeDownwards() { - AddStep("move mouse after current time", () => InputManager.MoveMouseTo(this.ChildrenOfType().Single())); + AddStep("move mouse after current time", () => + { + var column = this.ChildrenOfType().Single(); + InputManager.MoveMouseTo(column.ScreenSpacePositionAtTime(100)); + }); AddStep("click", () => InputManager.Click(MouseButton.Left)); From c83c8040573ad0cdd3c650487b1aa9a28d756d71 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 29 Apr 2021 14:42:41 +0900 Subject: [PATCH 092/839] Expose lifetime entries from HOC --- osu.Game/Rulesets/UI/HitObjectContainer.cs | 24 +++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/UI/HitObjectContainer.cs b/osu.Game/Rulesets/UI/HitObjectContainer.cs index 3d74cdedd6..1d32313f2b 100644 --- a/osu.Game/Rulesets/UI/HitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/HitObjectContainer.cs @@ -19,6 +19,16 @@ namespace osu.Game.Rulesets.UI { public class HitObjectContainer : CompositeDrawable, IHitObjectContainer { + /// + /// All entries in this including dead entries. + /// + public IEnumerable Entries => allEntries; + + /// + /// All alive entries and s used by the entries. + /// + public IEnumerable<(HitObjectLifetimeEntry Entry, DrawableHitObject Drawable)> AliveEntries => drawableMap.Select(x => (x.Key, x.Value)); + public IEnumerable Objects => InternalChildren.Cast().OrderBy(h => h.HitObject.StartTime); public IEnumerable AliveObjects => AliveInternalChildren.Cast().OrderBy(h => h.HitObject.StartTime); @@ -60,10 +70,13 @@ namespace osu.Game.Rulesets.UI internal double FutureLifetimeExtension { get; set; } private readonly Dictionary startTimeMap = new Dictionary(); + private readonly Dictionary drawableMap = new Dictionary(); - private readonly LifetimeEntryManager lifetimeManager = new LifetimeEntryManager(); private readonly Dictionary nonPooledDrawableMap = new Dictionary(); + private readonly LifetimeEntryManager lifetimeManager = new LifetimeEntryManager(); + private readonly HashSet allEntries = new HashSet(); + [Resolved(CanBeNull = true)] private IPooledHitObjectProvider pooledObjectProvider { get; set; } @@ -86,13 +99,18 @@ namespace osu.Game.Rulesets.UI #region Pooling support - public void Add(HitObjectLifetimeEntry entry) => lifetimeManager.AddEntry(entry); + public void Add(HitObjectLifetimeEntry entry) + { + allEntries.Add(entry); + lifetimeManager.AddEntry(entry); + } public bool Remove(HitObjectLifetimeEntry entry) { if (!lifetimeManager.RemoveEntry(entry)) return false; - // It has to be done here because non-pooled entry may be removed by specifying its entry. + // The entry has to be removed from the non-pooled map here because non-pooled entry may be removed by specifying its entry. nonPooledDrawableMap.Remove(entry); + allEntries.Remove(entry); return true; } From 632bb70e0f7d051c1ee82502122ce5946227cd95 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 29 Apr 2021 15:04:32 +0900 Subject: [PATCH 093/839] Use entry to calculate lifetime in ScrollingHOC DHOs cannot be used to calculate lifetime, it is not created before the entry became alive. --- .../Scrolling/ScrollingHitObjectContainer.cs | 60 ++++++++----------- 1 file changed, 24 insertions(+), 36 deletions(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index a9eaf3da68..915bab9a51 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -5,7 +5,9 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Primitives; using osu.Framework.Layout; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; using osuTK; @@ -17,16 +19,18 @@ namespace osu.Game.Rulesets.UI.Scrolling private readonly IBindable timeRange = new BindableDouble(); private readonly IBindable direction = new Bindable(); - /// - /// Hit objects which require lifetime computation in the next update call. - /// - private readonly HashSet toComputeLifetime = new HashSet(); - /// /// A set containing all which have an up-to-date layout. /// private readonly HashSet layoutComputed = new HashSet(); + /// + /// A conservative estimate of maximum bounding box of a + /// with respect to the start time position of the hit object. + /// It is used to calculate when the object appears inbound. + /// + protected virtual RectangleF GetDrawRectangle(HitObjectLifetimeEntry entry) => new RectangleF().Inflate(100); + [Resolved] private IScrollingInfo scrollingInfo { get; set; } @@ -54,7 +58,6 @@ namespace osu.Game.Rulesets.UI.Scrolling { base.Clear(); - toComputeLifetime.Clear(); layoutComputed.Clear(); } @@ -166,7 +169,6 @@ namespace osu.Game.Rulesets.UI.Scrolling private void onRemoveRecursive(DrawableHitObject hitObject) { - toComputeLifetime.Remove(hitObject); layoutComputed.Remove(hitObject); hitObject.DefaultsApplied -= invalidateHitObject; @@ -175,14 +177,11 @@ namespace osu.Game.Rulesets.UI.Scrolling onRemoveRecursive(nested); } - /// - /// Make this lifetime and layout computed in next update. - /// private void invalidateHitObject(DrawableHitObject hitObject) { - // Lifetime computation is delayed until next update because - // when the hit object is not pooled this container is not loaded here and `scrollLength` cannot be computed. - toComputeLifetime.Add(hitObject); + if (hitObject.ParentHitObject == null) + updateLifetime(hitObject.Entry); + layoutComputed.Remove(hitObject); } @@ -194,13 +193,8 @@ namespace osu.Game.Rulesets.UI.Scrolling if (!layoutCache.IsValid) { - toComputeLifetime.Clear(); - - foreach (var hitObject in Objects) - { - if (hitObject.HitObject != null) - toComputeLifetime.Add(hitObject); - } + foreach (var entry in Entries) + updateLifetime(entry); layoutComputed.Clear(); @@ -220,11 +214,6 @@ namespace osu.Game.Rulesets.UI.Scrolling layoutCache.Validate(); } - - foreach (var hitObject in toComputeLifetime) - hitObject.LifetimeStart = computeOriginAdjustedLifetimeStart(hitObject); - - toComputeLifetime.Clear(); } protected override void UpdateAfterChildrenLife() @@ -247,32 +236,31 @@ namespace osu.Game.Rulesets.UI.Scrolling } } - private double computeOriginAdjustedLifetimeStart(DrawableHitObject hitObject) + private void updateLifetime(HitObjectLifetimeEntry entry) { - float originAdjustment = 0.0f; + var rectangle = GetDrawRectangle(entry); + float startOffset = 0; - // calculate the dimension of the part of the hitobject that should already be visible - // when the hitobject origin first appears inside the scrolling container switch (direction.Value) { - case ScrollingDirection.Up: - originAdjustment = hitObject.OriginPosition.Y; + case ScrollingDirection.Right: + startOffset = rectangle.Right; break; case ScrollingDirection.Down: - originAdjustment = hitObject.DrawHeight - hitObject.OriginPosition.Y; + startOffset = rectangle.Bottom; break; case ScrollingDirection.Left: - originAdjustment = hitObject.OriginPosition.X; + startOffset = -rectangle.Left; break; - case ScrollingDirection.Right: - originAdjustment = hitObject.DrawWidth - hitObject.OriginPosition.X; + case ScrollingDirection.Up: + startOffset = -rectangle.Top; break; } - return scrollingInfo.Algorithm.GetDisplayStartTime(hitObject.HitObject.StartTime, originAdjustment, timeRange.Value, scrollLength); + entry.LifetimeStart = scrollingInfo.Algorithm.GetDisplayStartTime(entry.HitObject.StartTime, startOffset, timeRange.Value, scrollLength); } private void updateLayoutRecursive(DrawableHitObject hitObject) From 73dfb04df8ba3b45ab9fbdd5bb1934d55f61defc Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 29 Apr 2021 15:17:30 +0900 Subject: [PATCH 094/839] Fix uninitialized scrollLength value is used --- .../Scrolling/ScrollingHitObjectContainer.cs | 33 +++++-------------- 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index 915bab9a51..538d4d1d11 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -185,8 +185,6 @@ namespace osu.Game.Rulesets.UI.Scrolling layoutComputed.Remove(hitObject); } - private float scrollLength; - protected override void Update() { base.Update(); @@ -199,29 +197,16 @@ namespace osu.Game.Rulesets.UI.Scrolling layoutComputed.Clear(); scrollingInfo.Algorithm.Reset(); - - switch (direction.Value) - { - case ScrollingDirection.Up: - case ScrollingDirection.Down: - scrollLength = DrawSize.Y; - break; - - default: - scrollLength = DrawSize.X; - break; - } - layoutCache.Validate(); } } + // We need to calculate hit object positions (including nested hit objects) as soon as possible after lifetimes + // to prevent hit objects displayed in a wrong position for one frame. protected override void UpdateAfterChildrenLife() { base.UpdateAfterChildrenLife(); - // We need to calculate hit object positions (including nested hit objects) as soon as possible after lifetimes - // to prevent hit objects displayed in a wrong position for one frame. // Only AliveObjects need to be considered for layout (reduces overhead in the case of scroll speed changes). foreach (var obj in AliveObjects) { @@ -260,7 +245,7 @@ namespace osu.Game.Rulesets.UI.Scrolling break; } - entry.LifetimeStart = scrollingInfo.Algorithm.GetDisplayStartTime(entry.HitObject.StartTime, startOffset, timeRange.Value, scrollLength); + entry.LifetimeStart = scrollingInfo.Algorithm.GetDisplayStartTime(entry.HitObject.StartTime, startOffset, timeRange.Value, getLength()); } private void updateLayoutRecursive(DrawableHitObject hitObject) @@ -271,12 +256,12 @@ namespace osu.Game.Rulesets.UI.Scrolling { case ScrollingDirection.Up: case ScrollingDirection.Down: - hitObject.Height = scrollingInfo.Algorithm.GetLength(hitObject.HitObject.StartTime, e.EndTime, timeRange.Value, scrollLength); + hitObject.Height = scrollingInfo.Algorithm.GetLength(hitObject.HitObject.StartTime, e.EndTime, timeRange.Value, getLength()); break; case ScrollingDirection.Left: case ScrollingDirection.Right: - hitObject.Width = scrollingInfo.Algorithm.GetLength(hitObject.HitObject.StartTime, e.EndTime, timeRange.Value, scrollLength); + hitObject.Width = scrollingInfo.Algorithm.GetLength(hitObject.HitObject.StartTime, e.EndTime, timeRange.Value, getLength()); break; } } @@ -295,19 +280,19 @@ namespace osu.Game.Rulesets.UI.Scrolling switch (direction.Value) { case ScrollingDirection.Up: - hitObject.Y = scrollingInfo.Algorithm.PositionAt(hitObject.HitObject.StartTime, currentTime, timeRange.Value, scrollLength); + hitObject.Y = scrollingInfo.Algorithm.PositionAt(hitObject.HitObject.StartTime, currentTime, timeRange.Value, getLength()); break; case ScrollingDirection.Down: - hitObject.Y = -scrollingInfo.Algorithm.PositionAt(hitObject.HitObject.StartTime, currentTime, timeRange.Value, scrollLength); + hitObject.Y = -scrollingInfo.Algorithm.PositionAt(hitObject.HitObject.StartTime, currentTime, timeRange.Value, getLength()); break; case ScrollingDirection.Left: - hitObject.X = scrollingInfo.Algorithm.PositionAt(hitObject.HitObject.StartTime, currentTime, timeRange.Value, scrollLength); + hitObject.X = scrollingInfo.Algorithm.PositionAt(hitObject.HitObject.StartTime, currentTime, timeRange.Value, getLength()); break; case ScrollingDirection.Right: - hitObject.X = -scrollingInfo.Algorithm.PositionAt(hitObject.HitObject.StartTime, currentTime, timeRange.Value, scrollLength); + hitObject.X = -scrollingInfo.Algorithm.PositionAt(hitObject.HitObject.StartTime, currentTime, timeRange.Value, getLength()); break; } } From fd8e552a8bb12d2aa854adef2ab0d66ed7d5ea9b Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 29 Apr 2021 19:36:52 +0900 Subject: [PATCH 095/839] Fix filename not matching class name --- ...estSceneDrawableNote.cs => TestSceneDrawableManiaHitObject.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename osu.Game.Rulesets.Mania.Tests/{TestSceneDrawableNote.cs => TestSceneDrawableManiaHitObject.cs} (100%) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneDrawableNote.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneDrawableManiaHitObject.cs similarity index 100% rename from osu.Game.Rulesets.Mania.Tests/TestSceneDrawableNote.cs rename to osu.Game.Rulesets.Mania.Tests/TestSceneDrawableManiaHitObject.cs From 6bed268bd8ff7719b36bdba5e253de0291c6deaa Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Sat, 1 May 2021 04:01:43 +0200 Subject: [PATCH 096/839] Enhance mod settings and add option "Random seed" + slight adjustments --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 161 ++++++++++++++++++--- 1 file changed, 144 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 0124a3c28e..9a6127cdad 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -2,13 +2,20 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Logging; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Platform; using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Configuration; +using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.UI; using osuTK; @@ -25,36 +32,58 @@ namespace osu.Game.Rulesets.Osu.Mods // The distances from the hit objects to the borders of the playfield they start to "turn around" and curve towards the middle. // The closer the hit objects draw to the border, the sharper the turn - private const byte border_distance_x = 128; - private const byte border_distance_y = 96; + private const byte border_distance_x = 192; + private const byte border_distance_y = 144; - [SettingSource("Seed", "Seed for the random number generator")] - public Bindable Seed { get; } = new Bindable + private static readonly Bindable seed = new Bindable { - Value = RNG.Next().ToString() + Default = -1 }; - public void ApplyToBeatmap(IBeatmap beatmap) + private static readonly BindableBool random_seed = new BindableBool { - if (!int.TryParse(Seed.Value, out var seed)) - { - var e = new FormatException("Seed must be an integer"); - Logger.Error(e, "Could not load beatmap: RNG seed must be an integer."); + Value = true, + Default = true + }; + [SettingSource("Random seed", "Generate a random seed for the beatmap generation")] + public BindableBool RandomSeed => random_seed; + + [SettingSource("Seed", "Seed for the random beatmap generation", SettingControlType = typeof(OsuModRandomSettingsControl))] + public Bindable Seed => seed; + + internal static bool CustomSeedDisabled => random_seed.Value; + + public OsuModRandom() + { + if (seed.Default != -1) return; - } - var rng = new Random(seed); + var random = RNG.Next(); + seed.Value = random; + seed.Default = random; + seed.BindValueChanged(e => seed.Default = e.NewValue); + } + + public void ApplyToBeatmap(IBeatmap iBeatmap) + { + if (!(iBeatmap is OsuBeatmap beatmap)) + return; + + if (RandomSeed.Value) + seed.Value = RNG.Next(); + + var rng = new Random(seed.Value); // Absolute angle float prevAngleRad = 0; // Absolute positions - Vector2 prevPosUnchanged = ((OsuHitObject)beatmap.HitObjects[0]).Position; - Vector2 prevPosChanged = ((OsuHitObject)beatmap.HitObjects[0]).Position; + Vector2 prevPosUnchanged = beatmap.HitObjects[0].Position; + Vector2 prevPosChanged = beatmap.HitObjects[0].Position; // rateOfChangeMultiplier changes every i iterations to prevent shaky-line-shaped streams - byte i = 5; + byte i = 3; float rateOfChangeMultiplier = 0; foreach (var beatmapHitObject in beatmap.HitObjects) @@ -69,7 +98,7 @@ namespace osu.Game.Rulesets.Osu.Mods // Angle of the vector pointing from the last to the current hit object float angleRad = 0; - if (i >= 5) + if (i >= 3) { i = 0; rateOfChangeMultiplier = (float)rng.NextDouble() * 2 - 1; @@ -234,4 +263,102 @@ namespace osu.Game.Rulesets.Osu.Mods ); } } + + public class OsuModRandomSettingsControl : SettingsItem + { + [Resolved] + private static GameHost host { get; set; } + + [BackgroundDependencyLoader] + private void load(GameHost gameHost) => host = gameHost; + + protected override Drawable CreateControl() => new SeedControl + { + RelativeSizeAxes = Axes.X, + Margin = new MarginPadding { Top = 5 } + }; + + private sealed class SeedControl : CompositeDrawable, IHasCurrentValue + { + private readonly BindableWithCurrent current = new BindableWithCurrent(); + + public Bindable Current + { + get => current; + set => Scheduler.Add(() => current.Current = value); + } + + private readonly OsuNumberBox seedNumberBox; + + public SeedControl() + { + AutoSizeAxes = Axes.Y; + + InternalChildren = new[] + { + new GridContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + ColumnDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.Absolute, 2), + new Dimension(GridSizeMode.Relative, 0.25f) + }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize) + }, + Content = new[] + { + new Drawable[] + { + seedNumberBox = new OsuNumberBox + { + RelativeSizeAxes = Axes.X, + CommitOnFocusLost = true + }, + null, + new TriangleButton + { + RelativeSizeAxes = Axes.Both, + Height = 1, + Text = "Copy", + Action = copySeedToClipboard + } + } + } + } + }; + + seedNumberBox.Current.BindValueChanged(onTextBoxValueChanged); + } + + private void onTextBoxValueChanged(ValueChangedEvent e) + { + string seed = e.NewValue; + + while (!string.IsNullOrEmpty(seed) && !int.TryParse(seed, out _)) + seed = seed[..^1]; + + if (!int.TryParse(seed, out var intVal)) + intVal = 0; + + current.Value = intVal; + } + + private void copySeedToClipboard() => host.GetClipboard().SetText(seedNumberBox.Text); + + protected override void Update() + { + seedNumberBox.ReadOnly = OsuModRandom.CustomSeedDisabled; + + if (seedNumberBox.HasFocus) + return; + + seedNumberBox.Text = current.Current.Value.ToString(); + } + } + } } From ce8b2c1e379de69697ca880a24be662ae726a120 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Mon, 17 May 2021 01:08:02 +0700 Subject: [PATCH 097/839] add WikiMarkdownContainer --- .../Wiki/Markdown/WikiMarkdownContainer.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs new file mode 100644 index 0000000000..ac128d79dc --- /dev/null +++ b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs @@ -0,0 +1,15 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Graphics.Containers.Markdown; + +namespace osu.Game.Overlays.Wiki.Markdown +{ + public class WikiMarkdownContainer : OsuMarkdownContainer + { + public string CurrentPath + { + set => Schedule(() => DocumentUrl += $"wiki/{value}"); + } + } +} From a38c33841a8b4cb46f07acc16ad5bdc79b635a58 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sun, 23 May 2021 16:30:41 +0700 Subject: [PATCH 098/839] add test scene wiki container --- .../Online/TestSceneWikiMarkdownContainer.cs | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs new file mode 100644 index 0000000000..fd4c2e2372 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs @@ -0,0 +1,58 @@ +// 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.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Overlays; +using osu.Game.Overlays.Wiki.Markdown; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneWikiMarkdownContainer : OsuTestScene + { + private WikiMarkdownContainer markdownContainer; + + [Cached] + private readonly OverlayColourProvider overlayColour = new OverlayColourProvider(OverlayColourScheme.Orange); + + [SetUp] + public void Setup() => Schedule(() => + { + Children = new Drawable[] + { + new Box + { + Colour = overlayColour.Background5, + RelativeSizeAxes = Axes.Both, + }, + new BasicScrollContainer + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding(20), + Child = markdownContainer = new WikiMarkdownContainer() + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + } + } + }; + }); + + [Test] + public void TestLink() + { + AddStep("set current path", () => markdownContainer.CurrentPath = "Article_styling_criteria/"); + + AddStep("set '/wiki/Main_Page''", () => markdownContainer.Text = "[wiki main page](/wiki/Main_Page)"); + + AddStep("set '../FAQ''", () => markdownContainer.Text = "[FAQ](../FAQ)"); + + AddStep("set './Writing''", () => markdownContainer.Text = "[wiki writing guidline](./Writing)"); + + AddStep("set 'Formatting''", () => markdownContainer.Text = "[wiki formatting guidline](Formatting)"); + } + } +} From 98af998978c7f128733cda2dc97db0694a64fd10 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 25 May 2021 09:56:26 +0700 Subject: [PATCH 099/839] add test markdown class --- .../Online/TestSceneWikiMarkdownContainer.cs | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs index fd4c2e2372..993525e7db 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs @@ -1,11 +1,15 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using Markdig.Syntax.Inlines; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Containers.Markdown; using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics.Containers.Markdown; using osu.Game.Overlays; using osu.Game.Overlays.Wiki.Markdown; @@ -13,7 +17,7 @@ namespace osu.Game.Tests.Visual.Online { public class TestSceneWikiMarkdownContainer : OsuTestScene { - private WikiMarkdownContainer markdownContainer; + private TestMarkdownContainer markdownContainer; [Cached] private readonly OverlayColourProvider overlayColour = new OverlayColourProvider(OverlayColourScheme.Orange); @@ -32,7 +36,7 @@ namespace osu.Game.Tests.Visual.Online { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding(20), - Child = markdownContainer = new WikiMarkdownContainer() + Child = markdownContainer = new TestMarkdownContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, @@ -54,5 +58,27 @@ namespace osu.Game.Tests.Visual.Online AddStep("set 'Formatting''", () => markdownContainer.Text = "[wiki formatting guidline](Formatting)"); } + + private class TestMarkdownContainer : WikiMarkdownContainer + { + public LinkInline Link; + + public override MarkdownTextFlowContainer CreateTextFlow() => new TestMarkdownTextFlowContainer + { + UrlAdded = link => Link = link, + }; + + private class TestMarkdownTextFlowContainer : OsuMarkdownTextFlowContainer + { + public Action UrlAdded; + + protected override void AddLinkText(string text, LinkInline linkInline) + { + base.AddLinkText(text, linkInline); + + UrlAdded?.Invoke(linkInline); + } + } + } } } From 5c1c43dea395211307b848bfa3dbdd218289814b Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 25 May 2021 10:44:22 +0700 Subject: [PATCH 100/839] add link assert --- .../Visual/Online/TestSceneWikiMarkdownContainer.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs index 993525e7db..cadf876094 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers.Markdown; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics.Containers.Markdown; +using osu.Game.Online.API; using osu.Game.Overlays; using osu.Game.Overlays.Wiki.Markdown; @@ -22,6 +23,9 @@ namespace osu.Game.Tests.Visual.Online [Cached] private readonly OverlayColourProvider overlayColour = new OverlayColourProvider(OverlayColourScheme.Orange); + [Cached] + private readonly IAPIProvider api = new DummyAPIAccess(); + [SetUp] public void Setup() => Schedule(() => { @@ -51,12 +55,16 @@ namespace osu.Game.Tests.Visual.Online AddStep("set current path", () => markdownContainer.CurrentPath = "Article_styling_criteria/"); AddStep("set '/wiki/Main_Page''", () => markdownContainer.Text = "[wiki main page](/wiki/Main_Page)"); + AddAssert("check url", () => markdownContainer.Link.Url == $"{api.WebsiteRootUrl}/wiki/Main_Page"); AddStep("set '../FAQ''", () => markdownContainer.Text = "[FAQ](../FAQ)"); + AddAssert("check url", () => markdownContainer.Link.Url == $"{api.WebsiteRootUrl}/wiki/FAQ"); AddStep("set './Writing''", () => markdownContainer.Text = "[wiki writing guidline](./Writing)"); + AddAssert("check url", () => markdownContainer.Link.Url == $"{api.WebsiteRootUrl}/wiki/Article_styling_criteria/Writing"); AddStep("set 'Formatting''", () => markdownContainer.Text = "[wiki formatting guidline](Formatting)"); + AddAssert("check url", () => markdownContainer.Link.Url == $"{api.WebsiteRootUrl}/wiki/Article_styling_criteria/Formatting"); } private class TestMarkdownContainer : WikiMarkdownContainer From 292f314feeba625149aa30b639503ca794b2a67f Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 21 May 2021 16:04:08 +0700 Subject: [PATCH 101/839] initial wiki notice container --- .../Wiki/Markdown/WikiMarkdownContainer.cs | 19 +++++++++++++++++++ .../Wiki/Markdown/WikiNoticeContainer.cs | 19 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs index ac128d79dc..3dfb9828cf 100644 --- a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs +++ b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs @@ -1,6 +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 Markdig.Extensions.Yaml; +using Markdig.Syntax; +using osu.Framework.Graphics.Containers; using osu.Game.Graphics.Containers.Markdown; namespace osu.Game.Overlays.Wiki.Markdown @@ -11,5 +14,21 @@ namespace osu.Game.Overlays.Wiki.Markdown { set => Schedule(() => DocumentUrl += $"wiki/{value}"); } + + protected override void AddMarkdownComponent(IMarkdownObject markdownObject, FillFlowContainer container, int level) + { + switch (markdownObject) + { + case YamlFrontMatterBlock yamlFrontMatterBlock: + container.Add(CreateNotice(yamlFrontMatterBlock)); + break; + + default: + base.AddMarkdownComponent(markdownObject, container, level); + break; + } + } + + protected virtual FillFlowContainer CreateNotice(YamlFrontMatterBlock yamlFrontMatterBlock) => new WikiNoticeContainer(yamlFrontMatterBlock); } } diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs b/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs new file mode 100644 index 0000000000..e42834cea7 --- /dev/null +++ b/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.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 Markdig.Extensions.Yaml; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; + +namespace osu.Game.Overlays.Wiki.Markdown +{ + public class WikiNoticeContainer : FillFlowContainer + { + public WikiNoticeContainer(YamlFrontMatterBlock yamlFrontMatterBlock) + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Direction = FillDirection.Vertical; + } + } +} From 8f1b48d766ad31253a3a105c832120516d488b58 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sun, 23 May 2021 17:29:48 +0700 Subject: [PATCH 102/839] add test for notice box --- .../Online/TestSceneWikiMarkdownContainer.cs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs index cadf876094..018e1bf9f1 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs @@ -67,6 +67,40 @@ namespace osu.Game.Tests.Visual.Online AddAssert("check url", () => markdownContainer.Link.Url == $"{api.WebsiteRootUrl}/wiki/Article_styling_criteria/Formatting"); } + [Test] + public void TestOutdatedNoticeBox() + { + AddStep("Add outdated yaml header", () => + { + markdownContainer.Text = @"--- +outdated: true +---"; + }); + } + + [Test] + public void TestNeedsCleanupNoticeBox() + { + AddStep("Add needs cleanup yaml header", () => + { + markdownContainer.Text = @"--- +needs_cleanup: true +---"; + }); + } + + [Test] + public void TestOnlyShowOutdatedNoticeBox() + { + AddStep("Add outdated and needs cleanup yaml", () => + { + markdownContainer.Text = @"--- +outdated: true +needs_cleanup: true +---"; + }); + } + private class TestMarkdownContainer : WikiMarkdownContainer { public LinkInline Link; From b3eff65a0cf70742e9dca2dae768015b988b06e5 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 21 May 2021 16:05:37 +0700 Subject: [PATCH 103/839] parse isOutdated and needsCleanup --- .../Wiki/Markdown/WikiNoticeContainer.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs b/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs index e42834cea7..3070d20798 100644 --- a/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs +++ b/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs @@ -9,11 +9,28 @@ namespace osu.Game.Overlays.Wiki.Markdown { public class WikiNoticeContainer : FillFlowContainer { + private readonly bool isOutdated; + private readonly bool needsCleanup; + public WikiNoticeContainer(YamlFrontMatterBlock yamlFrontMatterBlock) { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; Direction = FillDirection.Vertical; + + foreach (var line in yamlFrontMatterBlock.Lines) + { + switch (line.ToString()) + { + case "outdated: true": + isOutdated = true; + break; + + case "needs_cleanup: true": + needsCleanup = true; + break; + } + } } } } From e23ea00197ff7eb0c6d2e719aa7f54b3c5625025 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 21 May 2021 16:06:43 +0700 Subject: [PATCH 104/839] add notice box --- .../Wiki/Markdown/WikiNoticeContainer.cs | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs b/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs index 3070d20798..f2821d14eb 100644 --- a/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs +++ b/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs @@ -2,8 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using Markdig.Extensions.Yaml; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Containers.Markdown; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; namespace osu.Game.Overlays.Wiki.Markdown { @@ -32,5 +36,41 @@ namespace osu.Game.Overlays.Wiki.Markdown } } } + + private class NoticeBox : Container + { + [Resolved] + private IMarkdownTextFlowComponent parentFlowComponent { get; set; } + + public string Text { get; set; } + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider, OsuColour colour) + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + MarkdownTextFlowContainer textFlow; + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background4, + }, + textFlow = parentFlowComponent.CreateTextFlow().With(t => + { + t.Colour = colour.Orange1; + t.Padding = new MarginPadding + { + Vertical = 10, + Horizontal = 15, + }; + }) + }; + + textFlow.AddText(Text); + } + } } } From 7a8a37f4a0e6536fdd659e447e820a37e746937a Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 21 May 2021 16:08:30 +0700 Subject: [PATCH 105/839] load notice box --- .../Wiki/Markdown/WikiNoticeContainer.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs b/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs index f2821d14eb..421806eea8 100644 --- a/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs +++ b/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs @@ -37,6 +37,27 @@ namespace osu.Game.Overlays.Wiki.Markdown } } + [BackgroundDependencyLoader] + private void load() + { + // Reference : https://github.com/ppy/osu-web/blob/master/resources/views/wiki/_notice.blade.php and https://github.com/ppy/osu-web/blob/master/resources/lang/en/wiki.php + // TODO : add notice box for fallback translation, legal translation and outdated translation after implement wiki locale in the future. + if (isOutdated) + { + Add(new NoticeBox + { + Text = "The content on this page is incomplete or outdated. If you are able to help out, please consider updating the article!", + }); + } + else if (needsCleanup) + { + Add(new NoticeBox + { + Text = "This page does not meet the standards of the osu! wiki and needs to be cleaned up or rewritten. If you are able to help out, please consider updating the article!", + }); + } + } + private class NoticeBox : Container { [Resolved] From 6585dd3a3e7eea2d7db371111b92a63434cab84a Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sun, 23 May 2021 16:30:41 +0700 Subject: [PATCH 106/839] add image test --- .../Online/TestSceneWikiMarkdownContainer.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs index 018e1bf9f1..3731e7a782 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs @@ -101,6 +101,25 @@ needs_cleanup: true }); } + [Test] + public void TestAbsoluteImage() + { + AddStep("Add absolute image", () => + { + markdownContainer.Text = "![intro](/wiki/Interface/img/intro-screen.jpg)"; + }); + } + + [Test] + public void TestRelativeImage() + { + AddStep("Add relative image", () => + { + markdownContainer.CurrentPath = "Interface/"; + markdownContainer.Text = "![intro](img/intro-screen.jpg)"; + }); + } + private class TestMarkdownContainer : WikiMarkdownContainer { public LinkInline Link; From ef707bd09951b88a39f75ce889cd0524f78c3e5a Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sun, 2 May 2021 22:02:06 +0700 Subject: [PATCH 107/839] add WikiMarkdownImage --- .../Wiki/Markdown/WikiMarkdownContainer.cs | 3 ++ .../Wiki/Markdown/WikiMarkdownImage.cs | 38 +++++++++++++++++++ .../Markdown/WikiMarkdownTextFlowContainer.cs | 13 +++++++ 3 files changed, 54 insertions(+) create mode 100644 osu.Game/Overlays/Wiki/Markdown/WikiMarkdownImage.cs create mode 100644 osu.Game/Overlays/Wiki/Markdown/WikiMarkdownTextFlowContainer.cs diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs index 3dfb9828cf..994bab7d04 100644 --- a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs +++ b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs @@ -4,6 +4,7 @@ using Markdig.Extensions.Yaml; using Markdig.Syntax; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Containers.Markdown; using osu.Game.Graphics.Containers.Markdown; namespace osu.Game.Overlays.Wiki.Markdown @@ -29,6 +30,8 @@ namespace osu.Game.Overlays.Wiki.Markdown } } + public override MarkdownTextFlowContainer CreateTextFlow() => new WikiMarkdownTextFlowContainer(); + protected virtual FillFlowContainer CreateNotice(YamlFrontMatterBlock yamlFrontMatterBlock) => new WikiNoticeContainer(yamlFrontMatterBlock); } } diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownImage.cs b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownImage.cs new file mode 100644 index 0000000000..361aa2e95f --- /dev/null +++ b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownImage.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 Markdig.Syntax.Inlines; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Containers.Markdown; +using osu.Framework.Graphics.Cursor; +using osu.Game.Online.API; + +namespace osu.Game.Overlays.Wiki.Markdown +{ + public class WikiMarkdownImage : MarkdownImage, IHasTooltip + { + private readonly string url; + + public string TooltipText { get; } + + public WikiMarkdownImage(LinkInline linkInline) + : base(linkInline.Url) + { + url = linkInline.Url; + TooltipText = linkInline.Title; + } + + [BackgroundDependencyLoader] + private void load(IAPIProvider api) + { + // The idea is replace "{api.WebsiteRootUrl}/wiki/{path-to-image}" to "{api.WebsiteRootUrl}/wiki/images/{path-to-image}" + // "/wiki/images/*" is route to fetch wiki image from osu!web server (see: https://github.com/ppy/osu-web/blob/4205eb66a4da86bdee7835045e4bf28c35456e04/routes/web.php#L289) + // Currently all image in dev server (https://dev.ppy.sh/wiki/image/*) is 404 + // So for now just replace "{api.WebsiteRootUrl}/wiki/*" to "https://osu.ppy.sh/wiki/images/*" for simplicity + var imageUrl = url.Replace($"{api.WebsiteRootUrl}/wiki", "https://osu.ppy.sh/wiki/images"); + + InternalChild = new DelayedLoadWrapper(CreateImageContainer(imageUrl)); + } + } +} diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownTextFlowContainer.cs b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownTextFlowContainer.cs new file mode 100644 index 0000000000..1c2b37a219 --- /dev/null +++ b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownTextFlowContainer.cs @@ -0,0 +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 Markdig.Syntax.Inlines; +using osu.Game.Graphics.Containers.Markdown; + +namespace osu.Game.Overlays.Wiki.Markdown +{ + public class WikiMarkdownTextFlowContainer : OsuMarkdownTextFlowContainer + { + protected override void AddImage(LinkInline linkInline) => AddDrawable(new WikiMarkdownImage(linkInline)); + } +} From b78ec8307df7880f518626bf24160ece2d6dbd12 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sun, 2 May 2021 22:22:22 +0700 Subject: [PATCH 108/839] add markdown paragraph with image block --- .../Wiki/Markdown/WikiMarkdownContainer.cs | 2 + .../Wiki/Markdown/WikiMarkdownParagraph.cs | 40 +++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 osu.Game/Overlays/Wiki/Markdown/WikiMarkdownParagraph.cs diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs index 994bab7d04..81115293d4 100644 --- a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs +++ b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs @@ -32,6 +32,8 @@ namespace osu.Game.Overlays.Wiki.Markdown public override MarkdownTextFlowContainer CreateTextFlow() => new WikiMarkdownTextFlowContainer(); + protected override MarkdownParagraph CreateParagraph(ParagraphBlock paragraphBlock, int level) => new WikiMarkdownParagraph(paragraphBlock); + protected virtual FillFlowContainer CreateNotice(YamlFrontMatterBlock yamlFrontMatterBlock) => new WikiNoticeContainer(yamlFrontMatterBlock); } } diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownParagraph.cs b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownParagraph.cs new file mode 100644 index 0000000000..4a7ce24aba --- /dev/null +++ b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownParagraph.cs @@ -0,0 +1,40 @@ +// 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 Markdig.Syntax; +using Markdig.Syntax.Inlines; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers.Markdown; +using osuTK; + +namespace osu.Game.Overlays.Wiki.Markdown +{ + public class WikiMarkdownParagraph : MarkdownParagraph + { + private readonly ParagraphBlock paragraphBlock; + + public WikiMarkdownParagraph(ParagraphBlock paragraphBlock) + : base(paragraphBlock) + { + this.paragraphBlock = paragraphBlock; + } + + [BackgroundDependencyLoader] + private void load() + { + MarkdownTextFlowContainer textFlow; + InternalChild = textFlow = CreateTextFlow(); + textFlow.AddInlineText(paragraphBlock.Inline); + + // Check if paragraph only contains an image. + if (paragraphBlock.Inline.Count() == 1 && paragraphBlock.Inline.FirstChild is LinkInline { IsImage: true } linkInline) + { + textFlow.TextAnchor = Anchor.TopCentre; + textFlow.Spacing = new Vector2(0, 5); + textFlow.AddText($"\n{linkInline.Title}"); + } + } + } +} From b9220d4dc728f681a16d6d4e664d4ae26a133951 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 3 May 2021 15:57:57 +0200 Subject: [PATCH 109/839] Fix possible multiple enumeration --- .../UserInterface/TestSceneUpdateableBeatmapSetCover.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapSetCover.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapSetCover.cs index ecb076d356..c928c0103f 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapSetCover.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapSetCover.cs @@ -57,11 +57,13 @@ namespace osu.Game.Tests.Visual.UserInterface } }; - var coverTypes = Enum.GetValues(typeof(BeatmapSetCoverType)).Cast(); + var coverTypes = Enum.GetValues(typeof(BeatmapSetCoverType)) + .Cast() + .ToList(); for (int i = 0; i < 25; i++) { - var coverType = coverTypes.ElementAt(i % coverTypes.Count()); + var coverType = coverTypes[i % coverTypes.Count]; var cover = new UpdateableBeatmapSetCover(coverType) { From 36438175a0f61efc7c990ad8cf0f127cd09078bc Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 4 May 2021 16:04:58 +0900 Subject: [PATCH 110/839] Throw an exception if try to modify lifetime of PoolableDrawableWithLifetime without lifetime --- .../Pooling/PoolableDrawableWithLifetime.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs b/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs index e94b6dca9d..d8565f4b30 100644 --- a/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs +++ b/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs @@ -3,6 +3,7 @@ #nullable enable +using System; using System.Diagnostics; using osu.Framework.Graphics.Performance; using osu.Framework.Graphics.Pooling; @@ -31,7 +32,12 @@ namespace osu.Game.Rulesets.Objects.Pooling get => Entry?.LifetimeStart ?? double.MinValue; set { - if (Entry != null) Entry.LifetimeStart = value; + if (LifetimeStart == value) return; + + if (Entry == null) + throw new InvalidOperationException($"Cannot modify lifetime of {nameof(PoolableDrawableWithLifetime)} when entry is not set"); + + Entry.LifetimeStart = value; } } @@ -40,7 +46,12 @@ namespace osu.Game.Rulesets.Objects.Pooling get => Entry?.LifetimeEnd ?? double.MaxValue; set { - if (Entry != null) Entry.LifetimeEnd = value; + if (LifetimeEnd == value) return; + + if (Entry == null) + throw new InvalidOperationException($"Cannot modify lifetime of {nameof(PoolableDrawableWithLifetime)} when entry is not set"); + + Entry.LifetimeEnd = value; } } From 913fc8c3bc9eb4c37a0ff66bdccb5e90754dcf8a Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 4 May 2021 16:44:48 +0900 Subject: [PATCH 111/839] Revert the change of not adding non-pooled DHO to HOC until alive --- osu.Game/Rulesets/UI/HitObjectContainer.cs | 78 +++++++++++++--------- 1 file changed, 48 insertions(+), 30 deletions(-) diff --git a/osu.Game/Rulesets/UI/HitObjectContainer.cs b/osu.Game/Rulesets/UI/HitObjectContainer.cs index 1d32313f2b..dcf350cbd4 100644 --- a/osu.Game/Rulesets/UI/HitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/HitObjectContainer.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.UI /// /// All alive entries and s used by the entries. /// - public IEnumerable<(HitObjectLifetimeEntry Entry, DrawableHitObject Drawable)> AliveEntries => drawableMap.Select(x => (x.Key, x.Value)); + public IEnumerable<(HitObjectLifetimeEntry Entry, DrawableHitObject Drawable)> AliveEntries => aliveDrawableMap.Select(x => (x.Key, x.Value)); public IEnumerable Objects => InternalChildren.Cast().OrderBy(h => h.HitObject.StartTime); @@ -71,7 +71,7 @@ namespace osu.Game.Rulesets.UI private readonly Dictionary startTimeMap = new Dictionary(); - private readonly Dictionary drawableMap = new Dictionary(); + private readonly Dictionary aliveDrawableMap = new Dictionary(); private readonly Dictionary nonPooledDrawableMap = new Dictionary(); private readonly LifetimeEntryManager lifetimeManager = new LifetimeEntryManager(); @@ -108,55 +108,70 @@ namespace osu.Game.Rulesets.UI public bool Remove(HitObjectLifetimeEntry entry) { if (!lifetimeManager.RemoveEntry(entry)) return false; - // The entry has to be removed from the non-pooled map here because non-pooled entry may be removed by specifying its entry. - nonPooledDrawableMap.Remove(entry); + + // This logic is not in `Remove(DrawableHitObject)` because a non-pooled drawable may be removed by specifying its entry. + if (nonPooledDrawableMap.Remove(entry, out var drawable)) + removeDrawable(drawable); + allEntries.Remove(entry); return true; } - private void entryBecameAlive(LifetimeEntry entry) => addDrawable((HitObjectLifetimeEntry)entry); - - private void entryBecameDead(LifetimeEntry entry) => removeDrawable((HitObjectLifetimeEntry)entry); - - private void addDrawable(HitObjectLifetimeEntry entry) + private void entryBecameAlive(LifetimeEntry lifetimeEntry) { - Debug.Assert(!drawableMap.ContainsKey(entry)); + var entry = (HitObjectLifetimeEntry)lifetimeEntry; + Debug.Assert(!aliveDrawableMap.ContainsKey(entry)); - nonPooledDrawableMap.TryGetValue(entry, out var drawable); + bool isNonPooled = nonPooledDrawableMap.TryGetValue(entry, out var drawable); drawable ??= pooledObjectProvider?.GetPooledDrawableRepresentation(entry.HitObject, null); if (drawable == null) throw new InvalidOperationException($"A drawable representation could not be retrieved for hitobject type: {entry.HitObject.GetType().ReadableName()}."); + aliveDrawableMap[entry] = drawable; + OnAdd(drawable); + + if (isNonPooled) return; + + addDrawable(drawable); + HitObjectUsageBegan?.Invoke(entry.HitObject); + } + + private void entryBecameDead(LifetimeEntry lifetimeEntry) + { + var entry = (HitObjectLifetimeEntry)lifetimeEntry; + Debug.Assert(aliveDrawableMap.ContainsKey(entry)); + + var drawable = aliveDrawableMap[entry]; + bool isNonPooled = nonPooledDrawableMap.ContainsKey(entry); + + drawable.OnKilled(); + aliveDrawableMap.Remove(entry); + OnRemove(drawable); + + if (isNonPooled) return; + + removeDrawable(drawable); + // The hit object is not freed when the DHO was not pooled. + HitObjectUsageFinished?.Invoke(entry.HitObject); + } + + private void addDrawable(DrawableHitObject drawable) + { drawable.OnNewResult += onNewResult; drawable.OnRevertResult += onRevertResult; bindStartTime(drawable); - AddInternal(drawableMap[entry] = drawable); - OnAdd(drawable); - - HitObjectUsageBegan?.Invoke(entry.HitObject); + AddInternal(drawable); } - private void removeDrawable(HitObjectLifetimeEntry entry) + private void removeDrawable(DrawableHitObject drawable) { - Debug.Assert(drawableMap.ContainsKey(entry)); - - var drawable = drawableMap[entry]; - - // OnKilled can potentially change the hitobject's result, so it needs to run first before unbinding. - drawable.OnKilled(); drawable.OnNewResult -= onNewResult; drawable.OnRevertResult -= onRevertResult; - drawableMap.Remove(entry); - - OnRemove(drawable); unbindStartTime(drawable); - RemoveInternal(drawable); - // The hit object is not freed when the DHO was not pooled. - if (!nonPooledDrawableMap.ContainsKey(entry)) - HitObjectUsageFinished?.Invoke(entry.HitObject); + RemoveInternal(drawable); } #endregion @@ -169,6 +184,7 @@ namespace osu.Game.Rulesets.UI throw new InvalidOperationException($"May not add a {nameof(DrawableHitObject)} without {nameof(HitObject)} associated"); nonPooledDrawableMap.Add(drawable.Entry, drawable); + addDrawable(drawable); Add(drawable.Entry); } @@ -217,8 +233,10 @@ namespace osu.Game.Rulesets.UI public virtual void Clear() { lifetimeManager.ClearEntries(); + foreach (var drawable in nonPooledDrawableMap.Values) + removeDrawable(drawable); nonPooledDrawableMap.Clear(); - Debug.Assert(InternalChildren.Count == 0 && startTimeMap.Count == 0 && drawableMap.Count == 0, "All hit objects should have been removed"); + Debug.Assert(InternalChildren.Count == 0 && startTimeMap.Count == 0 && aliveDrawableMap.Count == 0, "All hit objects should have been removed"); } protected override bool CheckChildrenLife() From 39bccc50489cf502c367891dc0a4071cec4c1dfc Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 4 May 2021 16:45:24 +0900 Subject: [PATCH 112/839] Revert "Adopt HitObjectContainer change in a test" This reverts commit f55aa016 --- osu.Game.Tests/Gameplay/TestSceneHitObjectContainer.cs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/osu.Game.Tests/Gameplay/TestSceneHitObjectContainer.cs b/osu.Game.Tests/Gameplay/TestSceneHitObjectContainer.cs index 8f3d3f1276..f2bfccb6de 100644 --- a/osu.Game.Tests/Gameplay/TestSceneHitObjectContainer.cs +++ b/osu.Game.Tests/Gameplay/TestSceneHitObjectContainer.cs @@ -4,7 +4,6 @@ using JetBrains.Annotations; using NUnit.Framework; using osu.Framework.Testing; -using osu.Framework.Timing; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI; @@ -20,14 +19,7 @@ namespace osu.Game.Tests.Gameplay [SetUp] public void Setup() => Schedule(() => { - Child = container = new HitObjectContainer - { - Clock = new FramedClock(new ManualClock - { - // Make sure hit objects with `StartTime == 0` are alive - CurrentTime = -1 - }) - }; + Child = container = new HitObjectContainer(); }); [Test] From 787bfd6bd08a4afaea8e937ab327445199572948 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 4 May 2021 16:45:39 +0900 Subject: [PATCH 113/839] Revert "Fix failing taiko tests" This reverts commit 971ca398 --- .../TestSceneFlyingHits.cs | 16 ++++++++-------- osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs | 8 ++------ 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneFlyingHits.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneFlyingHits.cs index 5738be05d7..63854e7ead 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneFlyingHits.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneFlyingHits.cs @@ -20,19 +20,20 @@ namespace osu.Game.Rulesets.Taiko.Tests [TestCase(HitType.Rim)] public void TestFlyingHits(HitType hitType) { + DrawableFlyingHit flyingHit = null; + AddStep("add flying hit", () => { addFlyingHit(hitType); - }); - AddAssert("hit type is correct", () => - { // flying hits all land in one common scrolling container (and stay there for rewind purposes), // so we need to manually get the latest one. - return this.ChildrenOfType() - .OrderByDescending(h => h.HitObject.StartTime) - .FirstOrDefault()?.HitObject.Type == hitType; + flyingHit = this.ChildrenOfType() + .OrderByDescending(h => h.HitObject.StartTime) + .FirstOrDefault(); }); + + AddAssert("hit type is correct", () => flyingHit.HitObject.Type == hitType); } private void addFlyingHit(HitType hitType) @@ -41,8 +42,7 @@ namespace osu.Game.Rulesets.Taiko.Tests DrawableDrumRollTick h; DrawableRuleset.Playfield.Add(h = new DrawableDrumRollTick(tick) { JudgementType = hitType }); - h.OnLoadComplete += _ => - ((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h, new JudgementResult(tick, new TaikoDrumRollTickJudgement()) { Type = HitResult.Great }); + ((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h, new JudgementResult(tick, new TaikoDrumRollTickJudgement()) { Type = HitResult.Great }); } } } diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs index 06acdad3d6..87c936d386 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs @@ -129,12 +129,8 @@ namespace osu.Game.Rulesets.Taiko.Tests DrawableRuleset.Playfield.Add(h); - h.OnLoadComplete += _ => - { - ((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h, new JudgementResult(hit, new TaikoJudgement()) { Type = hitResult }); - ((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h.NestedHitObjects.Single(), - new JudgementResult(hit.NestedHitObjects.Single(), new TaikoStrongJudgement()) { Type = HitResult.Great }); - }; + ((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h, new JudgementResult(hit, new TaikoJudgement()) { Type = hitResult }); + ((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h.NestedHitObjects.Single(), new JudgementResult(hit.NestedHitObjects.Single(), new TaikoStrongJudgement()) { Type = HitResult.Great }); } private void addMissJudgement() From 4a93e27e8394ed5d347ca06989951319afb6e6c4 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 4 May 2021 16:46:30 +0900 Subject: [PATCH 114/839] Revert "Fix mania editor null reference" This reverts commit 1d023dce --- .../Edit/Blueprints/ManiaSelectionBlueprint.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs index 8f8f45c0dd..384f49d9b2 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs @@ -18,9 +18,6 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints [Resolved] private IScrollingInfo scrollingInfo { get; set; } - // Overriding the base because this method is called right after `Column` is changed and `DrawableObject` is not yet loaded and Parent is not set. - public override Vector2 GetInstantDelta(Vector2 screenSpacePosition) => Parent.ToLocalSpace(screenSpacePosition) - Position; - protected ManiaSelectionBlueprint(DrawableHitObject drawableObject) : base(drawableObject) { From aa42cf2fc055eeee7a01188fbf61ef3735651dd3 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 4 May 2021 16:56:48 +0900 Subject: [PATCH 115/839] Fix setting lifetime during KeepAlive is ignored --- .../Pooling/PoolableDrawableWithLifetime.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs b/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs index d8565f4b30..ed0430012a 100644 --- a/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs +++ b/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs @@ -32,12 +32,11 @@ namespace osu.Game.Rulesets.Objects.Pooling get => Entry?.LifetimeStart ?? double.MinValue; set { - if (LifetimeStart == value) return; - - if (Entry == null) + if (Entry == null && LifetimeStart != value) throw new InvalidOperationException($"Cannot modify lifetime of {nameof(PoolableDrawableWithLifetime)} when entry is not set"); - Entry.LifetimeStart = value; + if (Entry != null) + Entry.LifetimeStart = value; } } @@ -46,12 +45,11 @@ namespace osu.Game.Rulesets.Objects.Pooling get => Entry?.LifetimeEnd ?? double.MaxValue; set { - if (LifetimeEnd == value) return; - - if (Entry == null) + if (Entry == null && LifetimeEnd != value) throw new InvalidOperationException($"Cannot modify lifetime of {nameof(PoolableDrawableWithLifetime)} when entry is not set"); - Entry.LifetimeEnd = value; + if (Entry != null) + Entry.LifetimeEnd = value; } } From 040d393dd462931068601879ba7fc0b09f597136 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 4 May 2021 21:00:12 +0200 Subject: [PATCH 116/839] Add visual test case for crossfade behaviour --- .../TestSceneUpdateableBeatmapSetCover.cs | 47 +++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapSetCover.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapSetCover.cs index c928c0103f..4fef93e291 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapSetCover.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapSetCover.cs @@ -108,14 +108,52 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("no cover added", () => !updateableCover.ChildrenOfType().Any()); } + [Test] + public void TestCoverChangeOnNewBeatmap() + { + TestUpdateableBeatmapSetCover updateableCover = null; + BeatmapSetCover initialCover = null; + + AddStep("setup cover", () => Child = updateableCover = new TestUpdateableBeatmapSetCover(0) + { + BeatmapSet = createBeatmapWithCover("https://assets.ppy.sh/beatmaps/1189904/covers/cover.jpg"), + RelativeSizeAxes = Axes.Both, + Masking = true, + Alpha = 0.4f + }); + + AddUntilStep("cover loaded", () => updateableCover.ChildrenOfType().Any()); + AddStep("store initial cover", () => initialCover = updateableCover.ChildrenOfType().Single()); + AddUntilStep("wait for fade complete", () => initialCover.Alpha == 1); + + AddStep("switch beatmap", + () => updateableCover.BeatmapSet = createBeatmapWithCover("https://assets.ppy.sh/beatmaps/1079428/covers/cover.jpg")); + AddUntilStep("new cover loaded", () => updateableCover.ChildrenOfType().Except(new[] { initialCover }).Any()); + } + + private static BeatmapSetInfo createBeatmapWithCover(string coverUrl) => new BeatmapSetInfo + { + OnlineInfo = new BeatmapSetOnlineInfo + { + Covers = new BeatmapSetOnlineCovers { Cover = coverUrl } + } + }; + private class TestUpdateableBeatmapSetCover : UpdateableBeatmapSetCover { + private readonly int loadDelay; + + public TestUpdateableBeatmapSetCover(int loadDelay = 10000) + { + this.loadDelay = loadDelay; + } + protected override Drawable CreateDrawable(BeatmapSetInfo model) { if (model == null) return null; - return new TestBeatmapSetCover(model) + return new TestBeatmapSetCover(model, loadDelay) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -127,15 +165,18 @@ namespace osu.Game.Tests.Visual.UserInterface private class TestBeatmapSetCover : BeatmapSetCover { - public TestBeatmapSetCover(BeatmapSetInfo set) + private readonly int loadDelay; + + public TestBeatmapSetCover(BeatmapSetInfo set, int loadDelay) : base(set) { + this.loadDelay = loadDelay; } [BackgroundDependencyLoader] private void load() { - Thread.Sleep(10000); + Thread.Sleep(loadDelay); } } } From 32b3ea70b966e4087d70d72f1306f55b4fa8b264 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 4 May 2021 21:12:50 +0200 Subject: [PATCH 117/839] Fix both covers showing if cover is not fully opaque --- osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs index 3a4423e42e..b376690436 100644 --- a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs +++ b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs @@ -41,6 +41,10 @@ namespace osu.Game.Beatmaps.Drawables protected override DelayedLoadWrapper CreateDelayedLoadWrapper(Func createContentFunc, double timeBeforeLoad) => new DelayedLoadUnloadWrapper(createContentFunc, timeBeforeLoad); + // by default, ModelBackedDrawable hides the old drawable only after the new one has been fully loaded. + // this can lead to weird appearance if the cover is not fully opaque, so fade out as soon as a new load is requested in this particular case. + protected override void OnLoadStarted() => ApplyHideTransforms(DisplayedDrawable); + protected override Drawable CreateDrawable(BeatmapSetInfo model) { if (model == null) From 283488ea53f3c240ca1c856bb294ab8247a476d1 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 7 May 2021 03:36:30 +0300 Subject: [PATCH 118/839] Use `TransformImmediately` instead at beatmap listing search control Applies two changes: - Use `TransformImmediately` which achieves the same wanted transition behaviour without any issues. - Move the transition behaviour override into `BeatmapListingSearchControl` in a nested subclass of `UpdateableBeatmapSetCover`. --- osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs | 4 ---- .../Overlays/BeatmapListing/BeatmapListingSearchControl.cs | 7 ++++++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs index b376690436..3a4423e42e 100644 --- a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs +++ b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs @@ -41,10 +41,6 @@ namespace osu.Game.Beatmaps.Drawables protected override DelayedLoadWrapper CreateDelayedLoadWrapper(Func createContentFunc, double timeBeforeLoad) => new DelayedLoadUnloadWrapper(createContentFunc, timeBeforeLoad); - // by default, ModelBackedDrawable hides the old drawable only after the new one has been fully loaded. - // this can lead to weird appearance if the cover is not fully opaque, so fade out as soon as a new load is requested in this particular case. - protected override void OnLoadStarted() => ApplyHideTransforms(DisplayedDrawable); - protected override Drawable CreateDrawable(BeatmapSetInfo model) { if (model == null) diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs index 1576431d40..97ccb66599 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs @@ -90,7 +90,7 @@ namespace osu.Game.Overlays.BeatmapListing { RelativeSizeAxes = Axes.Both, Masking = true, - Child = beatmapCover = new UpdateableBeatmapSetCover + Child = beatmapCover = new TopSearchBeatmapSetCover { RelativeSizeAxes = Axes.Both, Alpha = 0, @@ -184,5 +184,10 @@ namespace osu.Game.Overlays.BeatmapListing return true; } } + + private class TopSearchBeatmapSetCover : UpdateableBeatmapSetCover + { + protected override bool TransformImmediately => true; + } } } From c7325f0f775357a51f8d1c9963c825f8fc32cd2a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 7 May 2021 08:07:12 +0300 Subject: [PATCH 119/839] Add missing load delay That was a bad one... `ModelBackedDrawable` has a default of 0ms load delay, while previously the wrapper created for beatmap set cover used default 500ms, this change is bringing the load delay back, to avoid firing hundreds of web requests just when doing a quick long scroll on beatmap listing. --- osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs index 3a4423e42e..7248c9213c 100644 --- a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs +++ b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs @@ -36,7 +36,9 @@ namespace osu.Game.Beatmaps.Drawables }; } - protected override double TransformDuration => 400.0; + protected override double LoadDelay => 500; + + protected override double TransformDuration => 400; protected override DelayedLoadWrapper CreateDelayedLoadWrapper(Func createContentFunc, double timeBeforeLoad) => new DelayedLoadUnloadWrapper(createContentFunc, timeBeforeLoad); From 075350e12572188f25d5a8b02fedf1a9edf73f83 Mon Sep 17 00:00:00 2001 From: Ibby Date: Sun, 9 May 2021 15:51:17 +1000 Subject: [PATCH 120/839] Adding a reset button to individual keybinds --- .../Settings/TestSceneKeyBindingPanel.cs | 40 +++++++++++++++++++ osu.Game/Overlays/KeyBinding/KeyBindingRow.cs | 28 +++++++++++++ 2 files changed, 68 insertions(+) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs index f495e0fb23..75bbd8f86c 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs @@ -104,6 +104,46 @@ namespace osu.Game.Tests.Visual.Settings } } + [Test] + public void TestSingleBindResetButton() + { + KeyBindingRow multiBindingRow = null; + + AddStep("click first row with two bindings", () => + { + multiBindingRow = panel.ChildrenOfType().First(row => row.Defaults.Count() > 1); + InputManager.MoveMouseTo(multiBindingRow); + InputManager.Click(MouseButton.Left); + }); + + clickSingleBindResetButton(); + + AddAssert("first binding cleared", () => multiBindingRow.ChildrenOfType().ElementAt(0).KeyBinding.KeyCombination.Equals(multiBindingRow.Defaults.ElementAt(0))); + + AddStep("click second binding", () => + { + var target = multiBindingRow.ChildrenOfType().ElementAt(1); + + InputManager.MoveMouseTo(target); + InputManager.Click(MouseButton.Left); + }); + + clickSingleBindResetButton(); + + AddAssert("second binding cleared", () => multiBindingRow.ChildrenOfType().ElementAt(1).KeyBinding.KeyCombination.Equals(multiBindingRow.Defaults.ElementAt(1))); + + void clickSingleBindResetButton() + { + AddStep("click reset button for single binding", () => + { + var clearButton = multiBindingRow.ChildrenOfType().Single(); + + InputManager.MoveMouseTo(clearButton); + InputManager.Click(MouseButton.Left); + }); + } + } + [Test] public void TestClickRowSelectsFirstBinding() { diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs index 300fce962a..1fbaa374d4 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs @@ -109,6 +109,7 @@ namespace osu.Game.Overlays.KeyBinding Children = new Drawable[] { new CancelButton { Action = finalise }, + new SingleBindResetButton { Action = singleBindReset }, new ClearButton { Action = clear }, }, } @@ -281,6 +282,15 @@ namespace osu.Game.Overlays.KeyBinding finalise(); } + private void singleBindReset() + { + if (bindTarget == null) + return; + + bindTarget.UpdateKeyCombination(Defaults.ElementAt(buttons.IndexOf(bindTarget))); + finalise(); + } + private void finalise() { if (bindTarget != null) @@ -339,6 +349,24 @@ namespace osu.Game.Overlays.KeyBinding } } + public class SingleBindResetButton : TriangleButton + { + public SingleBindResetButton() + { + Text = "Reset"; + Size = new Vector2(80, 20); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + BackgroundColour = colours.Green; + + Triangles.ColourDark = colours.GreenDark; + Triangles.ColourLight = colours.GreenLight; + } + } + public class ClearButton : TriangleButton { public ClearButton() From 1a465c60ca54ea5f386bab902b62df12f512ac34 Mon Sep 17 00:00:00 2001 From: Swords Date: Sun, 9 May 2021 16:07:18 +1000 Subject: [PATCH 121/839] Rename Tests --- osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs index 75bbd8f86c..41b65e84b6 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs @@ -105,7 +105,7 @@ namespace osu.Game.Tests.Visual.Settings } [Test] - public void TestSingleBindResetButton() + public void TestResetButtonOnBindings() { KeyBindingRow multiBindingRow = null; From 7ca3e13712b974ce4cee0b431602ddf2fa654eed Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 10 May 2021 07:43:01 +0300 Subject: [PATCH 122/839] Implement basic years panel --- .../Visual/Online/TestSceneNewsYearsPanel.cs | 34 ++++++ osu.Game/Overlays/News/Sidebar/YearsPanel.cs | 108 ++++++++++++++++++ 2 files changed, 142 insertions(+) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneNewsYearsPanel.cs create mode 100644 osu.Game/Overlays/News/Sidebar/YearsPanel.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsYearsPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsYearsPanel.cs new file mode 100644 index 0000000000..75975f04f8 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneNewsYearsPanel.cs @@ -0,0 +1,34 @@ +// 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.Game.Overlays.News.Sidebar; +using osu.Framework.Allocation; +using osu.Game.Overlays; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneNewsYearsPanel : OsuTestScene + { + [Cached] + private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); + + private readonly YearsPanel panel; + + public TestSceneNewsYearsPanel() + { + Add(panel = new YearsPanel() + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre + }); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + AddStep("Load years", () => panel.Years = new[] { 1000, 2000, 3000, 4000 }); + AddStep("Load different years", () => panel.Years = new[] { 1001, 2001, 3001, 4001, 5001, 6001, 7001, 8001 }); + } + } +} diff --git a/osu.Game/Overlays/News/Sidebar/YearsPanel.cs b/osu.Game/Overlays/News/Sidebar/YearsPanel.cs new file mode 100644 index 0000000000..d71c7ba48e --- /dev/null +++ b/osu.Game/Overlays/News/Sidebar/YearsPanel.cs @@ -0,0 +1,108 @@ +// 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.Containers; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osuTK; +using System.Linq; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using System.Collections.Generic; +using osu.Game.Graphics; +using osu.Framework.Bindables; +using System.Collections.Specialized; + +namespace osu.Game.Overlays.News.Sidebar +{ + public class YearsPanel : CompositeDrawable + { + public int[] Years + { + set + { + years.Clear(); + years.AddRange(value); + } + } + + private readonly BindableList years = new BindableList(); + + private FillFlowContainer flow; + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + Width = 160; + AutoSizeAxes = Axes.Y; + Masking = true; + CornerRadius = 6; + InternalChildren = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background3 + }, + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding(5), + Child = flow = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(5) + } + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + years.BindCollectionChanged((u, v) => + { + switch (v.Action) + { + case NotifyCollectionChangedAction.Add: + flow.Children = years.Select(y => new YearButton(y)).ToArray(); + break; + } + }, true); + } + + private class YearButton : OsuHoverContainer + { + protected override IEnumerable EffectTargets => new[] { text }; + + private readonly int year; + private readonly OsuSpriteText text; + + public YearButton(int year) + { + this.year = year; + + Size = new Vector2(33.75f, 15); + Child = text = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = OsuFont.GetFont(size: 12), + Text = year.ToString() + }; + } + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + IdleColour = colourProvider.Light2; + HoverColour = colourProvider.Light1; + Action = () => { }; // TODO + } + } + } +} From 7971a2ef485bf8e80104a2032c378da069acdb7f Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 10 May 2021 08:47:00 +0300 Subject: [PATCH 123/839] Implement MonthPanel component --- .../Visual/Online/TestSceneNewsMonthPanel.cs | 80 +++++++++ osu.Game/Overlays/News/Sidebar/MonthPanel.cs | 155 ++++++++++++++++++ 2 files changed, 235 insertions(+) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneNewsMonthPanel.cs create mode 100644 osu.Game/Overlays/News/Sidebar/MonthPanel.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsMonthPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsMonthPanel.cs new file mode 100644 index 0000000000..75e02b66e1 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneNewsMonthPanel.cs @@ -0,0 +1,80 @@ +// 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 NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Overlays; +using osu.Game.Overlays.News.Sidebar; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneNewsMonthPanel : OsuTestScene + { + [Cached] + private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); + + [Test] + public void CreateClosedMonthPanel() + { + AddStep("Create", () => Child = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.TopCentre, + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background2, + }, + new MonthPanel(DateTime.Now, posts), + } + }); + } + + [Test] + public void CreateOpenMonthPanel() + { + AddStep("Create", () => Child = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.TopCentre, + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background2, + }, + new MonthPanel(DateTime.Now, posts) + { + IsOpen = { Value = true } + }, + } + }); + } + + private static APINewsPost[] posts => new[] + { + new APINewsPost + { + Title = "Short title" + }, + new APINewsPost + { + Title = "Oh boy that's a long post title I wonder if it will break anything" + }, + new APINewsPost + { + Title = "Medium title, nothing to see here" + } + }; + } +} diff --git a/osu.Game/Overlays/News/Sidebar/MonthPanel.cs b/osu.Game/Overlays/News/Sidebar/MonthPanel.cs new file mode 100644 index 0000000000..1dd1c561ab --- /dev/null +++ b/osu.Game/Overlays/News/Sidebar/MonthPanel.cs @@ -0,0 +1,155 @@ +// 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.Containers; +using osu.Framework.Graphics; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Graphics.Containers; +using osuTK; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics; +using System.Linq; +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Sprites; + +namespace osu.Game.Overlays.News.Sidebar +{ + public class MonthPanel : CompositeDrawable + { + public readonly BindableBool IsOpen = new BindableBool(); + + private readonly FillFlowContainer postsFlow; + + public MonthPanel(DateTime date, APINewsPost[] posts) + { + Width = 160; + AutoSizeDuration = 250; + AutoSizeEasing = Easing.OutQuint; + InternalChild = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 5), + Children = new Drawable[] + { + new DropdownButton(date) + { + IsOpen = { BindTarget = IsOpen } + }, + postsFlow = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 5), + Children = posts.Select(p => new PostButton(p)).ToArray() + } + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + IsOpen.BindValueChanged(open => + { + ClearTransforms(); + + if (open.NewValue) + { + AutoSizeAxes = Axes.Y; + postsFlow.FadeIn(250, Easing.OutQuint); + } + else + { + AutoSizeAxes = Axes.None; + this.ResizeHeightTo(15, 250, Easing.OutQuint); + + postsFlow.FadeOut(250, Easing.OutQuint); + } + }, true); + + // First state change should be instant. + FinishTransforms(); + postsFlow.FinishTransforms(); + } + + private class DropdownButton : OsuHoverContainer + { + public readonly BindableBool IsOpen = new BindableBool(); + + protected override IEnumerable EffectTargets => null; + + private readonly SpriteIcon icon; + + public DropdownButton(DateTime date) + { + Size = new Vector2(160, 15); + Action = IsOpen.Toggle; + Children = new Drawable[] + { + new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold), + Text = date.ToString("MMM yyyy") + }, + icon = new SpriteIcon + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Size = new Vector2(10), + Icon = FontAwesome.Solid.ChevronDown + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + IsOpen.BindValueChanged(open => + { + icon.Scale = new Vector2(1, open.NewValue ? -1 : 1); + }, true); + } + } + + private class PostButton : OsuHoverContainer + { + protected override IEnumerable EffectTargets => new[] { text }; + + private readonly APINewsPost post; + private readonly TextFlowContainer text; + + public PostButton(APINewsPost post) + { + this.post = post; + + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + Child = text = new TextFlowContainer(t => t.Font = OsuFont.GetFont(size: 12)) + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Text = post.Title + }; + } + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + IdleColour = colourProvider.Light2; + HoverColour = colourProvider.Light1; + Action = () => { }; // TODO + } + } + } +} From 4b972249320ad7832619a999679e1a6558a9e7bf Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 10 May 2021 09:53:52 +0300 Subject: [PATCH 124/839] Implement NewsSideBar component --- .../Visual/Online/TestSceneNewsSideBar.cs | 114 ++++++++++++++++++ .../Online/API/Requests/GetNewsResponse.cs | 3 + .../API/Requests/Responses/APINewsSidebar.cs | 20 +++ osu.Game/Overlays/News/Sidebar/NewsSideBar.cs | 114 ++++++++++++++++++ 4 files changed, 251 insertions(+) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneNewsSideBar.cs create mode 100644 osu.Game/Online/API/Requests/Responses/APINewsSidebar.cs create mode 100644 osu.Game/Overlays/News/Sidebar/NewsSideBar.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsSideBar.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsSideBar.cs new file mode 100644 index 0000000000..931341f837 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneNewsSideBar.cs @@ -0,0 +1,114 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Overlays; +using osu.Game.Overlays.News.Sidebar; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneNewsSideBar : OsuTestScene + { + [Cached] + private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); + + private NewsSideBar sidebar; + + [Test] + public void TestCreateEmpty() + { + createSidebar(null); + } + + [Test] + public void TestCreateWithData() + { + createSidebar(metadata); + } + + [Test] + public void TestDataChange() + { + createSidebar(null); + AddStep("Add data", () => + { + if (sidebar != null) + sidebar.Metadata.Value = metadata; + }); + } + + private void createSidebar(APINewsSidebar metadata) => AddStep("Create", () => Child = sidebar = new NewsSideBar + { + Metadata = { Value = metadata } + }); + + private static APINewsSidebar metadata = new APINewsSidebar + { + CurrentYear = 2021, + Years = new[] + { + 2021, + 2020, + 2019, + 2018, + 2017, + 2016, + 2015, + 2014, + 2013 + }, + NewsPosts = new List + { + new APINewsPost + { + Title = "(Mar) Short title", + PublishedAt = new DateTime(2021, 3, 1) + }, + new APINewsPost + { + Title = "(Mar) Oh boy that's a long post title I wonder if it will break anything", + PublishedAt = new DateTime(2021, 3, 1) + }, + new APINewsPost + { + Title = "(Mar) Medium title, nothing to see here", + PublishedAt = new DateTime(2021, 3, 1) + }, + new APINewsPost + { + Title = "(Feb) Short title", + PublishedAt = new DateTime(2021, 2, 1) + }, + new APINewsPost + { + Title = "(Feb) Oh boy that's a long post title I wonder if it will break anything", + PublishedAt = new DateTime(2021, 2, 1) + }, + new APINewsPost + { + Title = "(Feb) Medium title, nothing to see here", + PublishedAt = new DateTime(2021, 2, 1) + }, + new APINewsPost + { + Title = "Short title", + PublishedAt = new DateTime(2021, 1, 1) + }, + new APINewsPost + { + Title = "Oh boy that's a long post title I wonder if it will break anything", + PublishedAt = new DateTime(2021, 1, 1) + }, + new APINewsPost + { + Title = "Medium title, nothing to see here", + PublishedAt = new DateTime(2021, 1, 1) + } + } + }; + } +} diff --git a/osu.Game/Online/API/Requests/GetNewsResponse.cs b/osu.Game/Online/API/Requests/GetNewsResponse.cs index 835289a51d..98f76d105c 100644 --- a/osu.Game/Online/API/Requests/GetNewsResponse.cs +++ b/osu.Game/Online/API/Requests/GetNewsResponse.cs @@ -11,5 +11,8 @@ namespace osu.Game.Online.API.Requests { [JsonProperty("news_posts")] public IEnumerable NewsPosts; + + [JsonProperty("news_sidebar")] + public APINewsSidebar SidebarMetadata; } } diff --git a/osu.Game/Online/API/Requests/Responses/APINewsSidebar.cs b/osu.Game/Online/API/Requests/Responses/APINewsSidebar.cs new file mode 100644 index 0000000000..b8d6469a1d --- /dev/null +++ b/osu.Game/Online/API/Requests/Responses/APINewsSidebar.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 Newtonsoft.Json; +using System.Collections.Generic; + +namespace osu.Game.Online.API.Requests.Responses +{ + public class APINewsSidebar + { + [JsonProperty("current_year")] + public int CurrentYear { get; set; } + + [JsonProperty("news_posts")] + public IEnumerable NewsPosts { get; set; } + + [JsonProperty("years")] + public int[] Years { get; set; } + } +} diff --git a/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs b/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs new file mode 100644 index 0000000000..34c995cab6 --- /dev/null +++ b/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs @@ -0,0 +1,114 @@ +// 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.Framework.Graphics; +using osu.Game.Online.API.Requests.Responses; +using osu.Framework.Graphics.Shapes; +using osuTK; +using System.Collections.Generic; +using System; + +namespace osu.Game.Overlays.News.Sidebar +{ + public class NewsSideBar : CompositeDrawable + { + public readonly Bindable Metadata = new Bindable(); + + private YearsPanel yearsPanel; + private FillFlowContainer monthsFlow; + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + RelativeSizeAxes = Axes.Y; + Width = 250; + InternalChildren = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background4 + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding + { + Top = 20, + Left = 50, + Right = 30 + }, + Child = new FillFlowContainer + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Direction = FillDirection.Vertical, + AutoSizeAxes = Axes.Both, + Spacing = new Vector2(0, 20), + Children = new Drawable[] + { + yearsPanel = new YearsPanel(), + monthsFlow = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 10) + } + } + } + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Metadata.BindValueChanged(metadata => + { + monthsFlow.Clear(); + + if (metadata.NewValue == null) + { + yearsPanel.Hide(); + return; + } + + yearsPanel.Years = metadata.NewValue.Years; + yearsPanel.Show(); + + if (metadata.NewValue != null) + { + var dict = new Dictionary>(); + + foreach (var p in metadata.NewValue.NewsPosts) + { + var month = p.PublishedAt.Month; + + if (dict.ContainsKey(month)) + dict[month].Add(p); + else + { + dict.Add(month, new List(new[] { p })); + } + } + + bool isFirst = true; + + foreach (var keyValuePair in dict) + { + monthsFlow.Add(new MonthPanel(new DateTime(metadata.NewValue.CurrentYear, keyValuePair.Key, 1), keyValuePair.Value.ToArray()) + { + IsOpen = { Value = isFirst } + }); + + isFirst = false; + } + } + }, true); + } + } +} From 0d243be457f514eab35edd8a3499f51fd0f573c7 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 10 May 2021 10:07:43 +0300 Subject: [PATCH 125/839] CI fixes --- osu.Game.Tests/Visual/Online/TestSceneNewsSideBar.cs | 2 +- osu.Game.Tests/Visual/Online/TestSceneNewsYearsPanel.cs | 2 +- osu.Game/Overlays/News/Sidebar/MonthPanel.cs | 3 --- osu.Game/Overlays/News/Sidebar/YearsPanel.cs | 3 --- 4 files changed, 2 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsSideBar.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsSideBar.cs index 931341f837..96161c28ed 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneNewsSideBar.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneNewsSideBar.cs @@ -46,7 +46,7 @@ namespace osu.Game.Tests.Visual.Online Metadata = { Value = metadata } }); - private static APINewsSidebar metadata = new APINewsSidebar + private static readonly APINewsSidebar metadata = new APINewsSidebar { CurrentYear = 2021, Years = new[] diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsYearsPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsYearsPanel.cs index 75975f04f8..446cd06eea 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneNewsYearsPanel.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneNewsYearsPanel.cs @@ -17,7 +17,7 @@ namespace osu.Game.Tests.Visual.Online public TestSceneNewsYearsPanel() { - Add(panel = new YearsPanel() + Add(panel = new YearsPanel { Anchor = Anchor.Centre, Origin = Anchor.Centre diff --git a/osu.Game/Overlays/News/Sidebar/MonthPanel.cs b/osu.Game/Overlays/News/Sidebar/MonthPanel.cs index 1dd1c561ab..8b405eae10 100644 --- a/osu.Game/Overlays/News/Sidebar/MonthPanel.cs +++ b/osu.Game/Overlays/News/Sidebar/MonthPanel.cs @@ -125,13 +125,10 @@ namespace osu.Game.Overlays.News.Sidebar { protected override IEnumerable EffectTargets => new[] { text }; - private readonly APINewsPost post; private readonly TextFlowContainer text; public PostButton(APINewsPost post) { - this.post = post; - RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; diff --git a/osu.Game/Overlays/News/Sidebar/YearsPanel.cs b/osu.Game/Overlays/News/Sidebar/YearsPanel.cs index d71c7ba48e..046e1804bd 100644 --- a/osu.Game/Overlays/News/Sidebar/YearsPanel.cs +++ b/osu.Game/Overlays/News/Sidebar/YearsPanel.cs @@ -79,13 +79,10 @@ namespace osu.Game.Overlays.News.Sidebar { protected override IEnumerable EffectTargets => new[] { text }; - private readonly int year; private readonly OsuSpriteText text; public YearButton(int year) { - this.year = year; - Size = new Vector2(33.75f, 15); Child = text = new OsuSpriteText { From 220eef035181e3d08b0fde4430b966b3f69c3cb7 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 10 May 2021 17:00:18 +0300 Subject: [PATCH 126/839] Remove overcomplicated date logic in MonthPanel --- .../Visual/Online/TestSceneNewsMonthPanel.cs | 10 ++++++---- osu.Game/Overlays/News/Sidebar/MonthPanel.cs | 6 +++--- osu.Game/Overlays/News/Sidebar/NewsSideBar.cs | 3 +-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsMonthPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsMonthPanel.cs index 75e02b66e1..ee7fb8b407 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneNewsMonthPanel.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneNewsMonthPanel.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -33,7 +34,7 @@ namespace osu.Game.Tests.Visual.Online RelativeSizeAxes = Axes.Both, Colour = colourProvider.Background2, }, - new MonthPanel(DateTime.Now, posts), + new MonthPanel(posts), } }); } @@ -53,7 +54,7 @@ namespace osu.Game.Tests.Visual.Online RelativeSizeAxes = Axes.Both, Colour = colourProvider.Background2, }, - new MonthPanel(DateTime.Now, posts) + new MonthPanel(posts) { IsOpen = { Value = true } }, @@ -61,11 +62,12 @@ namespace osu.Game.Tests.Visual.Online }); } - private static APINewsPost[] posts => new[] + private static List posts => new List { new APINewsPost { - Title = "Short title" + Title = "Short title", + PublishedAt = DateTimeOffset.Now }, new APINewsPost { diff --git a/osu.Game/Overlays/News/Sidebar/MonthPanel.cs b/osu.Game/Overlays/News/Sidebar/MonthPanel.cs index 8b405eae10..5f2acd63d1 100644 --- a/osu.Game/Overlays/News/Sidebar/MonthPanel.cs +++ b/osu.Game/Overlays/News/Sidebar/MonthPanel.cs @@ -23,7 +23,7 @@ namespace osu.Game.Overlays.News.Sidebar private readonly FillFlowContainer postsFlow; - public MonthPanel(DateTime date, APINewsPost[] posts) + public MonthPanel(List posts) { Width = 160; AutoSizeDuration = 250; @@ -36,7 +36,7 @@ namespace osu.Game.Overlays.News.Sidebar Spacing = new Vector2(0, 5), Children = new Drawable[] { - new DropdownButton(date) + new DropdownButton(posts[0].PublishedAt) { IsOpen = { BindTarget = IsOpen } }, @@ -87,7 +87,7 @@ namespace osu.Game.Overlays.News.Sidebar private readonly SpriteIcon icon; - public DropdownButton(DateTime date) + public DropdownButton(DateTimeOffset date) { Size = new Vector2(160, 15); Action = IsOpen.Toggle; diff --git a/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs b/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs index 34c995cab6..9a2fdf2d97 100644 --- a/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs +++ b/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs @@ -9,7 +9,6 @@ using osu.Game.Online.API.Requests.Responses; using osu.Framework.Graphics.Shapes; using osuTK; using System.Collections.Generic; -using System; namespace osu.Game.Overlays.News.Sidebar { @@ -100,7 +99,7 @@ namespace osu.Game.Overlays.News.Sidebar foreach (var keyValuePair in dict) { - monthsFlow.Add(new MonthPanel(new DateTime(metadata.NewValue.CurrentYear, keyValuePair.Key, 1), keyValuePair.Value.ToArray()) + monthsFlow.Add(new MonthPanel(keyValuePair.Value) { IsOpen = { Value = isFirst } }); From 69f01e82db6cf14052f6fe05d063619042449b86 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 11 May 2021 14:42:56 +0300 Subject: [PATCH 127/839] Add bottom padding for NewsSideBar content --- osu.Game/Overlays/News/Sidebar/NewsSideBar.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs b/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs index 9a2fdf2d97..4444cc79b8 100644 --- a/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs +++ b/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs @@ -36,7 +36,7 @@ namespace osu.Game.Overlays.News.Sidebar RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { - Top = 20, + Vertical = 20, Left = 50, Right = 30 }, From 711e7ba860cd0e3e2113fa09ed53b79c6e6a07e3 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 11 May 2021 14:43:23 +0300 Subject: [PATCH 128/839] Apply suggestions for MonthPanel --- osu.Game/Overlays/News/Sidebar/MonthPanel.cs | 37 ++++++++++++++------ 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/News/Sidebar/MonthPanel.cs b/osu.Game/Overlays/News/Sidebar/MonthPanel.cs index 5f2acd63d1..4d7a5f18aa 100644 --- a/osu.Game/Overlays/News/Sidebar/MonthPanel.cs +++ b/osu.Game/Overlays/News/Sidebar/MonthPanel.cs @@ -19,6 +19,9 @@ namespace osu.Game.Overlays.News.Sidebar { public class MonthPanel : CompositeDrawable { + private const int header_height = 15; + private const int animation_duration = 250; + public readonly BindableBool IsOpen = new BindableBool(); private readonly FillFlowContainer postsFlow; @@ -26,8 +29,7 @@ namespace osu.Game.Overlays.News.Sidebar public MonthPanel(List posts) { Width = 160; - AutoSizeDuration = 250; - AutoSizeEasing = Easing.OutQuint; + Masking = true; InternalChild = new FillFlowContainer { RelativeSizeAxes = Axes.X, @@ -63,33 +65,46 @@ namespace osu.Game.Overlays.News.Sidebar if (open.NewValue) { AutoSizeAxes = Axes.Y; - postsFlow.FadeIn(250, Easing.OutQuint); + postsFlow.FadeIn(animation_duration, Easing.OutQuint); } else { AutoSizeAxes = Axes.None; - this.ResizeHeightTo(15, 250, Easing.OutQuint); + this.ResizeHeightTo(header_height, animation_duration, Easing.OutQuint); - postsFlow.FadeOut(250, Easing.OutQuint); + postsFlow.FadeOut(animation_duration, Easing.OutQuint); } }, true); // First state change should be instant. - FinishTransforms(); - postsFlow.FinishTransforms(); + FinishTransforms(true); } - private class DropdownButton : OsuHoverContainer + private bool shouldUpdateAutosize = true; + + // Workaround to allow the dropdown to be opened immediately since FinishTransforms doesn't work for AutosizeDuration. + protected override void UpdateAfterAutoSize() + { + base.UpdateAfterAutoSize(); + + if (shouldUpdateAutosize) + { + AutoSizeDuration = animation_duration; + AutoSizeEasing = Easing.OutQuint; + + shouldUpdateAutosize = false; + } + } + + private class DropdownButton : OsuClickableContainer { public readonly BindableBool IsOpen = new BindableBool(); - protected override IEnumerable EffectTargets => null; - private readonly SpriteIcon icon; public DropdownButton(DateTimeOffset date) { - Size = new Vector2(160, 15); + Size = new Vector2(160, header_height); Action = IsOpen.Toggle; Children = new Drawable[] { From e736240a064020b4f7b767acfb278ca15d02f3c7 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 11 May 2021 15:12:04 +0300 Subject: [PATCH 129/839] Use lookup instead of dictionary to distribute posts --- osu.Game/Overlays/News/Sidebar/MonthPanel.cs | 4 +-- osu.Game/Overlays/News/Sidebar/NewsSideBar.cs | 29 +++++-------------- 2 files changed, 10 insertions(+), 23 deletions(-) diff --git a/osu.Game/Overlays/News/Sidebar/MonthPanel.cs b/osu.Game/Overlays/News/Sidebar/MonthPanel.cs index 4d7a5f18aa..5460ccce16 100644 --- a/osu.Game/Overlays/News/Sidebar/MonthPanel.cs +++ b/osu.Game/Overlays/News/Sidebar/MonthPanel.cs @@ -26,7 +26,7 @@ namespace osu.Game.Overlays.News.Sidebar private readonly FillFlowContainer postsFlow; - public MonthPanel(List posts) + public MonthPanel(IEnumerable posts) { Width = 160; Masking = true; @@ -38,7 +38,7 @@ namespace osu.Game.Overlays.News.Sidebar Spacing = new Vector2(0, 5), Children = new Drawable[] { - new DropdownButton(posts[0].PublishedAt) + new DropdownButton(posts.ElementAt(0).PublishedAt) { IsOpen = { BindTarget = IsOpen } }, diff --git a/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs b/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs index 4444cc79b8..4d2d3949bd 100644 --- a/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs +++ b/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs @@ -8,7 +8,7 @@ using osu.Framework.Graphics; using osu.Game.Online.API.Requests.Responses; using osu.Framework.Graphics.Shapes; using osuTK; -using System.Collections.Generic; +using System.Linq; namespace osu.Game.Overlays.News.Sidebar { @@ -81,30 +81,17 @@ namespace osu.Game.Overlays.News.Sidebar if (metadata.NewValue != null) { - var dict = new Dictionary>(); + var lookup = metadata.NewValue.NewsPosts.ToLookup(post => post.PublishedAt.Month); - foreach (var p in metadata.NewValue.NewsPosts) + var keys = lookup.Select(kvp => kvp.Key); + var sortedKeys = keys.OrderByDescending(k => k).ToList(); + + for (int i = 0; i < sortedKeys.Count; i++) { - var month = p.PublishedAt.Month; - - if (dict.ContainsKey(month)) - dict[month].Add(p); - else + monthsFlow.Add(new MonthPanel(lookup[sortedKeys[i]]) { - dict.Add(month, new List(new[] { p })); - } - } - - bool isFirst = true; - - foreach (var keyValuePair in dict) - { - monthsFlow.Add(new MonthPanel(keyValuePair.Value) - { - IsOpen = { Value = isFirst } + IsOpen = { Value = i == 0 } }); - - isFirst = false; } } }, true); From 9603712aa1a21b930179533f788e36448c9ec8a1 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 11 May 2021 15:33:27 +0300 Subject: [PATCH 130/839] Cache metadata in NewsSideBar --- .../Visual/Online/TestSceneNewsYearsPanel.cs | 32 ++++++++++++++----- osu.Game/Overlays/News/Sidebar/NewsSideBar.cs | 13 ++------ osu.Game/Overlays/News/Sidebar/YearsPanel.cs | 29 +++++++---------- 3 files changed, 38 insertions(+), 36 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsYearsPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsYearsPanel.cs index 446cd06eea..031a98dd8b 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneNewsYearsPanel.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneNewsYearsPanel.cs @@ -5,6 +5,9 @@ using osu.Framework.Graphics; using osu.Game.Overlays.News.Sidebar; using osu.Framework.Allocation; using osu.Game.Overlays; +using osu.Framework.Bindables; +using osu.Game.Online.API.Requests.Responses; +using NUnit.Framework; namespace osu.Game.Tests.Visual.Online { @@ -13,22 +16,35 @@ namespace osu.Game.Tests.Visual.Online [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); - private readonly YearsPanel panel; + [Cached] + private readonly Bindable metadataBindable = new Bindable(); - public TestSceneNewsYearsPanel() + private YearsPanel panel; + + [SetUp] + public void SetUp() { - Add(panel = new YearsPanel + Child = panel = new YearsPanel { Anchor = Anchor.Centre, Origin = Anchor.Centre - }); + }; } - protected override void LoadComplete() + [Test] + public void TestMetadata() { - base.LoadComplete(); - AddStep("Load years", () => panel.Years = new[] { 1000, 2000, 3000, 4000 }); - AddStep("Load different years", () => panel.Years = new[] { 1001, 2001, 3001, 4001, 5001, 6001, 7001, 8001 }); + AddStep("Change metadata to null", () => metadataBindable.Value = null); + AddAssert("Panel is hidden", () => panel.IsPresent == false); + AddStep("Change metadata", () => metadataBindable.Value = metadata); + AddAssert("Panel is visible", () => panel.IsPresent == true); + AddStep("Change metadata to null", () => metadataBindable.Value = null); + AddAssert("Panel is hidden", () => panel.IsPresent == false); } + + private static readonly APINewsSidebar metadata = new APINewsSidebar + { + Years = new[] { 1001, 2001, 3001, 4001, 5001, 6001, 7001, 8001, 9001 } + }; } } diff --git a/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs b/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs index 4d2d3949bd..75726c5fed 100644 --- a/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs +++ b/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs @@ -14,9 +14,9 @@ namespace osu.Game.Overlays.News.Sidebar { public class NewsSideBar : CompositeDrawable { + [Cached] public readonly Bindable Metadata = new Bindable(); - private YearsPanel yearsPanel; private FillFlowContainer monthsFlow; [BackgroundDependencyLoader] @@ -49,7 +49,7 @@ namespace osu.Game.Overlays.News.Sidebar Spacing = new Vector2(0, 20), Children = new Drawable[] { - yearsPanel = new YearsPanel(), + new YearsPanel(), monthsFlow = new FillFlowContainer { AutoSizeAxes = Axes.Both, @@ -70,15 +70,6 @@ namespace osu.Game.Overlays.News.Sidebar { monthsFlow.Clear(); - if (metadata.NewValue == null) - { - yearsPanel.Hide(); - return; - } - - yearsPanel.Years = metadata.NewValue.Years; - yearsPanel.Show(); - if (metadata.NewValue != null) { var lookup = metadata.NewValue.NewsPosts.ToLookup(post => post.PublishedAt.Month); diff --git a/osu.Game/Overlays/News/Sidebar/YearsPanel.cs b/osu.Game/Overlays/News/Sidebar/YearsPanel.cs index 046e1804bd..23dd8d8a5e 100644 --- a/osu.Game/Overlays/News/Sidebar/YearsPanel.cs +++ b/osu.Game/Overlays/News/Sidebar/YearsPanel.cs @@ -12,28 +12,21 @@ using osu.Game.Graphics.Sprites; using System.Collections.Generic; using osu.Game.Graphics; using osu.Framework.Bindables; -using System.Collections.Specialized; +using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Overlays.News.Sidebar { public class YearsPanel : CompositeDrawable { - public int[] Years - { - set - { - years.Clear(); - years.AddRange(value); - } - } - - private readonly BindableList years = new BindableList(); + private readonly Bindable metadata = new Bindable(); private FillFlowContainer flow; [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) + private void load(OverlayColourProvider colourProvider, Bindable metadata) { + this.metadata.BindTo(metadata); + Width = 160; AutoSizeAxes = Axes.Y; Masking = true; @@ -64,14 +57,16 @@ namespace osu.Game.Overlays.News.Sidebar { base.LoadComplete(); - years.BindCollectionChanged((u, v) => + metadata.BindValueChanged(m => { - switch (v.Action) + if (m.NewValue == null) { - case NotifyCollectionChangedAction.Add: - flow.Children = years.Select(y => new YearButton(y)).ToArray(); - break; + Hide(); + return; } + + flow.Children = m.NewValue.Years.Select(y => new YearButton(y)).ToArray(); + Show(); }, true); } From 0a9c3c9413e89efaf183793fc781e3bcc7f4a5bc Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 11 May 2021 15:39:50 +0300 Subject: [PATCH 131/839] Move metadata change logic to it's own method --- .../Visual/Online/TestSceneNewsYearsPanel.cs | 2 +- osu.Game/Overlays/News/Sidebar/NewsSideBar.cs | 43 +++++++++++-------- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsYearsPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsYearsPanel.cs index 031a98dd8b..014a9ac7ed 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneNewsYearsPanel.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneNewsYearsPanel.cs @@ -32,7 +32,7 @@ namespace osu.Game.Tests.Visual.Online } [Test] - public void TestMetadata() + public void TestVisibility() { AddStep("Change metadata to null", () => metadataBindable.Value = null); AddAssert("Panel is hidden", () => panel.IsPresent == false); diff --git a/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs b/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs index 75726c5fed..d90b73aa82 100644 --- a/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs +++ b/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs @@ -66,26 +66,35 @@ namespace osu.Game.Overlays.News.Sidebar { base.LoadComplete(); - Metadata.BindValueChanged(metadata => + Metadata.BindValueChanged(onMetadataChanged, true); + } + + private void onMetadataChanged(ValueChangedEvent metadata) + { + monthsFlow.Clear(); + + if (metadata.NewValue == null) + return; + + var allPosts = metadata.NewValue.NewsPosts; + + if (!allPosts?.Any() ?? false) + return; + + var lookup = metadata.NewValue.NewsPosts.ToLookup(post => post.PublishedAt.Month); + + var keys = lookup.Select(kvp => kvp.Key); + var sortedKeys = keys.OrderByDescending(k => k).ToList(); + + for (int i = 0; i < sortedKeys.Count; i++) { - monthsFlow.Clear(); + var posts = lookup[sortedKeys[i]]; - if (metadata.NewValue != null) + monthsFlow.Add(new MonthPanel(posts) { - var lookup = metadata.NewValue.NewsPosts.ToLookup(post => post.PublishedAt.Month); - - var keys = lookup.Select(kvp => kvp.Key); - var sortedKeys = keys.OrderByDescending(k => k).ToList(); - - for (int i = 0; i < sortedKeys.Count; i++) - { - monthsFlow.Add(new MonthPanel(lookup[sortedKeys[i]]) - { - IsOpen = { Value = i == 0 } - }); - } - } - }, true); + IsOpen = { Value = i == 0 } + }); + } } } } From 705aad262aa8c2e4ea798a79306d2df820693a61 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 11 May 2021 15:42:40 +0300 Subject: [PATCH 132/839] Rename MonthPanel to MonthDropdown --- ...SceneNewsMonthPanel.cs => TestSceneNewsMonthDropdown.cs} | 6 +++--- .../News/Sidebar/{MonthPanel.cs => MonthDropdown.cs} | 4 ++-- osu.Game/Overlays/News/Sidebar/NewsSideBar.cs | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) rename osu.Game.Tests/Visual/Online/{TestSceneNewsMonthPanel.cs => TestSceneNewsMonthDropdown.cs} (94%) rename osu.Game/Overlays/News/Sidebar/{MonthPanel.cs => MonthDropdown.cs} (97%) diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsMonthPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsMonthDropdown.cs similarity index 94% rename from osu.Game.Tests/Visual/Online/TestSceneNewsMonthPanel.cs rename to osu.Game.Tests/Visual/Online/TestSceneNewsMonthDropdown.cs index ee7fb8b407..c51e299f78 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneNewsMonthPanel.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneNewsMonthDropdown.cs @@ -14,7 +14,7 @@ using osu.Game.Overlays.News.Sidebar; namespace osu.Game.Tests.Visual.Online { - public class TestSceneNewsMonthPanel : OsuTestScene + public class TestSceneNewsMonthDropdown : OsuTestScene { [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); @@ -34,7 +34,7 @@ namespace osu.Game.Tests.Visual.Online RelativeSizeAxes = Axes.Both, Colour = colourProvider.Background2, }, - new MonthPanel(posts), + new MonthDropdown(posts), } }); } @@ -54,7 +54,7 @@ namespace osu.Game.Tests.Visual.Online RelativeSizeAxes = Axes.Both, Colour = colourProvider.Background2, }, - new MonthPanel(posts) + new MonthDropdown(posts) { IsOpen = { Value = true } }, diff --git a/osu.Game/Overlays/News/Sidebar/MonthPanel.cs b/osu.Game/Overlays/News/Sidebar/MonthDropdown.cs similarity index 97% rename from osu.Game/Overlays/News/Sidebar/MonthPanel.cs rename to osu.Game/Overlays/News/Sidebar/MonthDropdown.cs index 5460ccce16..87a72a3eaf 100644 --- a/osu.Game/Overlays/News/Sidebar/MonthPanel.cs +++ b/osu.Game/Overlays/News/Sidebar/MonthDropdown.cs @@ -17,7 +17,7 @@ using osu.Framework.Graphics.Sprites; namespace osu.Game.Overlays.News.Sidebar { - public class MonthPanel : CompositeDrawable + public class MonthDropdown : CompositeDrawable { private const int header_height = 15; private const int animation_duration = 250; @@ -26,7 +26,7 @@ namespace osu.Game.Overlays.News.Sidebar private readonly FillFlowContainer postsFlow; - public MonthPanel(IEnumerable posts) + public MonthDropdown(IEnumerable posts) { Width = 160; Masking = true; diff --git a/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs b/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs index d90b73aa82..1518d61d59 100644 --- a/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs +++ b/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs @@ -17,7 +17,7 @@ namespace osu.Game.Overlays.News.Sidebar [Cached] public readonly Bindable Metadata = new Bindable(); - private FillFlowContainer monthsFlow; + private FillFlowContainer monthsFlow; [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider) @@ -50,7 +50,7 @@ namespace osu.Game.Overlays.News.Sidebar Children = new Drawable[] { new YearsPanel(), - monthsFlow = new FillFlowContainer + monthsFlow = new FillFlowContainer { AutoSizeAxes = Axes.Both, Direction = FillDirection.Vertical, @@ -90,7 +90,7 @@ namespace osu.Game.Overlays.News.Sidebar { var posts = lookup[sortedKeys[i]]; - monthsFlow.Add(new MonthPanel(posts) + monthsFlow.Add(new MonthDropdown(posts) { IsOpen = { Value = i == 0 } }); From 01f5c77dac4b5705e78ba6ec10b2d864ea92073f Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 11 May 2021 15:56:50 +0300 Subject: [PATCH 133/839] Add better comments explaining empty actions --- osu.Game/Overlays/News/Sidebar/MonthDropdown.cs | 2 +- osu.Game/Overlays/News/Sidebar/YearsPanel.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/News/Sidebar/MonthDropdown.cs b/osu.Game/Overlays/News/Sidebar/MonthDropdown.cs index 87a72a3eaf..ff50bfe4c2 100644 --- a/osu.Game/Overlays/News/Sidebar/MonthDropdown.cs +++ b/osu.Game/Overlays/News/Sidebar/MonthDropdown.cs @@ -160,7 +160,7 @@ namespace osu.Game.Overlays.News.Sidebar { IdleColour = colourProvider.Light2; HoverColour = colourProvider.Light1; - Action = () => { }; // TODO + Action = () => { }; // Avoid button being disabled since there's no proper action assigned. } } } diff --git a/osu.Game/Overlays/News/Sidebar/YearsPanel.cs b/osu.Game/Overlays/News/Sidebar/YearsPanel.cs index 23dd8d8a5e..c06a8424f6 100644 --- a/osu.Game/Overlays/News/Sidebar/YearsPanel.cs +++ b/osu.Game/Overlays/News/Sidebar/YearsPanel.cs @@ -93,7 +93,7 @@ namespace osu.Game.Overlays.News.Sidebar { IdleColour = colourProvider.Light2; HoverColour = colourProvider.Light1; - Action = () => { }; // TODO + Action = () => { }; // Avoid button being disabled since there's no proper action assigned. } } } From 208224cc0deb0ae2d79c155f04073577332f322f Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 11 May 2021 16:08:09 +0300 Subject: [PATCH 134/839] CI fixes --- osu.Game.Tests/Visual/Online/TestSceneNewsYearsPanel.cs | 6 +++--- osu.Game/Overlays/News/Sidebar/MonthDropdown.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsYearsPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsYearsPanel.cs index 014a9ac7ed..39bb97fe98 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneNewsYearsPanel.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneNewsYearsPanel.cs @@ -35,11 +35,11 @@ namespace osu.Game.Tests.Visual.Online public void TestVisibility() { AddStep("Change metadata to null", () => metadataBindable.Value = null); - AddAssert("Panel is hidden", () => panel.IsPresent == false); + AddAssert("Panel is hidden", () => !panel.IsPresent); AddStep("Change metadata", () => metadataBindable.Value = metadata); - AddAssert("Panel is visible", () => panel.IsPresent == true); + AddAssert("Panel is visible", () => panel.IsPresent); AddStep("Change metadata to null", () => metadataBindable.Value = null); - AddAssert("Panel is hidden", () => panel.IsPresent == false); + AddAssert("Panel is hidden", () => !panel.IsPresent); } private static readonly APINewsSidebar metadata = new APINewsSidebar diff --git a/osu.Game/Overlays/News/Sidebar/MonthDropdown.cs b/osu.Game/Overlays/News/Sidebar/MonthDropdown.cs index ff50bfe4c2..35092acdc1 100644 --- a/osu.Game/Overlays/News/Sidebar/MonthDropdown.cs +++ b/osu.Game/Overlays/News/Sidebar/MonthDropdown.cs @@ -160,7 +160,7 @@ namespace osu.Game.Overlays.News.Sidebar { IdleColour = colourProvider.Light2; HoverColour = colourProvider.Light1; - Action = () => { }; // Avoid button being disabled since there's no proper action assigned. + Action = () => { }; // Avoid button being disabled since there's no proper action assigned. } } } From 1c0b0996cf43249e4127238f39965133d65c9d4f Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 11 May 2021 16:42:18 +0300 Subject: [PATCH 135/839] Rename DropdownButton to DropdownHeader --- osu.Game/Overlays/News/Sidebar/MonthDropdown.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/News/Sidebar/MonthDropdown.cs b/osu.Game/Overlays/News/Sidebar/MonthDropdown.cs index 35092acdc1..cc06f48544 100644 --- a/osu.Game/Overlays/News/Sidebar/MonthDropdown.cs +++ b/osu.Game/Overlays/News/Sidebar/MonthDropdown.cs @@ -38,7 +38,7 @@ namespace osu.Game.Overlays.News.Sidebar Spacing = new Vector2(0, 5), Children = new Drawable[] { - new DropdownButton(posts.ElementAt(0).PublishedAt) + new DropdownHeader(posts.ElementAt(0).PublishedAt) { IsOpen = { BindTarget = IsOpen } }, @@ -96,15 +96,16 @@ namespace osu.Game.Overlays.News.Sidebar } } - private class DropdownButton : OsuClickableContainer + private class DropdownHeader : OsuClickableContainer { public readonly BindableBool IsOpen = new BindableBool(); private readonly SpriteIcon icon; - public DropdownButton(DateTimeOffset date) + public DropdownHeader(DateTimeOffset date) { - Size = new Vector2(160, header_height); + RelativeSizeAxes = Axes.X; + Height = header_height; Action = IsOpen.Toggle; Children = new Drawable[] { From c2ba16f977ba8e3fb04d72f56bb81cd647def6d2 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 11 May 2021 16:51:59 +0300 Subject: [PATCH 136/839] Use relative sizing for MonthDropdown --- .../Online/TestSceneNewsMonthDropdown.cs | 55 ++++++++----------- .../Overlays/News/Sidebar/MonthDropdown.cs | 2 +- osu.Game/Overlays/News/Sidebar/NewsSideBar.cs | 8 +-- 3 files changed, 27 insertions(+), 38 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsMonthDropdown.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsMonthDropdown.cs index c51e299f78..f03b7d3f58 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneNewsMonthDropdown.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneNewsMonthDropdown.cs @@ -22,46 +22,35 @@ namespace osu.Game.Tests.Visual.Online [Test] public void CreateClosedMonthPanel() { - AddStep("Create", () => Child = new Container - { - Anchor = Anchor.Centre, - Origin = Anchor.TopCentre, - AutoSizeAxes = Axes.Both, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colourProvider.Background2, - }, - new MonthDropdown(posts), - } - }); + create(false); } [Test] public void CreateOpenMonthPanel() { - AddStep("Create", () => Child = new Container - { - Anchor = Anchor.Centre, - Origin = Anchor.TopCentre, - AutoSizeAxes = Axes.Both, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colourProvider.Background2, - }, - new MonthDropdown(posts) - { - IsOpen = { Value = true } - }, - } - }); + create(true); } + private void create(bool isOpen) => AddStep("Create", () => Child = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.TopCentre, + AutoSizeAxes = Axes.Y, + Width = 160, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background2, + }, + new MonthDropdown(posts) + { + IsOpen = { Value = isOpen } + } + } + }); + private static List posts => new List { new APINewsPost diff --git a/osu.Game/Overlays/News/Sidebar/MonthDropdown.cs b/osu.Game/Overlays/News/Sidebar/MonthDropdown.cs index cc06f48544..11c0ce863f 100644 --- a/osu.Game/Overlays/News/Sidebar/MonthDropdown.cs +++ b/osu.Game/Overlays/News/Sidebar/MonthDropdown.cs @@ -28,7 +28,7 @@ namespace osu.Game.Overlays.News.Sidebar public MonthDropdown(IEnumerable posts) { - Width = 160; + RelativeSizeAxes = Axes.X; Masking = true; InternalChild = new FillFlowContainer { diff --git a/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs b/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs index 1518d61d59..7de7e4dd71 100644 --- a/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs +++ b/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs @@ -42,17 +42,17 @@ namespace osu.Game.Overlays.News.Sidebar }, Child = new FillFlowContainer { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, Direction = FillDirection.Vertical, - AutoSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, Spacing = new Vector2(0, 20), Children = new Drawable[] { new YearsPanel(), monthsFlow = new FillFlowContainer { - AutoSizeAxes = Axes.Both, + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, Direction = FillDirection.Vertical, Spacing = new Vector2(0, 10) } From b79a0237a3b57104df4dfee944ad1d2033af49ce Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 11 May 2021 16:54:19 +0300 Subject: [PATCH 137/839] Fix TestSceneNewsYearsPanel error --- .../Visual/Online/TestSceneNewsYearsPanel.cs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsYearsPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsYearsPanel.cs index 39bb97fe98..3199b83a7d 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneNewsYearsPanel.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneNewsYearsPanel.cs @@ -22,24 +22,21 @@ namespace osu.Game.Tests.Visual.Online private YearsPanel panel; [SetUp] - public void SetUp() + public void SetUp() => Schedule(() => Child = panel = new YearsPanel { - Child = panel = new YearsPanel - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre - }; - } + Anchor = Anchor.Centre, + Origin = Anchor.Centre + }); [Test] public void TestVisibility() { AddStep("Change metadata to null", () => metadataBindable.Value = null); - AddAssert("Panel is hidden", () => !panel.IsPresent); + AddUntilStep("Panel is hidden", () => panel?.Alpha == 0); AddStep("Change metadata", () => metadataBindable.Value = metadata); - AddAssert("Panel is visible", () => panel.IsPresent); + AddUntilStep("Panel is visible", () => panel?.Alpha == 1); AddStep("Change metadata to null", () => metadataBindable.Value = null); - AddAssert("Panel is hidden", () => !panel.IsPresent); + AddUntilStep("Panel is hidden", () => panel?.Alpha == 0); } private static readonly APINewsSidebar metadata = new APINewsSidebar From 822d99e69f92f1ef07b04f242219210eaf0c9971 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 11 May 2021 20:42:13 +0300 Subject: [PATCH 138/839] Remove pointless test scenes --- .../Online/TestSceneNewsMonthDropdown.cs | 71 ------------------- .../Visual/Online/TestSceneNewsSideBar.cs | 31 +++----- .../Visual/Online/TestSceneNewsYearsPanel.cs | 47 ------------ 3 files changed, 9 insertions(+), 140 deletions(-) delete mode 100644 osu.Game.Tests/Visual/Online/TestSceneNewsMonthDropdown.cs delete mode 100644 osu.Game.Tests/Visual/Online/TestSceneNewsYearsPanel.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsMonthDropdown.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsMonthDropdown.cs deleted file mode 100644 index f03b7d3f58..0000000000 --- a/osu.Game.Tests/Visual/Online/TestSceneNewsMonthDropdown.cs +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Collections.Generic; -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Online.API.Requests.Responses; -using osu.Game.Overlays; -using osu.Game.Overlays.News.Sidebar; - -namespace osu.Game.Tests.Visual.Online -{ - public class TestSceneNewsMonthDropdown : OsuTestScene - { - [Cached] - private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); - - [Test] - public void CreateClosedMonthPanel() - { - create(false); - } - - [Test] - public void CreateOpenMonthPanel() - { - create(true); - } - - private void create(bool isOpen) => AddStep("Create", () => Child = new Container - { - Anchor = Anchor.Centre, - Origin = Anchor.TopCentre, - AutoSizeAxes = Axes.Y, - Width = 160, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colourProvider.Background2, - }, - new MonthDropdown(posts) - { - IsOpen = { Value = isOpen } - } - } - }); - - private static List posts => new List - { - new APINewsPost - { - Title = "Short title", - PublishedAt = DateTimeOffset.Now - }, - new APINewsPost - { - Title = "Oh boy that's a long post title I wonder if it will break anything" - }, - new APINewsPost - { - Title = "Medium title, nothing to see here" - } - }; - } -} diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsSideBar.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsSideBar.cs index 96161c28ed..8b09a3d176 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneNewsSideBar.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneNewsSideBar.cs @@ -3,8 +3,10 @@ using System; using System.Collections.Generic; +using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Testing; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; using osu.Game.Overlays.News.Sidebar; @@ -18,33 +20,18 @@ namespace osu.Game.Tests.Visual.Online private NewsSideBar sidebar; - [Test] - public void TestCreateEmpty() - { - createSidebar(null); - } + [SetUp] + public void SetUp() => Schedule(() => Child = sidebar = new NewsSideBar()); [Test] - public void TestCreateWithData() + public void TestMetadataChange() { - createSidebar(metadata); + AddUntilStep("Years panel is hidden", () => yearsPanel?.Alpha == 0); + AddStep("Add data", () => sidebar.Metadata.Value = metadata); + AddUntilStep("Years panel is visible", () => yearsPanel?.Alpha == 1); } - [Test] - public void TestDataChange() - { - createSidebar(null); - AddStep("Add data", () => - { - if (sidebar != null) - sidebar.Metadata.Value = metadata; - }); - } - - private void createSidebar(APINewsSidebar metadata) => AddStep("Create", () => Child = sidebar = new NewsSideBar - { - Metadata = { Value = metadata } - }); + private YearsPanel yearsPanel => sidebar.ChildrenOfType().FirstOrDefault(); private static readonly APINewsSidebar metadata = new APINewsSidebar { diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsYearsPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsYearsPanel.cs deleted file mode 100644 index 3199b83a7d..0000000000 --- a/osu.Game.Tests/Visual/Online/TestSceneNewsYearsPanel.cs +++ /dev/null @@ -1,47 +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.Graphics; -using osu.Game.Overlays.News.Sidebar; -using osu.Framework.Allocation; -using osu.Game.Overlays; -using osu.Framework.Bindables; -using osu.Game.Online.API.Requests.Responses; -using NUnit.Framework; - -namespace osu.Game.Tests.Visual.Online -{ - public class TestSceneNewsYearsPanel : OsuTestScene - { - [Cached] - private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); - - [Cached] - private readonly Bindable metadataBindable = new Bindable(); - - private YearsPanel panel; - - [SetUp] - public void SetUp() => Schedule(() => Child = panel = new YearsPanel - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre - }); - - [Test] - public void TestVisibility() - { - AddStep("Change metadata to null", () => metadataBindable.Value = null); - AddUntilStep("Panel is hidden", () => panel?.Alpha == 0); - AddStep("Change metadata", () => metadataBindable.Value = metadata); - AddUntilStep("Panel is visible", () => panel?.Alpha == 1); - AddStep("Change metadata to null", () => metadataBindable.Value = null); - AddUntilStep("Panel is hidden", () => panel?.Alpha == 0); - } - - private static readonly APINewsSidebar metadata = new APINewsSidebar - { - Years = new[] { 1001, 2001, 3001, 4001, 5001, 6001, 7001, 8001, 9001 } - }; - } -} From b0297c6324cff8807952194154271261f4155b4a Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 11 May 2021 20:52:11 +0300 Subject: [PATCH 139/839] Fix incorrect no posts handling and add corresponding test --- .../Visual/Online/TestSceneNewsSideBar.cs | 28 ++++++++++++++++++- osu.Game/Overlays/News/Sidebar/NewsSideBar.cs | 2 +- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsSideBar.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsSideBar.cs index 8b09a3d176..b5405a979e 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneNewsSideBar.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneNewsSideBar.cs @@ -24,13 +24,20 @@ namespace osu.Game.Tests.Visual.Online public void SetUp() => Schedule(() => Child = sidebar = new NewsSideBar()); [Test] - public void TestMetadataChange() + public void TestYearsPanelVisibility() { AddUntilStep("Years panel is hidden", () => yearsPanel?.Alpha == 0); AddStep("Add data", () => sidebar.Metadata.Value = metadata); AddUntilStep("Years panel is visible", () => yearsPanel?.Alpha == 1); } + [Test] + public void TestMetadataWithNoPosts() + { + AddStep("Add data with no posts", () => sidebar.Metadata.Value = metadata_with_no_posts); + AddUntilStep("No dropdowns were created", () => !sidebar.ChildrenOfType().Any()); + } + private YearsPanel yearsPanel => sidebar.ChildrenOfType().FirstOrDefault(); private static readonly APINewsSidebar metadata = new APINewsSidebar @@ -97,5 +104,24 @@ namespace osu.Game.Tests.Visual.Online } } }; + + private static readonly APINewsSidebar metadata_with_no_posts = new APINewsSidebar + { + CurrentYear = 2022, + Years = new[] + { + 2022, + 2021, + 2020, + 2019, + 2018, + 2017, + 2016, + 2015, + 2014, + 2013 + }, + NewsPosts = Array.Empty() + }; } } diff --git a/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs b/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs index 7de7e4dd71..baa1826185 100644 --- a/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs +++ b/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs @@ -78,7 +78,7 @@ namespace osu.Game.Overlays.News.Sidebar var allPosts = metadata.NewValue.NewsPosts; - if (!allPosts?.Any() ?? false) + if (!allPosts?.Any() ?? true) return; var lookup = metadata.NewValue.NewsPosts.ToLookup(post => post.PublishedAt.Month); From f4801c08ff9693c8e97a5252b96a38fe6f288760 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 11 May 2021 22:34:01 +0300 Subject: [PATCH 140/839] Refactor MonthDropdown to ensure all the posts are within a given month --- osu.Game/Overlays/News/Sidebar/MonthDropdown.cs | 11 ++++++++--- osu.Game/Overlays/News/Sidebar/NewsSideBar.cs | 7 +++++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/News/Sidebar/MonthDropdown.cs b/osu.Game/Overlays/News/Sidebar/MonthDropdown.cs index 11c0ce863f..91412d9527 100644 --- a/osu.Game/Overlays/News/Sidebar/MonthDropdown.cs +++ b/osu.Game/Overlays/News/Sidebar/MonthDropdown.cs @@ -14,6 +14,7 @@ using System.Linq; using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics.Sprites; +using System.Diagnostics; namespace osu.Game.Overlays.News.Sidebar { @@ -26,8 +27,10 @@ namespace osu.Game.Overlays.News.Sidebar private readonly FillFlowContainer postsFlow; - public MonthDropdown(IEnumerable posts) + public MonthDropdown(int month, int year, IEnumerable posts) { + Debug.Assert(posts.All(p => p.PublishedAt.Month == month && p.PublishedAt.Year == year)); + RelativeSizeAxes = Axes.X; Masking = true; InternalChild = new FillFlowContainer @@ -38,7 +41,7 @@ namespace osu.Game.Overlays.News.Sidebar Spacing = new Vector2(0, 5), Children = new Drawable[] { - new DropdownHeader(posts.ElementAt(0).PublishedAt) + new DropdownHeader(month, year) { IsOpen = { BindTarget = IsOpen } }, @@ -102,8 +105,10 @@ namespace osu.Game.Overlays.News.Sidebar private readonly SpriteIcon icon; - public DropdownHeader(DateTimeOffset date) + public DropdownHeader(int month, int year) { + var date = new DateTime(year, month, 1); + RelativeSizeAxes = Axes.X; Height = header_height; Action = IsOpen.Toggle; diff --git a/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs b/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs index baa1826185..3851dea83a 100644 --- a/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs +++ b/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs @@ -86,11 +86,14 @@ namespace osu.Game.Overlays.News.Sidebar var keys = lookup.Select(kvp => kvp.Key); var sortedKeys = keys.OrderByDescending(k => k).ToList(); + var year = metadata.NewValue.CurrentYear; + for (int i = 0; i < sortedKeys.Count; i++) { - var posts = lookup[sortedKeys[i]]; + var month = sortedKeys[i]; + var posts = lookup[month]; - monthsFlow.Add(new MonthDropdown(posts) + monthsFlow.Add(new MonthDropdown(month, year, posts) { IsOpen = { Value = i == 0 } }); From 20a6903a40e1a59a253714780718192e1f195837 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 11 May 2021 23:43:01 +0300 Subject: [PATCH 141/839] Use GridContainer to distribute buttons in YearsPanel --- osu.Game/Overlays/News/Sidebar/YearsPanel.cs | 93 +++++++++++++++++--- 1 file changed, 79 insertions(+), 14 deletions(-) diff --git a/osu.Game/Overlays/News/Sidebar/YearsPanel.cs b/osu.Game/Overlays/News/Sidebar/YearsPanel.cs index c06a8424f6..2a4e2024d2 100644 --- a/osu.Game/Overlays/News/Sidebar/YearsPanel.cs +++ b/osu.Game/Overlays/News/Sidebar/YearsPanel.cs @@ -5,14 +5,13 @@ using osu.Framework.Allocation; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; -using osuTK; -using System.Linq; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using System.Collections.Generic; using osu.Game.Graphics; using osu.Framework.Bindables; using osu.Game.Online.API.Requests.Responses; +using System; namespace osu.Game.Overlays.News.Sidebar { @@ -20,15 +19,15 @@ namespace osu.Game.Overlays.News.Sidebar { private readonly Bindable metadata = new Bindable(); - private FillFlowContainer flow; + private Container gridPlaceholder; [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider, Bindable metadata) { this.metadata.BindTo(metadata); - Width = 160; AutoSizeAxes = Axes.Y; + RelativeSizeAxes = Axes.X; Masking = true; CornerRadius = 6; InternalChildren = new Drawable[] @@ -38,17 +37,11 @@ namespace osu.Game.Overlays.News.Sidebar RelativeSizeAxes = Axes.Both, Colour = colourProvider.Background3 }, - new Container + gridPlaceholder = new Container { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Padding = new MarginPadding(5), - Child = flow = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Spacing = new Vector2(5) - } + Padding = new MarginPadding(5) } }; } @@ -65,7 +58,7 @@ namespace osu.Game.Overlays.News.Sidebar return; } - flow.Children = m.NewValue.Years.Select(y => new YearButton(y)).ToArray(); + gridPlaceholder.Child = new YearsGridContainer(m.NewValue.Years); Show(); }, true); } @@ -78,7 +71,8 @@ namespace osu.Game.Overlays.News.Sidebar public YearButton(int year) { - Size = new Vector2(33.75f, 15); + RelativeSizeAxes = Axes.X; + Height = 15; Child = text = new OsuSpriteText { Anchor = Anchor.Centre, @@ -96,5 +90,76 @@ namespace osu.Game.Overlays.News.Sidebar Action = () => { }; // Avoid button being disabled since there's no proper action assigned. } } + + private class YearsGridContainer : GridContainer + { + private const int column_count = 4; + private const float spacing = 5f; + + private readonly int rowCount; + private readonly int[] years; + + public YearsGridContainer(int[] years) + { + this.years = years; + rowCount = (int)Math.Ceiling((float)years.Length / column_count); + + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + RowDimensions = getRowDimensions(); + ColumnDimensions = getColumnDimensions(); + Content = createContent(); + } + + private Dimension[] getRowDimensions() + { + var rowDimensions = new Dimension[rowCount]; + for (int i = 0; i < rowCount; i++) + rowDimensions[i] = new Dimension(GridSizeMode.AutoSize); + + return rowDimensions; + } + + private Dimension[] getColumnDimensions() + { + var columnDimensions = new Dimension[column_count]; + for (int i = 0; i < column_count; i++) + columnDimensions[i] = new Dimension(GridSizeMode.Relative, size: 1f / column_count); + + return columnDimensions; + } + + private Drawable[][] createContent() + { + var buttons = new Drawable[rowCount][]; + + for (int i = 0; i < rowCount; i++) + { + buttons[i] = new Drawable[column_count]; + + for (int j = 0; j < column_count; j++) + { + var index = i * column_count + j; + buttons[i][j] = index >= years.Length + ? Empty() + : new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding + { + Top = i == 0 ? 0 : spacing / 2, + Bottom = i == rowCount - 1 ? 0 : spacing / 2, + Left = j == 0 ? 0 : spacing / 2, + Right = j == column_count - 1 ? 0 : spacing / 2 + }, + Child = new YearButton(years[index]) + }; + } + } + + return buttons; + } + } } } From 5692cecaa47e9e071fb6079d9f3847b8b1de0972 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 12 May 2021 16:35:05 +0900 Subject: [PATCH 142/839] Initial implementation of DHO pooling --- .../Skinning/TestSceneColumnBackground.cs | 4 +- .../Skinning/TestSceneKeyArea.cs | 4 +- osu.Game.Rulesets.Mania/ManiaSkinComponent.cs | 10 +--- .../Objects/Drawables/DrawableHoldNote.cs | 55 +++++++++---------- .../Objects/Drawables/DrawableHoldNoteHead.cs | 12 +++- .../Objects/Drawables/DrawableHoldNoteTail.cs | 17 ++++-- .../Objects/Drawables/DrawableHoldNoteTick.cs | 47 +++++++++++++--- .../Drawables/DrawableManiaHitObject.cs | 20 ++++++- .../Objects/Drawables/DrawableNote.cs | 26 +++++---- osu.Game.Rulesets.Mania/Objects/HeadNote.cs | 9 +++ osu.Game.Rulesets.Mania/Objects/HoldNote.cs | 4 +- osu.Game.Rulesets.Mania/UI/Column.cs | 11 +++- .../UI/Components/ColumnHitObjectArea.cs | 2 +- .../UI/PoolableHitExplosion.cs | 2 +- 14 files changed, 146 insertions(+), 77 deletions(-) create mode 100644 osu.Game.Rulesets.Mania/Objects/HeadNote.cs diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneColumnBackground.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneColumnBackground.cs index bde323f187..ca323b5911 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneColumnBackground.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneColumnBackground.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning { RelativeSizeAxes = Axes.Both, Width = 0.5f, - Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground, 0), _ => new DefaultColumnBackground()) + Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground), _ => new DefaultColumnBackground()) { RelativeSizeAxes = Axes.Both } @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning { RelativeSizeAxes = Axes.Both, Width = 0.5f, - Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground, 1), _ => new DefaultColumnBackground()) + Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground), _ => new DefaultColumnBackground()) { RelativeSizeAxes = Axes.Both } diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneKeyArea.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneKeyArea.cs index 7e80419944..c58c07c83b 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneKeyArea.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneKeyArea.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning { RelativeSizeAxes = Axes.Both, Width = 0.5f, - Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.KeyArea, 0), _ => new DefaultKeyArea()) + Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.KeyArea), _ => new DefaultKeyArea()) { RelativeSizeAxes = Axes.Both }, @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning { RelativeSizeAxes = Axes.Both, Width = 0.5f, - Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.KeyArea, 1), _ => new DefaultKeyArea()) + Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.KeyArea), _ => new DefaultKeyArea()) { RelativeSizeAxes = Axes.Both }, diff --git a/osu.Game.Rulesets.Mania/ManiaSkinComponent.cs b/osu.Game.Rulesets.Mania/ManiaSkinComponent.cs index f078345fc1..9aebf51576 100644 --- a/osu.Game.Rulesets.Mania/ManiaSkinComponent.cs +++ b/osu.Game.Rulesets.Mania/ManiaSkinComponent.cs @@ -9,12 +9,6 @@ namespace osu.Game.Rulesets.Mania { public class ManiaSkinComponent : GameplaySkinComponent { - /// - /// The intended index for this component. - /// May be null if the component does not exist in a . - /// - public readonly int? TargetColumn; - /// /// The intended for this component. /// May be null if the component is not a direct member of a . @@ -25,12 +19,10 @@ namespace osu.Game.Rulesets.Mania /// Creates a new . /// /// The component. - /// The intended index for this component. May be null if the component does not exist in a . /// The intended for this component. May be null if the component is not a direct member of a . - public ManiaSkinComponent(ManiaSkinComponents component, int? targetColumn = null, StageDefinition? stageDefinition = null) + public ManiaSkinComponent(ManiaSkinComponents component, StageDefinition? stageDefinition = null) : base(component) { - TargetColumn = targetColumn; StageDefinition = stageDefinition; } diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index 02829d87bd..e1cf1851df 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -29,21 +30,21 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables public DrawableHoldNoteHead Head => headContainer.Child; public DrawableHoldNoteTail Tail => tailContainer.Child; - private readonly Container headContainer; - private readonly Container tailContainer; - private readonly Container tickContainer; + private Container headContainer; + private Container tailContainer; + private Container tickContainer; /// /// Contains the size of the hold note covering the whole head/tail bounds. The size of this container changes as the hold note is being pressed. /// - private readonly Container sizingContainer; + private Container sizingContainer; /// /// Contains the contents of the hold note that should be masked as the hold note is being pressed. Follows changes in the size of . /// - private readonly Container maskingContainer; + private Container maskingContainer; - private readonly SkinnableDrawable bodyPiece; + private SkinnableDrawable bodyPiece; /// /// Time at which the user started holding this hold note. Null if the user is not holding this hold note. @@ -60,11 +61,19 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables /// private double? releaseTime; + public DrawableHoldNote() + : this(null) + { + } + public DrawableHoldNote(HoldNote hitObject) : base(hitObject) { - RelativeSizeAxes = Axes.X; + } + [BackgroundDependencyLoader] + private void load() + { Container maskedContents; AddRangeInternal(new Drawable[] @@ -86,7 +95,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables headContainer = new Container { RelativeSizeAxes = Axes.Both } } }, - bodyPiece = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HoldNoteBody, hitObject.Column), _ => new DefaultBodyPiece + bodyPiece = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HoldNoteBody), _ => new DefaultBodyPiece { RelativeSizeAxes = Axes.Both, }) @@ -128,37 +137,23 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables protected override void ClearNestedHitObjects() { base.ClearNestedHitObjects(); - headContainer.Clear(); - tailContainer.Clear(); - tickContainer.Clear(); + headContainer.Clear(false); + tailContainer.Clear(false); + tickContainer.Clear(false); } protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject) { switch (hitObject) { - case TailNote _: - return new DrawableHoldNoteTail(this) - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - AccentColour = { BindTarget = AccentColour } - }; + case TailNote tail: + return new DrawableHoldNoteTail(tail); - case Note _: - return new DrawableHoldNoteHead(this) - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - AccentColour = { BindTarget = AccentColour } - }; + case HeadNote head: + return new DrawableHoldNoteHead(head); case HoldNoteTick tick: - return new DrawableHoldNoteTick(tick) - { - HoldStartTime = () => HoldStartTime, - AccentColour = { BindTarget = AccentColour } - }; + return new DrawableHoldNoteTick(tick); } return base.CreateNestedHitObject(hitObject); diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs index 35ba2465fa..be600f0d47 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.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 osu.Framework.Graphics; using osu.Game.Rulesets.Objects.Drawables; namespace osu.Game.Rulesets.Mania.Objects.Drawables @@ -12,11 +13,18 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables { protected override ManiaSkinComponents Component => ManiaSkinComponents.HoldNoteHead; - public DrawableHoldNoteHead(DrawableHoldNote holdNote) - : base(holdNote.HitObject.Head) + public DrawableHoldNoteHead() + : this(null) { } + public DrawableHoldNoteHead(HeadNote headNote) + : base(headNote) + { + Anchor = Anchor.TopCentre; + Origin = Anchor.TopCentre; + } + public void UpdateResult() => base.UpdateResult(true); protected override void UpdateInitialTransforms() diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTail.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTail.cs index 3a00933e4d..18aa3f66d4 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTail.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTail.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Diagnostics; +using osu.Framework.Graphics; using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Mania.Objects.Drawables @@ -20,12 +21,18 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables protected override ManiaSkinComponents Component => ManiaSkinComponents.HoldNoteTail; - private readonly DrawableHoldNote holdNote; + protected DrawableHoldNote HoldNote => (DrawableHoldNote)ParentHitObject; - public DrawableHoldNoteTail(DrawableHoldNote holdNote) - : base(holdNote.HitObject.Tail) + public DrawableHoldNoteTail() + : this(null) { - this.holdNote = holdNote; + } + + public DrawableHoldNoteTail(TailNote tailNote) + : base(tailNote) + { + Anchor = Anchor.TopCentre; + Origin = Anchor.TopCentre; } public void UpdateResult() => base.UpdateResult(true); @@ -54,7 +61,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables ApplyResult(r => { // If the head wasn't hit or the hold note was broken, cap the max score to Meh. - if (result > HitResult.Meh && (!holdNote.Head.IsHit || holdNote.HoldBrokenTime != null)) + if (result > HitResult.Meh && (!HoldNote.Head.IsHit || HoldNote.HoldBrokenTime != null)) result = HitResult.Meh; r.Type = result; diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs index 98931dceed..6a57849fb4 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs @@ -2,7 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using System; -using osuTK; +using System.Diagnostics; +using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -19,20 +20,28 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables /// /// References the time at which the user started holding the hold note. /// - public Func HoldStartTime; + private Func holdStartTime; + + private Container glowContainer; + + public DrawableHoldNoteTick() + : this(null) + { + } public DrawableHoldNoteTick(HoldNoteTick hitObject) : base(hitObject) { - Container glowContainer; - Anchor = Anchor.TopCentre; Origin = Anchor.TopCentre; RelativeSizeAxes = Axes.X; - Size = new Vector2(1); + } - AddRangeInternal(new[] + [BackgroundDependencyLoader] + private void load() + { + InternalChildren = new[] { glowContainer = new CircularContainer { @@ -50,7 +59,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables } } } - }); + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); AccentColour.BindValueChanged(colour => { @@ -64,12 +78,29 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables }, true); } + protected override void OnApply() + { + base.OnApply(); + + Debug.Assert(ParentHitObject != null); + + var holdNote = (DrawableHoldNote)ParentHitObject; + holdStartTime = () => holdNote.HoldStartTime; + } + + protected override void OnFree() + { + base.OnFree(); + + holdStartTime = null; + } + protected override void CheckForResult(bool userTriggered, double timeOffset) { if (Time.Current < HitObject.StartTime) return; - var startTime = HoldStartTime?.Invoke(); + var startTime = holdStartTime?.Invoke(); if (startTime == null || startTime > HitObject.StartTime) ApplyResult(r => r.Type = r.Judgement.MinResult); diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs index 003646d654..7323ae9df1 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs @@ -50,6 +50,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables protected DrawableManiaHitObject(ManiaHitObject hitObject) : base(hitObject) { + RelativeSizeAxes = Axes.X; } [BackgroundDependencyLoader(true)] @@ -62,6 +63,22 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables Direction.BindValueChanged(OnDirectionChanged, true); } + protected override void OnApply() + { + base.OnApply(); + + if (ParentHitObject != null) + AccentColour.BindTo(ParentHitObject.AccentColour); + } + + protected override void OnFree() + { + base.OnFree(); + + if (ParentHitObject != null) + AccentColour.UnbindFrom(ParentHitObject.AccentColour); + } + private double computedLifetimeStart; public override double LifetimeStart @@ -147,12 +164,11 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables public abstract class DrawableManiaHitObject : DrawableManiaHitObject where TObject : ManiaHitObject { - public new readonly TObject HitObject; + public new TObject HitObject => (TObject)base.HitObject; protected DrawableManiaHitObject(TObject hitObject) : base(hitObject) { - HitObject = hitObject; } } } diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs index 36565e14aa..16feec6b3a 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs @@ -33,31 +33,35 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables protected virtual ManiaSkinComponents Component => ManiaSkinComponents.Note; - private readonly Drawable headPiece; + private Drawable headPiece; + + public DrawableNote() + : this(null) + { + } public DrawableNote(Note hitObject) : base(hitObject) { - RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; - - AddInternal(headPiece = new SkinnableDrawable(new ManiaSkinComponent(Component, hitObject.Column), _ => new DefaultNotePiece()) - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y - }); } [BackgroundDependencyLoader(true)] private void load(ManiaRulesetConfigManager rulesetConfig) { rulesetConfig?.BindWith(ManiaRulesetSetting.TimingBasedNoteColouring, configTimingBasedNoteColouring); + + InternalChild = headPiece = new SkinnableDrawable(new ManiaSkinComponent(Component), _ => new DefaultNotePiece()) + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y + }; } protected override void LoadComplete() { - HitObject.StartTimeBindable.BindValueChanged(_ => updateSnapColour()); - configTimingBasedNoteColouring.BindValueChanged(_ => updateSnapColour(), true); + configTimingBasedNoteColouring.BindValueChanged(_ => updateSnapColour()); + StartTimeBindable.BindValueChanged(_ => updateSnapColour(), true); } protected override void OnDirectionChanged(ValueChangedEvent e) @@ -102,7 +106,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables private void updateSnapColour() { - if (beatmap == null) return; + if (beatmap == null || HitObject == null) return; int snapDivisor = beatmap.ControlPointInfo.GetClosestBeatDivisor(HitObject.StartTime); diff --git a/osu.Game.Rulesets.Mania/Objects/HeadNote.cs b/osu.Game.Rulesets.Mania/Objects/HeadNote.cs new file mode 100644 index 0000000000..e69cc62aed --- /dev/null +++ b/osu.Game.Rulesets.Mania/Objects/HeadNote.cs @@ -0,0 +1,9 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Rulesets.Mania.Objects +{ + public class HeadNote : Note + { + } +} diff --git a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs index 6cc7ff92d3..43e876b7aa 100644 --- a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs @@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Mania.Objects /// /// The head note of the hold. /// - public Note Head { get; private set; } + public HeadNote Head { get; private set; } /// /// The tail note of the hold. @@ -98,7 +98,7 @@ namespace osu.Game.Rulesets.Mania.Objects createTicks(cancellationToken); - AddNested(Head = new Note + AddNested(Head = new HeadNote { StartTime = StartTime, Column = Column, diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index 0f02e2cd4b..8247a37881 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -17,6 +17,7 @@ using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Skinning; using osuTK; using osu.Game.Rulesets.Mania.Beatmaps; +using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects.Drawables; namespace osu.Game.Rulesets.Mania.UI @@ -55,7 +56,7 @@ namespace osu.Game.Rulesets.Mania.UI RelativeSizeAxes = Axes.Y; Width = COLUMN_WIDTH; - Drawable background = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground, Index), _ => new DefaultColumnBackground()) + Drawable background = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground), _ => new DefaultColumnBackground()) { RelativeSizeAxes = Axes.Both }; @@ -66,7 +67,7 @@ namespace osu.Game.Rulesets.Mania.UI // For input purposes, the background is added at the highest depth, but is then proxied back below all other elements background.CreateProxy(), HitObjectArea = new ColumnHitObjectArea(Index, HitObjectContainer) { RelativeSizeAxes = Axes.Both }, - new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.KeyArea, Index), _ => new DefaultKeyArea()) + new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.KeyArea), _ => new DefaultKeyArea()) { RelativeSizeAxes = Axes.Both }, @@ -83,6 +84,12 @@ namespace osu.Game.Rulesets.Mania.UI hitPolicy = new OrderedHitPolicy(HitObjectContainer); TopLevelContainer.Add(HitObjectArea.Explosions.CreateProxy()); + + RegisterPool(5, 20); + RegisterPool(5, 20); + RegisterPool(5, 20); + RegisterPool(5, 20); + RegisterPool(50, 200); } public ColumnType ColumnType { get; set; } diff --git a/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs b/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs index b365ae45a9..f69d2aafdc 100644 --- a/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs +++ b/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Mania.UI.Components RelativeSizeAxes = Axes.Both, Depth = 2, }, - hitTarget = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HitTarget, columnIndex), _ => new DefaultHitTarget()) + hitTarget = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HitTarget), _ => new DefaultHitTarget()) { RelativeSizeAxes = Axes.X, Depth = 1 diff --git a/osu.Game.Rulesets.Mania/UI/PoolableHitExplosion.cs b/osu.Game.Rulesets.Mania/UI/PoolableHitExplosion.cs index 64b7d7d550..90d3c6c4c7 100644 --- a/osu.Game.Rulesets.Mania/UI/PoolableHitExplosion.cs +++ b/osu.Game.Rulesets.Mania/UI/PoolableHitExplosion.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Mania.UI [BackgroundDependencyLoader] private void load() { - InternalChild = skinnableExplosion = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HitExplosion, column.Index), _ => new DefaultHitExplosion()) + InternalChild = skinnableExplosion = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HitExplosion), _ => new DefaultHitExplosion()) { RelativeSizeAxes = Axes.Both }; From 4e7551d50ef672362d11a9241b277617f47dac73 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 12 May 2021 16:40:46 +0900 Subject: [PATCH 143/839] Fix crashes --- .../Objects/Drawables/DrawableHoldNoteTick.cs | 25 ++++++++----------- .../Drawables/DrawableManiaHitObject.cs | 6 +++++ .../Objects/Drawables/DrawableNote.cs | 4 +-- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs index 6a57849fb4..f040dad135 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs @@ -41,25 +41,22 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables [BackgroundDependencyLoader] private void load() { - InternalChildren = new[] + AddInternal(glowContainer = new CircularContainer { - glowContainer = new CircularContainer + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.Both, + Masking = true, + Children = new[] { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - RelativeSizeAxes = Axes.Both, - Masking = true, - Children = new[] + new Box { - new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true - } + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true } } - }; + }); } protected override void LoadComplete() diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs index 7323ae9df1..380ab35339 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs @@ -60,6 +60,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables Action.BindTo(action); Direction.BindTo(scrollingInfo.Direction); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + Direction.BindValueChanged(OnDirectionChanged, true); } diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs index 16feec6b3a..b711e67f25 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs @@ -51,11 +51,11 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables { rulesetConfig?.BindWith(ManiaRulesetSetting.TimingBasedNoteColouring, configTimingBasedNoteColouring); - InternalChild = headPiece = new SkinnableDrawable(new ManiaSkinComponent(Component), _ => new DefaultNotePiece()) + AddInternal(headPiece = new SkinnableDrawable(new ManiaSkinComponent(Component), _ => new DefaultNotePiece()) { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y - }; + }); } protected override void LoadComplete() From 789025a7cea6f3621301f011dd182954f61ff806 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 12 May 2021 16:56:07 +0900 Subject: [PATCH 144/839] Update playfield/stage/column implementations for pooling --- osu.Game.Rulesets.Mania/UI/Column.cs | 31 +++++++--------- osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs | 5 +++ osu.Game.Rulesets.Mania/UI/Stage.cs | 37 ++++++++++++++++---- 3 files changed, 47 insertions(+), 26 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index 8247a37881..1ee3f8c668 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -92,6 +92,13 @@ namespace osu.Game.Rulesets.Mania.UI RegisterPool(50, 200); } + protected override void LoadComplete() + { + base.LoadComplete(); + + NewResult += OnNewResult; + } + public ColumnType ColumnType { get; set; } public bool IsSpecial => ColumnType == ColumnType.Special; @@ -105,28 +112,14 @@ namespace osu.Game.Rulesets.Mania.UI return dependencies; } - /// - /// Adds a DrawableHitObject to this Playfield. - /// - /// The DrawableHitObject to add. - public override void Add(DrawableHitObject hitObject) + protected override void OnNewDrawableHitObject(DrawableHitObject drawableHitObject) { - hitObject.AccentColour.Value = AccentColour; - hitObject.OnNewResult += OnNewResult; + base.OnNewDrawableHitObject(drawableHitObject); - DrawableManiaHitObject maniaObject = (DrawableManiaHitObject)hitObject; + drawableHitObject.AccentColour.Value = AccentColour; + + DrawableManiaHitObject maniaObject = (DrawableManiaHitObject)drawableHitObject; maniaObject.CheckHittable = hitPolicy.IsHittable; - - base.Add(hitObject); - } - - public override bool Remove(DrawableHitObject h) - { - if (!base.Remove(h)) - return false; - - h.OnNewResult -= OnNewResult; - return true; } internal void OnNewResult(DrawableHitObject judgedObject, JudgementResult result) diff --git a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs index 271e432e8d..8830c440c0 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs @@ -9,6 +9,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI.Scrolling; using osuTK; @@ -56,6 +57,10 @@ namespace osu.Game.Rulesets.Mania.UI } } + public override void Add(HitObject hitObject) => getStageByColumn(((ManiaHitObject)hitObject).Column).Add(hitObject); + + public override bool Remove(HitObject hitObject) => getStageByColumn(((ManiaHitObject)hitObject).Column).Remove(hitObject); + public override void Add(DrawableHitObject h) => getStageByColumn(((ManiaHitObject)h.HitObject).Column).Add(h); public override bool Remove(DrawableHitObject h) => getStageByColumn(((ManiaHitObject)h.HitObject).Column).Remove(h); diff --git a/osu.Game.Rulesets.Mania/UI/Stage.cs b/osu.Game.Rulesets.Mania/UI/Stage.cs index dc34bffab1..eee75f3200 100644 --- a/osu.Game.Rulesets.Mania/UI/Stage.cs +++ b/osu.Game.Rulesets.Mania/UI/Stage.cs @@ -11,6 +11,7 @@ using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.UI.Components; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; @@ -132,10 +133,37 @@ namespace osu.Game.Rulesets.Mania.UI } } + protected override void LoadComplete() + { + base.LoadComplete(); + NewResult += OnNewResult; + } + + public override void Add(HitObject hitObject) + { + var maniaObject = (ManiaHitObject)hitObject; + int columnIndex = -1; + + maniaObject.ColumnBindable.BindValueChanged(_ => + { + if (columnIndex != -1) + Columns.ElementAt(columnIndex).Remove(hitObject); + + columnIndex = maniaObject.Column - firstColumnIndex; + Columns.ElementAt(columnIndex).Add(hitObject); + }, true); + } + + public override bool Remove(HitObject hitObject) + { + var maniaObject = (ManiaHitObject)hitObject; + int columnIndex = maniaObject.Column - firstColumnIndex; + return Columns.ElementAt(columnIndex).Remove(hitObject); + } + public override void Add(DrawableHitObject h) { var maniaObject = (ManiaHitObject)h.HitObject; - int columnIndex = -1; maniaObject.ColumnBindable.BindValueChanged(_ => @@ -146,18 +174,13 @@ namespace osu.Game.Rulesets.Mania.UI columnIndex = maniaObject.Column - firstColumnIndex; Columns.ElementAt(columnIndex).Add(h); }, true); - - h.OnNewResult += OnNewResult; } public override bool Remove(DrawableHitObject h) { var maniaObject = (ManiaHitObject)h.HitObject; int columnIndex = maniaObject.Column - firstColumnIndex; - Columns.ElementAt(columnIndex).Remove(h); - - h.OnNewResult -= OnNewResult; - return true; + return Columns.ElementAt(columnIndex).Remove(h); } public void Add(BarLine barline) => base.Add(new DrawableBarLine(barline)); From 7913189aa911e814025e5083eddc7de5d74c5c51 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 12 May 2021 16:56:23 +0900 Subject: [PATCH 145/839] Turn on pooling --- .../UI/DrawableManiaRuleset.cs | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs index 4ee060e91e..e497646a13 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs @@ -18,7 +18,6 @@ using osu.Game.Replays; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Configuration; using osu.Game.Rulesets.Mania.Objects; -using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Replays; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; @@ -134,20 +133,7 @@ namespace osu.Game.Rulesets.Mania.UI protected override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, Variant); - public override DrawableHitObject CreateDrawableRepresentation(ManiaHitObject h) - { - switch (h) - { - case HoldNote holdNote: - return new DrawableHoldNote(holdNote); - - case Note note: - return new DrawableNote(note); - - default: - return null; - } - } + public override DrawableHitObject CreateDrawableRepresentation(ManiaHitObject h) => null; protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay); From 1af3bbf400d7267feac4627ef5d8e1ab2b87b855 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 12 May 2021 17:06:44 +0900 Subject: [PATCH 146/839] Fix base.OnLoadComplete() not being called --- osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs index b711e67f25..33d872dfb6 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs @@ -60,6 +60,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables protected override void LoadComplete() { + base.LoadComplete(); + configTimingBasedNoteColouring.BindValueChanged(_ => updateSnapColour()); StartTimeBindable.BindValueChanged(_ => updateSnapColour(), true); } From f992b59b4f9b3ef5b8460704f3f878e818fe7367 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 12 May 2021 17:07:42 +0900 Subject: [PATCH 147/839] Fix DrawableHoldNote retaining hit states through applications --- .../Objects/Drawables/DrawableHoldNote.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index e1cf1851df..d1310d42eb 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -13,6 +13,7 @@ using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Skinning; +using osuTK; namespace osu.Game.Rulesets.Mania.Objects.Drawables { @@ -114,6 +115,16 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables }); } + protected override void OnApply() + { + base.OnApply(); + + sizingContainer.Size = Vector2.One; + HoldStartTime = null; + HoldBrokenTime = null; + releaseTime = null; + } + protected override void AddNestedHitObject(DrawableHitObject hitObject) { base.AddNestedHitObject(hitObject); From 0fa3027ab9eea096fc792a6a4bd9ff6a4944e130 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 12 May 2021 17:36:26 +0900 Subject: [PATCH 148/839] Increase pool sizes a bit --- osu.Game.Rulesets.Mania/UI/Column.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index 1ee3f8c668..57d3f54716 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -85,11 +85,11 @@ namespace osu.Game.Rulesets.Mania.UI TopLevelContainer.Add(HitObjectArea.Explosions.CreateProxy()); - RegisterPool(5, 20); - RegisterPool(5, 20); - RegisterPool(5, 20); - RegisterPool(5, 20); - RegisterPool(50, 200); + RegisterPool(10, 50); + RegisterPool(10, 50); + RegisterPool(10, 50); + RegisterPool(10, 50); + RegisterPool(50, 250); } protected override void LoadComplete() From 946abfbb83d89caea8e7273901a1e1a020824813 Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Wed, 12 May 2021 18:11:50 +0200 Subject: [PATCH 149/839] Rework settings; Add seed to ScorePanel; Apply requested changes from @bdach --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 200 +++++++----------- osu.Game/Rulesets/Mods/ModRandom.cs | 1 + osu.Game/Scoring/ScoreInfo.cs | 18 ++ .../Expanded/ExpandedPanelMiddleContent.cs | 47 +++- osu.Game/Screens/Ranking/ScorePanel.cs | 8 +- 5 files changed, 152 insertions(+), 122 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 9a6127cdad..2d7c52d535 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -35,68 +35,46 @@ namespace osu.Game.Rulesets.Osu.Mods private const byte border_distance_x = 192; private const byte border_distance_y = 144; - private static readonly Bindable seed = new Bindable + [SettingSource("Custom seed", "Use a custom seed instead of a random one", SettingControlType = typeof(OsuModRandomSettingsControl))] + public Bindable CustomSeed { get; } = new Bindable { - Default = -1 + Default = null, + Value = null }; - private static readonly BindableBool random_seed = new BindableBool + public void ApplyToBeatmap(IBeatmap beatmap) { - Value = true, - Default = true - }; - - [SettingSource("Random seed", "Generate a random seed for the beatmap generation")] - public BindableBool RandomSeed => random_seed; - - [SettingSource("Seed", "Seed for the random beatmap generation", SettingControlType = typeof(OsuModRandomSettingsControl))] - public Bindable Seed => seed; - - internal static bool CustomSeedDisabled => random_seed.Value; - - public OsuModRandom() - { - if (seed.Default != -1) + if (!(beatmap is OsuBeatmap osuBeatmap)) return; - var random = RNG.Next(); - seed.Value = random; - seed.Default = random; - seed.BindValueChanged(e => seed.Default = e.NewValue); - } + var seed = RNG.Next(); - public void ApplyToBeatmap(IBeatmap iBeatmap) - { - if (!(iBeatmap is OsuBeatmap beatmap)) - return; + if (CustomSeed.Value != null) + seed = (int)CustomSeed.Value; - if (RandomSeed.Value) - seed.Value = RNG.Next(); + Seed = seed; - var rng = new Random(seed.Value); + var rng = new Random(seed); - // Absolute angle - float prevAngleRad = 0; - - // Absolute positions - Vector2 prevPosUnchanged = beatmap.HitObjects[0].Position; - Vector2 prevPosChanged = beatmap.HitObjects[0].Position; + var prevObjectInfo = new HitObjectInfo + { + AngleRad = 0, + PosUnchanged = osuBeatmap.HitObjects[0].Position, + PosChanged = osuBeatmap.HitObjects[0].Position + }; // rateOfChangeMultiplier changes every i iterations to prevent shaky-line-shaped streams byte i = 3; float rateOfChangeMultiplier = 0; - foreach (var beatmapHitObject in beatmap.HitObjects) + foreach (var currentHitObject in osuBeatmap.HitObjects) { - if (!(beatmapHitObject is OsuHitObject hitObject)) - return; - - // posUnchanged: position from the original beatmap (not randomised) - var posUnchanged = hitObject.EndPosition; - var posChanged = Vector2.Zero; - - // Angle of the vector pointing from the last to the current hit object - float angleRad = 0; + var currentObjectInfo = new HitObjectInfo + { + AngleRad = 0, + PosUnchanged = currentHitObject.EndPosition, + PosChanged = Vector2.Zero + }; if (i >= 3) { @@ -104,24 +82,23 @@ namespace osu.Game.Rulesets.Osu.Mods rateOfChangeMultiplier = (float)rng.NextDouble() * 2 - 1; } - if (hitObject is HitCircle circle) + if (currentHitObject is HitCircle circle) { - var distanceToPrev = Vector2.Distance(posUnchanged, prevPosUnchanged); + var distanceToPrev = Vector2.Distance(currentObjectInfo.PosUnchanged, prevObjectInfo.PosUnchanged); - circle.Position = posChanged = getRandomisedPosition( + getObjectInfo( rateOfChangeMultiplier, - prevPosChanged, - prevAngleRad, + prevObjectInfo, distanceToPrev, - out angleRad + ref currentObjectInfo ); + + circle.Position = currentObjectInfo.PosChanged; } // TODO: Implement slider position randomisation - prevAngleRad = angleRad; - prevPosUnchanged = posUnchanged; - prevPosChanged = posChanged; + prevObjectInfo = currentObjectInfo; i++; } } @@ -130,12 +107,11 @@ namespace osu.Game.Rulesets.Osu.Mods /// Returns the final position of the hit object /// /// Final position of the hit object - private Vector2 getRandomisedPosition( + private void getObjectInfo( float rateOfChangeMultiplier, - Vector2 prevPosChanged, - float prevAngleRad, + HitObjectInfo prevObjectInfo, float distanceToPrev, - out float newAngle) + ref HitObjectInfo currentObjectInfo) { // The max. angle (relative to the angle of the vector pointing from the 2nd last to the last hit object) // is proportional to the distance between the last and the current hit object @@ -143,19 +119,19 @@ namespace osu.Game.Rulesets.Osu.Mods var maxDistance = OsuPlayfield.BASE_SIZE.LengthFast; var randomAngleRad = rateOfChangeMultiplier * 2 * Math.PI * distanceToPrev / maxDistance; - newAngle = (float)randomAngleRad + prevAngleRad; - if (newAngle < 0) - newAngle += 2 * (float)Math.PI; + currentObjectInfo.AngleRad = (float)randomAngleRad + prevObjectInfo.AngleRad; + if (currentObjectInfo.AngleRad < 0) + currentObjectInfo.AngleRad += 2 * (float)Math.PI; var posRelativeToPrev = new Vector2( - distanceToPrev * (float)Math.Cos(newAngle), - distanceToPrev * (float)Math.Sin(newAngle) + distanceToPrev * (float)Math.Cos(currentObjectInfo.AngleRad), + distanceToPrev * (float)Math.Sin(currentObjectInfo.AngleRad) ); - posRelativeToPrev = getRotatedVector(prevPosChanged, posRelativeToPrev); + posRelativeToPrev = getRotatedVector(prevObjectInfo.PosChanged, posRelativeToPrev); - newAngle = (float)Math.Atan2(posRelativeToPrev.Y, posRelativeToPrev.X); - var position = Vector2.Add(prevPosChanged, posRelativeToPrev); + currentObjectInfo.AngleRad = (float)Math.Atan2(posRelativeToPrev.Y, posRelativeToPrev.X); + var position = Vector2.Add(prevObjectInfo.PosChanged, posRelativeToPrev); // Move hit objects back into the playfield if they are outside of it, // which would sometimes happen during big jumps otherwise. @@ -169,7 +145,7 @@ namespace osu.Game.Rulesets.Osu.Mods else if (position.Y > OsuPlayfield.BASE_SIZE.Y) position.Y = OsuPlayfield.BASE_SIZE.Y; - return position; + currentObjectInfo.PosChanged = position; } /// @@ -214,7 +190,7 @@ namespace osu.Game.Rulesets.Osu.Mods return rotateVectorTowardsVector( posRelativeToPrev, Vector2.Subtract(playfieldMiddle, prevPosChanged), - relativeRotationDistance + relativeRotationDistance / 2 ); } @@ -230,10 +206,6 @@ namespace osu.Game.Rulesets.Osu.Mods var initialAngleRad = Math.Atan2(initial.Y, initial.X); var destAngleRad = Math.Atan2(destination.Y, destination.X); - // Divide by 2 to limit the max. angle to 90° - // (90° is enough to prevent the hit objects from leaving the playfield) - relativeDistance /= 2; - var diff = destAngleRad - initialAngleRad; while (diff < -Math.PI) @@ -246,46 +218,45 @@ namespace osu.Game.Rulesets.Osu.Mods diff -= 2 * Math.PI; } - var finalAngle = 0d; - - if (diff > 0) - { - finalAngle = initialAngleRad + relativeDistance * diff; - } - else if (diff < 0) - { - finalAngle = initialAngleRad + relativeDistance * diff; - } + var finalAngleRad = initialAngleRad + relativeDistance * diff; return new Vector2( - initial.Length * (float)Math.Cos(finalAngle), - initial.Length * (float)Math.Sin(finalAngle) + initial.Length * (float)Math.Cos(finalAngleRad), + initial.Length * (float)Math.Sin(finalAngleRad) ); } + + private struct HitObjectInfo + { + internal float AngleRad { get; set; } + internal Vector2 PosUnchanged { get; set; } + internal Vector2 PosChanged { get; set; } + } } - public class OsuModRandomSettingsControl : SettingsItem + public class OsuModRandomSettingsControl : SettingsItem { - [Resolved] - private static GameHost host { get; set; } - - [BackgroundDependencyLoader] - private void load(GameHost gameHost) => host = gameHost; - protected override Drawable CreateControl() => new SeedControl { RelativeSizeAxes = Axes.X, Margin = new MarginPadding { Top = 5 } }; - private sealed class SeedControl : CompositeDrawable, IHasCurrentValue + private sealed class SeedControl : CompositeDrawable, IHasCurrentValue { - private readonly BindableWithCurrent current = new BindableWithCurrent(); + [Resolved] + private GameHost host { get; set; } - public Bindable Current + private readonly BindableWithCurrent current = new BindableWithCurrent(); + + public Bindable Current { get => current; - set => Scheduler.Add(() => current.Current = value); + set + { + current.Current = value; + seedNumberBox.Text = value.Value.ToString(); + } } private readonly OsuNumberBox seedNumberBox; @@ -324,40 +295,29 @@ namespace osu.Game.Rulesets.Osu.Mods { RelativeSizeAxes = Axes.Both, Height = 1, - Text = "Copy", - Action = copySeedToClipboard + Text = "Paste", + Action = () => seedNumberBox.Text = host.GetClipboard().GetText() } } } } }; - seedNumberBox.Current.BindValueChanged(onTextBoxValueChanged); + seedNumberBox.Current.BindValueChanged(e => + { + int? value = null; + + if (int.TryParse(e.NewValue, out var intVal)) + value = intVal; + + current.Value = value; + }); } - private void onTextBoxValueChanged(ValueChangedEvent e) - { - string seed = e.NewValue; - - while (!string.IsNullOrEmpty(seed) && !int.TryParse(seed, out _)) - seed = seed[..^1]; - - if (!int.TryParse(seed, out var intVal)) - intVal = 0; - - current.Value = intVal; - } - - private void copySeedToClipboard() => host.GetClipboard().SetText(seedNumberBox.Text); - protected override void Update() { - seedNumberBox.ReadOnly = OsuModRandom.CustomSeedDisabled; - - if (seedNumberBox.HasFocus) - return; - - seedNumberBox.Text = current.Current.Value.ToString(); + if (current.Value == null) + seedNumberBox.Text = current.Current.Value.ToString(); } } } diff --git a/osu.Game/Rulesets/Mods/ModRandom.cs b/osu.Game/Rulesets/Mods/ModRandom.cs index da55ab3fbf..382792f75c 100644 --- a/osu.Game/Rulesets/Mods/ModRandom.cs +++ b/osu.Game/Rulesets/Mods/ModRandom.cs @@ -13,5 +13,6 @@ namespace osu.Game.Rulesets.Mods public override ModType Type => ModType.Conversion; public override IconUsage? Icon => OsuIcon.Dice; public override double ScoreMultiplier => 1; + public int? Seed { get; protected set; } } } diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index a6faaf6379..b584d24370 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -88,6 +88,24 @@ namespace osu.Game.Scoring } } + public bool ContainsModOfType(out T mod) + { + if (mods != null) + { + foreach (var currentMod in mods) + { + if (!(currentMod is T modOfType)) + continue; + + mod = modOfType; + return true; + } + } + + mod = default; + return false; + } + // Used for API serialisation/deserialisation. [JsonProperty("mods")] [NotMapped] diff --git a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs index 6a6b39b61c..4d81290a75 100644 --- a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs @@ -7,11 +7,13 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Localisation; +using osu.Framework.Platform; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Play.HUD; @@ -55,6 +57,9 @@ namespace osu.Game.Screens.Ranking.Expanded Padding = new MarginPadding(padding); } + [Resolved] + private GameHost host { get; set; } + [BackgroundDependencyLoader] private void load(BeatmapDifficultyCache beatmapDifficultyCache) { @@ -224,7 +229,47 @@ namespace osu.Game.Screens.Ranking.Expanded } } } - } + }.With(t => + { + if (!score.ContainsModOfType(out var mod) || mod.Seed == null) + return; + + t.Add(new GridContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Content = new[] + { + new Drawable[] + { + new OsuSpriteText + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Margin = new MarginPadding + { + Top = 3f, + Right = 5f + }, + Font = OsuFont.GetFont(size: 12, weight: FontWeight.Medium), + Text = $"Seed: {mod.Seed}" + }, + new TriangleButton + { + RelativeSizeAxes = Axes.Both, + Height = 1.2f, + Width = 0.5f, + Text = "Copy", + Action = () => host.GetClipboard().SetText(mod.Seed.ToString()) + } + } + }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + } + }); + }) } }, new OsuSpriteText diff --git a/osu.Game/Screens/Ranking/ScorePanel.cs b/osu.Game/Screens/Ranking/ScorePanel.cs index df710e4eb8..33b06571fe 100644 --- a/osu.Game/Screens/Ranking/ScorePanel.cs +++ b/osu.Game/Screens/Ranking/ScorePanel.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; +using osu.Game.Rulesets.Mods; using osu.Game.Scoring; using osu.Game.Screens.Ranking.Contracted; using osu.Game.Screens.Ranking.Expanded; @@ -206,7 +207,12 @@ namespace osu.Game.Screens.Ranking switch (state) { case PanelState.Expanded: - Size = new Vector2(EXPANDED_WIDTH, expanded_height); + var height = expanded_height; + + if (Score.ContainsModOfType(out var mod) && mod.Seed != null) + height += 20f; + + Size = new Vector2(EXPANDED_WIDTH, height); topLayerBackground.FadeColour(expanded_top_layer_colour, resize_duration, Easing.OutQuint); middleLayerBackground.FadeColour(expanded_middle_layer_colour, resize_duration, Easing.OutQuint); From 315a2b8314a044689eb607dfdced7bc24fc1f4c9 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 12 May 2021 20:50:20 +0300 Subject: [PATCH 150/839] Refactor MonthDropdown to decouple autosized logic --- .../Overlays/News/Sidebar/MonthDropdown.cs | 122 ++++++++++-------- 1 file changed, 69 insertions(+), 53 deletions(-) diff --git a/osu.Game/Overlays/News/Sidebar/MonthDropdown.cs b/osu.Game/Overlays/News/Sidebar/MonthDropdown.cs index 91412d9527..d6d7527a6c 100644 --- a/osu.Game/Overlays/News/Sidebar/MonthDropdown.cs +++ b/osu.Game/Overlays/News/Sidebar/MonthDropdown.cs @@ -20,85 +20,37 @@ namespace osu.Game.Overlays.News.Sidebar { public class MonthDropdown : CompositeDrawable { - private const int header_height = 15; private const int animation_duration = 250; public readonly BindableBool IsOpen = new BindableBool(); - private readonly FillFlowContainer postsFlow; - public MonthDropdown(int month, int year, IEnumerable posts) { Debug.Assert(posts.All(p => p.PublishedAt.Month == month && p.PublishedAt.Year == year)); RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; Masking = true; InternalChild = new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 5), Children = new Drawable[] { new DropdownHeader(month, year) { IsOpen = { BindTarget = IsOpen } }, - postsFlow = new FillFlowContainer + new PostsContainer { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 5), + IsOpen = { BindTarget = IsOpen }, Children = posts.Select(p => new PostButton(p)).ToArray() } } }; } - protected override void LoadComplete() - { - base.LoadComplete(); - - IsOpen.BindValueChanged(open => - { - ClearTransforms(); - - if (open.NewValue) - { - AutoSizeAxes = Axes.Y; - postsFlow.FadeIn(animation_duration, Easing.OutQuint); - } - else - { - AutoSizeAxes = Axes.None; - this.ResizeHeightTo(header_height, animation_duration, Easing.OutQuint); - - postsFlow.FadeOut(animation_duration, Easing.OutQuint); - } - }, true); - - // First state change should be instant. - FinishTransforms(true); - } - - private bool shouldUpdateAutosize = true; - - // Workaround to allow the dropdown to be opened immediately since FinishTransforms doesn't work for AutosizeDuration. - protected override void UpdateAfterAutoSize() - { - base.UpdateAfterAutoSize(); - - if (shouldUpdateAutosize) - { - AutoSizeDuration = animation_duration; - AutoSizeEasing = Easing.OutQuint; - - shouldUpdateAutosize = false; - } - } - private class DropdownHeader : OsuClickableContainer { public readonly BindableBool IsOpen = new BindableBool(); @@ -110,7 +62,7 @@ namespace osu.Game.Overlays.News.Sidebar var date = new DateTime(year, month, 1); RelativeSizeAxes = Axes.X; - Height = header_height; + Height = 15; Action = IsOpen.Toggle; Children = new Drawable[] { @@ -152,7 +104,6 @@ namespace osu.Game.Overlays.News.Sidebar { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; - Child = text = new TextFlowContainer(t => t.Font = OsuFont.GetFont(size: 12)) { RelativeSizeAxes = Axes.X, @@ -169,5 +120,70 @@ namespace osu.Game.Overlays.News.Sidebar Action = () => { }; // Avoid button being disabled since there's no proper action assigned. } } + + private class PostsContainer : Container + { + public readonly BindableBool IsOpen = new BindableBool(); + + protected override Container Content => content; + + private readonly FillFlowContainer content; + + public PostsContainer() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + InternalChild = content = new FillFlowContainer + { + Margin = new MarginPadding { Top = 5 }, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 5) + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + IsOpen.BindValueChanged(open => + { + ClearTransforms(); + + if (open.NewValue) + { + AutoSizeAxes = Axes.Y; + content.FadeIn(animation_duration, Easing.OutQuint); + } + else + { + AutoSizeAxes = Axes.None; + this.ResizeHeightTo(0, animation_duration, Easing.OutQuint); + + content.FadeOut(animation_duration, Easing.OutQuint); + } + }, true); + + // First state change should be instant. + FinishTransforms(true); + } + + private bool shouldUpdateAutosize = true; + + // Workaround to allow the dropdown to be opened immediately since FinishTransforms doesn't work for AutosizeDuration. + protected override void UpdateAfterAutoSize() + { + base.UpdateAfterAutoSize(); + + if (shouldUpdateAutosize) + { + AutoSizeDuration = animation_duration; + AutoSizeEasing = Easing.OutQuint; + + shouldUpdateAutosize = false; + } + } + } } } From 1ae57a6105b124bbfb39a8a2f9e980014385f6f0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 13 May 2021 20:11:04 +0900 Subject: [PATCH 151/839] Fix hold note input test Not sure why this was checking visibility. If it needs to be tested, it does not belong in an "Input" test. --- osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs index 668487f673..aa4eb77280 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs @@ -5,13 +5,11 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Screens; -using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Replays; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mania.Objects; -using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Replays; using osu.Game.Rulesets.Mania.Scoring; using osu.Game.Rulesets.Objects; @@ -414,13 +412,6 @@ namespace osu.Game.Rulesets.Mania.Tests AddUntilStep("Beatmap at 0", () => Beatmap.Value.Track.CurrentTime == 0); AddUntilStep("Wait until player is loaded", () => currentPlayer.IsCurrentScreen()); - AddUntilStep("wait for head", () => currentPlayer.GameplayClockContainer.GameplayClock.CurrentTime >= time_head); - AddAssert("head is visible", - () => currentPlayer.ChildrenOfType() - .Single(note => note.HitObject == beatmap.HitObjects[0]) - .Head - .Alpha == 1); - AddUntilStep("Wait for completion", () => currentPlayer.ScoreProcessor.HasCompleted.Value); } From 98e77a30d39910c35593ac89ea7369787b6b3fb6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 13 May 2021 20:13:50 +0900 Subject: [PATCH 152/839] Move column changing logic to ManiaSelectionHandler --- .../Edit/ManiaSelectionHandler.cs | 5 ++- osu.Game.Rulesets.Mania/UI/Stage.cs | 44 ++----------------- 2 files changed, 7 insertions(+), 42 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs index 7042110423..83fafbc9d1 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs @@ -59,8 +59,9 @@ namespace osu.Game.Rulesets.Mania.Edit EditorBeatmap.PerformOnSelection(h => { - if (h is ManiaHitObject maniaObj) - maniaObj.Column += columnDelta; + maniaPlayfield.Remove(h); + ((ManiaHitObject)h).Column += columnDelta; + maniaPlayfield.Add(h); }); } } diff --git a/osu.Game.Rulesets.Mania/UI/Stage.cs b/osu.Game.Rulesets.Mania/UI/Stage.cs index eee75f3200..8c703e7a8a 100644 --- a/osu.Game.Rulesets.Mania/UI/Stage.cs +++ b/osu.Game.Rulesets.Mania/UI/Stage.cs @@ -139,49 +139,13 @@ namespace osu.Game.Rulesets.Mania.UI NewResult += OnNewResult; } - public override void Add(HitObject hitObject) - { - var maniaObject = (ManiaHitObject)hitObject; - int columnIndex = -1; + public override void Add(HitObject hitObject) => Columns.ElementAt(((ManiaHitObject)hitObject).Column - firstColumnIndex).Add(hitObject); - maniaObject.ColumnBindable.BindValueChanged(_ => - { - if (columnIndex != -1) - Columns.ElementAt(columnIndex).Remove(hitObject); + public override bool Remove(HitObject hitObject) => Columns.ElementAt(((ManiaHitObject)hitObject).Column - firstColumnIndex).Remove(hitObject); - columnIndex = maniaObject.Column - firstColumnIndex; - Columns.ElementAt(columnIndex).Add(hitObject); - }, true); - } + public override void Add(DrawableHitObject h) => Columns.ElementAt(((ManiaHitObject)h.HitObject).Column - firstColumnIndex).Add(h); - public override bool Remove(HitObject hitObject) - { - var maniaObject = (ManiaHitObject)hitObject; - int columnIndex = maniaObject.Column - firstColumnIndex; - return Columns.ElementAt(columnIndex).Remove(hitObject); - } - - public override void Add(DrawableHitObject h) - { - var maniaObject = (ManiaHitObject)h.HitObject; - int columnIndex = -1; - - maniaObject.ColumnBindable.BindValueChanged(_ => - { - if (columnIndex != -1) - Columns.ElementAt(columnIndex).Remove(h); - - columnIndex = maniaObject.Column - firstColumnIndex; - Columns.ElementAt(columnIndex).Add(h); - }, true); - } - - public override bool Remove(DrawableHitObject h) - { - var maniaObject = (ManiaHitObject)h.HitObject; - int columnIndex = maniaObject.Column - firstColumnIndex; - return Columns.ElementAt(columnIndex).Remove(h); - } + public override bool Remove(DrawableHitObject h) => Columns.ElementAt(((ManiaHitObject)h.HitObject).Column - firstColumnIndex).Remove(h); public void Add(BarLine barline) => base.Add(new DrawableBarLine(barline)); From 86042e176387aaf259d1ac4a378058af74f4c6cb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 13 May 2021 20:56:38 +0900 Subject: [PATCH 153/839] Implement HitObjectContainerEventQueue --- .../Compose/HitObjectContainerEventQueue.cs | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 osu.Game/Screens/Edit/Compose/HitObjectContainerEventQueue.cs diff --git a/osu.Game/Screens/Edit/Compose/HitObjectContainerEventQueue.cs b/osu.Game/Screens/Edit/Compose/HitObjectContainerEventQueue.cs new file mode 100644 index 0000000000..b22d0a75e9 --- /dev/null +++ b/osu.Game/Screens/Edit/Compose/HitObjectContainerEventQueue.cs @@ -0,0 +1,102 @@ +// 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.Diagnostics; +using System.Linq; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Screens.Edit.Compose +{ + /// + /// A queue which processes events from the many s in a nested hierarchy. + /// + internal class HitObjectContainerEventQueue : Component + { + /// + /// Invoked when a becomes used by a . + /// + /// + /// If the ruleset uses pooled objects, this represents the time when the s become alive. + /// + public event Action HitObjectUsageBegan; + + /// + /// Invoked when a becomes unused by a . + /// + /// + /// If the ruleset uses pooled objects, this represents the time when the s become dead. + /// + public event Action HitObjectUsageFinished; + + /// + /// Invoked when a has been transferred to another . + /// + public event Action HitObjectUsageTransferred; + + private readonly Playfield playfield; + + /// + /// Creates a new . + /// + /// The most top-level . + public HitObjectContainerEventQueue(Playfield playfield) + { + this.playfield = playfield; + + bindPlayfieldRecursive(playfield); + } + + private void bindPlayfieldRecursive(Playfield p) + { + p.HitObjectContainer.HitObjectUsageBegan += onHitObjectUsageBegan; + p.HitObjectContainer.HitObjectUsageFinished += onHitObjectUsageFinished; + + foreach (var nested in p.NestedPlayfields) + bindPlayfieldRecursive(nested); + } + + private readonly Dictionary pendingUsagesBegan = new Dictionary(); + private readonly Dictionary pendingUsagesFinished = new Dictionary(); + + private void onHitObjectUsageBegan(HitObject hitObject) => pendingUsagesBegan[hitObject] = pendingUsagesBegan.GetValueOrDefault(hitObject, 0) + 1; + + private void onHitObjectUsageFinished(HitObject hitObject) => pendingUsagesFinished[hitObject] = pendingUsagesFinished.GetValueOrDefault(hitObject, 0) + 1; + + protected override void Update() + { + base.Update(); + + foreach (var (hitObject, countBegan) in pendingUsagesBegan) + { + if (pendingUsagesFinished.TryGetValue(hitObject, out int countFinished)) + { + Debug.Assert(countFinished > 0); + + if (countBegan > countFinished) + { + // The hitobject is still in use, but transferred to a different HOC. + HitObjectUsageTransferred?.Invoke(hitObject, playfield.AllHitObjects.Single(d => d.HitObject == hitObject)); + pendingUsagesFinished.Remove(hitObject); + } + } + else + { + // This is a new usage of the hitobject. + HitObjectUsageBegan?.Invoke(hitObject, playfield.AllHitObjects.Single(d => d.HitObject == hitObject)); + } + } + + // Go through any remaining pending finished usages. + foreach (var (hitObject, _) in pendingUsagesFinished) + HitObjectUsageFinished?.Invoke(hitObject); + + pendingUsagesBegan.Clear(); + pendingUsagesFinished.Clear(); + } + } +} From aaf31af32668d4fd9560c7645c6139a3e021b9c6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 13 May 2021 21:16:19 +0900 Subject: [PATCH 154/839] Add blueprint transferral --- .../Compose/Components/BlueprintContainer.cs | 7 +++++ .../Components/ComposeBlueprintContainer.cs | 9 +++++++ .../Components/EditorBlueprintContainer.cs | 18 +++++++------ .../Compose/HitObjectContainerEventQueue.cs | 27 ++++++++++--------- 4 files changed, 40 insertions(+), 21 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 361e98e0dd..951cc99c85 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -276,6 +276,13 @@ namespace osu.Game.Screens.Edit.Compose.Components { } + /// + /// Retrieves an item's blueprint. + /// + /// The item to retrieve the blueprint of. + /// The blueprint. + protected SelectionBlueprint GetBlueprintFor(T item) => blueprintMap[item]; + #endregion #region Selection diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs index 95f4069edb..e231f7f648 100644 --- a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs @@ -16,6 +16,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; using osu.Game.Screens.Edit.Components.TernaryButtons; using osuTK; @@ -73,6 +74,14 @@ namespace osu.Game.Screens.Edit.Compose.Components } } + protected override void TransferBlueprintFor(HitObject hitObject, DrawableHitObject drawableObject) + { + base.TransferBlueprintFor(hitObject, drawableObject); + + var blueprint = (HitObjectSelectionBlueprint)GetBlueprintFor(hitObject); + blueprint.DrawableObject = drawableObject; + } + protected override bool OnKeyDown(KeyDownEvent e) { if (e.ControlPressed) diff --git a/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs index db322faf65..063023c849 100644 --- a/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Drawables; namespace osu.Game.Screens.Edit.Compose.Components { @@ -65,8 +66,11 @@ namespace osu.Game.Screens.Edit.Compose.Components foreach (var obj in Composer.HitObjects) AddBlueprintFor(obj.HitObject); - Composer.Playfield.HitObjectUsageBegan += AddBlueprintFor; - Composer.Playfield.HitObjectUsageFinished += RemoveBlueprintFor; + var eventQueue = new HitObjectContainerEventQueue(Composer.Playfield); + eventQueue.HitObjectUsageBegan += AddBlueprintFor; + eventQueue.HitObjectUsageFinished += RemoveBlueprintFor; + eventQueue.HitObjectUsageTransferred += TransferBlueprintFor; + AddInternal(eventQueue); } } @@ -100,6 +104,10 @@ namespace osu.Game.Screens.Edit.Compose.Components base.AddBlueprintFor(item); } + protected virtual void TransferBlueprintFor(HitObject hitObject, DrawableHitObject drawableObject) + { + } + protected override void DragOperationCompleted() { base.DragOperationCompleted(); @@ -152,12 +160,6 @@ namespace osu.Game.Screens.Edit.Compose.Components Beatmap.HitObjectAdded -= AddBlueprintFor; Beatmap.HitObjectRemoved -= RemoveBlueprintFor; } - - if (Composer != null) - { - Composer.Playfield.HitObjectUsageBegan -= AddBlueprintFor; - Composer.Playfield.HitObjectUsageFinished -= RemoveBlueprintFor; - } } } } diff --git a/osu.Game/Screens/Edit/Compose/HitObjectContainerEventQueue.cs b/osu.Game/Screens/Edit/Compose/HitObjectContainerEventQueue.cs index b22d0a75e9..26f5a28113 100644 --- a/osu.Game/Screens/Edit/Compose/HitObjectContainerEventQueue.cs +++ b/osu.Game/Screens/Edit/Compose/HitObjectContainerEventQueue.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Graphics; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; @@ -23,7 +24,7 @@ namespace osu.Game.Screens.Edit.Compose /// /// If the ruleset uses pooled objects, this represents the time when the s become alive. /// - public event Action HitObjectUsageBegan; + public event Action HitObjectUsageBegan; /// /// Invoked when a becomes unused by a . @@ -44,20 +45,12 @@ namespace osu.Game.Screens.Edit.Compose /// Creates a new . /// /// The most top-level . - public HitObjectContainerEventQueue(Playfield playfield) + public HitObjectContainerEventQueue([NotNull] Playfield playfield) { this.playfield = playfield; - bindPlayfieldRecursive(playfield); - } - - private void bindPlayfieldRecursive(Playfield p) - { - p.HitObjectContainer.HitObjectUsageBegan += onHitObjectUsageBegan; - p.HitObjectContainer.HitObjectUsageFinished += onHitObjectUsageFinished; - - foreach (var nested in p.NestedPlayfields) - bindPlayfieldRecursive(nested); + playfield.HitObjectUsageBegan += onHitObjectUsageBegan; + playfield.HitObjectUsageFinished += onHitObjectUsageFinished; } private readonly Dictionary pendingUsagesBegan = new Dictionary(); @@ -87,7 +80,7 @@ namespace osu.Game.Screens.Edit.Compose else { // This is a new usage of the hitobject. - HitObjectUsageBegan?.Invoke(hitObject, playfield.AllHitObjects.Single(d => d.HitObject == hitObject)); + HitObjectUsageBegan?.Invoke(hitObject); } } @@ -98,5 +91,13 @@ namespace osu.Game.Screens.Edit.Compose pendingUsagesBegan.Clear(); pendingUsagesFinished.Clear(); } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + playfield.HitObjectUsageBegan -= onHitObjectUsageBegan; + playfield.HitObjectUsageFinished -= onHitObjectUsageFinished; + } } } From 362a09ca73d491890698e1f84c6de2239ee6b129 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 13 May 2021 21:41:18 +0900 Subject: [PATCH 155/839] Fix up + reduce complexity of HOCEventQueue --- .../Compose/HitObjectContainerEventQueue.cs | 70 ++++++++++++------- 1 file changed, 46 insertions(+), 24 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/HitObjectContainerEventQueue.cs b/osu.Game/Screens/Edit/Compose/HitObjectContainerEventQueue.cs index 26f5a28113..7c21573b18 100644 --- a/osu.Game/Screens/Edit/Compose/HitObjectContainerEventQueue.cs +++ b/osu.Game/Screens/Edit/Compose/HitObjectContainerEventQueue.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using JetBrains.Annotations; using osu.Framework.Graphics; @@ -53,43 +52,59 @@ namespace osu.Game.Screens.Edit.Compose playfield.HitObjectUsageFinished += onHitObjectUsageFinished; } - private readonly Dictionary pendingUsagesBegan = new Dictionary(); - private readonly Dictionary pendingUsagesFinished = new Dictionary(); + private readonly Dictionary pendingEvents = new Dictionary(); - private void onHitObjectUsageBegan(HitObject hitObject) => pendingUsagesBegan[hitObject] = pendingUsagesBegan.GetValueOrDefault(hitObject, 0) + 1; + private void onHitObjectUsageBegan(HitObject hitObject) => updateEvent(hitObject, EventType.Began); - private void onHitObjectUsageFinished(HitObject hitObject) => pendingUsagesFinished[hitObject] = pendingUsagesFinished.GetValueOrDefault(hitObject, 0) + 1; + private void onHitObjectUsageFinished(HitObject hitObject) => updateEvent(hitObject, EventType.Finished); + + private void updateEvent(HitObject hitObject, EventType newEvent) + { + if (!pendingEvents.TryGetValue(hitObject, out EventType existingEvent)) + { + pendingEvents[hitObject] = newEvent; + return; + } + + switch (existingEvent, newEvent) + { + case (EventType.Transferred, EventType.Finished): + pendingEvents[hitObject] = EventType.Finished; + break; + + case (EventType.Began, EventType.Finished): + case (EventType.Finished, EventType.Began): + pendingEvents[hitObject] = EventType.Transferred; + break; + + default: + throw new ArgumentOutOfRangeException($"Unexpected event update ({existingEvent} => {newEvent})."); + } + } protected override void Update() { base.Update(); - foreach (var (hitObject, countBegan) in pendingUsagesBegan) + foreach (var (hitObject, e) in pendingEvents) { - if (pendingUsagesFinished.TryGetValue(hitObject, out int countFinished)) + switch (e) { - Debug.Assert(countFinished > 0); + case EventType.Began: + HitObjectUsageBegan?.Invoke(hitObject); + break; - if (countBegan > countFinished) - { - // The hitobject is still in use, but transferred to a different HOC. + case EventType.Transferred: HitObjectUsageTransferred?.Invoke(hitObject, playfield.AllHitObjects.Single(d => d.HitObject == hitObject)); - pendingUsagesFinished.Remove(hitObject); - } - } - else - { - // This is a new usage of the hitobject. - HitObjectUsageBegan?.Invoke(hitObject); + break; + + case EventType.Finished: + HitObjectUsageFinished?.Invoke(hitObject); + break; } } - // Go through any remaining pending finished usages. - foreach (var (hitObject, _) in pendingUsagesFinished) - HitObjectUsageFinished?.Invoke(hitObject); - - pendingUsagesBegan.Clear(); - pendingUsagesFinished.Clear(); + pendingEvents.Clear(); } protected override void Dispose(bool isDisposing) @@ -99,5 +114,12 @@ namespace osu.Game.Screens.Edit.Compose playfield.HitObjectUsageBegan -= onHitObjectUsageBegan; playfield.HitObjectUsageFinished -= onHitObjectUsageFinished; } + + private enum EventType + { + Began, + Finished, + Transferred + } } } From 38c0ba2d1034b756f5d6e8b1f007afee44c9e648 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 13 May 2021 16:16:19 +0300 Subject: [PATCH 156/839] Implement current year highlight in YearsPanel --- osu.Game/Overlays/News/Sidebar/YearsPanel.cs | 39 +++++++++++++------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/osu.Game/Overlays/News/Sidebar/YearsPanel.cs b/osu.Game/Overlays/News/Sidebar/YearsPanel.cs index 2a4e2024d2..2d405e832b 100644 --- a/osu.Game/Overlays/News/Sidebar/YearsPanel.cs +++ b/osu.Game/Overlays/News/Sidebar/YearsPanel.cs @@ -12,6 +12,7 @@ using osu.Game.Graphics; using osu.Framework.Bindables; using osu.Game.Online.API.Requests.Responses; using System; +using osuTK.Graphics; namespace osu.Game.Overlays.News.Sidebar { @@ -58,7 +59,7 @@ namespace osu.Game.Overlays.News.Sidebar return; } - gridPlaceholder.Child = new YearsGridContainer(m.NewValue.Years); + gridPlaceholder.Child = new YearsGridContainer(m.NewValue.Years, m.NewValue.CurrentYear); Show(); }, true); } @@ -68,16 +69,19 @@ namespace osu.Game.Overlays.News.Sidebar protected override IEnumerable EffectTargets => new[] { text }; private readonly OsuSpriteText text; + private readonly bool isCurrent; - public YearButton(int year) + public YearButton(int year, bool isCurrent) { + this.isCurrent = isCurrent; + RelativeSizeAxes = Axes.X; Height = 15; Child = text = new OsuSpriteText { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Font = OsuFont.GetFont(size: 12), + Font = OsuFont.GetFont(size: 12, weight: isCurrent ? FontWeight.SemiBold : FontWeight.Medium), Text = year.ToString() }; } @@ -85,8 +89,8 @@ namespace osu.Game.Overlays.News.Sidebar [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider) { - IdleColour = colourProvider.Light2; - HoverColour = colourProvider.Light1; + IdleColour = isCurrent ? Color4.White : colourProvider.Light2; + HoverColour = isCurrent ? Color4.White : colourProvider.Light1; Action = () => { }; // Avoid button being disabled since there's no proper action assigned. } } @@ -97,18 +101,16 @@ namespace osu.Game.Overlays.News.Sidebar private const float spacing = 5f; private readonly int rowCount; - private readonly int[] years; - public YearsGridContainer(int[] years) + public YearsGridContainer(int[] years, int currentYear) { - this.years = years; rowCount = (int)Math.Ceiling((float)years.Length / column_count); RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; RowDimensions = getRowDimensions(); ColumnDimensions = getColumnDimensions(); - Content = createContent(); + Content = createContent(years, currentYear); } private Dimension[] getRowDimensions() @@ -129,7 +131,7 @@ namespace osu.Game.Overlays.News.Sidebar return columnDimensions; } - private Drawable[][] createContent() + private Drawable[][] createContent(int[] years, int currentYear) { var buttons = new Drawable[rowCount][]; @@ -140,9 +142,17 @@ namespace osu.Game.Overlays.News.Sidebar for (int j = 0; j < column_count; j++) { var index = i * column_count + j; - buttons[i][j] = index >= years.Length - ? Empty() - : new Container + + if (index >= years.Length) + { + buttons[i][j] = Empty(); + } + else + { + var year = years[index]; + var isCurrent = year == currentYear; + + buttons[i][j] = new Container { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, @@ -153,8 +163,9 @@ namespace osu.Game.Overlays.News.Sidebar Left = j == 0 ? 0 : spacing / 2, Right = j == column_count - 1 ? 0 : spacing / 2 }, - Child = new YearButton(years[index]) + Child = new YearButton(year, isCurrent) }; + } } } From a9d5211e81593f30be9f7d7c5a91218a52b47a2a Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Fri, 14 May 2021 01:42:39 +0200 Subject: [PATCH 157/839] Remove seed from the ScorePanel and "Paste" button --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 15 ------ osu.Game/Rulesets/Mods/ModRandom.cs | 1 - osu.Game/Scoring/ScoreInfo.cs | 18 ------- .../Expanded/ExpandedPanelMiddleContent.cs | 47 +------------------ osu.Game/Screens/Ranking/ScorePanel.cs | 8 +--- 5 files changed, 2 insertions(+), 87 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 2d7c52d535..1b8f8ee7f5 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -2,12 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using System; -using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; -using osu.Framework.Platform; using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Configuration; @@ -52,8 +50,6 @@ namespace osu.Game.Rulesets.Osu.Mods if (CustomSeed.Value != null) seed = (int)CustomSeed.Value; - Seed = seed; - var rng = new Random(seed); var prevObjectInfo = new HitObjectInfo @@ -244,9 +240,6 @@ namespace osu.Game.Rulesets.Osu.Mods private sealed class SeedControl : CompositeDrawable, IHasCurrentValue { - [Resolved] - private GameHost host { get; set; } - private readonly BindableWithCurrent current = new BindableWithCurrent(); public Bindable Current @@ -289,14 +282,6 @@ namespace osu.Game.Rulesets.Osu.Mods { RelativeSizeAxes = Axes.X, CommitOnFocusLost = true - }, - null, - new TriangleButton - { - RelativeSizeAxes = Axes.Both, - Height = 1, - Text = "Paste", - Action = () => seedNumberBox.Text = host.GetClipboard().GetText() } } } diff --git a/osu.Game/Rulesets/Mods/ModRandom.cs b/osu.Game/Rulesets/Mods/ModRandom.cs index 382792f75c..da55ab3fbf 100644 --- a/osu.Game/Rulesets/Mods/ModRandom.cs +++ b/osu.Game/Rulesets/Mods/ModRandom.cs @@ -13,6 +13,5 @@ namespace osu.Game.Rulesets.Mods public override ModType Type => ModType.Conversion; public override IconUsage? Icon => OsuIcon.Dice; public override double ScoreMultiplier => 1; - public int? Seed { get; protected set; } } } diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index b584d24370..a6faaf6379 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -88,24 +88,6 @@ namespace osu.Game.Scoring } } - public bool ContainsModOfType(out T mod) - { - if (mods != null) - { - foreach (var currentMod in mods) - { - if (!(currentMod is T modOfType)) - continue; - - mod = modOfType; - return true; - } - } - - mod = default; - return false; - } - // Used for API serialisation/deserialisation. [JsonProperty("mods")] [NotMapped] diff --git a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs index 4d81290a75..6a6b39b61c 100644 --- a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs @@ -7,13 +7,11 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Localisation; -using osu.Framework.Platform; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; -using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Play.HUD; @@ -57,9 +55,6 @@ namespace osu.Game.Screens.Ranking.Expanded Padding = new MarginPadding(padding); } - [Resolved] - private GameHost host { get; set; } - [BackgroundDependencyLoader] private void load(BeatmapDifficultyCache beatmapDifficultyCache) { @@ -229,47 +224,7 @@ namespace osu.Game.Screens.Ranking.Expanded } } } - }.With(t => - { - if (!score.ContainsModOfType(out var mod) || mod.Seed == null) - return; - - t.Add(new GridContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Content = new[] - { - new Drawable[] - { - new OsuSpriteText - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Margin = new MarginPadding - { - Top = 3f, - Right = 5f - }, - Font = OsuFont.GetFont(size: 12, weight: FontWeight.Medium), - Text = $"Seed: {mod.Seed}" - }, - new TriangleButton - { - RelativeSizeAxes = Axes.Both, - Height = 1.2f, - Width = 0.5f, - Text = "Copy", - Action = () => host.GetClipboard().SetText(mod.Seed.ToString()) - } - } - }, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize), - } - }); - }) + } } }, new OsuSpriteText diff --git a/osu.Game/Screens/Ranking/ScorePanel.cs b/osu.Game/Screens/Ranking/ScorePanel.cs index 33b06571fe..df710e4eb8 100644 --- a/osu.Game/Screens/Ranking/ScorePanel.cs +++ b/osu.Game/Screens/Ranking/ScorePanel.cs @@ -10,7 +10,6 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; -using osu.Game.Rulesets.Mods; using osu.Game.Scoring; using osu.Game.Screens.Ranking.Contracted; using osu.Game.Screens.Ranking.Expanded; @@ -207,12 +206,7 @@ namespace osu.Game.Screens.Ranking switch (state) { case PanelState.Expanded: - var height = expanded_height; - - if (Score.ContainsModOfType(out var mod) && mod.Seed != null) - height += 20f; - - Size = new Vector2(EXPANDED_WIDTH, height); + Size = new Vector2(EXPANDED_WIDTH, expanded_height); topLayerBackground.FadeColour(expanded_top_layer_colour, resize_duration, Easing.OutQuint); middleLayerBackground.FadeColour(expanded_middle_layer_colour, resize_duration, Easing.OutQuint); From ac04e8afa28a8bc5041176f0c59f8c77a109d2f2 Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Fri, 14 May 2021 01:50:11 +0200 Subject: [PATCH 158/839] Change name of option "Custom seed" to "Seed" and set its value to the generated seed --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 1b8f8ee7f5..1141de58a2 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -33,8 +33,8 @@ namespace osu.Game.Rulesets.Osu.Mods private const byte border_distance_x = 192; private const byte border_distance_y = 144; - [SettingSource("Custom seed", "Use a custom seed instead of a random one", SettingControlType = typeof(OsuModRandomSettingsControl))] - public Bindable CustomSeed { get; } = new Bindable + [SettingSource("Seed", "Use a custom seed instead of a random one", SettingControlType = typeof(OsuModRandomSettingsControl))] + public Bindable Seed { get; } = new Bindable { Default = null, Value = null @@ -45,12 +45,9 @@ namespace osu.Game.Rulesets.Osu.Mods if (!(beatmap is OsuBeatmap osuBeatmap)) return; - var seed = RNG.Next(); + Seed.Value ??= RNG.Next(); - if (CustomSeed.Value != null) - seed = (int)CustomSeed.Value; - - var rng = new Random(seed); + var rng = new Random((int)Seed.Value); var prevObjectInfo = new HitObjectInfo { From dbc23187105279560e8a7bb26d206a450cf4604d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 May 2021 14:13:35 +0900 Subject: [PATCH 159/839] Initial tidying up --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 29 +++++++++------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 1141de58a2..dc194aa464 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -45,6 +45,8 @@ namespace osu.Game.Rulesets.Osu.Mods if (!(beatmap is OsuBeatmap osuBeatmap)) return; + var hitObjects = osuBeatmap.HitObjects; + Seed.Value ??= RNG.Next(); var rng = new Random((int)Seed.Value); @@ -52,30 +54,28 @@ namespace osu.Game.Rulesets.Osu.Mods var prevObjectInfo = new HitObjectInfo { AngleRad = 0, - PosUnchanged = osuBeatmap.HitObjects[0].Position, - PosChanged = osuBeatmap.HitObjects[0].Position + PosUnchanged = hitObjects[0].Position, + PosChanged = hitObjects[0].Position }; - // rateOfChangeMultiplier changes every i iterations to prevent shaky-line-shaped streams - byte i = 3; float rateOfChangeMultiplier = 0; - foreach (var currentHitObject in osuBeatmap.HitObjects) + for (int i = 0; i < hitObjects.Count; i++) { + var h = hitObjects[i]; + var currentObjectInfo = new HitObjectInfo { AngleRad = 0, - PosUnchanged = currentHitObject.EndPosition, + PosUnchanged = h.EndPosition, PosChanged = Vector2.Zero }; - if (i >= 3) - { - i = 0; + // rateOfChangeMultiplier only changes every i iterations to prevent shaky-line-shaped streams + if (i % 3 == 0) rateOfChangeMultiplier = (float)rng.NextDouble() * 2 - 1; - } - if (currentHitObject is HitCircle circle) + if (h is HitCircle circle) { var distanceToPrev = Vector2.Distance(currentObjectInfo.PosUnchanged, prevObjectInfo.PosUnchanged); @@ -92,7 +92,6 @@ namespace osu.Game.Rulesets.Osu.Mods // TODO: Implement slider position randomisation prevObjectInfo = currentObjectInfo; - i++; } } @@ -100,11 +99,7 @@ namespace osu.Game.Rulesets.Osu.Mods /// Returns the final position of the hit object /// /// Final position of the hit object - private void getObjectInfo( - float rateOfChangeMultiplier, - HitObjectInfo prevObjectInfo, - float distanceToPrev, - ref HitObjectInfo currentObjectInfo) + private void getObjectInfo(float rateOfChangeMultiplier, HitObjectInfo prevObjectInfo, float distanceToPrev, ref HitObjectInfo currentObjectInfo) { // The max. angle (relative to the angle of the vector pointing from the 2nd last to the last hit object) // is proportional to the distance between the last and the current hit object From 0725088fdef8cd2aef018e78c8f29f74153a860f Mon Sep 17 00:00:00 2001 From: Swords Date: Sat, 15 May 2021 01:01:17 +1000 Subject: [PATCH 160/839] Well it works, just pretty ugly looking. --- .../Settings/TestSceneKeyBindingPanel.cs | 69 +++++++--- .../Visual/Settings/TestSceneSettingsItem.cs | 5 +- osu.Game/Overlays/KeyBinding/KeyBindingRow.cs | 76 ++++++----- .../KeyBinding/KeyBindingsSubsection.cs | 13 +- .../KeyBinding/SettingsKeyBindingRow.cs | 70 +++++++++++ .../Overlays/RestoreDefaultValueButton.cs | 119 ++++++++++++++++++ osu.Game/Overlays/Settings/SettingsItem.cs | 103 +-------------- 7 files changed, 299 insertions(+), 156 deletions(-) create mode 100644 osu.Game/Overlays/KeyBinding/SettingsKeyBindingRow.cs create mode 100644 osu.Game/Overlays/RestoreDefaultValueButton.cs diff --git a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs index 41b65e84b6..09c8696ee0 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Linq; using NUnit.Framework; +using osu.Framework.Input.Bindings; using osu.Framework.Testing; using osu.Framework.Threading; using osu.Game.Overlays; @@ -31,13 +32,16 @@ namespace osu.Game.Tests.Visual.Settings [Test] public void TestClickTwiceOnClearButton() { + SettingsKeyBindingRow firstSettingRow = null; KeyBindingRow firstRow = null; AddStep("click first row", () => { - firstRow = panel.ChildrenOfType().First(); - InputManager.MoveMouseTo(firstRow); + firstSettingRow = panel.ChildrenOfType().First(); + InputManager.MoveMouseTo(firstSettingRow); InputManager.Click(MouseButton.Left); + + firstRow = firstSettingRow.KeyBindingRow; }); AddStep("schedule button clicks", () => @@ -71,7 +75,7 @@ namespace osu.Game.Tests.Visual.Settings AddStep("click first row with two bindings", () => { - multiBindingRow = panel.ChildrenOfType().First(row => row.Defaults.Count() > 1); + multiBindingRow = panel.ChildrenOfType().First(row => row.KeyBindingRow.Defaults.Count() > 1).KeyBindingRow; InputManager.MoveMouseTo(multiBindingRow); InputManager.Click(MouseButton.Left); }); @@ -105,38 +109,67 @@ namespace osu.Game.Tests.Visual.Settings } [Test] - public void TestResetButtonOnBindings() + public void TestSingleBindResetButton() { + SettingsKeyBindingRow multiSettingsBindingRow = null; KeyBindingRow multiBindingRow = null; AddStep("click first row with two bindings", () => { + multiSettingsBindingRow = panel.ChildrenOfType().First(row => row.KeyBindingRow.Defaults.Count() > 1); multiBindingRow = panel.ChildrenOfType().First(row => row.Defaults.Count() > 1); InputManager.MoveMouseTo(multiBindingRow); InputManager.Click(MouseButton.Left); + InputManager.PressKey(Key.P); + InputManager.ReleaseKey(Key.P); }); + AddUntilStep("restore button shown", () => multiSettingsBindingRow.ChildrenOfType>().First().Alpha > 0); + clickSingleBindResetButton(); AddAssert("first binding cleared", () => multiBindingRow.ChildrenOfType().ElementAt(0).KeyBinding.KeyCombination.Equals(multiBindingRow.Defaults.ElementAt(0))); - - AddStep("click second binding", () => - { - var target = multiBindingRow.ChildrenOfType().ElementAt(1); - - InputManager.MoveMouseTo(target); - InputManager.Click(MouseButton.Left); - }); - - clickSingleBindResetButton(); - AddAssert("second binding cleared", () => multiBindingRow.ChildrenOfType().ElementAt(1).KeyBinding.KeyCombination.Equals(multiBindingRow.Defaults.ElementAt(1))); + AddUntilStep("restore button hidden", () => multiSettingsBindingRow.ChildrenOfType>().First().Alpha == 0); + void clickSingleBindResetButton() { - AddStep("click reset button for single binding", () => + AddStep("click reset button for bindings", () => { - var clearButton = multiBindingRow.ChildrenOfType().Single(); + var clearButton = multiSettingsBindingRow.ChildrenOfType>().Single(); + + InputManager.MoveMouseTo(clearButton); + InputManager.Click(MouseButton.Left); + }); + } + } + + [Test] + public void TestResetAllBindingsButton() + { + SettingsKeyBindingRow multiSettingsBindingRow = null; + KeyBindingRow multiBindingRow = null; + + AddStep("click first row and press p", () => + { + multiSettingsBindingRow = panel.ChildrenOfType().First(row => row.KeyBindingRow.Defaults.Count() > 1); + multiBindingRow = panel.ChildrenOfType().First(); + InputManager.MoveMouseTo(multiBindingRow); + InputManager.Click(MouseButton.Left); + InputManager.PressKey(Key.P); + InputManager.ReleaseKey(Key.P); + }); + + clickResetAllBindingsButton(); + + AddAssert("bindings cleared", () => multiBindingRow.ChildrenOfType().ElementAt(0).KeyBinding.KeyCombination.Equals(multiBindingRow.Defaults.ElementAt(0))); + + void clickResetAllBindingsButton() + { + AddStep("click reset button for all bindings", () => + { + var clearButton = panel.ChildrenOfType().First(); InputManager.MoveMouseTo(clearButton); InputManager.Click(MouseButton.Left); @@ -176,4 +209,4 @@ namespace osu.Game.Tests.Visual.Settings AddAssert("first binding selected", () => multiBindingRow.ChildrenOfType().First().IsBinding); } } -} +} \ No newline at end of file diff --git a/osu.Game.Tests/Visual/Settings/TestSceneSettingsItem.cs b/osu.Game.Tests/Visual/Settings/TestSceneSettingsItem.cs index 8f1c17ed29..f63145f534 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneSettingsItem.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneSettingsItem.cs @@ -7,6 +7,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Testing; using osu.Game.Overlays.Settings; +using osu.Game.Overlays; namespace osu.Game.Tests.Visual.Settings { @@ -37,7 +38,7 @@ namespace osu.Game.Tests.Visual.Settings private class TestSettingsTextBox : SettingsTextBox { - public new Drawable RestoreDefaultValueButton => this.ChildrenOfType().Single(); + public Drawable RestoreDefaultValueButton => this.ChildrenOfType>().Single(); } } -} +} \ No newline at end of file diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs index 1fbaa374d4..4d2738f244 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs @@ -4,12 +4,14 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Game.Graphics; @@ -22,7 +24,7 @@ using osuTK.Input; namespace osu.Game.Overlays.KeyBinding { - public class KeyBindingRow : Container, IFilterable + public class KeyBindingRow : Container, IFilterable, IHasCurrentValue { private readonly object action; private readonly IEnumerable bindings; @@ -51,6 +53,13 @@ namespace osu.Game.Overlays.KeyBinding private FillFlowContainer cancelAndClearButtons; private FillFlowContainer buttons; + private BindableWithCurrent isKeysDefaultValue; + public Bindable Current + { + get => isKeysDefaultValue.Current; + set => isKeysDefaultValue.Current = value; + } + public IEnumerable FilterTerms => bindings.Select(b => b.KeyCombination.ReadableString()).Prepend(text.Text.ToString()); public KeyBindingRow(object action, IEnumerable bindings) @@ -58,6 +67,11 @@ namespace osu.Game.Overlays.KeyBinding this.action = action; this.bindings = bindings; + isKeysDefaultValue = new BindableWithCurrent() + { + Default = true + }; + RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; @@ -71,6 +85,17 @@ namespace osu.Game.Overlays.KeyBinding [BackgroundDependencyLoader] private void load(OsuColour colours) { + + isKeysDefaultValue.Value = bindings.Select(b => b.KeyCombination).SequenceEqual(Defaults); + isKeysDefaultValue.BindValueChanged(resetButtons => + { + if (resetButtons.NewValue != resetButtons.OldValue && resetButtons.NewValue && !bindings.Select(b => b.KeyCombination).SequenceEqual(Defaults)) + { + RestoreDefaults(); + finalise(); + } + }); + EdgeEffect = new EdgeEffectParameters { Radius = 2, @@ -109,7 +134,6 @@ namespace osu.Game.Overlays.KeyBinding Children = new Drawable[] { new CancelButton { Action = finalise }, - new SingleBindResetButton { Action = singleBindReset }, new ClearButton { Action = clear }, }, } @@ -129,6 +153,8 @@ namespace osu.Game.Overlays.KeyBinding button.UpdateKeyCombination(d); store.Update(button.KeyBinding); } + + isKeysDefaultValue.Value = true; } protected override bool OnHover(HoverEvent e) @@ -282,21 +308,29 @@ namespace osu.Game.Overlays.KeyBinding finalise(); } - private void singleBindReset() - { - if (bindTarget == null) - return; - - bindTarget.UpdateKeyCombination(Defaults.ElementAt(buttons.IndexOf(bindTarget))); - finalise(); - } - private void finalise() { if (bindTarget != null) { store.Update(bindTarget.KeyBinding); + KeyCombination keyDefault = Defaults.ElementAt(buttons.IndexOf(bindTarget)); + if (isKeysDefaultValue.Value) + { + if (!keyDefault.Equals(bindTarget.KeyBinding.KeyCombination)) + { + isKeysDefaultValue.Value = false; + } + } + else + { + if (keyDefault.Equals(bindTarget.KeyBinding.KeyCombination) && + buttons.Select(b => b.KeyBinding.KeyCombination).SequenceEqual(Defaults)) + { + isKeysDefaultValue.Value = true; + } + } + bindTarget.IsBinding = false; Schedule(() => { @@ -349,24 +383,6 @@ namespace osu.Game.Overlays.KeyBinding } } - public class SingleBindResetButton : TriangleButton - { - public SingleBindResetButton() - { - Text = "Reset"; - Size = new Vector2(80, 20); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - BackgroundColour = colours.Green; - - Triangles.ColourDark = colours.GreenDark; - Triangles.ColourLight = colours.GreenLight; - } - } - public class ClearButton : TriangleButton { public ClearButton() @@ -487,4 +503,4 @@ namespace osu.Game.Overlays.KeyBinding } } } -} +} \ No newline at end of file diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs b/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs index d784b7aec9..d130a3f536 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs @@ -1,17 +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 System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Input; using osu.Game.Overlays.Settings; using osu.Game.Rulesets; using osuTK; using osu.Game.Graphics; +using osu.Framework.Input.Bindings; namespace osu.Game.Overlays.KeyBinding { @@ -41,16 +44,12 @@ namespace osu.Game.Overlays.KeyBinding int intKey = (int)defaultGroup.Key; // one row per valid action. - Add(new KeyBindingRow(defaultGroup.Key, bindings.Where(b => ((int)b.Action).Equals(intKey))) - { - AllowMainMouseButtons = Ruleset != null, - Defaults = defaultGroup.Select(d => d.KeyCombination) - }); + Add(new SettingsKeyBindingRow(defaultGroup, bindings, Ruleset)); } Add(new ResetButton { - Action = () => Children.OfType().ForEach(k => k.RestoreDefaults()) + Action = () => Children.OfType().ForEach(k => k.KeyBindingRow.RestoreDefaults()) }); } } @@ -72,4 +71,4 @@ namespace osu.Game.Overlays.KeyBinding Triangles.ColourLight = colours.Pink; } } -} +} \ No newline at end of file diff --git a/osu.Game/Overlays/KeyBinding/SettingsKeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/SettingsKeyBindingRow.cs new file mode 100644 index 0000000000..da8a628b51 --- /dev/null +++ b/osu.Game/Overlays/KeyBinding/SettingsKeyBindingRow.cs @@ -0,0 +1,70 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets; + +namespace osu.Game.Overlays.KeyBinding +{ + public class SettingsKeyBindingRow : Container, IFilterable + { + private readonly IGrouping defaultGroup; + private readonly IEnumerable bindings; + public readonly KeyBindingRow KeyBindingRow; + + private bool matchingFilter; + + public bool MatchingFilter + { + get => matchingFilter; + set + { + matchingFilter = value; + this.FadeTo(!matchingFilter ? 0 : 1); + } + } + + public bool FilteringActive { get; set; } + + public IEnumerable FilterTerms => bindings.Select(b => b.KeyCombination.ReadableString()).Prepend(defaultGroup.Key.ToString()); + + public SettingsKeyBindingRow( + IGrouping defaultGroup, + IEnumerable bindings, + RulesetInfo ruleset) + { + this.defaultGroup = defaultGroup; + this.bindings = bindings; + + KeyBindingRow = new KeyBindingRow(defaultGroup.Key, bindings.Where(b => ((int)b.Action).Equals((int)defaultGroup.Key))) + { + AllowMainMouseButtons = ruleset != null, + Defaults = defaultGroup.Select(d => d.KeyCombination) + }; + + + RestoreDefaultValueButton restoreDefaultButton; + + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Padding = new MarginPadding { Right = SettingsPanel.CONTENT_MARGINS }; + + InternalChildren = new Drawable[] + { + restoreDefaultButton = new RestoreDefaultValueButton(), + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Left = SettingsPanel.CONTENT_MARGINS }, + Child = KeyBindingRow + }, + }; + + restoreDefaultButton.Bindable = KeyBindingRow.Current; + } + } +} \ No newline at end of file diff --git a/osu.Game/Overlays/RestoreDefaultValueButton.cs b/osu.Game/Overlays/RestoreDefaultValueButton.cs new file mode 100644 index 0000000000..6d6616a893 --- /dev/null +++ b/osu.Game/Overlays/RestoreDefaultValueButton.cs @@ -0,0 +1,119 @@ +// 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 osuTK.Graphics; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Effects; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; +using osu.Game.Graphics; +using osuTK; + +namespace osu.Game.Overlays +{ + public class RestoreDefaultValueButton : Container, IHasTooltip + { + public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks; + + private Bindable bindable; + + public Bindable Bindable + { + get => bindable; + set + { + bindable = value; + bindable.ValueChanged += _ => UpdateState(); + bindable.DisabledChanged += _ => UpdateState(); + bindable.DefaultChanged += _ => UpdateState(); + UpdateState(); + } + } + + private Color4 buttonColour; + + private bool hovering; + + public RestoreDefaultValueButton() + { + RelativeSizeAxes = Axes.Y; + Width = SettingsPanel.CONTENT_MARGINS; + Alpha = 0f; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colour) + { + buttonColour = colour.Yellow; + + Child = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + CornerRadius = 3, + Masking = true, + Colour = buttonColour, + EdgeEffect = new EdgeEffectParameters + { + Colour = buttonColour.Opacity(0.1f), + Type = EdgeEffectType.Glow, + Radius = 2, + }, + Size = new Vector2(0.33f, 0.8f), + Child = new Box { RelativeSizeAxes = Axes.Both }, + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + UpdateState(); + } + + public string TooltipText => "revert to default"; + + protected override bool OnClick(ClickEvent e) + { + if (bindable != null && !bindable.Disabled) + bindable.SetDefault(); + return true; + } + + protected override bool OnHover(HoverEvent e) + { + hovering = true; + UpdateState(); + return false; + } + + protected override void OnHoverLost(HoverLostEvent e) + { + hovering = false; + UpdateState(); + } + + public void SetButtonColour(Color4 buttonColour) + { + this.buttonColour = buttonColour; + UpdateState(); + } + + public void UpdateState() => Scheduler.AddOnce(updateState); + + private void updateState() + { + if (bindable == null) + return; + + this.FadeTo(bindable.IsDefault ? 0f : + hovering && !bindable.Disabled ? 1f : 0.65f, 200, Easing.OutQuint); + this.FadeColour(bindable.Disabled ? Color4.Gray : buttonColour, 200, Easing.OutQuint); + } + } +} \ No newline at end of file diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 86a836d29b..7677931fde 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -108,7 +108,8 @@ namespace osu.Game.Overlays.Settings protected SettingsItem() { - RestoreDefaultValueButton restoreDefaultButton; + + RestoreDefaultValueButton restoreDefaultButton; RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; @@ -116,7 +117,7 @@ namespace osu.Game.Overlays.Settings InternalChildren = new Drawable[] { - restoreDefaultButton = new RestoreDefaultValueButton(), + restoreDefaultButton = new RestoreDefaultValueButton(), FlowContent = new FillFlowContainer { RelativeSizeAxes = Axes.X, @@ -146,101 +147,5 @@ namespace osu.Game.Overlays.Settings if (labelText != null) labelText.Alpha = controlWithCurrent.Current.Disabled ? 0.3f : 1; } - - protected internal class RestoreDefaultValueButton : Container, IHasTooltip - { - public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks; - - private Bindable bindable; - - public Bindable Bindable - { - get => bindable; - set - { - bindable = value; - bindable.ValueChanged += _ => UpdateState(); - bindable.DisabledChanged += _ => UpdateState(); - bindable.DefaultChanged += _ => UpdateState(); - UpdateState(); - } - } - - private Color4 buttonColour; - - private bool hovering; - - public RestoreDefaultValueButton() - { - RelativeSizeAxes = Axes.Y; - Width = SettingsPanel.CONTENT_MARGINS; - Padding = new MarginPadding { Vertical = 1.5f }; - Alpha = 0f; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colour) - { - buttonColour = colour.Yellow; - - Child = new Container - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - CornerRadius = 3, - Masking = true, - Colour = buttonColour, - EdgeEffect = new EdgeEffectParameters - { - Colour = buttonColour.Opacity(0.1f), - Type = EdgeEffectType.Glow, - Radius = 2, - }, - Width = 0.33f, - Child = new Box { RelativeSizeAxes = Axes.Both }, - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - UpdateState(); - } - - public string TooltipText => "revert to default"; - - protected override bool OnClick(ClickEvent e) - { - if (bindable != null && !bindable.Disabled) - bindable.SetDefault(); - return true; - } - - protected override bool OnHover(HoverEvent e) - { - hovering = true; - UpdateState(); - return false; - } - - protected override void OnHoverLost(HoverLostEvent e) - { - hovering = false; - UpdateState(); - } - - public void UpdateState() => Scheduler.AddOnce(updateState); - - private void updateState() - { - if (bindable == null) - return; - - this.FadeTo(bindable.IsDefault ? 0f : - hovering && !bindable.Disabled ? 1f : 0.65f, 200, Easing.OutQuint); - this.FadeColour(bindable.Disabled ? Color4.Gray : buttonColour, 200, Easing.OutQuint); - } - } } -} +} \ No newline at end of file From 753bdf208385285eb8ce24213365ddb74299a290 Mon Sep 17 00:00:00 2001 From: Swords Date: Sat, 15 May 2021 01:04:15 +1000 Subject: [PATCH 161/839] Fixed formatting --- osu.Game/Overlays/KeyBinding/KeyBindingRow.cs | 1 - osu.Game/Overlays/KeyBinding/SettingsKeyBindingRow.cs | 1 - osu.Game/Overlays/Settings/SettingsItem.cs | 1 - 3 files changed, 3 deletions(-) diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs index 4d2738f244..eeb9a5f7ec 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs @@ -85,7 +85,6 @@ namespace osu.Game.Overlays.KeyBinding [BackgroundDependencyLoader] private void load(OsuColour colours) { - isKeysDefaultValue.Value = bindings.Select(b => b.KeyCombination).SequenceEqual(Defaults); isKeysDefaultValue.BindValueChanged(resetButtons => { diff --git a/osu.Game/Overlays/KeyBinding/SettingsKeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/SettingsKeyBindingRow.cs index da8a628b51..15392efc9a 100644 --- a/osu.Game/Overlays/KeyBinding/SettingsKeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/SettingsKeyBindingRow.cs @@ -45,7 +45,6 @@ namespace osu.Game.Overlays.KeyBinding Defaults = defaultGroup.Select(d => d.KeyCombination) }; - RestoreDefaultValueButton restoreDefaultButton; RelativeSizeAxes = Axes.X; diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 7677931fde..57d1549910 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -108,7 +108,6 @@ namespace osu.Game.Overlays.Settings protected SettingsItem() { - RestoreDefaultValueButton restoreDefaultButton; RelativeSizeAxes = Axes.X; From 1603b92211a478846e8db583747fb66826034ceb Mon Sep 17 00:00:00 2001 From: Swords Date: Sat, 15 May 2021 01:30:54 +1000 Subject: [PATCH 162/839] Reformatting --- .../Settings/TestSceneKeyBindingPanel.cs | 19 +++++++------------ osu.Game/Overlays/KeyBinding/KeyBindingRow.cs | 6 ++++-- .../KeyBinding/KeyBindingsSubsection.cs | 5 ----- osu.Game/Overlays/Settings/SettingsItem.cs | 5 ----- 4 files changed, 11 insertions(+), 24 deletions(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs index 09c8696ee0..9126808c88 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs @@ -4,7 +4,6 @@ using System.Diagnostics; using System.Linq; using NUnit.Framework; -using osu.Framework.Input.Bindings; using osu.Framework.Testing; using osu.Framework.Threading; using osu.Game.Overlays; @@ -32,16 +31,14 @@ namespace osu.Game.Tests.Visual.Settings [Test] public void TestClickTwiceOnClearButton() { - SettingsKeyBindingRow firstSettingRow = null; KeyBindingRow firstRow = null; AddStep("click first row", () => { - firstSettingRow = panel.ChildrenOfType().First(); - InputManager.MoveMouseTo(firstSettingRow); + InputManager.MoveMouseTo(panel.ChildrenOfType().First()); InputManager.Click(MouseButton.Left); - firstRow = firstSettingRow.KeyBindingRow; + firstRow = panel.ChildrenOfType().First().KeyBindingRow; }); AddStep("schedule button clicks", () => @@ -111,12 +108,10 @@ namespace osu.Game.Tests.Visual.Settings [Test] public void TestSingleBindResetButton() { - SettingsKeyBindingRow multiSettingsBindingRow = null; KeyBindingRow multiBindingRow = null; AddStep("click first row with two bindings", () => { - multiSettingsBindingRow = panel.ChildrenOfType().First(row => row.KeyBindingRow.Defaults.Count() > 1); multiBindingRow = panel.ChildrenOfType().First(row => row.Defaults.Count() > 1); InputManager.MoveMouseTo(multiBindingRow); InputManager.Click(MouseButton.Left); @@ -124,20 +119,20 @@ namespace osu.Game.Tests.Visual.Settings InputManager.ReleaseKey(Key.P); }); - AddUntilStep("restore button shown", () => multiSettingsBindingRow.ChildrenOfType>().First().Alpha > 0); + AddUntilStep("restore button shown", () => panel.ChildrenOfType().First(row => row.KeyBindingRow.Defaults.Count() > 1).ChildrenOfType>().First().Alpha > 0); clickSingleBindResetButton(); - AddAssert("first binding cleared", () => multiBindingRow.ChildrenOfType().ElementAt(0).KeyBinding.KeyCombination.Equals(multiBindingRow.Defaults.ElementAt(0))); - AddAssert("second binding cleared", () => multiBindingRow.ChildrenOfType().ElementAt(1).KeyBinding.KeyCombination.Equals(multiBindingRow.Defaults.ElementAt(1))); + AddAssert("first binding cleared", () => panel.ChildrenOfType().First(row => row.KeyBindingRow.Defaults.Count() > 1).ChildrenOfType().ElementAt(0).KeyBinding.KeyCombination.Equals(multiBindingRow.Defaults.ElementAt(0))); + AddAssert("second binding cleared", () => panel.ChildrenOfType().First(row => row.KeyBindingRow.Defaults.Count() > 1).ChildrenOfType().ElementAt(1).KeyBinding.KeyCombination.Equals(multiBindingRow.Defaults.ElementAt(1))); - AddUntilStep("restore button hidden", () => multiSettingsBindingRow.ChildrenOfType>().First().Alpha == 0); + AddUntilStep("restore button hidden", () => panel.ChildrenOfType().First(row => row.KeyBindingRow.Defaults.Count() > 1).ChildrenOfType>().First().Alpha == 0); void clickSingleBindResetButton() { AddStep("click reset button for bindings", () => { - var clearButton = multiSettingsBindingRow.ChildrenOfType>().Single(); + var clearButton = panel.ChildrenOfType().First(row => row.KeyBindingRow.Defaults.Count() > 1).ChildrenOfType>().Single(); InputManager.MoveMouseTo(clearButton); InputManager.Click(MouseButton.Left); diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs index eeb9a5f7ec..3d89028f96 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs @@ -53,7 +53,8 @@ namespace osu.Game.Overlays.KeyBinding private FillFlowContainer cancelAndClearButtons; private FillFlowContainer buttons; - private BindableWithCurrent isKeysDefaultValue; + private readonly BindableWithCurrent isKeysDefaultValue; + public Bindable Current { get => isKeysDefaultValue.Current; @@ -67,7 +68,7 @@ namespace osu.Game.Overlays.KeyBinding this.action = action; this.bindings = bindings; - isKeysDefaultValue = new BindableWithCurrent() + isKeysDefaultValue = new BindableWithCurrent { Default = true }; @@ -314,6 +315,7 @@ namespace osu.Game.Overlays.KeyBinding store.Update(bindTarget.KeyBinding); KeyCombination keyDefault = Defaults.ElementAt(buttons.IndexOf(bindTarget)); + if (isKeysDefaultValue.Value) { if (!keyDefault.Equals(bindTarget.KeyBinding.KeyCombination)) diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs b/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs index d130a3f536..84630703bd 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs @@ -1,20 +1,17 @@ // 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.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Input; using osu.Game.Overlays.Settings; using osu.Game.Rulesets; using osuTK; using osu.Game.Graphics; -using osu.Framework.Input.Bindings; namespace osu.Game.Overlays.KeyBinding { @@ -41,8 +38,6 @@ namespace osu.Game.Overlays.KeyBinding foreach (var defaultGroup in Defaults.GroupBy(d => d.Action)) { - int intKey = (int)defaultGroup.Key; - // one row per valid action. Add(new SettingsKeyBindingRow(defaultGroup, bindings, Ruleset)); } diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 57d1549910..aa5cbf3d4e 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -5,16 +5,11 @@ using System; using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osuTK.Graphics; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; -using osu.Framework.Graphics.Effects; -using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; From 843da30f9d0f3ae60687eef1ddb1180178585f08 Mon Sep 17 00:00:00 2001 From: Swords Date: Sat, 15 May 2021 01:52:16 +1000 Subject: [PATCH 163/839] Reformatting --- osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs | 4 +--- osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs index 9126808c88..cfef0ecf5a 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs @@ -143,13 +143,11 @@ namespace osu.Game.Tests.Visual.Settings [Test] public void TestResetAllBindingsButton() { - SettingsKeyBindingRow multiSettingsBindingRow = null; KeyBindingRow multiBindingRow = null; AddStep("click first row and press p", () => { - multiSettingsBindingRow = panel.ChildrenOfType().First(row => row.KeyBindingRow.Defaults.Count() > 1); - multiBindingRow = panel.ChildrenOfType().First(); + multiBindingRow = panel.ChildrenOfType().First().KeyBindingRow; InputManager.MoveMouseTo(multiBindingRow); InputManager.Click(MouseButton.Left); InputManager.PressKey(Key.P); diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs b/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs index 73f4e6c371..e07e5a52c6 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs @@ -11,7 +11,6 @@ using osu.Game.Input; using osu.Game.Overlays.Settings; using osu.Game.Rulesets; using osuTK; -using osu.Game.Graphics; namespace osu.Game.Overlays.KeyBinding { From 3fa6a0413b8a3a3223432301e5f35057177e81dc Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Fri, 14 May 2021 23:04:09 +0200 Subject: [PATCH 164/839] Add slider position randomisation --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 90 ++++++++++++++++------ 1 file changed, 68 insertions(+), 22 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index dc194aa464..90036e6839 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -53,9 +53,10 @@ namespace osu.Game.Rulesets.Osu.Mods var prevObjectInfo = new HitObjectInfo { - AngleRad = 0, - PosUnchanged = hitObjects[0].Position, - PosChanged = hitObjects[0].Position + StartPosUnchanged = hitObjects[0].Position, + EndPosUnchanged = hitObjects[0].EndPosition, + StartPosChanged = hitObjects[0].Position, + EndPosChanged = hitObjects[0].EndPosition }; float rateOfChangeMultiplier = 0; @@ -66,31 +67,51 @@ namespace osu.Game.Rulesets.Osu.Mods var currentObjectInfo = new HitObjectInfo { - AngleRad = 0, - PosUnchanged = h.EndPosition, - PosChanged = Vector2.Zero + StartPosUnchanged = h.Position, + EndPosUnchanged = h.EndPosition, + StartPosChanged = Vector2.Zero, + EndPosChanged = Vector2.Zero }; // rateOfChangeMultiplier only changes every i iterations to prevent shaky-line-shaped streams if (i % 3 == 0) rateOfChangeMultiplier = (float)rng.NextDouble() * 2 - 1; - if (h is HitCircle circle) + var distanceToPrev = Vector2.Distance(prevObjectInfo.EndPosUnchanged, currentObjectInfo.StartPosUnchanged); + + switch (h) { - var distanceToPrev = Vector2.Distance(currentObjectInfo.PosUnchanged, prevObjectInfo.PosUnchanged); + case HitCircle circle: + getObjectInfo( + rateOfChangeMultiplier, + prevObjectInfo, + distanceToPrev, + ref currentObjectInfo + ); - getObjectInfo( - rateOfChangeMultiplier, - prevObjectInfo, - distanceToPrev, - ref currentObjectInfo - ); + circle.Position = currentObjectInfo.StartPosChanged; + currentObjectInfo.EndPosChanged = currentObjectInfo.StartPosChanged; + break; - circle.Position = currentObjectInfo.PosChanged; + case Slider slider: + currentObjectInfo.EndPosUnchanged = slider.EndPosition; + + currentObjectInfo.EndPosUnchanged = slider.TailCircle.Position; + + getObjectInfo( + rateOfChangeMultiplier, + prevObjectInfo, + distanceToPrev, + ref currentObjectInfo + ); + + slider.Position = currentObjectInfo.StartPosChanged; + currentObjectInfo.EndPosChanged = slider.TailCircle.Position; + + moveSliderIntoPlayfield(ref slider, ref currentObjectInfo); + break; } - // TODO: Implement slider position randomisation - prevObjectInfo = currentObjectInfo; } } @@ -116,10 +137,10 @@ namespace osu.Game.Rulesets.Osu.Mods distanceToPrev * (float)Math.Sin(currentObjectInfo.AngleRad) ); - posRelativeToPrev = getRotatedVector(prevObjectInfo.PosChanged, posRelativeToPrev); + posRelativeToPrev = getRotatedVector(prevObjectInfo.EndPosChanged, posRelativeToPrev); currentObjectInfo.AngleRad = (float)Math.Atan2(posRelativeToPrev.Y, posRelativeToPrev.X); - var position = Vector2.Add(prevObjectInfo.PosChanged, posRelativeToPrev); + var position = Vector2.Add(prevObjectInfo.EndPosChanged, posRelativeToPrev); // Move hit objects back into the playfield if they are outside of it, // which would sometimes happen during big jumps otherwise. @@ -133,7 +154,30 @@ namespace osu.Game.Rulesets.Osu.Mods else if (position.Y > OsuPlayfield.BASE_SIZE.Y) position.Y = OsuPlayfield.BASE_SIZE.Y; - currentObjectInfo.PosChanged = position; + currentObjectInfo.StartPosChanged = position; + } + + private void moveSliderIntoPlayfield(ref Slider slider, ref HitObjectInfo currentObjectInfo) + { + foreach (var controlPoint in slider.Path.ControlPoints) + { + // Position of controlPoint relative to slider.Position + var pos = controlPoint.Position.Value; + + var playfieldSize = OsuPlayfield.BASE_SIZE; + + if (pos.X + slider.Position.X < 0) + slider.Position = new Vector2(-pos.X, slider.Position.Y); + else if (pos.X + slider.Position.X > playfieldSize.X) + slider.Position = new Vector2(playfieldSize.X - pos.X, slider.Position.Y); + + if (pos.Y + slider.Position.Y < 0) + slider.Position = new Vector2(slider.Position.X, -pos.Y); + else if (pos.Y + slider.Position.Y > playfieldSize.Y) + slider.Position = new Vector2(slider.Position.X, playfieldSize.Y - pos.Y); + } + + currentObjectInfo.EndPosChanged = slider.TailCircle.Position; } /// @@ -217,8 +261,10 @@ namespace osu.Game.Rulesets.Osu.Mods private struct HitObjectInfo { internal float AngleRad { get; set; } - internal Vector2 PosUnchanged { get; set; } - internal Vector2 PosChanged { get; set; } + internal Vector2 StartPosUnchanged { get; set; } + internal Vector2 EndPosUnchanged { get; set; } + internal Vector2 StartPosChanged { get; set; } + internal Vector2 EndPosChanged { get; set; } } } From 878182fbdf480348d1114f0bc1e9f47c6141552c Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Sat, 15 May 2021 02:07:24 +0200 Subject: [PATCH 165/839] Fix slider ticks not being shifted along with their parent sliders --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 90036e6839..d06e807500 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -91,6 +92,7 @@ namespace osu.Game.Rulesets.Osu.Mods circle.Position = currentObjectInfo.StartPosChanged; currentObjectInfo.EndPosChanged = currentObjectInfo.StartPosChanged; + break; case Slider slider: @@ -109,6 +111,12 @@ namespace osu.Game.Rulesets.Osu.Mods currentObjectInfo.EndPosChanged = slider.TailCircle.Position; moveSliderIntoPlayfield(ref slider, ref currentObjectInfo); + + var sliderShift = Vector2.Subtract(slider.Position, currentObjectInfo.StartPosUnchanged); + + foreach (var tick in slider.NestedHitObjects.OfType()) + tick.Position = Vector2.Add(tick.Position, sliderShift); + break; } From 304caf8bdf79aabbd588d07b907e85e32915ba8e Mon Sep 17 00:00:00 2001 From: Swords Date: Sat, 15 May 2021 11:24:08 +1000 Subject: [PATCH 166/839] Adding Requested changed --- .../Settings/TestSceneKeyBindingPanel.cs | 67 ++++++++----------- osu.Game/Overlays/KeyBinding/KeyBindingRow.cs | 32 ++------- .../KeyBinding/SettingsKeyBindingRow.cs | 6 +- .../Overlays/RestoreDefaultValueButton.cs | 33 ++++----- osu.Game/Overlays/Settings/SettingsItem.cs | 2 +- 5 files changed, 56 insertions(+), 84 deletions(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs index cfef0ecf5a..669338d714 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs @@ -35,10 +35,10 @@ namespace osu.Game.Tests.Visual.Settings AddStep("click first row", () => { - InputManager.MoveMouseTo(panel.ChildrenOfType().First()); - InputManager.Click(MouseButton.Left); + firstRow = panel.ChildrenOfType().First(); - firstRow = panel.ChildrenOfType().First().KeyBindingRow; + InputManager.MoveMouseTo(firstRow); + InputManager.Click(MouseButton.Left); }); AddStep("schedule button clicks", () => @@ -72,7 +72,7 @@ namespace osu.Game.Tests.Visual.Settings AddStep("click first row with two bindings", () => { - multiBindingRow = panel.ChildrenOfType().First(row => row.KeyBindingRow.Defaults.Count() > 1).KeyBindingRow; + multiBindingRow = panel.ChildrenOfType().First(row => row.Defaults.Count() > 1); InputManager.MoveMouseTo(multiBindingRow); InputManager.Click(MouseButton.Left); }); @@ -108,66 +108,55 @@ namespace osu.Game.Tests.Visual.Settings [Test] public void TestSingleBindResetButton() { - KeyBindingRow multiBindingRow = null; + SettingsKeyBindingRow settingsKeyBindingRow = null; AddStep("click first row with two bindings", () => { - multiBindingRow = panel.ChildrenOfType().First(row => row.Defaults.Count() > 1); - InputManager.MoveMouseTo(multiBindingRow); + settingsKeyBindingRow = panel.ChildrenOfType().First(row => row.KeyBindingRow.Defaults.Count() > 1); + InputManager.MoveMouseTo(settingsKeyBindingRow.KeyBindingRow); InputManager.Click(MouseButton.Left); InputManager.PressKey(Key.P); InputManager.ReleaseKey(Key.P); }); - AddUntilStep("restore button shown", () => panel.ChildrenOfType().First(row => row.KeyBindingRow.Defaults.Count() > 1).ChildrenOfType>().First().Alpha > 0); + AddUntilStep("restore button shown", () => settingsKeyBindingRow.ChildrenOfType>().First().Alpha > 0); - clickSingleBindResetButton(); - - AddAssert("first binding cleared", () => panel.ChildrenOfType().First(row => row.KeyBindingRow.Defaults.Count() > 1).ChildrenOfType().ElementAt(0).KeyBinding.KeyCombination.Equals(multiBindingRow.Defaults.ElementAt(0))); - AddAssert("second binding cleared", () => panel.ChildrenOfType().First(row => row.KeyBindingRow.Defaults.Count() > 1).ChildrenOfType().ElementAt(1).KeyBinding.KeyCombination.Equals(multiBindingRow.Defaults.ElementAt(1))); - - AddUntilStep("restore button hidden", () => panel.ChildrenOfType().First(row => row.KeyBindingRow.Defaults.Count() > 1).ChildrenOfType>().First().Alpha == 0); - - void clickSingleBindResetButton() + AddStep("click reset button for bindings", () => { - AddStep("click reset button for bindings", () => - { - var clearButton = panel.ChildrenOfType().First(row => row.KeyBindingRow.Defaults.Count() > 1).ChildrenOfType>().Single(); + var clearButton = settingsKeyBindingRow.ChildrenOfType>().Single(); - InputManager.MoveMouseTo(clearButton); - InputManager.Click(MouseButton.Left); - }); - } + InputManager.MoveMouseTo(clearButton); + InputManager.Click(MouseButton.Left); + }); + + AddUntilStep("restore button hidden", () => settingsKeyBindingRow.ChildrenOfType>().First().Alpha == 0); + + AddAssert("first binding cleared", () => settingsKeyBindingRow.ChildrenOfType().ElementAt(0).KeyBinding.KeyCombination.Equals(settingsKeyBindingRow.KeyBindingRow.Defaults.ElementAt(0))); + AddAssert("second binding cleared", () => settingsKeyBindingRow.ChildrenOfType().ElementAt(1).KeyBinding.KeyCombination.Equals(settingsKeyBindingRow.KeyBindingRow.Defaults.ElementAt(1))); } [Test] public void TestResetAllBindingsButton() { - KeyBindingRow multiBindingRow = null; + KeyBindingRow keyBindingRow = null; AddStep("click first row and press p", () => { - multiBindingRow = panel.ChildrenOfType().First().KeyBindingRow; - InputManager.MoveMouseTo(multiBindingRow); + keyBindingRow = panel.ChildrenOfType().First(); + InputManager.MoveMouseTo(keyBindingRow); InputManager.Click(MouseButton.Left); InputManager.PressKey(Key.P); InputManager.ReleaseKey(Key.P); }); - - clickResetAllBindingsButton(); - - AddAssert("bindings cleared", () => multiBindingRow.ChildrenOfType().ElementAt(0).KeyBinding.KeyCombination.Equals(multiBindingRow.Defaults.ElementAt(0))); - - void clickResetAllBindingsButton() + AddStep("click reset button for all bindings", () => { - AddStep("click reset button for all bindings", () => - { - var clearButton = panel.ChildrenOfType().First(); + var clearButton = panel.ChildrenOfType().First(); - InputManager.MoveMouseTo(clearButton); - InputManager.Click(MouseButton.Left); - }); - } + InputManager.MoveMouseTo(clearButton); + InputManager.Click(MouseButton.Left); + }); + + AddUntilStep("bindings cleared", () => keyBindingRow.ChildrenOfType().ElementAt(0).KeyBinding.KeyCombination.Equals(keyBindingRow.Defaults.ElementAt(0))); } [Test] diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs index 8336dae9ac..6f4c5f1179 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs @@ -53,7 +53,11 @@ namespace osu.Game.Overlays.KeyBinding private FillFlowContainer cancelAndClearButtons; private FillFlowContainer buttons; - private readonly BindableWithCurrent isKeysDefaultValue; + private readonly BindableWithCurrent isKeysDefaultValue = new BindableWithCurrent + { + Default = true + }; + public Bindable Current { @@ -67,12 +71,6 @@ namespace osu.Game.Overlays.KeyBinding { this.action = action; this.bindings = bindings; - - isKeysDefaultValue = new BindableWithCurrent - { - Default = true - }; - RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; @@ -89,7 +87,7 @@ namespace osu.Game.Overlays.KeyBinding isKeysDefaultValue.Value = bindings.Select(b => b.KeyCombination).SequenceEqual(Defaults); isKeysDefaultValue.BindValueChanged(resetButtons => { - if (resetButtons.NewValue != resetButtons.OldValue && resetButtons.NewValue && !bindings.Select(b => b.KeyCombination).SequenceEqual(Defaults)) + if (resetButtons.NewValue && !bindings.Select(b => b.KeyCombination).SequenceEqual(Defaults)) { RestoreDefaults(); finalise(); @@ -314,23 +312,7 @@ namespace osu.Game.Overlays.KeyBinding { store.Update(bindTarget.KeyBinding); - KeyCombination keyDefault = Defaults.ElementAt(buttons.IndexOf(bindTarget)); - - if (isKeysDefaultValue.Value) - { - if (!keyDefault.Equals(bindTarget.KeyBinding.KeyCombination)) - { - isKeysDefaultValue.Value = false; - } - } - else - { - if (keyDefault.Equals(bindTarget.KeyBinding.KeyCombination) && - buttons.Select(b => b.KeyBinding.KeyCombination).SequenceEqual(Defaults)) - { - isKeysDefaultValue.Value = true; - } - } + isKeysDefaultValue.Value = buttons.Select(b => b.KeyBinding.KeyCombination).SequenceEqual(Defaults); bindTarget.IsBinding = false; Schedule(() => diff --git a/osu.Game/Overlays/KeyBinding/SettingsKeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/SettingsKeyBindingRow.cs index 15392efc9a..31374a8120 100644 --- a/osu.Game/Overlays/KeyBinding/SettingsKeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/SettingsKeyBindingRow.cs @@ -12,7 +12,7 @@ namespace osu.Game.Overlays.KeyBinding public class SettingsKeyBindingRow : Container, IFilterable { private readonly IGrouping defaultGroup; - private readonly IEnumerable bindings; + private readonly ICollection bindings; public readonly KeyBindingRow KeyBindingRow; private bool matchingFilter; @@ -33,7 +33,7 @@ namespace osu.Game.Overlays.KeyBinding public SettingsKeyBindingRow( IGrouping defaultGroup, - IEnumerable bindings, + ICollection bindings, RulesetInfo ruleset) { this.defaultGroup = defaultGroup; @@ -63,7 +63,7 @@ namespace osu.Game.Overlays.KeyBinding }, }; - restoreDefaultButton.Bindable = KeyBindingRow.Current; + restoreDefaultButton.Current = KeyBindingRow.Current; } } } \ No newline at end of file diff --git a/osu.Game/Overlays/RestoreDefaultValueButton.cs b/osu.Game/Overlays/RestoreDefaultValueButton.cs index 6d6616a893..5d61b76915 100644 --- a/osu.Game/Overlays/RestoreDefaultValueButton.cs +++ b/osu.Game/Overlays/RestoreDefaultValueButton.cs @@ -10,27 +10,27 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; using osu.Game.Graphics; using osuTK; namespace osu.Game.Overlays { - public class RestoreDefaultValueButton : Container, IHasTooltip + public class RestoreDefaultValueButton : Container, IHasTooltip, IHasCurrentValue { public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks; - private Bindable bindable; + private BindableWithCurrent current = new BindableWithCurrent(); - public Bindable Bindable + public Bindable Current { - get => bindable; - set - { - bindable = value; - bindable.ValueChanged += _ => UpdateState(); - bindable.DisabledChanged += _ => UpdateState(); - bindable.DefaultChanged += _ => UpdateState(); + get => current.Current; + set { + current.Current = value; + current.ValueChanged += _ => UpdateState(); + current.DisabledChanged += _ => UpdateState(); + current.DefaultChanged += _ => UpdateState(); UpdateState(); } } @@ -43,6 +43,7 @@ namespace osu.Game.Overlays { RelativeSizeAxes = Axes.Y; Width = SettingsPanel.CONTENT_MARGINS; + Padding = new MarginPadding { Vertical = 1.5f }; Alpha = 0f; } @@ -80,8 +81,8 @@ namespace osu.Game.Overlays protected override bool OnClick(ClickEvent e) { - if (bindable != null && !bindable.Disabled) - bindable.SetDefault(); + if (current != null && !current.Disabled) + current.SetDefault(); return true; } @@ -108,12 +109,12 @@ namespace osu.Game.Overlays private void updateState() { - if (bindable == null) + if (current == null) return; - this.FadeTo(bindable.IsDefault ? 0f : - hovering && !bindable.Disabled ? 1f : 0.65f, 200, Easing.OutQuint); - this.FadeColour(bindable.Disabled ? Color4.Gray : buttonColour, 200, Easing.OutQuint); + this.FadeTo(current.IsDefault ? 0f : + hovering && !current.Disabled ? 1f : 0.65f, 200, Easing.OutQuint); + this.FadeColour(current.Disabled ? Color4.Gray : buttonColour, 200, Easing.OutQuint); } } } \ No newline at end of file diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index aa5cbf3d4e..807916e7f6 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -132,7 +132,7 @@ namespace osu.Game.Overlays.Settings controlWithCurrent.Current.DisabledChanged += _ => updateDisabled(); if (ShowsDefaultIndicator) - restoreDefaultButton.Bindable = controlWithCurrent.Current; + restoreDefaultButton.Current = controlWithCurrent.Current; } } From d85f17159f0e3f5e3cf4b61165ec1f9de5339837 Mon Sep 17 00:00:00 2001 From: Swords Date: Sat, 15 May 2021 11:25:56 +1000 Subject: [PATCH 167/839] Formatting --- osu.Game/Overlays/KeyBinding/KeyBindingRow.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs index 6f4c5f1179..7e81f64458 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs @@ -58,7 +58,6 @@ namespace osu.Game.Overlays.KeyBinding Default = true }; - public Bindable Current { get => isKeysDefaultValue.Current; From d988194dd3638749bcf3e53ef6f08ca63928ca52 Mon Sep 17 00:00:00 2001 From: Swords Date: Sat, 15 May 2021 11:40:42 +1000 Subject: [PATCH 168/839] Formatting --- osu.Game/Overlays/RestoreDefaultValueButton.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/RestoreDefaultValueButton.cs b/osu.Game/Overlays/RestoreDefaultValueButton.cs index 5d61b76915..e709e874ce 100644 --- a/osu.Game/Overlays/RestoreDefaultValueButton.cs +++ b/osu.Game/Overlays/RestoreDefaultValueButton.cs @@ -21,12 +21,13 @@ namespace osu.Game.Overlays { public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks; - private BindableWithCurrent current = new BindableWithCurrent(); + private readonly BindableWithCurrent current = new BindableWithCurrent(); public Bindable Current { get => current.Current; - set { + set + { current.Current = value; current.ValueChanged += _ => UpdateState(); current.DisabledChanged += _ => UpdateState(); From 50d5af9662475aba7dadbe7302750ca57f3d9899 Mon Sep 17 00:00:00 2001 From: Swords Date: Sat, 15 May 2021 12:05:22 +1000 Subject: [PATCH 169/839] Removed unused method --- osu.Game/Overlays/RestoreDefaultValueButton.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/osu.Game/Overlays/RestoreDefaultValueButton.cs b/osu.Game/Overlays/RestoreDefaultValueButton.cs index e709e874ce..5c07eac838 100644 --- a/osu.Game/Overlays/RestoreDefaultValueButton.cs +++ b/osu.Game/Overlays/RestoreDefaultValueButton.cs @@ -100,12 +100,6 @@ namespace osu.Game.Overlays UpdateState(); } - public void SetButtonColour(Color4 buttonColour) - { - this.buttonColour = buttonColour; - UpdateState(); - } - public void UpdateState() => Scheduler.AddOnce(updateState); private void updateState() From c282f0e6035a2388d0360086bceed1d96d04d931 Mon Sep 17 00:00:00 2001 From: Swords Date: Sat, 15 May 2021 19:42:33 +1000 Subject: [PATCH 170/839] Fixing tests For some reason moving the mouse and clicking doesn't work with "dotnet test", but works when you run the osu.Game.Tests project. --- .../Settings/TestSceneKeyBindingPanel.cs | 42 ++++++++++--------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs index 669338d714..8053a41de2 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs @@ -106,14 +106,15 @@ namespace osu.Game.Tests.Visual.Settings } [Test] - public void TestSingleBindResetButton() + public void TestSingleBindingResetButton() { SettingsKeyBindingRow settingsKeyBindingRow = null; - AddStep("click first row with two bindings", () => + AddStep("click first row", () => { - settingsKeyBindingRow = panel.ChildrenOfType().First(row => row.KeyBindingRow.Defaults.Count() > 1); - InputManager.MoveMouseTo(settingsKeyBindingRow.KeyBindingRow); + settingsKeyBindingRow = panel.ChildrenOfType().First(); + + InputManager.MoveMouseTo(settingsKeyBindingRow); InputManager.Click(MouseButton.Left); InputManager.PressKey(Key.P); InputManager.ReleaseKey(Key.P); @@ -123,40 +124,43 @@ namespace osu.Game.Tests.Visual.Settings AddStep("click reset button for bindings", () => { - var clearButton = settingsKeyBindingRow.ChildrenOfType>().Single(); + var resetButton = settingsKeyBindingRow.ChildrenOfType>().First(); - InputManager.MoveMouseTo(clearButton); - InputManager.Click(MouseButton.Left); + resetButton.Click(); }); AddUntilStep("restore button hidden", () => settingsKeyBindingRow.ChildrenOfType>().First().Alpha == 0); - AddAssert("first binding cleared", () => settingsKeyBindingRow.ChildrenOfType().ElementAt(0).KeyBinding.KeyCombination.Equals(settingsKeyBindingRow.KeyBindingRow.Defaults.ElementAt(0))); - AddAssert("second binding cleared", () => settingsKeyBindingRow.ChildrenOfType().ElementAt(1).KeyBinding.KeyCombination.Equals(settingsKeyBindingRow.KeyBindingRow.Defaults.ElementAt(1))); + AddAssert("binding cleared", () => settingsKeyBindingRow.ChildrenOfType().ElementAt(0).KeyBinding.KeyCombination.Equals(settingsKeyBindingRow.KeyBindingRow.Defaults.ElementAt(0))); } [Test] public void TestResetAllBindingsButton() { - KeyBindingRow keyBindingRow = null; + SettingsKeyBindingRow settingsKeyBindingRow = null; - AddStep("click first row and press p", () => + AddStep("click first row", () => { - keyBindingRow = panel.ChildrenOfType().First(); - InputManager.MoveMouseTo(keyBindingRow); + settingsKeyBindingRow = panel.ChildrenOfType().First(); + + InputManager.MoveMouseTo(settingsKeyBindingRow); InputManager.Click(MouseButton.Left); InputManager.PressKey(Key.P); InputManager.ReleaseKey(Key.P); }); - AddStep("click reset button for all bindings", () => - { - var clearButton = panel.ChildrenOfType().First(); - InputManager.MoveMouseTo(clearButton); - InputManager.Click(MouseButton.Left); + AddUntilStep("restore button shown", () => settingsKeyBindingRow.ChildrenOfType>().First().Alpha > 0); + + AddStep("click reset button for bindings", () => + { + var resetButton = panel.ChildrenOfType().First(); + + resetButton.Click(); }); - AddUntilStep("bindings cleared", () => keyBindingRow.ChildrenOfType().ElementAt(0).KeyBinding.KeyCombination.Equals(keyBindingRow.Defaults.ElementAt(0))); + AddUntilStep("restore button hidden", () => settingsKeyBindingRow.ChildrenOfType>().First().Alpha == 0); + + AddAssert("binding cleared", () => settingsKeyBindingRow.ChildrenOfType().ElementAt(0).KeyBinding.KeyCombination.Equals(settingsKeyBindingRow.KeyBindingRow.Defaults.ElementAt(0))); } [Test] From 4c25fe750f193f7e624ffce2185070d85267e73b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 15 May 2021 14:32:15 +0300 Subject: [PATCH 171/839] Disallow beatmap skin to fall back to default HUD components This should become a more generalized `AllowDefaultSkinFallback` when default legacy skin fallback is supported. --- osu.Game/Skinning/LegacyBeatmapSkin.cs | 1 + osu.Game/Skinning/LegacySkin.cs | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/osu.Game/Skinning/LegacyBeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs index 3ec205e897..1a298576f9 100644 --- a/osu.Game/Skinning/LegacyBeatmapSkin.cs +++ b/osu.Game/Skinning/LegacyBeatmapSkin.cs @@ -14,6 +14,7 @@ namespace osu.Game.Skinning public class LegacyBeatmapSkin : LegacySkin { protected override bool AllowManiaSkin => false; + protected override bool AllowDefaultHUDComponentsFallback => false; protected override bool UseCustomSampleBanks => true; public LegacyBeatmapSkin(BeatmapInfo beatmap, IResourceStore storage, IStorageResourceProvider resources) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index a6f8f45c0f..e7edba1e13 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -38,6 +38,11 @@ namespace osu.Game.Skinning protected virtual bool AllowManiaSkin => hasKeyTexture.Value; + /// + /// Whether this skin will fall back to default HUD components if it has no fonts for use. + /// + protected virtual bool AllowDefaultHUDComponentsFallback => true; + /// /// Whether this skin can use samples with a custom bank (custom sample set in stable terminology). /// Added in order to match sample lookup logic from stable (in stable, only the beatmap skin could use samples with a custom sample bank). @@ -331,6 +336,8 @@ namespace osu.Game.Skinning switch (target.Target) { case SkinnableTarget.MainHUDComponents: + if (!this.HasFont(LegacyFont.Score) && !AllowDefaultHUDComponentsFallback) + return null; var skinnableTargetWrapper = new SkinnableTargetComponentsContainer(container => { From 102842bcf1c1919c75a7693771e6b3ce51d83e6f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 15 May 2021 14:32:58 +0300 Subject: [PATCH 172/839] Expire legacy combo counters on catch ruleset --- .../Legacy/CatchLegacySkinTransformer.cs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs index 1b48832ed6..6c477154ac 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs @@ -1,8 +1,10 @@ // 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.Bindables; using osu.Framework.Graphics; +using osu.Game.Screens.Play.HUD; using osu.Game.Skinning; using osuTK.Graphics; @@ -22,14 +24,17 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy public override Drawable GetDrawableComponent(ISkinComponent component) { - if (component is HUDSkinComponent hudComponent) + if (component is SkinnableTargetComponent targetComponent && targetComponent.Target == SkinnableTarget.MainHUDComponents) { - switch (hudComponent.Component) - { - case HUDSkinComponents.ComboCounter: - // catch may provide its own combo counter; hide the default. - return providesComboCounter ? Drawable.Empty() : null; - } + if (!providesComboCounter || !(Source.GetDrawableComponent(component) is SkinnableTargetComponentsContainer components)) + return null; + + // catch may provide its own combo counter; hide the default. + // todo: this should probably be done in an elegant way. + foreach (var legacyComboCounter in components.OfType()) + legacyComboCounter.Expire(); + + return components; } if (!(component is CatchSkinComponent catchSkinComponent)) From 243c8aa58557719f8ce8983dd848395e4f399d44 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 15 May 2021 18:02:38 +0300 Subject: [PATCH 173/839] Add test coverage --- .../TestSceneCatchPlayerLegacySkin.cs | 26 +++++++++++++++++++ .../Tests/Visual/LegacySkinPlayerTestScene.cs | 10 +++++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs index 64695153b5..d49470079b 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs @@ -1,8 +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.Linq; using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Screens; +using osu.Framework.Testing; +using osu.Game.Screens.Play.HUD; +using osu.Game.Skinning; using osu.Game.Tests.Visual; +using osuTK; namespace osu.Game.Rulesets.Catch.Tests { @@ -10,5 +18,23 @@ namespace osu.Game.Rulesets.Catch.Tests public class TestSceneCatchPlayerLegacySkin : LegacySkinPlayerTestScene { protected override Ruleset CreatePlayerRuleset() => new CatchRuleset(); + + [Test] + [Ignore("HUD components broken, remove when fixed.")] + public void TestLegacyHUDComboCounterHidden([Values] bool withModifiedSkin) + { + if (withModifiedSkin) + { + AddStep("change component scale", () => Player.ChildrenOfType().First().Scale = new Vector2(2f)); + AddStep("update target", () => Player.ChildrenOfType().ForEach(LegacySkin.UpdateDrawableTarget)); + AddStep("exit player", () => Player.Exit()); + CreateTest(null); + } + + AddAssert("legacy HUD combo counter hidden", () => + { + return Player.ChildrenOfType().All(counter => !counter.IsPresent || !counter.IsAlive); + }); + } } } diff --git a/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs b/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs index c186525757..80f09e21f3 100644 --- a/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs +++ b/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs @@ -3,7 +3,11 @@ using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; +using osu.Framework.Platform; +using osu.Game.IO; using osu.Game.Rulesets; using osu.Game.Skinning; @@ -12,6 +16,8 @@ namespace osu.Game.Tests.Visual [TestFixture] public abstract class LegacySkinPlayerTestScene : PlayerTestScene { + protected LegacySkin LegacySkin { get; private set; } + private ISkinSource legacySkinSource; protected override TestPlayer CreatePlayer(Ruleset ruleset) => new SkinProvidingPlayer(legacySkinSource); @@ -19,8 +25,8 @@ namespace osu.Game.Tests.Visual [BackgroundDependencyLoader] private void load(OsuGameBase game, SkinManager skins) { - var legacySkin = new DefaultLegacySkin(new NamespacedResourceStore(game.Resources, "Skins/Legacy"), skins); - legacySkinSource = new SkinProvidingContainer(legacySkin); + LegacySkin = new DefaultLegacySkin(new NamespacedResourceStore(game.Resources, "Skins/Legacy"), skins); + legacySkinSource = new SkinProvidingContainer(LegacySkin); } public class SkinProvidingPlayer : TestPlayer From f00799cc643f34b0d76ef202c1af6c8efa74472d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 15 May 2021 18:36:46 +0300 Subject: [PATCH 174/839] Remove unused using directive ...damn it --- .../TestSceneCatchPlayerLegacySkin.cs | 1 - osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs | 4 ---- 2 files changed, 5 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs index d49470079b..7f0cbc6943 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs @@ -3,7 +3,6 @@ using System.Linq; using NUnit.Framework; -using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Screens; using osu.Framework.Testing; diff --git a/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs b/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs index 80f09e21f3..907a37f4b9 100644 --- a/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs +++ b/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs @@ -3,11 +3,7 @@ using NUnit.Framework; using osu.Framework.Allocation; -using osu.Framework.Audio; -using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; -using osu.Framework.Platform; -using osu.Game.IO; using osu.Game.Rulesets; using osu.Game.Skinning; From e479db91864dd9a097aa90407940fd9b3639004b Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 15 May 2021 19:14:02 +0300 Subject: [PATCH 175/839] Clear transforms in PostsContainer for all children --- osu.Game/Overlays/News/Sidebar/MonthDropdown.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/News/Sidebar/MonthDropdown.cs b/osu.Game/Overlays/News/Sidebar/MonthDropdown.cs index d6d7527a6c..85a06c8227 100644 --- a/osu.Game/Overlays/News/Sidebar/MonthDropdown.cs +++ b/osu.Game/Overlays/News/Sidebar/MonthDropdown.cs @@ -149,7 +149,7 @@ namespace osu.Game.Overlays.News.Sidebar IsOpen.BindValueChanged(open => { - ClearTransforms(); + ClearTransforms(true); if (open.NewValue) { From 3e1b1c6c3ebb539c8bc9d8cbb3b5c709aab2b0fc Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 15 May 2021 19:14:58 +0300 Subject: [PATCH 176/839] Improve statement readability --- osu.Game/Overlays/News/Sidebar/NewsSideBar.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs b/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs index 3851dea83a..837c661cbd 100644 --- a/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs +++ b/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs @@ -78,7 +78,7 @@ namespace osu.Game.Overlays.News.Sidebar var allPosts = metadata.NewValue.NewsPosts; - if (!allPosts?.Any() ?? true) + if (allPosts?.Any() != true) return; var lookup = metadata.NewValue.NewsPosts.ToLookup(post => post.PublishedAt.Month); From 264d8b9b86c17f09448c78df43aa6d904c492201 Mon Sep 17 00:00:00 2001 From: Swords Date: Sun, 16 May 2021 14:48:00 +1000 Subject: [PATCH 177/839] Finishing requested changes, and tidy up --- .../Settings/TestSceneKeyBindingPanel.cs | 8 +++--- .../KeyBinding/KeyBindingsSubsection.cs | 4 +-- ...ndingRow.cs => RestorableKeyBindingRow.cs} | 28 ++++++++++--------- 3 files changed, 21 insertions(+), 19 deletions(-) rename osu.Game/Overlays/KeyBinding/{SettingsKeyBindingRow.cs => RestorableKeyBindingRow.cs} (74%) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs index 8053a41de2..3edba2ddd7 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs @@ -108,11 +108,11 @@ namespace osu.Game.Tests.Visual.Settings [Test] public void TestSingleBindingResetButton() { - SettingsKeyBindingRow settingsKeyBindingRow = null; + RestorableKeyBindingRow settingsKeyBindingRow = null; AddStep("click first row", () => { - settingsKeyBindingRow = panel.ChildrenOfType().First(); + settingsKeyBindingRow = panel.ChildrenOfType().First(); InputManager.MoveMouseTo(settingsKeyBindingRow); InputManager.Click(MouseButton.Left); @@ -137,11 +137,11 @@ namespace osu.Game.Tests.Visual.Settings [Test] public void TestResetAllBindingsButton() { - SettingsKeyBindingRow settingsKeyBindingRow = null; + RestorableKeyBindingRow settingsKeyBindingRow = null; AddStep("click first row", () => { - settingsKeyBindingRow = panel.ChildrenOfType().First(); + settingsKeyBindingRow = panel.ChildrenOfType().First(); InputManager.MoveMouseTo(settingsKeyBindingRow); InputManager.Click(MouseButton.Left); diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs b/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs index e07e5a52c6..dc29f0b4e5 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs @@ -38,12 +38,12 @@ namespace osu.Game.Overlays.KeyBinding foreach (var defaultGroup in Defaults.GroupBy(d => d.Action)) { // one row per valid action. - Add(new SettingsKeyBindingRow(defaultGroup, bindings, Ruleset)); + Add(new RestorableKeyBindingRow(defaultGroup.Key, bindings, Ruleset, defaultGroup.Select(d => d.KeyCombination))); } Add(new ResetButton { - Action = () => Children.OfType().ForEach(k => k.KeyBindingRow.RestoreDefaults()) + Action = () => Children.OfType().ForEach(k => k.KeyBindingRow.RestoreDefaults()) }); } } diff --git a/osu.Game/Overlays/KeyBinding/SettingsKeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs similarity index 74% rename from osu.Game/Overlays/KeyBinding/SettingsKeyBindingRow.cs rename to osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs index 31374a8120..0461ae4f35 100644 --- a/osu.Game/Overlays/KeyBinding/SettingsKeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs @@ -5,13 +5,14 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Input.Bindings; using osu.Game.Rulesets; namespace osu.Game.Overlays.KeyBinding { - public class SettingsKeyBindingRow : Container, IFilterable + public class RestorableKeyBindingRow : Container, IFilterable { - private readonly IGrouping defaultGroup; + private readonly object key; private readonly ICollection bindings; public readonly KeyBindingRow KeyBindingRow; @@ -29,28 +30,29 @@ namespace osu.Game.Overlays.KeyBinding public bool FilteringActive { get; set; } - public IEnumerable FilterTerms => bindings.Select(b => b.KeyCombination.ReadableString()).Prepend(defaultGroup.Key.ToString()); + public IEnumerable FilterTerms => bindings.Select(b => b.KeyCombination.ReadableString()).Prepend(key.ToString()); - public SettingsKeyBindingRow( - IGrouping defaultGroup, + public RestorableKeyBindingRow( + object key, ICollection bindings, - RulesetInfo ruleset) + RulesetInfo ruleset, + IEnumerable defaults) { - this.defaultGroup = defaultGroup; + this.key = key; this.bindings = bindings; - KeyBindingRow = new KeyBindingRow(defaultGroup.Key, bindings.Where(b => ((int)b.Action).Equals((int)defaultGroup.Key))) - { - AllowMainMouseButtons = ruleset != null, - Defaults = defaultGroup.Select(d => d.KeyCombination) - }; - RestoreDefaultValueButton restoreDefaultButton; RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; Padding = new MarginPadding { Right = SettingsPanel.CONTENT_MARGINS }; + KeyBindingRow = new KeyBindingRow(key, bindings.Where(b => ((int)b.Action).Equals((int)key))) + { + AllowMainMouseButtons = ruleset != null, + Defaults = defaults + }; + InternalChildren = new Drawable[] { restoreDefaultButton = new RestoreDefaultValueButton(), From 90f00a76633eb622b72073e32214ff4fc62710e3 Mon Sep 17 00:00:00 2001 From: Swords Date: Sun, 16 May 2021 15:01:19 +1000 Subject: [PATCH 178/839] Fixes ResotreDefaultValue to use the BindableWithCurrent correctly --- osu.Game/Overlays/RestoreDefaultValueButton.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/RestoreDefaultValueButton.cs b/osu.Game/Overlays/RestoreDefaultValueButton.cs index 5c07eac838..f390fb1e46 100644 --- a/osu.Game/Overlays/RestoreDefaultValueButton.cs +++ b/osu.Game/Overlays/RestoreDefaultValueButton.cs @@ -29,9 +29,6 @@ namespace osu.Game.Overlays set { current.Current = value; - current.ValueChanged += _ => UpdateState(); - current.DisabledChanged += _ => UpdateState(); - current.DefaultChanged += _ => UpdateState(); UpdateState(); } } @@ -46,6 +43,10 @@ namespace osu.Game.Overlays Width = SettingsPanel.CONTENT_MARGINS; Padding = new MarginPadding { Vertical = 1.5f }; Alpha = 0f; + + Current.ValueChanged += _ => UpdateState(); + Current.DisabledChanged += _ => UpdateState(); + Current.DefaultChanged += _ => UpdateState(); } [BackgroundDependencyLoader] From 463774d4f2621d7e5a42de0d4e9ed9612873f3de Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Wed, 19 May 2021 19:12:16 +0700 Subject: [PATCH 179/839] add ShowPage method in WikiOverlay --- osu.Game/Overlays/WikiOverlay.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs index 964daa3368..1022c56f26 100644 --- a/osu.Game/Overlays/WikiOverlay.cs +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -62,6 +62,12 @@ namespace osu.Game.Overlays }); } + public void ShowPage(string pagePath = index_path) + { + path.Value = pagePath.Trim('/'); + Show(); + } + protected override WikiHeader CreateHeader() => new WikiHeader(); protected override void LoadComplete() From f2de28814a3388c4d1544f4af4df13688a6b467c Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Mon, 17 May 2021 00:43:59 +0700 Subject: [PATCH 180/839] add and handle OpenWiki link action --- osu.Game/Online/Chat/MessageFormatter.cs | 6 +++++- osu.Game/OsuGame.cs | 10 ++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs index b80720a0aa..1041758b0c 100644 --- a/osu.Game/Online/Chat/MessageFormatter.cs +++ b/osu.Game/Online/Chat/MessageFormatter.cs @@ -167,6 +167,9 @@ namespace osu.Game.Online.Chat case "u": case "users": return new LinkDetails(LinkAction.OpenUserProfile, mainArg); + + case "wiki": + return new LinkDetails(LinkAction.OpenWiki, string.Join('/', args.Skip(3))); } } @@ -311,7 +314,8 @@ namespace osu.Game.Online.Chat JoinMultiplayerMatch, Spectate, OpenUserProfile, - Custom + OpenWiki, + Custom, } public class Link : IComparable diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 42a49aa65e..b3b0773eff 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -309,6 +309,10 @@ namespace osu.Game ShowUser(userId); break; + case LinkAction.OpenWiki: + ShowWiki(link.Argument); + break; + default: throw new NotImplementedException($"This {nameof(LinkAction)} ({link.Action.ToString()}) is missing an associated action."); } @@ -356,6 +360,12 @@ namespace osu.Game /// The beatmap to show. public void ShowBeatmap(int beatmapId) => waitForReady(() => beatmapSetOverlay, _ => beatmapSetOverlay.FetchAndShowBeatmap(beatmapId)); + /// + /// Show a wiki's page as an overlay + /// + /// The wiki page to show + public void ShowWiki(string path) => waitForReady(() => wikiOverlay, _ => wikiOverlay.ShowPage(path)); + /// /// Present a beatmap at song select immediately. /// The user should have already requested this interactively. From 50e2b5a32761768102975a76511d099bc51180d0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 17 May 2021 16:00:36 +0900 Subject: [PATCH 181/839] SideBar -> Sidebar --- osu.Game.Tests/Visual/Online/TestSceneNewsSideBar.cs | 6 +++--- osu.Game/Overlays/News/Sidebar/NewsSideBar.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsSideBar.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsSideBar.cs index b5405a979e..e376d9b1ed 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneNewsSideBar.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneNewsSideBar.cs @@ -13,15 +13,15 @@ using osu.Game.Overlays.News.Sidebar; namespace osu.Game.Tests.Visual.Online { - public class TestSceneNewsSideBar : OsuTestScene + public class TestSceneNewsSidebar : OsuTestScene { [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); - private NewsSideBar sidebar; + private NewsSidebar sidebar; [SetUp] - public void SetUp() => Schedule(() => Child = sidebar = new NewsSideBar()); + public void SetUp() => Schedule(() => Child = sidebar = new NewsSidebar()); [Test] public void TestYearsPanelVisibility() diff --git a/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs b/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs index 837c661cbd..bad334cb2f 100644 --- a/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs +++ b/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs @@ -12,7 +12,7 @@ using System.Linq; namespace osu.Game.Overlays.News.Sidebar { - public class NewsSideBar : CompositeDrawable + public class NewsSidebar : CompositeDrawable { [Cached] public readonly Bindable Metadata = new Bindable(); From 22561cda1956e51f3747952f19fb1f59c9ebc9db Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 17 May 2021 16:02:21 +0900 Subject: [PATCH 182/839] MonthDropdown -> MonthSection --- osu.Game.Tests/Visual/Online/TestSceneNewsSideBar.cs | 2 +- .../News/Sidebar/{MonthDropdown.cs => MonthSection.cs} | 4 ++-- osu.Game/Overlays/News/Sidebar/NewsSideBar.cs | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) rename osu.Game/Overlays/News/Sidebar/{MonthDropdown.cs => MonthSection.cs} (97%) diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsSideBar.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsSideBar.cs index e376d9b1ed..5e8cd397bc 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneNewsSideBar.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneNewsSideBar.cs @@ -35,7 +35,7 @@ namespace osu.Game.Tests.Visual.Online public void TestMetadataWithNoPosts() { AddStep("Add data with no posts", () => sidebar.Metadata.Value = metadata_with_no_posts); - AddUntilStep("No dropdowns were created", () => !sidebar.ChildrenOfType().Any()); + AddUntilStep("No dropdowns were created", () => !sidebar.ChildrenOfType().Any()); } private YearsPanel yearsPanel => sidebar.ChildrenOfType().FirstOrDefault(); diff --git a/osu.Game/Overlays/News/Sidebar/MonthDropdown.cs b/osu.Game/Overlays/News/Sidebar/MonthSection.cs similarity index 97% rename from osu.Game/Overlays/News/Sidebar/MonthDropdown.cs rename to osu.Game/Overlays/News/Sidebar/MonthSection.cs index 85a06c8227..80c408bda5 100644 --- a/osu.Game/Overlays/News/Sidebar/MonthDropdown.cs +++ b/osu.Game/Overlays/News/Sidebar/MonthSection.cs @@ -18,13 +18,13 @@ using System.Diagnostics; namespace osu.Game.Overlays.News.Sidebar { - public class MonthDropdown : CompositeDrawable + public class MonthSection : CompositeDrawable { private const int animation_duration = 250; public readonly BindableBool IsOpen = new BindableBool(); - public MonthDropdown(int month, int year, IEnumerable posts) + public MonthSection(int month, int year, IEnumerable posts) { Debug.Assert(posts.All(p => p.PublishedAt.Month == month && p.PublishedAt.Year == year)); diff --git a/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs b/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs index bad334cb2f..b8d283b7e2 100644 --- a/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs +++ b/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs @@ -17,7 +17,7 @@ namespace osu.Game.Overlays.News.Sidebar [Cached] public readonly Bindable Metadata = new Bindable(); - private FillFlowContainer monthsFlow; + private FillFlowContainer monthsFlow; [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider) @@ -49,7 +49,7 @@ namespace osu.Game.Overlays.News.Sidebar Children = new Drawable[] { new YearsPanel(), - monthsFlow = new FillFlowContainer + monthsFlow = new FillFlowContainer { AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, @@ -93,7 +93,7 @@ namespace osu.Game.Overlays.News.Sidebar var month = sortedKeys[i]; var posts = lookup[month]; - monthsFlow.Add(new MonthDropdown(month, year, posts) + monthsFlow.Add(new MonthSection(month, year, posts) { IsOpen = { Value = i == 0 } }); From 032f60819d0836fdd24a6fe715b08a3835e454c2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 17 May 2021 16:11:46 +0900 Subject: [PATCH 183/839] Rename content container --- osu.Game/Overlays/News/Sidebar/YearsPanel.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/News/Sidebar/YearsPanel.cs b/osu.Game/Overlays/News/Sidebar/YearsPanel.cs index 2d405e832b..932494f740 100644 --- a/osu.Game/Overlays/News/Sidebar/YearsPanel.cs +++ b/osu.Game/Overlays/News/Sidebar/YearsPanel.cs @@ -20,7 +20,7 @@ namespace osu.Game.Overlays.News.Sidebar { private readonly Bindable metadata = new Bindable(); - private Container gridPlaceholder; + private Container gridContent; [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider, Bindable metadata) @@ -38,7 +38,7 @@ namespace osu.Game.Overlays.News.Sidebar RelativeSizeAxes = Axes.Both, Colour = colourProvider.Background3 }, - gridPlaceholder = new Container + gridContent = new Container { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, @@ -59,7 +59,7 @@ namespace osu.Game.Overlays.News.Sidebar return; } - gridPlaceholder.Child = new YearsGridContainer(m.NewValue.Years, m.NewValue.CurrentYear); + gridContent.Child = new YearsGridContainer(m.NewValue.Years, m.NewValue.CurrentYear); Show(); }, true); } @@ -77,6 +77,7 @@ namespace osu.Game.Overlays.News.Sidebar RelativeSizeAxes = Axes.X; Height = 15; + Child = text = new OsuSpriteText { Anchor = Anchor.Centre, From ae1e62288d57d1477155f439f4d2b094edd68447 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 17 May 2021 16:16:50 +0900 Subject: [PATCH 184/839] Reorder tests to not have the first test show nothing --- .../Visual/Online/TestSceneNewsSideBar.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsSideBar.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsSideBar.cs index 5e8cd397bc..706de2b310 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneNewsSideBar.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneNewsSideBar.cs @@ -23,6 +23,13 @@ namespace osu.Game.Tests.Visual.Online [SetUp] public void SetUp() => Schedule(() => Child = sidebar = new NewsSidebar()); + [Test] + public void TestMetadataWithNoPosts() + { + AddStep("Add data with no posts", () => sidebar.Metadata.Value = metadata_with_no_posts); + AddUntilStep("No dropdowns were created", () => !sidebar.ChildrenOfType().Any()); + } + [Test] public void TestYearsPanelVisibility() { @@ -31,13 +38,6 @@ namespace osu.Game.Tests.Visual.Online AddUntilStep("Years panel is visible", () => yearsPanel?.Alpha == 1); } - [Test] - public void TestMetadataWithNoPosts() - { - AddStep("Add data with no posts", () => sidebar.Metadata.Value = metadata_with_no_posts); - AddUntilStep("No dropdowns were created", () => !sidebar.ChildrenOfType().Any()); - } - private YearsPanel yearsPanel => sidebar.ChildrenOfType().FirstOrDefault(); private static readonly APINewsSidebar metadata = new APINewsSidebar From abeeda5d04ddb2922629fba6fea2812ca5335c27 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 17 May 2021 16:24:43 +0900 Subject: [PATCH 185/839] Rewrite `YearsPanel` to not be insanely long --- osu.Game/Overlays/News/Sidebar/YearsPanel.cs | 49 ++++---------------- 1 file changed, 9 insertions(+), 40 deletions(-) diff --git a/osu.Game/Overlays/News/Sidebar/YearsPanel.cs b/osu.Game/Overlays/News/Sidebar/YearsPanel.cs index 932494f740..4573ae530f 100644 --- a/osu.Game/Overlays/News/Sidebar/YearsPanel.cs +++ b/osu.Game/Overlays/News/Sidebar/YearsPanel.cs @@ -1,17 +1,17 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Graphics.Containers; +using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; -using System.Collections.Generic; -using osu.Game.Graphics; -using osu.Framework.Bindables; using osu.Game.Online.API.Requests.Responses; -using System; using osuTK.Graphics; namespace osu.Game.Overlays.News.Sidebar @@ -77,6 +77,7 @@ namespace osu.Game.Overlays.News.Sidebar RelativeSizeAxes = Axes.X; Height = 15; + Padding = new MarginPadding { Vertical = 2.5f }; Child = text = new OsuSpriteText { @@ -99,39 +100,19 @@ namespace osu.Game.Overlays.News.Sidebar private class YearsGridContainer : GridContainer { private const int column_count = 4; - private const float spacing = 5f; private readonly int rowCount; public YearsGridContainer(int[] years, int currentYear) { - rowCount = (int)Math.Ceiling((float)years.Length / column_count); + rowCount = (years.Length + column_count - 1) / column_count; RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; - RowDimensions = getRowDimensions(); - ColumnDimensions = getColumnDimensions(); + RowDimensions = Enumerable.Range(0, rowCount).Select(_ => new Dimension(GridSizeMode.AutoSize)).ToArray(); Content = createContent(years, currentYear); } - private Dimension[] getRowDimensions() - { - var rowDimensions = new Dimension[rowCount]; - for (int i = 0; i < rowCount; i++) - rowDimensions[i] = new Dimension(GridSizeMode.AutoSize); - - return rowDimensions; - } - - private Dimension[] getColumnDimensions() - { - var columnDimensions = new Dimension[column_count]; - for (int i = 0; i < column_count; i++) - columnDimensions[i] = new Dimension(GridSizeMode.Relative, size: 1f / column_count); - - return columnDimensions; - } - private Drawable[][] createContent(int[] years, int currentYear) { var buttons = new Drawable[rowCount][]; @@ -153,19 +134,7 @@ namespace osu.Game.Overlays.News.Sidebar var year = years[index]; var isCurrent = year == currentYear; - buttons[i][j] = new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding - { - Top = i == 0 ? 0 : spacing / 2, - Bottom = i == rowCount - 1 ? 0 : spacing / 2, - Left = j == 0 ? 0 : spacing / 2, - Right = j == column_count - 1 ? 0 : spacing / 2 - }, - Child = new YearButton(year, isCurrent) - }; + buttons[i][j] = new YearButton(year, isCurrent); } } } From e754d2e59028940ff1a2e8b06cf66e88d1187299 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 17 May 2021 10:54:45 +0300 Subject: [PATCH 186/839] Simplify YearButton --- osu.Game/Overlays/News/Sidebar/YearsPanel.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Overlays/News/Sidebar/YearsPanel.cs b/osu.Game/Overlays/News/Sidebar/YearsPanel.cs index 4573ae530f..2528d51331 100644 --- a/osu.Game/Overlays/News/Sidebar/YearsPanel.cs +++ b/osu.Game/Overlays/News/Sidebar/YearsPanel.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 System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -66,9 +65,6 @@ namespace osu.Game.Overlays.News.Sidebar private class YearButton : OsuHoverContainer { - protected override IEnumerable EffectTargets => new[] { text }; - - private readonly OsuSpriteText text; private readonly bool isCurrent; public YearButton(int year, bool isCurrent) @@ -79,7 +75,7 @@ namespace osu.Game.Overlays.News.Sidebar Height = 15; Padding = new MarginPadding { Vertical = 2.5f }; - Child = text = new OsuSpriteText + Child = new OsuSpriteText { Anchor = Anchor.Centre, Origin = Anchor.Centre, From 5059bfaef99e987293d6b3f3d3f5b7629dcab9d9 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 17 May 2021 11:17:02 +0300 Subject: [PATCH 187/839] Use FillFlowContainer in YearsPanel --- osu.Game/Overlays/News/Sidebar/YearsPanel.cs | 70 +++++--------------- 1 file changed, 18 insertions(+), 52 deletions(-) diff --git a/osu.Game/Overlays/News/Sidebar/YearsPanel.cs b/osu.Game/Overlays/News/Sidebar/YearsPanel.cs index 2528d51331..331a7e10e1 100644 --- a/osu.Game/Overlays/News/Sidebar/YearsPanel.cs +++ b/osu.Game/Overlays/News/Sidebar/YearsPanel.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.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -11,6 +10,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Online.API.Requests.Responses; +using osuTK; using osuTK.Graphics; namespace osu.Game.Overlays.News.Sidebar @@ -19,7 +19,7 @@ namespace osu.Game.Overlays.News.Sidebar { private readonly Bindable metadata = new Bindable(); - private Container gridContent; + private FillFlowContainer yearsFlow; [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider, Bindable metadata) @@ -37,11 +37,17 @@ namespace osu.Game.Overlays.News.Sidebar RelativeSizeAxes = Axes.Both, Colour = colourProvider.Background3 }, - gridContent = new Container + new Container { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Padding = new MarginPadding(5) + Padding = new MarginPadding(5), + Child = yearsFlow = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(0, 5) + } } }; } @@ -52,13 +58,19 @@ namespace osu.Game.Overlays.News.Sidebar metadata.BindValueChanged(m => { + yearsFlow.Clear(); + if (m.NewValue == null) { Hide(); return; } - gridContent.Child = new YearsGridContainer(m.NewValue.Years, m.NewValue.CurrentYear); + var currentYear = m.NewValue.CurrentYear; + + foreach (var y in m.NewValue.Years) + yearsFlow.Add(new YearButton(y, y == currentYear)); + Show(); }, true); } @@ -72,8 +84,8 @@ namespace osu.Game.Overlays.News.Sidebar this.isCurrent = isCurrent; RelativeSizeAxes = Axes.X; + Width = 0.25f; Height = 15; - Padding = new MarginPadding { Vertical = 2.5f }; Child = new OsuSpriteText { @@ -92,51 +104,5 @@ namespace osu.Game.Overlays.News.Sidebar Action = () => { }; // Avoid button being disabled since there's no proper action assigned. } } - - private class YearsGridContainer : GridContainer - { - private const int column_count = 4; - - private readonly int rowCount; - - public YearsGridContainer(int[] years, int currentYear) - { - rowCount = (years.Length + column_count - 1) / column_count; - - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - RowDimensions = Enumerable.Range(0, rowCount).Select(_ => new Dimension(GridSizeMode.AutoSize)).ToArray(); - Content = createContent(years, currentYear); - } - - private Drawable[][] createContent(int[] years, int currentYear) - { - var buttons = new Drawable[rowCount][]; - - for (int i = 0; i < rowCount; i++) - { - buttons[i] = new Drawable[column_count]; - - for (int j = 0; j < column_count; j++) - { - var index = i * column_count + j; - - if (index >= years.Length) - { - buttons[i][j] = Empty(); - } - else - { - var year = years[index]; - var isCurrent = year == currentYear; - - buttons[i][j] = new YearButton(year, isCurrent); - } - } - } - - return buttons; - } - } } } From c0cfbd11ddabc1c4544b3b54872e4e4c3458ce14 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 17 May 2021 11:20:31 +0300 Subject: [PATCH 188/839] Add tooltip and action for PostButton in MonthSection --- osu.Game/Overlays/News/Sidebar/MonthSection.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/News/Sidebar/MonthSection.cs b/osu.Game/Overlays/News/Sidebar/MonthSection.cs index 80c408bda5..20c4d2e83e 100644 --- a/osu.Game/Overlays/News/Sidebar/MonthSection.cs +++ b/osu.Game/Overlays/News/Sidebar/MonthSection.cs @@ -15,6 +15,7 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics.Sprites; using System.Diagnostics; +using osu.Framework.Platform; namespace osu.Game.Overlays.News.Sidebar { @@ -99,9 +100,12 @@ namespace osu.Game.Overlays.News.Sidebar protected override IEnumerable EffectTargets => new[] { text }; private readonly TextFlowContainer text; + private readonly APINewsPost post; public PostButton(APINewsPost post) { + this.post = post; + RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; Child = text = new TextFlowContainer(t => t.Font = OsuFont.GetFont(size: 12)) @@ -113,11 +117,13 @@ namespace osu.Game.Overlays.News.Sidebar } [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) + private void load(OverlayColourProvider colourProvider, GameHost host) { IdleColour = colourProvider.Light2; HoverColour = colourProvider.Light1; - Action = () => { }; // Avoid button being disabled since there's no proper action assigned. + + TooltipText = "view in browser"; + Action = () => host.OpenUrlExternally("https://osu.ppy.sh/home/news/" + post.Slug); } } From 586c5c7365b3d0d0291447fb57b316b1e01b098e Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 17 May 2021 11:36:53 +0300 Subject: [PATCH 189/839] Emulate year changes in the test scene --- .../Visual/Online/TestSceneNewsSideBar.cs | 47 +++++++++++++------ osu.Game/Overlays/News/Sidebar/YearsPanel.cs | 5 +- 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsSideBar.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsSideBar.cs index 706de2b310..376c270689 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneNewsSideBar.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneNewsSideBar.cs @@ -10,6 +10,7 @@ using osu.Framework.Testing; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; using osu.Game.Overlays.News.Sidebar; +using static osu.Game.Overlays.News.Sidebar.YearsPanel; namespace osu.Game.Tests.Visual.Online { @@ -18,10 +19,10 @@ namespace osu.Game.Tests.Visual.Online [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); - private NewsSidebar sidebar; + private TestNewsSidebar sidebar; [SetUp] - public void SetUp() => Schedule(() => Child = sidebar = new NewsSidebar()); + public void SetUp() => Schedule(() => Child = sidebar = new TestNewsSidebar { YearChanged = onYearChanged }); [Test] public void TestMetadataWithNoPosts() @@ -34,15 +35,17 @@ namespace osu.Game.Tests.Visual.Online public void TestYearsPanelVisibility() { AddUntilStep("Years panel is hidden", () => yearsPanel?.Alpha == 0); - AddStep("Add data", () => sidebar.Metadata.Value = metadata); + AddStep("Add data", () => sidebar.Metadata.Value = getMetadata(2021)); AddUntilStep("Years panel is visible", () => yearsPanel?.Alpha == 1); } + private void onYearChanged(int year) => sidebar.Metadata.Value = getMetadata(year); + private YearsPanel yearsPanel => sidebar.ChildrenOfType().FirstOrDefault(); - private static readonly APINewsSidebar metadata = new APINewsSidebar + private APINewsSidebar getMetadata(int year) => new APINewsSidebar { - CurrentYear = 2021, + CurrentYear = year, Years = new[] { 2021, @@ -60,47 +63,47 @@ namespace osu.Game.Tests.Visual.Online new APINewsPost { Title = "(Mar) Short title", - PublishedAt = new DateTime(2021, 3, 1) + PublishedAt = new DateTime(year, 3, 1) }, new APINewsPost { Title = "(Mar) Oh boy that's a long post title I wonder if it will break anything", - PublishedAt = new DateTime(2021, 3, 1) + PublishedAt = new DateTime(year, 3, 1) }, new APINewsPost { Title = "(Mar) Medium title, nothing to see here", - PublishedAt = new DateTime(2021, 3, 1) + PublishedAt = new DateTime(year, 3, 1) }, new APINewsPost { Title = "(Feb) Short title", - PublishedAt = new DateTime(2021, 2, 1) + PublishedAt = new DateTime(year, 2, 1) }, new APINewsPost { Title = "(Feb) Oh boy that's a long post title I wonder if it will break anything", - PublishedAt = new DateTime(2021, 2, 1) + PublishedAt = new DateTime(year, 2, 1) }, new APINewsPost { Title = "(Feb) Medium title, nothing to see here", - PublishedAt = new DateTime(2021, 2, 1) + PublishedAt = new DateTime(year, 2, 1) }, new APINewsPost { Title = "Short title", - PublishedAt = new DateTime(2021, 1, 1) + PublishedAt = new DateTime(year, 1, 1) }, new APINewsPost { Title = "Oh boy that's a long post title I wonder if it will break anything", - PublishedAt = new DateTime(2021, 1, 1) + PublishedAt = new DateTime(year, 1, 1) }, new APINewsPost { Title = "Medium title, nothing to see here", - PublishedAt = new DateTime(2021, 1, 1) + PublishedAt = new DateTime(year, 1, 1) } } }; @@ -123,5 +126,21 @@ namespace osu.Game.Tests.Visual.Online }, NewsPosts = Array.Empty() }; + + private class TestNewsSidebar : NewsSidebar + { + public Action YearChanged; + + protected override void LoadComplete() + { + base.LoadComplete(); + + Metadata.BindValueChanged(m => + { + foreach (var b in this.ChildrenOfType()) + b.Action = () => YearChanged?.Invoke(b.Year); + }, true); + } + } } } diff --git a/osu.Game/Overlays/News/Sidebar/YearsPanel.cs b/osu.Game/Overlays/News/Sidebar/YearsPanel.cs index 331a7e10e1..ffdb5cf22e 100644 --- a/osu.Game/Overlays/News/Sidebar/YearsPanel.cs +++ b/osu.Game/Overlays/News/Sidebar/YearsPanel.cs @@ -75,12 +75,15 @@ namespace osu.Game.Overlays.News.Sidebar }, true); } - private class YearButton : OsuHoverContainer + public class YearButton : OsuHoverContainer { + public int Year { get; } + private readonly bool isCurrent; public YearButton(int year, bool isCurrent) { + Year = year; this.isCurrent = isCurrent; RelativeSizeAxes = Axes.X; From df248ea41b76cb5425b37b45c2e270d8843115ee Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 17 May 2021 11:41:53 +0300 Subject: [PATCH 190/839] Improve code readability --- .../Legacy/CatchLegacySkinTransformer.cs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs index 6c477154ac..db8c9a2e95 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs @@ -26,15 +26,20 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy { if (component is SkinnableTargetComponent targetComponent && targetComponent.Target == SkinnableTarget.MainHUDComponents) { - if (!providesComboCounter || !(Source.GetDrawableComponent(component) is SkinnableTargetComponentsContainer components)) + if (!providesComboCounter) return null; - // catch may provide its own combo counter; hide the default. - // todo: this should probably be done in an elegant way. - foreach (var legacyComboCounter in components.OfType()) - legacyComboCounter.Expire(); + if (Source.GetDrawableComponent(component) is SkinnableTargetComponentsContainer components) + { + // catch may provide its own combo counter; hide the default. + // todo: this should probably be done in an elegant way. + foreach (var legacyComboCounter in components.OfType()) + legacyComboCounter.Expire(); - return components; + return components; + } + + return null; } if (!(component is CatchSkinComponent catchSkinComponent)) From 01090de1fd45bfd0d1a1c1b743847d089b521e93 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 17 May 2021 11:55:55 +0300 Subject: [PATCH 191/839] Fix filenames does not match contained type --- .../Visual/Online/TestSceneNewsSidebar.cs | 146 ++++++++++++++++++ osu.Game/Overlays/News/Sidebar/NewsSidebar.cs | 103 ++++++++++++ 2 files changed, 249 insertions(+) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneNewsSidebar.cs create mode 100644 osu.Game/Overlays/News/Sidebar/NewsSidebar.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsSidebar.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsSidebar.cs new file mode 100644 index 0000000000..376c270689 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneNewsSidebar.cs @@ -0,0 +1,146 @@ +// 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 NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Testing; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Overlays; +using osu.Game.Overlays.News.Sidebar; +using static osu.Game.Overlays.News.Sidebar.YearsPanel; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneNewsSidebar : OsuTestScene + { + [Cached] + private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); + + private TestNewsSidebar sidebar; + + [SetUp] + public void SetUp() => Schedule(() => Child = sidebar = new TestNewsSidebar { YearChanged = onYearChanged }); + + [Test] + public void TestMetadataWithNoPosts() + { + AddStep("Add data with no posts", () => sidebar.Metadata.Value = metadata_with_no_posts); + AddUntilStep("No dropdowns were created", () => !sidebar.ChildrenOfType().Any()); + } + + [Test] + public void TestYearsPanelVisibility() + { + AddUntilStep("Years panel is hidden", () => yearsPanel?.Alpha == 0); + AddStep("Add data", () => sidebar.Metadata.Value = getMetadata(2021)); + AddUntilStep("Years panel is visible", () => yearsPanel?.Alpha == 1); + } + + private void onYearChanged(int year) => sidebar.Metadata.Value = getMetadata(year); + + private YearsPanel yearsPanel => sidebar.ChildrenOfType().FirstOrDefault(); + + private APINewsSidebar getMetadata(int year) => new APINewsSidebar + { + CurrentYear = year, + Years = new[] + { + 2021, + 2020, + 2019, + 2018, + 2017, + 2016, + 2015, + 2014, + 2013 + }, + NewsPosts = new List + { + new APINewsPost + { + Title = "(Mar) Short title", + PublishedAt = new DateTime(year, 3, 1) + }, + new APINewsPost + { + Title = "(Mar) Oh boy that's a long post title I wonder if it will break anything", + PublishedAt = new DateTime(year, 3, 1) + }, + new APINewsPost + { + Title = "(Mar) Medium title, nothing to see here", + PublishedAt = new DateTime(year, 3, 1) + }, + new APINewsPost + { + Title = "(Feb) Short title", + PublishedAt = new DateTime(year, 2, 1) + }, + new APINewsPost + { + Title = "(Feb) Oh boy that's a long post title I wonder if it will break anything", + PublishedAt = new DateTime(year, 2, 1) + }, + new APINewsPost + { + Title = "(Feb) Medium title, nothing to see here", + PublishedAt = new DateTime(year, 2, 1) + }, + new APINewsPost + { + Title = "Short title", + PublishedAt = new DateTime(year, 1, 1) + }, + new APINewsPost + { + Title = "Oh boy that's a long post title I wonder if it will break anything", + PublishedAt = new DateTime(year, 1, 1) + }, + new APINewsPost + { + Title = "Medium title, nothing to see here", + PublishedAt = new DateTime(year, 1, 1) + } + } + }; + + private static readonly APINewsSidebar metadata_with_no_posts = new APINewsSidebar + { + CurrentYear = 2022, + Years = new[] + { + 2022, + 2021, + 2020, + 2019, + 2018, + 2017, + 2016, + 2015, + 2014, + 2013 + }, + NewsPosts = Array.Empty() + }; + + private class TestNewsSidebar : NewsSidebar + { + public Action YearChanged; + + protected override void LoadComplete() + { + base.LoadComplete(); + + Metadata.BindValueChanged(m => + { + foreach (var b in this.ChildrenOfType()) + b.Action = () => YearChanged?.Invoke(b.Year); + }, true); + } + } + } +} diff --git a/osu.Game/Overlays/News/Sidebar/NewsSidebar.cs b/osu.Game/Overlays/News/Sidebar/NewsSidebar.cs new file mode 100644 index 0000000000..b8d283b7e2 --- /dev/null +++ b/osu.Game/Overlays/News/Sidebar/NewsSidebar.cs @@ -0,0 +1,103 @@ +// 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.Framework.Graphics; +using osu.Game.Online.API.Requests.Responses; +using osu.Framework.Graphics.Shapes; +using osuTK; +using System.Linq; + +namespace osu.Game.Overlays.News.Sidebar +{ + public class NewsSidebar : CompositeDrawable + { + [Cached] + public readonly Bindable Metadata = new Bindable(); + + private FillFlowContainer monthsFlow; + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + RelativeSizeAxes = Axes.Y; + Width = 250; + InternalChildren = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background4 + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding + { + Vertical = 20, + Left = 50, + Right = 30 + }, + Child = new FillFlowContainer + { + Direction = FillDirection.Vertical, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(0, 20), + Children = new Drawable[] + { + new YearsPanel(), + monthsFlow = new FillFlowContainer + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 10) + } + } + } + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Metadata.BindValueChanged(onMetadataChanged, true); + } + + private void onMetadataChanged(ValueChangedEvent metadata) + { + monthsFlow.Clear(); + + if (metadata.NewValue == null) + return; + + var allPosts = metadata.NewValue.NewsPosts; + + if (allPosts?.Any() != true) + return; + + var lookup = metadata.NewValue.NewsPosts.ToLookup(post => post.PublishedAt.Month); + + var keys = lookup.Select(kvp => kvp.Key); + var sortedKeys = keys.OrderByDescending(k => k).ToList(); + + var year = metadata.NewValue.CurrentYear; + + for (int i = 0; i < sortedKeys.Count; i++) + { + var month = sortedKeys[i]; + var posts = lookup[month]; + + monthsFlow.Add(new MonthSection(month, year, posts) + { + IsOpen = { Value = i == 0 } + }); + } + } + } +} From fc6e65b7dbaf9826f3a8a2832fade1a8521fb645 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 17 May 2021 12:02:06 +0300 Subject: [PATCH 192/839] Delete TestSceneNewsSideBar.cs --- .../Visual/Online/TestSceneNewsSideBar.cs | 146 ------------------ 1 file changed, 146 deletions(-) delete mode 100644 osu.Game.Tests/Visual/Online/TestSceneNewsSideBar.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsSideBar.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsSideBar.cs deleted file mode 100644 index 376c270689..0000000000 --- a/osu.Game.Tests/Visual/Online/TestSceneNewsSideBar.cs +++ /dev/null @@ -1,146 +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 NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Testing; -using osu.Game.Online.API.Requests.Responses; -using osu.Game.Overlays; -using osu.Game.Overlays.News.Sidebar; -using static osu.Game.Overlays.News.Sidebar.YearsPanel; - -namespace osu.Game.Tests.Visual.Online -{ - public class TestSceneNewsSidebar : OsuTestScene - { - [Cached] - private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); - - private TestNewsSidebar sidebar; - - [SetUp] - public void SetUp() => Schedule(() => Child = sidebar = new TestNewsSidebar { YearChanged = onYearChanged }); - - [Test] - public void TestMetadataWithNoPosts() - { - AddStep("Add data with no posts", () => sidebar.Metadata.Value = metadata_with_no_posts); - AddUntilStep("No dropdowns were created", () => !sidebar.ChildrenOfType().Any()); - } - - [Test] - public void TestYearsPanelVisibility() - { - AddUntilStep("Years panel is hidden", () => yearsPanel?.Alpha == 0); - AddStep("Add data", () => sidebar.Metadata.Value = getMetadata(2021)); - AddUntilStep("Years panel is visible", () => yearsPanel?.Alpha == 1); - } - - private void onYearChanged(int year) => sidebar.Metadata.Value = getMetadata(year); - - private YearsPanel yearsPanel => sidebar.ChildrenOfType().FirstOrDefault(); - - private APINewsSidebar getMetadata(int year) => new APINewsSidebar - { - CurrentYear = year, - Years = new[] - { - 2021, - 2020, - 2019, - 2018, - 2017, - 2016, - 2015, - 2014, - 2013 - }, - NewsPosts = new List - { - new APINewsPost - { - Title = "(Mar) Short title", - PublishedAt = new DateTime(year, 3, 1) - }, - new APINewsPost - { - Title = "(Mar) Oh boy that's a long post title I wonder if it will break anything", - PublishedAt = new DateTime(year, 3, 1) - }, - new APINewsPost - { - Title = "(Mar) Medium title, nothing to see here", - PublishedAt = new DateTime(year, 3, 1) - }, - new APINewsPost - { - Title = "(Feb) Short title", - PublishedAt = new DateTime(year, 2, 1) - }, - new APINewsPost - { - Title = "(Feb) Oh boy that's a long post title I wonder if it will break anything", - PublishedAt = new DateTime(year, 2, 1) - }, - new APINewsPost - { - Title = "(Feb) Medium title, nothing to see here", - PublishedAt = new DateTime(year, 2, 1) - }, - new APINewsPost - { - Title = "Short title", - PublishedAt = new DateTime(year, 1, 1) - }, - new APINewsPost - { - Title = "Oh boy that's a long post title I wonder if it will break anything", - PublishedAt = new DateTime(year, 1, 1) - }, - new APINewsPost - { - Title = "Medium title, nothing to see here", - PublishedAt = new DateTime(year, 1, 1) - } - } - }; - - private static readonly APINewsSidebar metadata_with_no_posts = new APINewsSidebar - { - CurrentYear = 2022, - Years = new[] - { - 2022, - 2021, - 2020, - 2019, - 2018, - 2017, - 2016, - 2015, - 2014, - 2013 - }, - NewsPosts = Array.Empty() - }; - - private class TestNewsSidebar : NewsSidebar - { - public Action YearChanged; - - protected override void LoadComplete() - { - base.LoadComplete(); - - Metadata.BindValueChanged(m => - { - foreach (var b in this.ChildrenOfType()) - b.Action = () => YearChanged?.Invoke(b.Year); - }, true); - } - } - } -} From 555e3e2db30ac722b57a893799e7b8e63ab54550 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 17 May 2021 12:02:33 +0300 Subject: [PATCH 193/839] Delete NewsSideBar.cs --- osu.Game/Overlays/News/Sidebar/NewsSideBar.cs | 103 ------------------ 1 file changed, 103 deletions(-) delete mode 100644 osu.Game/Overlays/News/Sidebar/NewsSideBar.cs diff --git a/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs b/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs deleted file mode 100644 index b8d283b7e2..0000000000 --- a/osu.Game/Overlays/News/Sidebar/NewsSideBar.cs +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics; -using osu.Game.Online.API.Requests.Responses; -using osu.Framework.Graphics.Shapes; -using osuTK; -using System.Linq; - -namespace osu.Game.Overlays.News.Sidebar -{ - public class NewsSidebar : CompositeDrawable - { - [Cached] - public readonly Bindable Metadata = new Bindable(); - - private FillFlowContainer monthsFlow; - - [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) - { - RelativeSizeAxes = Axes.Y; - Width = 250; - InternalChildren = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colourProvider.Background4 - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding - { - Vertical = 20, - Left = 50, - Right = 30 - }, - Child = new FillFlowContainer - { - Direction = FillDirection.Vertical, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Spacing = new Vector2(0, 20), - Children = new Drawable[] - { - new YearsPanel(), - monthsFlow = new FillFlowContainer - { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 10) - } - } - } - } - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - Metadata.BindValueChanged(onMetadataChanged, true); - } - - private void onMetadataChanged(ValueChangedEvent metadata) - { - monthsFlow.Clear(); - - if (metadata.NewValue == null) - return; - - var allPosts = metadata.NewValue.NewsPosts; - - if (allPosts?.Any() != true) - return; - - var lookup = metadata.NewValue.NewsPosts.ToLookup(post => post.PublishedAt.Month); - - var keys = lookup.Select(kvp => kvp.Key); - var sortedKeys = keys.OrderByDescending(k => k).ToList(); - - var year = metadata.NewValue.CurrentYear; - - for (int i = 0; i < sortedKeys.Count; i++) - { - var month = sortedKeys[i]; - var posts = lookup[month]; - - monthsFlow.Add(new MonthSection(month, year, posts) - { - IsOpen = { Value = i == 0 } - }); - } - } - } -} From 7137315fa7612f02bbd963e55e6b2c6ac512b892 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 17 May 2021 19:46:50 +0900 Subject: [PATCH 194/839] Remove `HitErrorDisplay` container and hook up data --- osu.Game/Screens/Play/HUD/HitErrorDisplay.cs | 127 ------------------ .../HUD/HitErrorMeters/BarHitErrorMeter.cs | 15 ++- .../HUD/HitErrorMeters/ColourHitErrorMeter.cs | 4 +- .../Play/HUD/HitErrorMeters/HitErrorMeter.cs | 11 +- osu.Game/Screens/Play/HUDOverlay.cs | 18 +-- 5 files changed, 20 insertions(+), 155 deletions(-) delete mode 100644 osu.Game/Screens/Play/HUD/HitErrorDisplay.cs diff --git a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs deleted file mode 100644 index a24d9c10cb..0000000000 --- a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Configuration; -using osu.Game.Rulesets.Scoring; -using osu.Game.Screens.Play.HUD.HitErrorMeters; - -namespace osu.Game.Screens.Play.HUD -{ - public class HitErrorDisplay : Container - { - private const int fade_duration = 200; - private const int margin = 10; - - private readonly Bindable type = new Bindable(); - - private readonly HitWindows hitWindows; - - public HitErrorDisplay(HitWindows hitWindows) - { - this.hitWindows = hitWindows; - - RelativeSizeAxes = Axes.Both; - } - - [BackgroundDependencyLoader] - private void load(OsuConfigManager config) - { - config.BindWith(OsuSetting.ScoreMeter, type); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - type.BindValueChanged(typeChanged, true); - } - - private void typeChanged(ValueChangedEvent type) - { - Children.ForEach(c => c.FadeOut(fade_duration, Easing.OutQuint)); - - if (hitWindows == null) - return; - - switch (type.NewValue) - { - case ScoreMeterType.HitErrorBoth: - createBar(Anchor.CentreLeft); - createBar(Anchor.CentreRight); - break; - - case ScoreMeterType.HitErrorLeft: - createBar(Anchor.CentreLeft); - break; - - case ScoreMeterType.HitErrorRight: - createBar(Anchor.CentreRight); - break; - - case ScoreMeterType.HitErrorBottom: - createBar(Anchor.BottomCentre); - break; - - case ScoreMeterType.ColourBoth: - createColour(Anchor.CentreLeft); - createColour(Anchor.CentreRight); - break; - - case ScoreMeterType.ColourLeft: - createColour(Anchor.CentreLeft); - break; - - case ScoreMeterType.ColourRight: - createColour(Anchor.CentreRight); - break; - - case ScoreMeterType.ColourBottom: - createColour(Anchor.BottomCentre); - break; - } - } - - private void createBar(Anchor anchor) - { - bool rightAligned = (anchor & Anchor.x2) > 0; - bool bottomAligned = (anchor & Anchor.y2) > 0; - - var display = new BarHitErrorMeter(hitWindows, rightAligned) - { - Margin = new MarginPadding(margin), - Anchor = anchor, - Origin = bottomAligned ? Anchor.CentreLeft : anchor, - Alpha = 0, - Rotation = bottomAligned ? 270 : 0 - }; - - completeDisplayLoading(display); - } - - private void createColour(Anchor anchor) - { - bool bottomAligned = (anchor & Anchor.y2) > 0; - - var display = new ColourHitErrorMeter(hitWindows) - { - Margin = new MarginPadding(margin), - Anchor = anchor, - Origin = bottomAligned ? Anchor.CentreLeft : anchor, - Alpha = 0, - Rotation = bottomAligned ? 270 : 0 - }; - - completeDisplayLoading(display); - } - - private void completeDisplayLoading(HitErrorMeter display) - { - Add(display); - display.FadeInFromZero(fade_duration, Easing.OutQuint); - } - } -} diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs index 0e147f9238..c303f3889d 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs @@ -43,10 +43,10 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters private double maxHitWindow; - public BarHitErrorMeter(HitWindows hitWindows, bool rightAligned = false) - : base(hitWindows) + public BarHitErrorMeter() { - alignment = rightAligned ? Anchor.x0 : Anchor.x2; + // todo: investigate. + alignment = false ? Anchor.x0 : Anchor.x2; AutoSizeAxes = Axes.Both; } @@ -152,14 +152,17 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters { var windows = HitWindows.GetAllAvailableWindows().ToArray(); - maxHitWindow = windows.First().length; + // max to avoid div-by-zero. + maxHitWindow = Math.Max(1, windows.First().length); for (var i = 0; i < windows.Length; i++) { var (result, length) = windows[i]; - colourBarsEarly.Add(createColourBar(result, (float)(length / maxHitWindow), i == 0)); - colourBarsLate.Add(createColourBar(result, (float)(length / maxHitWindow), i == 0)); + var hitWindow = (float)(length / maxHitWindow); + + colourBarsEarly.Add(createColourBar(result, hitWindow, i == 0)); + colourBarsLate.Add(createColourBar(result, hitWindow, i == 0)); } // a little nub to mark the centre point. diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs index 465439cf19..0eb2367f73 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs @@ -7,7 +7,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Scoring; using osuTK; using osuTK.Graphics; @@ -19,8 +18,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters private readonly JudgementFlow judgementsFlow; - public ColourHitErrorMeter(HitWindows hitWindows) - : base(hitWindows) + public ColourHitErrorMeter() { AutoSizeAxes = Axes.Both; InternalChild = judgementsFlow = new JudgementFlow(); diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs index 37e9ea43c5..b0f9928b13 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs @@ -6,13 +6,15 @@ using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; +using osu.Game.Skinning; using osuTK.Graphics; namespace osu.Game.Screens.Play.HUD.HitErrorMeters { - public abstract class HitErrorMeter : CompositeDrawable + public abstract class HitErrorMeter : CompositeDrawable, ISkinnableDrawable { - protected readonly HitWindows HitWindows; + protected HitWindows HitWindows { get; private set; } [Resolved] private ScoreProcessor processor { get; set; } @@ -20,9 +22,10 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters [Resolved] private OsuColour colours { get; set; } - protected HitErrorMeter(HitWindows hitWindows) + [BackgroundDependencyLoader(true)] + private void load(DrawableRuleset drawableRuleset) { - HitWindows = hitWindows; + HitWindows = drawableRuleset?.FirstAvailableHitWindows ?? HitWindows.Empty; } protected override void LoadComplete() diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index dcee64ff0d..ab5b01cab6 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -87,22 +87,10 @@ namespace osu.Game.Screens.Play visibilityContainer = new Container { RelativeSizeAxes = Axes.Both, - Children = new Drawable[] + Child = mainComponents = new SkinnableTargetContainer(SkinnableTarget.MainHUDComponents) { - mainComponents = new SkinnableTargetContainer(SkinnableTarget.MainHUDComponents) - { - RelativeSizeAxes = Axes.Both, - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - // still need to be migrated; a bit more involved. - new HitErrorDisplay(this.drawableRuleset?.FirstAvailableHitWindows), - } - }, - } + RelativeSizeAxes = Axes.Both, + }, }, topRightElements = new FillFlowContainer { From 5d5b1e1f0e441de05c7c5fda27399d05a84415f9 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 8 May 2021 11:00:22 +0200 Subject: [PATCH 195/839] Add StableImportManager --- osu.Game/Database/StableImportManager.cs | 92 +++++++++++++++++++ .../StableDirectorySelectScreen.cs | 42 +++++++++ 2 files changed, 134 insertions(+) create mode 100644 osu.Game/Database/StableImportManager.cs create mode 100644 osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs diff --git a/osu.Game/Database/StableImportManager.cs b/osu.Game/Database/StableImportManager.cs new file mode 100644 index 0000000000..46c6aab2fe --- /dev/null +++ b/osu.Game/Database/StableImportManager.cs @@ -0,0 +1,92 @@ +// 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.Text; +using System.Threading.Tasks; +using JetBrains.Annotations; +using osu.Framework.Allocation; +using osu.Framework.Extensions.EnumExtensions; +using osu.Framework.Graphics; +using osu.Framework.Platform; +using osu.Framework.Screens; +using osu.Game.Beatmaps; +using osu.Game.Collections; +using osu.Game.IO; +using osu.Game.Overlays.Settings.Sections.Maintenance; +using osu.Game.Scoring; +using osu.Game.Skinning; + +namespace osu.Game.Database +{ + public class StableImportManager : Component + { + [Resolved] + private SkinManager skins { get; set; } + + [Resolved] + private BeatmapManager beatmaps { get; set; } + + [Resolved] + private ScoreManager scores { get; set; } + + [Resolved] + private CollectionManager collections { get; set; } + + [Resolved] + private OsuGame game { get; set; } + + [Resolved(CanBeNull = true)] + private DesktopGameHost desktopGameHost { get; set; } + + private StableStorage cachedStorage; + + public async Task ImportFromStableAsync(StableContent content) + { + //var stableStorage = await getStableStorage().ConfigureAwait(false); + var importTasks = new List(); + + if (content.HasFlagFast(StableContent.Beatmaps)) + importTasks.Add(beatmaps.ImportFromStableAsync()); + + if (content.HasFlagFast(StableContent.Collections)) + importTasks.Add(collections.ImportFromStableAsync()); + + if (content.HasFlagFast(StableContent.Scores)) + importTasks.Add(scores.ImportFromStableAsync()); + + if (content.HasFlagFast(StableContent.Skins)) + importTasks.Add(skins.ImportFromStableAsync()); + + await Task.WhenAll(importTasks.ToArray()).ConfigureAwait(false); + } + + private async Task getStableStorage() + { + var stableStorage = game.GetStorageForStableInstall(); + if (stableStorage != null) + return stableStorage; + + if (cachedStorage != null) + return cachedStorage; + + var taskCompletionSource = new TaskCompletionSource(); + Schedule(() => game.PerformFromScreen(t => t.Push(new StableDirectorySelectScreen(taskCompletionSource)))); + var stablePath = await taskCompletionSource.Task.ConfigureAwait(false); + + return cachedStorage = new StableStorage(stablePath, desktopGameHost); + } + + } + + [Flags] + public enum StableContent + { + Beatmaps = 0x1, + Scores = 0x2, + Skins = 0x3, + Collections = 0x4, + All = Beatmaps | Scores | Skins | Collections + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs new file mode 100644 index 0000000000..d935bcf526 --- /dev/null +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.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.IO; +using System.Linq; +using System.Threading.Tasks; +using osu.Framework.Screens; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Overlays.Settings.Sections.Maintenance +{ + public class StableDirectorySelectScreen : DirectorySelectScreen + { + private readonly TaskCompletionSource taskCompletionSource; + + protected override bool IsValidDirectory(DirectoryInfo info) => info?.GetFiles("osu!.*.cfg").Any() ?? false; + + protected override OsuSpriteText CreateHeader() => new OsuSpriteText + { + Text = "Please select stable location", + Font = OsuFont.Default.With(size: 40) + }; + + public StableDirectorySelectScreen(TaskCompletionSource taskCompletionSource) + { + this.taskCompletionSource = taskCompletionSource; + } + + protected override void OnSelection(DirectoryInfo directory) + { + taskCompletionSource.TrySetResult(directory.FullName); + this.Exit(); + } + + public override bool OnBackButton() + { + taskCompletionSource.TrySetCanceled(); + return base.OnBackButton(); + } + } +} From 851e33fd1542c21dea6717c4416db71ab1866f36 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sun, 9 May 2021 17:12:58 +0200 Subject: [PATCH 196/839] Hook up StableImportManager. --- osu.Game/Collections/CollectionManager.cs | 12 ++++----- osu.Game/Database/ArchiveModelManager.cs | 5 +--- osu.Game/Database/StableImportManager.cs | 25 ++++++++++--------- osu.Game/OsuGame.cs | 4 +++ .../Sections/Maintenance/GeneralSettings.cs | 19 +++++++------- osu.Game/Screens/Select/SongSelect.cs | 13 +++------- 6 files changed, 36 insertions(+), 42 deletions(-) diff --git a/osu.Game/Collections/CollectionManager.cs b/osu.Game/Collections/CollectionManager.cs index 9723409c79..fbd12cf672 100644 --- a/osu.Game/Collections/CollectionManager.cs +++ b/osu.Game/Collections/CollectionManager.cs @@ -15,6 +15,7 @@ using osu.Framework.Graphics; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game.Beatmaps; +using osu.Game.IO; using osu.Game.IO.Legacy; using osu.Game.Overlays.Notifications; @@ -38,8 +39,6 @@ namespace osu.Game.Collections public readonly BindableList Collections = new BindableList(); - public bool SupportsImportFromStable => RuntimeInfo.IsDesktop; - [Resolved] private GameHost host { get; set; } @@ -104,17 +103,16 @@ namespace osu.Game.Collections /// /// This is a temporary method and will likely be replaced by a full-fledged (and more correctly placed) migration process in the future. /// - public Task ImportFromStableAsync() + public Task ImportFromStableAsync(StableStorage stableStorage) { - var stable = GetStableStorage?.Invoke(); - if (stable == null) + if (stableStorage == null) { Logger.Log("No osu!stable installation available!", LoggingTarget.Information, LogLevel.Error); return Task.CompletedTask; } - if (!stable.Exists(database_name)) + if (!stableStorage.Exists(database_name)) { // This handles situations like when the user does not have a collections.db file Logger.Log($"No {database_name} available in osu!stable installation", LoggingTarget.Information, LogLevel.Error); @@ -123,7 +121,7 @@ namespace osu.Game.Collections return Task.Run(async () => { - using (var stream = stable.GetStream(database_name)) + using (var stream = stableStorage.GetStream(database_name)) await Import(stream).ConfigureAwait(false); }); } diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index e0f80d2743..38a6af4654 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -81,8 +81,6 @@ namespace osu.Game.Database public virtual IEnumerable HandledExtensions => new[] { ".zip" }; - public virtual bool SupportsImportFromStable => RuntimeInfo.IsDesktop; - protected readonly FileStore Files; protected readonly IDatabaseContextFactory ContextFactory; @@ -700,9 +698,8 @@ namespace osu.Game.Database /// /// This is a temporary method and will likely be replaced by a full-fledged (and more correctly placed) migration process in the future. /// - public Task ImportFromStableAsync() + public Task ImportFromStableAsync(StableStorage stableStorage) { - var stableStorage = GetStableStorage?.Invoke(); if (stableStorage == null) { diff --git a/osu.Game/Database/StableImportManager.cs b/osu.Game/Database/StableImportManager.cs index 46c6aab2fe..8baf03b7a3 100644 --- a/osu.Game/Database/StableImportManager.cs +++ b/osu.Game/Database/StableImportManager.cs @@ -3,9 +3,8 @@ using System; using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; -using JetBrains.Annotations; +using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; @@ -19,7 +18,7 @@ using osu.Game.Scoring; using osu.Game.Skinning; namespace osu.Game.Database -{ +{ public class StableImportManager : Component { [Resolved] @@ -42,22 +41,24 @@ namespace osu.Game.Database private StableStorage cachedStorage; + public bool SupportsImportFromStable => RuntimeInfo.IsDesktop; + public async Task ImportFromStableAsync(StableContent content) { - //var stableStorage = await getStableStorage().ConfigureAwait(false); + var stableStorage = await getStableStorage().ConfigureAwait(false); var importTasks = new List(); if (content.HasFlagFast(StableContent.Beatmaps)) - importTasks.Add(beatmaps.ImportFromStableAsync()); + importTasks.Add(beatmaps.ImportFromStableAsync(stableStorage)); if (content.HasFlagFast(StableContent.Collections)) - importTasks.Add(collections.ImportFromStableAsync()); + importTasks.Add(collections.ImportFromStableAsync(stableStorage)); if (content.HasFlagFast(StableContent.Scores)) - importTasks.Add(scores.ImportFromStableAsync()); + importTasks.Add(scores.ImportFromStableAsync(stableStorage)); if (content.HasFlagFast(StableContent.Skins)) - importTasks.Add(skins.ImportFromStableAsync()); + importTasks.Add(skins.ImportFromStableAsync(stableStorage)); await Task.WhenAll(importTasks.ToArray()).ConfigureAwait(false); } @@ -83,10 +84,10 @@ namespace osu.Game.Database [Flags] public enum StableContent { - Beatmaps = 0x1, - Scores = 0x2, - Skins = 0x3, - Collections = 0x4, + Beatmaps = 1, + Scores = 2, + Skins = 4, + Collections = 8, All = Beatmaps | Scores | Skins | Collections } } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index f860cd8dd2..1cfe8ace43 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -100,6 +100,9 @@ namespace osu.Game [Cached] private readonly DifficultyRecommender difficultyRecommender = new DifficultyRecommender(); + [Cached] + private readonly StableImportManager stableImportManager = new StableImportManager(); + [Cached] private readonly ScreenshotManager screenshotManager = new ScreenshotManager(); @@ -694,6 +697,7 @@ namespace osu.Game }, Add, true); loadComponentSingleFile(difficultyRecommender, Add); + loadComponentSingleFile(stableImportManager, Add); loadComponentSingleFile(screenshotManager, Add); diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs index 848ce381a9..9bd360679e 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs @@ -8,6 +8,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Collections; +using osu.Game.Database; using osu.Game.Graphics.UserInterface; using osu.Game.Scoring; using osu.Game.Skinning; @@ -29,9 +30,9 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance private TriangleButton undeleteButton; [BackgroundDependencyLoader(permitNulls: true)] - private void load(BeatmapManager beatmaps, ScoreManager scores, SkinManager skins, [CanBeNull] CollectionManager collectionManager, DialogOverlay dialogOverlay) + private void load(BeatmapManager beatmaps, ScoreManager scores, SkinManager skins, [CanBeNull] CollectionManager collectionManager, StableImportManager stableImportManager, DialogOverlay dialogOverlay) { - if (beatmaps.SupportsImportFromStable) + if (stableImportManager.SupportsImportFromStable) { Add(importBeatmapsButton = new SettingsButton { @@ -39,7 +40,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance Action = () => { importBeatmapsButton.Enabled.Value = false; - beatmaps.ImportFromStableAsync().ContinueWith(t => Schedule(() => importBeatmapsButton.Enabled.Value = true)); + stableImportManager.ImportFromStableAsync(StableContent.Beatmaps).ContinueWith(t => Schedule(() => importBeatmapsButton.Enabled.Value = true)); } }); } @@ -57,7 +58,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance } }); - if (scores.SupportsImportFromStable) + if (stableImportManager.SupportsImportFromStable) { Add(importScoresButton = new SettingsButton { @@ -65,7 +66,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance Action = () => { importScoresButton.Enabled.Value = false; - scores.ImportFromStableAsync().ContinueWith(t => Schedule(() => importScoresButton.Enabled.Value = true)); + stableImportManager.ImportFromStableAsync(StableContent.Scores).ContinueWith(t => Schedule(() => importScoresButton.Enabled.Value = true)); } }); } @@ -83,7 +84,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance } }); - if (skins.SupportsImportFromStable) + if (stableImportManager.SupportsImportFromStable) { Add(importSkinsButton = new SettingsButton { @@ -91,7 +92,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance Action = () => { importSkinsButton.Enabled.Value = false; - skins.ImportFromStableAsync().ContinueWith(t => Schedule(() => importSkinsButton.Enabled.Value = true)); + stableImportManager.ImportFromStableAsync(StableContent.Skins).ContinueWith(t => Schedule(() => importSkinsButton.Enabled.Value = true)); } }); } @@ -111,7 +112,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance if (collectionManager != null) { - if (collectionManager.SupportsImportFromStable) + if (stableImportManager.SupportsImportFromStable) { Add(importCollectionsButton = new SettingsButton { @@ -119,7 +120,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance Action = () => { importCollectionsButton.Enabled.Value = false; - collectionManager.ImportFromStableAsync().ContinueWith(t => Schedule(() => importCollectionsButton.Enabled.Value = true)); + stableImportManager.ImportFromStableAsync(StableContent.Collections).ContinueWith(t => Schedule(() => importCollectionsButton.Enabled.Value = true)); } }); } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 215700d87c..ff72f36c75 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -22,7 +22,6 @@ using osu.Game.Rulesets.Mods; using osu.Game.Screens.Edit; using osu.Game.Screens.Menu; using osu.Game.Screens.Select.Options; -using osu.Game.Skinning; using osuTK; using osuTK.Graphics; using osuTK.Input; @@ -35,9 +34,9 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; using osu.Game.Collections; using osu.Game.Graphics.UserInterface; -using osu.Game.Scoring; using System.Diagnostics; using osu.Game.Screens.Play; +using osu.Game.Database; namespace osu.Game.Screens.Select { @@ -101,7 +100,7 @@ namespace osu.Game.Screens.Select private MusicController music { get; set; } [BackgroundDependencyLoader(true)] - private void load(AudioManager audio, DialogOverlay dialog, OsuColour colours, SkinManager skins, ScoreManager scores, CollectionManager collections, ManageCollectionsDialog manageCollectionsDialog, DifficultyRecommender recommender) + private void load(AudioManager audio, DialogOverlay dialog, OsuColour colours, StableImportManager stableImportManager, ManageCollectionsDialog manageCollectionsDialog, DifficultyRecommender recommender) { // initial value transfer is required for FilterControl (it uses our re-cached bindables in its async load for the initial filter). transferRulesetValue(); @@ -287,13 +286,7 @@ namespace osu.Game.Screens.Select { dialogOverlay.Push(new ImportFromStablePopup(() => { - Task.Run(beatmaps.ImportFromStableAsync) - .ContinueWith(_ => - { - Task.Run(scores.ImportFromStableAsync); - Task.Run(collections.ImportFromStableAsync); - }, TaskContinuationOptions.OnlyOnRanToCompletion); - Task.Run(skins.ImportFromStableAsync); + Task.Run(() => stableImportManager.ImportFromStableAsync(StableContent.All)); })); } }); From 325a689d6553b9988c7c4378103135d960600376 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sun, 9 May 2021 18:15:21 +0200 Subject: [PATCH 197/839] Order imports depending on beatmap imports if any is running. --- osu.Game/Database/StableImportManager.cs | 25 +++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/osu.Game/Database/StableImportManager.cs b/osu.Game/Database/StableImportManager.cs index 8baf03b7a3..24b8aa9f6a 100644 --- a/osu.Game/Database/StableImportManager.cs +++ b/osu.Game/Database/StableImportManager.cs @@ -48,18 +48,29 @@ namespace osu.Game.Database var stableStorage = await getStableStorage().ConfigureAwait(false); var importTasks = new List(); + Task beatmapImportTask = default; if (content.HasFlagFast(StableContent.Beatmaps)) - importTasks.Add(beatmaps.ImportFromStableAsync(stableStorage)); - - if (content.HasFlagFast(StableContent.Collections)) - importTasks.Add(collections.ImportFromStableAsync(stableStorage)); - - if (content.HasFlagFast(StableContent.Scores)) - importTasks.Add(scores.ImportFromStableAsync(stableStorage)); + importTasks.Add(beatmapImportTask = beatmaps.ImportFromStableAsync(stableStorage)); if (content.HasFlagFast(StableContent.Skins)) importTasks.Add(skins.ImportFromStableAsync(stableStorage)); + if (content.HasFlagFast(StableContent.Collections)) + { + if (beatmapImportTask != null) + importTasks.Add(beatmapImportTask.ContinueWith(_ => collections.ImportFromStableAsync(stableStorage), TaskContinuationOptions.OnlyOnRanToCompletion)); + else + importTasks.Add(collections.ImportFromStableAsync(stableStorage)); + } + + if (content.HasFlagFast(StableContent.Scores)) + { + if (beatmapImportTask != null) + importTasks.Add(beatmapImportTask.ContinueWith(_ => scores.ImportFromStableAsync(stableStorage), TaskContinuationOptions.OnlyOnRanToCompletion)); + else + importTasks.Add(scores.ImportFromStableAsync(stableStorage)); + } + await Task.WhenAll(importTasks.ToArray()).ConfigureAwait(false); } From 481b0a0125c68f311d4678f910f3cbf47bb3fef1 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sun, 9 May 2021 19:50:43 +0200 Subject: [PATCH 198/839] Add StableDirectoryLocationDialog --- osu.Game/Database/StableImportManager.cs | 10 ++--- .../StableDirectoryLocationDialog.cs | 38 +++++++++++++++++++ 2 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs diff --git a/osu.Game/Database/StableImportManager.cs b/osu.Game/Database/StableImportManager.cs index 24b8aa9f6a..09a08920dd 100644 --- a/osu.Game/Database/StableImportManager.cs +++ b/osu.Game/Database/StableImportManager.cs @@ -9,16 +9,16 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Platform; -using osu.Framework.Screens; using osu.Game.Beatmaps; using osu.Game.Collections; using osu.Game.IO; +using osu.Game.Overlays; using osu.Game.Overlays.Settings.Sections.Maintenance; using osu.Game.Scoring; using osu.Game.Skinning; namespace osu.Game.Database -{ +{ public class StableImportManager : Component { [Resolved] @@ -34,7 +34,7 @@ namespace osu.Game.Database private CollectionManager collections { get; set; } [Resolved] - private OsuGame game { get; set; } + private DialogOverlay dialogOverlay { get; set; } [Resolved(CanBeNull = true)] private DesktopGameHost desktopGameHost { get; set; } @@ -83,8 +83,8 @@ namespace osu.Game.Database if (cachedStorage != null) return cachedStorage; - var taskCompletionSource = new TaskCompletionSource(); - Schedule(() => game.PerformFromScreen(t => t.Push(new StableDirectorySelectScreen(taskCompletionSource)))); + var taskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + dialogOverlay.Push(new StableDirectoryLocationDialog(taskCompletionSource)); var stablePath = await taskCompletionSource.Task.ConfigureAwait(false); return cachedStorage = new StableStorage(stablePath, desktopGameHost); diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs new file mode 100644 index 0000000000..273ee5dc89 --- /dev/null +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.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 System.Threading.Tasks; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Screens; +using osu.Game.Overlays.Dialog; + +namespace osu.Game.Overlays.Settings.Sections.Maintenance +{ + public class StableDirectoryLocationDialog : PopupDialog + { + [Resolved] + private OsuGame game { get; set; } + + public StableDirectoryLocationDialog(TaskCompletionSource taskCompletionSource) + { + HeaderText = "Failed to automatically locate a stable installation."; + BodyText = "osu! failed to automatically locate a stable installation. Maybe you can tell osu! where it is located?"; + Icon = FontAwesome.Solid.QuestionCircle; + + Buttons = new PopupDialogButton[] + { + new PopupDialogOkButton + { + Text = "Sure! I know where it is located!", + Action = () => Schedule(() => game.PerformFromScreen(screen => screen.Push(new StableDirectorySelectScreen(taskCompletionSource)))) + }, + new PopupDialogCancelButton + { + Text = "Actually I don't have osu!stable installed.", + Action = () => taskCompletionSource.TrySetCanceled() + } + }; + } + } +} From 8ba50b185445358419ed2b01508d4859e9273e8c Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sun, 9 May 2021 19:52:26 +0200 Subject: [PATCH 199/839] Bring back injected dependency incorrectly marked as unused. --- osu.Game/Database/StableImportManager.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Database/StableImportManager.cs b/osu.Game/Database/StableImportManager.cs index 09a08920dd..e4c89062f2 100644 --- a/osu.Game/Database/StableImportManager.cs +++ b/osu.Game/Database/StableImportManager.cs @@ -33,6 +33,9 @@ namespace osu.Game.Database [Resolved] private CollectionManager collections { get; set; } + [Resolved] + private OsuGame game { get; set; } + [Resolved] private DialogOverlay dialogOverlay { get; set; } From a7b740fd1de6cd6211c19d52697925f6b86e79fa Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sun, 9 May 2021 21:29:41 +0200 Subject: [PATCH 200/839] Reword ImportFromStablePopup and display the popup regardless of whether a stable install is detected. --- osu.Game/Database/StableImportManager.cs | 2 +- osu.Game/Screens/Select/ImportFromStablePopup.cs | 2 +- osu.Game/Screens/Select/SongSelect.cs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Database/StableImportManager.cs b/osu.Game/Database/StableImportManager.cs index e4c89062f2..475077c54e 100644 --- a/osu.Game/Database/StableImportManager.cs +++ b/osu.Game/Database/StableImportManager.cs @@ -87,7 +87,7 @@ namespace osu.Game.Database return cachedStorage; var taskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - dialogOverlay.Push(new StableDirectoryLocationDialog(taskCompletionSource)); + Schedule(() => dialogOverlay.Push(new StableDirectoryLocationDialog(taskCompletionSource))); var stablePath = await taskCompletionSource.Task.ConfigureAwait(false); return cachedStorage = new StableStorage(stablePath, desktopGameHost); diff --git a/osu.Game/Screens/Select/ImportFromStablePopup.cs b/osu.Game/Screens/Select/ImportFromStablePopup.cs index 8dab83b24c..e3a1505518 100644 --- a/osu.Game/Screens/Select/ImportFromStablePopup.cs +++ b/osu.Game/Screens/Select/ImportFromStablePopup.cs @@ -12,7 +12,7 @@ namespace osu.Game.Screens.Select public ImportFromStablePopup(Action importFromStable) { HeaderText = @"You have no beatmaps!"; - BodyText = "An existing copy of osu! was found, though.\nWould you like to import your beatmaps, skins, collections and scores?\nThis will create a second copy of all files on disk."; + BodyText = "You can import files from over a stable install, though.\nWould you like to import your beatmaps, skins, collections and scores?\nThis will create a second copy of all files on disk."; Icon = FontAwesome.Solid.Plane; diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index ff72f36c75..d8ad752151 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -281,8 +281,8 @@ namespace osu.Game.Screens.Select { Schedule(() => { - // if we have no beatmaps but osu-stable is found, let's prompt the user to import. - if (!beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.Minimal).Any() && beatmaps.StableInstallationAvailable) + // if we have no beatmaps, let's prompt the user to import from over a stable install if he has one. + if (!beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.Minimal).Any() && stableImportManager.SupportsImportFromStable) { dialogOverlay.Push(new ImportFromStablePopup(() => { From dabe8bd4c7f358e19edc3ac57130093c170d6d64 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Mon, 10 May 2021 12:21:51 +0200 Subject: [PATCH 201/839] Fix code inspections and remove now unused code. --- osu.Game/Collections/CollectionManager.cs | 7 ------- osu.Game/Database/ArchiveModelManager.cs | 12 ------------ osu.Game/Database/StableImportManager.cs | 13 ++++--------- osu.Game/OsuGame.cs | 4 ---- .../Maintenance/StableDirectorySelectScreen.cs | 2 +- 5 files changed, 5 insertions(+), 33 deletions(-) diff --git a/osu.Game/Collections/CollectionManager.cs b/osu.Game/Collections/CollectionManager.cs index fbd12cf672..e707b463cb 100644 --- a/osu.Game/Collections/CollectionManager.cs +++ b/osu.Game/Collections/CollectionManager.cs @@ -8,7 +8,6 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; -using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -95,17 +94,11 @@ namespace osu.Game.Collections /// public Action PostNotification { protected get; set; } - /// - /// Set a storage with access to an osu-stable install for import purposes. - /// - public Func GetStableStorage { private get; set; } - /// /// This is a temporary method and will likely be replaced by a full-fledged (and more correctly placed) migration process in the future. /// public Task ImportFromStableAsync(StableStorage stableStorage) { - if (stableStorage == null) { Logger.Log("No osu!stable installation available!", LoggingTarget.Information, LogLevel.Error); diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 38a6af4654..93e2880ba0 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -10,7 +10,6 @@ using System.Threading.Tasks; using Humanizer; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; -using osu.Framework; using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Extensions.IEnumerableExtensions; @@ -667,16 +666,6 @@ namespace osu.Game.Database #region osu-stable import - /// - /// Set a storage with access to an osu-stable install for import purposes. - /// - public Func GetStableStorage { private get; set; } - - /// - /// Denotes whether an osu-stable installation is present to perform automated imports from. - /// - public bool StableInstallationAvailable => GetStableStorage?.Invoke() != null; - /// /// The relative path from osu-stable's data directory to import items from. /// @@ -700,7 +689,6 @@ namespace osu.Game.Database /// public Task ImportFromStableAsync(StableStorage stableStorage) { - if (stableStorage == null) { Logger.Log("No osu!stable installation available!", LoggingTarget.Information, LogLevel.Error); diff --git a/osu.Game/Database/StableImportManager.cs b/osu.Game/Database/StableImportManager.cs index 475077c54e..6f8225519d 100644 --- a/osu.Game/Database/StableImportManager.cs +++ b/osu.Game/Database/StableImportManager.cs @@ -60,18 +60,14 @@ namespace osu.Game.Database if (content.HasFlagFast(StableContent.Collections)) { - if (beatmapImportTask != null) - importTasks.Add(beatmapImportTask.ContinueWith(_ => collections.ImportFromStableAsync(stableStorage), TaskContinuationOptions.OnlyOnRanToCompletion)); - else - importTasks.Add(collections.ImportFromStableAsync(stableStorage)); + importTasks.Add(beatmapImportTask?.ContinueWith(_ => collections.ImportFromStableAsync(stableStorage), TaskContinuationOptions.OnlyOnRanToCompletion) + ?? collections.ImportFromStableAsync(stableStorage)); } if (content.HasFlagFast(StableContent.Scores)) { - if (beatmapImportTask != null) - importTasks.Add(beatmapImportTask.ContinueWith(_ => scores.ImportFromStableAsync(stableStorage), TaskContinuationOptions.OnlyOnRanToCompletion)); - else - importTasks.Add(scores.ImportFromStableAsync(stableStorage)); + importTasks.Add(beatmapImportTask?.ContinueWith(_ => scores.ImportFromStableAsync(stableStorage), TaskContinuationOptions.OnlyOnRanToCompletion) + ?? scores.ImportFromStableAsync(stableStorage)); } await Task.WhenAll(importTasks.ToArray()).ConfigureAwait(false); @@ -92,7 +88,6 @@ namespace osu.Game.Database return cachedStorage = new StableStorage(stablePath, desktopGameHost); } - } [Flags] diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 1cfe8ace43..06e0b6e9bf 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -569,14 +569,11 @@ namespace osu.Game // todo: all archive managers should be able to be looped here. SkinManager.PostNotification = n => notifications.Post(n); - SkinManager.GetStableStorage = GetStorageForStableInstall; BeatmapManager.PostNotification = n => notifications.Post(n); - BeatmapManager.GetStableStorage = GetStorageForStableInstall; BeatmapManager.PresentImport = items => PresentBeatmap(items.First()); ScoreManager.PostNotification = n => notifications.Post(n); - ScoreManager.GetStableStorage = GetStorageForStableInstall; ScoreManager.PresentImport = items => PresentScore(items.First()); // make config aware of how to lookup skins for on-screen display purposes. @@ -693,7 +690,6 @@ namespace osu.Game loadComponentSingleFile(new CollectionManager(Storage) { PostNotification = n => notifications.Post(n), - GetStableStorage = GetStorageForStableInstall }, Add, true); loadComponentSingleFile(difficultyRecommender, Add); diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs index d935bcf526..6065122545 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs @@ -28,7 +28,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance } protected override void OnSelection(DirectoryInfo directory) - { + { taskCompletionSource.TrySetResult(directory.FullName); this.Exit(); } From e15e8068d3bd33170fd19497f2d2d3a4a73f8365 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Thu, 13 May 2021 12:09:37 +0200 Subject: [PATCH 202/839] Reword StableDirectoryLocationDialog. Co-authored-by: Dean Herbert --- .../Sections/Maintenance/StableDirectoryLocationDialog.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs index 273ee5dc89..298f7d2433 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs @@ -16,7 +16,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance public StableDirectoryLocationDialog(TaskCompletionSource taskCompletionSource) { - HeaderText = "Failed to automatically locate a stable installation."; + HeaderText = "Failed to automatically locate an osu!stable installation."; BodyText = "osu! failed to automatically locate a stable installation. Maybe you can tell osu! where it is located?"; Icon = FontAwesome.Solid.QuestionCircle; From f60dbbfbbd2b53cb0d1207b82333b89bbb77ceef Mon Sep 17 00:00:00 2001 From: Lucas A Date: Thu, 13 May 2021 12:10:26 +0200 Subject: [PATCH 203/839] Reword import dialogs. Co-authored-by: Dean Herbert --- .../Sections/Maintenance/StableDirectoryLocationDialog.cs | 2 +- osu.Game/Screens/Select/ImportFromStablePopup.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs index 298f7d2433..904c9deaae 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs @@ -17,7 +17,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance public StableDirectoryLocationDialog(TaskCompletionSource taskCompletionSource) { HeaderText = "Failed to automatically locate an osu!stable installation."; - BodyText = "osu! failed to automatically locate a stable installation. Maybe you can tell osu! where it is located?"; + BodyText = "An existing install could not be located. If you know where it is, you can help locate it."; Icon = FontAwesome.Solid.QuestionCircle; Buttons = new PopupDialogButton[] diff --git a/osu.Game/Screens/Select/ImportFromStablePopup.cs b/osu.Game/Screens/Select/ImportFromStablePopup.cs index e3a1505518..d8137432bd 100644 --- a/osu.Game/Screens/Select/ImportFromStablePopup.cs +++ b/osu.Game/Screens/Select/ImportFromStablePopup.cs @@ -12,7 +12,7 @@ namespace osu.Game.Screens.Select public ImportFromStablePopup(Action importFromStable) { HeaderText = @"You have no beatmaps!"; - BodyText = "You can import files from over a stable install, though.\nWould you like to import your beatmaps, skins, collections and scores?\nThis will create a second copy of all files on disk."; + BodyText = "Would you like to import your beatmaps, skins, collections and scores from an existing osu!stable installation?\nThis will create a second copy of all files on disk."; Icon = FontAwesome.Solid.Plane; From bec06cfac7827423bbea001dd9cd5fd039b8bb09 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Thu, 13 May 2021 15:17:33 +0200 Subject: [PATCH 204/839] Reword `StableDirectoryLocationDialog` header Co-authored-by: Salman Ahmed --- .../Sections/Maintenance/StableDirectorySelectScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs index 6065122545..f7c7934c63 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs @@ -18,7 +18,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance protected override OsuSpriteText CreateHeader() => new OsuSpriteText { - Text = "Please select stable location", + Text = "Please select your osu!stable install location", Font = OsuFont.Default.With(size: 40) }; From 41fafdf643f1a3858907559ff4bddbc5fa538f00 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 14 May 2021 17:24:36 +0200 Subject: [PATCH 205/839] Remove now unreachable code paths. --- osu.Game/Collections/CollectionManager.cs | 6 ------ osu.Game/Database/ArchiveModelManager.cs | 6 ------ 2 files changed, 12 deletions(-) diff --git a/osu.Game/Collections/CollectionManager.cs b/osu.Game/Collections/CollectionManager.cs index e707b463cb..3a63587b30 100644 --- a/osu.Game/Collections/CollectionManager.cs +++ b/osu.Game/Collections/CollectionManager.cs @@ -99,12 +99,6 @@ namespace osu.Game.Collections /// public Task ImportFromStableAsync(StableStorage stableStorage) { - if (stableStorage == null) - { - Logger.Log("No osu!stable installation available!", LoggingTarget.Information, LogLevel.Error); - return Task.CompletedTask; - } - if (!stableStorage.Exists(database_name)) { // This handles situations like when the user does not have a collections.db file diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 93e2880ba0..550daf36b5 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -689,12 +689,6 @@ namespace osu.Game.Database /// public Task ImportFromStableAsync(StableStorage stableStorage) { - if (stableStorage == null) - { - Logger.Log("No osu!stable installation available!", LoggingTarget.Information, LogLevel.Error); - return Task.CompletedTask; - } - var storage = PrepareStableStorage(stableStorage); if (!storage.ExistsDirectory(ImportFromStablePath)) From fe11426238eaf2713414ddc63b0499df4c4b66ba Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sun, 16 May 2021 15:35:44 +0200 Subject: [PATCH 206/839] Disable appearance of the stable import prompt waiting for user interaction in tests, which caused them to fail. --- .../Navigation/TestScenePerformFromScreen.cs | 21 ++++++++++++------- osu.Game/Screens/Select/SongSelect.cs | 4 +++- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs index 2791952b66..078bb817f8 100644 --- a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs +++ b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs @@ -37,17 +37,17 @@ namespace osu.Game.Tests.Visual.Navigation [Test] public void TestPerformAtSongSelect() { - PushAndConfirm(() => new PlaySongSelect()); + PushAndConfirm(() => new TestPlaySongSelect()); - AddStep("perform immediately", () => Game.PerformFromScreen(_ => actionPerformed = true, new[] { typeof(PlaySongSelect) })); + AddStep("perform immediately", () => Game.PerformFromScreen(_ => actionPerformed = true, new[] { typeof(TestPlaySongSelect) })); AddAssert("did perform", () => actionPerformed); - AddAssert("screen didn't change", () => Game.ScreenStack.CurrentScreen is PlaySongSelect); + AddAssert("screen didn't change", () => Game.ScreenStack.CurrentScreen is TestPlaySongSelect); } [Test] public void TestPerformAtMenuFromSongSelect() { - PushAndConfirm(() => new PlaySongSelect()); + PushAndConfirm(() => new TestPlaySongSelect()); AddStep("try to perform", () => Game.PerformFromScreen(_ => actionPerformed = true)); AddUntilStep("returned to menu", () => Game.ScreenStack.CurrentScreen is MainMenu); @@ -57,18 +57,18 @@ namespace osu.Game.Tests.Visual.Navigation [Test] public void TestPerformAtSongSelectFromPlayerLoader() { - PushAndConfirm(() => new PlaySongSelect()); + PushAndConfirm(() => new TestPlaySongSelect()); PushAndConfirm(() => new PlayerLoader(() => new SoloPlayer())); - AddStep("try to perform", () => Game.PerformFromScreen(_ => actionPerformed = true, new[] { typeof(PlaySongSelect) })); - AddUntilStep("returned to song select", () => Game.ScreenStack.CurrentScreen is PlaySongSelect); + AddStep("try to perform", () => Game.PerformFromScreen(_ => actionPerformed = true, new[] { typeof(TestPlaySongSelect) })); + AddUntilStep("returned to song select", () => Game.ScreenStack.CurrentScreen is TestPlaySongSelect); AddAssert("did perform", () => actionPerformed); } [Test] public void TestPerformAtMenuFromPlayerLoader() { - PushAndConfirm(() => new PlaySongSelect()); + PushAndConfirm(() => new TestPlaySongSelect()); PushAndConfirm(() => new PlayerLoader(() => new SoloPlayer())); AddStep("try to perform", () => Game.PerformFromScreen(_ => actionPerformed = true)); @@ -187,5 +187,10 @@ namespace osu.Game.Tests.Visual.Navigation return base.OnExiting(next); } } + + public class TestPlaySongSelect : PlaySongSelect + { + protected override bool DisplayStableImportPrompt => false; + } } } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index d8ad752151..5a48ee7606 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -51,6 +51,8 @@ namespace osu.Game.Screens.Select protected virtual bool ShowFooter => true; + protected virtual bool DisplayStableImportPrompt => true; + /// /// Can be null if is false. /// @@ -282,7 +284,7 @@ namespace osu.Game.Screens.Select Schedule(() => { // if we have no beatmaps, let's prompt the user to import from over a stable install if he has one. - if (!beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.Minimal).Any() && stableImportManager.SupportsImportFromStable) + if (!beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.Minimal).Any() && stableImportManager.SupportsImportFromStable && DisplayStableImportPrompt) { dialogOverlay.Push(new ImportFromStablePopup(() => { From ed4c025c7e79669b198eb331e8d6fcc20ec04a9e Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sun, 16 May 2021 17:14:23 +0200 Subject: [PATCH 207/839] Fix other tests and move TestPlaySongSelect class declaration. --- .../Navigation/TestScenePerformFromScreen.cs | 7 +--- .../Navigation/TestSceneScreenNavigation.cs | 34 ++++++++++--------- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs index 078bb817f8..3cedaf9d45 100644 --- a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs +++ b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs @@ -11,8 +11,8 @@ using osu.Game.Overlays; using osu.Game.Screens; using osu.Game.Screens.Menu; using osu.Game.Screens.Play; -using osu.Game.Screens.Select; using osuTK.Input; +using static osu.Game.Tests.Visual.Navigation.TestSceneScreenNavigation; namespace osu.Game.Tests.Visual.Navigation { @@ -187,10 +187,5 @@ namespace osu.Game.Tests.Visual.Navigation return base.OnExiting(next); } } - - public class TestPlaySongSelect : PlaySongSelect - { - protected override bool DisplayStableImportPrompt => false; - } } } diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index 859cefe3a9..253e448bb4 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -34,9 +34,9 @@ namespace osu.Game.Tests.Visual.Navigation [Test] public void TestExitSongSelectWithEscape() { - TestSongSelect songSelect = null; + TestPlaySongSelect songSelect = null; - PushAndConfirm(() => songSelect = new TestSongSelect()); + PushAndConfirm(() => songSelect = new TestPlaySongSelect()); AddStep("Show mods overlay", () => songSelect.ModSelectOverlay.Show()); AddAssert("Overlay was shown", () => songSelect.ModSelectOverlay.State.Value == Visibility.Visible); pushEscape(); @@ -51,9 +51,9 @@ namespace osu.Game.Tests.Visual.Navigation [Test] public void TestOpenModSelectOverlayUsingAction() { - TestSongSelect songSelect = null; + TestPlaySongSelect songSelect = null; - PushAndConfirm(() => songSelect = new TestSongSelect()); + PushAndConfirm(() => songSelect = new TestPlaySongSelect()); AddStep("Show mods overlay", () => InputManager.Key(Key.F1)); AddAssert("Overlay was shown", () => songSelect.ModSelectOverlay.State.Value == Visibility.Visible); } @@ -63,7 +63,7 @@ namespace osu.Game.Tests.Visual.Navigation { Player player = null; - PushAndConfirm(() => new TestSongSelect()); + PushAndConfirm(() => new TestPlaySongSelect()); AddStep("import beatmap", () => ImportBeatmapTest.LoadQuickOszIntoOsu(Game).Wait()); @@ -89,7 +89,7 @@ namespace osu.Game.Tests.Visual.Navigation WorkingBeatmap beatmap() => Game.Beatmap.Value; - PushAndConfirm(() => new TestSongSelect()); + PushAndConfirm(() => new TestPlaySongSelect()); AddStep("import beatmap", () => ImportBeatmapTest.LoadQuickOszIntoOsu(Game).Wait()); @@ -113,7 +113,7 @@ namespace osu.Game.Tests.Visual.Navigation WorkingBeatmap beatmap() => Game.Beatmap.Value; - PushAndConfirm(() => new TestSongSelect()); + PushAndConfirm(() => new TestPlaySongSelect()); AddStep("import beatmap", () => ImportBeatmapTest.LoadOszIntoOsu(Game, virtualTrack: true).Wait()); @@ -139,9 +139,9 @@ namespace osu.Game.Tests.Visual.Navigation [Test] public void TestMenuMakesMusic() { - TestSongSelect songSelect = null; + TestPlaySongSelect songSelect = null; - PushAndConfirm(() => songSelect = new TestSongSelect()); + PushAndConfirm(() => songSelect = new TestPlaySongSelect()); AddUntilStep("wait for no track", () => Game.MusicController.CurrentTrack.IsDummyDevice); @@ -153,9 +153,9 @@ namespace osu.Game.Tests.Visual.Navigation [Test] public void TestExitSongSelectWithClick() { - TestSongSelect songSelect = null; + TestPlaySongSelect songSelect = null; - PushAndConfirm(() => songSelect = new TestSongSelect()); + PushAndConfirm(() => songSelect = new TestPlaySongSelect()); AddStep("Show mods overlay", () => songSelect.ModSelectOverlay.Show()); AddAssert("Overlay was shown", () => songSelect.ModSelectOverlay.State.Value == Visibility.Visible); AddStep("Move mouse to backButton", () => InputManager.MoveMouseTo(backButtonPosition)); @@ -213,9 +213,9 @@ namespace osu.Game.Tests.Visual.Navigation [Test] public void TestModSelectInput() { - TestSongSelect songSelect = null; + TestPlaySongSelect songSelect = null; - PushAndConfirm(() => songSelect = new TestSongSelect()); + PushAndConfirm(() => songSelect = new TestPlaySongSelect()); AddStep("Show mods overlay", () => songSelect.ModSelectOverlay.Show()); @@ -234,9 +234,9 @@ namespace osu.Game.Tests.Visual.Navigation [Test] public void TestBeatmapOptionsInput() { - TestSongSelect songSelect = null; + TestPlaySongSelect songSelect = null; - PushAndConfirm(() => songSelect = new TestSongSelect()); + PushAndConfirm(() => songSelect = new TestPlaySongSelect()); AddStep("Show options overlay", () => songSelect.BeatmapOptionsOverlay.Show()); @@ -312,11 +312,13 @@ namespace osu.Game.Tests.Visual.Navigation ConfirmAtMainMenu(); } - private class TestSongSelect : PlaySongSelect + public class TestPlaySongSelect : PlaySongSelect { public ModSelectOverlay ModSelectOverlay => ModSelect; public BeatmapOptionsOverlay BeatmapOptionsOverlay => BeatmapOptions; + + protected override bool DisplayStableImportPrompt => false; } } } From db255e6814fa463c2a69893bde37cbcde0b58576 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sun, 16 May 2021 17:49:49 +0200 Subject: [PATCH 208/839] Mark StableImportManager as potentially null in tests. (StableImportManager is added to the DI in OsuGame and not in the OsuGameBase) --- .../Settings/Sections/Maintenance/GeneralSettings.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs index 9bd360679e..20fc0e962a 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs @@ -30,9 +30,9 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance private TriangleButton undeleteButton; [BackgroundDependencyLoader(permitNulls: true)] - private void load(BeatmapManager beatmaps, ScoreManager scores, SkinManager skins, [CanBeNull] CollectionManager collectionManager, StableImportManager stableImportManager, DialogOverlay dialogOverlay) + private void load(BeatmapManager beatmaps, ScoreManager scores, SkinManager skins, [CanBeNull] CollectionManager collectionManager, [CanBeNull] StableImportManager stableImportManager, DialogOverlay dialogOverlay) { - if (stableImportManager.SupportsImportFromStable) + if (stableImportManager?.SupportsImportFromStable ?? false) { Add(importBeatmapsButton = new SettingsButton { @@ -58,7 +58,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance } }); - if (stableImportManager.SupportsImportFromStable) + if (stableImportManager?.SupportsImportFromStable ?? false) { Add(importScoresButton = new SettingsButton { @@ -84,7 +84,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance } }); - if (stableImportManager.SupportsImportFromStable) + if (stableImportManager?.SupportsImportFromStable ?? false) { Add(importSkinsButton = new SettingsButton { @@ -112,7 +112,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance if (collectionManager != null) { - if (stableImportManager.SupportsImportFromStable) + if (stableImportManager?.SupportsImportFromStable ?? false) { Add(importCollectionsButton = new SettingsButton { From a38fc1a2e0bff4bcd36e34e926be4c98d3fcf7e3 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Mon, 17 May 2021 13:04:49 +0200 Subject: [PATCH 209/839] Override text header. --- .../Sections/Maintenance/StableDirectorySelectScreen.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs index f7c7934c63..4ea53a3fc1 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs @@ -4,9 +4,8 @@ using System.IO; using System.Linq; using System.Threading.Tasks; +using osu.Framework.Localisation; using osu.Framework.Screens; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; namespace osu.Game.Overlays.Settings.Sections.Maintenance { @@ -16,11 +15,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance protected override bool IsValidDirectory(DirectoryInfo info) => info?.GetFiles("osu!.*.cfg").Any() ?? false; - protected override OsuSpriteText CreateHeader() => new OsuSpriteText - { - Text = "Please select your osu!stable install location", - Font = OsuFont.Default.With(size: 40) - }; + public override LocalisableString HeaderText => "Please select your osu!stable install location"; public StableDirectorySelectScreen(TaskCompletionSource taskCompletionSource) { From 5ca4fd5ab4f53c61f88a4900b97c6c4150fb43b0 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Mon, 17 May 2021 13:28:59 +0200 Subject: [PATCH 210/839] Block overlays to prevent getting into a bad state. --- .../Sections/Maintenance/StableDirectorySelectScreen.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs index 4ea53a3fc1..4aea05fb14 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs @@ -13,6 +13,8 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance { private readonly TaskCompletionSource taskCompletionSource; + protected override OverlayActivation InitialOverlayActivationMode => OverlayActivation.Disabled; + protected override bool IsValidDirectory(DirectoryInfo info) => info?.GetFiles("osu!.*.cfg").Any() ?? false; public override LocalisableString HeaderText => "Please select your osu!stable install location"; @@ -28,10 +30,10 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance this.Exit(); } - public override bool OnBackButton() + public override bool OnExiting(IScreen next) { taskCompletionSource.TrySetCanceled(); - return base.OnBackButton(); + return base.OnExiting(next); } } } From 6110a847aa82a2e0dd5c0bf90aa8c1db6bee1399 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Mon, 17 May 2021 16:30:13 +0200 Subject: [PATCH 211/839] Simplify import ordering logic by making beatmapImportTask non-nullable. --- osu.Game/Database/StableImportManager.cs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/osu.Game/Database/StableImportManager.cs b/osu.Game/Database/StableImportManager.cs index 6f8225519d..67f91d3bdb 100644 --- a/osu.Game/Database/StableImportManager.cs +++ b/osu.Game/Database/StableImportManager.cs @@ -51,7 +51,7 @@ namespace osu.Game.Database var stableStorage = await getStableStorage().ConfigureAwait(false); var importTasks = new List(); - Task beatmapImportTask = default; + Task beatmapImportTask = Task.CompletedTask; if (content.HasFlagFast(StableContent.Beatmaps)) importTasks.Add(beatmapImportTask = beatmaps.ImportFromStableAsync(stableStorage)); @@ -59,16 +59,10 @@ namespace osu.Game.Database importTasks.Add(skins.ImportFromStableAsync(stableStorage)); if (content.HasFlagFast(StableContent.Collections)) - { - importTasks.Add(beatmapImportTask?.ContinueWith(_ => collections.ImportFromStableAsync(stableStorage), TaskContinuationOptions.OnlyOnRanToCompletion) - ?? collections.ImportFromStableAsync(stableStorage)); - } + importTasks.Add(beatmapImportTask.ContinueWith(_ => collections.ImportFromStableAsync(stableStorage), TaskContinuationOptions.OnlyOnRanToCompletion)); if (content.HasFlagFast(StableContent.Scores)) - { - importTasks.Add(beatmapImportTask?.ContinueWith(_ => scores.ImportFromStableAsync(stableStorage), TaskContinuationOptions.OnlyOnRanToCompletion) - ?? scores.ImportFromStableAsync(stableStorage)); - } + importTasks.Add(beatmapImportTask.ContinueWith(_ => scores.ImportFromStableAsync(stableStorage), TaskContinuationOptions.OnlyOnRanToCompletion)); await Task.WhenAll(importTasks.ToArray()).ConfigureAwait(false); } From 97952bc3f0fd105535999cd47a3f280640fa4c73 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Mon, 17 May 2021 18:39:04 +0200 Subject: [PATCH 212/839] Fix backwards stable install resolution logic. --- osu.Game/Database/StableImportManager.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Database/StableImportManager.cs b/osu.Game/Database/StableImportManager.cs index 67f91d3bdb..331764f274 100644 --- a/osu.Game/Database/StableImportManager.cs +++ b/osu.Game/Database/StableImportManager.cs @@ -69,13 +69,13 @@ namespace osu.Game.Database private async Task getStableStorage() { - var stableStorage = game.GetStorageForStableInstall(); - if (stableStorage != null) - return stableStorage; - if (cachedStorage != null) return cachedStorage; + var stableStorage = game.GetStorageForStableInstall(); + if (stableStorage != null) + return cachedStorage = stableStorage; + var taskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); Schedule(() => dialogOverlay.Push(new StableDirectoryLocationDialog(taskCompletionSource))); var stablePath = await taskCompletionSource.Task.ConfigureAwait(false); From 7befcf74ffbb54fc4783d22f9235667db6c0e1c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 17 May 2021 18:53:09 +0200 Subject: [PATCH 213/839] Split value change callbacks out to separate methods --- .../Overlays/News/Sidebar/MonthSection.cs | 36 ++++++++++--------- osu.Game/Overlays/News/Sidebar/YearsPanel.cs | 28 ++++++++------- 2 files changed, 34 insertions(+), 30 deletions(-) diff --git a/osu.Game/Overlays/News/Sidebar/MonthSection.cs b/osu.Game/Overlays/News/Sidebar/MonthSection.cs index 20c4d2e83e..77f09b750d 100644 --- a/osu.Game/Overlays/News/Sidebar/MonthSection.cs +++ b/osu.Game/Overlays/News/Sidebar/MonthSection.cs @@ -153,28 +153,30 @@ namespace osu.Game.Overlays.News.Sidebar { base.LoadComplete(); - IsOpen.BindValueChanged(open => - { - ClearTransforms(true); - - if (open.NewValue) - { - AutoSizeAxes = Axes.Y; - content.FadeIn(animation_duration, Easing.OutQuint); - } - else - { - AutoSizeAxes = Axes.None; - this.ResizeHeightTo(0, animation_duration, Easing.OutQuint); - - content.FadeOut(animation_duration, Easing.OutQuint); - } - }, true); + IsOpen.BindValueChanged(_ => updateState(), true); // First state change should be instant. FinishTransforms(true); } + private void updateState() + { + ClearTransforms(true); + + if (IsOpen.Value) + { + AutoSizeAxes = Axes.Y; + content.FadeIn(animation_duration, Easing.OutQuint); + } + else + { + AutoSizeAxes = Axes.None; + this.ResizeHeightTo(0, animation_duration, Easing.OutQuint); + + content.FadeOut(animation_duration, Easing.OutQuint); + } + } + private bool shouldUpdateAutosize = true; // Workaround to allow the dropdown to be opened immediately since FinishTransforms doesn't work for AutosizeDuration. diff --git a/osu.Game/Overlays/News/Sidebar/YearsPanel.cs b/osu.Game/Overlays/News/Sidebar/YearsPanel.cs index ffdb5cf22e..849cdbf659 100644 --- a/osu.Game/Overlays/News/Sidebar/YearsPanel.cs +++ b/osu.Game/Overlays/News/Sidebar/YearsPanel.cs @@ -56,23 +56,25 @@ namespace osu.Game.Overlays.News.Sidebar { base.LoadComplete(); - metadata.BindValueChanged(m => + metadata.BindValueChanged(_ => recreateDrawables(), true); + } + + private void recreateDrawables() + { + yearsFlow.Clear(); + + if (metadata.Value == null) { - yearsFlow.Clear(); + Hide(); + return; + } - if (m.NewValue == null) - { - Hide(); - return; - } + var currentYear = metadata.Value.CurrentYear; - var currentYear = m.NewValue.CurrentYear; + foreach (var y in metadata.Value.Years) + yearsFlow.Add(new YearButton(y, y == currentYear)); - foreach (var y in m.NewValue.Years) - yearsFlow.Add(new YearButton(y, y == currentYear)); - - Show(); - }, true); + Show(); } public class YearButton : OsuHoverContainer From d614a47614218d9a6bc1406736ced7e2937d41e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 17 May 2021 18:54:17 +0200 Subject: [PATCH 214/839] Rename variable to better explain purpose --- osu.Game/Overlays/News/Sidebar/MonthSection.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/News/Sidebar/MonthSection.cs b/osu.Game/Overlays/News/Sidebar/MonthSection.cs index 77f09b750d..166da97f93 100644 --- a/osu.Game/Overlays/News/Sidebar/MonthSection.cs +++ b/osu.Game/Overlays/News/Sidebar/MonthSection.cs @@ -177,19 +177,19 @@ namespace osu.Game.Overlays.News.Sidebar } } - private bool shouldUpdateAutosize = true; + private bool autoSizeTransitionApplied; - // Workaround to allow the dropdown to be opened immediately since FinishTransforms doesn't work for AutosizeDuration. + // Workaround to allow the dropdown to be opened immediately since FinishTransforms doesn't work for AutoSize{Duration,Easing}. protected override void UpdateAfterAutoSize() { base.UpdateAfterAutoSize(); - if (shouldUpdateAutosize) + if (!autoSizeTransitionApplied) { AutoSizeDuration = animation_duration; AutoSizeEasing = Easing.OutQuint; - shouldUpdateAutosize = false; + autoSizeTransitionApplied = true; } } } From 400984457ca08221dbb99178fc6dcba920311a43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 17 May 2021 19:16:30 +0200 Subject: [PATCH 215/839] Fix weird behaviour in test scene Due to a callback set up in another place, clicking away from the 2022 year after launching the test scene would remove the 2022 button (because the callback was returning metadata without it). For simplicity just trim the 2022 year to make sure both test scenes use the same consistent set of years. --- osu.Game.Tests/Visual/Online/TestSceneNewsSidebar.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsSidebar.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsSidebar.cs index 376c270689..6cd3bd7d51 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneNewsSidebar.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneNewsSidebar.cs @@ -110,10 +110,9 @@ namespace osu.Game.Tests.Visual.Online private static readonly APINewsSidebar metadata_with_no_posts = new APINewsSidebar { - CurrentYear = 2022, + CurrentYear = 2021, Years = new[] { - 2022, 2021, 2020, 2019, From e2018f81f3c2f59d46e9261b0518d01677277625 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Mon, 17 May 2021 19:54:21 +0200 Subject: [PATCH 216/839] Use equality check for nullable types. --- .../Settings/Sections/Maintenance/GeneralSettings.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs index 20fc0e962a..a38ca81e23 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs @@ -32,7 +32,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance [BackgroundDependencyLoader(permitNulls: true)] private void load(BeatmapManager beatmaps, ScoreManager scores, SkinManager skins, [CanBeNull] CollectionManager collectionManager, [CanBeNull] StableImportManager stableImportManager, DialogOverlay dialogOverlay) { - if (stableImportManager?.SupportsImportFromStable ?? false) + if (stableImportManager?.SupportsImportFromStable == true) { Add(importBeatmapsButton = new SettingsButton { @@ -58,7 +58,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance } }); - if (stableImportManager?.SupportsImportFromStable ?? false) + if (stableImportManager?.SupportsImportFromStable == true) { Add(importScoresButton = new SettingsButton { @@ -84,7 +84,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance } }); - if (stableImportManager?.SupportsImportFromStable ?? false) + if (stableImportManager?.SupportsImportFromStable == true) { Add(importSkinsButton = new SettingsButton { @@ -112,7 +112,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance if (collectionManager != null) { - if (stableImportManager?.SupportsImportFromStable ?? false) + if (stableImportManager?.SupportsImportFromStable == true) { Add(importCollectionsButton = new SettingsButton { From f667ea3fd0045f5d365dfe20a90bd32936c630d8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 17 May 2021 21:28:14 +0300 Subject: [PATCH 217/839] Replace `AllowDefaultHUDComponentsFallback` with a temporary override at `LegacyBeatmapSkin` --- osu.Game/Skinning/LegacyBeatmapSkin.cs | 15 ++++++++++++++- osu.Game/Skinning/LegacySkin.cs | 8 -------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/osu.Game/Skinning/LegacyBeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs index 1a298576f9..91b70395f4 100644 --- a/osu.Game/Skinning/LegacyBeatmapSkin.cs +++ b/osu.Game/Skinning/LegacyBeatmapSkin.cs @@ -3,6 +3,7 @@ using osu.Framework.Audio.Sample; using osu.Framework.Bindables; +using osu.Framework.Graphics; using osu.Framework.IO.Stores; using osu.Game.Audio; using osu.Game.Beatmaps; @@ -14,7 +15,6 @@ namespace osu.Game.Skinning public class LegacyBeatmapSkin : LegacySkin { protected override bool AllowManiaSkin => false; - protected override bool AllowDefaultHUDComponentsFallback => false; protected override bool UseCustomSampleBanks => true; public LegacyBeatmapSkin(BeatmapInfo beatmap, IResourceStore storage, IStorageResourceProvider resources) @@ -24,6 +24,19 @@ namespace osu.Game.Skinning Configuration.AllowDefaultComboColoursFallback = false; } + public override Drawable GetDrawableComponent(ISkinComponent component) + { + if (component is SkinnableTargetComponent targetComponent && targetComponent.Target == SkinnableTarget.MainHUDComponents) + { + // for now, if the beatmap skin doesn't skin the score font, fall back to current skin + // instead of potentially returning default lazer skin HUD components from here. + if (!this.HasFont(LegacyFont.Score)) + return null; + } + + return base.GetDrawableComponent(component); + } + public override IBindable GetConfig(TLookup lookup) { switch (lookup) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index e7edba1e13..6b4f140c12 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -38,11 +38,6 @@ namespace osu.Game.Skinning protected virtual bool AllowManiaSkin => hasKeyTexture.Value; - /// - /// Whether this skin will fall back to default HUD components if it has no fonts for use. - /// - protected virtual bool AllowDefaultHUDComponentsFallback => true; - /// /// Whether this skin can use samples with a custom bank (custom sample set in stable terminology). /// Added in order to match sample lookup logic from stable (in stable, only the beatmap skin could use samples with a custom sample bank). @@ -336,9 +331,6 @@ namespace osu.Game.Skinning switch (target.Target) { case SkinnableTarget.MainHUDComponents: - if (!this.HasFont(LegacyFont.Score) && !AllowDefaultHUDComponentsFallback) - return null; - var skinnableTargetWrapper = new SkinnableTargetComponentsContainer(container => { var score = container.OfType().FirstOrDefault(); From 33fe843ba9eba9fcdedd8a59969b016a2abde848 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 17 May 2021 20:44:47 +0200 Subject: [PATCH 218/839] Move value change bindings to `LoadComplete()` Also removes a redundant one from the setter of `Current`. If the set to current comes with an associated value change, the bind-unbind flow that `BindableWithCurrent` implements will handle that change anyway. --- osu.Game/Overlays/RestoreDefaultValueButton.cs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/osu.Game/Overlays/RestoreDefaultValueButton.cs b/osu.Game/Overlays/RestoreDefaultValueButton.cs index f390fb1e46..34ebe270ef 100644 --- a/osu.Game/Overlays/RestoreDefaultValueButton.cs +++ b/osu.Game/Overlays/RestoreDefaultValueButton.cs @@ -26,11 +26,7 @@ namespace osu.Game.Overlays public Bindable Current { get => current.Current; - set - { - current.Current = value; - UpdateState(); - } + set => current.Current = value; } private Color4 buttonColour; @@ -43,10 +39,6 @@ namespace osu.Game.Overlays Width = SettingsPanel.CONTENT_MARGINS; Padding = new MarginPadding { Vertical = 1.5f }; Alpha = 0f; - - Current.ValueChanged += _ => UpdateState(); - Current.DisabledChanged += _ => UpdateState(); - Current.DefaultChanged += _ => UpdateState(); } [BackgroundDependencyLoader] @@ -76,6 +68,11 @@ namespace osu.Game.Overlays protected override void LoadComplete() { base.LoadComplete(); + + Current.ValueChanged += _ => UpdateState(); + Current.DisabledChanged += _ => UpdateState(); + Current.DefaultChanged += _ => UpdateState(); + UpdateState(); } @@ -113,4 +110,4 @@ namespace osu.Game.Overlays this.FadeColour(current.Disabled ? Color4.Gray : buttonColour, 200, Easing.OutQuint); } } -} \ No newline at end of file +} From 30d7768971208bf50324aeb32bf103e1851cba30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 17 May 2021 20:47:56 +0200 Subject: [PATCH 219/839] Remove now-redundant null check --- osu.Game/Overlays/RestoreDefaultValueButton.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/RestoreDefaultValueButton.cs b/osu.Game/Overlays/RestoreDefaultValueButton.cs index 34ebe270ef..3f4a9785a6 100644 --- a/osu.Game/Overlays/RestoreDefaultValueButton.cs +++ b/osu.Game/Overlays/RestoreDefaultValueButton.cs @@ -80,7 +80,7 @@ namespace osu.Game.Overlays protected override bool OnClick(ClickEvent e) { - if (current != null && !current.Disabled) + if (!current.Disabled) current.SetDefault(); return true; } From 8530b31e39ca3eb5d6e275cc5f88ae9d5baf8efa Mon Sep 17 00:00:00 2001 From: Lucas A Date: Mon, 17 May 2021 21:02:45 +0200 Subject: [PATCH 220/839] Use bitshifts for enum values instead of literal values. --- osu.Game/Database/StableImportManager.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Database/StableImportManager.cs b/osu.Game/Database/StableImportManager.cs index 331764f274..63a6db35c0 100644 --- a/osu.Game/Database/StableImportManager.cs +++ b/osu.Game/Database/StableImportManager.cs @@ -87,10 +87,10 @@ namespace osu.Game.Database [Flags] public enum StableContent { - Beatmaps = 1, - Scores = 2, - Skins = 4, - Collections = 8, + Beatmaps = 1 << 0, + Scores = 1 << 1, + Skins = 1 << 2, + Collections = 1 << 3, All = Beatmaps | Scores | Skins | Collections } } From ef114f240799be93ef7448e7bf33da9693390dd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 17 May 2021 20:57:48 +0200 Subject: [PATCH 221/839] Simplify bindable flow in `KeyBindingRow` --- osu.Game/Overlays/KeyBinding/KeyBindingRow.cs | 45 ++++++++++--------- .../KeyBinding/RestorableKeyBindingRow.cs | 4 +- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs index 7e81f64458..33f32493ad 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs @@ -11,7 +11,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Game.Graphics; @@ -24,7 +23,7 @@ using osuTK.Input; namespace osu.Game.Overlays.KeyBinding { - public class KeyBindingRow : Container, IFilterable, IHasCurrentValue + public class KeyBindingRow : Container, IFilterable { private readonly object action; private readonly IEnumerable bindings; @@ -53,17 +52,11 @@ namespace osu.Game.Overlays.KeyBinding private FillFlowContainer cancelAndClearButtons; private FillFlowContainer buttons; - private readonly BindableWithCurrent isKeysDefaultValue = new BindableWithCurrent + public Bindable IsDefault { get; } = new BindableBool(true) { Default = true }; - public Bindable Current - { - get => isKeysDefaultValue.Current; - set => isKeysDefaultValue.Current = value; - } - public IEnumerable FilterTerms => bindings.Select(b => b.KeyCombination.ReadableString()).Prepend(text.Text.ToString()); public KeyBindingRow(object action, IEnumerable bindings) @@ -83,15 +76,7 @@ namespace osu.Game.Overlays.KeyBinding [BackgroundDependencyLoader] private void load(OsuColour colours) { - isKeysDefaultValue.Value = bindings.Select(b => b.KeyCombination).SequenceEqual(Defaults); - isKeysDefaultValue.BindValueChanged(resetButtons => - { - if (resetButtons.NewValue && !bindings.Select(b => b.KeyCombination).SequenceEqual(Defaults)) - { - RestoreDefaults(); - finalise(); - } - }); + updateIsDefaultValue(); EdgeEffect = new EdgeEffectParameters { @@ -140,6 +125,24 @@ namespace osu.Game.Overlays.KeyBinding buttons.Add(new KeyButton(b)); } + protected override void LoadComplete() + { + base.LoadComplete(); + + IsDefault.BindValueChanged(resetButtons => + { + if (resetButtons.NewValue && !computeIsDefaultValue()) + { + RestoreDefaults(); + finalise(); + } + }); + } + + private void updateIsDefaultValue() => IsDefault.Value = computeIsDefaultValue(); + + private bool computeIsDefaultValue() => bindings.Select(b => b.KeyCombination).SequenceEqual(Defaults); + public void RestoreDefaults() { int i = 0; @@ -151,7 +154,7 @@ namespace osu.Game.Overlays.KeyBinding store.Update(button.KeyBinding); } - isKeysDefaultValue.Value = true; + updateIsDefaultValue(); } protected override bool OnHover(HoverEvent e) @@ -311,7 +314,7 @@ namespace osu.Game.Overlays.KeyBinding { store.Update(bindTarget.KeyBinding); - isKeysDefaultValue.Value = buttons.Select(b => b.KeyBinding.KeyCombination).SequenceEqual(Defaults); + updateIsDefaultValue(); bindTarget.IsBinding = false; Schedule(() => @@ -476,4 +479,4 @@ namespace osu.Game.Overlays.KeyBinding } } } -} \ No newline at end of file +} diff --git a/osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs index 0461ae4f35..b09c21378e 100644 --- a/osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs @@ -65,7 +65,7 @@ namespace osu.Game.Overlays.KeyBinding }, }; - restoreDefaultButton.Current = KeyBindingRow.Current; + restoreDefaultButton.Current = KeyBindingRow.IsDefault; } } -} \ No newline at end of file +} From 0e91a00a7e3b4084ee3395c1d88d1437740a87f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 17 May 2021 21:18:45 +0200 Subject: [PATCH 222/839] Revert to previous width spec instead of size --- osu.Game/Overlays/RestoreDefaultValueButton.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Overlays/RestoreDefaultValueButton.cs b/osu.Game/Overlays/RestoreDefaultValueButton.cs index 3f4a9785a6..51fb87da1d 100644 --- a/osu.Game/Overlays/RestoreDefaultValueButton.cs +++ b/osu.Game/Overlays/RestoreDefaultValueButton.cs @@ -13,7 +13,6 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; using osu.Game.Graphics; -using osuTK; namespace osu.Game.Overlays { @@ -60,7 +59,7 @@ namespace osu.Game.Overlays Type = EdgeEffectType.Glow, Radius = 2, }, - Size = new Vector2(0.33f, 0.8f), + Width = 0.33f, Child = new Box { RelativeSizeAxes = Axes.Both }, }; } From 79740dd2d8f70645becc7edf5134b3ee3a7041f2 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Mon, 17 May 2021 22:01:05 +0200 Subject: [PATCH 223/839] Merge conditionnal expression. --- osu.Game/Screens/Select/SongSelect.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 5a48ee7606..729e25203f 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -51,7 +51,7 @@ namespace osu.Game.Screens.Select protected virtual bool ShowFooter => true; - protected virtual bool DisplayStableImportPrompt => true; + protected virtual bool DisplayStableImportPrompt => stableImportManager.SupportsImportFromStable; /// /// Can be null if is false. @@ -85,6 +85,9 @@ namespace osu.Game.Screens.Select [Resolved] private BeatmapManager beatmaps { get; set; } + [Resolved] + private StableImportManager stableImportManager { get; set; } + protected ModSelectOverlay ModSelect { get; private set; } protected Sample SampleConfirm { get; private set; } @@ -102,7 +105,7 @@ namespace osu.Game.Screens.Select private MusicController music { get; set; } [BackgroundDependencyLoader(true)] - private void load(AudioManager audio, DialogOverlay dialog, OsuColour colours, StableImportManager stableImportManager, ManageCollectionsDialog manageCollectionsDialog, DifficultyRecommender recommender) + private void load(AudioManager audio, DialogOverlay dialog, OsuColour colours, ManageCollectionsDialog manageCollectionsDialog, DifficultyRecommender recommender) { // initial value transfer is required for FilterControl (it uses our re-cached bindables in its async load for the initial filter). transferRulesetValue(); @@ -284,7 +287,7 @@ namespace osu.Game.Screens.Select Schedule(() => { // if we have no beatmaps, let's prompt the user to import from over a stable install if he has one. - if (!beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.Minimal).Any() && stableImportManager.SupportsImportFromStable && DisplayStableImportPrompt) + if (!beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.Minimal).Any() && DisplayStableImportPrompt) { dialogOverlay.Push(new ImportFromStablePopup(() => { From 06389c08dc19287fdece6019170de4257365e7d6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 May 2021 13:11:22 +0900 Subject: [PATCH 224/839] Add basic test to show data how one would expect it to be displayed --- osu.Game.Tests/Visual/Online/TestSceneNewsSidebar.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsSidebar.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsSidebar.cs index 6cd3bd7d51..b000553a7b 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneNewsSidebar.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneNewsSidebar.cs @@ -24,11 +24,18 @@ namespace osu.Game.Tests.Visual.Online [SetUp] public void SetUp() => Schedule(() => Child = sidebar = new TestNewsSidebar { YearChanged = onYearChanged }); + [Test] + public void TestBasic() + { + AddStep("Add metadata", () => sidebar.Metadata.Value = getMetadata(2021)); + AddUntilStep("Month sections exist", () => sidebar.ChildrenOfType().Any()); + } + [Test] public void TestMetadataWithNoPosts() { AddStep("Add data with no posts", () => sidebar.Metadata.Value = metadata_with_no_posts); - AddUntilStep("No dropdowns were created", () => !sidebar.ChildrenOfType().Any()); + AddUntilStep("No month sections were created", () => !sidebar.ChildrenOfType().Any()); } [Test] @@ -134,7 +141,7 @@ namespace osu.Game.Tests.Visual.Online { base.LoadComplete(); - Metadata.BindValueChanged(m => + Metadata.BindValueChanged(metadata => { foreach (var b in this.ChildrenOfType()) b.Action = () => YearChanged?.Invoke(b.Year); From c71d53a0f9ce174dc65640879df256a55397b1c3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 May 2021 13:40:26 +0900 Subject: [PATCH 225/839] Fix text and button layout --- .../Maintenance/DirectorySelectScreen.cs | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs index e7c69e89fe..349a112477 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs @@ -11,9 +11,9 @@ using osuTK; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Localisation; -using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Framework.Screens; +using osu.Game.Graphics.Containers; namespace osu.Game.Overlays.Settings.Sections.Maintenance { @@ -69,20 +69,24 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance RelativeSizeAxes = Axes.Both, RowDimensions = new[] { + new Dimension(GridSizeMode.AutoSize), new Dimension(), - new Dimension(GridSizeMode.Relative, 0.8f), - new Dimension(), + new Dimension(GridSizeMode.AutoSize), }, Content = new[] { new Drawable[] { - new OsuSpriteText + new OsuTextFlowContainer(cp => { - Text = HeaderText, - Font = OsuFont.Default.With(size: 40), - Origin = Anchor.Centre, - Anchor = Anchor.Centre, + cp.Font = OsuFont.Default.With(size: 24); + }) + { + Text = HeaderText.ToString(), + TextAnchor = Anchor.TopCentre, + Margin = new MarginPadding(10), + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, } }, new Drawable[] @@ -99,6 +103,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance Anchor = Anchor.Centre, Origin = Anchor.Centre, Width = 300, + Margin = new MarginPadding(10), Text = "Select directory", Action = () => OnSelection(directorySelector.CurrentPath.Value) }, From 3a5b21c0f5caafc98e93783f5d014dbc34841f8c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 May 2021 13:47:39 +0900 Subject: [PATCH 226/839] Update "reset all bindings" button to better match new key binding row widths --- osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs b/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs index dc29f0b4e5..737c640b5a 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs @@ -55,10 +55,13 @@ namespace osu.Game.Overlays.KeyBinding { Text = "Reset all bindings in section"; RelativeSizeAxes = Axes.X; - Margin = new MarginPadding { Top = 5 }; - Height = 20; + Width = 0.5f; + Anchor = Anchor.TopCentre; + Origin = Anchor.TopCentre; + Margin = new MarginPadding { Top = 15 }; + Height = 30; Content.CornerRadius = 5; } } -} \ No newline at end of file +} From 0100b88a8601a693de45531576d4b2ad32900ae0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 May 2021 14:11:26 +0900 Subject: [PATCH 227/839] Move private methods down --- osu.Game/Overlays/KeyBinding/KeyBindingRow.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs index 33f32493ad..959ba36c6a 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs @@ -129,9 +129,9 @@ namespace osu.Game.Overlays.KeyBinding { base.LoadComplete(); - IsDefault.BindValueChanged(resetButtons => + IsDefault.BindValueChanged(isDefault => { - if (resetButtons.NewValue && !computeIsDefaultValue()) + if (isDefault.NewValue && !computeIsDefaultValue()) { RestoreDefaults(); finalise(); @@ -139,10 +139,6 @@ namespace osu.Game.Overlays.KeyBinding }); } - private void updateIsDefaultValue() => IsDefault.Value = computeIsDefaultValue(); - - private bool computeIsDefaultValue() => bindings.Select(b => b.KeyCombination).SequenceEqual(Defaults); - public void RestoreDefaults() { int i = 0; @@ -331,6 +327,10 @@ namespace osu.Game.Overlays.KeyBinding cancelAndClearButtons.BypassAutoSizeAxes |= Axes.Y; } + private void updateIsDefaultValue() => IsDefault.Value = computeIsDefaultValue(); + + private bool computeIsDefaultValue() => bindings.Select(b => b.KeyCombination).SequenceEqual(Defaults); + protected override void OnFocus(FocusEvent e) { AutoSizeDuration = 500; From 829d326e36cdab8a4a1be3e9624a0003d4910d48 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 May 2021 14:50:10 +0900 Subject: [PATCH 228/839] Remove alignment logic completely for the time being This was overly complex and does not play well with the new layout customisation system. We can add it back as required. --- .../HUD/HitErrorMeters/BarHitErrorMeter.cs | 56 +++++++++---------- 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs index c303f3889d..32d7f3525f 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs @@ -20,8 +20,6 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters { public class BarHitErrorMeter : HitErrorMeter { - private readonly Anchor alignment; - private const int arrow_move_duration = 400; private const int judgement_line_width = 6; @@ -45,9 +43,6 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters public BarHitErrorMeter() { - // todo: investigate. - alignment = false ? Anchor.x0 : Anchor.x2; - AutoSizeAxes = Axes.Both; } @@ -63,33 +58,42 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters Margin = new MarginPadding(2), Children = new Drawable[] { - judgementsContainer = new Container + new Container { - Anchor = Anchor.y1 | alignment, - Origin = Anchor.y1 | alignment, - Width = judgement_line_width, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Width = chevron_size, RelativeSizeAxes = Axes.Y, + Child = arrow = new SpriteIcon + { + Anchor = Anchor.TopCentre, + Origin = Anchor.Centre, + RelativePositionAxes = Axes.Y, + Y = 0.5f, + Icon = FontAwesome.Solid.ChevronRight, + Size = new Vector2(chevron_size), + } }, colourBars = new Container { Width = bar_width, RelativeSizeAxes = Axes.Y, - Anchor = Anchor.y1 | alignment, - Origin = Anchor.y1 | alignment, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, Children = new Drawable[] { colourBarsEarly = new Container { - Anchor = Anchor.y1 | alignment, - Origin = alignment, + Anchor = Anchor.CentreLeft, + Origin = Anchor.x2, RelativeSizeAxes = Axes.Both, Height = 0.5f, Scale = new Vector2(1, -1), }, colourBarsLate = new Container { - Anchor = Anchor.y1 | alignment, - Origin = alignment, + Anchor = Anchor.CentreLeft, + Origin = Anchor.x2, RelativeSizeAxes = Axes.Both, Height = 0.5f, }, @@ -115,21 +119,12 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters } } }, - new Container + judgementsContainer = new Container { - Anchor = Anchor.y1 | alignment, - Origin = Anchor.y1 | alignment, - Width = chevron_size, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Width = judgement_line_width, RelativeSizeAxes = Axes.Y, - Child = arrow = new SpriteIcon - { - Anchor = Anchor.TopCentre, - Origin = Anchor.Centre, - RelativePositionAxes = Axes.Y, - Y = 0.5f, - Icon = alignment == Anchor.x2 ? FontAwesome.Solid.ChevronRight : FontAwesome.Solid.ChevronLeft, - Size = new Vector2(chevron_size), - } }, } }; @@ -167,7 +162,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters // a little nub to mark the centre point. var centre = createColourBar(windows.Last().result, 0.01f); - centre.Anchor = centre.Origin = Anchor.y1 | (alignment == Anchor.x2 ? Anchor.x0 : Anchor.x2); + centre.Anchor = centre.Origin = Anchor.CentreLeft; centre.Width = 2.5f; colourBars.Add(centre); @@ -239,8 +234,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters judgementsContainer.Add(new JudgementLine { Y = getRelativeJudgementPosition(judgement.TimeOffset), - Anchor = alignment == Anchor.x2 ? Anchor.x0 : Anchor.x2, - Origin = Anchor.y1 | (alignment == Anchor.x2 ? Anchor.x0 : Anchor.x2), + Origin = Anchor.CentreLeft, }); arrow.MoveToY( From ff419af5128a3e0f2d188e10fcb05aed712ac128 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 18 May 2021 09:08:56 +0300 Subject: [PATCH 229/839] Hide the combo counter content rather than full death --- .../TestSceneCatchPlayerLegacySkin.cs | 3 +- .../Legacy/CatchLegacySkinTransformer.cs | 4 +- .../Screens/Play/HUD/LegacyComboCounter.cs | 50 ++++++++++++------- 3 files changed, 36 insertions(+), 21 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs index 7f0cbc6943..4e56e4b4a7 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs @@ -4,6 +4,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Graphics.Containers; using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Screens.Play.HUD; @@ -32,7 +33,7 @@ namespace osu.Game.Rulesets.Catch.Tests AddAssert("legacy HUD combo counter hidden", () => { - return Player.ChildrenOfType().All(counter => !counter.IsPresent || !counter.IsAlive); + return Player.ChildrenOfType().All(c => !c.ChildrenOfType().Single().IsPresent); }); } } diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs index db8c9a2e95..d757f36cde 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs @@ -32,9 +32,9 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy if (Source.GetDrawableComponent(component) is SkinnableTargetComponentsContainer components) { // catch may provide its own combo counter; hide the default. - // todo: this should probably be done in an elegant way. + // todo: this should be done in an elegant way per ruleset, defining which HUD skin components should be displayed. foreach (var legacyComboCounter in components.OfType()) - legacyComboCounter.Expire(); + legacyComboCounter.ContentVisible = false; return components; } diff --git a/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs b/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs index d64513d41e..ee00c71b0f 100644 --- a/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs +++ b/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs @@ -17,7 +17,7 @@ namespace osu.Game.Screens.Play.HUD /// public class LegacyComboCounter : CompositeDrawable, ISkinnableDrawable { - public Bindable Current { get; } = new BindableInt { MinValue = 0, }; + public Bindable Current { get; } = new BindableInt { MinValue = 0 }; private uint scheduledPopOutCurrentId; @@ -32,9 +32,9 @@ namespace osu.Game.Screens.Play.HUD /// private const double rolling_duration = 20; - private Drawable popOutCount; + private readonly Drawable popOutCount; - private Drawable displayedCountSpriteText; + private readonly Drawable displayedCountSpriteText; private int previousValue; @@ -45,6 +45,13 @@ namespace osu.Game.Screens.Play.HUD [Resolved] private ISkinSource skin { get; set; } + private readonly Container counterContainer; + + public bool ContentVisible + { + set => counterContainer.Alpha = value ? 1 : 0; + } + public LegacyComboCounter() { AutoSizeAxes = Axes.Both; @@ -55,6 +62,25 @@ namespace osu.Game.Screens.Play.HUD Margin = new MarginPadding(10); Scale = new Vector2(1.2f); + + InternalChild = counterContainer = new Container + { + AutoSizeAxes = Axes.Both, + AlwaysPresent = true, + Children = new[] + { + popOutCount = new LegacySpriteText(LegacyFont.Combo) + { + Alpha = 0, + Margin = new MarginPadding(0.05f), + Blending = BlendingParameters.Additive, + }, + displayedCountSpriteText = new LegacySpriteText(LegacyFont.Combo) + { + Alpha = 0, + }, + } + }; } /// @@ -82,20 +108,6 @@ namespace osu.Game.Screens.Play.HUD [BackgroundDependencyLoader] private void load(ScoreProcessor scoreProcessor) { - InternalChildren = new[] - { - popOutCount = new LegacySpriteText(LegacyFont.Combo) - { - Alpha = 0, - Margin = new MarginPadding(0.05f), - Blending = BlendingParameters.Additive, - }, - displayedCountSpriteText = new LegacySpriteText(LegacyFont.Combo) - { - Alpha = 0, - }, - }; - Current.BindTo(scoreProcessor.Combo); } @@ -105,10 +117,12 @@ namespace osu.Game.Screens.Play.HUD ((IHasText)displayedCountSpriteText).Text = formatCount(Current.Value); + counterContainer.Anchor = Anchor; + counterContainer.Origin = Origin; displayedCountSpriteText.Anchor = Anchor; displayedCountSpriteText.Origin = Origin; - popOutCount.Origin = Origin; popOutCount.Anchor = Anchor; + popOutCount.Origin = Origin; Current.BindValueChanged(combo => updateCount(combo.NewValue == 0), true); } From c885ad87d5884105319658e0e2c476a45e759d3b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 May 2021 15:12:29 +0900 Subject: [PATCH 230/839] Update `HitErrorDisplay` tests --- .../Visual/Gameplay/TestSceneHitErrorMeter.cs | 71 +++++++++++++++---- 1 file changed, 58 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs index 6cefd01aab..2c5443fe08 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs @@ -1,6 +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.Diagnostics.CodeAnalysis; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -11,29 +14,35 @@ using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Catch.Scoring; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mania.Scoring; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Scoring; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Scoring; +using osu.Game.Rulesets.UI; +using osu.Game.Scoring; using osu.Game.Screens.Play.HUD.HitErrorMeters; namespace osu.Game.Tests.Visual.Gameplay { public class TestSceneHitErrorMeter : OsuTestScene { - private HitWindows hitWindows; - [Cached] private ScoreProcessor scoreProcessor = new ScoreProcessor(); + [Cached(typeof(DrawableRuleset))] + private TestDrawableRuleset drawableRuleset = new TestDrawableRuleset(); + public TestSceneHitErrorMeter() { recreateDisplay(new OsuHitWindows(), 5); AddRepeatStep("New random judgement", () => newJudgement(), 40); - AddRepeatStep("New max negative", () => newJudgement(-hitWindows.WindowFor(HitResult.Meh)), 20); - AddRepeatStep("New max positive", () => newJudgement(hitWindows.WindowFor(HitResult.Meh)), 20); + AddRepeatStep("New max negative", () => newJudgement(-drawableRuleset.HitWindows.WindowFor(HitResult.Meh)), 20); + AddRepeatStep("New max positive", () => newJudgement(drawableRuleset.HitWindows.WindowFor(HitResult.Meh)), 20); AddStep("New fixed judgement (50ms)", () => newJudgement(50)); AddStep("Judgement barrage", () => @@ -83,10 +92,10 @@ namespace osu.Game.Tests.Visual.Gameplay private void recreateDisplay(HitWindows hitWindows, float overallDifficulty) { - this.hitWindows = hitWindows; - hitWindows?.SetDifficulty(overallDifficulty); + drawableRuleset.HitWindows = hitWindows; + Clear(); Add(new FillFlowContainer @@ -103,40 +112,40 @@ namespace osu.Game.Tests.Visual.Gameplay } }); - Add(new BarHitErrorMeter(hitWindows, true) + Add(new BarHitErrorMeter { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, }); - Add(new BarHitErrorMeter(hitWindows, false) + Add(new BarHitErrorMeter { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, }); - Add(new BarHitErrorMeter(hitWindows, true) + Add(new BarHitErrorMeter { Anchor = Anchor.BottomCentre, Origin = Anchor.CentreLeft, Rotation = 270, }); - Add(new ColourHitErrorMeter(hitWindows) + Add(new ColourHitErrorMeter { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, Margin = new MarginPadding { Right = 50 } }); - Add(new ColourHitErrorMeter(hitWindows) + Add(new ColourHitErrorMeter { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, Margin = new MarginPadding { Left = 50 } }); - Add(new ColourHitErrorMeter(hitWindows) + Add(new ColourHitErrorMeter { Anchor = Anchor.BottomCentre, Origin = Anchor.CentreLeft, @@ -147,11 +156,47 @@ namespace osu.Game.Tests.Visual.Gameplay private void newJudgement(double offset = 0) { - scoreProcessor.ApplyResult(new JudgementResult(new HitCircle { HitWindows = hitWindows }, new Judgement()) + scoreProcessor.ApplyResult(new JudgementResult(new HitCircle { HitWindows = drawableRuleset.HitWindows }, new Judgement()) { TimeOffset = offset == 0 ? RNG.Next(-150, 150) : offset, Type = HitResult.Perfect, }); } + + [SuppressMessage("ReSharper", "UnassignedGetOnlyAutoProperty")] + private class TestDrawableRuleset : DrawableRuleset + { + public HitWindows HitWindows; + + public override IEnumerable Objects => new[] { new HitCircle { HitWindows = HitWindows } }; + + public override event Action NewResult; + public override event Action RevertResult; + + public override Playfield Playfield { get; } + public override Container Overlays { get; } + public override Container FrameStableComponents { get; } + public override IFrameStableClock FrameStableClock { get; } + public override IReadOnlyList Mods { get; } + + public override double GameplayStartTime { get; } + public override GameplayCursorContainer Cursor { get; } + + public TestDrawableRuleset() + : base(new OsuRuleset()) + { + // won't compile without this. + NewResult?.Invoke(null); + RevertResult?.Invoke(null); + } + + public override void SetReplayScore(Score replayScore) => throw new NotImplementedException(); + + public override void SetRecordTarget(Score score) => throw new NotImplementedException(); + + public override void RequestResume(Action continueResume) => throw new NotImplementedException(); + + public override void CancelResume() => throw new NotImplementedException(); + } } } From 265a89e5cc778e5c7054f566bdcaba16d61bf3a6 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 18 May 2021 09:45:32 +0300 Subject: [PATCH 231/839] Fix `LegacySkinPlayerTestScene` overriden by default beatmap skin --- .../Tests/Visual/LegacySkinPlayerTestScene.cs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs b/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs index c186525757..27ddd77c03 100644 --- a/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs +++ b/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs @@ -1,11 +1,17 @@ // 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 NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Audio; using osu.Framework.IO.Stores; +using osu.Framework.Testing; +using osu.Framework.Timing; +using osu.Game.Beatmaps; using osu.Game.Rulesets; using osu.Game.Skinning; +using osu.Game.Storyboards; namespace osu.Game.Tests.Visual { @@ -16,6 +22,9 @@ namespace osu.Game.Tests.Visual protected override TestPlayer CreatePlayer(Ruleset ruleset) => new SkinProvidingPlayer(legacySkinSource); + protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) + => new LegacySkinWorkingBeatmap(beatmap, storyboard, Clock, Audio); + [BackgroundDependencyLoader] private void load(OsuGameBase game, SkinManager skins) { @@ -23,6 +32,14 @@ namespace osu.Game.Tests.Visual legacySkinSource = new SkinProvidingContainer(legacySkin); } + public override void SetUpSteps() + { + base.SetUpSteps(); + + // check presence of a random legacy HUD component to ensure this is using legacy skin. + AddAssert("using legacy skin", () => this.ChildrenOfType().Any()); + } + public class SkinProvidingPlayer : TestPlayer { [Cached(typeof(ISkinSource))] @@ -33,5 +50,15 @@ namespace osu.Game.Tests.Visual this.skinSource = skinSource; } } + + private class LegacySkinWorkingBeatmap : ClockBackedTestWorkingBeatmap + { + public LegacySkinWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard, IFrameBasedClock frameBasedClock, AudioManager audio) + : base(beatmap, storyboard, frameBasedClock, audio) + { + } + + protected override ISkin GetSkin() => new LegacyBeatmapSkin(BeatmapInfo, null, null); + } } } From 5acb708939776029f96e6ec7bca92cec86ca3a40 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 May 2021 15:50:14 +0900 Subject: [PATCH 232/839] Remove customisation of hit error via standard settings --- osu.Game/Configuration/OsuConfigManager.cs | 2 - osu.Game/Configuration/ScoreMeterType.cs | 37 ------------------- .../Sections/Gameplay/GeneralSettings.cs | 5 --- 3 files changed, 44 deletions(-) delete mode 100644 osu.Game/Configuration/ScoreMeterType.cs diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 09412b1f1b..43bbd725c3 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -104,7 +104,6 @@ namespace osu.Game.Configuration SetDefault(OsuSetting.KeyOverlay, false); SetDefault(OsuSetting.PositionalHitSounds, true); SetDefault(OsuSetting.AlwaysPlayFirstComboBreak, true); - SetDefault(OsuSetting.ScoreMeter, ScoreMeterType.HitErrorBoth); SetDefault(OsuSetting.FloatingComments, false); @@ -213,7 +212,6 @@ namespace osu.Game.Configuration KeyOverlay, PositionalHitSounds, AlwaysPlayFirstComboBreak, - ScoreMeter, FloatingComments, HUDVisibilityMode, ShowProgressGraph, diff --git a/osu.Game/Configuration/ScoreMeterType.cs b/osu.Game/Configuration/ScoreMeterType.cs deleted file mode 100644 index ddbd2327c2..0000000000 --- a/osu.Game/Configuration/ScoreMeterType.cs +++ /dev/null @@ -1,37 +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.ComponentModel; - -namespace osu.Game.Configuration -{ - public enum ScoreMeterType - { - [Description("None")] - None, - - [Description("Hit Error (left)")] - HitErrorLeft, - - [Description("Hit Error (right)")] - HitErrorRight, - - [Description("Hit Error (left+right)")] - HitErrorBoth, - - [Description("Hit Error (bottom)")] - HitErrorBottom, - - [Description("Colour (left)")] - ColourLeft, - - [Description("Colour (right)")] - ColourRight, - - [Description("Colour (left+right)")] - ColourBoth, - - [Description("Colour (bottom)")] - ColourBottom, - } -} diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs index be464fa2b7..0b5ec4f338 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs @@ -73,11 +73,6 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay LabelText = "Always play first combo break sound", Current = config.GetBindable(OsuSetting.AlwaysPlayFirstComboBreak) }, - new SettingsEnumDropdown - { - LabelText = "Score meter type", - Current = config.GetBindable(OsuSetting.ScoreMeter) - }, new SettingsEnumDropdown { LabelText = "Score display mode", From 10c730b37d569e13f0dc9c522b57911b6671d7b8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 May 2021 15:50:40 +0900 Subject: [PATCH 233/839] Add new default locations for hit bar error displays --- osu.Game/Screens/Play/SongProgress.cs | 10 +++++++--- osu.Game/Skinning/DefaultSkin.cs | 26 ++++++++++++++++++++++++++ osu.Game/Skinning/HUDSkinComponents.cs | 2 ++ osu.Game/Skinning/LegacySkin.cs | 16 ++++++++++++++++ 4 files changed, 51 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/SongProgress.cs b/osu.Game/Screens/Play/SongProgress.cs index b7939b5e75..cab44c7473 100644 --- a/osu.Game/Screens/Play/SongProgress.cs +++ b/osu.Game/Screens/Play/SongProgress.cs @@ -20,10 +20,14 @@ namespace osu.Game.Screens.Play { public class SongProgress : OverlayContainer, ISkinnableDrawable { - private const int info_height = 20; - private const int bottom_bar_height = 5; + public const float MAX_HEIGHT = info_height + bottom_bar_height + graph_height + handle_height; + + private const float info_height = 20; + private const float bottom_bar_height = 5; private const float graph_height = SquareGraph.Column.WIDTH * 6; - private static readonly Vector2 handle_size = new Vector2(10, 18); + private const float handle_height = 18; + + private static readonly Vector2 handle_size = new Vector2(10, handle_height); private const float transition_duration = 200; diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index d13ddcf22b..84f40df0cf 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -14,6 +14,7 @@ using osu.Game.Extensions; using osu.Game.IO; using osu.Game.Screens.Play; using osu.Game.Screens.Play.HUD; +using osu.Game.Screens.Play.HUD.HitErrorMeters; using osuTK; using osuTK.Graphics; @@ -78,6 +79,23 @@ namespace osu.Game.Skinning combo.Position = new Vector2(accuracy.ScreenSpaceDeltaToParentSpace(score.ScreenSpaceDrawQuad.Size).X / 2 + horizontal_padding, vertical_offset + 5); combo.Anchor = Anchor.TopCentre; } + + var hitError = container.OfType().FirstOrDefault(); + + if (hitError != null) + { + hitError.Anchor = Anchor.CentreLeft; + hitError.Origin = Anchor.CentreLeft; + } + + var hitError2 = container.OfType().LastOrDefault(); + + if (hitError2 != null) + { + hitError2.Anchor = Anchor.CentreRight; + hitError2.Origin = Anchor.CentreLeft; + hitError2.Scale = new Vector2(-1, 1); + } } }) { @@ -88,6 +106,8 @@ namespace osu.Game.Skinning GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.AccuracyCounter)), GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.HealthDisplay)), GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.SongProgress)), + GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.BarHitErrorMeter)), + GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.BarHitErrorMeter)), } }; @@ -114,6 +134,12 @@ namespace osu.Game.Skinning case HUDSkinComponents.SongProgress: return new SongProgress(); + + case HUDSkinComponents.BarHitErrorMeter: + return new BarHitErrorMeter(); + + case HUDSkinComponents.ColourHitErrorMeter: + return new ColourHitErrorMeter(); } break; diff --git a/osu.Game/Skinning/HUDSkinComponents.cs b/osu.Game/Skinning/HUDSkinComponents.cs index 2e6c3a9937..ea39c98635 100644 --- a/osu.Game/Skinning/HUDSkinComponents.cs +++ b/osu.Game/Skinning/HUDSkinComponents.cs @@ -10,5 +10,7 @@ namespace osu.Game.Skinning AccuracyCounter, HealthDisplay, SongProgress, + BarHitErrorMeter, + ColourHitErrorMeter, } } diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 6c8d6ee45a..7a64f38840 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -19,6 +19,7 @@ using osu.Game.IO; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Play; using osu.Game.Screens.Play.HUD; +using osu.Game.Screens.Play.HUD.HitErrorMeters; using osuTK.Graphics; namespace osu.Game.Skinning @@ -342,6 +343,20 @@ namespace osu.Game.Skinning { accuracy.Y = container.ToLocalSpace(score.ScreenSpaceDrawQuad.BottomRight).Y; } + + var songProgress = container.OfType().FirstOrDefault(); + + var hitError = container.OfType().FirstOrDefault(); + + if (hitError != null) + { + hitError.Anchor = Anchor.BottomCentre; + hitError.Origin = Anchor.CentreLeft; + hitError.Rotation = -90; + + if (songProgress != null) + hitError.Y -= SongProgress.MAX_HEIGHT; + } }) { Children = new[] @@ -352,6 +367,7 @@ namespace osu.Game.Skinning GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.AccuracyCounter)) ?? new DefaultAccuracyCounter(), GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.HealthDisplay)) ?? new DefaultHealthDisplay(), GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.SongProgress)) ?? new SongProgress(), + GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.BarHitErrorMeter)) ?? new BarHitErrorMeter(), } }; From 4e12a2734ccf28dae236d6c78385e79d69bf32d7 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 18 May 2021 09:51:58 +0300 Subject: [PATCH 234/839] Remove ignore attribute from now fixed test scene --- .../TestSceneCatchPlayerLegacySkin.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs index 4e56e4b4a7..b7cd6737b1 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs @@ -20,7 +20,6 @@ namespace osu.Game.Rulesets.Catch.Tests protected override Ruleset CreatePlayerRuleset() => new CatchRuleset(); [Test] - [Ignore("HUD components broken, remove when fixed.")] public void TestLegacyHUDComboCounterHidden([Values] bool withModifiedSkin) { if (withModifiedSkin) @@ -33,7 +32,7 @@ namespace osu.Game.Rulesets.Catch.Tests AddAssert("legacy HUD combo counter hidden", () => { - return Player.ChildrenOfType().All(c => !c.ChildrenOfType().Single().IsPresent); + return Player.ChildrenOfType().All(c => c.ChildrenOfType().Single().Alpha == 0f); }); } } From ed957df162bbbfafdecaacab836ba6394c94e3a6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 May 2021 16:40:56 +0900 Subject: [PATCH 235/839] Add simple xmldoc to `TransferBlueprintFor` method --- .../Edit/Compose/Components/EditorBlueprintContainer.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs index 6825e0f6a0..c62ea43331 100644 --- a/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs @@ -84,6 +84,11 @@ namespace osu.Game.Screens.Edit.Compose.Components base.AddBlueprintFor(item); } + /// + /// Invoked when a has been transferred to another . + /// + /// The hit object which has been assigned to a new drawable. + /// The new drawable that is representing the hit object. protected virtual void TransferBlueprintFor(HitObject hitObject, DrawableHitObject drawableObject) { } From 61a41d97a4a61df9e95f47489b2ba69b23891f2d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 18 May 2021 17:39:45 +0900 Subject: [PATCH 236/839] Add some xmldocs + comments --- .../Compose/HitObjectContainerEventQueue.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/osu.Game/Screens/Edit/Compose/HitObjectContainerEventQueue.cs b/osu.Game/Screens/Edit/Compose/HitObjectContainerEventQueue.cs index 7c21573b18..363f08cb41 100644 --- a/osu.Game/Screens/Edit/Compose/HitObjectContainerEventQueue.cs +++ b/osu.Game/Screens/Edit/Compose/HitObjectContainerEventQueue.cs @@ -68,6 +68,9 @@ namespace osu.Game.Screens.Edit.Compose switch (existingEvent, newEvent) { + // This mostly exists as a safeguard to ensure that the sequence: Began -> { Finished -> Began } -> Finished, where { ... } indicates a transferral within a single frame, + // correctly leads into a final "Finished" state. It's unlikely for this to happen normally as it requires the hitobject usage to finish (for the final time) + // immediately after the HitObjectContainer updates lifetime, but it's not inconceivable to occur with the Editor's scheduling and execution order. case (EventType.Transferred, EventType.Finished): pendingEvents[hitObject] = EventType.Finished; break; @@ -117,8 +120,24 @@ namespace osu.Game.Screens.Edit.Compose private enum EventType { + /// + /// A has started being used by a . + /// Began, + + /// + /// A has finished being used by a . + /// Finished, + + /// + /// An internal intermediate state that occurs when a has finished being used by one + /// and started being used by another in the same frame. The may be the same instance in both cases. + /// + /// + /// This usually occurs when a is transferred between s, + /// but also occurs if the dies and becomes alive again in the same frame within the same . + /// Transferred } } From 08ee1e4853af9ac70c107ed042b37d23cd1792da Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 18 May 2021 12:37:23 +0300 Subject: [PATCH 237/839] Remove HUD skin component lookup in favour of `MainHUDComponents` target system --- osu.Game/Skinning/DefaultSkin.cs | 35 +++---------------- osu.Game/Skinning/HUDSkinComponent.cs | 22 ------------ osu.Game/Skinning/LegacySkin.cs | 49 +++++++++------------------ 3 files changed, 21 insertions(+), 85 deletions(-) delete mode 100644 osu.Game/Skinning/HUDSkinComponent.cs diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index d13ddcf22b..65e8fd1b82 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -12,7 +12,6 @@ using osu.Framework.Graphics.Textures; using osu.Game.Audio; using osu.Game.Extensions; using osu.Game.IO; -using osu.Game.Screens.Play; using osu.Game.Screens.Play.HUD; using osuTK; using osuTK.Graphics; @@ -81,13 +80,12 @@ namespace osu.Game.Skinning } }) { - Children = new[] + Children = new Drawable[] { - GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.ComboCounter)), - GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.ScoreCounter)), - GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.AccuracyCounter)), - GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.HealthDisplay)), - GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.SongProgress)), + new DefaultComboCounter(), + new DefaultScoreCounter(), + new DefaultAccuracyCounter(), + new DefaultHealthDisplay(), } }; @@ -95,29 +93,6 @@ namespace osu.Game.Skinning } break; - - case HUDSkinComponent hudComponent: - { - switch (hudComponent.Component) - { - case HUDSkinComponents.ComboCounter: - return new DefaultComboCounter(); - - case HUDSkinComponents.ScoreCounter: - return new DefaultScoreCounter(); - - case HUDSkinComponents.AccuracyCounter: - return new DefaultAccuracyCounter(); - - case HUDSkinComponents.HealthDisplay: - return new DefaultHealthDisplay(); - - case HUDSkinComponents.SongProgress: - return new SongProgress(); - } - - break; - } } return null; diff --git a/osu.Game/Skinning/HUDSkinComponent.cs b/osu.Game/Skinning/HUDSkinComponent.cs deleted file mode 100644 index cc053421b7..0000000000 --- a/osu.Game/Skinning/HUDSkinComponent.cs +++ /dev/null @@ -1,22 +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.Linq; - -namespace osu.Game.Skinning -{ - public class HUDSkinComponent : ISkinComponent - { - public readonly HUDSkinComponents Component; - - public HUDSkinComponent(HUDSkinComponents component) - { - Component = component; - } - - protected virtual string ComponentName => Component.ToString(); - - public string LookupName => - string.Join('/', new[] { "HUD", ComponentName }.Where(s => !string.IsNullOrEmpty(s))); - } -} diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 6c8d6ee45a..981d8beb08 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -17,7 +17,6 @@ using osu.Game.Audio; using osu.Game.Beatmaps.Formats; using osu.Game.IO; using osu.Game.Rulesets.Scoring; -using osu.Game.Screens.Play; using osu.Game.Screens.Play.HUD; using osuTK.Graphics; @@ -344,15 +343,22 @@ namespace osu.Game.Skinning } }) { - Children = new[] - { - // TODO: these should fallback to the osu!classic skin. - GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.ComboCounter)) ?? new DefaultComboCounter(), - GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.ScoreCounter)) ?? new DefaultScoreCounter(), - GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.AccuracyCounter)) ?? new DefaultAccuracyCounter(), - GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.HealthDisplay)) ?? new DefaultHealthDisplay(), - GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.SongProgress)) ?? new SongProgress(), - } + Children = this.HasFont(LegacyFont.Score) + ? new Drawable[] + { + new LegacyComboCounter(), + new LegacyScoreCounter(), + new LegacyAccuracyCounter(), + new LegacyHealthDisplay(), + } + : new Drawable[] + { + // TODO: these should fallback to using osu!classic skin textures, rather than doing this. + new DefaultComboCounter(), + new DefaultScoreCounter(), + new DefaultAccuracyCounter(), + new DefaultHealthDisplay(), + } }; return skinnableTargetWrapper; @@ -360,29 +366,6 @@ namespace osu.Game.Skinning return null; - case HUDSkinComponent hudComponent: - { - if (!this.HasFont(LegacyFont.Score)) - return null; - - switch (hudComponent.Component) - { - case HUDSkinComponents.ComboCounter: - return new LegacyComboCounter(); - - case HUDSkinComponents.ScoreCounter: - return new LegacyScoreCounter(); - - case HUDSkinComponents.AccuracyCounter: - return new LegacyAccuracyCounter(); - - case HUDSkinComponents.HealthDisplay: - return new LegacyHealthDisplay(); - } - - return null; - } - case GameplaySkinComponent resultComponent: Func createDrawable = () => getJudgementAnimation(resultComponent.Component); From e5b6ad10bd6b1c9e4b1e28d85cb668ec9e5cb137 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 18 May 2021 12:37:36 +0300 Subject: [PATCH 238/839] Remove no longer working combo counter hide code --- .../Skinning/Legacy/CatchLegacySkinTransformer.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs index 1b48832ed6..da7d0e667d 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs @@ -22,16 +22,6 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy public override Drawable GetDrawableComponent(ISkinComponent component) { - if (component is HUDSkinComponent hudComponent) - { - switch (hudComponent.Component) - { - case HUDSkinComponents.ComboCounter: - // catch may provide its own combo counter; hide the default. - return providesComboCounter ? Drawable.Empty() : null; - } - } - if (!(component is CatchSkinComponent catchSkinComponent)) return null; From d1272d5e13b22f4118aa7a5820bc72cc319b7312 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 18 May 2021 12:38:06 +0300 Subject: [PATCH 239/839] Group all skinnable test scenes to one `TestSceneSkinnableHUDComponents` --- .../Visual/Gameplay/TestSceneComboCounter.cs | 35 --------- .../TestSceneSkinnableAccuracyCounter.cs | 36 --------- .../TestSceneSkinnableHUDComponents.cs | 76 +++++++++++++++++++ .../TestSceneSkinnableHealthDisplay.cs | 57 -------------- .../TestSceneSkinnableScoreCounter.cs | 41 ---------- 5 files changed, 76 insertions(+), 169 deletions(-) delete mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneComboCounter.cs delete mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableAccuracyCounter.cs create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDComponents.cs delete mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs delete mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableScoreCounter.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneComboCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneComboCounter.cs deleted file mode 100644 index b22af0f7ac..0000000000 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneComboCounter.cs +++ /dev/null @@ -1,35 +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 NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Testing; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Osu; -using osu.Game.Rulesets.Scoring; -using osu.Game.Skinning; - -namespace osu.Game.Tests.Visual.Gameplay -{ - public class TestSceneComboCounter : SkinnableTestScene - { - protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); - - [Cached] - private ScoreProcessor scoreProcessor = new ScoreProcessor(); - - [SetUpSteps] - public void SetUpSteps() - { - AddStep("Create combo counters", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.ComboCounter)))); - } - - [Test] - public void TestComboCounterIncrementing() - { - AddRepeatStep("increase combo", () => scoreProcessor.Combo.Value++, 10); - - AddStep("reset combo", () => scoreProcessor.Combo.Value = 0); - } - } -} diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableAccuracyCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableAccuracyCounter.cs deleted file mode 100644 index 6f4e6a2420..0000000000 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableAccuracyCounter.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 NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Testing; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Osu; -using osu.Game.Rulesets.Scoring; -using osu.Game.Skinning; - -namespace osu.Game.Tests.Visual.Gameplay -{ - public class TestSceneSkinnableAccuracyCounter : SkinnableTestScene - { - protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); - - [Cached] - private ScoreProcessor scoreProcessor = new ScoreProcessor(); - - [SetUpSteps] - public void SetUpSteps() - { - AddStep("Set initial accuracy", () => scoreProcessor.Accuracy.Value = 1); - AddStep("Create accuracy counters", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.AccuracyCounter)))); - } - - [Test] - public void TestChangingAccuracy() - { - AddStep(@"Reset all", () => scoreProcessor.Accuracy.Value = 1); - - AddStep(@"Miss :(", () => scoreProcessor.Accuracy.Value -= 0.023); - } - } -} diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDComponents.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDComponents.cs new file mode 100644 index 0000000000..1c2f572d9e --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDComponents.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 NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Judgements; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Scoring; +using osu.Game.Skinning; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public class TestSceneSkinnableHUDComponents : SkinnableTestScene + { + protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); + + [Cached] + private ScoreProcessor scoreProcessor = new ScoreProcessor(); + + [Cached(typeof(HealthProcessor))] + private HealthProcessor healthProcessor = new DrainingHealthProcessor(0); + + [BackgroundDependencyLoader] + private void load() + { + SetContents(() => new SkinnableTargetContainer(SkinnableTarget.MainHUDComponents) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + }); + } + + [Test] + public void TestScoreCounter() + { + AddStep(@"reset total score", () => scoreProcessor.TotalScore.Value = 0); + AddStep(@"increment total score", () => scoreProcessor.TotalScore.Value += 300); + AddStep(@"set large score", () => scoreProcessor.TotalScore.Value = 1_000_000_000); + } + + [Test] + public void TestComboCounter() + { + AddStep(@"reset combo", () => scoreProcessor.Combo.Value = 0); + AddRepeatStep(@"increase combo", () => scoreProcessor.Combo.Value++, 10); + } + + [Test] + public void TestAccuracyCounter() + { + AddStep(@"reset accuracy", () => scoreProcessor.Accuracy.Value = 1); + AddStep(@"decrease accuracy", () => scoreProcessor.Accuracy.Value -= 0.023); + } + + [Test] + public void TestHealthDisplay() + { + AddStep(@"reset health", () => healthProcessor.Health.Value = 1); + AddRepeatStep(@"decrease hp", () => healthProcessor.Health.Value -= 0.08f, 10); + AddRepeatStep(@"decrease hp without flash", () => healthProcessor.Health.Value += 0.1f, 3); + AddRepeatStep(@"increase hp with flash", () => + { + healthProcessor.Health.Value += 0.1f; + healthProcessor.ApplyResult(new JudgementResult(new HitCircle(), new OsuJudgement()) + { + Type = HitResult.Perfect + }); + }, 3); + } + } +} diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs deleted file mode 100644 index ead27bf017..0000000000 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs +++ /dev/null @@ -1,57 +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 NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Testing; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Osu; -using osu.Game.Rulesets.Osu.Judgements; -using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Scoring; -using osu.Game.Skinning; - -namespace osu.Game.Tests.Visual.Gameplay -{ - public class TestSceneSkinnableHealthDisplay : SkinnableTestScene - { - protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); - - [Cached(typeof(HealthProcessor))] - private HealthProcessor healthProcessor = new DrainingHealthProcessor(0); - - [SetUpSteps] - public void SetUpSteps() - { - AddStep("Create health displays", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.HealthDisplay)))); - AddStep(@"Reset all", delegate - { - healthProcessor.Health.Value = 1; - }); - } - - [Test] - public void TestHealthDisplayIncrementing() - { - AddRepeatStep(@"decrease hp", delegate - { - healthProcessor.Health.Value -= 0.08f; - }, 10); - - AddRepeatStep(@"increase hp without flash", delegate - { - healthProcessor.Health.Value += 0.1f; - }, 3); - - AddRepeatStep(@"increase hp with flash", delegate - { - healthProcessor.Health.Value += 0.1f; - healthProcessor.ApplyResult(new JudgementResult(new HitCircle(), new OsuJudgement()) - { - Type = HitResult.Perfect - }); - }, 3); - } - } -} diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableScoreCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableScoreCounter.cs deleted file mode 100644 index 8d633c3ca2..0000000000 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableScoreCounter.cs +++ /dev/null @@ -1,41 +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 NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Testing; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Osu; -using osu.Game.Rulesets.Scoring; -using osu.Game.Skinning; - -namespace osu.Game.Tests.Visual.Gameplay -{ - public class TestSceneSkinnableScoreCounter : SkinnableTestScene - { - protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); - - [Cached] - private ScoreProcessor scoreProcessor = new ScoreProcessor(); - - [SetUpSteps] - public void SetUpSteps() - { - AddStep("Create score counters", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.ScoreCounter)))); - } - - [Test] - public void TestScoreCounterIncrementing() - { - AddStep(@"Reset all", () => scoreProcessor.TotalScore.Value = 0); - - AddStep(@"Hit! :D", () => scoreProcessor.TotalScore.Value += 300); - } - - [Test] - public void TestVeryLargeScore() - { - AddStep("set large score", () => scoreProcessor.TotalScore.Value = 1_000_000_000); - } - } -} From a31a6947bb4771472686cfd195d69747d1106c0d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 18 May 2021 18:49:05 +0900 Subject: [PATCH 240/839] Add test --- .../TestSceneHitObjectContainerEventQueue.cs | 168 ++++++++++++++++++ osu.Game/Rulesets/UI/Playfield.cs | 7 +- 2 files changed, 173 insertions(+), 2 deletions(-) create mode 100644 osu.Game.Tests/Editing/TestSceneHitObjectContainerEventQueue.cs diff --git a/osu.Game.Tests/Editing/TestSceneHitObjectContainerEventQueue.cs b/osu.Game.Tests/Editing/TestSceneHitObjectContainerEventQueue.cs new file mode 100644 index 0000000000..3f4d1b835f --- /dev/null +++ b/osu.Game.Tests/Editing/TestSceneHitObjectContainerEventQueue.cs @@ -0,0 +1,168 @@ +// 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 NUnit.Framework; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.UI; +using osu.Game.Screens.Edit.Compose; +using osu.Game.Tests.Visual; + +namespace osu.Game.Tests.Editing +{ + public class TestSceneHitObjectContainerEventQueue : OsuTestScene + { + private readonly TestHitObject testObj = new TestHitObject(); + + private TestPlayfield playfield1; + private TestPlayfield playfield2; + private TestDrawable intermediateDrawable; + private HitObjectContainerEventQueue eventQueue; + + private HitObject beganUsage; + private HitObject finishedUsage; + private HitObject transferredUsage; + + [SetUp] + public void Setup() => Schedule(() => + { + reset(); + + if (eventQueue != null) + { + eventQueue.HitObjectUsageBegan -= onHitObjectUsageBegan; + eventQueue.HitObjectUsageFinished -= onHitObjectUsageFinished; + eventQueue.HitObjectUsageTransferred -= onHitObjectUsageTransferred; + } + + var topPlayfield = new TestPlayfield(); + topPlayfield.AddNested(playfield1 = new TestPlayfield()); + topPlayfield.AddNested(playfield2 = new TestPlayfield()); + + eventQueue = new HitObjectContainerEventQueue(topPlayfield); + eventQueue.HitObjectUsageBegan += onHitObjectUsageBegan; + eventQueue.HitObjectUsageFinished += onHitObjectUsageFinished; + eventQueue.HitObjectUsageTransferred += onHitObjectUsageTransferred; + + Children = new Drawable[] + { + topPlayfield, + intermediateDrawable = new TestDrawable(), + eventQueue + }; + }); + + private void onHitObjectUsageBegan(HitObject obj) => beganUsage = obj; + + private void onHitObjectUsageFinished(HitObject obj) => finishedUsage = obj; + + private void onHitObjectUsageTransferred(HitObject obj, DrawableHitObject drawableObj) => transferredUsage = obj; + + [Test] + public void TestUsageBeganAfterAdd() + { + AddStep("add hitobject", () => playfield1.Add(testObj)); + addCheckStep(began: true); + } + + [Test] + public void TestUsageFinishedAfterRemove() + { + AddStep("add hitobject", () => playfield1.Add(testObj)); + addResetStep(); + AddStep("remove hitobject", () => playfield1.Remove(testObj)); + addCheckStep(finished: true); + } + + [Test] + public void TestUsageTransferredWhenMovedBetweenPlayfields() + { + AddStep("add hitobject", () => playfield1.Add(testObj)); + addResetStep(); + AddStep("transfer hitobject to other playfield", () => + { + playfield1.Remove(testObj); + playfield2.Add(testObj); + }); + + addCheckStep(transferred: true); + } + + [Test] + public void TestRemoveImmediatelyAfterUsageBegan() + { + AddStep("add hitobject and schedule removal", () => + { + playfield1.Add(testObj); + intermediateDrawable.Schedule(() => playfield1.Remove(testObj)); + }); + + addCheckStep(); + } + + [Test] + public void TestRemoveImmediatelyAfterTransferred() + { + AddStep("add hitobject", () => playfield1.Add(testObj)); + addResetStep(); + AddStep("transfer hitobject to other playfield and schedule removal", () => + { + playfield1.Remove(testObj); + playfield2.Add(testObj); + intermediateDrawable.Schedule(() => playfield2.Remove(testObj)); + }); + + addCheckStep(finished: true); + } + + private void addResetStep() => AddStep("reset", reset); + + private void reset() + { + beganUsage = null; + finishedUsage = null; + transferredUsage = null; + } + + private void addCheckStep(bool began = false, bool finished = false, bool transferred = false) + => AddAssert($"began = {began}, finished = {finished}, transferred = {transferred}", + () => (beganUsage == testObj) == began && (finishedUsage == testObj) == finished && (transferredUsage == testObj) == transferred); + + private class TestPlayfield : Playfield + { + public TestPlayfield() + { + RegisterPool(1); + } + + public new void AddNested(Playfield playfield) + { + AddInternal(playfield); + base.AddNested(playfield); + } + + protected override HitObjectLifetimeEntry CreateLifetimeEntry(HitObject hitObject) + { + var entry = base.CreateLifetimeEntry(hitObject); + entry.KeepAlive = true; + return entry; + } + } + + private class TestHitObject : HitObject + { + public override string ToString() => "TestHitObject"; + } + + private class TestDrawableHitObject : DrawableHitObject + { + } + + private class TestDrawable : Drawable + { + public new void Schedule(Action action) => base.Schedule(action); + } + } +} diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs index 17d3cf01a4..b154288dba 100644 --- a/osu.Game/Rulesets/UI/Playfield.cs +++ b/osu.Game/Rulesets/UI/Playfield.cs @@ -354,8 +354,11 @@ namespace osu.Game.Rulesets.UI // If this is the first time this DHO is being used, then apply the DHO mods. // This is done before Apply() so that the state is updated once when the hitobject is applied. - foreach (var m in mods.OfType()) - m.ApplyToDrawableHitObjects(dho.Yield()); + if (mods != null) + { + foreach (var m in mods.OfType()) + m.ApplyToDrawableHitObjects(dho.Yield()); + } } if (!lifetimeEntryMap.TryGetValue(hitObject, out var entry)) From bfc0205e9bf378039a87d6d67c05102a7aa4fed3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 18 May 2021 18:49:11 +0900 Subject: [PATCH 241/839] Fix (began, finished) event --- .../Edit/Compose/HitObjectContainerEventQueue.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/HitObjectContainerEventQueue.cs b/osu.Game/Screens/Edit/Compose/HitObjectContainerEventQueue.cs index 363f08cb41..6c1a3b06bb 100644 --- a/osu.Game/Screens/Edit/Compose/HitObjectContainerEventQueue.cs +++ b/osu.Game/Screens/Edit/Compose/HitObjectContainerEventQueue.cs @@ -68,14 +68,20 @@ namespace osu.Game.Screens.Edit.Compose switch (existingEvent, newEvent) { - // This mostly exists as a safeguard to ensure that the sequence: Began -> { Finished -> Began } -> Finished, where { ... } indicates a transferral within a single frame, - // correctly leads into a final "Finished" state. It's unlikely for this to happen normally as it requires the hitobject usage to finish (for the final time) - // immediately after the HitObjectContainer updates lifetime, but it's not inconceivable to occur with the Editor's scheduling and execution order. + // This exists as a safeguard to ensure that the sequence: { Began -> Finished }, where { ... } indicates a sequence within a single frame, does not trigger any events. + // This is unlikely to occur in practice as it requires the usage to finish immediately after the HitObjectContainer updates hitobject lifetimes, + // however, an Editor action scheduled somewhere between the lifetime update and this event queue's own Update() could cause this. + case (EventType.Began, EventType.Finished): + pendingEvents.Remove(hitObject); + break; + + // This exists as a safeguard to ensure that the sequence: Began -> { Finished -> Began -> Finished }, where { ... } indicates a sequence within a single frame, + // correctly leads into a final "finished" state rather than remaining in the intermediate "transferred" state. + // As above, this is unlikely to occur in practice. case (EventType.Transferred, EventType.Finished): pendingEvents[hitObject] = EventType.Finished; break; - case (EventType.Began, EventType.Finished): case (EventType.Finished, EventType.Began): pendingEvents[hitObject] = EventType.Transferred; break; From 633f841a0f691ecb89965cc4762c510e7f3cd4b8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 18 May 2021 18:57:02 +0900 Subject: [PATCH 242/839] Rename to HitObjectUsageEventBuffer --- .../TestSceneHitObjectContainerEventQueue.cs | 20 +++++++++---------- .../Components/EditorBlueprintContainer.cs | 2 +- ...tQueue.cs => HitObjectUsageEventBuffer.cs} | 10 +++++----- 3 files changed, 16 insertions(+), 16 deletions(-) rename osu.Game/Screens/Edit/Compose/{HitObjectContainerEventQueue.cs => HitObjectUsageEventBuffer.cs} (93%) diff --git a/osu.Game.Tests/Editing/TestSceneHitObjectContainerEventQueue.cs b/osu.Game.Tests/Editing/TestSceneHitObjectContainerEventQueue.cs index 3f4d1b835f..ebf98c4c56 100644 --- a/osu.Game.Tests/Editing/TestSceneHitObjectContainerEventQueue.cs +++ b/osu.Game.Tests/Editing/TestSceneHitObjectContainerEventQueue.cs @@ -19,7 +19,7 @@ namespace osu.Game.Tests.Editing private TestPlayfield playfield1; private TestPlayfield playfield2; private TestDrawable intermediateDrawable; - private HitObjectContainerEventQueue eventQueue; + private HitObjectUsageEventBuffer eventBuffer; private HitObject beganUsage; private HitObject finishedUsage; @@ -30,27 +30,27 @@ namespace osu.Game.Tests.Editing { reset(); - if (eventQueue != null) + if (eventBuffer != null) { - eventQueue.HitObjectUsageBegan -= onHitObjectUsageBegan; - eventQueue.HitObjectUsageFinished -= onHitObjectUsageFinished; - eventQueue.HitObjectUsageTransferred -= onHitObjectUsageTransferred; + eventBuffer.HitObjectUsageBegan -= onHitObjectUsageBegan; + eventBuffer.HitObjectUsageFinished -= onHitObjectUsageFinished; + eventBuffer.HitObjectUsageTransferred -= onHitObjectUsageTransferred; } var topPlayfield = new TestPlayfield(); topPlayfield.AddNested(playfield1 = new TestPlayfield()); topPlayfield.AddNested(playfield2 = new TestPlayfield()); - eventQueue = new HitObjectContainerEventQueue(topPlayfield); - eventQueue.HitObjectUsageBegan += onHitObjectUsageBegan; - eventQueue.HitObjectUsageFinished += onHitObjectUsageFinished; - eventQueue.HitObjectUsageTransferred += onHitObjectUsageTransferred; + eventBuffer = new HitObjectUsageEventBuffer(topPlayfield); + eventBuffer.HitObjectUsageBegan += onHitObjectUsageBegan; + eventBuffer.HitObjectUsageFinished += onHitObjectUsageFinished; + eventBuffer.HitObjectUsageTransferred += onHitObjectUsageTransferred; Children = new Drawable[] { topPlayfield, intermediateDrawable = new TestDrawable(), - eventQueue + eventBuffer }; }); diff --git a/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs index c62ea43331..fdea26d92b 100644 --- a/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs @@ -46,7 +46,7 @@ namespace osu.Game.Screens.Edit.Compose.Components foreach (var obj in Composer.HitObjects) AddBlueprintFor(obj.HitObject); - var eventQueue = new HitObjectContainerEventQueue(Composer.Playfield); + var eventQueue = new HitObjectUsageEventBuffer(Composer.Playfield); eventQueue.HitObjectUsageBegan += AddBlueprintFor; eventQueue.HitObjectUsageFinished += RemoveBlueprintFor; eventQueue.HitObjectUsageTransferred += TransferBlueprintFor; diff --git a/osu.Game/Screens/Edit/Compose/HitObjectContainerEventQueue.cs b/osu.Game/Screens/Edit/Compose/HitObjectUsageEventBuffer.cs similarity index 93% rename from osu.Game/Screens/Edit/Compose/HitObjectContainerEventQueue.cs rename to osu.Game/Screens/Edit/Compose/HitObjectUsageEventBuffer.cs index 6c1a3b06bb..8c69e9e707 100644 --- a/osu.Game/Screens/Edit/Compose/HitObjectContainerEventQueue.cs +++ b/osu.Game/Screens/Edit/Compose/HitObjectUsageEventBuffer.cs @@ -13,9 +13,9 @@ using osu.Game.Rulesets.UI; namespace osu.Game.Screens.Edit.Compose { /// - /// A queue which processes events from the many s in a nested hierarchy. + /// Buffers events from the many s in a nested hierarchy. /// - internal class HitObjectContainerEventQueue : Component + internal class HitObjectUsageEventBuffer : Component { /// /// Invoked when a becomes used by a . @@ -41,10 +41,10 @@ namespace osu.Game.Screens.Edit.Compose private readonly Playfield playfield; /// - /// Creates a new . + /// Creates a new . /// /// The most top-level . - public HitObjectContainerEventQueue([NotNull] Playfield playfield) + public HitObjectUsageEventBuffer([NotNull] Playfield playfield) { this.playfield = playfield; @@ -70,7 +70,7 @@ namespace osu.Game.Screens.Edit.Compose { // This exists as a safeguard to ensure that the sequence: { Began -> Finished }, where { ... } indicates a sequence within a single frame, does not trigger any events. // This is unlikely to occur in practice as it requires the usage to finish immediately after the HitObjectContainer updates hitobject lifetimes, - // however, an Editor action scheduled somewhere between the lifetime update and this event queue's own Update() could cause this. + // however, an Editor action scheduled somewhere between the lifetime update and this buffer's own Update() could cause this. case (EventType.Began, EventType.Finished): pendingEvents.Remove(hitObject); break; From 97f4f7bbd1473b7664ab9d07cc8a38382b72391a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 18 May 2021 18:59:45 +0900 Subject: [PATCH 243/839] Remove Component inheritance --- ...TestSceneHitObjectContainerEventBuffer.cs} | 9 +++++++-- .../Components/EditorBlueprintContainer.cs | 19 ++++++++++++++----- .../Edit/Compose/HitObjectUsageEventBuffer.cs | 18 ++++++++---------- 3 files changed, 29 insertions(+), 17 deletions(-) rename osu.Game.Tests/Editing/{TestSceneHitObjectContainerEventQueue.cs => TestSceneHitObjectContainerEventBuffer.cs} (96%) diff --git a/osu.Game.Tests/Editing/TestSceneHitObjectContainerEventQueue.cs b/osu.Game.Tests/Editing/TestSceneHitObjectContainerEventBuffer.cs similarity index 96% rename from osu.Game.Tests/Editing/TestSceneHitObjectContainerEventQueue.cs rename to osu.Game.Tests/Editing/TestSceneHitObjectContainerEventBuffer.cs index ebf98c4c56..30e72150f1 100644 --- a/osu.Game.Tests/Editing/TestSceneHitObjectContainerEventQueue.cs +++ b/osu.Game.Tests/Editing/TestSceneHitObjectContainerEventBuffer.cs @@ -12,7 +12,7 @@ using osu.Game.Tests.Visual; namespace osu.Game.Tests.Editing { - public class TestSceneHitObjectContainerEventQueue : OsuTestScene + public class TestSceneHitObjectContainerEventBuffer : OsuTestScene { private readonly TestHitObject testObj = new TestHitObject(); @@ -50,7 +50,6 @@ namespace osu.Game.Tests.Editing { topPlayfield, intermediateDrawable = new TestDrawable(), - eventBuffer }; }); @@ -117,6 +116,12 @@ namespace osu.Game.Tests.Editing addCheckStep(finished: true); } + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + eventBuffer.Update(); + } + private void addResetStep() => AddStep("reset", reset); private void reset() diff --git a/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs index fdea26d92b..5a6f98f504 100644 --- a/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs @@ -23,6 +23,8 @@ namespace osu.Game.Screens.Edit.Compose.Components protected readonly HitObjectComposer Composer; + private HitObjectUsageEventBuffer usageEventBuffer; + protected EditorBlueprintContainer(HitObjectComposer composer) { Composer = composer; @@ -46,14 +48,19 @@ namespace osu.Game.Screens.Edit.Compose.Components foreach (var obj in Composer.HitObjects) AddBlueprintFor(obj.HitObject); - var eventQueue = new HitObjectUsageEventBuffer(Composer.Playfield); - eventQueue.HitObjectUsageBegan += AddBlueprintFor; - eventQueue.HitObjectUsageFinished += RemoveBlueprintFor; - eventQueue.HitObjectUsageTransferred += TransferBlueprintFor; - AddInternal(eventQueue); + usageEventBuffer = new HitObjectUsageEventBuffer(Composer.Playfield); + usageEventBuffer.HitObjectUsageBegan += AddBlueprintFor; + usageEventBuffer.HitObjectUsageFinished += RemoveBlueprintFor; + usageEventBuffer.HitObjectUsageTransferred += TransferBlueprintFor; } } + protected override void Update() + { + base.Update(); + usageEventBuffer?.Update(); + } + protected override IEnumerable> SortForMovement(IReadOnlyList> blueprints) => blueprints.OrderBy(b => b.Item.StartTime); @@ -145,6 +152,8 @@ namespace osu.Game.Screens.Edit.Compose.Components Beatmap.HitObjectAdded -= AddBlueprintFor; Beatmap.HitObjectRemoved -= RemoveBlueprintFor; } + + usageEventBuffer?.Dispose(); } } } diff --git a/osu.Game/Screens/Edit/Compose/HitObjectUsageEventBuffer.cs b/osu.Game/Screens/Edit/Compose/HitObjectUsageEventBuffer.cs index 8c69e9e707..cbaf9b4f26 100644 --- a/osu.Game/Screens/Edit/Compose/HitObjectUsageEventBuffer.cs +++ b/osu.Game/Screens/Edit/Compose/HitObjectUsageEventBuffer.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Linq; using JetBrains.Annotations; -using osu.Framework.Graphics; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI; @@ -15,7 +14,7 @@ namespace osu.Game.Screens.Edit.Compose /// /// Buffers events from the many s in a nested hierarchy. /// - internal class HitObjectUsageEventBuffer : Component + internal class HitObjectUsageEventBuffer : IDisposable { /// /// Invoked when a becomes used by a . @@ -91,10 +90,8 @@ namespace osu.Game.Screens.Edit.Compose } } - protected override void Update() + public void Update() { - base.Update(); - foreach (var (hitObject, e) in pendingEvents) { switch (e) @@ -116,12 +113,13 @@ namespace osu.Game.Screens.Edit.Compose pendingEvents.Clear(); } - protected override void Dispose(bool isDisposing) + public void Dispose() { - base.Dispose(isDisposing); - - playfield.HitObjectUsageBegan -= onHitObjectUsageBegan; - playfield.HitObjectUsageFinished -= onHitObjectUsageFinished; + if (playfield != null) + { + playfield.HitObjectUsageBegan -= onHitObjectUsageBegan; + playfield.HitObjectUsageFinished -= onHitObjectUsageFinished; + } } private enum EventType From ab6a79f84cccfe9b72f7dcb9966010366f4b0cda Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 18 May 2021 19:10:45 +0900 Subject: [PATCH 244/839] Simplify --- .../TestSceneHitObjectContainerEventBuffer.cs | 4 +- .../Edit/Compose/HitObjectUsageEventBuffer.cs | 88 +++---------------- 2 files changed, 13 insertions(+), 79 deletions(-) diff --git a/osu.Game.Tests/Editing/TestSceneHitObjectContainerEventBuffer.cs b/osu.Game.Tests/Editing/TestSceneHitObjectContainerEventBuffer.cs index 30e72150f1..5233cbc0be 100644 --- a/osu.Game.Tests/Editing/TestSceneHitObjectContainerEventBuffer.cs +++ b/osu.Game.Tests/Editing/TestSceneHitObjectContainerEventBuffer.cs @@ -98,7 +98,7 @@ namespace osu.Game.Tests.Editing intermediateDrawable.Schedule(() => playfield1.Remove(testObj)); }); - addCheckStep(); + addCheckStep(began: true, finished: true); } [Test] @@ -113,7 +113,7 @@ namespace osu.Game.Tests.Editing intermediateDrawable.Schedule(() => playfield2.Remove(testObj)); }); - addCheckStep(finished: true); + addCheckStep(transferred: true, finished: true); } protected override void UpdateAfterChildren() diff --git a/osu.Game/Screens/Edit/Compose/HitObjectUsageEventBuffer.cs b/osu.Game/Screens/Edit/Compose/HitObjectUsageEventBuffer.cs index cbaf9b4f26..fce5aa42ac 100644 --- a/osu.Game/Screens/Edit/Compose/HitObjectUsageEventBuffer.cs +++ b/osu.Game/Screens/Edit/Compose/HitObjectUsageEventBuffer.cs @@ -51,66 +51,23 @@ namespace osu.Game.Screens.Edit.Compose playfield.HitObjectUsageFinished += onHitObjectUsageFinished; } - private readonly Dictionary pendingEvents = new Dictionary(); + private readonly List usageFinishedHitObjects = new List(); - private void onHitObjectUsageBegan(HitObject hitObject) => updateEvent(hitObject, EventType.Began); - - private void onHitObjectUsageFinished(HitObject hitObject) => updateEvent(hitObject, EventType.Finished); - - private void updateEvent(HitObject hitObject, EventType newEvent) + private void onHitObjectUsageBegan(HitObject hitObject) { - if (!pendingEvents.TryGetValue(hitObject, out EventType existingEvent)) - { - pendingEvents[hitObject] = newEvent; - return; - } - - switch (existingEvent, newEvent) - { - // This exists as a safeguard to ensure that the sequence: { Began -> Finished }, where { ... } indicates a sequence within a single frame, does not trigger any events. - // This is unlikely to occur in practice as it requires the usage to finish immediately after the HitObjectContainer updates hitobject lifetimes, - // however, an Editor action scheduled somewhere between the lifetime update and this buffer's own Update() could cause this. - case (EventType.Began, EventType.Finished): - pendingEvents.Remove(hitObject); - break; - - // This exists as a safeguard to ensure that the sequence: Began -> { Finished -> Began -> Finished }, where { ... } indicates a sequence within a single frame, - // correctly leads into a final "finished" state rather than remaining in the intermediate "transferred" state. - // As above, this is unlikely to occur in practice. - case (EventType.Transferred, EventType.Finished): - pendingEvents[hitObject] = EventType.Finished; - break; - - case (EventType.Finished, EventType.Began): - pendingEvents[hitObject] = EventType.Transferred; - break; - - default: - throw new ArgumentOutOfRangeException($"Unexpected event update ({existingEvent} => {newEvent})."); - } + if (usageFinishedHitObjects.Remove(hitObject)) + HitObjectUsageTransferred?.Invoke(hitObject, playfield.AllHitObjects.Single(d => d.HitObject == hitObject)); + else + HitObjectUsageBegan?.Invoke(hitObject); } + private void onHitObjectUsageFinished(HitObject hitObject) => usageFinishedHitObjects.Add(hitObject); + public void Update() { - foreach (var (hitObject, e) in pendingEvents) - { - switch (e) - { - case EventType.Began: - HitObjectUsageBegan?.Invoke(hitObject); - break; - - case EventType.Transferred: - HitObjectUsageTransferred?.Invoke(hitObject, playfield.AllHitObjects.Single(d => d.HitObject == hitObject)); - break; - - case EventType.Finished: - HitObjectUsageFinished?.Invoke(hitObject); - break; - } - } - - pendingEvents.Clear(); + foreach (var hitObject in usageFinishedHitObjects) + HitObjectUsageFinished?.Invoke(hitObject); + usageFinishedHitObjects.Clear(); } public void Dispose() @@ -121,28 +78,5 @@ namespace osu.Game.Screens.Edit.Compose playfield.HitObjectUsageFinished -= onHitObjectUsageFinished; } } - - private enum EventType - { - /// - /// A has started being used by a . - /// - Began, - - /// - /// A has finished being used by a . - /// - Finished, - - /// - /// An internal intermediate state that occurs when a has finished being used by one - /// and started being used by another in the same frame. The may be the same instance in both cases. - /// - /// - /// This usually occurs when a is transferred between s, - /// but also occurs if the dies and becomes alive again in the same frame within the same . - /// - Transferred - } } } From d93ac7ac9842d08a0f0995779e1c179ed031870e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 18 May 2021 19:13:13 +0900 Subject: [PATCH 245/839] Change class xmldoc a bit --- osu.Game/Screens/Edit/Compose/HitObjectUsageEventBuffer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/HitObjectUsageEventBuffer.cs b/osu.Game/Screens/Edit/Compose/HitObjectUsageEventBuffer.cs index fce5aa42ac..621c901fb9 100644 --- a/osu.Game/Screens/Edit/Compose/HitObjectUsageEventBuffer.cs +++ b/osu.Game/Screens/Edit/Compose/HitObjectUsageEventBuffer.cs @@ -12,7 +12,8 @@ using osu.Game.Rulesets.UI; namespace osu.Game.Screens.Edit.Compose { /// - /// Buffers events from the many s in a nested hierarchy. + /// Buffers events from the many s in a nested hierarchy + /// to ensure correct ordering of events. /// internal class HitObjectUsageEventBuffer : IDisposable { From 2c65b8fa9366fded6ae5f3f2167b768a3017f3d5 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 18 May 2021 19:55:25 +0900 Subject: [PATCH 246/839] Revert "Fix uninitialized scrollLength value is used" This reverts commit 73dfb04d --- .../Scrolling/ScrollingHitObjectContainer.cs | 33 ++++++++++++++----- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index 538d4d1d11..915bab9a51 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -185,6 +185,8 @@ namespace osu.Game.Rulesets.UI.Scrolling layoutComputed.Remove(hitObject); } + private float scrollLength; + protected override void Update() { base.Update(); @@ -197,16 +199,29 @@ namespace osu.Game.Rulesets.UI.Scrolling layoutComputed.Clear(); scrollingInfo.Algorithm.Reset(); + + switch (direction.Value) + { + case ScrollingDirection.Up: + case ScrollingDirection.Down: + scrollLength = DrawSize.Y; + break; + + default: + scrollLength = DrawSize.X; + break; + } + layoutCache.Validate(); } } - // We need to calculate hit object positions (including nested hit objects) as soon as possible after lifetimes - // to prevent hit objects displayed in a wrong position for one frame. protected override void UpdateAfterChildrenLife() { base.UpdateAfterChildrenLife(); + // We need to calculate hit object positions (including nested hit objects) as soon as possible after lifetimes + // to prevent hit objects displayed in a wrong position for one frame. // Only AliveObjects need to be considered for layout (reduces overhead in the case of scroll speed changes). foreach (var obj in AliveObjects) { @@ -245,7 +260,7 @@ namespace osu.Game.Rulesets.UI.Scrolling break; } - entry.LifetimeStart = scrollingInfo.Algorithm.GetDisplayStartTime(entry.HitObject.StartTime, startOffset, timeRange.Value, getLength()); + entry.LifetimeStart = scrollingInfo.Algorithm.GetDisplayStartTime(entry.HitObject.StartTime, startOffset, timeRange.Value, scrollLength); } private void updateLayoutRecursive(DrawableHitObject hitObject) @@ -256,12 +271,12 @@ namespace osu.Game.Rulesets.UI.Scrolling { case ScrollingDirection.Up: case ScrollingDirection.Down: - hitObject.Height = scrollingInfo.Algorithm.GetLength(hitObject.HitObject.StartTime, e.EndTime, timeRange.Value, getLength()); + hitObject.Height = scrollingInfo.Algorithm.GetLength(hitObject.HitObject.StartTime, e.EndTime, timeRange.Value, scrollLength); break; case ScrollingDirection.Left: case ScrollingDirection.Right: - hitObject.Width = scrollingInfo.Algorithm.GetLength(hitObject.HitObject.StartTime, e.EndTime, timeRange.Value, getLength()); + hitObject.Width = scrollingInfo.Algorithm.GetLength(hitObject.HitObject.StartTime, e.EndTime, timeRange.Value, scrollLength); break; } } @@ -280,19 +295,19 @@ namespace osu.Game.Rulesets.UI.Scrolling switch (direction.Value) { case ScrollingDirection.Up: - hitObject.Y = scrollingInfo.Algorithm.PositionAt(hitObject.HitObject.StartTime, currentTime, timeRange.Value, getLength()); + hitObject.Y = scrollingInfo.Algorithm.PositionAt(hitObject.HitObject.StartTime, currentTime, timeRange.Value, scrollLength); break; case ScrollingDirection.Down: - hitObject.Y = -scrollingInfo.Algorithm.PositionAt(hitObject.HitObject.StartTime, currentTime, timeRange.Value, getLength()); + hitObject.Y = -scrollingInfo.Algorithm.PositionAt(hitObject.HitObject.StartTime, currentTime, timeRange.Value, scrollLength); break; case ScrollingDirection.Left: - hitObject.X = scrollingInfo.Algorithm.PositionAt(hitObject.HitObject.StartTime, currentTime, timeRange.Value, getLength()); + hitObject.X = scrollingInfo.Algorithm.PositionAt(hitObject.HitObject.StartTime, currentTime, timeRange.Value, scrollLength); break; case ScrollingDirection.Right: - hitObject.X = -scrollingInfo.Algorithm.PositionAt(hitObject.HitObject.StartTime, currentTime, timeRange.Value, getLength()); + hitObject.X = -scrollingInfo.Algorithm.PositionAt(hitObject.HitObject.StartTime, currentTime, timeRange.Value, scrollLength); break; } } From 84a1a86c6329ff60021190f4c070c28707e736c3 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 18 May 2021 19:55:31 +0900 Subject: [PATCH 247/839] Revert "Use entry to calculate lifetime in ScrollingHOC" This reverts commit 632bb70e --- .../Scrolling/ScrollingHitObjectContainer.cs | 60 +++++++++++-------- 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index 915bab9a51..a9eaf3da68 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -5,9 +5,7 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Graphics.Primitives; using osu.Framework.Layout; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; using osuTK; @@ -19,18 +17,16 @@ namespace osu.Game.Rulesets.UI.Scrolling private readonly IBindable timeRange = new BindableDouble(); private readonly IBindable direction = new Bindable(); + /// + /// Hit objects which require lifetime computation in the next update call. + /// + private readonly HashSet toComputeLifetime = new HashSet(); + /// /// A set containing all which have an up-to-date layout. /// private readonly HashSet layoutComputed = new HashSet(); - /// - /// A conservative estimate of maximum bounding box of a - /// with respect to the start time position of the hit object. - /// It is used to calculate when the object appears inbound. - /// - protected virtual RectangleF GetDrawRectangle(HitObjectLifetimeEntry entry) => new RectangleF().Inflate(100); - [Resolved] private IScrollingInfo scrollingInfo { get; set; } @@ -58,6 +54,7 @@ namespace osu.Game.Rulesets.UI.Scrolling { base.Clear(); + toComputeLifetime.Clear(); layoutComputed.Clear(); } @@ -169,6 +166,7 @@ namespace osu.Game.Rulesets.UI.Scrolling private void onRemoveRecursive(DrawableHitObject hitObject) { + toComputeLifetime.Remove(hitObject); layoutComputed.Remove(hitObject); hitObject.DefaultsApplied -= invalidateHitObject; @@ -177,11 +175,14 @@ namespace osu.Game.Rulesets.UI.Scrolling onRemoveRecursive(nested); } + /// + /// Make this lifetime and layout computed in next update. + /// private void invalidateHitObject(DrawableHitObject hitObject) { - if (hitObject.ParentHitObject == null) - updateLifetime(hitObject.Entry); - + // Lifetime computation is delayed until next update because + // when the hit object is not pooled this container is not loaded here and `scrollLength` cannot be computed. + toComputeLifetime.Add(hitObject); layoutComputed.Remove(hitObject); } @@ -193,8 +194,13 @@ namespace osu.Game.Rulesets.UI.Scrolling if (!layoutCache.IsValid) { - foreach (var entry in Entries) - updateLifetime(entry); + toComputeLifetime.Clear(); + + foreach (var hitObject in Objects) + { + if (hitObject.HitObject != null) + toComputeLifetime.Add(hitObject); + } layoutComputed.Clear(); @@ -214,6 +220,11 @@ namespace osu.Game.Rulesets.UI.Scrolling layoutCache.Validate(); } + + foreach (var hitObject in toComputeLifetime) + hitObject.LifetimeStart = computeOriginAdjustedLifetimeStart(hitObject); + + toComputeLifetime.Clear(); } protected override void UpdateAfterChildrenLife() @@ -236,31 +247,32 @@ namespace osu.Game.Rulesets.UI.Scrolling } } - private void updateLifetime(HitObjectLifetimeEntry entry) + private double computeOriginAdjustedLifetimeStart(DrawableHitObject hitObject) { - var rectangle = GetDrawRectangle(entry); - float startOffset = 0; + float originAdjustment = 0.0f; + // calculate the dimension of the part of the hitobject that should already be visible + // when the hitobject origin first appears inside the scrolling container switch (direction.Value) { - case ScrollingDirection.Right: - startOffset = rectangle.Right; + case ScrollingDirection.Up: + originAdjustment = hitObject.OriginPosition.Y; break; case ScrollingDirection.Down: - startOffset = rectangle.Bottom; + originAdjustment = hitObject.DrawHeight - hitObject.OriginPosition.Y; break; case ScrollingDirection.Left: - startOffset = -rectangle.Left; + originAdjustment = hitObject.OriginPosition.X; break; - case ScrollingDirection.Up: - startOffset = -rectangle.Top; + case ScrollingDirection.Right: + originAdjustment = hitObject.DrawWidth - hitObject.OriginPosition.X; break; } - entry.LifetimeStart = scrollingInfo.Algorithm.GetDisplayStartTime(entry.HitObject.StartTime, startOffset, timeRange.Value, scrollLength); + return scrollingInfo.Algorithm.GetDisplayStartTime(hitObject.HitObject.StartTime, originAdjustment, timeRange.Value, scrollLength); } private void updateLayoutRecursive(DrawableHitObject hitObject) From ee9fe3c4bef77f8c7e4c3d67882fa741e1d86932 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 18 May 2021 19:55:44 +0900 Subject: [PATCH 248/839] Revert "Add failing test showing lifetime not recomputed with pooled objects" This reverts commit b88e5a31 --- .../Gameplay/TestSceneDrawableScrollingRuleset.cs | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs index 75a5eec6f7..9931ee4a45 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs @@ -90,20 +90,6 @@ namespace osu.Game.Tests.Visual.Gameplay assertChildPosition(5); } - [TestCase("pooled")] - [TestCase("non-pooled")] - public void TestLifetimeRecomputedWhenTimeRangeChanges(string pooled) - { - var beatmap = createBeatmap(_ => pooled == "pooled" ? new TestPooledHitObject() : new TestHitObject()); - beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = time_range }); - createTest(beatmap); - - assertDead(3); - - AddStep("increase time range", () => drawableRuleset.TimeRange.Value = 3 * time_range); - assertPosition(3, 1); - } - [Test] public void TestRelativeBeatLengthScaleSingleTimingPoint() { From 5f94b3bdacfcd6c7f4eb53e038767df905d234d1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 18 May 2021 21:03:59 +0900 Subject: [PATCH 249/839] Remove legacy playlist item ID handling --- osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs index c0706b082d..7fe48d54b1 100644 --- a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs @@ -92,10 +92,6 @@ namespace osu.Game.Online.Multiplayer [Resolved] private UserLookupCache userLookupCache { get; set; } = null!; - // Only exists for compatibility with old osu-server-spectator build. - // Todo: Can be removed on 2021/02/26. - private long defaultPlaylistItemId; - private Room? apiRoom; [BackgroundDependencyLoader] @@ -143,7 +139,6 @@ namespace osu.Game.Online.Multiplayer { Room = joinedRoom; apiRoom = room; - defaultPlaylistItemId = apiRoom.Playlist.FirstOrDefault()?.ID ?? 0; foreach (var user in joinedRoom.Users) updateUserPlayingState(user.UserID, user.State); }, cancellationSource.Token).ConfigureAwait(false); @@ -581,7 +576,7 @@ namespace osu.Game.Online.Multiplayer void updateItem(PlaylistItem item) { - item.ID = settings.PlaylistItemId == 0 ? defaultPlaylistItemId : settings.PlaylistItemId; + item.ID = settings.PlaylistItemId; item.Beatmap.Value = beatmap; item.Ruleset.Value = ruleset.RulesetInfo; item.RequiredMods.Clear(); From c6160d53044b35abd0ee8168b0bb28344dd4d345 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 18 May 2021 21:17:33 +0900 Subject: [PATCH 250/839] Only ignore online score id for database import --- osu.Game/Screens/Play/Player.cs | 17 ++++++++++++++--- osu.Game/Screens/Play/SubmittingPlayer.cs | 7 +------ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 890883f0d2..6317a41bec 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -929,11 +929,11 @@ namespace osu.Game.Screens.Play /// /// The to import. /// The imported score. - protected virtual Task ImportScore(Score score) + protected virtual async Task ImportScore(Score score) { // Replays are already populated and present in the game's database, so should not be re-imported. if (DrawableRuleset.ReplayScore != null) - return Task.CompletedTask; + return; LegacyByteArrayReader replayReader; @@ -943,7 +943,18 @@ namespace osu.Game.Screens.Play replayReader = new LegacyByteArrayReader(stream.ToArray(), "replay.osr"); } - return scoreManager.Import(score.ScoreInfo, replayReader); + // For the time being, online ID responses are not really useful for anything. + // In addition, the IDs provided via new (lazer) endpoints are based on a different autoincrement from legacy (stable) scores. + // + // Until we better define the server-side logic behind this, let's not store the online ID to avoid potential unique constraint + // conflicts across various systems (ie. solo and multiplayer). + long? onlineScoreId = score.ScoreInfo.OnlineScoreID; + score.ScoreInfo.OnlineScoreID = null; + + await scoreManager.Import(score.ScoreInfo, replayReader).ConfigureAwait(false); + + // ... And restore the online ID for other processes to handle correctly (e.g. de-duplication for the results screen). + score.ScoreInfo.OnlineScoreID = onlineScoreId; } /// diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index d5ad87c70c..23b9037244 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -116,12 +116,7 @@ namespace osu.Game.Screens.Play request.Success += s => { - // For the time being, online ID responses are not really useful for anything. - // In addition, the IDs provided via new (lazer) endpoints are based on a different autoincrement from legacy (stable) scores. - // - // Until we better define the server-side logic behind this, let's not store the online ID to avoid potential unique constraint - // conflicts across various systems (ie. solo and multiplayer). - // score.ScoreInfo.OnlineScoreID = s.ID; + score.ScoreInfo.OnlineScoreID = s.ID; tcs.SetResult(true); }; From 775e0fbde5e6de0a7d20558e105b251ed994f8da Mon Sep 17 00:00:00 2001 From: Lucas A Date: Tue, 18 May 2021 15:27:20 +0200 Subject: [PATCH 251/839] Mark StableImportManager as nullable. --- osu.Game/Screens/Select/SongSelect.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 729e25203f..74e10037ab 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -51,7 +51,7 @@ namespace osu.Game.Screens.Select protected virtual bool ShowFooter => true; - protected virtual bool DisplayStableImportPrompt => stableImportManager.SupportsImportFromStable; + protected virtual bool DisplayStableImportPrompt => stableImportManager?.SupportsImportFromStable == true; /// /// Can be null if is false. @@ -85,7 +85,7 @@ namespace osu.Game.Screens.Select [Resolved] private BeatmapManager beatmaps { get; set; } - [Resolved] + [Resolved(CanBeNull = true)] private StableImportManager stableImportManager { get; set; } protected ModSelectOverlay ModSelect { get; private set; } From 76a377f3e09d88bde52e9309558633219f118e3f Mon Sep 17 00:00:00 2001 From: Vinicius Barbosa Date: Tue, 18 May 2021 15:30:45 +0200 Subject: [PATCH 252/839] Fixed applause sound stopping after switching scores --- .../Ranking/Expanded/Accuracy/AccuracyCircle.cs | 10 ---------- osu.Game/Screens/Ranking/ResultsScreen.cs | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs index bca3a07fa6..28829c4ed8 100644 --- a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs +++ b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs @@ -83,8 +83,6 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy private Container badges; private RankText rankText; - private SkinnableSound applauseSound; - public AccuracyCircle(ScoreInfo score, bool withFlair) { this.score = score; @@ -211,13 +209,6 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy }, rankText = new RankText(score.Rank) }; - - if (withFlair) - { - AddInternal(applauseSound = score.Rank >= ScoreRank.A - ? new SkinnableSound(new SampleInfo("Results/rankpass", "applause")) - : new SkinnableSound(new SampleInfo("Results/rankfail"))); - } } private ScoreRank getRank(ScoreRank rank) @@ -256,7 +247,6 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy using (BeginDelayedSequence(TEXT_APPEAR_DELAY, true)) { - this.Delay(-1440).Schedule(() => applauseSound?.Play()); rankText.Appear(); } } diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index c1f5d92d17..20480c5367 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Bindings; using osu.Framework.Screens; +using osu.Game.Audio; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; @@ -19,7 +20,9 @@ using osu.Game.Online.API; using osu.Game.Rulesets.Mods; using osu.Game.Scoring; using osu.Game.Screens.Play; +using osu.Game.Screens.Ranking.Expanded.Accuracy; using osu.Game.Screens.Ranking.Statistics; +using osu.Game.Skinning; using osuTK; namespace osu.Game.Screens.Ranking @@ -56,6 +59,8 @@ namespace osu.Game.Screens.Ranking private readonly bool allowRetry; private readonly bool allowWatchingReplay; + private SkinnableSound applauseSound; + protected ResultsScreen(ScoreInfo score, bool allowRetry, bool allowWatchingReplay = true) { Score = score; @@ -146,6 +151,13 @@ namespace osu.Game.Screens.Ranking bool shouldFlair = player != null && !Score.Mods.Any(m => m is ModAutoplay); ScorePanelList.AddScore(Score, shouldFlair); + + if (shouldFlair) + { + AddInternal(applauseSound = Score.Rank >= ScoreRank.A + ? new SkinnableSound(new SampleInfo("Results/rankpass", "applause")) + : new SkinnableSound(new SampleInfo("Results/rankfail"))); + } } if (allowWatchingReplay) @@ -183,6 +195,11 @@ namespace osu.Game.Screens.Ranking api.Queue(req); statisticsPanel.State.BindValueChanged(onStatisticsStateChanged, true); + + using(BeginDelayedSequence(AccuracyCircle.ACCURACY_TRANSFORM_DELAY + AccuracyCircle.TEXT_APPEAR_DELAY, true)) + { + this.Delay(-1000).Schedule(() => applauseSound?.Play()); + } } protected override void Update() From 076fcec3dff504fbab201aa82094b7f994a50e03 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 18 May 2021 17:45:15 +0300 Subject: [PATCH 253/839] Revert "Remove ignore attribute from now fixed test scene" This reverts commit 4e12a2734ccf28dae236d6c78385e79d69bf32d7. --- .../TestSceneCatchPlayerLegacySkin.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs index b7cd6737b1..4e56e4b4a7 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs @@ -20,6 +20,7 @@ namespace osu.Game.Rulesets.Catch.Tests protected override Ruleset CreatePlayerRuleset() => new CatchRuleset(); [Test] + [Ignore("HUD components broken, remove when fixed.")] public void TestLegacyHUDComboCounterHidden([Values] bool withModifiedSkin) { if (withModifiedSkin) @@ -32,7 +33,7 @@ namespace osu.Game.Rulesets.Catch.Tests AddAssert("legacy HUD combo counter hidden", () => { - return Player.ChildrenOfType().All(c => c.ChildrenOfType().Single().Alpha == 0f); + return Player.ChildrenOfType().All(c => !c.ChildrenOfType().Single().IsPresent); }); } } From e7d2f42149955a0fb623c39809f279b34f4a35a0 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 18 May 2021 17:46:15 +0300 Subject: [PATCH 254/839] Revert "Merge branch 'fix-legacy-skin-test' into catch-hide-combo-workaround" This reverts commit 380d004683ad658d9ecbc408f7844eaa4c2b43c8, reversing changes made to ff419af5128a3e0f2d188e10fcb05aed712ac128. --- .../TestSceneBeatmapMetadataDisplay.cs | 151 ------------------ osu.Game/Beatmaps/BeatmapDifficultyCache.cs | 4 +- .../Screens/Play/BeatmapMetadataDisplay.cs | 76 ++------- .../Tests/Visual/LegacySkinPlayerTestScene.cs | 27 ---- 4 files changed, 15 insertions(+), 243 deletions(-) delete mode 100644 osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs deleted file mode 100644 index 271fbde5c3..0000000000 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs +++ /dev/null @@ -1,151 +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 System.Threading; -using System.Threading.Tasks; -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Utils; -using osu.Game.Beatmaps; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Mods; -using osu.Game.Screens.Menu; -using osu.Game.Screens.Play; -using osuTK; - -namespace osu.Game.Tests.Visual.SongSelect -{ - public class TestSceneBeatmapMetadataDisplay : OsuTestScene - { - private BeatmapMetadataDisplay display; - - [Resolved] - private BeatmapManager manager { get; set; } - - [Cached(typeof(BeatmapDifficultyCache))] - private readonly TestBeatmapDifficultyCache testDifficultyCache = new TestBeatmapDifficultyCache(); - - [Test] - public void TestLocal([Values("Beatmap", "Some long title and stuff")] - string title, - [Values("Trial", "Some1's very hardest difficulty")] - string version) - { - showMetadataForBeatmap(() => CreateWorkingBeatmap(new Beatmap - { - BeatmapInfo = - { - Metadata = new BeatmapMetadata - { - Title = title, - }, - Version = version, - StarDifficulty = RNG.NextDouble(0, 10), - } - })); - } - - [Test] - public void TestDelayedStarRating() - { - AddStep("block calculation", () => testDifficultyCache.BlockCalculation = true); - - showMetadataForBeatmap(() => CreateWorkingBeatmap(new Beatmap - { - BeatmapInfo = - { - Metadata = new BeatmapMetadata - { - Title = "Heavy beatmap", - }, - Version = "10k objects", - StarDifficulty = 99.99f, - } - })); - - AddStep("allow calculation", () => testDifficultyCache.BlockCalculation = false); - } - - [Test] - public void TestRandomFromDatabase() - { - showMetadataForBeatmap(() => - { - var allBeatmapSets = manager.GetAllUsableBeatmapSets(IncludedDetails.Minimal); - if (allBeatmapSets.Count == 0) - return manager.DefaultBeatmap; - - var randomBeatmapSet = allBeatmapSets[RNG.Next(0, allBeatmapSets.Count - 1)]; - var randomBeatmap = randomBeatmapSet.Beatmaps[RNG.Next(0, randomBeatmapSet.Beatmaps.Count - 1)]; - - return manager.GetWorkingBeatmap(randomBeatmap); - }); - } - - private void showMetadataForBeatmap(Func getBeatmap) - { - AddStep("setup display", () => - { - var randomMods = Ruleset.Value.CreateInstance().GetAllMods().OrderBy(_ => RNG.Next()).Take(5).ToList(); - - OsuLogo logo = new OsuLogo { Scale = new Vector2(0.15f) }; - - Remove(testDifficultyCache); - - Children = new Drawable[] - { - testDifficultyCache, - display = new BeatmapMetadataDisplay(getBeatmap(), new Bindable>(randomMods), logo) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Alpha = 0f, - } - }; - - display.FadeIn(400, Easing.OutQuint); - }); - - AddWaitStep("wait a bit", 5); - - AddStep("finish loading", () => display.Loading = false); - } - - private class TestBeatmapDifficultyCache : BeatmapDifficultyCache - { - private TaskCompletionSource calculationBlocker; - - private bool blockCalculation; - - public bool BlockCalculation - { - get => blockCalculation; - set - { - if (value == blockCalculation) - return; - - blockCalculation = value; - - if (value) - calculationBlocker = new TaskCompletionSource(); - else - calculationBlocker?.SetResult(false); - } - } - - public override async Task GetDifficultyAsync(BeatmapInfo beatmapInfo, RulesetInfo rulesetInfo = null, IEnumerable mods = null, CancellationToken cancellationToken = default) - { - if (blockCalculation) - await calculationBlocker.Task; - - return await base.GetDifficultyAsync(beatmapInfo, rulesetInfo, mods, cancellationToken); - } - } - } -} diff --git a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs index 6ed623d0c0..53d82c385d 100644 --- a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs +++ b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs @@ -103,8 +103,8 @@ namespace osu.Game.Beatmaps /// The s to get the difficulty with. /// An optional which stops computing the star difficulty. /// The . - public virtual Task GetDifficultyAsync([NotNull] BeatmapInfo beatmapInfo, [CanBeNull] RulesetInfo rulesetInfo = null, - [CanBeNull] IEnumerable mods = null, CancellationToken cancellationToken = default) + public Task GetDifficultyAsync([NotNull] BeatmapInfo beatmapInfo, [CanBeNull] RulesetInfo rulesetInfo = null, [CanBeNull] IEnumerable mods = null, + CancellationToken cancellationToken = default) { // In the case that the user hasn't given us a ruleset, use the beatmap's default ruleset. rulesetInfo ??= beatmapInfo.Ruleset; diff --git a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs index fd1150650c..c56344a8fb 100644 --- a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs +++ b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -15,7 +14,6 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Play.HUD; -using osu.Game.Screens.Ranking.Expanded; using osuTK; namespace osu.Game.Screens.Play @@ -27,7 +25,7 @@ namespace osu.Game.Screens.Play { private readonly WorkingBeatmap beatmap; private readonly Bindable> mods; - private readonly Drawable logoFacade; + private readonly Drawable facade; private LoadingSpinner loading; public IBindable> Mods => mods; @@ -43,24 +41,19 @@ namespace osu.Game.Screens.Play } } - public BeatmapMetadataDisplay(WorkingBeatmap beatmap, Bindable> mods, Drawable logoFacade) + public BeatmapMetadataDisplay(WorkingBeatmap beatmap, Bindable> mods, Drawable facade) { this.beatmap = beatmap; - this.logoFacade = logoFacade; + this.facade = facade; this.mods = new Bindable>(); this.mods.BindTo(mods); } - private IBindable starDifficulty; - - private FillFlowContainer versionFlow; - private StarRatingDisplay starRatingDisplay; - [BackgroundDependencyLoader] - private void load(BeatmapDifficultyCache difficultyCache) + private void load() { - var metadata = beatmap.BeatmapInfo.Metadata; + var metadata = beatmap.BeatmapInfo?.Metadata ?? new BeatmapMetadata(); AutoSizeAxes = Axes.Both; Children = new Drawable[] @@ -73,7 +66,7 @@ namespace osu.Game.Screens.Play Direction = FillDirection.Vertical, Children = new[] { - logoFacade.With(d => + facade.With(d => { d.Anchor = Anchor.TopCentre; d.Origin = Anchor.TopCentre; @@ -114,30 +107,16 @@ namespace osu.Game.Screens.Play loading = new LoadingLayer(true) } }, - versionFlow = new FillFlowContainer + new OsuSpriteText { - AutoSizeAxes = Axes.Both, - Anchor = Anchor.TopCentre, + Text = beatmap?.BeatmapInfo?.Version, + Font = OsuFont.GetFont(size: 26, italics: true), Origin = Anchor.TopCentre, - Direction = FillDirection.Vertical, - Spacing = new Vector2(5f), - Margin = new MarginPadding { Bottom = 40 }, - Children = new Drawable[] + Anchor = Anchor.TopCentre, + Margin = new MarginPadding { - new OsuSpriteText - { - Text = beatmap?.BeatmapInfo?.Version, - Font = OsuFont.GetFont(size: 26, italics: true), - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - }, - starRatingDisplay = new StarRatingDisplay(default) - { - Alpha = 0f, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - } - } + Bottom = 40 + }, }, new GridContainer { @@ -180,38 +159,9 @@ namespace osu.Game.Screens.Play } }; - starDifficulty = difficultyCache.GetBindableDifficulty(beatmap.BeatmapInfo); - Loading = true; } - protected override void LoadComplete() - { - base.LoadComplete(); - - if (starDifficulty.Value != null) - { - starRatingDisplay.Current.Value = starDifficulty.Value.Value; - starRatingDisplay.Show(); - } - else - { - starRatingDisplay.Hide(); - - starDifficulty.ValueChanged += d => - { - Debug.Assert(d.NewValue != null); - - starRatingDisplay.Current.Value = d.NewValue.Value; - - versionFlow.AutoSizeDuration = 300; - versionFlow.AutoSizeEasing = Easing.OutQuint; - - starRatingDisplay.FadeIn(300, Easing.InQuint); - }; - } - } - private class MetadataLineLabel : OsuSpriteText { public MetadataLineLabel(string text) diff --git a/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs b/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs index 25c1ae26f9..907a37f4b9 100644 --- a/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs +++ b/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs @@ -1,17 +1,11 @@ // 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 NUnit.Framework; using osu.Framework.Allocation; -using osu.Framework.Audio; using osu.Framework.IO.Stores; -using osu.Framework.Testing; -using osu.Framework.Timing; -using osu.Game.Beatmaps; using osu.Game.Rulesets; using osu.Game.Skinning; -using osu.Game.Storyboards; namespace osu.Game.Tests.Visual { @@ -24,9 +18,6 @@ namespace osu.Game.Tests.Visual protected override TestPlayer CreatePlayer(Ruleset ruleset) => new SkinProvidingPlayer(legacySkinSource); - protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) - => new LegacySkinWorkingBeatmap(beatmap, storyboard, Clock, Audio); - [BackgroundDependencyLoader] private void load(OsuGameBase game, SkinManager skins) { @@ -34,14 +25,6 @@ namespace osu.Game.Tests.Visual legacySkinSource = new SkinProvidingContainer(LegacySkin); } - public override void SetUpSteps() - { - base.SetUpSteps(); - - // check presence of a random legacy HUD component to ensure this is using legacy skin. - AddAssert("using legacy skin", () => this.ChildrenOfType().Any()); - } - public class SkinProvidingPlayer : TestPlayer { [Cached(typeof(ISkinSource))] @@ -52,15 +35,5 @@ namespace osu.Game.Tests.Visual this.skinSource = skinSource; } } - - private class LegacySkinWorkingBeatmap : ClockBackedTestWorkingBeatmap - { - public LegacySkinWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard, IFrameBasedClock frameBasedClock, AudioManager audio) - : base(beatmap, storyboard, frameBasedClock, audio) - { - } - - protected override ISkin GetSkin() => new LegacyBeatmapSkin(BeatmapInfo, null, null); - } } } From 06fffc499b3dd28759e44af39a5ca66176fbd193 Mon Sep 17 00:00:00 2001 From: Vinicius Barbosa Date: Tue, 18 May 2021 16:56:07 +0200 Subject: [PATCH 255/839] Removed unused variables and directives --- osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs | 5 ----- osu.Game/Screens/Ranking/ResultsScreen.cs | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs index 28829c4ed8..ec48a6313b 100644 --- a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs +++ b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs @@ -10,11 +10,9 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Utils; -using osu.Game.Audio; using osu.Game.Graphics; using osu.Game.Rulesets.Mods; using osu.Game.Scoring; -using osu.Game.Skinning; using osuTK; namespace osu.Game.Screens.Ranking.Expanded.Accuracy @@ -76,8 +74,6 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy private readonly ScoreInfo score; - private readonly bool withFlair; - private SmoothCircularProgress accuracyCircle; private SmoothCircularProgress innerMask; private Container badges; @@ -86,7 +82,6 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy public AccuracyCircle(ScoreInfo score, bool withFlair) { this.score = score; - this.withFlair = withFlair; } [BackgroundDependencyLoader] diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index 20480c5367..ab065f7dd4 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -196,7 +196,7 @@ namespace osu.Game.Screens.Ranking statisticsPanel.State.BindValueChanged(onStatisticsStateChanged, true); - using(BeginDelayedSequence(AccuracyCircle.ACCURACY_TRANSFORM_DELAY + AccuracyCircle.TEXT_APPEAR_DELAY, true)) + using (BeginDelayedSequence(AccuracyCircle.ACCURACY_TRANSFORM_DELAY + AccuracyCircle.TEXT_APPEAR_DELAY, true)) { this.Delay(-1000).Schedule(() => applauseSound?.Play()); } From ac5fe0c18cf50c2fa8b1bd112d0f9499b0516f18 Mon Sep 17 00:00:00 2001 From: timiimit Date: Tue, 18 May 2021 15:58:18 +0200 Subject: [PATCH 256/839] Change larger freemod selection overlay --- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 375aac729d..fa30826ee9 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -73,7 +73,7 @@ namespace osu.Game.Screens.OnlinePlay.Match Origin = Anchor.BottomLeft, Depth = float.MinValue, RelativeSizeAxes = Axes.Both, - Height = 0.5f, + Height = 1.0f, Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING }, Child = userModsSelectOverlay = new UserModSelectOverlay { From d05ffdf120c0f13080f43d71b16884efb9d5da8c Mon Sep 17 00:00:00 2001 From: Vinicius Barbosa Date: Tue, 18 May 2021 20:19:18 +0200 Subject: [PATCH 257/839] Added constants for delay value --- .../Visual/Ranking/TestSceneAccuracyCircle.cs | 2 +- .../Expanded/Accuracy/AccuracyCircle.cs | 7 ++++++- .../Expanded/ExpandedPanelMiddleContent.cs | 2 +- osu.Game/Screens/Ranking/ResultsScreen.cs | 2 +- osu.Game/Screens/Ranking/ScorePanel.cs | 18 +++++++++--------- 5 files changed, 18 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs b/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs index 2af15923a0..1e87893f39 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs @@ -120,7 +120,7 @@ namespace osu.Game.Tests.Visual.Ranking } } }, - new AccuracyCircle(score, true) + new AccuracyCircle(score) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs index ec48a6313b..4fca4759f2 100644 --- a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs +++ b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs @@ -37,6 +37,11 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy /// public const double ACCURACY_TRANSFORM_DURATION = 3000; + /// + /// Delay before the default applause sound is played to match the timing + /// + public const double APPLAUSE_DELAY = 1440; + /// /// Delay after for the rank text (A/B/C/D/S/SS) to appear. /// @@ -79,7 +84,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy private Container badges; private RankText rankText; - public AccuracyCircle(ScoreInfo score, bool withFlair) + public AccuracyCircle(ScoreInfo score) { this.score = score; } diff --git a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs index 6a6b39b61c..4895240314 100644 --- a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs @@ -122,7 +122,7 @@ namespace osu.Game.Screens.Ranking.Expanded Margin = new MarginPadding { Top = 40 }, RelativeSizeAxes = Axes.X, Height = 230, - Child = new AccuracyCircle(score, withFlair) + Child = new AccuracyCircle(score) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index ab065f7dd4..e25bbbe253 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -198,7 +198,7 @@ namespace osu.Game.Screens.Ranking using (BeginDelayedSequence(AccuracyCircle.ACCURACY_TRANSFORM_DELAY + AccuracyCircle.TEXT_APPEAR_DELAY, true)) { - this.Delay(-1000).Schedule(() => applauseSound?.Play()); + this.Delay(ScorePanel.RESIZE_DURATION + ScorePanel.TOP_LAYER_EXPAND_DELAY - AccuracyCircle.APPLAUSE_DELAY).Schedule(() => applauseSound?.Play()); } } diff --git a/osu.Game/Screens/Ranking/ScorePanel.cs b/osu.Game/Screens/Ranking/ScorePanel.cs index df710e4eb8..f66a998db6 100644 --- a/osu.Game/Screens/Ranking/ScorePanel.cs +++ b/osu.Game/Screens/Ranking/ScorePanel.cs @@ -54,12 +54,12 @@ namespace osu.Game.Screens.Ranking /// /// Duration for the panel to resize into its expanded/contracted size. /// - private const double resize_duration = 200; + public const double RESIZE_DURATION = 200; /// - /// Delay after before the top layer is expanded. + /// Delay after before the top layer is expanded. /// - private const double top_layer_expand_delay = 100; + public const double TOP_LAYER_EXPAND_DELAY = 100; /// /// Duration for the top layer expansion. @@ -208,8 +208,8 @@ namespace osu.Game.Screens.Ranking case PanelState.Expanded: Size = new Vector2(EXPANDED_WIDTH, expanded_height); - topLayerBackground.FadeColour(expanded_top_layer_colour, resize_duration, Easing.OutQuint); - middleLayerBackground.FadeColour(expanded_middle_layer_colour, resize_duration, Easing.OutQuint); + topLayerBackground.FadeColour(expanded_top_layer_colour, RESIZE_DURATION, Easing.OutQuint); + middleLayerBackground.FadeColour(expanded_middle_layer_colour, RESIZE_DURATION, Easing.OutQuint); topLayerContentContainer.Add(topLayerContent = new ExpandedPanelTopContent(Score.User).With(d => d.Alpha = 0)); middleLayerContentContainer.Add(middleLayerContent = new ExpandedPanelMiddleContent(Score, displayWithFlair).With(d => d.Alpha = 0)); @@ -221,20 +221,20 @@ namespace osu.Game.Screens.Ranking case PanelState.Contracted: Size = new Vector2(CONTRACTED_WIDTH, contracted_height); - topLayerBackground.FadeColour(contracted_top_layer_colour, resize_duration, Easing.OutQuint); - middleLayerBackground.FadeColour(contracted_middle_layer_colour, resize_duration, Easing.OutQuint); + topLayerBackground.FadeColour(contracted_top_layer_colour, RESIZE_DURATION, Easing.OutQuint); + middleLayerBackground.FadeColour(contracted_middle_layer_colour, RESIZE_DURATION, Easing.OutQuint); topLayerContentContainer.Add(topLayerContent = new ContractedPanelTopContent(Score).With(d => d.Alpha = 0)); middleLayerContentContainer.Add(middleLayerContent = new ContractedPanelMiddleContent(Score).With(d => d.Alpha = 0)); break; } - content.ResizeTo(Size, resize_duration, Easing.OutQuint); + content.ResizeTo(Size, RESIZE_DURATION, Easing.OutQuint); bool topLayerExpanded = topLayerContainer.Y < 0; // If the top layer was already expanded, then we don't need to wait for the resize and can instead transform immediately. This looks better when changing the panel state. - using (BeginDelayedSequence(topLayerExpanded ? 0 : resize_duration + top_layer_expand_delay, true)) + using (BeginDelayedSequence(topLayerExpanded ? 0 : RESIZE_DURATION + TOP_LAYER_EXPAND_DELAY, true)) { topLayerContainer.FadeIn(); From d70d37b7f46ec340113e0e531ac38f0975eccf63 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 18 May 2021 22:30:36 +0300 Subject: [PATCH 258/839] Remove convoluted autosize logic in MonthSection --- .../Overlays/News/Sidebar/MonthSection.cs | 25 +++---------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/osu.Game/Overlays/News/Sidebar/MonthSection.cs b/osu.Game/Overlays/News/Sidebar/MonthSection.cs index 166da97f93..2fc88b3909 100644 --- a/osu.Game/Overlays/News/Sidebar/MonthSection.cs +++ b/osu.Game/Overlays/News/Sidebar/MonthSection.cs @@ -139,24 +139,23 @@ namespace osu.Game.Overlays.News.Sidebar { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; + AutoSizeDuration = animation_duration; + AutoSizeEasing = Easing.Out; InternalChild = content = new FillFlowContainer { Margin = new MarginPadding { Top = 5 }, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 5) + Spacing = new Vector2(0, 5), + Alpha = 0 }; } protected override void LoadComplete() { base.LoadComplete(); - IsOpen.BindValueChanged(_ => updateState(), true); - - // First state change should be instant. - FinishTransforms(true); } private void updateState() @@ -176,22 +175,6 @@ namespace osu.Game.Overlays.News.Sidebar content.FadeOut(animation_duration, Easing.OutQuint); } } - - private bool autoSizeTransitionApplied; - - // Workaround to allow the dropdown to be opened immediately since FinishTransforms doesn't work for AutoSize{Duration,Easing}. - protected override void UpdateAfterAutoSize() - { - base.UpdateAfterAutoSize(); - - if (!autoSizeTransitionApplied) - { - AutoSizeDuration = animation_duration; - AutoSizeEasing = Easing.OutQuint; - - autoSizeTransitionApplied = true; - } - } } } } From 1fd00d1313bd7ff8c5acf828843170b5f287b283 Mon Sep 17 00:00:00 2001 From: timiimit Date: Tue, 18 May 2021 21:52:28 +0200 Subject: [PATCH 259/839] Change from fullscreen to 0.7 --- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index fa30826ee9..7395b346a4 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -73,7 +73,7 @@ namespace osu.Game.Screens.OnlinePlay.Match Origin = Anchor.BottomLeft, Depth = float.MinValue, RelativeSizeAxes = Axes.Both, - Height = 1.0f, + Height = 0.7f, Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING }, Child = userModsSelectOverlay = new UserModSelectOverlay { From bde7f88eb3bbc69e0528cab118b698fa08600825 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Tue, 18 May 2021 14:23:22 -0700 Subject: [PATCH 260/839] Don't show warning screen when registering on dev server --- osu.Game/Overlays/AccountCreation/ScreenWarning.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/AccountCreation/ScreenWarning.cs b/osu.Game/Overlays/AccountCreation/ScreenWarning.cs index b2096968fe..ea5f1a6278 100644 --- a/osu.Game/Overlays/AccountCreation/ScreenWarning.cs +++ b/osu.Game/Overlays/AccountCreation/ScreenWarning.cs @@ -26,11 +26,14 @@ namespace osu.Game.Overlays.AccountCreation [Resolved(CanBeNull = true)] private IAPIProvider api { get; set; } + [Resolved] + private OsuGameBase game { get; set; } + private const string help_centre_url = "/help/wiki/Help_Centre#login"; public override void OnEntering(IScreen last) { - if (string.IsNullOrEmpty(api?.ProvidedUsername)) + if (string.IsNullOrEmpty(api?.ProvidedUsername) || game.UseDevelopmentServer) { this.FadeOut(); this.Push(new ScreenEntry()); From 4cc573690e7d366cf801a235c156350fda7a0261 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Tue, 18 May 2021 14:32:56 -0700 Subject: [PATCH 261/839] Move OsuGame out of load method --- osu.Game/Overlays/AccountCreation/ScreenWarning.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/AccountCreation/ScreenWarning.cs b/osu.Game/Overlays/AccountCreation/ScreenWarning.cs index ea5f1a6278..2605a0a6d6 100644 --- a/osu.Game/Overlays/AccountCreation/ScreenWarning.cs +++ b/osu.Game/Overlays/AccountCreation/ScreenWarning.cs @@ -26,8 +26,8 @@ namespace osu.Game.Overlays.AccountCreation [Resolved(CanBeNull = true)] private IAPIProvider api { get; set; } - [Resolved] - private OsuGameBase game { get; set; } + [Resolved(canBeNull: true)] + private OsuGame game { get; set; } private const string help_centre_url = "/help/wiki/Help_Centre#login"; @@ -44,7 +44,7 @@ namespace osu.Game.Overlays.AccountCreation } [BackgroundDependencyLoader(true)] - private void load(OsuColour colours, OsuGame game, TextureStore textures) + private void load(OsuColour colours, TextureStore textures) { if (string.IsNullOrEmpty(api?.ProvidedUsername)) return; From e8bc2cac5bc058bdae888dcd0bc67e9d84b6fc47 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 19 May 2021 13:36:39 +0900 Subject: [PATCH 262/839] Fix test not being marked as headless --- .../Editing/TestSceneHitObjectContainerEventBuffer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Editing/TestSceneHitObjectContainerEventBuffer.cs b/osu.Game.Tests/Editing/TestSceneHitObjectContainerEventBuffer.cs index 5233cbc0be..592971dbaf 100644 --- a/osu.Game.Tests/Editing/TestSceneHitObjectContainerEventBuffer.cs +++ b/osu.Game.Tests/Editing/TestSceneHitObjectContainerEventBuffer.cs @@ -4,6 +4,7 @@ using System; using NUnit.Framework; using osu.Framework.Graphics; +using osu.Framework.Testing; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI; @@ -12,6 +13,7 @@ using osu.Game.Tests.Visual; namespace osu.Game.Tests.Editing { + [HeadlessTest] public class TestSceneHitObjectContainerEventBuffer : OsuTestScene { private readonly TestHitObject testObj = new TestHitObject(); From 013fe4928fe2177802a99bcd1715d9d31077021e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 19 May 2021 08:48:21 +0300 Subject: [PATCH 263/839] Unrevert irrelevant changes --- .../TestSceneBeatmapMetadataDisplay.cs | 151 ++++++++++++++++++ osu.Game/Beatmaps/BeatmapDifficultyCache.cs | 4 +- .../Screens/Play/BeatmapMetadataDisplay.cs | 76 +++++++-- 3 files changed, 216 insertions(+), 15 deletions(-) create mode 100644 osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs new file mode 100644 index 0000000000..271fbde5c3 --- /dev/null +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs @@ -0,0 +1,151 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Utils; +using osu.Game.Beatmaps; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; +using osu.Game.Screens.Menu; +using osu.Game.Screens.Play; +using osuTK; + +namespace osu.Game.Tests.Visual.SongSelect +{ + public class TestSceneBeatmapMetadataDisplay : OsuTestScene + { + private BeatmapMetadataDisplay display; + + [Resolved] + private BeatmapManager manager { get; set; } + + [Cached(typeof(BeatmapDifficultyCache))] + private readonly TestBeatmapDifficultyCache testDifficultyCache = new TestBeatmapDifficultyCache(); + + [Test] + public void TestLocal([Values("Beatmap", "Some long title and stuff")] + string title, + [Values("Trial", "Some1's very hardest difficulty")] + string version) + { + showMetadataForBeatmap(() => CreateWorkingBeatmap(new Beatmap + { + BeatmapInfo = + { + Metadata = new BeatmapMetadata + { + Title = title, + }, + Version = version, + StarDifficulty = RNG.NextDouble(0, 10), + } + })); + } + + [Test] + public void TestDelayedStarRating() + { + AddStep("block calculation", () => testDifficultyCache.BlockCalculation = true); + + showMetadataForBeatmap(() => CreateWorkingBeatmap(new Beatmap + { + BeatmapInfo = + { + Metadata = new BeatmapMetadata + { + Title = "Heavy beatmap", + }, + Version = "10k objects", + StarDifficulty = 99.99f, + } + })); + + AddStep("allow calculation", () => testDifficultyCache.BlockCalculation = false); + } + + [Test] + public void TestRandomFromDatabase() + { + showMetadataForBeatmap(() => + { + var allBeatmapSets = manager.GetAllUsableBeatmapSets(IncludedDetails.Minimal); + if (allBeatmapSets.Count == 0) + return manager.DefaultBeatmap; + + var randomBeatmapSet = allBeatmapSets[RNG.Next(0, allBeatmapSets.Count - 1)]; + var randomBeatmap = randomBeatmapSet.Beatmaps[RNG.Next(0, randomBeatmapSet.Beatmaps.Count - 1)]; + + return manager.GetWorkingBeatmap(randomBeatmap); + }); + } + + private void showMetadataForBeatmap(Func getBeatmap) + { + AddStep("setup display", () => + { + var randomMods = Ruleset.Value.CreateInstance().GetAllMods().OrderBy(_ => RNG.Next()).Take(5).ToList(); + + OsuLogo logo = new OsuLogo { Scale = new Vector2(0.15f) }; + + Remove(testDifficultyCache); + + Children = new Drawable[] + { + testDifficultyCache, + display = new BeatmapMetadataDisplay(getBeatmap(), new Bindable>(randomMods), logo) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Alpha = 0f, + } + }; + + display.FadeIn(400, Easing.OutQuint); + }); + + AddWaitStep("wait a bit", 5); + + AddStep("finish loading", () => display.Loading = false); + } + + private class TestBeatmapDifficultyCache : BeatmapDifficultyCache + { + private TaskCompletionSource calculationBlocker; + + private bool blockCalculation; + + public bool BlockCalculation + { + get => blockCalculation; + set + { + if (value == blockCalculation) + return; + + blockCalculation = value; + + if (value) + calculationBlocker = new TaskCompletionSource(); + else + calculationBlocker?.SetResult(false); + } + } + + public override async Task GetDifficultyAsync(BeatmapInfo beatmapInfo, RulesetInfo rulesetInfo = null, IEnumerable mods = null, CancellationToken cancellationToken = default) + { + if (blockCalculation) + await calculationBlocker.Task; + + return await base.GetDifficultyAsync(beatmapInfo, rulesetInfo, mods, cancellationToken); + } + } + } +} diff --git a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs index 53d82c385d..6ed623d0c0 100644 --- a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs +++ b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs @@ -103,8 +103,8 @@ namespace osu.Game.Beatmaps /// The s to get the difficulty with. /// An optional which stops computing the star difficulty. /// The . - public Task GetDifficultyAsync([NotNull] BeatmapInfo beatmapInfo, [CanBeNull] RulesetInfo rulesetInfo = null, [CanBeNull] IEnumerable mods = null, - CancellationToken cancellationToken = default) + public virtual Task GetDifficultyAsync([NotNull] BeatmapInfo beatmapInfo, [CanBeNull] RulesetInfo rulesetInfo = null, + [CanBeNull] IEnumerable mods = null, CancellationToken cancellationToken = default) { // In the case that the user hasn't given us a ruleset, use the beatmap's default ruleset. rulesetInfo ??= beatmapInfo.Ruleset; diff --git a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs index c56344a8fb..fd1150650c 100644 --- a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs +++ b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -14,6 +15,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Play.HUD; +using osu.Game.Screens.Ranking.Expanded; using osuTK; namespace osu.Game.Screens.Play @@ -25,7 +27,7 @@ namespace osu.Game.Screens.Play { private readonly WorkingBeatmap beatmap; private readonly Bindable> mods; - private readonly Drawable facade; + private readonly Drawable logoFacade; private LoadingSpinner loading; public IBindable> Mods => mods; @@ -41,19 +43,24 @@ namespace osu.Game.Screens.Play } } - public BeatmapMetadataDisplay(WorkingBeatmap beatmap, Bindable> mods, Drawable facade) + public BeatmapMetadataDisplay(WorkingBeatmap beatmap, Bindable> mods, Drawable logoFacade) { this.beatmap = beatmap; - this.facade = facade; + this.logoFacade = logoFacade; this.mods = new Bindable>(); this.mods.BindTo(mods); } + private IBindable starDifficulty; + + private FillFlowContainer versionFlow; + private StarRatingDisplay starRatingDisplay; + [BackgroundDependencyLoader] - private void load() + private void load(BeatmapDifficultyCache difficultyCache) { - var metadata = beatmap.BeatmapInfo?.Metadata ?? new BeatmapMetadata(); + var metadata = beatmap.BeatmapInfo.Metadata; AutoSizeAxes = Axes.Both; Children = new Drawable[] @@ -66,7 +73,7 @@ namespace osu.Game.Screens.Play Direction = FillDirection.Vertical, Children = new[] { - facade.With(d => + logoFacade.With(d => { d.Anchor = Anchor.TopCentre; d.Origin = Anchor.TopCentre; @@ -107,16 +114,30 @@ namespace osu.Game.Screens.Play loading = new LoadingLayer(true) } }, - new OsuSpriteText + versionFlow = new FillFlowContainer { - Text = beatmap?.BeatmapInfo?.Version, - Font = OsuFont.GetFont(size: 26, italics: true), - Origin = Anchor.TopCentre, + AutoSizeAxes = Axes.Both, Anchor = Anchor.TopCentre, - Margin = new MarginPadding + Origin = Anchor.TopCentre, + Direction = FillDirection.Vertical, + Spacing = new Vector2(5f), + Margin = new MarginPadding { Bottom = 40 }, + Children = new Drawable[] { - Bottom = 40 - }, + new OsuSpriteText + { + Text = beatmap?.BeatmapInfo?.Version, + Font = OsuFont.GetFont(size: 26, italics: true), + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }, + starRatingDisplay = new StarRatingDisplay(default) + { + Alpha = 0f, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + } + } }, new GridContainer { @@ -159,9 +180,38 @@ namespace osu.Game.Screens.Play } }; + starDifficulty = difficultyCache.GetBindableDifficulty(beatmap.BeatmapInfo); + Loading = true; } + protected override void LoadComplete() + { + base.LoadComplete(); + + if (starDifficulty.Value != null) + { + starRatingDisplay.Current.Value = starDifficulty.Value.Value; + starRatingDisplay.Show(); + } + else + { + starRatingDisplay.Hide(); + + starDifficulty.ValueChanged += d => + { + Debug.Assert(d.NewValue != null); + + starRatingDisplay.Current.Value = d.NewValue.Value; + + versionFlow.AutoSizeDuration = 300; + versionFlow.AutoSizeEasing = Easing.OutQuint; + + starRatingDisplay.FadeIn(300, Easing.InQuint); + }; + } + } + private class MetadataLineLabel : OsuSpriteText { public MetadataLineLabel(string text) From 539e5179fe8ef9d9cd148882a063595295872815 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 May 2021 15:45:24 +0900 Subject: [PATCH 264/839] Tidy up content and bind event code --- .../Overlays/News/Sidebar/MonthSection.cs | 22 +++++++++---------- osu.Game/Overlays/News/Sidebar/YearsPanel.cs | 4 ++-- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/osu.Game/Overlays/News/Sidebar/MonthSection.cs b/osu.Game/Overlays/News/Sidebar/MonthSection.cs index 2fc88b3909..9fc4f49bd3 100644 --- a/osu.Game/Overlays/News/Sidebar/MonthSection.cs +++ b/osu.Game/Overlays/News/Sidebar/MonthSection.cs @@ -117,10 +117,10 @@ namespace osu.Game.Overlays.News.Sidebar } [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider, GameHost host) + private void load(OverlayColourProvider overlayColours, GameHost host) { - IdleColour = colourProvider.Light2; - HoverColour = colourProvider.Light1; + IdleColour = overlayColours.Light2; + HoverColour = overlayColours.Light1; TooltipText = "view in browser"; Action = () => host.OpenUrlExternally("https://osu.ppy.sh/home/news/" + post.Slug); @@ -131,9 +131,7 @@ namespace osu.Game.Overlays.News.Sidebar { public readonly BindableBool IsOpen = new BindableBool(); - protected override Container Content => content; - - private readonly FillFlowContainer content; + protected override Container Content { get; } public PostsContainer() { @@ -141,7 +139,7 @@ namespace osu.Game.Overlays.News.Sidebar AutoSizeAxes = Axes.Y; AutoSizeDuration = animation_duration; AutoSizeEasing = Easing.Out; - InternalChild = content = new FillFlowContainer + InternalChild = Content = new FillFlowContainer { Margin = new MarginPadding { Top = 5 }, RelativeSizeAxes = Axes.X, @@ -155,24 +153,24 @@ namespace osu.Game.Overlays.News.Sidebar protected override void LoadComplete() { base.LoadComplete(); - IsOpen.BindValueChanged(_ => updateState(), true); + IsOpen.BindValueChanged(updateState, true); } - private void updateState() + private void updateState(ValueChangedEvent isOpen) { ClearTransforms(true); - if (IsOpen.Value) + if (isOpen.NewValue) { AutoSizeAxes = Axes.Y; - content.FadeIn(animation_duration, Easing.OutQuint); + Content.FadeIn(animation_duration, Easing.OutQuint); } else { AutoSizeAxes = Axes.None; this.ResizeHeightTo(0, animation_duration, Easing.OutQuint); - content.FadeOut(animation_duration, Easing.OutQuint); + Content.FadeOut(animation_duration, Easing.OutQuint); } } } diff --git a/osu.Game/Overlays/News/Sidebar/YearsPanel.cs b/osu.Game/Overlays/News/Sidebar/YearsPanel.cs index 849cdbf659..b6bbdbb6d4 100644 --- a/osu.Game/Overlays/News/Sidebar/YearsPanel.cs +++ b/osu.Game/Overlays/News/Sidebar/YearsPanel.cs @@ -22,7 +22,7 @@ namespace osu.Game.Overlays.News.Sidebar private FillFlowContainer yearsFlow; [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider, Bindable metadata) + private void load(OverlayColourProvider overlayColours, Bindable metadata) { this.metadata.BindTo(metadata); @@ -35,7 +35,7 @@ namespace osu.Game.Overlays.News.Sidebar new Box { RelativeSizeAxes = Axes.Both, - Colour = colourProvider.Background3 + Colour = overlayColours.Background3 }, new Container { From 19a07b01073f821f4c02131f7834474396ea8d49 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 May 2021 15:48:31 +0900 Subject: [PATCH 265/839] IsOpen -> Expanded --- .../Overlays/News/Sidebar/MonthSection.cs | 21 ++++++++++--------- osu.Game/Overlays/News/Sidebar/NewsSidebar.cs | 2 +- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/News/Sidebar/MonthSection.cs b/osu.Game/Overlays/News/Sidebar/MonthSection.cs index 9fc4f49bd3..b300a755f9 100644 --- a/osu.Game/Overlays/News/Sidebar/MonthSection.cs +++ b/osu.Game/Overlays/News/Sidebar/MonthSection.cs @@ -23,7 +23,7 @@ namespace osu.Game.Overlays.News.Sidebar { private const int animation_duration = 250; - public readonly BindableBool IsOpen = new BindableBool(); + public readonly BindableBool Expanded = new BindableBool(); public MonthSection(int month, int year, IEnumerable posts) { @@ -32,6 +32,7 @@ namespace osu.Game.Overlays.News.Sidebar RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; Masking = true; + InternalChild = new FillFlowContainer { RelativeSizeAxes = Axes.X, @@ -41,11 +42,11 @@ namespace osu.Game.Overlays.News.Sidebar { new DropdownHeader(month, year) { - IsOpen = { BindTarget = IsOpen } + Expanded = { BindTarget = Expanded } }, new PostsContainer { - IsOpen = { BindTarget = IsOpen }, + Expanded = { BindTarget = Expanded }, Children = posts.Select(p => new PostButton(p)).ToArray() } } @@ -54,7 +55,7 @@ namespace osu.Game.Overlays.News.Sidebar private class DropdownHeader : OsuClickableContainer { - public readonly BindableBool IsOpen = new BindableBool(); + public readonly BindableBool Expanded = new BindableBool(); private readonly SpriteIcon icon; @@ -64,7 +65,7 @@ namespace osu.Game.Overlays.News.Sidebar RelativeSizeAxes = Axes.X; Height = 15; - Action = IsOpen.Toggle; + Action = Expanded.Toggle; Children = new Drawable[] { new OsuSpriteText @@ -88,7 +89,7 @@ namespace osu.Game.Overlays.News.Sidebar { base.LoadComplete(); - IsOpen.BindValueChanged(open => + Expanded.BindValueChanged(open => { icon.Scale = new Vector2(1, open.NewValue ? -1 : 1); }, true); @@ -129,7 +130,7 @@ namespace osu.Game.Overlays.News.Sidebar private class PostsContainer : Container { - public readonly BindableBool IsOpen = new BindableBool(); + public readonly BindableBool Expanded = new BindableBool(); protected override Container Content { get; } @@ -153,14 +154,14 @@ namespace osu.Game.Overlays.News.Sidebar protected override void LoadComplete() { base.LoadComplete(); - IsOpen.BindValueChanged(updateState, true); + Expanded.BindValueChanged(updateState, true); } - private void updateState(ValueChangedEvent isOpen) + private void updateState(ValueChangedEvent expanded) { ClearTransforms(true); - if (isOpen.NewValue) + if (expanded.NewValue) { AutoSizeAxes = Axes.Y; Content.FadeIn(animation_duration, Easing.OutQuint); diff --git a/osu.Game/Overlays/News/Sidebar/NewsSidebar.cs b/osu.Game/Overlays/News/Sidebar/NewsSidebar.cs index b8d283b7e2..d14ad90ef4 100644 --- a/osu.Game/Overlays/News/Sidebar/NewsSidebar.cs +++ b/osu.Game/Overlays/News/Sidebar/NewsSidebar.cs @@ -95,7 +95,7 @@ namespace osu.Game.Overlays.News.Sidebar monthsFlow.Add(new MonthSection(month, year, posts) { - IsOpen = { Value = i == 0 } + Expanded = { Value = i == 0 } }); } } From 124ac689b88e5ea7d48f5018c62454235f1eeceb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 May 2021 16:26:27 +0900 Subject: [PATCH 266/839] Add method to `PopupDialog` to press the first OK button --- osu.Game/Overlays/Dialog/PopupDialog.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs index 1bcbe4dd2f..df085b56f2 100644 --- a/osu.Game/Overlays/Dialog/PopupDialog.cs +++ b/osu.Game/Overlays/Dialog/PopupDialog.cs @@ -207,6 +207,11 @@ namespace osu.Game.Overlays.Dialog }; } + /// + /// Programmatically clicks the first . + /// + public void PerformOkAction() => Buttons.OfType().First().Click(); + protected override bool OnKeyDown(KeyDownEvent e) { if (e.Repeat) return false; From b83322281e2effed91b67d81aa10fd71e54f32df Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 May 2021 16:27:38 +0900 Subject: [PATCH 267/839] Consume updated logic at `MainMenu` --- osu.Game/Screens/Menu/MainMenu.cs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index baeb86c976..e53b46f391 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -1,9 +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.Linq; -using osuTK; -using osuTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -22,6 +19,8 @@ using osu.Game.Screens.Edit; using osu.Game.Screens.OnlinePlay.Multiplayer; using osu.Game.Screens.OnlinePlay.Playlists; using osu.Game.Screens.Select; +using osuTK; +using osuTK.Graphics; namespace osu.Game.Screens.Menu { @@ -120,7 +119,7 @@ namespace osu.Game.Screens.Menu Origin = Anchor.TopRight, Margin = new MarginPadding { Right = 15, Top = 5 } }, - exitConfirmOverlay?.CreateProxy() ?? Drawable.Empty() + exitConfirmOverlay?.CreateProxy() ?? Empty() }); buttons.StateChanged += state => @@ -270,15 +269,11 @@ namespace osu.Game.Screens.Menu if (!exitConfirmed && dialogOverlay != null) { if (dialogOverlay.CurrentDialog is ConfirmExitDialog exitDialog) - { - exitConfirmed = true; - exitDialog.Buttons.First().Click(); - } + exitDialog.PerformOkAction(); else - { dialogOverlay.Push(new ConfirmExitDialog(confirmAndExit, () => exitConfirmOverlay.Abort())); - return true; - } + + return true; } buttons.State = ButtonSystemState.Exit; From 44e22b31a905b18a35ca0f84282f581c5c908440 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 May 2021 16:28:25 +0900 Subject: [PATCH 268/839] Refactor editor exit sequence to avoid any scenario where a dialog could go stray --- osu.Game/Screens/Edit/Editor.cs | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 5ac3401720..5e990ff81e 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -473,25 +473,23 @@ namespace osu.Game.Screens.Edit { if (!exitConfirmed) { - // if the confirm dialog is already showing (or we can't show it, ie. in tests) exit without save. - if (dialogOverlay == null || dialogOverlay.CurrentDialog is PromptForSaveDialog) + // dialog overlay may not be available in visual tests. + if (dialogOverlay == null) { confirmExit(); - return base.OnExiting(next); + return true; + } + + // if the dialog is already displayed, confirm exit with no save. + if (dialogOverlay.CurrentDialog is PromptForSaveDialog saveDialog) + { + saveDialog.PerformOkAction(); + return true; } if (isNewBeatmap || HasUnsavedChanges) { - dialogOverlay?.Push(new PromptForSaveDialog(() => - { - confirmExit(); - this.Exit(); - }, () => - { - confirmExitWithSave(); - this.Exit(); - })); - + dialogOverlay?.Push(new PromptForSaveDialog(confirmExit, confirmExitWithSave)); return true; } } @@ -506,8 +504,10 @@ namespace osu.Game.Screens.Edit private void confirmExitWithSave() { - exitConfirmed = true; Save(); + + exitConfirmed = true; + this.Exit(); } private void confirmExit() @@ -529,6 +529,7 @@ namespace osu.Game.Screens.Edit } exitConfirmed = true; + this.Exit(); } private readonly Bindable clipboard = new Bindable(); From fc5987bf6978f565bf038d474650fb2409cf5ac8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 May 2021 16:52:34 +0900 Subject: [PATCH 269/839] Refactor `DialogOverlay` logic to avoid dismissal potentially being unhandled --- osu.Game/Overlays/Dialog/PopupDialog.cs | 6 ++++++ osu.Game/Overlays/DialogOverlay.cs | 7 ++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs index df085b56f2..c04ad7afb5 100644 --- a/osu.Game/Overlays/Dialog/PopupDialog.cs +++ b/osu.Game/Overlays/Dialog/PopupDialog.cs @@ -95,6 +95,8 @@ namespace osu.Game.Overlays.Dialog } } + protected override bool StartHidden => true; + protected PopupDialog() { RelativeSizeAxes = Axes.Both; @@ -205,6 +207,10 @@ namespace osu.Game.Overlays.Dialog }, }, }; + + // It's important we start in a visible state so our state fires on hide, even before load. + // This is used by the DialogOverlay to know when the dialog was dismissed. + Show(); } /// diff --git a/osu.Game/Overlays/DialogOverlay.cs b/osu.Game/Overlays/DialogOverlay.cs index 4cc17a4c14..bc3b0e6c9a 100644 --- a/osu.Game/Overlays/DialogOverlay.cs +++ b/osu.Game/Overlays/DialogOverlay.cs @@ -35,15 +35,16 @@ namespace osu.Game.Overlays public void Push(PopupDialog dialog) { - if (dialog == CurrentDialog) return; + if (dialog == CurrentDialog || dialog.State.Value != Visibility.Visible) return; + // if any existing dialog is being displayed, dismiss it before showing a new one. CurrentDialog?.Hide(); + CurrentDialog = dialog; + CurrentDialog.State.ValueChanged += state => onDialogOnStateChanged(dialog, state.NewValue); dialogContainer.Add(CurrentDialog); - CurrentDialog.Show(); - CurrentDialog.State.ValueChanged += state => onDialogOnStateChanged(dialog, state.NewValue); Show(); } From b33d5e7a7a4c0a906d0b13f44bdaf0366f9d3938 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 May 2021 17:00:52 +0900 Subject: [PATCH 270/839] Add new test and assertions to existing `DialogOverlay` test --- .../UserInterface/TestSceneDialogOverlay.cs | 57 +++++++++++++++++-- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs index cc4a57fb83..f5cba2c900 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs @@ -2,7 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; +using osu.Framework.Testing; using osu.Game.Overlays; using osu.Game.Overlays.Dialog; @@ -11,13 +13,20 @@ namespace osu.Game.Tests.Visual.UserInterface [TestFixture] public class TestSceneDialogOverlay : OsuTestScene { - public TestSceneDialogOverlay() + private DialogOverlay overlay; + + [SetUpSteps] + public void SetUpSteps() { - DialogOverlay overlay; + AddStep("create dialog overlay", () => Child = overlay = new DialogOverlay()); + } - Add(overlay = new DialogOverlay()); + [Test] + public void TestBasic() + { + TestPopupDialog dialog = null; - AddStep("dialog #1", () => overlay.Push(new TestPopupDialog + AddStep("dialog #1", () => overlay.Push(dialog = new TestPopupDialog { Icon = FontAwesome.Regular.TrashAlt, HeaderText = @"Confirm deletion of", @@ -37,7 +46,9 @@ namespace osu.Game.Tests.Visual.UserInterface }, })); - AddStep("dialog #2", () => overlay.Push(new TestPopupDialog + AddAssert("first dialog displayed", () => overlay.CurrentDialog == dialog); + + AddStep("dialog #2", () => overlay.Push(dialog = new TestPopupDialog { Icon = FontAwesome.Solid.Cog, HeaderText = @"What do you want to do with", @@ -70,6 +81,42 @@ namespace osu.Game.Tests.Visual.UserInterface }, }, })); + + AddAssert("second dialog displayed", () => overlay.CurrentDialog == dialog); + } + + [Test] + public void TestDismissBeforePush() + { + AddStep("dismissed dialog push", () => + { + overlay.Push(new TestPopupDialog + { + State = { Value = Visibility.Hidden } + }); + }); + + AddAssert("no dialog pushed", () => overlay.CurrentDialog == null); + } + + [Test] + public void TestDismissBeforePushViaButtonPress() + { + AddStep("dismissed dialog push", () => + { + TestPopupDialog dialog; + overlay.Push(dialog = new TestPopupDialog + { + Buttons = new PopupDialogButton[] + { + new PopupDialogOkButton { Text = @"OK" }, + }, + }); + + dialog.PerformOkAction(); + }); + + AddAssert("no dialog pushed", () => overlay.CurrentDialog == null); } private class TestPopupDialog : PopupDialog From cc5c702a92ddf6306e8b136699f299b1d1c7d484 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 May 2021 17:31:47 +0900 Subject: [PATCH 271/839] Apply all properties after cast (looks cleaner) --- osu.Game.Rulesets.Mania/UI/Column.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index 57d3f54716..9b5893b268 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -116,9 +116,9 @@ namespace osu.Game.Rulesets.Mania.UI { base.OnNewDrawableHitObject(drawableHitObject); - drawableHitObject.AccentColour.Value = AccentColour; - DrawableManiaHitObject maniaObject = (DrawableManiaHitObject)drawableHitObject; + + maniaObject.AccentColour.Value = AccentColour; maniaObject.CheckHittable = hitPolicy.IsHittable; } From a0f67ef3bc1bab4ae0d07115d6df97ef23bb88d8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 May 2021 18:57:52 +0900 Subject: [PATCH 272/839] Move scaling logic out of `OsuSelectionHandler` for reuse --- .../Edit/OsuSelectionHandler.cs | 18 +---------------- .../Compose/Components/SelectionHandler.cs | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index aaf3517c9c..8cb86bc108 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -225,26 +225,10 @@ namespace osu.Game.Rulesets.Osu.Edit private void scaleHitObjects(OsuHitObject[] hitObjects, Anchor reference, Vector2 scale) { scale = getClampedScale(hitObjects, reference, scale); - - // move the selection before scaling if dragging from top or left anchors. - float xOffset = ((reference & Anchor.x0) > 0) ? -scale.X : 0; - float yOffset = ((reference & Anchor.y0) > 0) ? -scale.Y : 0; - Quad selectionQuad = getSurroundingQuad(hitObjects); foreach (var h in hitObjects) - { - var newPosition = h.Position; - - // guard against no-ops and NaN. - if (scale.X != 0 && selectionQuad.Width > 0) - newPosition.X = selectionQuad.TopLeft.X + xOffset + (h.X - selectionQuad.TopLeft.X) / selectionQuad.Width * (selectionQuad.Width + scale.X); - - if (scale.Y != 0 && selectionQuad.Height > 0) - newPosition.Y = selectionQuad.TopLeft.Y + yOffset + (h.Y - selectionQuad.TopLeft.Y) / selectionQuad.Height * (selectionQuad.Height + scale.Y); - - h.Position = newPosition; - } + h.Position = GetScaledPosition(reference, scale, selectionQuad, h.Position); } private (bool X, bool Y) isQuadInBounds(Quad quad) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index bfd5ab7afa..26328b4dc7 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -375,6 +375,26 @@ namespace osu.Game.Screens.Edit.Compose.Components return position; } + /// + /// Given a scale vector, a surrounding quad for all selected objects, and a position, + /// will return the scaled position in screen space coordinates. + /// + protected static Vector2 GetScaledPosition(Anchor reference, Vector2 scale, Quad selectionQuad, Vector2 position) + { + // adjust the direction of scale depending on which side the user is dragging. + float xOffset = ((reference & Anchor.x0) > 0) ? -scale.X : 0; + float yOffset = ((reference & Anchor.y0) > 0) ? -scale.Y : 0; + + // guard against no-ops and NaN. + if (scale.X != 0 && selectionQuad.Width > 0) + position.X = selectionQuad.TopLeft.X + xOffset + (position.X - selectionQuad.TopLeft.X) / selectionQuad.Width * (selectionQuad.Width + scale.X); + + if (scale.Y != 0 && selectionQuad.Height > 0) + position.Y = selectionQuad.TopLeft.Y + yOffset + (position.Y - selectionQuad.TopLeft.Y) / selectionQuad.Height * (selectionQuad.Height + scale.Y); + + return position; + } + /// /// Returns a quad surrounding the provided points. /// From 9806d94b745348e7c44af36cc93b76401234d225 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 15 May 2021 17:56:19 +0300 Subject: [PATCH 273/839] Move beatmap skin info creation to static method at `IBeatmapSkin` --- osu.Game/Skinning/BeatmapSkinExtensions.cs | 16 ++++++++++++++++ osu.Game/Skinning/LegacyBeatmapSkin.cs | 5 +---- 2 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 osu.Game/Skinning/BeatmapSkinExtensions.cs diff --git a/osu.Game/Skinning/BeatmapSkinExtensions.cs b/osu.Game/Skinning/BeatmapSkinExtensions.cs new file mode 100644 index 0000000000..18ef09c392 --- /dev/null +++ b/osu.Game/Skinning/BeatmapSkinExtensions.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.Game.Beatmaps; + +namespace osu.Game.Skinning +{ + public static class BeatmapSkinExtensions + { + public static SkinInfo CreateSkinInfo(BeatmapInfo beatmap) => new SkinInfo + { + Name = beatmap.ToString(), + Creator = beatmap.Metadata?.AuthorString, + }; + } +} diff --git a/osu.Game/Skinning/LegacyBeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs index 3ec205e897..5ee436e8bb 100644 --- a/osu.Game/Skinning/LegacyBeatmapSkin.cs +++ b/osu.Game/Skinning/LegacyBeatmapSkin.cs @@ -17,7 +17,7 @@ namespace osu.Game.Skinning protected override bool UseCustomSampleBanks => true; public LegacyBeatmapSkin(BeatmapInfo beatmap, IResourceStore storage, IStorageResourceProvider resources) - : base(createSkinInfo(beatmap), new LegacySkinResourceStore(beatmap.BeatmapSet, storage), resources, beatmap.Path) + : base(BeatmapSkinExtensions.CreateSkinInfo(beatmap), new LegacySkinResourceStore(beatmap.BeatmapSet, storage), resources, beatmap.Path) { // Disallow default colours fallback on beatmap skins to allow using parent skin combo colours. (via SkinProvidingContainer) Configuration.AllowDefaultComboColoursFallback = false; @@ -49,8 +49,5 @@ namespace osu.Game.Skinning return base.GetSample(sampleInfo); } - - private static SkinInfo createSkinInfo(BeatmapInfo beatmap) => - new SkinInfo { Name = beatmap.ToString(), Creator = beatmap.Metadata.Author.ToString() }; } } From a639132825f38ed9c159247a7ef7b6769b7254ee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 May 2021 17:58:28 +0900 Subject: [PATCH 274/839] Avoid doing any re-fetch on beatmap from test scenes --- osu.Game/Beatmaps/BeatmapManager.cs | 3 +-- osu.Game/Screens/Edit/Editor.cs | 6 +++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 5e975de77c..dadc0624b3 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -286,11 +286,10 @@ namespace osu.Game.Beatmaps { if (beatmapInfo?.ID > 0 && previous != null && previous.BeatmapInfo?.ID == beatmapInfo.ID) return previous; - if (beatmapInfo?.BeatmapSet == null || beatmapInfo == DefaultBeatmap?.BeatmapInfo) return DefaultBeatmap; - if (beatmapInfo.BeatmapSet.Files == null) + if (beatmapInfo.BeatmapSet.Files == null || beatmapInfo.ID == 0) { var info = beatmapInfo; beatmapInfo = QueryBeatmap(b => b.ID == info.ID); diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 5e990ff81e..eafa5847a3 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -497,7 +497,11 @@ namespace osu.Game.Screens.Edit ApplyToBackground(b => b.FadeColour(Color4.White, 500)); resetTrack(); - Beatmap.Value = beatmapManager.GetWorkingBeatmap(Beatmap.Value.BeatmapInfo); + var refetchedBeatmap = beatmapManager.GetWorkingBeatmap(Beatmap.Value.BeatmapInfo); + + // beatmap re-fetch may not be feasible in tests. + if (!(refetchedBeatmap is DummyWorkingBeatmap)) + Beatmap.Value = refetchedBeatmap; return base.OnExiting(next); } From 825d61e22c92646d3166f65d7a44ae631b8fb343 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 19 May 2021 12:06:09 +0300 Subject: [PATCH 275/839] Separate default beatmap skins from `DefaultSkin` --- osu.Game/Beatmaps/WorkingBeatmap.cs | 2 +- osu.Game/Skinning/BeatmapSkin.cs | 32 +++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Skinning/BeatmapSkin.cs diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 3576b149bf..ead8572c54 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -324,7 +324,7 @@ namespace osu.Game.Beatmaps public bool SkinLoaded => skin.IsResultAvailable; public ISkin Skin => skin.Value; - protected virtual ISkin GetSkin() => new DefaultSkin(null); + protected virtual ISkin GetSkin() => new BeatmapSkin(BeatmapInfo); private readonly RecyclableLazy skin; public abstract Stream GetStream(string storagePath); diff --git a/osu.Game/Skinning/BeatmapSkin.cs b/osu.Game/Skinning/BeatmapSkin.cs new file mode 100644 index 0000000000..14b845faeb --- /dev/null +++ b/osu.Game/Skinning/BeatmapSkin.cs @@ -0,0 +1,32 @@ +// 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.Sample; +using osu.Framework.Bindables; +using osu.Framework.Graphics.OpenGL.Textures; +using osu.Framework.Graphics.Textures; +using osu.Game.Audio; +using osu.Game.Beatmaps; + +namespace osu.Game.Skinning +{ + /// + /// An empty implementation of a beatmap skin, serves as a temporary default for s. + /// + /// + /// This should be removed once becomes instantiable or a new skin type for osu!lazer beatmaps is defined. + /// + public class BeatmapSkin : Skin + { + public BeatmapSkin(BeatmapInfo beatmap) + : base(BeatmapSkinExtensions.CreateSkinInfo(beatmap), null) + { + } + + public override Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => null; + + public override IBindable GetConfig(TLookup lookup) => null; + + public override ISample GetSample(ISampleInfo sampleInfo) => null; + } +} From b2c736b42a52c91f8780ad00602a0dd18d1e6b83 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 May 2021 18:09:46 +0900 Subject: [PATCH 276/839] Combine and move `const` closer to usage --- .../Ranking/Expanded/Accuracy/AccuracyCircle.cs | 5 ----- osu.Game/Screens/Ranking/ResultsScreen.cs | 11 +++++++---- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs index 4fca4759f2..c70b4dd35b 100644 --- a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs +++ b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs @@ -37,11 +37,6 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy /// public const double ACCURACY_TRANSFORM_DURATION = 3000; - /// - /// Delay before the default applause sound is played to match the timing - /// - public const double APPLAUSE_DELAY = 1440; - /// /// Delay after for the rank text (A/B/C/D/S/SS) to appear. /// diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index e25bbbe253..a0ea27b640 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -29,6 +29,11 @@ namespace osu.Game.Screens.Ranking { public abstract class ResultsScreen : ScreenWithBeatmapBackground, IKeyBindingHandler { + /// + /// Delay before the default applause sound should be played, in order to match the grade display timing in . + /// + public const double APPLAUSE_DELAY = AccuracyCircle.ACCURACY_TRANSFORM_DELAY + AccuracyCircle.TEXT_APPEAR_DELAY + ScorePanel.RESIZE_DURATION + ScorePanel.TOP_LAYER_EXPAND_DELAY - 1440; + protected const float BACKGROUND_BLUR = 20; private static readonly float screen_height = 768 - TwoLayerButton.SIZE_EXTENDED.Y; @@ -196,10 +201,8 @@ namespace osu.Game.Screens.Ranking statisticsPanel.State.BindValueChanged(onStatisticsStateChanged, true); - using (BeginDelayedSequence(AccuracyCircle.ACCURACY_TRANSFORM_DELAY + AccuracyCircle.TEXT_APPEAR_DELAY, true)) - { - this.Delay(ScorePanel.RESIZE_DURATION + ScorePanel.TOP_LAYER_EXPAND_DELAY - AccuracyCircle.APPLAUSE_DELAY).Schedule(() => applauseSound?.Play()); - } + using (BeginDelayedSequence(APPLAUSE_DELAY)) + Schedule(() => applauseSound?.Play()); } protected override void Update() From 6c4709e7b4546faf39e7df98b4b143e461ebef9d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 May 2021 18:34:02 +0900 Subject: [PATCH 277/839] Fix `PlacementBlueprint` using the wrong beatmap when applying defaults Closes #12855. --- osu.Game/Rulesets/Edit/PlacementBlueprint.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs index 4ad8c815fe..82e90399c9 100644 --- a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs +++ b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs @@ -36,7 +36,8 @@ namespace osu.Game.Rulesets.Edit [Resolved(canBeNull: true)] protected EditorClock EditorClock { get; private set; } - private readonly IBindable beatmap = new Bindable(); + [Resolved] + private EditorBeatmap beatmap { get; set; } private Bindable startTimeBindable; @@ -58,10 +59,8 @@ namespace osu.Game.Rulesets.Edit } [BackgroundDependencyLoader] - private void load(IBindable beatmap) + private void load() { - this.beatmap.BindTo(beatmap); - startTimeBindable = HitObject.StartTimeBindable.GetBoundCopy(); startTimeBindable.BindValueChanged(_ => ApplyDefaultsToHitObject(), true); } @@ -113,7 +112,7 @@ namespace osu.Game.Rulesets.Edit /// Invokes , /// refreshing and parameters for the . /// - protected void ApplyDefaultsToHitObject() => HitObject.ApplyDefaults(beatmap.Value.Beatmap.ControlPointInfo, beatmap.Value.Beatmap.BeatmapInfo.BaseDifficulty); + protected void ApplyDefaultsToHitObject() => HitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty); public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Parent?.ReceivePositionalInputAt(screenSpacePos) ?? false; From ec89a149dd752d242e1675a4462c8af4e360eb3e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 19 May 2021 13:36:10 +0300 Subject: [PATCH 278/839] Add failing test case --- .../TestSceneCatchPlayerLegacySkin.cs | 40 ++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs index 64695153b5..a0b6b6dbe1 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs @@ -1,7 +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 System.Linq; using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Skinning.Legacy; +using osu.Game.Skinning; using osu.Game.Tests.Visual; namespace osu.Game.Rulesets.Catch.Tests @@ -9,6 +15,38 @@ namespace osu.Game.Rulesets.Catch.Tests [TestFixture] public class TestSceneCatchPlayerLegacySkin : LegacySkinPlayerTestScene { - protected override Ruleset CreatePlayerRuleset() => new CatchRuleset(); + [Test] + public void TestUsingLegacySkin() + { + // check for the existence of a random legacy component to ensure using legacy skin. + // this should exist in LegacySkinPlayerTestScene but the weird transformer logic below needs to be "fixed" or otherwise first. + AddAssert("using legacy skin", () => this.ChildrenOfType().Any()); + } + + protected override Ruleset CreatePlayerRuleset() => new TestCatchRuleset(); + + private class TestCatchRuleset : CatchRuleset + { + public override ISkin CreateLegacySkinProvider(ISkinSource source, IBeatmap beatmap) => new TestCatchLegacySkinTransformer(source); + } + + private class TestCatchLegacySkinTransformer : CatchLegacySkinTransformer + { + public TestCatchLegacySkinTransformer(ISkinSource source) + : base(source) + { + } + + public override Drawable GetDrawableComponent(ISkinComponent component) + { + var drawable = base.GetDrawableComponent(component); + if (drawable != null) + return drawable; + + // it shouldn't really matter whether to return null or return this, + // but returning null skips over the beatmap skin, so this needs to exist to test things properly. + return Source.GetDrawableComponent(component); + } + } } } From 6a3c58b9adfc6184f23ae0eefa6c39c38bbfdc47 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 May 2021 19:58:55 +0900 Subject: [PATCH 279/839] Implement proper scaling algorithms --- .../Skinning/Editor/SkinSelectionHandler.cs | 98 +++++++++++++++++-- 1 file changed, 88 insertions(+), 10 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index 2eb4ea107d..18c9341db9 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -34,9 +34,94 @@ namespace osu.Game.Skinning.Editor { adjustScaleFromAnchor(ref scale, anchor); - foreach (var c in SelectedBlueprints) - // TODO: this is temporary and will be fixed with a separate refactor of selection transform logic. - ((Drawable)c.Item).Scale += scale * 0.02f; + if (SelectedBlueprints.Count > 1) + { + var selectionQuad = GetSurroundingQuad(SelectedBlueprints.SelectMany(b => + b.Item.ScreenSpaceDrawQuad.GetVertices().ToArray())); + + // the selection quad is always upright, so use a rect to make mutating the values easier. + var adjustedRect = selectionQuad.AABBFloat; + + // for now aspect lock scale adjustments that occur at corners. + if (!anchor.HasFlagFast(Anchor.x1) && !anchor.HasFlagFast(Anchor.y1)) + scale.Y = scale.X / selectionQuad.Width * selectionQuad.Height; + + if (anchor.HasFlagFast(Anchor.x0)) + { + adjustedRect.X -= scale.X; + adjustedRect.Width += scale.X; + } + else if (anchor.HasFlagFast(Anchor.x2)) + { + adjustedRect.Width += scale.X; + } + + if (anchor.HasFlagFast(Anchor.y0)) + { + adjustedRect.Y -= scale.Y; + adjustedRect.Height += scale.Y; + } + else if (anchor.HasFlagFast(Anchor.y2)) + { + adjustedRect.Height += scale.Y; + } + + // scale adjust should match that of the quad itself. + var scaledDelta = new Vector2( + adjustedRect.Width / selectionQuad.Width - 1, + adjustedRect.Height / selectionQuad.Height - 1 + ); + + foreach (var b in SelectedBlueprints) + { + var drawableItem = (Drawable)b.Item; + + if (SelectedBlueprints.Count > 1) + { + // each drawable's relative position should be maintained in the scaled quad. + var screenPosition = b.ScreenSpaceSelectionPoint; + + var relativePositionInOriginal = + new Vector2( + (screenPosition.X - selectionQuad.TopLeft.X) / selectionQuad.Width, + (screenPosition.Y - selectionQuad.TopLeft.Y) / selectionQuad.Height + ); + + var newPositionInAdjusted = new Vector2( + adjustedRect.TopLeft.X + adjustedRect.Width * relativePositionInOriginal.X, + adjustedRect.TopLeft.Y + adjustedRect.Height * relativePositionInOriginal.Y + ); + + drawableItem.Position = drawableItem.Parent.ToLocalSpace(newPositionInAdjusted) - drawableItem.AnchorPosition; + drawableItem.Scale += scaledDelta; + } + } + } + else + { + var blueprint = SelectedBlueprints.First(); + var drawableItem = (Drawable)blueprint.Item; + + // the number of local "pixels" the drag operation resulted in. + // our goal is to increase the drawable's draw size by this amount. + var scaledDelta = drawableItem.ScreenSpaceDeltaToParentSpace(scale); + + scaledDelta = new Vector2( + scaledDelta.X / drawableItem.DrawWidth, + scaledDelta.Y / drawableItem.DrawHeight + ); + + // handle the case where scaling with a centre origin needs double the adjustments to match + // user cursor movement. + if (drawableItem.Origin.HasFlagFast(Anchor.x1)) scaledDelta.X *= 2; + if (drawableItem.Origin.HasFlagFast(Anchor.y1)) scaledDelta.Y *= 2; + + // for now aspect lock scale adjustments that occur at corners. + if (!anchor.HasFlagFast(Anchor.x1) && !anchor.HasFlagFast(Anchor.y1)) + scaledDelta.Y = scaledDelta.X; + + drawableItem.Scale += scaledDelta; + } return true; } @@ -158,13 +243,6 @@ namespace osu.Game.Skinning.Editor // reverse the scale direction if dragging from top or left. if ((reference & Anchor.x0) > 0) scale.X = -scale.X; if ((reference & Anchor.y0) > 0) scale.Y = -scale.Y; - - // for now aspect lock scale adjustments that occur at corners. - if (!reference.HasFlagFast(Anchor.x1) && !reference.HasFlagFast(Anchor.y1)) - { - // TODO: temporary implementation - only dragging the corner handles across the X axis changes size. - scale.Y = scale.X; - } } public class AnchorMenuItem : TernaryStateMenuItem From 16ffedde8a8ac640e61f98c0949324c699a5b05f Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 19 May 2021 15:17:57 +0300 Subject: [PATCH 280/839] Add year parameter to GetNewsRequest --- osu.Game/Online/API/Requests/GetNewsRequest.cs | 8 +++++++- osu.Game/Overlays/News/Displays/FrontPageDisplay.cs | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetNewsRequest.cs b/osu.Game/Online/API/Requests/GetNewsRequest.cs index 36d9dc0652..3eb29f1292 100644 --- a/osu.Game/Online/API/Requests/GetNewsRequest.cs +++ b/osu.Game/Online/API/Requests/GetNewsRequest.cs @@ -8,10 +8,12 @@ namespace osu.Game.Online.API.Requests { public class GetNewsRequest : APIRequest { + private readonly int year; private readonly Cursor cursor; - public GetNewsRequest(Cursor cursor = null) + public GetNewsRequest(int year = 0, Cursor cursor = null) { + this.year = year; this.cursor = cursor; } @@ -19,6 +21,10 @@ namespace osu.Game.Online.API.Requests { var req = base.CreateWebRequest(); req.AddCursor(cursor); + + if (year != 0) + req.AddParameter("year", year.ToString()); + return req; } diff --git a/osu.Game/Overlays/News/Displays/FrontPageDisplay.cs b/osu.Game/Overlays/News/Displays/FrontPageDisplay.cs index a1bc6c650b..45e11a6f15 100644 --- a/osu.Game/Overlays/News/Displays/FrontPageDisplay.cs +++ b/osu.Game/Overlays/News/Displays/FrontPageDisplay.cs @@ -74,7 +74,7 @@ namespace osu.Game.Overlays.News.Displays { request?.Cancel(); - request = new GetNewsRequest(lastCursor); + request = new GetNewsRequest(cursor: lastCursor); request.Success += response => Schedule(() => onSuccess(response)); api.PerformAsync(request); } From 150ed01c627e49e00a75f8c7b3a8a68e0bfea424 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 19 May 2021 15:22:55 +0300 Subject: [PATCH 281/839] Make NewsSidebar scrollable --- osu.Game/Overlays/News/Sidebar/NewsSidebar.cs | 60 +++++++++++++------ 1 file changed, 43 insertions(+), 17 deletions(-) diff --git a/osu.Game/Overlays/News/Sidebar/NewsSidebar.cs b/osu.Game/Overlays/News/Sidebar/NewsSidebar.cs index d14ad90ef4..9e397e78c8 100644 --- a/osu.Game/Overlays/News/Sidebar/NewsSidebar.cs +++ b/osu.Game/Overlays/News/Sidebar/NewsSidebar.cs @@ -9,6 +9,7 @@ using osu.Game.Online.API.Requests.Responses; using osu.Framework.Graphics.Shapes; using osuTK; using System.Linq; +using osu.Game.Graphics.Containers; namespace osu.Game.Overlays.News.Sidebar { @@ -31,30 +32,55 @@ namespace osu.Game.Overlays.News.Sidebar RelativeSizeAxes = Axes.Both, Colour = colourProvider.Background4 }, + new Box + { + RelativeSizeAxes = Axes.Y, + Width = OsuScrollContainer.SCROLL_BAR_HEIGHT, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Colour = colourProvider.Background3, + Alpha = 0.5f + }, new Container { RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding + Padding = new MarginPadding { Right = -3 }, // Compensate for scrollbar margin + Child = new OsuScrollContainer { - Vertical = 20, - Left = 50, - Right = 30 - }, - Child = new FillFlowContainer - { - Direction = FillDirection.Vertical, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Spacing = new Vector2(0, 20), - Children = new Drawable[] + RelativeSizeAxes = Axes.Both, + Child = new Container { - new YearsPanel(), - monthsFlow = new FillFlowContainer + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Right = 3 }, // Addeded 3px back + Child = new Container { - AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 10) + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding + { + Vertical = 20, + Left = 50, + Right = 30 + }, + Child = new FillFlowContainer + { + Direction = FillDirection.Vertical, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(0, 20), + Children = new Drawable[] + { + new YearsPanel(), + monthsFlow = new FillFlowContainer + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 10) + } + } + } } } } From 6cc4ffadabda1eb13d8b8d96a7a7d311f9314721 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 19 May 2021 15:28:12 +0300 Subject: [PATCH 282/839] Implement sticky container for sidebar in NewsOverlay --- osu.Game/Overlays/NewsOverlay.cs | 46 ++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/osu.Game/Overlays/NewsOverlay.cs b/osu.Game/Overlays/NewsOverlay.cs index 5beb285216..ddd328c860 100644 --- a/osu.Game/Overlays/NewsOverlay.cs +++ b/osu.Game/Overlays/NewsOverlay.cs @@ -1,11 +1,14 @@ // 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.Threading; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Overlays.News; using osu.Game.Overlays.News.Displays; +using osu.Game.Overlays.News.Sidebar; namespace osu.Game.Overlays { @@ -13,9 +16,44 @@ namespace osu.Game.Overlays { private readonly Bindable article = new Bindable(null); + protected override Container Content => content; + + private readonly Container content; + private readonly Container sidebarContainer; + public NewsOverlay() : base(OverlayColourScheme.Purple, false) { + base.Content.Add(new GridContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize) + }, + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + new Dimension() + }, + Content = new[] + { + new Drawable[] + { + sidebarContainer = new Container + { + AutoSizeAxes = Axes.X, + Child = new NewsSidebar() + }, + content = new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y + } + } + } + }); } protected override void LoadComplete() @@ -90,6 +128,14 @@ namespace osu.Game.Overlays }, (cancellationToken = new CancellationTokenSource()).Token); } + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + sidebarContainer.Height = DrawHeight; + sidebarContainer.Y = Math.Clamp(ScrollFlow.Current - Header.DrawHeight, 0, Math.Max(ScrollFlow.ScrollContent.DrawHeight - DrawHeight - Header.DrawHeight, 0)); + } + protected override void Dispose(bool isDisposing) { cancellationToken?.Cancel(); From e3ed9b8135b93c3b14685de298f1a6e25ec44f6b Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 19 May 2021 15:36:05 +0300 Subject: [PATCH 283/839] Implement sidebar metadata handling in NewsOverlay --- .../News/Displays/FrontPageDisplay.cs | 20 ++++++++- osu.Game/Overlays/NewsOverlay.cs | 44 ++++++++++++++++--- 2 files changed, 58 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/News/Displays/FrontPageDisplay.cs b/osu.Game/Overlays/News/Displays/FrontPageDisplay.cs index 45e11a6f15..7691e4a901 100644 --- a/osu.Game/Overlays/News/Displays/FrontPageDisplay.cs +++ b/osu.Game/Overlays/News/Displays/FrontPageDisplay.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Linq; using System.Threading; using osu.Framework.Allocation; @@ -15,6 +16,8 @@ namespace osu.Game.Overlays.News.Displays { public class FrontPageDisplay : CompositeDrawable { + public Action ResponseReceived; + [Resolved] private IAPIProvider api { get; set; } @@ -24,6 +27,13 @@ namespace osu.Game.Overlays.News.Displays private GetNewsRequest request; private Cursor lastCursor; + private readonly int year; + + public FrontPageDisplay(int year = 0) + { + this.year = year; + } + [BackgroundDependencyLoader] private void load() { @@ -74,13 +84,15 @@ namespace osu.Game.Overlays.News.Displays { request?.Cancel(); - request = new GetNewsRequest(cursor: lastCursor); + request = new GetNewsRequest(year, lastCursor); request.Success += response => Schedule(() => onSuccess(response)); api.PerformAsync(request); } private CancellationTokenSource cancellationToken; + private bool initialLoad = true; + private void onSuccess(GetNewsResponse response) { cancellationToken?.Cancel(); @@ -101,6 +113,12 @@ namespace osu.Game.Overlays.News.Displays content.Add(loaded); showMore.IsLoading = false; showMore.Alpha = lastCursor == null ? 0 : 1; + + if (initialLoad) + { + ResponseReceived?.Invoke(response); + initialLoad = false; + } }, (cancellationToken = new CancellationTokenSource()).Token); } diff --git a/osu.Game/Overlays/NewsOverlay.cs b/osu.Game/Overlays/NewsOverlay.cs index ddd328c860..d3f407fc0f 100644 --- a/osu.Game/Overlays/NewsOverlay.cs +++ b/osu.Game/Overlays/NewsOverlay.cs @@ -20,6 +20,7 @@ namespace osu.Game.Overlays private readonly Container content; private readonly Container sidebarContainer; + private readonly NewsSidebar sidebar; public NewsOverlay() : base(OverlayColourScheme.Purple, false) @@ -44,7 +45,7 @@ namespace osu.Game.Overlays sidebarContainer = new Container { AutoSizeAxes = Axes.X, - Child = new NewsSidebar() + Child = sidebar = new NewsSidebar() }, content = new Container { @@ -94,6 +95,12 @@ namespace osu.Game.Overlays Show(); } + public void ShowYear(int year) + { + showYear(year); + Show(); + } + public void ShowArticle(string slug) { article.Value = slug; @@ -102,6 +109,14 @@ namespace osu.Game.Overlays private CancellationTokenSource cancellationToken; + private void showYear(int year) + { + cancellationToken?.Cancel(); + Loading.Show(); + + loadFrontPage(year); + } + private void onArticleChanged(ValueChangedEvent e) { cancellationToken?.Cancel(); @@ -109,13 +124,33 @@ namespace osu.Game.Overlays if (e.NewValue == null) { - Header.SetFrontPage(); - LoadDisplay(new FrontPageDisplay()); + loadFrontPage(); return; } - Header.SetArticle(e.NewValue); + loadArticle(e.NewValue); + } + + private void loadFrontPage(int year = 0) + { + Header.SetFrontPage(); + + var page = new FrontPageDisplay(year); + page.ResponseReceived += r => + { + sidebar.Metadata.Value = r.SidebarMetadata; + Loading.Hide(); + }; + LoadDisplay(page); + } + + private void loadArticle(string article) + { + Header.SetArticle(article); + + // Temporary, should be handled by ArticleDisplay later LoadDisplay(Empty()); + Loading.Hide(); } protected void LoadDisplay(Drawable display) @@ -124,7 +159,6 @@ namespace osu.Game.Overlays LoadComponentAsync(display, loaded => { Child = loaded; - Loading.Hide(); }, (cancellationToken = new CancellationTokenSource()).Token); } From d60478851f8bf9a63aa158b29b9a3a3d784451c8 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 19 May 2021 15:38:53 +0300 Subject: [PATCH 284/839] Add proper action to YearButton --- osu.Game/Overlays/News/Sidebar/YearsPanel.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/News/Sidebar/YearsPanel.cs b/osu.Game/Overlays/News/Sidebar/YearsPanel.cs index b6bbdbb6d4..232b995cd6 100644 --- a/osu.Game/Overlays/News/Sidebar/YearsPanel.cs +++ b/osu.Game/Overlays/News/Sidebar/YearsPanel.cs @@ -81,6 +81,9 @@ namespace osu.Game.Overlays.News.Sidebar { public int Year { get; } + [Resolved(canBeNull: true)] + private NewsOverlay overlay { get; set; } + private readonly bool isCurrent; public YearButton(int year, bool isCurrent) @@ -106,7 +109,7 @@ namespace osu.Game.Overlays.News.Sidebar { IdleColour = isCurrent ? Color4.White : colourProvider.Light2; HoverColour = isCurrent ? Color4.White : colourProvider.Light1; - Action = () => { }; // Avoid button being disabled since there's no proper action assigned. + Action = () => overlay?.ShowYear(Year); } } } From 14af86d6c55ee8ccae7ae0b531b537eb0d9cbbb2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 May 2021 21:46:41 +0900 Subject: [PATCH 285/839] Use the same code path for all scaling --- .../Skinning/Editor/SkinSelectionHandler.cs | 127 +++++++----------- 1 file changed, 48 insertions(+), 79 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index 18c9341db9..af013bfd52 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -34,92 +34,61 @@ namespace osu.Game.Skinning.Editor { adjustScaleFromAnchor(ref scale, anchor); - if (SelectedBlueprints.Count > 1) + var selectionQuad = GetSurroundingQuad(SelectedBlueprints.SelectMany(b => + b.Item.ScreenSpaceDrawQuad.GetVertices().ToArray())); + + // the selection quad is always upright, so use a rect to make mutating the values easier. + var adjustedRect = selectionQuad.AABBFloat; + + // for now aspect lock scale adjustments that occur at corners. + if (!anchor.HasFlagFast(Anchor.x1) && !anchor.HasFlagFast(Anchor.y1)) + scale.Y = scale.X / selectionQuad.Width * selectionQuad.Height; + + if (anchor.HasFlagFast(Anchor.x0)) { - var selectionQuad = GetSurroundingQuad(SelectedBlueprints.SelectMany(b => - b.Item.ScreenSpaceDrawQuad.GetVertices().ToArray())); - - // the selection quad is always upright, so use a rect to make mutating the values easier. - var adjustedRect = selectionQuad.AABBFloat; - - // for now aspect lock scale adjustments that occur at corners. - if (!anchor.HasFlagFast(Anchor.x1) && !anchor.HasFlagFast(Anchor.y1)) - scale.Y = scale.X / selectionQuad.Width * selectionQuad.Height; - - if (anchor.HasFlagFast(Anchor.x0)) - { - adjustedRect.X -= scale.X; - adjustedRect.Width += scale.X; - } - else if (anchor.HasFlagFast(Anchor.x2)) - { - adjustedRect.Width += scale.X; - } - - if (anchor.HasFlagFast(Anchor.y0)) - { - adjustedRect.Y -= scale.Y; - adjustedRect.Height += scale.Y; - } - else if (anchor.HasFlagFast(Anchor.y2)) - { - adjustedRect.Height += scale.Y; - } - - // scale adjust should match that of the quad itself. - var scaledDelta = new Vector2( - adjustedRect.Width / selectionQuad.Width - 1, - adjustedRect.Height / selectionQuad.Height - 1 - ); - - foreach (var b in SelectedBlueprints) - { - var drawableItem = (Drawable)b.Item; - - if (SelectedBlueprints.Count > 1) - { - // each drawable's relative position should be maintained in the scaled quad. - var screenPosition = b.ScreenSpaceSelectionPoint; - - var relativePositionInOriginal = - new Vector2( - (screenPosition.X - selectionQuad.TopLeft.X) / selectionQuad.Width, - (screenPosition.Y - selectionQuad.TopLeft.Y) / selectionQuad.Height - ); - - var newPositionInAdjusted = new Vector2( - adjustedRect.TopLeft.X + adjustedRect.Width * relativePositionInOriginal.X, - adjustedRect.TopLeft.Y + adjustedRect.Height * relativePositionInOriginal.Y - ); - - drawableItem.Position = drawableItem.Parent.ToLocalSpace(newPositionInAdjusted) - drawableItem.AnchorPosition; - drawableItem.Scale += scaledDelta; - } - } + adjustedRect.X -= scale.X; + adjustedRect.Width += scale.X; } - else + else if (anchor.HasFlagFast(Anchor.x2)) { - var blueprint = SelectedBlueprints.First(); - var drawableItem = (Drawable)blueprint.Item; + adjustedRect.Width += scale.X; + } - // the number of local "pixels" the drag operation resulted in. - // our goal is to increase the drawable's draw size by this amount. - var scaledDelta = drawableItem.ScreenSpaceDeltaToParentSpace(scale); + if (anchor.HasFlagFast(Anchor.y0)) + { + adjustedRect.Y -= scale.Y; + adjustedRect.Height += scale.Y; + } + else if (anchor.HasFlagFast(Anchor.y2)) + { + adjustedRect.Height += scale.Y; + } - scaledDelta = new Vector2( - scaledDelta.X / drawableItem.DrawWidth, - scaledDelta.Y / drawableItem.DrawHeight + // scale adjust should match that of the quad itself. + var scaledDelta = new Vector2( + adjustedRect.Width / selectionQuad.Width - 1, + adjustedRect.Height / selectionQuad.Height - 1 + ); + + foreach (var b in SelectedBlueprints) + { + var drawableItem = (Drawable)b.Item; + + // each drawable's relative position should be maintained in the scaled quad. + var screenPosition = b.ScreenSpaceSelectionPoint; + + var relativePositionInOriginal = + new Vector2( + (screenPosition.X - selectionQuad.TopLeft.X) / selectionQuad.Width, + (screenPosition.Y - selectionQuad.TopLeft.Y) / selectionQuad.Height + ); + + var newPositionInAdjusted = new Vector2( + adjustedRect.TopLeft.X + adjustedRect.Width * relativePositionInOriginal.X, + adjustedRect.TopLeft.Y + adjustedRect.Height * relativePositionInOriginal.Y ); - // handle the case where scaling with a centre origin needs double the adjustments to match - // user cursor movement. - if (drawableItem.Origin.HasFlagFast(Anchor.x1)) scaledDelta.X *= 2; - if (drawableItem.Origin.HasFlagFast(Anchor.y1)) scaledDelta.Y *= 2; - - // for now aspect lock scale adjustments that occur at corners. - if (!anchor.HasFlagFast(Anchor.x1) && !anchor.HasFlagFast(Anchor.y1)) - scaledDelta.Y = scaledDelta.X; - + drawableItem.Position = drawableItem.Parent.ToLocalSpace(newPositionInAdjusted) - drawableItem.AnchorPosition; drawableItem.Scale += scaledDelta; } From a55879e5115895c940411bc9d7000532e32d5219 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 May 2021 01:47:31 +0900 Subject: [PATCH 286/839] Fix oversights in scale algorithm --- osu.Game/Skinning/Editor/SkinSelectionHandler.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index af013bfd52..2f9611ba65 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -32,6 +32,9 @@ namespace osu.Game.Skinning.Editor public override bool HandleScale(Vector2 scale, Anchor anchor) { + // convert scale to screen space + scale = ToScreenSpace(scale) - ToScreenSpace(Vector2.Zero); + adjustScaleFromAnchor(ref scale, anchor); var selectionQuad = GetSurroundingQuad(SelectedBlueprints.SelectMany(b => @@ -66,8 +69,8 @@ namespace osu.Game.Skinning.Editor // scale adjust should match that of the quad itself. var scaledDelta = new Vector2( - adjustedRect.Width / selectionQuad.Width - 1, - adjustedRect.Height / selectionQuad.Height - 1 + adjustedRect.Width / selectionQuad.Width, + adjustedRect.Height / selectionQuad.Height ); foreach (var b in SelectedBlueprints) @@ -89,7 +92,7 @@ namespace osu.Game.Skinning.Editor ); drawableItem.Position = drawableItem.Parent.ToLocalSpace(newPositionInAdjusted) - drawableItem.AnchorPosition; - drawableItem.Scale += scaledDelta; + drawableItem.Scale *= scaledDelta; } return true; From 00ed6993400e35f6ba5c7416219932e90e63bd08 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 May 2021 01:53:24 +0900 Subject: [PATCH 287/839] Fix origin specifications using incorrect flags --- osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs index 32d7f3525f..5d0263772d 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs @@ -85,7 +85,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters colourBarsEarly = new Container { Anchor = Anchor.CentreLeft, - Origin = Anchor.x2, + Origin = Anchor.TopRight, RelativeSizeAxes = Axes.Both, Height = 0.5f, Scale = new Vector2(1, -1), @@ -93,7 +93,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters colourBarsLate = new Container { Anchor = Anchor.CentreLeft, - Origin = Anchor.x2, + Origin = Anchor.TopRight, RelativeSizeAxes = Axes.Both, Height = 0.5f, }, From 22337e0fc79c6e45783eb5af137db8df93378244 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 May 2021 01:59:30 +0900 Subject: [PATCH 288/839] Add comment explaining why origin is flipped --- osu.Game/Skinning/DefaultSkin.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index 84f40df0cf..ba31816a07 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -93,8 +93,9 @@ namespace osu.Game.Skinning if (hitError2 != null) { hitError2.Anchor = Anchor.CentreRight; - hitError2.Origin = Anchor.CentreLeft; hitError2.Scale = new Vector2(-1, 1); + // origin flipped to match scale above. + hitError2.Origin = Anchor.CentreLeft; } } }) From eb5db8ff0357a0814491437a59d8b427d84bf2fd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 May 2021 02:01:25 +0900 Subject: [PATCH 289/839] Disable border display on skin editor to avoid crashes This wasn't being displayed correctly anyway, so rather than fixing let's just remove it for now. Closes #12868. --- osu.Game/Skinning/Editor/SkinEditorOverlay.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinEditorOverlay.cs b/osu.Game/Skinning/Editor/SkinEditorOverlay.cs index 2d7cae71ff..88020896bb 100644 --- a/osu.Game/Skinning/Editor/SkinEditorOverlay.cs +++ b/osu.Game/Skinning/Editor/SkinEditorOverlay.cs @@ -65,8 +65,6 @@ namespace osu.Game.Skinning.Editor if (visibility.NewValue == Visibility.Visible) { target.Masking = true; - target.BorderThickness = 5; - target.BorderColour = colours.Yellow; target.AllowScaling = false; target.RelativePositionAxes = Axes.Both; @@ -75,7 +73,6 @@ namespace osu.Game.Skinning.Editor } else { - target.BorderThickness = 0; target.AllowScaling = true; target.ScaleTo(1, SkinEditor.TRANSITION_DURATION, Easing.OutQuint).OnComplete(_ => target.Masking = false); From bc1cad0963c4624ec3cd09acd3124ebc349c8e7a Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 20 May 2021 00:28:26 +0700 Subject: [PATCH 290/839] change header breadcrumb when page change --- osu.Game/Overlays/Wiki/WikiHeader.cs | 31 ++++++++++++++++++++++++++++ osu.Game/Overlays/WikiOverlay.cs | 1 + 2 files changed, 32 insertions(+) diff --git a/osu.Game/Overlays/Wiki/WikiHeader.cs b/osu.Game/Overlays/Wiki/WikiHeader.cs index 91377c63da..f5010d82b6 100644 --- a/osu.Game/Overlays/Wiki/WikiHeader.cs +++ b/osu.Game/Overlays/Wiki/WikiHeader.cs @@ -1,17 +1,48 @@ // 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.Graphics; +using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Overlays.Wiki { public class WikiHeader : BreadcrumbControlOverlayHeader { private const string index_page_string = "index"; + private const string index_path = "Main_Page"; + + public Bindable WikiPageData = new Bindable(); public WikiHeader() { TabControl.AddItem(index_page_string); + Current.Value = index_page_string; + + WikiPageData.BindValueChanged(onWikiPageChange); + } + + private void onWikiPageChange(ValueChangedEvent e) + { + if (e.NewValue == null) + return; + + TabControl.Clear(); + Current.Value = null; + + TabControl.AddItem(index_page_string); + + if (e.NewValue.Path == index_path) + { + Current.Value = index_page_string; + return; + } + + if (e.NewValue.Subtitle != null) + TabControl.AddItem(e.NewValue.Subtitle); + + TabControl.AddItem(e.NewValue.Title); + Current.Value = e.NewValue.Title; } protected override Drawable CreateBackground() => new OverlayHeaderBackground(@"Headers/wiki"); diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs index 1022c56f26..366923a4f1 100644 --- a/osu.Game/Overlays/WikiOverlay.cs +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -74,6 +74,7 @@ namespace osu.Game.Overlays { base.LoadComplete(); path.BindValueChanged(onPathChanged); + wikiData.BindTo(Header.WikiPageData); } protected override void PopIn() From 434377aa52300a5af7698733722f462196434840 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 19 May 2021 21:35:06 +0300 Subject: [PATCH 291/839] Revert "Add failing test case" This reverts commit ec89a149dd752d242e1675a4462c8af4e360eb3e. --- .../TestSceneCatchPlayerLegacySkin.cs | 40 +------------------ 1 file changed, 1 insertion(+), 39 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs index a0b6b6dbe1..64695153b5 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs @@ -1,13 +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 NUnit.Framework; -using osu.Framework.Graphics; -using osu.Framework.Testing; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Catch.Skinning.Legacy; -using osu.Game.Skinning; using osu.Game.Tests.Visual; namespace osu.Game.Rulesets.Catch.Tests @@ -15,38 +9,6 @@ namespace osu.Game.Rulesets.Catch.Tests [TestFixture] public class TestSceneCatchPlayerLegacySkin : LegacySkinPlayerTestScene { - [Test] - public void TestUsingLegacySkin() - { - // check for the existence of a random legacy component to ensure using legacy skin. - // this should exist in LegacySkinPlayerTestScene but the weird transformer logic below needs to be "fixed" or otherwise first. - AddAssert("using legacy skin", () => this.ChildrenOfType().Any()); - } - - protected override Ruleset CreatePlayerRuleset() => new TestCatchRuleset(); - - private class TestCatchRuleset : CatchRuleset - { - public override ISkin CreateLegacySkinProvider(ISkinSource source, IBeatmap beatmap) => new TestCatchLegacySkinTransformer(source); - } - - private class TestCatchLegacySkinTransformer : CatchLegacySkinTransformer - { - public TestCatchLegacySkinTransformer(ISkinSource source) - : base(source) - { - } - - public override Drawable GetDrawableComponent(ISkinComponent component) - { - var drawable = base.GetDrawableComponent(component); - if (drawable != null) - return drawable; - - // it shouldn't really matter whether to return null or return this, - // but returning null skips over the beatmap skin, so this needs to exist to test things properly. - return Source.GetDrawableComponent(component); - } - } + protected override Ruleset CreatePlayerRuleset() => new CatchRuleset(); } } From e9cab29134eec87685d6a343babcfb361b530b29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 19 May 2021 20:48:06 +0200 Subject: [PATCH 292/839] Cache editor beatmap in placement blueprint test scene --- osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs b/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs index 78a6bcc3db..2dc77fa72a 100644 --- a/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs +++ b/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs @@ -33,8 +33,12 @@ namespace osu.Game.Tests.Visual protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); + dependencies.CacheAs(new EditorClock()); + var playable = Beatmap.Value.GetPlayableBeatmap(Beatmap.Value.BeatmapInfo.Ruleset); + dependencies.CacheAs(new EditorBeatmap(playable)); + return dependencies; } From 6fc06a10a15037bb4890fc2d35ffc46d74bf8df5 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 19 May 2021 21:52:29 +0300 Subject: [PATCH 293/839] Add extensible test scene for beatmap skins fallback instead --- .../Gameplay/TestSceneBeatmapSkinFallbacks.cs | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs new file mode 100644 index 0000000000..760fc5a139 --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs @@ -0,0 +1,129 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Graphics; +using osu.Framework.Lists; +using osu.Framework.Testing; +using osu.Framework.Timing; +using osu.Framework.Utils; +using osu.Game.Beatmaps; +using osu.Game.Extensions; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Skinning.Legacy; +using osu.Game.Rulesets.Scoring; +using osu.Game.Screens.Play.HUD; +using osu.Game.Skinning; +using osu.Game.Storyboards; +using osu.Game.Tests.Beatmaps; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public class TestSceneBeatmapSkinFallbacks : OsuPlayerTestScene + { + private ISkin currentBeatmapSkin; + + [Resolved] + private SkinManager skinManager { get; set; } + + [Cached] + private ScoreProcessor scoreProcessor = new ScoreProcessor(); + + [Cached(typeof(HealthProcessor))] + private HealthProcessor healthProcessor = new DrainingHealthProcessor(0); + + protected override bool HasCustomSteps => true; + + [Test] + public void TestEmptyDefaultBeatmapSkinFallsBack() + { + CreateSkinTest(DefaultLegacySkin.Info, () => new TestWorkingBeatmap(CreateBeatmap(new OsuRuleset().RulesetInfo)).Skin); + AssertComponentsFromExpectedSource(SkinnableTarget.MainHUDComponents, () => skinManager.CurrentSkin.Value); + } + + protected void CreateSkinTest(SkinInfo gameCurrentSkin, Func beatmapSkin) + { + CreateTest(() => + { + AddStep("setup skins", () => + { + skinManager.CurrentSkinInfo.Value = gameCurrentSkin; + currentBeatmapSkin = beatmapSkin(); + }); + }); + } + + protected void AssertComponentsFromExpectedSource(SkinnableTarget target, Func expectedSource) + { + AddAssert($"{target} from {expectedSource.GetType().Name}", () => + { + var expectedComponentsContainer = (SkinnableTargetComponentsContainer)expectedSource().GetDrawableComponent(new SkinnableTargetComponent(target)); + + Add(expectedComponentsContainer); + expectedComponentsContainer?.UpdateSubTree(); + var expectedInfo = expectedComponentsContainer?.CreateSkinnableInfo(); + Remove(expectedComponentsContainer); + + var actualInfo = Player.ChildrenOfType().First(s => s.Target == target) + .ChildrenOfType().Single().CreateSkinnableInfo(); + + return almostEqual(actualInfo, expectedInfo, 2f); + }); + + static bool almostEqual(SkinnableInfo info, SkinnableInfo other, float positionTolerance) => + other != null + && info.Anchor == other.Anchor + && info.Origin == other.Origin + && Precision.AlmostEquals(info.Position, other.Position, positionTolerance) + && info.Children.SequenceEqual(other.Children, new FuncEqualityComparer((s1, s2) => almostEqual(s1, s2, positionTolerance))); + } + + protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) + => new CustomSkinWorkingBeatmap(beatmap, storyboard, Clock, Audio, currentBeatmapSkin); + + protected override Ruleset CreatePlayerRuleset() => new TestOsuRuleset(); + + private class CustomSkinWorkingBeatmap : ClockBackedTestWorkingBeatmap + { + private readonly ISkin beatmapSkin; + + public CustomSkinWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard, IFrameBasedClock referenceClock, AudioManager audio, ISkin beatmapSkin) + : base(beatmap, storyboard, referenceClock, audio) + { + this.beatmapSkin = beatmapSkin; + } + + protected override ISkin GetSkin() => beatmapSkin; + } + + private class TestOsuRuleset : OsuRuleset + { + public override ISkin CreateLegacySkinProvider(ISkinSource source, IBeatmap beatmap) => new TestOsuLegacySkinTransformer(source); + + private class TestOsuLegacySkinTransformer : OsuLegacySkinTransformer + { + public TestOsuLegacySkinTransformer(ISkinSource source) + : base(source) + { + } + + public override Drawable GetDrawableComponent(ISkinComponent component) + { + var drawable = base.GetDrawableComponent(component); + if (drawable != null) + return drawable; + + // this isn't really supposed to make a difference from returning null, + // but it appears it does, returning null will skip over falling back to beatmap skin, + // while calling Source.GetDrawableComponent() doesn't. + return Source.GetDrawableComponent(component); + } + } + } + } +} From 71da9600009ce891be550e07be5cafda3b9ec065 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 19 May 2021 22:53:21 +0300 Subject: [PATCH 294/839] Extract assert step addition out of assertion method --- .../Gameplay/TestSceneBeatmapSkinFallbacks.cs | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs index 760fc5a139..1d0caa86e2 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs @@ -43,37 +43,34 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestEmptyDefaultBeatmapSkinFallsBack() { CreateSkinTest(DefaultLegacySkin.Info, () => new TestWorkingBeatmap(CreateBeatmap(new OsuRuleset().RulesetInfo)).Skin); - AssertComponentsFromExpectedSource(SkinnableTarget.MainHUDComponents, () => skinManager.CurrentSkin.Value); + AddAssert("hud from default legacy skin", () => AssertComponentsFromExpectedSource(SkinnableTarget.MainHUDComponents, skinManager.CurrentSkin.Value)); } - protected void CreateSkinTest(SkinInfo gameCurrentSkin, Func beatmapSkin) + protected void CreateSkinTest(SkinInfo gameCurrentSkin, Func getBeatmapSkin) { CreateTest(() => { AddStep("setup skins", () => { skinManager.CurrentSkinInfo.Value = gameCurrentSkin; - currentBeatmapSkin = beatmapSkin(); + currentBeatmapSkin = getBeatmapSkin(); }); }); } - protected void AssertComponentsFromExpectedSource(SkinnableTarget target, Func expectedSource) + protected bool AssertComponentsFromExpectedSource(SkinnableTarget target, ISkin expectedSource) { - AddAssert($"{target} from {expectedSource.GetType().Name}", () => - { - var expectedComponentsContainer = (SkinnableTargetComponentsContainer)expectedSource().GetDrawableComponent(new SkinnableTargetComponent(target)); + var expectedComponentsContainer = (SkinnableTargetComponentsContainer)expectedSource.GetDrawableComponent(new SkinnableTargetComponent(target)); - Add(expectedComponentsContainer); - expectedComponentsContainer?.UpdateSubTree(); - var expectedInfo = expectedComponentsContainer?.CreateSkinnableInfo(); - Remove(expectedComponentsContainer); + Add(expectedComponentsContainer); + expectedComponentsContainer?.UpdateSubTree(); + var expectedInfo = expectedComponentsContainer?.CreateSkinnableInfo(); + Remove(expectedComponentsContainer); - var actualInfo = Player.ChildrenOfType().First(s => s.Target == target) - .ChildrenOfType().Single().CreateSkinnableInfo(); + var actualInfo = Player.ChildrenOfType().First(s => s.Target == target) + .ChildrenOfType().Single().CreateSkinnableInfo(); - return almostEqual(actualInfo, expectedInfo, 2f); - }); + return almostEqual(actualInfo, expectedInfo, 2f); static bool almostEqual(SkinnableInfo info, SkinnableInfo other, float positionTolerance) => other != null From 9f3ea150f58247f6de0c7ed14f04282e1ae72c4b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 19 May 2021 23:01:27 +0300 Subject: [PATCH 295/839] Fix legacy beatmap skins not falling back properly on HUD components --- osu.Game/Skinning/LegacyBeatmapSkin.cs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/osu.Game/Skinning/LegacyBeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs index 5ee436e8bb..9ff2238e4e 100644 --- a/osu.Game/Skinning/LegacyBeatmapSkin.cs +++ b/osu.Game/Skinning/LegacyBeatmapSkin.cs @@ -3,6 +3,7 @@ using osu.Framework.Audio.Sample; using osu.Framework.Bindables; +using osu.Framework.Graphics; using osu.Framework.IO.Stores; using osu.Game.Audio; using osu.Game.Beatmaps; @@ -23,6 +24,25 @@ namespace osu.Game.Skinning Configuration.AllowDefaultComboColoursFallback = false; } + public override Drawable GetDrawableComponent(ISkinComponent component) + { + if (component is SkinnableTargetComponent targetComponent) + { + switch (targetComponent.Target) + { + case SkinnableTarget.MainHUDComponents: + // this should exist in LegacySkin instead, but there isn't a fallback skin for LegacySkins yet. + // therefore keep the check here until fallback default legacy skin is supported. + if (!this.HasFont(LegacyFont.Score)) + return null; + + break; + } + } + + return base.GetDrawableComponent(component); + } + public override IBindable GetConfig(TLookup lookup) { switch (lookup) From 97c84998844240a63a3ec7b32f766d03d657a1f1 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 19 May 2021 23:01:41 +0300 Subject: [PATCH 296/839] Add test coverage --- .../Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs index 1d0caa86e2..48b5e67814 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs @@ -46,6 +46,13 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("hud from default legacy skin", () => AssertComponentsFromExpectedSource(SkinnableTarget.MainHUDComponents, skinManager.CurrentSkin.Value)); } + [Test] + public void TestEmptyLegacyBeatmapSkinFallsBack() + { + CreateSkinTest(SkinInfo.Default, () => new LegacyBeatmapSkin(new BeatmapInfo(), null, null)); + AddAssert("hud from default skin", () => AssertComponentsFromExpectedSource(SkinnableTarget.MainHUDComponents, skinManager.CurrentSkin.Value)); + } + protected void CreateSkinTest(SkinInfo gameCurrentSkin, Func getBeatmapSkin) { CreateTest(() => From a730349629c5b3f39a0107980a39bbb5161125cf Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 19 May 2021 23:18:54 +0300 Subject: [PATCH 297/839] Remove the ignore attribute once again --- osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs index 4e56e4b4a7..79394c1f19 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs @@ -20,7 +20,6 @@ namespace osu.Game.Rulesets.Catch.Tests protected override Ruleset CreatePlayerRuleset() => new CatchRuleset(); [Test] - [Ignore("HUD components broken, remove when fixed.")] public void TestLegacyHUDComboCounterHidden([Values] bool withModifiedSkin) { if (withModifiedSkin) From e20c25b0d95b782126d1d49d5c47ce89ecc1a7f3 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 19 May 2021 23:32:16 +0300 Subject: [PATCH 298/839] Fix miswritten test assertion --- osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs index 79394c1f19..b7cd6737b1 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Catch.Tests AddAssert("legacy HUD combo counter hidden", () => { - return Player.ChildrenOfType().All(c => !c.ChildrenOfType().Single().IsPresent); + return Player.ChildrenOfType().All(c => c.ChildrenOfType().Single().Alpha == 0f); }); } } From a4d52a7f529c1693aace27ed772d2e9805f236a6 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 19 May 2021 23:37:22 +0300 Subject: [PATCH 299/839] =?UTF-8?q?Use=20switch=E2=80=94case=20instead?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Legacy/CatchLegacySkinTransformer.cs | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs index d757f36cde..46a9cc6f70 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs @@ -24,19 +24,25 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy public override Drawable GetDrawableComponent(ISkinComponent component) { - if (component is SkinnableTargetComponent targetComponent && targetComponent.Target == SkinnableTarget.MainHUDComponents) + if (component is SkinnableTargetComponent targetComponent) { - if (!providesComboCounter) - return null; - - if (Source.GetDrawableComponent(component) is SkinnableTargetComponentsContainer components) + switch (targetComponent.Target) { - // catch may provide its own combo counter; hide the default. - // todo: this should be done in an elegant way per ruleset, defining which HUD skin components should be displayed. - foreach (var legacyComboCounter in components.OfType()) - legacyComboCounter.ContentVisible = false; + case SkinnableTarget.MainHUDComponents: + if (!providesComboCounter) + break; - return components; + if (Source.GetDrawableComponent(component) is SkinnableTargetComponentsContainer components) + { + // catch may provide its own combo counter; hide the default. + // todo: this should be done in an elegant way per ruleset, defining which HUD skin components should be displayed. + foreach (var legacyComboCounter in components.OfType()) + legacyComboCounter.ContentVisible = false; + + return components; + } + + break; } return null; From d8efcc0793313d0e09d5f566467805021cb645eb Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 19 May 2021 23:44:53 +0300 Subject: [PATCH 300/839] Remove drive-by change --- osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs b/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs index 907a37f4b9..c186525757 100644 --- a/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs +++ b/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs @@ -12,8 +12,6 @@ namespace osu.Game.Tests.Visual [TestFixture] public abstract class LegacySkinPlayerTestScene : PlayerTestScene { - protected LegacySkin LegacySkin { get; private set; } - private ISkinSource legacySkinSource; protected override TestPlayer CreatePlayer(Ruleset ruleset) => new SkinProvidingPlayer(legacySkinSource); @@ -21,8 +19,8 @@ namespace osu.Game.Tests.Visual [BackgroundDependencyLoader] private void load(OsuGameBase game, SkinManager skins) { - LegacySkin = new DefaultLegacySkin(new NamespacedResourceStore(game.Resources, "Skins/Legacy"), skins); - legacySkinSource = new SkinProvidingContainer(LegacySkin); + var legacySkin = new DefaultLegacySkin(new NamespacedResourceStore(game.Resources, "Skins/Legacy"), skins); + legacySkinSource = new SkinProvidingContainer(legacySkin); } public class SkinProvidingPlayer : TestPlayer From 85a3027f1ba4107bee90ee3d2c7492d93b1546f4 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Wed, 19 May 2021 13:58:41 -0700 Subject: [PATCH 301/839] Add failing test --- .../Visual/Online/TestSceneAccountCreationOverlay.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs index 3d65e7e4ba..b120a9b4db 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs @@ -1,12 +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.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; using osu.Game.Overlays; +using osu.Game.Overlays.AccountCreation; +using osu.Game.Overlays.Settings; using osu.Game.Users; namespace osu.Game.Tests.Visual.Online @@ -51,6 +55,9 @@ namespace osu.Game.Tests.Visual.Online AddStep("show manually", () => accountCreation.Show()); AddUntilStep("overlay is visible", () => accountCreation.State.Value == Visibility.Visible); + AddStep("click button", () => accountCreation.ChildrenOfType().Single().Click()); + AddUntilStep("warning screen is present", () => accountCreation.ChildrenOfType().Single().IsPresent); + AddStep("log back in", () => API.Login("dummy", "password")); AddUntilStep("overlay is hidden", () => accountCreation.State.Value == Visibility.Hidden); } From 3d99b89633b622b93a59423f72660d2af57eec01 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 20 May 2021 00:03:10 +0300 Subject: [PATCH 302/839] Add back actually needed change *no comment* --- osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs b/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs index c186525757..907a37f4b9 100644 --- a/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs +++ b/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs @@ -12,6 +12,8 @@ namespace osu.Game.Tests.Visual [TestFixture] public abstract class LegacySkinPlayerTestScene : PlayerTestScene { + protected LegacySkin LegacySkin { get; private set; } + private ISkinSource legacySkinSource; protected override TestPlayer CreatePlayer(Ruleset ruleset) => new SkinProvidingPlayer(legacySkinSource); @@ -19,8 +21,8 @@ namespace osu.Game.Tests.Visual [BackgroundDependencyLoader] private void load(OsuGameBase game, SkinManager skins) { - var legacySkin = new DefaultLegacySkin(new NamespacedResourceStore(game.Resources, "Skins/Legacy"), skins); - legacySkinSource = new SkinProvidingContainer(legacySkin); + LegacySkin = new DefaultLegacySkin(new NamespacedResourceStore(game.Resources, "Skins/Legacy"), skins); + legacySkinSource = new SkinProvidingContainer(LegacySkin); } public class SkinProvidingPlayer : TestPlayer From 3da2cdfd05314d975cf3a979709646729b4bca13 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Wed, 19 May 2021 14:06:21 -0700 Subject: [PATCH 303/839] Fix nullref in test --- osu.Game/Overlays/AccountCreation/ScreenWarning.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/AccountCreation/ScreenWarning.cs b/osu.Game/Overlays/AccountCreation/ScreenWarning.cs index 2605a0a6d6..6efbf8896f 100644 --- a/osu.Game/Overlays/AccountCreation/ScreenWarning.cs +++ b/osu.Game/Overlays/AccountCreation/ScreenWarning.cs @@ -33,7 +33,7 @@ namespace osu.Game.Overlays.AccountCreation public override void OnEntering(IScreen last) { - if (string.IsNullOrEmpty(api?.ProvidedUsername) || game.UseDevelopmentServer) + if (string.IsNullOrEmpty(api?.ProvidedUsername) || game?.UseDevelopmentServer == true) { this.FadeOut(); this.Push(new ScreenEntry()); From f1fd40dcca30d348cbdad21ded8f6cef557a4fb8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 May 2021 13:19:00 +0900 Subject: [PATCH 304/839] Fix test not working for various reasons --- .../Visual/Online/TestSceneAccountCreationOverlay.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs index b120a9b4db..437c5b07c9 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs @@ -40,8 +40,6 @@ namespace osu.Game.Tests.Visual.Online [BackgroundDependencyLoader] private void load() { - API.Logout(); - localUser = API.LocalUser.GetBoundCopy(); localUser.BindValueChanged(user => { userPanelArea.Child = new UserGridPanel(user.NewValue) { Width = 200 }; }, true); } @@ -50,13 +48,13 @@ namespace osu.Game.Tests.Visual.Online public void TestOverlayVisibility() { AddStep("start hidden", () => accountCreation.Hide()); - AddStep("log out", API.Logout); + AddStep("log out", () => API.Logout()); AddStep("show manually", () => accountCreation.Show()); AddUntilStep("overlay is visible", () => accountCreation.State.Value == Visibility.Visible); AddStep("click button", () => accountCreation.ChildrenOfType().Single().Click()); - AddUntilStep("warning screen is present", () => accountCreation.ChildrenOfType().Single().IsPresent); + AddUntilStep("warning screen is present", () => accountCreation.ChildrenOfType().SingleOrDefault()?.IsPresent == true); AddStep("log back in", () => API.Login("dummy", "password")); AddUntilStep("overlay is hidden", () => accountCreation.State.Value == Visibility.Hidden); From 3c201fb8e78a2c04e541b03cd9b7a1435c98468e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 May 2021 13:20:35 +0900 Subject: [PATCH 305/839] Standardise `canBeNull` specification --- osu.Game/Overlays/AccountCreation/ScreenWarning.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/AccountCreation/ScreenWarning.cs b/osu.Game/Overlays/AccountCreation/ScreenWarning.cs index 6efbf8896f..3d46e9ed94 100644 --- a/osu.Game/Overlays/AccountCreation/ScreenWarning.cs +++ b/osu.Game/Overlays/AccountCreation/ScreenWarning.cs @@ -23,7 +23,7 @@ namespace osu.Game.Overlays.AccountCreation private OsuTextFlowContainer multiAccountExplanationText; private LinkFlowContainer furtherAssistance; - [Resolved(CanBeNull = true)] + [Resolved(canBeNull: true)] private IAPIProvider api { get; set; } [Resolved(canBeNull: true)] From 8f5b28d26452211a913f7e2a2b67aece638367a0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 May 2021 13:51:08 +0900 Subject: [PATCH 306/839] Fix "folder missing" message showing incorrectly for beatmaps folder --- osu.Game/Beatmaps/BeatmapManager.cs | 2 +- osu.Game/Database/ArchiveModelManager.cs | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 5e975de77c..dcbfbf1332 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -65,7 +65,7 @@ namespace osu.Game.Beatmaps protected override string[] HashableFileTypes => new[] { ".osu" }; - protected override string ImportFromStablePath => "."; + protected override string ImportFromStablePath => string.Empty; protected override Storage PrepareStableStorage(StableStorage stableStorage) => stableStorage.GetSongStorage(); diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 550daf36b5..8efd451857 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -691,10 +691,12 @@ namespace osu.Game.Database { var storage = PrepareStableStorage(stableStorage); + // Handle situations like when the user does not have a Skins folder. if (!storage.ExistsDirectory(ImportFromStablePath)) { - // This handles situations like when the user does not have a Skins folder - Logger.Log($"No {ImportFromStablePath} folder available in osu!stable installation", LoggingTarget.Information, LogLevel.Error); + string fullPath = storage.GetFullPath(ImportFromStablePath); + + Logger.Log($"Folder \"{fullPath}\" not available in the target osu!stable installation to import {HumanisedModelName}s.", LoggingTarget.Information, LogLevel.Error); return Task.CompletedTask; } From 713f69ea5505d21359a34c3d9bb3c8980580709b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 May 2021 14:11:42 +0900 Subject: [PATCH 307/839] Tidy up load process --- osu.Game/Overlays/NewsOverlay.cs | 34 ++++++++++++++------------------ 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/osu.Game/Overlays/NewsOverlay.cs b/osu.Game/Overlays/NewsOverlay.cs index d3f407fc0f..f7294dd880 100644 --- a/osu.Game/Overlays/NewsOverlay.cs +++ b/osu.Game/Overlays/NewsOverlay.cs @@ -22,6 +22,8 @@ namespace osu.Game.Overlays private readonly Container sidebarContainer; private readonly NewsSidebar sidebar; + private CancellationTokenSource cancellationToken; + public NewsOverlay() : base(OverlayColourScheme.Purple, false) { @@ -97,7 +99,7 @@ namespace osu.Game.Overlays public void ShowYear(int year) { - showYear(year); + loadFrontPage(year); Show(); } @@ -107,32 +109,18 @@ namespace osu.Game.Overlays Show(); } - private CancellationTokenSource cancellationToken; - - private void showYear(int year) - { - cancellationToken?.Cancel(); - Loading.Show(); - - loadFrontPage(year); - } - private void onArticleChanged(ValueChangedEvent e) { - cancellationToken?.Cancel(); - Loading.Show(); - if (e.NewValue == null) - { loadFrontPage(); - return; - } - - loadArticle(e.NewValue); + else + loadArticle(e.NewValue); } private void loadFrontPage(int year = 0) { + beginLoading(); + Header.SetFrontPage(); var page = new FrontPageDisplay(year); @@ -146,6 +134,8 @@ namespace osu.Game.Overlays private void loadArticle(string article) { + beginLoading(); + Header.SetArticle(article); // Temporary, should be handled by ArticleDisplay later @@ -153,6 +143,12 @@ namespace osu.Game.Overlays Loading.Hide(); } + private void beginLoading() + { + cancellationToken?.Cancel(); + Loading.Show(); + } + protected void LoadDisplay(Drawable display) { ScrollFlow.ScrollToStart(); From ac8efdeabdceac44fd261febeae971bb2deaa087 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 May 2021 14:12:34 +0900 Subject: [PATCH 308/839] Move private methods down --- osu.Game/Overlays/NewsOverlay.cs | 38 ++++++++++++++++---------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/osu.Game/Overlays/NewsOverlay.cs b/osu.Game/Overlays/NewsOverlay.cs index f7294dd880..280e224255 100644 --- a/osu.Game/Overlays/NewsOverlay.cs +++ b/osu.Game/Overlays/NewsOverlay.cs @@ -24,6 +24,8 @@ namespace osu.Game.Overlays private CancellationTokenSource cancellationToken; + private bool displayUpdateRequired = true; + public NewsOverlay() : base(OverlayColourScheme.Purple, false) { @@ -72,8 +74,6 @@ namespace osu.Game.Overlays ShowFrontPage = ShowFrontPage }; - private bool displayUpdateRequired = true; - protected override void PopIn() { base.PopIn(); @@ -109,6 +109,23 @@ namespace osu.Game.Overlays Show(); } + protected void LoadDisplay(Drawable display) + { + ScrollFlow.ScrollToStart(); + LoadComponentAsync(display, loaded => + { + Child = loaded; + }, (cancellationToken = new CancellationTokenSource()).Token); + } + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + sidebarContainer.Height = DrawHeight; + sidebarContainer.Y = Math.Clamp(ScrollFlow.Current - Header.DrawHeight, 0, Math.Max(ScrollFlow.ScrollContent.DrawHeight - DrawHeight - Header.DrawHeight, 0)); + } + private void onArticleChanged(ValueChangedEvent e) { if (e.NewValue == null) @@ -149,23 +166,6 @@ namespace osu.Game.Overlays Loading.Show(); } - protected void LoadDisplay(Drawable display) - { - ScrollFlow.ScrollToStart(); - LoadComponentAsync(display, loaded => - { - Child = loaded; - }, (cancellationToken = new CancellationTokenSource()).Token); - } - - protected override void UpdateAfterChildren() - { - base.UpdateAfterChildren(); - - sidebarContainer.Height = DrawHeight; - sidebarContainer.Y = Math.Clamp(ScrollFlow.Current - Header.DrawHeight, 0, Math.Max(ScrollFlow.ScrollContent.DrawHeight - DrawHeight - Header.DrawHeight, 0)); - } - protected override void Dispose(bool isDisposing) { cancellationToken?.Cancel(); From 673ca4c2a11252b24219222dd9f5f971fc523102 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 May 2021 14:30:40 +0900 Subject: [PATCH 309/839] Tidy up content container specification --- osu.Game/Overlays/NewsOverlay.cs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/osu.Game/Overlays/NewsOverlay.cs b/osu.Game/Overlays/NewsOverlay.cs index 280e224255..b082614a6e 100644 --- a/osu.Game/Overlays/NewsOverlay.cs +++ b/osu.Game/Overlays/NewsOverlay.cs @@ -16,12 +16,11 @@ namespace osu.Game.Overlays { private readonly Bindable article = new Bindable(null); - protected override Container Content => content; - - private readonly Container content; private readonly Container sidebarContainer; private readonly NewsSidebar sidebar; + private readonly Container content; + private CancellationTokenSource cancellationToken; private bool displayUpdateRequired = true; @@ -29,7 +28,7 @@ namespace osu.Game.Overlays public NewsOverlay() : base(OverlayColourScheme.Purple, false) { - base.Content.Add(new GridContainer + Child = new GridContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, @@ -58,7 +57,7 @@ namespace osu.Game.Overlays } } } - }); + }; } protected override void LoadComplete() @@ -112,16 +111,12 @@ namespace osu.Game.Overlays protected void LoadDisplay(Drawable display) { ScrollFlow.ScrollToStart(); - LoadComponentAsync(display, loaded => - { - Child = loaded; - }, (cancellationToken = new CancellationTokenSource()).Token); + LoadComponentAsync(display, loaded => content.Child = loaded, (cancellationToken = new CancellationTokenSource()).Token); } protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); - sidebarContainer.Height = DrawHeight; sidebarContainer.Y = Math.Clamp(ScrollFlow.Current - Header.DrawHeight, 0, Math.Max(ScrollFlow.ScrollContent.DrawHeight - DrawHeight - Header.DrawHeight, 0)); } From 0489ae719de8109fdf1dc3cc26ffe65ccf609b7e Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 20 May 2021 14:53:33 +0900 Subject: [PATCH 310/839] Don't couple `PoolableDrawableWithLifetime` lifetime with its entry It turns out the incompatibility with `LifetimeManagementContainer` causes more issues than anticipated. --- .../Pooling/PoolableDrawableWithLifetime.cs | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs b/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs index ed0430012a..64e1ac16bd 100644 --- a/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs +++ b/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs @@ -3,7 +3,6 @@ #nullable enable -using System; using System.Diagnostics; using osu.Framework.Graphics.Performance; using osu.Framework.Graphics.Pooling; @@ -27,13 +26,14 @@ namespace osu.Game.Rulesets.Objects.Pooling /// protected bool HasEntryApplied { get; private set; } + // Drawable's lifetime gets out of sync with entry's lifetime if entry's lifetime is modified. + // We cannot delegate getter to `Entry.LifetimeStart` because it is incompatible with `LifetimeManagementContainer` due to how lifetime change is detected. public override double LifetimeStart { - get => Entry?.LifetimeStart ?? double.MinValue; + get => base.LifetimeStart; set { - if (Entry == null && LifetimeStart != value) - throw new InvalidOperationException($"Cannot modify lifetime of {nameof(PoolableDrawableWithLifetime)} when entry is not set"); + base.LifetimeStart = value; if (Entry != null) Entry.LifetimeStart = value; @@ -42,11 +42,10 @@ namespace osu.Game.Rulesets.Objects.Pooling public override double LifetimeEnd { - get => Entry?.LifetimeEnd ?? double.MaxValue; + get => base.LifetimeEnd; set { - if (Entry == null && LifetimeEnd != value) - throw new InvalidOperationException($"Cannot modify lifetime of {nameof(PoolableDrawableWithLifetime)} when entry is not set"); + base.LifetimeEnd = value; if (Entry != null) Entry.LifetimeEnd = value; @@ -80,7 +79,12 @@ namespace osu.Game.Rulesets.Objects.Pooling free(); Entry = entry; + + base.LifetimeStart = entry.LifetimeStart; + base.LifetimeEnd = entry.LifetimeEnd; + OnApply(entry); + HasEntryApplied = true; } @@ -112,7 +116,11 @@ namespace osu.Game.Rulesets.Objects.Pooling Debug.Assert(Entry != null && HasEntryApplied); OnFree(Entry); + Entry = null; + base.LifetimeStart = double.MinValue; + base.LifetimeEnd = double.MaxValue; + HasEntryApplied = false; } } From 489caebf5996f1ca676e7d1700e44da8fd21110e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 May 2021 15:15:19 +0900 Subject: [PATCH 311/839] Move bind `LoadComplete` code out of constructor --- osu.Game/Overlays/News/NewsHeader.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/News/NewsHeader.cs b/osu.Game/Overlays/News/NewsHeader.cs index 63174128e7..94bfd62c32 100644 --- a/osu.Game/Overlays/News/NewsHeader.cs +++ b/osu.Game/Overlays/News/NewsHeader.cs @@ -19,13 +19,18 @@ namespace osu.Game.Overlays.News { TabControl.AddItem(front_page_string); + article.BindValueChanged(onArticleChanged, true); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + Current.BindValueChanged(e => { if (e.NewValue == front_page_string) ShowFrontPage?.Invoke(); }); - - article.BindValueChanged(onArticleChanged, true); } public void SetFrontPage() => article.Value = null; From d4530313aa8605f9071d9a40f8e1d0bf192d7014 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 May 2021 15:15:46 +0900 Subject: [PATCH 312/839] Tidy event parameter naming --- osu.Game/Overlays/NewsOverlay.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/NewsOverlay.cs b/osu.Game/Overlays/NewsOverlay.cs index b082614a6e..df564704fa 100644 --- a/osu.Game/Overlays/NewsOverlay.cs +++ b/osu.Game/Overlays/NewsOverlay.cs @@ -68,10 +68,7 @@ namespace osu.Game.Overlays article.BindValueChanged(onArticleChanged); } - protected override NewsHeader CreateHeader() => new NewsHeader - { - ShowFrontPage = ShowFrontPage - }; + protected override NewsHeader CreateHeader() => new NewsHeader { ShowFrontPage = ShowFrontPage }; protected override void PopIn() { @@ -121,12 +118,12 @@ namespace osu.Game.Overlays sidebarContainer.Y = Math.Clamp(ScrollFlow.Current - Header.DrawHeight, 0, Math.Max(ScrollFlow.ScrollContent.DrawHeight - DrawHeight - Header.DrawHeight, 0)); } - private void onArticleChanged(ValueChangedEvent e) + private void onArticleChanged(ValueChangedEvent article) { - if (e.NewValue == null) + if (article.NewValue == null) loadFrontPage(); else - loadArticle(e.NewValue); + loadArticle(article.NewValue); } private void loadFrontPage(int year = 0) From 9267d23dc282976abbacebd82281da2ba71cfd3c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 May 2021 15:23:49 +0900 Subject: [PATCH 313/839] Make year nullable rather than defaulting to zero --- osu.Game/Online/API/Requests/GetNewsRequest.cs | 8 ++++---- .../Displays/{FrontPageDisplay.cs => ArticleListing.cs} | 0 2 files changed, 4 insertions(+), 4 deletions(-) rename osu.Game/Overlays/News/Displays/{FrontPageDisplay.cs => ArticleListing.cs} (100%) diff --git a/osu.Game/Online/API/Requests/GetNewsRequest.cs b/osu.Game/Online/API/Requests/GetNewsRequest.cs index 3eb29f1292..992ccc6d59 100644 --- a/osu.Game/Online/API/Requests/GetNewsRequest.cs +++ b/osu.Game/Online/API/Requests/GetNewsRequest.cs @@ -8,10 +8,10 @@ namespace osu.Game.Online.API.Requests { public class GetNewsRequest : APIRequest { - private readonly int year; + private readonly int? year; private readonly Cursor cursor; - public GetNewsRequest(int year = 0, Cursor cursor = null) + public GetNewsRequest(int? year = null, Cursor cursor = null) { this.year = year; this.cursor = cursor; @@ -22,8 +22,8 @@ namespace osu.Game.Online.API.Requests var req = base.CreateWebRequest(); req.AddCursor(cursor); - if (year != 0) - req.AddParameter("year", year.ToString()); + if (year.HasValue) + req.AddParameter("year", year.Value.ToString()); return req; } diff --git a/osu.Game/Overlays/News/Displays/FrontPageDisplay.cs b/osu.Game/Overlays/News/Displays/ArticleListing.cs similarity index 100% rename from osu.Game/Overlays/News/Displays/FrontPageDisplay.cs rename to osu.Game/Overlays/News/Displays/ArticleListing.cs From 958d51141da905b123f6167a0a60d506862b5f86 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 May 2021 15:24:01 +0900 Subject: [PATCH 314/839] Rename `FrontPageDisplay` to `ArticleListing` --- osu.Game/Overlays/News/Displays/ArticleListing.cs | 13 ++++++++++--- osu.Game/Overlays/NewsOverlay.cs | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/News/Displays/ArticleListing.cs b/osu.Game/Overlays/News/Displays/ArticleListing.cs index 7691e4a901..e713b3de84 100644 --- a/osu.Game/Overlays/News/Displays/ArticleListing.cs +++ b/osu.Game/Overlays/News/Displays/ArticleListing.cs @@ -14,7 +14,10 @@ using osuTK; namespace osu.Game.Overlays.News.Displays { - public class FrontPageDisplay : CompositeDrawable + /// + /// Lists articles in a vertical flow for a specified year. + /// + public class ArticleListing : CompositeDrawable { public Action ResponseReceived; @@ -27,9 +30,13 @@ namespace osu.Game.Overlays.News.Displays private GetNewsRequest request; private Cursor lastCursor; - private readonly int year; + private readonly int? year; - public FrontPageDisplay(int year = 0) + /// + /// Instantiate a listing for the specified year. + /// + /// The year to load articles from. If null, will show the most recent articles. + public ArticleListing(int? year = null) { this.year = year; } diff --git a/osu.Game/Overlays/NewsOverlay.cs b/osu.Game/Overlays/NewsOverlay.cs index df564704fa..510cdba020 100644 --- a/osu.Game/Overlays/NewsOverlay.cs +++ b/osu.Game/Overlays/NewsOverlay.cs @@ -132,7 +132,7 @@ namespace osu.Game.Overlays Header.SetFrontPage(); - var page = new FrontPageDisplay(year); + var page = new ArticleListing(year); page.ResponseReceived += r => { sidebar.Metadata.Value = r.SidebarMetadata; From abf96db54535e8e24a818db00ee6d19e921217ce Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 20 May 2021 15:27:08 +0900 Subject: [PATCH 315/839] Add regression test for the pattern of using DHO proxy in `LifetimeManagementContainer` --- .../Gameplay/TestSceneProxyContainer.cs | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 osu.Game.Tests/Gameplay/TestSceneProxyContainer.cs diff --git a/osu.Game.Tests/Gameplay/TestSceneProxyContainer.cs b/osu.Game.Tests/Gameplay/TestSceneProxyContainer.cs new file mode 100644 index 0000000000..9975c65085 --- /dev/null +++ b/osu.Game.Tests/Gameplay/TestSceneProxyContainer.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 NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; +using osu.Framework.Timing; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.UI; +using osu.Game.Tests.Visual; + +namespace osu.Game.Tests.Gameplay +{ + [HeadlessTest] + public class TestSceneProxyContainer : OsuTestScene + { + private HitObjectContainer hitObjectContainer; + private ProxyContainer proxyContainer; + private readonly ManualClock clock = new ManualClock(); + + [SetUp] + public void SetUp() => Schedule(() => + { + Child = new Container + { + Children = new Drawable[] + { + hitObjectContainer = new HitObjectContainer(), + proxyContainer = new ProxyContainer() + }, + Clock = new FramedClock(clock) + }; + clock.CurrentTime = 0; + }); + + [Test] + public void TestProxyLifetimeManagement() + { + AddStep("Add proxy drawables", () => + { + addProxy(new TestDrawableHitObject(1000)); + addProxy(new TestDrawableHitObject(3000)); + addProxy(new TestDrawableHitObject(5000)); + }); + + AddStep($"time = 1000", () => clock.CurrentTime = 1000); + AddAssert("One proxy is alive", () => proxyContainer.AliveChildren.Count == 1); + AddStep($"time = 5000", () => clock.CurrentTime = 5000); + AddAssert("One proxy is alive", () => proxyContainer.AliveChildren.Count == 1); + AddStep($"time = 6000", () => clock.CurrentTime = 6000); + AddAssert("No proxy is alive", () => proxyContainer.AliveChildren.Count == 0); + } + + private void addProxy(DrawableHitObject drawableHitObject) + { + hitObjectContainer.Add(drawableHitObject); + proxyContainer.AddProxy(drawableHitObject); + } + + private class ProxyContainer : LifetimeManagementContainer + { + public IReadOnlyList AliveChildren => AliveInternalChildren; + + public void AddProxy(Drawable d) => AddInternal(d.CreateProxy()); + } + + private class TestDrawableHitObject : DrawableHitObject + { + protected override double InitialLifetimeOffset => 100; + + public TestDrawableHitObject(double startTime) + : base(new HitObject { StartTime = startTime }) + { + } + + protected override void UpdateInitialTransforms() + { + LifetimeEnd = LifetimeStart + 500; + } + } + } +} From dbfaaecd9cd004e86fbfd0c741b8100e001dbabe Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 May 2021 15:39:29 +0900 Subject: [PATCH 316/839] Reword comment to not mention tests driectly --- osu.Game/Screens/Edit/Editor.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index eafa5847a3..986a4efb28 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -497,9 +497,11 @@ namespace osu.Game.Screens.Edit ApplyToBackground(b => b.FadeColour(Color4.White, 500)); resetTrack(); + // To update the game-wide beatmap with any changes, perform a re-fetch on exit. + // This is required as the editor makes its local changes via EditorBeatmap + // (which are not propagated outwards to a potentially cached WorkingBeatmap). var refetchedBeatmap = beatmapManager.GetWorkingBeatmap(Beatmap.Value.BeatmapInfo); - // beatmap re-fetch may not be feasible in tests. if (!(refetchedBeatmap is DummyWorkingBeatmap)) Beatmap.Value = refetchedBeatmap; From d197a7f6f5f539ec2e75f1bf2e7935bc8642d9a9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 20 May 2021 15:39:45 +0900 Subject: [PATCH 317/839] Rename multiplayer client classes --- .../Multiplayer/TestSceneMultiplayer.cs | 2 +- .../Online/Multiplayer/MultiplayerClient.cs | 645 +++++++++++++++--- .../Multiplayer/OnlineMultiplayerClient.cs | 158 +++++ .../Multiplayer/StatefulMultiplayerClient.cs | 642 ----------------- osu.Game/OsuGameBase.cs | 4 +- .../CreateMultiplayerMatchButton.cs | 2 +- .../Match/MultiplayerMatchSettingsOverlay.cs | 2 +- .../OnlinePlay/Multiplayer/Multiplayer.cs | 2 +- .../Multiplayer/MultiplayerLoungeSubScreen.cs | 2 +- .../Multiplayer/MultiplayerMatchSongSelect.cs | 2 +- .../Multiplayer/MultiplayerMatchSubScreen.cs | 2 +- .../Multiplayer/MultiplayerPlayer.cs | 2 +- .../Multiplayer/MultiplayerRoomComposite.cs | 2 +- .../Multiplayer/MultiplayerRoomManager.cs | 2 +- .../Participants/ParticipantsListHeader.cs | 2 +- .../Spectate/MultiSpectatorScreen.cs | 2 +- .../HUD/MultiplayerGameplayLeaderboard.cs | 2 +- .../Multiplayer/MultiplayerTestScene.cs | 2 +- .../Multiplayer/TestMultiplayerClient.cs | 2 +- .../TestMultiplayerRoomContainer.cs | 2 +- 20 files changed, 742 insertions(+), 739 deletions(-) create mode 100644 osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs delete mode 100644 osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index bba7e2b391..424efb255b 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -195,7 +195,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private class TestMultiplayer : Screens.OnlinePlay.Multiplayer.Multiplayer { - [Cached(typeof(StatefulMultiplayerClient))] + [Cached(typeof(MultiplayerClient))] public readonly TestMultiplayerClient Client; public TestMultiplayer() diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 4529dfd0a7..2e65f7cf1c 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -3,132 +3,621 @@ #nullable enable +using System; using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.AspNetCore.SignalR.Client; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.ObjectExtensions; +using osu.Framework.Graphics; +using osu.Framework.Logging; using osu.Game.Beatmaps; +using osu.Game.Database; using osu.Game.Online.API; -using osu.Game.Online.API.Requests; using osu.Game.Online.Rooms; +using osu.Game.Online.Rooms.RoomStatuses; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; +using osu.Game.Users; +using osu.Game.Utils; namespace osu.Game.Online.Multiplayer { - public class MultiplayerClient : StatefulMultiplayerClient + public abstract class MultiplayerClient : Component, IMultiplayerClient, IMultiplayerRoomServer { - private readonly string endpoint; + /// + /// Invoked when any change occurs to the multiplayer room. + /// + public event Action? RoomUpdated; - private IHubClientConnector? connector; + /// + /// Invoked when the multiplayer server requests the current beatmap to be loaded into play. + /// + public event Action? LoadRequested; - public override IBindable IsConnected { get; } = new BindableBool(); + /// + /// Invoked when the multiplayer server requests gameplay to be started. + /// + public event Action? MatchStarted; - private HubConnection? connection => connector?.CurrentConnection; + /// + /// Invoked when the multiplayer server has finished collating results. + /// + public event Action? ResultsReady; - public MultiplayerClient(EndpointConfiguration endpoints) + /// + /// Whether the is currently connected. + /// This is NOT thread safe and usage should be scheduled. + /// + public abstract IBindable IsConnected { get; } + + /// + /// The joined . + /// + public MultiplayerRoom? Room { get; private set; } + + /// + /// The users in the joined which are participating in the current gameplay loop. + /// + public readonly BindableList CurrentMatchPlayingUserIds = new BindableList(); + + public readonly Bindable CurrentMatchPlayingItem = new Bindable(); + + /// + /// The corresponding to the local player, if available. + /// + public MultiplayerRoomUser? LocalUser => Room?.Users.SingleOrDefault(u => u.User?.Id == API.LocalUser.Value.Id); + + /// + /// Whether the is the host in . + /// + public bool IsHost { - endpoint = endpoints.MultiplayerEndpointUrl; - } - - [BackgroundDependencyLoader] - private void load(IAPIProvider api) - { - connector = api.GetHubConnector(nameof(MultiplayerClient), endpoint); - - if (connector != null) + get { - connector.ConfigureConnection = connection => - { - // this is kind of SILLY - // https://github.com/dotnet/aspnetcore/issues/15198 - connection.On(nameof(IMultiplayerClient.RoomStateChanged), ((IMultiplayerClient)this).RoomStateChanged); - connection.On(nameof(IMultiplayerClient.UserJoined), ((IMultiplayerClient)this).UserJoined); - connection.On(nameof(IMultiplayerClient.UserLeft), ((IMultiplayerClient)this).UserLeft); - connection.On(nameof(IMultiplayerClient.HostChanged), ((IMultiplayerClient)this).HostChanged); - connection.On(nameof(IMultiplayerClient.SettingsChanged), ((IMultiplayerClient)this).SettingsChanged); - connection.On(nameof(IMultiplayerClient.UserStateChanged), ((IMultiplayerClient)this).UserStateChanged); - connection.On(nameof(IMultiplayerClient.LoadRequested), ((IMultiplayerClient)this).LoadRequested); - connection.On(nameof(IMultiplayerClient.MatchStarted), ((IMultiplayerClient)this).MatchStarted); - connection.On(nameof(IMultiplayerClient.ResultsReady), ((IMultiplayerClient)this).ResultsReady); - connection.On>(nameof(IMultiplayerClient.UserModsChanged), ((IMultiplayerClient)this).UserModsChanged); - connection.On(nameof(IMultiplayerClient.UserBeatmapAvailabilityChanged), ((IMultiplayerClient)this).UserBeatmapAvailabilityChanged); - }; - - IsConnected.BindTo(connector.IsConnected); + var localUser = LocalUser; + return localUser != null && Room?.Host != null && localUser.Equals(Room.Host); } } - protected override Task JoinRoom(long roomId) - { - if (!IsConnected.Value) - return Task.FromCanceled(new CancellationToken(true)); + [Resolved] + protected IAPIProvider API { get; private set; } = null!; - return connection.InvokeAsync(nameof(IMultiplayerServer.JoinRoom), roomId); + [Resolved] + protected RulesetStore Rulesets { get; private set; } = null!; + + [Resolved] + private UserLookupCache userLookupCache { get; set; } = null!; + + private Room? apiRoom; + + [BackgroundDependencyLoader] + private void load() + { + IsConnected.BindValueChanged(connected => + { + // clean up local room state on server disconnect. + if (!connected.NewValue && Room != null) + { + Logger.Log("Connection to multiplayer server was lost.", LoggingTarget.Runtime, LogLevel.Important); + LeaveRoom(); + } + }); } - protected override Task LeaveRoomInternal() - { - if (!IsConnected.Value) - return Task.FromCanceled(new CancellationToken(true)); + private readonly TaskChain joinOrLeaveTaskChain = new TaskChain(); + private CancellationTokenSource? joinCancellationSource; - return connection.InvokeAsync(nameof(IMultiplayerServer.LeaveRoom)); + /// + /// Joins the for a given API . + /// + /// The API . + public async Task JoinRoom(Room room) + { + var cancellationSource = joinCancellationSource = new CancellationTokenSource(); + + await joinOrLeaveTaskChain.Add(async () => + { + if (Room != null) + throw new InvalidOperationException("Cannot join a multiplayer room while already in one."); + + Debug.Assert(room.RoomID.Value != null); + + // Join the server-side room. + var joinedRoom = await JoinRoom(room.RoomID.Value.Value).ConfigureAwait(false); + Debug.Assert(joinedRoom != null); + + // Populate users. + Debug.Assert(joinedRoom.Users != null); + await Task.WhenAll(joinedRoom.Users.Select(PopulateUser)).ConfigureAwait(false); + + // Update the stored room (must be done on update thread for thread-safety). + await scheduleAsync(() => + { + Room = joinedRoom; + apiRoom = room; + foreach (var user in joinedRoom.Users) + updateUserPlayingState(user.UserID, user.State); + }, cancellationSource.Token).ConfigureAwait(false); + + // Update room settings. + await updateLocalRoomSettings(joinedRoom.Settings, cancellationSource.Token).ConfigureAwait(false); + }, cancellationSource.Token).ConfigureAwait(false); } - public override Task TransferHost(int userId) + /// + /// Joins the with a given ID. + /// + /// The room ID. + /// The joined . + protected abstract Task JoinRoom(long roomId); + + public Task LeaveRoom() { - if (!IsConnected.Value) + // The join may have not completed yet, so certain tasks that either update the room or reference the room should be cancelled. + // This includes the setting of Room itself along with the initial update of the room settings on join. + joinCancellationSource?.Cancel(); + + // Leaving rooms is expected to occur instantaneously whilst the operation is finalised in the background. + // However a few members need to be reset immediately to prevent other components from entering invalid states whilst the operation hasn't yet completed. + // For example, if a room was left and the user immediately pressed the "create room" button, then the user could be taken into the lobby if the value of Room is not reset in time. + var scheduledReset = scheduleAsync(() => + { + apiRoom = null; + Room = null; + CurrentMatchPlayingUserIds.Clear(); + + RoomUpdated?.Invoke(); + }); + + return joinOrLeaveTaskChain.Add(async () => + { + await scheduledReset.ConfigureAwait(false); + await LeaveRoomInternal().ConfigureAwait(false); + }); + } + + protected abstract Task LeaveRoomInternal(); + + /// + /// Change the current settings. + /// + /// + /// A room must be joined for this to have any effect. + /// + /// The new room name, if any. + /// The new room playlist item, if any. + public Task ChangeSettings(Optional name = default, Optional item = default) + { + if (Room == null) + throw new InvalidOperationException("Must be joined to a match to change settings."); + + // A dummy playlist item filled with the current room settings (except mods). + var existingPlaylistItem = new PlaylistItem + { + Beatmap = + { + Value = new BeatmapInfo + { + OnlineBeatmapID = Room.Settings.BeatmapID, + MD5Hash = Room.Settings.BeatmapChecksum + } + }, + RulesetID = Room.Settings.RulesetID + }; + + return ChangeSettings(new MultiplayerRoomSettings + { + Name = name.GetOr(Room.Settings.Name), + BeatmapID = item.GetOr(existingPlaylistItem).BeatmapID, + BeatmapChecksum = item.GetOr(existingPlaylistItem).Beatmap.Value.MD5Hash, + RulesetID = item.GetOr(existingPlaylistItem).RulesetID, + RequiredMods = item.HasValue ? item.Value.AsNonNull().RequiredMods.Select(m => new APIMod(m)).ToList() : Room.Settings.RequiredMods, + AllowedMods = item.HasValue ? item.Value.AsNonNull().AllowedMods.Select(m => new APIMod(m)).ToList() : Room.Settings.AllowedMods, + }); + } + + /// + /// Toggles the 's ready state. + /// + /// If a toggle of ready state is not valid at this time. + public async Task ToggleReady() + { + var localUser = LocalUser; + + if (localUser == null) + return; + + switch (localUser.State) + { + case MultiplayerUserState.Idle: + await ChangeState(MultiplayerUserState.Ready).ConfigureAwait(false); + return; + + case MultiplayerUserState.Ready: + await ChangeState(MultiplayerUserState.Idle).ConfigureAwait(false); + return; + + default: + throw new InvalidOperationException($"Cannot toggle ready when in {localUser.State}"); + } + } + + /// + /// Toggles the 's spectating state. + /// + /// If a toggle of the spectating state is not valid at this time. + public async Task ToggleSpectate() + { + var localUser = LocalUser; + + if (localUser == null) + return; + + switch (localUser.State) + { + case MultiplayerUserState.Idle: + case MultiplayerUserState.Ready: + await ChangeState(MultiplayerUserState.Spectating).ConfigureAwait(false); + return; + + case MultiplayerUserState.Spectating: + await ChangeState(MultiplayerUserState.Idle).ConfigureAwait(false); + return; + + default: + throw new InvalidOperationException($"Cannot toggle spectate when in {localUser.State}"); + } + } + + public abstract Task TransferHost(int userId); + + public abstract Task ChangeSettings(MultiplayerRoomSettings settings); + + public abstract Task ChangeState(MultiplayerUserState newState); + + public abstract Task ChangeBeatmapAvailability(BeatmapAvailability newBeatmapAvailability); + + /// + /// Change the local user's mods in the currently joined room. + /// + /// The proposed new mods, excluding any required by the room itself. + public Task ChangeUserMods(IEnumerable newMods) => ChangeUserMods(newMods.Select(m => new APIMod(m)).ToList()); + + public abstract Task ChangeUserMods(IEnumerable newMods); + + public abstract Task StartMatch(); + + Task IMultiplayerClient.RoomStateChanged(MultiplayerRoomState state) + { + if (Room == null) return Task.CompletedTask; - return connection.InvokeAsync(nameof(IMultiplayerServer.TransferHost), userId); + Scheduler.Add(() => + { + if (Room == null) + return; + + Debug.Assert(apiRoom != null); + + Room.State = state; + + switch (state) + { + case MultiplayerRoomState.Open: + apiRoom.Status.Value = new RoomStatusOpen(); + break; + + case MultiplayerRoomState.Playing: + apiRoom.Status.Value = new RoomStatusPlaying(); + break; + + case MultiplayerRoomState.Closed: + apiRoom.Status.Value = new RoomStatusEnded(); + break; + } + + RoomUpdated?.Invoke(); + }, false); + + return Task.CompletedTask; } - public override Task ChangeSettings(MultiplayerRoomSettings settings) + async Task IMultiplayerClient.UserJoined(MultiplayerRoomUser user) { - if (!IsConnected.Value) + if (Room == null) + return; + + await PopulateUser(user).ConfigureAwait(false); + + Scheduler.Add(() => + { + if (Room == null) + return; + + // for sanity, ensure that there can be no duplicate users in the room user list. + if (Room.Users.Any(existing => existing.UserID == user.UserID)) + return; + + Room.Users.Add(user); + + RoomUpdated?.Invoke(); + }, false); + } + + Task IMultiplayerClient.UserLeft(MultiplayerRoomUser user) + { + if (Room == null) return Task.CompletedTask; - return connection.InvokeAsync(nameof(IMultiplayerServer.ChangeSettings), settings); + Scheduler.Add(() => + { + if (Room == null) + return; + + Room.Users.Remove(user); + CurrentMatchPlayingUserIds.Remove(user.UserID); + + RoomUpdated?.Invoke(); + }, false); + + return Task.CompletedTask; } - public override Task ChangeState(MultiplayerUserState newState) + Task IMultiplayerClient.HostChanged(int userId) { - if (!IsConnected.Value) + if (Room == null) return Task.CompletedTask; - return connection.InvokeAsync(nameof(IMultiplayerServer.ChangeState), newState); + Scheduler.Add(() => + { + if (Room == null) + return; + + Debug.Assert(apiRoom != null); + + var user = Room.Users.FirstOrDefault(u => u.UserID == userId); + + Room.Host = user; + apiRoom.Host.Value = user?.User; + + RoomUpdated?.Invoke(); + }, false); + + return Task.CompletedTask; } - public override Task ChangeBeatmapAvailability(BeatmapAvailability newBeatmapAvailability) + Task IMultiplayerClient.SettingsChanged(MultiplayerRoomSettings newSettings) { - if (!IsConnected.Value) + updateLocalRoomSettings(newSettings); + return Task.CompletedTask; + } + + Task IMultiplayerClient.UserStateChanged(int userId, MultiplayerUserState state) + { + if (Room == null) return Task.CompletedTask; - return connection.InvokeAsync(nameof(IMultiplayerServer.ChangeBeatmapAvailability), newBeatmapAvailability); + Scheduler.Add(() => + { + if (Room == null) + return; + + Room.Users.Single(u => u.UserID == userId).State = state; + + updateUserPlayingState(userId, state); + + RoomUpdated?.Invoke(); + }, false); + + return Task.CompletedTask; } - public override Task ChangeUserMods(IEnumerable newMods) + Task IMultiplayerClient.UserBeatmapAvailabilityChanged(int userId, BeatmapAvailability beatmapAvailability) { - if (!IsConnected.Value) + if (Room == null) return Task.CompletedTask; - return connection.InvokeAsync(nameof(IMultiplayerServer.ChangeUserMods), newMods); + Scheduler.Add(() => + { + var user = Room?.Users.SingleOrDefault(u => u.UserID == userId); + + // errors here are not critical - beatmap availability state is mostly for display. + if (user == null) + return; + + user.BeatmapAvailability = beatmapAvailability; + + RoomUpdated?.Invoke(); + }, false); + + return Task.CompletedTask; } - public override Task StartMatch() + public Task UserModsChanged(int userId, IEnumerable mods) { - if (!IsConnected.Value) + if (Room == null) return Task.CompletedTask; - return connection.InvokeAsync(nameof(IMultiplayerServer.StartMatch)); + Scheduler.Add(() => + { + var user = Room?.Users.SingleOrDefault(u => u.UserID == userId); + + // errors here are not critical - user mods are mostly for display. + if (user == null) + return; + + user.Mods = mods; + + RoomUpdated?.Invoke(); + }, false); + + return Task.CompletedTask; } - protected override Task GetOnlineBeatmapSet(int beatmapId, CancellationToken cancellationToken = default) + Task IMultiplayerClient.LoadRequested() { - var tcs = new TaskCompletionSource(); - var req = new GetBeatmapSetRequest(beatmapId, BeatmapSetLookupType.BeatmapId); + if (Room == null) + return Task.CompletedTask; - req.Success += res => + Scheduler.Add(() => + { + if (Room == null) + return; + + LoadRequested?.Invoke(); + }, false); + + return Task.CompletedTask; + } + + Task IMultiplayerClient.MatchStarted() + { + if (Room == null) + return Task.CompletedTask; + + Scheduler.Add(() => + { + if (Room == null) + return; + + MatchStarted?.Invoke(); + }, false); + + return Task.CompletedTask; + } + + Task IMultiplayerClient.ResultsReady() + { + if (Room == null) + return Task.CompletedTask; + + Scheduler.Add(() => + { + if (Room == null) + return; + + ResultsReady?.Invoke(); + }, false); + + return Task.CompletedTask; + } + + /// + /// Populates the for a given . + /// + /// The to populate. + protected async Task PopulateUser(MultiplayerRoomUser multiplayerUser) => multiplayerUser.User ??= await userLookupCache.GetUserAsync(multiplayerUser.UserID).ConfigureAwait(false); + + /// + /// Updates the local room settings with the given . + /// + /// + /// This updates both the joined and the respective API . + /// + /// The new to update from. + /// The to cancel the update. + private Task updateLocalRoomSettings(MultiplayerRoomSettings settings, CancellationToken cancellationToken = default) => scheduleAsync(() => + { + if (Room == null) + return; + + Debug.Assert(apiRoom != null); + + // Update a few properties of the room instantaneously. + Room.Settings = settings; + apiRoom.Name.Value = Room.Settings.Name; + + // The current item update is delayed until an online beatmap lookup (below) succeeds. + // In-order for the client to not display an outdated beatmap, the current item is forcefully cleared here. + CurrentMatchPlayingItem.Value = null; + + RoomUpdated?.Invoke(); + + GetOnlineBeatmapSet(settings.BeatmapID, cancellationToken).ContinueWith(set => Schedule(() => + { + if (cancellationToken.IsCancellationRequested) + return; + + updatePlaylist(settings, set.Result); + }), TaskContinuationOptions.OnlyOnRanToCompletion); + }, cancellationToken); + + private void updatePlaylist(MultiplayerRoomSettings settings, BeatmapSetInfo beatmapSet) + { + if (Room == null || !Room.Settings.Equals(settings)) + return; + + Debug.Assert(apiRoom != null); + + var beatmap = beatmapSet.Beatmaps.Single(b => b.OnlineBeatmapID == settings.BeatmapID); + beatmap.MD5Hash = settings.BeatmapChecksum; + + var ruleset = Rulesets.GetRuleset(settings.RulesetID).CreateInstance(); + var mods = settings.RequiredMods.Select(m => m.ToMod(ruleset)); + var allowedMods = settings.AllowedMods.Select(m => m.ToMod(ruleset)); + + // Try to retrieve the existing playlist item from the API room. + var playlistItem = apiRoom.Playlist.FirstOrDefault(i => i.ID == settings.PlaylistItemId); + + if (playlistItem != null) + updateItem(playlistItem); + else + { + // An existing playlist item does not exist, so append a new one. + updateItem(playlistItem = new PlaylistItem()); + apiRoom.Playlist.Add(playlistItem); + } + + CurrentMatchPlayingItem.Value = playlistItem; + + void updateItem(PlaylistItem item) + { + item.ID = settings.PlaylistItemId; + item.Beatmap.Value = beatmap; + item.Ruleset.Value = ruleset.RulesetInfo; + item.RequiredMods.Clear(); + item.RequiredMods.AddRange(mods); + item.AllowedMods.Clear(); + item.AllowedMods.AddRange(allowedMods); + } + } + + /// + /// Retrieves a from an online source. + /// + /// The beatmap set ID. + /// A token to cancel the request. + /// The retrieval task. + protected abstract Task GetOnlineBeatmapSet(int beatmapId, CancellationToken cancellationToken = default); + + /// + /// For the provided user ID, update whether the user is included in . + /// + /// The user's ID. + /// The new state of the user. + private void updateUserPlayingState(int userId, MultiplayerUserState state) + { + bool wasPlaying = CurrentMatchPlayingUserIds.Contains(userId); + bool isPlaying = state >= MultiplayerUserState.WaitingForLoad && state <= MultiplayerUserState.FinishedPlay; + + if (isPlaying == wasPlaying) + return; + + if (isPlaying) + CurrentMatchPlayingUserIds.Add(userId); + else + CurrentMatchPlayingUserIds.Remove(userId); + } + + private Task scheduleAsync(Action action, CancellationToken cancellationToken = default) + { + var tcs = new TaskCompletionSource(); + + Scheduler.Add(() => { if (cancellationToken.IsCancellationRequested) { @@ -136,20 +625,18 @@ namespace osu.Game.Online.Multiplayer return; } - tcs.SetResult(res.ToBeatmapSet(Rulesets)); - }; - - req.Failure += e => tcs.SetException(e); - - API.Queue(req); + try + { + action(); + tcs.SetResult(true); + } + catch (Exception ex) + { + tcs.SetException(ex); + } + }); return tcs.Task; } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - connector?.Dispose(); - } } } diff --git a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs new file mode 100644 index 0000000000..cf1e18e059 --- /dev/null +++ b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs @@ -0,0 +1,158 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.SignalR.Client; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Game.Beatmaps; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Online.Rooms; + +namespace osu.Game.Online.Multiplayer +{ + /// + /// A with online connectivity. + /// + public class OnlineMultiplayerClient : MultiplayerClient + { + private readonly string endpoint; + + private IHubClientConnector? connector; + + public override IBindable IsConnected { get; } = new BindableBool(); + + private HubConnection? connection => connector?.CurrentConnection; + + public OnlineMultiplayerClient(EndpointConfiguration endpoints) + { + endpoint = endpoints.MultiplayerEndpointUrl; + } + + [BackgroundDependencyLoader] + private void load(IAPIProvider api) + { + connector = api.GetHubConnector(nameof(OnlineMultiplayerClient), endpoint); + + if (connector != null) + { + connector.ConfigureConnection = connection => + { + // this is kind of SILLY + // https://github.com/dotnet/aspnetcore/issues/15198 + connection.On(nameof(IMultiplayerClient.RoomStateChanged), ((IMultiplayerClient)this).RoomStateChanged); + connection.On(nameof(IMultiplayerClient.UserJoined), ((IMultiplayerClient)this).UserJoined); + connection.On(nameof(IMultiplayerClient.UserLeft), ((IMultiplayerClient)this).UserLeft); + connection.On(nameof(IMultiplayerClient.HostChanged), ((IMultiplayerClient)this).HostChanged); + connection.On(nameof(IMultiplayerClient.SettingsChanged), ((IMultiplayerClient)this).SettingsChanged); + connection.On(nameof(IMultiplayerClient.UserStateChanged), ((IMultiplayerClient)this).UserStateChanged); + connection.On(nameof(IMultiplayerClient.LoadRequested), ((IMultiplayerClient)this).LoadRequested); + connection.On(nameof(IMultiplayerClient.MatchStarted), ((IMultiplayerClient)this).MatchStarted); + connection.On(nameof(IMultiplayerClient.ResultsReady), ((IMultiplayerClient)this).ResultsReady); + connection.On>(nameof(IMultiplayerClient.UserModsChanged), ((IMultiplayerClient)this).UserModsChanged); + connection.On(nameof(IMultiplayerClient.UserBeatmapAvailabilityChanged), ((IMultiplayerClient)this).UserBeatmapAvailabilityChanged); + }; + + IsConnected.BindTo(connector.IsConnected); + } + } + + protected override Task JoinRoom(long roomId) + { + if (!IsConnected.Value) + return Task.FromCanceled(new CancellationToken(true)); + + return connection.InvokeAsync(nameof(IMultiplayerServer.JoinRoom), roomId); + } + + protected override Task LeaveRoomInternal() + { + if (!IsConnected.Value) + return Task.FromCanceled(new CancellationToken(true)); + + return connection.InvokeAsync(nameof(IMultiplayerServer.LeaveRoom)); + } + + public override Task TransferHost(int userId) + { + if (!IsConnected.Value) + return Task.CompletedTask; + + return connection.InvokeAsync(nameof(IMultiplayerServer.TransferHost), userId); + } + + public override Task ChangeSettings(MultiplayerRoomSettings settings) + { + if (!IsConnected.Value) + return Task.CompletedTask; + + return connection.InvokeAsync(nameof(IMultiplayerServer.ChangeSettings), settings); + } + + public override Task ChangeState(MultiplayerUserState newState) + { + if (!IsConnected.Value) + return Task.CompletedTask; + + return connection.InvokeAsync(nameof(IMultiplayerServer.ChangeState), newState); + } + + public override Task ChangeBeatmapAvailability(BeatmapAvailability newBeatmapAvailability) + { + if (!IsConnected.Value) + return Task.CompletedTask; + + return connection.InvokeAsync(nameof(IMultiplayerServer.ChangeBeatmapAvailability), newBeatmapAvailability); + } + + public override Task ChangeUserMods(IEnumerable newMods) + { + if (!IsConnected.Value) + return Task.CompletedTask; + + return connection.InvokeAsync(nameof(IMultiplayerServer.ChangeUserMods), newMods); + } + + public override Task StartMatch() + { + if (!IsConnected.Value) + return Task.CompletedTask; + + return connection.InvokeAsync(nameof(IMultiplayerServer.StartMatch)); + } + + protected override Task GetOnlineBeatmapSet(int beatmapId, CancellationToken cancellationToken = default) + { + var tcs = new TaskCompletionSource(); + var req = new GetBeatmapSetRequest(beatmapId, BeatmapSetLookupType.BeatmapId); + + req.Success += res => + { + if (cancellationToken.IsCancellationRequested) + { + tcs.SetCanceled(); + return; + } + + tcs.SetResult(res.ToBeatmapSet(Rulesets)); + }; + + req.Failure += e => tcs.SetException(e); + + API.Queue(req); + + return tcs.Task; + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + connector?.Dispose(); + } + } +} diff --git a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs deleted file mode 100644 index 7fe48d54b1..0000000000 --- a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs +++ /dev/null @@ -1,642 +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 enable - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Extensions.ObjectExtensions; -using osu.Framework.Graphics; -using osu.Framework.Logging; -using osu.Game.Beatmaps; -using osu.Game.Database; -using osu.Game.Online.API; -using osu.Game.Online.Rooms; -using osu.Game.Online.Rooms.RoomStatuses; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Mods; -using osu.Game.Users; -using osu.Game.Utils; - -namespace osu.Game.Online.Multiplayer -{ - public abstract class StatefulMultiplayerClient : Component, IMultiplayerClient, IMultiplayerRoomServer - { - /// - /// Invoked when any change occurs to the multiplayer room. - /// - public event Action? RoomUpdated; - - /// - /// Invoked when the multiplayer server requests the current beatmap to be loaded into play. - /// - public event Action? LoadRequested; - - /// - /// Invoked when the multiplayer server requests gameplay to be started. - /// - public event Action? MatchStarted; - - /// - /// Invoked when the multiplayer server has finished collating results. - /// - public event Action? ResultsReady; - - /// - /// Whether the is currently connected. - /// This is NOT thread safe and usage should be scheduled. - /// - public abstract IBindable IsConnected { get; } - - /// - /// The joined . - /// - public MultiplayerRoom? Room { get; private set; } - - /// - /// The users in the joined which are participating in the current gameplay loop. - /// - public readonly BindableList CurrentMatchPlayingUserIds = new BindableList(); - - public readonly Bindable CurrentMatchPlayingItem = new Bindable(); - - /// - /// The corresponding to the local player, if available. - /// - public MultiplayerRoomUser? LocalUser => Room?.Users.SingleOrDefault(u => u.User?.Id == API.LocalUser.Value.Id); - - /// - /// Whether the is the host in . - /// - public bool IsHost - { - get - { - var localUser = LocalUser; - return localUser != null && Room?.Host != null && localUser.Equals(Room.Host); - } - } - - [Resolved] - protected IAPIProvider API { get; private set; } = null!; - - [Resolved] - protected RulesetStore Rulesets { get; private set; } = null!; - - [Resolved] - private UserLookupCache userLookupCache { get; set; } = null!; - - private Room? apiRoom; - - [BackgroundDependencyLoader] - private void load() - { - IsConnected.BindValueChanged(connected => - { - // clean up local room state on server disconnect. - if (!connected.NewValue && Room != null) - { - Logger.Log("Connection to multiplayer server was lost.", LoggingTarget.Runtime, LogLevel.Important); - LeaveRoom(); - } - }); - } - - private readonly TaskChain joinOrLeaveTaskChain = new TaskChain(); - private CancellationTokenSource? joinCancellationSource; - - /// - /// Joins the for a given API . - /// - /// The API . - public async Task JoinRoom(Room room) - { - var cancellationSource = joinCancellationSource = new CancellationTokenSource(); - - await joinOrLeaveTaskChain.Add(async () => - { - if (Room != null) - throw new InvalidOperationException("Cannot join a multiplayer room while already in one."); - - Debug.Assert(room.RoomID.Value != null); - - // Join the server-side room. - var joinedRoom = await JoinRoom(room.RoomID.Value.Value).ConfigureAwait(false); - Debug.Assert(joinedRoom != null); - - // Populate users. - Debug.Assert(joinedRoom.Users != null); - await Task.WhenAll(joinedRoom.Users.Select(PopulateUser)).ConfigureAwait(false); - - // Update the stored room (must be done on update thread for thread-safety). - await scheduleAsync(() => - { - Room = joinedRoom; - apiRoom = room; - foreach (var user in joinedRoom.Users) - updateUserPlayingState(user.UserID, user.State); - }, cancellationSource.Token).ConfigureAwait(false); - - // Update room settings. - await updateLocalRoomSettings(joinedRoom.Settings, cancellationSource.Token).ConfigureAwait(false); - }, cancellationSource.Token).ConfigureAwait(false); - } - - /// - /// Joins the with a given ID. - /// - /// The room ID. - /// The joined . - protected abstract Task JoinRoom(long roomId); - - public Task LeaveRoom() - { - // The join may have not completed yet, so certain tasks that either update the room or reference the room should be cancelled. - // This includes the setting of Room itself along with the initial update of the room settings on join. - joinCancellationSource?.Cancel(); - - // Leaving rooms is expected to occur instantaneously whilst the operation is finalised in the background. - // However a few members need to be reset immediately to prevent other components from entering invalid states whilst the operation hasn't yet completed. - // For example, if a room was left and the user immediately pressed the "create room" button, then the user could be taken into the lobby if the value of Room is not reset in time. - var scheduledReset = scheduleAsync(() => - { - apiRoom = null; - Room = null; - CurrentMatchPlayingUserIds.Clear(); - - RoomUpdated?.Invoke(); - }); - - return joinOrLeaveTaskChain.Add(async () => - { - await scheduledReset.ConfigureAwait(false); - await LeaveRoomInternal().ConfigureAwait(false); - }); - } - - protected abstract Task LeaveRoomInternal(); - - /// - /// Change the current settings. - /// - /// - /// A room must be joined for this to have any effect. - /// - /// The new room name, if any. - /// The new room playlist item, if any. - public Task ChangeSettings(Optional name = default, Optional item = default) - { - if (Room == null) - throw new InvalidOperationException("Must be joined to a match to change settings."); - - // A dummy playlist item filled with the current room settings (except mods). - var existingPlaylistItem = new PlaylistItem - { - Beatmap = - { - Value = new BeatmapInfo - { - OnlineBeatmapID = Room.Settings.BeatmapID, - MD5Hash = Room.Settings.BeatmapChecksum - } - }, - RulesetID = Room.Settings.RulesetID - }; - - return ChangeSettings(new MultiplayerRoomSettings - { - Name = name.GetOr(Room.Settings.Name), - BeatmapID = item.GetOr(existingPlaylistItem).BeatmapID, - BeatmapChecksum = item.GetOr(existingPlaylistItem).Beatmap.Value.MD5Hash, - RulesetID = item.GetOr(existingPlaylistItem).RulesetID, - RequiredMods = item.HasValue ? item.Value.AsNonNull().RequiredMods.Select(m => new APIMod(m)).ToList() : Room.Settings.RequiredMods, - AllowedMods = item.HasValue ? item.Value.AsNonNull().AllowedMods.Select(m => new APIMod(m)).ToList() : Room.Settings.AllowedMods, - }); - } - - /// - /// Toggles the 's ready state. - /// - /// If a toggle of ready state is not valid at this time. - public async Task ToggleReady() - { - var localUser = LocalUser; - - if (localUser == null) - return; - - switch (localUser.State) - { - case MultiplayerUserState.Idle: - await ChangeState(MultiplayerUserState.Ready).ConfigureAwait(false); - return; - - case MultiplayerUserState.Ready: - await ChangeState(MultiplayerUserState.Idle).ConfigureAwait(false); - return; - - default: - throw new InvalidOperationException($"Cannot toggle ready when in {localUser.State}"); - } - } - - /// - /// Toggles the 's spectating state. - /// - /// If a toggle of the spectating state is not valid at this time. - public async Task ToggleSpectate() - { - var localUser = LocalUser; - - if (localUser == null) - return; - - switch (localUser.State) - { - case MultiplayerUserState.Idle: - case MultiplayerUserState.Ready: - await ChangeState(MultiplayerUserState.Spectating).ConfigureAwait(false); - return; - - case MultiplayerUserState.Spectating: - await ChangeState(MultiplayerUserState.Idle).ConfigureAwait(false); - return; - - default: - throw new InvalidOperationException($"Cannot toggle spectate when in {localUser.State}"); - } - } - - public abstract Task TransferHost(int userId); - - public abstract Task ChangeSettings(MultiplayerRoomSettings settings); - - public abstract Task ChangeState(MultiplayerUserState newState); - - public abstract Task ChangeBeatmapAvailability(BeatmapAvailability newBeatmapAvailability); - - /// - /// Change the local user's mods in the currently joined room. - /// - /// The proposed new mods, excluding any required by the room itself. - public Task ChangeUserMods(IEnumerable newMods) => ChangeUserMods(newMods.Select(m => new APIMod(m)).ToList()); - - public abstract Task ChangeUserMods(IEnumerable newMods); - - public abstract Task StartMatch(); - - Task IMultiplayerClient.RoomStateChanged(MultiplayerRoomState state) - { - if (Room == null) - return Task.CompletedTask; - - Scheduler.Add(() => - { - if (Room == null) - return; - - Debug.Assert(apiRoom != null); - - Room.State = state; - - switch (state) - { - case MultiplayerRoomState.Open: - apiRoom.Status.Value = new RoomStatusOpen(); - break; - - case MultiplayerRoomState.Playing: - apiRoom.Status.Value = new RoomStatusPlaying(); - break; - - case MultiplayerRoomState.Closed: - apiRoom.Status.Value = new RoomStatusEnded(); - break; - } - - RoomUpdated?.Invoke(); - }, false); - - return Task.CompletedTask; - } - - async Task IMultiplayerClient.UserJoined(MultiplayerRoomUser user) - { - if (Room == null) - return; - - await PopulateUser(user).ConfigureAwait(false); - - Scheduler.Add(() => - { - if (Room == null) - return; - - // for sanity, ensure that there can be no duplicate users in the room user list. - if (Room.Users.Any(existing => existing.UserID == user.UserID)) - return; - - Room.Users.Add(user); - - RoomUpdated?.Invoke(); - }, false); - } - - Task IMultiplayerClient.UserLeft(MultiplayerRoomUser user) - { - if (Room == null) - return Task.CompletedTask; - - Scheduler.Add(() => - { - if (Room == null) - return; - - Room.Users.Remove(user); - CurrentMatchPlayingUserIds.Remove(user.UserID); - - RoomUpdated?.Invoke(); - }, false); - - return Task.CompletedTask; - } - - Task IMultiplayerClient.HostChanged(int userId) - { - if (Room == null) - return Task.CompletedTask; - - Scheduler.Add(() => - { - if (Room == null) - return; - - Debug.Assert(apiRoom != null); - - var user = Room.Users.FirstOrDefault(u => u.UserID == userId); - - Room.Host = user; - apiRoom.Host.Value = user?.User; - - RoomUpdated?.Invoke(); - }, false); - - return Task.CompletedTask; - } - - Task IMultiplayerClient.SettingsChanged(MultiplayerRoomSettings newSettings) - { - updateLocalRoomSettings(newSettings); - return Task.CompletedTask; - } - - Task IMultiplayerClient.UserStateChanged(int userId, MultiplayerUserState state) - { - if (Room == null) - return Task.CompletedTask; - - Scheduler.Add(() => - { - if (Room == null) - return; - - Room.Users.Single(u => u.UserID == userId).State = state; - - updateUserPlayingState(userId, state); - - RoomUpdated?.Invoke(); - }, false); - - return Task.CompletedTask; - } - - Task IMultiplayerClient.UserBeatmapAvailabilityChanged(int userId, BeatmapAvailability beatmapAvailability) - { - if (Room == null) - return Task.CompletedTask; - - Scheduler.Add(() => - { - var user = Room?.Users.SingleOrDefault(u => u.UserID == userId); - - // errors here are not critical - beatmap availability state is mostly for display. - if (user == null) - return; - - user.BeatmapAvailability = beatmapAvailability; - - RoomUpdated?.Invoke(); - }, false); - - return Task.CompletedTask; - } - - public Task UserModsChanged(int userId, IEnumerable mods) - { - if (Room == null) - return Task.CompletedTask; - - Scheduler.Add(() => - { - var user = Room?.Users.SingleOrDefault(u => u.UserID == userId); - - // errors here are not critical - user mods are mostly for display. - if (user == null) - return; - - user.Mods = mods; - - RoomUpdated?.Invoke(); - }, false); - - return Task.CompletedTask; - } - - Task IMultiplayerClient.LoadRequested() - { - if (Room == null) - return Task.CompletedTask; - - Scheduler.Add(() => - { - if (Room == null) - return; - - LoadRequested?.Invoke(); - }, false); - - return Task.CompletedTask; - } - - Task IMultiplayerClient.MatchStarted() - { - if (Room == null) - return Task.CompletedTask; - - Scheduler.Add(() => - { - if (Room == null) - return; - - MatchStarted?.Invoke(); - }, false); - - return Task.CompletedTask; - } - - Task IMultiplayerClient.ResultsReady() - { - if (Room == null) - return Task.CompletedTask; - - Scheduler.Add(() => - { - if (Room == null) - return; - - ResultsReady?.Invoke(); - }, false); - - return Task.CompletedTask; - } - - /// - /// Populates the for a given . - /// - /// The to populate. - protected async Task PopulateUser(MultiplayerRoomUser multiplayerUser) => multiplayerUser.User ??= await userLookupCache.GetUserAsync(multiplayerUser.UserID).ConfigureAwait(false); - - /// - /// Updates the local room settings with the given . - /// - /// - /// This updates both the joined and the respective API . - /// - /// The new to update from. - /// The to cancel the update. - private Task updateLocalRoomSettings(MultiplayerRoomSettings settings, CancellationToken cancellationToken = default) => scheduleAsync(() => - { - if (Room == null) - return; - - Debug.Assert(apiRoom != null); - - // Update a few properties of the room instantaneously. - Room.Settings = settings; - apiRoom.Name.Value = Room.Settings.Name; - - // The current item update is delayed until an online beatmap lookup (below) succeeds. - // In-order for the client to not display an outdated beatmap, the current item is forcefully cleared here. - CurrentMatchPlayingItem.Value = null; - - RoomUpdated?.Invoke(); - - GetOnlineBeatmapSet(settings.BeatmapID, cancellationToken).ContinueWith(set => Schedule(() => - { - if (cancellationToken.IsCancellationRequested) - return; - - updatePlaylist(settings, set.Result); - }), TaskContinuationOptions.OnlyOnRanToCompletion); - }, cancellationToken); - - private void updatePlaylist(MultiplayerRoomSettings settings, BeatmapSetInfo beatmapSet) - { - if (Room == null || !Room.Settings.Equals(settings)) - return; - - Debug.Assert(apiRoom != null); - - var beatmap = beatmapSet.Beatmaps.Single(b => b.OnlineBeatmapID == settings.BeatmapID); - beatmap.MD5Hash = settings.BeatmapChecksum; - - var ruleset = Rulesets.GetRuleset(settings.RulesetID).CreateInstance(); - var mods = settings.RequiredMods.Select(m => m.ToMod(ruleset)); - var allowedMods = settings.AllowedMods.Select(m => m.ToMod(ruleset)); - - // Try to retrieve the existing playlist item from the API room. - var playlistItem = apiRoom.Playlist.FirstOrDefault(i => i.ID == settings.PlaylistItemId); - - if (playlistItem != null) - updateItem(playlistItem); - else - { - // An existing playlist item does not exist, so append a new one. - updateItem(playlistItem = new PlaylistItem()); - apiRoom.Playlist.Add(playlistItem); - } - - CurrentMatchPlayingItem.Value = playlistItem; - - void updateItem(PlaylistItem item) - { - item.ID = settings.PlaylistItemId; - item.Beatmap.Value = beatmap; - item.Ruleset.Value = ruleset.RulesetInfo; - item.RequiredMods.Clear(); - item.RequiredMods.AddRange(mods); - item.AllowedMods.Clear(); - item.AllowedMods.AddRange(allowedMods); - } - } - - /// - /// Retrieves a from an online source. - /// - /// The beatmap set ID. - /// A token to cancel the request. - /// The retrieval task. - protected abstract Task GetOnlineBeatmapSet(int beatmapId, CancellationToken cancellationToken = default); - - /// - /// For the provided user ID, update whether the user is included in . - /// - /// The user's ID. - /// The new state of the user. - private void updateUserPlayingState(int userId, MultiplayerUserState state) - { - bool wasPlaying = CurrentMatchPlayingUserIds.Contains(userId); - bool isPlaying = state >= MultiplayerUserState.WaitingForLoad && state <= MultiplayerUserState.FinishedPlay; - - if (isPlaying == wasPlaying) - return; - - if (isPlaying) - CurrentMatchPlayingUserIds.Add(userId); - else - CurrentMatchPlayingUserIds.Remove(userId); - } - - private Task scheduleAsync(Action action, CancellationToken cancellationToken = default) - { - var tcs = new TaskCompletionSource(); - - Scheduler.Add(() => - { - if (cancellationToken.IsCancellationRequested) - { - tcs.SetCanceled(); - return; - } - - try - { - action(); - tcs.SetResult(true); - } - catch (Exception ex) - { - tcs.SetException(ex); - } - }); - - return tcs.Task; - } - } -} diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index fbe4022cc1..656d6319b4 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -86,7 +86,7 @@ namespace osu.Game protected IAPIProvider API; private SpectatorStreamingClient spectatorStreaming; - private StatefulMultiplayerClient multiplayerClient; + private MultiplayerClient multiplayerClient; protected MenuCursorContainer MenuCursorContainer; @@ -241,7 +241,7 @@ namespace osu.Game dependencies.CacheAs(API ??= new APIAccess(LocalConfig, endpoints, VersionHash)); dependencies.CacheAs(spectatorStreaming = new SpectatorStreamingClient(endpoints)); - dependencies.CacheAs(multiplayerClient = new MultiplayerClient(endpoints)); + dependencies.CacheAs(multiplayerClient = new OnlineMultiplayerClient(endpoints)); var defaultBeatmap = new DummyWorkingBeatmap(Audio, Textures); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs index a13d2cf540..cc51b5b691 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs @@ -14,7 +14,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private IBindable operationInProgress; [Resolved] - private StatefulMultiplayerClient multiplayerClient { get; set; } + private MultiplayerClient multiplayerClient { get; set; } [Resolved] private OngoingOperationTracker ongoingOperationTracker { get; set; } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index 3199232f6f..fe9979b161 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -59,7 +59,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match private IRoomManager manager { get; set; } [Resolved] - private StatefulMultiplayerClient client { get; set; } + private MultiplayerClient client { get; set; } [Resolved] private Bindable currentRoom { get; set; } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs index 085c824bdc..a065d04f64 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs @@ -15,7 +15,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer public class Multiplayer : OnlinePlayScreen { [Resolved] - private StatefulMultiplayerClient client { get; set; } + private MultiplayerClient client { get; set; } public override void OnResuming(IScreen last) { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs index 0a9a3f680f..4d20652465 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs @@ -18,7 +18,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer protected override RoomSubScreen CreateRoomSubScreen(Room room) => new MultiplayerMatchSubScreen(room); [Resolved] - private StatefulMultiplayerClient client { get; set; } + private MultiplayerClient client { get; set; } public override void Open(Room room) { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs index c9f0f6de90..3733b85a5e 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs @@ -17,7 +17,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer public class MultiplayerMatchSongSelect : OnlinePlaySongSelect { [Resolved] - private StatefulMultiplayerClient client { get; set; } + private MultiplayerClient client { get; set; } private LoadingLayer loadingLayer; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 783b8b4bf2..62ef70ed68 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -43,7 +43,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer public override string ShortTitle => "room"; [Resolved] - private StatefulMultiplayerClient client { get; set; } + private MultiplayerClient client { get; set; } [Resolved] private OngoingOperationTracker ongoingOperationTracker { get; set; } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index ae2042fbe8..1bbe49a705 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -26,7 +26,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer protected override bool CheckModsAllowFailure() => false; [Resolved] - private StatefulMultiplayerClient client { get; set; } + private MultiplayerClient client { get; set; } private IBindable isConnected; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomComposite.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomComposite.cs index 8030107ad8..d334c618f5 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomComposite.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomComposite.cs @@ -13,7 +13,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer protected MultiplayerRoom Room => Client.Room; [Resolved] - protected StatefulMultiplayerClient Client { get; private set; } + protected MultiplayerClient Client { get; private set; } protected override void LoadComplete() { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs index 1e57847f04..8526196902 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs @@ -19,7 +19,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer public class MultiplayerRoomManager : RoomManager { [Resolved] - private StatefulMultiplayerClient multiplayerClient { get; set; } + private MultiplayerClient multiplayerClient { get; set; } public readonly Bindable TimeBetweenListingPolls = new Bindable(); public readonly Bindable TimeBetweenSelectionPolls = new Bindable(); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantsListHeader.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantsListHeader.cs index 6c1a55a0eb..7e442c6568 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantsListHeader.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantsListHeader.cs @@ -10,7 +10,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants public class ParticipantsListHeader : OverlinedHeader { [Resolved] - private StatefulMultiplayerClient client { get; set; } + private MultiplayerClient client { get; set; } public ParticipantsListHeader() : base("Participants") diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs index 8c7b7bab01..a0245a1e59 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs @@ -31,7 +31,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate private SpectatorStreamingClient spectatorClient { get; set; } [Resolved] - private StatefulMultiplayerClient multiplayerClient { get; set; } + private MultiplayerClient multiplayerClient { get; set; } private readonly PlayerArea[] instances; private MasterGameplayClockContainer masterClockContainer; diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index 70de067784..bbb3c5ebb2 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -25,7 +25,7 @@ namespace osu.Game.Screens.Play.HUD private SpectatorStreamingClient streamingClient { get; set; } [Resolved] - private StatefulMultiplayerClient multiplayerClient { get; set; } + private MultiplayerClient multiplayerClient { get; set; } [Resolved] private UserLookupCache userLookupCache { get; set; } diff --git a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs index db344b28dd..c76d1053b2 100644 --- a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs +++ b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs @@ -19,7 +19,7 @@ namespace osu.Game.Tests.Visual.Multiplayer public const int PLAYER_1_ID = 55; public const int PLAYER_2_ID = 56; - [Cached(typeof(StatefulMultiplayerClient))] + [Cached(typeof(MultiplayerClient))] public TestMultiplayerClient Client { get; } [Cached(typeof(IRoomManager))] diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 167cf705a7..b12bd8091d 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -20,7 +20,7 @@ using osu.Game.Users; namespace osu.Game.Tests.Visual.Multiplayer { - public class TestMultiplayerClient : StatefulMultiplayerClient + public class TestMultiplayerClient : MultiplayerClient { public override IBindable IsConnected => isConnected; private readonly Bindable isConnected = new Bindable(true); diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomContainer.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomContainer.cs index e57411d04d..1abf4d8f5d 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomContainer.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomContainer.cs @@ -16,7 +16,7 @@ namespace osu.Game.Tests.Visual.Multiplayer protected override Container Content => content; private readonly Container content; - [Cached(typeof(StatefulMultiplayerClient))] + [Cached(typeof(MultiplayerClient))] public readonly TestMultiplayerClient Client; [Cached(typeof(IRoomManager))] From 7980bdd384baef8e557f0f72523b878353b7dee8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 May 2021 15:41:58 +0900 Subject: [PATCH 318/839] Revert incorrectly changed requery code --- osu.Game/Beatmaps/BeatmapManager.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index dadc0624b3..5e975de77c 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -286,10 +286,11 @@ namespace osu.Game.Beatmaps { if (beatmapInfo?.ID > 0 && previous != null && previous.BeatmapInfo?.ID == beatmapInfo.ID) return previous; + if (beatmapInfo?.BeatmapSet == null || beatmapInfo == DefaultBeatmap?.BeatmapInfo) return DefaultBeatmap; - if (beatmapInfo.BeatmapSet.Files == null || beatmapInfo.ID == 0) + if (beatmapInfo.BeatmapSet.Files == null) { var info = beatmapInfo; beatmapInfo = QueryBeatmap(b => b.ID == info.ID); From 6beeb7f7c433d636e30cc46f56d03b585c71d647 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 20 May 2021 15:55:07 +0900 Subject: [PATCH 319/839] Rename SpectatorStreamingClient -> SpectatorClient --- .../Visual/Gameplay/TestSceneSpectator.cs | 14 ++++++------- .../Gameplay/TestSceneSpectatorPlayback.cs | 14 ++++++------- .../TestSceneMultiSpectatorLeaderboard.cs | 14 ++++++------- .../TestSceneMultiSpectatorScreen.cs | 20 +++++++++---------- ...TestSceneMultiplayerGameplayLeaderboard.cs | 18 ++++++++--------- .../TestSceneCurrentlyPlayingDisplay.cs | 14 ++++++------- ...rStreamingClient.cs => SpectatorClient.cs} | 6 +++--- osu.Game/OsuGameBase.cs | 6 +++--- .../Dashboard/CurrentlyPlayingDisplay.cs | 4 ++-- osu.Game/Rulesets/UI/ReplayRecorder.cs | 8 ++++---- .../Spectate/MultiSpectatorScreen.cs | 2 +- .../HUD/MultiplayerGameplayLeaderboard.cs | 14 ++++++------- osu.Game/Screens/Play/SpectatorPlayer.cs | 10 +++++----- .../Screens/Play/SpectatorResultsScreen.cs | 8 ++++---- osu.Game/Screens/Spectate/SpectatorScreen.cs | 2 +- ...eamingClient.cs => TestSpectatorClient.cs} | 4 ++-- 16 files changed, 79 insertions(+), 79 deletions(-) rename osu.Game/Online/Spectator/{SpectatorStreamingClient.cs => SpectatorClient.cs} (97%) rename osu.Game/Tests/Visual/Spectator/{TestSpectatorStreamingClient.cs => TestSpectatorClient.cs} (96%) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs index a7ed217b4d..56a4ab8cba 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs @@ -27,8 +27,8 @@ namespace osu.Game.Tests.Visual.Gameplay { private readonly User streamingUser = new User { Id = MultiplayerTestScene.PLAYER_1_ID, Username = "Test user" }; - [Cached(typeof(SpectatorStreamingClient))] - private TestSpectatorStreamingClient testSpectatorStreamingClient = new TestSpectatorStreamingClient(); + [Cached(typeof(SpectatorClient))] + private TestSpectatorClient testSpectatorClient = new TestSpectatorClient(); [Cached(typeof(UserLookupCache))] private UserLookupCache lookupCache = new TestUserLookupCache(); @@ -61,8 +61,8 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("add streaming client", () => { - Remove(testSpectatorStreamingClient); - Add(testSpectatorStreamingClient); + Remove(testSpectatorClient); + Add(testSpectatorClient); }); finish(); @@ -212,9 +212,9 @@ namespace osu.Game.Tests.Visual.Gameplay private void waitForPlayer() => AddUntilStep("wait for player", () => Stack.CurrentScreen is Player); - private void start(int? beatmapId = null) => AddStep("start play", () => testSpectatorStreamingClient.StartPlay(streamingUser.Id, beatmapId ?? importedBeatmapId)); + private void start(int? beatmapId = null) => AddStep("start play", () => testSpectatorClient.StartPlay(streamingUser.Id, beatmapId ?? importedBeatmapId)); - private void finish(int? beatmapId = null) => AddStep("end play", () => testSpectatorStreamingClient.EndPlay(streamingUser.Id, beatmapId ?? importedBeatmapId)); + private void finish(int? beatmapId = null) => AddStep("end play", () => testSpectatorClient.EndPlay(streamingUser.Id, beatmapId ?? importedBeatmapId)); private void checkPaused(bool state) => AddUntilStep($"game is {(state ? "paused" : "playing")}", () => player.ChildrenOfType().First().IsPaused.Value == state); @@ -223,7 +223,7 @@ namespace osu.Game.Tests.Visual.Gameplay { AddStep("send frames", () => { - testSpectatorStreamingClient.SendFrames(streamingUser.Id, nextFrame, count); + testSpectatorClient.SendFrames(streamingUser.Id, nextFrame, count); nextFrame += count; }); } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs index 9c763814f3..469f594fdc 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs @@ -59,7 +59,7 @@ namespace osu.Game.Tests.Visual.Gameplay private IAPIProvider api { get; set; } [Resolved] - private SpectatorStreamingClient streamingClient { get; set; } + private SpectatorClient spectatorClient { get; set; } [Cached] private GameplayBeatmap gameplayBeatmap = new GameplayBeatmap(new Beatmap()); @@ -69,7 +69,7 @@ namespace osu.Game.Tests.Visual.Gameplay { replay = new Replay(); - users.BindTo(streamingClient.PlayingUsers); + users.BindTo(spectatorClient.PlayingUsers); users.BindCollectionChanged((obj, args) => { switch (args.Action) @@ -80,7 +80,7 @@ namespace osu.Game.Tests.Visual.Gameplay foreach (int user in args.NewItems) { if (user == api.LocalUser.Value.Id) - streamingClient.WatchUser(user); + spectatorClient.WatchUser(user); } break; @@ -91,14 +91,14 @@ namespace osu.Game.Tests.Visual.Gameplay foreach (int user in args.OldItems) { if (user == api.LocalUser.Value.Id) - streamingClient.StopWatchingUser(user); + spectatorClient.StopWatchingUser(user); } break; } }, true); - streamingClient.OnNewFrames += onNewFrames; + spectatorClient.OnNewFrames += onNewFrames; Add(new GridContainer { @@ -189,7 +189,7 @@ namespace osu.Game.Tests.Visual.Gameplay { } - private double latency = SpectatorStreamingClient.TIME_BETWEEN_SENDS; + private double latency = SpectatorClient.TIME_BETWEEN_SENDS; protected override void Update() { @@ -233,7 +233,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("stop recorder", () => { recorder.Expire(); - streamingClient.OnNewFrames -= onNewFrames; + spectatorClient.OnNewFrames -= onNewFrames; }); } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.cs index 263adc07e1..afd4401a63 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.cs @@ -23,8 +23,8 @@ namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneMultiSpectatorLeaderboard : MultiplayerTestScene { - [Cached(typeof(SpectatorStreamingClient))] - private TestSpectatorStreamingClient streamingClient = new TestSpectatorStreamingClient(); + [Cached(typeof(SpectatorClient))] + private TestSpectatorClient spectatorClient = new TestSpectatorClient(); [Cached(typeof(UserLookupCache))] private UserLookupCache lookupCache = new TestUserLookupCache(); @@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { base.Content.AddRange(new Drawable[] { - streamingClient, + spectatorClient, lookupCache, content = new Container { RelativeSizeAxes = Axes.Both } }); @@ -59,7 +59,7 @@ namespace osu.Game.Tests.Visual.Multiplayer foreach (var (userId, clock) in clocks) { - streamingClient.EndPlay(userId, 0); + spectatorClient.EndPlay(userId, 0); clock.CurrentTime = 0; } }); @@ -67,7 +67,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("create leaderboard", () => { foreach (var (userId, _) in clocks) - streamingClient.StartPlay(userId, 0); + spectatorClient.StartPlay(userId, 0); Beatmap.Value = CreateWorkingBeatmap(Ruleset.Value); @@ -96,10 +96,10 @@ namespace osu.Game.Tests.Visual.Multiplayer // For player 2, send frames in sets of 10. for (int i = 0; i < 100; i++) { - streamingClient.SendFrames(PLAYER_1_ID, i, 1); + spectatorClient.SendFrames(PLAYER_1_ID, i, 1); if (i % 10 == 0) - streamingClient.SendFrames(PLAYER_2_ID, i, 10); + spectatorClient.SendFrames(PLAYER_2_ID, i, 10); } }); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs index 689c249d05..23095a1ea8 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs @@ -22,8 +22,8 @@ namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneMultiSpectatorScreen : MultiplayerTestScene { - [Cached(typeof(SpectatorStreamingClient))] - private TestSpectatorStreamingClient streamingClient = new TestSpectatorStreamingClient(); + [Cached(typeof(SpectatorClient))] + private TestSpectatorClient spectatorClient = new TestSpectatorClient(); [Cached(typeof(UserLookupCache))] private UserLookupCache lookupCache = new TestUserLookupCache(); @@ -59,14 +59,14 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("add streaming client", () => { - Remove(streamingClient); - Add(streamingClient); + Remove(spectatorClient); + Add(spectatorClient); }); AddStep("finish previous gameplay", () => { foreach (var id in playingUserIds) - streamingClient.EndPlay(id, importedBeatmapId); + spectatorClient.EndPlay(id, importedBeatmapId); playingUserIds.Clear(); }); } @@ -87,11 +87,11 @@ namespace osu.Game.Tests.Visual.Multiplayer loadSpectateScreen(false); AddWaitStep("wait a bit", 10); - AddStep("load player first_player_id", () => streamingClient.StartPlay(PLAYER_1_ID, importedBeatmapId)); + AddStep("load player first_player_id", () => spectatorClient.StartPlay(PLAYER_1_ID, importedBeatmapId)); AddUntilStep("one player added", () => spectatorScreen.ChildrenOfType().Count() == 1); AddWaitStep("wait a bit", 10); - AddStep("load player second_player_id", () => streamingClient.StartPlay(PLAYER_2_ID, importedBeatmapId)); + AddStep("load player second_player_id", () => spectatorClient.StartPlay(PLAYER_2_ID, importedBeatmapId)); AddUntilStep("two players added", () => spectatorScreen.ChildrenOfType().Count() == 2); } @@ -251,7 +251,7 @@ namespace osu.Game.Tests.Visual.Multiplayer foreach (int id in userIds) { Client.CurrentMatchPlayingUserIds.Add(id); - streamingClient.StartPlay(id, beatmapId ?? importedBeatmapId); + spectatorClient.StartPlay(id, beatmapId ?? importedBeatmapId); playingUserIds.Add(id); nextFrame[id] = 0; } @@ -262,7 +262,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("end play", () => { - streamingClient.EndPlay(userId, beatmapId ?? importedBeatmapId); + spectatorClient.EndPlay(userId, beatmapId ?? importedBeatmapId); playingUserIds.Remove(userId); nextFrame.Remove(userId); }); @@ -276,7 +276,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { foreach (int id in userIds) { - streamingClient.SendFrames(id, nextFrame[id], count); + spectatorClient.SendFrames(id, nextFrame[id], count); nextFrame[id] += count; } }); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs index 6813a6e7dd..80b9aa8228 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs @@ -28,8 +28,8 @@ namespace osu.Game.Tests.Visual.Multiplayer { private const int users = 16; - [Cached(typeof(SpectatorStreamingClient))] - private TestMultiplayerStreaming streamingClient = new TestMultiplayerStreaming(); + [Cached(typeof(SpectatorClient))] + private TestMultiplayerSpectatorClient spectatorClient = new TestMultiplayerSpectatorClient(); [Cached(typeof(UserLookupCache))] private UserLookupCache lookupCache = new TestSceneCurrentlyPlayingDisplay.TestUserLookupCache(); @@ -44,7 +44,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { base.Content.Children = new Drawable[] { - streamingClient, + spectatorClient, lookupCache, Content }; @@ -71,10 +71,10 @@ namespace osu.Game.Tests.Visual.Multiplayer var playable = Beatmap.Value.GetPlayableBeatmap(Ruleset.Value); for (int i = 0; i < users; i++) - streamingClient.StartPlay(i, Beatmap.Value.BeatmapInfo.OnlineBeatmapID ?? 0); + spectatorClient.StartPlay(i, Beatmap.Value.BeatmapInfo.OnlineBeatmapID ?? 0); Client.CurrentMatchPlayingUserIds.Clear(); - Client.CurrentMatchPlayingUserIds.AddRange(streamingClient.PlayingUsers); + Client.CurrentMatchPlayingUserIds.AddRange(spectatorClient.PlayingUsers); Children = new Drawable[] { @@ -83,7 +83,7 @@ namespace osu.Game.Tests.Visual.Multiplayer scoreProcessor.ApplyBeatmap(playable); - LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(scoreProcessor, streamingClient.PlayingUsers.ToArray()) + LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(scoreProcessor, spectatorClient.PlayingUsers.ToArray()) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -96,7 +96,7 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestScoreUpdates() { - AddRepeatStep("update state", () => streamingClient.RandomlyUpdateState(), 100); + AddRepeatStep("update state", () => spectatorClient.RandomlyUpdateState(), 100); AddToggleStep("switch compact mode", expanded => leaderboard.Expanded.Value = expanded); } @@ -109,12 +109,12 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestChangeScoringMode() { - AddRepeatStep("update state", () => streamingClient.RandomlyUpdateState(), 5); + AddRepeatStep("update state", () => spectatorClient.RandomlyUpdateState(), 5); AddStep("change to classic", () => config.SetValue(OsuSetting.ScoreDisplayMode, ScoringMode.Classic)); AddStep("change to standardised", () => config.SetValue(OsuSetting.ScoreDisplayMode, ScoringMode.Standardised)); } - public class TestMultiplayerStreaming : TestSpectatorStreamingClient + public class TestMultiplayerSpectatorClient : TestSpectatorClient { private readonly Dictionary lastHeaders = new Dictionary(); diff --git a/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs index 8ae6398003..9bc0b32eee 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs @@ -19,8 +19,8 @@ namespace osu.Game.Tests.Visual.Online { public class TestSceneCurrentlyPlayingDisplay : OsuTestScene { - [Cached(typeof(SpectatorStreamingClient))] - private TestSpectatorStreamingClient testSpectatorStreamingClient = new TestSpectatorStreamingClient(); + [Cached(typeof(SpectatorClient))] + private TestSpectatorClient testSpectatorClient = new TestSpectatorClient(); private CurrentlyPlayingDisplay currentlyPlaying; @@ -34,7 +34,7 @@ namespace osu.Game.Tests.Visual.Online { AddStep("add streaming client", () => { - nestedContainer?.Remove(testSpectatorStreamingClient); + nestedContainer?.Remove(testSpectatorClient); Remove(lookupCache); Children = new Drawable[] @@ -45,7 +45,7 @@ namespace osu.Game.Tests.Visual.Online RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - testSpectatorStreamingClient, + testSpectatorClient, currentlyPlaying = new CurrentlyPlayingDisplay { RelativeSizeAxes = Axes.Both, @@ -55,15 +55,15 @@ namespace osu.Game.Tests.Visual.Online }; }); - AddStep("Reset players", () => testSpectatorStreamingClient.PlayingUsers.Clear()); + AddStep("Reset players", () => testSpectatorClient.PlayingUsers.Clear()); } [Test] public void TestBasicDisplay() { - AddStep("Add playing user", () => testSpectatorStreamingClient.PlayingUsers.Add(2)); + AddStep("Add playing user", () => testSpectatorClient.PlayingUsers.Add(2)); AddUntilStep("Panel loaded", () => currentlyPlaying.ChildrenOfType()?.FirstOrDefault()?.User.Id == 2); - AddStep("Remove playing user", () => testSpectatorStreamingClient.PlayingUsers.Remove(2)); + AddStep("Remove playing user", () => testSpectatorClient.PlayingUsers.Remove(2)); AddUntilStep("Panel no longer present", () => !currentlyPlaying.ChildrenOfType().Any()); } diff --git a/osu.Game/Online/Spectator/SpectatorStreamingClient.cs b/osu.Game/Online/Spectator/SpectatorClient.cs similarity index 97% rename from osu.Game/Online/Spectator/SpectatorStreamingClient.cs rename to osu.Game/Online/Spectator/SpectatorClient.cs index ec6d1bf9d8..43115d577c 100644 --- a/osu.Game/Online/Spectator/SpectatorStreamingClient.cs +++ b/osu.Game/Online/Spectator/SpectatorClient.cs @@ -23,7 +23,7 @@ using osu.Game.Screens.Play; namespace osu.Game.Online.Spectator { - public class SpectatorStreamingClient : Component, ISpectatorClient + public class SpectatorClient : Component, ISpectatorClient { /// /// The maximum milliseconds between frame bundle sends. @@ -80,7 +80,7 @@ namespace osu.Game.Online.Spectator /// public event Action OnUserFinishedPlaying; - public SpectatorStreamingClient(EndpointConfiguration endpoints) + public SpectatorClient(EndpointConfiguration endpoints) { endpoint = endpoints.SpectatorEndpointUrl; } @@ -88,7 +88,7 @@ namespace osu.Game.Online.Spectator [BackgroundDependencyLoader] private void load(IAPIProvider api) { - connector = api.GetHubConnector(nameof(SpectatorStreamingClient), endpoint); + connector = api.GetHubConnector(nameof(SpectatorClient), endpoint); if (connector != null) { diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index fbe4022cc1..3707e3b7be 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -85,7 +85,7 @@ namespace osu.Game protected IAPIProvider API; - private SpectatorStreamingClient spectatorStreaming; + private SpectatorClient spectatorClient; private StatefulMultiplayerClient multiplayerClient; protected MenuCursorContainer MenuCursorContainer; @@ -240,7 +240,7 @@ namespace osu.Game dependencies.CacheAs(API ??= new APIAccess(LocalConfig, endpoints, VersionHash)); - dependencies.CacheAs(spectatorStreaming = new SpectatorStreamingClient(endpoints)); + dependencies.CacheAs(spectatorClient = new SpectatorClient(endpoints)); dependencies.CacheAs(multiplayerClient = new MultiplayerClient(endpoints)); var defaultBeatmap = new DummyWorkingBeatmap(Audio, Textures); @@ -313,7 +313,7 @@ namespace osu.Game // add api components to hierarchy. if (API is APIAccess apiAccess) AddInternal(apiAccess); - AddInternal(spectatorStreaming); + AddInternal(spectatorClient); AddInternal(multiplayerClient); AddInternal(RulesetConfigCache); diff --git a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs index 336430fd9b..3051ca7dbe 100644 --- a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs +++ b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs @@ -25,7 +25,7 @@ namespace osu.Game.Overlays.Dashboard private FillFlowContainer userFlow; [Resolved] - private SpectatorStreamingClient spectatorStreaming { get; set; } + private SpectatorClient spectatorClient { get; set; } [BackgroundDependencyLoader] private void load() @@ -52,7 +52,7 @@ namespace osu.Game.Overlays.Dashboard { base.LoadComplete(); - playingUsers.BindTo(spectatorStreaming.PlayingUsers); + playingUsers.BindTo(spectatorClient.PlayingUsers); playingUsers.BindCollectionChanged(onUsersChanged, true); } diff --git a/osu.Game/Rulesets/UI/ReplayRecorder.cs b/osu.Game/Rulesets/UI/ReplayRecorder.cs index 643ded4cad..d18e0f9541 100644 --- a/osu.Game/Rulesets/UI/ReplayRecorder.cs +++ b/osu.Game/Rulesets/UI/ReplayRecorder.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.UI public int RecordFrameRate = 60; [Resolved(canBeNull: true)] - private SpectatorStreamingClient spectatorStreaming { get; set; } + private SpectatorClient spectatorClient { get; set; } [Resolved] private GameplayBeatmap gameplayBeatmap { get; set; } @@ -49,13 +49,13 @@ namespace osu.Game.Rulesets.UI inputManager = GetContainingInputManager(); - spectatorStreaming?.BeginPlaying(gameplayBeatmap, target); + spectatorClient?.BeginPlaying(gameplayBeatmap, target); } protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); - spectatorStreaming?.EndPlaying(); + spectatorClient?.EndPlaying(); } protected override void Update() @@ -98,7 +98,7 @@ namespace osu.Game.Rulesets.UI { target.Replay.Frames.Add(frame); - spectatorStreaming?.HandleFrame(frame); + spectatorClient?.HandleFrame(frame); } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs index 8c7b7bab01..3ffaeb772a 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs @@ -28,7 +28,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate public bool AllPlayersLoaded => instances.All(p => p?.PlayerLoaded == true); [Resolved] - private SpectatorStreamingClient spectatorClient { get; set; } + private SpectatorClient spectatorClient { get; set; } [Resolved] private StatefulMultiplayerClient multiplayerClient { get; set; } diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index 70de067784..7f59a836c2 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -22,7 +22,7 @@ namespace osu.Game.Screens.Play.HUD protected readonly Dictionary UserScores = new Dictionary(); [Resolved] - private SpectatorStreamingClient streamingClient { get; set; } + private SpectatorClient spectatorClient { get; set; } [Resolved] private StatefulMultiplayerClient multiplayerClient { get; set; } @@ -55,7 +55,7 @@ namespace osu.Game.Screens.Play.HUD foreach (var userId in playingUsers) { - streamingClient.WatchUser(userId); + spectatorClient.WatchUser(userId); // probably won't be required in the final implementation. var resolvedUser = userLookupCache.GetUserAsync(userId).Result; @@ -88,7 +88,7 @@ namespace osu.Game.Screens.Play.HUD playingUsers.BindCollectionChanged(usersChanged); // this leaderboard should be guaranteed to be completely loaded before the gameplay starts (is a prerequisite in MultiplayerPlayer). - streamingClient.OnNewFrames += handleIncomingFrames; + spectatorClient.OnNewFrames += handleIncomingFrames; } private void usersChanged(object sender, NotifyCollectionChangedEventArgs e) @@ -98,7 +98,7 @@ namespace osu.Game.Screens.Play.HUD case NotifyCollectionChangedAction.Remove: foreach (var userId in e.OldItems.OfType()) { - streamingClient.StopWatchingUser(userId); + spectatorClient.StopWatchingUser(userId); if (UserScores.TryGetValue(userId, out var trackedData)) trackedData.MarkUserQuit(); @@ -123,14 +123,14 @@ namespace osu.Game.Screens.Play.HUD { base.Dispose(isDisposing); - if (streamingClient != null) + if (spectatorClient != null) { foreach (var user in playingUsers) { - streamingClient.StopWatchingUser(user); + spectatorClient.StopWatchingUser(user); } - streamingClient.OnNewFrames -= handleIncomingFrames; + spectatorClient.OnNewFrames -= handleIncomingFrames; } } diff --git a/osu.Game/Screens/Play/SpectatorPlayer.cs b/osu.Game/Screens/Play/SpectatorPlayer.cs index 9822f62dd8..a8125dfded 100644 --- a/osu.Game/Screens/Play/SpectatorPlayer.cs +++ b/osu.Game/Screens/Play/SpectatorPlayer.cs @@ -31,12 +31,12 @@ namespace osu.Game.Screens.Play } [Resolved] - private SpectatorStreamingClient spectatorStreaming { get; set; } + private SpectatorClient spectatorClient { get; set; } [BackgroundDependencyLoader] private void load() { - spectatorStreaming.OnUserBeganPlaying += userBeganPlaying; + spectatorClient.OnUserBeganPlaying += userBeganPlaying; AddInternal(new OsuSpriteText { @@ -66,7 +66,7 @@ namespace osu.Game.Screens.Play public override bool OnExiting(IScreen next) { - spectatorStreaming.OnUserBeganPlaying -= userBeganPlaying; + spectatorClient.OnUserBeganPlaying -= userBeganPlaying; return base.OnExiting(next); } @@ -84,8 +84,8 @@ namespace osu.Game.Screens.Play { base.Dispose(isDisposing); - if (spectatorStreaming != null) - spectatorStreaming.OnUserBeganPlaying -= userBeganPlaying; + if (spectatorClient != null) + spectatorClient.OnUserBeganPlaying -= userBeganPlaying; } } } diff --git a/osu.Game/Screens/Play/SpectatorResultsScreen.cs b/osu.Game/Screens/Play/SpectatorResultsScreen.cs index dabdf0a139..fd7af3af85 100644 --- a/osu.Game/Screens/Play/SpectatorResultsScreen.cs +++ b/osu.Game/Screens/Play/SpectatorResultsScreen.cs @@ -17,12 +17,12 @@ namespace osu.Game.Screens.Play } [Resolved] - private SpectatorStreamingClient spectatorStreaming { get; set; } + private SpectatorClient spectatorClient { get; set; } [BackgroundDependencyLoader] private void load() { - spectatorStreaming.OnUserBeganPlaying += userBeganPlaying; + spectatorClient.OnUserBeganPlaying += userBeganPlaying; } private void userBeganPlaying(int userId, SpectatorState state) @@ -40,8 +40,8 @@ namespace osu.Game.Screens.Play { base.Dispose(isDisposing); - if (spectatorStreaming != null) - spectatorStreaming.OnUserBeganPlaying -= userBeganPlaying; + if (spectatorClient != null) + spectatorClient.OnUserBeganPlaying -= userBeganPlaying; } } } diff --git a/osu.Game/Screens/Spectate/SpectatorScreen.cs b/osu.Game/Screens/Spectate/SpectatorScreen.cs index bcebd51954..1cf7bc30ee 100644 --- a/osu.Game/Screens/Spectate/SpectatorScreen.cs +++ b/osu.Game/Screens/Spectate/SpectatorScreen.cs @@ -37,7 +37,7 @@ namespace osu.Game.Screens.Spectate private RulesetStore rulesets { get; set; } [Resolved] - private SpectatorStreamingClient spectatorClient { get; set; } + private SpectatorClient spectatorClient { get; set; } [Resolved] private UserLookupCache userLookupCache { get; set; } diff --git a/osu.Game/Tests/Visual/Spectator/TestSpectatorStreamingClient.cs b/osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs similarity index 96% rename from osu.Game/Tests/Visual/Spectator/TestSpectatorStreamingClient.cs rename to osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs index cc8437479d..985e293981 100644 --- a/osu.Game/Tests/Visual/Spectator/TestSpectatorStreamingClient.cs +++ b/osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs @@ -12,7 +12,7 @@ using osu.Game.Scoring; namespace osu.Game.Tests.Visual.Spectator { - public class TestSpectatorStreamingClient : SpectatorStreamingClient + public class TestSpectatorClient : SpectatorClient { public new BindableList PlayingUsers => (BindableList)base.PlayingUsers; private readonly ConcurrentDictionary watchingUsers = new ConcurrentDictionary(); @@ -20,7 +20,7 @@ namespace osu.Game.Tests.Visual.Spectator private readonly Dictionary userBeatmapDictionary = new Dictionary(); private readonly Dictionary userSentStateDictionary = new Dictionary(); - public TestSpectatorStreamingClient() + public TestSpectatorClient() : base(new DevelopmentEndpointConfiguration()) { } From 1578b0462d10a9c63d1f378f080c07fa27cf43a0 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 20 May 2021 13:58:17 +0700 Subject: [PATCH 320/839] add showParentPage method --- osu.Game/Overlays/WikiOverlay.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs index 366923a4f1..1efa84d026 100644 --- a/osu.Game/Overlays/WikiOverlay.cs +++ b/osu.Game/Overlays/WikiOverlay.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 System.Threading; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -62,6 +63,12 @@ namespace osu.Game.Overlays }); } + private void showParentPage() + { + var parentPath = string.Join("/", path.Value.Split('/').SkipLast(1)); + ShowPage(parentPath); + } + public void ShowPage(string pagePath = index_path) { path.Value = pagePath.Trim('/'); From 9c824ece1b068c91d84cab4fe6508336090ce5c9 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 20 May 2021 14:00:22 +0700 Subject: [PATCH 321/839] handle page change when clicking breadcrumb --- osu.Game/Overlays/Wiki/WikiHeader.cs | 22 +++++++++++++++++++++- osu.Game/Overlays/WikiOverlay.cs | 6 +++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Wiki/WikiHeader.cs b/osu.Game/Overlays/Wiki/WikiHeader.cs index f5010d82b6..6b8cba48b4 100644 --- a/osu.Game/Overlays/Wiki/WikiHeader.cs +++ b/osu.Game/Overlays/Wiki/WikiHeader.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.Bindables; using osu.Framework.Graphics; using osu.Game.Online.API.Requests.Responses; @@ -12,7 +14,10 @@ namespace osu.Game.Overlays.Wiki private const string index_page_string = "index"; private const string index_path = "Main_Page"; - public Bindable WikiPageData = new Bindable(); + public readonly Bindable WikiPageData = new Bindable(); + + public Action ShowIndexPage; + public Action ShowParentPage; public WikiHeader() { @@ -20,6 +25,7 @@ namespace osu.Game.Overlays.Wiki Current.Value = index_page_string; WikiPageData.BindValueChanged(onWikiPageChange); + Current.BindValueChanged(onCurrentChange); } private void onWikiPageChange(ValueChangedEvent e) @@ -45,6 +51,20 @@ namespace osu.Game.Overlays.Wiki Current.Value = e.NewValue.Title; } + private void onCurrentChange(ValueChangedEvent e) + { + if (e.NewValue == TabControl.Items.LastOrDefault()) + return; + + if (e.NewValue == index_page_string) + { + ShowIndexPage?.Invoke(); + return; + } + + ShowParentPage?.Invoke(); + } + protected override Drawable CreateBackground() => new OverlayHeaderBackground(@"Headers/wiki"); protected override OverlayTitle CreateTitle() => new WikiHeaderTitle(); diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs index 1efa84d026..5505eeac68 100644 --- a/osu.Game/Overlays/WikiOverlay.cs +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -75,7 +75,11 @@ namespace osu.Game.Overlays Show(); } - protected override WikiHeader CreateHeader() => new WikiHeader(); + protected override WikiHeader CreateHeader() => new WikiHeader + { + ShowIndexPage = () => ShowPage(), + ShowParentPage = showParentPage, + }; protected override void LoadComplete() { From 3018a41ab5daf24a61671cc1684cc8f089049e8a Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 20 May 2021 16:00:49 +0900 Subject: [PATCH 322/839] Remove redundant string interpolation --- osu.Game.Tests/Gameplay/TestSceneProxyContainer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Gameplay/TestSceneProxyContainer.cs b/osu.Game.Tests/Gameplay/TestSceneProxyContainer.cs index 9975c65085..1264d575a4 100644 --- a/osu.Game.Tests/Gameplay/TestSceneProxyContainer.cs +++ b/osu.Game.Tests/Gameplay/TestSceneProxyContainer.cs @@ -46,11 +46,11 @@ namespace osu.Game.Tests.Gameplay addProxy(new TestDrawableHitObject(5000)); }); - AddStep($"time = 1000", () => clock.CurrentTime = 1000); + AddStep("time = 1000", () => clock.CurrentTime = 1000); AddAssert("One proxy is alive", () => proxyContainer.AliveChildren.Count == 1); - AddStep($"time = 5000", () => clock.CurrentTime = 5000); + AddStep("time = 5000", () => clock.CurrentTime = 5000); AddAssert("One proxy is alive", () => proxyContainer.AliveChildren.Count == 1); - AddStep($"time = 6000", () => clock.CurrentTime = 6000); + AddStep("time = 6000", () => clock.CurrentTime = 6000); AddAssert("No proxy is alive", () => proxyContainer.AliveChildren.Count == 0); } From 11099702780ab3f7366781aad49aa940eb7261d1 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 20 May 2021 14:24:28 +0700 Subject: [PATCH 323/839] add more test for Wiki Header --- .../Visual/Online/TestSceneWikiHeader.cs | 83 ++++++++++++++++++- 1 file changed, 80 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs index 51d8abc516..863fa48ddf 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs @@ -1,8 +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 System.Collections.Generic; +using System.Linq; +using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; using osu.Game.Overlays.Wiki; @@ -13,13 +18,85 @@ namespace osu.Game.Tests.Visual.Online [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Orange); - public TestSceneWikiHeader() + [Cached] + private readonly Bindable wikiPageData = new Bindable(new APIWikiPage { - Child = new WikiHeader + Title = "Main Page", + Path = "Main_Page", + }); + + private TestHeader header; + + [SetUp] + public void SetUp() => Schedule(() => + { + Child = header = new TestHeader { Anchor = Anchor.Centre, - Origin = Anchor.Centre + Origin = Anchor.Centre, + ShowIndexPage = dummyShowIndexPage, + ShowParentPage = dummyShowParentPage, }; + wikiPageData.BindTo(header.WikiPageData); + }); + + [Test] + public void TestWikiHeader() + { + AddAssert("Current is index", () => checkCurrent("index")); + + AddStep("Change wiki page data", () => wikiPageData.Value = new APIWikiPage + { + Title = "Welcome", + Path = "Welcome" + }); + AddAssert("Current is welcome", () => checkCurrent("Welcome")); + AddAssert("Check breadcrumb", checkBreadcrumb); + + AddStep("Change current to index", () => header.Current.Value = "index"); + AddAssert("Current is index", () => checkCurrent("index")); + + AddStep("Change wiki page data", () => wikiPageData.Value = new APIWikiPage + { + Title = "Developers", + Path = "People/The_Team/Developers", + Subtitle = "The Team", + }); + AddAssert("Current is 'Developers'", () => checkCurrent("Developers")); + AddAssert("Check breadcrumb", checkBreadcrumb); + + AddStep("Change current to 'The Team'", () => header.Current.Value = "The Team"); + AddAssert("Current is 'The Team'", () => checkCurrent("The Team")); + AddAssert("Check breadcrumb", checkBreadcrumb); + } + + private bool checkCurrent(string expectedCurrent) => header.Current.Value == expectedCurrent; + + private bool checkBreadcrumb() + { + var result = header.TabControlItems.Contains(wikiPageData.Value.Title); + + if (wikiPageData.Value.Subtitle != null) + result = header.TabControlItems.Contains(wikiPageData.Value.Subtitle) && result; + + return result; + } + + private void dummyShowIndexPage() => wikiPageData.SetDefault(); + + private void dummyShowParentPage() + { + wikiPageData.Value = new APIWikiPage + { + Path = "People/The_Team", + Title = "The Team", + Subtitle = "People" + }; + } + + private class TestHeader : WikiHeader + { + public IReadOnlyList TabControlItems => TabControl.Items; } } } From df80531a0a8d6a687b3875628732f67712c12682 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 20 May 2021 16:30:56 +0900 Subject: [PATCH 324/839] Split online connectivity into OnlineSpectatorClient --- .../Online/Spectator/OnlineSpectatorClient.cs | 89 +++++++++++ osu.Game/Online/Spectator/SpectatorClient.cs | 140 +++++++----------- osu.Game/OsuGameBase.cs | 2 +- 3 files changed, 142 insertions(+), 89 deletions(-) create mode 100644 osu.Game/Online/Spectator/OnlineSpectatorClient.cs diff --git a/osu.Game/Online/Spectator/OnlineSpectatorClient.cs b/osu.Game/Online/Spectator/OnlineSpectatorClient.cs new file mode 100644 index 0000000000..753796158e --- /dev/null +++ b/osu.Game/Online/Spectator/OnlineSpectatorClient.cs @@ -0,0 +1,89 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +using System.Threading.Tasks; +using Microsoft.AspNetCore.SignalR.Client; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Game.Online.API; + +namespace osu.Game.Online.Spectator +{ + public class OnlineSpectatorClient : SpectatorClient + { + private readonly string endpoint; + + private IHubClientConnector? connector; + + public override IBindable IsConnected { get; } = new BindableBool(); + + private HubConnection? connection => connector?.CurrentConnection; + + public OnlineSpectatorClient(EndpointConfiguration endpoints) + { + endpoint = endpoints.SpectatorEndpointUrl; + } + + [BackgroundDependencyLoader] + private void load(IAPIProvider api) + { + connector = api.GetHubConnector(nameof(SpectatorClient), endpoint); + + if (connector != null) + { + connector.ConfigureConnection = connection => + { + // until strong typed client support is added, each method must be manually bound + // (see https://github.com/dotnet/aspnetcore/issues/15198) + connection.On(nameof(ISpectatorClient.UserBeganPlaying), ((ISpectatorClient)this).UserBeganPlaying); + connection.On(nameof(ISpectatorClient.UserSentFrames), ((ISpectatorClient)this).UserSentFrames); + connection.On(nameof(ISpectatorClient.UserFinishedPlaying), ((ISpectatorClient)this).UserFinishedPlaying); + }; + + IsConnected.BindTo(connector.IsConnected); + } + } + + protected override Task BeginPlayingInternal(SpectatorState state) + { + if (!IsConnected.Value) + return Task.CompletedTask; + + return connection.SendAsync(nameof(ISpectatorServer.BeginPlaySession), state); + } + + protected override Task SendFramesInternal(FrameDataBundle data) + { + if (!IsConnected.Value) + return Task.CompletedTask; + + return connection.SendAsync(nameof(ISpectatorServer.SendFrameData), data); + } + + protected override Task EndPlayingInternal(SpectatorState state) + { + if (!IsConnected.Value) + return Task.CompletedTask; + + return connection.SendAsync(nameof(ISpectatorServer.EndPlaySession), state); + } + + protected override Task WatchUserInternal(int userId) + { + if (!IsConnected.Value) + return Task.CompletedTask; + + return connection.SendAsync(nameof(ISpectatorServer.StartWatchingUser), userId); + } + + protected override Task StopWatchingUserInternal(int userId) + { + if (!IsConnected.Value) + return Task.CompletedTask; + + return connection.SendAsync(nameof(ISpectatorServer.EndWatchingUser), userId); + } + } +} diff --git a/osu.Game/Online/Spectator/SpectatorClient.cs b/osu.Game/Online/Spectator/SpectatorClient.cs index 43115d577c..5ea31a49fb 100644 --- a/osu.Game/Online/Spectator/SpectatorClient.cs +++ b/osu.Game/Online/Spectator/SpectatorClient.cs @@ -7,7 +7,6 @@ using System.Diagnostics; using System.Linq; using System.Threading.Tasks; using JetBrains.Annotations; -using Microsoft.AspNetCore.SignalR.Client; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -23,21 +22,18 @@ using osu.Game.Screens.Play; namespace osu.Game.Online.Spectator { - public class SpectatorClient : Component, ISpectatorClient + public abstract class SpectatorClient : Component, ISpectatorClient { /// /// The maximum milliseconds between frame bundle sends. /// public const double TIME_BETWEEN_SENDS = 200; - private readonly string endpoint; - - [CanBeNull] - private IHubClientConnector connector; - - private readonly IBindable isConnected = new BindableBool(); - - private HubConnection connection => connector?.CurrentConnection; + /// + /// Whether the is currently connected. + /// This is NOT thread safe and usage should be scheduled. + /// + public abstract IBindable IsConnected { get; } private readonly List watchingUsers = new List(); @@ -63,7 +59,7 @@ namespace osu.Game.Online.Spectator private readonly SpectatorState currentState = new SpectatorState(); - private bool isPlaying; + protected bool IsPlaying { get; private set; } /// /// Called whenever new frames arrive from the server. @@ -80,59 +76,39 @@ namespace osu.Game.Online.Spectator /// public event Action OnUserFinishedPlaying; - public SpectatorClient(EndpointConfiguration endpoints) - { - endpoint = endpoints.SpectatorEndpointUrl; - } - [BackgroundDependencyLoader] - private void load(IAPIProvider api) + private void load() { - connector = api.GetHubConnector(nameof(SpectatorClient), endpoint); - - if (connector != null) + IsConnected.BindValueChanged(connected => { - connector.ConfigureConnection = connection => + if (connected.NewValue) { - // until strong typed client support is added, each method must be manually bound - // (see https://github.com/dotnet/aspnetcore/issues/15198) - connection.On(nameof(ISpectatorClient.UserBeganPlaying), ((ISpectatorClient)this).UserBeganPlaying); - connection.On(nameof(ISpectatorClient.UserSentFrames), ((ISpectatorClient)this).UserSentFrames); - connection.On(nameof(ISpectatorClient.UserFinishedPlaying), ((ISpectatorClient)this).UserFinishedPlaying); - }; + // get all the users that were previously being watched + int[] users; - isConnected.BindTo(connector.IsConnected); - isConnected.BindValueChanged(connected => + lock (userLock) + { + users = watchingUsers.ToArray(); + watchingUsers.Clear(); + } + + // resubscribe to watched users. + foreach (var userId in users) + WatchUser(userId); + + // re-send state in case it wasn't received + if (IsPlaying) + BeginPlayingInternal(currentState); + } + else { - if (connected.NewValue) + lock (userLock) { - // get all the users that were previously being watched - int[] users; - - lock (userLock) - { - users = watchingUsers.ToArray(); - watchingUsers.Clear(); - } - - // resubscribe to watched users. - foreach (var userId in users) - WatchUser(userId); - - // re-send state in case it wasn't received - if (isPlaying) - beginPlaying(); + playingUsers.Clear(); + playingUserStates.Clear(); } - else - { - lock (userLock) - { - playingUsers.Clear(); - playingUserStates.Clear(); - } - } - }, true); - } + } + }, true); } Task ISpectatorClient.UserBeganPlaying(int userId, SpectatorState state) @@ -176,10 +152,10 @@ namespace osu.Game.Online.Spectator public void BeginPlaying(GameplayBeatmap beatmap, Score score) { - if (isPlaying) + if (IsPlaying) throw new InvalidOperationException($"Cannot invoke {nameof(BeginPlaying)} when already playing"); - isPlaying = true; + IsPlaying = true; // transfer state at point of beginning play currentState.BeatmapID = beatmap.BeatmapInfo.OnlineBeatmapID; @@ -189,36 +165,20 @@ namespace osu.Game.Online.Spectator currentBeatmap = beatmap.PlayableBeatmap; currentScore = score; - beginPlaying(); + BeginPlayingInternal(currentState); } - private void beginPlaying() - { - Debug.Assert(isPlaying); - - if (!isConnected.Value) return; - - connection.SendAsync(nameof(ISpectatorServer.BeginPlaySession), currentState); - } - - public void SendFrames(FrameDataBundle data) - { - if (!isConnected.Value) return; - - lastSend = connection.SendAsync(nameof(ISpectatorServer.SendFrameData), data); - } + public void SendFrames(FrameDataBundle data) => lastSend = SendFramesInternal(data); public void EndPlaying() { - isPlaying = false; + IsPlaying = false; currentBeatmap = null; - if (!isConnected.Value) return; - - connection.SendAsync(nameof(ISpectatorServer.EndPlaySession), currentState); + EndPlayingInternal(currentState); } - public virtual void WatchUser(int userId) + public void WatchUser(int userId) { lock (userLock) { @@ -226,27 +186,31 @@ namespace osu.Game.Online.Spectator return; watchingUsers.Add(userId); - - if (!isConnected.Value) - return; } - connection.SendAsync(nameof(ISpectatorServer.StartWatchingUser), userId); + WatchUserInternal(userId); } - public virtual void StopWatchingUser(int userId) + public void StopWatchingUser(int userId) { lock (userLock) { watchingUsers.Remove(userId); - - if (!isConnected.Value) - return; } - connection.SendAsync(nameof(ISpectatorServer.EndWatchingUser), userId); + StopWatchingUserInternal(userId); } + protected abstract Task BeginPlayingInternal(SpectatorState state); + + protected abstract Task SendFramesInternal(FrameDataBundle data); + + protected abstract Task EndPlayingInternal(SpectatorState state); + + protected abstract Task WatchUserInternal(int userId); + + protected abstract Task StopWatchingUserInternal(int userId); + private readonly Queue pendingFrames = new Queue(); private double lastSendTime; diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 3707e3b7be..41984839ab 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -240,7 +240,7 @@ namespace osu.Game dependencies.CacheAs(API ??= new APIAccess(LocalConfig, endpoints, VersionHash)); - dependencies.CacheAs(spectatorClient = new SpectatorClient(endpoints)); + dependencies.CacheAs(spectatorClient = new OnlineSpectatorClient(endpoints)); dependencies.CacheAs(multiplayerClient = new MultiplayerClient(endpoints)); var defaultBeatmap = new DummyWorkingBeatmap(Audio, Textures); From fdbd421040efcbd9718d95841034471f930e6291 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 May 2021 16:01:14 +0900 Subject: [PATCH 325/839] Fix editor tests failing due to empty files being specified --- .../Editing/TestSceneEditorBeatmapCreation.cs | 2 + osu.Game/Beatmaps/BeatmapManager.cs | 7 ++- osu.Game/Tests/Beatmaps/TestBeatmap.cs | 1 - osu.Game/Tests/Visual/EditorTestScene.cs | 43 ++++++++++++++++++- 4 files changed, 48 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs index 7584c74c71..dd5e01adbb 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs @@ -24,6 +24,8 @@ namespace osu.Game.Tests.Visual.Editing protected override bool EditorComponentsReady => Editor.ChildrenOfType().SingleOrDefault()?.IsLoaded == true; + protected override bool IsolateSavingFromDatabase => false; + [Resolved] private BeatmapManager beatmapManager { get; set; } diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 5e975de77c..d5dc70317c 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Linq.Expressions; @@ -240,10 +241,12 @@ namespace osu.Game.Beatmaps /// The to save the content against. The file referenced by will be replaced. /// The content to write. /// The beatmap content to write, null if to be omitted. - public void Save(BeatmapInfo info, IBeatmap beatmapContent, ISkin beatmapSkin = null) + public virtual void Save(BeatmapInfo info, IBeatmap beatmapContent, ISkin beatmapSkin = null) { var setInfo = info.BeatmapSet; + Debug.Assert(setInfo.Files != null); + using (var stream = new MemoryStream()) { using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) @@ -282,7 +285,7 @@ namespace osu.Game.Beatmaps /// The beatmap to lookup. /// The currently loaded . Allows for optimisation where elements are shared with the new beatmap. May be returned if beatmapInfo requested matches /// A instance correlating to the provided . - public WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo, WorkingBeatmap previous = null) + public virtual WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo, WorkingBeatmap previous = null) { if (beatmapInfo?.ID > 0 && previous != null && previous.BeatmapInfo?.ID == beatmapInfo.ID) return previous; diff --git a/osu.Game/Tests/Beatmaps/TestBeatmap.cs b/osu.Game/Tests/Beatmaps/TestBeatmap.cs index fa6dc5647d..2717146c99 100644 --- a/osu.Game/Tests/Beatmaps/TestBeatmap.cs +++ b/osu.Game/Tests/Beatmaps/TestBeatmap.cs @@ -29,7 +29,6 @@ namespace osu.Game.Tests.Beatmaps BeatmapInfo.Ruleset = ruleset; BeatmapInfo.RulesetID = ruleset.ID ?? 0; BeatmapInfo.BeatmapSet.Metadata = BeatmapInfo.Metadata; - BeatmapInfo.BeatmapSet.Files = new List(); BeatmapInfo.BeatmapSet.Beatmaps = new List { BeatmapInfo }; BeatmapInfo.Length = 75000; BeatmapInfo.OnlineInfo = new BeatmapOnlineInfo(); diff --git a/osu.Game/Tests/Visual/EditorTestScene.cs b/osu.Game/Tests/Visual/EditorTestScene.cs index a9ee8e2668..de7fdd0cf8 100644 --- a/osu.Game/Tests/Visual/EditorTestScene.cs +++ b/osu.Game/Tests/Visual/EditorTestScene.cs @@ -4,11 +4,18 @@ using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Platform; using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Database; +using osu.Game.IO.Archives; +using osu.Game.Online.API; using osu.Game.Rulesets; using osu.Game.Rulesets.Edit; using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Compose.Components.Timeline; +using osu.Game.Skinning; namespace osu.Game.Tests.Visual { @@ -20,10 +27,20 @@ namespace osu.Game.Tests.Visual protected EditorClock EditorClock { get; private set; } + /// + /// Whether any saves performed by the editor should be isolate (and not persist) to the underlying . + /// + protected virtual bool IsolateSavingFromDatabase => true; + [BackgroundDependencyLoader] - private void load() + private void load(GameHost host, AudioManager audio, RulesetStore rulesets) { - Beatmap.Value = CreateWorkingBeatmap(Ruleset.Value); + var working = CreateWorkingBeatmap(Ruleset.Value); + + Beatmap.Value = working; + + if (IsolateSavingFromDatabase) + Dependencies.CacheAs(new TestBeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default, working)); } protected virtual bool EditorComponentsReady => Editor.ChildrenOfType().FirstOrDefault()?.IsLoaded == true @@ -65,5 +82,27 @@ namespace osu.Game.Tests.Visual public new bool HasUnsavedChanges => base.HasUnsavedChanges; } + + private class TestBeatmapManager : BeatmapManager + { + private readonly WorkingBeatmap testBeatmap; + + public TestBeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, GameHost host, WorkingBeatmap defaultBeatmap, WorkingBeatmap testBeatmap) + : base(storage, contextFactory, rulesets, api, audioManager, host, defaultBeatmap, false) + { + this.testBeatmap = testBeatmap; + } + + protected override string ComputeHash(BeatmapSetInfo item, ArchiveReader reader = null) + => string.Empty; + + public override WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo, WorkingBeatmap previous = null) + => testBeatmap; + + public override void Save(BeatmapInfo info, IBeatmap beatmapContent, ISkin beatmapSkin = null) + { + // don't actually care about saving for this context. + } + } } } From 750a5c3ea96530fae27195fd5afb0fe89ff4dd0c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 20 May 2021 17:20:30 +0900 Subject: [PATCH 326/839] Fix test compilation error --- .../Visual/Spectator/TestSpectatorClient.cs | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs b/osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs index 985e293981..6bd9f1a920 100644 --- a/osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs +++ b/osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs @@ -1,11 +1,15 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + using System.Collections.Concurrent; using System.Collections.Generic; +using System.Threading.Tasks; +using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Utils; -using osu.Game.Online; +using osu.Game.Online.API; using osu.Game.Online.Spectator; using osu.Game.Replays.Legacy; using osu.Game.Scoring; @@ -14,16 +18,16 @@ namespace osu.Game.Tests.Visual.Spectator { public class TestSpectatorClient : SpectatorClient { + public override IBindable IsConnected { get; } = new Bindable(true); + public new BindableList PlayingUsers => (BindableList)base.PlayingUsers; private readonly ConcurrentDictionary watchingUsers = new ConcurrentDictionary(); private readonly Dictionary userBeatmapDictionary = new Dictionary(); private readonly Dictionary userSentStateDictionary = new Dictionary(); - public TestSpectatorClient() - : base(new DevelopmentEndpointConfiguration()) - { - } + [Resolved] + private IAPIProvider api { get; set; } = null!; public void StartPlay(int userId, int beatmapId) { @@ -61,19 +65,25 @@ namespace osu.Game.Tests.Visual.Spectator sendState(userId, userBeatmapDictionary[userId]); } - public override void WatchUser(int userId) - { - base.WatchUser(userId); + protected override Task BeginPlayingInternal(SpectatorState state) => ((ISpectatorClient)this).UserBeganPlaying(api.LocalUser.Value.Id, state); + protected override Task SendFramesInternal(FrameDataBundle data) => ((ISpectatorClient)this).UserSentFrames(api.LocalUser.Value.Id, data); + + protected override Task EndPlayingInternal(SpectatorState state) => ((ISpectatorClient)this).UserFinishedPlaying(api.LocalUser.Value.Id, state); + + protected override Task WatchUserInternal(int userId) + { // When newly watching a user, the server sends the playing state immediately. if (watchingUsers.TryAdd(userId, 0) && PlayingUsers.Contains(userId)) sendState(userId, userBeatmapDictionary[userId]); + + return Task.CompletedTask; } - public override void StopWatchingUser(int userId) + protected override Task StopWatchingUserInternal(int userId) { - base.StopWatchingUser(userId); watchingUsers.TryRemove(userId, out _); + return Task.CompletedTask; } private void sendState(int userId, int beatmapId) From c24712642cc567f158f2d4a7b559429567347d8b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 May 2021 17:16:38 +0900 Subject: [PATCH 327/839] Make `BeatmapSetInfo.Files` non-nullable --- osu.Game/Beatmaps/BeatmapManager.cs | 22 ++++++++++------------ osu.Game/Beatmaps/BeatmapSetInfo.cs | 10 ++++++---- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index d5dc70317c..18fbd1f5c2 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; using System.Linq.Expressions; @@ -245,8 +244,6 @@ namespace osu.Game.Beatmaps { var setInfo = info.BeatmapSet; - Debug.Assert(setInfo.Files != null); - using (var stream = new MemoryStream()) { using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) @@ -287,20 +284,21 @@ namespace osu.Game.Beatmaps /// A instance correlating to the provided . public virtual WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo, WorkingBeatmap previous = null) { - if (beatmapInfo?.ID > 0 && previous != null && previous.BeatmapInfo?.ID == beatmapInfo.ID) - return previous; - - if (beatmapInfo?.BeatmapSet == null || beatmapInfo == DefaultBeatmap?.BeatmapInfo) + if (beatmapInfo?.BeatmapSet == null) return DefaultBeatmap; - if (beatmapInfo.BeatmapSet.Files == null) + if (beatmapInfo == DefaultBeatmap?.BeatmapInfo) + return DefaultBeatmap; + + // if there are no files, presume the full beatmap info has not yet been fetched from the database. + if (beatmapInfo.BeatmapSet.Files.Count == 0) { - var info = beatmapInfo; - beatmapInfo = QueryBeatmap(b => b.ID == info.ID); + int lookupId = beatmapInfo.ID; + beatmapInfo = QueryBeatmap(b => b.ID == lookupId); } - if (beatmapInfo == null) - return DefaultBeatmap; + if (beatmapInfo.ID > 0 && previous != null && previous.BeatmapInfo?.ID == beatmapInfo.ID) + return previous; lock (workingCache) { diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index 1ce42535a0..3b1ff4ced0 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Testing; using osu.Game.Database; @@ -31,6 +32,9 @@ namespace osu.Game.Beatmaps public List Beatmaps { get; set; } + [NotNull] + public List Files { get; set; } = new List(); + [NotMapped] public BeatmapSetOnlineInfo OnlineInfo { get; set; } @@ -57,16 +61,14 @@ namespace osu.Game.Beatmaps public string Hash { get; set; } - public string StoryboardFile => Files?.Find(f => f.Filename.EndsWith(".osb", StringComparison.OrdinalIgnoreCase))?.Filename; + public string StoryboardFile => Files.Find(f => f.Filename.EndsWith(".osb", StringComparison.OrdinalIgnoreCase))?.Filename; /// /// Returns the storage path for the file in this beatmapset with the given filename, if any exists, otherwise null. /// The path returned is relative to the user file storage. /// /// The name of the file to get the storage path of. - public string GetPathForFile(string filename) => Files?.SingleOrDefault(f => string.Equals(f.Filename, filename, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath; - - public List Files { get; set; } + public string GetPathForFile(string filename) => Files.SingleOrDefault(f => string.Equals(f.Filename, filename, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath; public override string ToString() => Metadata?.ToString() ?? base.ToString(); From 9d07749959aa346be7106918e9411999e1cd21d9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 20 May 2021 17:41:46 +0900 Subject: [PATCH 328/839] Improve implementation of TestSpectatorClient There was a lot of weirdness here, such as storing the playing users, clearing the playing users from test scenes (!!), and storing the users being wathed. This was all a thing because the previous implementation overrode the base method implementations, which is no longer a thing. --- .../Visual/Gameplay/TestSceneSpectator.cs | 2 +- .../TestSceneMultiSpectatorLeaderboard.cs | 2 +- .../TestSceneMultiSpectatorScreen.cs | 6 +- .../TestSceneCurrentlyPlayingDisplay.cs | 8 ++- .../Visual/Spectator/TestSpectatorClient.cs | 62 +++++++++++-------- 5 files changed, 46 insertions(+), 34 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs index 56a4ab8cba..e9894ff469 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs @@ -214,7 +214,7 @@ namespace osu.Game.Tests.Visual.Gameplay private void start(int? beatmapId = null) => AddStep("start play", () => testSpectatorClient.StartPlay(streamingUser.Id, beatmapId ?? importedBeatmapId)); - private void finish(int? beatmapId = null) => AddStep("end play", () => testSpectatorClient.EndPlay(streamingUser.Id, beatmapId ?? importedBeatmapId)); + private void finish() => AddStep("end play", () => testSpectatorClient.EndPlay(streamingUser.Id)); private void checkPaused(bool state) => AddUntilStep($"game is {(state ? "paused" : "playing")}", () => player.ChildrenOfType().First().IsPaused.Value == state); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.cs index afd4401a63..5ad35be0ec 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.cs @@ -59,7 +59,7 @@ namespace osu.Game.Tests.Visual.Multiplayer foreach (var (userId, clock) in clocks) { - spectatorClient.EndPlay(userId, 0); + spectatorClient.EndPlay(userId); clock.CurrentTime = 0; } }); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs index 23095a1ea8..b91391c409 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs @@ -66,7 +66,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("finish previous gameplay", () => { foreach (var id in playingUserIds) - spectatorClient.EndPlay(id, importedBeatmapId); + spectatorClient.EndPlay(id); playingUserIds.Clear(); }); } @@ -258,11 +258,11 @@ namespace osu.Game.Tests.Visual.Multiplayer }); } - private void finish(int userId, int? beatmapId = null) + private void finish(int userId) { AddStep("end play", () => { - spectatorClient.EndPlay(userId, beatmapId ?? importedBeatmapId); + spectatorClient.EndPlay(userId); playingUserIds.Remove(userId); nextFrame.Remove(userId); }); diff --git a/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs index 9bc0b32eee..30785fd163 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs @@ -19,6 +19,8 @@ namespace osu.Game.Tests.Visual.Online { public class TestSceneCurrentlyPlayingDisplay : OsuTestScene { + private readonly User streamingUser = new User { Id = 2, Username = "Test user" }; + [Cached(typeof(SpectatorClient))] private TestSpectatorClient testSpectatorClient = new TestSpectatorClient(); @@ -55,15 +57,15 @@ namespace osu.Game.Tests.Visual.Online }; }); - AddStep("Reset players", () => testSpectatorClient.PlayingUsers.Clear()); + AddStep("Reset players", () => testSpectatorClient.EndPlay(streamingUser.Id)); } [Test] public void TestBasicDisplay() { - AddStep("Add playing user", () => testSpectatorClient.PlayingUsers.Add(2)); + AddStep("Add playing user", () => testSpectatorClient.StartPlay(streamingUser.Id, 0)); AddUntilStep("Panel loaded", () => currentlyPlaying.ChildrenOfType()?.FirstOrDefault()?.User.Id == 2); - AddStep("Remove playing user", () => testSpectatorClient.PlayingUsers.Remove(2)); + AddStep("Remove playing user", () => testSpectatorClient.EndPlay(streamingUser.Id)); AddUntilStep("Panel no longer present", () => !currentlyPlaying.ChildrenOfType().Any()); } diff --git a/osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs b/osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs index 6bd9f1a920..3a5ffa8770 100644 --- a/osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs +++ b/osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs @@ -3,8 +3,9 @@ #nullable enable -using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -20,33 +21,44 @@ namespace osu.Game.Tests.Visual.Spectator { public override IBindable IsConnected { get; } = new Bindable(true); - public new BindableList PlayingUsers => (BindableList)base.PlayingUsers; - private readonly ConcurrentDictionary watchingUsers = new ConcurrentDictionary(); - private readonly Dictionary userBeatmapDictionary = new Dictionary(); - private readonly Dictionary userSentStateDictionary = new Dictionary(); [Resolved] private IAPIProvider api { get; set; } = null!; + /// + /// Starts play for an arbitrary user. + /// + /// The user to start play for. + /// The playing beatmap id. public void StartPlay(int userId, int beatmapId) { userBeatmapDictionary[userId] = beatmapId; - sendState(userId, beatmapId); + sendPlayingState(userId); } - public void EndPlay(int userId, int beatmapId) + /// + /// Ends play for an arbitrary user. + /// + /// The user to end play for. + public void EndPlay(int userId) { + if (!PlayingUsers.Contains(userId)) + return; + ((ISpectatorClient)this).UserFinishedPlaying(userId, new SpectatorState { - BeatmapID = beatmapId, + BeatmapID = userBeatmapDictionary[userId], RulesetID = 0, }); - - userBeatmapDictionary.Remove(userId); - userSentStateDictionary.Remove(userId); } + /// + /// Sends frames for an arbitrary user. + /// + /// The user to send frames for. + /// The frame index. + /// The number of frames to send. public void SendFrames(int userId, int index, int count) { var frames = new List(); @@ -60,12 +72,16 @@ namespace osu.Game.Tests.Visual.Spectator var bundle = new FrameDataBundle(new ScoreInfo { Combo = index + count }, frames); ((ISpectatorClient)this).UserSentFrames(userId, bundle); - - if (!userSentStateDictionary[userId]) - sendState(userId, userBeatmapDictionary[userId]); } - protected override Task BeginPlayingInternal(SpectatorState state) => ((ISpectatorClient)this).UserBeganPlaying(api.LocalUser.Value.Id, state); + protected override Task BeginPlayingInternal(SpectatorState state) + { + // Track the local user's playing beatmap ID. + Debug.Assert(state.BeatmapID != null); + userBeatmapDictionary[api.LocalUser.Value.Id] = state.BeatmapID.Value; + + return ((ISpectatorClient)this).UserBeganPlaying(api.LocalUser.Value.Id, state); + } protected override Task SendFramesInternal(FrameDataBundle data) => ((ISpectatorClient)this).UserSentFrames(api.LocalUser.Value.Id, data); @@ -74,27 +90,21 @@ namespace osu.Game.Tests.Visual.Spectator protected override Task WatchUserInternal(int userId) { // When newly watching a user, the server sends the playing state immediately. - if (watchingUsers.TryAdd(userId, 0) && PlayingUsers.Contains(userId)) - sendState(userId, userBeatmapDictionary[userId]); + if (PlayingUsers.Contains(userId)) + sendPlayingState(userId); return Task.CompletedTask; } - protected override Task StopWatchingUserInternal(int userId) - { - watchingUsers.TryRemove(userId, out _); - return Task.CompletedTask; - } + protected override Task StopWatchingUserInternal(int userId) => Task.CompletedTask; - private void sendState(int userId, int beatmapId) + private void sendPlayingState(int userId) { ((ISpectatorClient)this).UserBeganPlaying(userId, new SpectatorState { - BeatmapID = beatmapId, + BeatmapID = userBeatmapDictionary[userId], RulesetID = 0, }); - - userSentStateDictionary[userId] = true; } } } From 6eff8d513e6c34b3efd8c88bdf17ea698a977943 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 20 May 2021 17:51:09 +0900 Subject: [PATCH 329/839] Annotate nullables --- osu.Game/Online/Spectator/SpectatorClient.cs | 24 +++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/osu.Game/Online/Spectator/SpectatorClient.cs b/osu.Game/Online/Spectator/SpectatorClient.cs index 5ea31a49fb..cb98b01bed 100644 --- a/osu.Game/Online/Spectator/SpectatorClient.cs +++ b/osu.Game/Online/Spectator/SpectatorClient.cs @@ -1,12 +1,13 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -45,36 +46,37 @@ namespace osu.Game.Online.Spectator private readonly Dictionary playingUserStates = new Dictionary(); - [CanBeNull] - private IBeatmap currentBeatmap; + private IBeatmap? currentBeatmap; - [CanBeNull] - private Score currentScore; + private Score? currentScore; [Resolved] - private IBindable currentRuleset { get; set; } + private IBindable currentRuleset { get; set; } = null!; [Resolved] - private IBindable> currentMods { get; set; } + private IBindable> currentMods { get; set; } = null!; private readonly SpectatorState currentState = new SpectatorState(); + /// + /// Whether the local user is playing. + /// protected bool IsPlaying { get; private set; } /// /// Called whenever new frames arrive from the server. /// - public event Action OnNewFrames; + public event Action? OnNewFrames; /// /// Called whenever a user starts a play session, or immediately if the user is being watched and currently in a play session. /// - public event Action OnUserBeganPlaying; + public event Action? OnUserBeganPlaying; /// /// Called whenever a user finishes a play session. /// - public event Action OnUserFinishedPlaying; + public event Action? OnUserFinishedPlaying; [BackgroundDependencyLoader] private void load() @@ -215,7 +217,7 @@ namespace osu.Game.Online.Spectator private double lastSendTime; - private Task lastSend; + private Task? lastSend; private const int max_pending_frames = 30; From 27e81d6504aff75e4e0673496c878c8fb0698ac8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 May 2021 18:21:16 +0900 Subject: [PATCH 330/839] Implement proper rotation algorithm for skin editor --- .../Edit/OsuSelectionHandler.cs | 30 ++-------------- .../Compose/Components/SelectionHandler.cs | 24 +++++++++++++ .../Skinning/Editor/SkinSelectionHandler.cs | 34 ++++++++++++++++--- 3 files changed, 57 insertions(+), 31 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index 8cb86bc108..57d0cd859d 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Graphics; @@ -12,7 +11,7 @@ using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Screens.Edit.Compose.Components; -using Vector2 = osuTK.Vector2; +using osuTK; namespace osu.Game.Rulesets.Osu.Edit { @@ -173,12 +172,12 @@ namespace osu.Game.Rulesets.Osu.Edit foreach (var h in hitObjects) { - h.Position = rotatePointAroundOrigin(h.Position, referenceOrigin.Value, delta); + h.Position = RotatePointAroundOrigin(h.Position, referenceOrigin.Value, delta); if (h is IHasPath path) { foreach (var point in path.Path.ControlPoints) - point.Position.Value = rotatePointAroundOrigin(point.Position.Value, Vector2.Zero, delta); + point.Position.Value = RotatePointAroundOrigin(point.Position.Value, Vector2.Zero, delta); } } @@ -324,28 +323,5 @@ namespace osu.Game.Rulesets.Osu.Edit private OsuHitObject[] selectedMovableObjects => SelectedItems.OfType() .Where(h => !(h is Spinner)) .ToArray(); - - /// - /// Rotate a point around an arbitrary origin. - /// - /// The point. - /// The centre origin to rotate around. - /// The angle to rotate (in degrees). - private static Vector2 rotatePointAroundOrigin(Vector2 point, Vector2 origin, float angle) - { - angle = -angle; - - point.X -= origin.X; - point.Y -= origin.Y; - - Vector2 ret; - ret.X = point.X * MathF.Cos(MathUtils.DegreesToRadians(angle)) + point.Y * MathF.Sin(MathUtils.DegreesToRadians(angle)); - ret.Y = point.X * -MathF.Sin(MathUtils.DegreesToRadians(angle)) + point.Y * MathF.Cos(MathUtils.DegreesToRadians(angle)); - - ret.X += origin.X; - ret.Y += origin.Y; - - return ret; - } } } diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index 26328b4dc7..8939be925a 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -14,6 +14,7 @@ using osu.Framework.Graphics.UserInterface; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Framework.Utils; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Edit; @@ -353,6 +354,29 @@ namespace osu.Game.Screens.Edit.Compose.Components #region Helper Methods + /// + /// Rotate a point around an arbitrary origin. + /// + /// The point. + /// The centre origin to rotate around. + /// The angle to rotate (in degrees). + protected static Vector2 RotatePointAroundOrigin(Vector2 point, Vector2 origin, float angle) + { + angle = -angle; + + point.X -= origin.X; + point.Y -= origin.Y; + + Vector2 ret; + ret.X = point.X * MathF.Cos(MathUtils.DegreesToRadians(angle)) + point.Y * MathF.Sin(MathUtils.DegreesToRadians(angle)); + ret.Y = point.X * -MathF.Sin(MathUtils.DegreesToRadians(angle)) + point.Y * MathF.Cos(MathUtils.DegreesToRadians(angle)); + + ret.X += origin.X; + ret.Y += origin.Y; + + return ret; + } + /// /// Given a flip direction, a surrounding quad for all selected objects, and a position, /// will return the flipped position in screen space coordinates. diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index 2f9611ba65..cc4c120e23 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -18,16 +18,42 @@ namespace osu.Game.Skinning.Editor { public class SkinSelectionHandler : SelectionHandler { + private Vector2? referenceOrigin; + [Resolved] private SkinEditor skinEditor { get; set; } + protected override void OnOperationEnded() + { + base.OnOperationEnded(); + referenceOrigin = null; + } + public override bool HandleRotation(float angle) { - // TODO: this doesn't correctly account for origin/anchor specs being different in a multi-selection. - foreach (var c in SelectedBlueprints) - ((Drawable)c.Item).Rotation += angle; + if (SelectedBlueprints.Count == 1) + { + // for single items, rotate around the origin rather than the selection centre. + ((Drawable)SelectedBlueprints.First().Item).Rotation += angle; + } + else + { + var selectionQuad = GetSurroundingQuad(SelectedBlueprints.SelectMany(b => + b.Item.ScreenSpaceDrawQuad.GetVertices().ToArray())); - return base.HandleRotation(angle); + referenceOrigin ??= selectionQuad.Centre; + + foreach (var b in SelectedBlueprints) + { + var drawableItem = (Drawable)b.Item; + + drawableItem.Position = drawableItem.Parent.ToLocalSpace(RotatePointAroundOrigin(b.ScreenSpaceSelectionPoint, referenceOrigin.Value, angle)) - drawableItem.AnchorPosition; + drawableItem.Rotation += angle; + } + } + + // this isn't always the case but let's be lenient for now. + return true; } public override bool HandleScale(Vector2 scale, Anchor anchor) From 95c78b918510e8c4ebbc68a95496e0956a676278 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 May 2021 18:24:25 +0900 Subject: [PATCH 331/839] Split out common selection quad logic --- osu.Game/Skinning/Editor/SkinSelectionHandler.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index cc4c120e23..ec45830f94 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -7,6 +7,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.UserInterface; using osu.Game.Extensions; using osu.Game.Graphics.UserInterface; @@ -38,8 +39,7 @@ namespace osu.Game.Skinning.Editor } else { - var selectionQuad = GetSurroundingQuad(SelectedBlueprints.SelectMany(b => - b.Item.ScreenSpaceDrawQuad.GetVertices().ToArray())); + var selectionQuad = getSelectionQuad(); referenceOrigin ??= selectionQuad.Centre; @@ -63,8 +63,7 @@ namespace osu.Game.Skinning.Editor adjustScaleFromAnchor(ref scale, anchor); - var selectionQuad = GetSurroundingQuad(SelectedBlueprints.SelectMany(b => - b.Item.ScreenSpaceDrawQuad.GetVertices().ToArray())); + var selectionQuad = getSelectionQuad(); // the selection quad is always upright, so use a rect to make mutating the values easier. var adjustedRect = selectionQuad.AABBFloat; @@ -220,6 +219,13 @@ namespace osu.Game.Skinning.Editor } } + /// + /// A screen-space quad surrounding all selected drawables, accounting for their full displayed size. + /// + /// + private Quad getSelectionQuad() => + GetSurroundingQuad(SelectedBlueprints.SelectMany(b => b.Item.ScreenSpaceDrawQuad.GetVertices().ToArray())); + private void applyAnchor(Anchor anchor) { foreach (var item in SelectedItems) From 6f75c59760e3bca0110e772c4e48a65addd496a7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 May 2021 18:31:51 +0900 Subject: [PATCH 332/839] Fix flip logic not using the full selection quad --- osu.Game/Skinning/Editor/SkinSelectionHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index ec45830f94..88372e23a3 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -125,7 +125,7 @@ namespace osu.Game.Skinning.Editor public override bool HandleFlip(Direction direction) { - var selectionQuad = GetSurroundingQuad(SelectedBlueprints.Select(b => b.ScreenSpaceSelectionPoint)); + var selectionQuad = getSelectionQuad(); foreach (var b in SelectedBlueprints) { From 20f1ef43180b20c8ee310c62a262f0f06bf3b5c3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 May 2021 18:35:13 +0900 Subject: [PATCH 333/839] Extract common implementation of updating drawable position from screen space pos --- osu.Game/Skinning/Editor/SkinSelectionHandler.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index 88372e23a3..bdb1d1c054 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -47,7 +47,8 @@ namespace osu.Game.Skinning.Editor { var drawableItem = (Drawable)b.Item; - drawableItem.Position = drawableItem.Parent.ToLocalSpace(RotatePointAroundOrigin(b.ScreenSpaceSelectionPoint, referenceOrigin.Value, angle)) - drawableItem.AnchorPosition; + var rotatedPosition = RotatePointAroundOrigin(b.ScreenSpaceSelectionPoint, referenceOrigin.Value, angle); + updateDrawablePosition(drawableItem, rotatedPosition); drawableItem.Rotation += angle; } } @@ -116,7 +117,7 @@ namespace osu.Game.Skinning.Editor adjustedRect.TopLeft.Y + adjustedRect.Height * relativePositionInOriginal.Y ); - drawableItem.Position = drawableItem.Parent.ToLocalSpace(newPositionInAdjusted) - drawableItem.AnchorPosition; + updateDrawablePosition(drawableItem, newPositionInAdjusted); drawableItem.Scale *= scaledDelta; } @@ -131,8 +132,9 @@ namespace osu.Game.Skinning.Editor { var drawableItem = (Drawable)b.Item; - drawableItem.Position = - drawableItem.Parent.ToLocalSpace(GetFlippedPosition(direction, selectionQuad, b.ScreenSpaceSelectionPoint)) - drawableItem.AnchorPosition; + var flippedPosition = GetFlippedPosition(direction, selectionQuad, b.ScreenSpaceSelectionPoint); + + updateDrawablePosition(drawableItem, flippedPosition); drawableItem.Scale *= new Vector2( direction == Direction.Horizontal ? -1 : 1, @@ -207,6 +209,12 @@ namespace osu.Game.Skinning.Editor } } + private static void updateDrawablePosition(Drawable drawable, Vector2 screenSpacePosition) + { + drawable.Position = + drawable.Parent.ToLocalSpace(screenSpacePosition) - drawable.AnchorPosition; + } + private void applyOrigin(Anchor anchor) { foreach (var item in SelectedItems) From 10597f7e6a508de8db6aade5faf6664c22f26d46 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 20 May 2021 18:37:27 +0900 Subject: [PATCH 334/839] Remove locking from SpectatorClient --- osu.Game/Online/Spectator/SpectatorClient.cs | 66 +++++++------------- 1 file changed, 23 insertions(+), 43 deletions(-) diff --git a/osu.Game/Online/Spectator/SpectatorClient.cs b/osu.Game/Online/Spectator/SpectatorClient.cs index cb98b01bed..f930328846 100644 --- a/osu.Game/Online/Spectator/SpectatorClient.cs +++ b/osu.Game/Online/Spectator/SpectatorClient.cs @@ -38,8 +38,6 @@ namespace osu.Game.Online.Spectator private readonly List watchingUsers = new List(); - private readonly object userLock = new object(); - public IBindableList PlayingUsers => playingUsers; private readonly BindableList playingUsers = new BindableList(); @@ -81,18 +79,13 @@ namespace osu.Game.Online.Spectator [BackgroundDependencyLoader] private void load() { - IsConnected.BindValueChanged(connected => + IsConnected.BindValueChanged(connected => Schedule(() => { if (connected.NewValue) { // get all the users that were previously being watched - int[] users; - - lock (userLock) - { - users = watchingUsers.ToArray(); - watchingUsers.Clear(); - } + int[] users = watchingUsers.ToArray(); + watchingUsers.Clear(); // resubscribe to watched users. foreach (var userId in users) @@ -104,18 +97,15 @@ namespace osu.Game.Online.Spectator } else { - lock (userLock) - { - playingUsers.Clear(); - playingUserStates.Clear(); - } + playingUsers.Clear(); + playingUserStates.Clear(); } - }, true); + }), true); } Task ISpectatorClient.UserBeganPlaying(int userId, SpectatorState state) { - lock (userLock) + Schedule(() => { if (!playingUsers.Contains(userId)) playingUsers.Add(userId); @@ -125,29 +115,29 @@ namespace osu.Game.Online.Spectator // We don't want the user states to update unless the player is being watched, otherwise calling BindUserBeganPlaying() can lead to double invocations. if (watchingUsers.Contains(userId)) playingUserStates[userId] = state; - } - OnUserBeganPlaying?.Invoke(userId, state); + OnUserBeganPlaying?.Invoke(userId, state); + }); return Task.CompletedTask; } Task ISpectatorClient.UserFinishedPlaying(int userId, SpectatorState state) { - lock (userLock) + Schedule(() => { playingUsers.Remove(userId); playingUserStates.Remove(userId); - } - OnUserFinishedPlaying?.Invoke(userId, state); + OnUserFinishedPlaying?.Invoke(userId, state); + }); return Task.CompletedTask; } Task ISpectatorClient.UserSentFrames(int userId, FrameDataBundle data) { - OnNewFrames?.Invoke(userId, data); + Schedule(() => OnNewFrames?.Invoke(userId, data)); return Task.CompletedTask; } @@ -182,23 +172,17 @@ namespace osu.Game.Online.Spectator public void WatchUser(int userId) { - lock (userLock) - { - if (watchingUsers.Contains(userId)) - return; + if (watchingUsers.Contains(userId)) + return; - watchingUsers.Add(userId); - } + watchingUsers.Add(userId); WatchUserInternal(userId); } public void StopWatchingUser(int userId) { - lock (userLock) - { - watchingUsers.Remove(userId); - } + watchingUsers.Remove(userId); StopWatchingUserInternal(userId); } @@ -262,8 +246,7 @@ namespace osu.Game.Online.Spectator /// true if successful (the user is playing), false otherwise. public bool TryGetPlayingUserState(int userId, out SpectatorState state) { - lock (userLock) - return playingUserStates.TryGetValue(userId, out state); + return playingUserStates.TryGetValue(userId, out state); } /// @@ -274,16 +257,13 @@ namespace osu.Game.Online.Spectator public void BindUserBeganPlaying(Action callback, bool runOnceImmediately = false) { // The lock is taken before the event is subscribed to to prevent doubling of events. - lock (userLock) - { - OnUserBeganPlaying += callback; + OnUserBeganPlaying += callback; - if (!runOnceImmediately) - return; + if (!runOnceImmediately) + return; - foreach (var (userId, state) in playingUserStates) - callback(userId, state); - } + foreach (var (userId, state) in playingUserStates) + callback(userId, state); } } } From f74dbb9e1f2abfafca3bfe4499c5488dc6198c49 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 20 May 2021 18:52:20 +0900 Subject: [PATCH 335/839] Remove locking from SpectatorScreen --- osu.Game/Screens/Spectate/SpectatorScreen.cs | 181 ++++++++----------- 1 file changed, 78 insertions(+), 103 deletions(-) diff --git a/osu.Game/Screens/Spectate/SpectatorScreen.cs b/osu.Game/Screens/Spectate/SpectatorScreen.cs index 1cf7bc30ee..e6c9a0acd4 100644 --- a/osu.Game/Screens/Spectate/SpectatorScreen.cs +++ b/osu.Game/Screens/Spectate/SpectatorScreen.cs @@ -42,9 +42,6 @@ namespace osu.Game.Screens.Spectate [Resolved] private UserLookupCache userLookupCache { get; set; } - // A lock is used to synchronise access to spectator/gameplay states, since this class is a screen which may become non-current and stop receiving updates at any point. - private readonly object stateLock = new object(); - private readonly Dictionary userMap = new Dictionary(); private readonly Dictionary gameplayStates = new Dictionary(); @@ -63,8 +60,11 @@ namespace osu.Game.Screens.Spectate { base.LoadComplete(); - populateAllUsers().ContinueWith(_ => Schedule(() => + getAllUsers().ContinueWith(users => Schedule(() => { + foreach (var u in users.Result) + userMap[u.Id] = u; + spectatorClient.BindUserBeganPlaying(userBeganPlaying, true); spectatorClient.OnUserFinishedPlaying += userFinishedPlaying; spectatorClient.OnNewFrames += userSentFrames; @@ -72,27 +72,23 @@ namespace osu.Game.Screens.Spectate managerUpdated = beatmaps.ItemUpdated.GetBoundCopy(); managerUpdated.BindValueChanged(beatmapUpdated); - lock (stateLock) - { - foreach (var (id, _) in userMap) - spectatorClient.WatchUser(id); - } + foreach (var (id, _) in userMap) + spectatorClient.WatchUser(id); })); } - private Task populateAllUsers() + private Task getAllUsers() { - var userLookupTasks = new List(); + var userLookupTasks = new List>(); foreach (var u in userIds) { userLookupTasks.Add(userLookupCache.GetUserAsync(u).ContinueWith(task => { if (!task.IsCompletedSuccessfully) - return; + return null; - lock (stateLock) - userMap[u] = task.Result; + return task.Result; })); } @@ -104,16 +100,13 @@ namespace osu.Game.Screens.Spectate if (!e.NewValue.TryGetTarget(out var beatmapSet)) return; - lock (stateLock) + foreach (var (userId, _) in userMap) { - foreach (var (userId, _) in userMap) - { - if (!spectatorClient.TryGetPlayingUserState(userId, out var userState)) - continue; + if (!spectatorClient.TryGetPlayingUserState(userId, out var userState)) + continue; - if (beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID == userState.BeatmapID)) - updateGameplayState(userId); - } + if (beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID == userState.BeatmapID)) + updateGameplayState(userId); } } @@ -122,101 +115,89 @@ namespace osu.Game.Screens.Spectate if (state.RulesetID == null || state.BeatmapID == null) return; - lock (stateLock) - { - if (!userMap.ContainsKey(userId)) - return; + if (!userMap.ContainsKey(userId)) + return; - // The user may have stopped playing. - if (!spectatorClient.TryGetPlayingUserState(userId, out _)) - return; + // The user may have stopped playing. + if (!spectatorClient.TryGetPlayingUserState(userId, out _)) + return; - Schedule(() => OnUserStateChanged(userId, state)); + Schedule(() => OnUserStateChanged(userId, state)); - updateGameplayState(userId); - } + updateGameplayState(userId); } private void updateGameplayState(int userId) { - lock (stateLock) + Debug.Assert(userMap.ContainsKey(userId)); + + // The user may have stopped playing. + if (!spectatorClient.TryGetPlayingUserState(userId, out var spectatorState)) + return; + + var user = userMap[userId]; + + var resolvedRuleset = rulesets.AvailableRulesets.FirstOrDefault(r => r.ID == spectatorState.RulesetID)?.CreateInstance(); + if (resolvedRuleset == null) + return; + + var resolvedBeatmap = beatmaps.QueryBeatmap(b => b.OnlineBeatmapID == spectatorState.BeatmapID); + if (resolvedBeatmap == null) + return; + + var score = new Score { - Debug.Assert(userMap.ContainsKey(userId)); - - // The user may have stopped playing. - if (!spectatorClient.TryGetPlayingUserState(userId, out var spectatorState)) - return; - - var user = userMap[userId]; - - var resolvedRuleset = rulesets.AvailableRulesets.FirstOrDefault(r => r.ID == spectatorState.RulesetID)?.CreateInstance(); - if (resolvedRuleset == null) - return; - - var resolvedBeatmap = beatmaps.QueryBeatmap(b => b.OnlineBeatmapID == spectatorState.BeatmapID); - if (resolvedBeatmap == null) - return; - - var score = new Score + ScoreInfo = new ScoreInfo { - ScoreInfo = new ScoreInfo - { - Beatmap = resolvedBeatmap, - User = user, - Mods = spectatorState.Mods.Select(m => m.ToMod(resolvedRuleset)).ToArray(), - Ruleset = resolvedRuleset.RulesetInfo, - }, - Replay = new Replay { HasReceivedAllFrames = false }, - }; + Beatmap = resolvedBeatmap, + User = user, + Mods = spectatorState.Mods.Select(m => m.ToMod(resolvedRuleset)).ToArray(), + Ruleset = resolvedRuleset.RulesetInfo, + }, + Replay = new Replay { HasReceivedAllFrames = false }, + }; - var gameplayState = new GameplayState(score, resolvedRuleset, beatmaps.GetWorkingBeatmap(resolvedBeatmap)); + var gameplayState = new GameplayState(score, resolvedRuleset, beatmaps.GetWorkingBeatmap(resolvedBeatmap)); - gameplayStates[userId] = gameplayState; - Schedule(() => StartGameplay(userId, gameplayState)); - } + gameplayStates[userId] = gameplayState; + Schedule(() => StartGameplay(userId, gameplayState)); } private void userSentFrames(int userId, FrameDataBundle bundle) { - lock (stateLock) + if (!userMap.ContainsKey(userId)) + return; + + if (!gameplayStates.TryGetValue(userId, out var gameplayState)) + return; + + // The ruleset instance should be guaranteed to be in sync with the score via ScoreLock. + Debug.Assert(gameplayState.Ruleset != null && gameplayState.Ruleset.RulesetInfo.Equals(gameplayState.Score.ScoreInfo.Ruleset)); + + foreach (var frame in bundle.Frames) { - if (!userMap.ContainsKey(userId)) - return; + IConvertibleReplayFrame convertibleFrame = gameplayState.Ruleset.CreateConvertibleReplayFrame(); + convertibleFrame.FromLegacy(frame, gameplayState.Beatmap.Beatmap); - if (!gameplayStates.TryGetValue(userId, out var gameplayState)) - return; + var convertedFrame = (ReplayFrame)convertibleFrame; + convertedFrame.Time = frame.Time; - // The ruleset instance should be guaranteed to be in sync with the score via ScoreLock. - Debug.Assert(gameplayState.Ruleset != null && gameplayState.Ruleset.RulesetInfo.Equals(gameplayState.Score.ScoreInfo.Ruleset)); - - foreach (var frame in bundle.Frames) - { - IConvertibleReplayFrame convertibleFrame = gameplayState.Ruleset.CreateConvertibleReplayFrame(); - convertibleFrame.FromLegacy(frame, gameplayState.Beatmap.Beatmap); - - var convertedFrame = (ReplayFrame)convertibleFrame; - convertedFrame.Time = frame.Time; - - gameplayState.Score.Replay.Frames.Add(convertedFrame); - } + gameplayState.Score.Replay.Frames.Add(convertedFrame); } } private void userFinishedPlaying(int userId, SpectatorState state) { - lock (stateLock) - { - if (!userMap.ContainsKey(userId)) - return; + if (!userMap.ContainsKey(userId)) + return; - if (!gameplayStates.TryGetValue(userId, out var gameplayState)) - return; + if (!gameplayStates.TryGetValue(userId, out var gameplayState)) + return; - gameplayState.Score.Replay.HasReceivedAllFrames = true; + gameplayState.Score.Replay.HasReceivedAllFrames = true; - gameplayStates.Remove(userId); - Schedule(() => EndGameplay(userId)); - } + gameplayStates.Remove(userId); + Schedule(() => EndGameplay(userId)); } /// @@ -245,15 +226,12 @@ namespace osu.Game.Screens.Spectate /// The user to stop spectating. protected void RemoveUser(int userId) { - lock (stateLock) - { - userFinishedPlaying(userId, null); + userFinishedPlaying(userId, null); - userIds.Remove(userId); - userMap.Remove(userId); + userIds.Remove(userId); + userMap.Remove(userId); - spectatorClient.StopWatchingUser(userId); - } + spectatorClient.StopWatchingUser(userId); } protected override void Dispose(bool isDisposing) @@ -266,11 +244,8 @@ namespace osu.Game.Screens.Spectate spectatorClient.OnUserFinishedPlaying -= userFinishedPlaying; spectatorClient.OnNewFrames -= userSentFrames; - lock (stateLock) - { - foreach (var (userId, _) in userMap) - spectatorClient.StopWatchingUser(userId); - } + foreach (var (userId, _) in userMap) + spectatorClient.StopWatchingUser(userId); } managerUpdated?.UnbindAll(); From 89b4f695884ca229193dc8e7bd2b5d009847a55b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 20 May 2021 19:19:39 +0900 Subject: [PATCH 336/839] Expose playing user states as bindable dictionary --- osu.Game/Online/Spectator/SpectatorClient.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/Spectator/SpectatorClient.cs b/osu.Game/Online/Spectator/SpectatorClient.cs index f930328846..810299e90d 100644 --- a/osu.Game/Online/Spectator/SpectatorClient.cs +++ b/osu.Game/Online/Spectator/SpectatorClient.cs @@ -39,10 +39,10 @@ namespace osu.Game.Online.Spectator private readonly List watchingUsers = new List(); public IBindableList PlayingUsers => playingUsers; - private readonly BindableList playingUsers = new BindableList(); - private readonly Dictionary playingUserStates = new Dictionary(); + public IBindableDictionary PlayingUserStates => playingUserStates; + private readonly BindableDictionary playingUserStates = new BindableDictionary(); private IBeatmap? currentBeatmap; From b515fe3cb1f9ee8732bd427dfc842cbefca3a9b8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 20 May 2021 19:19:58 +0900 Subject: [PATCH 337/839] Fix playing user state not removed on stop watching --- osu.Game/Online/Spectator/SpectatorClient.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Online/Spectator/SpectatorClient.cs b/osu.Game/Online/Spectator/SpectatorClient.cs index 810299e90d..649af03bc4 100644 --- a/osu.Game/Online/Spectator/SpectatorClient.cs +++ b/osu.Game/Online/Spectator/SpectatorClient.cs @@ -183,6 +183,7 @@ namespace osu.Game.Online.Spectator public void StopWatchingUser(int userId) { watchingUsers.Remove(userId); + playingUserStates.Remove(userId); StopWatchingUserInternal(userId); } From 7ee81669f7a304982bb88cccf5cf354a9cc72131 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 20 May 2021 19:27:34 +0900 Subject: [PATCH 338/839] Remove bind helpers from SpectatorClient --- osu.Game/Online/Spectator/SpectatorClient.cs | 28 -------- osu.Game/Screens/Spectate/SpectatorScreen.cs | 68 +++++++++++--------- 2 files changed, 39 insertions(+), 57 deletions(-) diff --git a/osu.Game/Online/Spectator/SpectatorClient.cs b/osu.Game/Online/Spectator/SpectatorClient.cs index 649af03bc4..04904f66ac 100644 --- a/osu.Game/Online/Spectator/SpectatorClient.cs +++ b/osu.Game/Online/Spectator/SpectatorClient.cs @@ -238,33 +238,5 @@ namespace osu.Game.Online.Spectator lastSendTime = Time.Current; } - - /// - /// Attempts to retrieve the for a currently-playing user. - /// - /// The user. - /// The current for the user, if they're playing. null if the user is not playing. - /// true if successful (the user is playing), false otherwise. - public bool TryGetPlayingUserState(int userId, out SpectatorState state) - { - return playingUserStates.TryGetValue(userId, out state); - } - - /// - /// Bind an action to with the option of running the bound action once immediately. - /// - /// The action to perform when a user begins playing. - /// Whether the action provided in should be run once immediately for all users currently playing. - public void BindUserBeganPlaying(Action callback, bool runOnceImmediately = false) - { - // The lock is taken before the event is subscribed to to prevent doubling of events. - OnUserBeganPlaying += callback; - - if (!runOnceImmediately) - return; - - foreach (var (userId, state) in playingUserStates) - callback(userId, state); - } } } diff --git a/osu.Game/Screens/Spectate/SpectatorScreen.cs b/osu.Game/Screens/Spectate/SpectatorScreen.cs index e6c9a0acd4..0adc5b863f 100644 --- a/osu.Game/Screens/Spectate/SpectatorScreen.cs +++ b/osu.Game/Screens/Spectate/SpectatorScreen.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.ObjectExtensions; using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.Online.Spectator; @@ -42,6 +43,8 @@ namespace osu.Game.Screens.Spectate [Resolved] private UserLookupCache userLookupCache { get; set; } + private readonly IBindableDictionary playingUserStates = new BindableDictionary(); + private readonly Dictionary userMap = new Dictionary(); private readonly Dictionary gameplayStates = new Dictionary(); @@ -65,8 +68,9 @@ namespace osu.Game.Screens.Spectate foreach (var u in users.Result) userMap[u.Id] = u; - spectatorClient.BindUserBeganPlaying(userBeganPlaying, true); - spectatorClient.OnUserFinishedPlaying += userFinishedPlaying; + playingUserStates.BindTo(spectatorClient.PlayingUserStates); + playingUserStates.BindCollectionChanged(onPlayingUserStatesChanged, true); + spectatorClient.OnNewFrames += userSentFrames; managerUpdated = beatmaps.ItemUpdated.GetBoundCopy(); @@ -102,7 +106,7 @@ namespace osu.Game.Screens.Spectate foreach (var (userId, _) in userMap) { - if (!spectatorClient.TryGetPlayingUserState(userId, out var userState)) + if (!playingUserStates.TryGetValue(userId, out var userState)) continue; if (beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID == userState.BeatmapID)) @@ -110,7 +114,23 @@ namespace osu.Game.Screens.Spectate } } - private void userBeganPlaying(int userId, SpectatorState state) + private void onPlayingUserStatesChanged(object sender, NotifyDictionaryChangedEventArgs e) + { + switch (e.Action) + { + case NotifyDictionaryChangedAction.Add: + foreach (var (userId, state) in e.NewItems.AsNonNull()) + onUserStateAdded(userId, state); + break; + + case NotifyDictionaryChangedAction.Remove: + foreach (var (userId, _) in e.OldItems.AsNonNull()) + onUserStateRemoved(userId); + break; + } + } + + private void onUserStateAdded(int userId, SpectatorState state) { if (state.RulesetID == null || state.BeatmapID == null) return; @@ -118,24 +138,30 @@ namespace osu.Game.Screens.Spectate if (!userMap.ContainsKey(userId)) return; - // The user may have stopped playing. - if (!spectatorClient.TryGetPlayingUserState(userId, out _)) + Schedule(() => OnUserStateChanged(userId, state)); + updateGameplayState(userId); + } + + private void onUserStateRemoved(int userId) + { + if (!userMap.ContainsKey(userId)) return; - Schedule(() => OnUserStateChanged(userId, state)); + if (!gameplayStates.TryGetValue(userId, out var gameplayState)) + return; - updateGameplayState(userId); + gameplayState.Score.Replay.HasReceivedAllFrames = true; + + gameplayStates.Remove(userId); + Schedule(() => EndGameplay(userId)); } private void updateGameplayState(int userId) { Debug.Assert(userMap.ContainsKey(userId)); - // The user may have stopped playing. - if (!spectatorClient.TryGetPlayingUserState(userId, out var spectatorState)) - return; - var user = userMap[userId]; + var spectatorState = playingUserStates[userId]; var resolvedRuleset = rulesets.AvailableRulesets.FirstOrDefault(r => r.ID == spectatorState.RulesetID)?.CreateInstance(); if (resolvedRuleset == null) @@ -186,20 +212,6 @@ namespace osu.Game.Screens.Spectate } } - private void userFinishedPlaying(int userId, SpectatorState state) - { - if (!userMap.ContainsKey(userId)) - return; - - if (!gameplayStates.TryGetValue(userId, out var gameplayState)) - return; - - gameplayState.Score.Replay.HasReceivedAllFrames = true; - - gameplayStates.Remove(userId); - Schedule(() => EndGameplay(userId)); - } - /// /// Invoked when a spectated user's state has changed. /// @@ -226,7 +238,7 @@ namespace osu.Game.Screens.Spectate /// The user to stop spectating. protected void RemoveUser(int userId) { - userFinishedPlaying(userId, null); + onUserStateRemoved(userId); userIds.Remove(userId); userMap.Remove(userId); @@ -240,8 +252,6 @@ namespace osu.Game.Screens.Spectate if (spectatorClient != null) { - spectatorClient.OnUserBeganPlaying -= userBeganPlaying; - spectatorClient.OnUserFinishedPlaying -= userFinishedPlaying; spectatorClient.OnNewFrames -= userSentFrames; foreach (var (userId, _) in userMap) From df5970fab4585e6649a2cec85f7d38e5ee47b264 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 May 2021 19:34:53 +0900 Subject: [PATCH 339/839] Create base implementations of the two most common `TernaryStateMenuItem`s --- .../Components/PathControlPointVisualiser.cs | 12 +--- .../Edit/TaikoSelectionHandler.cs | 4 +- .../TestSceneStatefulMenuItem.cs | 56 +++++++++++++++++-- .../UserInterface/TernaryStateMenuItem.cs | 37 ++---------- .../TernaryStateRadioMenuItem.cs | 23 ++++++++ .../TernaryStateToggleMenuItem.cs | 42 ++++++++++++++ .../Components/EditorSelectionHandler.cs | 4 +- .../Carousel/DrawableCarouselBeatmapSet.cs | 2 +- .../Skinning/Editor/SkinSelectionHandler.cs | 14 +---- 9 files changed, 129 insertions(+), 65 deletions(-) create mode 100644 osu.Game/Graphics/UserInterface/TernaryStateRadioMenuItem.cs create mode 100644 osu.Game/Graphics/UserInterface/TernaryStateToggleMenuItem.cs diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index 44c3056910..c36768baba 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -243,7 +243,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components int totalCount = Pieces.Count(p => p.IsSelected.Value); int countOfState = Pieces.Where(p => p.IsSelected.Value).Count(p => p.ControlPoint.Type.Value == type); - var item = new PathTypeMenuItem(type, () => + var item = new TernaryStateRadioMenuItem(type == null ? "Inherit" : type.ToString().Humanize(), MenuItemType.Standard, _ => { foreach (var p in Pieces.Where(p => p.IsSelected.Value)) updatePathType(p, type); @@ -258,15 +258,5 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components return item; } - - private class PathTypeMenuItem : TernaryStateMenuItem - { - public PathTypeMenuItem(PathType? type, Action action) - : base(type == null ? "Inherit" : type.ToString().Humanize(), changeState, MenuItemType.Standard, _ => action?.Invoke()) - { - } - - private static TernaryState changeState(TernaryState state) => TernaryState.True; - } } } diff --git a/osu.Game.Rulesets.Taiko/Edit/TaikoSelectionHandler.cs b/osu.Game.Rulesets.Taiko/Edit/TaikoSelectionHandler.cs index 48ee0d4cf4..a24130d6ac 100644 --- a/osu.Game.Rulesets.Taiko/Edit/TaikoSelectionHandler.cs +++ b/osu.Game.Rulesets.Taiko/Edit/TaikoSelectionHandler.cs @@ -76,10 +76,10 @@ namespace osu.Game.Rulesets.Taiko.Edit protected override IEnumerable GetContextMenuItemsForSelection(IEnumerable> selection) { if (selection.All(s => s.Item is Hit)) - yield return new TernaryStateMenuItem("Rim") { State = { BindTarget = selectionRimState } }; + yield return new TernaryStateToggleMenuItem("Rim") { State = { BindTarget = selectionRimState } }; if (selection.All(s => s.Item is TaikoHitObject)) - yield return new TernaryStateMenuItem("Strong") { State = { BindTarget = selectionStrongState } }; + yield return new TernaryStateToggleMenuItem("Strong") { State = { BindTarget = selectionStrongState } }; foreach (var item in base.GetContextMenuItemsForSelection(selection)) yield return item; diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneStatefulMenuItem.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneStatefulMenuItem.cs index 29aeb6a4b2..18ec631f37 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneStatefulMenuItem.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneStatefulMenuItem.cs @@ -14,7 +14,7 @@ namespace osu.Game.Tests.Visual.UserInterface public class TestSceneStatefulMenuItem : OsuManualInputManagerTestScene { [Test] - public void TestTernaryMenuItem() + public void TestTernaryRadioMenuItem() { OsuMenu menu = null; @@ -30,9 +30,57 @@ namespace osu.Game.Tests.Visual.UserInterface Origin = Anchor.Centre, Items = new[] { - new TernaryStateMenuItem("First"), - new TernaryStateMenuItem("Second") { State = { BindTarget = state } }, - new TernaryStateMenuItem("Third") { State = { Value = TernaryState.True } }, + new TernaryStateRadioMenuItem("First"), + new TernaryStateRadioMenuItem("Second") { State = { BindTarget = state } }, + new TernaryStateRadioMenuItem("Third") { State = { Value = TernaryState.True } }, + } + }; + }); + + checkState(TernaryState.Indeterminate); + + click(); + checkState(TernaryState.True); + + click(); + checkState(TernaryState.True); + + click(); + checkState(TernaryState.True); + + AddStep("change state via bindable", () => state.Value = TernaryState.True); + + void click() => + AddStep("click", () => + { + InputManager.MoveMouseTo(menu.ScreenSpaceDrawQuad.Centre); + InputManager.Click(MouseButton.Left); + }); + + void checkState(TernaryState expected) + => AddAssert($"state is {expected}", () => state.Value == expected); + } + + [Test] + public void TestTernaryToggleMenuItem() + { + OsuMenu menu = null; + + Bindable state = new Bindable(TernaryState.Indeterminate); + + AddStep("create menu", () => + { + state.Value = TernaryState.Indeterminate; + + Child = menu = new OsuMenu(Direction.Vertical, true) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Items = new[] + { + new TernaryStateToggleMenuItem("First"), + new TernaryStateToggleMenuItem("Second") { State = { BindTarget = state } }, + new TernaryStateToggleMenuItem("Third") { State = { Value = TernaryState.True } }, } }; }); diff --git a/osu.Game/Graphics/UserInterface/TernaryStateMenuItem.cs b/osu.Game/Graphics/UserInterface/TernaryStateMenuItem.cs index acf4065f49..5c623150b7 100644 --- a/osu.Game/Graphics/UserInterface/TernaryStateMenuItem.cs +++ b/osu.Game/Graphics/UserInterface/TernaryStateMenuItem.cs @@ -9,28 +9,17 @@ namespace osu.Game.Graphics.UserInterface /// /// An with three possible states. /// - public class TernaryStateMenuItem : StatefulMenuItem + public abstract class TernaryStateMenuItem : StatefulMenuItem { /// /// Creates a new . /// /// The text to display. + /// A function to inform what the next state should be when this item is clicked. /// The type of action which this performs. /// A delegate to be invoked when this is pressed. - public TernaryStateMenuItem(string text, MenuItemType type = MenuItemType.Standard, Action action = null) - : this(text, getNextState, type, action) - { - } - - /// - /// Creates a new . - /// - /// The text to display. - /// A function that mutates a state to another state after this is pressed. - /// The type of action which this performs. - /// A delegate to be invoked when this is pressed. - protected TernaryStateMenuItem(string text, Func changeStateFunc, MenuItemType type, Action action) - : base(text, changeStateFunc, type, action) + protected TernaryStateMenuItem(string text, Func nextStateFunction, MenuItemType type = MenuItemType.Standard, Action action = null) + : base(text, nextStateFunction, type, action) { } @@ -47,23 +36,5 @@ namespace osu.Game.Graphics.UserInterface return null; } - - private static TernaryState getNextState(TernaryState state) - { - switch (state) - { - case TernaryState.False: - return TernaryState.True; - - case TernaryState.Indeterminate: - return TernaryState.True; - - case TernaryState.True: - return TernaryState.False; - - default: - throw new ArgumentOutOfRangeException(nameof(state), state, null); - } - } } } diff --git a/osu.Game/Graphics/UserInterface/TernaryStateRadioMenuItem.cs b/osu.Game/Graphics/UserInterface/TernaryStateRadioMenuItem.cs new file mode 100644 index 0000000000..aa83b0567b --- /dev/null +++ b/osu.Game/Graphics/UserInterface/TernaryStateRadioMenuItem.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; + +namespace osu.Game.Graphics.UserInterface +{ + public class TernaryStateRadioMenuItem : TernaryStateMenuItem + { + /// + /// Creates a new . + /// + /// The text to display. + /// The type of action which this performs. + /// A delegate to be invoked when this is pressed. + public TernaryStateRadioMenuItem(string text, MenuItemType type = MenuItemType.Standard, Action action = null) + : base(text, getNextState, type, action) + { + } + + private static TernaryState getNextState(TernaryState state) => TernaryState.True; + } +} diff --git a/osu.Game/Graphics/UserInterface/TernaryStateToggleMenuItem.cs b/osu.Game/Graphics/UserInterface/TernaryStateToggleMenuItem.cs new file mode 100644 index 0000000000..ce951984fd --- /dev/null +++ b/osu.Game/Graphics/UserInterface/TernaryStateToggleMenuItem.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; + +namespace osu.Game.Graphics.UserInterface +{ + /// + /// A ternary state menu item which toggles the state of this item false if clicked when true. + /// + public class TernaryStateToggleMenuItem : TernaryStateMenuItem + { + /// + /// Creates a new . + /// + /// The text to display. + /// The type of action which this performs. + /// A delegate to be invoked when this is pressed. + public TernaryStateToggleMenuItem(string text, MenuItemType type = MenuItemType.Standard, Action action = null) + : base(text, getNextState, type, action) + { + } + + private static TernaryState getNextState(TernaryState state) + { + switch (state) + { + case TernaryState.False: + return TernaryState.True; + + case TernaryState.Indeterminate: + return TernaryState.True; + + case TernaryState.True: + return TernaryState.False; + + default: + throw new ArgumentOutOfRangeException(nameof(state), state, null); + } + } + } +} diff --git a/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs index 6ab4ca8267..2141c490df 100644 --- a/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs @@ -168,13 +168,13 @@ namespace osu.Game.Screens.Edit.Compose.Components { if (SelectedBlueprints.All(b => b.Item is IHasComboInformation)) { - yield return new TernaryStateMenuItem("New combo") { State = { BindTarget = SelectionNewComboState } }; + yield return new TernaryStateToggleMenuItem("New combo") { State = { BindTarget = SelectionNewComboState } }; } yield return new OsuMenuItem("Sound") { Items = SelectionSampleStates.Select(kvp => - new TernaryStateMenuItem(kvp.Value.Description) { State = { BindTarget = kvp.Value } }).ToArray() + new TernaryStateToggleMenuItem(kvp.Value.Description) { State = { BindTarget = kvp.Value } }).ToArray() }; } diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index d7e901b71e..a3fca3d4e1 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -250,7 +250,7 @@ namespace osu.Game.Screens.Select.Carousel else state = TernaryState.False; - return new TernaryStateMenuItem(collection.Name.Value, MenuItemType.Standard, s => + return new TernaryStateToggleMenuItem(collection.Name.Value, MenuItemType.Standard, s => { foreach (var b in beatmapSet.Beatmaps) { diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index 2eb4ea107d..2cfb9d0f96 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -100,7 +100,7 @@ namespace osu.Game.Skinning.Editor foreach (var item in base.GetContextMenuItemsForSelection(selection)) yield return item; - IEnumerable createAnchorItems(Func checkFunction, Action applyFunction) + IEnumerable createAnchorItems(Func checkFunction, Action applyFunction) { var displayableAnchors = new[] { @@ -117,7 +117,7 @@ namespace osu.Game.Skinning.Editor return displayableAnchors.Select(a => { - return new AnchorMenuItem(a, selection, _ => applyFunction(a)) + return new TernaryStateRadioMenuItem(a.ToString(), MenuItemType.Standard, _ => applyFunction(a)) { State = { Value = GetStateFromSelection(selection, c => checkFunction((Drawable)c.Item) == a) } }; @@ -166,15 +166,5 @@ namespace osu.Game.Skinning.Editor scale.Y = scale.X; } } - - public class AnchorMenuItem : TernaryStateMenuItem - { - public AnchorMenuItem(Anchor anchor, IEnumerable> selection, Action action) - : base(anchor.ToString(), getNextState, MenuItemType.Standard, action) - { - } - - private static TernaryState getNextState(TernaryState state) => TernaryState.True; - } } } From ee4bca9ed12d5f65cc55d3b24f1164851c89c746 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 20 May 2021 19:37:43 +0900 Subject: [PATCH 340/839] Handle collection changed event --- osu.Game/Screens/Spectate/SpectatorScreen.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Screens/Spectate/SpectatorScreen.cs b/osu.Game/Screens/Spectate/SpectatorScreen.cs index 0adc5b863f..9a20bb58b8 100644 --- a/osu.Game/Screens/Spectate/SpectatorScreen.cs +++ b/osu.Game/Screens/Spectate/SpectatorScreen.cs @@ -127,6 +127,14 @@ namespace osu.Game.Screens.Spectate foreach (var (userId, _) in e.OldItems.AsNonNull()) onUserStateRemoved(userId); break; + + case NotifyDictionaryChangedAction.Replace: + foreach (var (userId, _) in e.OldItems.AsNonNull()) + onUserStateRemoved(userId); + + foreach (var (userId, state) in e.NewItems.AsNonNull()) + onUserStateAdded(userId, state); + break; } } From 5a8b8782d34e76faaad394c42cdd597289acf633 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 20 May 2021 19:44:43 +0900 Subject: [PATCH 341/839] Fix WatchUser being called asynchronously in BDL --- osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index ed83bbf693..c3bfe19b29 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -55,8 +55,6 @@ namespace osu.Game.Screens.Play.HUD foreach (var userId in playingUsers) { - spectatorClient.WatchUser(userId); - // probably won't be required in the final implementation. var resolvedUser = userLookupCache.GetUserAsync(userId).Result; @@ -80,6 +78,8 @@ namespace osu.Game.Screens.Play.HUD // BindableList handles binding in a really bad way (Clear then AddRange) so we need to do this manually.. foreach (int userId in playingUsers) { + spectatorClient.WatchUser(userId); + if (!multiplayerClient.CurrentMatchPlayingUserIds.Contains(userId)) usersChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, new[] { userId })); } From 06c99e8c7c259e8ffb712ff5320163b38e0f4aa3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 20 May 2021 19:45:11 +0900 Subject: [PATCH 342/839] Fix race due to StopWatchingUser() being called asynchronously --- osu.Game/Online/Spectator/SpectatorClient.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/Spectator/SpectatorClient.cs b/osu.Game/Online/Spectator/SpectatorClient.cs index f930328846..de5e57a1d0 100644 --- a/osu.Game/Online/Spectator/SpectatorClient.cs +++ b/osu.Game/Online/Spectator/SpectatorClient.cs @@ -182,9 +182,13 @@ namespace osu.Game.Online.Spectator public void StopWatchingUser(int userId) { - watchingUsers.Remove(userId); - - StopWatchingUserInternal(userId); + // This method is most commonly called via Dispose(), which is asynchronous. + // Todo: This should not be a thing, but requires framework changes. + Schedule(() => + { + watchingUsers.Remove(userId); + StopWatchingUserInternal(userId); + }); } protected abstract Task BeginPlayingInternal(SpectatorState state); From ae0949fc145d0f8aebb8424ec4ac99adf487c3a1 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 25 May 2021 14:20:04 +0700 Subject: [PATCH 343/839] add main page in wiki overlay --- osu.Game/Overlays/WikiOverlay.cs | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs index 5505eeac68..6e07de71d0 100644 --- a/osu.Game/Overlays/WikiOverlay.cs +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -54,13 +54,24 @@ namespace osu.Game.Overlays private void onSuccess(APIWikiPage response) { wikiData.Value = response; - LoadDisplay(new WikiMarkdownContainer + + if (response.Layout == "main_page") { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - CurrentPath = $"{path.Value}/", - Text = response.Markdown, - }); + LoadDisplay(new WikiMainPage + { + Markdown = response.Markdown + }); + } + else + { + LoadDisplay(new WikiMarkdownContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + CurrentPath = $"{path.Value}/", + Text = response.Markdown, + }); + } } private void showParentPage() From 6257504bb6170bfb186f2dad856f96fa9052e284 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 25 May 2021 14:37:14 +0700 Subject: [PATCH 344/839] add padding spacing --- osu.Game/Overlays/WikiOverlay.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs index 6e07de71d0..fa8ba66bcd 100644 --- a/osu.Game/Overlays/WikiOverlay.cs +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -59,7 +59,12 @@ namespace osu.Game.Overlays { LoadDisplay(new WikiMainPage { - Markdown = response.Markdown + Markdown = response.Markdown, + Padding = new MarginPadding + { + Vertical = 20, + Horizontal = 50, + }, }); } else @@ -70,6 +75,13 @@ namespace osu.Game.Overlays AutoSizeAxes = Axes.Y, CurrentPath = $"{path.Value}/", Text = response.Markdown, + DocumentMargin = new MarginPadding(0), + DocumentPadding = new MarginPadding + { + Vertical = 20, + Left = 30, + Right = 50, + }, }); } } From 1ad3aee126e29aac93fb28089460c1acbe0e589c Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 20 May 2021 17:49:20 +0700 Subject: [PATCH 345/839] add article page test wiki overlay --- .../Visual/Online/TestSceneWikiOverlay.cs | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs index 371be8a003..da4bf82948 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs @@ -21,11 +21,18 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestMainPage() { - setUpNewsResponse(responseExample); + setUpWikiResponse(responseMainPage); AddStep("Show Main Page", () => wiki.Show()); } - private void setUpNewsResponse(APIWikiPage r) + [Test] + public void TestArticlePage() + { + setUpWikiResponse(responseArticlePage); + AddStep("Show Article Page", () => wiki.ShowPage("Interface")); + } + + private void setUpWikiResponse(APIWikiPage r) => AddStep("set up response", () => { dummyAPI.HandleRequest = request => @@ -39,14 +46,27 @@ namespace osu.Game.Tests.Visual.Online }); // From https://osu.ppy.sh/api/v2/wiki/en/Main_Page - private APIWikiPage responseExample => new APIWikiPage + private APIWikiPage responseMainPage => new APIWikiPage { Title = "Main Page", Layout = "main_page", Path = "Main_Page", Locale = "en", Subtitle = null, - Markdown = "---\nlayout: main_page\n---\n\n\n\n
\nWelcome to the osu! wiki, a project containing a wide range of osu! related information.\n
\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# Game client\n\n[Interface](/wiki/Interface) • [Options](/wiki/Options) • [Visual settings](/wiki/Visual_Settings) • [Shortcut key reference](/wiki/Shortcut_key_reference) • [Configuration file](/wiki/osu!_Program_Files/User_Configuration_File) • [Program files](/wiki/osu!_Program_Files)\n\n[File formats](/wiki/osu!_File_Formats): [.osz](/wiki/osu!_File_Formats/Osz_(file_format)) • [.osk](/wiki/osu!_File_Formats/Osk_(file_format)) • [.osr](/wiki/osu!_File_Formats/Osr_(file_format)) • [.osu](/wiki/osu!_File_Formats/Osu_(file_format)) • [.osb](/wiki/osu!_File_Formats/Osb_(file_format)) • [.db](/wiki/osu!_File_Formats/Db_(file_format))\n\n
\n
\n\n# Gameplay\n\n[Game modes](/wiki/Game_mode): [osu!](/wiki/Game_mode/osu!) • [osu!taiko](/wiki/Game_mode/osu!taiko) • [osu!catch](/wiki/Game_mode/osu!catch) • [osu!mania](/wiki/Game_mode/osu!mania)\n\n[Beatmap](/wiki/Beatmap) • [Hit object](/wiki/Hit_object) • [Mods](/wiki/Game_modifier) • [Score](/wiki/Score) • [Replay](/wiki/Replay) • [Multi](/wiki/Multi)\n\n
\n
\n\n# [Beatmap editor](/wiki/Beatmap_Editor)\n\nSections: [Compose](/wiki/Beatmap_Editor/Compose) • [Design](/wiki/Beatmap_Editor/Design) • [Timing](/wiki/Beatmap_Editor/Timing) • [Song setup](/wiki/Beatmap_Editor/Song_Setup)\n\nComponents: [AiMod](/wiki/Beatmap_Editor/AiMod) • [Beat snap divisor](/wiki/Beatmap_Editor/Beat_Snap_Divisor) • [Distance snap](/wiki/Beatmap_Editor/Distance_Snap) • [Menu](/wiki/Beatmap_Editor/Menu) • [SB load](/wiki/Beatmap_Editor/SB_Load) • [Timelines](/wiki/Beatmap_Editor/Timelines)\n\n[Beatmapping](/wiki/Beatmapping) • [Difficulty](/wiki/Beatmap/Difficulty) • [Mapping techniques](/wiki/Mapping_Techniques) • [Storyboarding](/wiki/Storyboarding)\n\n
\n
\n\n# Beatmap submission and ranking\n\n[Submission](/wiki/Submission) • [Modding](/wiki/Modding) • [Ranking procedure](/wiki/Beatmap_ranking_procedure) • [Mappers' Guild](/wiki/Mappers_Guild) • [Project Loved](/wiki/Project_Loved)\n\n[Ranking criteria](/wiki/Ranking_Criteria): [osu!](/wiki/Ranking_Criteria/osu!) • [osu!taiko](/wiki/Ranking_Criteria/osu!taiko) • [osu!catch](/wiki/Ranking_Criteria/osu!catch) • [osu!mania](/wiki/Ranking_Criteria/osu!mania)\n\n
\n
\n\n# Community\n\n[Tournaments](/wiki/Tournaments) • [Skinning](/wiki/Skinning) • [Projects](/wiki/Projects) • [Guides](/wiki/Guides) • [osu!dev Discord server](/wiki/osu!dev_Discord_server) • [How you can help](/wiki/How_You_Can_Help!) • [Glossary](/wiki/Glossary)\n\n
\n
\n\n# People\n\n[The Team](/wiki/People/The_Team): [Developers](/wiki/People/The_Team/Developers) • [Global Moderation Team](/wiki/People/The_Team/Global_Moderation_Team) • [Support Team](/wiki/People/The_Team/Support_Team) • [Nomination Assessment Team](/wiki/People/The_Team/Nomination_Assessment_Team) • [Beatmap Nominators](/wiki/People/The_Team/Beatmap_Nominators) • [osu! Alumni](/wiki/People/The_Team/osu!_Alumni) • [Project Loved Team](/wiki/People/The_Team/Project_Loved_Team)\n\nOrganisations: [osu! UCI](/wiki/Organisations/osu!_UCI)\n\n[Community Contributors](/wiki/People/Community_Contributors) • [Users with unique titles](/wiki/People/Users_with_unique_titles)\n\n
\n
\n\n# For developers\n\n[API](/wiki/osu!api) • [Bot account](/wiki/Bot_account) • [Brand identity guidelines](/wiki/Brand_identity_guidelines)\n\n
\n
\n\n# About the wiki\n\n[Sitemap](/wiki/Sitemap) • [Contribution guide](/wiki/osu!_wiki_Contribution_Guide) • [Article styling criteria](/wiki/Article_Styling_Criteria) • [News styling criteria](/wiki/News_Styling_Criteria)\n\n
\n
\n", + Markdown = + "---\nlayout: main_page\n---\n\n\n\n
\nWelcome to the osu! wiki, a project containing a wide range of osu! related information.\n
\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# Game client\n\n[Interface](/wiki/Interface) • [Options](/wiki/Options) • [Visual settings](/wiki/Visual_Settings) • [Shortcut key reference](/wiki/Shortcut_key_reference) • [Configuration file](/wiki/osu!_Program_Files/User_Configuration_File) • [Program files](/wiki/osu!_Program_Files)\n\n[File formats](/wiki/osu!_File_Formats): [.osz](/wiki/osu!_File_Formats/Osz_(file_format)) • [.osk](/wiki/osu!_File_Formats/Osk_(file_format)) • [.osr](/wiki/osu!_File_Formats/Osr_(file_format)) • [.osu](/wiki/osu!_File_Formats/Osu_(file_format)) • [.osb](/wiki/osu!_File_Formats/Osb_(file_format)) • [.db](/wiki/osu!_File_Formats/Db_(file_format))\n\n
\n
\n\n# Gameplay\n\n[Game modes](/wiki/Game_mode): [osu!](/wiki/Game_mode/osu!) • [osu!taiko](/wiki/Game_mode/osu!taiko) • [osu!catch](/wiki/Game_mode/osu!catch) • [osu!mania](/wiki/Game_mode/osu!mania)\n\n[Beatmap](/wiki/Beatmap) • [Hit object](/wiki/Hit_object) • [Mods](/wiki/Game_modifier) • [Score](/wiki/Score) • [Replay](/wiki/Replay) • [Multi](/wiki/Multi)\n\n
\n
\n\n# [Beatmap editor](/wiki/Beatmap_Editor)\n\nSections: [Compose](/wiki/Beatmap_Editor/Compose) • [Design](/wiki/Beatmap_Editor/Design) • [Timing](/wiki/Beatmap_Editor/Timing) • [Song setup](/wiki/Beatmap_Editor/Song_Setup)\n\nComponents: [AiMod](/wiki/Beatmap_Editor/AiMod) • [Beat snap divisor](/wiki/Beatmap_Editor/Beat_Snap_Divisor) • [Distance snap](/wiki/Beatmap_Editor/Distance_Snap) • [Menu](/wiki/Beatmap_Editor/Menu) • [SB load](/wiki/Beatmap_Editor/SB_Load) • [Timelines](/wiki/Beatmap_Editor/Timelines)\n\n[Beatmapping](/wiki/Beatmapping) • [Difficulty](/wiki/Beatmap/Difficulty) • [Mapping techniques](/wiki/Mapping_Techniques) • [Storyboarding](/wiki/Storyboarding)\n\n
\n
\n\n# Beatmap submission and ranking\n\n[Submission](/wiki/Submission) • [Modding](/wiki/Modding) • [Ranking procedure](/wiki/Beatmap_ranking_procedure) • [Mappers' Guild](/wiki/Mappers_Guild) • [Project Loved](/wiki/Project_Loved)\n\n[Ranking criteria](/wiki/Ranking_Criteria): [osu!](/wiki/Ranking_Criteria/osu!) • [osu!taiko](/wiki/Ranking_Criteria/osu!taiko) • [osu!catch](/wiki/Ranking_Criteria/osu!catch) • [osu!mania](/wiki/Ranking_Criteria/osu!mania)\n\n
\n
\n\n# Community\n\n[Tournaments](/wiki/Tournaments) • [Skinning](/wiki/Skinning) • [Projects](/wiki/Projects) • [Guides](/wiki/Guides) • [osu!dev Discord server](/wiki/osu!dev_Discord_server) • [How you can help](/wiki/How_You_Can_Help!) • [Glossary](/wiki/Glossary)\n\n
\n
\n\n# People\n\n[The Team](/wiki/People/The_Team): [Developers](/wiki/People/The_Team/Developers) • [Global Moderation Team](/wiki/People/The_Team/Global_Moderation_Team) • [Support Team](/wiki/People/The_Team/Support_Team) • [Nomination Assessment Team](/wiki/People/The_Team/Nomination_Assessment_Team) • [Beatmap Nominators](/wiki/People/The_Team/Beatmap_Nominators) • [osu! Alumni](/wiki/People/The_Team/osu!_Alumni) • [Project Loved Team](/wiki/People/The_Team/Project_Loved_Team)\n\nOrganisations: [osu! UCI](/wiki/Organisations/osu!_UCI)\n\n[Community Contributors](/wiki/People/Community_Contributors) • [Users with unique titles](/wiki/People/Users_with_unique_titles)\n\n
\n
\n\n# For developers\n\n[API](/wiki/osu!api) • [Bot account](/wiki/Bot_account) • [Brand identity guidelines](/wiki/Brand_identity_guidelines)\n\n
\n
\n\n# About the wiki\n\n[Sitemap](/wiki/Sitemap) • [Contribution guide](/wiki/osu!_wiki_Contribution_Guide) • [Article styling criteria](/wiki/Article_Styling_Criteria) • [News styling criteria](/wiki/News_Styling_Criteria)\n\n
\n
\n", + }; + + // From https://osu.ppy.sh/api/v2/wiki/en/Interface + private APIWikiPage responseArticlePage => new APIWikiPage + { + Title = "Interface", + Layout = "markdown_page", + Path = "Interface", + Locale = "en", + Subtitle = null, + Markdown = + "# Interface\n\n![](img/intro-screen.jpg \"Introduction screen\")\n\n## Main Menu\n\n![](img/main-menu.jpg \"Main Menu\")\n\nThe [osu!cookie](/wiki/Glossary#cookie) \\[1\\] pulses according to the [BPM](/wiki/Beatmapping/Beats_per_minute) of any song currently playing on the main menu. In addition, bars will extend out of the osu!cookie in accordance to the song's volume. If no song is playing, it pulses at a slow 60 BPM. The elements of the main menu are as follows:\n\n- \\[2\\] Click Play (`P`) or the logo to switch to the Solo mode song selection screen.\n- \\[3\\] Click Edit (`E`) to open the Editor mode song selection screen.\n- \\[4\\] Click Options (`O`) to go to the Options screen.\n- \\[5\\] Click Exit (`Esc`) to exit osu!.\n- \\[6\\] A random useful tip is displayed below the menu.\n- \\[7\\] In the lower-left is a link to the osu! website, as well as copyright information.\n- \\[8\\] Connection result to [Bancho](/wiki/Glossary#bancho)! In this picture it is not shown, but the connection result looks like a chain link.\n- \\[9\\] In the bottom right are the chat controls for the extended [chat window](/wiki/Chat_Console) (called \"Player List\" here) and the regular chat window (`F9` & `F8`, respectively).\n- \\[10\\] In the upper right is the osu! jukebox which plays the songs in random order. The top shows the song currently playing. The buttons, from left to right, do as follows:\n - Previous Track\n - Play\n - Pause\n - Stop (the difference between Play and Stop is that Stop will reset the song to the beginning, while Pause simply pauses it)\n - Next Track\n - View Song Info. This toggles the top bar showing the song info between being permanent and temporary. When permanent, the info bar will stay visible until it fades out with the rest of the UI. When temporary, it will disappear a little while after a song has been chosen. It will stay hidden until it is toggled again, or another song plays.\n- \\[11\\] The number of beatmaps you have available, how long your osu!client has been running, and your system clock.\n- \\[12\\] Your profile, click on it to display the User Options (see below).\n\n## User Options\n\n![](img/user-options.jpg \"User Options\")\n\nAccess this screen by clicking your profile at the top left of the main menu. You cannot access the Chat Consoles while viewing the user option screen. You can select any item by pressing the corresponding number on the option:\n\n1. `View Profile`: Opens up your profile page in your default web browser.\n2. `Sign Out`: Sign out of your account (after signing out, the [Options](/wiki/Options) sidebar will prompt you to sign in).\n3. `Change Avatar`: Open up the edit avatar page in your default web browser.\n4. `Close`: Close this dialog\n\n## Play Menu\n\n![](img/play-menu.jpg \"Play Menu\")\n\n- Click `Solo` (`P`) to play alone.\n- Click `Multi` (`M`) to play with other people. You will be directed to the [Multi](/wiki/Multi) Lobby (see below).\n- Click `Back` to return to the main menu.\n\n## Multi Lobby\n\n*Main page: [Multi](/wiki/Multi)*\n\n![](img/multi-lobby.jpg \"Multi Lobby\")\n\n![](img/multi-room.jpg \"Multi Host\")\n\n1. Your rank in the match. This is also shown next to your name.\n2. Your profile information.\n3. The jukebox.\n4. Player list - displays player names, their rank (host or player), their [mods](/wiki/Game_modifier) activated (if any, see \\#7), their osu! ranking, and their team (if applicable).\n5. The name of the match and the password settings.\n6. The beatmap selected. It shows the beatmap as it would in the solo song selection screen.\n7. The [mods](/wiki/Game_modifier) that you have activated (see #12), as well as the option to select them. The option marked \"Free Mods\" toggles whether or not players can select their own mods. If yes, they can pick any combination of mods *except for speed-altering mods like [Double Time](/wiki/Game_modifier/Double_Time)*. If no, the host decides what mods will be used. The host can pick speed-altering mods regardless of whether or not Free Mods is turned on.\n8. The team mode and win conditions.\n9. The ready button.\n10. The [chat console](/wiki/Chat_Console).\n11. The leave button.\n12. Where your activated mods appear.\n\n## Song Selection Screen\n\n![](img/song-selection.jpg \"Song Selection\")\n\nYou can identify the current mode selected by either looking at the icon in the bottom left, above Mode, or by looking at the transparent icon in the center of the screen. These are the four you will see:\n\n- ![](/wiki/shared/mode/osu.png) is [osu!](/wiki/Game_mode/osu!)\n- ![](/wiki/shared/mode/taiko.png) is [osu!taiko](/wiki/Game_mode/osu!taiko)\n- ![](/wiki/shared/mode/catch.png) is [osu!catch](/wiki/Game_mode/osu!catch)\n- ![](/wiki/shared/mode/mania.png) is [osu!mania](/wiki/Game_mode/osu!mania)\n\nBefore continuing on, this screen has too many elements to note with easily, noticeable numbers. The subsections below will focus on one part of the screen at a time, starting from the top down and left to right.\n\n### Beatmap Information\n\n![](img/metadata-comparison.jpg)\n\n![](img/beatmap-metadata.jpg)\n\nThis area displays **information on the beatmap difficulty currently selected.** By default, the beatmap whose song is heard in the osu! jukebox is selected when entering the selection screen. In the top left is the ranked status of the beatmap. The title is next. Normally, the romanised title is shown, but if you select `Prefer metadata in original language` in the [Options](/wiki/Options), it will show the Unicode title; this is shown in the upper picture. The beatmapper is also shown, and beatmap information is shown below. From left to right, the values are as follows:\n\n- **Length**: The total length of the beatmap, from start to finish and including breaks. Not to be confused with [drain time](/wiki/Glossary#drain-time).\n- **BPM**: The BPM of the beatmap. If (like in the lower picture) there are two BPMS and one in parentheses, this means that the BPM changes throughout the song. It shows the slowest and fastest BPMs, and the value in parentheses is the BPM at the start of the beatmap.\n- **Objects**: The total amount of [hit objects](/wiki/Hit_Objects) in the beatmap.\n- **Circles**: The total amount of hit circles in the beatmap.\n- **Sliders**: The total amount of sliders in the beatmap.\n- **Spinners**: The total amount of spinners in the beatmap.\n- **OD**: The Overall Difficulty of the beatmap.\n- **HP**: The drain rate of your HP. In osu!, this is how much of an HP loss you receive upon missing a note, how fast the life bar idly drains, and how much HP is received for hitting a note. In osu!mania, this is the same except there is no idle HP drain. In osu!taiko, this determines how slowly the HP bar fills and how much HP is lost when a note is missed. osu!catch is the same as osu!.\n- **Stars**: The star difficulty of the beatmap. This is graphically visible in the beatmap rectangle itself.\n\n### Group and Sort\n\n![](img/beatmap-filters.jpg)\n\nClick on one of the tabs to **sort your song list according to the selected criterion**.\n\n**Group** - Most options organize beatmaps into various expandable groups:\n\n- `No grouping` - Beatmaps will not be grouped but will still be sorted in the order specified by Sort.\n- `By Difficulty` - Beatmaps will be grouped by their star difficulty, rounded to the nearest whole number.\n- `By Artist` - Beatmaps will be grouped by the artist's first character of their name.\n- `Recently Played` - Beatmaps will be grouped by when you last played them.\n- `Collections` - This will show the collections you have created. *Note that this will hide beatmaps not listed in a collection!*\n- `By BPM` - Beatmaps will be grouped according to BPM in multiples of 60, starting at 120.\n- `By Creator` - Beatmaps will be grouped by the beatmap creator's name's first character.\n- `By Date Added` - Beatmaps will be grouped according to when they were added, from today to 4+ months ago.\n- `By Length` - Beatmaps will be grouped according to their length: 1 minute or less, 2 minutes or less, 3, 4, 5, and 10.\n- `By Mode` - Beatmaps will be grouped according to their game mode.\n- `By Rank Achieved` - Beatmaps will be sorted by the highest rank achieved on them.\n- `By Title` - Beatmaps will be grouped by the first letter of their title.\n- `Favourites` - Only beatmaps you have favorited online will be shown.\n- `My Maps` - Only beatmaps you have mapped (that is, whose creator matches your profile name) will be shown.\n- `Ranked Status` - Beatmaps will be grouped by their ranked status: ranked, pending, not submitted, unknown, or loved.\n\nThe first five groupings are available in tabs below Group and Sort.\n\n**Sort** - Sorts beatmaps in a certain order\n\n- `By Artist` - Beatmaps will be sorted alphabetically by the artist's name's first character.\n- `By BPM` - Beatmaps will be sorted lowest to highest by their BPM. For maps with multiple BPMs, the highest will be used.\n- `By Creator` - Beatmaps will be sorted alphabetically by the creator's name's first character.\n- `By Date Added` - Beatmaps will be sorted from oldest to newest by when they were added.\n- `By Difficulty` - Beatmaps will be sorted from easiest to hardest by star difficulty. *Note that this will split apart mapsets!*\n- `By Length` - Beatmaps will be sorted from shortest to longest by length.\n- `By Rank Achieved` - Beatmaps will be sorted from poorest to best by the highest rank achieved on them.\n- `By Title` - Beatmaps will be sorted alphabetically by the first character of their name.\n\n### Search\n\n![](img/search-bar.jpg)\n\n*Note: You cannot have the chat console or the options sidebar open if you want to search; otherwise, anything you type will be perceived as chat text or as an options search query.*\n\nOnly beatmaps that match the criteria of your search will be shown. By default, any search will be matched against the beatmaps' artists, titles, creators, and tags.\n\nIn addition to searching these fields, you can use filters to search through other metadata by combining one of the supported filters with a comparison to a value (for example, `ar=9`).\n\nSupported filters:\n\n- `artist`: Name of the artist\n- `creator`: Name of the beatmap creator\n- `ar`: Approach Rate\n- `cs`: Circle Size\n- `od`: Overall Difficulty\n- `hp`: HP Drain Rate\n- `keys`: Number of keys (osu!mania and converted beatmaps only)\n- `stars`: Star Difficulty\n- `bpm`: Beats per minute\n- `length`: Length in seconds\n- `drain`: Drain Time in seconds\n- `mode`: Mode. Value can be `osu`, `taiko`, `catchthebeat`, or `mania`, or `o`/`t`/`c`/`m` for short.\n- `status`: Ranked status. Value can be `ranked`, `approved`, `pending`, `notsubmitted`, `unknown`, or `loved`, or `r`/`a`/`p`/`n`/`u`/`l` for short.\n- `played`: Time since last played in days\n- `unplayed`: Shows only unplayed maps. A comparison with no set value must be used. The comparison itself is ignored.\n- `speed`: Saved osu!mania scroll speed. Always 0 for unplayed maps or if the [Remember osu!mania scroll speed per beatmap](/wiki/Options#gameplay) option is off\n\nSupported comparisons:\n\n- `=` or `==`: Equal to\n- `!=`: Not equal to\n- `<`: Less than\n- `>`: Greater than\n- `<=`: Less than or equal to\n- `>=`: Greater than or equal to\n\nYou may also enter a beatmap or beatmapset ID in your search to get a single result.\n\n### Rankings\n\n![](img/leaderboards.jpg)\n\n A variety of things can appear in this space:\n\n- A \"Not Submitted\" box denotes a beatmap that has not been uploaded to the osu! site using the Beatmap Submission System or was deleted by the mapper.\n- An \"Update to latest version\" box appears if there is a new version of the beatmap available for download. Click on the button to update.\n - **Note:** Once you update the beatmap, it cannot be reversed. If you want to preserve the older version for some reason (say, to keep scores), then do not update.\n- A \"Latest pending version\" box appears means that the beatmap has been uploaded to the osu!website but is not ranked yet.\n- If replays matching the view setting of the beatmap exist, they will be displayed instead of a box denoting the ranked/played status of the beatmap. This is shown in the above picture.\n - Under public rankings (e.g. Global, Friends, etc.), your high score will be shown at the bottom, as well as your rank on the leaderboard.\n- A \"No records set!\" box means that there are no replays for the current view setting (this is typically seen in the Local view setting if you just downloaded or edited the beatmap).\n - Note: Scores for Multi are not counted as records.\n\nThese are the view settings:\n\n- Local Ranking\n- Country Ranking\\*\n- Global Ranking\n- Global Ranking (Selected Mods)\\*\n- Friend Ranking\\*\n\n\\*Requires you to be an [osu!supporter](/wiki/osu!supporter) to access them.\n\nClick the word bubble icon to call up the **Quick Web Access** screen for the selected beatmap:\n\n- Press `1` or click the `Beatmap Listing/Scores` button and your default internet browser will pull up the Beatmap Listing and score page of the beatmap set the selected beatmap belongs to.\n- Press `2` or click `Beatmap Modding` and your default internet browser will pull up the modding page of the beatmap set the selected beatmap belongs to.\n- Press `3` or `Esc` or click `Cancel` to return to the Song Selection Screen.\n\nWhile you are on the Quick Web Access Screen, you cannot access the Chat and Extended Chat Consoles.\n\n### Song\n\n![](img/beatmap-cards.jpg)\n\nThe song list displays all available beatmaps. Different beatmaps may have different coloured boxes:\n\n- **Pink**: This beatmap has not been played yet.\n- **Orange**: At least one beatmap from the beatmapset has been completed.\n- **Light Blue**: Other beatmaps in the same set, shown when a mapset is expanded.\n- **White**: Currently selected beatmap.\n\nYou can navigate the beatmap list by using the mouse wheel, using the up and down arrow keys, dragging it while holding the left mouse button or clicking the right mouse button (previously known as Absolute Scrolling), which will move the scroll bar to your mouse's Y position. Click on a box to select that beatmap and display its information on the upper left, high scores (if any) on the left and, if you've cleared it, the letter grade of the highest score you've achieved. Click the box again, press `Enter` or click the osu!cookie at the lower right to begin playing the beatmap.\n\n### Gameplay toolbox\n\n![](img/game-mode-selector.jpg \"List of available game modes\")\n\n![](img/gameplay-toolbox.jpg)\n\nThis section can be called the gameplay toolbox. We will cover each button's use from left to right.\n\nPress `Esc` or click the `Back` button to return to main menu.\n\nClick on the `Mode` button to open up a list of gameplay modes available on osu!. Click on your desired gameplay mode and osu! will switch to that gameplay mode style - the scoreboard will change accordingly. Alternatively, you can press `Ctrl` and `1` (osu!), `2` (osu!taiko), `3` (osu!catch), or `4` (osu!mania) to change the gamemode.\n\nThe background transparent icon and the \"Mode\" box will change to depict what mode is currently selected.\n\n![](img/game-modifiers.jpg \"Mod Selection Screen\")\n\nClick the `Mods` button or press `F1` to open the **[Mod Selection Screen](/wiki/Game_modifier)**.\n\nIn this screen, you can apply modifications (\"mods\" for short) to gameplay. Some mods lower difficulty and apply a multiplier that lowers the score you achieve. Conversely, some mods increase the difficulty, but apply a multiplier that increases the score you achieve. Finally, some mods modify gameplay in a different way. [Relax](/wiki/Game_modifier/Relax) and [Auto Pilot](/wiki/Game_modifier/Autopilot) fall in that category.\n\nPlace your mouse on a mod's icon to see a short description of its effect. Click on an icon to select or deselect that mod. Some mods, like Double Time, have multiple variations; click on the mod again to cycle through. The score multiplier value displays the combined effect the multipliers of the mod(s) of you have selected will have on your score. Click `Reset all mods` or press `1` to deselect all currently selected mods. Click `Close` or press `2` or `Esc` to return to the Song Selection Screen.\n\nWhile you are on the Mod Selection Screen, you cannot access the Chat and Extended Chat Consoles. In addition, skins can alter the text and/or icon of the mods, but the effects will still be the same.\n\nClick the `Random` button or press `F2` to have the game **randomly scroll through all of your beatmaps and pick one.** You cannot select a beatmap yourself until it has finished scrolling.\n\n*Note: You can press `Shift` + the `Random` button or `F2` to go back to the beatmap you had selected before you randomized your selection.*\n\n![](img/beatmap-options.jpg \"Possible commands for a beatmap\")\n\nClick the `Beatmap Options` button, press `F3` or right-click your mouse while hovering over the beatmap to call up the **Beatmap Options Menu for options on the currently selected beatmap**.\n\n- Press `1` or click the `Manage Collections` button to bring up the Collections screen - here, you can manage pre-existing collections, as well as add or remove the currently selected beatmap or mapset to or from a collection.\n- Press `2` or click `Delete...` to delete the \\[1\\] currently selected beatmapset, \\[2\\] delete the currently selected beatmap, or \\[3\\] delete **all VISIBLE beatmaps**.\n - Note that deleted beatmaps are moved to the Recycle Bin.\n- Press `3` or click `Remove from Unplayed` to mark an unplayed beatmap as played (that is, change its box colour from pink to orange).\n- Press `4` or click `Clear local scores` to delete all records of the scores you have achieved in this beatmap.\n- Press `5` or click `Edit` to open the selected beatmap in osu!'s Editor.\n- Press `6` or `Esc` or click `Close` to return to the Song Selection Screen.\n\nClick on **your user panel** to access the **User Options Menu**.\n\nClick the **[osu!cookie](/wiki/Glossary#cookie)** to **start playing the selected beatmap**.\n\n## Results screen\n\n![](img/results-osu.jpg \"Accuracy in osu!\")\n\nThis is the results screen shown after you have successfully passed the beatmap. You can access your online results by scrolling down or pressing the obvious button.\n\n**Note:** The results screen may change depending on the used skin.\n\nBelow are the results screens of the other game modes.\n\n![](img/results-taiko.jpg \"Accuracy in osu!taiko\")\n\n![](img/results-mania.jpg \"Accuracy in osu!mania\")\n\n![](img/results-catch.jpg \"Accuracy in osu!catch\")\n\n### Online Leaderboard\n\n![](img/extended-results-screen.jpg \"An example of an osu!online score\")\n\nThis is your online leaderboard. You can go here by scrolling down from the results screen. Your Local Scoreboard will show your name and the score as usual.\n\n1. Your player bar. It shows your [PP](/wiki/Performance_Points), Global Rank, Total Score, Overall [Accuracy](/wiki/Accuracy), and level bar.\n2. `Save replay to Replays folder`: You can watch the replay later either by opening it from a local leaderboard, or by going to `Replays` directory and double clicking it.\n3. `Add as online favourite`: Include the beatmap into your list of favourites, which is located on your osu! profile page under the \"Beatmaps\" section.\n4. Local Leaderboard: All your results are stored on your computer. To see them, navigate to the [song selection screen](#song-selection-screen), then select `Local Rankings` from the drop-down menu on the left.\n5. `Beatmap Ranking` section. Available only for maps with online leaderboards ([qualified](/wiki/Beatmap/Category#qualified), [ranked](/wiki/Beatmap/Category#ranked), or [loved](/wiki/Beatmap/Category#loved)). You also need to be online to see this section.\n 1. `Overall`: Your position on the map's leaderboard, where you compete against players that used [mods](/wiki/Game_modifier), even if you didn't use any yourself.\n 2. `Accuracy`: How [precisely](/wiki/Accuracy) did you play the beatmap. Will only be counted when your old score is surpassed.\n 3. `Max Combo`: Your longest combo on the map you played.\n 4. `Ranked Score`: Your [best result](/wiki/Score#ranked-score) on the beatmap.\n 5. `Total Score`: Not taken into account, since it does not affect your position in online rankings.\n 6. `Performance`: The amount of [unweighted PP](/wiki/Performance_points#why-didnt-i-gain-the-full-amount-of-pp-from-a-map-i-played) you would receive for the play.\n6. `Overall Ranking` section. It's available only for beatmaps with online leaderboards. You also need to be online to see this section.\n 1. `Overall`: Your global ranking in the world.\n 2. `Accuracy`: Your average [accuracy](/wiki/Accuracy#accuracy) over all beatmaps you have played.\n 3. `Max Combo`: The longest combo over all beatmaps you have played.\n 4. [`Ranked Score`](/wiki/Score#ranked-score): The number of points earned from all ranked beatmaps that you have ever played, with every map being counted exactly once.\n 5. [`Total Score`](/wiki/Score#total-score): Same as ranked score, but it takes into account all beatmaps available on the osu! website, and also underplayed or failed beatmaps. This counts towards your level.\n 6. `Perfomance`: Displays your total amount of Performance Points, and also how many PP the submitted play was worth.\n7. Information about the beatmap with its playcount and pass rate.\n8. Beatmap rating. Use your personal discretion based on whether you enjoy the beatmap or not. Best left alone if you can't decide.\n9. Click here to return to the song selection screen.\n\n![](img/medal-unlock.jpg \"Unlocking a medal\")\n\nAbove is what it looks like to receive a medal.\n", }; } } From 1848bd902d11f5053237b8aa15636a96931f3867 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 May 2021 19:51:07 +0900 Subject: [PATCH 346/839] Fix skin editor context menus not dismissing when clicking away --- osu.Game/Skinning/Editor/SkinEditor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index f24b0c71c0..07a94cac7a 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -19,7 +19,7 @@ using osuTK; namespace osu.Game.Skinning.Editor { [Cached(typeof(SkinEditor))] - public class SkinEditor : FocusedOverlayContainer + public class SkinEditor : VisibilityContainer { public const double TRANSITION_DURATION = 500; From 0f4b502fdf8c1f9651d0b81dc6828f8358826d59 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 May 2021 20:09:22 +0900 Subject: [PATCH 347/839] Add missing xmldoc --- osu.Game/Graphics/UserInterface/TernaryStateRadioMenuItem.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Graphics/UserInterface/TernaryStateRadioMenuItem.cs b/osu.Game/Graphics/UserInterface/TernaryStateRadioMenuItem.cs index aa83b0567b..46eda06294 100644 --- a/osu.Game/Graphics/UserInterface/TernaryStateRadioMenuItem.cs +++ b/osu.Game/Graphics/UserInterface/TernaryStateRadioMenuItem.cs @@ -5,6 +5,9 @@ using System; namespace osu.Game.Graphics.UserInterface { + /// + /// A ternary state menu item which will always set the item to true on click, even if already true. + /// public class TernaryStateRadioMenuItem : TernaryStateMenuItem { /// From c48b5eebdd7346cbea7b37c727f925f12e173c37 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 20 May 2021 15:45:39 +0300 Subject: [PATCH 348/839] Don't reload the context when clicking selected year button --- osu.Game/Overlays/News/Sidebar/YearsPanel.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/News/Sidebar/YearsPanel.cs b/osu.Game/Overlays/News/Sidebar/YearsPanel.cs index 232b995cd6..b07c9924b9 100644 --- a/osu.Game/Overlays/News/Sidebar/YearsPanel.cs +++ b/osu.Game/Overlays/News/Sidebar/YearsPanel.cs @@ -109,7 +109,11 @@ namespace osu.Game.Overlays.News.Sidebar { IdleColour = isCurrent ? Color4.White : colourProvider.Light2; HoverColour = isCurrent ? Color4.White : colourProvider.Light1; - Action = () => overlay?.ShowYear(Year); + Action = () => + { + if (!isCurrent) + overlay?.ShowYear(Year); + }; } } } From 40ca94cd7b9e16d32b81b199e7a24f22f191d6ce Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 20 May 2021 16:04:51 +0300 Subject: [PATCH 349/839] Fix incorrect year being passed on first load --- osu.Game/Overlays/NewsOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/NewsOverlay.cs b/osu.Game/Overlays/NewsOverlay.cs index 510cdba020..af3fa9c3b0 100644 --- a/osu.Game/Overlays/NewsOverlay.cs +++ b/osu.Game/Overlays/NewsOverlay.cs @@ -126,7 +126,7 @@ namespace osu.Game.Overlays loadArticle(article.NewValue); } - private void loadFrontPage(int year = 0) + private void loadFrontPage(int? year = null) { beginLoading(); From 262a27610c94dbaae44925e88534166b34318fd9 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 20 May 2021 20:47:40 +0300 Subject: [PATCH 350/839] Improve components assertion logic --- .../Gameplay/TestSceneBeatmapSkinFallbacks.cs | 39 ++++++++++++++----- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs index 1d0caa86e2..dd77a71ca5 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs @@ -7,7 +7,9 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Lists; +using osu.Framework.Logging; using osu.Framework.Testing; using osu.Framework.Timing; using osu.Framework.Utils; @@ -60,24 +62,41 @@ namespace osu.Game.Tests.Visual.Gameplay protected bool AssertComponentsFromExpectedSource(SkinnableTarget target, ISkin expectedSource) { + var actualComponentsContainer = Player.ChildrenOfType().First(s => s.Target == target) + .ChildrenOfType().SingleOrDefault(); + + if (actualComponentsContainer == null) + return false; + + var actualInfo = actualComponentsContainer.CreateSkinnableInfo(); + var expectedComponentsContainer = (SkinnableTargetComponentsContainer)expectedSource.GetDrawableComponent(new SkinnableTargetComponent(target)); + if (expectedComponentsContainer == null) + return false; - Add(expectedComponentsContainer); - expectedComponentsContainer?.UpdateSubTree(); + var expectedComponentsAdjustmentContainer = new Container + { + Position = actualComponentsContainer.Parent.ToSpaceOfOtherDrawable(actualComponentsContainer.DrawPosition, Content), + Size = actualComponentsContainer.DrawSize, + Child = expectedComponentsContainer, + }; + + Add(expectedComponentsAdjustmentContainer); + expectedComponentsAdjustmentContainer?.UpdateSubTree(); var expectedInfo = expectedComponentsContainer?.CreateSkinnableInfo(); - Remove(expectedComponentsContainer); + Remove(expectedComponentsAdjustmentContainer); - var actualInfo = Player.ChildrenOfType().First(s => s.Target == target) - .ChildrenOfType().Single().CreateSkinnableInfo(); + return almostEqual(actualInfo, expectedInfo); - return almostEqual(actualInfo, expectedInfo, 2f); - - static bool almostEqual(SkinnableInfo info, SkinnableInfo other, float positionTolerance) => + static bool almostEqual(SkinnableInfo info, SkinnableInfo other) => other != null + && info.Type == other.Type && info.Anchor == other.Anchor && info.Origin == other.Origin - && Precision.AlmostEquals(info.Position, other.Position, positionTolerance) - && info.Children.SequenceEqual(other.Children, new FuncEqualityComparer((s1, s2) => almostEqual(s1, s2, positionTolerance))); + && Precision.AlmostEquals(info.Position, other.Position) + && Precision.AlmostEquals(info.Scale, other.Scale) + && Precision.AlmostEquals(info.Rotation, other.Rotation) + && info.Children.SequenceEqual(other.Children, new FuncEqualityComparer(almostEqual)); } protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) From eaae9a1b678ae64b3c680c7ff1d172205b122396 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 20 May 2021 21:08:31 +0300 Subject: [PATCH 351/839] Remove unrequired null conditional --- .../Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs index dd77a71ca5..ecdb6fa4af 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs @@ -82,8 +82,8 @@ namespace osu.Game.Tests.Visual.Gameplay }; Add(expectedComponentsAdjustmentContainer); - expectedComponentsAdjustmentContainer?.UpdateSubTree(); - var expectedInfo = expectedComponentsContainer?.CreateSkinnableInfo(); + expectedComponentsAdjustmentContainer.UpdateSubTree(); + var expectedInfo = expectedComponentsContainer.CreateSkinnableInfo(); Remove(expectedComponentsAdjustmentContainer); return almostEqual(actualInfo, expectedInfo); From c0dfe379655d9e68496d0de3a6182a764c1f904f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 20 May 2021 21:08:36 +0300 Subject: [PATCH 352/839] Remove unused using directive --- osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs index ecdb6fa4af..22959c082a 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs @@ -9,7 +9,6 @@ using osu.Framework.Audio; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Lists; -using osu.Framework.Logging; using osu.Framework.Testing; using osu.Framework.Timing; using osu.Framework.Utils; From 092d0f9b7678a075011029b3300a07b5d111f70f Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 21 May 2021 01:20:18 +0700 Subject: [PATCH 353/839] add breadcrumb header test scene --- .../TestSceneBreadcrumbControlHeader.cs | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneBreadcrumbControlHeader.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBreadcrumbControlHeader.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBreadcrumbControlHeader.cs new file mode 100644 index 0000000000..1439c65a14 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBreadcrumbControlHeader.cs @@ -0,0 +1,86 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Overlays; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneBreadcrumbControlHeader : OsuTestScene + { + private static readonly string[] items = { "first", "second", "third", "fourth", "fifth" }; + + [Cached] + private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Red); + + private TestHeader header; + + [SetUp] + public void SetUp() => Schedule(() => + { + Child = header = new TestHeader + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }; + }); + + [Test] + public void TestAddAndRemoveItem() + { + foreach (var item in items.Skip(1)) + AddStep($"Add {item} item", () => header.AddItem(item)); + + foreach (var item in items.Reverse().SkipLast(3)) + AddStep($"Remove {item} item", () => header.RemoveItem(item)); + + AddStep($"Clear item", () => header.ClearItem()); + + foreach (var item in items) + AddStep($"Add {item} item", () => header.AddItem(item)); + + foreach (var item in items) + AddStep($"Remove {item} item", () => header.RemoveItem(item)); + } + + private class TestHeader : BreadcrumbControlOverlayHeader + { + public TestHeader() + { + TabControl.AddItem(items[0]); + Current.Value = items[0]; + } + + public void AddItem(string value) + { + TabControl.AddItem(value); + Current.Value = TabControl.Items.LastOrDefault(); + } + + public void RemoveItem(string value) + { + TabControl.RemoveItem(value); + Current.Value = TabControl.Items.LastOrDefault(); + } + + public void ClearItem() + { + TabControl.Clear(); + Current.Value = null; + } + + protected override OverlayTitle CreateTitle() => new TestTitle(); + } + + private class TestTitle : OverlayTitle + { + public TestTitle() + { + Title = "Test Title"; + } + } + } +} From 236124496d208c9bcf554ee36ead97170f5705ce Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 21 May 2021 01:20:38 +0700 Subject: [PATCH 354/839] add missing accent colour in control tab item --- osu.Game/Overlays/BreadcrumbControlOverlayHeader.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BreadcrumbControlOverlayHeader.cs b/osu.Game/Overlays/BreadcrumbControlOverlayHeader.cs index 81315f9638..443b3dcf01 100644 --- a/osu.Game/Overlays/BreadcrumbControlOverlayHeader.cs +++ b/osu.Game/Overlays/BreadcrumbControlOverlayHeader.cs @@ -26,7 +26,10 @@ namespace osu.Game.Overlays AccentColour = colourProvider.Light2; } - protected override TabItem CreateTabItem(string value) => new ControlTabItem(value); + protected override TabItem CreateTabItem(string value) => new ControlTabItem(value) + { + AccentColour = AccentColour, + }; private class ControlTabItem : BreadcrumbTabItem { From b521405ec8f06393b2b1d841dd5ea4a7c148ae66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 20 May 2021 20:46:18 +0200 Subject: [PATCH 355/839] Trim redundant string interpolation --- .../Visual/UserInterface/TestSceneBreadcrumbControlHeader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBreadcrumbControlHeader.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBreadcrumbControlHeader.cs index 1439c65a14..45868b2872 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBreadcrumbControlHeader.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBreadcrumbControlHeader.cs @@ -37,7 +37,7 @@ namespace osu.Game.Tests.Visual.UserInterface foreach (var item in items.Reverse().SkipLast(3)) AddStep($"Remove {item} item", () => header.RemoveItem(item)); - AddStep($"Clear item", () => header.ClearItem()); + AddStep("Clear item", () => header.ClearItem()); foreach (var item in items) AddStep($"Add {item} item", () => header.AddItem(item)); From f35a07fee729586abf7723a742a146164c866fd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 20 May 2021 20:47:50 +0200 Subject: [PATCH 356/839] Rename method for better comprehension --- .../Visual/UserInterface/TestSceneBreadcrumbControlHeader.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBreadcrumbControlHeader.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBreadcrumbControlHeader.cs index 45868b2872..90c3e142df 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBreadcrumbControlHeader.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBreadcrumbControlHeader.cs @@ -37,7 +37,7 @@ namespace osu.Game.Tests.Visual.UserInterface foreach (var item in items.Reverse().SkipLast(3)) AddStep($"Remove {item} item", () => header.RemoveItem(item)); - AddStep("Clear item", () => header.ClearItem()); + AddStep("Clear items", () => header.ClearItems()); foreach (var item in items) AddStep($"Add {item} item", () => header.AddItem(item)); @@ -66,7 +66,7 @@ namespace osu.Game.Tests.Visual.UserInterface Current.Value = TabControl.Items.LastOrDefault(); } - public void ClearItem() + public void ClearItems() { TabControl.Clear(); Current.Value = null; From 49aa0f3a180cd203d6ca1031b0a7ef8d3c7c4deb Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 21 May 2021 10:04:45 +0700 Subject: [PATCH 357/839] initial main page --- .../Visual/Online/TestSceneWikiMainPage.cs | 36 +++++++++++++++++++ osu.Game/Overlays/Wiki/WikiMainPage.cs | 17 +++++++++ 2 files changed, 53 insertions(+) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs create mode 100644 osu.Game/Overlays/Wiki/WikiMainPage.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs new file mode 100644 index 0000000000..e193f5b98e --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs @@ -0,0 +1,36 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Overlays; +using osu.Game.Overlays.Wiki; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneWikiMainPage : OsuTestScene + { + [Cached] + private readonly OverlayColourProvider overlayColour = new OverlayColourProvider(OverlayColourScheme.Orange); + + public TestSceneWikiMainPage() + { + Children = new Drawable[] + { + new Box + { + Colour = overlayColour.Background5, + RelativeSizeAxes = Axes.Both, + }, + new BasicScrollContainer + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding(20), + Child = new WikiMainPage(), + } + }; + } + } +} diff --git a/osu.Game/Overlays/Wiki/WikiMainPage.cs b/osu.Game/Overlays/Wiki/WikiMainPage.cs new file mode 100644 index 0000000000..1ae1631a6e --- /dev/null +++ b/osu.Game/Overlays/Wiki/WikiMainPage.cs @@ -0,0 +1,17 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; + +namespace osu.Game.Overlays.Wiki +{ + public class WikiMainPage : FillFlowContainer + { + public WikiMainPage() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + } + } +} From 5964ee23cb2fb95bbecd94115b10d55bfe0344fb Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 21 May 2021 09:54:54 +0700 Subject: [PATCH 358/839] add HtmlAgilityPack dependency --- osu.Game/osu.Game.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index fa2945db6a..e3331cd365 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -19,6 +19,7 @@ + From 216b87691c203c35f76263859dfbe56dda9ab5d4 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 21 May 2021 10:04:45 +0700 Subject: [PATCH 359/839] add blurb --- osu.Game/Overlays/Wiki/WikiMainPage.cs | 41 ++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/osu.Game/Overlays/Wiki/WikiMainPage.cs b/osu.Game/Overlays/Wiki/WikiMainPage.cs index 1ae1631a6e..cb6c80cb73 100644 --- a/osu.Game/Overlays/Wiki/WikiMainPage.cs +++ b/osu.Game/Overlays/Wiki/WikiMainPage.cs @@ -3,15 +3,56 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using System.Linq; +using HtmlAgilityPack; +using osu.Framework.Allocation; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; namespace osu.Game.Overlays.Wiki { public class WikiMainPage : FillFlowContainer { + public string Markdown; + public WikiMainPage() { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; } + + [BackgroundDependencyLoader] + private void load() + { + var html = new HtmlDocument(); + html.LoadHtml(Markdown); + + Children = new Drawable[] + { + createBlurb(html) + }; + } + + private Container createBlurb(HtmlDocument html) + { + var blurbNode = html.DocumentNode.SelectNodes("//div[contains(@class, 'wiki-main-page__blurb')]").First(); + + return new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding + { + Vertical = 30, + }, + Child = new OsuSpriteText + { + Text = blurbNode.InnerText, + Font = OsuFont.GetFont(size: 12), + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + } + }; + } } } From 895eb14c5ae222877b49cd322409f0ce3736f927 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 21 May 2021 14:09:30 +0900 Subject: [PATCH 360/839] Forcefully end playing to fix test failures --- osu.Game/Online/Spectator/SpectatorClient.cs | 3 +++ osu.Game/Screens/Play/Player.cs | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/osu.Game/Online/Spectator/SpectatorClient.cs b/osu.Game/Online/Spectator/SpectatorClient.cs index cb98b01bed..3a29e73b8f 100644 --- a/osu.Game/Online/Spectator/SpectatorClient.cs +++ b/osu.Game/Online/Spectator/SpectatorClient.cs @@ -174,6 +174,9 @@ namespace osu.Game.Online.Spectator public void EndPlaying() { + if (!IsPlaying) + return; + IsPlaying = false; currentBeatmap = null; diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 6317a41bec..39f9e2d388 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -22,6 +22,7 @@ using osu.Game.Configuration; using osu.Game.Graphics.Containers; using osu.Game.IO.Archives; using osu.Game.Online.API; +using osu.Game.Online.Spectator; using osu.Game.Overlays; using osu.Game.Replays; using osu.Game.Rulesets; @@ -93,6 +94,9 @@ namespace osu.Game.Screens.Play [Resolved] private MusicController musicController { get; set; } + [Resolved] + private SpectatorClient spectatorClient { get; set; } + private Sample sampleRestart; public BreakOverlay BreakOverlay; @@ -882,6 +886,11 @@ namespace osu.Game.Screens.Play return true; } + // EndPlaying() is typically called from ReplayRecorder.Dispose(). Disposal is currently asynchronous. + // To resolve test failures, forcefully end playing synchronously when this screen exits. + // Todo: Replace this with a more permanent solution once osu-framework has a synchronous cleanup method. + spectatorClient.EndPlaying(); + // GameplayClockContainer performs seeks / start / stop operations on the beatmap's track. // as we are no longer the current screen, we cannot guarantee the track is still usable. (GameplayClockContainer as MasterGameplayClockContainer)?.StopUsingBeatmapClock(); From c00e6e29a6f2c32da0ae882f98344afb94770812 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 May 2021 14:21:56 +0900 Subject: [PATCH 361/839] Remove `static` usage --- .../Edit/Blueprints/HitPlacementBlueprint.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.cs b/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.cs index 17e7fb81f6..0d0fd136a7 100644 --- a/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.cs @@ -14,10 +14,10 @@ namespace osu.Game.Rulesets.Taiko.Edit.Blueprints { private readonly HitPiece piece; - private static Hit hit; + public new Hit HitObject => (Hit)base.HitObject; public HitPlacementBlueprint() - : base(hit = new Hit()) + : base(new Hit()) { InternalChild = piece = new HitPiece { @@ -30,12 +30,12 @@ namespace osu.Game.Rulesets.Taiko.Edit.Blueprints switch (e.Button) { case MouseButton.Left: - hit.Type = HitType.Centre; + HitObject.Type = HitType.Centre; EndPlacement(true); return true; case MouseButton.Right: - hit.Type = HitType.Rim; + HitObject.Type = HitType.Rim; EndPlacement(true); return true; } From 40c8378d81a67b944c63d54a86b413ab70af7cb7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 May 2021 14:37:22 +0900 Subject: [PATCH 362/839] Fix type-to-sample mapping being applied too late --- .../Objects/Drawables/DrawableHit.cs | 28 +---------------- osu.Game.Rulesets.Taiko/Objects/Hit.cs | 31 ++++++++++++++++++- 2 files changed, 31 insertions(+), 28 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index 38cda69a46..948d2c2f69 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -55,16 +55,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables type.BindValueChanged(_ => { updateActionsFromType(); - - // will overwrite samples, should only be called on subsequent changes - // after the initial application. - updateSamplesFromTypeChange(); - RecreatePieces(); - }); - - // action update also has to happen immediately on application. - updateActionsFromType(); + }, true); base.OnApply(); } @@ -92,24 +84,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables type.Value = getRimSamples().Any() ? HitType.Rim : HitType.Centre; } - private void updateSamplesFromTypeChange() - { - var rimSamples = getRimSamples(); - - bool isRimType = HitObject.Type == HitType.Rim; - - if (isRimType != rimSamples.Any()) - { - if (isRimType) - HitObject.Samples.Add(new HitSampleInfo(HitSampleInfo.HIT_CLAP)); - else - { - foreach (var sample in rimSamples) - HitObject.Samples.Remove(sample); - } - } - } - private void updateActionsFromType() { HitActions = diff --git a/osu.Game.Rulesets.Taiko/Objects/Hit.cs b/osu.Game.Rulesets.Taiko/Objects/Hit.cs index 1b51288605..f4a66c39a8 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Hit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Hit.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.Linq; using osu.Framework.Bindables; +using osu.Game.Audio; namespace osu.Game.Rulesets.Taiko.Objects { @@ -15,9 +17,36 @@ namespace osu.Game.Rulesets.Taiko.Objects public HitType Type { get => TypeBindable.Value; - set => TypeBindable.Value = value; + set + { + TypeBindable.Value = value; + updateSamplesFromType(); + } } + private void updateSamplesFromType() + { + var rimSamples = getRimSamples(); + + bool isRimType = Type == HitType.Rim; + + if (isRimType != rimSamples.Any()) + { + if (isRimType) + Samples.Add(new HitSampleInfo(HitSampleInfo.HIT_CLAP)); + else + { + foreach (var sample in rimSamples) + Samples.Remove(sample); + } + } + } + + /// + /// Returns an array of any samples which would cause this object to be a "rim" type hit. + /// + private HitSampleInfo[] getRimSamples() => Samples.Where(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE).ToArray(); + protected override StrongNestedHitObject CreateStrongNestedHit(double startTime) => new StrongNestedHit { StartTime = startTime }; public class StrongNestedHit : StrongNestedHitObject From f9d51656b6dff672eab55e46eb1bad287d70b84e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 May 2021 15:02:36 +0900 Subject: [PATCH 363/839] Fix scaling of rotated items not behaving in an understandable way --- .../Skinning/Editor/SkinSelectionHandler.cs | 61 ++++++++++--------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index 2f9611ba65..accb65483b 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -8,6 +8,7 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Utils; using osu.Game.Extensions; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Edit; @@ -37,40 +38,44 @@ namespace osu.Game.Skinning.Editor adjustScaleFromAnchor(ref scale, anchor); - var selectionQuad = GetSurroundingQuad(SelectedBlueprints.SelectMany(b => - b.Item.ScreenSpaceDrawQuad.GetVertices().ToArray())); + // the selection quad is always upright, so use an AABB rect to make mutating the values easier. + var selectionRect = GetSurroundingQuad(SelectedBlueprints.SelectMany(b => + b.Item.ScreenSpaceDrawQuad.GetVertices().ToArray())).AABBFloat; - // the selection quad is always upright, so use a rect to make mutating the values easier. - var adjustedRect = selectionQuad.AABBFloat; + // copy to mutate, as we will need to compare to the original later on. + var adjustedRect = selectionRect; - // for now aspect lock scale adjustments that occur at corners. - if (!anchor.HasFlagFast(Anchor.x1) && !anchor.HasFlagFast(Anchor.y1)) - scale.Y = scale.X / selectionQuad.Width * selectionQuad.Height; + // first, remove any scale axis we are not interested in. + if (anchor.HasFlagFast(Anchor.x1)) scale.X = 0; + if (anchor.HasFlagFast(Anchor.y1)) scale.Y = 0; - if (anchor.HasFlagFast(Anchor.x0)) + bool shouldAspectLock = + // for now aspect lock scale adjustments that occur at corners.. + (!anchor.HasFlagFast(Anchor.x1) && !anchor.HasFlagFast(Anchor.y1)) + // ..or if any of the selection have been rotated. + // this is to avoid requiring skew logic (which would likely not be the user's expected transform anyway). + || SelectedBlueprints.Any(b => !Precision.AlmostEquals(((Drawable)b.Item).Rotation % 90, 0)); + + if (shouldAspectLock) { - adjustedRect.X -= scale.X; - adjustedRect.Width += scale.X; - } - else if (anchor.HasFlagFast(Anchor.x2)) - { - adjustedRect.Width += scale.X; + if (anchor.HasFlagFast(Anchor.x1)) + // if dragging from the horizontal centre, only a vertical component is available. + scale.X = scale.Y / selectionRect.Height * selectionRect.Width; + else + // in all other cases (arbitrarily) use the horizontal component for aspect lock. + scale.Y = scale.X / selectionRect.Width * selectionRect.Height; } - if (anchor.HasFlagFast(Anchor.y0)) - { - adjustedRect.Y -= scale.Y; - adjustedRect.Height += scale.Y; - } - else if (anchor.HasFlagFast(Anchor.y2)) - { - adjustedRect.Height += scale.Y; - } + if (anchor.HasFlagFast(Anchor.x0)) adjustedRect.X -= scale.X; + if (anchor.HasFlagFast(Anchor.y0)) adjustedRect.Y -= scale.Y; - // scale adjust should match that of the quad itself. + adjustedRect.Width += scale.X; + adjustedRect.Height += scale.Y; + + // scale adjust applied to each individual item should match that of the quad itself. var scaledDelta = new Vector2( - adjustedRect.Width / selectionQuad.Width, - adjustedRect.Height / selectionQuad.Height + adjustedRect.Width / selectionRect.Width, + adjustedRect.Height / selectionRect.Height ); foreach (var b in SelectedBlueprints) @@ -82,8 +87,8 @@ namespace osu.Game.Skinning.Editor var relativePositionInOriginal = new Vector2( - (screenPosition.X - selectionQuad.TopLeft.X) / selectionQuad.Width, - (screenPosition.Y - selectionQuad.TopLeft.Y) / selectionQuad.Height + (screenPosition.X - selectionRect.TopLeft.X) / selectionRect.Width, + (screenPosition.Y - selectionRect.TopLeft.Y) / selectionRect.Height ); var newPositionInAdjusted = new Vector2( From 0d575f572866e84421829b7002ef0b695ad4e59f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 May 2021 15:06:53 +0900 Subject: [PATCH 364/839] Remove incorrect (and unintended) modulus logic --- osu.Game/Skinning/Editor/SkinSelectionHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index accb65483b..0b6d9222ce 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -54,7 +54,7 @@ namespace osu.Game.Skinning.Editor (!anchor.HasFlagFast(Anchor.x1) && !anchor.HasFlagFast(Anchor.y1)) // ..or if any of the selection have been rotated. // this is to avoid requiring skew logic (which would likely not be the user's expected transform anyway). - || SelectedBlueprints.Any(b => !Precision.AlmostEquals(((Drawable)b.Item).Rotation % 90, 0)); + || SelectedBlueprints.Any(b => !Precision.AlmostEquals(((Drawable)b.Item).Rotation, 0)); if (shouldAspectLock) { From fbe4d7e03c2d9e8aca399adb1077f2bf2a47b8b5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 May 2021 15:41:31 +0900 Subject: [PATCH 365/839] Improve code quality around cursor and upwards passing of response data --- .../Overlays/News/Displays/ArticleListing.cs | 30 +++++++------------ osu.Game/Overlays/NewsOverlay.cs | 6 ++-- 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/osu.Game/Overlays/News/Displays/ArticleListing.cs b/osu.Game/Overlays/News/Displays/ArticleListing.cs index e713b3de84..b49326a1f1 100644 --- a/osu.Game/Overlays/News/Displays/ArticleListing.cs +++ b/osu.Game/Overlays/News/Displays/ArticleListing.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Online.API.Requests; +using osu.Game.Online.API.Requests.Responses; using osuTK; namespace osu.Game.Overlays.News.Displays @@ -19,7 +20,7 @@ namespace osu.Game.Overlays.News.Displays /// public class ArticleListing : CompositeDrawable { - public Action ResponseReceived; + public Action SidebarMetadataUpdated; [Resolved] private IAPIProvider api { get; set; } @@ -98,34 +99,23 @@ namespace osu.Game.Overlays.News.Displays private CancellationTokenSource cancellationToken; - private bool initialLoad = true; - private void onSuccess(GetNewsResponse response) { cancellationToken?.Cancel(); + // only needs to be updated on the initial load, as the content won't change during pagination. + if (lastCursor == null) + SidebarMetadataUpdated?.Invoke(response.SidebarMetadata); + + // store cursor for next pagination request. lastCursor = response.Cursor; - var flow = new FillFlowContainer + LoadComponentsAsync(response.NewsPosts.Select(p => new NewsCard(p)).ToList(), loaded => { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 10), - Children = response.NewsPosts.Select(p => new NewsCard(p)).ToList() - }; + content.AddRange(loaded); - LoadComponentAsync(flow, loaded => - { - content.Add(loaded); showMore.IsLoading = false; - showMore.Alpha = lastCursor == null ? 0 : 1; - - if (initialLoad) - { - ResponseReceived?.Invoke(response); - initialLoad = false; - } + showMore.Alpha = response.Cursor != null ? 1 : 0; }, (cancellationToken = new CancellationTokenSource()).Token); } diff --git a/osu.Game/Overlays/NewsOverlay.cs b/osu.Game/Overlays/NewsOverlay.cs index af3fa9c3b0..dd6de40ecb 100644 --- a/osu.Game/Overlays/NewsOverlay.cs +++ b/osu.Game/Overlays/NewsOverlay.cs @@ -133,11 +133,11 @@ namespace osu.Game.Overlays Header.SetFrontPage(); var page = new ArticleListing(year); - page.ResponseReceived += r => + page.SidebarMetadataUpdated += metadata => Schedule(() => { - sidebar.Metadata.Value = r.SidebarMetadata; + sidebar.Metadata.Value = metadata; Loading.Hide(); - }; + }); LoadDisplay(page); } From 2fdf8aa1aa49da528b1fb3d7811a957435b7be3d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 21 May 2021 15:57:31 +0900 Subject: [PATCH 366/839] Add update thread assertions --- osu.Game/Online/Spectator/SpectatorClient.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Online/Spectator/SpectatorClient.cs b/osu.Game/Online/Spectator/SpectatorClient.cs index de5e57a1d0..b90fec09d6 100644 --- a/osu.Game/Online/Spectator/SpectatorClient.cs +++ b/osu.Game/Online/Spectator/SpectatorClient.cs @@ -10,6 +10,7 @@ using System.Linq; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Development; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Online.API; @@ -144,6 +145,8 @@ namespace osu.Game.Online.Spectator public void BeginPlaying(GameplayBeatmap beatmap, Score score) { + Debug.Assert(ThreadSafety.IsUpdateThread); + if (IsPlaying) throw new InvalidOperationException($"Cannot invoke {nameof(BeginPlaying)} when already playing"); @@ -172,6 +175,8 @@ namespace osu.Game.Online.Spectator public void WatchUser(int userId) { + Debug.Assert(ThreadSafety.IsUpdateThread); + if (watchingUsers.Contains(userId)) return; @@ -219,6 +224,8 @@ namespace osu.Game.Online.Spectator public void HandleFrame(ReplayFrame frame) { + Debug.Assert(ThreadSafety.IsUpdateThread); + if (frame is IConvertibleReplayFrame convertible) pendingFrames.Enqueue(convertible.ToLegacy(currentBeatmap)); From 7f712a4d04b64b4f9801208048f30f2a272b0a4e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 21 May 2021 15:57:39 +0900 Subject: [PATCH 367/839] Fix EndPlaying potentially doing cross-thread mutation --- osu.Game/Online/Spectator/SpectatorClient.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/Spectator/SpectatorClient.cs b/osu.Game/Online/Spectator/SpectatorClient.cs index b90fec09d6..48e2528528 100644 --- a/osu.Game/Online/Spectator/SpectatorClient.cs +++ b/osu.Game/Online/Spectator/SpectatorClient.cs @@ -167,10 +167,15 @@ namespace osu.Game.Online.Spectator public void EndPlaying() { - IsPlaying = false; - currentBeatmap = null; + // This method is most commonly called via Dispose(), which is asynchronous. + // Todo: This should not be a thing, but requires framework changes. + Schedule(() => + { + IsPlaying = false; + currentBeatmap = null; - EndPlayingInternal(currentState); + EndPlayingInternal(currentState); + }); } public void WatchUser(int userId) From 7c59fb37f10874db49102bf4e46e08406a385f60 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 21 May 2021 16:00:58 +0900 Subject: [PATCH 368/839] Move check into callback --- osu.Game/Online/Spectator/SpectatorClient.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/Spectator/SpectatorClient.cs b/osu.Game/Online/Spectator/SpectatorClient.cs index 9f270a8345..0067a55fd8 100644 --- a/osu.Game/Online/Spectator/SpectatorClient.cs +++ b/osu.Game/Online/Spectator/SpectatorClient.cs @@ -167,13 +167,13 @@ namespace osu.Game.Online.Spectator public void EndPlaying() { - if (!IsPlaying) - return; - // This method is most commonly called via Dispose(), which is asynchronous. // Todo: This should not be a thing, but requires framework changes. Schedule(() => { + if (!IsPlaying) + return; + IsPlaying = false; currentBeatmap = null; From a5ca736e3729d7a57f414834634531d4c8372c28 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 May 2021 16:10:48 +0900 Subject: [PATCH 369/839] Fix `RecreatePieces` being called more than once --- .../Objects/Drawables/DrawableHit.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index 948d2c2f69..f2b1284a95 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -52,15 +52,18 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables protected override void OnApply() { type.BindTo(HitObject.TypeBindable); - type.BindValueChanged(_ => - { - updateActionsFromType(); - RecreatePieces(); - }, true); + // this doesn't need to be run inline as RecreatePieces is called by the base call below. + type.BindValueChanged(_ => RecreatePieces()); base.OnApply(); } + protected override void RecreatePieces() + { + updateActionsFromType(); + base.RecreatePieces(); + } + protected override void OnFree() { base.OnFree(); From 7bc8a4bb5fa9e65937340f06560d5ab27ad5ac80 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 May 2021 16:15:27 +0900 Subject: [PATCH 370/839] Apply same logic changes to `IsStrong` status --- .../Objects/Drawables/DrawableHit.cs | 9 ----- .../DrawableTaikoStrongableHitObject.cs | 36 ++----------------- .../Objects/TaikoStrongableHitObject.cs | 26 +++++++++++++- 3 files changed, 27 insertions(+), 44 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index f2b1284a95..5cb2024bb7 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -78,15 +78,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables validActionPressed = pressHandledThisFrame = false; } - private HitSampleInfo[] getRimSamples() => HitObject.Samples.Where(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE).ToArray(); - - protected override void LoadSamples() - { - base.LoadSamples(); - - type.Value = getRimSamples().Any() ? HitType.Rim : HitType.Centre; - } - private void updateActionsFromType() { HitActions = diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongableHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongableHitObject.cs index 4f1523eb3f..b4acfa9968 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongableHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongableHitObject.cs @@ -1,11 +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.Linq; using JetBrains.Annotations; using osu.Framework.Bindables; using osu.Framework.Graphics.Containers; -using osu.Game.Audio; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osuTK; @@ -29,14 +27,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables protected override void OnApply() { isStrong.BindTo(HitObject.IsStrongBindable); - isStrong.BindValueChanged(_ => - { - // will overwrite samples, should only be called on subsequent changes - // after the initial application. - updateSamplesFromStrong(); - - RecreatePieces(); - }); + // this doesn't need to be run inline as RecreatePieces is called by the base call below. + isStrong.BindValueChanged(_ => RecreatePieces()); base.OnApply(); } @@ -50,30 +42,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables isStrong.UnbindEvents(); } - private HitSampleInfo[] getStrongSamples() => HitObject.Samples.Where(s => s.Name == HitSampleInfo.HIT_FINISH).ToArray(); - - protected override void LoadSamples() - { - base.LoadSamples(); - isStrong.Value = getStrongSamples().Any(); - } - - private void updateSamplesFromStrong() - { - var strongSamples = getStrongSamples(); - - if (isStrong.Value != strongSamples.Any()) - { - if (isStrong.Value) - HitObject.Samples.Add(new HitSampleInfo(HitSampleInfo.HIT_FINISH)); - else - { - foreach (var sample in strongSamples) - HitObject.Samples.Remove(sample); - } - } - } - protected override void RecreatePieces() { base.RecreatePieces(); diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs index fcd055bcec..cac56d1269 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs @@ -1,8 +1,10 @@ // 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.Threading; using osu.Framework.Bindables; +using osu.Game.Audio; using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Taiko.Objects @@ -31,9 +33,31 @@ namespace osu.Game.Rulesets.Taiko.Objects public bool IsStrong { get => IsStrongBindable.Value; - set => IsStrongBindable.Value = value; + set + { + IsStrongBindable.Value = value; + updateSamplesFromStrong(); + } } + private void updateSamplesFromStrong() + { + var strongSamples = getStrongSamples(); + + if (IsStrongBindable.Value != strongSamples.Any()) + { + if (IsStrongBindable.Value) + Samples.Add(new HitSampleInfo(HitSampleInfo.HIT_FINISH)); + else + { + foreach (var sample in strongSamples) + Samples.Remove(sample); + } + } + } + + private HitSampleInfo[] getStrongSamples() => Samples.Where(s => s.Name == HitSampleInfo.HIT_FINISH).ToArray(); + protected override void CreateNestedHitObjects(CancellationToken cancellationToken) { base.CreateNestedHitObjects(cancellationToken); From 0bcd0cda6b1ed7facd0264fae35b133bff7f2f5c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 May 2021 16:41:39 +0900 Subject: [PATCH 371/839] Fix taiko drawable hit content not correctly being removed on regeneration --- .../Objects/Drawables/DrawableTaikoHitObject.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs index 6041eccb51..6a8d8a611c 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs @@ -137,7 +137,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { Size = BaseSize = new Vector2(TaikoHitObject.DEFAULT_SIZE); - MainPiece?.Expire(); + if (MainPiece != null) + Content.Remove(MainPiece); + Content.Add(MainPiece = CreateMainPiece()); } From 6471ce902d44fce94676f7a8a73ee4e960a07d40 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 May 2021 16:45:28 +0900 Subject: [PATCH 372/839] Run `RecreatePieces` using `AddOnce` to avoid multiple unnecessary calls --- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs | 2 +- .../Objects/Drawables/DrawableTaikoStrongableHitObject.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index 5cb2024bb7..1e9fc187eb 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { type.BindTo(HitObject.TypeBindable); // this doesn't need to be run inline as RecreatePieces is called by the base call below. - type.BindValueChanged(_ => RecreatePieces()); + type.BindValueChanged(_ => Scheduler.AddOnce(RecreatePieces)); base.OnApply(); } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongableHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongableHitObject.cs index b4acfa9968..70d4371e99 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongableHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongableHitObject.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { isStrong.BindTo(HitObject.IsStrongBindable); // this doesn't need to be run inline as RecreatePieces is called by the base call below. - isStrong.BindValueChanged(_ => RecreatePieces()); + isStrong.BindValueChanged(_ => Scheduler.AddOnce(RecreatePieces)); base.OnApply(); } From 0c504c3b7d0adad741bf4d6e0fe2f25538644e8c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 May 2021 17:24:22 +0900 Subject: [PATCH 373/839] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 90d131b117..57550cfb93 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 587bdaf622..1e3b77cd70 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -29,7 +29,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 7ba7a554d6..a2a9ac35fc 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 8085a5420580065222be24b465fefcd26b1f60ed Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 May 2021 17:28:25 +0900 Subject: [PATCH 374/839] Add test coverage of different grade types to `TestSceneResultsScreen` --- .../Visual/Ranking/TestSceneAccuracyCircle.cs | 91 +++---------------- .../Visual/Ranking/TestSceneResultsScreen.cs | 36 +++++--- 2 files changed, 35 insertions(+), 92 deletions(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs b/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs index 1e87893f39..f305b7255e 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs @@ -22,82 +22,17 @@ namespace osu.Game.Tests.Visual.Ranking { public class TestSceneAccuracyCircle : OsuTestScene { - [Test] - public void TestLowDRank() + [TestCase(0.2, ScoreRank.D)] + [TestCase(0.5, ScoreRank.D)] + [TestCase(0.75, ScoreRank.C)] + [TestCase(0.85, ScoreRank.B)] + [TestCase(0.925, ScoreRank.A)] + [TestCase(0.975, ScoreRank.S)] + [TestCase(0.9999, ScoreRank.S)] + [TestCase(1, ScoreRank.X)] + public void TestRank(double accuracy, ScoreRank rank) { - var score = createScore(); - score.Accuracy = 0.2; - score.Rank = ScoreRank.D; - - addCircleStep(score); - } - - [Test] - public void TestDRank() - { - var score = createScore(); - score.Accuracy = 0.5; - score.Rank = ScoreRank.D; - - addCircleStep(score); - } - - [Test] - public void TestCRank() - { - var score = createScore(); - score.Accuracy = 0.75; - score.Rank = ScoreRank.C; - - addCircleStep(score); - } - - [Test] - public void TestBRank() - { - var score = createScore(); - score.Accuracy = 0.85; - score.Rank = ScoreRank.B; - - addCircleStep(score); - } - - [Test] - public void TestARank() - { - var score = createScore(); - score.Accuracy = 0.925; - score.Rank = ScoreRank.A; - - addCircleStep(score); - } - - [Test] - public void TestSRank() - { - var score = createScore(); - score.Accuracy = 0.975; - score.Rank = ScoreRank.S; - - addCircleStep(score); - } - - [Test] - public void TestAlmostSSRank() - { - var score = createScore(); - score.Accuracy = 0.9999; - score.Rank = ScoreRank.S; - - addCircleStep(score); - } - - [Test] - public void TestSSRank() - { - var score = createScore(); - score.Accuracy = 1; - score.Rank = ScoreRank.X; + var score = createScore(accuracy, rank); addCircleStep(score); } @@ -129,7 +64,7 @@ namespace osu.Game.Tests.Visual.Ranking }; }); - private ScoreInfo createScore() => new ScoreInfo + private ScoreInfo createScore(double accuracy, ScoreRank rank) => new ScoreInfo { User = new User { @@ -139,9 +74,9 @@ namespace osu.Game.Tests.Visual.Ranking Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo, Mods = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime() }, TotalScore = 2845370, - Accuracy = 0.95, + Accuracy = accuracy, MaxCombo = 999, - Rank = ScoreRank.S, + Rank = rank, Date = DateTimeOffset.Now, Statistics = { diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs b/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs index b2be7cdf88..ba6b6bd529 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs @@ -29,13 +29,8 @@ namespace osu.Game.Tests.Visual.Ranking [TestFixture] public class TestSceneResultsScreen : OsuManualInputManagerTestScene { - private BeatmapManager beatmaps; - - [BackgroundDependencyLoader] - private void load(BeatmapManager beatmaps) - { - this.beatmaps = beatmaps; - } + [Resolved] + private BeatmapManager beatmaps { get; set; } protected override void LoadComplete() { @@ -46,10 +41,6 @@ namespace osu.Game.Tests.Visual.Ranking Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmapInfo); } - private TestResultsScreen createResultsScreen() => new TestResultsScreen(new TestScoreInfo(new OsuRuleset().RulesetInfo)); - - private UnrankedSoloResultsScreen createUnrankedSoloResultsScreen() => new UnrankedSoloResultsScreen(new TestScoreInfo(new OsuRuleset().RulesetInfo)); - [Test] public void TestResultsWithoutPlayer() { @@ -69,12 +60,25 @@ namespace osu.Game.Tests.Visual.Ranking AddAssert("retry overlay not present", () => screen.RetryOverlay == null); } - [Test] - public void TestResultsWithPlayer() + [TestCase(0.2, ScoreRank.D)] + [TestCase(0.5, ScoreRank.D)] + [TestCase(0.75, ScoreRank.C)] + [TestCase(0.85, ScoreRank.B)] + [TestCase(0.925, ScoreRank.A)] + [TestCase(0.975, ScoreRank.S)] + [TestCase(0.9999, ScoreRank.S)] + [TestCase(1, ScoreRank.X)] + public void TestResultsWithPlayer(double accuracy, ScoreRank rank) { TestResultsScreen screen = null; - AddStep("load results", () => Child = new TestResultsContainer(screen = createResultsScreen())); + var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) + { + Accuracy = accuracy, + Rank = rank + }; + + AddStep("load results", () => Child = new TestResultsContainer(screen = createResultsScreen(score))); AddUntilStep("wait for loaded", () => screen.IsLoaded); AddAssert("retry overlay present", () => screen.RetryOverlay != null); } @@ -232,6 +236,10 @@ namespace osu.Game.Tests.Visual.Ranking AddAssert("download button is enabled", () => screen.ChildrenOfType().Last().Enabled.Value); } + private TestResultsScreen createResultsScreen(ScoreInfo score = null) => new TestResultsScreen(score ?? new TestScoreInfo(new OsuRuleset().RulesetInfo)); + + private UnrankedSoloResultsScreen createUnrankedSoloResultsScreen() => new UnrankedSoloResultsScreen(new TestScoreInfo(new OsuRuleset().RulesetInfo)); + private class TestResultsContainer : Container { [Cached(typeof(Player))] From 41c4afb3d592e05ff1ab62003736e3716c7be856 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 May 2021 17:46:25 +0900 Subject: [PATCH 375/839] Restore path specification to `"."` for consistency --- osu.Game/Beatmaps/BeatmapManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index dcbfbf1332..5e975de77c 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -65,7 +65,7 @@ namespace osu.Game.Beatmaps protected override string[] HashableFileTypes => new[] { ".osu" }; - protected override string ImportFromStablePath => string.Empty; + protected override string ImportFromStablePath => "."; protected override Storage PrepareStableStorage(StableStorage stableStorage) => stableStorage.GetSongStorage(); From abc96057b2cf50e02e0ec939645f6421684495d4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 May 2021 17:55:46 +0900 Subject: [PATCH 376/839] Remove relative height specification and use constant height --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 3 ++- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 754b260bf0..e31e307d4d 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -96,7 +96,8 @@ namespace osu.Game.Overlays.Mods Waves.ThirdWaveColour = Color4Extensions.FromHex(@"005774"); Waves.FourthWaveColour = Color4Extensions.FromHex(@"003a4e"); - RelativeSizeAxes = Axes.Both; + RelativeSizeAxes = Axes.X; + Height = HEIGHT; Padding = new MarginPadding { Horizontal = -OsuScreen.HORIZONTAL_OVERFLOW_PADDING }; diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 7395b346a4..a53e253581 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -72,8 +72,8 @@ namespace osu.Game.Screens.OnlinePlay.Match Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, Depth = float.MinValue, - RelativeSizeAxes = Axes.Both, - Height = 0.7f, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING }, Child = userModsSelectOverlay = new UserModSelectOverlay { From 0acf4cf85c861c7e144dee9ab1d0d3143dbb10f9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 May 2021 18:44:15 +0900 Subject: [PATCH 377/839] Translate remaining `ButtonSystem` strings and rename to match class name --- ...{MainMenu.ja.resx => ButtonSystem.ja.resx} | 0 .../{MainMenu.resx => ButtonSystem.resx} | 0 osu.Game/Localisation/ButtonSystemStrings.cs | 44 +++++++++++++++++++ osu.Game/Localisation/MainMenuStrings.cs | 24 ---------- osu.Game/Screens/Menu/ButtonSystem.cs | 14 +++--- 5 files changed, 51 insertions(+), 31 deletions(-) rename osu.Game/Localisation/{MainMenu.ja.resx => ButtonSystem.ja.resx} (100%) rename osu.Game/Localisation/{MainMenu.resx => ButtonSystem.resx} (100%) create mode 100644 osu.Game/Localisation/ButtonSystemStrings.cs delete mode 100644 osu.Game/Localisation/MainMenuStrings.cs diff --git a/osu.Game/Localisation/MainMenu.ja.resx b/osu.Game/Localisation/ButtonSystem.ja.resx similarity index 100% rename from osu.Game/Localisation/MainMenu.ja.resx rename to osu.Game/Localisation/ButtonSystem.ja.resx diff --git a/osu.Game/Localisation/MainMenu.resx b/osu.Game/Localisation/ButtonSystem.resx similarity index 100% rename from osu.Game/Localisation/MainMenu.resx rename to osu.Game/Localisation/ButtonSystem.resx diff --git a/osu.Game/Localisation/ButtonSystemStrings.cs b/osu.Game/Localisation/ButtonSystemStrings.cs new file mode 100644 index 0000000000..1d26a73360 --- /dev/null +++ b/osu.Game/Localisation/ButtonSystemStrings.cs @@ -0,0 +1,44 @@ +// 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.Localisation +{ + public static class ButtonSystemStrings + { + private const string prefix = @"osu.Game.Localisation.ButtonSystem"; + + /// + /// "solo" + /// + public static LocalisableString Solo => new TranslatableString(getKey(@"solo"), @"solo"); + + /// + /// "multi" + /// + public static LocalisableString Multi => new TranslatableString(getKey(@"multi"), @"multi"); + + /// + /// "playlists" + /// + public static LocalisableString Playlists => new TranslatableString(getKey(@"playlists"), @"playlists"); + + /// + /// "play" + /// + public static LocalisableString Play => new TranslatableString(getKey(@"play"), @"play"); + + /// + /// "edit" + /// + public static LocalisableString Edit => new TranslatableString(getKey(@"edit"), @"edit"); + + /// + /// "browse" + /// + public static LocalisableString Browse => new TranslatableString(getKey(@"browse"), @"browse"); + + private static string getKey(string key) => $@"{prefix}:{key}"; + } +} diff --git a/osu.Game/Localisation/MainMenuStrings.cs b/osu.Game/Localisation/MainMenuStrings.cs deleted file mode 100644 index fd9647467a..0000000000 --- a/osu.Game/Localisation/MainMenuStrings.cs +++ /dev/null @@ -1,24 +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.Localisation; - -namespace osu.Game.Localisation -{ - public static class MainMenuStrings - { - private const string prefix = "osu.Game.Localisation.MainMenu"; - - /// - /// "solo" - /// - public static LocalisableString Solo => new TranslatableString(getKey("solo"), "solo"); - - /// - /// "multi" - /// - public static LocalisableString Multi => new TranslatableString(getKey("multi"), "multi"); - - private static string getKey(string key) => $"{prefix}:{key}"; - } -} diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index ff5ad37b9d..c60d1bd4e0 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -125,17 +125,17 @@ namespace osu.Game.Screens.Menu [BackgroundDependencyLoader(true)] private void load(AudioManager audio, IdleTracker idleTracker, GameHost host, LocalisationManager strings) { - buttonsPlay.Add(new Button(MainMenuStrings.Solo, @"button-solo-select", FontAwesome.Solid.User, new Color4(102, 68, 204, 255), () => OnSolo?.Invoke(), WEDGE_WIDTH, Key.P)); - buttonsPlay.Add(new Button(MainMenuStrings.Multi, @"button-generic-select", FontAwesome.Solid.Users, new Color4(94, 63, 186, 255), onMultiplayer, 0, Key.M)); - buttonsPlay.Add(new Button(@"playlists", @"button-generic-select", OsuIcon.Charts, new Color4(94, 63, 186, 255), onPlaylists, 0, Key.L)); + buttonsPlay.Add(new Button(ButtonSystemStrings.Solo, @"button-solo-select", FontAwesome.Solid.User, new Color4(102, 68, 204, 255), () => OnSolo?.Invoke(), WEDGE_WIDTH, Key.P)); + buttonsPlay.Add(new Button(ButtonSystemStrings.Multi, @"button-generic-select", FontAwesome.Solid.Users, new Color4(94, 63, 186, 255), onMultiplayer, 0, Key.M)); + buttonsPlay.Add(new Button(ButtonSystemStrings.Playlists, @"button-generic-select", OsuIcon.Charts, new Color4(94, 63, 186, 255), onPlaylists, 0, Key.L)); buttonsPlay.ForEach(b => b.VisibleState = ButtonSystemState.Play); - buttonsTopLevel.Add(new Button(@"play", @"button-play-select", OsuIcon.Logo, new Color4(102, 68, 204, 255), () => State = ButtonSystemState.Play, WEDGE_WIDTH, Key.P)); - buttonsTopLevel.Add(new Button(@"edit", @"button-edit-select", OsuIcon.EditCircle, new Color4(238, 170, 0, 255), () => OnEdit?.Invoke(), 0, Key.E)); - buttonsTopLevel.Add(new Button(@"browse", @"button-direct-select", OsuIcon.ChevronDownCircle, new Color4(165, 204, 0, 255), () => OnBeatmapListing?.Invoke(), 0, Key.D)); + buttonsTopLevel.Add(new Button(ButtonSystemStrings.Play, @"button-play-select", OsuIcon.Logo, new Color4(102, 68, 204, 255), () => State = ButtonSystemState.Play, WEDGE_WIDTH, Key.P)); + buttonsTopLevel.Add(new Button(ButtonSystemStrings.Edit, @"button-edit-select", OsuIcon.EditCircle, new Color4(238, 170, 0, 255), () => OnEdit?.Invoke(), 0, Key.E)); + buttonsTopLevel.Add(new Button(ButtonSystemStrings.Browse, @"button-direct-select", OsuIcon.ChevronDownCircle, new Color4(165, 204, 0, 255), () => OnBeatmapListing?.Invoke(), 0, Key.D)); if (host.CanExit) - buttonsTopLevel.Add(new Button(@"exit", string.Empty, OsuIcon.CrossCircle, new Color4(238, 51, 153, 255), () => OnExit?.Invoke(), 0, Key.Q)); + buttonsTopLevel.Add(new Button("exit", string.Empty, OsuIcon.CrossCircle, new Color4(238, 51, 153, 255), () => OnExit?.Invoke(), 0, Key.Q)); buttonArea.AddRange(buttonsPlay); buttonArea.AddRange(buttonsTopLevel); From bf4db60ef4b2ec73fdc114ad07716582027373dc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 May 2021 18:48:42 +0900 Subject: [PATCH 378/839] Remove placeholder translations --- osu.Game/Localisation/Common.ja.resx | 17 ----------------- osu.Game/Localisation/Common.resx | 17 ----------------- 2 files changed, 34 deletions(-) delete mode 100644 osu.Game/Localisation/Common.ja.resx delete mode 100644 osu.Game/Localisation/Common.resx diff --git a/osu.Game/Localisation/Common.ja.resx b/osu.Game/Localisation/Common.ja.resx deleted file mode 100644 index 174751c455..0000000000 --- a/osu.Game/Localisation/Common.ja.resx +++ /dev/null @@ -1,17 +0,0 @@ - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - やめとくわ - - diff --git a/osu.Game/Localisation/Common.resx b/osu.Game/Localisation/Common.resx deleted file mode 100644 index f63fb90086..0000000000 --- a/osu.Game/Localisation/Common.resx +++ /dev/null @@ -1,17 +0,0 @@ - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Cancel - - From 318e5fc60bf5455653e8e2ae9846360b0b531542 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 21 May 2021 20:14:26 +0300 Subject: [PATCH 379/839] Mark `WorkingBeatmap.GetSkin()` as abstract --- osu.Game/Beatmaps/WorkingBeatmap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index ead8572c54..0a55678fb7 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -324,7 +324,7 @@ namespace osu.Game.Beatmaps public bool SkinLoaded => skin.IsResultAvailable; public ISkin Skin => skin.Value; - protected virtual ISkin GetSkin() => new BeatmapSkin(BeatmapInfo); + protected abstract ISkin GetSkin(); private readonly RecyclableLazy skin; public abstract Stream GetStream(string storagePath); From 04e75d8f2b19ab70dd32a49922dc1157e304fb2b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 21 May 2021 20:16:51 +0300 Subject: [PATCH 380/839] Return empty skin for `GetSkin()` in `TestWorkingBeatmap` --- osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs index 852006bc9b..2e4a9fa28f 100644 --- a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs +++ b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs @@ -3,9 +3,15 @@ using System.IO; using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Audio.Track; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Textures; +using osu.Game.Audio; using osu.Game.Beatmaps; +using osu.Game.Skinning; using osu.Game.Storyboards; namespace osu.Game.Tests.Beatmaps @@ -36,10 +42,23 @@ namespace osu.Game.Tests.Beatmaps protected override Storyboard GetStoryboard() => storyboard ?? base.GetStoryboard(); + protected override ISkin GetSkin() => new EmptySkin(); + public override Stream GetStream(string storagePath) => null; protected override Texture GetBackground() => null; protected override Track GetBeatmapTrack() => null; + + private class EmptySkin : ISkin + { + public Drawable GetDrawableComponent(ISkinComponent component) => null; + + public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => null; + + public ISample GetSample(ISampleInfo sampleInfo) => null; + + public IBindable GetConfig(TLookup lookup) => null; + } } } From 4f6de6fdc66a0e18aad8f2640dbaa848ea8c8888 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 21 May 2021 20:21:00 +0300 Subject: [PATCH 381/839] Implement `GetSkin()` for other working beatmaps --- osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs | 2 ++ osu.Game.Tests/WaveformTestBeatmap.cs | 3 +++ osu.Game/Beatmaps/BeatmapManager.cs | 1 + osu.Game/Beatmaps/DummyWorkingBeatmap.cs | 3 +++ osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs | 3 +++ osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs | 3 +++ 6 files changed, 15 insertions(+) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs index a18f82fe4a..059432eeaf 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs @@ -169,6 +169,8 @@ namespace osu.Game.Tests.Beatmaps.Formats protected override Track GetBeatmapTrack() => throw new NotImplementedException(); + protected override ISkin GetSkin() => throw new NotImplementedException(); + public override Stream GetStream(string storagePath) => throw new NotImplementedException(); } } diff --git a/osu.Game.Tests/WaveformTestBeatmap.cs b/osu.Game.Tests/WaveformTestBeatmap.cs index cbed28641c..5477e4a0f8 100644 --- a/osu.Game.Tests/WaveformTestBeatmap.cs +++ b/osu.Game.Tests/WaveformTestBeatmap.cs @@ -11,6 +11,7 @@ using osu.Game.Beatmaps; using osu.Game.IO.Archives; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; +using osu.Game.Skinning; using osu.Game.Tests.Beatmaps; using osu.Game.Tests.Resources; @@ -52,6 +53,8 @@ namespace osu.Game.Tests protected override Waveform GetWaveform() => new Waveform(trackStore.GetStream(firstAudioFile)); + protected override ISkin GetSkin() => null; + public override Stream GetStream(string storagePath) => null; protected override Track GetBeatmapTrack() => trackStore.Get(firstAudioFile); diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 5e975de77c..46e3a4f6d7 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -526,6 +526,7 @@ namespace osu.Game.Beatmaps protected override IBeatmap GetBeatmap() => beatmap; protected override Texture GetBackground() => null; protected override Track GetBeatmapTrack() => null; + protected override ISkin GetSkin() => null; public override Stream GetStream(string storagePath) => null; } } diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs index 6922d1c286..ea7f45e53f 100644 --- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs @@ -15,6 +15,7 @@ using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.UI; +using osu.Game.Skinning; namespace osu.Game.Beatmaps { @@ -49,6 +50,8 @@ namespace osu.Game.Beatmaps protected override Track GetBeatmapTrack() => GetVirtualTrack(); + protected override ISkin GetSkin() => null; + public override Stream GetStream(string storagePath) => null; private class DummyRulesetInfo : RulesetInfo diff --git a/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs b/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs index 66784fda54..6f92db98ee 100644 --- a/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs +++ b/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs @@ -11,6 +11,7 @@ using osu.Framework.Audio.Track; using osu.Framework.Graphics.Textures; using osu.Game.Beatmaps; using osu.Game.IO; +using osu.Game.Skinning; using Decoder = osu.Game.Beatmaps.Formats.Decoder; namespace osu.Game.Screens.Edit @@ -117,6 +118,8 @@ namespace osu.Game.Screens.Edit protected override Track GetBeatmapTrack() => throw new NotImplementedException(); + protected override ISkin GetSkin() => throw new NotImplementedException(); + public override Stream GetStream(string storagePath) => throw new NotImplementedException(); } } diff --git a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs index a97f6defe9..4935f7fc13 100644 --- a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs +++ b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs @@ -17,6 +17,7 @@ using osu.Game.IO; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; +using osu.Game.Skinning; namespace osu.Game.Tests.Beatmaps { @@ -216,6 +217,8 @@ namespace osu.Game.Tests.Beatmaps protected override Track GetBeatmapTrack() => throw new NotImplementedException(); + protected override ISkin GetSkin() => throw new NotImplementedException(); + public override Stream GetStream(string storagePath) => throw new NotImplementedException(); protected override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap, Ruleset ruleset) From afb33f1641a924b04bd28f3cf52af74c7d730af1 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 21 May 2021 20:21:50 +0300 Subject: [PATCH 382/839] Remove no longer necessary test case --- .../Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs index 22959c082a..bece80903f 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs @@ -3,7 +3,6 @@ using System; using System.Linq; -using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Graphics; @@ -21,7 +20,6 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Play.HUD; using osu.Game.Skinning; using osu.Game.Storyboards; -using osu.Game.Tests.Beatmaps; namespace osu.Game.Tests.Visual.Gameplay { @@ -40,13 +38,6 @@ namespace osu.Game.Tests.Visual.Gameplay protected override bool HasCustomSteps => true; - [Test] - public void TestEmptyDefaultBeatmapSkinFallsBack() - { - CreateSkinTest(DefaultLegacySkin.Info, () => new TestWorkingBeatmap(CreateBeatmap(new OsuRuleset().RulesetInfo)).Skin); - AddAssert("hud from default legacy skin", () => AssertComponentsFromExpectedSource(SkinnableTarget.MainHUDComponents, skinManager.CurrentSkin.Value)); - } - protected void CreateSkinTest(SkinInfo gameCurrentSkin, Func getBeatmapSkin) { CreateTest(() => From cd3f5433942133703acaa0822b9d444efc9218ba Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 22 May 2021 02:32:55 +0900 Subject: [PATCH 383/839] Add LocalisationAnalyser package and tools --- .config/dotnet-tools.json | 8 +++++++- osu.Game/osu.Game.csproj | 4 ++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 58c24181d3..54d9ff3077 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -31,6 +31,12 @@ "commands": [ "CodeFileSanity" ] + }, + "ppy.localisationanalyser.tools": { + "version": "2021.521.1", + "commands": [ + "localisation" + ] } } -} \ No newline at end of file +} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 587bdaf622..385aa5c7e1 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -29,6 +29,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + From 2fc53017fc11c428a552a5af273487a8147810d6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 22 May 2021 13:46:09 +0900 Subject: [PATCH 384/839] Generate initial resx files --- osu.Game/Localisation/ButtonSystem.resx | 94 ++++++++++++++++++++---- osu.Game/Localisation/Chat.resx | 67 +++++++++++++++++ osu.Game/Localisation/Common.resx | 64 ++++++++++++++++ osu.Game/Localisation/Notifications.resx | 67 +++++++++++++++++ osu.Game/Localisation/NowPlaying.resx | 67 +++++++++++++++++ osu.Game/Localisation/Settings.resx | 67 +++++++++++++++++ 6 files changed, 410 insertions(+), 16 deletions(-) create mode 100644 osu.Game/Localisation/Chat.resx create mode 100644 osu.Game/Localisation/Common.resx create mode 100644 osu.Game/Localisation/Notifications.resx create mode 100644 osu.Game/Localisation/NowPlaying.resx create mode 100644 osu.Game/Localisation/Settings.resx diff --git a/osu.Game/Localisation/ButtonSystem.resx b/osu.Game/Localisation/ButtonSystem.resx index 845b412d88..1c6f614c46 100644 --- a/osu.Game/Localisation/ButtonSystem.resx +++ b/osu.Game/Localisation/ButtonSystem.resx @@ -1,17 +1,79 @@ + - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - solo - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.NetStandard.ResXResourceReader, System.Resources.NetStandard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + + + System.Resources.NetStandard.ResXResourceWriter, System.Resources.NetStandard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + + + solo + + + multi + + + playlists + + + play + + + edit + + + browse + + \ No newline at end of file diff --git a/osu.Game/Localisation/Chat.resx b/osu.Game/Localisation/Chat.resx new file mode 100644 index 0000000000..3762f800c5 --- /dev/null +++ b/osu.Game/Localisation/Chat.resx @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.NetStandard.ResXResourceReader, System.Resources.NetStandard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + + + System.Resources.NetStandard.ResXResourceWriter, System.Resources.NetStandard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + + + chat + + + join the real-time discussion + + \ No newline at end of file diff --git a/osu.Game/Localisation/Common.resx b/osu.Game/Localisation/Common.resx new file mode 100644 index 0000000000..8a3ae29c77 --- /dev/null +++ b/osu.Game/Localisation/Common.resx @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.NetStandard.ResXResourceReader, System.Resources.NetStandard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + + + System.Resources.NetStandard.ResXResourceWriter, System.Resources.NetStandard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + + + Cancel + + \ No newline at end of file diff --git a/osu.Game/Localisation/Notifications.resx b/osu.Game/Localisation/Notifications.resx new file mode 100644 index 0000000000..d61e5744e4 --- /dev/null +++ b/osu.Game/Localisation/Notifications.resx @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.NetStandard.ResXResourceReader, System.Resources.NetStandard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + + + System.Resources.NetStandard.ResXResourceWriter, System.Resources.NetStandard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + + + notifications + + + waiting for 'ya + + \ No newline at end of file diff --git a/osu.Game/Localisation/NowPlaying.resx b/osu.Game/Localisation/NowPlaying.resx new file mode 100644 index 0000000000..71df15e488 --- /dev/null +++ b/osu.Game/Localisation/NowPlaying.resx @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.NetStandard.ResXResourceReader, System.Resources.NetStandard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + + + System.Resources.NetStandard.ResXResourceWriter, System.Resources.NetStandard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + + + now playing + + + manage the currently playing track + + \ No newline at end of file diff --git a/osu.Game/Localisation/Settings.resx b/osu.Game/Localisation/Settings.resx new file mode 100644 index 0000000000..1770147dd9 --- /dev/null +++ b/osu.Game/Localisation/Settings.resx @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.NetStandard.ResXResourceReader, System.Resources.NetStandard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + + + System.Resources.NetStandard.ResXResourceWriter, System.Resources.NetStandard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + + + settings + + + change the way osu! behaves + + \ No newline at end of file From b6db9ef3346cf75a1b3c9fdaefebec7f76cd208e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 22 May 2021 13:54:52 +0900 Subject: [PATCH 385/839] Fill out Japanese localisation via resx --- osu.Game/Localisation/ButtonSystem.ja.resx | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/osu.Game/Localisation/ButtonSystem.ja.resx b/osu.Game/Localisation/ButtonSystem.ja.resx index 20c85110ad..54c956758c 100644 --- a/osu.Game/Localisation/ButtonSystem.ja.resx +++ b/osu.Game/Localisation/ButtonSystem.ja.resx @@ -14,4 +14,19 @@ ソロ - \ No newline at end of file + + プレイリスト + + + 遊ぶ + + + マルチ + + + エディット + + + ブラウズ + + From fb5672814d6e558881d0101fb5a68799aa047fd1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 22 May 2021 13:55:15 +0900 Subject: [PATCH 386/839] Add remaining strings for `ButtonSystem` --- osu.Game/Localisation/ButtonSystem.ja.resx | 6 ++++++ osu.Game/Localisation/ButtonSystem.resx | 9 +++++++++ osu.Game/Localisation/ButtonSystemStrings.cs | 17 ++++++++++++++++- osu.Game/Screens/Menu/ButtonSystem.cs | 6 +++--- 4 files changed, 34 insertions(+), 4 deletions(-) diff --git a/osu.Game/Localisation/ButtonSystem.ja.resx b/osu.Game/Localisation/ButtonSystem.ja.resx index 54c956758c..02f3e7ce2f 100644 --- a/osu.Game/Localisation/ButtonSystem.ja.resx +++ b/osu.Game/Localisation/ButtonSystem.ja.resx @@ -29,4 +29,10 @@ ブラウズ + + 閉じる + + + 設定 + diff --git a/osu.Game/Localisation/ButtonSystem.resx b/osu.Game/Localisation/ButtonSystem.resx index 1c6f614c46..e8f555b6c3 100644 --- a/osu.Game/Localisation/ButtonSystem.resx +++ b/osu.Game/Localisation/ButtonSystem.resx @@ -76,4 +76,13 @@ browse + + settings + + + back + + + exit + \ No newline at end of file diff --git a/osu.Game/Localisation/ButtonSystemStrings.cs b/osu.Game/Localisation/ButtonSystemStrings.cs index 1d26a73360..8083f80782 100644 --- a/osu.Game/Localisation/ButtonSystemStrings.cs +++ b/osu.Game/Localisation/ButtonSystemStrings.cs @@ -39,6 +39,21 @@ namespace osu.Game.Localisation ///
public static LocalisableString Browse => new TranslatableString(getKey(@"browse"), @"browse"); + /// + /// "settings" + /// + public static LocalisableString Settings => new TranslatableString(getKey(@"settings"), @"settings"); + + /// + /// "back" + /// + public static LocalisableString Back => new TranslatableString(getKey(@"back"), @"back"); + + /// + /// "exit" + /// + public static LocalisableString Exit => new TranslatableString(getKey(@"exit"), @"exit"); + private static string getKey(string key) => $@"{prefix}:{key}"; } -} +} \ No newline at end of file diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index c60d1bd4e0..a836f7bf09 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -99,8 +99,8 @@ namespace osu.Game.Screens.Menu buttonArea.AddRange(new Drawable[] { - new Button(@"settings", string.Empty, FontAwesome.Solid.Cog, new Color4(85, 85, 85, 255), () => OnSettings?.Invoke(), -WEDGE_WIDTH, Key.O), - backButton = new Button(@"back", @"button-back-select", OsuIcon.LeftCircle, new Color4(51, 58, 94, 255), () => State = ButtonSystemState.TopLevel, -WEDGE_WIDTH) + new Button(ButtonSystemStrings.Settings, string.Empty, FontAwesome.Solid.Cog, new Color4(85, 85, 85, 255), () => OnSettings?.Invoke(), -WEDGE_WIDTH, Key.O), + backButton = new Button(ButtonSystemStrings.Back, @"button-back-select", OsuIcon.LeftCircle, new Color4(51, 58, 94, 255), () => State = ButtonSystemState.TopLevel, -WEDGE_WIDTH) { VisibleState = ButtonSystemState.Play, }, @@ -135,7 +135,7 @@ namespace osu.Game.Screens.Menu buttonsTopLevel.Add(new Button(ButtonSystemStrings.Browse, @"button-direct-select", OsuIcon.ChevronDownCircle, new Color4(165, 204, 0, 255), () => OnBeatmapListing?.Invoke(), 0, Key.D)); if (host.CanExit) - buttonsTopLevel.Add(new Button("exit", string.Empty, OsuIcon.CrossCircle, new Color4(238, 51, 153, 255), () => OnExit?.Invoke(), 0, Key.Q)); + buttonsTopLevel.Add(new Button(ButtonSystemStrings.Exit, string.Empty, OsuIcon.CrossCircle, new Color4(238, 51, 153, 255), () => OnExit?.Invoke(), 0, Key.Q)); buttonArea.AddRange(buttonsPlay); buttonArea.AddRange(buttonsTopLevel); From 7d88a19d7f67bc540646eed0992ba909fd51990b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 22 May 2021 21:03:40 +0900 Subject: [PATCH 387/839] Remove unnecessary field storage of origin reference --- osu.Game/Skinning/Editor/SkinSelectionHandler.cs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index 7f04252c6b..7d9ed6b46c 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -20,17 +20,9 @@ namespace osu.Game.Skinning.Editor { public class SkinSelectionHandler : SelectionHandler { - private Vector2? referenceOrigin; - [Resolved] private SkinEditor skinEditor { get; set; } - protected override void OnOperationEnded() - { - base.OnOperationEnded(); - referenceOrigin = null; - } - public override bool HandleRotation(float angle) { if (SelectedBlueprints.Count == 1) @@ -42,13 +34,11 @@ namespace osu.Game.Skinning.Editor { var selectionQuad = getSelectionQuad(); - referenceOrigin ??= selectionQuad.Centre; - foreach (var b in SelectedBlueprints) { var drawableItem = (Drawable)b.Item; - drawableItem.Position = drawableItem.Parent.ToLocalSpace(RotatePointAroundOrigin(b.ScreenSpaceSelectionPoint, referenceOrigin.Value, angle)) - drawableItem.AnchorPosition; + drawableItem.Position = drawableItem.Parent.ToLocalSpace(RotatePointAroundOrigin(b.ScreenSpaceSelectionPoint, selectionQuad.Centre, angle)) - drawableItem.AnchorPosition; drawableItem.Rotation += angle; } } From 2fd0038154082ec3d622aa3ddff0e25ee29684ad Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sat, 22 May 2021 16:42:20 -0700 Subject: [PATCH 388/839] Fix checkmark being hidden after clicking current waveform opacity setting --- osu.Game/Screens/Edit/WaveformOpacityMenuItem.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/WaveformOpacityMenuItem.cs b/osu.Game/Screens/Edit/WaveformOpacityMenuItem.cs index 053c2fa4b0..7e095f526e 100644 --- a/osu.Game/Screens/Edit/WaveformOpacityMenuItem.cs +++ b/osu.Game/Screens/Edit/WaveformOpacityMenuItem.cs @@ -12,7 +12,7 @@ namespace osu.Game.Screens.Edit { private readonly Bindable waveformOpacity; - private readonly Dictionary menuItemLookup = new Dictionary(); + private readonly Dictionary menuItemLookup = new Dictionary(); public WaveformOpacityMenuItem(Bindable waveformOpacity) : base("Waveform opacity") @@ -29,13 +29,13 @@ namespace osu.Game.Screens.Edit waveformOpacity.BindValueChanged(opacity => { foreach (var kvp in menuItemLookup) - kvp.Value.State.Value = kvp.Key == opacity.NewValue; + kvp.Value.State.Value = kvp.Key == opacity.NewValue ? TernaryState.True : TernaryState.False; }, true); } - private ToggleMenuItem createMenuItem(float opacity) + private TernaryStateRadioMenuItem createMenuItem(float opacity) { - var item = new ToggleMenuItem($"{opacity * 100}%", MenuItemType.Standard, _ => updateOpacity(opacity)); + var item = new TernaryStateRadioMenuItem($"{opacity * 100}%", MenuItemType.Standard, _ => updateOpacity(opacity)); menuItemLookup[opacity] = item; return item; } From caa2c5638e2695dae4719613edcdf49e00c57cb0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 23 May 2021 16:46:32 +0900 Subject: [PATCH 389/839] Fix legacy combo counter not accounting for song progress bar --- osu.Game/Skinning/LegacySkin.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 7a64f38840..fb9cf47cb7 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -338,6 +338,7 @@ namespace osu.Game.Skinning { var score = container.OfType().FirstOrDefault(); var accuracy = container.OfType().FirstOrDefault(); + var combo = container.OfType().FirstOrDefault(); if (score != null && accuracy != null) { @@ -353,9 +354,12 @@ namespace osu.Game.Skinning hitError.Anchor = Anchor.BottomCentre; hitError.Origin = Anchor.CentreLeft; hitError.Rotation = -90; + } - if (songProgress != null) - hitError.Y -= SongProgress.MAX_HEIGHT; + if (songProgress != null) + { + if (hitError != null) hitError.Y -= SongProgress.MAX_HEIGHT; + if (combo != null) combo.Y -= SongProgress.MAX_HEIGHT; } }) { From b8a5b5aaf8b7097f0b1a0be57156a5fd15aefa0a Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sun, 23 May 2021 17:22:31 +0700 Subject: [PATCH 390/839] add test for image block and inline image --- .../Online/TestSceneWikiMarkdownContainer.cs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs index 3731e7a782..67030631b0 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs @@ -120,6 +120,29 @@ needs_cleanup: true }); } + [Test] + public void TestBlockImage() + { + AddStep("Add paragraph with block image", () => + { + markdownContainer.CurrentPath = "Interface/"; + markdownContainer.Text = @"Line before image + +![play menu](img/play-menu.jpg ""Main Menu in osu!"") + +Line after image"; + }); + } + + [Test] + public void TestInlineImage() + { + AddStep("Add inline image", () => + { + markdownContainer.Text = "![osu! mode icon](/wiki/shared/mode/osu.png) osu!"; + }); + } + private class TestMarkdownContainer : WikiMarkdownContainer { public LinkInline Link; From 0448f6fdb362dcfe370041738f27395949a601fa Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 25 May 2021 13:21:08 +0700 Subject: [PATCH 391/839] add main page markdown --- osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs index e193f5b98e..3a2bafb128 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs @@ -28,9 +28,16 @@ namespace osu.Game.Tests.Visual.Online { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding(20), - Child = new WikiMainPage(), + Child = new WikiMainPage + { + Markdown = main_page_markdown + } } }; } + + // From https://osu.ppy.sh/api/v2/wiki/en/Main_Page + private const string main_page_markdown = + "---\nlayout: main_page\n---\n\n\n\n
\nWelcome to the osu! wiki, a project containing a wide range of osu! related information.\n
\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# Game client\n\n[Interface](/wiki/Interface) • [Options](/wiki/Options) • [Visual settings](/wiki/Visual_Settings) • [Shortcut key reference](/wiki/Shortcut_key_reference) • [Configuration file](/wiki/osu!_Program_Files/User_Configuration_File) • [Program files](/wiki/osu!_Program_Files)\n\n[File formats](/wiki/osu!_File_Formats): [.osz](/wiki/osu!_File_Formats/Osz_(file_format)) • [.osk](/wiki/osu!_File_Formats/Osk_(file_format)) • [.osr](/wiki/osu!_File_Formats/Osr_(file_format)) • [.osu](/wiki/osu!_File_Formats/Osu_(file_format)) • [.osb](/wiki/osu!_File_Formats/Osb_(file_format)) • [.db](/wiki/osu!_File_Formats/Db_(file_format))\n\n
\n
\n\n# Gameplay\n\n[Game modes](/wiki/Game_mode): [osu!](/wiki/Game_mode/osu!) • [osu!taiko](/wiki/Game_mode/osu!taiko) • [osu!catch](/wiki/Game_mode/osu!catch) • [osu!mania](/wiki/Game_mode/osu!mania)\n\n[Beatmap](/wiki/Beatmap) • [Hit object](/wiki/Hit_object) • [Mods](/wiki/Game_modifier) • [Score](/wiki/Score) • [Replay](/wiki/Replay) • [Multi](/wiki/Multi)\n\n
\n
\n\n# [Beatmap editor](/wiki/Beatmap_Editor)\n\nSections: [Compose](/wiki/Beatmap_Editor/Compose) • [Design](/wiki/Beatmap_Editor/Design) • [Timing](/wiki/Beatmap_Editor/Timing) • [Song setup](/wiki/Beatmap_Editor/Song_Setup)\n\nComponents: [AiMod](/wiki/Beatmap_Editor/AiMod) • [Beat snap divisor](/wiki/Beatmap_Editor/Beat_Snap_Divisor) • [Distance snap](/wiki/Beatmap_Editor/Distance_Snap) • [Menu](/wiki/Beatmap_Editor/Menu) • [SB load](/wiki/Beatmap_Editor/SB_Load) • [Timelines](/wiki/Beatmap_Editor/Timelines)\n\n[Beatmapping](/wiki/Beatmapping) • [Difficulty](/wiki/Beatmap/Difficulty) • [Mapping techniques](/wiki/Mapping_Techniques) • [Storyboarding](/wiki/Storyboarding)\n\n
\n
\n\n# Beatmap submission and ranking\n\n[Submission](/wiki/Submission) • [Modding](/wiki/Modding) • [Ranking procedure](/wiki/Beatmap_ranking_procedure) • [Mappers' Guild](/wiki/Mappers_Guild) • [Project Loved](/wiki/Project_Loved)\n\n[Ranking criteria](/wiki/Ranking_Criteria): [osu!](/wiki/Ranking_Criteria/osu!) • [osu!taiko](/wiki/Ranking_Criteria/osu!taiko) • [osu!catch](/wiki/Ranking_Criteria/osu!catch) • [osu!mania](/wiki/Ranking_Criteria/osu!mania)\n\n
\n
\n\n# Community\n\n[Tournaments](/wiki/Tournaments) • [Skinning](/wiki/Skinning) • [Projects](/wiki/Projects) • [Guides](/wiki/Guides) • [osu!dev Discord server](/wiki/osu!dev_Discord_server) • [How you can help](/wiki/How_You_Can_Help!) • [Glossary](/wiki/Glossary)\n\n
\n
\n\n# People\n\n[The Team](/wiki/People/The_Team): [Developers](/wiki/People/The_Team/Developers) • [Global Moderation Team](/wiki/People/The_Team/Global_Moderation_Team) • [Support Team](/wiki/People/The_Team/Support_Team) • [Nomination Assessment Team](/wiki/People/The_Team/Nomination_Assessment_Team) • [Beatmap Nominators](/wiki/People/The_Team/Beatmap_Nominators) • [osu! Alumni](/wiki/People/The_Team/osu!_Alumni) • [Project Loved Team](/wiki/People/The_Team/Project_Loved_Team)\n\nOrganisations: [osu! UCI](/wiki/Organisations/osu!_UCI)\n\n[Community Contributors](/wiki/People/Community_Contributors) • [Users with unique titles](/wiki/People/Users_with_unique_titles)\n\n
\n
\n\n# For developers\n\n[API](/wiki/osu!api) • [Bot account](/wiki/Bot_account) • [Brand identity guidelines](/wiki/Brand_identity_guidelines)\n\n
\n
\n\n# About the wiki\n\n[Sitemap](/wiki/Sitemap) • [Contribution guide](/wiki/osu!_wiki_Contribution_Guide) • [Article styling criteria](/wiki/Article_Styling_Criteria) • [News styling criteria](/wiki/News_Styling_Criteria)\n\n
\n
\n"; } } From 4a543c25575fe80982ac296a5b3af2e17c1ed61a Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sun, 23 May 2021 17:49:26 +0700 Subject: [PATCH 392/839] add simple panels --- osu.Game/Overlays/Wiki/WikiMainPage.cs | 56 ++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/osu.Game/Overlays/Wiki/WikiMainPage.cs b/osu.Game/Overlays/Wiki/WikiMainPage.cs index cb6c80cb73..258f6a4f19 100644 --- a/osu.Game/Overlays/Wiki/WikiMainPage.cs +++ b/osu.Game/Overlays/Wiki/WikiMainPage.cs @@ -6,13 +6,23 @@ using osu.Framework.Graphics.Containers; using System.Linq; using HtmlAgilityPack; using osu.Framework.Allocation; +using System.Collections.Generic; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics.Effects; +using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Overlays.Wiki.Markdown; +using osuTK; +using osuTK.Graphics; namespace osu.Game.Overlays.Wiki { public class WikiMainPage : FillFlowContainer { + [Resolved] + private OverlayColourProvider colourProvider { get; set; } + public string Markdown; public WikiMainPage() @@ -31,6 +41,7 @@ namespace osu.Game.Overlays.Wiki { createBlurb(html) }; + AddRange(createPanels(html)); } private Container createBlurb(HtmlDocument html) @@ -54,5 +65,50 @@ namespace osu.Game.Overlays.Wiki } }; } + + private IEnumerable createPanels(HtmlDocument html) + { + var panelsNode = html.DocumentNode.SelectNodes("//div[contains(@class, 'wiki-main-page-panel')]"); + + foreach (var panel in panelsNode) + { + var isFullWidth = panel.HasClass("wiki-main-page-panel--full"); + + yield return new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Width = isFullWidth ? 1.0f : 0.5f, + Padding = new MarginPadding(3), + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + CornerRadius = 4, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(25), + Offset = new Vector2(0, 1), + Radius = 3, + }, + Child = new Box + { + Colour = colourProvider.Background4, + RelativeSizeAxes = Axes.Both, + }, + }, + new WikiMarkdownContainer + { + Text = panel.InnerText, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + } + } + }; + } + } } } From d9e898a2eea4ee56508255ce9988be9a9978aa0a Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sun, 23 May 2021 17:56:27 +0700 Subject: [PATCH 393/839] extract WikiPanelContainer --- osu.Game/Overlays/Wiki/WikiMainPage.cs | 44 ++------------- osu.Game/Overlays/Wiki/WikiPanelContainer.cs | 56 ++++++++++++++++++++ 2 files changed, 60 insertions(+), 40 deletions(-) create mode 100644 osu.Game/Overlays/Wiki/WikiPanelContainer.cs diff --git a/osu.Game/Overlays/Wiki/WikiMainPage.cs b/osu.Game/Overlays/Wiki/WikiMainPage.cs index 258f6a4f19..bdd0b4dc11 100644 --- a/osu.Game/Overlays/Wiki/WikiMainPage.cs +++ b/osu.Game/Overlays/Wiki/WikiMainPage.cs @@ -1,28 +1,19 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; +using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using System.Linq; using HtmlAgilityPack; using osu.Framework.Allocation; -using System.Collections.Generic; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics.Effects; -using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osu.Game.Overlays.Wiki.Markdown; -using osuTK; -using osuTK.Graphics; namespace osu.Game.Overlays.Wiki { public class WikiMainPage : FillFlowContainer { - [Resolved] - private OverlayColourProvider colourProvider { get; set; } - public string Markdown; public WikiMainPage() @@ -74,39 +65,12 @@ namespace osu.Game.Overlays.Wiki { var isFullWidth = panel.HasClass("wiki-main-page-panel--full"); - yield return new Container + yield return new WikiPanelContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Width = isFullWidth ? 1.0f : 0.5f, - Padding = new MarginPadding(3), - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - Masking = true, - CornerRadius = 4, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Colour = Color4.Black.Opacity(25), - Offset = new Vector2(0, 1), - Radius = 3, - }, - Child = new Box - { - Colour = colourProvider.Background4, - RelativeSizeAxes = Axes.Both, - }, - }, - new WikiMarkdownContainer - { - Text = panel.InnerText, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - } - } + Text = panel.InnerText, }; } } diff --git a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs new file mode 100644 index 0000000000..33c9369ccb --- /dev/null +++ b/osu.Game/Overlays/Wiki/WikiPanelContainer.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 osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; +using osu.Framework.Graphics.Shapes; +using osu.Game.Overlays.Wiki.Markdown; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Overlays.Wiki +{ + public class WikiPanelContainer : Container + { + [Resolved] + private OverlayColourProvider colourProvider { get; set; } + + public string Text; + + [BackgroundDependencyLoader] + private void load() + { + Padding = new MarginPadding(3); + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + CornerRadius = 4, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(25), + Offset = new Vector2(0, 1), + Radius = 3, + }, + Child = new Box + { + Colour = colourProvider.Background4, + RelativeSizeAxes = Axes.Both, + }, + }, + new WikiMarkdownContainer + { + Text = Text, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + } + }; + } + } +} From 4d222467ccb6d580de5f86b849058153fdaae745 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sun, 23 May 2021 18:00:54 +0700 Subject: [PATCH 394/839] initial WikiPanelMarkdownContainer --- osu.Game/Overlays/Wiki/WikiPanelContainer.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs index 33c9369ccb..2d5d806b12 100644 --- a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs +++ b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs @@ -44,7 +44,7 @@ namespace osu.Game.Overlays.Wiki RelativeSizeAxes = Axes.Both, }, }, - new WikiMarkdownContainer + new WikiPanelMarkdownContainer { Text = Text, RelativeSizeAxes = Axes.X, @@ -52,5 +52,15 @@ namespace osu.Game.Overlays.Wiki } }; } + + private class WikiPanelMarkdownContainer : WikiMarkdownContainer + { + public WikiPanelMarkdownContainer() + { + LineSpacing = 0; + DocumentPadding = new MarginPadding(30); + DocumentMargin = new MarginPadding(0); + } + } } } From 365a0b25f28a2bc7043816dde0b1046d52cc0e36 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sun, 23 May 2021 18:03:38 +0700 Subject: [PATCH 395/839] add IsFullWidth --- osu.Game/Overlays/Wiki/WikiMainPage.cs | 1 + osu.Game/Overlays/Wiki/WikiPanelContainer.cs | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/osu.Game/Overlays/Wiki/WikiMainPage.cs b/osu.Game/Overlays/Wiki/WikiMainPage.cs index bdd0b4dc11..c013cbec59 100644 --- a/osu.Game/Overlays/Wiki/WikiMainPage.cs +++ b/osu.Game/Overlays/Wiki/WikiMainPage.cs @@ -71,6 +71,7 @@ namespace osu.Game.Overlays.Wiki AutoSizeAxes = Axes.Y, Width = isFullWidth ? 1.0f : 0.5f, Text = panel.InnerText, + IsFullWidth = isFullWidth, }; } } diff --git a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs index 2d5d806b12..b58ba9bb09 100644 --- a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs +++ b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs @@ -20,6 +20,8 @@ namespace osu.Game.Overlays.Wiki public string Text; + public bool IsFullWidth; + [BackgroundDependencyLoader] private void load() { @@ -49,12 +51,15 @@ namespace osu.Game.Overlays.Wiki Text = Text, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, + IsFullWidth = IsFullWidth, } }; } private class WikiPanelMarkdownContainer : WikiMarkdownContainer { + public bool IsFullWidth; + public WikiPanelMarkdownContainer() { LineSpacing = 0; From 1e5f34567ae2f28598c8f08f355c84dffebd3a97 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sun, 23 May 2021 18:07:27 +0700 Subject: [PATCH 396/839] add panel heading main page --- osu.Game/Overlays/Wiki/WikiPanelContainer.cs | 31 ++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs index b58ba9bb09..00afb7b062 100644 --- a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs +++ b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs @@ -1,12 +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 Markdig.Syntax; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Containers.Markdown; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers.Markdown; using osu.Game.Overlays.Wiki.Markdown; using osuTK; using osuTK.Graphics; @@ -66,6 +70,33 @@ namespace osu.Game.Overlays.Wiki DocumentPadding = new MarginPadding(30); DocumentMargin = new MarginPadding(0); } + + protected override MarkdownHeading CreateHeading(HeadingBlock headingBlock) => new WikiPanelHeading(headingBlock) + { + IsFullWidth = IsFullWidth, + }; + } + + private class WikiPanelHeading : OsuMarkdownHeading + { + public bool IsFullWidth; + + public WikiPanelHeading(HeadingBlock headingBlock) + : base(headingBlock) + { + Margin = new MarginPadding { Bottom = 40 }; + } + + public override MarkdownTextFlowContainer CreateTextFlow() => base.CreateTextFlow().With(f => + { + f.Anchor = Anchor.TopCentre; + f.Origin = Anchor.TopCentre; + f.TextAnchor = Anchor.TopCentre; + }); + + protected override FontWeight GetFontWeightByLevel(int level) => FontWeight.Regular; + + protected override float GetFontSizeByLevel(int level) => base.GetFontSizeByLevel(IsFullWidth ? level : 3); } } } From 512d6d2f7fd7b2435b8e4920a59875df6c8c85d1 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sun, 23 May 2021 18:09:09 +0700 Subject: [PATCH 397/839] centering link inside panel --- osu.Game/Overlays/Wiki/WikiPanelContainer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs index 00afb7b062..31b939d2ac 100644 --- a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs +++ b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs @@ -71,6 +71,8 @@ namespace osu.Game.Overlays.Wiki DocumentMargin = new MarginPadding(0); } + public override MarkdownTextFlowContainer CreateTextFlow() => base.CreateTextFlow().With(f => f.TextAnchor = Anchor.TopCentre); + protected override MarkdownHeading CreateHeading(HeadingBlock headingBlock) => new WikiPanelHeading(headingBlock) { IsFullWidth = IsFullWidth, From cd211de7295f3dfb3464f0dcd29054423ea2ac86 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sun, 23 May 2021 18:10:56 +0700 Subject: [PATCH 398/839] make panel text bold --- osu.Game/Overlays/Wiki/WikiPanelContainer.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs index 31b939d2ac..c5dd8549da 100644 --- a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs +++ b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers.Markdown; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Graphics.Containers.Markdown; using osu.Game.Overlays.Wiki.Markdown; @@ -71,6 +72,8 @@ namespace osu.Game.Overlays.Wiki DocumentMargin = new MarginPadding(0); } + public override SpriteText CreateSpriteText() => base.CreateSpriteText().With(t => t.Font = t.Font.With(weight: FontWeight.Bold)); + public override MarkdownTextFlowContainer CreateTextFlow() => base.CreateTextFlow().With(f => f.TextAnchor = Anchor.TopCentre); protected override MarkdownHeading CreateHeading(HeadingBlock headingBlock) => new WikiPanelHeading(headingBlock) From 40688810966e0ecb29cc05fc7f17f09d26e77606 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sun, 23 May 2021 18:11:54 +0700 Subject: [PATCH 399/839] add bottom margin for paragraph --- osu.Game/Overlays/Wiki/WikiPanelContainer.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs index c5dd8549da..727cae766d 100644 --- a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs +++ b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs @@ -76,6 +76,9 @@ namespace osu.Game.Overlays.Wiki public override MarkdownTextFlowContainer CreateTextFlow() => base.CreateTextFlow().With(f => f.TextAnchor = Anchor.TopCentre); + protected override MarkdownParagraph CreateParagraph(ParagraphBlock paragraphBlock, int level) + => base.CreateParagraph(paragraphBlock, level).With(p => p.Margin = new MarginPadding { Bottom = 10 }); + protected override MarkdownHeading CreateHeading(HeadingBlock headingBlock) => new WikiPanelHeading(headingBlock) { IsFullWidth = IsFullWidth, From dc322d1c63f33712d0d4a1488bda78b0d4222448 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 23 May 2021 20:22:48 +0900 Subject: [PATCH 400/839] Run all type and sample mutations through standardising methods --- osu.Game.Rulesets.Taiko/Objects/Hit.cs | 21 +++++---------- .../Objects/TaikoHitObject.cs | 26 +++++++++++++++++++ .../Objects/TaikoStrongableHitObject.cs | 22 +++++++++++----- 3 files changed, 49 insertions(+), 20 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Hit.cs b/osu.Game.Rulesets.Taiko/Objects/Hit.cs index f4a66c39a8..8ede21fdad 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Hit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Hit.cs @@ -2,30 +2,23 @@ // See the LICENCE file in the repository root for full licence text. using System.Linq; -using osu.Framework.Bindables; using osu.Game.Audio; namespace osu.Game.Rulesets.Taiko.Objects { public class Hit : TaikoStrongableHitObject { - public readonly Bindable TypeBindable = new Bindable(); - - /// - /// The that actuates this . - /// - public HitType Type + protected override void UpdateTypeFromSamples() { - get => TypeBindable.Value; - set - { - TypeBindable.Value = value; - updateSamplesFromType(); - } + base.UpdateTypeFromSamples(); + + Type = getRimSamples().Any() ? HitType.Rim : HitType.Centre; } - private void updateSamplesFromType() + protected override void UpdateSamplesFromType() { + base.UpdateSamplesFromType(); + var rimSamples = getRimSamples(); bool isRimType = Type == HitType.Rim; diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs index f047c03f4b..46b864e7de 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.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 osu.Framework.Bindables; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; @@ -11,6 +12,17 @@ namespace osu.Game.Rulesets.Taiko.Objects { public abstract class TaikoHitObject : HitObject { + public readonly Bindable TypeBindable = new Bindable(); + + /// + /// The that actuates this . + /// + public HitType Type + { + get => TypeBindable.Value; + set => TypeBindable.Value = value; + } + /// /// Default size of a drawable taiko hit object. /// @@ -19,5 +31,19 @@ namespace osu.Game.Rulesets.Taiko.Objects public override Judgement CreateJudgement() => new TaikoJudgement(); protected override HitWindows CreateHitWindows() => new TaikoHitWindows(); + + protected TaikoHitObject() + { + SamplesBindable.BindCollectionChanged((_, __) => UpdateTypeFromSamples()); + TypeBindable.BindValueChanged(_ => UpdateSamplesFromType()); + } + + protected virtual void UpdateSamplesFromType() + { + } + + protected virtual void UpdateTypeFromSamples() + { + } } } diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs index cac56d1269..237000474d 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs @@ -33,15 +33,25 @@ namespace osu.Game.Rulesets.Taiko.Objects public bool IsStrong { get => IsStrongBindable.Value; - set - { - IsStrongBindable.Value = value; - updateSamplesFromStrong(); - } + set => IsStrongBindable.Value = value; } - private void updateSamplesFromStrong() + protected TaikoStrongableHitObject() { + IsStrongBindable.BindValueChanged(_ => UpdateSamplesFromType()); + } + + protected override void UpdateTypeFromSamples() + { + base.UpdateTypeFromSamples(); + + IsStrong = getStrongSamples().Any(); + } + + protected override void UpdateSamplesFromType() + { + base.UpdateSamplesFromType(); + var strongSamples = getStrongSamples(); if (IsStrongBindable.Value != strongSamples.Any()) From 8389d90f7ee1d1a6edb3197201c625700cac9907 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sun, 23 May 2021 18:52:20 +0700 Subject: [PATCH 401/839] add GridContainer in wiki main page --- osu.Game/Overlays/Wiki/WikiMainPage.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Wiki/WikiMainPage.cs b/osu.Game/Overlays/Wiki/WikiMainPage.cs index c013cbec59..2da5e5e928 100644 --- a/osu.Game/Overlays/Wiki/WikiMainPage.cs +++ b/osu.Game/Overlays/Wiki/WikiMainPage.cs @@ -30,9 +30,13 @@ namespace osu.Game.Overlays.Wiki Children = new Drawable[] { - createBlurb(html) + createBlurb(html), + new GridContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + }, }; - AddRange(createPanels(html)); } private Container createBlurb(HtmlDocument html) From 24fef221e304ac4d0f1c9fff9b551c8f5d11359f Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sun, 23 May 2021 19:07:10 +0700 Subject: [PATCH 402/839] change createPanels method to create grid content --- osu.Game/Overlays/Wiki/WikiMainPage.cs | 48 ++++++++++++++++++++------ 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/Wiki/WikiMainPage.cs b/osu.Game/Overlays/Wiki/WikiMainPage.cs index 2da5e5e928..12c17d5225 100644 --- a/osu.Game/Overlays/Wiki/WikiMainPage.cs +++ b/osu.Game/Overlays/Wiki/WikiMainPage.cs @@ -61,22 +61,48 @@ namespace osu.Game.Overlays.Wiki }; } - private IEnumerable createPanels(HtmlDocument html) + private IEnumerable createPanels(HtmlDocument html) { - var panelsNode = html.DocumentNode.SelectNodes("//div[contains(@class, 'wiki-main-page-panel')]"); + var panelsNode = html.DocumentNode.SelectNodes("//div[contains(@class, 'wiki-main-page-panel')]").ToArray(); - foreach (var panel in panelsNode) + for (var i = 0; i < panelsNode.Length; i++) { - var isFullWidth = panel.HasClass("wiki-main-page-panel--full"); + var isFullWidth = panelsNode[i].HasClass("wiki-main-page-panel--full"); - yield return new WikiPanelContainer + if (isFullWidth) { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Width = isFullWidth ? 1.0f : 0.5f, - Text = panel.InnerText, - IsFullWidth = isFullWidth, - }; + yield return new Drawable[] + { + new WikiPanelContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Text = panelsNode[i].InnerText, + IsFullWidth = true, + Width = 2, + }, + null, + }; + } + + if (i % 2 == 1) + { + yield return new Drawable[] + { + new WikiPanelContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Text = panelsNode[i].InnerText, + }, + new WikiPanelContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Text = panelsNode[i + 1].InnerText, + }, + }; + } } } } From 10c4ba3a746ac11b9899ce5c57523f0830bbaa2d Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sun, 23 May 2021 19:07:22 +0700 Subject: [PATCH 403/839] add panels to grid content --- osu.Game/Overlays/Wiki/WikiMainPage.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Overlays/Wiki/WikiMainPage.cs b/osu.Game/Overlays/Wiki/WikiMainPage.cs index 12c17d5225..c4e43d1438 100644 --- a/osu.Game/Overlays/Wiki/WikiMainPage.cs +++ b/osu.Game/Overlays/Wiki/WikiMainPage.cs @@ -28,6 +28,8 @@ namespace osu.Game.Overlays.Wiki var html = new HtmlDocument(); html.LoadHtml(Markdown); + var panels = createPanels(html).ToArray(); + Children = new Drawable[] { createBlurb(html), @@ -35,6 +37,8 @@ namespace osu.Game.Overlays.Wiki { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, + RowDimensions = Enumerable.Repeat(new Dimension(GridSizeMode.AutoSize), panels.Length).ToArray(), + Content = panels, }, }; } From bbfd7ea23f622f96bcec0f0d4713068bd1eda920 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 23 May 2021 21:20:08 +0900 Subject: [PATCH 404/839] Ensure `RegenerateAutoplay` is only run once per frame --- osu.Game/Rulesets/Edit/DrawableEditorRulesetWrapper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/DrawableEditorRulesetWrapper.cs b/osu.Game/Rulesets/Edit/DrawableEditorRulesetWrapper.cs index 62e2539c2a..8166e6b8ce 100644 --- a/osu.Game/Rulesets/Edit/DrawableEditorRulesetWrapper.cs +++ b/osu.Game/Rulesets/Edit/DrawableEditorRulesetWrapper.cs @@ -60,7 +60,7 @@ namespace osu.Game.Rulesets.Edit } } - private void updateReplay() => drawableRuleset.RegenerateAutoplay(); + private void updateReplay() => Scheduler.AddOnce(drawableRuleset.RegenerateAutoplay); private void addHitObject(HitObject hitObject) { From 4c9d72e62ae4d82404aa69a0b0d34b81db4b93a7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 23 May 2021 21:19:38 +0900 Subject: [PATCH 405/839] Ensure `EditorBeatmap.Update` is called inside `PerformOnSelection` calls --- osu.Game.Rulesets.Taiko/Edit/TaikoSelectionHandler.cs | 6 +++++- .../Edit/Compose/Components/EditorBlueprintContainer.cs | 8 +++++++- .../Edit/Compose/Components/EditorSelectionHandler.cs | 7 ++++++- .../Components/Timeline/TimelineBlueprintContainer.cs | 6 +++++- 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Edit/TaikoSelectionHandler.cs b/osu.Game.Rulesets.Taiko/Edit/TaikoSelectionHandler.cs index a24130d6ac..ab3b729307 100644 --- a/osu.Game.Rulesets.Taiko/Edit/TaikoSelectionHandler.cs +++ b/osu.Game.Rulesets.Taiko/Edit/TaikoSelectionHandler.cs @@ -69,7 +69,11 @@ namespace osu.Game.Rulesets.Taiko.Edit { EditorBeatmap.PerformOnSelection(h => { - if (h is Hit taikoHit) taikoHit.Type = state ? HitType.Rim : HitType.Centre; + if (h is Hit taikoHit) + { + taikoHit.Type = state ? HitType.Rim : HitType.Centre; + EditorBeatmap.Update(h); + } }); } diff --git a/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs index 5a6f98f504..22b211f257 100644 --- a/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs @@ -77,7 +77,13 @@ namespace osu.Game.Screens.Edit.Compose.Components double offset = result.Time.Value - blueprints.First().Item.StartTime; if (offset != 0) - Beatmap.PerformOnSelection(obj => obj.StartTime += offset); + { + Beatmap.PerformOnSelection(obj => + { + obj.StartTime += offset; + Beatmap.Update(obj); + }); + } } return true; diff --git a/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs index 2141c490df..246d4aa8d7 100644 --- a/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs @@ -125,6 +125,7 @@ namespace osu.Game.Screens.Edit.Compose.Components return; h.Samples.Add(new HitSampleInfo(sampleName)); + EditorBeatmap.Update(h); }); } @@ -134,7 +135,11 @@ namespace osu.Game.Screens.Edit.Compose.Components /// The name of the hit sample. public void RemoveHitSample(string sampleName) { - EditorBeatmap.PerformOnSelection(h => h.SamplesBindable.RemoveAll(s => s.Name == sampleName)); + EditorBeatmap.PerformOnSelection(h => + { + h.SamplesBindable.RemoveAll(s => s.Name == sampleName); + EditorBeatmap.Update(h); + }); } /// diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index 7c1bbd65f9..6f04f36b83 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -276,7 +276,11 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline var timingPoint = EditorBeatmap.ControlPointInfo.TimingPointAt(selected.First().StartTime); double adjustment = timingPoint.BeatLength / EditorBeatmap.BeatDivisor * amount; - EditorBeatmap.PerformOnSelection(h => h.StartTime += adjustment); + EditorBeatmap.PerformOnSelection(h => + { + h.StartTime += adjustment; + EditorBeatmap.Update(h); + }); } } From 6751d79ce8ef94b465360384c7d8ffbaca84330c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 23 May 2021 16:21:27 +0300 Subject: [PATCH 406/839] Fix oversight in HUD overlay components top positioning logic --- osu.Game/Screens/Play/HUDOverlay.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index ab5b01cab6..16285ab035 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -176,10 +176,7 @@ namespace osu.Game.Screens.Play foreach (var element in mainComponents.Components.Cast()) { // for now align top-right components with the bottom-edge of the lowest top-anchored hud element. - if (!element.RelativeSizeAxes.HasFlagFast(Axes.X)) - continue; - - if (element.Anchor.HasFlagFast(Anchor.TopRight)) + if (element.Anchor.HasFlagFast(Anchor.TopRight) || (element.Anchor.HasFlagFast(Anchor.y0) && element.RelativeSizeAxes == Axes.X)) { // health bars are excluded for the sake of hacky legacy skins which extend the health bar to take up the full screen area. if (element is LegacyHealthDisplay) From d605b6bb8db010c73a59129d93185466a3ee3a8c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 23 May 2021 16:22:51 +0300 Subject: [PATCH 407/839] Fix HUD overlay components bottom positioning logic accounting for combo --- osu.Game/Screens/Play/HUDOverlay.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 16285ab035..31bb640d17 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -186,7 +186,8 @@ namespace osu.Game.Screens.Play if (lowestTopScreenSpace == null || bottomRight.Y > lowestTopScreenSpace.Value.Y) lowestTopScreenSpace = bottomRight; } - else if (element.Anchor.HasFlagFast(Anchor.y2)) + // and align bottom-right components with the top-edge of the highest bottom-anchored hud element. + else if (element.Anchor.HasFlagFast(Anchor.BottomRight) || (element.Anchor.HasFlagFast(Anchor.y2) && element.RelativeSizeAxes == Axes.X)) { var topLeft = element.ScreenSpaceDrawQuad.TopLeft; if (highestBottomScreenSpace == null || topLeft.Y < highestBottomScreenSpace.Value.Y) From 24960c4fb83cfea649ce22a04767abfefc748431 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sun, 23 May 2021 21:31:49 +0700 Subject: [PATCH 408/839] move panel spacing to ctor --- osu.Game/Overlays/Wiki/WikiMainPage.cs | 6 ------ osu.Game/Overlays/Wiki/WikiPanelContainer.cs | 8 +++++++- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Wiki/WikiMainPage.cs b/osu.Game/Overlays/Wiki/WikiMainPage.cs index c4e43d1438..8ff85c1404 100644 --- a/osu.Game/Overlays/Wiki/WikiMainPage.cs +++ b/osu.Game/Overlays/Wiki/WikiMainPage.cs @@ -79,8 +79,6 @@ namespace osu.Game.Overlays.Wiki { new WikiPanelContainer { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, Text = panelsNode[i].InnerText, IsFullWidth = true, Width = 2, @@ -95,14 +93,10 @@ namespace osu.Game.Overlays.Wiki { new WikiPanelContainer { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, Text = panelsNode[i].InnerText, }, new WikiPanelContainer { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, Text = panelsNode[i + 1].InnerText, }, }; diff --git a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs index 727cae766d..64f4d9fbcd 100644 --- a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs +++ b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs @@ -27,10 +27,16 @@ namespace osu.Game.Overlays.Wiki public bool IsFullWidth; + public WikiPanelContainer() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Padding = new MarginPadding(3); + } + [BackgroundDependencyLoader] private void load() { - Padding = new MarginPadding(3); Children = new Drawable[] { new Container From bd1454bdd1c26de28c5ec91cf6ed89dec218bc94 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sun, 23 May 2021 21:42:58 +0700 Subject: [PATCH 409/839] update height to max of its parent height --- osu.Game/Overlays/Wiki/WikiPanelContainer.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs index 64f4d9fbcd..f0fecce8dc 100644 --- a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs +++ b/osu.Game/Overlays/Wiki/WikiPanelContainer.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 Markdig.Syntax; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; @@ -23,6 +24,8 @@ namespace osu.Game.Overlays.Wiki [Resolved] private OverlayColourProvider colourProvider { get; set; } + private WikiPanelMarkdownContainer panelContainer; + public string Text; public bool IsFullWidth; @@ -30,7 +33,6 @@ namespace osu.Game.Overlays.Wiki public WikiPanelContainer() { RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; Padding = new MarginPadding(3); } @@ -57,7 +59,7 @@ namespace osu.Game.Overlays.Wiki RelativeSizeAxes = Axes.Both, }, }, - new WikiPanelMarkdownContainer + panelContainer = new WikiPanelMarkdownContainer { Text = Text, RelativeSizeAxes = Axes.X, @@ -67,6 +69,12 @@ namespace osu.Game.Overlays.Wiki }; } + protected override void Update() + { + base.Update(); + Height = Math.Max(panelContainer.Height, Parent.DrawHeight); + } + private class WikiPanelMarkdownContainer : WikiMarkdownContainer { public bool IsFullWidth; From 222c34c0a1c77113a0ceb128f37db1acfab547e0 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sun, 23 May 2021 21:46:41 +0700 Subject: [PATCH 410/839] fix heading font weight to light --- osu.Game/Overlays/Wiki/WikiPanelContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs index f0fecce8dc..71b492b375 100644 --- a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs +++ b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs @@ -116,7 +116,7 @@ namespace osu.Game.Overlays.Wiki f.TextAnchor = Anchor.TopCentre; }); - protected override FontWeight GetFontWeightByLevel(int level) => FontWeight.Regular; + protected override FontWeight GetFontWeightByLevel(int level) => FontWeight.Light; protected override float GetFontSizeByLevel(int level) => base.GetFontSizeByLevel(IsFullWidth ? level : 3); } From 593fea0d5fe8ae8a9286a41b967833fab1443c2d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 24 May 2021 13:14:18 +0900 Subject: [PATCH 411/839] Limit automatically calculated HUD offsets to keep menu items on screen --- osu.Game/Screens/Play/HUDOverlay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 31bb640d17..676d7f10e1 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -196,12 +196,12 @@ namespace osu.Game.Screens.Play } if (lowestTopScreenSpace.HasValue) - topRightElements.Y = TopScoringElementsHeight = ToLocalSpace(lowestTopScreenSpace.Value).Y; + topRightElements.Y = TopScoringElementsHeight = Math.Max(0, ToLocalSpace(lowestTopScreenSpace.Value).Y); else topRightElements.Y = 0; if (highestBottomScreenSpace.HasValue) - bottomRightElements.Y = BottomScoringElementsHeight = -(DrawHeight - ToLocalSpace(highestBottomScreenSpace.Value).Y); + bottomRightElements.Y = BottomScoringElementsHeight = -Math.Max(0, (DrawHeight - ToLocalSpace(highestBottomScreenSpace.Value).Y)); else bottomRightElements.Y = 0; } From 83981b692eda95bf0ea97faedfc92381cf2fb370 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 24 May 2021 13:44:13 +0900 Subject: [PATCH 412/839] Also handle items exiting bounds on the opposite side --- osu.Game/Screens/Play/HUDOverlay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 676d7f10e1..ffe03815f5 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -196,12 +196,12 @@ namespace osu.Game.Screens.Play } if (lowestTopScreenSpace.HasValue) - topRightElements.Y = TopScoringElementsHeight = Math.Max(0, ToLocalSpace(lowestTopScreenSpace.Value).Y); + topRightElements.Y = TopScoringElementsHeight = MathHelper.Clamp(ToLocalSpace(lowestTopScreenSpace.Value).Y, 0, DrawHeight - topRightElements.DrawHeight); else topRightElements.Y = 0; if (highestBottomScreenSpace.HasValue) - bottomRightElements.Y = BottomScoringElementsHeight = -Math.Max(0, (DrawHeight - ToLocalSpace(highestBottomScreenSpace.Value).Y)); + bottomRightElements.Y = BottomScoringElementsHeight = -MathHelper.Clamp(DrawHeight - ToLocalSpace(highestBottomScreenSpace.Value).Y, 0, DrawHeight - bottomRightElements.DrawHeight); else bottomRightElements.Y = 0; } From a69a1b521105654d716a7876b279a0d0a1322bd4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 24 May 2021 13:53:51 +0900 Subject: [PATCH 413/839] Fix `Player` potentially running `MakeCurrent` when already removed from the screen stack Closes #12919. --- 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 39f9e2d388..ee940fae40 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -519,7 +519,7 @@ namespace osu.Game.Screens.Play // there is a chance that the exit was performed after the transition to results has started. // we want to give the user what they want, so forcefully return to this screen (to proceed with the upwards exit process). - if (!this.IsCurrentScreen()) + if (!this.IsCurrentScreen() && this.GetChildScreen() != null) { ValidForResume = false; this.MakeCurrent(); From 7494ddeef40293f989c412e51438e450dd93997b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 24 May 2021 14:07:40 +0900 Subject: [PATCH 414/839] Fix DHOs not receiving initial skin changed events --- .../Objects/Drawables/DrawableHitObject.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 86c733c392..cc663c37af 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -172,7 +172,13 @@ namespace osu.Game.Rulesets.Objects.Drawables base.AddInternal(Samples = new PausableSkinnableSound()); CurrentSkin = skinSource; - CurrentSkin.SourceChanged += onSkinSourceChanged; + CurrentSkin.SourceChanged += skinSourceChanged; + } + + protected override void LoadAsyncComplete() + { + base.LoadAsyncComplete(); + skinChanged(); } protected override void LoadComplete() @@ -495,7 +501,9 @@ namespace osu.Game.Rulesets.Objects.Drawables protected ISkinSource CurrentSkin { get; private set; } - private void onSkinSourceChanged() => Scheduler.AddOnce(() => + private void skinSourceChanged() => Scheduler.AddOnce(skinChanged); + + private void skinChanged() { UpdateComboColour(); @@ -503,7 +511,7 @@ namespace osu.Game.Rulesets.Objects.Drawables if (IsLoaded) updateState(State.Value, true); - }); + } protected void UpdateComboColour() { @@ -747,7 +755,7 @@ namespace osu.Game.Rulesets.Objects.Drawables if (HitObject != null) HitObject.DefaultsApplied -= onDefaultsApplied; - CurrentSkin.SourceChanged -= onSkinSourceChanged; + CurrentSkin.SourceChanged -= skinSourceChanged; } } From 8dd3f11d28db64bb85256c4901f5ff8c26e47108 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 24 May 2021 14:19:10 +0900 Subject: [PATCH 415/839] Tidy up struct and previous object handling --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 188 +++++++++++---------- 1 file changed, 97 insertions(+), 91 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index d06e807500..5214020a84 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -52,35 +52,32 @@ namespace osu.Game.Rulesets.Osu.Mods var rng = new Random((int)Seed.Value); - var prevObjectInfo = new HitObjectInfo + var prevObjectInfo = new RandomObjectInfo { - StartPosUnchanged = hitObjects[0].Position, - EndPosUnchanged = hitObjects[0].EndPosition, - StartPosChanged = hitObjects[0].Position, - EndPosChanged = hitObjects[0].EndPosition + PositionOriginal = hitObjects[0].Position, + EndPositionOriginal = hitObjects[0].EndPosition, + PositionRandomised = hitObjects[0].Position, + EndPositionRandomised = hitObjects[0].EndPosition }; float rateOfChangeMultiplier = 0; for (int i = 0; i < hitObjects.Count; i++) { - var h = hitObjects[i]; + var hitObject = hitObjects[i]; - var currentObjectInfo = new HitObjectInfo - { - StartPosUnchanged = h.Position, - EndPosUnchanged = h.EndPosition, - StartPosChanged = Vector2.Zero, - EndPosChanged = Vector2.Zero - }; + var currentObjectInfo = new RandomObjectInfo(hitObject); + + if (i == 0) + prevObjectInfo = currentObjectInfo; // rateOfChangeMultiplier only changes every i iterations to prevent shaky-line-shaped streams if (i % 3 == 0) rateOfChangeMultiplier = (float)rng.NextDouble() * 2 - 1; - var distanceToPrev = Vector2.Distance(prevObjectInfo.EndPosUnchanged, currentObjectInfo.StartPosUnchanged); + var distanceToPrev = Vector2.Distance(prevObjectInfo.EndPositionOriginal, currentObjectInfo.PositionOriginal); - switch (h) + switch (hitObject) { case HitCircle circle: getObjectInfo( @@ -90,15 +87,15 @@ namespace osu.Game.Rulesets.Osu.Mods ref currentObjectInfo ); - circle.Position = currentObjectInfo.StartPosChanged; - currentObjectInfo.EndPosChanged = currentObjectInfo.StartPosChanged; + circle.Position = currentObjectInfo.PositionRandomised; + currentObjectInfo.EndPositionRandomised = currentObjectInfo.PositionRandomised; break; case Slider slider: - currentObjectInfo.EndPosUnchanged = slider.EndPosition; + currentObjectInfo.EndPositionOriginal = slider.EndPosition; - currentObjectInfo.EndPosUnchanged = slider.TailCircle.Position; + currentObjectInfo.EndPositionOriginal = slider.TailCircle.Position; getObjectInfo( rateOfChangeMultiplier, @@ -107,12 +104,12 @@ namespace osu.Game.Rulesets.Osu.Mods ref currentObjectInfo ); - slider.Position = currentObjectInfo.StartPosChanged; - currentObjectInfo.EndPosChanged = slider.TailCircle.Position; + slider.Position = currentObjectInfo.PositionRandomised; + currentObjectInfo.EndPositionRandomised = slider.TailCircle.Position; moveSliderIntoPlayfield(ref slider, ref currentObjectInfo); - var sliderShift = Vector2.Subtract(slider.Position, currentObjectInfo.StartPosUnchanged); + var sliderShift = Vector2.Subtract(slider.Position, currentObjectInfo.PositionOriginal); foreach (var tick in slider.NestedHitObjects.OfType()) tick.Position = Vector2.Add(tick.Position, sliderShift); @@ -128,7 +125,7 @@ namespace osu.Game.Rulesets.Osu.Mods /// Returns the final position of the hit object /// /// Final position of the hit object - private void getObjectInfo(float rateOfChangeMultiplier, HitObjectInfo prevObjectInfo, float distanceToPrev, ref HitObjectInfo currentObjectInfo) + private void getObjectInfo(float rateOfChangeMultiplier, RandomObjectInfo prevObjectInfo, float distanceToPrev, ref RandomObjectInfo currentObjectInfo) { // The max. angle (relative to the angle of the vector pointing from the 2nd last to the last hit object) // is proportional to the distance between the last and the current hit object @@ -145,10 +142,10 @@ namespace osu.Game.Rulesets.Osu.Mods distanceToPrev * (float)Math.Sin(currentObjectInfo.AngleRad) ); - posRelativeToPrev = getRotatedVector(prevObjectInfo.EndPosChanged, posRelativeToPrev); + posRelativeToPrev = getRotatedVector(prevObjectInfo.EndPositionRandomised, posRelativeToPrev); currentObjectInfo.AngleRad = (float)Math.Atan2(posRelativeToPrev.Y, posRelativeToPrev.X); - var position = Vector2.Add(prevObjectInfo.EndPosChanged, posRelativeToPrev); + var position = Vector2.Add(prevObjectInfo.EndPositionRandomised, posRelativeToPrev); // Move hit objects back into the playfield if they are outside of it, // which would sometimes happen during big jumps otherwise. @@ -162,10 +159,10 @@ namespace osu.Game.Rulesets.Osu.Mods else if (position.Y > OsuPlayfield.BASE_SIZE.Y) position.Y = OsuPlayfield.BASE_SIZE.Y; - currentObjectInfo.StartPosChanged = position; + currentObjectInfo.PositionRandomised = position; } - private void moveSliderIntoPlayfield(ref Slider slider, ref HitObjectInfo currentObjectInfo) + private void moveSliderIntoPlayfield(ref Slider slider, ref RandomObjectInfo currentObjectInfo) { foreach (var controlPoint in slider.Path.ControlPoints) { @@ -185,7 +182,7 @@ namespace osu.Game.Rulesets.Osu.Mods slider.Position = new Vector2(slider.Position.X, playfieldSize.Y - pos.Y); } - currentObjectInfo.EndPosChanged = slider.TailCircle.Position; + currentObjectInfo.EndPositionRandomised = slider.TailCircle.Position; } /// @@ -266,89 +263,98 @@ namespace osu.Game.Rulesets.Osu.Mods ); } - private struct HitObjectInfo + private struct RandomObjectInfo { internal float AngleRad { get; set; } - internal Vector2 StartPosUnchanged { get; set; } - internal Vector2 EndPosUnchanged { get; set; } - internal Vector2 StartPosChanged { get; set; } - internal Vector2 EndPosChanged { get; set; } - } - } - public class OsuModRandomSettingsControl : SettingsItem - { - protected override Drawable CreateControl() => new SeedControl - { - RelativeSizeAxes = Axes.X, - Margin = new MarginPadding { Top = 5 } - }; + internal Vector2 PositionOriginal { get; set; } + internal Vector2 PositionRandomised { get; set; } - private sealed class SeedControl : CompositeDrawable, IHasCurrentValue - { - private readonly BindableWithCurrent current = new BindableWithCurrent(); + internal Vector2 EndPositionOriginal { get; set; } + internal Vector2 EndPositionRandomised { get; set; } - public Bindable Current + public RandomObjectInfo(OsuHitObject hitObject) { - get => current; - set - { - current.Current = value; - seedNumberBox.Text = value.Value.ToString(); - } + PositionRandomised = PositionOriginal = hitObject.Position; + EndPositionRandomised = EndPositionOriginal = hitObject.EndPosition; + AngleRad = 0; } + } - private readonly OsuNumberBox seedNumberBox; - - public SeedControl() + public class OsuModRandomSettingsControl : SettingsItem + { + protected override Drawable CreateControl() => new SeedControl { - AutoSizeAxes = Axes.Y; + RelativeSizeAxes = Axes.X, + Margin = new MarginPadding { Top = 5 } + }; - InternalChildren = new[] + private sealed class SeedControl : CompositeDrawable, IHasCurrentValue + { + private readonly BindableWithCurrent current = new BindableWithCurrent(); + + public Bindable Current { - new GridContainer + get => current; + set { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - ColumnDimensions = new[] + current.Current = value; + seedNumberBox.Text = value.Value.ToString(); + } + } + + private readonly OsuNumberBox seedNumberBox; + + public SeedControl() + { + AutoSizeAxes = Axes.Y; + + InternalChildren = new[] + { + new GridContainer { - new Dimension(), - new Dimension(GridSizeMode.Absolute, 2), - new Dimension(GridSizeMode.Relative, 0.25f) - }, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize) - }, - Content = new[] - { - new Drawable[] + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + ColumnDimensions = new[] { - seedNumberBox = new OsuNumberBox + new Dimension(), + new Dimension(GridSizeMode.Absolute, 2), + new Dimension(GridSizeMode.Relative, 0.25f) + }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize) + }, + Content = new[] + { + new Drawable[] { - RelativeSizeAxes = Axes.X, - CommitOnFocusLost = true + seedNumberBox = new OsuNumberBox + { + RelativeSizeAxes = Axes.X, + CommitOnFocusLost = true + } } } } - } - }; + }; - seedNumberBox.Current.BindValueChanged(e => + seedNumberBox.Current.BindValueChanged(e => + { + int? value = null; + + if (int.TryParse(e.NewValue, out var intVal)) + value = intVal; + + current.Value = value; + }); + } + + protected override void Update() { - int? value = null; - - if (int.TryParse(e.NewValue, out var intVal)) - value = intVal; - - current.Value = value; - }); - } - - protected override void Update() - { - if (current.Value == null) - seedNumberBox.Text = current.Current.Value.ToString(); + if (current.Value == null) + seedNumberBox.Text = current.Current.Value.ToString(); + } } } } From 06fe0563d30d515a40729c7946ed326c41292ac7 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 24 May 2021 08:26:44 +0300 Subject: [PATCH 416/839] Move GetNewsRequest from ArticleListing to NewsOverlay --- .../Overlays/News/Displays/ArticleListing.cs | 46 ++++------------ osu.Game/Overlays/NewsOverlay.cs | 54 ++++++++++++++++--- 2 files changed, 56 insertions(+), 44 deletions(-) diff --git a/osu.Game/Overlays/News/Displays/ArticleListing.cs b/osu.Game/Overlays/News/Displays/ArticleListing.cs index b49326a1f1..b554b462a9 100644 --- a/osu.Game/Overlays/News/Displays/ArticleListing.cs +++ b/osu.Game/Overlays/News/Displays/ArticleListing.cs @@ -8,9 +8,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.UserInterface; -using osu.Game.Online.API; using osu.Game.Online.API.Requests; -using osu.Game.Online.API.Requests.Responses; using osuTK; namespace osu.Game.Overlays.News.Displays @@ -20,26 +18,20 @@ namespace osu.Game.Overlays.News.Displays /// public class ArticleListing : CompositeDrawable { - public Action SidebarMetadataUpdated; - - [Resolved] - private IAPIProvider api { get; set; } + public Action RequestMorePosts; private FillFlowContainer content; private ShowMoreButton showMore; - private GetNewsRequest request; - private Cursor lastCursor; - - private readonly int? year; + private readonly GetNewsResponse initialResponse; /// /// Instantiate a listing for the specified year. /// - /// The year to load articles from. If null, will show the most recent articles. - public ArticleListing(int? year = null) + /// Initial response to create articles from. + public ArticleListing(GetNewsResponse initialResponse) { - this.year = year; + this.initialResponse = initialResponse; } [BackgroundDependencyLoader] @@ -69,7 +61,8 @@ namespace osu.Game.Overlays.News.Displays RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 10) + Spacing = new Vector2(0, 10), + Children = initialResponse.NewsPosts.Select(p => new NewsCard(p)).ToList() }, showMore = new ShowMoreButton { @@ -79,37 +72,19 @@ namespace osu.Game.Overlays.News.Displays { Top = 15 }, - Action = performFetch, - Alpha = 0 + Action = RequestMorePosts, + Alpha = initialResponse.Cursor != null ? 1 : 0 } } }; - - performFetch(); - } - - private void performFetch() - { - request?.Cancel(); - - request = new GetNewsRequest(year, lastCursor); - request.Success += response => Schedule(() => onSuccess(response)); - api.PerformAsync(request); } private CancellationTokenSource cancellationToken; - private void onSuccess(GetNewsResponse response) + public void AddPosts(GetNewsResponse response) { cancellationToken?.Cancel(); - // only needs to be updated on the initial load, as the content won't change during pagination. - if (lastCursor == null) - SidebarMetadataUpdated?.Invoke(response.SidebarMetadata); - - // store cursor for next pagination request. - lastCursor = response.Cursor; - LoadComponentsAsync(response.NewsPosts.Select(p => new NewsCard(p)).ToList(), loaded => { content.AddRange(loaded); @@ -121,7 +96,6 @@ namespace osu.Game.Overlays.News.Displays protected override void Dispose(bool isDisposing) { - request?.Cancel(); cancellationToken?.Cancel(); base.Dispose(isDisposing); } diff --git a/osu.Game/Overlays/NewsOverlay.cs b/osu.Game/Overlays/NewsOverlay.cs index dd6de40ecb..751ac1d10a 100644 --- a/osu.Game/Overlays/NewsOverlay.cs +++ b/osu.Game/Overlays/NewsOverlay.cs @@ -6,6 +6,8 @@ using System.Threading; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; using osu.Game.Overlays.News; using osu.Game.Overlays.News.Displays; using osu.Game.Overlays.News.Sidebar; @@ -18,9 +20,12 @@ namespace osu.Game.Overlays private readonly Container sidebarContainer; private readonly NewsSidebar sidebar; - private readonly Container content; + private APIRequest lastRequest; + private Cursor lastCursor; + private int? year; + private CancellationTokenSource cancellationToken; private bool displayUpdateRequired = true; @@ -108,7 +113,11 @@ namespace osu.Game.Overlays protected void LoadDisplay(Drawable display) { ScrollFlow.ScrollToStart(); - LoadComponentAsync(display, loaded => content.Child = loaded, (cancellationToken = new CancellationTokenSource()).Token); + LoadComponentAsync(display, loaded => + { + content.Child = loaded; + Loading.Hide(); + }, (cancellationToken = new CancellationTokenSource()).Token); } protected override void UpdateAfterChildren() @@ -132,13 +141,41 @@ namespace osu.Game.Overlays Header.SetFrontPage(); - var page = new ArticleListing(year); - page.SidebarMetadataUpdated += metadata => Schedule(() => + this.year = year; + lastCursor = null; + + performListingRequest(response => { - sidebar.Metadata.Value = metadata; - Loading.Hide(); + sidebar.Metadata.Value = response.SidebarMetadata; + + var listing = new ArticleListing(response); + listing.RequestMorePosts += getMorePosts; + + LoadDisplay(listing); }); - LoadDisplay(page); + } + + private void getMorePosts() + { + lastRequest?.Cancel(); + performListingRequest(response => + { + if (content.Child is ArticleListing listing) + listing.AddPosts(response); + }); + } + + private void performListingRequest(Action onSuccess) + { + lastRequest = new GetNewsRequest(year, lastCursor); + + ((GetNewsRequest)lastRequest).Success += response => Schedule(() => + { + lastCursor = response.Cursor; + onSuccess?.Invoke(response); + }); + + API.PerformAsync(lastRequest); } private void loadArticle(string article) @@ -149,17 +186,18 @@ namespace osu.Game.Overlays // Temporary, should be handled by ArticleDisplay later LoadDisplay(Empty()); - Loading.Hide(); } private void beginLoading() { + lastRequest?.Cancel(); cancellationToken?.Cancel(); Loading.Show(); } protected override void Dispose(bool isDisposing) { + lastRequest?.Cancel(); cancellationToken?.Cancel(); base.Dispose(isDisposing); } From 88d7bc195dea687d508e6667b8dfd91c1162d1e0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 24 May 2021 14:24:56 +0900 Subject: [PATCH 417/839] Split out and clean up playfield sizing references --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 27 +++++++++++----------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 5214020a84..bbae746f67 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -29,10 +29,16 @@ namespace osu.Game.Rulesets.Osu.Mods public override string Description => "It never gets boring!"; public override bool Ranked => false; - // The distances from the hit objects to the borders of the playfield they start to "turn around" and curve towards the middle. + // The relative distance to the edge of the playfield before objects' positions should start to "turn around" and curve towards the middle. // The closer the hit objects draw to the border, the sharper the turn - private const byte border_distance_x = 192; - private const byte border_distance_y = 144; + private const float playfield_edge_ratio = 0.375f; + + private static readonly float border_distance_x = OsuPlayfield.BASE_SIZE.X * playfield_edge_ratio; + private static readonly float border_distance_y = OsuPlayfield.BASE_SIZE.Y * playfield_edge_ratio; + + private static readonly Vector2 playfield_middle = Vector2.Divide(OsuPlayfield.BASE_SIZE, 2); + + private static readonly float playfield_diagonal = OsuPlayfield.BASE_SIZE.LengthFast; [SettingSource("Seed", "Use a custom seed instead of a random one", SettingControlType = typeof(OsuModRandomSettingsControl))] public Bindable Seed { get; } = new Bindable @@ -130,8 +136,7 @@ namespace osu.Game.Rulesets.Osu.Mods // The max. angle (relative to the angle of the vector pointing from the 2nd last to the last hit object) // is proportional to the distance between the last and the current hit object // to allow jumps and prevent too sharp turns during streams. - var maxDistance = OsuPlayfield.BASE_SIZE.LengthFast; - var randomAngleRad = rateOfChangeMultiplier * 2 * Math.PI * distanceToPrev / maxDistance; + var randomAngleRad = rateOfChangeMultiplier * 2 * Math.PI * distanceToPrev / playfield_diagonal; currentObjectInfo.AngleRad = (float)randomAngleRad + prevObjectInfo.AngleRad; if (currentObjectInfo.AngleRad < 0) @@ -145,6 +150,7 @@ namespace osu.Game.Rulesets.Osu.Mods posRelativeToPrev = getRotatedVector(prevObjectInfo.EndPositionRandomised, posRelativeToPrev); currentObjectInfo.AngleRad = (float)Math.Atan2(posRelativeToPrev.Y, posRelativeToPrev.X); + var position = Vector2.Add(prevObjectInfo.EndPositionRandomised, posRelativeToPrev); // Move hit objects back into the playfield if they are outside of it, @@ -192,9 +198,8 @@ namespace osu.Game.Rulesets.Osu.Mods private Vector2 getRotatedVector(Vector2 prevPosChanged, Vector2 posRelativeToPrev) { var relativeRotationDistance = 0f; - var playfieldMiddle = Vector2.Divide(OsuPlayfield.BASE_SIZE, 2); - if (prevPosChanged.X < playfieldMiddle.X) + if (prevPosChanged.X < playfield_middle.X) { relativeRotationDistance = Math.Max( (border_distance_x - prevPosChanged.X) / border_distance_x, @@ -209,7 +214,7 @@ namespace osu.Game.Rulesets.Osu.Mods ); } - if (prevPosChanged.Y < playfieldMiddle.Y) + if (prevPosChanged.Y < playfield_middle.Y) { relativeRotationDistance = Math.Max( (border_distance_y - prevPosChanged.Y) / border_distance_y, @@ -224,11 +229,7 @@ namespace osu.Game.Rulesets.Osu.Mods ); } - return rotateVectorTowardsVector( - posRelativeToPrev, - Vector2.Subtract(playfieldMiddle, prevPosChanged), - relativeRotationDistance / 2 - ); + return rotateVectorTowardsVector(posRelativeToPrev, playfield_middle - prevPosChanged, relativeRotationDistance / 2); } /// From a92ded8a2fc8e9e947d1258777000d5252aa81cd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 24 May 2021 14:28:07 +0900 Subject: [PATCH 418/839] Apply renaming and general code clean-up --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index bbae746f67..61559c06b9 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -86,7 +86,7 @@ namespace osu.Game.Rulesets.Osu.Mods switch (hitObject) { case HitCircle circle: - getObjectInfo( + applyRandomisation( rateOfChangeMultiplier, prevObjectInfo, distanceToPrev, @@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Osu.Mods currentObjectInfo.EndPositionOriginal = slider.TailCircle.Position; - getObjectInfo( + applyRandomisation( rateOfChangeMultiplier, prevObjectInfo, distanceToPrev, @@ -131,7 +131,7 @@ namespace osu.Game.Rulesets.Osu.Mods /// Returns the final position of the hit object /// /// Final position of the hit object - private void getObjectInfo(float rateOfChangeMultiplier, RandomObjectInfo prevObjectInfo, float distanceToPrev, ref RandomObjectInfo currentObjectInfo) + private void applyRandomisation(float rateOfChangeMultiplier, RandomObjectInfo prevObjectInfo, float distanceToPrev, ref RandomObjectInfo currentObjectInfo) { // The max. angle (relative to the angle of the vector pointing from the 2nd last to the last hit object) // is proportional to the distance between the last and the current hit object @@ -155,15 +155,8 @@ namespace osu.Game.Rulesets.Osu.Mods // Move hit objects back into the playfield if they are outside of it, // which would sometimes happen during big jumps otherwise. - if (position.X < 0) - position.X = 0; - else if (position.X > OsuPlayfield.BASE_SIZE.X) - position.X = OsuPlayfield.BASE_SIZE.X; - - if (position.Y < 0) - position.Y = 0; - else if (position.Y > OsuPlayfield.BASE_SIZE.Y) - position.Y = OsuPlayfield.BASE_SIZE.Y; + position.X = MathHelper.Clamp(position.X, 0, OsuPlayfield.BASE_SIZE.X); + position.Y = MathHelper.Clamp(position.Y, 0, OsuPlayfield.BASE_SIZE.Y); currentObjectInfo.PositionRandomised = position; } From 53b5341bb98d7427b53edccbaed14bdd5250fbe5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 24 May 2021 14:33:07 +0900 Subject: [PATCH 419/839] Simplify application logic --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 52 +++++++--------------- 1 file changed, 17 insertions(+), 35 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 61559c06b9..cc7732372f 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -83,36 +83,24 @@ namespace osu.Game.Rulesets.Osu.Mods var distanceToPrev = Vector2.Distance(prevObjectInfo.EndPositionOriginal, currentObjectInfo.PositionOriginal); + if (hitObject is Spinner) + continue; + + applyRandomisation( + rateOfChangeMultiplier, + prevObjectInfo, + distanceToPrev, + ref currentObjectInfo + ); + + hitObject.Position = currentObjectInfo.PositionRandomised; + + // update end position as it may have changed as a result of the position update. + currentObjectInfo.EndPositionRandomised = currentObjectInfo.PositionRandomised; + switch (hitObject) { - case HitCircle circle: - applyRandomisation( - rateOfChangeMultiplier, - prevObjectInfo, - distanceToPrev, - ref currentObjectInfo - ); - - circle.Position = currentObjectInfo.PositionRandomised; - currentObjectInfo.EndPositionRandomised = currentObjectInfo.PositionRandomised; - - break; - case Slider slider: - currentObjectInfo.EndPositionOriginal = slider.EndPosition; - - currentObjectInfo.EndPositionOriginal = slider.TailCircle.Position; - - applyRandomisation( - rateOfChangeMultiplier, - prevObjectInfo, - distanceToPrev, - ref currentObjectInfo - ); - - slider.Position = currentObjectInfo.PositionRandomised; - currentObjectInfo.EndPositionRandomised = slider.TailCircle.Position; - moveSliderIntoPlayfield(ref slider, ref currentObjectInfo); var sliderShift = Vector2.Subtract(slider.Position, currentObjectInfo.PositionOriginal); @@ -239,15 +227,9 @@ namespace osu.Game.Rulesets.Osu.Mods var diff = destAngleRad - initialAngleRad; - while (diff < -Math.PI) - { - diff += 2 * Math.PI; - } + while (diff < -Math.PI) diff += 2 * Math.PI; - while (diff > Math.PI) - { - diff -= 2 * Math.PI; - } + while (diff > Math.PI) diff -= 2 * Math.PI; var finalAngleRad = initialAngleRad + relativeDistance * diff; From 83364285746b651bd8ccd27be11e0aaa127b493b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 24 May 2021 15:10:33 +0900 Subject: [PATCH 420/839] Add regression test for spinner sample actually transforming its frequency --- osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs | 14 +++++++++++++- .../Objects/Drawables/DrawableSpinner.cs | 6 +++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs index f697a77d94..0a7ef443b1 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs @@ -5,12 +5,14 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; +using osu.Framework.Testing; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Skinning; using osuTK; namespace osu.Game.Rulesets.Osu.Tests @@ -32,6 +34,16 @@ namespace osu.Game.Rulesets.Osu.Tests AddStep($"{term} Small", () => SetContents(() => testSingle(7, autoplay))); } + [Test] + public void TestSpinningSamplePitchShift() + { + AddStep("Add spinner", () => SetContents(() => testSingle(5, true, 4000))); + AddUntilStep("Pitch starts low", () => getSpinningSample().Frequency.Value < 0.8); + AddUntilStep("Pitch increases", () => getSpinningSample().Frequency.Value > 0.8); + + PausableSkinnableSound getSpinningSample() => drawableSpinner.ChildrenOfType().FirstOrDefault(s => s.Samples.Any(i => i.LookupNames.Any(l => l.Contains("spinnerspin")))); + } + [TestCase(false)] [TestCase(true)] public void TestLongSpinner(bool autoplay) @@ -93,7 +105,7 @@ namespace osu.Game.Rulesets.Osu.Tests { base.Update(); if (auto) - RotationTracker.AddRotation((float)(Clock.ElapsedFrameTime * 3)); + RotationTracker.AddRotation((float)(Clock.ElapsedFrameTime * 2)); } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index 3a4753761a..19cee61f26 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -39,6 +39,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private Bindable isSpinning; private bool spinnerFrequencyModulate; + private const float spinning_sample_initial_frequency = 1.0f; + private const float spinning_sample_modulated_base_frequency = 0.5f; + /// /// The amount of bonus score gained from spinning after the required number of spins, for display purposes. /// @@ -106,9 +109,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables isSpinning.BindValueChanged(updateSpinningSample); } - private const float spinning_sample_initial_frequency = 1.0f; - private const float spinning_sample_modulated_base_frequency = 0.5f; - protected override void OnFree() { base.OnFree(); From 100e2d14a5084c6373da786d8370767ec686ce8a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 24 May 2021 15:14:55 +0900 Subject: [PATCH 421/839] Move call inside conditional --- osu.Game/Screens/Play/Player.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index ee940fae40..a9f3edf049 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -519,10 +519,13 @@ namespace osu.Game.Screens.Play // there is a chance that the exit was performed after the transition to results has started. // we want to give the user what they want, so forcefully return to this screen (to proceed with the upwards exit process). - if (!this.IsCurrentScreen() && this.GetChildScreen() != null) + if (!this.IsCurrentScreen()) { ValidForResume = false; - this.MakeCurrent(); + + // in the potential case that this instance has already been exited, this is required to avoid a crash. + if (this.GetChildScreen() != null) + this.MakeCurrent(); return; } From e5f586f2a6940679a41b87ebe3f58d818226795e Mon Sep 17 00:00:00 2001 From: Firmatorenio Date: Mon, 24 May 2021 13:29:12 +0600 Subject: [PATCH 422/839] fix colour hit error meter not pushing misses when wrong colour note is hit in taiko --- .../Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs index 0eb2367f73..3a79183d34 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Scoring; using osuTK; using osuTK.Graphics; @@ -24,7 +25,8 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters InternalChild = judgementsFlow = new JudgementFlow(); } - protected override void OnNewJudgement(JudgementResult judgement) => judgementsFlow.Push(GetColourForHitResult(HitWindows.ResultFor(judgement.TimeOffset))); + //Taiko-specific: hitting a wrong colour note should result in a miss being pushed. + protected override void OnNewJudgement(JudgementResult judgement) => judgementsFlow.Push(judgement.Type == HitResult.Miss ? GetColourForHitResult(HitResult.Miss) : GetColourForHitResult(HitWindows.ResultFor(judgement.TimeOffset))); private class JudgementFlow : FillFlowContainer { From 4fc6ba50b7beaf51c9ca1c22a87cfa7334a65bda Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 24 May 2021 17:03:54 +0900 Subject: [PATCH 423/839] Fix editor placement ending early if a blueprint becomes alive from a pool Closes https://github.com/ppy/osu/issues/12630. --- .../Edit/Compose/Components/ComposeBlueprintContainer.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs index e231f7f648..3e97e15cca 100644 --- a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs @@ -61,6 +61,8 @@ namespace osu.Game.Screens.Edit.Compose.Components inputManager = GetContainingInputManager(); + Beatmap.HitObjectAdded += hitObjectAdded; + // updates to selected are handled for us by SelectionHandler. NewCombo.BindTo(SelectionHandler.SelectionNewComboState); @@ -259,10 +261,9 @@ namespace osu.Game.Screens.Edit.Compose.Components public virtual HitObjectSelectionBlueprint CreateHitObjectBlueprintFor(HitObject hitObject) => null; - protected override void OnBlueprintAdded(HitObject item) + private void hitObjectAdded(HitObject obj) { - base.OnBlueprintAdded(item); - + // refresh the tool to handle the case of placement completing. refreshTool(); // on successful placement, the new combo button should be reset as this is the most common user interaction. From f8c615049379316b8fce79ac55e34d4a745f140f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 24 May 2021 17:11:01 +0900 Subject: [PATCH 424/839] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 57550cfb93..b3842a528d 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 87d1707420..ed033b3934 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -33,7 +33,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index a2a9ac35fc..e35b1b5c42 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 3db995c7782c0f643239c97798c9bbc90aafd843 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 24 May 2021 17:15:57 +0900 Subject: [PATCH 425/839] Fix sliders jumping around the screen on movement --- .../Sliders/SliderSelectionBlueprint.cs | 7 ++++++- .../Objects/Drawables/DrawableSlider.cs | 17 +++++++++-------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index ec97f1fd78..e810d2fe0c 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs @@ -16,6 +16,7 @@ using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Compose; using osuTK; @@ -25,6 +26,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders { public class SliderSelectionBlueprint : OsuSelectionBlueprint { + protected new DrawableSlider DrawableObject => (DrawableSlider)base.DrawableObject; + protected SliderBodyPiece BodyPiece { get; private set; } protected SliderCircleOverlay HeadOverlay { get; private set; } protected SliderCircleOverlay TailOverlay { get; private set; } @@ -236,7 +239,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders new OsuMenuItem("Add control point", MenuItemType.Standard, () => addControlPoint(rightClickPosition)), }; - public override Vector2 ScreenSpaceSelectionPoint => BodyPiece.ToScreenSpace(BodyPiece.PathStartLocation); + // Always refer to the drawable object's slider body so subsequent movement deltas are calculated with updated positions. + public override Vector2 ScreenSpaceSelectionPoint => DrawableObject.SliderBody?.ToScreenSpace(DrawableObject.SliderBody.PathOffset) + ?? BodyPiece.ToScreenSpace(BodyPiece.PathStartLocation); public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => BodyPiece.ReceivePositionalInputAt(screenSpacePos) || ControlPointVisualiser?.Pieces.Any(p => p.ReceivePositionalInputAt(screenSpacePos)) == true; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 82b5492de6..0bec33bf77 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -34,7 +34,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public override bool DisplayResult => !HitObject.OnlyJudgeNestedObjects; - private PlaySliderBody sliderBody => Body.Drawable as PlaySliderBody; + [CanBeNull] + public PlaySliderBody SliderBody => Body.Drawable as PlaySliderBody; public IBindable PathVersion => pathVersion; private readonly Bindable pathVersion = new Bindable(); @@ -215,16 +216,16 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables double completionProgress = Math.Clamp((Time.Current - HitObject.StartTime) / HitObject.Duration, 0, 1); Ball.UpdateProgress(completionProgress); - sliderBody?.UpdateProgress(completionProgress); + SliderBody?.UpdateProgress(completionProgress); foreach (DrawableHitObject hitObject in NestedHitObjects) { - if (hitObject is ITrackSnaking s) s.UpdateSnakingPosition(HitObject.Path.PositionAt(sliderBody?.SnakedStart ?? 0), HitObject.Path.PositionAt(sliderBody?.SnakedEnd ?? 0)); + if (hitObject is ITrackSnaking s) s.UpdateSnakingPosition(HitObject.Path.PositionAt(SliderBody?.SnakedStart ?? 0), HitObject.Path.PositionAt(SliderBody?.SnakedEnd ?? 0)); if (hitObject is IRequireTracking t) t.Tracking = Ball.Tracking; } - Size = sliderBody?.Size ?? Vector2.Zero; - OriginPosition = sliderBody?.PathOffset ?? Vector2.Zero; + Size = SliderBody?.Size ?? Vector2.Zero; + OriginPosition = SliderBody?.PathOffset ?? Vector2.Zero; if (DrawSize != Vector2.Zero) { @@ -238,7 +239,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public override void OnKilled() { base.OnKilled(); - sliderBody?.RecyclePath(); + SliderBody?.RecyclePath(); } protected override void ApplySkin(ISkinSource skin, bool allowFallback) @@ -324,7 +325,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { case ArmedState.Hit: Ball.ScaleTo(HitObject.Scale * 1.4f, fade_out_time, Easing.Out); - if (sliderBody?.SnakingOut.Value == true) + if (SliderBody?.SnakingOut.Value == true) Body.FadeOut(40); // short fade to allow for any body colour to smoothly disappear. break; } @@ -332,7 +333,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables this.FadeOut(fade_out_time, Easing.OutQuint).Expire(); } - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => sliderBody?.ReceivePositionalInputAt(screenSpacePos) ?? base.ReceivePositionalInputAt(screenSpacePos); + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => SliderBody?.ReceivePositionalInputAt(screenSpacePos) ?? base.ReceivePositionalInputAt(screenSpacePos); private class DefaultSliderBody : PlaySliderBody { From 7961dba1d3dccd79e6bd79b60de5d21f8a396de6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 24 May 2021 17:22:55 +0900 Subject: [PATCH 426/839] Reorder `OrderBy` for legibility --- osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs b/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs index cd8b486f23..23b09e8fb1 100644 --- a/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs +++ b/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs @@ -73,10 +73,11 @@ namespace osu.Game.Input.Bindings else { KeyBindings = store.Query(ruleset?.ID, variant) + .OrderBy(b => defaults.FindIndex(d => (int)d.Action == b.IntAction)) // this ordering is important to ensure that we read entries from the database in the order // enforced by DefaultKeyBindings. allow for song select to handle actions that may otherwise // have been eaten by the music controller due to query order. - .OrderBy(b => defaults.FindIndex(d => (int)d.Action == b.IntAction)).ToList(); + .ToList(); } } } From 7792efb154f271e528f4ed0df0ddf966123d5208 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 24 May 2021 12:28:06 +0300 Subject: [PATCH 427/839] Remove no longer used `BeatmapSkin` --- osu.Game/Skinning/BeatmapSkin.cs | 32 -------------------------------- 1 file changed, 32 deletions(-) delete mode 100644 osu.Game/Skinning/BeatmapSkin.cs diff --git a/osu.Game/Skinning/BeatmapSkin.cs b/osu.Game/Skinning/BeatmapSkin.cs deleted file mode 100644 index 14b845faeb..0000000000 --- a/osu.Game/Skinning/BeatmapSkin.cs +++ /dev/null @@ -1,32 +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.Audio.Sample; -using osu.Framework.Bindables; -using osu.Framework.Graphics.OpenGL.Textures; -using osu.Framework.Graphics.Textures; -using osu.Game.Audio; -using osu.Game.Beatmaps; - -namespace osu.Game.Skinning -{ - /// - /// An empty implementation of a beatmap skin, serves as a temporary default for s. - /// - /// - /// This should be removed once becomes instantiable or a new skin type for osu!lazer beatmaps is defined. - /// - public class BeatmapSkin : Skin - { - public BeatmapSkin(BeatmapInfo beatmap) - : base(BeatmapSkinExtensions.CreateSkinInfo(beatmap), null) - { - } - - public override Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => null; - - public override IBindable GetConfig(TLookup lookup) => null; - - public override ISample GetSample(ISampleInfo sampleInfo) => null; - } -} From 3585e2900ed8a488d6bb6fb69fddfd6305c37e32 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 24 May 2021 12:28:30 +0300 Subject: [PATCH 428/839] Replace unnecessary empty skin implementation with null --- osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs index 2e4a9fa28f..bfce59c7de 100644 --- a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs +++ b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs @@ -3,13 +3,8 @@ using System.IO; using osu.Framework.Audio; -using osu.Framework.Audio.Sample; using osu.Framework.Audio.Track; -using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Textures; -using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Skinning; using osu.Game.Storyboards; @@ -42,23 +37,12 @@ namespace osu.Game.Tests.Beatmaps protected override Storyboard GetStoryboard() => storyboard ?? base.GetStoryboard(); - protected override ISkin GetSkin() => new EmptySkin(); + protected override ISkin GetSkin() => null; public override Stream GetStream(string storagePath) => null; protected override Texture GetBackground() => null; protected override Track GetBeatmapTrack() => null; - - private class EmptySkin : ISkin - { - public Drawable GetDrawableComponent(ISkinComponent component) => null; - - public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => null; - - public ISample GetSample(ISampleInfo sampleInfo) => null; - - public IBindable GetConfig(TLookup lookup) => null; - } } } From 57640810b5023025b3b4ac933d35495663de39bb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 24 May 2021 17:23:09 +0900 Subject: [PATCH 429/839] Ignore certain banned `InputKey`s for gameplay purposes --- osu.Game/Input/KeyBindingStore.cs | 22 +++++++++++++++++++++ osu.Game/Rulesets/UI/RulesetInputManager.cs | 8 ++++++++ 2 files changed, 30 insertions(+) diff --git a/osu.Game/Input/KeyBindingStore.cs b/osu.Game/Input/KeyBindingStore.cs index 9d0cfedc03..1d20f0d2c3 100644 --- a/osu.Game/Input/KeyBindingStore.cs +++ b/osu.Game/Input/KeyBindingStore.cs @@ -16,6 +16,17 @@ namespace osu.Game.Input { public event Action KeyBindingChanged; + /// + /// Keys which should not be allowed for gameplay input purposes. + /// + private static readonly IEnumerable banned_keys = new[] + { + InputKey.MouseWheelDown, + InputKey.MouseWheelLeft, + InputKey.MouseWheelUp, + InputKey.MouseWheelRight + }; + public KeyBindingStore(DatabaseContextFactory contextFactory, RulesetStore rulesets, Storage storage = null) : base(contextFactory, storage) { @@ -103,5 +114,16 @@ namespace osu.Game.Input KeyBindingChanged?.Invoke(); } + + public static bool CheckValidForGameplay(KeyCombination combination) + { + foreach (var key in banned_keys) + { + if (combination.Keys.Contains(key)) + return false; + } + + return true; + } } } diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index d6f002ea2c..75c3a4661c 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -13,6 +13,7 @@ using osu.Framework.Input.Events; using osu.Framework.Input.StateChanges.Events; using osu.Framework.Input.States; using osu.Game.Configuration; +using osu.Game.Input; using osu.Game.Input.Bindings; using osu.Game.Input.Handlers; using osu.Game.Screens.Play; @@ -169,6 +170,13 @@ namespace osu.Game.Rulesets.UI : base(ruleset, variant, unique) { } + + protected override void ReloadMappings() + { + base.ReloadMappings(); + + KeyBindings = KeyBindings.Where(b => KeyBindingStore.CheckValidForGameplay(b.KeyCombination)).ToList(); + } } } From deabce7140fea2922a1916b63f35ce98cbb18f7c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 24 May 2021 18:40:56 +0900 Subject: [PATCH 430/839] Disallow updating the database to an invalid value --- osu.Game/Input/KeyBindingStore.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Input/KeyBindingStore.cs b/osu.Game/Input/KeyBindingStore.cs index 1d20f0d2c3..23b0e0c0d0 100644 --- a/osu.Game/Input/KeyBindingStore.cs +++ b/osu.Game/Input/KeyBindingStore.cs @@ -104,6 +104,10 @@ namespace osu.Game.Input using (ContextFactory.GetForWrite()) { var dbKeyBinding = (DatabasedKeyBinding)keyBinding; + + if (dbKeyBinding.RulesetID != null && !CheckValidForGameplay(keyBinding.KeyCombination)) + return; + Refresh(ref dbKeyBinding); if (dbKeyBinding.KeyCombination.Equals(keyBinding.KeyCombination)) From a00f226ab3566028bb2f0590d84caaeeeb32cb55 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 24 May 2021 18:41:39 +0900 Subject: [PATCH 431/839] Add assert on storing to database --- osu.Game/Input/KeyBindingStore.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Input/KeyBindingStore.cs b/osu.Game/Input/KeyBindingStore.cs index 23b0e0c0d0..3ef9923487 100644 --- a/osu.Game/Input/KeyBindingStore.cs +++ b/osu.Game/Input/KeyBindingStore.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using osu.Framework.Input.Bindings; using osu.Framework.Platform; @@ -105,8 +106,7 @@ namespace osu.Game.Input { var dbKeyBinding = (DatabasedKeyBinding)keyBinding; - if (dbKeyBinding.RulesetID != null && !CheckValidForGameplay(keyBinding.KeyCombination)) - return; + Debug.Assert(dbKeyBinding.RulesetID == null || CheckValidForGameplay(keyBinding.KeyCombination)); Refresh(ref dbKeyBinding); From 471f17547aa12befa1fb5162be18c4a8d03afede Mon Sep 17 00:00:00 2001 From: Firmatorenio Date: Mon, 24 May 2021 16:49:58 +0600 Subject: [PATCH 432/839] switch determining the hit result by offset to getting it from the judgement directly --- .../Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs index 3a79183d34..e9ccbcdae2 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs @@ -7,7 +7,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Scoring; using osuTK; using osuTK.Graphics; @@ -25,8 +24,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters InternalChild = judgementsFlow = new JudgementFlow(); } - //Taiko-specific: hitting a wrong colour note should result in a miss being pushed. - protected override void OnNewJudgement(JudgementResult judgement) => judgementsFlow.Push(judgement.Type == HitResult.Miss ? GetColourForHitResult(HitResult.Miss) : GetColourForHitResult(HitWindows.ResultFor(judgement.TimeOffset))); + protected override void OnNewJudgement(JudgementResult judgement) => judgementsFlow.Push(GetColourForHitResult(judgement.Type)); private class JudgementFlow : FillFlowContainer { From ca1d1c58aba7888cf89bd42f8ced6539a1833d6a Mon Sep 17 00:00:00 2001 From: Swords Date: Mon, 24 May 2021 21:34:47 +1000 Subject: [PATCH 433/839] RestoreDefaultValueButton implements OsuButton --- osu.Game/Overlays/KeyBinding/KeyBindingRow.cs | 3 +- .../KeyBinding/RestorableKeyBindingRow.cs | 1 + .../Overlays/RestoreDefaultValueButton.cs | 44 +++++++------------ 3 files changed, 18 insertions(+), 30 deletions(-) diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs index 959ba36c6a..8cc03160a2 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs @@ -131,9 +131,8 @@ namespace osu.Game.Overlays.KeyBinding IsDefault.BindValueChanged(isDefault => { - if (isDefault.NewValue && !computeIsDefaultValue()) + if (isDefault.NewValue) { - RestoreDefaults(); finalise(); } }); diff --git a/osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs index b09c21378e..d07fffe6bc 100644 --- a/osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs @@ -65,6 +65,7 @@ namespace osu.Game.Overlays.KeyBinding }, }; + restoreDefaultButton.Action = () => { KeyBindingRow.RestoreDefaults(); }; restoreDefaultButton.Current = KeyBindingRow.IsDefault; } } diff --git a/osu.Game/Overlays/RestoreDefaultValueButton.cs b/osu.Game/Overlays/RestoreDefaultValueButton.cs index 51fb87da1d..d75657ea7a 100644 --- a/osu.Game/Overlays/RestoreDefaultValueButton.cs +++ b/osu.Game/Overlays/RestoreDefaultValueButton.cs @@ -6,17 +6,16 @@ using osu.Framework.Bindables; using osuTK.Graphics; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Effects; -using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; namespace osu.Game.Overlays { - public class RestoreDefaultValueButton : Container, IHasTooltip, IHasCurrentValue + public class RestoreDefaultValueButton : OsuButton, IHasTooltip, IHasCurrentValue { public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks; @@ -34,34 +33,30 @@ namespace osu.Game.Overlays public RestoreDefaultValueButton() { + Height = 1; + RelativeSizeAxes = Axes.Y; Width = SettingsPanel.CONTENT_MARGINS; - Padding = new MarginPadding { Vertical = 1.5f }; - Alpha = 0f; } [BackgroundDependencyLoader] private void load(OsuColour colour) { + BackgroundColour = colour.Yellow; buttonColour = colour.Yellow; - - Child = new Container + Content.Width = 0.33f; + Content.CornerRadius = 3; + Content.EdgeEffect = new EdgeEffectParameters { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - CornerRadius = 3, - Masking = true, - Colour = buttonColour, - EdgeEffect = new EdgeEffectParameters - { - Colour = buttonColour.Opacity(0.1f), - Type = EdgeEffectType.Glow, - Radius = 2, - }, - Width = 0.33f, - Child = new Box { RelativeSizeAxes = Axes.Both }, + Colour = buttonColour.Opacity(0.1f), + Type = EdgeEffectType.Glow, + Radius = 2, }; + + Padding = new MarginPadding { Vertical = 1.5f }; + Alpha = 0f; + + Action += () => { if (!current.Disabled) current.SetDefault(); }; } protected override void LoadComplete() @@ -77,13 +72,6 @@ namespace osu.Game.Overlays public string TooltipText => "revert to default"; - protected override bool OnClick(ClickEvent e) - { - if (!current.Disabled) - current.SetDefault(); - return true; - } - protected override bool OnHover(HoverEvent e) { hovering = true; From 441e4e7d56fe35088bb2faef2ed741bb3ad82814 Mon Sep 17 00:00:00 2001 From: Swords Date: Mon, 24 May 2021 22:08:34 +1000 Subject: [PATCH 434/839] Formatting --- osu.Game/Overlays/RestoreDefaultValueButton.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/RestoreDefaultValueButton.cs b/osu.Game/Overlays/RestoreDefaultValueButton.cs index d75657ea7a..0fe7b7322f 100644 --- a/osu.Game/Overlays/RestoreDefaultValueButton.cs +++ b/osu.Game/Overlays/RestoreDefaultValueButton.cs @@ -56,7 +56,10 @@ namespace osu.Game.Overlays Padding = new MarginPadding { Vertical = 1.5f }; Alpha = 0f; - Action += () => { if (!current.Disabled) current.SetDefault(); }; + Action += () => + { + if (!current.Disabled) current.SetDefault(); + }; } protected override void LoadComplete() From 02cdd0b2deff3a03ee3a020d6e5a46684b5daa17 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Mon, 24 May 2021 19:10:37 +0700 Subject: [PATCH 435/839] use drawable link compiler in markdown link --- .../Markdown/OsuMarkdownLinkText.cs | 52 ++++++++++++------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownLinkText.cs b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownLinkText.cs index 2efb60d125..f44f818bf0 100644 --- a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownLinkText.cs +++ b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownLinkText.cs @@ -1,48 +1,62 @@ // 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 Markdig.Syntax.Inlines; using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Framework.Graphics.Containers.Markdown; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Input.Events; +using osu.Game.Online.Chat; using osu.Game.Overlays; namespace osu.Game.Graphics.Containers.Markdown { public class OsuMarkdownLinkText : MarkdownLinkText { - [Resolved] - private OverlayColourProvider colourProvider { get; set; } + [Resolved(canBeNull: true)] + private OsuGame game { get; set; } - private SpriteText spriteText; + protected string Text; + protected string Title; public OsuMarkdownLinkText(string text, LinkInline linkInline) : base(text, linkInline) { + Text = text; + Title = linkInline.Title; } [BackgroundDependencyLoader] - private void load() + private void load(OverlayColourProvider colourProvider) { - spriteText.Colour = colourProvider.Light2; + var text = CreateSpriteText().With(t => t.Text = Text); + InternalChildren = new Drawable[] + { + text, + new OsuMarkdownLinkCompiler(new[] { text }) + { + RelativeSizeAxes = Axes.Both, + Action = OnLinkPressed, + TooltipText = Title ?? Url, + } + }; } - public override SpriteText CreateSpriteText() - { - return spriteText = base.CreateSpriteText(); - } + protected override void OnLinkPressed() => game?.HandleLink(Url); - protected override bool OnHover(HoverEvent e) + private class OsuMarkdownLinkCompiler : DrawableLinkCompiler { - spriteText.Colour = colourProvider.Light1; - return base.OnHover(e); - } + public OsuMarkdownLinkCompiler(IEnumerable parts) + : base(parts) + { + } - protected override void OnHoverLost(HoverLostEvent e) - { - spriteText.Colour = colourProvider.Light2; - base.OnHoverLost(e); + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + IdleColour = colourProvider.Light2; + HoverColour = colourProvider.Light1; + } } } } From fe9abd5b8048b029b5c103cf5e35d1ec76bb318f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 24 May 2021 21:37:05 +0900 Subject: [PATCH 436/839] Upgrade packages and fix ResX files --- .config/dotnet-tools.json | 4 ++-- osu.Game/Localisation/ButtonSystem.resx | 4 ++-- osu.Game/Localisation/Chat.resx | 4 ++-- osu.Game/Localisation/Common.resx | 4 ++-- osu.Game/Localisation/Notifications.resx | 4 ++-- osu.Game/Localisation/NowPlaying.resx | 4 ++-- osu.Game/Localisation/Settings.resx | 4 ++-- osu.Game/osu.Game.csproj | 2 +- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 54d9ff3077..b51ecb4f7e 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -33,10 +33,10 @@ ] }, "ppy.localisationanalyser.tools": { - "version": "2021.521.1", + "version": "2021.524.0", "commands": [ "localisation" ] } } -} +} \ No newline at end of file diff --git a/osu.Game/Localisation/ButtonSystem.resx b/osu.Game/Localisation/ButtonSystem.resx index e8f555b6c3..d72ffff8be 100644 --- a/osu.Game/Localisation/ButtonSystem.resx +++ b/osu.Game/Localisation/ButtonSystem.resx @@ -53,10 +53,10 @@ 2.0 - System.Resources.NetStandard.ResXResourceReader, System.Resources.NetStandard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.NetStandard.ResXResourceWriter, System.Resources.NetStandard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 solo diff --git a/osu.Game/Localisation/Chat.resx b/osu.Game/Localisation/Chat.resx index 3762f800c5..055e351463 100644 --- a/osu.Game/Localisation/Chat.resx +++ b/osu.Game/Localisation/Chat.resx @@ -53,10 +53,10 @@ 2.0 - System.Resources.NetStandard.ResXResourceReader, System.Resources.NetStandard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.NetStandard.ResXResourceWriter, System.Resources.NetStandard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 chat diff --git a/osu.Game/Localisation/Common.resx b/osu.Game/Localisation/Common.resx index 8a3ae29c77..59de16a037 100644 --- a/osu.Game/Localisation/Common.resx +++ b/osu.Game/Localisation/Common.resx @@ -53,10 +53,10 @@ 2.0 - System.Resources.NetStandard.ResXResourceReader, System.Resources.NetStandard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.NetStandard.ResXResourceWriter, System.Resources.NetStandard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Cancel diff --git a/osu.Game/Localisation/Notifications.resx b/osu.Game/Localisation/Notifications.resx index d61e5744e4..08db240ba2 100644 --- a/osu.Game/Localisation/Notifications.resx +++ b/osu.Game/Localisation/Notifications.resx @@ -53,10 +53,10 @@ 2.0 - System.Resources.NetStandard.ResXResourceReader, System.Resources.NetStandard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.NetStandard.ResXResourceWriter, System.Resources.NetStandard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 notifications diff --git a/osu.Game/Localisation/NowPlaying.resx b/osu.Game/Localisation/NowPlaying.resx index 71df15e488..40fda3e25b 100644 --- a/osu.Game/Localisation/NowPlaying.resx +++ b/osu.Game/Localisation/NowPlaying.resx @@ -53,10 +53,10 @@ 2.0 - System.Resources.NetStandard.ResXResourceReader, System.Resources.NetStandard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.NetStandard.ResXResourceWriter, System.Resources.NetStandard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 now playing diff --git a/osu.Game/Localisation/Settings.resx b/osu.Game/Localisation/Settings.resx index 1770147dd9..85c224cedf 100644 --- a/osu.Game/Localisation/Settings.resx +++ b/osu.Game/Localisation/Settings.resx @@ -53,10 +53,10 @@ 2.0 - System.Resources.NetStandard.ResXResourceReader, System.Resources.NetStandard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.NetStandard.ResXResourceWriter, System.Resources.NetStandard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 settings diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index ed033b3934..fa2945db6a 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -29,7 +29,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive From 518999ffab72b414a5d82bb0ba908d505d7f5187 Mon Sep 17 00:00:00 2001 From: Swords Date: Mon, 24 May 2021 22:49:40 +1000 Subject: [PATCH 437/839] Renaming files --- .../Settings/TestSceneKeyBindingPanel.cs | 42 +- .../Overlays/KeyBinding/BasicKeyBindingRow.cs | 463 +++++++++++++++++ osu.Game/Overlays/KeyBinding/KeyBindingRow.cs | 465 ++---------------- .../KeyBinding/KeyBindingsSubsection.cs | 4 +- .../KeyBinding/RestorableKeyBindingRow.cs | 72 --- 5 files changed, 514 insertions(+), 532 deletions(-) create mode 100644 osu.Game/Overlays/KeyBinding/BasicKeyBindingRow.cs delete mode 100644 osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs diff --git a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs index 3edba2ddd7..face2a498d 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs @@ -31,11 +31,11 @@ namespace osu.Game.Tests.Visual.Settings [Test] public void TestClickTwiceOnClearButton() { - KeyBindingRow firstRow = null; + BasicKeyBindingRow firstRow = null; AddStep("click first row", () => { - firstRow = panel.ChildrenOfType().First(); + firstRow = panel.ChildrenOfType().First(); InputManager.MoveMouseTo(firstRow); InputManager.Click(MouseButton.Left); @@ -43,7 +43,7 @@ namespace osu.Game.Tests.Visual.Settings AddStep("schedule button clicks", () => { - var clearButton = firstRow.ChildrenOfType().Single(); + var clearButton = firstRow.ChildrenOfType().Single(); InputManager.MoveMouseTo(clearButton); @@ -68,22 +68,22 @@ namespace osu.Game.Tests.Visual.Settings [Test] public void TestClearButtonOnBindings() { - KeyBindingRow multiBindingRow = null; + BasicKeyBindingRow multiBindingRow = null; AddStep("click first row with two bindings", () => { - multiBindingRow = panel.ChildrenOfType().First(row => row.Defaults.Count() > 1); + multiBindingRow = panel.ChildrenOfType().First(row => row.Defaults.Count() > 1); InputManager.MoveMouseTo(multiBindingRow); InputManager.Click(MouseButton.Left); }); clickClearButton(); - AddAssert("first binding cleared", () => string.IsNullOrEmpty(multiBindingRow.ChildrenOfType().First().Text.Text.ToString())); + AddAssert("first binding cleared", () => string.IsNullOrEmpty(multiBindingRow.ChildrenOfType().First().Text.Text.ToString())); AddStep("click second binding", () => { - var target = multiBindingRow.ChildrenOfType().ElementAt(1); + var target = multiBindingRow.ChildrenOfType().ElementAt(1); InputManager.MoveMouseTo(target); InputManager.Click(MouseButton.Left); @@ -91,13 +91,13 @@ namespace osu.Game.Tests.Visual.Settings clickClearButton(); - AddAssert("second binding cleared", () => string.IsNullOrEmpty(multiBindingRow.ChildrenOfType().ElementAt(1).Text.Text.ToString())); + AddAssert("second binding cleared", () => string.IsNullOrEmpty(multiBindingRow.ChildrenOfType().ElementAt(1).Text.Text.ToString())); void clickClearButton() { AddStep("click clear button", () => { - var clearButton = multiBindingRow.ChildrenOfType().Single(); + var clearButton = multiBindingRow.ChildrenOfType().Single(); InputManager.MoveMouseTo(clearButton); InputManager.Click(MouseButton.Left); @@ -108,11 +108,11 @@ namespace osu.Game.Tests.Visual.Settings [Test] public void TestSingleBindingResetButton() { - RestorableKeyBindingRow settingsKeyBindingRow = null; + KeyBindingRow settingsKeyBindingRow = null; AddStep("click first row", () => { - settingsKeyBindingRow = panel.ChildrenOfType().First(); + settingsKeyBindingRow = panel.ChildrenOfType().First(); InputManager.MoveMouseTo(settingsKeyBindingRow); InputManager.Click(MouseButton.Left); @@ -131,17 +131,17 @@ namespace osu.Game.Tests.Visual.Settings AddUntilStep("restore button hidden", () => settingsKeyBindingRow.ChildrenOfType>().First().Alpha == 0); - AddAssert("binding cleared", () => settingsKeyBindingRow.ChildrenOfType().ElementAt(0).KeyBinding.KeyCombination.Equals(settingsKeyBindingRow.KeyBindingRow.Defaults.ElementAt(0))); + AddAssert("binding cleared", () => settingsKeyBindingRow.ChildrenOfType().ElementAt(0).KeyBinding.KeyCombination.Equals(settingsKeyBindingRow.BasicKeyBindingRow.Defaults.ElementAt(0))); } [Test] public void TestResetAllBindingsButton() { - RestorableKeyBindingRow settingsKeyBindingRow = null; + KeyBindingRow settingsKeyBindingRow = null; AddStep("click first row", () => { - settingsKeyBindingRow = panel.ChildrenOfType().First(); + settingsKeyBindingRow = panel.ChildrenOfType().First(); InputManager.MoveMouseTo(settingsKeyBindingRow); InputManager.Click(MouseButton.Left); @@ -160,26 +160,26 @@ namespace osu.Game.Tests.Visual.Settings AddUntilStep("restore button hidden", () => settingsKeyBindingRow.ChildrenOfType>().First().Alpha == 0); - AddAssert("binding cleared", () => settingsKeyBindingRow.ChildrenOfType().ElementAt(0).KeyBinding.KeyCombination.Equals(settingsKeyBindingRow.KeyBindingRow.Defaults.ElementAt(0))); + AddAssert("binding cleared", () => settingsKeyBindingRow.ChildrenOfType().ElementAt(0).KeyBinding.KeyCombination.Equals(settingsKeyBindingRow.BasicKeyBindingRow.Defaults.ElementAt(0))); } [Test] public void TestClickRowSelectsFirstBinding() { - KeyBindingRow multiBindingRow = null; + BasicKeyBindingRow multiBindingRow = null; AddStep("click first row with two bindings", () => { - multiBindingRow = panel.ChildrenOfType().First(row => row.Defaults.Count() > 1); + multiBindingRow = panel.ChildrenOfType().First(row => row.Defaults.Count() > 1); InputManager.MoveMouseTo(multiBindingRow); InputManager.Click(MouseButton.Left); }); - AddAssert("first binding selected", () => multiBindingRow.ChildrenOfType().First().IsBinding); + AddAssert("first binding selected", () => multiBindingRow.ChildrenOfType().First().IsBinding); AddStep("click second binding", () => { - var target = multiBindingRow.ChildrenOfType().ElementAt(1); + var target = multiBindingRow.ChildrenOfType().ElementAt(1); InputManager.MoveMouseTo(target); InputManager.Click(MouseButton.Left); @@ -187,12 +187,12 @@ namespace osu.Game.Tests.Visual.Settings AddStep("click back binding row", () => { - multiBindingRow = panel.ChildrenOfType().ElementAt(10); + multiBindingRow = panel.ChildrenOfType().ElementAt(10); InputManager.MoveMouseTo(multiBindingRow); InputManager.Click(MouseButton.Left); }); - AddAssert("first binding selected", () => multiBindingRow.ChildrenOfType().First().IsBinding); + AddAssert("first binding selected", () => multiBindingRow.ChildrenOfType().First().IsBinding); } } } \ No newline at end of file diff --git a/osu.Game/Overlays/KeyBinding/BasicKeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/BasicKeyBindingRow.cs new file mode 100644 index 0000000000..acdf273622 --- /dev/null +++ b/osu.Game/Overlays/KeyBinding/BasicKeyBindingRow.cs @@ -0,0 +1,463 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Extensions; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Input; +using osuTK; +using osuTK.Graphics; +using osuTK.Input; + +namespace osu.Game.Overlays.KeyBinding +{ + public class BasicKeyBindingRow : Container + { + private readonly object action; + private readonly IEnumerable bindings; + + private const float transition_time = 150; + + private const float height = 20; + + private const float padding = 5; + + private FillFlowContainer cancelAndClearButtons; + private FillFlowContainer buttons; + + public Bindable IsDefault { get; } = new BindableBool(true) + { + Default = true + }; + + public BasicKeyBindingRow(object action, IEnumerable bindings) + { + this.action = action; + this.bindings = bindings; + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + Masking = true; + CornerRadius = padding; + } + + [Resolved] + private KeyBindingStore store { get; set; } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + updateIsDefaultValue(); + + EdgeEffect = new EdgeEffectParameters + { + Radius = 2, + Colour = colours.YellowDark.Opacity(0), + Type = EdgeEffectType.Shadow, + Hollow = true, + }; + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + Alpha = 0.6f, + }, + new OsuSpriteText + { + Text = action.GetDescription(), + Margin = new MarginPadding(padding), + }, + buttons = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight + }, + cancelAndClearButtons = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Padding = new MarginPadding(padding) { Top = height + padding * 2 }, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Alpha = 0, + Spacing = new Vector2(5), + Children = new Drawable[] + { + new CancelButton { Action = finalise }, + new ClearButton { Action = clear }, + }, + } + }; + foreach (var b in bindings) + buttons.Add(new KeyButton(b)); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + IsDefault.BindValueChanged(isDefault => + { + if (isDefault.NewValue) + { + finalise(); + } + }); + } + + public void RestoreDefaults() + { + int i = 0; + + foreach (var d in Defaults) + { + var button = buttons[i++]; + button.UpdateKeyCombination(d); + store.Update(button.KeyBinding); + } + + updateIsDefaultValue(); + } + + protected override bool OnHover(HoverEvent e) + { + FadeEdgeEffectTo(1, transition_time, Easing.OutQuint); + + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + FadeEdgeEffectTo(0, transition_time, Easing.OutQuint); + + base.OnHoverLost(e); + } + + public override bool AcceptsFocus => bindTarget == null; + + private KeyButton bindTarget; + + public bool AllowMainMouseButtons; + + public IEnumerable Defaults; + + private bool isModifier(Key k) => k < Key.F1; + + protected override bool OnClick(ClickEvent e) => true; + + protected override bool OnMouseDown(MouseDownEvent e) + { + if (!HasFocus || !bindTarget.IsHovered) + return base.OnMouseDown(e); + + if (!AllowMainMouseButtons) + { + switch (e.Button) + { + case MouseButton.Left: + case MouseButton.Right: + return true; + } + } + + bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(e.CurrentState)); + return true; + } + + protected override void OnMouseUp(MouseUpEvent e) + { + // don't do anything until the last button is released. + if (!HasFocus || e.HasAnyButtonPressed) + { + base.OnMouseUp(e); + return; + } + + if (bindTarget.IsHovered) + finalise(); + // prevent updating bind target before clear button's action + else if (!cancelAndClearButtons.Any(b => b.IsHovered)) + updateBindTarget(); + } + + protected override bool OnScroll(ScrollEvent e) + { + if (HasFocus) + { + if (bindTarget.IsHovered) + { + bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(e.CurrentState, e.ScrollDelta)); + finalise(); + return true; + } + } + + return base.OnScroll(e); + } + + protected override bool OnKeyDown(KeyDownEvent e) + { + if (!HasFocus) + return false; + + bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(e.CurrentState)); + if (!isModifier(e.Key)) finalise(); + + return true; + } + + protected override void OnKeyUp(KeyUpEvent e) + { + if (!HasFocus) + { + base.OnKeyUp(e); + return; + } + + finalise(); + } + + protected override bool OnJoystickPress(JoystickPressEvent e) + { + if (!HasFocus) + return false; + + bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(e.CurrentState)); + finalise(); + + return true; + } + + protected override void OnJoystickRelease(JoystickReleaseEvent e) + { + if (!HasFocus) + { + base.OnJoystickRelease(e); + return; + } + + finalise(); + } + + protected override bool OnMidiDown(MidiDownEvent e) + { + if (!HasFocus) + return false; + + bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(e.CurrentState)); + finalise(); + + return true; + } + + protected override void OnMidiUp(MidiUpEvent e) + { + if (!HasFocus) + { + base.OnMidiUp(e); + return; + } + + finalise(); + } + + private void clear() + { + if (bindTarget == null) + return; + + bindTarget.UpdateKeyCombination(InputKey.None); + finalise(); + } + + private void finalise() + { + if (bindTarget != null) + { + store.Update(bindTarget.KeyBinding); + + updateIsDefaultValue(); + + bindTarget.IsBinding = false; + Schedule(() => + { + // schedule to ensure we don't instantly get focus back on next OnMouseClick (see AcceptFocus impl.) + bindTarget = null; + }); + } + + if (HasFocus) + GetContainingInputManager().ChangeFocus(null); + + cancelAndClearButtons.FadeOut(300, Easing.OutQuint); + cancelAndClearButtons.BypassAutoSizeAxes |= Axes.Y; + } + + private void updateIsDefaultValue() => IsDefault.Value = computeIsDefaultValue(); + + private bool computeIsDefaultValue() => bindings.Select(b => b.KeyCombination).SequenceEqual(Defaults); + + protected override void OnFocus(FocusEvent e) + { + AutoSizeDuration = 500; + AutoSizeEasing = Easing.OutQuint; + + cancelAndClearButtons.FadeIn(300, Easing.OutQuint); + cancelAndClearButtons.BypassAutoSizeAxes &= ~Axes.Y; + + updateBindTarget(); + base.OnFocus(e); + } + + protected override void OnFocusLost(FocusLostEvent e) + { + finalise(); + base.OnFocusLost(e); + } + + /// + /// Updates the bind target to the currently hovered key button or the first if clicked anywhere else. + /// + private void updateBindTarget() + { + if (bindTarget != null) bindTarget.IsBinding = false; + bindTarget = buttons.FirstOrDefault(b => b.IsHovered) ?? buttons.FirstOrDefault(); + if (bindTarget != null) bindTarget.IsBinding = true; + } + + private class CancelButton : TriangleButton + { + public CancelButton() + { + Text = "Cancel"; + Size = new Vector2(80, 20); + } + } + + public class ClearButton : DangerousTriangleButton + { + public ClearButton() + { + Text = "Clear"; + Size = new Vector2(80, 20); + } + } + + public class KeyButton : Container + { + public readonly Framework.Input.Bindings.KeyBinding KeyBinding; + + private readonly Box box; + public readonly OsuSpriteText Text; + + private Color4 hoverColour; + + private bool isBinding; + + public bool IsBinding + { + get => isBinding; + set + { + if (value == isBinding) return; + + isBinding = value; + + updateHoverState(); + } + } + + public KeyButton(Framework.Input.Bindings.KeyBinding keyBinding) + { + KeyBinding = keyBinding; + + Margin = new MarginPadding(padding); + + // todo: use this in a meaningful way + // var isDefault = keyBinding.Action is Enum; + + Masking = true; + CornerRadius = padding; + + Height = height; + AutoSizeAxes = Axes.X; + + Children = new Drawable[] + { + new Container + { + AlwaysPresent = true, + Width = 80, + Height = height, + }, + box = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black + }, + Text = new OsuSpriteText + { + Font = OsuFont.Numeric.With(size: 10), + Margin = new MarginPadding(5), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = keyBinding.KeyCombination.ReadableString(), + }, + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + hoverColour = colours.YellowDark; + } + + protected override bool OnHover(HoverEvent e) + { + updateHoverState(); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + updateHoverState(); + base.OnHoverLost(e); + } + + private void updateHoverState() + { + if (isBinding) + { + box.FadeColour(Color4.White, transition_time, Easing.OutQuint); + Text.FadeColour(Color4.Black, transition_time, Easing.OutQuint); + } + else + { + box.FadeColour(IsHovered ? hoverColour : Color4.Black, transition_time, Easing.OutQuint); + Text.FadeColour(IsHovered ? Color4.Black : Color4.White, transition_time, Easing.OutQuint); + } + } + + public void UpdateKeyCombination(KeyCombination newCombination) + { + KeyBinding.KeyCombination = newCombination; + Text.Text = KeyBinding.KeyCombination.ReadableString(); + } + } + } +} diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs index 8cc03160a2..3221b66bce 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs @@ -1,38 +1,20 @@ -// 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 System.Collections.Generic; using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Extensions; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Effects; -using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Bindings; -using osu.Framework.Input.Events; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; -using osu.Game.Input; -using osuTK; -using osuTK.Graphics; -using osuTK.Input; +using osu.Game.Rulesets; namespace osu.Game.Overlays.KeyBinding { public class KeyBindingRow : Container, IFilterable { - private readonly object action; - private readonly IEnumerable bindings; - - private const float transition_time = 150; - - private const float height = 20; - - private const float padding = 5; + private readonly object key; + private readonly ICollection bindings; + public readonly BasicKeyBindingRow BasicKeyBindingRow; private bool matchingFilter; @@ -48,434 +30,43 @@ namespace osu.Game.Overlays.KeyBinding public bool FilteringActive { get; set; } - private OsuSpriteText text; - private FillFlowContainer cancelAndClearButtons; - private FillFlowContainer buttons; + public IEnumerable FilterTerms => bindings.Select(b => b.KeyCombination.ReadableString()).Prepend(key.ToString()); - public Bindable IsDefault { get; } = new BindableBool(true) + public KeyBindingRow( + object key, + ICollection bindings, + RulesetInfo ruleset, + IEnumerable defaults) { - Default = true - }; - - public IEnumerable FilterTerms => bindings.Select(b => b.KeyCombination.ReadableString()).Prepend(text.Text.ToString()); - - public KeyBindingRow(object action, IEnumerable bindings) - { - this.action = action; + this.key = key; this.bindings = bindings; + + RestoreDefaultValueButton restoreDefaultButton; + RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; + Padding = new MarginPadding { Right = SettingsPanel.CONTENT_MARGINS }; - Masking = true; - CornerRadius = padding; - } - - [Resolved] - private KeyBindingStore store { get; set; } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - updateIsDefaultValue(); - - EdgeEffect = new EdgeEffectParameters + BasicKeyBindingRow = new BasicKeyBindingRow(key, bindings.Where(b => ((int)b.Action).Equals((int)key))) { - Radius = 2, - Colour = colours.YellowDark.Opacity(0), - Type = EdgeEffectType.Shadow, - Hollow = true, + AllowMainMouseButtons = ruleset != null, + Defaults = defaults }; - Children = new Drawable[] + InternalChildren = new Drawable[] { - new Box + restoreDefaultButton = new RestoreDefaultValueButton(), + new FillFlowContainer { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - Alpha = 0.6f, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Left = SettingsPanel.CONTENT_MARGINS }, + Child = BasicKeyBindingRow }, - text = new OsuSpriteText - { - Text = action.GetDescription(), - Margin = new MarginPadding(padding), - }, - buttons = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight - }, - cancelAndClearButtons = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Padding = new MarginPadding(padding) { Top = height + padding * 2 }, - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Alpha = 0, - Spacing = new Vector2(5), - Children = new Drawable[] - { - new CancelButton { Action = finalise }, - new ClearButton { Action = clear }, - }, - } }; - foreach (var b in bindings) - buttons.Add(new KeyButton(b)); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - IsDefault.BindValueChanged(isDefault => - { - if (isDefault.NewValue) - { - finalise(); - } - }); - } - - public void RestoreDefaults() - { - int i = 0; - - foreach (var d in Defaults) - { - var button = buttons[i++]; - button.UpdateKeyCombination(d); - store.Update(button.KeyBinding); - } - - updateIsDefaultValue(); - } - - protected override bool OnHover(HoverEvent e) - { - FadeEdgeEffectTo(1, transition_time, Easing.OutQuint); - - return base.OnHover(e); - } - - protected override void OnHoverLost(HoverLostEvent e) - { - FadeEdgeEffectTo(0, transition_time, Easing.OutQuint); - - base.OnHoverLost(e); - } - - public override bool AcceptsFocus => bindTarget == null; - - private KeyButton bindTarget; - - public bool AllowMainMouseButtons; - - public IEnumerable Defaults; - - private bool isModifier(Key k) => k < Key.F1; - - protected override bool OnClick(ClickEvent e) => true; - - protected override bool OnMouseDown(MouseDownEvent e) - { - if (!HasFocus || !bindTarget.IsHovered) - return base.OnMouseDown(e); - - if (!AllowMainMouseButtons) - { - switch (e.Button) - { - case MouseButton.Left: - case MouseButton.Right: - return true; - } - } - - bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(e.CurrentState)); - return true; - } - - protected override void OnMouseUp(MouseUpEvent e) - { - // don't do anything until the last button is released. - if (!HasFocus || e.HasAnyButtonPressed) - { - base.OnMouseUp(e); - return; - } - - if (bindTarget.IsHovered) - finalise(); - // prevent updating bind target before clear button's action - else if (!cancelAndClearButtons.Any(b => b.IsHovered)) - updateBindTarget(); - } - - protected override bool OnScroll(ScrollEvent e) - { - if (HasFocus) - { - if (bindTarget.IsHovered) - { - bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(e.CurrentState, e.ScrollDelta)); - finalise(); - return true; - } - } - - return base.OnScroll(e); - } - - protected override bool OnKeyDown(KeyDownEvent e) - { - if (!HasFocus) - return false; - - bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(e.CurrentState)); - if (!isModifier(e.Key)) finalise(); - - return true; - } - - protected override void OnKeyUp(KeyUpEvent e) - { - if (!HasFocus) - { - base.OnKeyUp(e); - return; - } - - finalise(); - } - - protected override bool OnJoystickPress(JoystickPressEvent e) - { - if (!HasFocus) - return false; - - bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(e.CurrentState)); - finalise(); - - return true; - } - - protected override void OnJoystickRelease(JoystickReleaseEvent e) - { - if (!HasFocus) - { - base.OnJoystickRelease(e); - return; - } - - finalise(); - } - - protected override bool OnMidiDown(MidiDownEvent e) - { - if (!HasFocus) - return false; - - bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(e.CurrentState)); - finalise(); - - return true; - } - - protected override void OnMidiUp(MidiUpEvent e) - { - if (!HasFocus) - { - base.OnMidiUp(e); - return; - } - - finalise(); - } - - private void clear() - { - if (bindTarget == null) - return; - - bindTarget.UpdateKeyCombination(InputKey.None); - finalise(); - } - - private void finalise() - { - if (bindTarget != null) - { - store.Update(bindTarget.KeyBinding); - - updateIsDefaultValue(); - - bindTarget.IsBinding = false; - Schedule(() => - { - // schedule to ensure we don't instantly get focus back on next OnMouseClick (see AcceptFocus impl.) - bindTarget = null; - }); - } - - if (HasFocus) - GetContainingInputManager().ChangeFocus(null); - - cancelAndClearButtons.FadeOut(300, Easing.OutQuint); - cancelAndClearButtons.BypassAutoSizeAxes |= Axes.Y; - } - - private void updateIsDefaultValue() => IsDefault.Value = computeIsDefaultValue(); - - private bool computeIsDefaultValue() => bindings.Select(b => b.KeyCombination).SequenceEqual(Defaults); - - protected override void OnFocus(FocusEvent e) - { - AutoSizeDuration = 500; - AutoSizeEasing = Easing.OutQuint; - - cancelAndClearButtons.FadeIn(300, Easing.OutQuint); - cancelAndClearButtons.BypassAutoSizeAxes &= ~Axes.Y; - - updateBindTarget(); - base.OnFocus(e); - } - - protected override void OnFocusLost(FocusLostEvent e) - { - finalise(); - base.OnFocusLost(e); - } - - /// - /// Updates the bind target to the currently hovered key button or the first if clicked anywhere else. - /// - private void updateBindTarget() - { - if (bindTarget != null) bindTarget.IsBinding = false; - bindTarget = buttons.FirstOrDefault(b => b.IsHovered) ?? buttons.FirstOrDefault(); - if (bindTarget != null) bindTarget.IsBinding = true; - } - - private class CancelButton : TriangleButton - { - public CancelButton() - { - Text = "Cancel"; - Size = new Vector2(80, 20); - } - } - - public class ClearButton : DangerousTriangleButton - { - public ClearButton() - { - Text = "Clear"; - Size = new Vector2(80, 20); - } - } - - public class KeyButton : Container - { - public readonly Framework.Input.Bindings.KeyBinding KeyBinding; - - private readonly Box box; - public readonly OsuSpriteText Text; - - private Color4 hoverColour; - - private bool isBinding; - - public bool IsBinding - { - get => isBinding; - set - { - if (value == isBinding) return; - - isBinding = value; - - updateHoverState(); - } - } - - public KeyButton(Framework.Input.Bindings.KeyBinding keyBinding) - { - KeyBinding = keyBinding; - - Margin = new MarginPadding(padding); - - // todo: use this in a meaningful way - // var isDefault = keyBinding.Action is Enum; - - Masking = true; - CornerRadius = padding; - - Height = height; - AutoSizeAxes = Axes.X; - - Children = new Drawable[] - { - new Container - { - AlwaysPresent = true, - Width = 80, - Height = height, - }, - box = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black - }, - Text = new OsuSpriteText - { - Font = OsuFont.Numeric.With(size: 10), - Margin = new MarginPadding(5), - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Text = keyBinding.KeyCombination.ReadableString(), - }, - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - hoverColour = colours.YellowDark; - } - - protected override bool OnHover(HoverEvent e) - { - updateHoverState(); - return base.OnHover(e); - } - - protected override void OnHoverLost(HoverLostEvent e) - { - updateHoverState(); - base.OnHoverLost(e); - } - - private void updateHoverState() - { - if (isBinding) - { - box.FadeColour(Color4.White, transition_time, Easing.OutQuint); - Text.FadeColour(Color4.Black, transition_time, Easing.OutQuint); - } - else - { - box.FadeColour(IsHovered ? hoverColour : Color4.Black, transition_time, Easing.OutQuint); - Text.FadeColour(IsHovered ? Color4.Black : Color4.White, transition_time, Easing.OutQuint); - } - } - - public void UpdateKeyCombination(KeyCombination newCombination) - { - KeyBinding.KeyCombination = newCombination; - Text.Text = KeyBinding.KeyCombination.ReadableString(); - } + restoreDefaultButton.Action = () => { BasicKeyBindingRow.RestoreDefaults(); }; + restoreDefaultButton.Current = BasicKeyBindingRow.IsDefault; } } } diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs b/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs index 737c640b5a..b1a5895449 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs @@ -38,12 +38,12 @@ namespace osu.Game.Overlays.KeyBinding foreach (var defaultGroup in Defaults.GroupBy(d => d.Action)) { // one row per valid action. - Add(new RestorableKeyBindingRow(defaultGroup.Key, bindings, Ruleset, defaultGroup.Select(d => d.KeyCombination))); + Add(new KeyBindingRow(defaultGroup.Key, bindings, Ruleset, defaultGroup.Select(d => d.KeyCombination))); } Add(new ResetButton { - Action = () => Children.OfType().ForEach(k => k.KeyBindingRow.RestoreDefaults()) + Action = () => Children.OfType().ForEach(k => k.BasicKeyBindingRow.RestoreDefaults()) }); } } diff --git a/osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs deleted file mode 100644 index d07fffe6bc..0000000000 --- a/osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Input.Bindings; -using osu.Game.Rulesets; - -namespace osu.Game.Overlays.KeyBinding -{ - public class RestorableKeyBindingRow : Container, IFilterable - { - private readonly object key; - private readonly ICollection bindings; - public readonly KeyBindingRow KeyBindingRow; - - private bool matchingFilter; - - public bool MatchingFilter - { - get => matchingFilter; - set - { - matchingFilter = value; - this.FadeTo(!matchingFilter ? 0 : 1); - } - } - - public bool FilteringActive { get; set; } - - public IEnumerable FilterTerms => bindings.Select(b => b.KeyCombination.ReadableString()).Prepend(key.ToString()); - - public RestorableKeyBindingRow( - object key, - ICollection bindings, - RulesetInfo ruleset, - IEnumerable defaults) - { - this.key = key; - this.bindings = bindings; - - RestoreDefaultValueButton restoreDefaultButton; - - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - Padding = new MarginPadding { Right = SettingsPanel.CONTENT_MARGINS }; - - KeyBindingRow = new KeyBindingRow(key, bindings.Where(b => ((int)b.Action).Equals((int)key))) - { - AllowMainMouseButtons = ruleset != null, - Defaults = defaults - }; - - InternalChildren = new Drawable[] - { - restoreDefaultButton = new RestoreDefaultValueButton(), - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Left = SettingsPanel.CONTENT_MARGINS }, - Child = KeyBindingRow - }, - }; - - restoreDefaultButton.Action = () => { KeyBindingRow.RestoreDefaults(); }; - restoreDefaultButton.Current = KeyBindingRow.IsDefault; - } - } -} From 6f155fbd08500f58ce3629a0b958b8d549546114 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 24 May 2021 21:54:55 +0900 Subject: [PATCH 438/839] Make inspection a hint --- osu.sln.DotSettings | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index 4ac796ccd0..62751cebb1 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -120,6 +120,7 @@ WARNING WARNING HINT + HINT WARNING HINT HINT From 62b6cadb64eefd23b8b1ffaaf37987bfbf4fd5cf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 24 May 2021 18:41:51 +0900 Subject: [PATCH 439/839] Ensure settings rows cannot set an invalid value in the first place --- osu.Game/Overlays/KeyBinding/KeyBindingRow.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs index 43942d2d52..9c09b6e7d0 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs @@ -16,6 +16,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Input; +using osu.Game.Input.Bindings; using osuTK; using osuTK.Graphics; using osuTK.Input; @@ -445,6 +446,9 @@ namespace osu.Game.Overlays.KeyBinding public void UpdateKeyCombination(KeyCombination newCombination) { + if ((KeyBinding as DatabasedKeyBinding)?.RulesetID != null && !KeyBindingStore.CheckValidForGameplay(newCombination)) + return; + KeyBinding.KeyCombination = newCombination; Text.Text = KeyBinding.KeyCombination.ReadableString(); } From 37f6ceef798eb47b5778884bc1d41187ee293e76 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 24 May 2021 21:56:47 +0900 Subject: [PATCH 440/839] Add test coverage --- .../Settings/TestSceneKeyBindingPanel.cs | 66 +++++++++++++++++++ osu.Game/Overlays/SettingsPanel.cs | 2 +- 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs index f495e0fb23..55c5b5b9c2 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs @@ -6,6 +6,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Testing; using osu.Framework.Threading; +using osu.Game.Graphics.Sprites; using osu.Game.Overlays; using osu.Game.Overlays.KeyBinding; using osuTK.Input; @@ -28,6 +29,39 @@ namespace osu.Game.Tests.Visual.Settings panel.Show(); } + [SetUpSteps] + public void SetUpSteps() + { + AddStep("Scroll to top", () => panel.ChildrenOfType().First().ScrollToTop()); + AddWaitStep("wait for scroll", 5); + } + + [Test] + public void TestBindingMouseWheelToNonGameplay() + { + scrollToAndStartBinding("Increase volume"); + AddStep("press k", () => InputManager.Key(Key.K)); + checkBinding("Increase volume", "K"); + + AddStep("click again", () => InputManager.Click(MouseButton.Left)); + AddStep("scroll mouse wheel", () => InputManager.ScrollVerticalBy(1)); + + checkBinding("Increase volume", "Wheel Up"); + } + + [Test] + public void TestBindingMouseWheelToGameplay() + { + scrollToAndStartBinding("Left button"); + AddStep("press k", () => InputManager.Key(Key.Z)); + checkBinding("Left button", "Z"); + + AddStep("click again", () => InputManager.Click(MouseButton.Left)); + AddStep("scroll mouse wheel", () => InputManager.ScrollVerticalBy(1)); + + checkBinding("Left button", "Z"); + } + [Test] public void TestClickTwiceOnClearButton() { @@ -135,5 +169,37 @@ namespace osu.Game.Tests.Visual.Settings AddAssert("first binding selected", () => multiBindingRow.ChildrenOfType().First().IsBinding); } + + private void checkBinding(string name, string keyName) + { + AddAssert($"Check {name} is bound to {keyName}", () => + { + var firstRow = panel.ChildrenOfType().First(r => r.ChildrenOfType().Any(s => s.Text == name)); + var firstButton = firstRow.ChildrenOfType().First(); + + return firstButton.Text.Text == keyName; + }); + } + + private void scrollToAndStartBinding(string name) + { + KeyBindingRow.KeyButton firstButton = null; + + AddStep($"Scroll to {name}", () => + { + var firstRow = panel.ChildrenOfType().First(r => r.ChildrenOfType().Any(s => s.Text == name)); + firstButton = firstRow.ChildrenOfType().First(); + + panel.ChildrenOfType().First().ScrollTo(firstButton); + }); + + AddWaitStep("wait for scroll", 5); + + AddStep("click to bind", () => + { + InputManager.MoveMouseTo(firstButton); + InputManager.Click(MouseButton.Left); + }); + } } } diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs index 8f3274b2b5..f0a11d67b7 100644 --- a/osu.Game/Overlays/SettingsPanel.cs +++ b/osu.Game/Overlays/SettingsPanel.cs @@ -191,7 +191,7 @@ namespace osu.Game.Overlays Padding = new MarginPadding { Top = GetToolbarHeight?.Invoke() ?? 0 }; } - protected class SettingsSectionsContainer : SectionsContainer + public class SettingsSectionsContainer : SectionsContainer { public SearchContainer SearchContainer; From 098d8c213178827f6b759ec9841cbb6a8cb8549b Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Mon, 24 May 2021 15:13:31 +0200 Subject: [PATCH 441/839] Add complete randomisation for first hit object and hit objects after spinners --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 33 ++++++++++++++-------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index cc7732372f..ad2f4585f6 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -58,35 +58,32 @@ namespace osu.Game.Rulesets.Osu.Mods var rng = new Random((int)Seed.Value); - var prevObjectInfo = new RandomObjectInfo - { - PositionOriginal = hitObjects[0].Position, - EndPositionOriginal = hitObjects[0].EndPosition, - PositionRandomised = hitObjects[0].Position, - EndPositionRandomised = hitObjects[0].EndPosition - }; + RandomObjectInfo? prevObjectInfo = null; float rateOfChangeMultiplier = 0; for (int i = 0; i < hitObjects.Count; i++) { + var distanceToPrev = 0f; + var hitObject = hitObjects[i]; var currentObjectInfo = new RandomObjectInfo(hitObject); - if (i == 0) - prevObjectInfo = currentObjectInfo; + if (i > 0 && hitObjects[i - 1] is Spinner) + prevObjectInfo = null; + else if (prevObjectInfo != null) + distanceToPrev = Vector2.Distance(((RandomObjectInfo)prevObjectInfo).EndPositionOriginal, currentObjectInfo.PositionOriginal); // rateOfChangeMultiplier only changes every i iterations to prevent shaky-line-shaped streams if (i % 3 == 0) rateOfChangeMultiplier = (float)rng.NextDouble() * 2 - 1; - var distanceToPrev = Vector2.Distance(prevObjectInfo.EndPositionOriginal, currentObjectInfo.PositionOriginal); - if (hitObject is Spinner) continue; applyRandomisation( + rng, rateOfChangeMultiplier, prevObjectInfo, distanceToPrev, @@ -119,8 +116,20 @@ namespace osu.Game.Rulesets.Osu.Mods /// Returns the final position of the hit object ///
/// Final position of the hit object - private void applyRandomisation(float rateOfChangeMultiplier, RandomObjectInfo prevObjectInfo, float distanceToPrev, ref RandomObjectInfo currentObjectInfo) + private void applyRandomisation(Random rng, float rateOfChangeMultiplier, RandomObjectInfo? prevObjectInfoNullable, float distanceToPrev, ref RandomObjectInfo currentObjectInfo) { + if (prevObjectInfoNullable == null) + { + var playfieldSize = OsuPlayfield.BASE_SIZE; + + currentObjectInfo.AngleRad = (float)(rng.NextDouble() * 2 * Math.PI - Math.PI); + currentObjectInfo.PositionRandomised = new Vector2((float)rng.NextDouble() * playfieldSize.X, (float)rng.NextDouble() * playfieldSize.Y); + + return; + } + + var prevObjectInfo = (RandomObjectInfo)prevObjectInfoNullable; + // The max. angle (relative to the angle of the vector pointing from the 2nd last to the last hit object) // is proportional to the distance between the last and the current hit object // to allow jumps and prevent too sharp turns during streams. From 2e6d46390152f59efecfdc6a6e43fbfa36d175cf Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Mon, 24 May 2021 20:45:47 +0700 Subject: [PATCH 442/839] add test link with title --- .../UserInterface/TestSceneOsuMarkdownContainer.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuMarkdownContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuMarkdownContainer.cs index dc41f184f2..931af7bc95 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuMarkdownContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuMarkdownContainer.cs @@ -86,6 +86,15 @@ _**italic with underscore, bold with asterisk**_"; }); } + [Test] + public void TestLinkWithTitle() + { + AddStep("Add Link with title", () => + { + markdownContainer.Text = "[wikipedia](https://www.wikipedia.org \"The Free Encyclopedia\")"; + }); + } + [Test] public void TestInlineCode() { From 728258d93a243e8c3655ba6655f36232deafa58e Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 25 May 2021 00:29:59 +0700 Subject: [PATCH 443/839] add website root url as document url --- .../Graphics/Containers/Markdown/OsuMarkdownContainer.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownContainer.cs b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownContainer.cs index 6facf4e26c..9ecedba59a 100644 --- a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownContainer.cs +++ b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownContainer.cs @@ -6,11 +6,13 @@ using Markdig.Extensions.AutoIdentifiers; using Markdig.Extensions.Tables; using Markdig.Extensions.Yaml; using Markdig.Syntax; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers.Markdown; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics.Sprites; +using osu.Game.Online.API; namespace osu.Game.Graphics.Containers.Markdown { @@ -21,6 +23,12 @@ namespace osu.Game.Graphics.Containers.Markdown LineSpacing = 21; } + [BackgroundDependencyLoader] + private void load(IAPIProvider api) + { + DocumentUrl = api.WebsiteRootUrl; + } + protected override void AddMarkdownComponent(IMarkdownObject markdownObject, FillFlowContainer container, int level) { switch (markdownObject) From 65649e5a6264707f8079e5714931135431851cbf Mon Sep 17 00:00:00 2001 From: kamp Date: Mon, 24 May 2021 21:36:42 +0200 Subject: [PATCH 444/839] Prevent skin editor crash when scaling 0 area drawables Some skinnable drawables can have 0 width or height in certain cases, leading to division by 0 and a crash when the position is updated. --- osu.Game/Skinning/Editor/SkinSelectionHandler.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index 99bd22c0bf..7ef07541b4 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -59,6 +59,10 @@ namespace osu.Game.Skinning.Editor // the selection quad is always upright, so use an AABB rect to make mutating the values easier. var selectionRect = getSelectionQuad().AABBFloat; + // If the selection has no area we cannot scale it + if (selectionRect.Area == 0.0) + return false; + // copy to mutate, as we will need to compare to the original later on. var adjustedRect = selectionRect; From 9de07de46784c479fd32f6867eee3692f911693d Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 25 May 2021 12:14:07 +0700 Subject: [PATCH 445/839] move text flow container inside markdown container --- .../Overlays/Wiki/Markdown/WikiMarkdownContainer.cs | 6 ++++++ .../Wiki/Markdown/WikiMarkdownTextFlowContainer.cs | 13 ------------- 2 files changed, 6 insertions(+), 13 deletions(-) delete mode 100644 osu.Game/Overlays/Wiki/Markdown/WikiMarkdownTextFlowContainer.cs diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs index 81115293d4..d4ad0bee4d 100644 --- a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs +++ b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs @@ -3,6 +3,7 @@ using Markdig.Extensions.Yaml; using Markdig.Syntax; +using Markdig.Syntax.Inlines; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers.Markdown; using osu.Game.Graphics.Containers.Markdown; @@ -35,5 +36,10 @@ namespace osu.Game.Overlays.Wiki.Markdown protected override MarkdownParagraph CreateParagraph(ParagraphBlock paragraphBlock, int level) => new WikiMarkdownParagraph(paragraphBlock); protected virtual FillFlowContainer CreateNotice(YamlFrontMatterBlock yamlFrontMatterBlock) => new WikiNoticeContainer(yamlFrontMatterBlock); + + private class WikiMarkdownTextFlowContainer : OsuMarkdownTextFlowContainer + { + protected override void AddImage(LinkInline linkInline) => AddDrawable(new WikiMarkdownImage(linkInline)); + } } } diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownTextFlowContainer.cs b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownTextFlowContainer.cs deleted file mode 100644 index 1c2b37a219..0000000000 --- a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownTextFlowContainer.cs +++ /dev/null @@ -1,13 +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 Markdig.Syntax.Inlines; -using osu.Game.Graphics.Containers.Markdown; - -namespace osu.Game.Overlays.Wiki.Markdown -{ - public class WikiMarkdownTextFlowContainer : OsuMarkdownTextFlowContainer - { - protected override void AddImage(LinkInline linkInline) => AddDrawable(new WikiMarkdownImage(linkInline)); - } -} From e3507d545391b6cbf665de5e58fdd485e7e48ab5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 May 2021 16:06:39 +0900 Subject: [PATCH 446/839] Move `DrawableStoryboard`'s aspect application to inside its own class --- osu.Game/Storyboards/Drawables/DrawableStoryboard.cs | 3 +++ osu.Game/Storyboards/Storyboard.cs | 8 ++------ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs index 4c42823779..1cd9b40089 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs @@ -58,6 +58,9 @@ namespace osu.Game.Storyboards.Drawables { Storyboard = storyboard; Size = new Vector2(640, 480); + + Width = Height * (storyboard.BeatmapInfo.WidescreenStoryboard ? 16 / 9f : 4 / 3f); + Anchor = Anchor.Centre; Origin = Anchor.Centre; diff --git a/osu.Game/Storyboards/Storyboard.cs b/osu.Game/Storyboards/Storyboard.cs index bc61f704dd..08e80bc48c 100644 --- a/osu.Game/Storyboards/Storyboard.cs +++ b/osu.Game/Storyboards/Storyboard.cs @@ -85,12 +85,8 @@ namespace osu.Game.Storyboards } } - public DrawableStoryboard CreateDrawable(WorkingBeatmap working = null) - { - var drawable = new DrawableStoryboard(this); - drawable.Width = drawable.Height * (BeatmapInfo.WidescreenStoryboard ? 16 / 9f : 4 / 3f); - return drawable; - } + public DrawableStoryboard CreateDrawable(WorkingBeatmap working = null) => + new DrawableStoryboard(this); public Drawable CreateSpriteFromResourcePath(string path, TextureStore textureStore) { From 0c55bba220ec1f95bdfaf27cd105c0a72eafb6a2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 May 2021 16:07:17 +0900 Subject: [PATCH 447/839] Allow storyboards to be widescreen if only a video element exists This matches stable behaviour, which will allow videos to display filling the screen if they are the only thing contained within the "storyboard". --- osu.Game/Storyboards/Drawables/DrawableStoryboard.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs index 1cd9b40089..bf67194e84 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs @@ -57,9 +57,12 @@ namespace osu.Game.Storyboards.Drawables public DrawableStoryboard(Storyboard storyboard) { Storyboard = storyboard; + Size = new Vector2(640, 480); - Width = Height * (storyboard.BeatmapInfo.WidescreenStoryboard ? 16 / 9f : 4 / 3f); + bool onlyHasVideoElements = !Storyboard.Layers.Any(l => l.Elements.Any(e => !(e is StoryboardVideo))); + + Width = Height * (storyboard.BeatmapInfo.WidescreenStoryboard || onlyHasVideoElements ? 16 / 9f : 4 / 3f); Anchor = Anchor.Centre; Origin = Anchor.Centre; From 5ea948aabe67b7ec36eddb884c158d4540141051 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 May 2021 16:17:28 +0900 Subject: [PATCH 448/839] Bypass 640x480 coordinate space for video storyboard elements This allows the `FillMode.Fill` to take up the full space of the storyboard container. --- .../Drawables/DrawableStoryboardLayer.cs | 11 ++++--- osu.Game/Storyboards/Storyboard.cs | 2 +- osu.Game/Storyboards/StoryboardLayer.cs | 2 +- osu.Game/Storyboards/StoryboardLayerVideo.cs | 32 +++++++++++++++++++ 4 files changed, 41 insertions(+), 6 deletions(-) create mode 100644 osu.Game/Storyboards/StoryboardLayerVideo.cs diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardLayer.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardLayer.cs index 2ada83c3b4..1085b52d65 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardLayer.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardLayer.cs @@ -5,6 +5,7 @@ using System.Threading; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osuTK; namespace osu.Game.Storyboards.Drawables { @@ -15,6 +16,8 @@ namespace osu.Game.Storyboards.Drawables public override bool IsPresent => Enabled && base.IsPresent; + protected LayerElementContainer ElementContainer { get; } + public DrawableStoryboardLayer(StoryboardLayer layer) { Layer = layer; @@ -24,10 +27,10 @@ namespace osu.Game.Storyboards.Drawables Enabled = layer.VisibleWhenPassing; Masking = layer.Masking; - InternalChild = new LayerElementContainer(layer); + InternalChild = ElementContainer = new LayerElementContainer(layer); } - private class LayerElementContainer : LifetimeManagementContainer + protected class LayerElementContainer : LifetimeManagementContainer { private readonly StoryboardLayer storyboardLayer; @@ -35,8 +38,8 @@ namespace osu.Game.Storyboards.Drawables { storyboardLayer = layer; - Width = 640; - Height = 480; + Size = new Vector2(640, 480); + Anchor = Anchor.Centre; Origin = Anchor.Centre; } diff --git a/osu.Game/Storyboards/Storyboard.cs b/osu.Game/Storyboards/Storyboard.cs index 08e80bc48c..06be6c2d20 100644 --- a/osu.Game/Storyboards/Storyboard.cs +++ b/osu.Game/Storyboards/Storyboard.cs @@ -53,7 +53,7 @@ namespace osu.Game.Storyboards public Storyboard() { - layers.Add("Video", new StoryboardLayer("Video", 4, false)); + layers.Add("Video", new StoryboardLayerVideo("Video", 4, false)); layers.Add("Background", new StoryboardLayer("Background", 3)); layers.Add("Fail", new StoryboardLayer("Fail", 2) { VisibleWhenPassing = false, }); layers.Add("Pass", new StoryboardLayer("Pass", 1) { VisibleWhenFailing = false, }); diff --git a/osu.Game/Storyboards/StoryboardLayer.cs b/osu.Game/Storyboards/StoryboardLayer.cs index 1cde7cf67a..fa9d4ebfea 100644 --- a/osu.Game/Storyboards/StoryboardLayer.cs +++ b/osu.Game/Storyboards/StoryboardLayer.cs @@ -32,7 +32,7 @@ namespace osu.Game.Storyboards Elements.Add(element); } - public DrawableStoryboardLayer CreateDrawable() + public virtual DrawableStoryboardLayer CreateDrawable() => new DrawableStoryboardLayer(this) { Depth = Depth, Name = Name }; } } diff --git a/osu.Game/Storyboards/StoryboardLayerVideo.cs b/osu.Game/Storyboards/StoryboardLayerVideo.cs new file mode 100644 index 0000000000..7235df7a41 --- /dev/null +++ b/osu.Game/Storyboards/StoryboardLayerVideo.cs @@ -0,0 +1,32 @@ +// 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.Game.Storyboards.Drawables; +using osuTK; + +namespace osu.Game.Storyboards +{ + public class StoryboardLayerVideo : StoryboardLayer + { + public StoryboardLayerVideo(string name, int depth, bool masking) + : base(name, depth, masking) + { + } + + public override DrawableStoryboardLayer CreateDrawable() + => new DrawableStoryboardLayerVideo(this) { Depth = Depth, Name = Name }; + + public class DrawableStoryboardLayerVideo : DrawableStoryboardLayer + { + public DrawableStoryboardLayerVideo(StoryboardLayerVideo layer) + : base(layer) + { + // for videos we want to take on the full size of the storyboard container hierarchy + // to allow the video to fill the full available region. + ElementContainer.RelativeSizeAxes = Axes.Both; + ElementContainer.Size = Vector2.One; + } + } + } +} From 4c8f19af69cd09f0db9b9ad79931d90294f3f3b0 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 25 May 2021 14:47:39 +0700 Subject: [PATCH 449/839] load empty page when fail --- osu.Game/Overlays/WikiOverlay.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs index fa8ba66bcd..df93c35500 100644 --- a/osu.Game/Overlays/WikiOverlay.cs +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -47,6 +47,7 @@ namespace osu.Game.Overlays Loading.Show(); request.Success += response => Schedule(() => onSuccess(response)); + request.Failure += _ => Schedule(() => LoadDisplay(Empty())); api.PerformAsync(request); } From a3c78674a12022506f8cd42d2407e7dfe8ad176a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 May 2021 18:09:24 +0900 Subject: [PATCH 450/839] Add new interface for autoplay mods --- .../Mods/EmptyFreeformModAutoplay.cs | 3 +-- .../Mods/PippidonModAutoplay.cs | 3 +-- .../Mods/EmptyScrollingModAutoplay.cs | 3 +-- .../Mods/PippidonModAutoplay.cs | 3 +-- osu.Game.Rulesets.Catch/Mods/CatchModAutoplay.cs | 3 +-- osu.Game.Rulesets.Mania/Mods/ManiaModAutoplay.cs | 3 +-- osu.Game.Rulesets.Osu/Mods/OsuModAutoplay.cs | 3 +-- osu.Game.Rulesets.Taiko/Mods/TaikoModAutoplay.cs | 3 +-- osu.Game/Rulesets/Mods/ICreateReplay.cs | 14 ++++++++++++++ osu.Game/Rulesets/Mods/ModAutoplay.cs | 13 +------------ 10 files changed, 23 insertions(+), 28 deletions(-) create mode 100644 osu.Game/Rulesets/Mods/ICreateReplay.cs diff --git a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/Mods/EmptyFreeformModAutoplay.cs b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/Mods/EmptyFreeformModAutoplay.cs index d5c1e9bd15..f705009d18 100644 --- a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/Mods/EmptyFreeformModAutoplay.cs +++ b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/Mods/EmptyFreeformModAutoplay.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using osu.Game.Beatmaps; -using osu.Game.Rulesets.EmptyFreeform.Objects; using osu.Game.Rulesets.EmptyFreeform.Replays; using osu.Game.Rulesets.Mods; using osu.Game.Scoring; @@ -11,7 +10,7 @@ using osu.Game.Users; namespace osu.Game.Rulesets.EmptyFreeform.Mods { - public class EmptyFreeformModAutoplay : ModAutoplay + public class EmptyFreeformModAutoplay : ModAutoplay { public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList mods) => new Score { diff --git a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/Mods/PippidonModAutoplay.cs b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/Mods/PippidonModAutoplay.cs index 8ea334c99c..4565c97d1a 100644 --- a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/Mods/PippidonModAutoplay.cs +++ b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/Mods/PippidonModAutoplay.cs @@ -4,14 +4,13 @@ using System.Collections.Generic; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Pippidon.Objects; using osu.Game.Rulesets.Pippidon.Replays; using osu.Game.Scoring; using osu.Game.Users; namespace osu.Game.Rulesets.Pippidon.Mods { - public class PippidonModAutoplay : ModAutoplay + public class PippidonModAutoplay : ModAutoplay { public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList mods) => new Score { diff --git a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/Mods/EmptyScrollingModAutoplay.cs b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/Mods/EmptyScrollingModAutoplay.cs index 6dad1ff43b..431994e098 100644 --- a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/Mods/EmptyScrollingModAutoplay.cs +++ b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/Mods/EmptyScrollingModAutoplay.cs @@ -3,7 +3,6 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.EmptyScrolling.Objects; using osu.Game.Rulesets.EmptyScrolling.Replays; using osu.Game.Scoring; using osu.Game.Users; @@ -11,7 +10,7 @@ using System.Collections.Generic; namespace osu.Game.Rulesets.EmptyScrolling.Mods { - public class EmptyScrollingModAutoplay : ModAutoplay + public class EmptyScrollingModAutoplay : ModAutoplay { public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList mods) => new Score { diff --git a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Mods/PippidonModAutoplay.cs b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Mods/PippidonModAutoplay.cs index 8ea334c99c..4565c97d1a 100644 --- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Mods/PippidonModAutoplay.cs +++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Mods/PippidonModAutoplay.cs @@ -4,14 +4,13 @@ using System.Collections.Generic; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Pippidon.Objects; using osu.Game.Rulesets.Pippidon.Replays; using osu.Game.Scoring; using osu.Game.Users; namespace osu.Game.Rulesets.Pippidon.Mods { - public class PippidonModAutoplay : ModAutoplay + public class PippidonModAutoplay : ModAutoplay { public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList mods) => new Score { diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModAutoplay.cs b/osu.Game.Rulesets.Catch/Mods/CatchModAutoplay.cs index e1eceea606..f1b51e51d0 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModAutoplay.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModAutoplay.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using osu.Game.Beatmaps; -using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Replays; using osu.Game.Rulesets.Mods; using osu.Game.Scoring; @@ -11,7 +10,7 @@ using osu.Game.Users; namespace osu.Game.Rulesets.Catch.Mods { - public class CatchModAutoplay : ModAutoplay + public class CatchModAutoplay : ModAutoplay { public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList mods) => new Score { diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModAutoplay.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModAutoplay.cs index 105d88129c..6ae854e7f3 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModAutoplay.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModAutoplay.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps; -using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Replays; using osu.Game.Rulesets.Mods; using osu.Game.Scoring; @@ -12,7 +11,7 @@ using osu.Game.Users; namespace osu.Game.Rulesets.Mania.Mods { - public class ManiaModAutoplay : ModAutoplay + public class ManiaModAutoplay : ModAutoplay { public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList mods) => new Score { diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModAutoplay.cs b/osu.Game.Rulesets.Osu/Mods/OsuModAutoplay.cs index 3b1f271d41..652da7123e 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModAutoplay.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModAutoplay.cs @@ -6,14 +6,13 @@ using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Replays; using osu.Game.Scoring; using osu.Game.Users; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModAutoplay : ModAutoplay + public class OsuModAutoplay : ModAutoplay { public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).Append(typeof(OsuModSpunOut)).ToArray(); diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModAutoplay.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModAutoplay.cs index 64e59b64d0..31d9abf8b2 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModAutoplay.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModAutoplay.cs @@ -4,14 +4,13 @@ using System.Collections.Generic; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Replays; using osu.Game.Scoring; using osu.Game.Users; namespace osu.Game.Rulesets.Taiko.Mods { - public class TaikoModAutoplay : ModAutoplay + public class TaikoModAutoplay : ModAutoplay { public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList mods) => new Score { diff --git a/osu.Game/Rulesets/Mods/ICreateReplay.cs b/osu.Game/Rulesets/Mods/ICreateReplay.cs new file mode 100644 index 0000000000..098bd8799a --- /dev/null +++ b/osu.Game/Rulesets/Mods/ICreateReplay.cs @@ -0,0 +1,14 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using osu.Game.Beatmaps; +using osu.Game.Scoring; + +namespace osu.Game.Rulesets.Mods +{ + public interface ICreateReplay + { + public Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList mods); + } +} diff --git a/osu.Game/Rulesets/Mods/ModAutoplay.cs b/osu.Game/Rulesets/Mods/ModAutoplay.cs index d6e1d46b06..b84b5671e1 100644 --- a/osu.Game/Rulesets/Mods/ModAutoplay.cs +++ b/osu.Game/Rulesets/Mods/ModAutoplay.cs @@ -7,22 +7,11 @@ using osu.Framework.Graphics.Sprites; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Replays; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.UI; using osu.Game.Scoring; namespace osu.Game.Rulesets.Mods { - public abstract class ModAutoplay : ModAutoplay, IApplicableToDrawableRuleset - where T : HitObject - { - public virtual void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) - { - drawableRuleset.SetReplayScore(CreateReplayScore(drawableRuleset.Beatmap, drawableRuleset.Mods)); - } - } - - public abstract class ModAutoplay : Mod, IApplicableFailOverride + public abstract class ModAutoplay : Mod, IApplicableFailOverride, ICreateReplay { public override string Name => "Autoplay"; public override string Acronym => "AT"; From c2b938a29f5fef339a3adeb9ab62fa647a06dc12 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 May 2021 18:09:37 +0900 Subject: [PATCH 451/839] Remove autoplay consideration from `Player` --- osu.Game/Screens/Play/Player.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 39f9e2d388..b818dbea08 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -161,9 +161,7 @@ namespace osu.Game.Screens.Play if (!LoadedBeatmapSuccessfully) return; - // replays should never be recorded or played back when autoplay is enabled - if (!Mods.Value.Any(m => m is ModAutoplay)) - PrepareReplay(); + PrepareReplay(); gameActive.BindValueChanged(_ => updatePauseOnFocusLostState(), true); } From 7f9318d97658d1dcbce49162c1fa3d7d9bdde4db Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 May 2021 18:36:47 +0900 Subject: [PATCH 452/839] Expose `GameplayBeatmap` to derived `Player` classes --- osu.Game/Screens/Play/Player.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index b818dbea08..8e9c2fadcd 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -145,7 +145,7 @@ namespace osu.Game.Screens.Play Configuration = configuration ?? new PlayerConfiguration(); } - private GameplayBeatmap gameplayBeatmap; + protected GameplayBeatmap GameplayBeatmap { get; private set; } private ScreenSuspensionHandler screenSuspension; @@ -221,10 +221,10 @@ namespace osu.Game.Screens.Play InternalChild = GameplayClockContainer = CreateGameplayClockContainer(Beatmap.Value, DrawableRuleset.GameplayStartTime); - AddInternal(gameplayBeatmap = new GameplayBeatmap(playableBeatmap)); + AddInternal(GameplayBeatmap = new GameplayBeatmap(playableBeatmap)); AddInternal(screenSuspension = new ScreenSuspensionHandler(GameplayClockContainer)); - dependencies.CacheAs(gameplayBeatmap); + dependencies.CacheAs(GameplayBeatmap); var beatmapSkinProvider = new BeatmapSkinProvidingContainer(Beatmap.Value.Skin); @@ -282,7 +282,7 @@ namespace osu.Game.Screens.Play { HealthProcessor.ApplyResult(r); ScoreProcessor.ApplyResult(r); - gameplayBeatmap.ApplyResult(r); + GameplayBeatmap.ApplyResult(r); }; DrawableRuleset.RevertResult += r => @@ -946,7 +946,7 @@ namespace osu.Game.Screens.Play using (var stream = new MemoryStream()) { - new LegacyScoreEncoder(score, gameplayBeatmap.PlayableBeatmap).Encode(stream); + new LegacyScoreEncoder(score, GameplayBeatmap.PlayableBeatmap).Encode(stream); replayReader = new LegacyByteArrayReader(stream.ToArray(), "replay.osr"); } From 7c89dbcd3502e68639476f9d99ab92e63205c870 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 May 2021 18:37:04 +0900 Subject: [PATCH 453/839] Externalise autoplay generation --- osu.Game/Screens/Play/ReplayPlayer.cs | 21 ++++++++++++++++-- osu.Game/Screens/Select/PlaySongSelect.cs | 26 +++++++++++++++-------- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs index e23cc22929..07c3d197da 100644 --- a/osu.Game/Screens/Play/ReplayPlayer.cs +++ b/osu.Game/Screens/Play/ReplayPlayer.cs @@ -1,9 +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 System; +using System.Collections.Generic; using System.Threading.Tasks; +using osu.Framework.Allocation; using osu.Framework.Input.Bindings; using osu.Game.Input.Bindings; +using osu.Game.Rulesets.Mods; using osu.Game.Scoring; using osu.Game.Screens.Ranking; @@ -11,15 +15,28 @@ namespace osu.Game.Screens.Play { public class ReplayPlayer : Player, IKeyBindingHandler { - protected readonly Score Score; + protected Score Score { get; private set; } + + private readonly Func, Score> createScore; // Disallow replays from failing. (see https://github.com/ppy/osu/issues/6108) protected override bool CheckModsAllowFailure() => false; public ReplayPlayer(Score score, PlayerConfiguration configuration = null) + : this((_, __) => score, configuration) + { + } + + public ReplayPlayer(Func, Score> createScore, PlayerConfiguration configuration = null) : base(configuration) { - Score = score; + this.createScore = createScore; + } + + [BackgroundDependencyLoader] + private void load() + { + Score = createScore(GameplayBeatmap, Mods.Value); } protected override void PrepareReplay() diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index dfb4b59060..357222c109 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -21,7 +21,7 @@ namespace osu.Game.Screens.Select public class PlaySongSelect : SongSelect { private bool removeAutoModOnResume; - private OsuScreen player; + private OsuScreen playerLoader; [Resolved(CanBeNull = true)] private NotificationOverlay notifications { get; set; } @@ -49,7 +49,7 @@ namespace osu.Game.Screens.Select { base.OnResuming(last); - player = null; + playerLoader = null; if (removeAutoModOnResume) { @@ -79,14 +79,14 @@ namespace osu.Game.Screens.Select protected override bool OnStart() { - if (player != null) return false; + if (playerLoader != null) return false; // Ctrl+Enter should start map with autoplay enabled. if (GetContainingInputManager().CurrentState?.Keyboard.ControlPressed == true) { - var autoplayMod = getAutoplayMod(); + var autoInstance = getAutoplayMod(); - if (autoplayMod == null) + if (autoInstance == null) { notifications?.Post(new SimpleNotification { @@ -97,18 +97,26 @@ namespace osu.Game.Screens.Select var mods = Mods.Value; - if (mods.All(m => m.GetType() != autoplayMod.GetType())) + if (mods.All(m => m.GetType() != autoInstance.GetType())) { - Mods.Value = mods.Append(autoplayMod).ToArray(); + Mods.Value = mods.Append(autoInstance).ToArray(); removeAutoModOnResume = true; } } SampleConfirm?.Play(); - this.Push(player = new PlayerLoader(() => new SoloPlayer())); - + this.Push(playerLoader = new PlayerLoader(createPlayer)); return true; + + Player createPlayer() + { + var autoplayMod = Mods.Value.OfType().FirstOrDefault(); + if (autoplayMod != null) + return new ReplayPlayer((beatmap, mods) => autoplayMod.CreateReplayScore(beatmap, mods)); + + return new SoloPlayer(); + } } } } From 871ca8054f4cfe36567fe653f45f9a333c5018bc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 May 2021 18:50:33 +0900 Subject: [PATCH 454/839] Rename classes as per review feedback --- osu.Game/Storyboards/Storyboard.cs | 2 +- ...StoryboardLayerVideo.cs => StoryboardVideoLayer.cs} | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) rename osu.Game/Storyboards/{StoryboardLayerVideo.cs => StoryboardVideoLayer.cs} (71%) diff --git a/osu.Game/Storyboards/Storyboard.cs b/osu.Game/Storyboards/Storyboard.cs index 06be6c2d20..3486c1d66a 100644 --- a/osu.Game/Storyboards/Storyboard.cs +++ b/osu.Game/Storyboards/Storyboard.cs @@ -53,7 +53,7 @@ namespace osu.Game.Storyboards public Storyboard() { - layers.Add("Video", new StoryboardLayerVideo("Video", 4, false)); + layers.Add("Video", new StoryboardVideoLayer("Video", 4, false)); layers.Add("Background", new StoryboardLayer("Background", 3)); layers.Add("Fail", new StoryboardLayer("Fail", 2) { VisibleWhenPassing = false, }); layers.Add("Pass", new StoryboardLayer("Pass", 1) { VisibleWhenFailing = false, }); diff --git a/osu.Game/Storyboards/StoryboardLayerVideo.cs b/osu.Game/Storyboards/StoryboardVideoLayer.cs similarity index 71% rename from osu.Game/Storyboards/StoryboardLayerVideo.cs rename to osu.Game/Storyboards/StoryboardVideoLayer.cs index 7235df7a41..2a01c2274a 100644 --- a/osu.Game/Storyboards/StoryboardLayerVideo.cs +++ b/osu.Game/Storyboards/StoryboardVideoLayer.cs @@ -7,19 +7,19 @@ using osuTK; namespace osu.Game.Storyboards { - public class StoryboardLayerVideo : StoryboardLayer + public class StoryboardVideoLayer : StoryboardLayer { - public StoryboardLayerVideo(string name, int depth, bool masking) + public StoryboardVideoLayer(string name, int depth, bool masking) : base(name, depth, masking) { } public override DrawableStoryboardLayer CreateDrawable() - => new DrawableStoryboardLayerVideo(this) { Depth = Depth, Name = Name }; + => new DrawableStoryboardVideoLayer(this) { Depth = Depth, Name = Name }; - public class DrawableStoryboardLayerVideo : DrawableStoryboardLayer + public class DrawableStoryboardVideoLayer : DrawableStoryboardLayer { - public DrawableStoryboardLayerVideo(StoryboardLayerVideo layer) + public DrawableStoryboardVideoLayer(StoryboardVideoLayer layer) : base(layer) { // for videos we want to take on the full size of the storyboard container hierarchy From 342acadae2381c0822c596f5f3c71efcdc9afef5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 May 2021 18:51:49 +0900 Subject: [PATCH 455/839] Change LINQ query for better readability --- osu.Game/Storyboards/Drawables/DrawableStoryboard.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs index bf67194e84..ca041da801 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs @@ -60,7 +60,7 @@ namespace osu.Game.Storyboards.Drawables Size = new Vector2(640, 480); - bool onlyHasVideoElements = !Storyboard.Layers.Any(l => l.Elements.Any(e => !(e is StoryboardVideo))); + bool onlyHasVideoElements = Storyboard.Layers.SelectMany(l => l.Elements).Any(e => !(e is StoryboardVideo)); Width = Height * (storyboard.BeatmapInfo.WidescreenStoryboard || onlyHasVideoElements ? 16 / 9f : 4 / 3f); From ce845a9f8d5748c0ef1ca35edcde6e5469abb076 Mon Sep 17 00:00:00 2001 From: Swords Date: Tue, 25 May 2021 21:00:38 +1000 Subject: [PATCH 456/839] Apply the rest of requested changes --- .../Overlays/KeyBinding/BasicKeyBindingRow.cs | 5 +--- osu.Game/Overlays/KeyBinding/KeyBindingRow.cs | 30 +++++++------------ 2 files changed, 12 insertions(+), 23 deletions(-) diff --git a/osu.Game/Overlays/KeyBinding/BasicKeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/BasicKeyBindingRow.cs index acdf273622..91d9aa70bd 100644 --- a/osu.Game/Overlays/KeyBinding/BasicKeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/BasicKeyBindingRow.cs @@ -37,10 +37,7 @@ namespace osu.Game.Overlays.KeyBinding private FillFlowContainer cancelAndClearButtons; private FillFlowContainer buttons; - public Bindable IsDefault { get; } = new BindableBool(true) - { - Default = true - }; + public Bindable IsDefault { get; } = new BindableBool(true); public BasicKeyBindingRow(object action, IEnumerable bindings) { diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs index 3221b66bce..f799b4810f 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs @@ -32,41 +32,33 @@ namespace osu.Game.Overlays.KeyBinding public IEnumerable FilterTerms => bindings.Select(b => b.KeyCombination.ReadableString()).Prepend(key.ToString()); - public KeyBindingRow( - object key, - ICollection bindings, - RulesetInfo ruleset, - IEnumerable defaults) - { + public KeyBindingRow(object key, ICollection bindings, RulesetInfo ruleset, IEnumerable defaults) { this.key = key; this.bindings = bindings; - RestoreDefaultValueButton restoreDefaultButton; - RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; Padding = new MarginPadding { Right = SettingsPanel.CONTENT_MARGINS }; - BasicKeyBindingRow = new BasicKeyBindingRow(key, bindings.Where(b => ((int)b.Action).Equals((int)key))) - { - AllowMainMouseButtons = ruleset != null, - Defaults = defaults - }; - InternalChildren = new Drawable[] { - restoreDefaultButton = new RestoreDefaultValueButton(), + new RestoreDefaultValueButton() + { + Current = BasicKeyBindingRow.IsDefault, + Action = () => { BasicKeyBindingRow.RestoreDefaults(); } + }, new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Padding = new MarginPadding { Left = SettingsPanel.CONTENT_MARGINS }, - Child = BasicKeyBindingRow + Child = BasicKeyBindingRow = new BasicKeyBindingRow(key, bindings.Where(b => ((int)b.Action).Equals((int)key))) + { + AllowMainMouseButtons = ruleset != null, + Defaults = defaults + } }, }; - - restoreDefaultButton.Action = () => { BasicKeyBindingRow.RestoreDefaults(); }; - restoreDefaultButton.Current = BasicKeyBindingRow.IsDefault; } } } From d9f5b578bf6f81fad7037480cf8ce95ac47eadad Mon Sep 17 00:00:00 2001 From: Swords Date: Tue, 25 May 2021 21:08:40 +1000 Subject: [PATCH 457/839] Restore class names --- .../Settings/TestSceneKeyBindingPanel.cs | 42 +- .../Overlays/KeyBinding/BasicKeyBindingRow.cs | 460 ----------------- osu.Game/Overlays/KeyBinding/KeyBindingRow.cs | 474 ++++++++++++++++-- .../KeyBinding/KeyBindingsSubsection.cs | 4 +- .../KeyBinding/RestorableKeyBindingRow.cs | 64 +++ 5 files changed, 522 insertions(+), 522 deletions(-) delete mode 100644 osu.Game/Overlays/KeyBinding/BasicKeyBindingRow.cs create mode 100644 osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs diff --git a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs index face2a498d..bd3e94dffa 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs @@ -31,11 +31,11 @@ namespace osu.Game.Tests.Visual.Settings [Test] public void TestClickTwiceOnClearButton() { - BasicKeyBindingRow firstRow = null; + KeyBindingRow firstRow = null; AddStep("click first row", () => { - firstRow = panel.ChildrenOfType().First(); + firstRow = panel.ChildrenOfType().First(); InputManager.MoveMouseTo(firstRow); InputManager.Click(MouseButton.Left); @@ -43,7 +43,7 @@ namespace osu.Game.Tests.Visual.Settings AddStep("schedule button clicks", () => { - var clearButton = firstRow.ChildrenOfType().Single(); + var clearButton = firstRow.ChildrenOfType().Single(); InputManager.MoveMouseTo(clearButton); @@ -68,22 +68,22 @@ namespace osu.Game.Tests.Visual.Settings [Test] public void TestClearButtonOnBindings() { - BasicKeyBindingRow multiBindingRow = null; + KeyBindingRow multiBindingRow = null; AddStep("click first row with two bindings", () => { - multiBindingRow = panel.ChildrenOfType().First(row => row.Defaults.Count() > 1); + multiBindingRow = panel.ChildrenOfType().First(row => row.Defaults.Count() > 1); InputManager.MoveMouseTo(multiBindingRow); InputManager.Click(MouseButton.Left); }); clickClearButton(); - AddAssert("first binding cleared", () => string.IsNullOrEmpty(multiBindingRow.ChildrenOfType().First().Text.Text.ToString())); + AddAssert("first binding cleared", () => string.IsNullOrEmpty(multiBindingRow.ChildrenOfType().First().Text.Text.ToString())); AddStep("click second binding", () => { - var target = multiBindingRow.ChildrenOfType().ElementAt(1); + var target = multiBindingRow.ChildrenOfType().ElementAt(1); InputManager.MoveMouseTo(target); InputManager.Click(MouseButton.Left); @@ -91,13 +91,13 @@ namespace osu.Game.Tests.Visual.Settings clickClearButton(); - AddAssert("second binding cleared", () => string.IsNullOrEmpty(multiBindingRow.ChildrenOfType().ElementAt(1).Text.Text.ToString())); + AddAssert("second binding cleared", () => string.IsNullOrEmpty(multiBindingRow.ChildrenOfType().ElementAt(1).Text.Text.ToString())); void clickClearButton() { AddStep("click clear button", () => { - var clearButton = multiBindingRow.ChildrenOfType().Single(); + var clearButton = multiBindingRow.ChildrenOfType().Single(); InputManager.MoveMouseTo(clearButton); InputManager.Click(MouseButton.Left); @@ -108,11 +108,11 @@ namespace osu.Game.Tests.Visual.Settings [Test] public void TestSingleBindingResetButton() { - KeyBindingRow settingsKeyBindingRow = null; + RestorableKeyBindingRow settingsKeyBindingRow = null; AddStep("click first row", () => { - settingsKeyBindingRow = panel.ChildrenOfType().First(); + settingsKeyBindingRow = panel.ChildrenOfType().First(); InputManager.MoveMouseTo(settingsKeyBindingRow); InputManager.Click(MouseButton.Left); @@ -131,17 +131,17 @@ namespace osu.Game.Tests.Visual.Settings AddUntilStep("restore button hidden", () => settingsKeyBindingRow.ChildrenOfType>().First().Alpha == 0); - AddAssert("binding cleared", () => settingsKeyBindingRow.ChildrenOfType().ElementAt(0).KeyBinding.KeyCombination.Equals(settingsKeyBindingRow.BasicKeyBindingRow.Defaults.ElementAt(0))); + AddAssert("binding cleared", () => settingsKeyBindingRow.ChildrenOfType().ElementAt(0).KeyBinding.KeyCombination.Equals(settingsKeyBindingRow.BasicKeyBindingRow.Defaults.ElementAt(0))); } [Test] public void TestResetAllBindingsButton() { - KeyBindingRow settingsKeyBindingRow = null; + RestorableKeyBindingRow settingsKeyBindingRow = null; AddStep("click first row", () => { - settingsKeyBindingRow = panel.ChildrenOfType().First(); + settingsKeyBindingRow = panel.ChildrenOfType().First(); InputManager.MoveMouseTo(settingsKeyBindingRow); InputManager.Click(MouseButton.Left); @@ -160,26 +160,26 @@ namespace osu.Game.Tests.Visual.Settings AddUntilStep("restore button hidden", () => settingsKeyBindingRow.ChildrenOfType>().First().Alpha == 0); - AddAssert("binding cleared", () => settingsKeyBindingRow.ChildrenOfType().ElementAt(0).KeyBinding.KeyCombination.Equals(settingsKeyBindingRow.BasicKeyBindingRow.Defaults.ElementAt(0))); + AddAssert("binding cleared", () => settingsKeyBindingRow.ChildrenOfType().ElementAt(0).KeyBinding.KeyCombination.Equals(settingsKeyBindingRow.BasicKeyBindingRow.Defaults.ElementAt(0))); } [Test] public void TestClickRowSelectsFirstBinding() { - BasicKeyBindingRow multiBindingRow = null; + KeyBindingRow multiBindingRow = null; AddStep("click first row with two bindings", () => { - multiBindingRow = panel.ChildrenOfType().First(row => row.Defaults.Count() > 1); + multiBindingRow = panel.ChildrenOfType().First(row => row.Defaults.Count() > 1); InputManager.MoveMouseTo(multiBindingRow); InputManager.Click(MouseButton.Left); }); - AddAssert("first binding selected", () => multiBindingRow.ChildrenOfType().First().IsBinding); + AddAssert("first binding selected", () => multiBindingRow.ChildrenOfType().First().IsBinding); AddStep("click second binding", () => { - var target = multiBindingRow.ChildrenOfType().ElementAt(1); + var target = multiBindingRow.ChildrenOfType().ElementAt(1); InputManager.MoveMouseTo(target); InputManager.Click(MouseButton.Left); @@ -187,12 +187,12 @@ namespace osu.Game.Tests.Visual.Settings AddStep("click back binding row", () => { - multiBindingRow = panel.ChildrenOfType().ElementAt(10); + multiBindingRow = panel.ChildrenOfType().ElementAt(10); InputManager.MoveMouseTo(multiBindingRow); InputManager.Click(MouseButton.Left); }); - AddAssert("first binding selected", () => multiBindingRow.ChildrenOfType().First().IsBinding); + AddAssert("first binding selected", () => multiBindingRow.ChildrenOfType().First().IsBinding); } } } \ No newline at end of file diff --git a/osu.Game/Overlays/KeyBinding/BasicKeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/BasicKeyBindingRow.cs deleted file mode 100644 index 91d9aa70bd..0000000000 --- a/osu.Game/Overlays/KeyBinding/BasicKeyBindingRow.cs +++ /dev/null @@ -1,460 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Extensions; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Effects; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.Bindings; -using osu.Framework.Input.Events; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; -using osu.Game.Input; -using osuTK; -using osuTK.Graphics; -using osuTK.Input; - -namespace osu.Game.Overlays.KeyBinding -{ - public class BasicKeyBindingRow : Container - { - private readonly object action; - private readonly IEnumerable bindings; - - private const float transition_time = 150; - - private const float height = 20; - - private const float padding = 5; - - private FillFlowContainer cancelAndClearButtons; - private FillFlowContainer buttons; - - public Bindable IsDefault { get; } = new BindableBool(true); - - public BasicKeyBindingRow(object action, IEnumerable bindings) - { - this.action = action; - this.bindings = bindings; - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - - Masking = true; - CornerRadius = padding; - } - - [Resolved] - private KeyBindingStore store { get; set; } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - updateIsDefaultValue(); - - EdgeEffect = new EdgeEffectParameters - { - Radius = 2, - Colour = colours.YellowDark.Opacity(0), - Type = EdgeEffectType.Shadow, - Hollow = true, - }; - - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - Alpha = 0.6f, - }, - new OsuSpriteText - { - Text = action.GetDescription(), - Margin = new MarginPadding(padding), - }, - buttons = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight - }, - cancelAndClearButtons = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Padding = new MarginPadding(padding) { Top = height + padding * 2 }, - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Alpha = 0, - Spacing = new Vector2(5), - Children = new Drawable[] - { - new CancelButton { Action = finalise }, - new ClearButton { Action = clear }, - }, - } - }; - foreach (var b in bindings) - buttons.Add(new KeyButton(b)); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - IsDefault.BindValueChanged(isDefault => - { - if (isDefault.NewValue) - { - finalise(); - } - }); - } - - public void RestoreDefaults() - { - int i = 0; - - foreach (var d in Defaults) - { - var button = buttons[i++]; - button.UpdateKeyCombination(d); - store.Update(button.KeyBinding); - } - - updateIsDefaultValue(); - } - - protected override bool OnHover(HoverEvent e) - { - FadeEdgeEffectTo(1, transition_time, Easing.OutQuint); - - return base.OnHover(e); - } - - protected override void OnHoverLost(HoverLostEvent e) - { - FadeEdgeEffectTo(0, transition_time, Easing.OutQuint); - - base.OnHoverLost(e); - } - - public override bool AcceptsFocus => bindTarget == null; - - private KeyButton bindTarget; - - public bool AllowMainMouseButtons; - - public IEnumerable Defaults; - - private bool isModifier(Key k) => k < Key.F1; - - protected override bool OnClick(ClickEvent e) => true; - - protected override bool OnMouseDown(MouseDownEvent e) - { - if (!HasFocus || !bindTarget.IsHovered) - return base.OnMouseDown(e); - - if (!AllowMainMouseButtons) - { - switch (e.Button) - { - case MouseButton.Left: - case MouseButton.Right: - return true; - } - } - - bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(e.CurrentState)); - return true; - } - - protected override void OnMouseUp(MouseUpEvent e) - { - // don't do anything until the last button is released. - if (!HasFocus || e.HasAnyButtonPressed) - { - base.OnMouseUp(e); - return; - } - - if (bindTarget.IsHovered) - finalise(); - // prevent updating bind target before clear button's action - else if (!cancelAndClearButtons.Any(b => b.IsHovered)) - updateBindTarget(); - } - - protected override bool OnScroll(ScrollEvent e) - { - if (HasFocus) - { - if (bindTarget.IsHovered) - { - bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(e.CurrentState, e.ScrollDelta)); - finalise(); - return true; - } - } - - return base.OnScroll(e); - } - - protected override bool OnKeyDown(KeyDownEvent e) - { - if (!HasFocus) - return false; - - bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(e.CurrentState)); - if (!isModifier(e.Key)) finalise(); - - return true; - } - - protected override void OnKeyUp(KeyUpEvent e) - { - if (!HasFocus) - { - base.OnKeyUp(e); - return; - } - - finalise(); - } - - protected override bool OnJoystickPress(JoystickPressEvent e) - { - if (!HasFocus) - return false; - - bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(e.CurrentState)); - finalise(); - - return true; - } - - protected override void OnJoystickRelease(JoystickReleaseEvent e) - { - if (!HasFocus) - { - base.OnJoystickRelease(e); - return; - } - - finalise(); - } - - protected override bool OnMidiDown(MidiDownEvent e) - { - if (!HasFocus) - return false; - - bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(e.CurrentState)); - finalise(); - - return true; - } - - protected override void OnMidiUp(MidiUpEvent e) - { - if (!HasFocus) - { - base.OnMidiUp(e); - return; - } - - finalise(); - } - - private void clear() - { - if (bindTarget == null) - return; - - bindTarget.UpdateKeyCombination(InputKey.None); - finalise(); - } - - private void finalise() - { - if (bindTarget != null) - { - store.Update(bindTarget.KeyBinding); - - updateIsDefaultValue(); - - bindTarget.IsBinding = false; - Schedule(() => - { - // schedule to ensure we don't instantly get focus back on next OnMouseClick (see AcceptFocus impl.) - bindTarget = null; - }); - } - - if (HasFocus) - GetContainingInputManager().ChangeFocus(null); - - cancelAndClearButtons.FadeOut(300, Easing.OutQuint); - cancelAndClearButtons.BypassAutoSizeAxes |= Axes.Y; - } - - private void updateIsDefaultValue() => IsDefault.Value = computeIsDefaultValue(); - - private bool computeIsDefaultValue() => bindings.Select(b => b.KeyCombination).SequenceEqual(Defaults); - - protected override void OnFocus(FocusEvent e) - { - AutoSizeDuration = 500; - AutoSizeEasing = Easing.OutQuint; - - cancelAndClearButtons.FadeIn(300, Easing.OutQuint); - cancelAndClearButtons.BypassAutoSizeAxes &= ~Axes.Y; - - updateBindTarget(); - base.OnFocus(e); - } - - protected override void OnFocusLost(FocusLostEvent e) - { - finalise(); - base.OnFocusLost(e); - } - - /// - /// Updates the bind target to the currently hovered key button or the first if clicked anywhere else. - /// - private void updateBindTarget() - { - if (bindTarget != null) bindTarget.IsBinding = false; - bindTarget = buttons.FirstOrDefault(b => b.IsHovered) ?? buttons.FirstOrDefault(); - if (bindTarget != null) bindTarget.IsBinding = true; - } - - private class CancelButton : TriangleButton - { - public CancelButton() - { - Text = "Cancel"; - Size = new Vector2(80, 20); - } - } - - public class ClearButton : DangerousTriangleButton - { - public ClearButton() - { - Text = "Clear"; - Size = new Vector2(80, 20); - } - } - - public class KeyButton : Container - { - public readonly Framework.Input.Bindings.KeyBinding KeyBinding; - - private readonly Box box; - public readonly OsuSpriteText Text; - - private Color4 hoverColour; - - private bool isBinding; - - public bool IsBinding - { - get => isBinding; - set - { - if (value == isBinding) return; - - isBinding = value; - - updateHoverState(); - } - } - - public KeyButton(Framework.Input.Bindings.KeyBinding keyBinding) - { - KeyBinding = keyBinding; - - Margin = new MarginPadding(padding); - - // todo: use this in a meaningful way - // var isDefault = keyBinding.Action is Enum; - - Masking = true; - CornerRadius = padding; - - Height = height; - AutoSizeAxes = Axes.X; - - Children = new Drawable[] - { - new Container - { - AlwaysPresent = true, - Width = 80, - Height = height, - }, - box = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black - }, - Text = new OsuSpriteText - { - Font = OsuFont.Numeric.With(size: 10), - Margin = new MarginPadding(5), - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Text = keyBinding.KeyCombination.ReadableString(), - }, - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - hoverColour = colours.YellowDark; - } - - protected override bool OnHover(HoverEvent e) - { - updateHoverState(); - return base.OnHover(e); - } - - protected override void OnHoverLost(HoverLostEvent e) - { - updateHoverState(); - base.OnHoverLost(e); - } - - private void updateHoverState() - { - if (isBinding) - { - box.FadeColour(Color4.White, transition_time, Easing.OutQuint); - Text.FadeColour(Color4.Black, transition_time, Easing.OutQuint); - } - else - { - box.FadeColour(IsHovered ? hoverColour : Color4.Black, transition_time, Easing.OutQuint); - Text.FadeColour(IsHovered ? Color4.Black : Color4.White, transition_time, Easing.OutQuint); - } - } - - public void UpdateKeyCombination(KeyCombination newCombination) - { - KeyBinding.KeyCombination = newCombination; - Text.Text = KeyBinding.KeyCombination.ReadableString(); - } - } - } -} diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs index f799b4810f..216eabcf67 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs @@ -1,64 +1,460 @@ -// 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 System.Collections.Generic; using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Extensions; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; +using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Bindings; -using osu.Game.Rulesets; +using osu.Framework.Input.Events; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Input; +using osuTK; +using osuTK.Graphics; +using osuTK.Input; namespace osu.Game.Overlays.KeyBinding { - public class KeyBindingRow : Container, IFilterable + public class KeyBindingRow : Container { - private readonly object key; - private readonly ICollection bindings; - public readonly BasicKeyBindingRow BasicKeyBindingRow; + private readonly object action; + private readonly IEnumerable bindings; - private bool matchingFilter; + private const float transition_time = 150; - public bool MatchingFilter + private const float height = 20; + + private const float padding = 5; + + private FillFlowContainer cancelAndClearButtons; + private FillFlowContainer buttons; + + public Bindable IsDefault { get; } = new BindableBool(true); + + public KeyBindingRow(object action, IEnumerable bindings) { - get => matchingFilter; - set + this.action = action; + this.bindings = bindings; + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + Masking = true; + CornerRadius = padding; + } + + [Resolved] + private KeyBindingStore store { get; set; } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + updateIsDefaultValue(); + + EdgeEffect = new EdgeEffectParameters { - matchingFilter = value; - this.FadeTo(!matchingFilter ? 0 : 1); + Radius = 2, + Colour = colours.YellowDark.Opacity(0), + Type = EdgeEffectType.Shadow, + Hollow = true, + }; + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + Alpha = 0.6f, + }, + new OsuSpriteText + { + Text = action.GetDescription(), + Margin = new MarginPadding(padding), + }, + buttons = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight + }, + cancelAndClearButtons = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Padding = new MarginPadding(padding) { Top = height + padding * 2 }, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Alpha = 0, + Spacing = new Vector2(5), + Children = new Drawable[] + { + new CancelButton { Action = finalise }, + new ClearButton { Action = clear }, + }, + } + }; + foreach (var b in bindings) + buttons.Add(new KeyButton(b)); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + IsDefault.BindValueChanged(isDefault => + { + if (isDefault.NewValue) + { + finalise(); + } + }); + } + + public void RestoreDefaults() + { + int i = 0; + + foreach (var d in Defaults) + { + var button = buttons[i++]; + button.UpdateKeyCombination(d); + store.Update(button.KeyBinding); + } + + updateIsDefaultValue(); + } + + protected override bool OnHover(HoverEvent e) + { + FadeEdgeEffectTo(1, transition_time, Easing.OutQuint); + + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + FadeEdgeEffectTo(0, transition_time, Easing.OutQuint); + + base.OnHoverLost(e); + } + + public override bool AcceptsFocus => bindTarget == null; + + private KeyButton bindTarget; + + public bool AllowMainMouseButtons; + + public IEnumerable Defaults; + + private bool isModifier(Key k) => k < Key.F1; + + protected override bool OnClick(ClickEvent e) => true; + + protected override bool OnMouseDown(MouseDownEvent e) + { + if (!HasFocus || !bindTarget.IsHovered) + return base.OnMouseDown(e); + + if (!AllowMainMouseButtons) + { + switch (e.Button) + { + case MouseButton.Left: + case MouseButton.Right: + return true; + } + } + + bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(e.CurrentState)); + return true; + } + + protected override void OnMouseUp(MouseUpEvent e) + { + // don't do anything until the last button is released. + if (!HasFocus || e.HasAnyButtonPressed) + { + base.OnMouseUp(e); + return; + } + + if (bindTarget.IsHovered) + finalise(); + // prevent updating bind target before clear button's action + else if (!cancelAndClearButtons.Any(b => b.IsHovered)) + updateBindTarget(); + } + + protected override bool OnScroll(ScrollEvent e) + { + if (HasFocus) + { + if (bindTarget.IsHovered) + { + bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(e.CurrentState, e.ScrollDelta)); + finalise(); + return true; + } + } + + return base.OnScroll(e); + } + + protected override bool OnKeyDown(KeyDownEvent e) + { + if (!HasFocus) + return false; + + bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(e.CurrentState)); + if (!isModifier(e.Key)) finalise(); + + return true; + } + + protected override void OnKeyUp(KeyUpEvent e) + { + if (!HasFocus) + { + base.OnKeyUp(e); + return; + } + + finalise(); + } + + protected override bool OnJoystickPress(JoystickPressEvent e) + { + if (!HasFocus) + return false; + + bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(e.CurrentState)); + finalise(); + + return true; + } + + protected override void OnJoystickRelease(JoystickReleaseEvent e) + { + if (!HasFocus) + { + base.OnJoystickRelease(e); + return; + } + + finalise(); + } + + protected override bool OnMidiDown(MidiDownEvent e) + { + if (!HasFocus) + return false; + + bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(e.CurrentState)); + finalise(); + + return true; + } + + protected override void OnMidiUp(MidiUpEvent e) + { + if (!HasFocus) + { + base.OnMidiUp(e); + return; + } + + finalise(); + } + + private void clear() + { + if (bindTarget == null) + return; + + bindTarget.UpdateKeyCombination(InputKey.None); + finalise(); + } + + private void finalise() + { + if (bindTarget != null) + { + store.Update(bindTarget.KeyBinding); + + updateIsDefaultValue(); + + bindTarget.IsBinding = false; + Schedule(() => + { + // schedule to ensure we don't instantly get focus back on next OnMouseClick (see AcceptFocus impl.) + bindTarget = null; + }); + } + + if (HasFocus) + GetContainingInputManager().ChangeFocus(null); + + cancelAndClearButtons.FadeOut(300, Easing.OutQuint); + cancelAndClearButtons.BypassAutoSizeAxes |= Axes.Y; + } + + private void updateIsDefaultValue() => IsDefault.Value = computeIsDefaultValue(); + + private bool computeIsDefaultValue() => bindings.Select(b => b.KeyCombination).SequenceEqual(Defaults); + + protected override void OnFocus(FocusEvent e) + { + AutoSizeDuration = 500; + AutoSizeEasing = Easing.OutQuint; + + cancelAndClearButtons.FadeIn(300, Easing.OutQuint); + cancelAndClearButtons.BypassAutoSizeAxes &= ~Axes.Y; + + updateBindTarget(); + base.OnFocus(e); + } + + protected override void OnFocusLost(FocusLostEvent e) + { + finalise(); + base.OnFocusLost(e); + } + + /// + /// Updates the bind target to the currently hovered key button or the first if clicked anywhere else. + /// + private void updateBindTarget() + { + if (bindTarget != null) bindTarget.IsBinding = false; + bindTarget = buttons.FirstOrDefault(b => b.IsHovered) ?? buttons.FirstOrDefault(); + if (bindTarget != null) bindTarget.IsBinding = true; + } + + private class CancelButton : TriangleButton + { + public CancelButton() + { + Text = "Cancel"; + Size = new Vector2(80, 20); } } - public bool FilteringActive { get; set; } - - public IEnumerable FilterTerms => bindings.Select(b => b.KeyCombination.ReadableString()).Prepend(key.ToString()); - - public KeyBindingRow(object key, ICollection bindings, RulesetInfo ruleset, IEnumerable defaults) { - this.key = key; - this.bindings = bindings; - - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - Padding = new MarginPadding { Right = SettingsPanel.CONTENT_MARGINS }; - - InternalChildren = new Drawable[] + public class ClearButton : DangerousTriangleButton + { + public ClearButton() { - new RestoreDefaultValueButton() + Text = "Clear"; + Size = new Vector2(80, 20); + } + } + + public class KeyButton : Container + { + public readonly Framework.Input.Bindings.KeyBinding KeyBinding; + + private readonly Box box; + public readonly OsuSpriteText Text; + + private Color4 hoverColour; + + private bool isBinding; + + public bool IsBinding + { + get => isBinding; + set { - Current = BasicKeyBindingRow.IsDefault, - Action = () => { BasicKeyBindingRow.RestoreDefaults(); } - }, - new FillFlowContainer + if (value == isBinding) return; + + isBinding = value; + + updateHoverState(); + } + } + + public KeyButton(Framework.Input.Bindings.KeyBinding keyBinding) + { + KeyBinding = keyBinding; + + Margin = new MarginPadding(padding); + + // todo: use this in a meaningful way + // var isDefault = keyBinding.Action is Enum; + + Masking = true; + CornerRadius = padding; + + Height = height; + AutoSizeAxes = Axes.X; + + Children = new Drawable[] { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Left = SettingsPanel.CONTENT_MARGINS }, - Child = BasicKeyBindingRow = new BasicKeyBindingRow(key, bindings.Where(b => ((int)b.Action).Equals((int)key))) + new Container { - AllowMainMouseButtons = ruleset != null, - Defaults = defaults - } - }, - }; + AlwaysPresent = true, + Width = 80, + Height = height, + }, + box = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black + }, + Text = new OsuSpriteText + { + Font = OsuFont.Numeric.With(size: 10), + Margin = new MarginPadding(5), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = keyBinding.KeyCombination.ReadableString(), + }, + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + hoverColour = colours.YellowDark; + } + + protected override bool OnHover(HoverEvent e) + { + updateHoverState(); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + updateHoverState(); + base.OnHoverLost(e); + } + + private void updateHoverState() + { + if (isBinding) + { + box.FadeColour(Color4.White, transition_time, Easing.OutQuint); + Text.FadeColour(Color4.Black, transition_time, Easing.OutQuint); + } + else + { + box.FadeColour(IsHovered ? hoverColour : Color4.Black, transition_time, Easing.OutQuint); + Text.FadeColour(IsHovered ? Color4.Black : Color4.White, transition_time, Easing.OutQuint); + } + } + + public void UpdateKeyCombination(KeyCombination newCombination) + { + KeyBinding.KeyCombination = newCombination; + Text.Text = KeyBinding.KeyCombination.ReadableString(); + } } } } diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs b/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs index b1a5895449..fc370bd87e 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs @@ -38,12 +38,12 @@ namespace osu.Game.Overlays.KeyBinding foreach (var defaultGroup in Defaults.GroupBy(d => d.Action)) { // one row per valid action. - Add(new KeyBindingRow(defaultGroup.Key, bindings, Ruleset, defaultGroup.Select(d => d.KeyCombination))); + Add(new RestorableKeyBindingRow(defaultGroup.Key, bindings, Ruleset, defaultGroup.Select(d => d.KeyCombination))); } Add(new ResetButton { - Action = () => Children.OfType().ForEach(k => k.BasicKeyBindingRow.RestoreDefaults()) + Action = () => Children.OfType().ForEach(k => k.BasicKeyBindingRow.RestoreDefaults()) }); } } diff --git a/osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs new file mode 100644 index 0000000000..9bfdda727c --- /dev/null +++ b/osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.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 System.Collections.Generic; +using System.Linq; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input.Bindings; +using osu.Game.Rulesets; + +namespace osu.Game.Overlays.KeyBinding +{ + public class RestorableKeyBindingRow : Container, IFilterable + { + private readonly object key; + private readonly ICollection bindings; + public readonly KeyBindingRow BasicKeyBindingRow; + + private bool matchingFilter; + + public bool MatchingFilter + { + get => matchingFilter; + set + { + matchingFilter = value; + this.FadeTo(!matchingFilter ? 0 : 1); + } + } + + public bool FilteringActive { get; set; } + + public IEnumerable FilterTerms => bindings.Select(b => b.KeyCombination.ReadableString()).Prepend(key.ToString()); + + public RestorableKeyBindingRow(object key, ICollection bindings, RulesetInfo ruleset, IEnumerable defaults) { + this.key = key; + this.bindings = bindings; + + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Padding = new MarginPadding { Right = SettingsPanel.CONTENT_MARGINS }; + + InternalChildren = new Drawable[] + { + new RestoreDefaultValueButton() + { + Current = BasicKeyBindingRow.IsDefault, + Action = () => { BasicKeyBindingRow.RestoreDefaults(); } + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Left = SettingsPanel.CONTENT_MARGINS }, + Child = BasicKeyBindingRow = new KeyBindingRow(key, bindings.Where(b => ((int)b.Action).Equals((int)key))) + { + AllowMainMouseButtons = ruleset != null, + Defaults = defaults + } + }, + }; + } + } +} From d5feb8353d874c00f73b2d79623e47e3457871a4 Mon Sep 17 00:00:00 2001 From: Swords Date: Tue, 25 May 2021 21:37:08 +1000 Subject: [PATCH 458/839] Formatting, renaming --- .../Visual/Settings/TestSceneKeyBindingPanel.cs | 4 ++-- osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs | 2 +- .../Overlays/KeyBinding/RestorableKeyBindingRow.cs | 11 ++++++----- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs index bd3e94dffa..3edba2ddd7 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs @@ -131,7 +131,7 @@ namespace osu.Game.Tests.Visual.Settings AddUntilStep("restore button hidden", () => settingsKeyBindingRow.ChildrenOfType>().First().Alpha == 0); - AddAssert("binding cleared", () => settingsKeyBindingRow.ChildrenOfType().ElementAt(0).KeyBinding.KeyCombination.Equals(settingsKeyBindingRow.BasicKeyBindingRow.Defaults.ElementAt(0))); + AddAssert("binding cleared", () => settingsKeyBindingRow.ChildrenOfType().ElementAt(0).KeyBinding.KeyCombination.Equals(settingsKeyBindingRow.KeyBindingRow.Defaults.ElementAt(0))); } [Test] @@ -160,7 +160,7 @@ namespace osu.Game.Tests.Visual.Settings AddUntilStep("restore button hidden", () => settingsKeyBindingRow.ChildrenOfType>().First().Alpha == 0); - AddAssert("binding cleared", () => settingsKeyBindingRow.ChildrenOfType().ElementAt(0).KeyBinding.KeyCombination.Equals(settingsKeyBindingRow.BasicKeyBindingRow.Defaults.ElementAt(0))); + AddAssert("binding cleared", () => settingsKeyBindingRow.ChildrenOfType().ElementAt(0).KeyBinding.KeyCombination.Equals(settingsKeyBindingRow.KeyBindingRow.Defaults.ElementAt(0))); } [Test] diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs b/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs index fc370bd87e..737c640b5a 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs @@ -43,7 +43,7 @@ namespace osu.Game.Overlays.KeyBinding Add(new ResetButton { - Action = () => Children.OfType().ForEach(k => k.BasicKeyBindingRow.RestoreDefaults()) + Action = () => Children.OfType().ForEach(k => k.KeyBindingRow.RestoreDefaults()) }); } } diff --git a/osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs index 9bfdda727c..2981c77e15 100644 --- a/osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs @@ -14,7 +14,7 @@ namespace osu.Game.Overlays.KeyBinding { private readonly object key; private readonly ICollection bindings; - public readonly KeyBindingRow BasicKeyBindingRow; + public readonly KeyBindingRow KeyBindingRow; private bool matchingFilter; @@ -32,7 +32,8 @@ namespace osu.Game.Overlays.KeyBinding public IEnumerable FilterTerms => bindings.Select(b => b.KeyCombination.ReadableString()).Prepend(key.ToString()); - public RestorableKeyBindingRow(object key, ICollection bindings, RulesetInfo ruleset, IEnumerable defaults) { + public RestorableKeyBindingRow(object key, ICollection bindings, RulesetInfo ruleset, IEnumerable defaults) + { this.key = key; this.bindings = bindings; @@ -44,15 +45,15 @@ namespace osu.Game.Overlays.KeyBinding { new RestoreDefaultValueButton() { - Current = BasicKeyBindingRow.IsDefault, - Action = () => { BasicKeyBindingRow.RestoreDefaults(); } + Current = KeyBindingRow.IsDefault, + Action = () => { KeyBindingRow.RestoreDefaults(); } }, new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Padding = new MarginPadding { Left = SettingsPanel.CONTENT_MARGINS }, - Child = BasicKeyBindingRow = new KeyBindingRow(key, bindings.Where(b => ((int)b.Action).Equals((int)key))) + Child = KeyBindingRow = new KeyBindingRow(key, bindings.Where(b => ((int)b.Action).Equals((int)key))) { AllowMainMouseButtons = ruleset != null, Defaults = defaults From 9c2dca8229b5b956f1715bc67acc5cf42b7460c5 Mon Sep 17 00:00:00 2001 From: Swords Date: Tue, 25 May 2021 21:53:00 +1000 Subject: [PATCH 459/839] Removing redundant argument list --- osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs index 2981c77e15..62a56ab055 100644 --- a/osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs @@ -43,7 +43,7 @@ namespace osu.Game.Overlays.KeyBinding InternalChildren = new Drawable[] { - new RestoreDefaultValueButton() + new RestoreDefaultValueButton { Current = KeyBindingRow.IsDefault, Action = () => { KeyBindingRow.RestoreDefaults(); } From 07a24d2747acc4e38d5a88ca67b290617b76de19 Mon Sep 17 00:00:00 2001 From: Swords Date: Tue, 25 May 2021 23:54:13 +1000 Subject: [PATCH 460/839] Fixing errors --- .../Overlays/KeyBinding/RestorableKeyBindingRow.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs index 62a56ab055..09b2efd7fa 100644 --- a/osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs @@ -41,6 +41,12 @@ namespace osu.Game.Overlays.KeyBinding AutoSizeAxes = Axes.Y; Padding = new MarginPadding { Right = SettingsPanel.CONTENT_MARGINS }; + KeyBindingRow = new KeyBindingRow(key, bindings.Where(b => ((int)b.Action).Equals((int)key))) + { + AllowMainMouseButtons = ruleset != null, + Defaults = defaults + }; + InternalChildren = new Drawable[] { new RestoreDefaultValueButton @@ -53,11 +59,7 @@ namespace osu.Game.Overlays.KeyBinding RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Padding = new MarginPadding { Left = SettingsPanel.CONTENT_MARGINS }, - Child = KeyBindingRow = new KeyBindingRow(key, bindings.Where(b => ((int)b.Action).Equals((int)key))) - { - AllowMainMouseButtons = ruleset != null, - Defaults = defaults - } + Child = KeyBindingRow }, }; } From bdbd64c88d6a9cace3862fcdd45ccb6109d38cda Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Tue, 25 May 2021 21:32:18 +0200 Subject: [PATCH 461/839] Fix sliders being partly outside of the playfield in some cases --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 68 ++++++++++++++++------ 1 file changed, 49 insertions(+), 19 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index ad2f4585f6..98488fef8c 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -98,13 +98,9 @@ namespace osu.Game.Rulesets.Osu.Mods switch (hitObject) { case Slider slider: + shiftNestedObjects(slider, Vector2.Subtract(slider.Position, currentObjectInfo.PositionOriginal)); moveSliderIntoPlayfield(ref slider, ref currentObjectInfo); - var sliderShift = Vector2.Subtract(slider.Position, currentObjectInfo.PositionOriginal); - - foreach (var tick in slider.NestedHitObjects.OfType()) - tick.Position = Vector2.Add(tick.Position, sliderShift); - break; } @@ -158,27 +154,61 @@ namespace osu.Game.Rulesets.Osu.Mods currentObjectInfo.PositionRandomised = position; } + /// + /// Moves the and all necessary nested s into the if they aren't already. + /// private void moveSliderIntoPlayfield(ref Slider slider, ref RandomObjectInfo currentObjectInfo) { - foreach (var controlPoint in slider.Path.ControlPoints) + var oldPos = new Vector2(slider.Position.X, slider.Position.Y); + + // Min. distances from the slider's position to the playfield border + var minMargin = new MarginPadding(0); + + foreach (var hitObject in slider.NestedHitObjects.Where(o => o is SliderTick || o is SliderEndCircle)) { - // Position of controlPoint relative to slider.Position - var pos = controlPoint.Position.Value; + if (!(hitObject is OsuHitObject osuHitObject)) + continue; - var playfieldSize = OsuPlayfield.BASE_SIZE; + var relativePos = Vector2.Subtract(osuHitObject.Position, slider.Position); - if (pos.X + slider.Position.X < 0) - slider.Position = new Vector2(-pos.X, slider.Position.Y); - else if (pos.X + slider.Position.X > playfieldSize.X) - slider.Position = new Vector2(playfieldSize.X - pos.X, slider.Position.Y); - - if (pos.Y + slider.Position.Y < 0) - slider.Position = new Vector2(slider.Position.X, -pos.Y); - else if (pos.Y + slider.Position.Y > playfieldSize.Y) - slider.Position = new Vector2(slider.Position.X, playfieldSize.Y - pos.Y); + minMargin.Left = Math.Max(minMargin.Left, -relativePos.X); + minMargin.Right = Math.Max(minMargin.Right, relativePos.X); + minMargin.Top = Math.Max(minMargin.Top, -relativePos.Y); + minMargin.Bottom = Math.Max(minMargin.Bottom, relativePos.Y); } - currentObjectInfo.EndPositionRandomised = slider.TailCircle.Position; + if (slider.Position.X < minMargin.Left) + slider.Position = new Vector2(minMargin.Left, slider.Position.Y); + else if (slider.Position.X + minMargin.Right > OsuPlayfield.BASE_SIZE.X) + slider.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - minMargin.Right, slider.Position.Y); + + if (slider.Position.Y < minMargin.Top) + slider.Position = new Vector2(slider.Position.X, minMargin.Top); + else if (slider.Position.Y + minMargin.Bottom > OsuPlayfield.BASE_SIZE.Y) + slider.Position = new Vector2(slider.Position.X, OsuPlayfield.BASE_SIZE.Y - minMargin.Bottom); + + currentObjectInfo.PositionRandomised = slider.Position; + currentObjectInfo.EndPositionRandomised = slider.EndPosition; + + var shift = Vector2.Subtract(slider.Position, oldPos); + + shiftNestedObjects(slider, shift); + } + + /// + /// Shifts all nested s and s by the specified shift. + /// + /// whose nested s and s should be shifted + /// The the 's nested s and s should be shifted by + private void shiftNestedObjects(Slider slider, Vector2 shift) + { + foreach (var hitObject in slider.NestedHitObjects.Where(o => o is SliderTick || o is SliderRepeat)) + { + if (!(hitObject is OsuHitObject osuHitObject)) + continue; + + osuHitObject.Position = Vector2.Add(osuHitObject.Position, shift); + } } /// From c85d5513bedb30f407594555b6bdaba3d10a2864 Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Tue, 25 May 2021 21:42:26 +0200 Subject: [PATCH 462/839] Remove redundant parameter and unused setters --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 98488fef8c..0f6b6d1afa 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -162,7 +162,7 @@ namespace osu.Game.Rulesets.Osu.Mods var oldPos = new Vector2(slider.Position.X, slider.Position.Y); // Min. distances from the slider's position to the playfield border - var minMargin = new MarginPadding(0); + var minMargin = new MarginPadding(); foreach (var hitObject in slider.NestedHitObjects.Where(o => o is SliderTick || o is SliderEndCircle)) { @@ -282,10 +282,10 @@ namespace osu.Game.Rulesets.Osu.Mods { internal float AngleRad { get; set; } - internal Vector2 PositionOriginal { get; set; } + internal Vector2 PositionOriginal { get; } internal Vector2 PositionRandomised { get; set; } - internal Vector2 EndPositionOriginal { get; set; } + internal Vector2 EndPositionOriginal { get; } internal Vector2 EndPositionRandomised { get; set; } public RandomObjectInfo(OsuHitObject hitObject) From 9223d85f37d33481fd0704349adaf19880728102 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 13:22:58 +0900 Subject: [PATCH 463/839] Remove all local type update logic from `TaikoBeatmapConverter` I believe the original goal was to keep this in the converter with the idea that samples may not always be hard coupled to the strong/rim states. But for now I think we can assume this coupling is going to continue into the near future, so let's keep all the logic in `TaikoHitObject`. --- .../Beatmaps/TaikoBeatmapConverter.cs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index b51f096d7d..90c99316b1 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -79,8 +79,6 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps // Old osu! used hit sounding to determine various hit type information IList samples = obj.Samples; - bool strong = samples.Any(s => s.Name == HitSampleInfo.HIT_FINISH); - switch (obj) { case IHasDistance distanceData: @@ -94,15 +92,11 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps for (double j = obj.StartTime; j <= obj.StartTime + taikoDuration + tickSpacing / 8; j += tickSpacing) { IList currentSamples = allSamples[i]; - bool isRim = currentSamples.Any(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE); - strong = currentSamples.Any(s => s.Name == HitSampleInfo.HIT_FINISH); yield return new Hit { StartTime = j, - Type = isRim ? HitType.Rim : HitType.Centre, Samples = currentSamples, - IsStrong = strong }; i = (i + 1) % allSamples.Count; @@ -117,7 +111,6 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps { StartTime = obj.StartTime, Samples = obj.Samples, - IsStrong = strong, Duration = taikoDuration, TickRate = beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate == 3 ? 3 : 4 }; @@ -143,16 +136,10 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps default: { - bool isRimDefinition(HitSampleInfo s) => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE; - - bool isRim = samples.Any(isRimDefinition); - yield return new Hit { StartTime = obj.StartTime, - Type = isRim ? HitType.Rim : HitType.Centre, Samples = samples, - IsStrong = strong }; break; From 912748b4280b152f3dda8bb9d49b074d5f6e80e1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 13:24:22 +0900 Subject: [PATCH 464/839] Avoid bindable feedback causing overwrites --- osu.Game.Rulesets.Taiko/Objects/Hit.cs | 35 ------------------- .../Objects/TaikoHitObject.cs | 26 ++++++++++++-- .../Objects/TaikoStrongableHitObject.cs | 6 ++-- 3 files changed, 26 insertions(+), 41 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Hit.cs b/osu.Game.Rulesets.Taiko/Objects/Hit.cs index 8ede21fdad..6b6c04e92e 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Hit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Hit.cs @@ -1,45 +1,10 @@ // 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.Game.Audio; - namespace osu.Game.Rulesets.Taiko.Objects { public class Hit : TaikoStrongableHitObject { - protected override void UpdateTypeFromSamples() - { - base.UpdateTypeFromSamples(); - - Type = getRimSamples().Any() ? HitType.Rim : HitType.Centre; - } - - protected override void UpdateSamplesFromType() - { - base.UpdateSamplesFromType(); - - var rimSamples = getRimSamples(); - - bool isRimType = Type == HitType.Rim; - - if (isRimType != rimSamples.Any()) - { - if (isRimType) - Samples.Add(new HitSampleInfo(HitSampleInfo.HIT_CLAP)); - else - { - foreach (var sample in rimSamples) - Samples.Remove(sample); - } - } - } - - /// - /// Returns an array of any samples which would cause this object to be a "rim" type hit. - /// - private HitSampleInfo[] getRimSamples() => Samples.Where(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE).ToArray(); - protected override StrongNestedHitObject CreateStrongNestedHit(double startTime) => new StrongNestedHit { StartTime = startTime }; public class StrongNestedHit : StrongNestedHitObject diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs index 46b864e7de..71214a4017 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.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.Linq; using osu.Framework.Bindables; +using osu.Game.Audio; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; @@ -35,15 +37,35 @@ namespace osu.Game.Rulesets.Taiko.Objects protected TaikoHitObject() { SamplesBindable.BindCollectionChanged((_, __) => UpdateTypeFromSamples()); - TypeBindable.BindValueChanged(_ => UpdateSamplesFromType()); + TypeBindable.BindValueChanged(_ => updateSamplesFromType()); } - protected virtual void UpdateSamplesFromType() + private void updateSamplesFromType() { + var rimSamples = getRimSamples(); + + bool isRimType = Type == HitType.Rim; + + if (isRimType != rimSamples.Any()) + { + if (isRimType) + Samples.Add(new HitSampleInfo(HitSampleInfo.HIT_CLAP)); + else + { + foreach (var sample in rimSamples) + Samples.Remove(sample); + } + } } protected virtual void UpdateTypeFromSamples() { + Type = getRimSamples().Any() ? HitType.Rim : HitType.Centre; } + + /// + /// Returns an array of any samples which would cause this object to be a "rim" type hit. + /// + private HitSampleInfo[] getRimSamples() => Samples.Where(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE).ToArray(); } } diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs index 237000474d..5cddc00a1e 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Taiko.Objects protected TaikoStrongableHitObject() { - IsStrongBindable.BindValueChanged(_ => UpdateSamplesFromType()); + IsStrongBindable.BindValueChanged(_ => updateSamplesFromType()); } protected override void UpdateTypeFromSamples() @@ -48,10 +48,8 @@ namespace osu.Game.Rulesets.Taiko.Objects IsStrong = getStrongSamples().Any(); } - protected override void UpdateSamplesFromType() + private void updateSamplesFromType() { - base.UpdateSamplesFromType(); - var strongSamples = getStrongSamples(); if (IsStrongBindable.Value != strongSamples.Any()) From cbad7bb7f0d4f58707c737809e9384936a8e476c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 13:40:36 +0900 Subject: [PATCH 465/839] Move taiko `Type` to `Hit` and localise all bind handling --- osu.Game.Rulesets.Taiko/Objects/Hit.cs | 49 +++++++++++++++++++ .../Objects/TaikoHitObject.cs | 48 ------------------ .../Objects/TaikoStrongableHitObject.cs | 5 +- 3 files changed, 51 insertions(+), 51 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Hit.cs b/osu.Game.Rulesets.Taiko/Objects/Hit.cs index 6b6c04e92e..b4ed242893 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Hit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Hit.cs @@ -1,10 +1,59 @@ // 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.Bindables; +using osu.Game.Audio; + namespace osu.Game.Rulesets.Taiko.Objects { public class Hit : TaikoStrongableHitObject { + public readonly Bindable TypeBindable = new Bindable(); + + /// + /// The that actuates this . + /// + public HitType Type + { + get => TypeBindable.Value; + set => TypeBindable.Value = value; + } + + public Hit() + { + TypeBindable.BindValueChanged(_ => updateSamplesFromType()); + SamplesBindable.BindCollectionChanged((_, __) => updateTypeFromSamples()); + } + + private void updateTypeFromSamples() + { + Type = getRimSamples().Any() ? HitType.Rim : HitType.Centre; + } + + /// + /// Returns an array of any samples which would cause this object to be a "rim" type hit. + /// + private HitSampleInfo[] getRimSamples() => Samples.Where(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE).ToArray(); + + private void updateSamplesFromType() + { + var rimSamples = getRimSamples(); + + bool isRimType = Type == HitType.Rim; + + if (isRimType != rimSamples.Any()) + { + if (isRimType) + Samples.Add(new HitSampleInfo(HitSampleInfo.HIT_CLAP)); + else + { + foreach (var sample in rimSamples) + Samples.Remove(sample); + } + } + } + protected override StrongNestedHitObject CreateStrongNestedHit(double startTime) => new StrongNestedHit { StartTime = startTime }; public class StrongNestedHit : StrongNestedHitObject diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs index 71214a4017..f047c03f4b 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs @@ -1,9 +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.Linq; -using osu.Framework.Bindables; -using osu.Game.Audio; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; @@ -14,17 +11,6 @@ namespace osu.Game.Rulesets.Taiko.Objects { public abstract class TaikoHitObject : HitObject { - public readonly Bindable TypeBindable = new Bindable(); - - /// - /// The that actuates this . - /// - public HitType Type - { - get => TypeBindable.Value; - set => TypeBindable.Value = value; - } - /// /// Default size of a drawable taiko hit object. /// @@ -33,39 +19,5 @@ namespace osu.Game.Rulesets.Taiko.Objects public override Judgement CreateJudgement() => new TaikoJudgement(); protected override HitWindows CreateHitWindows() => new TaikoHitWindows(); - - protected TaikoHitObject() - { - SamplesBindable.BindCollectionChanged((_, __) => UpdateTypeFromSamples()); - TypeBindable.BindValueChanged(_ => updateSamplesFromType()); - } - - private void updateSamplesFromType() - { - var rimSamples = getRimSamples(); - - bool isRimType = Type == HitType.Rim; - - if (isRimType != rimSamples.Any()) - { - if (isRimType) - Samples.Add(new HitSampleInfo(HitSampleInfo.HIT_CLAP)); - else - { - foreach (var sample in rimSamples) - Samples.Remove(sample); - } - } - } - - protected virtual void UpdateTypeFromSamples() - { - Type = getRimSamples().Any() ? HitType.Rim : HitType.Centre; - } - - /// - /// Returns an array of any samples which would cause this object to be a "rim" type hit. - /// - private HitSampleInfo[] getRimSamples() => Samples.Where(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE).ToArray(); } } diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs index 5cddc00a1e..6c17573b50 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs @@ -39,12 +39,11 @@ namespace osu.Game.Rulesets.Taiko.Objects protected TaikoStrongableHitObject() { IsStrongBindable.BindValueChanged(_ => updateSamplesFromType()); + SamplesBindable.BindCollectionChanged((_, __) => updateTypeFromSamples()); } - protected override void UpdateTypeFromSamples() + private void updateTypeFromSamples() { - base.UpdateTypeFromSamples(); - IsStrong = getStrongSamples().Any(); } From 200592114f9e20d0f2ede64a70135f50f0319e00 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 14:13:00 +0900 Subject: [PATCH 466/839] Make protected variables private --- .../Containers/Markdown/OsuMarkdownLinkText.cs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownLinkText.cs b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownLinkText.cs index f44f818bf0..840bf77348 100644 --- a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownLinkText.cs +++ b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownLinkText.cs @@ -16,28 +16,29 @@ namespace osu.Game.Graphics.Containers.Markdown [Resolved(canBeNull: true)] private OsuGame game { get; set; } - protected string Text; - protected string Title; + private readonly string text; + private readonly string title; public OsuMarkdownLinkText(string text, LinkInline linkInline) : base(text, linkInline) { - Text = text; - Title = linkInline.Title; + this.text = text; + title = linkInline.Title; } [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider) { - var text = CreateSpriteText().With(t => t.Text = Text); + var textDrawable = CreateSpriteText().With(t => t.Text = text); + InternalChildren = new Drawable[] { - text, - new OsuMarkdownLinkCompiler(new[] { text }) + textDrawable, + new OsuMarkdownLinkCompiler(new[] { textDrawable }) { RelativeSizeAxes = Axes.Both, Action = OnLinkPressed, - TooltipText = Title ?? Url, + TooltipText = title ?? Url, } }; } From 7b09955d59c7538b8ff6d526c71193e9943ead34 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 14:17:40 +0900 Subject: [PATCH 467/839] Remove redundant default bindable value --- osu.Game/Overlays/NewsOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/NewsOverlay.cs b/osu.Game/Overlays/NewsOverlay.cs index 751ac1d10a..d4ccf4970b 100644 --- a/osu.Game/Overlays/NewsOverlay.cs +++ b/osu.Game/Overlays/NewsOverlay.cs @@ -16,7 +16,7 @@ namespace osu.Game.Overlays { public class NewsOverlay : OnlineOverlay { - private readonly Bindable article = new Bindable(null); + private readonly Bindable article = new Bindable(); private readonly Container sidebarContainer; private readonly NewsSidebar sidebar; From 8ffa7f4a5ae5087e7c6f3e3e3f44f012fa95e1e3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 14:28:20 +0900 Subject: [PATCH 468/839] Tidy up code --- osu.Game/Overlays/NewsOverlay.cs | 43 +++++++++++++++++--------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/osu.Game/Overlays/NewsOverlay.cs b/osu.Game/Overlays/NewsOverlay.cs index d4ccf4970b..400505ba52 100644 --- a/osu.Game/Overlays/NewsOverlay.cs +++ b/osu.Game/Overlays/NewsOverlay.cs @@ -6,7 +6,6 @@ using System.Threading; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Overlays.News; using osu.Game.Overlays.News.Displays; @@ -22,9 +21,14 @@ namespace osu.Game.Overlays private readonly NewsSidebar sidebar; private readonly Container content; - private APIRequest lastRequest; + private GetNewsRequest lastRequest; + private Cursor lastCursor; - private int? year; + + /// + /// The year currently being displayed. If null, the main listing is being displayed. + /// + private int? displayedYear; private CancellationTokenSource cancellationToken; @@ -100,7 +104,7 @@ namespace osu.Game.Overlays public void ShowYear(int year) { - loadFrontPage(year); + loadListing(year); Show(); } @@ -130,18 +134,18 @@ namespace osu.Game.Overlays private void onArticleChanged(ValueChangedEvent article) { if (article.NewValue == null) - loadFrontPage(); + loadListing(); else loadArticle(article.NewValue); } - private void loadFrontPage(int? year = null) + private void loadListing(int? year = null) { beginLoading(); Header.SetFrontPage(); - this.year = year; + displayedYear = year; lastCursor = null; performListingRequest(response => @@ -165,19 +169,6 @@ namespace osu.Game.Overlays }); } - private void performListingRequest(Action onSuccess) - { - lastRequest = new GetNewsRequest(year, lastCursor); - - ((GetNewsRequest)lastRequest).Success += response => Schedule(() => - { - lastCursor = response.Cursor; - onSuccess?.Invoke(response); - }); - - API.PerformAsync(lastRequest); - } - private void loadArticle(string article) { beginLoading(); @@ -188,6 +179,18 @@ namespace osu.Game.Overlays LoadDisplay(Empty()); } + private void performListingRequest(Action onSuccess) + { + lastRequest = new GetNewsRequest(displayedYear, lastCursor); + lastRequest.Success += response => Schedule(() => + { + lastCursor = response.Cursor; + onSuccess?.Invoke(response); + }); + + API.PerformAsync(lastRequest); + } + private void beginLoading() { lastRequest?.Cancel(); From 0f21510b8bc3a57347027c19ab9ecca306e32637 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 14:29:46 +0900 Subject: [PATCH 469/839] Move code around --- osu.Game/Overlays/NewsOverlay.cs | 36 +++++++++++++++----------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/osu.Game/Overlays/NewsOverlay.cs b/osu.Game/Overlays/NewsOverlay.cs index 400505ba52..ede9432a17 100644 --- a/osu.Game/Overlays/NewsOverlay.cs +++ b/osu.Game/Overlays/NewsOverlay.cs @@ -74,7 +74,13 @@ namespace osu.Game.Overlays base.LoadComplete(); // should not be run until first pop-in to avoid requesting data before user views. - article.BindValueChanged(onArticleChanged); + article.BindValueChanged(a => + { + if (a.NewValue == null) + loadListing(); + else + loadArticle(a.NewValue); + }); } protected override NewsHeader CreateHeader() => new NewsHeader { ShowFrontPage = ShowFrontPage }; @@ -131,14 +137,6 @@ namespace osu.Game.Overlays sidebarContainer.Y = Math.Clamp(ScrollFlow.Current - Header.DrawHeight, 0, Math.Max(ScrollFlow.ScrollContent.DrawHeight - DrawHeight - Header.DrawHeight, 0)); } - private void onArticleChanged(ValueChangedEvent article) - { - if (article.NewValue == null) - loadListing(); - else - loadArticle(article.NewValue); - } - private void loadListing(int? year = null) { beginLoading(); @@ -159,16 +157,6 @@ namespace osu.Game.Overlays }); } - private void getMorePosts() - { - lastRequest?.Cancel(); - performListingRequest(response => - { - if (content.Child is ArticleListing listing) - listing.AddPosts(response); - }); - } - private void loadArticle(string article) { beginLoading(); @@ -179,6 +167,16 @@ namespace osu.Game.Overlays LoadDisplay(Empty()); } + private void getMorePosts() + { + lastRequest?.Cancel(); + performListingRequest(response => + { + if (content.Child is ArticleListing listing) + listing.AddPosts(response); + }); + } + private void performListingRequest(Action onSuccess) { lastRequest = new GetNewsRequest(displayedYear, lastCursor); From d165a758233d355f9fc9e70ad94740da2d955ce1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 14:37:52 +0900 Subject: [PATCH 470/839] Inline request flow to make it easier to understand --- osu.Game/Overlays/NewsOverlay.cs | 60 +++++++++++++++----------------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/osu.Game/Overlays/NewsOverlay.cs b/osu.Game/Overlays/NewsOverlay.cs index ede9432a17..8d0d242e39 100644 --- a/osu.Game/Overlays/NewsOverlay.cs +++ b/osu.Game/Overlays/NewsOverlay.cs @@ -21,7 +21,7 @@ namespace osu.Game.Overlays private readonly NewsSidebar sidebar; private readonly Container content; - private GetNewsRequest lastRequest; + private GetNewsRequest request; private Cursor lastCursor; @@ -139,66 +139,64 @@ namespace osu.Game.Overlays private void loadListing(int? year = null) { - beginLoading(); - Header.SetFrontPage(); displayedYear = year; lastCursor = null; - performListingRequest(response => + beginLoading(true); + + request = new GetNewsRequest(displayedYear); + request.Success += response => Schedule(() => { + lastCursor = response.Cursor; sidebar.Metadata.Value = response.SidebarMetadata; - var listing = new ArticleListing(response); - listing.RequestMorePosts += getMorePosts; - - LoadDisplay(listing); + LoadDisplay(new ArticleListing(response) + { + RequestMorePosts = getMorePosts + }); }); - } - private void loadArticle(string article) - { - beginLoading(); - - Header.SetArticle(article); - - // Temporary, should be handled by ArticleDisplay later - LoadDisplay(Empty()); + API.PerformAsync(request); } private void getMorePosts() { - lastRequest?.Cancel(); - performListingRequest(response => + beginLoading(false); + + request = new GetNewsRequest(displayedYear, lastCursor); + request.Success += response => Schedule(() => { + lastCursor = response.Cursor; if (content.Child is ArticleListing listing) listing.AddPosts(response); }); + + API.PerformAsync(request); } - private void performListingRequest(Action onSuccess) + private void loadArticle(string article) { - lastRequest = new GetNewsRequest(displayedYear, lastCursor); - lastRequest.Success += response => Schedule(() => - { - lastCursor = response.Cursor; - onSuccess?.Invoke(response); - }); + // This is not yet implemented nor called from anywhere. + beginLoading(true); - API.PerformAsync(lastRequest); + Header.SetArticle(article); + LoadDisplay(Empty()); } - private void beginLoading() + private void beginLoading(bool showLoadingOverlay) { - lastRequest?.Cancel(); + request?.Cancel(); cancellationToken?.Cancel(); - Loading.Show(); + + if (showLoadingOverlay) + Loading.Show(); } protected override void Dispose(bool isDisposing) { - lastRequest?.Cancel(); + request?.Cancel(); cancellationToken?.Cancel(); base.Dispose(isDisposing); } From e4780abdfddf1642ff454e2fcad4e882adbe58e7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 14:43:59 +0900 Subject: [PATCH 471/839] Split out `base` call from `switch` statement --- osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs index d4ad0bee4d..b9037a5c77 100644 --- a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs +++ b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs @@ -23,12 +23,10 @@ namespace osu.Game.Overlays.Wiki.Markdown { case YamlFrontMatterBlock yamlFrontMatterBlock: container.Add(CreateNotice(yamlFrontMatterBlock)); - break; - - default: - base.AddMarkdownComponent(markdownObject, container, level); - break; + return; } + + base.AddMarkdownComponent(markdownObject, container, level); } public override MarkdownTextFlowContainer CreateTextFlow() => new WikiMarkdownTextFlowContainer(); From ba8544f614570a565531fbcb04637bcacc66eb9e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 14:55:44 +0900 Subject: [PATCH 472/839] Update resources --- 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 b3842a528d..f7ad06f5ca 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index e3331cd365..a9152b8cb8 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -35,7 +35,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index e35b1b5c42..a0894bc86a 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -71,7 +71,7 @@ - + From b36b40cb3430e6bd511390f82c39ba23195dc8cb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 15:20:47 +0900 Subject: [PATCH 473/839] Remove unnecessary double specification --- osu.Game/Skinning/Editor/SkinSelectionHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index 7ef07541b4..9cca0ba2c7 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -60,7 +60,7 @@ namespace osu.Game.Skinning.Editor var selectionRect = getSelectionQuad().AABBFloat; // If the selection has no area we cannot scale it - if (selectionRect.Area == 0.0) + if (selectionRect.Area == 0) return false; // copy to mutate, as we will need to compare to the original later on. From b3b39c4c137d0068c188fb3c8b52d0e8739930b8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 15:42:26 +0900 Subject: [PATCH 474/839] Fix `BeatmapCarousel` accessing `ScreenSpaceDrawQuad` of non-loaded children Fixes failure seen at https://ci.appveyor.com/project/peppy/osu/builds/39302762/tests. --- osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index a3fca3d4e1..5875685965 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -36,7 +36,7 @@ namespace osu.Game.Screens.Select.Carousel [Resolved(CanBeNull = true)] private ManageCollectionsDialog manageCollectionsDialog { get; set; } - public IEnumerable DrawableBeatmaps => beatmapContainer?.Children ?? Enumerable.Empty(); + public IEnumerable DrawableBeatmaps => beatmapContainer?.AliveChildren ?? Enumerable.Empty(); [CanBeNull] private Container beatmapContainer; From 04f16c07836e57ea8ae8ef571ef2769c86fba054 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Wed, 26 May 2021 13:55:16 +0700 Subject: [PATCH 475/839] Set `DocumentUrl` inside `CreateChildDependencies` Co-authored-by: Dean Herbert --- .../Graphics/Containers/Markdown/OsuMarkdownContainer.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownContainer.cs b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownContainer.cs index 9ecedba59a..ad11a9625e 100644 --- a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownContainer.cs +++ b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownContainer.cs @@ -23,10 +23,14 @@ namespace osu.Game.Graphics.Containers.Markdown LineSpacing = 21; } - [BackgroundDependencyLoader] - private void load(IAPIProvider api) + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { + var api = parent.Get(); + + // needs to be set before the base BDL call executes to avoid invalidating any already populated markdown content. DocumentUrl = api.WebsiteRootUrl; + + return base.CreateChildDependencies(parent); } protected override void AddMarkdownComponent(IMarkdownObject markdownObject, FillFlowContainer container, int level) From e02739a13608866df8391f515b7ab8f31e4d6f6b Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Wed, 26 May 2021 13:57:35 +0700 Subject: [PATCH 476/839] remove unused colour provider --- osu.Game/Graphics/Containers/Markdown/OsuMarkdownLinkText.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownLinkText.cs b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownLinkText.cs index 840bf77348..f91a0e40e3 100644 --- a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownLinkText.cs +++ b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownLinkText.cs @@ -27,7 +27,7 @@ namespace osu.Game.Graphics.Containers.Markdown } [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) + private void load() { var textDrawable = CreateSpriteText().With(t => t.Text = text); From c5ff05209602584c388c8263b65395a0b8f1d22f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 16:31:25 +0900 Subject: [PATCH 477/839] Change `internal` to `public` --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 0f6b6d1afa..b81e9fe3c5 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -280,13 +280,13 @@ namespace osu.Game.Rulesets.Osu.Mods private struct RandomObjectInfo { - internal float AngleRad { get; set; } + public float AngleRad { get; set; } - internal Vector2 PositionOriginal { get; } - internal Vector2 PositionRandomised { get; set; } + public Vector2 PositionOriginal { get; } + public Vector2 PositionRandomised { get; set; } - internal Vector2 EndPositionOriginal { get; } - internal Vector2 EndPositionRandomised { get; set; } + public Vector2 EndPositionOriginal { get; } + public Vector2 EndPositionRandomised { get; set; } public RandomObjectInfo(OsuHitObject hitObject) { From 6181b1ac92302f5c8bae6f93ff2091da5cf81bde Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 16:36:14 +0900 Subject: [PATCH 478/839] Simplify previous object handling by using a class instead of struct --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 31 +++++++++++----------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index b81e9fe3c5..d1d1d24f1b 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Osu.Mods var rng = new Random((int)Seed.Value); - RandomObjectInfo? prevObjectInfo = null; + RandomObjectInfo prevObjectInfo = null; float rateOfChangeMultiplier = 0; @@ -70,24 +70,25 @@ namespace osu.Game.Rulesets.Osu.Mods var currentObjectInfo = new RandomObjectInfo(hitObject); - if (i > 0 && hitObjects[i - 1] is Spinner) - prevObjectInfo = null; - else if (prevObjectInfo != null) - distanceToPrev = Vector2.Distance(((RandomObjectInfo)prevObjectInfo).EndPositionOriginal, currentObjectInfo.PositionOriginal); + if (prevObjectInfo != null) + distanceToPrev = Vector2.Distance(prevObjectInfo.EndPositionOriginal, currentObjectInfo.PositionOriginal); // rateOfChangeMultiplier only changes every i iterations to prevent shaky-line-shaped streams if (i % 3 == 0) rateOfChangeMultiplier = (float)rng.NextDouble() * 2 - 1; if (hitObject is Spinner) + { + prevObjectInfo = null; continue; + } applyRandomisation( rng, rateOfChangeMultiplier, prevObjectInfo, distanceToPrev, - ref currentObjectInfo + currentObjectInfo ); hitObject.Position = currentObjectInfo.PositionRandomised; @@ -99,7 +100,7 @@ namespace osu.Game.Rulesets.Osu.Mods { case Slider slider: shiftNestedObjects(slider, Vector2.Subtract(slider.Position, currentObjectInfo.PositionOriginal)); - moveSliderIntoPlayfield(ref slider, ref currentObjectInfo); + moveSliderIntoPlayfield(slider, currentObjectInfo); break; } @@ -112,9 +113,9 @@ namespace osu.Game.Rulesets.Osu.Mods /// Returns the final position of the hit object /// /// Final position of the hit object - private void applyRandomisation(Random rng, float rateOfChangeMultiplier, RandomObjectInfo? prevObjectInfoNullable, float distanceToPrev, ref RandomObjectInfo currentObjectInfo) + private void applyRandomisation(float rateOfChangeMultiplier, RandomObjectInfo prevObject, float distanceToPrev, RandomObjectInfo currentObjectInfo) { - if (prevObjectInfoNullable == null) + if (prevObject == null) { var playfieldSize = OsuPlayfield.BASE_SIZE; @@ -124,14 +125,12 @@ namespace osu.Game.Rulesets.Osu.Mods return; } - var prevObjectInfo = (RandomObjectInfo)prevObjectInfoNullable; - // The max. angle (relative to the angle of the vector pointing from the 2nd last to the last hit object) // is proportional to the distance between the last and the current hit object // to allow jumps and prevent too sharp turns during streams. var randomAngleRad = rateOfChangeMultiplier * 2 * Math.PI * distanceToPrev / playfield_diagonal; - currentObjectInfo.AngleRad = (float)randomAngleRad + prevObjectInfo.AngleRad; + currentObjectInfo.AngleRad = (float)randomAngleRad + prevObject.AngleRad; if (currentObjectInfo.AngleRad < 0) currentObjectInfo.AngleRad += 2 * (float)Math.PI; @@ -140,11 +139,11 @@ namespace osu.Game.Rulesets.Osu.Mods distanceToPrev * (float)Math.Sin(currentObjectInfo.AngleRad) ); - posRelativeToPrev = getRotatedVector(prevObjectInfo.EndPositionRandomised, posRelativeToPrev); + posRelativeToPrev = getRotatedVector(prevObject.EndPositionRandomised, posRelativeToPrev); currentObjectInfo.AngleRad = (float)Math.Atan2(posRelativeToPrev.Y, posRelativeToPrev.X); - var position = Vector2.Add(prevObjectInfo.EndPositionRandomised, posRelativeToPrev); + var position = Vector2.Add(prevObject.EndPositionRandomised, posRelativeToPrev); // Move hit objects back into the playfield if they are outside of it, // which would sometimes happen during big jumps otherwise. @@ -157,7 +156,7 @@ namespace osu.Game.Rulesets.Osu.Mods /// /// Moves the and all necessary nested s into the if they aren't already. /// - private void moveSliderIntoPlayfield(ref Slider slider, ref RandomObjectInfo currentObjectInfo) + private void moveSliderIntoPlayfield(Slider slider, RandomObjectInfo currentObjectInfo) { var oldPos = new Vector2(slider.Position.X, slider.Position.Y); @@ -278,7 +277,7 @@ namespace osu.Game.Rulesets.Osu.Mods ); } - private struct RandomObjectInfo + private class RandomObjectInfo { public float AngleRad { get; set; } From 6ca9b37c28279e05d599903047c2be4484a28ce6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 16:37:30 +0900 Subject: [PATCH 479/839] Make random generator a field to avoid passing around internally --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index d1d1d24f1b..c53c262ffb 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -40,6 +40,8 @@ namespace osu.Game.Rulesets.Osu.Mods private static readonly float playfield_diagonal = OsuPlayfield.BASE_SIZE.LengthFast; + private Random rng; + [SettingSource("Seed", "Use a custom seed instead of a random one", SettingControlType = typeof(OsuModRandomSettingsControl))] public Bindable Seed { get; } = new Bindable { @@ -56,7 +58,7 @@ namespace osu.Game.Rulesets.Osu.Mods Seed.Value ??= RNG.Next(); - var rng = new Random((int)Seed.Value); + rng = new Random((int)Seed.Value); RandomObjectInfo prevObjectInfo = null; @@ -83,9 +85,7 @@ namespace osu.Game.Rulesets.Osu.Mods continue; } - applyRandomisation( - rng, - rateOfChangeMultiplier, + applyRandomisation(rateOfChangeMultiplier, prevObjectInfo, distanceToPrev, currentObjectInfo From ad3e4287cd24da058cff136293b3b7913b68730c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 16:44:05 +0900 Subject: [PATCH 480/839] Move `distanceToPrev` inside randomisation function --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 39 +++++++++------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index c53c262ffb..1c59569517 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -66,15 +66,10 @@ namespace osu.Game.Rulesets.Osu.Mods for (int i = 0; i < hitObjects.Count; i++) { - var distanceToPrev = 0f; - var hitObject = hitObjects[i]; var currentObjectInfo = new RandomObjectInfo(hitObject); - if (prevObjectInfo != null) - distanceToPrev = Vector2.Distance(prevObjectInfo.EndPositionOriginal, currentObjectInfo.PositionOriginal); - // rateOfChangeMultiplier only changes every i iterations to prevent shaky-line-shaped streams if (i % 3 == 0) rateOfChangeMultiplier = (float)rng.NextDouble() * 2 - 1; @@ -85,11 +80,7 @@ namespace osu.Game.Rulesets.Osu.Mods continue; } - applyRandomisation(rateOfChangeMultiplier, - prevObjectInfo, - distanceToPrev, - currentObjectInfo - ); + applyRandomisation(rateOfChangeMultiplier, prevObjectInfo, currentObjectInfo); hitObject.Position = currentObjectInfo.PositionRandomised; @@ -113,44 +104,46 @@ namespace osu.Game.Rulesets.Osu.Mods /// Returns the final position of the hit object ///
/// Final position of the hit object - private void applyRandomisation(float rateOfChangeMultiplier, RandomObjectInfo prevObject, float distanceToPrev, RandomObjectInfo currentObjectInfo) + private void applyRandomisation(float rateOfChangeMultiplier, RandomObjectInfo previous, RandomObjectInfo current) { - if (prevObject == null) + if (previous == null) { var playfieldSize = OsuPlayfield.BASE_SIZE; - currentObjectInfo.AngleRad = (float)(rng.NextDouble() * 2 * Math.PI - Math.PI); - currentObjectInfo.PositionRandomised = new Vector2((float)rng.NextDouble() * playfieldSize.X, (float)rng.NextDouble() * playfieldSize.Y); + current.AngleRad = (float)(rng.NextDouble() * 2 * Math.PI - Math.PI); + current.PositionRandomised = new Vector2((float)rng.NextDouble() * playfieldSize.X, (float)rng.NextDouble() * playfieldSize.Y); return; } + float distanceToPrev = Vector2.Distance(previous.EndPositionOriginal, current.PositionOriginal); + // The max. angle (relative to the angle of the vector pointing from the 2nd last to the last hit object) // is proportional to the distance between the last and the current hit object // to allow jumps and prevent too sharp turns during streams. var randomAngleRad = rateOfChangeMultiplier * 2 * Math.PI * distanceToPrev / playfield_diagonal; - currentObjectInfo.AngleRad = (float)randomAngleRad + prevObject.AngleRad; - if (currentObjectInfo.AngleRad < 0) - currentObjectInfo.AngleRad += 2 * (float)Math.PI; + current.AngleRad = (float)randomAngleRad + previous.AngleRad; + if (current.AngleRad < 0) + current.AngleRad += 2 * (float)Math.PI; var posRelativeToPrev = new Vector2( - distanceToPrev * (float)Math.Cos(currentObjectInfo.AngleRad), - distanceToPrev * (float)Math.Sin(currentObjectInfo.AngleRad) + distanceToPrev * (float)Math.Cos(current.AngleRad), + distanceToPrev * (float)Math.Sin(current.AngleRad) ); - posRelativeToPrev = getRotatedVector(prevObject.EndPositionRandomised, posRelativeToPrev); + posRelativeToPrev = getRotatedVector(previous.EndPositionRandomised, posRelativeToPrev); - currentObjectInfo.AngleRad = (float)Math.Atan2(posRelativeToPrev.Y, posRelativeToPrev.X); + current.AngleRad = (float)Math.Atan2(posRelativeToPrev.Y, posRelativeToPrev.X); - var position = Vector2.Add(prevObject.EndPositionRandomised, posRelativeToPrev); + var position = Vector2.Add(previous.EndPositionRandomised, posRelativeToPrev); // Move hit objects back into the playfield if they are outside of it, // which would sometimes happen during big jumps otherwise. position.X = MathHelper.Clamp(position.X, 0, OsuPlayfield.BASE_SIZE.X); position.Y = MathHelper.Clamp(position.Y, 0, OsuPlayfield.BASE_SIZE.Y); - currentObjectInfo.PositionRandomised = position; + current.PositionRandomised = position; } /// From d6c4be207b05530b39fc520cb03a28f7c082680b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 16:44:44 +0900 Subject: [PATCH 481/839] Simplify naming --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 1c59569517..a9aeba99fa 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -60,7 +60,7 @@ namespace osu.Game.Rulesets.Osu.Mods rng = new Random((int)Seed.Value); - RandomObjectInfo prevObjectInfo = null; + RandomObjectInfo previous = null; float rateOfChangeMultiplier = 0; @@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Osu.Mods { var hitObject = hitObjects[i]; - var currentObjectInfo = new RandomObjectInfo(hitObject); + var current = new RandomObjectInfo(hitObject); // rateOfChangeMultiplier only changes every i iterations to prevent shaky-line-shaped streams if (i % 3 == 0) @@ -76,27 +76,27 @@ namespace osu.Game.Rulesets.Osu.Mods if (hitObject is Spinner) { - prevObjectInfo = null; + previous = null; continue; } - applyRandomisation(rateOfChangeMultiplier, prevObjectInfo, currentObjectInfo); + applyRandomisation(rateOfChangeMultiplier, previous, current); - hitObject.Position = currentObjectInfo.PositionRandomised; + hitObject.Position = current.PositionRandomised; // update end position as it may have changed as a result of the position update. - currentObjectInfo.EndPositionRandomised = currentObjectInfo.PositionRandomised; + current.EndPositionRandomised = current.PositionRandomised; switch (hitObject) { case Slider slider: - shiftNestedObjects(slider, Vector2.Subtract(slider.Position, currentObjectInfo.PositionOriginal)); - moveSliderIntoPlayfield(slider, currentObjectInfo); + shiftNestedObjects(slider, Vector2.Subtract(slider.Position, current.PositionOriginal)); + moveSliderIntoPlayfield(slider, current); break; } - prevObjectInfo = currentObjectInfo; + previous = current; } } From a08a4aa9111448432fb708fdc17a8014f66a1c59 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 16:48:16 +0900 Subject: [PATCH 482/839] Move second call to `shiftNestedObjects` to a more understandable location --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index a9aeba99fa..58ace92905 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -91,8 +91,13 @@ namespace osu.Game.Rulesets.Osu.Mods { case Slider slider: shiftNestedObjects(slider, Vector2.Subtract(slider.Position, current.PositionOriginal)); + + var oldPos = new Vector2(slider.Position.X, slider.Position.Y); + moveSliderIntoPlayfield(slider, current); + shiftNestedObjects(slider, Vector2.Subtract(slider.Position, oldPos)); + break; } @@ -151,8 +156,6 @@ namespace osu.Game.Rulesets.Osu.Mods /// private void moveSliderIntoPlayfield(Slider slider, RandomObjectInfo currentObjectInfo) { - var oldPos = new Vector2(slider.Position.X, slider.Position.Y); - // Min. distances from the slider's position to the playfield border var minMargin = new MarginPadding(); @@ -181,10 +184,6 @@ namespace osu.Game.Rulesets.Osu.Mods currentObjectInfo.PositionRandomised = slider.Position; currentObjectInfo.EndPositionRandomised = slider.EndPosition; - - var shift = Vector2.Subtract(slider.Position, oldPos); - - shiftNestedObjects(slider, shift); } /// From eeb6647bc50986ff123e483888df59f1af4b721b Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Wed, 26 May 2021 14:59:36 +0700 Subject: [PATCH 483/839] remove schedule in set current path --- osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs index b9037a5c77..dfec437fe4 100644 --- a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs +++ b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs @@ -14,7 +14,7 @@ namespace osu.Game.Overlays.Wiki.Markdown { public string CurrentPath { - set => Schedule(() => DocumentUrl += $"wiki/{value}"); + set => DocumentUrl = $"{DocumentUrl}wiki/{value}"; } protected override void AddMarkdownComponent(IMarkdownObject markdownObject, FillFlowContainer container, int level) From 47cbbee4d12760107bfe8c1294c7035224759b01 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Wed, 26 May 2021 15:01:16 +0700 Subject: [PATCH 484/839] remove CreateNotice method and move implementation to local --- osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs index dfec437fe4..fbfdc5feaf 100644 --- a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs +++ b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs @@ -22,7 +22,7 @@ namespace osu.Game.Overlays.Wiki.Markdown switch (markdownObject) { case YamlFrontMatterBlock yamlFrontMatterBlock: - container.Add(CreateNotice(yamlFrontMatterBlock)); + container.Add(new WikiNoticeContainer(yamlFrontMatterBlock)); return; } @@ -33,8 +33,6 @@ namespace osu.Game.Overlays.Wiki.Markdown protected override MarkdownParagraph CreateParagraph(ParagraphBlock paragraphBlock, int level) => new WikiMarkdownParagraph(paragraphBlock); - protected virtual FillFlowContainer CreateNotice(YamlFrontMatterBlock yamlFrontMatterBlock) => new WikiNoticeContainer(yamlFrontMatterBlock); - private class WikiMarkdownTextFlowContainer : OsuMarkdownTextFlowContainer { protected override void AddImage(LinkInline linkInline) => AddDrawable(new WikiMarkdownImage(linkInline)); From f8a3a3779721ec862d6e920aa5ede5f57ff90f11 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 May 2021 17:34:17 +0900 Subject: [PATCH 485/839] Remove outdated comment --- osu.Game/Overlays/KeyBinding/KeyBindingRow.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs index 546049ea9b..5a2a9baf44 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs @@ -386,9 +386,6 @@ namespace osu.Game.Overlays.KeyBinding Margin = new MarginPadding(padding); - // todo: use this in a meaningful way - // var isDefault = keyBinding.Action is Enum; - Masking = true; CornerRadius = padding; From 71f77eb902c8e680b31f1ae39e2d68e28de9f3b4 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Wed, 26 May 2021 15:04:04 +0700 Subject: [PATCH 486/839] fix image test --- .../Visual/Online/TestSceneWikiMarkdownContainer.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs index 67030631b0..ebabfc9479 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs @@ -106,6 +106,7 @@ needs_cleanup: true { AddStep("Add absolute image", () => { + markdownContainer.DocumentUrl = "https://osu.ppy.sh"; markdownContainer.Text = "![intro](/wiki/Interface/img/intro-screen.jpg)"; }); } @@ -115,6 +116,7 @@ needs_cleanup: true { AddStep("Add relative image", () => { + markdownContainer.DocumentUrl = "https://osu.ppy.sh"; markdownContainer.CurrentPath = "Interface/"; markdownContainer.Text = "![intro](img/intro-screen.jpg)"; }); @@ -125,6 +127,7 @@ needs_cleanup: true { AddStep("Add paragraph with block image", () => { + markdownContainer.DocumentUrl = "https://osu.ppy.sh"; markdownContainer.CurrentPath = "Interface/"; markdownContainer.Text = @"Line before image @@ -139,6 +142,7 @@ Line after image"; { AddStep("Add inline image", () => { + markdownContainer.DocumentUrl = "https://osu.ppy.sh"; markdownContainer.Text = "![osu! mode icon](/wiki/shared/mode/osu.png) osu!"; }); } @@ -147,6 +151,11 @@ Line after image"; { public LinkInline Link; + public new string DocumentUrl + { + set => base.DocumentUrl = value; + } + public override MarkdownTextFlowContainer CreateTextFlow() => new TestMarkdownTextFlowContainer { UrlAdded = link => Link = link, @@ -162,6 +171,8 @@ Line after image"; UrlAdded?.Invoke(linkInline); } + + protected override void AddImage(LinkInline linkInline) => AddDrawable(new WikiMarkdownImage(linkInline)); } } } From 49b4a6ea67babd011e9f815b04d2ac60884c5c3d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 17:07:24 +0900 Subject: [PATCH 487/839] Replace local namespace qualifiers with `using` --- osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs index 09b2efd7fa..70b4fabd6d 100644 --- a/osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs @@ -6,6 +6,7 @@ using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Bindings; +using osu.Game.Input.Bindings; using osu.Game.Rulesets; namespace osu.Game.Overlays.KeyBinding @@ -13,7 +14,7 @@ namespace osu.Game.Overlays.KeyBinding public class RestorableKeyBindingRow : Container, IFilterable { private readonly object key; - private readonly ICollection bindings; + private readonly ICollection bindings; public readonly KeyBindingRow KeyBindingRow; private bool matchingFilter; @@ -32,7 +33,7 @@ namespace osu.Game.Overlays.KeyBinding public IEnumerable FilterTerms => bindings.Select(b => b.KeyCombination.ReadableString()).Prepend(key.ToString()); - public RestorableKeyBindingRow(object key, ICollection bindings, RulesetInfo ruleset, IEnumerable defaults) + public RestorableKeyBindingRow(object key, ICollection bindings, RulesetInfo ruleset, IEnumerable defaults) { this.key = key; this.bindings = bindings; From 9c31b8856d8d4dc2d91a344249f02590c7809e61 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Wed, 26 May 2021 15:10:09 +0700 Subject: [PATCH 488/839] change image url replace implementation --- .../Overlays/Wiki/Markdown/WikiMarkdownImage.cs | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownImage.cs b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownImage.cs index 361aa2e95f..c2115efeb5 100644 --- a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownImage.cs +++ b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownImage.cs @@ -2,37 +2,28 @@ // See the LICENCE file in the repository root for full licence text. using Markdig.Syntax.Inlines; -using osu.Framework.Allocation; -using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers.Markdown; using osu.Framework.Graphics.Cursor; -using osu.Game.Online.API; namespace osu.Game.Overlays.Wiki.Markdown { public class WikiMarkdownImage : MarkdownImage, IHasTooltip { - private readonly string url; - public string TooltipText { get; } public WikiMarkdownImage(LinkInline linkInline) : base(linkInline.Url) { - url = linkInline.Url; TooltipText = linkInline.Title; } - [BackgroundDependencyLoader] - private void load(IAPIProvider api) + protected override ImageContainer CreateImageContainer(string url) { - // The idea is replace "{api.WebsiteRootUrl}/wiki/{path-to-image}" to "{api.WebsiteRootUrl}/wiki/images/{path-to-image}" + // The idea is replace "https://website.url/wiki/{path-to-image}" to "https://website.url/wiki/images/{path-to-image}" // "/wiki/images/*" is route to fetch wiki image from osu!web server (see: https://github.com/ppy/osu-web/blob/4205eb66a4da86bdee7835045e4bf28c35456e04/routes/web.php#L289) - // Currently all image in dev server (https://dev.ppy.sh/wiki/image/*) is 404 - // So for now just replace "{api.WebsiteRootUrl}/wiki/*" to "https://osu.ppy.sh/wiki/images/*" for simplicity - var imageUrl = url.Replace($"{api.WebsiteRootUrl}/wiki", "https://osu.ppy.sh/wiki/images"); + url = url.Replace("/wiki/", "/wiki/images/"); - InternalChild = new DelayedLoadWrapper(CreateImageContainer(imageUrl)); + return base.CreateImageContainer(url); } } } From 17334fd2e6b3ee4280ca5ebcbe0b70a7bb04fd22 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 17:12:12 +0900 Subject: [PATCH 489/839] Inline `KeyBindingRow` construction --- .../Overlays/KeyBinding/RestorableKeyBindingRow.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs index 70b4fabd6d..5d1dc6a4d1 100644 --- a/osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs @@ -42,12 +42,6 @@ namespace osu.Game.Overlays.KeyBinding AutoSizeAxes = Axes.Y; Padding = new MarginPadding { Right = SettingsPanel.CONTENT_MARGINS }; - KeyBindingRow = new KeyBindingRow(key, bindings.Where(b => ((int)b.Action).Equals((int)key))) - { - AllowMainMouseButtons = ruleset != null, - Defaults = defaults - }; - InternalChildren = new Drawable[] { new RestoreDefaultValueButton @@ -60,7 +54,11 @@ namespace osu.Game.Overlays.KeyBinding RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Padding = new MarginPadding { Left = SettingsPanel.CONTENT_MARGINS }, - Child = KeyBindingRow + Child = KeyBindingRow = new KeyBindingRow(key, bindings.Where(b => ((int)b.Action).Equals((int)key))) + { + AllowMainMouseButtons = ruleset != null, + Defaults = defaults + } }, }; } From 02806fedb06b7c90ac6e32217fe7d41e783ad59f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 17:17:02 +0900 Subject: [PATCH 490/839] Add missing newline --- osu.Game/Overlays/KeyBinding/KeyBindingRow.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs index 5a2a9baf44..e8f6a4d065 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs @@ -101,6 +101,7 @@ namespace osu.Game.Overlays.KeyBinding }, } }; + foreach (var b in bindings) buttons.Add(new KeyButton(b)); } From 7c9383b586ad2ab0cf73e22c3b3505957f26f0cb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 18:03:15 +0900 Subject: [PATCH 491/839] Combine `RestorableKeyBindingRow` back into `KeyBindingRow` --- .../Settings/TestSceneKeyBindingPanel.cs | 14 +- osu.Game/Overlays/KeyBinding/KeyBindingRow.cs | 132 ++++++++++++------ .../KeyBinding/KeyBindingsSubsection.cs | 10 +- .../KeyBinding/RestorableKeyBindingRow.cs | 66 --------- .../Overlays/RestoreDefaultValueButton.cs | 3 + 5 files changed, 104 insertions(+), 121 deletions(-) delete mode 100644 osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs diff --git a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs index 4d0321b29d..acf9deb3cb 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs @@ -142,11 +142,11 @@ namespace osu.Game.Tests.Visual.Settings [Test] public void TestSingleBindingResetButton() { - RestorableKeyBindingRow settingsKeyBindingRow = null; + KeyBindingRow settingsKeyBindingRow = null; AddStep("click first row", () => { - settingsKeyBindingRow = panel.ChildrenOfType().First(); + settingsKeyBindingRow = panel.ChildrenOfType().First(); InputManager.MoveMouseTo(settingsKeyBindingRow); InputManager.Click(MouseButton.Left); @@ -165,17 +165,17 @@ namespace osu.Game.Tests.Visual.Settings AddUntilStep("restore button hidden", () => settingsKeyBindingRow.ChildrenOfType>().First().Alpha == 0); - AddAssert("binding cleared", () => settingsKeyBindingRow.ChildrenOfType().ElementAt(0).KeyBinding.KeyCombination.Equals(settingsKeyBindingRow.KeyBindingRow.Defaults.ElementAt(0))); + AddAssert("binding cleared", () => settingsKeyBindingRow.ChildrenOfType().ElementAt(0).KeyBinding.KeyCombination.Equals(settingsKeyBindingRow.Defaults.ElementAt(0))); } [Test] public void TestResetAllBindingsButton() { - RestorableKeyBindingRow settingsKeyBindingRow = null; + KeyBindingRow settingsKeyBindingRow = null; AddStep("click first row", () => { - settingsKeyBindingRow = panel.ChildrenOfType().First(); + settingsKeyBindingRow = panel.ChildrenOfType().First(); InputManager.MoveMouseTo(settingsKeyBindingRow); InputManager.Click(MouseButton.Left); @@ -194,7 +194,7 @@ namespace osu.Game.Tests.Visual.Settings AddUntilStep("restore button hidden", () => settingsKeyBindingRow.ChildrenOfType>().First().Alpha == 0); - AddAssert("binding cleared", () => settingsKeyBindingRow.ChildrenOfType().ElementAt(0).KeyBinding.KeyCombination.Equals(settingsKeyBindingRow.KeyBindingRow.Defaults.ElementAt(0))); + AddAssert("binding cleared", () => settingsKeyBindingRow.ChildrenOfType().ElementAt(0).KeyBinding.KeyCombination.Equals(settingsKeyBindingRow.Defaults.ElementAt(0))); } [Test] @@ -261,4 +261,4 @@ namespace osu.Game.Tests.Visual.Settings }); } } -} \ No newline at end of file +} diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs index e8f6a4d065..c9ed64cc3f 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs @@ -24,7 +24,7 @@ using osuTK.Input; namespace osu.Game.Overlays.KeyBinding { - public class KeyBindingRow : Container + public class KeyBindingRow : Container, IFilterable { private readonly object action; private readonly IEnumerable bindings; @@ -35,20 +35,40 @@ namespace osu.Game.Overlays.KeyBinding private const float padding = 5; + private bool matchingFilter; + + public bool MatchingFilter + { + get => matchingFilter; + set + { + matchingFilter = value; + this.FadeTo(!matchingFilter ? 0 : 1); + } + } + + private Container content; + + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => + content.ReceivePositionalInputAt(screenSpacePos); + + public bool FilteringActive { get; set; } + + private OsuSpriteText text; private FillFlowContainer cancelAndClearButtons; private FillFlowContainer buttons; public Bindable IsDefault { get; } = new BindableBool(true); + public IEnumerable FilterTerms => bindings.Select(b => b.KeyCombination.ReadableString()).Prepend(text.Text.ToString()); + public KeyBindingRow(object action, IEnumerable bindings) { this.action = action; this.bindings = bindings; + RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; - - Masking = true; - CornerRadius = padding; } [Resolved] @@ -59,46 +79,65 @@ namespace osu.Game.Overlays.KeyBinding { updateIsDefaultValue(); - EdgeEffect = new EdgeEffectParameters - { - Radius = 2, - Colour = colours.YellowDark.Opacity(0), - Type = EdgeEffectType.Shadow, - Hollow = true, - }; + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Padding = new MarginPadding { Horizontal = SettingsPanel.CONTENT_MARGINS }; - Children = new Drawable[] + InternalChildren = new Drawable[] { - new Box + new RestoreDefaultValueButton { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - Alpha = 0.6f, - }, - new OsuSpriteText - { - Text = action.GetDescription(), - Margin = new MarginPadding(padding), - }, - buttons = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight - }, - cancelAndClearButtons = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Padding = new MarginPadding(padding) { Top = height + padding * 2 }, - Anchor = Anchor.TopRight, + Current = IsDefault, + Action = RestoreDefaults, Origin = Anchor.TopRight, - Alpha = 0, - Spacing = new Vector2(5), + }, + content = new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Masking = true, + CornerRadius = padding, + EdgeEffect = new EdgeEffectParameters + { + Radius = 2, + Colour = colours.YellowDark.Opacity(0), + Type = EdgeEffectType.Shadow, + Hollow = true, + }, Children = new Drawable[] { - new CancelButton { Action = finalise }, - new ClearButton { Action = clear }, - }, + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + Alpha = 0.6f, + }, + text = new OsuSpriteText + { + Text = action.GetDescription(), + Margin = new MarginPadding(padding), + }, + buttons = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight + }, + cancelAndClearButtons = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Padding = new MarginPadding(padding) { Top = height + padding * 2 }, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Alpha = 0, + Spacing = new Vector2(5), + Children = new Drawable[] + { + new CancelButton { Action = finalise }, + new ClearButton { Action = clear }, + }, + } + } } }; @@ -135,14 +174,14 @@ namespace osu.Game.Overlays.KeyBinding protected override bool OnHover(HoverEvent e) { - FadeEdgeEffectTo(1, transition_time, Easing.OutQuint); + content.FadeEdgeEffectTo(1, transition_time, Easing.OutQuint); return base.OnHover(e); } protected override void OnHoverLost(HoverLostEvent e) { - FadeEdgeEffectTo(0, transition_time, Easing.OutQuint); + content.FadeEdgeEffectTo(0, transition_time, Easing.OutQuint); base.OnHoverLost(e); } @@ -307,14 +346,10 @@ namespace osu.Game.Overlays.KeyBinding cancelAndClearButtons.BypassAutoSizeAxes |= Axes.Y; } - private void updateIsDefaultValue() => IsDefault.Value = computeIsDefaultValue(); - - private bool computeIsDefaultValue() => bindings.Select(b => b.KeyCombination).SequenceEqual(Defaults); - protected override void OnFocus(FocusEvent e) { - AutoSizeDuration = 500; - AutoSizeEasing = Easing.OutQuint; + content.AutoSizeDuration = 500; + content.AutoSizeEasing = Easing.OutQuint; cancelAndClearButtons.FadeIn(300, Easing.OutQuint); cancelAndClearButtons.BypassAutoSizeAxes &= ~Axes.Y; @@ -339,6 +374,11 @@ namespace osu.Game.Overlays.KeyBinding if (bindTarget != null) bindTarget.IsBinding = true; } + private void updateIsDefaultValue() + { + IsDefault.Value = bindings.Select(b => b.KeyCombination).SequenceEqual(Defaults); + } + private class CancelButton : TriangleButton { public CancelButton() diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs b/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs index 737c640b5a..5e1f9d8f75 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs @@ -37,13 +37,19 @@ namespace osu.Game.Overlays.KeyBinding foreach (var defaultGroup in Defaults.GroupBy(d => d.Action)) { + int intKey = (int)defaultGroup.Key; + // one row per valid action. - Add(new RestorableKeyBindingRow(defaultGroup.Key, bindings, Ruleset, defaultGroup.Select(d => d.KeyCombination))); + Add(new KeyBindingRow(defaultGroup.Key, bindings.Where(b => ((int)b.Action).Equals(intKey))) + { + AllowMainMouseButtons = Ruleset != null, + Defaults = defaultGroup.Select(d => d.KeyCombination) + }); } Add(new ResetButton { - Action = () => Children.OfType().ForEach(k => k.KeyBindingRow.RestoreDefaults()) + Action = () => Children.OfType().ForEach(k => k.RestoreDefaults()) }); } } diff --git a/osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs deleted file mode 100644 index 5d1dc6a4d1..0000000000 --- a/osu.Game/Overlays/KeyBinding/RestorableKeyBindingRow.cs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Input.Bindings; -using osu.Game.Input.Bindings; -using osu.Game.Rulesets; - -namespace osu.Game.Overlays.KeyBinding -{ - public class RestorableKeyBindingRow : Container, IFilterable - { - private readonly object key; - private readonly ICollection bindings; - public readonly KeyBindingRow KeyBindingRow; - - private bool matchingFilter; - - public bool MatchingFilter - { - get => matchingFilter; - set - { - matchingFilter = value; - this.FadeTo(!matchingFilter ? 0 : 1); - } - } - - public bool FilteringActive { get; set; } - - public IEnumerable FilterTerms => bindings.Select(b => b.KeyCombination.ReadableString()).Prepend(key.ToString()); - - public RestorableKeyBindingRow(object key, ICollection bindings, RulesetInfo ruleset, IEnumerable defaults) - { - this.key = key; - this.bindings = bindings; - - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - Padding = new MarginPadding { Right = SettingsPanel.CONTENT_MARGINS }; - - InternalChildren = new Drawable[] - { - new RestoreDefaultValueButton - { - Current = KeyBindingRow.IsDefault, - Action = () => { KeyBindingRow.RestoreDefaults(); } - }, - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Left = SettingsPanel.CONTENT_MARGINS }, - Child = KeyBindingRow = new KeyBindingRow(key, bindings.Where(b => ((int)b.Action).Equals((int)key))) - { - AllowMainMouseButtons = ruleset != null, - Defaults = defaults - } - }, - }; - } - } -} diff --git a/osu.Game/Overlays/RestoreDefaultValueButton.cs b/osu.Game/Overlays/RestoreDefaultValueButton.cs index 0fe7b7322f..213ad2ba68 100644 --- a/osu.Game/Overlays/RestoreDefaultValueButton.cs +++ b/osu.Game/Overlays/RestoreDefaultValueButton.cs @@ -21,6 +21,9 @@ namespace osu.Game.Overlays private readonly BindableWithCurrent current = new BindableWithCurrent(); + // this is done to ensure a click on this button doesn't trigger focus on a parent element which contains the button. + public override bool AcceptsFocus => true; + public Bindable Current { get => current.Current; From c05dfee22042f0ccdf305e9474b7b3d0b18de9ec Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 18:28:00 +0900 Subject: [PATCH 492/839] Simplify default handling flow --- osu.Game/Overlays/KeyBinding/KeyBindingRow.cs | 23 ++++--------------- 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs index c9ed64cc3f..0df3359c28 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs @@ -58,7 +58,7 @@ namespace osu.Game.Overlays.KeyBinding private FillFlowContainer cancelAndClearButtons; private FillFlowContainer buttons; - public Bindable IsDefault { get; } = new BindableBool(true); + private Bindable isDefault { get; } = new BindableBool(true); public IEnumerable FilterTerms => bindings.Select(b => b.KeyCombination.ReadableString()).Prepend(text.Text.ToString()); @@ -77,8 +77,6 @@ namespace osu.Game.Overlays.KeyBinding [BackgroundDependencyLoader] private void load(OsuColour colours) { - updateIsDefaultValue(); - RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; Padding = new MarginPadding { Horizontal = SettingsPanel.CONTENT_MARGINS }; @@ -87,7 +85,7 @@ namespace osu.Game.Overlays.KeyBinding { new RestoreDefaultValueButton { - Current = IsDefault, + Current = isDefault, Action = RestoreDefaults, Origin = Anchor.TopRight, }, @@ -143,19 +141,8 @@ namespace osu.Game.Overlays.KeyBinding foreach (var b in bindings) buttons.Add(new KeyButton(b)); - } - protected override void LoadComplete() - { - base.LoadComplete(); - - IsDefault.BindValueChanged(isDefault => - { - if (isDefault.NewValue) - { - finalise(); - } - }); + updateIsDefaultValue(); } public void RestoreDefaults() @@ -169,7 +156,7 @@ namespace osu.Game.Overlays.KeyBinding store.Update(button.KeyBinding); } - updateIsDefaultValue(); + isDefault.Value = true; } protected override bool OnHover(HoverEvent e) @@ -376,7 +363,7 @@ namespace osu.Game.Overlays.KeyBinding private void updateIsDefaultValue() { - IsDefault.Value = bindings.Select(b => b.KeyCombination).SequenceEqual(Defaults); + isDefault.Value = bindings.Select(b => b.KeyCombination).SequenceEqual(Defaults); } private class CancelButton : TriangleButton From a77de24746a1aa085e7b707f811c53e3a6b41e78 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 18:58:18 +0900 Subject: [PATCH 493/839] Fix `SlowLoadPlayer` potentially not being instantiated in time for test --- osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index cfdea31a75..1e0aee2149 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -176,11 +176,9 @@ namespace osu.Game.Tests.Visual.Gameplay { SlowLoadPlayer slowPlayer = null; - AddStep("load slow dummy beatmap", () => - { - LoadScreen(loader = new TestPlayerLoader(() => slowPlayer = new SlowLoadPlayer(false, false))); - Scheduler.AddDelayed(() => slowPlayer.AllowLoad.Set(), 5000); - }); + AddStep("load slow dummy beatmap", () => LoadScreen(loader = new TestPlayerLoader(() => slowPlayer = new SlowLoadPlayer(false, false)))); + AddUntilStep("wait for slow player to be instantiated", () => slowPlayer != null); + AddStep("schedule slow load", () => Scheduler.AddDelayed(() => slowPlayer.AllowLoad.Set(), 5000)); AddUntilStep("wait for player to be current", () => slowPlayer.IsCurrentScreen()); } From 878079d3d7d32e04bcac1b05b9fc26f91bae9586 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 19:08:00 +0900 Subject: [PATCH 494/839] Fix correct beatmap not being set if running test alone --- .../Visual/Gameplay/TestScenePlayerLoader.cs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index 1e0aee2149..8a7e4da693 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -88,13 +88,18 @@ namespace osu.Game.Tests.Visual.Gameplay { beforeLoadAction?.Invoke(); + prepareBeatmap(); + + LoadScreen(loader = new TestPlayerLoader(() => player = new TestPlayer(interactive, interactive))); + } + + private void prepareBeatmap() + { Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); Beatmap.Value.BeatmapInfo.EpilepsyWarning = epilepsyWarning; foreach (var mod in SelectedMods.Value.OfType()) mod.ApplyToTrack(Beatmap.Value.Track); - - LoadScreen(loader = new TestPlayerLoader(() => player = new TestPlayer(interactive, interactive))); } [Test] @@ -176,7 +181,12 @@ namespace osu.Game.Tests.Visual.Gameplay { SlowLoadPlayer slowPlayer = null; - AddStep("load slow dummy beatmap", () => LoadScreen(loader = new TestPlayerLoader(() => slowPlayer = new SlowLoadPlayer(false, false)))); + AddStep("load slow dummy beatmap", () => + { + prepareBeatmap(); + LoadScreen(loader = new TestPlayerLoader(() => slowPlayer = new SlowLoadPlayer(false, false))); + }); + AddUntilStep("wait for slow player to be instantiated", () => slowPlayer != null); AddStep("schedule slow load", () => Scheduler.AddDelayed(() => slowPlayer.AllowLoad.Set(), 5000)); From 7ed4cbf7bff7a6800ae7c12d92dddbb2ddf9e1d7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 19:25:05 +0900 Subject: [PATCH 495/839] Fix settings panel hide animation looking wrong when a sub-panel is visible when hidden --- osu.Game/Overlays/SettingsPanel.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs index f0a11d67b7..eae828c142 100644 --- a/osu.Game/Overlays/SettingsPanel.cs +++ b/osu.Game/Overlays/SettingsPanel.cs @@ -10,6 +10,7 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Game.Graphics; @@ -49,8 +50,6 @@ namespace osu.Game.Overlays private readonly bool showSidebar; - protected Box Background; - protected SettingsPanel(bool showSidebar) { this.showSidebar = showSidebar; @@ -63,13 +62,13 @@ namespace osu.Game.Overlays [BackgroundDependencyLoader] private void load() { - InternalChild = ContentContainer = new Container + InternalChild = ContentContainer = new NonMaskedContent { Width = WIDTH, RelativeSizeAxes = Axes.Y, Children = new Drawable[] { - Background = new Box + new Box { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, @@ -165,7 +164,7 @@ namespace osu.Game.Overlays { base.PopOut(); - ContentContainer.MoveToX(-WIDTH, TRANSITION_LENGTH, Easing.OutQuint); + ContentContainer.MoveToX(-WIDTH + ExpandedPosition, TRANSITION_LENGTH, Easing.OutQuint); Sidebar?.MoveToX(-sidebar_width, TRANSITION_LENGTH, Easing.OutQuint); this.FadeTo(0, TRANSITION_LENGTH, Easing.OutQuint); @@ -191,6 +190,12 @@ namespace osu.Game.Overlays Padding = new MarginPadding { Top = GetToolbarHeight?.Invoke() ?? 0 }; } + private class NonMaskedContent : Container + { + // masking breaks the pan-out transform with nested sub-settings panels. + protected override bool ComputeIsMaskedAway(RectangleF maskingBounds) => false; + } + public class SettingsSectionsContainer : SectionsContainer { public SearchContainer SearchContainer; From fbfbd992235b2a4ac2b7467521ae715f61795f4c Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Wed, 26 May 2021 19:20:39 +0700 Subject: [PATCH 496/839] change document url test to dev server --- .../Visual/Online/TestSceneWikiMarkdownContainer.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs index ebabfc9479..57bd8cd077 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs @@ -106,7 +106,7 @@ needs_cleanup: true { AddStep("Add absolute image", () => { - markdownContainer.DocumentUrl = "https://osu.ppy.sh"; + markdownContainer.DocumentUrl = "https://dev.ppy.sh"; markdownContainer.Text = "![intro](/wiki/Interface/img/intro-screen.jpg)"; }); } @@ -116,7 +116,7 @@ needs_cleanup: true { AddStep("Add relative image", () => { - markdownContainer.DocumentUrl = "https://osu.ppy.sh"; + markdownContainer.DocumentUrl = "https://dev.ppy.sh"; markdownContainer.CurrentPath = "Interface/"; markdownContainer.Text = "![intro](img/intro-screen.jpg)"; }); @@ -127,7 +127,7 @@ needs_cleanup: true { AddStep("Add paragraph with block image", () => { - markdownContainer.DocumentUrl = "https://osu.ppy.sh"; + markdownContainer.DocumentUrl = "https://dev.ppy.sh"; markdownContainer.CurrentPath = "Interface/"; markdownContainer.Text = @"Line before image @@ -142,7 +142,7 @@ Line after image"; { AddStep("Add inline image", () => { - markdownContainer.DocumentUrl = "https://osu.ppy.sh"; + markdownContainer.DocumentUrl = "https://dev.ppy.sh"; markdownContainer.Text = "![osu! mode icon](/wiki/shared/mode/osu.png) osu!"; }); } From 62fb09774a7588e9bb9babd29ded257c0325901d Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Wed, 26 May 2021 19:22:21 +0700 Subject: [PATCH 497/839] create WikiMarkdownImageBlock --- .../Wiki/Markdown/WikiMarkdownImageBlock.cs | 49 +++++++++++++++++++ .../Wiki/Markdown/WikiMarkdownParagraph.cs | 40 --------------- 2 files changed, 49 insertions(+), 40 deletions(-) create mode 100644 osu.Game/Overlays/Wiki/Markdown/WikiMarkdownImageBlock.cs delete mode 100644 osu.Game/Overlays/Wiki/Markdown/WikiMarkdownParagraph.cs diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownImageBlock.cs b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownImageBlock.cs new file mode 100644 index 0000000000..179762103a --- /dev/null +++ b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownImageBlock.cs @@ -0,0 +1,49 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Markdig.Syntax.Inlines; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Containers.Markdown; +using osuTK; + +namespace osu.Game.Overlays.Wiki.Markdown +{ + public class WikiMarkdownImageBlock : FillFlowContainer + { + [Resolved] + private IMarkdownTextComponent parentTextComponent { get; set; } + + private readonly LinkInline linkInline; + + public WikiMarkdownImageBlock(LinkInline linkInline) + { + this.linkInline = linkInline; + + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Direction = FillDirection.Vertical; + Spacing = new Vector2(0, 3); + } + + [BackgroundDependencyLoader] + private void load() + { + Children = new Drawable[] + { + new WikiMarkdownImage(linkInline) + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }, + parentTextComponent.CreateSpriteText().With(t => + { + t.Text = linkInline.Title; + t.Anchor = Anchor.TopCentre; + t.Origin = Anchor.TopCentre; + }), + }; + } + } +} diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownParagraph.cs b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownParagraph.cs deleted file mode 100644 index 4a7ce24aba..0000000000 --- a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownParagraph.cs +++ /dev/null @@ -1,40 +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.Linq; -using Markdig.Syntax; -using Markdig.Syntax.Inlines; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers.Markdown; -using osuTK; - -namespace osu.Game.Overlays.Wiki.Markdown -{ - public class WikiMarkdownParagraph : MarkdownParagraph - { - private readonly ParagraphBlock paragraphBlock; - - public WikiMarkdownParagraph(ParagraphBlock paragraphBlock) - : base(paragraphBlock) - { - this.paragraphBlock = paragraphBlock; - } - - [BackgroundDependencyLoader] - private void load() - { - MarkdownTextFlowContainer textFlow; - InternalChild = textFlow = CreateTextFlow(); - textFlow.AddInlineText(paragraphBlock.Inline); - - // Check if paragraph only contains an image. - if (paragraphBlock.Inline.Count() == 1 && paragraphBlock.Inline.FirstChild is LinkInline { IsImage: true } linkInline) - { - textFlow.TextAnchor = Anchor.TopCentre; - textFlow.Spacing = new Vector2(0, 5); - textFlow.AddText($"\n{linkInline.Title}"); - } - } - } -} From 2344a1a411cf1778ffd21635e2167f13e0039f89 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Wed, 26 May 2021 19:22:33 +0700 Subject: [PATCH 498/839] use image block in markdown container --- .../Wiki/Markdown/WikiMarkdownContainer.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs index fbfdc5feaf..4e671cca6d 100644 --- a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs +++ b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.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 Markdig.Extensions.Yaml; using Markdig.Syntax; using Markdig.Syntax.Inlines; @@ -23,7 +24,17 @@ namespace osu.Game.Overlays.Wiki.Markdown { case YamlFrontMatterBlock yamlFrontMatterBlock: container.Add(new WikiNoticeContainer(yamlFrontMatterBlock)); - return; + break; + + case ParagraphBlock paragraphBlock: + // Check if paragraph only contains an image + if (paragraphBlock.Inline.Count() == 1 && paragraphBlock.Inline.FirstChild is LinkInline { IsImage: true } linkInline) + { + container.Add(new WikiMarkdownImageBlock(linkInline)); + return; + } + + break; } base.AddMarkdownComponent(markdownObject, container, level); @@ -31,8 +42,6 @@ namespace osu.Game.Overlays.Wiki.Markdown public override MarkdownTextFlowContainer CreateTextFlow() => new WikiMarkdownTextFlowContainer(); - protected override MarkdownParagraph CreateParagraph(ParagraphBlock paragraphBlock, int level) => new WikiMarkdownParagraph(paragraphBlock); - private class WikiMarkdownTextFlowContainer : OsuMarkdownTextFlowContainer { protected override void AddImage(LinkInline linkInline) => AddDrawable(new WikiMarkdownImage(linkInline)); From 7546611c1421fb83eae255dd6544d78023e89ee4 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Wed, 26 May 2021 19:32:49 +0700 Subject: [PATCH 499/839] remove unused cached --- osu.Game/Overlays/WikiOverlay.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs index df93c35500..040e608574 100644 --- a/osu.Game/Overlays/WikiOverlay.cs +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -20,7 +20,6 @@ namespace osu.Game.Overlays private readonly Bindable path = new Bindable(index_path); - [Cached] private readonly Bindable wikiData = new Bindable(); [Resolved] From 905364b5fefc07cd309b1e8b77327ad6d9a7e115 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Wed, 26 May 2021 19:34:34 +0700 Subject: [PATCH 500/839] add url as argument for link action external --- osu.Game/Online/Chat/MessageFormatter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs index 1041758b0c..c57fc732be 100644 --- a/osu.Game/Online/Chat/MessageFormatter.cs +++ b/osu.Game/Online/Chat/MessageFormatter.cs @@ -173,7 +173,7 @@ namespace osu.Game.Online.Chat } } - return new LinkDetails(LinkAction.External, null); + return new LinkDetails(LinkAction.External, url); case "osu": // every internal link also needs some kind of argument From 1bde11a07e202e9c643d3602d66cd2948b83b807 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 26 May 2021 15:35:38 +0300 Subject: [PATCH 501/839] Refactor ArticleListing --- .../Overlays/News/Displays/ArticleListing.cs | 58 ++++++++++++------- osu.Game/Overlays/NewsOverlay.cs | 9 ++- 2 files changed, 40 insertions(+), 27 deletions(-) diff --git a/osu.Game/Overlays/News/Displays/ArticleListing.cs b/osu.Game/Overlays/News/Displays/ArticleListing.cs index b554b462a9..4bbc80c5f3 100644 --- a/osu.Game/Overlays/News/Displays/ArticleListing.cs +++ b/osu.Game/Overlays/News/Displays/ArticleListing.cs @@ -2,13 +2,16 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; +using System.Collections.Specialized; using System.Linq; using System.Threading; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.UserInterface; -using osu.Game.Online.API.Requests; +using osu.Game.Online.API.Requests.Responses; using osuTK; namespace osu.Game.Overlays.News.Displays @@ -20,20 +23,12 @@ namespace osu.Game.Overlays.News.Displays { public Action RequestMorePosts; + private readonly BindableList posts = new BindableList(); + private bool showMoreButtonIsVisible; + private FillFlowContainer content; private ShowMoreButton showMore; - private readonly GetNewsResponse initialResponse; - - /// - /// Instantiate a listing for the specified year. - /// - /// Initial response to create articles from. - public ArticleListing(GetNewsResponse initialResponse) - { - this.initialResponse = initialResponse; - } - [BackgroundDependencyLoader] private void load() { @@ -45,7 +40,6 @@ namespace osu.Game.Overlays.News.Displays Left = 30, Right = 50 }; - InternalChild = new FillFlowContainer { RelativeSizeAxes = Axes.X, @@ -61,8 +55,7 @@ namespace osu.Game.Overlays.News.Displays RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 10), - Children = initialResponse.NewsPosts.Select(p => new NewsCard(p)).ToList() + Spacing = new Vector2(0, 10) }, showMore = new ShowMoreButton { @@ -73,24 +66,45 @@ namespace osu.Game.Overlays.News.Displays Top = 15 }, Action = RequestMorePosts, - Alpha = initialResponse.Cursor != null ? 1 : 0 + Alpha = 0 } } }; } + protected override void LoadComplete() + { + base.LoadComplete(); + + posts.BindCollectionChanged((sender, args) => + { + switch (args.Action) + { + case NotifyCollectionChangedAction.Add: + addPosts(args.NewItems.Cast()); + break; + + default: + throw new NotSupportedException(@"You can only add items to this list. Other actions are not supported."); + } + }, true); + } + + public void AddPosts(IEnumerable posts, bool showMoreButtonIsVisible) + { + this.showMoreButtonIsVisible = showMoreButtonIsVisible; + this.posts.AddRange(posts); + } + private CancellationTokenSource cancellationToken; - public void AddPosts(GetNewsResponse response) + private void addPosts(IEnumerable posts) { - cancellationToken?.Cancel(); - - LoadComponentsAsync(response.NewsPosts.Select(p => new NewsCard(p)).ToList(), loaded => + LoadComponentsAsync(posts.Select(p => new NewsCard(p)).ToList(), loaded => { content.AddRange(loaded); - showMore.IsLoading = false; - showMore.Alpha = response.Cursor != null ? 1 : 0; + showMore.Alpha = showMoreButtonIsVisible ? 1 : 0; }, (cancellationToken = new CancellationTokenSource()).Token); } diff --git a/osu.Game/Overlays/NewsOverlay.cs b/osu.Game/Overlays/NewsOverlay.cs index 8d0d242e39..34bacd5540 100644 --- a/osu.Game/Overlays/NewsOverlay.cs +++ b/osu.Game/Overlays/NewsOverlay.cs @@ -152,10 +152,9 @@ namespace osu.Game.Overlays lastCursor = response.Cursor; sidebar.Metadata.Value = response.SidebarMetadata; - LoadDisplay(new ArticleListing(response) - { - RequestMorePosts = getMorePosts - }); + var listing = new ArticleListing { RequestMorePosts = getMorePosts }; + listing.AddPosts(response.NewsPosts, response.Cursor != null); + LoadDisplay(listing); }); API.PerformAsync(request); @@ -170,7 +169,7 @@ namespace osu.Game.Overlays { lastCursor = response.Cursor; if (content.Child is ArticleListing listing) - listing.AddPosts(response); + listing.AddPosts(response.NewsPosts, response.Cursor != null); }); API.PerformAsync(request); From 8e923a5d8ff233db20652edbc33d7d217830580a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 22:24:51 +0900 Subject: [PATCH 502/839] Instantiate immediately, rather than waiting for instantiation --- osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index 8a7e4da693..8160a62991 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -184,10 +184,10 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("load slow dummy beatmap", () => { prepareBeatmap(); - LoadScreen(loader = new TestPlayerLoader(() => slowPlayer = new SlowLoadPlayer(false, false))); + slowPlayer = new SlowLoadPlayer(false, false); + LoadScreen(loader = new TestPlayerLoader(() => slowPlayer)); }); - AddUntilStep("wait for slow player to be instantiated", () => slowPlayer != null); AddStep("schedule slow load", () => Scheduler.AddDelayed(() => slowPlayer.AllowLoad.Set(), 5000)); AddUntilStep("wait for player to be current", () => slowPlayer.IsCurrentScreen()); From 22a5af750e6be5cffea6f6a706f59581f39c9f1e Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Wed, 26 May 2021 20:25:48 +0700 Subject: [PATCH 503/839] fix test link external --- osu.Game.Tests/Chat/MessageFormatterTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Chat/MessageFormatterTests.cs b/osu.Game.Tests/Chat/MessageFormatterTests.cs index b80da928c8..c0fc19356e 100644 --- a/osu.Game.Tests/Chat/MessageFormatterTests.cs +++ b/osu.Game.Tests/Chat/MessageFormatterTests.cs @@ -24,10 +24,10 @@ namespace osu.Game.Tests.Chat [TestCase(LinkAction.OpenBeatmap, "456", "https://dev.ppy.sh/beatmapsets/123#osu/456")] [TestCase(LinkAction.OpenBeatmap, "456", "https://dev.ppy.sh/beatmapsets/123#osu/456?whatever")] [TestCase(LinkAction.OpenBeatmap, "456", "https://dev.ppy.sh/beatmapsets/123/456")] - [TestCase(LinkAction.External, null, "https://dev.ppy.sh/beatmapsets/abc/def")] + [TestCase(LinkAction.External, "https://dev.ppy.sh/beatmapsets/abc/def", "https://dev.ppy.sh/beatmapsets/abc/def")] [TestCase(LinkAction.OpenBeatmapSet, "123", "https://dev.ppy.sh/beatmapsets/123")] [TestCase(LinkAction.OpenBeatmapSet, "123", "https://dev.ppy.sh/beatmapsets/123/whatever")] - [TestCase(LinkAction.External, null, "https://dev.ppy.sh/beatmapsets/abc")] + [TestCase(LinkAction.External, "https://dev.ppy.sh/beatmapsets/abc", "https://dev.ppy.sh/beatmapsets/abc")] public void TestBeatmapLinks(LinkAction expectedAction, string expectedArg, string link) { MessageFormatter.WebsiteRootUrl = "dev.ppy.sh"; From 71de541245060ea4b48d38820c233e30d5ee29bb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 22:35:11 +0900 Subject: [PATCH 504/839] Minor spacing / reformatting --- osu.Game/Overlays/News/Displays/ArticleListing.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/News/Displays/ArticleListing.cs b/osu.Game/Overlays/News/Displays/ArticleListing.cs index 4bbc80c5f3..3524b8652c 100644 --- a/osu.Game/Overlays/News/Displays/ArticleListing.cs +++ b/osu.Game/Overlays/News/Displays/ArticleListing.cs @@ -34,12 +34,14 @@ namespace osu.Game.Overlays.News.Displays { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; + Padding = new MarginPadding { Vertical = 20, Left = 30, Right = 50 }; + InternalChild = new FillFlowContainer { RelativeSizeAxes = Axes.X, @@ -61,10 +63,7 @@ namespace osu.Game.Overlays.News.Displays { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Margin = new MarginPadding - { - Top = 15 - }, + Margin = new MarginPadding { Top = 15 }, Action = RequestMorePosts, Alpha = 0 } From 9947867e8408e8f2d0d4296061c9f67ea3bf1ffe Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 22:46:43 +0900 Subject: [PATCH 505/839] Remove unnecessary bindable flow --- .../Overlays/News/Displays/ArticleListing.cs | 42 +++---------------- 1 file changed, 6 insertions(+), 36 deletions(-) diff --git a/osu.Game/Overlays/News/Displays/ArticleListing.cs b/osu.Game/Overlays/News/Displays/ArticleListing.cs index 3524b8652c..48a3f5498a 100644 --- a/osu.Game/Overlays/News/Displays/ArticleListing.cs +++ b/osu.Game/Overlays/News/Displays/ArticleListing.cs @@ -3,11 +3,9 @@ using System; using System.Collections.Generic; -using System.Collections.Specialized; using System.Linq; using System.Threading; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.UserInterface; @@ -23,9 +21,6 @@ namespace osu.Game.Overlays.News.Displays { public Action RequestMorePosts; - private readonly BindableList posts = new BindableList(); - private bool showMoreButtonIsVisible; - private FillFlowContainer content; private ShowMoreButton showMore; @@ -71,41 +66,16 @@ namespace osu.Game.Overlays.News.Displays }; } - protected override void LoadComplete() - { - base.LoadComplete(); - - posts.BindCollectionChanged((sender, args) => - { - switch (args.Action) - { - case NotifyCollectionChangedAction.Add: - addPosts(args.NewItems.Cast()); - break; - - default: - throw new NotSupportedException(@"You can only add items to this list. Other actions are not supported."); - } - }, true); - } - - public void AddPosts(IEnumerable posts, bool showMoreButtonIsVisible) - { - this.showMoreButtonIsVisible = showMoreButtonIsVisible; - this.posts.AddRange(posts); - } - - private CancellationTokenSource cancellationToken; - - private void addPosts(IEnumerable posts) - { + public void AddPosts(IEnumerable posts, bool morePostsAvailable) => Schedule(() => LoadComponentsAsync(posts.Select(p => new NewsCard(p)).ToList(), loaded => { content.AddRange(loaded); showMore.IsLoading = false; - showMore.Alpha = showMoreButtonIsVisible ? 1 : 0; - }, (cancellationToken = new CancellationTokenSource()).Token); - } + showMore.Alpha = morePostsAvailable ? 1 : 0; + }, (cancellationToken = new CancellationTokenSource()).Token) + ); + + private CancellationTokenSource cancellationToken; protected override void Dispose(bool isDisposing) { From 735e7b9c741e2bc0d4d29df6e880daf5ff44a665 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 22:49:39 +0900 Subject: [PATCH 506/839] Pass fetch more action in via ctor to avoid potential nullref --- osu.Game/Overlays/News/Displays/ArticleListing.cs | 13 +++++++++---- osu.Game/Overlays/NewsOverlay.cs | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/News/Displays/ArticleListing.cs b/osu.Game/Overlays/News/Displays/ArticleListing.cs index 48a3f5498a..dc3b17b323 100644 --- a/osu.Game/Overlays/News/Displays/ArticleListing.cs +++ b/osu.Game/Overlays/News/Displays/ArticleListing.cs @@ -19,11 +19,18 @@ namespace osu.Game.Overlays.News.Displays /// public class ArticleListing : CompositeDrawable { - public Action RequestMorePosts; + private readonly Action fetchMorePosts; private FillFlowContainer content; private ShowMoreButton showMore; + private CancellationTokenSource cancellationToken; + + public ArticleListing(Action fetchMorePosts) + { + this.fetchMorePosts = fetchMorePosts; + } + [BackgroundDependencyLoader] private void load() { @@ -59,7 +66,7 @@ namespace osu.Game.Overlays.News.Displays Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Margin = new MarginPadding { Top = 15 }, - Action = RequestMorePosts, + Action = fetchMorePosts, Alpha = 0 } } @@ -75,8 +82,6 @@ namespace osu.Game.Overlays.News.Displays }, (cancellationToken = new CancellationTokenSource()).Token) ); - private CancellationTokenSource cancellationToken; - protected override void Dispose(bool isDisposing) { cancellationToken?.Cancel(); diff --git a/osu.Game/Overlays/NewsOverlay.cs b/osu.Game/Overlays/NewsOverlay.cs index 34bacd5540..12e3f81ca1 100644 --- a/osu.Game/Overlays/NewsOverlay.cs +++ b/osu.Game/Overlays/NewsOverlay.cs @@ -152,7 +152,7 @@ namespace osu.Game.Overlays lastCursor = response.Cursor; sidebar.Metadata.Value = response.SidebarMetadata; - var listing = new ArticleListing { RequestMorePosts = getMorePosts }; + var listing = new ArticleListing(getMorePosts); listing.AddPosts(response.NewsPosts, response.Cursor != null); LoadDisplay(listing); }); From c0a8382175a55e0323ab11ed456f3584489ea356 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 27 May 2021 00:12:22 +0900 Subject: [PATCH 507/839] Remove local API construction --- .../Visual/Online/TestSceneWikiMarkdownContainer.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs index 57bd8cd077..c423d46aa3 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs @@ -23,9 +23,6 @@ namespace osu.Game.Tests.Visual.Online [Cached] private readonly OverlayColourProvider overlayColour = new OverlayColourProvider(OverlayColourScheme.Orange); - [Cached] - private readonly IAPIProvider api = new DummyAPIAccess(); - [SetUp] public void Setup() => Schedule(() => { @@ -55,16 +52,16 @@ namespace osu.Game.Tests.Visual.Online AddStep("set current path", () => markdownContainer.CurrentPath = "Article_styling_criteria/"); AddStep("set '/wiki/Main_Page''", () => markdownContainer.Text = "[wiki main page](/wiki/Main_Page)"); - AddAssert("check url", () => markdownContainer.Link.Url == $"{api.WebsiteRootUrl}/wiki/Main_Page"); + AddAssert("check url", () => markdownContainer.Link.Url == $"{API.WebsiteRootUrl}/wiki/Main_Page"); AddStep("set '../FAQ''", () => markdownContainer.Text = "[FAQ](../FAQ)"); - AddAssert("check url", () => markdownContainer.Link.Url == $"{api.WebsiteRootUrl}/wiki/FAQ"); + AddAssert("check url", () => markdownContainer.Link.Url == $"{API.WebsiteRootUrl}/wiki/FAQ"); AddStep("set './Writing''", () => markdownContainer.Text = "[wiki writing guidline](./Writing)"); - AddAssert("check url", () => markdownContainer.Link.Url == $"{api.WebsiteRootUrl}/wiki/Article_styling_criteria/Writing"); + AddAssert("check url", () => markdownContainer.Link.Url == $"{API.WebsiteRootUrl}/wiki/Article_styling_criteria/Writing"); AddStep("set 'Formatting''", () => markdownContainer.Text = "[wiki formatting guidline](Formatting)"); - AddAssert("check url", () => markdownContainer.Link.Url == $"{api.WebsiteRootUrl}/wiki/Article_styling_criteria/Formatting"); + AddAssert("check url", () => markdownContainer.Link.Url == $"{API.WebsiteRootUrl}/wiki/Article_styling_criteria/Formatting"); } [Test] From 74fc0a17d5f668c3e178c428c2e6abb30e6871db Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 27 May 2021 00:55:05 +0900 Subject: [PATCH 508/839] Remove unused using statement --- osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs index c423d46aa3..1e19af933a 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs @@ -10,7 +10,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers.Markdown; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics.Containers.Markdown; -using osu.Game.Online.API; using osu.Game.Overlays; using osu.Game.Overlays.Wiki.Markdown; From d5de5ae6406f85fee9217c8c1649d93e0547e5dc Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Wed, 26 May 2021 20:50:31 +0200 Subject: [PATCH 509/839] Add comments explaining the usage of `shiftNestedObjects()` --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 58ace92905..7b28675511 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -90,12 +90,15 @@ namespace osu.Game.Rulesets.Osu.Mods switch (hitObject) { case Slider slider: + // Shift nested objects the same distance as the slider got shifted in the randomisation process + // so that moveSliderIntoPlayfield() can determine their relative distances to slider.Position and thus minMargin shiftNestedObjects(slider, Vector2.Subtract(slider.Position, current.PositionOriginal)); var oldPos = new Vector2(slider.Position.X, slider.Position.Y); moveSliderIntoPlayfield(slider, current); + // Shift them again to move them to their final position after the slider got moved into the playfield shiftNestedObjects(slider, Vector2.Subtract(slider.Position, oldPos)); break; From 9ac4ef273e72435d21f09e70c7297b53bcb32049 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Wed, 26 May 2021 23:21:05 +0300 Subject: [PATCH 510/839] Make DrawableSliderTail not require ITrackSnaking --- .../Objects/Drawables/DrawableSliderTail.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs index d81af053d1..cd6bf1d8d2 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs @@ -7,13 +7,14 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Skinning.Default; using osu.Game.Skinning; using osuTK; namespace osu.Game.Rulesets.Osu.Objects.Drawables { - public class DrawableSliderTail : DrawableOsuHitObject, IRequireTracking, ITrackSnaking, IHasMainCirclePiece + public class DrawableSliderTail : DrawableOsuHitObject, IRequireTracking, IHasMainCirclePiece { public new SliderTailCircle HitObject => (SliderTailCircle)base.HitObject; @@ -111,7 +112,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables ApplyResult(r => r.Type = Tracking ? r.Judgement.MaxResult : r.Judgement.MinResult); } - public void UpdateSnakingPosition(Vector2 start, Vector2 end) => - Position = HitObject.RepeatIndex % 2 == 0 ? end : start; + protected override void OnApply() + { + base.OnApply(); + + if (Slider != null) + Position = Slider.CurvePositionAt(HitObject.RepeatIndex % 2 == 0 ? 1 : 0); + } } } From a7865d3f22013bf85fb46ff1433ab3976d2c090b Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 27 May 2021 08:46:24 +0700 Subject: [PATCH 511/839] move colour provider to BDL --- osu.Game/Overlays/Wiki/WikiPanelContainer.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs index 71b492b375..ad30306475 100644 --- a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs +++ b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs @@ -21,9 +21,6 @@ namespace osu.Game.Overlays.Wiki { public class WikiPanelContainer : Container { - [Resolved] - private OverlayColourProvider colourProvider { get; set; } - private WikiPanelMarkdownContainer panelContainer; public string Text; @@ -37,7 +34,7 @@ namespace osu.Game.Overlays.Wiki } [BackgroundDependencyLoader] - private void load() + private void load(OverlayColourProvider colourProvider) { Children = new Drawable[] { From 0c2d3ae0e757b486b4e28180780387a8addcdf7e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 27 May 2021 05:08:49 +0300 Subject: [PATCH 512/839] Revert "Move beatmap skin info creation to static method at `IBeatmapSkin`" This reverts commit 9806d94b745348e7c44af36cc93b76401234d225. --- osu.Game/Skinning/BeatmapSkinExtensions.cs | 16 ---------------- osu.Game/Skinning/LegacyBeatmapSkin.cs | 5 ++++- 2 files changed, 4 insertions(+), 17 deletions(-) delete mode 100644 osu.Game/Skinning/BeatmapSkinExtensions.cs diff --git a/osu.Game/Skinning/BeatmapSkinExtensions.cs b/osu.Game/Skinning/BeatmapSkinExtensions.cs deleted file mode 100644 index 18ef09c392..0000000000 --- a/osu.Game/Skinning/BeatmapSkinExtensions.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. - -using osu.Game.Beatmaps; - -namespace osu.Game.Skinning -{ - public static class BeatmapSkinExtensions - { - public static SkinInfo CreateSkinInfo(BeatmapInfo beatmap) => new SkinInfo - { - Name = beatmap.ToString(), - Creator = beatmap.Metadata?.AuthorString, - }; - } -} diff --git a/osu.Game/Skinning/LegacyBeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs index 5ee436e8bb..3ec205e897 100644 --- a/osu.Game/Skinning/LegacyBeatmapSkin.cs +++ b/osu.Game/Skinning/LegacyBeatmapSkin.cs @@ -17,7 +17,7 @@ namespace osu.Game.Skinning protected override bool UseCustomSampleBanks => true; public LegacyBeatmapSkin(BeatmapInfo beatmap, IResourceStore storage, IStorageResourceProvider resources) - : base(BeatmapSkinExtensions.CreateSkinInfo(beatmap), new LegacySkinResourceStore(beatmap.BeatmapSet, storage), resources, beatmap.Path) + : base(createSkinInfo(beatmap), new LegacySkinResourceStore(beatmap.BeatmapSet, storage), resources, beatmap.Path) { // Disallow default colours fallback on beatmap skins to allow using parent skin combo colours. (via SkinProvidingContainer) Configuration.AllowDefaultComboColoursFallback = false; @@ -49,5 +49,8 @@ namespace osu.Game.Skinning return base.GetSample(sampleInfo); } + + private static SkinInfo createSkinInfo(BeatmapInfo beatmap) => + new SkinInfo { Name = beatmap.ToString(), Creator = beatmap.Metadata.Author.ToString() }; } } From d66f07fccb10cae2aaf76020bab1c03313a11b56 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 27 May 2021 14:04:50 +0900 Subject: [PATCH 513/839] Move text and `isFullWidth` parameters to constructor --- osu.Game/Overlays/Wiki/WikiMainPage.cs | 14 +++---------- osu.Game/Overlays/Wiki/WikiPanelContainer.cs | 22 ++++++++++++-------- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/osu.Game/Overlays/Wiki/WikiMainPage.cs b/osu.Game/Overlays/Wiki/WikiMainPage.cs index 8ff85c1404..bf4a76c3d8 100644 --- a/osu.Game/Overlays/Wiki/WikiMainPage.cs +++ b/osu.Game/Overlays/Wiki/WikiMainPage.cs @@ -77,10 +77,8 @@ namespace osu.Game.Overlays.Wiki { yield return new Drawable[] { - new WikiPanelContainer + new WikiPanelContainer(panelsNode[i].InnerText, true) { - Text = panelsNode[i].InnerText, - IsFullWidth = true, Width = 2, }, null, @@ -91,14 +89,8 @@ namespace osu.Game.Overlays.Wiki { yield return new Drawable[] { - new WikiPanelContainer - { - Text = panelsNode[i].InnerText, - }, - new WikiPanelContainer - { - Text = panelsNode[i + 1].InnerText, - }, + new WikiPanelContainer(panelsNode[i].InnerText), + new WikiPanelContainer(panelsNode[i + 1].InnerText), }; } } diff --git a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs index ad30306475..db213e4951 100644 --- a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs +++ b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs @@ -23,12 +23,15 @@ namespace osu.Game.Overlays.Wiki { private WikiPanelMarkdownContainer panelContainer; - public string Text; + private readonly string text; - public bool IsFullWidth; + private readonly bool isFullWidth; - public WikiPanelContainer() + public WikiPanelContainer(string text, bool isFullWidth = false) { + this.text = text; + this.isFullWidth = isFullWidth; + RelativeSizeAxes = Axes.X; Padding = new MarginPadding(3); } @@ -56,12 +59,11 @@ namespace osu.Game.Overlays.Wiki RelativeSizeAxes = Axes.Both, }, }, - panelContainer = new WikiPanelMarkdownContainer + panelContainer = new WikiPanelMarkdownContainer(isFullWidth) { - Text = Text, + Text = text, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - IsFullWidth = IsFullWidth, } }; } @@ -74,10 +76,12 @@ namespace osu.Game.Overlays.Wiki private class WikiPanelMarkdownContainer : WikiMarkdownContainer { - public bool IsFullWidth; + private readonly bool isFullWidth; - public WikiPanelMarkdownContainer() + public WikiPanelMarkdownContainer(bool isFullWidth) { + this.isFullWidth = isFullWidth; + LineSpacing = 0; DocumentPadding = new MarginPadding(30); DocumentMargin = new MarginPadding(0); @@ -92,7 +96,7 @@ namespace osu.Game.Overlays.Wiki protected override MarkdownHeading CreateHeading(HeadingBlock headingBlock) => new WikiPanelHeading(headingBlock) { - IsFullWidth = IsFullWidth, + IsFullWidth = isFullWidth, }; } From 4fd89faaa459d26ed3467675ad391d10c08dc1da Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 27 May 2021 11:48:48 +0900 Subject: [PATCH 514/839] Fix default skin not having resources or providing samples --- osu.Game/Skinning/DefaultSkin.cs | 15 ++++++++++++++- osu.Game/Skinning/SkinManager.cs | 4 +++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index ba31816a07..a17a052b97 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -22,6 +22,8 @@ namespace osu.Game.Skinning { public class DefaultSkin : Skin { + private readonly IStorageResourceProvider resources; + public DefaultSkin(IStorageResourceProvider resources) : this(SkinInfo.Default, resources) { @@ -31,12 +33,23 @@ namespace osu.Game.Skinning public DefaultSkin(SkinInfo skin, IStorageResourceProvider resources) : base(skin, resources) { + this.resources = resources; Configuration = new DefaultSkinConfiguration(); } public override Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => null; - public override ISample GetSample(ISampleInfo sampleInfo) => null; + public override ISample GetSample(ISampleInfo sampleInfo) + { + foreach (var lookup in sampleInfo.LookupNames) + { + var sample = resources.AudioManager.Samples.Get(lookup); + if (sample != null) + return sample; + } + + return null; + } public override Drawable GetDrawableComponent(ISkinComponent component) { diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 5793edda30..66c776d32d 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -39,7 +39,7 @@ namespace osu.Game.Skinning private readonly IResourceStore legacyDefaultResources; - public readonly Bindable CurrentSkin = new Bindable(new DefaultSkin(null)); + public readonly Bindable CurrentSkin = new Bindable(); public readonly Bindable CurrentSkinInfo = new Bindable(SkinInfo.Default) { Default = SkinInfo.Default }; public override IEnumerable HandledExtensions => new[] { ".osk" }; @@ -56,6 +56,8 @@ namespace osu.Game.Skinning this.legacyDefaultResources = legacyDefaultResources; + CurrentSkin.Value = new DefaultSkin(this); + CurrentSkinInfo.ValueChanged += skin => CurrentSkin.Value = GetSkin(skin.NewValue); CurrentSkin.ValueChanged += skin => { From 70a844ac10409380692e05343f21b2afee5f9229 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 27 May 2021 14:50:42 +0900 Subject: [PATCH 515/839] Remove `allowFallback` parameters completely --- .../UI/CatchComboDisplay.cs | 4 +-- osu.Game.Rulesets.Catch/UI/Catcher.cs | 4 +-- .../UI/Components/HitObjectArea.cs | 4 +-- .../Objects/Drawables/SkinnableLighting.cs | 4 +-- osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs | 2 +- .../Gameplay/TestSceneSkinnableDrawable.cs | 31 +++++++++---------- osu.Game/Skinning/PoolableSkinnableSample.cs | 13 ++------ osu.Game/Skinning/SkinManager.cs | 4 +-- osu.Game/Skinning/SkinReloadableDrawable.cs | 21 ++----------- osu.Game/Skinning/SkinnableDrawable.cs | 13 +++----- osu.Game/Skinning/SkinnableSprite.cs | 5 ++- osu.Game/Skinning/SkinnableSpriteText.cs | 8 ++--- osu.Game/Skinning/SkinnableTargetContainer.cs | 4 +-- .../Drawables/DrawableStoryboardSample.cs | 4 +-- 14 files changed, 45 insertions(+), 76 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatchComboDisplay.cs b/osu.Game.Rulesets.Catch/UI/CatchComboDisplay.cs index 75feb21298..ad344ff2dd 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchComboDisplay.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchComboDisplay.cs @@ -25,9 +25,9 @@ namespace osu.Game.Rulesets.Catch.UI { } - protected override void SkinChanged(ISkinSource skin, bool allowFallback) + protected override void SkinChanged(ISkinSource skin) { - base.SkinChanged(skin, allowFallback); + base.SkinChanged(skin); ComboCounter?.UpdateCombo(currentCombo); } diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 0d6a577d1e..b8c4d8f036 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -399,9 +399,9 @@ namespace osu.Game.Rulesets.Catch.UI private void updateTrailVisibility() => trails.DisplayTrail = Dashing || HyperDashing; - protected override void SkinChanged(ISkinSource skin, bool allowFallback) + protected override void SkinChanged(ISkinSource skin) { - base.SkinChanged(skin, allowFallback); + base.SkinChanged(skin); hyperDashColour = skin.GetConfig(CatchSkinColour.HyperDash)?.Value ?? diff --git a/osu.Game.Rulesets.Mania/UI/Components/HitObjectArea.cs b/osu.Game.Rulesets.Mania/UI/Components/HitObjectArea.cs index 8f7880dafa..b75b586ecf 100644 --- a/osu.Game.Rulesets.Mania/UI/Components/HitObjectArea.cs +++ b/osu.Game.Rulesets.Mania/UI/Components/HitObjectArea.cs @@ -33,9 +33,9 @@ namespace osu.Game.Rulesets.Mania.UI.Components Direction.BindValueChanged(onDirectionChanged, true); } - protected override void SkinChanged(ISkinSource skin, bool allowFallback) + protected override void SkinChanged(ISkinSource skin) { - base.SkinChanged(skin, allowFallback); + base.SkinChanged(skin); UpdateHitPosition(); } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/SkinnableLighting.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/SkinnableLighting.cs index 02dc770285..c72080c9e5 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/SkinnableLighting.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/SkinnableLighting.cs @@ -18,9 +18,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { } - protected override void SkinChanged(ISkinSource skin, bool allowFallback) + protected override void SkinChanged(ISkinSource skin) { - base.SkinChanged(skin, allowFallback); + base.SkinChanged(skin); updateColour(); } diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs index eea45c6c80..0e7d7cdcf3 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor Size = new Vector2(size); } - protected override void SkinChanged(ISkinSource skin, bool allowFallback) + protected override void SkinChanged(ISkinSource skin) { cursorExpand = skin.GetConfig(OsuSkinConfiguration.CursorExpand)?.Value ?? true; } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs index 7a6e2f54c2..38da7f7104 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs @@ -42,9 +42,9 @@ namespace osu.Game.Tests.Visual.Gameplay Spacing = new Vector2(10), Children = new[] { - new ExposedSkinnableDrawable("default", _ => new DefaultBox(), _ => true), - new ExposedSkinnableDrawable("available", _ => new DefaultBox(), _ => true), - new ExposedSkinnableDrawable("available", _ => new DefaultBox(), _ => true, ConfineMode.NoScaling) + new ExposedSkinnableDrawable("default", _ => new DefaultBox()), + new ExposedSkinnableDrawable("available", _ => new DefaultBox()), + new ExposedSkinnableDrawable("available", _ => new DefaultBox(), ConfineMode.NoScaling) } }, }; @@ -73,9 +73,9 @@ namespace osu.Game.Tests.Visual.Gameplay Spacing = new Vector2(10), Children = new[] { - new ExposedSkinnableDrawable("default", _ => new DefaultBox(), _ => true), - new ExposedSkinnableDrawable("available", _ => new DefaultBox(), _ => true, ConfineMode.ScaleToFit), - new ExposedSkinnableDrawable("available", _ => new DefaultBox(), _ => true, ConfineMode.NoScaling) + new ExposedSkinnableDrawable("default", _ => new DefaultBox()), + new ExposedSkinnableDrawable("available", _ => new DefaultBox(), ConfineMode.ScaleToFit), + new ExposedSkinnableDrawable("available", _ => new DefaultBox(), ConfineMode.NoScaling) } }, }; @@ -100,7 +100,7 @@ namespace osu.Game.Tests.Visual.Gameplay Child = new SkinProvidingContainer(secondarySource) { RelativeSizeAxes = Axes.Both, - Child = consumer = new SkinConsumer("test", name => new NamedBox("Default Implementation"), source => true) + Child = consumer = new SkinConsumer("test", name => new NamedBox("Default Implementation")) } }; }); @@ -129,7 +129,7 @@ namespace osu.Game.Tests.Visual.Gameplay }; }); - AddStep("add permissive", () => target.Add(consumer = new SkinConsumer("test", name => new NamedBox("Default Implementation"), source => true))); + AddStep("add permissive", () => target.Add(consumer = new SkinConsumer("test", name => new NamedBox("Default Implementation")))); AddAssert("consumer using override source", () => consumer.Drawable is SecondarySourceBox); AddAssert("skinchanged only called once", () => consumer.SkinChangedCount == 1); } @@ -152,7 +152,7 @@ namespace osu.Game.Tests.Visual.Gameplay }; }); - AddStep("add permissive", () => target.Add(consumer = new SkinConsumer("test", name => new NamedBox("Default Implementation"), source => true))); + AddStep("add permissive", () => target.Add(consumer = new SkinConsumer("test", name => new NamedBox("Default Implementation")))); AddAssert("consumer using override source", () => consumer.Drawable is SecondarySourceBox); AddStep("disable", () => target.Disable()); AddAssert("consumer using base source", () => consumer.Drawable is BaseSourceBox); @@ -180,9 +180,8 @@ namespace osu.Game.Tests.Visual.Gameplay { public new Drawable Drawable => base.Drawable; - public ExposedSkinnableDrawable(string name, Func defaultImplementation, Func allowFallback = null, - ConfineMode confineMode = ConfineMode.ScaleToFit) - : base(new TestSkinComponent(name), defaultImplementation, allowFallback, confineMode) + public ExposedSkinnableDrawable(string name, Func defaultImplementation, ConfineMode confineMode = ConfineMode.ScaleToFit) + : base(new TestSkinComponent(name), defaultImplementation, confineMode) { } } @@ -250,14 +249,14 @@ namespace osu.Game.Tests.Visual.Gameplay public new Drawable Drawable => base.Drawable; public int SkinChangedCount { get; private set; } - public SkinConsumer(string name, Func defaultImplementation, Func allowFallback = null) - : base(new TestSkinComponent(name), defaultImplementation, allowFallback) + public SkinConsumer(string name, Func defaultImplementation) + : base(new TestSkinComponent(name), defaultImplementation) { } - protected override void SkinChanged(ISkinSource skin, bool allowFallback) + protected override void SkinChanged(ISkinSource skin) { - base.SkinChanged(skin, allowFallback); + base.SkinChanged(skin); SkinChangedCount++; } } diff --git a/osu.Game/Skinning/PoolableSkinnableSample.cs b/osu.Game/Skinning/PoolableSkinnableSample.cs index b04158a58f..33e8c137f4 100644 --- a/osu.Game/Skinning/PoolableSkinnableSample.cs +++ b/osu.Game/Skinning/PoolableSkinnableSample.cs @@ -70,9 +70,9 @@ namespace osu.Game.Skinning updateSample(); } - protected override void SkinChanged(ISkinSource skin, bool allowFallback) + protected override void SkinChanged(ISkinSource skin) { - base.SkinChanged(skin, allowFallback); + base.SkinChanged(skin); updateSample(); } @@ -88,15 +88,6 @@ namespace osu.Game.Skinning var sample = CurrentSkin.GetSample(sampleInfo); - if (sample == null && AllowDefaultFallback) - { - foreach (var lookup in sampleInfo.LookupNames) - { - if ((sample = sampleStore.Get(lookup)) != null) - break; - } - } - if (sample == null) return; diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 66c776d32d..503c99d023 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -56,9 +56,9 @@ namespace osu.Game.Skinning this.legacyDefaultResources = legacyDefaultResources; - CurrentSkin.Value = new DefaultSkin(this); - CurrentSkinInfo.ValueChanged += skin => CurrentSkin.Value = GetSkin(skin.NewValue); + + CurrentSkin.Value = new DefaultSkin(this); CurrentSkin.ValueChanged += skin => { if (skin.NewValue.SkinInfo != CurrentSkinInfo.Value) diff --git a/osu.Game/Skinning/SkinReloadableDrawable.cs b/osu.Game/Skinning/SkinReloadableDrawable.cs index 50b4143375..dec546b82d 100644 --- a/osu.Game/Skinning/SkinReloadableDrawable.cs +++ b/osu.Game/Skinning/SkinReloadableDrawable.cs @@ -22,22 +22,6 @@ namespace osu.Game.Skinning ///
protected ISkinSource CurrentSkin { get; private set; } - private readonly Func allowFallback; - - /// - /// Whether fallback to default skin should be allowed if the custom skin is missing this resource. - /// - protected bool AllowDefaultFallback => allowFallback == null || allowFallback.Invoke(CurrentSkin); - - /// - /// Create a new - /// - /// A conditional to decide whether to allow fallback to the default implementation if a skinned element is not present. - protected SkinReloadableDrawable(Func allowFallback = null) - { - this.allowFallback = allowFallback; - } - [BackgroundDependencyLoader] private void load(ISkinSource source) { @@ -58,7 +42,7 @@ namespace osu.Game.Skinning private void skinChanged() { - SkinChanged(CurrentSkin, AllowDefaultFallback); + SkinChanged(CurrentSkin); OnSkinChanged?.Invoke(); } @@ -66,8 +50,7 @@ namespace osu.Game.Skinning /// Called when a change is made to the skin. ///
/// The new skin. - /// Whether fallback to default skin should be allowed if the custom skin is missing this resource. - protected virtual void SkinChanged(ISkinSource skin, bool allowFallback) + protected virtual void SkinChanged(ISkinSource skin) { } diff --git a/osu.Game/Skinning/SkinnableDrawable.cs b/osu.Game/Skinning/SkinnableDrawable.cs index fc2730ca44..72f64e2e12 100644 --- a/osu.Game/Skinning/SkinnableDrawable.cs +++ b/osu.Game/Skinning/SkinnableDrawable.cs @@ -40,17 +40,14 @@ namespace osu.Game.Skinning /// /// The namespace-complete resource name for this skinnable element. /// A function to create the default skin implementation of this element. - /// A conditional to decide whether to allow fallback to the default implementation if a skinned element is not present. /// How (if at all) the should be resize to fit within our own bounds. - public SkinnableDrawable(ISkinComponent component, Func defaultImplementation = null, Func allowFallback = null, - ConfineMode confineMode = ConfineMode.NoScaling) - : this(component, allowFallback, confineMode) + public SkinnableDrawable(ISkinComponent component, Func defaultImplementation = null, ConfineMode confineMode = ConfineMode.NoScaling) + : this(component, confineMode) { createDefault = defaultImplementation; } - protected SkinnableDrawable(ISkinComponent component, Func allowFallback = null, ConfineMode confineMode = ConfineMode.NoScaling) - : base(allowFallback) + protected SkinnableDrawable(ISkinComponent component, ConfineMode confineMode = ConfineMode.NoScaling) { this.component = component; this.confineMode = confineMode; @@ -76,13 +73,13 @@ namespace osu.Game.Skinning /// protected virtual bool ApplySizeRestrictionsToDefault => false; - protected override void SkinChanged(ISkinSource skin, bool allowFallback) + protected override void SkinChanged(ISkinSource skin) { Drawable = skin.GetDrawableComponent(component); isDefault = false; - if (Drawable == null && allowFallback) + if (Drawable == null) { Drawable = CreateDefault(component); isDefault = true; diff --git a/osu.Game/Skinning/SkinnableSprite.cs b/osu.Game/Skinning/SkinnableSprite.cs index 1340d1474c..56e576d081 100644 --- a/osu.Game/Skinning/SkinnableSprite.cs +++ b/osu.Game/Skinning/SkinnableSprite.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.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; @@ -19,8 +18,8 @@ namespace osu.Game.Skinning [Resolved] private TextureStore textures { get; set; } - public SkinnableSprite(string textureName, Func allowFallback = null, ConfineMode confineMode = ConfineMode.NoScaling) - : base(new SpriteComponent(textureName), allowFallback, confineMode) + public SkinnableSprite(string textureName, ConfineMode confineMode = ConfineMode.NoScaling) + : base(new SpriteComponent(textureName), confineMode) { } diff --git a/osu.Game/Skinning/SkinnableSpriteText.cs b/osu.Game/Skinning/SkinnableSpriteText.cs index 06461127b1..2bde3c4180 100644 --- a/osu.Game/Skinning/SkinnableSpriteText.cs +++ b/osu.Game/Skinning/SkinnableSpriteText.cs @@ -9,14 +9,14 @@ namespace osu.Game.Skinning { public class SkinnableSpriteText : SkinnableDrawable, IHasText { - public SkinnableSpriteText(ISkinComponent component, Func defaultImplementation, Func allowFallback = null, ConfineMode confineMode = ConfineMode.NoScaling) - : base(component, defaultImplementation, allowFallback, confineMode) + public SkinnableSpriteText(ISkinComponent component, Func defaultImplementation, ConfineMode confineMode = ConfineMode.NoScaling) + : base(component, defaultImplementation, confineMode) { } - protected override void SkinChanged(ISkinSource skin, bool allowFallback) + protected override void SkinChanged(ISkinSource skin) { - base.SkinChanged(skin, allowFallback); + base.SkinChanged(skin); if (Drawable is IHasText textDrawable) textDrawable.Text = Text; diff --git a/osu.Game/Skinning/SkinnableTargetContainer.cs b/osu.Game/Skinning/SkinnableTargetContainer.cs index d454e199dc..1338462dd6 100644 --- a/osu.Game/Skinning/SkinnableTargetContainer.cs +++ b/osu.Game/Skinning/SkinnableTargetContainer.cs @@ -73,9 +73,9 @@ namespace osu.Game.Skinning components.Remove(component); } - protected override void SkinChanged(ISkinSource skin, bool allowFallback) + protected override void SkinChanged(ISkinSource skin) { - base.SkinChanged(skin, allowFallback); + base.SkinChanged(skin); Reload(); } diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs index fbdd27e762..672274a2ad 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs @@ -31,9 +31,9 @@ namespace osu.Game.Storyboards.Drawables [Resolved] private IBindable> mods { get; set; } - protected override void SkinChanged(ISkinSource skin, bool allowFallback) + protected override void SkinChanged(ISkinSource skin) { - base.SkinChanged(skin, allowFallback); + base.SkinChanged(skin); foreach (var mod in mods.Value.OfType()) { From b13b732e02e3efb0a7a58cf96b03e1bf7d9a0d05 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 27 May 2021 14:50:56 +0900 Subject: [PATCH 516/839] Remove incorrect `DefaultSkin` usage --- osu.Game/Beatmaps/WorkingBeatmap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 3576b149bf..73a167b7aa 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -324,7 +324,7 @@ namespace osu.Game.Beatmaps public bool SkinLoaded => skin.IsResultAvailable; public ISkin Skin => skin.Value; - protected virtual ISkin GetSkin() => new DefaultSkin(null); + protected virtual ISkin GetSkin() => null; private readonly RecyclableLazy skin; public abstract Stream GetStream(string storagePath); From 122bb05aa81d78af1671cf8e00f21e06134a5798 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 27 May 2021 15:20:35 +0900 Subject: [PATCH 517/839] Add a mention that `OnApply/OnFree` is performed after `ApplyDefaults` --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index cc663c37af..cca55819c5 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -311,6 +311,7 @@ namespace osu.Game.Rulesets.Objects.Drawables /// /// Invoked for this to take on any values from a newly-applied . + /// This is also fired after any changes which occurred via an call. /// protected virtual void OnApply() { @@ -318,6 +319,7 @@ namespace osu.Game.Rulesets.Objects.Drawables /// /// Invoked for this to revert any values previously taken on from the currently-applied . + /// This is also fired after any changes which occurred via an call. /// protected virtual void OnFree() { From 4fbd43fcaec8cc6af378cd750bf3165a061ff35d Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 27 May 2021 13:24:06 +0700 Subject: [PATCH 518/839] add inline comment for width 2 --- osu.Game/Overlays/Wiki/WikiMainPage.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/Wiki/WikiMainPage.cs b/osu.Game/Overlays/Wiki/WikiMainPage.cs index bf4a76c3d8..2356ec03a6 100644 --- a/osu.Game/Overlays/Wiki/WikiMainPage.cs +++ b/osu.Game/Overlays/Wiki/WikiMainPage.cs @@ -79,6 +79,7 @@ namespace osu.Game.Overlays.Wiki { new WikiPanelContainer(panelsNode[i].InnerText, true) { + // This is required to fill up the space of "null" drawable below. Width = 2, }, null, From c39ea857012ca12f6591b737cfb212cffdf71a4a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 27 May 2021 15:39:35 +0900 Subject: [PATCH 519/839] Fix `TestSceneSkinnableSound` not doing DI correctly --- .../Visual/Gameplay/TestSceneSkinnableSound.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs index d792405eeb..0e3d22ff1d 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs @@ -29,14 +29,13 @@ namespace osu.Game.Tests.Visual.Gameplay { AddStep("setup hierarchy", () => { - Children = new Drawable[] + Child = skinSource = new TestSkinSourceContainer { - skinSource = new TestSkinSourceContainer - { - RelativeSizeAxes = Axes.Both, - Child = skinnableSound = new PausableSkinnableSound(new SampleInfo("Gameplay/normal-sliderslide")) - }, + RelativeSizeAxes = Axes.Both, }; + + // 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"))); }); } From c72e258bfb857e229bc8c46faffc9d12ec2ae238 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 27 May 2021 13:34:55 +0700 Subject: [PATCH 520/839] change for to while in create panels --- osu.Game/Overlays/Wiki/WikiMainPage.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Wiki/WikiMainPage.cs b/osu.Game/Overlays/Wiki/WikiMainPage.cs index 2356ec03a6..76e6b0261a 100644 --- a/osu.Game/Overlays/Wiki/WikiMainPage.cs +++ b/osu.Game/Overlays/Wiki/WikiMainPage.cs @@ -69,7 +69,9 @@ namespace osu.Game.Overlays.Wiki { var panelsNode = html.DocumentNode.SelectNodes("//div[contains(@class, 'wiki-main-page-panel')]").ToArray(); - for (var i = 0; i < panelsNode.Length; i++) + var i = 0; + + while (i < panelsNode.Length) { var isFullWidth = panelsNode[i].HasClass("wiki-main-page-panel--full"); @@ -77,7 +79,7 @@ namespace osu.Game.Overlays.Wiki { yield return new Drawable[] { - new WikiPanelContainer(panelsNode[i].InnerText, true) + new WikiPanelContainer(panelsNode[i++].InnerText, true) { // This is required to fill up the space of "null" drawable below. Width = 2, @@ -85,13 +87,12 @@ namespace osu.Game.Overlays.Wiki null, }; } - - if (i % 2 == 1) + else { yield return new Drawable[] { - new WikiPanelContainer(panelsNode[i].InnerText), - new WikiPanelContainer(panelsNode[i + 1].InnerText), + new WikiPanelContainer(panelsNode[i++].InnerText), + new WikiPanelContainer(panelsNode[i++].InnerText), }; } } From 5c44083856ca22055275fa4271933e2f024dbd3d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 27 May 2021 16:12:49 +0900 Subject: [PATCH 521/839] Fix test potentially not waiting for drawable beatmaps to be loaded --- .../Visual/SongSelect/TestSceneBeatmapCarousel.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs index 44c9361ff8..78ddfa9ed2 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs @@ -786,9 +786,12 @@ namespace osu.Game.Tests.Visual.SongSelect } } - private void checkVisibleItemCount(bool diff, int count) => - AddAssert($"{count} {(diff ? "diffs" : "sets")} visible", () => + private void checkVisibleItemCount(bool diff, int count) + { + // 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); + } private void checkNoSelection() => AddAssert("Selection is null", () => currentSelection == null); From b55ac413e7ca8953450319d3212ed9a1208d1ea2 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 27 May 2021 14:20:36 +0700 Subject: [PATCH 522/839] add many scenario of main page layout --- .../Visual/Online/TestSceneWikiMainPage.cs | 99 +++++++++++++++++-- 1 file changed, 93 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs index 3a2bafb128..7fa1a6e135 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.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 NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -15,7 +16,10 @@ namespace osu.Game.Tests.Visual.Online [Cached] private readonly OverlayColourProvider overlayColour = new OverlayColourProvider(OverlayColourScheme.Orange); - public TestSceneWikiMainPage() + private BasicScrollContainer container; + + [SetUp] + public void Setup() => Schedule(() => { Children = new Drawable[] { @@ -24,20 +28,103 @@ namespace osu.Game.Tests.Visual.Online Colour = overlayColour.Background5, RelativeSizeAxes = Axes.Both, }, - new BasicScrollContainer + container = new BasicScrollContainer { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding(20), - Child = new WikiMainPage - { - Markdown = main_page_markdown - } } }; + }); + + [Test] + public void TestWikiMainPage() + { + AddStep("Show main page", () => + { + container.Child = new WikiMainPage + { + Markdown = main_page_markdown, + }; + }); + } + + [Test] + public void TestNonFullWidthLayout() + { + AddStep("Show Layout", () => + { + container.Child = new WikiMainPage + { + Markdown = non_full_width, + }; + }); + } + + [Test] + public void TestFullWidthAtTheEnd() + { + AddStep("Show layout", () => + { + container.Child = new WikiMainPage + { + Markdown = full_width_end, + }; + }); + } + + [Test] + public void TestFullWidthAtTheStartAndEnd() + { + AddStep("Show layout", () => + { + container.Child = new WikiMainPage + { + Markdown = full_width_start_end, + }; + }); + } + + [Test] + public void TestFullWidthInTheMiddle() + { + AddStep("Show layout", () => + { + container.Child = new WikiMainPage + { + Markdown = full_width_in_the_middle, + }; + }); + } + + [Test] + public void TestFullWidthStacking() + { + AddStep("Show layout", () => + { + container.Child = new WikiMainPage + { + Markdown = full_width_stack, + }; + }); } // From https://osu.ppy.sh/api/v2/wiki/en/Main_Page private const string main_page_markdown = "---\nlayout: main_page\n---\n\n\n\n
\nWelcome to the osu! wiki, a project containing a wide range of osu! related information.\n
\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# Game client\n\n[Interface](/wiki/Interface) • [Options](/wiki/Options) • [Visual settings](/wiki/Visual_Settings) • [Shortcut key reference](/wiki/Shortcut_key_reference) • [Configuration file](/wiki/osu!_Program_Files/User_Configuration_File) • [Program files](/wiki/osu!_Program_Files)\n\n[File formats](/wiki/osu!_File_Formats): [.osz](/wiki/osu!_File_Formats/Osz_(file_format)) • [.osk](/wiki/osu!_File_Formats/Osk_(file_format)) • [.osr](/wiki/osu!_File_Formats/Osr_(file_format)) • [.osu](/wiki/osu!_File_Formats/Osu_(file_format)) • [.osb](/wiki/osu!_File_Formats/Osb_(file_format)) • [.db](/wiki/osu!_File_Formats/Db_(file_format))\n\n
\n
\n\n# Gameplay\n\n[Game modes](/wiki/Game_mode): [osu!](/wiki/Game_mode/osu!) • [osu!taiko](/wiki/Game_mode/osu!taiko) • [osu!catch](/wiki/Game_mode/osu!catch) • [osu!mania](/wiki/Game_mode/osu!mania)\n\n[Beatmap](/wiki/Beatmap) • [Hit object](/wiki/Hit_object) • [Mods](/wiki/Game_modifier) • [Score](/wiki/Score) • [Replay](/wiki/Replay) • [Multi](/wiki/Multi)\n\n
\n
\n\n# [Beatmap editor](/wiki/Beatmap_Editor)\n\nSections: [Compose](/wiki/Beatmap_Editor/Compose) • [Design](/wiki/Beatmap_Editor/Design) • [Timing](/wiki/Beatmap_Editor/Timing) • [Song setup](/wiki/Beatmap_Editor/Song_Setup)\n\nComponents: [AiMod](/wiki/Beatmap_Editor/AiMod) • [Beat snap divisor](/wiki/Beatmap_Editor/Beat_Snap_Divisor) • [Distance snap](/wiki/Beatmap_Editor/Distance_Snap) • [Menu](/wiki/Beatmap_Editor/Menu) • [SB load](/wiki/Beatmap_Editor/SB_Load) • [Timelines](/wiki/Beatmap_Editor/Timelines)\n\n[Beatmapping](/wiki/Beatmapping) • [Difficulty](/wiki/Beatmap/Difficulty) • [Mapping techniques](/wiki/Mapping_Techniques) • [Storyboarding](/wiki/Storyboarding)\n\n
\n
\n\n# Beatmap submission and ranking\n\n[Submission](/wiki/Submission) • [Modding](/wiki/Modding) • [Ranking procedure](/wiki/Beatmap_ranking_procedure) • [Mappers' Guild](/wiki/Mappers_Guild) • [Project Loved](/wiki/Project_Loved)\n\n[Ranking criteria](/wiki/Ranking_Criteria): [osu!](/wiki/Ranking_Criteria/osu!) • [osu!taiko](/wiki/Ranking_Criteria/osu!taiko) • [osu!catch](/wiki/Ranking_Criteria/osu!catch) • [osu!mania](/wiki/Ranking_Criteria/osu!mania)\n\n
\n
\n\n# Community\n\n[Tournaments](/wiki/Tournaments) • [Skinning](/wiki/Skinning) • [Projects](/wiki/Projects) • [Guides](/wiki/Guides) • [osu!dev Discord server](/wiki/osu!dev_Discord_server) • [How you can help](/wiki/How_You_Can_Help!) • [Glossary](/wiki/Glossary)\n\n
\n
\n\n# People\n\n[The Team](/wiki/People/The_Team): [Developers](/wiki/People/The_Team/Developers) • [Global Moderation Team](/wiki/People/The_Team/Global_Moderation_Team) • [Support Team](/wiki/People/The_Team/Support_Team) • [Nomination Assessment Team](/wiki/People/The_Team/Nomination_Assessment_Team) • [Beatmap Nominators](/wiki/People/The_Team/Beatmap_Nominators) • [osu! Alumni](/wiki/People/The_Team/osu!_Alumni) • [Project Loved Team](/wiki/People/The_Team/Project_Loved_Team)\n\nOrganisations: [osu! UCI](/wiki/Organisations/osu!_UCI)\n\n[Community Contributors](/wiki/People/Community_Contributors) • [Users with unique titles](/wiki/People/Users_with_unique_titles)\n\n
\n
\n\n# For developers\n\n[API](/wiki/osu!api) • [Bot account](/wiki/Bot_account) • [Brand identity guidelines](/wiki/Brand_identity_guidelines)\n\n
\n
\n\n# About the wiki\n\n[Sitemap](/wiki/Sitemap) • [Contribution guide](/wiki/osu!_wiki_Contribution_Guide) • [Article styling criteria](/wiki/Article_Styling_Criteria) • [News styling criteria](/wiki/News_Styling_Criteria)\n\n
\n
\n"; + + private const string non_full_width = + "
\nWelcome to the osu! wiki, a project containing a wide range of osu! related information.\n
\n\n
\n
\n\n# Game client\n\n[Interface](/wiki/Interface) • [Options](/wiki/Options) • [Visual settings](/wiki/Visual_Settings) • [Shortcut key reference](/wiki/Shortcut_key_reference) • [Configuration file](/wiki/osu!_Program_Files/User_Configuration_File) • [Program files](/wiki/osu!_Program_Files)\n\n[File formats](/wiki/osu!_File_Formats): [.osz](/wiki/osu!_File_Formats/Osz_(file_format)) • [.osk](/wiki/osu!_File_Formats/Osk_(file_format)) • [.osr](/wiki/osu!_File_Formats/Osr_(file_format)) • [.osu](/wiki/osu!_File_Formats/Osu_(file_format)) • [.osb](/wiki/osu!_File_Formats/Osb_(file_format)) • [.db](/wiki/osu!_File_Formats/Db_(file_format))\n\n
\n
\n\n# Gameplay\n\n[Game modes](/wiki/Game_mode): [osu!](/wiki/Game_mode/osu!) • [osu!taiko](/wiki/Game_mode/osu!taiko) • [osu!catch](/wiki/Game_mode/osu!catch) • [osu!mania](/wiki/Game_mode/osu!mania)\n\n[Beatmap](/wiki/Beatmap) • [Hit object](/wiki/Hit_object) • [Mods](/wiki/Game_modifier) • [Score](/wiki/Score) • [Replay](/wiki/Replay) • [Multi](/wiki/Multi)\n\n
\n
\n\n# [Beatmap editor](/wiki/Beatmap_Editor)\n\nSections: [Compose](/wiki/Beatmap_Editor/Compose) • [Design](/wiki/Beatmap_Editor/Design) • [Timing](/wiki/Beatmap_Editor/Timing) • [Song setup](/wiki/Beatmap_Editor/Song_Setup)\n\nComponents: [AiMod](/wiki/Beatmap_Editor/AiMod) • [Beat snap divisor](/wiki/Beatmap_Editor/Beat_Snap_Divisor) • [Distance snap](/wiki/Beatmap_Editor/Distance_Snap) • [Menu](/wiki/Beatmap_Editor/Menu) • [SB load](/wiki/Beatmap_Editor/SB_Load) • [Timelines](/wiki/Beatmap_Editor/Timelines)\n\n[Beatmapping](/wiki/Beatmapping) • [Difficulty](/wiki/Beatmap/Difficulty) • [Mapping techniques](/wiki/Mapping_Techniques) • [Storyboarding](/wiki/Storyboarding)\n\n
\n
\n\n# Beatmap submission and ranking\n\n[Submission](/wiki/Submission) • [Modding](/wiki/Modding) • [Ranking procedure](/wiki/Beatmap_ranking_procedure) • [Mappers' Guild](/wiki/Mappers_Guild) • [Project Loved](/wiki/Project_Loved)\n\n[Ranking criteria](/wiki/Ranking_Criteria): [osu!](/wiki/Ranking_Criteria/osu!) • [osu!taiko](/wiki/Ranking_Criteria/osu!taiko) • [osu!catch](/wiki/Ranking_Criteria/osu!catch) • [osu!mania](/wiki/Ranking_Criteria/osu!mania)\n\n
\n
\n\n# Community\n\n[Tournaments](/wiki/Tournaments) • [Skinning](/wiki/Skinning) • [Projects](/wiki/Projects) • [Guides](/wiki/Guides) • [osu!dev Discord server](/wiki/osu!dev_Discord_server) • [How you can help](/wiki/How_You_Can_Help!) • [Glossary](/wiki/Glossary)\n\n
\n
\n\n# People\n\n[The Team](/wiki/People/The_Team): [Developers](/wiki/People/The_Team/Developers) • [Global Moderation Team](/wiki/People/The_Team/Global_Moderation_Team) • [Support Team](/wiki/People/The_Team/Support_Team) • [Nomination Assessment Team](/wiki/People/The_Team/Nomination_Assessment_Team) • [Beatmap Nominators](/wiki/People/The_Team/Beatmap_Nominators) • [osu! Alumni](/wiki/People/The_Team/osu!_Alumni) • [Project Loved Team](/wiki/People/The_Team/Project_Loved_Team)\n\nOrganisations: [osu! UCI](/wiki/Organisations/osu!_UCI)\n\n[Community Contributors](/wiki/People/Community_Contributors) • [Users with unique titles](/wiki/People/Users_with_unique_titles)\n\n
\n
\n\n# For developers\n\n[API](/wiki/osu!api) • [Bot account](/wiki/Bot_account) • [Brand identity guidelines](/wiki/Brand_identity_guidelines)\n\n
\n
\n\n# About the wiki\n\n[Sitemap](/wiki/Sitemap) • [Contribution guide](/wiki/osu!_wiki_Contribution_Guide) • [Article styling criteria](/wiki/Article_Styling_Criteria) • [News styling criteria](/wiki/News_Styling_Criteria)\n\n
\n
\n"; + + private const string full_width_end = + "
\nWelcome to the osu! wiki, a project containing a wide range of osu! related information.\n
\n\n
\n
\n\n# Game client\n\n[Interface](/wiki/Interface) • [Options](/wiki/Options) • [Visual settings](/wiki/Visual_Settings) • [Shortcut key reference](/wiki/Shortcut_key_reference) • [Configuration file](/wiki/osu!_Program_Files/User_Configuration_File) • [Program files](/wiki/osu!_Program_Files)\n\n[File formats](/wiki/osu!_File_Formats): [.osz](/wiki/osu!_File_Formats/Osz_(file_format)) • [.osk](/wiki/osu!_File_Formats/Osk_(file_format)) • [.osr](/wiki/osu!_File_Formats/Osr_(file_format)) • [.osu](/wiki/osu!_File_Formats/Osu_(file_format)) • [.osb](/wiki/osu!_File_Formats/Osb_(file_format)) • [.db](/wiki/osu!_File_Formats/Db_(file_format))\n\n
\n
\n\n# Gameplay\n\n[Game modes](/wiki/Game_mode): [osu!](/wiki/Game_mode/osu!) • [osu!taiko](/wiki/Game_mode/osu!taiko) • [osu!catch](/wiki/Game_mode/osu!catch) • [osu!mania](/wiki/Game_mode/osu!mania)\n\n[Beatmap](/wiki/Beatmap) • [Hit object](/wiki/Hit_object) • [Mods](/wiki/Game_modifier) • [Score](/wiki/Score) • [Replay](/wiki/Replay) • [Multi](/wiki/Multi)\n\n
\n
\n\n# [Beatmap editor](/wiki/Beatmap_Editor)\n\nSections: [Compose](/wiki/Beatmap_Editor/Compose) • [Design](/wiki/Beatmap_Editor/Design) • [Timing](/wiki/Beatmap_Editor/Timing) • [Song setup](/wiki/Beatmap_Editor/Song_Setup)\n\nComponents: [AiMod](/wiki/Beatmap_Editor/AiMod) • [Beat snap divisor](/wiki/Beatmap_Editor/Beat_Snap_Divisor) • [Distance snap](/wiki/Beatmap_Editor/Distance_Snap) • [Menu](/wiki/Beatmap_Editor/Menu) • [SB load](/wiki/Beatmap_Editor/SB_Load) • [Timelines](/wiki/Beatmap_Editor/Timelines)\n\n[Beatmapping](/wiki/Beatmapping) • [Difficulty](/wiki/Beatmap/Difficulty) • [Mapping techniques](/wiki/Mapping_Techniques) • [Storyboarding](/wiki/Storyboarding)\n\n
\n
\n\n# Beatmap submission and ranking\n\n[Submission](/wiki/Submission) • [Modding](/wiki/Modding) • [Ranking procedure](/wiki/Beatmap_ranking_procedure) • [Mappers' Guild](/wiki/Mappers_Guild) • [Project Loved](/wiki/Project_Loved)\n\n[Ranking criteria](/wiki/Ranking_Criteria): [osu!](/wiki/Ranking_Criteria/osu!) • [osu!taiko](/wiki/Ranking_Criteria/osu!taiko) • [osu!catch](/wiki/Ranking_Criteria/osu!catch) • [osu!mania](/wiki/Ranking_Criteria/osu!mania)\n\n
\n
\n\n# Community\n\n[Tournaments](/wiki/Tournaments) • [Skinning](/wiki/Skinning) • [Projects](/wiki/Projects) • [Guides](/wiki/Guides) • [osu!dev Discord server](/wiki/osu!dev_Discord_server) • [How you can help](/wiki/How_You_Can_Help!) • [Glossary](/wiki/Glossary)\n\n
\n
\n\n# People\n\n[The Team](/wiki/People/The_Team): [Developers](/wiki/People/The_Team/Developers) • [Global Moderation Team](/wiki/People/The_Team/Global_Moderation_Team) • [Support Team](/wiki/People/The_Team/Support_Team) • [Nomination Assessment Team](/wiki/People/The_Team/Nomination_Assessment_Team) • [Beatmap Nominators](/wiki/People/The_Team/Beatmap_Nominators) • [osu! Alumni](/wiki/People/The_Team/osu!_Alumni) • [Project Loved Team](/wiki/People/The_Team/Project_Loved_Team)\n\nOrganisations: [osu! UCI](/wiki/Organisations/osu!_UCI)\n\n[Community Contributors](/wiki/People/Community_Contributors) • [Users with unique titles](/wiki/People/Users_with_unique_titles)\n\n
\n
\n\n# For developers\n\n[API](/wiki/osu!api) • [Bot account](/wiki/Bot_account) • [Brand identity guidelines](/wiki/Brand_identity_guidelines)\n\n
\n
\n\n# About the wiki\n\n[Sitemap](/wiki/Sitemap) • [Contribution guide](/wiki/osu!_wiki_Contribution_Guide) • [Article styling criteria](/wiki/Article_Styling_Criteria) • [News styling criteria](/wiki/News_Styling_Criteria)\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n"; + + private const string full_width_start_end = + "
\nWelcome to the osu! wiki, a project containing a wide range of osu! related information.\n
\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# Game client\n\n[Interface](/wiki/Interface) • [Options](/wiki/Options) • [Visual settings](/wiki/Visual_Settings) • [Shortcut key reference](/wiki/Shortcut_key_reference) • [Configuration file](/wiki/osu!_Program_Files/User_Configuration_File) • [Program files](/wiki/osu!_Program_Files)\n\n[File formats](/wiki/osu!_File_Formats): [.osz](/wiki/osu!_File_Formats/Osz_(file_format)) • [.osk](/wiki/osu!_File_Formats/Osk_(file_format)) • [.osr](/wiki/osu!_File_Formats/Osr_(file_format)) • [.osu](/wiki/osu!_File_Formats/Osu_(file_format)) • [.osb](/wiki/osu!_File_Formats/Osb_(file_format)) • [.db](/wiki/osu!_File_Formats/Db_(file_format))\n\n
\n
\n\n# Gameplay\n\n[Game modes](/wiki/Game_mode): [osu!](/wiki/Game_mode/osu!) • [osu!taiko](/wiki/Game_mode/osu!taiko) • [osu!catch](/wiki/Game_mode/osu!catch) • [osu!mania](/wiki/Game_mode/osu!mania)\n\n[Beatmap](/wiki/Beatmap) • [Hit object](/wiki/Hit_object) • [Mods](/wiki/Game_modifier) • [Score](/wiki/Score) • [Replay](/wiki/Replay) • [Multi](/wiki/Multi)\n\n
\n
\n\n# [Beatmap editor](/wiki/Beatmap_Editor)\n\nSections: [Compose](/wiki/Beatmap_Editor/Compose) • [Design](/wiki/Beatmap_Editor/Design) • [Timing](/wiki/Beatmap_Editor/Timing) • [Song setup](/wiki/Beatmap_Editor/Song_Setup)\n\nComponents: [AiMod](/wiki/Beatmap_Editor/AiMod) • [Beat snap divisor](/wiki/Beatmap_Editor/Beat_Snap_Divisor) • [Distance snap](/wiki/Beatmap_Editor/Distance_Snap) • [Menu](/wiki/Beatmap_Editor/Menu) • [SB load](/wiki/Beatmap_Editor/SB_Load) • [Timelines](/wiki/Beatmap_Editor/Timelines)\n\n[Beatmapping](/wiki/Beatmapping) • [Difficulty](/wiki/Beatmap/Difficulty) • [Mapping techniques](/wiki/Mapping_Techniques) • [Storyboarding](/wiki/Storyboarding)\n\n
\n
\n\n# Beatmap submission and ranking\n\n[Submission](/wiki/Submission) • [Modding](/wiki/Modding) • [Ranking procedure](/wiki/Beatmap_ranking_procedure) • [Mappers' Guild](/wiki/Mappers_Guild) • [Project Loved](/wiki/Project_Loved)\n\n[Ranking criteria](/wiki/Ranking_Criteria): [osu!](/wiki/Ranking_Criteria/osu!) • [osu!taiko](/wiki/Ranking_Criteria/osu!taiko) • [osu!catch](/wiki/Ranking_Criteria/osu!catch) • [osu!mania](/wiki/Ranking_Criteria/osu!mania)\n\n
\n
\n\n# Community\n\n[Tournaments](/wiki/Tournaments) • [Skinning](/wiki/Skinning) • [Projects](/wiki/Projects) • [Guides](/wiki/Guides) • [osu!dev Discord server](/wiki/osu!dev_Discord_server) • [How you can help](/wiki/How_You_Can_Help!) • [Glossary](/wiki/Glossary)\n\n
\n
\n\n# People\n\n[The Team](/wiki/People/The_Team): [Developers](/wiki/People/The_Team/Developers) • [Global Moderation Team](/wiki/People/The_Team/Global_Moderation_Team) • [Support Team](/wiki/People/The_Team/Support_Team) • [Nomination Assessment Team](/wiki/People/The_Team/Nomination_Assessment_Team) • [Beatmap Nominators](/wiki/People/The_Team/Beatmap_Nominators) • [osu! Alumni](/wiki/People/The_Team/osu!_Alumni) • [Project Loved Team](/wiki/People/The_Team/Project_Loved_Team)\n\nOrganisations: [osu! UCI](/wiki/Organisations/osu!_UCI)\n\n[Community Contributors](/wiki/People/Community_Contributors) • [Users with unique titles](/wiki/People/Users_with_unique_titles)\n\n
\n
\n\n# For developers\n\n[API](/wiki/osu!api) • [Bot account](/wiki/Bot_account) • [Brand identity guidelines](/wiki/Brand_identity_guidelines)\n\n
\n
\n\n# About the wiki\n\n[Sitemap](/wiki/Sitemap) • [Contribution guide](/wiki/osu!_wiki_Contribution_Guide) • [Article styling criteria](/wiki/Article_Styling_Criteria) • [News styling criteria](/wiki/News_Styling_Criteria)\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n"; + + private const string full_width_in_the_middle = + "
\nWelcome to the osu! wiki, a project containing a wide range of osu! related information.\n
\n\n
\n
\n\n# Game client\n\n[Interface](/wiki/Interface) • [Options](/wiki/Options) • [Visual settings](/wiki/Visual_Settings) • [Shortcut key reference](/wiki/Shortcut_key_reference) • [Configuration file](/wiki/osu!_Program_Files/User_Configuration_File) • [Program files](/wiki/osu!_Program_Files)\n\n[File formats](/wiki/osu!_File_Formats): [.osz](/wiki/osu!_File_Formats/Osz_(file_format)) • [.osk](/wiki/osu!_File_Formats/Osk_(file_format)) • [.osr](/wiki/osu!_File_Formats/Osr_(file_format)) • [.osu](/wiki/osu!_File_Formats/Osu_(file_format)) • [.osb](/wiki/osu!_File_Formats/Osb_(file_format)) • [.db](/wiki/osu!_File_Formats/Db_(file_format))\n\n
\n
\n\n# Gameplay\n\n[Game modes](/wiki/Game_mode): [osu!](/wiki/Game_mode/osu!) • [osu!taiko](/wiki/Game_mode/osu!taiko) • [osu!catch](/wiki/Game_mode/osu!catch) • [osu!mania](/wiki/Game_mode/osu!mania)\n\n[Beatmap](/wiki/Beatmap) • [Hit object](/wiki/Hit_object) • [Mods](/wiki/Game_modifier) • [Score](/wiki/Score) • [Replay](/wiki/Replay) • [Multi](/wiki/Multi)\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# [Beatmap editor](/wiki/Beatmap_Editor)\n\nSections: [Compose](/wiki/Beatmap_Editor/Compose) • [Design](/wiki/Beatmap_Editor/Design) • [Timing](/wiki/Beatmap_Editor/Timing) • [Song setup](/wiki/Beatmap_Editor/Song_Setup)\n\nComponents: [AiMod](/wiki/Beatmap_Editor/AiMod) • [Beat snap divisor](/wiki/Beatmap_Editor/Beat_Snap_Divisor) • [Distance snap](/wiki/Beatmap_Editor/Distance_Snap) • [Menu](/wiki/Beatmap_Editor/Menu) • [SB load](/wiki/Beatmap_Editor/SB_Load) • [Timelines](/wiki/Beatmap_Editor/Timelines)\n\n[Beatmapping](/wiki/Beatmapping) • [Difficulty](/wiki/Beatmap/Difficulty) • [Mapping techniques](/wiki/Mapping_Techniques) • [Storyboarding](/wiki/Storyboarding)\n\n
\n
\n\n# Beatmap submission and ranking\n\n[Submission](/wiki/Submission) • [Modding](/wiki/Modding) • [Ranking procedure](/wiki/Beatmap_ranking_procedure) • [Mappers' Guild](/wiki/Mappers_Guild) • [Project Loved](/wiki/Project_Loved)\n\n[Ranking criteria](/wiki/Ranking_Criteria): [osu!](/wiki/Ranking_Criteria/osu!) • [osu!taiko](/wiki/Ranking_Criteria/osu!taiko) • [osu!catch](/wiki/Ranking_Criteria/osu!catch) • [osu!mania](/wiki/Ranking_Criteria/osu!mania)\n\n
\n
\n\n# Community\n\n[Tournaments](/wiki/Tournaments) • [Skinning](/wiki/Skinning) • [Projects](/wiki/Projects) • [Guides](/wiki/Guides) • [osu!dev Discord server](/wiki/osu!dev_Discord_server) • [How you can help](/wiki/How_You_Can_Help!) • [Glossary](/wiki/Glossary)\n\n
\n
\n\n# People\n\n[The Team](/wiki/People/The_Team): [Developers](/wiki/People/The_Team/Developers) • [Global Moderation Team](/wiki/People/The_Team/Global_Moderation_Team) • [Support Team](/wiki/People/The_Team/Support_Team) • [Nomination Assessment Team](/wiki/People/The_Team/Nomination_Assessment_Team) • [Beatmap Nominators](/wiki/People/The_Team/Beatmap_Nominators) • [osu! Alumni](/wiki/People/The_Team/osu!_Alumni) • [Project Loved Team](/wiki/People/The_Team/Project_Loved_Team)\n\nOrganisations: [osu! UCI](/wiki/Organisations/osu!_UCI)\n\n[Community Contributors](/wiki/People/Community_Contributors) • [Users with unique titles](/wiki/People/Users_with_unique_titles)\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# For developers\n\n[API](/wiki/osu!api) • [Bot account](/wiki/Bot_account) • [Brand identity guidelines](/wiki/Brand_identity_guidelines)\n\n
\n
\n\n# About the wiki\n\n[Sitemap](/wiki/Sitemap) • [Contribution guide](/wiki/osu!_wiki_Contribution_Guide) • [Article styling criteria](/wiki/Article_Styling_Criteria) • [News styling criteria](/wiki/News_Styling_Criteria)\n\n
\n
\n"; + + private const string full_width_stack= + "
\nWelcome to the osu! wiki, a project containing a wide range of osu! related information.\n
\n\n
\n
\n\n# Game client\n\n[Interface](/wiki/Interface) • [Options](/wiki/Options) • [Visual settings](/wiki/Visual_Settings) • [Shortcut key reference](/wiki/Shortcut_key_reference) • [Configuration file](/wiki/osu!_Program_Files/User_Configuration_File) • [Program files](/wiki/osu!_Program_Files)\n\n[File formats](/wiki/osu!_File_Formats): [.osz](/wiki/osu!_File_Formats/Osz_(file_format)) • [.osk](/wiki/osu!_File_Formats/Osk_(file_format)) • [.osr](/wiki/osu!_File_Formats/Osr_(file_format)) • [.osu](/wiki/osu!_File_Formats/Osu_(file_format)) • [.osb](/wiki/osu!_File_Formats/Osb_(file_format)) • [.db](/wiki/osu!_File_Formats/Db_(file_format))\n\n
\n
\n\n# Gameplay\n\n[Game modes](/wiki/Game_mode): [osu!](/wiki/Game_mode/osu!) • [osu!taiko](/wiki/Game_mode/osu!taiko) • [osu!catch](/wiki/Game_mode/osu!catch) • [osu!mania](/wiki/Game_mode/osu!mania)\n\n[Beatmap](/wiki/Beatmap) • [Hit object](/wiki/Hit_object) • [Mods](/wiki/Game_modifier) • [Score](/wiki/Score) • [Replay](/wiki/Replay) • [Multi](/wiki/Multi)\n\n
\n
\n\n# [Beatmap editor](/wiki/Beatmap_Editor)\n\nSections: [Compose](/wiki/Beatmap_Editor/Compose) • [Design](/wiki/Beatmap_Editor/Design) • [Timing](/wiki/Beatmap_Editor/Timing) • [Song setup](/wiki/Beatmap_Editor/Song_Setup)\n\nComponents: [AiMod](/wiki/Beatmap_Editor/AiMod) • [Beat snap divisor](/wiki/Beatmap_Editor/Beat_Snap_Divisor) • [Distance snap](/wiki/Beatmap_Editor/Distance_Snap) • [Menu](/wiki/Beatmap_Editor/Menu) • [SB load](/wiki/Beatmap_Editor/SB_Load) • [Timelines](/wiki/Beatmap_Editor/Timelines)\n\n[Beatmapping](/wiki/Beatmapping) • [Difficulty](/wiki/Beatmap/Difficulty) • [Mapping techniques](/wiki/Mapping_Techniques) • [Storyboarding](/wiki/Storyboarding)\n\n
\n
\n\n# Beatmap submission and ranking\n\n[Submission](/wiki/Submission) • [Modding](/wiki/Modding) • [Ranking procedure](/wiki/Beatmap_ranking_procedure) • [Mappers' Guild](/wiki/Mappers_Guild) • [Project Loved](/wiki/Project_Loved)\n\n[Ranking criteria](/wiki/Ranking_Criteria): [osu!](/wiki/Ranking_Criteria/osu!) • [osu!taiko](/wiki/Ranking_Criteria/osu!taiko) • [osu!catch](/wiki/Ranking_Criteria/osu!catch) • [osu!mania](/wiki/Ranking_Criteria/osu!mania)\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# Community\n\n[Tournaments](/wiki/Tournaments) • [Skinning](/wiki/Skinning) • [Projects](/wiki/Projects) • [Guides](/wiki/Guides) • [osu!dev Discord server](/wiki/osu!dev_Discord_server) • [How you can help](/wiki/How_You_Can_Help!) • [Glossary](/wiki/Glossary)\n\n
\n
\n\n# People\n\n[The Team](/wiki/People/The_Team): [Developers](/wiki/People/The_Team/Developers) • [Global Moderation Team](/wiki/People/The_Team/Global_Moderation_Team) • [Support Team](/wiki/People/The_Team/Support_Team) • [Nomination Assessment Team](/wiki/People/The_Team/Nomination_Assessment_Team) • [Beatmap Nominators](/wiki/People/The_Team/Beatmap_Nominators) • [osu! Alumni](/wiki/People/The_Team/osu!_Alumni) • [Project Loved Team](/wiki/People/The_Team/Project_Loved_Team)\n\nOrganisations: [osu! UCI](/wiki/Organisations/osu!_UCI)\n\n[Community Contributors](/wiki/People/Community_Contributors) • [Users with unique titles](/wiki/People/Users_with_unique_titles)\n\n
\n
\n\n# For developers\n\n[API](/wiki/osu!api) • [Bot account](/wiki/Bot_account) • [Brand identity guidelines](/wiki/Brand_identity_guidelines)\n\n
\n
\n\n# About the wiki\n\n[Sitemap](/wiki/Sitemap) • [Contribution guide](/wiki/osu!_wiki_Contribution_Guide) • [Article styling criteria](/wiki/Article_Styling_Criteria) • [News styling criteria](/wiki/News_Styling_Criteria)\n\n
\n
\n"; } } From 820310543d56b23ed5fc99591da3f4e994a37ad2 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 27 May 2021 14:53:22 +0700 Subject: [PATCH 523/839] fix spacing style --- osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs index 7fa1a6e135..9fb3c685b7 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs @@ -124,7 +124,7 @@ namespace osu.Game.Tests.Visual.Online private const string full_width_in_the_middle = "
\nWelcome to the osu! wiki, a project containing a wide range of osu! related information.\n
\n\n
\n
\n\n# Game client\n\n[Interface](/wiki/Interface) • [Options](/wiki/Options) • [Visual settings](/wiki/Visual_Settings) • [Shortcut key reference](/wiki/Shortcut_key_reference) • [Configuration file](/wiki/osu!_Program_Files/User_Configuration_File) • [Program files](/wiki/osu!_Program_Files)\n\n[File formats](/wiki/osu!_File_Formats): [.osz](/wiki/osu!_File_Formats/Osz_(file_format)) • [.osk](/wiki/osu!_File_Formats/Osk_(file_format)) • [.osr](/wiki/osu!_File_Formats/Osr_(file_format)) • [.osu](/wiki/osu!_File_Formats/Osu_(file_format)) • [.osb](/wiki/osu!_File_Formats/Osb_(file_format)) • [.db](/wiki/osu!_File_Formats/Db_(file_format))\n\n
\n
\n\n# Gameplay\n\n[Game modes](/wiki/Game_mode): [osu!](/wiki/Game_mode/osu!) • [osu!taiko](/wiki/Game_mode/osu!taiko) • [osu!catch](/wiki/Game_mode/osu!catch) • [osu!mania](/wiki/Game_mode/osu!mania)\n\n[Beatmap](/wiki/Beatmap) • [Hit object](/wiki/Hit_object) • [Mods](/wiki/Game_modifier) • [Score](/wiki/Score) • [Replay](/wiki/Replay) • [Multi](/wiki/Multi)\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# [Beatmap editor](/wiki/Beatmap_Editor)\n\nSections: [Compose](/wiki/Beatmap_Editor/Compose) • [Design](/wiki/Beatmap_Editor/Design) • [Timing](/wiki/Beatmap_Editor/Timing) • [Song setup](/wiki/Beatmap_Editor/Song_Setup)\n\nComponents: [AiMod](/wiki/Beatmap_Editor/AiMod) • [Beat snap divisor](/wiki/Beatmap_Editor/Beat_Snap_Divisor) • [Distance snap](/wiki/Beatmap_Editor/Distance_Snap) • [Menu](/wiki/Beatmap_Editor/Menu) • [SB load](/wiki/Beatmap_Editor/SB_Load) • [Timelines](/wiki/Beatmap_Editor/Timelines)\n\n[Beatmapping](/wiki/Beatmapping) • [Difficulty](/wiki/Beatmap/Difficulty) • [Mapping techniques](/wiki/Mapping_Techniques) • [Storyboarding](/wiki/Storyboarding)\n\n
\n
\n\n# Beatmap submission and ranking\n\n[Submission](/wiki/Submission) • [Modding](/wiki/Modding) • [Ranking procedure](/wiki/Beatmap_ranking_procedure) • [Mappers' Guild](/wiki/Mappers_Guild) • [Project Loved](/wiki/Project_Loved)\n\n[Ranking criteria](/wiki/Ranking_Criteria): [osu!](/wiki/Ranking_Criteria/osu!) • [osu!taiko](/wiki/Ranking_Criteria/osu!taiko) • [osu!catch](/wiki/Ranking_Criteria/osu!catch) • [osu!mania](/wiki/Ranking_Criteria/osu!mania)\n\n
\n
\n\n# Community\n\n[Tournaments](/wiki/Tournaments) • [Skinning](/wiki/Skinning) • [Projects](/wiki/Projects) • [Guides](/wiki/Guides) • [osu!dev Discord server](/wiki/osu!dev_Discord_server) • [How you can help](/wiki/How_You_Can_Help!) • [Glossary](/wiki/Glossary)\n\n
\n
\n\n# People\n\n[The Team](/wiki/People/The_Team): [Developers](/wiki/People/The_Team/Developers) • [Global Moderation Team](/wiki/People/The_Team/Global_Moderation_Team) • [Support Team](/wiki/People/The_Team/Support_Team) • [Nomination Assessment Team](/wiki/People/The_Team/Nomination_Assessment_Team) • [Beatmap Nominators](/wiki/People/The_Team/Beatmap_Nominators) • [osu! Alumni](/wiki/People/The_Team/osu!_Alumni) • [Project Loved Team](/wiki/People/The_Team/Project_Loved_Team)\n\nOrganisations: [osu! UCI](/wiki/Organisations/osu!_UCI)\n\n[Community Contributors](/wiki/People/Community_Contributors) • [Users with unique titles](/wiki/People/Users_with_unique_titles)\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# For developers\n\n[API](/wiki/osu!api) • [Bot account](/wiki/Bot_account) • [Brand identity guidelines](/wiki/Brand_identity_guidelines)\n\n
\n
\n\n# About the wiki\n\n[Sitemap](/wiki/Sitemap) • [Contribution guide](/wiki/osu!_wiki_Contribution_Guide) • [Article styling criteria](/wiki/Article_Styling_Criteria) • [News styling criteria](/wiki/News_Styling_Criteria)\n\n
\n
\n"; - private const string full_width_stack= + private const string full_width_stack = "
\nWelcome to the osu! wiki, a project containing a wide range of osu! related information.\n
\n\n
\n
\n\n# Game client\n\n[Interface](/wiki/Interface) • [Options](/wiki/Options) • [Visual settings](/wiki/Visual_Settings) • [Shortcut key reference](/wiki/Shortcut_key_reference) • [Configuration file](/wiki/osu!_Program_Files/User_Configuration_File) • [Program files](/wiki/osu!_Program_Files)\n\n[File formats](/wiki/osu!_File_Formats): [.osz](/wiki/osu!_File_Formats/Osz_(file_format)) • [.osk](/wiki/osu!_File_Formats/Osk_(file_format)) • [.osr](/wiki/osu!_File_Formats/Osr_(file_format)) • [.osu](/wiki/osu!_File_Formats/Osu_(file_format)) • [.osb](/wiki/osu!_File_Formats/Osb_(file_format)) • [.db](/wiki/osu!_File_Formats/Db_(file_format))\n\n
\n
\n\n# Gameplay\n\n[Game modes](/wiki/Game_mode): [osu!](/wiki/Game_mode/osu!) • [osu!taiko](/wiki/Game_mode/osu!taiko) • [osu!catch](/wiki/Game_mode/osu!catch) • [osu!mania](/wiki/Game_mode/osu!mania)\n\n[Beatmap](/wiki/Beatmap) • [Hit object](/wiki/Hit_object) • [Mods](/wiki/Game_modifier) • [Score](/wiki/Score) • [Replay](/wiki/Replay) • [Multi](/wiki/Multi)\n\n
\n
\n\n# [Beatmap editor](/wiki/Beatmap_Editor)\n\nSections: [Compose](/wiki/Beatmap_Editor/Compose) • [Design](/wiki/Beatmap_Editor/Design) • [Timing](/wiki/Beatmap_Editor/Timing) • [Song setup](/wiki/Beatmap_Editor/Song_Setup)\n\nComponents: [AiMod](/wiki/Beatmap_Editor/AiMod) • [Beat snap divisor](/wiki/Beatmap_Editor/Beat_Snap_Divisor) • [Distance snap](/wiki/Beatmap_Editor/Distance_Snap) • [Menu](/wiki/Beatmap_Editor/Menu) • [SB load](/wiki/Beatmap_Editor/SB_Load) • [Timelines](/wiki/Beatmap_Editor/Timelines)\n\n[Beatmapping](/wiki/Beatmapping) • [Difficulty](/wiki/Beatmap/Difficulty) • [Mapping techniques](/wiki/Mapping_Techniques) • [Storyboarding](/wiki/Storyboarding)\n\n
\n
\n\n# Beatmap submission and ranking\n\n[Submission](/wiki/Submission) • [Modding](/wiki/Modding) • [Ranking procedure](/wiki/Beatmap_ranking_procedure) • [Mappers' Guild](/wiki/Mappers_Guild) • [Project Loved](/wiki/Project_Loved)\n\n[Ranking criteria](/wiki/Ranking_Criteria): [osu!](/wiki/Ranking_Criteria/osu!) • [osu!taiko](/wiki/Ranking_Criteria/osu!taiko) • [osu!catch](/wiki/Ranking_Criteria/osu!catch) • [osu!mania](/wiki/Ranking_Criteria/osu!mania)\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# Community\n\n[Tournaments](/wiki/Tournaments) • [Skinning](/wiki/Skinning) • [Projects](/wiki/Projects) • [Guides](/wiki/Guides) • [osu!dev Discord server](/wiki/osu!dev_Discord_server) • [How you can help](/wiki/How_You_Can_Help!) • [Glossary](/wiki/Glossary)\n\n
\n
\n\n# People\n\n[The Team](/wiki/People/The_Team): [Developers](/wiki/People/The_Team/Developers) • [Global Moderation Team](/wiki/People/The_Team/Global_Moderation_Team) • [Support Team](/wiki/People/The_Team/Support_Team) • [Nomination Assessment Team](/wiki/People/The_Team/Nomination_Assessment_Team) • [Beatmap Nominators](/wiki/People/The_Team/Beatmap_Nominators) • [osu! Alumni](/wiki/People/The_Team/osu!_Alumni) • [Project Loved Team](/wiki/People/The_Team/Project_Loved_Team)\n\nOrganisations: [osu! UCI](/wiki/Organisations/osu!_UCI)\n\n[Community Contributors](/wiki/People/Community_Contributors) • [Users with unique titles](/wiki/People/Users_with_unique_titles)\n\n
\n
\n\n# For developers\n\n[API](/wiki/osu!api) • [Bot account](/wiki/Bot_account) • [Brand identity guidelines](/wiki/Brand_identity_guidelines)\n\n
\n
\n\n# About the wiki\n\n[Sitemap](/wiki/Sitemap) • [Contribution guide](/wiki/osu!_wiki_Contribution_Guide) • [Article styling criteria](/wiki/Article_Styling_Criteria) • [News styling criteria](/wiki/News_Styling_Criteria)\n\n
\n
\n"; } } From 046087a367b6a608620b05f014a985e60232fdea Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 27 May 2021 16:58:01 +0900 Subject: [PATCH 524/839] Fix access to `AliveChildren` before `IsLoaded` --- osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index 5875685965..9773bd5ce9 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -36,7 +36,7 @@ namespace osu.Game.Screens.Select.Carousel [Resolved(CanBeNull = true)] private ManageCollectionsDialog manageCollectionsDialog { get; set; } - public IEnumerable DrawableBeatmaps => beatmapContainer?.AliveChildren ?? Enumerable.Empty(); + public IEnumerable DrawableBeatmaps => beatmapContainer?.IsLoaded != true ? Enumerable.Empty() : beatmapContainer.AliveChildren; [CanBeNull] private Container beatmapContainer; From 0b600db81487a766e9d91687c7a57e6ae8517cfb Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 27 May 2021 16:24:05 +0700 Subject: [PATCH 525/839] revert back main page test --- .../Visual/Online/TestSceneWikiMainPage.cs | 99 ++----------------- 1 file changed, 6 insertions(+), 93 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs index 9fb3c685b7..3a2bafb128 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.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 NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -16,10 +15,7 @@ namespace osu.Game.Tests.Visual.Online [Cached] private readonly OverlayColourProvider overlayColour = new OverlayColourProvider(OverlayColourScheme.Orange); - private BasicScrollContainer container; - - [SetUp] - public void Setup() => Schedule(() => + public TestSceneWikiMainPage() { Children = new Drawable[] { @@ -28,103 +24,20 @@ namespace osu.Game.Tests.Visual.Online Colour = overlayColour.Background5, RelativeSizeAxes = Axes.Both, }, - container = new BasicScrollContainer + new BasicScrollContainer { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding(20), + Child = new WikiMainPage + { + Markdown = main_page_markdown + } } }; - }); - - [Test] - public void TestWikiMainPage() - { - AddStep("Show main page", () => - { - container.Child = new WikiMainPage - { - Markdown = main_page_markdown, - }; - }); - } - - [Test] - public void TestNonFullWidthLayout() - { - AddStep("Show Layout", () => - { - container.Child = new WikiMainPage - { - Markdown = non_full_width, - }; - }); - } - - [Test] - public void TestFullWidthAtTheEnd() - { - AddStep("Show layout", () => - { - container.Child = new WikiMainPage - { - Markdown = full_width_end, - }; - }); - } - - [Test] - public void TestFullWidthAtTheStartAndEnd() - { - AddStep("Show layout", () => - { - container.Child = new WikiMainPage - { - Markdown = full_width_start_end, - }; - }); - } - - [Test] - public void TestFullWidthInTheMiddle() - { - AddStep("Show layout", () => - { - container.Child = new WikiMainPage - { - Markdown = full_width_in_the_middle, - }; - }); - } - - [Test] - public void TestFullWidthStacking() - { - AddStep("Show layout", () => - { - container.Child = new WikiMainPage - { - Markdown = full_width_stack, - }; - }); } // From https://osu.ppy.sh/api/v2/wiki/en/Main_Page private const string main_page_markdown = "---\nlayout: main_page\n---\n\n\n\n
\nWelcome to the osu! wiki, a project containing a wide range of osu! related information.\n
\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# Game client\n\n[Interface](/wiki/Interface) • [Options](/wiki/Options) • [Visual settings](/wiki/Visual_Settings) • [Shortcut key reference](/wiki/Shortcut_key_reference) • [Configuration file](/wiki/osu!_Program_Files/User_Configuration_File) • [Program files](/wiki/osu!_Program_Files)\n\n[File formats](/wiki/osu!_File_Formats): [.osz](/wiki/osu!_File_Formats/Osz_(file_format)) • [.osk](/wiki/osu!_File_Formats/Osk_(file_format)) • [.osr](/wiki/osu!_File_Formats/Osr_(file_format)) • [.osu](/wiki/osu!_File_Formats/Osu_(file_format)) • [.osb](/wiki/osu!_File_Formats/Osb_(file_format)) • [.db](/wiki/osu!_File_Formats/Db_(file_format))\n\n
\n
\n\n# Gameplay\n\n[Game modes](/wiki/Game_mode): [osu!](/wiki/Game_mode/osu!) • [osu!taiko](/wiki/Game_mode/osu!taiko) • [osu!catch](/wiki/Game_mode/osu!catch) • [osu!mania](/wiki/Game_mode/osu!mania)\n\n[Beatmap](/wiki/Beatmap) • [Hit object](/wiki/Hit_object) • [Mods](/wiki/Game_modifier) • [Score](/wiki/Score) • [Replay](/wiki/Replay) • [Multi](/wiki/Multi)\n\n
\n
\n\n# [Beatmap editor](/wiki/Beatmap_Editor)\n\nSections: [Compose](/wiki/Beatmap_Editor/Compose) • [Design](/wiki/Beatmap_Editor/Design) • [Timing](/wiki/Beatmap_Editor/Timing) • [Song setup](/wiki/Beatmap_Editor/Song_Setup)\n\nComponents: [AiMod](/wiki/Beatmap_Editor/AiMod) • [Beat snap divisor](/wiki/Beatmap_Editor/Beat_Snap_Divisor) • [Distance snap](/wiki/Beatmap_Editor/Distance_Snap) • [Menu](/wiki/Beatmap_Editor/Menu) • [SB load](/wiki/Beatmap_Editor/SB_Load) • [Timelines](/wiki/Beatmap_Editor/Timelines)\n\n[Beatmapping](/wiki/Beatmapping) • [Difficulty](/wiki/Beatmap/Difficulty) • [Mapping techniques](/wiki/Mapping_Techniques) • [Storyboarding](/wiki/Storyboarding)\n\n
\n
\n\n# Beatmap submission and ranking\n\n[Submission](/wiki/Submission) • [Modding](/wiki/Modding) • [Ranking procedure](/wiki/Beatmap_ranking_procedure) • [Mappers' Guild](/wiki/Mappers_Guild) • [Project Loved](/wiki/Project_Loved)\n\n[Ranking criteria](/wiki/Ranking_Criteria): [osu!](/wiki/Ranking_Criteria/osu!) • [osu!taiko](/wiki/Ranking_Criteria/osu!taiko) • [osu!catch](/wiki/Ranking_Criteria/osu!catch) • [osu!mania](/wiki/Ranking_Criteria/osu!mania)\n\n
\n
\n\n# Community\n\n[Tournaments](/wiki/Tournaments) • [Skinning](/wiki/Skinning) • [Projects](/wiki/Projects) • [Guides](/wiki/Guides) • [osu!dev Discord server](/wiki/osu!dev_Discord_server) • [How you can help](/wiki/How_You_Can_Help!) • [Glossary](/wiki/Glossary)\n\n
\n
\n\n# People\n\n[The Team](/wiki/People/The_Team): [Developers](/wiki/People/The_Team/Developers) • [Global Moderation Team](/wiki/People/The_Team/Global_Moderation_Team) • [Support Team](/wiki/People/The_Team/Support_Team) • [Nomination Assessment Team](/wiki/People/The_Team/Nomination_Assessment_Team) • [Beatmap Nominators](/wiki/People/The_Team/Beatmap_Nominators) • [osu! Alumni](/wiki/People/The_Team/osu!_Alumni) • [Project Loved Team](/wiki/People/The_Team/Project_Loved_Team)\n\nOrganisations: [osu! UCI](/wiki/Organisations/osu!_UCI)\n\n[Community Contributors](/wiki/People/Community_Contributors) • [Users with unique titles](/wiki/People/Users_with_unique_titles)\n\n
\n
\n\n# For developers\n\n[API](/wiki/osu!api) • [Bot account](/wiki/Bot_account) • [Brand identity guidelines](/wiki/Brand_identity_guidelines)\n\n
\n
\n\n# About the wiki\n\n[Sitemap](/wiki/Sitemap) • [Contribution guide](/wiki/osu!_wiki_Contribution_Guide) • [Article styling criteria](/wiki/Article_Styling_Criteria) • [News styling criteria](/wiki/News_Styling_Criteria)\n\n
\n
\n"; - - private const string non_full_width = - "
\nWelcome to the osu! wiki, a project containing a wide range of osu! related information.\n
\n\n
\n
\n\n# Game client\n\n[Interface](/wiki/Interface) • [Options](/wiki/Options) • [Visual settings](/wiki/Visual_Settings) • [Shortcut key reference](/wiki/Shortcut_key_reference) • [Configuration file](/wiki/osu!_Program_Files/User_Configuration_File) • [Program files](/wiki/osu!_Program_Files)\n\n[File formats](/wiki/osu!_File_Formats): [.osz](/wiki/osu!_File_Formats/Osz_(file_format)) • [.osk](/wiki/osu!_File_Formats/Osk_(file_format)) • [.osr](/wiki/osu!_File_Formats/Osr_(file_format)) • [.osu](/wiki/osu!_File_Formats/Osu_(file_format)) • [.osb](/wiki/osu!_File_Formats/Osb_(file_format)) • [.db](/wiki/osu!_File_Formats/Db_(file_format))\n\n
\n
\n\n# Gameplay\n\n[Game modes](/wiki/Game_mode): [osu!](/wiki/Game_mode/osu!) • [osu!taiko](/wiki/Game_mode/osu!taiko) • [osu!catch](/wiki/Game_mode/osu!catch) • [osu!mania](/wiki/Game_mode/osu!mania)\n\n[Beatmap](/wiki/Beatmap) • [Hit object](/wiki/Hit_object) • [Mods](/wiki/Game_modifier) • [Score](/wiki/Score) • [Replay](/wiki/Replay) • [Multi](/wiki/Multi)\n\n
\n
\n\n# [Beatmap editor](/wiki/Beatmap_Editor)\n\nSections: [Compose](/wiki/Beatmap_Editor/Compose) • [Design](/wiki/Beatmap_Editor/Design) • [Timing](/wiki/Beatmap_Editor/Timing) • [Song setup](/wiki/Beatmap_Editor/Song_Setup)\n\nComponents: [AiMod](/wiki/Beatmap_Editor/AiMod) • [Beat snap divisor](/wiki/Beatmap_Editor/Beat_Snap_Divisor) • [Distance snap](/wiki/Beatmap_Editor/Distance_Snap) • [Menu](/wiki/Beatmap_Editor/Menu) • [SB load](/wiki/Beatmap_Editor/SB_Load) • [Timelines](/wiki/Beatmap_Editor/Timelines)\n\n[Beatmapping](/wiki/Beatmapping) • [Difficulty](/wiki/Beatmap/Difficulty) • [Mapping techniques](/wiki/Mapping_Techniques) • [Storyboarding](/wiki/Storyboarding)\n\n
\n
\n\n# Beatmap submission and ranking\n\n[Submission](/wiki/Submission) • [Modding](/wiki/Modding) • [Ranking procedure](/wiki/Beatmap_ranking_procedure) • [Mappers' Guild](/wiki/Mappers_Guild) • [Project Loved](/wiki/Project_Loved)\n\n[Ranking criteria](/wiki/Ranking_Criteria): [osu!](/wiki/Ranking_Criteria/osu!) • [osu!taiko](/wiki/Ranking_Criteria/osu!taiko) • [osu!catch](/wiki/Ranking_Criteria/osu!catch) • [osu!mania](/wiki/Ranking_Criteria/osu!mania)\n\n
\n
\n\n# Community\n\n[Tournaments](/wiki/Tournaments) • [Skinning](/wiki/Skinning) • [Projects](/wiki/Projects) • [Guides](/wiki/Guides) • [osu!dev Discord server](/wiki/osu!dev_Discord_server) • [How you can help](/wiki/How_You_Can_Help!) • [Glossary](/wiki/Glossary)\n\n
\n
\n\n# People\n\n[The Team](/wiki/People/The_Team): [Developers](/wiki/People/The_Team/Developers) • [Global Moderation Team](/wiki/People/The_Team/Global_Moderation_Team) • [Support Team](/wiki/People/The_Team/Support_Team) • [Nomination Assessment Team](/wiki/People/The_Team/Nomination_Assessment_Team) • [Beatmap Nominators](/wiki/People/The_Team/Beatmap_Nominators) • [osu! Alumni](/wiki/People/The_Team/osu!_Alumni) • [Project Loved Team](/wiki/People/The_Team/Project_Loved_Team)\n\nOrganisations: [osu! UCI](/wiki/Organisations/osu!_UCI)\n\n[Community Contributors](/wiki/People/Community_Contributors) • [Users with unique titles](/wiki/People/Users_with_unique_titles)\n\n
\n
\n\n# For developers\n\n[API](/wiki/osu!api) • [Bot account](/wiki/Bot_account) • [Brand identity guidelines](/wiki/Brand_identity_guidelines)\n\n
\n
\n\n# About the wiki\n\n[Sitemap](/wiki/Sitemap) • [Contribution guide](/wiki/osu!_wiki_Contribution_Guide) • [Article styling criteria](/wiki/Article_Styling_Criteria) • [News styling criteria](/wiki/News_Styling_Criteria)\n\n
\n
\n"; - - private const string full_width_end = - "
\nWelcome to the osu! wiki, a project containing a wide range of osu! related information.\n
\n\n
\n
\n\n# Game client\n\n[Interface](/wiki/Interface) • [Options](/wiki/Options) • [Visual settings](/wiki/Visual_Settings) • [Shortcut key reference](/wiki/Shortcut_key_reference) • [Configuration file](/wiki/osu!_Program_Files/User_Configuration_File) • [Program files](/wiki/osu!_Program_Files)\n\n[File formats](/wiki/osu!_File_Formats): [.osz](/wiki/osu!_File_Formats/Osz_(file_format)) • [.osk](/wiki/osu!_File_Formats/Osk_(file_format)) • [.osr](/wiki/osu!_File_Formats/Osr_(file_format)) • [.osu](/wiki/osu!_File_Formats/Osu_(file_format)) • [.osb](/wiki/osu!_File_Formats/Osb_(file_format)) • [.db](/wiki/osu!_File_Formats/Db_(file_format))\n\n
\n
\n\n# Gameplay\n\n[Game modes](/wiki/Game_mode): [osu!](/wiki/Game_mode/osu!) • [osu!taiko](/wiki/Game_mode/osu!taiko) • [osu!catch](/wiki/Game_mode/osu!catch) • [osu!mania](/wiki/Game_mode/osu!mania)\n\n[Beatmap](/wiki/Beatmap) • [Hit object](/wiki/Hit_object) • [Mods](/wiki/Game_modifier) • [Score](/wiki/Score) • [Replay](/wiki/Replay) • [Multi](/wiki/Multi)\n\n
\n
\n\n# [Beatmap editor](/wiki/Beatmap_Editor)\n\nSections: [Compose](/wiki/Beatmap_Editor/Compose) • [Design](/wiki/Beatmap_Editor/Design) • [Timing](/wiki/Beatmap_Editor/Timing) • [Song setup](/wiki/Beatmap_Editor/Song_Setup)\n\nComponents: [AiMod](/wiki/Beatmap_Editor/AiMod) • [Beat snap divisor](/wiki/Beatmap_Editor/Beat_Snap_Divisor) • [Distance snap](/wiki/Beatmap_Editor/Distance_Snap) • [Menu](/wiki/Beatmap_Editor/Menu) • [SB load](/wiki/Beatmap_Editor/SB_Load) • [Timelines](/wiki/Beatmap_Editor/Timelines)\n\n[Beatmapping](/wiki/Beatmapping) • [Difficulty](/wiki/Beatmap/Difficulty) • [Mapping techniques](/wiki/Mapping_Techniques) • [Storyboarding](/wiki/Storyboarding)\n\n
\n
\n\n# Beatmap submission and ranking\n\n[Submission](/wiki/Submission) • [Modding](/wiki/Modding) • [Ranking procedure](/wiki/Beatmap_ranking_procedure) • [Mappers' Guild](/wiki/Mappers_Guild) • [Project Loved](/wiki/Project_Loved)\n\n[Ranking criteria](/wiki/Ranking_Criteria): [osu!](/wiki/Ranking_Criteria/osu!) • [osu!taiko](/wiki/Ranking_Criteria/osu!taiko) • [osu!catch](/wiki/Ranking_Criteria/osu!catch) • [osu!mania](/wiki/Ranking_Criteria/osu!mania)\n\n
\n
\n\n# Community\n\n[Tournaments](/wiki/Tournaments) • [Skinning](/wiki/Skinning) • [Projects](/wiki/Projects) • [Guides](/wiki/Guides) • [osu!dev Discord server](/wiki/osu!dev_Discord_server) • [How you can help](/wiki/How_You_Can_Help!) • [Glossary](/wiki/Glossary)\n\n
\n
\n\n# People\n\n[The Team](/wiki/People/The_Team): [Developers](/wiki/People/The_Team/Developers) • [Global Moderation Team](/wiki/People/The_Team/Global_Moderation_Team) • [Support Team](/wiki/People/The_Team/Support_Team) • [Nomination Assessment Team](/wiki/People/The_Team/Nomination_Assessment_Team) • [Beatmap Nominators](/wiki/People/The_Team/Beatmap_Nominators) • [osu! Alumni](/wiki/People/The_Team/osu!_Alumni) • [Project Loved Team](/wiki/People/The_Team/Project_Loved_Team)\n\nOrganisations: [osu! UCI](/wiki/Organisations/osu!_UCI)\n\n[Community Contributors](/wiki/People/Community_Contributors) • [Users with unique titles](/wiki/People/Users_with_unique_titles)\n\n
\n
\n\n# For developers\n\n[API](/wiki/osu!api) • [Bot account](/wiki/Bot_account) • [Brand identity guidelines](/wiki/Brand_identity_guidelines)\n\n
\n
\n\n# About the wiki\n\n[Sitemap](/wiki/Sitemap) • [Contribution guide](/wiki/osu!_wiki_Contribution_Guide) • [Article styling criteria](/wiki/Article_Styling_Criteria) • [News styling criteria](/wiki/News_Styling_Criteria)\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n"; - - private const string full_width_start_end = - "
\nWelcome to the osu! wiki, a project containing a wide range of osu! related information.\n
\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# Game client\n\n[Interface](/wiki/Interface) • [Options](/wiki/Options) • [Visual settings](/wiki/Visual_Settings) • [Shortcut key reference](/wiki/Shortcut_key_reference) • [Configuration file](/wiki/osu!_Program_Files/User_Configuration_File) • [Program files](/wiki/osu!_Program_Files)\n\n[File formats](/wiki/osu!_File_Formats): [.osz](/wiki/osu!_File_Formats/Osz_(file_format)) • [.osk](/wiki/osu!_File_Formats/Osk_(file_format)) • [.osr](/wiki/osu!_File_Formats/Osr_(file_format)) • [.osu](/wiki/osu!_File_Formats/Osu_(file_format)) • [.osb](/wiki/osu!_File_Formats/Osb_(file_format)) • [.db](/wiki/osu!_File_Formats/Db_(file_format))\n\n
\n
\n\n# Gameplay\n\n[Game modes](/wiki/Game_mode): [osu!](/wiki/Game_mode/osu!) • [osu!taiko](/wiki/Game_mode/osu!taiko) • [osu!catch](/wiki/Game_mode/osu!catch) • [osu!mania](/wiki/Game_mode/osu!mania)\n\n[Beatmap](/wiki/Beatmap) • [Hit object](/wiki/Hit_object) • [Mods](/wiki/Game_modifier) • [Score](/wiki/Score) • [Replay](/wiki/Replay) • [Multi](/wiki/Multi)\n\n
\n
\n\n# [Beatmap editor](/wiki/Beatmap_Editor)\n\nSections: [Compose](/wiki/Beatmap_Editor/Compose) • [Design](/wiki/Beatmap_Editor/Design) • [Timing](/wiki/Beatmap_Editor/Timing) • [Song setup](/wiki/Beatmap_Editor/Song_Setup)\n\nComponents: [AiMod](/wiki/Beatmap_Editor/AiMod) • [Beat snap divisor](/wiki/Beatmap_Editor/Beat_Snap_Divisor) • [Distance snap](/wiki/Beatmap_Editor/Distance_Snap) • [Menu](/wiki/Beatmap_Editor/Menu) • [SB load](/wiki/Beatmap_Editor/SB_Load) • [Timelines](/wiki/Beatmap_Editor/Timelines)\n\n[Beatmapping](/wiki/Beatmapping) • [Difficulty](/wiki/Beatmap/Difficulty) • [Mapping techniques](/wiki/Mapping_Techniques) • [Storyboarding](/wiki/Storyboarding)\n\n
\n
\n\n# Beatmap submission and ranking\n\n[Submission](/wiki/Submission) • [Modding](/wiki/Modding) • [Ranking procedure](/wiki/Beatmap_ranking_procedure) • [Mappers' Guild](/wiki/Mappers_Guild) • [Project Loved](/wiki/Project_Loved)\n\n[Ranking criteria](/wiki/Ranking_Criteria): [osu!](/wiki/Ranking_Criteria/osu!) • [osu!taiko](/wiki/Ranking_Criteria/osu!taiko) • [osu!catch](/wiki/Ranking_Criteria/osu!catch) • [osu!mania](/wiki/Ranking_Criteria/osu!mania)\n\n
\n
\n\n# Community\n\n[Tournaments](/wiki/Tournaments) • [Skinning](/wiki/Skinning) • [Projects](/wiki/Projects) • [Guides](/wiki/Guides) • [osu!dev Discord server](/wiki/osu!dev_Discord_server) • [How you can help](/wiki/How_You_Can_Help!) • [Glossary](/wiki/Glossary)\n\n
\n
\n\n# People\n\n[The Team](/wiki/People/The_Team): [Developers](/wiki/People/The_Team/Developers) • [Global Moderation Team](/wiki/People/The_Team/Global_Moderation_Team) • [Support Team](/wiki/People/The_Team/Support_Team) • [Nomination Assessment Team](/wiki/People/The_Team/Nomination_Assessment_Team) • [Beatmap Nominators](/wiki/People/The_Team/Beatmap_Nominators) • [osu! Alumni](/wiki/People/The_Team/osu!_Alumni) • [Project Loved Team](/wiki/People/The_Team/Project_Loved_Team)\n\nOrganisations: [osu! UCI](/wiki/Organisations/osu!_UCI)\n\n[Community Contributors](/wiki/People/Community_Contributors) • [Users with unique titles](/wiki/People/Users_with_unique_titles)\n\n
\n
\n\n# For developers\n\n[API](/wiki/osu!api) • [Bot account](/wiki/Bot_account) • [Brand identity guidelines](/wiki/Brand_identity_guidelines)\n\n
\n
\n\n# About the wiki\n\n[Sitemap](/wiki/Sitemap) • [Contribution guide](/wiki/osu!_wiki_Contribution_Guide) • [Article styling criteria](/wiki/Article_Styling_Criteria) • [News styling criteria](/wiki/News_Styling_Criteria)\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n"; - - private const string full_width_in_the_middle = - "
\nWelcome to the osu! wiki, a project containing a wide range of osu! related information.\n
\n\n
\n
\n\n# Game client\n\n[Interface](/wiki/Interface) • [Options](/wiki/Options) • [Visual settings](/wiki/Visual_Settings) • [Shortcut key reference](/wiki/Shortcut_key_reference) • [Configuration file](/wiki/osu!_Program_Files/User_Configuration_File) • [Program files](/wiki/osu!_Program_Files)\n\n[File formats](/wiki/osu!_File_Formats): [.osz](/wiki/osu!_File_Formats/Osz_(file_format)) • [.osk](/wiki/osu!_File_Formats/Osk_(file_format)) • [.osr](/wiki/osu!_File_Formats/Osr_(file_format)) • [.osu](/wiki/osu!_File_Formats/Osu_(file_format)) • [.osb](/wiki/osu!_File_Formats/Osb_(file_format)) • [.db](/wiki/osu!_File_Formats/Db_(file_format))\n\n
\n
\n\n# Gameplay\n\n[Game modes](/wiki/Game_mode): [osu!](/wiki/Game_mode/osu!) • [osu!taiko](/wiki/Game_mode/osu!taiko) • [osu!catch](/wiki/Game_mode/osu!catch) • [osu!mania](/wiki/Game_mode/osu!mania)\n\n[Beatmap](/wiki/Beatmap) • [Hit object](/wiki/Hit_object) • [Mods](/wiki/Game_modifier) • [Score](/wiki/Score) • [Replay](/wiki/Replay) • [Multi](/wiki/Multi)\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# [Beatmap editor](/wiki/Beatmap_Editor)\n\nSections: [Compose](/wiki/Beatmap_Editor/Compose) • [Design](/wiki/Beatmap_Editor/Design) • [Timing](/wiki/Beatmap_Editor/Timing) • [Song setup](/wiki/Beatmap_Editor/Song_Setup)\n\nComponents: [AiMod](/wiki/Beatmap_Editor/AiMod) • [Beat snap divisor](/wiki/Beatmap_Editor/Beat_Snap_Divisor) • [Distance snap](/wiki/Beatmap_Editor/Distance_Snap) • [Menu](/wiki/Beatmap_Editor/Menu) • [SB load](/wiki/Beatmap_Editor/SB_Load) • [Timelines](/wiki/Beatmap_Editor/Timelines)\n\n[Beatmapping](/wiki/Beatmapping) • [Difficulty](/wiki/Beatmap/Difficulty) • [Mapping techniques](/wiki/Mapping_Techniques) • [Storyboarding](/wiki/Storyboarding)\n\n
\n
\n\n# Beatmap submission and ranking\n\n[Submission](/wiki/Submission) • [Modding](/wiki/Modding) • [Ranking procedure](/wiki/Beatmap_ranking_procedure) • [Mappers' Guild](/wiki/Mappers_Guild) • [Project Loved](/wiki/Project_Loved)\n\n[Ranking criteria](/wiki/Ranking_Criteria): [osu!](/wiki/Ranking_Criteria/osu!) • [osu!taiko](/wiki/Ranking_Criteria/osu!taiko) • [osu!catch](/wiki/Ranking_Criteria/osu!catch) • [osu!mania](/wiki/Ranking_Criteria/osu!mania)\n\n
\n
\n\n# Community\n\n[Tournaments](/wiki/Tournaments) • [Skinning](/wiki/Skinning) • [Projects](/wiki/Projects) • [Guides](/wiki/Guides) • [osu!dev Discord server](/wiki/osu!dev_Discord_server) • [How you can help](/wiki/How_You_Can_Help!) • [Glossary](/wiki/Glossary)\n\n
\n
\n\n# People\n\n[The Team](/wiki/People/The_Team): [Developers](/wiki/People/The_Team/Developers) • [Global Moderation Team](/wiki/People/The_Team/Global_Moderation_Team) • [Support Team](/wiki/People/The_Team/Support_Team) • [Nomination Assessment Team](/wiki/People/The_Team/Nomination_Assessment_Team) • [Beatmap Nominators](/wiki/People/The_Team/Beatmap_Nominators) • [osu! Alumni](/wiki/People/The_Team/osu!_Alumni) • [Project Loved Team](/wiki/People/The_Team/Project_Loved_Team)\n\nOrganisations: [osu! UCI](/wiki/Organisations/osu!_UCI)\n\n[Community Contributors](/wiki/People/Community_Contributors) • [Users with unique titles](/wiki/People/Users_with_unique_titles)\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# For developers\n\n[API](/wiki/osu!api) • [Bot account](/wiki/Bot_account) • [Brand identity guidelines](/wiki/Brand_identity_guidelines)\n\n
\n
\n\n# About the wiki\n\n[Sitemap](/wiki/Sitemap) • [Contribution guide](/wiki/osu!_wiki_Contribution_Guide) • [Article styling criteria](/wiki/Article_Styling_Criteria) • [News styling criteria](/wiki/News_Styling_Criteria)\n\n
\n
\n"; - - private const string full_width_stack = - "
\nWelcome to the osu! wiki, a project containing a wide range of osu! related information.\n
\n\n
\n
\n\n# Game client\n\n[Interface](/wiki/Interface) • [Options](/wiki/Options) • [Visual settings](/wiki/Visual_Settings) • [Shortcut key reference](/wiki/Shortcut_key_reference) • [Configuration file](/wiki/osu!_Program_Files/User_Configuration_File) • [Program files](/wiki/osu!_Program_Files)\n\n[File formats](/wiki/osu!_File_Formats): [.osz](/wiki/osu!_File_Formats/Osz_(file_format)) • [.osk](/wiki/osu!_File_Formats/Osk_(file_format)) • [.osr](/wiki/osu!_File_Formats/Osr_(file_format)) • [.osu](/wiki/osu!_File_Formats/Osu_(file_format)) • [.osb](/wiki/osu!_File_Formats/Osb_(file_format)) • [.db](/wiki/osu!_File_Formats/Db_(file_format))\n\n
\n
\n\n# Gameplay\n\n[Game modes](/wiki/Game_mode): [osu!](/wiki/Game_mode/osu!) • [osu!taiko](/wiki/Game_mode/osu!taiko) • [osu!catch](/wiki/Game_mode/osu!catch) • [osu!mania](/wiki/Game_mode/osu!mania)\n\n[Beatmap](/wiki/Beatmap) • [Hit object](/wiki/Hit_object) • [Mods](/wiki/Game_modifier) • [Score](/wiki/Score) • [Replay](/wiki/Replay) • [Multi](/wiki/Multi)\n\n
\n
\n\n# [Beatmap editor](/wiki/Beatmap_Editor)\n\nSections: [Compose](/wiki/Beatmap_Editor/Compose) • [Design](/wiki/Beatmap_Editor/Design) • [Timing](/wiki/Beatmap_Editor/Timing) • [Song setup](/wiki/Beatmap_Editor/Song_Setup)\n\nComponents: [AiMod](/wiki/Beatmap_Editor/AiMod) • [Beat snap divisor](/wiki/Beatmap_Editor/Beat_Snap_Divisor) • [Distance snap](/wiki/Beatmap_Editor/Distance_Snap) • [Menu](/wiki/Beatmap_Editor/Menu) • [SB load](/wiki/Beatmap_Editor/SB_Load) • [Timelines](/wiki/Beatmap_Editor/Timelines)\n\n[Beatmapping](/wiki/Beatmapping) • [Difficulty](/wiki/Beatmap/Difficulty) • [Mapping techniques](/wiki/Mapping_Techniques) • [Storyboarding](/wiki/Storyboarding)\n\n
\n
\n\n# Beatmap submission and ranking\n\n[Submission](/wiki/Submission) • [Modding](/wiki/Modding) • [Ranking procedure](/wiki/Beatmap_ranking_procedure) • [Mappers' Guild](/wiki/Mappers_Guild) • [Project Loved](/wiki/Project_Loved)\n\n[Ranking criteria](/wiki/Ranking_Criteria): [osu!](/wiki/Ranking_Criteria/osu!) • [osu!taiko](/wiki/Ranking_Criteria/osu!taiko) • [osu!catch](/wiki/Ranking_Criteria/osu!catch) • [osu!mania](/wiki/Ranking_Criteria/osu!mania)\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# Community\n\n[Tournaments](/wiki/Tournaments) • [Skinning](/wiki/Skinning) • [Projects](/wiki/Projects) • [Guides](/wiki/Guides) • [osu!dev Discord server](/wiki/osu!dev_Discord_server) • [How you can help](/wiki/How_You_Can_Help!) • [Glossary](/wiki/Glossary)\n\n
\n
\n\n# People\n\n[The Team](/wiki/People/The_Team): [Developers](/wiki/People/The_Team/Developers) • [Global Moderation Team](/wiki/People/The_Team/Global_Moderation_Team) • [Support Team](/wiki/People/The_Team/Support_Team) • [Nomination Assessment Team](/wiki/People/The_Team/Nomination_Assessment_Team) • [Beatmap Nominators](/wiki/People/The_Team/Beatmap_Nominators) • [osu! Alumni](/wiki/People/The_Team/osu!_Alumni) • [Project Loved Team](/wiki/People/The_Team/Project_Loved_Team)\n\nOrganisations: [osu! UCI](/wiki/Organisations/osu!_UCI)\n\n[Community Contributors](/wiki/People/Community_Contributors) • [Users with unique titles](/wiki/People/Users_with_unique_titles)\n\n
\n
\n\n# For developers\n\n[API](/wiki/osu!api) • [Bot account](/wiki/Bot_account) • [Brand identity guidelines](/wiki/Brand_identity_guidelines)\n\n
\n
\n\n# About the wiki\n\n[Sitemap](/wiki/Sitemap) • [Contribution guide](/wiki/osu!_wiki_Contribution_Guide) • [Article styling criteria](/wiki/Article_Styling_Criteria) • [News styling criteria](/wiki/News_Styling_Criteria)\n\n
\n
\n"; } } From 37ef368738139e0214dc046693d67e928c939813 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 27 May 2021 19:03:59 +0900 Subject: [PATCH 526/839] Move async call out of `using` to better define the flow of data --- osu.Game/Collections/CollectionManager.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Collections/CollectionManager.cs b/osu.Game/Collections/CollectionManager.cs index 3a63587b30..086cc573d5 100644 --- a/osu.Game/Collections/CollectionManager.cs +++ b/osu.Game/Collections/CollectionManager.cs @@ -58,8 +58,13 @@ namespace osu.Game.Collections if (storage.Exists(database_name)) { + List beatmapCollections; + using (var stream = storage.GetStream(database_name)) - importCollections(readCollections(stream)); + beatmapCollections = readCollections(stream); + + // intentionally fire-and-forget async. + importCollections(beatmapCollections); } } From e1836cd1b2535a2d0329e9d0a71979bd2796a414 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 27 May 2021 17:12:15 +0700 Subject: [PATCH 527/839] add debug assert --- osu.Game/Overlays/Wiki/WikiMainPage.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/Wiki/WikiMainPage.cs b/osu.Game/Overlays/Wiki/WikiMainPage.cs index 76e6b0261a..bbbbc602bf 100644 --- a/osu.Game/Overlays/Wiki/WikiMainPage.cs +++ b/osu.Game/Overlays/Wiki/WikiMainPage.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -69,6 +70,8 @@ namespace osu.Game.Overlays.Wiki { var panelsNode = html.DocumentNode.SelectNodes("//div[contains(@class, 'wiki-main-page-panel')]").ToArray(); + Debug.Assert(panelsNode.Length > 1); + var i = 0; while (i < panelsNode.Length) From 0c4d4ee0d2f4792964a5b2630ec90c780853d208 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 27 May 2021 19:16:22 +0900 Subject: [PATCH 528/839] Fix collection import tests deadlocking due to `TaskCompletionSource` continuation triggering host disposal --- .../Collections/IO/ImportCollectionsTest.cs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Collections/IO/ImportCollectionsTest.cs b/osu.Game.Tests/Collections/IO/ImportCollectionsTest.cs index a8ee1bcc2e..040c0d7165 100644 --- a/osu.Game.Tests/Collections/IO/ImportCollectionsTest.cs +++ b/osu.Game.Tests/Collections/IO/ImportCollectionsTest.cs @@ -23,7 +23,7 @@ namespace osu.Game.Tests.Collections.IO { var osu = LoadOsuIntoHost(host); - await osu.CollectionManager.Import(new MemoryStream()); + await importCollectionsFromStream(osu, new MemoryStream()); Assert.That(osu.CollectionManager.Collections.Count, Is.Zero); } @@ -36,14 +36,14 @@ namespace osu.Game.Tests.Collections.IO [Test] public async Task TestImportWithNoBeatmaps() - { +{ using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) { try { var osu = LoadOsuIntoHost(host); - await osu.CollectionManager.Import(TestResources.OpenResource("Collections/collections.db")); + await importCollectionsFromStream(osu, TestResources.OpenResource("Collections/collections.db")); Assert.That(osu.CollectionManager.Collections.Count, Is.EqualTo(2)); @@ -69,7 +69,7 @@ namespace osu.Game.Tests.Collections.IO { var osu = LoadOsuIntoHost(host, true); - await osu.CollectionManager.Import(TestResources.OpenResource("Collections/collections.db")); + await importCollectionsFromStream(osu, TestResources.OpenResource("Collections/collections.db")); Assert.That(osu.CollectionManager.Collections.Count, Is.EqualTo(2)); @@ -110,7 +110,7 @@ namespace osu.Game.Tests.Collections.IO ms.Seek(0, SeekOrigin.Begin); - await osu.CollectionManager.Import(ms); + await importCollectionsFromStream(osu, ms); } Assert.That(host.UpdateThread.Running, Is.True); @@ -134,7 +134,7 @@ namespace osu.Game.Tests.Collections.IO { var osu = LoadOsuIntoHost(host, true); - await osu.CollectionManager.Import(TestResources.OpenResource("Collections/collections.db")); + await importCollectionsFromStream(osu, TestResources.OpenResource("Collections/collections.db")); // Move first beatmap from second collection into the first. osu.CollectionManager.Collections[0].Beatmaps.Add(osu.CollectionManager.Collections[1].Beatmaps[0]); @@ -169,5 +169,12 @@ namespace osu.Game.Tests.Collections.IO } } } + + private static async Task importCollectionsFromStream(TestOsuGameBase osu, Stream stream) + { + // intentionally spin this up on a separate task to avoid disposal deadlocks. + // see https://github.com/EventStore/EventStore/issues/1179 + await Task.Run(() => osu.CollectionManager.Import(stream).Wait()); + } } } From a2ed85bf460c128425a264a9409d6544b50cc4d5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 27 May 2021 19:34:39 +0900 Subject: [PATCH 529/839] Fix broken formatting --- osu.Game.Tests/Collections/IO/ImportCollectionsTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Collections/IO/ImportCollectionsTest.cs b/osu.Game.Tests/Collections/IO/ImportCollectionsTest.cs index 040c0d7165..a47631a83b 100644 --- a/osu.Game.Tests/Collections/IO/ImportCollectionsTest.cs +++ b/osu.Game.Tests/Collections/IO/ImportCollectionsTest.cs @@ -36,7 +36,7 @@ namespace osu.Game.Tests.Collections.IO [Test] public async Task TestImportWithNoBeatmaps() -{ + { using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) { try From bcf1e3db1ed995edc4236f18f90e18150db5a4b1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 27 May 2021 19:45:53 +0900 Subject: [PATCH 530/839] Fix test failures in `TestSceneStoryboardWithOutro` Test was not accounting for the fact that the results may not have loaded in time. --- osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs index 0ac8e01482..5ef3eff856 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs @@ -53,7 +53,8 @@ namespace osu.Game.Tests.Visual.Gameplay CreateTest(null); AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value); AddStep("skip outro", () => InputManager.Key(osuTK.Input.Key.Space)); - AddAssert("score shown", () => Player.IsScoreShown); + AddUntilStep("wait for score shown", () => Player.IsScoreShown); + AddUntilStep("time less than storyboard duration", () => Player.GameplayClockContainer.GameplayClock.CurrentTime < currentStoryboardDuration); } [Test] From 121dd175e67be92c3796462b5fa6e7f04124c5d4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 27 May 2021 19:57:19 +0900 Subject: [PATCH 531/839] Fix test failure in `TestSceneMultiplayerGameplayLeaderboard` The transfer of users was not accounting for the fact that the `StartPlay` calls are now scheduled and not necessarily run in time. --- .../TestSceneMultiplayerGameplayLeaderboard.cs | 8 ++++++-- osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs | 3 +++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs index 80b9aa8228..af2f6fa5fe 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs @@ -73,8 +73,11 @@ namespace osu.Game.Tests.Visual.Multiplayer for (int i = 0; i < users; i++) spectatorClient.StartPlay(i, Beatmap.Value.BeatmapInfo.OnlineBeatmapID ?? 0); - Client.CurrentMatchPlayingUserIds.Clear(); - Client.CurrentMatchPlayingUserIds.AddRange(spectatorClient.PlayingUsers); + spectatorClient.Schedule(() => + { + Client.CurrentMatchPlayingUserIds.Clear(); + Client.CurrentMatchPlayingUserIds.AddRange(spectatorClient.PlayingUsers); + }); Children = new Drawable[] { @@ -91,6 +94,7 @@ namespace osu.Game.Tests.Visual.Multiplayer }); AddUntilStep("wait for load", () => leaderboard.IsLoaded); + AddUntilStep("wait for user population", () => Client.CurrentMatchPlayingUserIds.Count > 0); } [Test] diff --git a/osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs b/osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs index 3a5ffa8770..c7aa43b377 100644 --- a/osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs +++ b/osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs @@ -3,6 +3,7 @@ #nullable enable +using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -53,6 +54,8 @@ namespace osu.Game.Tests.Visual.Spectator }); } + public new void Schedule(Action action) => base.Schedule(action); + /// /// Sends frames for an arbitrary user. /// From ff1fa71e6f7b03eafd91739f20d0b7ded6cdbe60 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 27 May 2021 20:06:37 +0900 Subject: [PATCH 532/839] Remove feature request issue template and link to discussions --- .github/ISSUE_TEMPLATE/02-feature-request-issues.md | 7 ------- .github/ISSUE_TEMPLATE/config.yml | 9 ++++++++- 2 files changed, 8 insertions(+), 8 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/02-feature-request-issues.md diff --git a/.github/ISSUE_TEMPLATE/02-feature-request-issues.md b/.github/ISSUE_TEMPLATE/02-feature-request-issues.md deleted file mode 100644 index c3357dd780..0000000000 --- a/.github/ISSUE_TEMPLATE/02-feature-request-issues.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -name: Feature Request -about: Propose a feature you would like to see in the game! ---- -**Describe the new feature:** - -**Proposal designs of the feature:** diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 69baeee60c..c62231e8e0 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1,12 @@ blank_issues_enabled: false contact_links: + - 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: 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: osu!stable issues url: https://github.com/ppy/osu-stable-issues - about: For issues regarding osu!stable (not osu!lazer), open them here. + about: For osu!stable bugs (not osu!lazer), check out the dedicated repository. Note that we only accept serious bug reports. + From d24a712dd4b8b7b2dcc3bf3203e63e56276ac0ee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 May 2021 02:27:06 +0900 Subject: [PATCH 533/839] Move protected properties to one location and mark setters `private` where feasible --- osu.Game/OsuGameBase.cs | 42 ++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 3c143c1db9..bbfaa62258 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -53,51 +53,53 @@ namespace osu.Game /// public class OsuGameBase : Framework.Game, ICanAcceptFiles { - public const string CLIENT_STREAM_NAME = "lazer"; + public const string CLIENT_STREAM_NAME = @"lazer"; public const int SAMPLE_CONCURRENCY = 6; public bool UseDevelopmentServer { get; } - protected OsuConfigManager LocalConfig; + protected OsuConfigManager LocalConfig { get; private set; } protected SessionStatics SessionStatics { get; private set; } - protected BeatmapManager BeatmapManager; + protected BeatmapManager BeatmapManager { get; private set; } - protected ScoreManager ScoreManager; + protected ScoreManager ScoreManager { get; private set; } - protected BeatmapDifficultyCache DifficultyCache; + protected BeatmapDifficultyCache DifficultyCache { get; private set; } - protected UserLookupCache UserCache; + protected UserLookupCache UserCache { get; private set; } - protected SkinManager SkinManager; + protected SkinManager SkinManager { get; private set; } - protected RulesetStore RulesetStore; + protected RulesetStore RulesetStore { get; private set; } - protected FileStore FileStore; + protected FileStore FileStore { get; private set; } - protected KeyBindingStore KeyBindingStore; + protected KeyBindingStore KeyBindingStore { get; private set; } - protected SettingsStore SettingsStore; + protected SettingsStore SettingsStore { get; private set; } - protected RulesetConfigCache RulesetConfigCache; + protected RulesetConfigCache RulesetConfigCache { get; private set; } - protected IAPIProvider API; + protected MenuCursorContainer MenuCursorContainer { get; private set; } + + protected MusicController MusicController { get; private set; } + + protected IAPIProvider API { get; set; } + + protected Storage Storage { get; set; } + + protected Bindable Beatmap { get; private set; } // cached via load() method private SpectatorClient spectatorClient; private MultiplayerClient multiplayerClient; - protected MenuCursorContainer MenuCursorContainer; - - protected MusicController MusicController; - private Container content; protected override Container Content => content; - protected Storage Storage { get; set; } - [Cached] [Cached(typeof(IBindable))] protected readonly Bindable Ruleset = new Bindable(); @@ -119,8 +121,6 @@ namespace osu.Game /// public readonly Bindable>> AvailableMods = new Bindable>>(); - protected Bindable Beatmap { get; private set; } // cached via load() method - private Bindable fpsDisplayVisible; public virtual Version AssemblyVersion => Assembly.GetEntryAssembly()?.GetName().Version ?? new Version(); From 4e49fbf7fbb0785019436e12e06117ce1f3f36a5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 May 2021 02:30:31 +0900 Subject: [PATCH 534/839] Switch protected properties to `private` where feasible --- osu.Game/OsuGameBase.cs | 55 +++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index bbfaa62258..97f838032a 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -67,22 +67,12 @@ namespace osu.Game protected ScoreManager ScoreManager { get; private set; } - protected BeatmapDifficultyCache DifficultyCache { get; private set; } - - protected UserLookupCache UserCache { get; private set; } - protected SkinManager SkinManager { get; private set; } protected RulesetStore RulesetStore { get; private set; } - protected FileStore FileStore { get; private set; } - protected KeyBindingStore KeyBindingStore { get; private set; } - protected SettingsStore SettingsStore { get; private set; } - - protected RulesetConfigCache RulesetConfigCache { get; private set; } - protected MenuCursorContainer MenuCursorContainer { get; private set; } protected MusicController MusicController { get; private set; } @@ -93,13 +83,6 @@ namespace osu.Game protected Bindable Beatmap { get; private set; } // cached via load() method - private SpectatorClient spectatorClient; - private MultiplayerClient multiplayerClient; - - private Container content; - - protected override Container Content => content; - [Cached] [Cached(typeof(IBindable))] protected readonly Bindable Ruleset = new Bindable(); @@ -121,6 +104,24 @@ namespace osu.Game /// public readonly Bindable>> AvailableMods = new Bindable>>(); + private BeatmapDifficultyCache difficultyCache; + + private UserLookupCache userCache; + + private FileStore fileStore; + + private SettingsStore settingsStore; + + private RulesetConfigCache rulesetConfigCache; + + private SpectatorClient spectatorClient; + + private MultiplayerClient multiplayerClient; + + protected override Container Content => content; + + private Container content; + private Bindable fpsDisplayVisible; public virtual Version AssemblyVersion => Assembly.GetEntryAssembly()?.GetName().Version ?? new Version(); @@ -246,10 +247,10 @@ namespace osu.Game var defaultBeatmap = new DummyWorkingBeatmap(Audio, Textures); dependencies.Cache(RulesetStore = new RulesetStore(contextFactory, Storage)); - dependencies.Cache(FileStore = new FileStore(contextFactory, Storage)); + dependencies.Cache(fileStore = new FileStore(contextFactory, Storage)); // ordering is important here to ensure foreign keys rules are not broken in ModelStore.Cleanup() - dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, () => BeatmapManager, Storage, API, contextFactory, Host, () => DifficultyCache, LocalConfig)); + dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, () => BeatmapManager, Storage, API, contextFactory, Host, () => difficultyCache, LocalConfig)); dependencies.Cache(BeatmapManager = new BeatmapManager(Storage, contextFactory, RulesetStore, API, Audio, Host, defaultBeatmap, true)); // this should likely be moved to ArchiveModelManager when another case appers where it is necessary @@ -273,19 +274,19 @@ namespace osu.Game ScoreManager.Undelete(getBeatmapScores(item), true); }); - dependencies.Cache(DifficultyCache = new BeatmapDifficultyCache()); - AddInternal(DifficultyCache); + dependencies.Cache(difficultyCache = new BeatmapDifficultyCache()); + AddInternal(difficultyCache); - dependencies.Cache(UserCache = new UserLookupCache()); - AddInternal(UserCache); + dependencies.Cache(userCache = new UserLookupCache()); + AddInternal(userCache); var scorePerformanceManager = new ScorePerformanceCache(); dependencies.Cache(scorePerformanceManager); AddInternal(scorePerformanceManager); dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory, RulesetStore)); - dependencies.Cache(SettingsStore = new SettingsStore(contextFactory)); - dependencies.Cache(RulesetConfigCache = new RulesetConfigCache(SettingsStore)); + dependencies.Cache(settingsStore = new SettingsStore(contextFactory)); + dependencies.Cache(rulesetConfigCache = new RulesetConfigCache(settingsStore)); var powerStatus = CreateBatteryInfo(); if (powerStatus != null) @@ -308,7 +309,7 @@ namespace osu.Game dependencies.CacheAs>(Beatmap); dependencies.CacheAs(Beatmap); - FileStore.Cleanup(); + fileStore.Cleanup(); // add api components to hierarchy. if (API is APIAccess apiAccess) @@ -316,7 +317,7 @@ namespace osu.Game AddInternal(spectatorClient); AddInternal(multiplayerClient); - AddInternal(RulesetConfigCache); + AddInternal(rulesetConfigCache); GlobalActionContainer globalBindings; From b8edca59ebe0db43c26c8f21dc3519de90e659d7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 May 2021 02:37:14 +0900 Subject: [PATCH 535/839] General function reorganisation --- osu.Game/OsuGameBase.cs | 94 ++++++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 97f838032a..22502b661c 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -57,8 +57,34 @@ namespace osu.Game public const int SAMPLE_CONCURRENCY = 6; + /// + /// The maximum volume at which audio tracks should playback. This can be set lower than 1 to create some head-room for sound effects. + /// + internal const double GLOBAL_TRACK_VOLUME_ADJUST = 0.5; + public bool UseDevelopmentServer { get; } + public virtual Version AssemblyVersion => Assembly.GetEntryAssembly()?.GetName().Version ?? new Version(); + + /// + /// MD5 representation of the game executable. + /// + public string VersionHash { get; private set; } + + public bool IsDeployedBuild => AssemblyVersion.Major > 0; + + public virtual string Version + { + get + { + if (!IsDeployedBuild) + return @"local " + (DebugUtils.IsDebugBuild ? @"debug" : @"release"); + + var version = AssemblyVersion; + return $@"{version.Major}.{version.Minor}.{version.Build}"; + } + } + protected OsuConfigManager LocalConfig { get; private set; } protected SessionStatics SessionStatics { get; private set; } @@ -118,32 +144,17 @@ namespace osu.Game private MultiplayerClient multiplayerClient; + private DatabaseContextFactory contextFactory; + protected override Container Content => content; private Container content; + private DependencyContainer dependencies; + private Bindable fpsDisplayVisible; - public virtual Version AssemblyVersion => Assembly.GetEntryAssembly()?.GetName().Version ?? new Version(); - - /// - /// MD5 representation of the game executable. - /// - public string VersionHash { get; private set; } - - public bool IsDeployedBuild => AssemblyVersion.Major > 0; - - public virtual string Version - { - get - { - if (!IsDeployedBuild) - return @"local " + (DebugUtils.IsDebugBuild ? @"debug" : @"release"); - - var version = AssemblyVersion; - return $@"{version.Major}.{version.Minor}.{version.Build}"; - } - } + private readonly BindableNumber globalTrackVolumeAdjust = new BindableNumber(GLOBAL_TRACK_VOLUME_ADJUST); public OsuGameBase() { @@ -151,23 +162,14 @@ namespace osu.Game Name = @"osu!lazer"; } - private DependencyContainer dependencies; - - protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => - dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - - private DatabaseContextFactory contextFactory; - protected override UserInputManager CreateUserInputManager() => new OsuUserInputManager(); protected virtual BatteryInfo CreateBatteryInfo() => null; - /// - /// The maximum volume at which audio tracks should playback. This can be set lower than 1 to create some head-room for sound effects. - /// - internal const double GLOBAL_TRACK_VOLUME_ADJUST = 0.5; + protected virtual Container CreateScalingContainer() => new DrawSizePreservingFillContainer(); - private readonly BindableNumber globalTrackVolumeAdjust = new BindableNumber(GLOBAL_TRACK_VOLUME_ADJUST); + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => + dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); [BackgroundDependencyLoader] private void load() @@ -345,6 +347,19 @@ namespace osu.Game Ruleset.BindValueChanged(onRulesetChanged); } + protected override void LoadComplete() + { + base.LoadComplete(); + + // TODO: This is temporary until we reimplement the local FPS display. + // It's just to allow end-users to access the framework FPS display without knowing the shortcut key. + fpsDisplayVisible = LocalConfig.GetBindable(OsuSetting.ShowFpsDisplay); + fpsDisplayVisible.ValueChanged += visible => { FrameStatistics.Value = visible.NewValue ? FrameStatisticsMode.Minimal : FrameStatisticsMode.None; }; + fpsDisplayVisible.TriggerChange(); + + FrameStatistics.ValueChanged += e => fpsDisplayVisible.Value = e.NewValue != FrameStatisticsMode.None; + } + private void onRulesetChanged(ValueChangedEvent r) { var dict = new Dictionary>(); @@ -361,21 +376,6 @@ namespace osu.Game AvailableMods.Value = dict; } - protected virtual Container CreateScalingContainer() => new DrawSizePreservingFillContainer(); - - protected override void LoadComplete() - { - base.LoadComplete(); - - // TODO: This is temporary until we reimplement the local FPS display. - // It's just to allow end-users to access the framework FPS display without knowing the shortcut key. - fpsDisplayVisible = LocalConfig.GetBindable(OsuSetting.ShowFpsDisplay); - fpsDisplayVisible.ValueChanged += visible => { FrameStatistics.Value = visible.NewValue ? FrameStatisticsMode.Minimal : FrameStatisticsMode.None; }; - fpsDisplayVisible.TriggerChange(); - - FrameStatistics.ValueChanged += e => fpsDisplayVisible.Value = e.NewValue != FrameStatisticsMode.None; - } - private void runMigrations() { try From b4c13d837d54855a9b9b3310e5790b77f61efca9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 May 2021 02:44:44 +0900 Subject: [PATCH 536/839] Move import logic out to partial class --- osu.Game/OsuGameBase.cs | 73 +------------------------------ osu.Game/OsuGameBase_Importing.cs | 57 ++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 72 deletions(-) create mode 100644 osu.Game/OsuGameBase_Importing.cs diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 22502b661c..9f73236fb0 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; -using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Bindables; @@ -41,7 +40,6 @@ using osu.Game.Rulesets.Mods; using osu.Game.Scoring; using osu.Game.Skinning; using osu.Game.Utils; -using osuTK.Input; using RuntimeInfo = osu.Framework.RuntimeInfo; namespace osu.Game @@ -51,7 +49,7 @@ namespace osu.Game /// Unlike , this class will not load any kind of UI, allowing it to be used /// for provide dependencies to test cases without interfering with them. /// - public class OsuGameBase : Framework.Game, ICanAcceptFiles + public partial class OsuGameBase : Framework.Game, ICanAcceptFiles { public const string CLIENT_STREAM_NAME = @"lazer"; @@ -399,75 +397,6 @@ namespace osu.Game } } - public override void SetHost(GameHost host) - { - base.SetHost(host); - - // may be non-null for certain tests - Storage ??= host.Storage; - - LocalConfig ??= UseDevelopmentServer - ? new DevelopmentOsuConfigManager(Storage) - : new OsuConfigManager(Storage); - } - - /// - /// Use to programatically exit the game as if the user was triggering via alt-f4. - /// Will keep persisting until an exit occurs (exit may be blocked multiple times). - /// - public void GracefullyExit() - { - if (!OnExiting()) - Exit(); - else - Scheduler.AddDelayed(GracefullyExit, 2000); - } - - protected override Storage CreateStorage(GameHost host, Storage defaultStorage) => new OsuStorage(host, defaultStorage); - - private readonly List fileImporters = new List(); - - /// - /// Register a global handler for file imports. Most recently registered will have precedence. - /// - /// The handler to register. - public void RegisterImportHandler(ICanAcceptFiles handler) => fileImporters.Insert(0, handler); - - /// - /// Unregister a global handler for file imports. - /// - /// The previously registered handler. - public void UnregisterImportHandler(ICanAcceptFiles handler) => fileImporters.Remove(handler); - - public async Task Import(params string[] paths) - { - if (paths.Length == 0) - return; - - var filesPerExtension = paths.GroupBy(p => Path.GetExtension(p).ToLowerInvariant()); - - foreach (var groups in filesPerExtension) - { - foreach (var importer in fileImporters) - { - if (importer.HandledExtensions.Contains(groups.Key)) - await importer.Import(groups.ToArray()).ConfigureAwait(false); - } - } - } - - public virtual async Task Import(params ImportTask[] tasks) - { - var tasksPerExtension = tasks.GroupBy(t => Path.GetExtension(t.Path).ToLowerInvariant()); - await Task.WhenAll(tasksPerExtension.Select(taskGroup => - { - var importer = fileImporters.FirstOrDefault(i => i.HandledExtensions.Contains(taskGroup.Key)); - return importer?.Import(taskGroup.ToArray()) ?? Task.CompletedTask; - })).ConfigureAwait(false); - } - - public IEnumerable HandledExtensions => fileImporters.SelectMany(i => i.HandledExtensions); - protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); diff --git a/osu.Game/OsuGameBase_Importing.cs b/osu.Game/OsuGameBase_Importing.cs new file mode 100644 index 0000000000..d712da3553 --- /dev/null +++ b/osu.Game/OsuGameBase_Importing.cs @@ -0,0 +1,57 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using osu.Game.Database; + +namespace osu.Game +{ + public partial class OsuGameBase + { + private readonly List fileImporters = new List(); + + /// + /// Register a global handler for file imports. Most recently registered will have precedence. + /// + /// The handler to register. + public void RegisterImportHandler(ICanAcceptFiles handler) => fileImporters.Insert(0, handler); + + /// + /// Unregister a global handler for file imports. + /// + /// The previously registered handler. + public void UnregisterImportHandler(ICanAcceptFiles handler) => fileImporters.Remove(handler); + + public async Task Import(params string[] paths) + { + if (paths.Length == 0) + return; + + var filesPerExtension = paths.GroupBy(p => Path.GetExtension(p).ToLowerInvariant()); + + foreach (var groups in filesPerExtension) + { + foreach (var importer in fileImporters) + { + if (importer.HandledExtensions.Contains(groups.Key)) + await importer.Import(groups.ToArray()).ConfigureAwait(false); + } + } + } + + public virtual async Task Import(params ImportTask[] tasks) + { + var tasksPerExtension = tasks.GroupBy(t => Path.GetExtension(t.Path).ToLowerInvariant()); + await Task.WhenAll(tasksPerExtension.Select(taskGroup => + { + var importer = fileImporters.FirstOrDefault(i => i.HandledExtensions.Contains(taskGroup.Key)); + return importer?.Import(taskGroup.ToArray()) ?? Task.CompletedTask; + })).ConfigureAwait(false); + } + + public IEnumerable HandledExtensions => fileImporters.SelectMany(i => i.HandledExtensions); + } +} From fe6b1936cb3d69eec4646407cd2713b16112eba2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 May 2021 02:45:04 +0900 Subject: [PATCH 537/839] Move `OsuUserInputManager` out from nested class --- osu.Game/Input/OsuUserInputManager.cs | 38 +++++++++++++++++++++++++++ osu.Game/OsuGameBase.cs | 32 ---------------------- 2 files changed, 38 insertions(+), 32 deletions(-) create mode 100644 osu.Game/Input/OsuUserInputManager.cs diff --git a/osu.Game/Input/OsuUserInputManager.cs b/osu.Game/Input/OsuUserInputManager.cs new file mode 100644 index 0000000000..621f1e1fc9 --- /dev/null +++ b/osu.Game/Input/OsuUserInputManager.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.Input; +using osuTK.Input; + +namespace osu.Game.Input +{ + public class OsuUserInputManager : UserInputManager + { + internal OsuUserInputManager() + { + } + + protected override MouseButtonEventManager CreateButtonEventManagerFor(MouseButton button) + { + switch (button) + { + case MouseButton.Right: + return new RightMouseManager(button); + } + + return base.CreateButtonEventManagerFor(button); + } + + private class RightMouseManager : MouseButtonEventManager + { + public RightMouseManager(MouseButton button) + : base(button) + { + } + + public override bool EnableDrag => true; // allow right-mouse dragging for absolute scroll in scroll containers. + public override bool EnableClick => false; + public override bool ChangeFocusOnClick => false; + } + } +} diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 9f73236fb0..659da1bc4e 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -407,37 +407,5 @@ namespace osu.Game contextFactory.FlushConnections(); } - - private class OsuUserInputManager : UserInputManager - { - protected override MouseButtonEventManager CreateButtonEventManagerFor(MouseButton button) - { - switch (button) - { - case MouseButton.Right: - return new RightMouseManager(button); - } - - return base.CreateButtonEventManagerFor(button); - } - - private class RightMouseManager : MouseButtonEventManager - { - public RightMouseManager(MouseButton button) - : base(button) - { - } - - public override bool EnableDrag => true; // allow right-mouse dragging for absolute scroll in scroll containers. - public override bool EnableClick => false; - public override bool ChangeFocusOnClick => false; - } - } - - public void Migrate(string path) - { - contextFactory.FlushConnections(); - (Storage as OsuStorage)?.Migrate(Host.GetStorage(path)); - } } } From ce96c58230db88a1ea14211ea4f4d4d927240aa3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 May 2021 02:46:26 +0900 Subject: [PATCH 538/839] Move public members up --- osu.Game/OsuGameBase.cs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 659da1bc4e..dfb9bd3a99 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -160,6 +160,36 @@ namespace osu.Game Name = @"osu!lazer"; } + public override void SetHost(GameHost host) + { + base.SetHost(host); + + // may be non-null for certain tests + Storage ??= host.Storage; + + LocalConfig ??= UseDevelopmentServer + ? new DevelopmentOsuConfigManager(Storage) + : new OsuConfigManager(Storage); + } + + /// + /// Use to programatically exit the game as if the user was triggering via alt-f4. + /// Will keep persisting until an exit occurs (exit may be blocked multiple times). + /// + public void GracefullyExit() + { + if (!OnExiting()) + Exit(); + else + Scheduler.AddDelayed(GracefullyExit, 2000); + } + + public void Migrate(string path) + { + contextFactory.FlushConnections(); + (Storage as OsuStorage)?.Migrate(Host.GetStorage(path)); + } + protected override UserInputManager CreateUserInputManager() => new OsuUserInputManager(); protected virtual BatteryInfo CreateBatteryInfo() => null; @@ -169,6 +199,8 @@ namespace osu.Game protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); + protected override Storage CreateStorage(GameHost host, Storage defaultStorage) => new OsuStorage(host, defaultStorage); + [BackgroundDependencyLoader] private void load() { From ead0e92d7d523b3a29a2f44e82a16fbd815ec8ff Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 28 May 2021 00:50:59 +0700 Subject: [PATCH 539/839] use select single node for blurb --- osu.Game/Overlays/Wiki/WikiMainPage.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Wiki/WikiMainPage.cs b/osu.Game/Overlays/Wiki/WikiMainPage.cs index bbbbc602bf..b34b5d9acf 100644 --- a/osu.Game/Overlays/Wiki/WikiMainPage.cs +++ b/osu.Game/Overlays/Wiki/WikiMainPage.cs @@ -46,7 +46,7 @@ namespace osu.Game.Overlays.Wiki private Container createBlurb(HtmlDocument html) { - var blurbNode = html.DocumentNode.SelectNodes("//div[contains(@class, 'wiki-main-page__blurb')]").First(); + var blurbNode = html.DocumentNode.SelectSingleNode("//div[contains(@class, 'wiki-main-page__blurb')]"); return new Container { From 854ef0abfdc67cf87c7e20552c6db320707eb8ba Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 28 May 2021 00:55:14 +0700 Subject: [PATCH 540/839] add simple bound check --- osu.Game/Overlays/Wiki/WikiMainPage.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Wiki/WikiMainPage.cs b/osu.Game/Overlays/Wiki/WikiMainPage.cs index b34b5d9acf..c4c0b83ef4 100644 --- a/osu.Game/Overlays/Wiki/WikiMainPage.cs +++ b/osu.Game/Overlays/Wiki/WikiMainPage.cs @@ -95,7 +95,7 @@ namespace osu.Game.Overlays.Wiki yield return new Drawable[] { new WikiPanelContainer(panelsNode[i++].InnerText), - new WikiPanelContainer(panelsNode[i++].InnerText), + i < panelsNode.Length ? new WikiPanelContainer(panelsNode[i++].InnerText) : null, }; } } From 5e8893b20336249b6139b22d2269d00b25e243ec Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 May 2021 10:42:13 +0900 Subject: [PATCH 541/839] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index b3842a528d..bcd5f9bd9a 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index fa2945db6a..5f0f3028f0 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -33,7 +33,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index e35b1b5c42..59b026e0ad 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 1af684c4b2cb41e45f473e90285f5ac5cc192d12 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 May 2021 14:15:28 +0900 Subject: [PATCH 542/839] Revert "Fix editor tests failing due to empty files being specified" This reverts commit fdbd421040efcbd9718d95841034471f930e6291. --- .../Editing/TestSceneEditorBeatmapCreation.cs | 2 - osu.Game/Beatmaps/BeatmapManager.cs | 4 +- osu.Game/Tests/Beatmaps/TestBeatmap.cs | 1 + osu.Game/Tests/Visual/EditorTestScene.cs | 43 +------------------ 4 files changed, 5 insertions(+), 45 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs index dd5e01adbb..7584c74c71 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs @@ -24,8 +24,6 @@ namespace osu.Game.Tests.Visual.Editing protected override bool EditorComponentsReady => Editor.ChildrenOfType().SingleOrDefault()?.IsLoaded == true; - protected override bool IsolateSavingFromDatabase => false; - [Resolved] private BeatmapManager beatmapManager { get; set; } diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 18fbd1f5c2..f459657a0e 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -240,7 +240,7 @@ namespace osu.Game.Beatmaps /// The to save the content against. The file referenced by will be replaced. /// The content to write. /// The beatmap content to write, null if to be omitted. - public virtual void Save(BeatmapInfo info, IBeatmap beatmapContent, ISkin beatmapSkin = null) + public void Save(BeatmapInfo info, IBeatmap beatmapContent, ISkin beatmapSkin = null) { var setInfo = info.BeatmapSet; @@ -282,7 +282,7 @@ namespace osu.Game.Beatmaps /// The beatmap to lookup. /// The currently loaded . Allows for optimisation where elements are shared with the new beatmap. May be returned if beatmapInfo requested matches /// A instance correlating to the provided . - public virtual WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo, WorkingBeatmap previous = null) + public WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo, WorkingBeatmap previous = null) { if (beatmapInfo?.BeatmapSet == null) return DefaultBeatmap; diff --git a/osu.Game/Tests/Beatmaps/TestBeatmap.cs b/osu.Game/Tests/Beatmaps/TestBeatmap.cs index 2717146c99..fa6dc5647d 100644 --- a/osu.Game/Tests/Beatmaps/TestBeatmap.cs +++ b/osu.Game/Tests/Beatmaps/TestBeatmap.cs @@ -29,6 +29,7 @@ namespace osu.Game.Tests.Beatmaps BeatmapInfo.Ruleset = ruleset; BeatmapInfo.RulesetID = ruleset.ID ?? 0; BeatmapInfo.BeatmapSet.Metadata = BeatmapInfo.Metadata; + BeatmapInfo.BeatmapSet.Files = new List(); BeatmapInfo.BeatmapSet.Beatmaps = new List { BeatmapInfo }; BeatmapInfo.Length = 75000; BeatmapInfo.OnlineInfo = new BeatmapOnlineInfo(); diff --git a/osu.Game/Tests/Visual/EditorTestScene.cs b/osu.Game/Tests/Visual/EditorTestScene.cs index de7fdd0cf8..a9ee8e2668 100644 --- a/osu.Game/Tests/Visual/EditorTestScene.cs +++ b/osu.Game/Tests/Visual/EditorTestScene.cs @@ -4,18 +4,11 @@ using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; -using osu.Framework.Audio; -using osu.Framework.Platform; using osu.Framework.Testing; -using osu.Game.Beatmaps; -using osu.Game.Database; -using osu.Game.IO.Archives; -using osu.Game.Online.API; using osu.Game.Rulesets; using osu.Game.Rulesets.Edit; using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Compose.Components.Timeline; -using osu.Game.Skinning; namespace osu.Game.Tests.Visual { @@ -27,20 +20,10 @@ namespace osu.Game.Tests.Visual protected EditorClock EditorClock { get; private set; } - /// - /// Whether any saves performed by the editor should be isolate (and not persist) to the underlying . - /// - protected virtual bool IsolateSavingFromDatabase => true; - [BackgroundDependencyLoader] - private void load(GameHost host, AudioManager audio, RulesetStore rulesets) + private void load() { - var working = CreateWorkingBeatmap(Ruleset.Value); - - Beatmap.Value = working; - - if (IsolateSavingFromDatabase) - Dependencies.CacheAs(new TestBeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default, working)); + Beatmap.Value = CreateWorkingBeatmap(Ruleset.Value); } protected virtual bool EditorComponentsReady => Editor.ChildrenOfType().FirstOrDefault()?.IsLoaded == true @@ -82,27 +65,5 @@ namespace osu.Game.Tests.Visual public new bool HasUnsavedChanges => base.HasUnsavedChanges; } - - private class TestBeatmapManager : BeatmapManager - { - private readonly WorkingBeatmap testBeatmap; - - public TestBeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, GameHost host, WorkingBeatmap defaultBeatmap, WorkingBeatmap testBeatmap) - : base(storage, contextFactory, rulesets, api, audioManager, host, defaultBeatmap, false) - { - this.testBeatmap = testBeatmap; - } - - protected override string ComputeHash(BeatmapSetInfo item, ArchiveReader reader = null) - => string.Empty; - - public override WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo, WorkingBeatmap previous = null) - => testBeatmap; - - public override void Save(BeatmapInfo info, IBeatmap beatmapContent, ISkin beatmapSkin = null) - { - // don't actually care about saving for this context. - } - } } } From 9c4f39e9681644d84b60828ee8ce613b6d81a89f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 May 2021 14:15:28 +0900 Subject: [PATCH 543/839] Revert "Make `BeatmapSetInfo.Files` non-nullable" This reverts commit c24712642cc567f158f2d4a7b559429567347d8b. --- osu.Game/Beatmaps/BeatmapManager.cs | 22 ++++++++++++---------- osu.Game/Beatmaps/BeatmapSetInfo.cs | 10 ++++------ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index f459657a0e..f70d29861f 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Linq.Expressions; @@ -244,6 +245,8 @@ namespace osu.Game.Beatmaps { var setInfo = info.BeatmapSet; + Debug.Assert(setInfo.Files != null); + using (var stream = new MemoryStream()) { using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) @@ -284,21 +287,20 @@ namespace osu.Game.Beatmaps /// A instance correlating to the provided . public WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo, WorkingBeatmap previous = null) { - if (beatmapInfo?.BeatmapSet == null) + if (beatmapInfo?.ID > 0 && previous != null && previous.BeatmapInfo?.ID == beatmapInfo.ID) + return previous; + + if (beatmapInfo?.BeatmapSet == null || beatmapInfo == DefaultBeatmap?.BeatmapInfo) return DefaultBeatmap; - if (beatmapInfo == DefaultBeatmap?.BeatmapInfo) - return DefaultBeatmap; - - // if there are no files, presume the full beatmap info has not yet been fetched from the database. - if (beatmapInfo.BeatmapSet.Files.Count == 0) + if (beatmapInfo.BeatmapSet.Files == null) { - int lookupId = beatmapInfo.ID; - beatmapInfo = QueryBeatmap(b => b.ID == lookupId); + var info = beatmapInfo; + beatmapInfo = QueryBeatmap(b => b.ID == info.ID); } - if (beatmapInfo.ID > 0 && previous != null && previous.BeatmapInfo?.ID == beatmapInfo.ID) - return previous; + if (beatmapInfo == null) + return DefaultBeatmap; lock (workingCache) { diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index 3b1ff4ced0..1ce42535a0 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; -using JetBrains.Annotations; using osu.Framework.Testing; using osu.Game.Database; @@ -32,9 +31,6 @@ namespace osu.Game.Beatmaps public List Beatmaps { get; set; } - [NotNull] - public List Files { get; set; } = new List(); - [NotMapped] public BeatmapSetOnlineInfo OnlineInfo { get; set; } @@ -61,14 +57,16 @@ namespace osu.Game.Beatmaps public string Hash { get; set; } - public string StoryboardFile => Files.Find(f => f.Filename.EndsWith(".osb", StringComparison.OrdinalIgnoreCase))?.Filename; + public string StoryboardFile => Files?.Find(f => f.Filename.EndsWith(".osb", StringComparison.OrdinalIgnoreCase))?.Filename; /// /// Returns the storage path for the file in this beatmapset with the given filename, if any exists, otherwise null. /// The path returned is relative to the user file storage. /// /// The name of the file to get the storage path of. - public string GetPathForFile(string filename) => Files.SingleOrDefault(f => string.Equals(f.Filename, filename, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath; + public string GetPathForFile(string filename) => Files?.SingleOrDefault(f => string.Equals(f.Filename, filename, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath; + + public List Files { get; set; } public override string ToString() => Metadata?.ToString() ?? base.ToString(); From e52c0a34f82a6b93c740b4c2d3d068bc3c52196a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 May 2021 14:31:09 +0900 Subject: [PATCH 544/839] Add temporary accounting for tests with null files --- osu.Game/Beatmaps/BeatmapManager.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index f70d29861f..2e2bc96c21 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -293,7 +293,11 @@ namespace osu.Game.Beatmaps if (beatmapInfo?.BeatmapSet == null || beatmapInfo == DefaultBeatmap?.BeatmapInfo) return DefaultBeatmap; - if (beatmapInfo.BeatmapSet.Files == null) + // files may be null in some tests. + if (beatmapInfo.BeatmapSet?.Files == null) + return DefaultBeatmap; + + if (beatmapInfo.BeatmapSet.Files.Count == 0) { var info = beatmapInfo; beatmapInfo = QueryBeatmap(b => b.ID == info.ID); From b349ff8693096103d14a94411b8f25cb647044d6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 May 2021 14:33:04 +0900 Subject: [PATCH 545/839] Revert "Add temporary accounting for tests with null files" This reverts commit e52c0a34f82a6b93c740b4c2d3d068bc3c52196a. --- osu.Game/Beatmaps/BeatmapManager.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 2e2bc96c21..f70d29861f 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -293,11 +293,7 @@ namespace osu.Game.Beatmaps if (beatmapInfo?.BeatmapSet == null || beatmapInfo == DefaultBeatmap?.BeatmapInfo) return DefaultBeatmap; - // files may be null in some tests. - if (beatmapInfo.BeatmapSet?.Files == null) - return DefaultBeatmap; - - if (beatmapInfo.BeatmapSet.Files.Count == 0) + if (beatmapInfo.BeatmapSet.Files == null) { var info = beatmapInfo; beatmapInfo = QueryBeatmap(b => b.ID == info.ID); From 41733af0ed8eefd3e7206115680ad45588d2e652 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 May 2021 14:33:05 +0900 Subject: [PATCH 546/839] Revert "Revert "Make `BeatmapSetInfo.Files` non-nullable"" This reverts commit 9c4f39e9681644d84b60828ee8ce613b6d81a89f. --- osu.Game/Beatmaps/BeatmapManager.cs | 22 ++++++++++------------ osu.Game/Beatmaps/BeatmapSetInfo.cs | 10 ++++++---- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index f70d29861f..f459657a0e 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; using System.Linq.Expressions; @@ -245,8 +244,6 @@ namespace osu.Game.Beatmaps { var setInfo = info.BeatmapSet; - Debug.Assert(setInfo.Files != null); - using (var stream = new MemoryStream()) { using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) @@ -287,20 +284,21 @@ namespace osu.Game.Beatmaps /// A instance correlating to the provided . public WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo, WorkingBeatmap previous = null) { - if (beatmapInfo?.ID > 0 && previous != null && previous.BeatmapInfo?.ID == beatmapInfo.ID) - return previous; - - if (beatmapInfo?.BeatmapSet == null || beatmapInfo == DefaultBeatmap?.BeatmapInfo) + if (beatmapInfo?.BeatmapSet == null) return DefaultBeatmap; - if (beatmapInfo.BeatmapSet.Files == null) + if (beatmapInfo == DefaultBeatmap?.BeatmapInfo) + return DefaultBeatmap; + + // if there are no files, presume the full beatmap info has not yet been fetched from the database. + if (beatmapInfo.BeatmapSet.Files.Count == 0) { - var info = beatmapInfo; - beatmapInfo = QueryBeatmap(b => b.ID == info.ID); + int lookupId = beatmapInfo.ID; + beatmapInfo = QueryBeatmap(b => b.ID == lookupId); } - if (beatmapInfo == null) - return DefaultBeatmap; + if (beatmapInfo.ID > 0 && previous != null && previous.BeatmapInfo?.ID == beatmapInfo.ID) + return previous; lock (workingCache) { diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index 1ce42535a0..3b1ff4ced0 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Testing; using osu.Game.Database; @@ -31,6 +32,9 @@ namespace osu.Game.Beatmaps public List Beatmaps { get; set; } + [NotNull] + public List Files { get; set; } = new List(); + [NotMapped] public BeatmapSetOnlineInfo OnlineInfo { get; set; } @@ -57,16 +61,14 @@ namespace osu.Game.Beatmaps public string Hash { get; set; } - public string StoryboardFile => Files?.Find(f => f.Filename.EndsWith(".osb", StringComparison.OrdinalIgnoreCase))?.Filename; + public string StoryboardFile => Files.Find(f => f.Filename.EndsWith(".osb", StringComparison.OrdinalIgnoreCase))?.Filename; /// /// Returns the storage path for the file in this beatmapset with the given filename, if any exists, otherwise null. /// The path returned is relative to the user file storage. /// /// The name of the file to get the storage path of. - public string GetPathForFile(string filename) => Files?.SingleOrDefault(f => string.Equals(f.Filename, filename, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath; - - public List Files { get; set; } + public string GetPathForFile(string filename) => Files.SingleOrDefault(f => string.Equals(f.Filename, filename, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath; public override string ToString() => Metadata?.ToString() ?? base.ToString(); From 581a86b91a231e5ed12d49bc1267d840fb58232b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 May 2021 14:33:06 +0900 Subject: [PATCH 547/839] Revert "Revert "Fix editor tests failing due to empty files being specified"" This reverts commit 1af684c4b2cb41e45f473e90285f5ac5cc192d12. --- .../Editing/TestSceneEditorBeatmapCreation.cs | 2 + osu.Game/Beatmaps/BeatmapManager.cs | 4 +- osu.Game/Tests/Beatmaps/TestBeatmap.cs | 1 - osu.Game/Tests/Visual/EditorTestScene.cs | 43 ++++++++++++++++++- 4 files changed, 45 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs index 7584c74c71..dd5e01adbb 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs @@ -24,6 +24,8 @@ namespace osu.Game.Tests.Visual.Editing protected override bool EditorComponentsReady => Editor.ChildrenOfType().SingleOrDefault()?.IsLoaded == true; + protected override bool IsolateSavingFromDatabase => false; + [Resolved] private BeatmapManager beatmapManager { get; set; } diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index f459657a0e..18fbd1f5c2 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -240,7 +240,7 @@ namespace osu.Game.Beatmaps /// The to save the content against. The file referenced by will be replaced. /// The content to write. /// The beatmap content to write, null if to be omitted. - public void Save(BeatmapInfo info, IBeatmap beatmapContent, ISkin beatmapSkin = null) + public virtual void Save(BeatmapInfo info, IBeatmap beatmapContent, ISkin beatmapSkin = null) { var setInfo = info.BeatmapSet; @@ -282,7 +282,7 @@ namespace osu.Game.Beatmaps /// The beatmap to lookup. /// The currently loaded . Allows for optimisation where elements are shared with the new beatmap. May be returned if beatmapInfo requested matches /// A instance correlating to the provided . - public WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo, WorkingBeatmap previous = null) + public virtual WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo, WorkingBeatmap previous = null) { if (beatmapInfo?.BeatmapSet == null) return DefaultBeatmap; diff --git a/osu.Game/Tests/Beatmaps/TestBeatmap.cs b/osu.Game/Tests/Beatmaps/TestBeatmap.cs index fa6dc5647d..2717146c99 100644 --- a/osu.Game/Tests/Beatmaps/TestBeatmap.cs +++ b/osu.Game/Tests/Beatmaps/TestBeatmap.cs @@ -29,7 +29,6 @@ namespace osu.Game.Tests.Beatmaps BeatmapInfo.Ruleset = ruleset; BeatmapInfo.RulesetID = ruleset.ID ?? 0; BeatmapInfo.BeatmapSet.Metadata = BeatmapInfo.Metadata; - BeatmapInfo.BeatmapSet.Files = new List(); BeatmapInfo.BeatmapSet.Beatmaps = new List { BeatmapInfo }; BeatmapInfo.Length = 75000; BeatmapInfo.OnlineInfo = new BeatmapOnlineInfo(); diff --git a/osu.Game/Tests/Visual/EditorTestScene.cs b/osu.Game/Tests/Visual/EditorTestScene.cs index a9ee8e2668..de7fdd0cf8 100644 --- a/osu.Game/Tests/Visual/EditorTestScene.cs +++ b/osu.Game/Tests/Visual/EditorTestScene.cs @@ -4,11 +4,18 @@ using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Platform; using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Database; +using osu.Game.IO.Archives; +using osu.Game.Online.API; using osu.Game.Rulesets; using osu.Game.Rulesets.Edit; using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Compose.Components.Timeline; +using osu.Game.Skinning; namespace osu.Game.Tests.Visual { @@ -20,10 +27,20 @@ namespace osu.Game.Tests.Visual protected EditorClock EditorClock { get; private set; } + /// + /// Whether any saves performed by the editor should be isolate (and not persist) to the underlying . + /// + protected virtual bool IsolateSavingFromDatabase => true; + [BackgroundDependencyLoader] - private void load() + private void load(GameHost host, AudioManager audio, RulesetStore rulesets) { - Beatmap.Value = CreateWorkingBeatmap(Ruleset.Value); + var working = CreateWorkingBeatmap(Ruleset.Value); + + Beatmap.Value = working; + + if (IsolateSavingFromDatabase) + Dependencies.CacheAs(new TestBeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default, working)); } protected virtual bool EditorComponentsReady => Editor.ChildrenOfType().FirstOrDefault()?.IsLoaded == true @@ -65,5 +82,27 @@ namespace osu.Game.Tests.Visual public new bool HasUnsavedChanges => base.HasUnsavedChanges; } + + private class TestBeatmapManager : BeatmapManager + { + private readonly WorkingBeatmap testBeatmap; + + public TestBeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, GameHost host, WorkingBeatmap defaultBeatmap, WorkingBeatmap testBeatmap) + : base(storage, contextFactory, rulesets, api, audioManager, host, defaultBeatmap, false) + { + this.testBeatmap = testBeatmap; + } + + protected override string ComputeHash(BeatmapSetInfo item, ArchiveReader reader = null) + => string.Empty; + + public override WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo, WorkingBeatmap previous = null) + => testBeatmap; + + public override void Save(BeatmapInfo info, IBeatmap beatmapContent, ISkin beatmapSkin = null) + { + // don't actually care about saving for this context. + } + } } } From 330d61862dba5abcd539f41997e614217de3f901 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 May 2021 14:41:01 +0900 Subject: [PATCH 548/839] Add a mention of why `StartHidden` is required --- osu.Game/Overlays/Dialog/PopupDialog.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs index c04ad7afb5..cd02900e88 100644 --- a/osu.Game/Overlays/Dialog/PopupDialog.cs +++ b/osu.Game/Overlays/Dialog/PopupDialog.cs @@ -95,6 +95,8 @@ namespace osu.Game.Overlays.Dialog } } + // We always want dialogs to show their appear animation, so we request they start hidden. + // Normally this would not be required, but is here due to the manual Show() call that occurs before LoadComplete(). protected override bool StartHidden => true; protected PopupDialog() From 631d217f788a7d6808acd4337bdf018d3320539e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 May 2021 15:42:35 +0900 Subject: [PATCH 549/839] Remove no longer necessary conditional access --- osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs | 2 +- osu.Game/Storyboards/Storyboard.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs index 4ea582ca4a..d746ff5ae5 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs @@ -29,7 +29,7 @@ namespace osu.Game.Storyboards.Drawables [BackgroundDependencyLoader(true)] private void load(IBindable beatmap, TextureStore textureStore) { - var path = beatmap.Value.BeatmapSetInfo?.Files?.Find(f => f.Filename.Equals(Video.Path, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath; + var path = beatmap.Value.BeatmapSetInfo?.Files.Find(f => f.Filename.Equals(Video.Path, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath; if (path == null) return; diff --git a/osu.Game/Storyboards/Storyboard.cs b/osu.Game/Storyboards/Storyboard.cs index 3486c1d66a..38e0e4e38c 100644 --- a/osu.Game/Storyboards/Storyboard.cs +++ b/osu.Game/Storyboards/Storyboard.cs @@ -91,7 +91,7 @@ namespace osu.Game.Storyboards public Drawable CreateSpriteFromResourcePath(string path, TextureStore textureStore) { Drawable drawable = null; - var storyboardPath = BeatmapInfo.BeatmapSet?.Files?.Find(f => f.Filename.Equals(path, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath; + var storyboardPath = BeatmapInfo.BeatmapSet?.Files.Find(f => f.Filename.Equals(path, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath; if (storyboardPath != null) drawable = new Sprite { Texture = textureStore.Get(storyboardPath) }; From 1d5e8f4a912584428dee430764e49e5c6286a17b Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 28 May 2021 17:19:36 +0900 Subject: [PATCH 550/839] Fix wrong/misleading comment of `InitialLifetimeOffset` --- .../Objects/Drawables/DrawableHitObject.cs | 15 +++------------ .../Rulesets/Objects/HitObjectLifetimeEntry.cs | 5 ++--- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index cca55819c5..5faefbf469 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -11,7 +11,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.TypeExtensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Performance; using osu.Framework.Graphics.Primitives; using osu.Framework.Threading; using osu.Game.Audio; @@ -445,9 +444,7 @@ namespace osu.Game.Rulesets.Objects.Drawables /// /// Apply (generally fade-in) transforms leading into the start time. - /// The local drawable hierarchy is recursively delayed to for convenience. - /// - /// By default this will fade in the object from zero with no duration. + /// By default, this will fade in the object from zero with no duration. /// /// /// This is called once before every . This is to ensure a good state in the case @@ -623,17 +620,11 @@ namespace osu.Game.Rulesets.Objects.Drawables protected internal new ScheduledDelegate Schedule(Action action) => base.Schedule(action); /// - /// A safe offset prior to the start time of at which this may begin displaying contents. + /// A offset prior to the start time of at which this may begin displaying contents. /// By default, s are assumed to display their contents within 10 seconds prior to the start time of . /// /// - /// This is only used as an optimisation to delay the initial update of this and may be tuned more aggressively if required. - /// It is indirectly used to decide the automatic transform offset provided to . - /// A more accurate should be set for further optimisation (in , for example). - /// - /// Only has an effect if this is not being pooled. - /// For pooled s, use instead. - /// + /// The initial transformation () starts at this offset before the start time of . /// protected virtual double InitialLifetimeOffset => 10000; diff --git a/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs b/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs index 0d1eb68f07..cf62226d94 100644 --- a/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs +++ b/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs @@ -82,9 +82,8 @@ namespace osu.Game.Rulesets.Objects /// By default, s are assumed to display their contents within 10 seconds prior to their start time. /// /// - /// This is only used as an optimisation to delay the initial update of the and may be tuned more aggressively if required. - /// It is indirectly used to decide the automatic transform offset provided to . - /// A more accurate should be set for further optimisation (in , for example). + /// This is only used as an optimisation to delay the initial application of the to a . + /// A more accurate should be set on the hit object application, for further optimisation. /// protected virtual double InitialLifetimeOffset => 10000; From ee5eb9576fc1f47c6d250b141d8f496ca2113258 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 May 2021 18:10:27 +0900 Subject: [PATCH 551/839] Fix completely wrong conditional logic --- osu.Game/Beatmaps/BeatmapManager.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 2e2bc96c21..23810367a1 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -293,11 +293,9 @@ namespace osu.Game.Beatmaps if (beatmapInfo?.BeatmapSet == null || beatmapInfo == DefaultBeatmap?.BeatmapInfo) return DefaultBeatmap; - // files may be null in some tests. - if (beatmapInfo.BeatmapSet?.Files == null) - return DefaultBeatmap; - - if (beatmapInfo.BeatmapSet.Files.Count == 0) + // force a re-query if files are not in a state which looks like the model has + // full database information present. + if (beatmapInfo.BeatmapSet.Files == null || beatmapInfo.BeatmapSet.Files.Count == 0) { var info = beatmapInfo; beatmapInfo = QueryBeatmap(b => b.ID == info.ID); From ba80361c4c5483f3981aa9103d365e8f3b74a27b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 May 2021 18:37:33 +0900 Subject: [PATCH 552/839] Fix tests that were previously doing reference comparisons --- osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 643f4131dc..2eb6d3f80e 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -358,7 +358,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap.Equals(target)); // this is an important check, to make sure updateComponentFromBeatmap() was actually run - AddUntilStep("selection shown on wedge", () => songSelect.CurrentBeatmapDetailsBeatmap.BeatmapInfo == target); + AddUntilStep("selection shown on wedge", () => songSelect.CurrentBeatmapDetailsBeatmap.BeatmapInfo.Equals(target)); } [Test] @@ -390,7 +390,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddUntilStep("has correct ruleset", () => Ruleset.Value.ID == 0); // this is an important check, to make sure updateComponentFromBeatmap() was actually run - AddUntilStep("selection shown on wedge", () => songSelect.CurrentBeatmapDetailsBeatmap.BeatmapInfo == target); + AddUntilStep("selection shown on wedge", () => songSelect.CurrentBeatmapDetailsBeatmap.BeatmapInfo.Equals(target)); } [Test] @@ -781,7 +781,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddUntilStep("Check ruleset changed to mania", () => Ruleset.Value.ID == 3); - AddAssert("Check first item in group selected", () => Beatmap.Value.BeatmapInfo == groupIcon.Items.First().Beatmap); + AddAssert("Check first item in group selected", () => Beatmap.Value.BeatmapInfo.Equals(groupIcon.Items.First().Beatmap)); } [Test] From 43bf734816db2051dc88b351f8be086a47a6734c Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 28 May 2021 21:26:21 +0900 Subject: [PATCH 553/839] Reset lifetime on `HitObject.DefaultsApplied` --- osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs b/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs index 0d1eb68f07..4e11c9aeee 100644 --- a/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs +++ b/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs @@ -35,7 +35,9 @@ namespace osu.Game.Rulesets.Objects HitObject = hitObject; startTimeBindable.BindTo(HitObject.StartTimeBindable); - startTimeBindable.BindValueChanged(onStartTimeChanged, true); + startTimeBindable.BindValueChanged(_ => setInitialLifetime(), true); + + HitObject.DefaultsApplied += _ => setInitialLifetime(); } // The lifetime, as set by the hitobject. @@ -89,8 +91,8 @@ namespace osu.Game.Rulesets.Objects protected virtual double InitialLifetimeOffset => 10000; /// - /// Resets according to the change in start time of the . + /// Set using . /// - private void onStartTimeChanged(ValueChangedEvent startTime) => LifetimeStart = HitObject.StartTime - InitialLifetimeOffset; + private void setInitialLifetime() => LifetimeStart = HitObject.StartTime - InitialLifetimeOffset; } } From 68c5f4813e6c0e5fa0964761b5be360236964d47 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 28 May 2021 21:27:10 +0900 Subject: [PATCH 554/839] Add test of lifetime update on DefaultsApplied --- osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs b/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs index 0bec02c488..5f99b886fb 100644 --- a/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs +++ b/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs @@ -3,6 +3,8 @@ using NUnit.Framework; using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Tests.Visual; @@ -70,6 +72,15 @@ namespace osu.Game.Tests.Gameplay AddAssert("Lifetime is changed", () => entry.LifetimeStart == double.MinValue && entry.LifetimeEnd == 1000); } + [Test] + public void TestLifetimeUpdatedOnDefaultApplied() + { + TestLifetimeEntry entry = null; + AddStep("Create entry", () => entry = new TestLifetimeEntry(new HitObject()) { LifetimeStart = 1 }); + AddStep("ApplyDefaults", () => entry.HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty())); + AddAssert("Lifetime is updated", () => entry.LifetimeStart == -TestLifetimeEntry.INITIAL_LIFETIME_OFFSET); + } + private class TestDrawableHitObject : DrawableHitObject { public const double INITIAL_LIFETIME_OFFSET = 100; From 265dfe54160221e05f2a5577fc10a21f7440d6b2 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 28 May 2021 21:51:48 +0900 Subject: [PATCH 555/839] Add test case of DHO setting LifetimeStart in OnApply --- .../Gameplay/TestSceneDrawableHitObject.cs | 22 +++++++++++++++++++ .../Objects/HitObjectLifetimeEntry.cs | 2 ++ 2 files changed, 24 insertions(+) diff --git a/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs b/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs index 5f99b886fb..3de2dc72bb 100644 --- a/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs +++ b/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs @@ -79,17 +79,39 @@ namespace osu.Game.Tests.Gameplay AddStep("Create entry", () => entry = new TestLifetimeEntry(new HitObject()) { LifetimeStart = 1 }); AddStep("ApplyDefaults", () => entry.HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty())); AddAssert("Lifetime is updated", () => entry.LifetimeStart == -TestLifetimeEntry.INITIAL_LIFETIME_OFFSET); + + TestDrawableHitObject dho = null; + AddStep("Create DHO", () => + { + dho = new TestDrawableHitObject(null); + dho.Apply(entry); + Child = dho; + dho.SetLifetimeStartOnApply = true; + }); + AddStep("ApplyDefaults", () => entry.HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty())); + AddAssert("Lifetime is correct", () => dho.LifetimeStart == TestDrawableHitObject.LIFETIME_ON_APPLY && entry.LifetimeStart == TestDrawableHitObject.LIFETIME_ON_APPLY); } private class TestDrawableHitObject : DrawableHitObject { public const double INITIAL_LIFETIME_OFFSET = 100; + public const double LIFETIME_ON_APPLY = 222; protected override double InitialLifetimeOffset => INITIAL_LIFETIME_OFFSET; + public bool SetLifetimeStartOnApply; + public TestDrawableHitObject(HitObject hitObject) : base(hitObject) { } + + protected override void OnApply() + { + base.OnApply(); + + if (SetLifetimeStartOnApply) + LifetimeStart = LIFETIME_ON_APPLY; + } } private class TestLifetimeEntry : HitObjectLifetimeEntry diff --git a/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs b/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs index 4e11c9aeee..3375f41cc3 100644 --- a/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs +++ b/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs @@ -37,6 +37,8 @@ namespace osu.Game.Rulesets.Objects startTimeBindable.BindTo(HitObject.StartTimeBindable); startTimeBindable.BindValueChanged(_ => setInitialLifetime(), true); + // It is important to subscribe to this event before applied to a DrawableHitObject. + // Otherwise DHO cannot overwrite LifetimeStart set in setInitialLifetime. HitObject.DefaultsApplied += _ => setInitialLifetime(); } From 2439de1c3dd3ce3ff085d41c6c1aa95e2cf1d1e2 Mon Sep 17 00:00:00 2001 From: Susko3 <16479013+Susko3@users.noreply.github.com> Date: Fri, 28 May 2021 18:40:58 +0200 Subject: [PATCH 556/839] fix capitalzation of osu!catch HitResults --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index e3c457693e..23ce444560 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -161,13 +161,13 @@ namespace osu.Game.Rulesets.Catch switch (result) { case HitResult.LargeTickHit: - return "large droplet"; + return "Large droplet"; case HitResult.SmallTickHit: - return "small droplet"; + return "Small droplet"; case HitResult.LargeBonus: - return "banana"; + return "Banana"; } return base.GetDisplayNameForHitResult(result); From 951fc5ef6ecff428a20b7b52c8f26cab9c170fe9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 29 May 2021 15:39:12 +0900 Subject: [PATCH 557/839] Remove misleading comment and misplaced bug fix This shouldn't be fixed in a test scene; the underlying issue should be fixed in actual game code. --- .../Gameplay/TestSceneBeatmapSkinFallbacks.cs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs index bece80903f..d6bedffaa8 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs @@ -5,7 +5,6 @@ using System; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Audio; -using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Lists; using osu.Framework.Testing; @@ -117,18 +116,6 @@ namespace osu.Game.Tests.Visual.Gameplay : base(source) { } - - public override Drawable GetDrawableComponent(ISkinComponent component) - { - var drawable = base.GetDrawableComponent(component); - if (drawable != null) - return drawable; - - // this isn't really supposed to make a difference from returning null, - // but it appears it does, returning null will skip over falling back to beatmap skin, - // while calling Source.GetDrawableComponent() doesn't. - return Source.GetDrawableComponent(component); - } } } } From 200fecc774d7e0e79b63dc3267e339a976cc58f4 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 29 May 2021 21:22:39 +0300 Subject: [PATCH 558/839] Fix missing using directive --- osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs index 31d29e6807..cc53e50884 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs @@ -3,6 +3,7 @@ using System; using System.Linq; +using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Graphics.Containers; From 53cbf369d7a55e1c6ef5f38af9d35de8ce4739ae Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 29 May 2021 21:22:46 +0300 Subject: [PATCH 559/839] Fix potential nullref --- osu.Game/Skinning/LegacyBeatmapSkin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacyBeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs index 04b2346c6a..caf37e5bc9 100644 --- a/osu.Game/Skinning/LegacyBeatmapSkin.cs +++ b/osu.Game/Skinning/LegacyBeatmapSkin.cs @@ -71,6 +71,6 @@ namespace osu.Game.Skinning } private static SkinInfo createSkinInfo(BeatmapInfo beatmap) => - new SkinInfo { Name = beatmap.ToString(), Creator = beatmap.Metadata.Author.ToString() }; + new SkinInfo { Name = beatmap.ToString(), Creator = beatmap.Metadata?.AuthorString }; } } From fbc316ea1d6cf401b0a3cf75ecb604b51f1c2211 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 27 May 2021 05:15:29 +0300 Subject: [PATCH 560/839] Fix legacy skin transformers potentially ignoring source implementations --- .../Legacy/CatchLegacySkinTransformer.cs | 20 +++++++++++-------- .../Legacy/ManiaLegacySkinTransformer.cs | 8 +++++--- .../Legacy/OsuLegacySkinTransformer.cs | 7 ++++--- .../Legacy/TaikoLegacySkinTransformer.cs | 7 ++++--- 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs index 1b48832ed6..6efd9e88c4 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs @@ -28,12 +28,15 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy { case HUDSkinComponents.ComboCounter: // catch may provide its own combo counter; hide the default. - return providesComboCounter ? Drawable.Empty() : null; + if (providesComboCounter) + return Drawable.Empty(); + + break; } } if (!(component is CatchSkinComponent catchSkinComponent)) - return null; + return Source.GetDrawableComponent(component); switch (catchSkinComponent.Component) { @@ -41,19 +44,19 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy if (GetTexture("fruit-pear") != null) return new LegacyFruitPiece(); - break; + return null; case CatchSkinComponents.Banana: if (GetTexture("fruit-bananas") != null) return new LegacyBananaPiece(); - break; + return null; case CatchSkinComponents.Droplet: if (GetTexture("fruit-drop") != null) return new LegacyDropletPiece(); - break; + return null; case CatchSkinComponents.CatcherIdle: return this.GetAnimation("fruit-catcher-idle", true, true, true) ?? @@ -71,10 +74,11 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy if (providesComboCounter) return new LegacyCatchComboCounter(Source); - break; - } + return null; - return null; + default: + return Source.GetDrawableComponent(component); + } } public override IBindable GetConfig(TLookup lookup) diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs index 24ccae895d..6617fcefe7 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs @@ -120,12 +120,14 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy case ManiaSkinComponents.StageForeground: return new LegacyStageForeground(); + + default: + return Source.GetDrawableComponent(component); } - break; + default: + return Source.GetDrawableComponent(component); } - - return null; } private Drawable getResult(HitResult result) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs index 88302ebc57..f1376551f5 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy public override Drawable GetDrawableComponent(ISkinComponent component) { if (!(component is OsuSkinComponent osuComponent)) - return null; + return Source.GetDrawableComponent(component); switch (osuComponent.Component) { @@ -115,9 +115,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy return new LegacyOldStyleSpinner(); return null; - } - return null; + default: + return Source.GetDrawableComponent(component); + } } public override IBindable GetConfig(TLookup lookup) diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs index 3e506f69ce..05778a80ef 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs @@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy } if (!(component is TaikoSkinComponent taikoComponent)) - return null; + return Source.GetDrawableComponent(component); switch (taikoComponent.Component) { @@ -130,9 +130,10 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy case TaikoSkinComponents.Mascot: return new DrawableTaikoMascot(); - } - return Source.GetDrawableComponent(component); + default: + return Source.GetDrawableComponent(component); + } } private string getHitName(TaikoSkinComponents component) From abb77b95b4c1e3e8a28f4c7b40b320c5ee459bc1 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Sun, 30 May 2021 13:06:28 +0900 Subject: [PATCH 561/839] Fix comment grammar --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 5faefbf469..6c688c1625 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -620,7 +620,7 @@ namespace osu.Game.Rulesets.Objects.Drawables protected internal new ScheduledDelegate Schedule(Action action) => base.Schedule(action); /// - /// A offset prior to the start time of at which this may begin displaying contents. + /// An offset prior to the start time of at which this may begin displaying contents. /// By default, s are assumed to display their contents within 10 seconds prior to the start time of . /// /// From 50d71faf565ea5f11df4d9192379de33d240e5f3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 30 May 2021 17:55:10 +0900 Subject: [PATCH 562/839] Restructure lookup code to avoid repeating the base call --- .../Legacy/CatchLegacySkinTransformer.cs | 63 +++++++++---------- 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs index 6efd9e88c4..e43d88ad40 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs @@ -35,50 +35,49 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy } } - if (!(component is CatchSkinComponent catchSkinComponent)) - return Source.GetDrawableComponent(component); - - switch (catchSkinComponent.Component) + if (component is CatchSkinComponent catchSkinComponent) { - case CatchSkinComponents.Fruit: - if (GetTexture("fruit-pear") != null) - return new LegacyFruitPiece(); + switch (catchSkinComponent.Component) + { + case CatchSkinComponents.Fruit: + if (GetTexture("fruit-pear") != null) + return new LegacyFruitPiece(); - return null; + return null; - case CatchSkinComponents.Banana: - if (GetTexture("fruit-bananas") != null) - return new LegacyBananaPiece(); + case CatchSkinComponents.Banana: + if (GetTexture("fruit-bananas") != null) + return new LegacyBananaPiece(); - return null; + return null; - case CatchSkinComponents.Droplet: - if (GetTexture("fruit-drop") != null) - return new LegacyDropletPiece(); + case CatchSkinComponents.Droplet: + if (GetTexture("fruit-drop") != null) + return new LegacyDropletPiece(); - return null; + return null; - case CatchSkinComponents.CatcherIdle: - return this.GetAnimation("fruit-catcher-idle", true, true, true) ?? - this.GetAnimation("fruit-ryuuta", true, true, true); + case CatchSkinComponents.CatcherIdle: + return this.GetAnimation("fruit-catcher-idle", true, true, true) ?? + this.GetAnimation("fruit-ryuuta", true, true, true); - case CatchSkinComponents.CatcherFail: - return this.GetAnimation("fruit-catcher-fail", true, true, true) ?? - this.GetAnimation("fruit-ryuuta", true, true, true); + case CatchSkinComponents.CatcherFail: + return this.GetAnimation("fruit-catcher-fail", true, true, true) ?? + this.GetAnimation("fruit-ryuuta", true, true, true); - case CatchSkinComponents.CatcherKiai: - return this.GetAnimation("fruit-catcher-kiai", true, true, true) ?? - this.GetAnimation("fruit-ryuuta", true, true, true); + case CatchSkinComponents.CatcherKiai: + return this.GetAnimation("fruit-catcher-kiai", true, true, true) ?? + this.GetAnimation("fruit-ryuuta", true, true, true); - case CatchSkinComponents.CatchComboCounter: - if (providesComboCounter) - return new LegacyCatchComboCounter(Source); + case CatchSkinComponents.CatchComboCounter: + if (providesComboCounter) + return new LegacyCatchComboCounter(Source); - return null; - - default: - return Source.GetDrawableComponent(component); + return null; + } } + + return Source.GetDrawableComponent(component); } public override IBindable GetConfig(TLookup lookup) From 17574833fb83dd3f71bcf86acb22b3d44c0603a7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 30 May 2021 19:15:59 +0900 Subject: [PATCH 563/839] Update other transformers with similar refactored logic --- .../Legacy/ManiaLegacySkinTransformer.cs | 8 +- .../Legacy/OsuLegacySkinTransformer.cs | 143 +++++++++--------- .../Legacy/TaikoLegacySkinTransformer.cs | 131 ++++++++-------- 3 files changed, 139 insertions(+), 143 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs index 6617fcefe7..261b8b1fad 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs @@ -120,14 +120,12 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy case ManiaSkinComponents.StageForeground: return new LegacyStageForeground(); - - default: - return Source.GetDrawableComponent(component); } - default: - return Source.GetDrawableComponent(component); + break; } + + return Source.GetDrawableComponent(component); } private Drawable getResult(HitResult result) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs index f1376551f5..ffd4f78400 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs @@ -34,91 +34,90 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy public override Drawable GetDrawableComponent(ISkinComponent component) { - if (!(component is OsuSkinComponent osuComponent)) - return Source.GetDrawableComponent(component); - - switch (osuComponent.Component) + if (component is OsuSkinComponent osuComponent) { - case OsuSkinComponents.FollowPoint: - return this.GetAnimation(component.LookupName, true, false, true, startAtCurrentTime: false); + switch (osuComponent.Component) + { + case OsuSkinComponents.FollowPoint: + return this.GetAnimation(component.LookupName, true, false, true, startAtCurrentTime: false); - case OsuSkinComponents.SliderFollowCircle: - var followCircle = this.GetAnimation("sliderfollowcircle", true, true, true); - if (followCircle != null) - // follow circles are 2x the hitcircle resolution in legacy skins (since they are scaled down from >1x - followCircle.Scale *= 0.5f; - return followCircle; + case OsuSkinComponents.SliderFollowCircle: + var followCircle = this.GetAnimation("sliderfollowcircle", true, true, true); + if (followCircle != null) + // follow circles are 2x the hitcircle resolution in legacy skins (since they are scaled down from >1x + followCircle.Scale *= 0.5f; + return followCircle; - case OsuSkinComponents.SliderBall: - var sliderBallContent = this.GetAnimation("sliderb", true, true, animationSeparator: ""); + case OsuSkinComponents.SliderBall: + var sliderBallContent = this.GetAnimation("sliderb", true, true, animationSeparator: ""); - // todo: slider ball has a custom frame delay based on velocity - // Math.Max((150 / Velocity) * GameBase.SIXTY_FRAME_TIME, GameBase.SIXTY_FRAME_TIME); + // todo: slider ball has a custom frame delay based on velocity + // Math.Max((150 / Velocity) * GameBase.SIXTY_FRAME_TIME, GameBase.SIXTY_FRAME_TIME); - if (sliderBallContent != null) - return new LegacySliderBall(sliderBallContent); + if (sliderBallContent != null) + return new LegacySliderBall(sliderBallContent); - return null; - - case OsuSkinComponents.SliderBody: - if (hasHitCircle.Value) - return new LegacySliderBody(); - - return null; - - case OsuSkinComponents.SliderTailHitCircle: - if (hasHitCircle.Value) - return new LegacyMainCirclePiece("sliderendcircle", false); - - return null; - - case OsuSkinComponents.SliderHeadHitCircle: - if (hasHitCircle.Value) - return new LegacyMainCirclePiece("sliderstartcircle"); - - return null; - - case OsuSkinComponents.HitCircle: - if (hasHitCircle.Value) - return new LegacyMainCirclePiece(); - - return null; - - case OsuSkinComponents.Cursor: - if (Source.GetTexture("cursor") != null) - return new LegacyCursor(); - - return null; - - case OsuSkinComponents.CursorTrail: - if (Source.GetTexture("cursortrail") != null) - return new LegacyCursorTrail(); - - return null; - - case OsuSkinComponents.HitCircleText: - if (!this.HasFont(LegacyFont.HitCircle)) return null; - return new LegacySpriteText(LegacyFont.HitCircle) - { - // stable applies a blanket 0.8x scale to hitcircle fonts - Scale = new Vector2(0.8f), - }; + case OsuSkinComponents.SliderBody: + if (hasHitCircle.Value) + return new LegacySliderBody(); - case OsuSkinComponents.SpinnerBody: - bool hasBackground = Source.GetTexture("spinner-background") != null; + return null; - if (Source.GetTexture("spinner-top") != null && !hasBackground) - return new LegacyNewStyleSpinner(); - else if (hasBackground) - return new LegacyOldStyleSpinner(); + case OsuSkinComponents.SliderTailHitCircle: + if (hasHitCircle.Value) + return new LegacyMainCirclePiece("sliderendcircle", false); - return null; + return null; - default: - return Source.GetDrawableComponent(component); + case OsuSkinComponents.SliderHeadHitCircle: + if (hasHitCircle.Value) + return new LegacyMainCirclePiece("sliderstartcircle"); + + return null; + + case OsuSkinComponents.HitCircle: + if (hasHitCircle.Value) + return new LegacyMainCirclePiece(); + + return null; + + case OsuSkinComponents.Cursor: + if (Source.GetTexture("cursor") != null) + return new LegacyCursor(); + + return null; + + case OsuSkinComponents.CursorTrail: + if (Source.GetTexture("cursortrail") != null) + return new LegacyCursorTrail(); + + return null; + + case OsuSkinComponents.HitCircleText: + if (!this.HasFont(LegacyFont.HitCircle)) + return null; + + return new LegacySpriteText(LegacyFont.HitCircle) + { + // stable applies a blanket 0.8x scale to hitcircle fonts + Scale = new Vector2(0.8f), + }; + + case OsuSkinComponents.SpinnerBody: + bool hasBackground = Source.GetTexture("spinner-background") != null; + + if (Source.GetTexture("spinner-top") != null && !hasBackground) + return new LegacyNewStyleSpinner(); + else if (hasBackground) + return new LegacyOldStyleSpinner(); + + return null; + } } + + return Source.GetDrawableComponent(component); } public override IBindable GetConfig(TLookup lookup) diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs index 05778a80ef..e0557c8617 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs @@ -38,102 +38,101 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy return Drawable.Empty().With(d => d.Expire()); } - if (!(component is TaikoSkinComponent taikoComponent)) - return Source.GetDrawableComponent(component); - - switch (taikoComponent.Component) + if (component is TaikoSkinComponent taikoComponent) { - case TaikoSkinComponents.DrumRollBody: - if (GetTexture("taiko-roll-middle") != null) - return new LegacyDrumRoll(); + switch (taikoComponent.Component) + { + case TaikoSkinComponents.DrumRollBody: + if (GetTexture("taiko-roll-middle") != null) + return new LegacyDrumRoll(); - return null; + return null; - case TaikoSkinComponents.InputDrum: - if (GetTexture("taiko-bar-left") != null) - return new LegacyInputDrum(); + case TaikoSkinComponents.InputDrum: + if (GetTexture("taiko-bar-left") != null) + return new LegacyInputDrum(); - return null; + return null; - case TaikoSkinComponents.CentreHit: - case TaikoSkinComponents.RimHit: + case TaikoSkinComponents.CentreHit: + case TaikoSkinComponents.RimHit: - if (GetTexture("taikohitcircle") != null) - return new LegacyHit(taikoComponent.Component); + if (GetTexture("taikohitcircle") != null) + return new LegacyHit(taikoComponent.Component); - return null; + return null; - case TaikoSkinComponents.DrumRollTick: - return this.GetAnimation("sliderscorepoint", false, false); + case TaikoSkinComponents.DrumRollTick: + return this.GetAnimation("sliderscorepoint", false, false); - case TaikoSkinComponents.HitTarget: - if (GetTexture("taikobigcircle") != null) - return new TaikoLegacyHitTarget(); + case TaikoSkinComponents.HitTarget: + if (GetTexture("taikobigcircle") != null) + return new TaikoLegacyHitTarget(); - return null; + return null; - case TaikoSkinComponents.PlayfieldBackgroundRight: - if (GetTexture("taiko-bar-right") != null) - return new TaikoLegacyPlayfieldBackgroundRight(); + case TaikoSkinComponents.PlayfieldBackgroundRight: + if (GetTexture("taiko-bar-right") != null) + return new TaikoLegacyPlayfieldBackgroundRight(); - return null; + return null; - case TaikoSkinComponents.PlayfieldBackgroundLeft: - // This is displayed inside LegacyInputDrum. It is required to be there for layout purposes (can be seen on legacy skins). - if (GetTexture("taiko-bar-right") != null) - return Drawable.Empty(); + case TaikoSkinComponents.PlayfieldBackgroundLeft: + // This is displayed inside LegacyInputDrum. It is required to be there for layout purposes (can be seen on legacy skins). + if (GetTexture("taiko-bar-right") != null) + return Drawable.Empty(); - return null; + return null; - case TaikoSkinComponents.BarLine: - if (GetTexture("taiko-barline") != null) - return new LegacyBarLine(); + case TaikoSkinComponents.BarLine: + if (GetTexture("taiko-barline") != null) + return new LegacyBarLine(); - return null; + return null; - case TaikoSkinComponents.TaikoExplosionMiss: + case TaikoSkinComponents.TaikoExplosionMiss: - var missSprite = this.GetAnimation(getHitName(taikoComponent.Component), true, false); - if (missSprite != null) - return new LegacyHitExplosion(missSprite); + var missSprite = this.GetAnimation(getHitName(taikoComponent.Component), true, false); + if (missSprite != null) + return new LegacyHitExplosion(missSprite); - return null; + return null; - case TaikoSkinComponents.TaikoExplosionOk: - case TaikoSkinComponents.TaikoExplosionGreat: + case TaikoSkinComponents.TaikoExplosionOk: + case TaikoSkinComponents.TaikoExplosionGreat: - var hitName = getHitName(taikoComponent.Component); - var hitSprite = this.GetAnimation(hitName, true, false); + var hitName = getHitName(taikoComponent.Component); + var hitSprite = this.GetAnimation(hitName, true, false); - if (hitSprite != null) - { - var strongHitSprite = this.GetAnimation($"{hitName}k", true, false); + if (hitSprite != null) + { + var strongHitSprite = this.GetAnimation($"{hitName}k", true, false); - return new LegacyHitExplosion(hitSprite, strongHitSprite); - } + return new LegacyHitExplosion(hitSprite, strongHitSprite); + } - return null; + return null; - case TaikoSkinComponents.TaikoExplosionKiai: - // suppress the default kiai explosion if the skin brings its own sprites. - // the drawable needs to expire as soon as possible to avoid accumulating empty drawables on the playfield. - if (hasExplosion.Value) - return Drawable.Empty().With(d => d.Expire()); + case TaikoSkinComponents.TaikoExplosionKiai: + // suppress the default kiai explosion if the skin brings its own sprites. + // the drawable needs to expire as soon as possible to avoid accumulating empty drawables on the playfield. + if (hasExplosion.Value) + return Drawable.Empty().With(d => d.Expire()); - return null; + return null; - case TaikoSkinComponents.Scroller: - if (GetTexture("taiko-slider") != null) - return new LegacyTaikoScroller(); + case TaikoSkinComponents.Scroller: + if (GetTexture("taiko-slider") != null) + return new LegacyTaikoScroller(); - return null; + return null; - case TaikoSkinComponents.Mascot: - return new DrawableTaikoMascot(); - - default: - return Source.GetDrawableComponent(component); + case TaikoSkinComponents.Mascot: + return new DrawableTaikoMascot(); + } } + + return Source.GetDrawableComponent(component); } private string getHitName(TaikoSkinComponents component) From 60b781701fb03854a19c6f3b9f7ebaac5249234b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 30 May 2021 14:20:03 +0300 Subject: [PATCH 564/839] Rewrite catch combo counter hide logic --- .../Skinning/Legacy/CatchLegacySkinTransformer.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs index 05580f6d06..802de55b7b 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs @@ -29,20 +29,17 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy switch (targetComponent.Target) { case SkinnableTarget.MainHUDComponents: - if (!providesComboCounter) - break; + var components = Source.GetDrawableComponent(component) as SkinnableTargetComponentsContainer; - if (Source.GetDrawableComponent(component) is SkinnableTargetComponentsContainer components) + if (providesComboCounter && components != null) { // catch may provide its own combo counter; hide the default. // todo: this should be done in an elegant way per ruleset, defining which HUD skin components should be displayed. foreach (var legacyComboCounter in components.OfType()) legacyComboCounter.ContentVisible = false; - - return components; } - break; + return components; } } From d12e93bfc67598007192899b440726396b8e202e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 31 May 2021 00:02:55 +0300 Subject: [PATCH 565/839] Add skin traget resetting on setup/teardown steps --- .../Tests/Visual/LegacySkinPlayerTestScene.cs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs b/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs index 907a37f4b9..f78d05c6e5 100644 --- a/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs +++ b/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs @@ -3,7 +3,9 @@ using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.IO.Stores; +using osu.Framework.Testing; using osu.Game.Rulesets; using osu.Game.Skinning; @@ -25,6 +27,29 @@ namespace osu.Game.Tests.Visual legacySkinSource = new SkinProvidingContainer(LegacySkin); } + [SetUpSteps] + public override void SetUpSteps() + { + base.SetUpSteps(); + addResetTargetsStep(); + } + + [TearDownSteps] + public override void TearDownSteps() + { + addResetTargetsStep(); + base.TearDownSteps(); + } + + private void addResetTargetsStep() + { + AddStep("reset targets", () => this.ChildrenOfType().ForEach(t => + { + LegacySkin.ResetDrawableTarget(t); + t.Reload(); + })); + } + public class SkinProvidingPlayer : TestPlayer { [Cached(typeof(ISkinSource))] From 3fbd4e276d632b2763858a8efa031a0d43b57d71 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 31 May 2021 00:07:29 +0300 Subject: [PATCH 566/839] Add simple xmldoc --- osu.Game/Screens/Play/HUD/LegacyComboCounter.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs b/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs index ee00c71b0f..112b36616c 100644 --- a/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs +++ b/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs @@ -47,6 +47,13 @@ namespace osu.Game.Screens.Play.HUD private readonly Container counterContainer; + /// + /// Changes the visibility state of the combo counter internally without affecting its . + /// + /// + /// This is temporarily done for rulesets that provide their own combo counter and don't want the HUD one to be visible, + /// without potentially affecting the user's selected skin. + /// public bool ContentVisible { set => counterContainer.Alpha = value ? 1 : 0; From bca8a9ea53890906a806e8f97764147bd5d2098f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 31 May 2021 10:02:02 +0900 Subject: [PATCH 567/839] Fix HandleFrame crashing when not playing --- osu.Game/Online/Spectator/SpectatorClient.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Online/Spectator/SpectatorClient.cs b/osu.Game/Online/Spectator/SpectatorClient.cs index a4fc963328..2546374b21 100644 --- a/osu.Game/Online/Spectator/SpectatorClient.cs +++ b/osu.Game/Online/Spectator/SpectatorClient.cs @@ -235,6 +235,9 @@ namespace osu.Game.Online.Spectator { Debug.Assert(ThreadSafety.IsUpdateThread); + if (!IsPlaying) + return; + if (frame is IConvertibleReplayFrame convertible) pendingFrames.Enqueue(convertible.ToLegacy(currentBeatmap)); From 113f90e92e341f826926a1a3d2f4dcd9387df952 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Mon, 31 May 2021 10:49:16 +0700 Subject: [PATCH 568/839] add back schedule is CurrentPath setter --- osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs index 4e671cca6d..1c5fd99ce3 100644 --- a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs +++ b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs @@ -15,7 +15,7 @@ namespace osu.Game.Overlays.Wiki.Markdown { public string CurrentPath { - set => DocumentUrl = $"{DocumentUrl}wiki/{value}"; + set => Schedule(() => DocumentUrl = $"{DocumentUrl}wiki/{value}"); } protected override void AddMarkdownComponent(IMarkdownObject markdownObject, FillFlowContainer container, int level) From fb111e23d86b02c5c8cc42c843fa706b5fe67481 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 31 May 2021 07:24:59 +0300 Subject: [PATCH 569/839] Remove "temporarily" --- osu.Game/Screens/Play/HUD/LegacyComboCounter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs b/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs index 112b36616c..6c2752569d 100644 --- a/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs +++ b/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs @@ -51,7 +51,7 @@ namespace osu.Game.Screens.Play.HUD /// Changes the visibility state of the combo counter internally without affecting its . /// /// - /// This is temporarily done for rulesets that provide their own combo counter and don't want the HUD one to be visible, + /// This is used for rulesets that provide their own combo counter and don't want the HUD one to be visible, /// without potentially affecting the user's selected skin. /// public bool ContentVisible From a4dca6f839d7973372b78023e8133b8e5f8e993f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 31 May 2021 13:39:18 +0900 Subject: [PATCH 570/839] Reorder methods around load() --- osu.Game/OsuGameBase.cs | 82 ++++++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index dfb9bd3a99..918f231a19 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -160,47 +160,6 @@ namespace osu.Game Name = @"osu!lazer"; } - public override void SetHost(GameHost host) - { - base.SetHost(host); - - // may be non-null for certain tests - Storage ??= host.Storage; - - LocalConfig ??= UseDevelopmentServer - ? new DevelopmentOsuConfigManager(Storage) - : new OsuConfigManager(Storage); - } - - /// - /// Use to programatically exit the game as if the user was triggering via alt-f4. - /// Will keep persisting until an exit occurs (exit may be blocked multiple times). - /// - public void GracefullyExit() - { - if (!OnExiting()) - Exit(); - else - Scheduler.AddDelayed(GracefullyExit, 2000); - } - - public void Migrate(string path) - { - contextFactory.FlushConnections(); - (Storage as OsuStorage)?.Migrate(Host.GetStorage(path)); - } - - protected override UserInputManager CreateUserInputManager() => new OsuUserInputManager(); - - protected virtual BatteryInfo CreateBatteryInfo() => null; - - protected virtual Container CreateScalingContainer() => new DrawSizePreservingFillContainer(); - - protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => - dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - - protected override Storage CreateStorage(GameHost host, Storage defaultStorage) => new OsuStorage(host, defaultStorage); - [BackgroundDependencyLoader] private void load() { @@ -390,6 +349,47 @@ namespace osu.Game FrameStatistics.ValueChanged += e => fpsDisplayVisible.Value = e.NewValue != FrameStatisticsMode.None; } + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => + dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); + + public override void SetHost(GameHost host) + { + base.SetHost(host); + + // may be non-null for certain tests + Storage ??= host.Storage; + + LocalConfig ??= UseDevelopmentServer + ? new DevelopmentOsuConfigManager(Storage) + : new OsuConfigManager(Storage); + } + + /// + /// Use to programatically exit the game as if the user was triggering via alt-f4. + /// Will keep persisting until an exit occurs (exit may be blocked multiple times). + /// + public void GracefullyExit() + { + if (!OnExiting()) + Exit(); + else + Scheduler.AddDelayed(GracefullyExit, 2000); + } + + public void Migrate(string path) + { + contextFactory.FlushConnections(); + (Storage as OsuStorage)?.Migrate(Host.GetStorage(path)); + } + + protected override UserInputManager CreateUserInputManager() => new OsuUserInputManager(); + + protected virtual BatteryInfo CreateBatteryInfo() => null; + + protected virtual Container CreateScalingContainer() => new DrawSizePreservingFillContainer(); + + protected override Storage CreateStorage(GameHost host, Storage defaultStorage) => new OsuStorage(host, defaultStorage); + private void onRulesetChanged(ValueChangedEvent r) { var dict = new Dictionary>(); From 0c101d285925cfd1e392dc383f8186a62a07a9a9 Mon Sep 17 00:00:00 2001 From: ekrctb <32995012+ekrctb@users.noreply.github.com> Date: Mon, 31 May 2021 13:41:49 +0900 Subject: [PATCH 571/839] Apply comment rewording suggestion Co-authored-by: Dan Balasescu --- osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs b/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs index 3375f41cc3..c275ba32f6 100644 --- a/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs +++ b/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs @@ -37,8 +37,8 @@ namespace osu.Game.Rulesets.Objects startTimeBindable.BindTo(HitObject.StartTimeBindable); startTimeBindable.BindValueChanged(_ => setInitialLifetime(), true); - // It is important to subscribe to this event before applied to a DrawableHitObject. - // Otherwise DHO cannot overwrite LifetimeStart set in setInitialLifetime. + // Subscribe to this event before the DrawableHitObject so that the local callback is invoked before the entry is re-applied as a result of DefaultsApplied. + // This way, the DrawableHitObject can use OnApply() to overwrite the LifetimeStart that was set inside setInitialLifetime(). HitObject.DefaultsApplied += _ => setInitialLifetime(); } From 06bd696cc21eb4e20667875093f790468cc474e6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 31 May 2021 14:11:57 +0900 Subject: [PATCH 572/839] Remove `previous` consumption logic in `GetWorkingBeatmap` This should not be required since the introduction of `workingCache`, which does the same thing in a more global way. --- osu.Game/Beatmaps/BeatmapManager.cs | 6 +----- osu.Game/Overlays/MusicController.cs | 4 ++-- osu.Game/Screens/Select/SongSelect.cs | 7 ++++--- osu.Game/Tests/Visual/EditorTestScene.cs | 2 +- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index b2cd0c9392..5a26f5b66c 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -280,9 +280,8 @@ namespace osu.Game.Beatmaps /// Retrieve a instance for the provided /// /// The beatmap to lookup. - /// The currently loaded . Allows for optimisation where elements are shared with the new beatmap. May be returned if beatmapInfo requested matches /// A instance correlating to the provided . - public virtual WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo, WorkingBeatmap previous = null) + public virtual WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo) { if (beatmapInfo?.BeatmapSet == null) return DefaultBeatmap; @@ -294,9 +293,6 @@ namespace osu.Game.Beatmaps beatmapInfo = QueryBeatmap(b => b.ID == lookupId); } - if (beatmapInfo.ID > 0 && previous != null && previous.BeatmapInfo?.ID == beatmapInfo.ID) - return previous; - lock (workingCache) { var working = workingCache.FirstOrDefault(w => w.BeatmapInfo?.ID == beatmapInfo.ID); diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 3a9a6261ba..a15f80ca21 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -252,7 +252,7 @@ namespace osu.Game.Overlays if (playable != null) { - changeBeatmap(beatmaps.GetWorkingBeatmap(playable.Beatmaps.First(), beatmap.Value)); + changeBeatmap(beatmaps.GetWorkingBeatmap(playable.Beatmaps.First())); restartTrack(); return PreviousTrackResult.Previous; } @@ -283,7 +283,7 @@ namespace osu.Game.Overlays if (playable != null) { - changeBeatmap(beatmaps.GetWorkingBeatmap(playable.Beatmaps.First(), beatmap.Value)); + changeBeatmap(beatmaps.GetWorkingBeatmap(playable.Beatmaps.First())); restartTrack(); return true; } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 74e10037ab..270addc8e6 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -505,12 +505,13 @@ namespace osu.Game.Screens.Select { Logger.Log($"beatmap changed from \"{Beatmap.Value.BeatmapInfo}\" to \"{beatmap}\""); - WorkingBeatmap previous = Beatmap.Value; - Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap, previous); + int? lastSetID = Beatmap.Value?.BeatmapInfo.BeatmapSetInfoID; + + Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap); if (beatmap != null) { - if (beatmap.BeatmapSetInfoID == previous?.BeatmapInfo.BeatmapSetInfoID) + if (beatmap.BeatmapSetInfoID == lastSetID) sampleChangeDifficulty.Play(); else sampleChangeBeatmap.Play(); diff --git a/osu.Game/Tests/Visual/EditorTestScene.cs b/osu.Game/Tests/Visual/EditorTestScene.cs index de7fdd0cf8..ca331088ce 100644 --- a/osu.Game/Tests/Visual/EditorTestScene.cs +++ b/osu.Game/Tests/Visual/EditorTestScene.cs @@ -96,7 +96,7 @@ namespace osu.Game.Tests.Visual protected override string ComputeHash(BeatmapSetInfo item, ArchiveReader reader = null) => string.Empty; - public override WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo, WorkingBeatmap previous = null) + public override WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo) => testBeatmap; public override void Save(BeatmapInfo info, IBeatmap beatmapContent, ISkin beatmapSkin = null) From 5925beaf21ffbebddee2239cc51110491ac07cda Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 31 May 2021 14:24:46 +0900 Subject: [PATCH 573/839] Fix bindable lease failure in editor beatmap creation tests --- .../Visual/Editing/TestSceneEditorBeatmapCreation.cs | 8 ++++++-- osu.Game/Tests/Visual/EditorTestScene.cs | 7 ++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs index 7584c74c71..49bc01aecc 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs @@ -29,8 +29,6 @@ namespace osu.Game.Tests.Visual.Editing public override void SetUpSteps() { - AddStep("set dummy", () => Beatmap.Value = new DummyWorkingBeatmap(Audio, null)); - base.SetUpSteps(); // if we save a beatmap with a hash collision, things fall over. @@ -38,6 +36,12 @@ namespace osu.Game.Tests.Visual.Editing AddStep("make new beatmap unique", () => EditorBeatmap.Metadata.Title = Guid.NewGuid().ToString()); } + protected override void LoadEditor() + { + Beatmap.Value = new DummyWorkingBeatmap(Audio, null); + base.LoadEditor(); + } + [Test] public void TestCreateNewBeatmap() { diff --git a/osu.Game/Tests/Visual/EditorTestScene.cs b/osu.Game/Tests/Visual/EditorTestScene.cs index a9ee8e2668..80e1fdcce5 100644 --- a/osu.Game/Tests/Visual/EditorTestScene.cs +++ b/osu.Game/Tests/Visual/EditorTestScene.cs @@ -33,12 +33,17 @@ namespace osu.Game.Tests.Visual { base.SetUpSteps(); - AddStep("load editor", () => LoadScreen(Editor = CreateEditor())); + AddStep("load editor", LoadEditor); AddUntilStep("wait for editor to load", () => EditorComponentsReady); AddStep("get beatmap", () => EditorBeatmap = Editor.ChildrenOfType().Single()); AddStep("get clock", () => EditorClock = Editor.ChildrenOfType().Single()); } + protected virtual void LoadEditor() + { + LoadScreen(Editor = CreateEditor()); + } + /// /// Creates the ruleset for providing a corresponding beatmap to load the editor on. /// From e25cca6fbe0271976e1a711ab0037c6493b412f0 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 31 May 2021 14:37:28 +0900 Subject: [PATCH 574/839] Update drawable lifetime on entry lifetime change --- .../Pooling/PoolableDrawableWithLifetime.cs | 42 +++++++++++++------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs b/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs index 64e1ac16bd..66d4421cdb 100644 --- a/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs +++ b/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs @@ -3,6 +3,7 @@ #nullable enable +using System; using System.Diagnostics; using osu.Framework.Graphics.Performance; using osu.Framework.Graphics.Pooling; @@ -26,17 +27,18 @@ namespace osu.Game.Rulesets.Objects.Pooling /// protected bool HasEntryApplied { get; private set; } - // Drawable's lifetime gets out of sync with entry's lifetime if entry's lifetime is modified. - // We cannot delegate getter to `Entry.LifetimeStart` because it is incompatible with `LifetimeManagementContainer` due to how lifetime change is detected. public override double LifetimeStart { get => base.LifetimeStart; set { - base.LifetimeStart = value; + if (Entry == null && LifetimeStart != value) + throw new InvalidOperationException($"Cannot modify lifetime of {nameof(PoolableDrawableWithLifetime)} when entry is not set"); - if (Entry != null) - Entry.LifetimeStart = value; + if (Entry == null) return; + + Entry.LifetimeStart = value; + base.LifetimeStart = Entry.LifetimeStart; } } @@ -45,10 +47,13 @@ namespace osu.Game.Rulesets.Objects.Pooling get => base.LifetimeEnd; set { - base.LifetimeEnd = value; + if (Entry == null && LifetimeEnd != value) + throw new InvalidOperationException($"Cannot modify lifetime of {nameof(PoolableDrawableWithLifetime)} when entry is not set"); - if (Entry != null) - Entry.LifetimeEnd = value; + if (Entry == null) return; + + Entry.LifetimeEnd = value; + base.LifetimeEnd = Entry.LifetimeEnd; } } @@ -79,9 +84,8 @@ namespace osu.Game.Rulesets.Objects.Pooling free(); Entry = entry; - - base.LifetimeStart = entry.LifetimeStart; - base.LifetimeEnd = entry.LifetimeEnd; + entry.LifetimeChanged += entryLifetimeChanged; + setLifetimeFromEntry(); OnApply(entry); @@ -117,11 +121,23 @@ namespace osu.Game.Rulesets.Objects.Pooling OnFree(Entry); + Entry.LifetimeChanged -= entryLifetimeChanged; Entry = null; - base.LifetimeStart = double.MinValue; - base.LifetimeEnd = double.MaxValue; + setLifetimeFromEntry(); HasEntryApplied = false; } + + private void entryLifetimeChanged(LifetimeEntry entry) + { + Debug.Assert(entry == Entry); + setLifetimeFromEntry(); + } + + private void setLifetimeFromEntry() + { + base.LifetimeStart = Entry?.LifetimeStart ?? double.MinValue; + base.LifetimeEnd = Entry?.LifetimeEnd ?? double.MaxValue; + } } } From b7afea37c4f8facce6a43e362371c2470110572c Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 31 May 2021 14:37:51 +0900 Subject: [PATCH 575/839] Test drawable lifetime change on entry lifetime change --- .../Gameplay/TestSceneDrawableHitObject.cs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs b/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs index 3de2dc72bb..5dfc9a2786 100644 --- a/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs +++ b/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs @@ -92,6 +92,35 @@ namespace osu.Game.Tests.Gameplay AddAssert("Lifetime is correct", () => dho.LifetimeStart == TestDrawableHitObject.LIFETIME_ON_APPLY && entry.LifetimeStart == TestDrawableHitObject.LIFETIME_ON_APPLY); } + [Test] + public void TestDrawableLifetimeUpdateOnEntryLifetimeChange() + { + TestDrawableHitObject dho = null; + TestLifetimeEntry entry = null; + AddStep("Create DHO", () => + { + dho = new TestDrawableHitObject(null); + dho.Apply(entry = new TestLifetimeEntry(new HitObject())); + Child = dho; + }); + + AddStep("Set entry lifetime", () => + { + entry.LifetimeStart = 777; + entry.LifetimeEnd = 888; + }); + AddAssert("Drawable lifetime is updated", () => dho.LifetimeStart == 777 && dho.LifetimeEnd == 888); + + AddStep("KeepAlive = true", () => entry.KeepAlive = true); + AddAssert("Drawable lifetime is updated", () => dho.LifetimeStart == double.MinValue && dho.LifetimeEnd == double.MaxValue); + + AddStep("Modify start time", () => entry.HitObject.StartTime = 100); + AddAssert("Drawable lifetime is correct", () => dho.LifetimeStart == double.MinValue); + + AddStep("KeepAlive = false", () => entry.KeepAlive = false); + AddAssert("Drawable lifetime is restored", () => dho.LifetimeStart == 100 - TestLifetimeEntry.INITIAL_LIFETIME_OFFSET); + } + private class TestDrawableHitObject : DrawableHitObject { public const double INITIAL_LIFETIME_OFFSET = 100; From 771f3c48c08874c7cf2b8564c153c01ef2561619 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 31 May 2021 14:48:52 +0900 Subject: [PATCH 576/839] Add failing test showing lifetime not recomputed with pooled objects --- .../Gameplay/TestSceneDrawableScrollingRuleset.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs index 9931ee4a45..75a5eec6f7 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs @@ -90,6 +90,20 @@ namespace osu.Game.Tests.Visual.Gameplay assertChildPosition(5); } + [TestCase("pooled")] + [TestCase("non-pooled")] + public void TestLifetimeRecomputedWhenTimeRangeChanges(string pooled) + { + var beatmap = createBeatmap(_ => pooled == "pooled" ? new TestPooledHitObject() : new TestHitObject()); + beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = time_range }); + createTest(beatmap); + + assertDead(3); + + AddStep("increase time range", () => drawableRuleset.TimeRange.Value = 3 * time_range); + assertPosition(3, 1); + } + [Test] public void TestRelativeBeatLengthScaleSingleTimingPoint() { From 4e186b0cf55360f9b5f9442c49844e42b0cd50e0 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 31 May 2021 09:24:26 +0300 Subject: [PATCH 577/839] `ContentVisible` -> `HiddenByRulesetImplementation` --- .../Skinning/Legacy/CatchLegacySkinTransformer.cs | 2 +- osu.Game/Screens/Play/HUD/LegacyComboCounter.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs index 802de55b7b..8c9e602cd4 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs @@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy // catch may provide its own combo counter; hide the default. // todo: this should be done in an elegant way per ruleset, defining which HUD skin components should be displayed. foreach (var legacyComboCounter in components.OfType()) - legacyComboCounter.ContentVisible = false; + legacyComboCounter.HiddenByRulesetImplementation = false; } return components; diff --git a/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs b/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs index 6c2752569d..1737634e31 100644 --- a/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs +++ b/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs @@ -48,13 +48,13 @@ namespace osu.Game.Screens.Play.HUD private readonly Container counterContainer; /// - /// Changes the visibility state of the combo counter internally without affecting its . + /// Hides the combo counter internally without affecting its . /// /// - /// This is used for rulesets that provide their own combo counter and don't want the HUD one to be visible, + /// This is used for rulesets that provide their own combo counter and don't want this HUD one to be visible, /// without potentially affecting the user's selected skin. /// - public bool ContentVisible + public bool HiddenByRulesetImplementation { set => counterContainer.Alpha = value ? 1 : 0; } From 56a0a24cbaf0d09df98929f2ff7576d85a00971a Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 31 May 2021 15:33:28 +0900 Subject: [PATCH 578/839] Make SetInitialLifetime public --- osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs b/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs index f654fa91cf..5ad0d6246e 100644 --- a/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs +++ b/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs @@ -35,11 +35,11 @@ namespace osu.Game.Rulesets.Objects HitObject = hitObject; startTimeBindable.BindTo(HitObject.StartTimeBindable); - startTimeBindable.BindValueChanged(_ => setInitialLifetime(), true); + startTimeBindable.BindValueChanged(_ => SetInitialLifetime(), true); // Subscribe to this event before the DrawableHitObject so that the local callback is invoked before the entry is re-applied as a result of DefaultsApplied. // This way, the DrawableHitObject can use OnApply() to overwrite the LifetimeStart that was set inside setInitialLifetime(). - HitObject.DefaultsApplied += _ => setInitialLifetime(); + HitObject.DefaultsApplied += _ => SetInitialLifetime(); } // The lifetime, as set by the hitobject. @@ -94,6 +94,6 @@ namespace osu.Game.Rulesets.Objects /// /// Set using . /// - private void setInitialLifetime() => LifetimeStart = HitObject.StartTime - InitialLifetimeOffset; + public void SetInitialLifetime() => LifetimeStart = HitObject.StartTime - InitialLifetimeOffset; } } From 3cedc0824dab6614bfe191aa6836d02882021c64 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 31 May 2021 16:02:33 +0900 Subject: [PATCH 579/839] Don't add nested hit objects to the sets Only top-level hit objects are checked for layout computation caching. Also, lifetime of nested hit objects are not managed by the HitObjectContainer. --- .../Scrolling/ScrollingHitObjectContainer.cs | 27 ++++++------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index a9eaf3da68..46308ac38e 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.UI.Scrolling private readonly HashSet toComputeLifetime = new HashSet(); /// - /// A set containing all which have an up-to-date layout. + /// A set of top-level s which have an up-to-date layout. /// private readonly HashSet layoutComputed = new HashSet(); @@ -150,29 +150,18 @@ namespace osu.Game.Rulesets.UI.Scrolling } } - protected override void OnAdd(DrawableHitObject drawableHitObject) => onAddRecursive(drawableHitObject); - - protected override void OnRemove(DrawableHitObject drawableHitObject) => onRemoveRecursive(drawableHitObject); - - private void onAddRecursive(DrawableHitObject hitObject) + protected override void OnAdd(DrawableHitObject drawableHitObject) { - invalidateHitObject(hitObject); - - hitObject.DefaultsApplied += invalidateHitObject; - - foreach (var nested in hitObject.NestedHitObjects) - onAddRecursive(nested); + invalidateHitObject(drawableHitObject); + drawableHitObject.DefaultsApplied += invalidateHitObject; } - private void onRemoveRecursive(DrawableHitObject hitObject) + protected override void OnRemove(DrawableHitObject drawableHitObject) { - toComputeLifetime.Remove(hitObject); - layoutComputed.Remove(hitObject); + toComputeLifetime.Remove(drawableHitObject); + layoutComputed.Remove(drawableHitObject); - hitObject.DefaultsApplied -= invalidateHitObject; - - foreach (var nested in hitObject.NestedHitObjects) - onRemoveRecursive(nested); + drawableHitObject.DefaultsApplied -= invalidateHitObject; } /// From 742c5b442be0186966de975e168c13905ba3b9db Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 31 May 2021 16:24:13 +0900 Subject: [PATCH 580/839] Remove delay of lifetime update The scheduling is no longer necessary because `OnAdd` is changed to not invoked immediately for non-pooled DHOs. --- .../Scrolling/ScrollingHitObjectContainer.cs | 62 ++++++------------- 1 file changed, 20 insertions(+), 42 deletions(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index 46308ac38e..aed12653d9 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -17,11 +17,6 @@ namespace osu.Game.Rulesets.UI.Scrolling private readonly IBindable timeRange = new BindableDouble(); private readonly IBindable direction = new Bindable(); - /// - /// Hit objects which require lifetime computation in the next update call. - /// - private readonly HashSet toComputeLifetime = new HashSet(); - /// /// A set of top-level s which have an up-to-date layout. /// @@ -54,7 +49,6 @@ namespace osu.Game.Rulesets.UI.Scrolling { base.Clear(); - toComputeLifetime.Clear(); layoutComputed.Clear(); } @@ -158,20 +152,14 @@ namespace osu.Game.Rulesets.UI.Scrolling protected override void OnRemove(DrawableHitObject drawableHitObject) { - toComputeLifetime.Remove(drawableHitObject); layoutComputed.Remove(drawableHitObject); drawableHitObject.DefaultsApplied -= invalidateHitObject; } - /// - /// Make this lifetime and layout computed in next update. - /// private void invalidateHitObject(DrawableHitObject hitObject) { - // Lifetime computation is delayed until next update because - // when the hit object is not pooled this container is not loaded here and `scrollLength` cannot be computed. - toComputeLifetime.Add(hitObject); + hitObject.LifetimeStart = computeOriginAdjustedLifetimeStart(hitObject); layoutComputed.Remove(hitObject); } @@ -181,39 +169,29 @@ namespace osu.Game.Rulesets.UI.Scrolling { base.Update(); - if (!layoutCache.IsValid) + if (layoutCache.IsValid) return; + + foreach (var hitObject in Objects) { - toComputeLifetime.Clear(); - - foreach (var hitObject in Objects) - { - if (hitObject.HitObject != null) - toComputeLifetime.Add(hitObject); - } - - layoutComputed.Clear(); - - scrollingInfo.Algorithm.Reset(); - - switch (direction.Value) - { - case ScrollingDirection.Up: - case ScrollingDirection.Down: - scrollLength = DrawSize.Y; - break; - - default: - scrollLength = DrawSize.X; - break; - } - - layoutCache.Validate(); + if (hitObject.HitObject != null) + invalidateHitObject(hitObject); } - foreach (var hitObject in toComputeLifetime) - hitObject.LifetimeStart = computeOriginAdjustedLifetimeStart(hitObject); + scrollingInfo.Algorithm.Reset(); - toComputeLifetime.Clear(); + switch (direction.Value) + { + case ScrollingDirection.Up: + case ScrollingDirection.Down: + scrollLength = DrawSize.Y; + break; + + default: + scrollLength = DrawSize.X; + break; + } + + layoutCache.Validate(); } protected override void UpdateAfterChildrenLife() From 6d968467761d962051963ddb215f9dcb161102e9 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 31 May 2021 16:28:02 +0900 Subject: [PATCH 581/839] Remove `scrollLength` caching field It was not clear when the field is updated. --- .../Scrolling/ScrollingHitObjectContainer.cs | 35 +++++++------------ 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index aed12653d9..b174632498 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -77,7 +77,7 @@ namespace osu.Game.Rulesets.UI.Scrolling flipPositionIfRequired(ref position); - return scrollingInfo.Algorithm.TimeAt(position, Time.Current, scrollingInfo.TimeRange.Value, getLength()); + return scrollingInfo.Algorithm.TimeAt(position, Time.Current, scrollingInfo.TimeRange.Value, scrollLength); } /// @@ -85,7 +85,7 @@ namespace osu.Game.Rulesets.UI.Scrolling /// public Vector2 ScreenSpacePositionAtTime(double time) { - var pos = scrollingInfo.Algorithm.PositionAt(time, Time.Current, scrollingInfo.TimeRange.Value, getLength()); + var pos = scrollingInfo.Algorithm.PositionAt(time, Time.Current, scrollingInfo.TimeRange.Value, scrollLength); flipPositionIfRequired(ref pos); @@ -100,16 +100,19 @@ namespace osu.Game.Rulesets.UI.Scrolling } } - private float getLength() + private float scrollLength { - switch (scrollingInfo.Direction.Value) + get { - case ScrollingDirection.Left: - case ScrollingDirection.Right: - return DrawWidth; + switch (scrollingInfo.Direction.Value) + { + case ScrollingDirection.Left: + case ScrollingDirection.Right: + return DrawWidth; - default: - return DrawHeight; + default: + return DrawHeight; + } } } @@ -163,8 +166,6 @@ namespace osu.Game.Rulesets.UI.Scrolling layoutComputed.Remove(hitObject); } - private float scrollLength; - protected override void Update() { base.Update(); @@ -179,18 +180,6 @@ namespace osu.Game.Rulesets.UI.Scrolling scrollingInfo.Algorithm.Reset(); - switch (direction.Value) - { - case ScrollingDirection.Up: - case ScrollingDirection.Down: - scrollLength = DrawSize.Y; - break; - - default: - scrollLength = DrawSize.X; - break; - } - layoutCache.Validate(); } From 86d1225aad80e1ac2cd79e2b22719b1df895713b Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 31 May 2021 16:10:31 +0900 Subject: [PATCH 582/839] Reset lifetime to initial lifetime when layout is invalidated --- .../UI/Scrolling/ScrollingHitObjectContainer.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index a9eaf3da68..10d6815c0a 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -195,15 +195,13 @@ namespace osu.Game.Rulesets.UI.Scrolling if (!layoutCache.IsValid) { toComputeLifetime.Clear(); - - foreach (var hitObject in Objects) - { - if (hitObject.HitObject != null) - toComputeLifetime.Add(hitObject); - } - layoutComputed.Clear(); + // Reset lifetime to the conservative estimation. + // If a drawable becomes alive by this lifetime, its lifetime will be updated to a more precise lifetime in the next update. + foreach (var entry in Entries) + entry.SetInitialLifetime(); + scrollingInfo.Algorithm.Reset(); switch (direction.Value) From 675fe3744692a8a525c8e2769051bbd5f3561fcd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 31 May 2021 18:24:42 +0900 Subject: [PATCH 583/839] Change check order around to ensure re-fetches which return no results don't nullref --- osu.Game/Beatmaps/BeatmapManager.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 5a26f5b66c..518ef2cc19 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -283,16 +283,16 @@ namespace osu.Game.Beatmaps /// A instance correlating to the provided . public virtual WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo) { - if (beatmapInfo?.BeatmapSet == null) - return DefaultBeatmap; - // if there are no files, presume the full beatmap info has not yet been fetched from the database. - if (beatmapInfo.BeatmapSet.Files.Count == 0) + if (beatmapInfo?.BeatmapSet?.Files.Count == 0) { int lookupId = beatmapInfo.ID; beatmapInfo = QueryBeatmap(b => b.ID == lookupId); } + if (beatmapInfo?.BeatmapSet == null) + return DefaultBeatmap; + lock (workingCache) { var working = workingCache.FirstOrDefault(w => w.BeatmapInfo?.ID == beatmapInfo.ID); From b16d10bd95af2d67c37adeeacfe6ca527abe52bf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 31 May 2021 18:37:32 +0900 Subject: [PATCH 584/839] Provide game-wide resources via `IStorageResourceProvider` --- .../TestSceneManiaHitObjectSamples.cs | 2 +- .../TestSceneTaikoHitObjectSamples.cs | 2 +- osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs | 2 +- osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs | 1 + .../TestSceneOnlinePlayBeatmapAvailabilityTracker.cs | 7 ++++--- .../Visual/Background/TestSceneUserDimBackgrounds.cs | 2 +- .../Collections/TestSceneManageCollectionsDialog.cs | 2 +- .../Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs | 2 +- osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs | 2 +- .../Multiplayer/TestSceneMultiplayerMatchSongSelect.cs | 2 +- .../Multiplayer/TestSceneMultiplayerMatchSubScreen.cs | 2 +- .../Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs | 2 +- .../Multiplayer/TestSceneMultiplayerSpectateButton.cs | 2 +- .../Visual/Multiplayer/TestScenePlaylistsSongSelect.cs | 2 +- .../Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs | 2 +- .../Visual/SongSelect/TestSceneFilterControl.cs | 2 +- .../Visual/SongSelect/TestScenePlaySongSelect.cs | 2 +- .../Visual/UserInterface/TestSceneDeleteLocalScore.cs | 2 +- osu.Game/Beatmaps/BeatmapManager.cs | 5 ++++- osu.Game/IO/IStorageResourceProvider.cs | 5 +++++ osu.Game/OsuGameBase.cs | 4 ++-- osu.Game/Skinning/SkinManager.cs | 8 ++++++-- osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs | 4 +++- osu.Game/Tests/Visual/OsuTestScene.cs | 5 +++++ osu.Game/Tests/Visual/SkinnableTestScene.cs | 1 + 25 files changed, 47 insertions(+), 25 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectSamples.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectSamples.cs index 0d726e1a50..eedabd1adb 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectSamples.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectSamples.cs @@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Mania.Tests public class TestSceneManiaHitObjectSamples : HitObjectSampleTest { protected override Ruleset CreatePlayerRuleset() => new ManiaRuleset(); - protected override IResourceStore Resources => new DllResourceStore(Assembly.GetAssembly(typeof(TestSceneManiaHitObjectSamples))); + public override IResourceStore Resources => new DllResourceStore(Assembly.GetAssembly(typeof(TestSceneManiaHitObjectSamples))); /// /// Tests that when a normal sample bank is used, the normal hitsound will be looked up. diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectSamples.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectSamples.cs index 7089ea6619..1258784a14 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectSamples.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectSamples.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Taiko.Tests { protected override Ruleset CreatePlayerRuleset() => new TaikoRuleset(); - protected override IResourceStore Resources => new DllResourceStore(Assembly.GetAssembly(typeof(TestSceneTaikoHitObjectSamples))); + public override IResourceStore Resources => new DllResourceStore(Assembly.GetAssembly(typeof(TestSceneTaikoHitObjectSamples))); [TestCase("taiko-normal-hitnormal")] [TestCase("normal-hitnormal")] diff --git a/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs b/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs index 64eaafbe75..afd4365c45 100644 --- a/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs +++ b/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs @@ -14,7 +14,7 @@ namespace osu.Game.Tests.Gameplay public class TestSceneHitObjectSamples : HitObjectSampleTest { protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); - protected override IResourceStore Resources => TestResources.GetStore(); + public override IResourceStore Resources => TestResources.GetStore(); /// /// Tests that a hitobject which provides no custom sample set retrieves samples from the user skin. diff --git a/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs b/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs index bbab9ae94d..aed28f5f84 100644 --- a/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs +++ b/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs @@ -219,6 +219,7 @@ namespace osu.Game.Tests.Gameplay public AudioManager AudioManager => Audio; public IResourceStore Files => null; + public new IResourceStore Resources => base.Resources; public IResourceStore CreateTextureLoaderStore(IResourceStore underlyingStore) => null; #endregion diff --git a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs index 8dab570e30..42848ffc0c 100644 --- a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs +++ b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs @@ -12,6 +12,7 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Bindables; using osu.Framework.Extensions; +using osu.Framework.IO.Stores; using osu.Framework.Platform; using osu.Framework.Testing; using osu.Game.Beatmaps; @@ -44,7 +45,7 @@ namespace osu.Game.Tests.Online private void load(AudioManager audio, GameHost host) { Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); - Dependencies.CacheAs(beatmaps = new TestBeatmapManager(LocalStorage, ContextFactory, rulesets, API, audio, host, Beatmap.Default)); + Dependencies.CacheAs(beatmaps = new TestBeatmapManager(LocalStorage, ContextFactory, rulesets, API, audio, Resources, host, Beatmap.Default)); } [SetUp] @@ -160,8 +161,8 @@ namespace osu.Game.Tests.Online protected override ArchiveDownloadRequest CreateDownloadRequest(BeatmapSetInfo set, bool minimiseDownloadSize) => new TestDownloadRequest(set); - public TestBeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, GameHost host = null, WorkingBeatmap defaultBeatmap = null, bool performOnlineLookups = false) - : base(storage, contextFactory, rulesets, api, audioManager, host, defaultBeatmap, performOnlineLookups) + public TestBeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, IResourceStore resources, GameHost host = null, WorkingBeatmap defaultBeatmap = null, bool performOnlineLookups = false) + : base(storage, contextFactory, rulesets, api, audioManager, resources, host, defaultBeatmap, performOnlineLookups) { } diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs index f89988cd1a..1670d86545 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs @@ -48,7 +48,7 @@ namespace osu.Game.Tests.Visual.Background private void load(GameHost host, AudioManager audio) { Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); - Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default)); + Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default)); Dependencies.Cache(new OsuConfigManager(LocalStorage)); manager.Import(TestResources.GetQuickTestBeatmapForImport()).Wait(); diff --git a/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs b/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs index eca857f9e5..28218ea220 100644 --- a/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs +++ b/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs @@ -36,7 +36,7 @@ namespace osu.Game.Tests.Visual.Collections private void load(GameHost host) { Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); - Dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, Audio, host, Beatmap.Default)); + Dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, Audio, Resources, host, Beatmap.Default)); beatmapManager.Import(TestResources.GetQuickTestBeatmapForImport()).Wait(); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs index 960aad10c6..dfb78a235b 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs @@ -37,7 +37,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private void load(GameHost host, AudioManager audio) { Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); - Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default)); + Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default)); manager.Import(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapSet).Wait(); } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 424efb255b..c5a6723508 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -43,7 +43,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private void load(GameHost host, AudioManager audio) { Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); - Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default)); + Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default)); } [SetUp] diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs index faa5d9e6fc..5b059c06f5 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs @@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private void load(GameHost host, AudioManager audio) { Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); - Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default)); + Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default)); beatmaps = new List(); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs index f611d5fecf..e8ebc0c426 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs @@ -40,7 +40,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private void load(GameHost host, AudioManager audio) { Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); - Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default)); + Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default)); beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).Wait(); importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First(); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs index dfb4306e67..929cd6ca80 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs @@ -41,7 +41,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private void load(GameHost host, AudioManager audio) { Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); - Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default)); + Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default)); beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).Wait(); Add(beatmapTracker = new OnlinePlayBeatmapAvailabilityTracker diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs index e59b342176..d00404102c 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs @@ -59,7 +59,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private void load(GameHost host, AudioManager audio) { Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); - Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default)); + Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default)); var beatmapTracker = new OnlinePlayBeatmapAvailabilityTracker { SelectedItem = { BindTarget = selectedItem } }; base.Content.Add(beatmapTracker); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs index 7d83ba569d..d95a95ebe5 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs @@ -38,7 +38,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private void load(GameHost host, AudioManager audio) { Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); - Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default)); + Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default)); var beatmaps = new List(); diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs index 264004b6c3..a08a91314b 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs @@ -38,7 +38,7 @@ namespace osu.Game.Tests.Visual.Playlists private void load(GameHost host, AudioManager audio) { Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); - Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default)); + Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default)); manager.Import(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapSet).Wait(); diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneFilterControl.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneFilterControl.cs index c13bdf0955..a5b90e6655 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneFilterControl.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneFilterControl.cs @@ -36,7 +36,7 @@ namespace osu.Game.Tests.Visual.SongSelect private void load(GameHost host) { Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); - Dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, Audio, host, Beatmap.Default)); + Dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, Audio, Resources, host, Beatmap.Default)); beatmapManager.Import(TestResources.GetQuickTestBeatmapForImport()).Wait(); diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 2eb6d3f80e..102e5ee425 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -47,7 +47,7 @@ namespace osu.Game.Tests.Visual.SongSelect private void load(GameHost host, AudioManager audio) { Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); - Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, defaultBeatmap = Beatmap.Default)); + Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, defaultBeatmap = Beatmap.Default)); Dependencies.Cache(music = new MusicController()); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs index 97f3b2954d..3f9e0048dd 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs @@ -81,7 +81,7 @@ namespace osu.Game.Tests.Visual.UserInterface var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); dependencies.Cache(rulesetStore = new RulesetStore(ContextFactory)); - dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesetStore, null, dependencies.Get(), dependencies.Get(), Beatmap.Default)); + dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesetStore, null, dependencies.Get(), Resources, dependencies.Get(), Beatmap.Default)); dependencies.Cache(scoreManager = new ScoreManager(rulesetStore, () => beatmapManager, LocalStorage, null, ContextFactory)); beatmap = beatmapManager.Import(new ImportTask(TestResources.GetQuickTestBeatmapForImport())).Result.Beatmaps[0]; diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index e7f6bb3c3a..829327d37b 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -73,6 +73,7 @@ namespace osu.Game.Beatmaps private readonly RulesetStore rulesets; private readonly BeatmapStore beatmaps; private readonly AudioManager audioManager; + private readonly IResourceStore resources; private readonly LargeTextureStore largeTextureStore; private readonly ITrackStore trackStore; @@ -82,12 +83,13 @@ namespace osu.Game.Beatmaps [CanBeNull] private readonly BeatmapOnlineLookupQueue onlineLookupQueue; - public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, GameHost host = null, + public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, IResourceStore resources, GameHost host = null, WorkingBeatmap defaultBeatmap = null, bool performOnlineLookups = false) : base(storage, contextFactory, api, new BeatmapStore(contextFactory), host) { this.rulesets = rulesets; this.audioManager = audioManager; + this.resources = resources; this.host = host; DefaultBeatmap = defaultBeatmap; @@ -511,6 +513,7 @@ namespace osu.Game.Beatmaps ITrackStore IBeatmapResourceProvider.Tracks => trackStore; AudioManager IStorageResourceProvider.AudioManager => audioManager; IResourceStore IStorageResourceProvider.Files => Files.Store; + IResourceStore IStorageResourceProvider.Resources => resources; IResourceStore IStorageResourceProvider.CreateTextureLoaderStore(IResourceStore underlyingStore) => host?.CreateTextureLoaderStore(underlyingStore); #endregion diff --git a/osu.Game/IO/IStorageResourceProvider.cs b/osu.Game/IO/IStorageResourceProvider.cs index cbd1039807..e4c97e18fa 100644 --- a/osu.Game/IO/IStorageResourceProvider.cs +++ b/osu.Game/IO/IStorageResourceProvider.cs @@ -19,6 +19,11 @@ namespace osu.Game.IO /// IResourceStore Files { get; } + /// + /// Access game-wide resources. + /// + IResourceStore Resources { get; } + /// /// Create a texture loader store based on an underlying data store. /// diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 918f231a19..7935815f38 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -209,7 +209,7 @@ namespace osu.Game runMigrations(); - dependencies.Cache(SkinManager = new SkinManager(Storage, contextFactory, Host, Audio, new NamespacedResourceStore(Resources, "Skins/Legacy"))); + dependencies.Cache(SkinManager = new SkinManager(Storage, contextFactory, Host, Resources, Audio)); dependencies.CacheAs(SkinManager); // needs to be done here rather than inside SkinManager to ensure thread safety of CurrentSkinInfo. @@ -242,7 +242,7 @@ namespace osu.Game // ordering is important here to ensure foreign keys rules are not broken in ModelStore.Cleanup() dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, () => BeatmapManager, Storage, API, contextFactory, Host, () => difficultyCache, LocalConfig)); - dependencies.Cache(BeatmapManager = new BeatmapManager(Storage, contextFactory, RulesetStore, API, Audio, Host, defaultBeatmap, true)); + dependencies.Cache(BeatmapManager = new BeatmapManager(Storage, contextFactory, RulesetStore, API, Audio, Resources, Host, defaultBeatmap, true)); // this should likely be moved to ArchiveModelManager when another case appers where it is necessary // to have inter-dependent model managers. this could be obtained with an IHasForeign interface to diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 5793edda30..034b920c1a 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -37,6 +37,8 @@ namespace osu.Game.Skinning private readonly GameHost host; + private readonly IResourceStore resources; + private readonly IResourceStore legacyDefaultResources; public readonly Bindable CurrentSkin = new Bindable(new DefaultSkin(null)); @@ -48,13 +50,14 @@ namespace osu.Game.Skinning protected override string ImportFromStablePath => "Skins"; - public SkinManager(Storage storage, DatabaseContextFactory contextFactory, GameHost host, AudioManager audio, IResourceStore legacyDefaultResources) + public SkinManager(Storage storage, DatabaseContextFactory contextFactory, GameHost host, IResourceStore resources, AudioManager audio) : base(storage, contextFactory, new SkinStore(contextFactory, storage), host) { this.audio = audio; this.host = host; + this.resources = resources; - this.legacyDefaultResources = legacyDefaultResources; + legacyDefaultResources = new NamespacedResourceStore(resources, "Skins/Legacy"); CurrentSkinInfo.ValueChanged += skin => CurrentSkin.Value = GetSkin(skin.NewValue); CurrentSkin.ValueChanged += skin => @@ -216,6 +219,7 @@ namespace osu.Game.Skinning #region IResourceStorageProvider AudioManager IStorageResourceProvider.AudioManager => audio; + IResourceStore IStorageResourceProvider.Resources => resources; IResourceStore IStorageResourceProvider.Files => Files.Store; IResourceStore IStorageResourceProvider.CreateTextureLoaderStore(IResourceStore underlyingStore) => host.CreateTextureLoaderStore(underlyingStore); diff --git a/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs b/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs index 62814d4ed4..038ab334e8 100644 --- a/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs +++ b/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs @@ -28,7 +28,8 @@ namespace osu.Game.Tests.Beatmaps [HeadlessTest] public abstract class HitObjectSampleTest : PlayerTestScene, IStorageResourceProvider { - protected abstract IResourceStore Resources { get; } + protected abstract IResourceStore RulesetResources { get; } + protected LegacySkin Skin { get; private set; } [Resolved] @@ -127,6 +128,7 @@ namespace osu.Game.Tests.Beatmaps public AudioManager AudioManager => Audio; public IResourceStore Files => userSkinResourceStore; + public new IResourceStore Resources => base.Resources; public IResourceStore CreateTextureLoaderStore(IResourceStore underlyingStore) => null; #endregion diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index 198d22fedd..be9a015ab2 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -14,6 +14,7 @@ using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.IO.Stores; using osu.Framework.Platform; using osu.Framework.Testing; using osu.Framework.Timing; @@ -49,6 +50,8 @@ namespace osu.Game.Tests.Visual private Lazy contextFactory; + protected IResourceStore Resources; + protected IAPIProvider API { get @@ -81,6 +84,8 @@ namespace osu.Game.Tests.Visual if (!UseFreshStoragePerRun) isolatedHostStorage = (parent.Get() as HeadlessGameHost)?.Storage; + Resources = parent.Get().Resources; + contextFactory = new Lazy(() => { var factory = new DatabaseContextFactory(LocalStorage); diff --git a/osu.Game/Tests/Visual/SkinnableTestScene.cs b/osu.Game/Tests/Visual/SkinnableTestScene.cs index 3d2c68c2ad..729c12187f 100644 --- a/osu.Game/Tests/Visual/SkinnableTestScene.cs +++ b/osu.Game/Tests/Visual/SkinnableTestScene.cs @@ -156,6 +156,7 @@ namespace osu.Game.Tests.Visual public AudioManager AudioManager => Audio; public IResourceStore Files => null; + public new IResourceStore Resources => base.Resources; public IResourceStore CreateTextureLoaderStore(IResourceStore underlyingStore) => host.CreateTextureLoaderStore(underlyingStore); #endregion From 65709ec7d7853657128db2613dda60ad5c80dd05 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 31 May 2021 18:58:40 +0900 Subject: [PATCH 585/839] Move leagcy resource store construction local to `DefaultLegacySkin` --- osu.Game/Skinning/DefaultLegacySkin.cs | 8 ++++---- osu.Game/Skinning/SkinInfo.cs | 6 +----- osu.Game/Skinning/SkinManager.cs | 6 +----- osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs | 5 ++--- osu.Game/Tests/Visual/SkinnableTestScene.cs | 4 ++-- 5 files changed, 10 insertions(+), 19 deletions(-) diff --git a/osu.Game/Skinning/DefaultLegacySkin.cs b/osu.Game/Skinning/DefaultLegacySkin.cs index f30130b1fb..30192182f3 100644 --- a/osu.Game/Skinning/DefaultLegacySkin.cs +++ b/osu.Game/Skinning/DefaultLegacySkin.cs @@ -11,14 +11,14 @@ namespace osu.Game.Skinning { public class DefaultLegacySkin : LegacySkin { - public DefaultLegacySkin(IResourceStore storage, IStorageResourceProvider resources) - : this(Info, storage, resources) + public DefaultLegacySkin(IStorageResourceProvider resources) + : this(Info, resources) { } [UsedImplicitly(ImplicitUseKindFlags.InstantiatedWithFixedConstructorSignature)] - public DefaultLegacySkin(SkinInfo skin, IResourceStore storage, IStorageResourceProvider resources) - : base(skin, storage, resources, string.Empty) + public DefaultLegacySkin(SkinInfo skin, IStorageResourceProvider resources) + : base(skin, new NamespacedResourceStore(resources.Resources, "Skins/Legacy"), resources, string.Empty) { Configuration.CustomColours["SliderBall"] = new Color4(2, 170, 255, 255); Configuration.AddComboColours( diff --git a/osu.Game/Skinning/SkinInfo.cs b/osu.Game/Skinning/SkinInfo.cs index 55760876e3..e30bc16d8b 100644 --- a/osu.Game/Skinning/SkinInfo.cs +++ b/osu.Game/Skinning/SkinInfo.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using osu.Framework.Extensions.ObjectExtensions; -using osu.Framework.IO.Stores; using osu.Game.Configuration; using osu.Game.Database; using osu.Game.Extensions; @@ -28,16 +27,13 @@ namespace osu.Game.Skinning public string InstantiationInfo { get; set; } - public virtual Skin CreateInstance(IResourceStore legacyDefaultResources, IStorageResourceProvider resources) + public virtual Skin CreateInstance(IStorageResourceProvider resources) { var type = string.IsNullOrEmpty(InstantiationInfo) // handle the case of skins imported before InstantiationInfo was added. ? typeof(LegacySkin) : Type.GetType(InstantiationInfo).AsNonNull(); - if (type == typeof(DefaultLegacySkin)) - return (Skin)Activator.CreateInstance(type, this, legacyDefaultResources, resources); - return (Skin)Activator.CreateInstance(type, this, resources); } diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 034b920c1a..079c537066 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -39,8 +39,6 @@ namespace osu.Game.Skinning private readonly IResourceStore resources; - private readonly IResourceStore legacyDefaultResources; - public readonly Bindable CurrentSkin = new Bindable(new DefaultSkin(null)); public readonly Bindable CurrentSkinInfo = new Bindable(SkinInfo.Default) { Default = SkinInfo.Default }; @@ -57,8 +55,6 @@ namespace osu.Game.Skinning this.host = host; this.resources = resources; - legacyDefaultResources = new NamespacedResourceStore(resources, "Skins/Legacy"); - CurrentSkinInfo.ValueChanged += skin => CurrentSkin.Value = GetSkin(skin.NewValue); CurrentSkin.ValueChanged += skin => { @@ -155,7 +151,7 @@ namespace osu.Game.Skinning /// /// The skin to lookup. /// A instance correlating to the provided . - public Skin GetSkin(SkinInfo skinInfo) => skinInfo.CreateInstance(legacyDefaultResources, this); + public Skin GetSkin(SkinInfo skinInfo) => skinInfo.CreateInstance(this); /// /// Ensure that the current skin is in a state it can accept user modifications. diff --git a/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs b/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs index c186525757..e3d7a21ab0 100644 --- a/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs +++ b/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs @@ -3,7 +3,6 @@ using NUnit.Framework; using osu.Framework.Allocation; -using osu.Framework.IO.Stores; using osu.Game.Rulesets; using osu.Game.Skinning; @@ -17,9 +16,9 @@ namespace osu.Game.Tests.Visual protected override TestPlayer CreatePlayer(Ruleset ruleset) => new SkinProvidingPlayer(legacySkinSource); [BackgroundDependencyLoader] - private void load(OsuGameBase game, SkinManager skins) + private void load(SkinManager skins) { - var legacySkin = new DefaultLegacySkin(new NamespacedResourceStore(game.Resources, "Skins/Legacy"), skins); + var legacySkin = new DefaultLegacySkin(skins); legacySkinSource = new SkinProvidingContainer(legacySkin); } diff --git a/osu.Game/Tests/Visual/SkinnableTestScene.cs b/osu.Game/Tests/Visual/SkinnableTestScene.cs index 729c12187f..e801412fc5 100644 --- a/osu.Game/Tests/Visual/SkinnableTestScene.cs +++ b/osu.Game/Tests/Visual/SkinnableTestScene.cs @@ -40,12 +40,12 @@ namespace osu.Game.Tests.Visual } [BackgroundDependencyLoader] - private void load(AudioManager audio, SkinManager skinManager, OsuGameBase game) + private void load(AudioManager audio, SkinManager skinManager) { var dllStore = new DllResourceStore(DynamicCompilationOriginal.GetType().Assembly); metricsSkin = new TestLegacySkin(new SkinInfo { Name = "metrics-skin" }, new NamespacedResourceStore(dllStore, "Resources/metrics_skin"), this, true); - defaultSkin = new DefaultLegacySkin(new NamespacedResourceStore(game.Resources, "Skins/Legacy"), this); + defaultSkin = new DefaultLegacySkin(this); specialSkin = new TestLegacySkin(new SkinInfo { Name = "special-skin" }, new NamespacedResourceStore(dllStore, "Resources/special_skin"), this, true); oldSkin = new TestLegacySkin(new SkinInfo { Name = "old-skin" }, new NamespacedResourceStore(dllStore, "Resources/old_skin"), this, true); } From ebfc24a499eb56e17b8ee12f008b7b4526931dad Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 31 May 2021 18:55:47 +0900 Subject: [PATCH 586/839] Rename conflicting resources --- .../TestSceneManiaHitObjectSamples.cs | 2 +- .../TestSceneTaikoHitObjectSamples.cs | 2 +- osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs | 2 +- osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs | 3 +-- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectSamples.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectSamples.cs index eedabd1adb..ea57e51d1c 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectSamples.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectSamples.cs @@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Mania.Tests public class TestSceneManiaHitObjectSamples : HitObjectSampleTest { protected override Ruleset CreatePlayerRuleset() => new ManiaRuleset(); - public override IResourceStore Resources => new DllResourceStore(Assembly.GetAssembly(typeof(TestSceneManiaHitObjectSamples))); + protected override IResourceStore RulesetResources => new DllResourceStore(Assembly.GetAssembly(typeof(TestSceneManiaHitObjectSamples))); /// /// Tests that when a normal sample bank is used, the normal hitsound will be looked up. diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectSamples.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectSamples.cs index 1258784a14..221d715a35 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectSamples.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectSamples.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Taiko.Tests { protected override Ruleset CreatePlayerRuleset() => new TaikoRuleset(); - public override IResourceStore Resources => new DllResourceStore(Assembly.GetAssembly(typeof(TestSceneTaikoHitObjectSamples))); + protected override IResourceStore RulesetResources => new DllResourceStore(Assembly.GetAssembly(typeof(TestSceneTaikoHitObjectSamples))); [TestCase("taiko-normal-hitnormal")] [TestCase("normal-hitnormal")] diff --git a/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs b/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs index afd4365c45..fc420e22a1 100644 --- a/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs +++ b/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs @@ -14,7 +14,7 @@ namespace osu.Game.Tests.Gameplay public class TestSceneHitObjectSamples : HitObjectSampleTest { protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); - public override IResourceStore Resources => TestResources.GetStore(); + protected override IResourceStore RulesetResources => TestResources.GetStore(); /// /// Tests that a hitobject which provides no custom sample set retrieves samples from the user skin. diff --git a/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs b/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs index 038ab334e8..7ee6c519b7 100644 --- a/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs +++ b/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs @@ -29,7 +29,6 @@ namespace osu.Game.Tests.Beatmaps public abstract class HitObjectSampleTest : PlayerTestScene, IStorageResourceProvider { protected abstract IResourceStore RulesetResources { get; } - protected LegacySkin Skin { get; private set; } [Resolved] @@ -76,7 +75,7 @@ namespace osu.Game.Tests.Beatmaps AddStep($"load {filename}", () => { - using (var reader = new LineBufferedReader(Resources.GetStream($"Resources/SampleLookups/{filename}"))) + using (var reader = new LineBufferedReader(RulesetResources.GetStream($"Resources/SampleLookups/{filename}"))) currentTestBeatmap = Decoder.GetDecoder(reader).Decode(reader); // populate ruleset for beatmap converters that require it to be present. From d2d089629881770193a5d2c42f7b4c915e0310d4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 31 May 2021 19:22:20 +0900 Subject: [PATCH 587/839] Expose GameplayBeatmap and GameplayRuleset from Player --- osu.Game/Screens/Play/Player.cs | 38 +++++++++++------------- osu.Game/Screens/Play/SpectatorPlayer.cs | 16 +++++----- 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index a9f3edf049..2258d509b7 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -84,10 +84,6 @@ namespace osu.Game.Screens.Play [Resolved] private ScoreManager scoreManager { get; set; } - private RulesetInfo rulesetInfo; - - private Ruleset ruleset; - [Resolved] private IAPIProvider api { get; set; } @@ -97,6 +93,10 @@ namespace osu.Game.Screens.Play [Resolved] private SpectatorClient spectatorClient { get; set; } + protected Ruleset GameplayRuleset { get; private set; } + + protected GameplayBeatmap GameplayBeatmap { get; private set; } + private Sample sampleRestart; public BreakOverlay BreakOverlay; @@ -145,8 +145,6 @@ namespace osu.Game.Screens.Play Configuration = configuration ?? new PlayerConfiguration(); } - private GameplayBeatmap gameplayBeatmap; - private ScreenSuspensionHandler screenSuspension; private DependencyContainer dependencies; @@ -204,16 +202,16 @@ namespace osu.Game.Screens.Play if (game is OsuGame osuGame) LocalUserPlaying.BindTo(osuGame.LocalUserPlaying); - DrawableRuleset = ruleset.CreateDrawableRulesetWith(playableBeatmap, Mods.Value); + DrawableRuleset = GameplayRuleset.CreateDrawableRulesetWith(playableBeatmap, Mods.Value); dependencies.CacheAs(DrawableRuleset); - ScoreProcessor = ruleset.CreateScoreProcessor(); + ScoreProcessor = GameplayRuleset.CreateScoreProcessor(); ScoreProcessor.ApplyBeatmap(playableBeatmap); ScoreProcessor.Mods.BindTo(Mods); dependencies.CacheAs(ScoreProcessor); - HealthProcessor = ruleset.CreateHealthProcessor(playableBeatmap.HitObjects[0].StartTime); + HealthProcessor = GameplayRuleset.CreateHealthProcessor(playableBeatmap.HitObjects[0].StartTime); HealthProcessor.ApplyBeatmap(playableBeatmap); dependencies.CacheAs(HealthProcessor); @@ -223,16 +221,16 @@ namespace osu.Game.Screens.Play InternalChild = GameplayClockContainer = CreateGameplayClockContainer(Beatmap.Value, DrawableRuleset.GameplayStartTime); - AddInternal(gameplayBeatmap = new GameplayBeatmap(playableBeatmap)); + AddInternal(GameplayBeatmap = new GameplayBeatmap(playableBeatmap)); AddInternal(screenSuspension = new ScreenSuspensionHandler(GameplayClockContainer)); - dependencies.CacheAs(gameplayBeatmap); + dependencies.CacheAs(GameplayBeatmap); var beatmapSkinProvider = new BeatmapSkinProvidingContainer(Beatmap.Value.Skin); // the beatmapSkinProvider is used as the fallback source here to allow the ruleset-specific skin implementation // full access to all skin sources. - var rulesetSkinProvider = new SkinProvidingContainer(ruleset.CreateLegacySkinProvider(beatmapSkinProvider, playableBeatmap)); + var rulesetSkinProvider = new SkinProvidingContainer(GameplayRuleset.CreateLegacySkinProvider(beatmapSkinProvider, playableBeatmap)); // load the skinning hierarchy first. // this is intentionally done in two stages to ensure things are in a loaded state before exposing the ruleset to skin sources. @@ -247,7 +245,7 @@ namespace osu.Game.Screens.Play // also give the HUD a ruleset container to allow rulesets to potentially override HUD elements (used to disable combo counters etc.) // we may want to limit this in the future to disallow rulesets from outright replacing elements the user expects to be there. - var hudRulesetContainer = new SkinProvidingContainer(ruleset.CreateLegacySkinProvider(beatmapSkinProvider, playableBeatmap)); + var hudRulesetContainer = new SkinProvidingContainer(GameplayRuleset.CreateLegacySkinProvider(beatmapSkinProvider, playableBeatmap)); // add the overlay components as a separate step as they proxy some elements from the above underlay/gameplay components. GameplayClockContainer.Add(hudRulesetContainer.WithChild(createOverlayComponents(Beatmap.Value))); @@ -284,7 +282,7 @@ namespace osu.Game.Screens.Play { HealthProcessor.ApplyResult(r); ScoreProcessor.ApplyResult(r); - gameplayBeatmap.ApplyResult(r); + GameplayBeatmap.ApplyResult(r); }; DrawableRuleset.RevertResult += r => @@ -473,18 +471,18 @@ namespace osu.Game.Screens.Play if (Beatmap.Value.Beatmap == null) throw new InvalidOperationException("Beatmap was not loaded"); - rulesetInfo = Ruleset.Value ?? Beatmap.Value.BeatmapInfo.Ruleset; - ruleset = rulesetInfo.CreateInstance(); + var rulesetInfo = Ruleset.Value ?? Beatmap.Value.BeatmapInfo.Ruleset; + GameplayRuleset = rulesetInfo.CreateInstance(); try { - playable = Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo, Mods.Value); + playable = Beatmap.Value.GetPlayableBeatmap(GameplayRuleset.RulesetInfo, Mods.Value); } catch (BeatmapInvalidForRulesetException) { // A playable beatmap may not be creatable with the user's preferred ruleset, so try using the beatmap's default ruleset rulesetInfo = Beatmap.Value.BeatmapInfo.Ruleset; - ruleset = rulesetInfo.CreateInstance(); + GameplayRuleset = rulesetInfo.CreateInstance(); playable = Beatmap.Value.GetPlayableBeatmap(rulesetInfo, Mods.Value); } @@ -915,7 +913,7 @@ namespace osu.Game.Screens.Play ScoreInfo = new ScoreInfo { Beatmap = Beatmap.Value.BeatmapInfo, - Ruleset = rulesetInfo, + Ruleset = GameplayRuleset.RulesetInfo, Mods = Mods.Value.ToArray(), } }; @@ -951,7 +949,7 @@ namespace osu.Game.Screens.Play using (var stream = new MemoryStream()) { - new LegacyScoreEncoder(score, gameplayBeatmap.PlayableBeatmap).Encode(stream); + new LegacyScoreEncoder(score, GameplayBeatmap.PlayableBeatmap).Encode(stream); replayReader = new LegacyByteArrayReader(stream.ToArray(), "replay.osr"); } diff --git a/osu.Game/Screens/Play/SpectatorPlayer.cs b/osu.Game/Screens/Play/SpectatorPlayer.cs index a8125dfded..ad0f05e931 100644 --- a/osu.Game/Screens/Play/SpectatorPlayer.cs +++ b/osu.Game/Screens/Play/SpectatorPlayer.cs @@ -16,6 +16,9 @@ namespace osu.Game.Screens.Play { public class SpectatorPlayer : Player { + [Resolved] + private SpectatorClient spectatorClient { get; set; } + private readonly Score score; protected override bool CheckModsAllowFailure() => false; // todo: better support starting mid-way through beatmap @@ -25,14 +28,6 @@ namespace osu.Game.Screens.Play this.score = score; } - protected override ResultsScreen CreateResults(ScoreInfo score) - { - return new SpectatorResultsScreen(score); - } - - [Resolved] - private SpectatorClient spectatorClient { get; set; } - [BackgroundDependencyLoader] private void load() { @@ -48,6 +43,11 @@ namespace osu.Game.Screens.Play }); } + protected override ResultsScreen CreateResults(ScoreInfo score) + { + return new SpectatorResultsScreen(score); + } + protected override void PrepareReplay() { DrawableRuleset?.SetReplayScore(score); From 7f24518004854d6e3e0167ac7a42c0e629fcc801 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 31 May 2021 19:24:22 +0900 Subject: [PATCH 588/839] Fix spectator crashing when converting mania replay frames --- osu.Game/Screens/Play/SpectatorPlayer.cs | 37 ++++++++++++++++++++ osu.Game/Screens/Spectate/SpectatorScreen.cs | 29 --------------- 2 files changed, 37 insertions(+), 29 deletions(-) diff --git a/osu.Game/Screens/Play/SpectatorPlayer.cs b/osu.Game/Screens/Play/SpectatorPlayer.cs index ad0f05e931..c5e26bdef6 100644 --- a/osu.Game/Screens/Play/SpectatorPlayer.cs +++ b/osu.Game/Screens/Play/SpectatorPlayer.cs @@ -9,6 +9,8 @@ using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Online.Spectator; +using osu.Game.Rulesets.Replays; +using osu.Game.Rulesets.Replays.Types; using osu.Game.Scoring; using osu.Game.Screens.Ranking; @@ -43,6 +45,36 @@ namespace osu.Game.Screens.Play }); } + protected override void LoadComplete() + { + base.LoadComplete(); + + spectatorClient.OnNewFrames += userSentFrames; + } + + private void userSentFrames(int userId, FrameDataBundle bundle) + { + if (userId != score.ScoreInfo.User.Id) + return; + + if (!LoadedBeatmapSuccessfully) + return; + + if (!this.IsCurrentScreen()) + return; + + foreach (var frame in bundle.Frames) + { + IConvertibleReplayFrame convertibleFrame = GameplayRuleset.CreateConvertibleReplayFrame(); + convertibleFrame.FromLegacy(frame, GameplayBeatmap.PlayableBeatmap); + + var convertedFrame = (ReplayFrame)convertibleFrame; + convertedFrame.Time = frame.Time; + + score.Replay.Frames.Add(convertedFrame); + } + } + protected override ResultsScreen CreateResults(ScoreInfo score) { return new SpectatorResultsScreen(score); @@ -67,6 +99,8 @@ namespace osu.Game.Screens.Play public override bool OnExiting(IScreen next) { spectatorClient.OnUserBeganPlaying -= userBeganPlaying; + spectatorClient.OnNewFrames -= userSentFrames; + return base.OnExiting(next); } @@ -85,7 +119,10 @@ namespace osu.Game.Screens.Play base.Dispose(isDisposing); if (spectatorClient != null) + { spectatorClient.OnUserBeganPlaying -= userBeganPlaying; + spectatorClient.OnNewFrames -= userSentFrames; + } } } } diff --git a/osu.Game/Screens/Spectate/SpectatorScreen.cs b/osu.Game/Screens/Spectate/SpectatorScreen.cs index 9a20bb58b8..8fc9222f59 100644 --- a/osu.Game/Screens/Spectate/SpectatorScreen.cs +++ b/osu.Game/Screens/Spectate/SpectatorScreen.cs @@ -15,8 +15,6 @@ using osu.Game.Database; using osu.Game.Online.Spectator; using osu.Game.Replays; using osu.Game.Rulesets; -using osu.Game.Rulesets.Replays; -using osu.Game.Rulesets.Replays.Types; using osu.Game.Scoring; using osu.Game.Users; @@ -71,8 +69,6 @@ namespace osu.Game.Screens.Spectate playingUserStates.BindTo(spectatorClient.PlayingUserStates); playingUserStates.BindCollectionChanged(onPlayingUserStatesChanged, true); - spectatorClient.OnNewFrames += userSentFrames; - managerUpdated = beatmaps.ItemUpdated.GetBoundCopy(); managerUpdated.BindValueChanged(beatmapUpdated); @@ -197,29 +193,6 @@ namespace osu.Game.Screens.Spectate Schedule(() => StartGameplay(userId, gameplayState)); } - private void userSentFrames(int userId, FrameDataBundle bundle) - { - if (!userMap.ContainsKey(userId)) - return; - - if (!gameplayStates.TryGetValue(userId, out var gameplayState)) - return; - - // The ruleset instance should be guaranteed to be in sync with the score via ScoreLock. - Debug.Assert(gameplayState.Ruleset != null && gameplayState.Ruleset.RulesetInfo.Equals(gameplayState.Score.ScoreInfo.Ruleset)); - - foreach (var frame in bundle.Frames) - { - IConvertibleReplayFrame convertibleFrame = gameplayState.Ruleset.CreateConvertibleReplayFrame(); - convertibleFrame.FromLegacy(frame, gameplayState.Beatmap.Beatmap); - - var convertedFrame = (ReplayFrame)convertibleFrame; - convertedFrame.Time = frame.Time; - - gameplayState.Score.Replay.Frames.Add(convertedFrame); - } - } - /// /// Invoked when a spectated user's state has changed. /// @@ -260,8 +233,6 @@ namespace osu.Game.Screens.Spectate if (spectatorClient != null) { - spectatorClient.OnNewFrames -= userSentFrames; - foreach (var (userId, _) in userMap) spectatorClient.StopWatchingUser(userId); } From e78391db7a298ec4e0d84516832dfe8c51e9f5a6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 31 May 2021 19:57:31 +0900 Subject: [PATCH 589/839] Fix usage of DI before it's ready in combo colour tests --- .../TestSceneLegacyBeatmapSkin.cs | 14 +++++++------- .../TestSceneLegacyBeatmapSkin.cs | 10 +++++----- .../Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs | 12 ++++++++---- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneLegacyBeatmapSkin.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneLegacyBeatmapSkin.cs index eea83ef7c1..28ff02cb1f 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneLegacyBeatmapSkin.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneLegacyBeatmapSkin.cs @@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Catch.Tests [TestCase(false, false)] public override void TestBeatmapComboColours(bool userHasCustomColours, bool useBeatmapSkin) { - TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, true); + PrepareBeatmap(() => new CatchCustomSkinWorkingBeatmap(audio, true)); base.TestBeatmapComboColours(userHasCustomColours, useBeatmapSkin); AddAssert("is beatmap skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestBeatmapSkin.Colours)); } @@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Catch.Tests [TestCase(false)] public override void TestBeatmapComboColoursOverride(bool useBeatmapSkin) { - TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, true); + PrepareBeatmap(() => new CatchCustomSkinWorkingBeatmap(audio, true)); base.TestBeatmapComboColoursOverride(useBeatmapSkin); AddAssert("is user custom skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestSkin.Colours)); } @@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Catch.Tests [TestCase(false)] public override void TestBeatmapComboColoursOverrideWithDefaultColours(bool useBeatmapSkin) { - TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, true); + PrepareBeatmap(() => new CatchCustomSkinWorkingBeatmap(audio, true)); base.TestBeatmapComboColoursOverrideWithDefaultColours(useBeatmapSkin); AddAssert("is default user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours)); } @@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Catch.Tests [TestCase(false, false)] public override void TestBeatmapNoComboColours(bool useBeatmapSkin, bool useBeatmapColour) { - TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, false); + PrepareBeatmap(() => new CatchCustomSkinWorkingBeatmap(audio, false)); base.TestBeatmapNoComboColours(useBeatmapSkin, useBeatmapColour); AddAssert("is default user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours)); } @@ -74,7 +74,7 @@ namespace osu.Game.Rulesets.Catch.Tests [TestCase(false, false)] public override void TestBeatmapNoComboColoursSkinOverride(bool useBeatmapSkin, bool useBeatmapColour) { - TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, false); + PrepareBeatmap(() => new CatchCustomSkinWorkingBeatmap(audio, false)); base.TestBeatmapNoComboColoursSkinOverride(useBeatmapSkin, useBeatmapColour); AddAssert("is custom user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestSkin.Colours)); } @@ -83,7 +83,7 @@ namespace osu.Game.Rulesets.Catch.Tests [TestCase(false)] public void TestBeatmapHyperDashColours(bool useBeatmapSkin) { - TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, true); + PrepareBeatmap(() => new CatchCustomSkinWorkingBeatmap(audio, true)); ConfigureTest(useBeatmapSkin, true, true); AddAssert("is custom hyper dash colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashColour == TestBeatmapSkin.HYPER_DASH_COLOUR); AddAssert("is custom hyper dash after image colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashAfterImageColour == TestBeatmapSkin.HYPER_DASH_AFTER_IMAGE_COLOUR); @@ -94,7 +94,7 @@ namespace osu.Game.Rulesets.Catch.Tests [TestCase(false)] public void TestBeatmapHyperDashColoursOverride(bool useBeatmapSkin) { - TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, true); + PrepareBeatmap(() => new CatchCustomSkinWorkingBeatmap(audio, true)); ConfigureTest(useBeatmapSkin, false, true); AddAssert("is custom hyper dash colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashColour == TestSkin.HYPER_DASH_COLOUR); AddAssert("is custom hyper dash after image colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashAfterImageColour == TestSkin.HYPER_DASH_AFTER_IMAGE_COLOUR); diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs index c26419b0e8..66f919f91f 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Osu.Tests [TestCase(false, false)] public override void TestBeatmapComboColours(bool userHasCustomColours, bool useBeatmapSkin) { - TestBeatmap = new OsuCustomSkinWorkingBeatmap(audio, true); + PrepareBeatmap(() => new OsuCustomSkinWorkingBeatmap(audio, true)); base.TestBeatmapComboColours(userHasCustomColours, useBeatmapSkin); AddAssert("is beatmap skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestBeatmapSkin.Colours)); } @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Osu.Tests [TestCase(false)] public override void TestBeatmapComboColoursOverride(bool useBeatmapSkin) { - TestBeatmap = new OsuCustomSkinWorkingBeatmap(audio, true); + PrepareBeatmap(() => new OsuCustomSkinWorkingBeatmap(audio, true)); base.TestBeatmapComboColoursOverride(useBeatmapSkin); AddAssert("is user custom skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestSkin.Colours)); } @@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.Tests [TestCase(false)] public override void TestBeatmapComboColoursOverrideWithDefaultColours(bool useBeatmapSkin) { - TestBeatmap = new OsuCustomSkinWorkingBeatmap(audio, true); + PrepareBeatmap(() => new OsuCustomSkinWorkingBeatmap(audio, true)); base.TestBeatmapComboColoursOverrideWithDefaultColours(useBeatmapSkin); AddAssert("is default user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours)); } @@ -61,7 +61,7 @@ namespace osu.Game.Rulesets.Osu.Tests [TestCase(false, false)] public override void TestBeatmapNoComboColours(bool useBeatmapSkin, bool useBeatmapColour) { - TestBeatmap = new OsuCustomSkinWorkingBeatmap(audio, false); + PrepareBeatmap(() => new OsuCustomSkinWorkingBeatmap(audio, false)); base.TestBeatmapNoComboColours(useBeatmapSkin, useBeatmapColour); AddAssert("is default user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours)); } @@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Osu.Tests [TestCase(false, false)] public override void TestBeatmapNoComboColoursSkinOverride(bool useBeatmapSkin, bool useBeatmapColour) { - TestBeatmap = new OsuCustomSkinWorkingBeatmap(audio, false); + PrepareBeatmap(() => new OsuCustomSkinWorkingBeatmap(audio, false)); base.TestBeatmapNoComboColoursSkinOverride(useBeatmapSkin, useBeatmapColour); AddAssert("is custom user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestSkin.Colours)); } diff --git a/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs b/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs index 051ede30b7..708963c760 100644 --- a/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs +++ b/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs @@ -21,8 +21,10 @@ namespace osu.Game.Tests.Beatmaps { protected readonly Bindable BeatmapSkins = new Bindable(); protected readonly Bindable BeatmapColours = new Bindable(); + protected ExposedPlayer TestPlayer; - protected WorkingBeatmap TestBeatmap; + + private WorkingBeatmap testBeatmap; public virtual void TestBeatmapComboColours(bool userHasCustomColours, bool useBeatmapSkin) => ConfigureTest(useBeatmapSkin, true, userHasCustomColours); @@ -34,10 +36,12 @@ namespace osu.Game.Tests.Beatmaps public virtual void TestBeatmapNoComboColoursSkinOverride(bool useBeatmapSkin, bool useBeatmapColour) => ConfigureTest(useBeatmapSkin, useBeatmapColour, true); - protected virtual void ConfigureTest(bool useBeatmapSkin, bool useBeatmapColours, bool userHasCustomColours) + protected void PrepareBeatmap(Func createBeatmap) => AddStep("prepare beatmap", () => testBeatmap = createBeatmap()); + + protected void ConfigureTest(bool useBeatmapSkin, bool useBeatmapColours, bool userHasCustomColours) { configureSettings(useBeatmapSkin, useBeatmapColours); - AddStep($"load {(((CustomSkinWorkingBeatmap)TestBeatmap).HasColours ? "coloured " : "")} beatmap", () => TestPlayer = LoadBeatmap(userHasCustomColours)); + AddStep("load beatmap", () => TestPlayer = LoadBeatmap(userHasCustomColours)); AddUntilStep("wait for player load", () => TestPlayer.IsLoaded); } @@ -57,7 +61,7 @@ namespace osu.Game.Tests.Beatmaps { ExposedPlayer player; - Beatmap.Value = TestBeatmap; + Beatmap.Value = testBeatmap; LoadScreen(player = CreateTestPlayer(userHasCustomColours)); From f60e9cb08588760c1355bc9b41a1fc012e308080 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 31 May 2021 20:00:47 +0900 Subject: [PATCH 590/839] Remove weird override logic in `TestCase` methods --- .../TestSceneLegacyBeatmapSkin.cs | 20 +++++++++---------- .../TestSceneLegacyBeatmapSkin.cs | 20 +++++++++---------- .../Beatmaps/LegacyBeatmapSkinColourTest.cs | 10 ---------- 3 files changed, 20 insertions(+), 30 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneLegacyBeatmapSkin.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneLegacyBeatmapSkin.cs index 28ff02cb1f..bc3daca16f 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneLegacyBeatmapSkin.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneLegacyBeatmapSkin.cs @@ -32,28 +32,28 @@ namespace osu.Game.Rulesets.Catch.Tests [TestCase(true, false)] [TestCase(false, true)] [TestCase(false, false)] - public override void TestBeatmapComboColours(bool userHasCustomColours, bool useBeatmapSkin) + public void TestBeatmapComboColours(bool userHasCustomColours, bool useBeatmapSkin) { PrepareBeatmap(() => new CatchCustomSkinWorkingBeatmap(audio, true)); - base.TestBeatmapComboColours(userHasCustomColours, useBeatmapSkin); + ConfigureTest(useBeatmapSkin, true, userHasCustomColours); AddAssert("is beatmap skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestBeatmapSkin.Colours)); } [TestCase(true)] [TestCase(false)] - public override void TestBeatmapComboColoursOverride(bool useBeatmapSkin) + public void TestBeatmapComboColoursOverride(bool useBeatmapSkin) { PrepareBeatmap(() => new CatchCustomSkinWorkingBeatmap(audio, true)); - base.TestBeatmapComboColoursOverride(useBeatmapSkin); + ConfigureTest(useBeatmapSkin, false, true); AddAssert("is user custom skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestSkin.Colours)); } [TestCase(true)] [TestCase(false)] - public override void TestBeatmapComboColoursOverrideWithDefaultColours(bool useBeatmapSkin) + public void TestBeatmapComboColoursOverrideWithDefaultColours(bool useBeatmapSkin) { PrepareBeatmap(() => new CatchCustomSkinWorkingBeatmap(audio, true)); - base.TestBeatmapComboColoursOverrideWithDefaultColours(useBeatmapSkin); + ConfigureTest(useBeatmapSkin, false, false); AddAssert("is default user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours)); } @@ -61,10 +61,10 @@ namespace osu.Game.Rulesets.Catch.Tests [TestCase(false, true)] [TestCase(true, false)] [TestCase(false, false)] - public override void TestBeatmapNoComboColours(bool useBeatmapSkin, bool useBeatmapColour) + public void TestBeatmapNoComboColours(bool useBeatmapSkin, bool useBeatmapColour) { PrepareBeatmap(() => new CatchCustomSkinWorkingBeatmap(audio, false)); - base.TestBeatmapNoComboColours(useBeatmapSkin, useBeatmapColour); + ConfigureTest(useBeatmapSkin, useBeatmapColour, false); AddAssert("is default user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours)); } @@ -72,10 +72,10 @@ namespace osu.Game.Rulesets.Catch.Tests [TestCase(false, true)] [TestCase(true, false)] [TestCase(false, false)] - public override void TestBeatmapNoComboColoursSkinOverride(bool useBeatmapSkin, bool useBeatmapColour) + public void TestBeatmapNoComboColoursSkinOverride(bool useBeatmapSkin, bool useBeatmapColour) { PrepareBeatmap(() => new CatchCustomSkinWorkingBeatmap(audio, false)); - base.TestBeatmapNoComboColoursSkinOverride(useBeatmapSkin, useBeatmapColour); + ConfigureTest(useBeatmapSkin, useBeatmapColour, true); AddAssert("is custom user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestSkin.Colours)); } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs index 66f919f91f..56307861f1 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs @@ -30,28 +30,28 @@ namespace osu.Game.Rulesets.Osu.Tests [TestCase(true, false)] [TestCase(false, true)] [TestCase(false, false)] - public override void TestBeatmapComboColours(bool userHasCustomColours, bool useBeatmapSkin) + public void TestBeatmapComboColours(bool userHasCustomColours, bool useBeatmapSkin) { PrepareBeatmap(() => new OsuCustomSkinWorkingBeatmap(audio, true)); - base.TestBeatmapComboColours(userHasCustomColours, useBeatmapSkin); + ConfigureTest(useBeatmapSkin, true, userHasCustomColours); AddAssert("is beatmap skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestBeatmapSkin.Colours)); } [TestCase(true)] [TestCase(false)] - public override void TestBeatmapComboColoursOverride(bool useBeatmapSkin) + public void TestBeatmapComboColoursOverride(bool useBeatmapSkin) { PrepareBeatmap(() => new OsuCustomSkinWorkingBeatmap(audio, true)); - base.TestBeatmapComboColoursOverride(useBeatmapSkin); + ConfigureTest(useBeatmapSkin, false, true); AddAssert("is user custom skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestSkin.Colours)); } [TestCase(true)] [TestCase(false)] - public override void TestBeatmapComboColoursOverrideWithDefaultColours(bool useBeatmapSkin) + public void TestBeatmapComboColoursOverrideWithDefaultColours(bool useBeatmapSkin) { PrepareBeatmap(() => new OsuCustomSkinWorkingBeatmap(audio, true)); - base.TestBeatmapComboColoursOverrideWithDefaultColours(useBeatmapSkin); + ConfigureTest(useBeatmapSkin, false, false); AddAssert("is default user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours)); } @@ -59,10 +59,10 @@ namespace osu.Game.Rulesets.Osu.Tests [TestCase(false, true)] [TestCase(true, false)] [TestCase(false, false)] - public override void TestBeatmapNoComboColours(bool useBeatmapSkin, bool useBeatmapColour) + public void TestBeatmapNoComboColours(bool useBeatmapSkin, bool useBeatmapColour) { PrepareBeatmap(() => new OsuCustomSkinWorkingBeatmap(audio, false)); - base.TestBeatmapNoComboColours(useBeatmapSkin, useBeatmapColour); + ConfigureTest(useBeatmapSkin, useBeatmapColour, false); AddAssert("is default user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours)); } @@ -70,10 +70,10 @@ namespace osu.Game.Rulesets.Osu.Tests [TestCase(false, true)] [TestCase(true, false)] [TestCase(false, false)] - public override void TestBeatmapNoComboColoursSkinOverride(bool useBeatmapSkin, bool useBeatmapColour) + public void TestBeatmapNoComboColoursSkinOverride(bool useBeatmapSkin, bool useBeatmapColour) { PrepareBeatmap(() => new OsuCustomSkinWorkingBeatmap(audio, false)); - base.TestBeatmapNoComboColoursSkinOverride(useBeatmapSkin, useBeatmapColour); + ConfigureTest(useBeatmapSkin, useBeatmapColour, true); AddAssert("is custom user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestSkin.Colours)); } diff --git a/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs b/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs index 708963c760..0a7fb1483d 100644 --- a/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs +++ b/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs @@ -26,16 +26,6 @@ namespace osu.Game.Tests.Beatmaps private WorkingBeatmap testBeatmap; - public virtual void TestBeatmapComboColours(bool userHasCustomColours, bool useBeatmapSkin) => ConfigureTest(useBeatmapSkin, true, userHasCustomColours); - - public virtual void TestBeatmapComboColoursOverride(bool useBeatmapSkin) => ConfigureTest(useBeatmapSkin, false, true); - - public virtual void TestBeatmapComboColoursOverrideWithDefaultColours(bool useBeatmapSkin) => ConfigureTest(useBeatmapSkin, false, false); - - public virtual void TestBeatmapNoComboColours(bool useBeatmapSkin, bool useBeatmapColour) => ConfigureTest(useBeatmapSkin, useBeatmapColour, false); - - public virtual void TestBeatmapNoComboColoursSkinOverride(bool useBeatmapSkin, bool useBeatmapColour) => ConfigureTest(useBeatmapSkin, useBeatmapColour, true); - protected void PrepareBeatmap(Func createBeatmap) => AddStep("prepare beatmap", () => testBeatmap = createBeatmap()); protected void ConfigureTest(bool useBeatmapSkin, bool useBeatmapColours, bool userHasCustomColours) From c787c008a50f3a7d623f0af37f49175ffcf71f04 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 31 May 2021 20:21:26 +0900 Subject: [PATCH 591/839] Fix test potentially not waiting for player to load --- osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs index e9894ff469..9606d8c828 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs @@ -210,7 +210,7 @@ namespace osu.Game.Tests.Visual.Gameplay private double currentFrameStableTime => player.ChildrenOfType().First().FrameStableClock.CurrentTime; - private void waitForPlayer() => AddUntilStep("wait for player", () => Stack.CurrentScreen is Player); + private void waitForPlayer() => AddUntilStep("wait for player", () => (Stack.CurrentScreen as Player)?.IsLoaded == true); private void start(int? beatmapId = null) => AddStep("start play", () => testSpectatorClient.StartPlay(streamingUser.Id, beatmapId ?? importedBeatmapId)); From ff0494229524c7c5d984895f8500c31b8329185e Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 31 May 2021 20:56:25 +0900 Subject: [PATCH 592/839] Change timing of `HitObjectContainer.OnAdd`/`OnRemove` Now, `OnAdd` is always invoked after the DHO is added to the container. This change is required by `ScrollingHitObjectContainer` to compute origin adjusted lifetime when origin position is relative to the parent drawable size. --- osu.Game/Rulesets/UI/HitObjectContainer.cs | 40 ++++++++++------------ 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/osu.Game/Rulesets/UI/HitObjectContainer.cs b/osu.Game/Rulesets/UI/HitObjectContainer.cs index dcf350cbd4..83033b2dd5 100644 --- a/osu.Game/Rulesets/UI/HitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/HitObjectContainer.cs @@ -122,18 +122,20 @@ namespace osu.Game.Rulesets.UI var entry = (HitObjectLifetimeEntry)lifetimeEntry; Debug.Assert(!aliveDrawableMap.ContainsKey(entry)); - bool isNonPooled = nonPooledDrawableMap.TryGetValue(entry, out var drawable); + bool isPooled = !nonPooledDrawableMap.TryGetValue(entry, out var drawable); drawable ??= pooledObjectProvider?.GetPooledDrawableRepresentation(entry.HitObject, null); if (drawable == null) throw new InvalidOperationException($"A drawable representation could not be retrieved for hitobject type: {entry.HitObject.GetType().ReadableName()}."); aliveDrawableMap[entry] = drawable; + + if (isPooled) + { + addDrawable(drawable); + HitObjectUsageBegan?.Invoke(entry.HitObject); + } + OnAdd(drawable); - - if (isNonPooled) return; - - addDrawable(drawable); - HitObjectUsageBegan?.Invoke(entry.HitObject); } private void entryBecameDead(LifetimeEntry lifetimeEntry) @@ -142,17 +144,18 @@ namespace osu.Game.Rulesets.UI Debug.Assert(aliveDrawableMap.ContainsKey(entry)); var drawable = aliveDrawableMap[entry]; - bool isNonPooled = nonPooledDrawableMap.ContainsKey(entry); + bool isPooled = !nonPooledDrawableMap.ContainsKey(entry); drawable.OnKilled(); aliveDrawableMap.Remove(entry); + + if (isPooled) + { + removeDrawable(drawable); + HitObjectUsageFinished?.Invoke(entry.HitObject); + } + OnRemove(drawable); - - if (isNonPooled) return; - - removeDrawable(drawable); - // The hit object is not freed when the DHO was not pooled. - HitObjectUsageFinished?.Invoke(entry.HitObject); } private void addDrawable(DrawableHitObject drawable) @@ -211,21 +214,16 @@ namespace osu.Game.Rulesets.UI #endregion /// - /// Invoked when a is added to this container. + /// Invoked after a is added to this container. /// - /// - /// This method is not invoked for nested s. - /// protected virtual void OnAdd(DrawableHitObject drawableHitObject) { + Debug.Assert(drawableHitObject.LoadState >= LoadState.Ready); } /// - /// Invoked when a is removed from this container. + /// Invoked after a is removed from this container. /// - /// - /// This method is not invoked for nested s. - /// protected virtual void OnRemove(DrawableHitObject drawableHitObject) { } From 4b27d43e26b3983bd7a60630f997f3317edecc26 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 31 May 2021 15:13:56 +0900 Subject: [PATCH 593/839] Add new parameter for default fallback logic in `LegacySkin` --- osu.Game/Skinning/LegacyBeatmapSkin.cs | 2 +- osu.Game/Skinning/LegacySkin.cs | 21 ++++++++++++++++++--- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/osu.Game/Skinning/LegacyBeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs index caf37e5bc9..6085eb1c37 100644 --- a/osu.Game/Skinning/LegacyBeatmapSkin.cs +++ b/osu.Game/Skinning/LegacyBeatmapSkin.cs @@ -18,7 +18,7 @@ namespace osu.Game.Skinning protected override bool UseCustomSampleBanks => true; public LegacyBeatmapSkin(BeatmapInfo beatmap, IResourceStore storage, IStorageResourceProvider resources) - : base(createSkinInfo(beatmap), new LegacySkinResourceStore(beatmap.BeatmapSet, storage), resources, beatmap.Path) + : base(createSkinInfo(beatmap), new LegacySkinResourceStore(beatmap.BeatmapSet, storage), resources, beatmap.Path, fallbackToDefault: false) { // Disallow default colours fallback on beatmap skins to allow using parent skin combo colours. (via SkinProvidingContainer) Configuration.AllowDefaultComboColoursFallback = false; diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index d3474caac9..908ed37b6a 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -26,6 +26,8 @@ namespace osu.Game.Skinning { public class LegacySkin : Skin { + private readonly bool fallbackToDefault; + [CanBeNull] protected TextureStore Textures; @@ -54,16 +56,29 @@ namespace osu.Game.Skinning private readonly Dictionary maniaConfigurations = new Dictionary(); + private readonly DefaultLegacySkin legacyDefaultFallback; + [UsedImplicitly(ImplicitUseKindFlags.InstantiatedWithFixedConstructorSignature)] public LegacySkin(SkinInfo skin, IStorageResourceProvider resources) - : this(skin, new LegacySkinResourceStore(skin, resources.Files), resources, "skin.ini") + : this(skin, new LegacySkinResourceStore(skin, resources.Files), resources, "skin.ini", true) { } - protected LegacySkin(SkinInfo skin, [CanBeNull] IResourceStore storage, [CanBeNull] IStorageResourceProvider resources, string filename) + /// + /// Construct a new legacy skin instance. + /// + /// The model for this skin. + /// A storage for looking up files within this skin using user-facing filenames. + /// Access to raw game resources. + /// The user-facing filename of the configuration file to be parsed. Can accept an .osu or skin.ini file. + /// Whether lookups should fallback to the implementations if not provided locally. + protected LegacySkin(SkinInfo skin, [CanBeNull] IResourceStore storage, [CanBeNull] IStorageResourceProvider resources, string configurationFilename, bool fallbackToDefault = false) : base(skin, resources) { - using (var stream = storage?.GetStream(filename)) + this.fallbackToDefault = fallbackToDefault; + legacyDefaultFallback = new DefaultLegacySkin(storage, resources); + + using (var stream = storage?.GetStream(configurationFilename)) { if (stream != null) { From 1d30791ab0b8d747eea64b7ea4c5696c2945e1e5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 31 May 2021 15:27:14 +0900 Subject: [PATCH 594/839] Add potential pathway for legacy lookups --- osu.Game/Skinning/LegacySkin.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 908ed37b6a..02d9c32281 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -142,7 +142,7 @@ namespace osu.Game.Skinning case LegacyManiaSkinConfigurationLookup maniaLookup: if (!AllowManiaSkin) - return null; + break; var result = lookupForMania(maniaLookup); if (result != null) @@ -157,7 +157,7 @@ namespace osu.Game.Skinning return genericLookup(lookup); } - return null; + return fallbackToDefault ? legacyDefaultFallback.GetConfig(lookup) : null; } private IBindable lookupForMania(LegacyManiaSkinConfigurationLookup maniaLookup) @@ -334,7 +334,7 @@ namespace osu.Game.Skinning { } - return null; + return fallbackToDefault ? legacyDefaultFallback.GetConfig(lookup) : null; } public override Drawable GetDrawableComponent(ISkinComponent component) @@ -516,7 +516,7 @@ namespace osu.Game.Skinning return sample; } - return null; + return fallbackToDefault ? legacyDefaultFallback.GetSample(sampleInfo) : null; } private IEnumerable getLegacyLookupNames(HitSampleInfo hitSample) From 88ed95e012ec2ea9609f8959285a5135c43ef6bb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 31 May 2021 17:04:38 +0900 Subject: [PATCH 595/839] Add `FindProvider` lookup function --- .../Skinning/Legacy/ManiaLegacySkinTransformer.cs | 6 +++--- .../Skinning/Legacy/OsuLegacySkinTransformer.cs | 2 +- osu.Game/Skinning/ISkin.cs | 8 ++++++++ osu.Game/Skinning/LegacySkin.cs | 11 +++++++++++ osu.Game/Skinning/SkinProvidingContainer.cs | 8 ++++++++ 5 files changed, 31 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs index 261b8b1fad..c137e2f7cb 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs @@ -69,10 +69,10 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy private void sourceChanged() { - isLegacySkin = new Lazy(() => Source.GetConfig(LegacySkinConfiguration.LegacySetting.Version) != null); - hasKeyTexture = new Lazy(() => Source.GetAnimation( + isLegacySkin = new Lazy(() => Source.FindProvider(s => s.GetConfig(LegacySkinConfiguration.LegacySetting.Version) != null) != null); + hasKeyTexture = new Lazy(() => Source.FindProvider(s => s.GetAnimation( this.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.KeyImage, 0)?.Value - ?? "mania-key1", true, true) != null); + ?? "mania-key1", true, true) != null) != null); } public override Drawable GetDrawableComponent(ISkinComponent component) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs index ffd4f78400..33dc59f30a 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy private void sourceChanged() { - hasHitCircle = new Lazy(() => Source.GetTexture("hitcircle") != null); + hasHitCircle = new Lazy(Source.FindProvider(s => s.GetTexture("hitcircle") != null) != null); } public override Drawable GetDrawableComponent(ISkinComponent component) diff --git a/osu.Game/Skinning/ISkin.cs b/osu.Game/Skinning/ISkin.cs index 73f7cf6d39..df346556fd 100644 --- a/osu.Game/Skinning/ISkin.cs +++ b/osu.Game/Skinning/ISkin.cs @@ -57,5 +57,13 @@ namespace osu.Game.Skinning /// A matching value boxed in an , or null if unavailable. [CanBeNull] IBindable GetConfig(TLookup lookup); + + /// + /// For the specified texture, find any potential skin that can fulfill the lookup. + /// This should be used for cases where subsequent lookups (for related components) need to occur on the same skin. + /// + /// The skin to be used for subsequent lookups, or null if none is available. + [CanBeNull] + ISkin FindProvider(Func lookupFunction) => lookupFunction(this) ? this : null; } } diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 02d9c32281..5ce2d74c99 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -556,5 +556,16 @@ namespace osu.Game.Skinning Textures?.Dispose(); Samples?.Dispose(); } + + ISkin ISkin.FindProvider(Func lookupFunction) + { + if (lookupFunction(this)) + return this; + + if (!fallbackToDefault) + return null; + + return (legacyDefaultFallback as ISkin)?.FindProvider(lookupFunction); + } } } diff --git a/osu.Game/Skinning/SkinProvidingContainer.cs b/osu.Game/Skinning/SkinProvidingContainer.cs index cf22b2e820..c183cd62df 100644 --- a/osu.Game/Skinning/SkinProvidingContainer.cs +++ b/osu.Game/Skinning/SkinProvidingContainer.cs @@ -41,6 +41,14 @@ namespace osu.Game.Skinning RelativeSizeAxes = Axes.Both; } + public ISkin FindProvider(Func lookupFunction) + { + if (lookupFunction(skin)) + return skin; + + return fallbackSource.FindProvider(lookupFunction); + } + public Drawable GetDrawableComponent(ISkinComponent component) { Drawable sourceDrawable; From 8e489754cc4595a3bf95ec34b6024431af7b15f0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 31 May 2021 17:09:30 +0900 Subject: [PATCH 596/839] Add ability for `LegacySkin`s to customise the fallback provider --- osu.Game/Skinning/DefaultLegacySkin.cs | 2 ++ osu.Game/Skinning/LegacySkin.cs | 26 ++++++++++++++++++-------- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/osu.Game/Skinning/DefaultLegacySkin.cs b/osu.Game/Skinning/DefaultLegacySkin.cs index 30192182f3..1d17b5ce20 100644 --- a/osu.Game/Skinning/DefaultLegacySkin.cs +++ b/osu.Game/Skinning/DefaultLegacySkin.cs @@ -31,6 +31,8 @@ namespace osu.Game.Skinning Configuration.LegacyVersion = 2.7m; } + protected override DefaultLegacySkin CreateFallbackSkin(IResourceStore storage, IStorageResourceProvider resources) => null; + public static SkinInfo Info { get; } = new SkinInfo { ID = SkinInfo.CLASSIC_SKIN, // this is temporary until database storage is decided upon. diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 5ce2d74c99..d249f63901 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -24,7 +24,7 @@ using osuTK.Graphics; namespace osu.Game.Skinning { - public class LegacySkin : Skin + public class LegacySkin : Skin, ISkin { private readonly bool fallbackToDefault; @@ -56,6 +56,7 @@ namespace osu.Game.Skinning private readonly Dictionary maniaConfigurations = new Dictionary(); + [CanBeNull] private readonly DefaultLegacySkin legacyDefaultFallback; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedWithFixedConstructorSignature)] @@ -71,12 +72,12 @@ namespace osu.Game.Skinning /// A storage for looking up files within this skin using user-facing filenames. /// Access to raw game resources. /// The user-facing filename of the configuration file to be parsed. Can accept an .osu or skin.ini file. - /// Whether lookups should fallback to the implementations if not provided locally. + /// Whether lookups via fallback to the implementations if not provided locally. protected LegacySkin(SkinInfo skin, [CanBeNull] IResourceStore storage, [CanBeNull] IStorageResourceProvider resources, string configurationFilename, bool fallbackToDefault = false) : base(skin, resources) { this.fallbackToDefault = fallbackToDefault; - legacyDefaultFallback = new DefaultLegacySkin(storage, resources); + legacyDefaultFallback = CreateFallbackSkin(storage, resources); using (var stream = storage?.GetStream(configurationFilename)) { @@ -117,6 +118,10 @@ namespace osu.Game.Skinning true) != null); } + [CanBeNull] + protected virtual DefaultLegacySkin CreateFallbackSkin(IResourceStore storage, IStorageResourceProvider resources) => + new DefaultLegacySkin(storage, resources); + public override IBindable GetConfig(TLookup lookup) { switch (lookup) @@ -157,7 +162,7 @@ namespace osu.Game.Skinning return genericLookup(lookup); } - return fallbackToDefault ? legacyDefaultFallback.GetConfig(lookup) : null; + return legacyDefaultFallback?.GetConfig(lookup); } private IBindable lookupForMania(LegacyManiaSkinConfigurationLookup maniaLookup) @@ -334,7 +339,7 @@ namespace osu.Game.Skinning { } - return fallbackToDefault ? legacyDefaultFallback.GetConfig(lookup) : null; + return legacyDefaultFallback?.GetConfig(lookup); } public override Drawable GetDrawableComponent(ISkinComponent component) @@ -434,7 +439,12 @@ namespace osu.Game.Skinning break; } - return this.GetAnimation(component.LookupName, false, false); + var animation = this.GetAnimation(component.LookupName, false, false); + + if (animation != null) + return animation; + + return legacyDefaultFallback?.GetDrawableComponent(component); } private Texture getParticleTexture(HitResult result) @@ -494,7 +504,7 @@ namespace osu.Game.Skinning return texture; } - return null; + return legacyDefaultFallback?.GetTexture(componentName, wrapModeS, wrapModeT); } public override ISample GetSample(ISampleInfo sampleInfo) @@ -516,7 +526,7 @@ namespace osu.Game.Skinning return sample; } - return fallbackToDefault ? legacyDefaultFallback.GetSample(sampleInfo) : null; + return legacyDefaultFallback?.GetSample(sampleInfo); } private IEnumerable getLegacyLookupNames(HitSampleInfo hitSample) From 3ff9f9c89de524d0ecbffafcc1c13fbe02223ce7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 31 May 2021 17:25:21 +0900 Subject: [PATCH 597/839] Make `FindProvider` non-default --- .../TestSceneCursorTrail.cs | 2 ++ .../TestSceneGameplayCursor.cs | 1 + .../TestSceneSkinFallbacks.cs | 1 + .../TestSceneHitObjectAccentColour.cs | 2 ++ .../Skinning/LegacySkinAnimationTest.cs | 1 + .../Skins/TestSceneSkinConfigurationLookup.cs | 3 ++ .../Gameplay/TestSceneSkinnableDrawable.cs | 6 ++++ .../Gameplay/TestSceneSkinnableSound.cs | 1 + osu.Game/Skinning/ISkin.cs | 3 +- osu.Game/Skinning/LegacyBeatmapSkin.cs | 12 ++++++- osu.Game/Skinning/LegacySkin.cs | 31 ++++++++----------- osu.Game/Skinning/LegacySkinTransformer.cs | 3 ++ osu.Game/Skinning/Skin.cs | 2 ++ osu.Game/Skinning/SkinManager.cs | 2 ++ 14 files changed, 50 insertions(+), 20 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs index 0ba97fac54..9997660c2d 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs @@ -102,6 +102,8 @@ namespace osu.Game.Rulesets.Osu.Tests public IBindable GetConfig(TLookup lookup) => null; + public ISkin FindProvider(Func lookupFunction) => null; + public event Action SourceChanged { add { } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs index 9a77292aff..76111342f0 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs @@ -113,6 +113,7 @@ namespace osu.Game.Rulesets.Osu.Tests public Drawable GetDrawableComponent(ISkinComponent component) => null; public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => null; public ISample GetSample(ISampleInfo sampleInfo) => null; + public ISkin FindProvider(Func lookupFunction) => null; public IBindable GetConfig(TLookup lookup) { diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs index 6c6f05c5c5..fd523fffcb 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs @@ -166,6 +166,7 @@ namespace osu.Game.Rulesets.Osu.Tests public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => default; public IBindable GetConfig(TLookup lookup) => null; + public ISkin FindProvider(Func lookupFunction) => null; public event Action SourceChanged; diff --git a/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs b/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs index 883791c35c..76e5437305 100644 --- a/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs +++ b/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs @@ -123,6 +123,8 @@ namespace osu.Game.Tests.Gameplay public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException(); + public ISkin FindProvider(Func lookupFunction) => null; + public IBindable GetConfig(TLookup lookup) { switch (lookup) diff --git a/osu.Game.Tests/NonVisual/Skinning/LegacySkinAnimationTest.cs b/osu.Game.Tests/NonVisual/Skinning/LegacySkinAnimationTest.cs index b08a228de3..e45b8f7dc5 100644 --- a/osu.Game.Tests/NonVisual/Skinning/LegacySkinAnimationTest.cs +++ b/osu.Game.Tests/NonVisual/Skinning/LegacySkinAnimationTest.cs @@ -61,6 +61,7 @@ namespace osu.Game.Tests.NonVisual.Skinning public Drawable GetDrawableComponent(ISkinComponent component) => throw new NotSupportedException(); public ISample GetSample(ISampleInfo sampleInfo) => throw new NotSupportedException(); public IBindable GetConfig(TLookup lookup) => throw new NotSupportedException(); + public ISkin FindProvider(Func lookupFunction) => null; } private class TestAnimationTimeReference : IAnimationTimeReference diff --git a/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs b/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs index 732a3f3f42..c15d804a19 100644 --- a/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs +++ b/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -222,6 +223,8 @@ namespace osu.Game.Tests.Skins public ISample GetSample(ISampleInfo sampleInfo) => skin.GetSample(sampleInfo); public IBindable GetConfig(TLookup lookup) => skin.GetConfig(lookup); + + public ISkin FindProvider(Func lookupFunction) => skin.FindProvider(lookupFunction); } } } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs index 7a6e2f54c2..bb45d568de 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs @@ -301,6 +301,8 @@ namespace osu.Game.Tests.Visual.Gameplay public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException(); public IBindable GetConfig(TLookup lookup) => throw new NotImplementedException(); + + public ISkin FindProvider(Func lookupFunction) => throw new NotImplementedException(); } private class SecondarySource : ISkin @@ -312,6 +314,8 @@ namespace osu.Game.Tests.Visual.Gameplay public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException(); public IBindable GetConfig(TLookup lookup) => throw new NotImplementedException(); + + public ISkin FindProvider(Func lookupFunction) => throw new NotImplementedException(); } [Cached(typeof(ISkinSource))] @@ -325,6 +329,8 @@ namespace osu.Game.Tests.Visual.Gameplay public IBindable GetConfig(TLookup lookup) => throw new NotImplementedException(); + public ISkin FindProvider(Func lookupFunction) => throw new NotImplementedException(); + public event Action SourceChanged { add { } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs index d792405eeb..d69395fbaa 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs @@ -147,6 +147,7 @@ namespace osu.Game.Tests.Visual.Gameplay 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) => source?.FindProvider(lookupFunction); public void TriggerSourceChanged() { diff --git a/osu.Game/Skinning/ISkin.cs b/osu.Game/Skinning/ISkin.cs index df346556fd..1c3598abb4 100644 --- a/osu.Game/Skinning/ISkin.cs +++ b/osu.Game/Skinning/ISkin.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 JetBrains.Annotations; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; @@ -64,6 +65,6 @@ namespace osu.Game.Skinning /// /// The skin to be used for subsequent lookups, or null if none is available. [CanBeNull] - ISkin FindProvider(Func lookupFunction) => lookupFunction(this) ? this : null; + ISkin FindProvider(Func lookupFunction); } } diff --git a/osu.Game/Skinning/LegacyBeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs index 6085eb1c37..0d6608f579 100644 --- a/osu.Game/Skinning/LegacyBeatmapSkin.cs +++ b/osu.Game/Skinning/LegacyBeatmapSkin.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.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -18,7 +19,7 @@ namespace osu.Game.Skinning protected override bool UseCustomSampleBanks => true; public LegacyBeatmapSkin(BeatmapInfo beatmap, IResourceStore storage, IStorageResourceProvider resources) - : base(createSkinInfo(beatmap), new LegacySkinResourceStore(beatmap.BeatmapSet, storage), resources, beatmap.Path, fallbackToDefault: false) + : base(createSkinInfo(beatmap), new LegacySkinResourceStore(beatmap.BeatmapSet, storage), resources, beatmap.Path) { // Disallow default colours fallback on beatmap skins to allow using parent skin combo colours. (via SkinProvidingContainer) Configuration.AllowDefaultComboColoursFallback = false; @@ -70,6 +71,15 @@ namespace osu.Game.Skinning return base.GetSample(sampleInfo); } + public override ISkin FindProvider(Func lookupFunction) + { + if (lookupFunction(this)) + return this; + + // beatmap skins don't do lookups on the default skin. this allows fallback to user / game default skins. + return null; + } + private static SkinInfo createSkinInfo(BeatmapInfo beatmap) => new SkinInfo { Name = beatmap.ToString(), Creator = beatmap.Metadata?.AuthorString }; } diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index d249f63901..856e795dc6 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -24,10 +24,8 @@ using osuTK.Graphics; namespace osu.Game.Skinning { - public class LegacySkin : Skin, ISkin + public class LegacySkin : Skin { - private readonly bool fallbackToDefault; - [CanBeNull] protected TextureStore Textures; @@ -61,7 +59,7 @@ namespace osu.Game.Skinning [UsedImplicitly(ImplicitUseKindFlags.InstantiatedWithFixedConstructorSignature)] public LegacySkin(SkinInfo skin, IStorageResourceProvider resources) - : this(skin, new LegacySkinResourceStore(skin, resources.Files), resources, "skin.ini", true) + : this(skin, new LegacySkinResourceStore(skin, resources.Files), resources, "skin.ini") { } @@ -72,11 +70,9 @@ namespace osu.Game.Skinning /// A storage for looking up files within this skin using user-facing filenames. /// Access to raw game resources. /// The user-facing filename of the configuration file to be parsed. Can accept an .osu or skin.ini file. - /// Whether lookups via fallback to the implementations if not provided locally. - protected LegacySkin(SkinInfo skin, [CanBeNull] IResourceStore storage, [CanBeNull] IStorageResourceProvider resources, string configurationFilename, bool fallbackToDefault = false) + protected LegacySkin(SkinInfo skin, [CanBeNull] IResourceStore storage, [CanBeNull] IStorageResourceProvider resources, string configurationFilename) : base(skin, resources) { - this.fallbackToDefault = fallbackToDefault; legacyDefaultFallback = CreateFallbackSkin(storage, resources); using (var stream = storage?.GetStream(configurationFilename)) @@ -529,6 +525,16 @@ namespace osu.Game.Skinning return legacyDefaultFallback?.GetSample(sampleInfo); } + public override ISkin FindProvider(Func lookupFunction) + { + var source = base.FindProvider(lookupFunction); + + if (source != null) + return source; + + return legacyDefaultFallback?.FindProvider(lookupFunction); + } + private IEnumerable getLegacyLookupNames(HitSampleInfo hitSample) { var lookupNames = hitSample.LookupNames.SelectMany(getFallbackNames); @@ -566,16 +572,5 @@ namespace osu.Game.Skinning Textures?.Dispose(); Samples?.Dispose(); } - - ISkin ISkin.FindProvider(Func lookupFunction) - { - if (lookupFunction(this)) - return this; - - if (!fallbackToDefault) - return null; - - return (legacyDefaultFallback as ISkin)?.FindProvider(lookupFunction); - } } } diff --git a/osu.Game/Skinning/LegacySkinTransformer.cs b/osu.Game/Skinning/LegacySkinTransformer.cs index ae8faf1a3b..cace4acf6c 100644 --- a/osu.Game/Skinning/LegacySkinTransformer.cs +++ b/osu.Game/Skinning/LegacySkinTransformer.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.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -47,5 +48,7 @@ namespace osu.Game.Skinning } public abstract IBindable GetConfig(TLookup lookup); + + public ISkin FindProvider(Func lookupFunction) => Source.FindProvider(lookupFunction); } } diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index b6cb8fc7a4..c12e9a64c2 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -35,6 +35,8 @@ namespace osu.Game.Skinning public abstract IBindable GetConfig(TLookup lookup); + public virtual ISkin FindProvider(Func lookupFunction) => lookupFunction(this) ? this : null; + protected Skin(SkinInfo skin, IStorageResourceProvider resources) { SkinInfo = skin; diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 079c537066..fa4f657882 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -212,6 +212,8 @@ namespace osu.Game.Skinning public IBindable GetConfig(TLookup lookup) => CurrentSkin.Value.GetConfig(lookup); + public ISkin FindProvider(Func lookupFunction) => CurrentSkin.Value.FindProvider(lookupFunction); + #region IResourceStorageProvider AudioManager IStorageResourceProvider.AudioManager => audio; From 282c5a917786cc99d08a178356ed971f039fbb35 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 31 May 2021 18:00:06 +0900 Subject: [PATCH 598/839] Fix potential nullref in `SkinProvidingContainer` --- osu.Game/Skinning/LegacySkin.cs | 6 +++--- osu.Game/Skinning/SkinProvidingContainer.cs | 9 ++++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 856e795dc6..92944a9c93 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -73,7 +73,8 @@ namespace osu.Game.Skinning protected LegacySkin(SkinInfo skin, [CanBeNull] IResourceStore storage, [CanBeNull] IStorageResourceProvider resources, string configurationFilename) : base(skin, resources) { - legacyDefaultFallback = CreateFallbackSkin(storage, resources); + if (resources != null) + legacyDefaultFallback = CreateFallbackSkin(storage, resources); using (var stream = storage?.GetStream(configurationFilename)) { @@ -115,8 +116,7 @@ namespace osu.Game.Skinning } [CanBeNull] - protected virtual DefaultLegacySkin CreateFallbackSkin(IResourceStore storage, IStorageResourceProvider resources) => - new DefaultLegacySkin(storage, resources); + protected virtual DefaultLegacySkin CreateFallbackSkin(IResourceStore storage, IStorageResourceProvider resources) => new DefaultLegacySkin(resources); public override IBindable GetConfig(TLookup lookup) { diff --git a/osu.Game/Skinning/SkinProvidingContainer.cs b/osu.Game/Skinning/SkinProvidingContainer.cs index c183cd62df..863b5f5a24 100644 --- a/osu.Game/Skinning/SkinProvidingContainer.cs +++ b/osu.Game/Skinning/SkinProvidingContainer.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; @@ -20,8 +21,10 @@ namespace osu.Game.Skinning { public event Action SourceChanged; + [CanBeNull] private readonly ISkin skin; + [CanBeNull] private ISkinSource fallbackSource; protected virtual bool AllowDrawableLookup(ISkinComponent component) => true; @@ -43,10 +46,10 @@ namespace osu.Game.Skinning public ISkin FindProvider(Func lookupFunction) { - if (lookupFunction(skin)) + if (skin != null && lookupFunction(skin)) return skin; - return fallbackSource.FindProvider(lookupFunction); + return fallbackSource?.FindProvider(lookupFunction); } public Drawable GetDrawableComponent(ISkinComponent component) @@ -93,7 +96,7 @@ namespace osu.Game.Skinning { if (canUseSkinLookup) { - var bindable = skin.GetConfig(lookup); + var bindable = skin?.GetConfig(lookup); if (bindable != null) return bindable; } From 1161378b6bec0fd430a3734a0d5e852fec41298e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 31 May 2021 20:15:26 +0900 Subject: [PATCH 599/839] Fix incorrect fallback logic in `LegacyBeatmapSkin` --- osu.Game/Skinning/LegacyBeatmapSkin.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/osu.Game/Skinning/LegacyBeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs index 0d6608f579..2374cb976b 100644 --- a/osu.Game/Skinning/LegacyBeatmapSkin.cs +++ b/osu.Game/Skinning/LegacyBeatmapSkin.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.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -71,12 +70,11 @@ namespace osu.Game.Skinning return base.GetSample(sampleInfo); } - public override ISkin FindProvider(Func lookupFunction) + protected override DefaultLegacySkin CreateFallbackSkin(IResourceStore storage, IStorageResourceProvider resources) { - if (lookupFunction(this)) - return this; - - // beatmap skins don't do lookups on the default skin. this allows fallback to user / game default skins. + // for simplicity, beatmap skins don't do lookups on the default skin. + // this will mean that fallback always occurs to the user (then default) skin. + // this may not offer perfect behaviour, but helps keep things simple. return null; } From 33577cbad5fe2f3a262877879ffdd9dcafc7753b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 31 May 2021 20:43:01 +0900 Subject: [PATCH 600/839] Fix multiple issues with default lookups --- .../Skinning/Legacy/ManiaLegacySkinTransformer.cs | 4 ++-- .../Skinning/Legacy/OsuLegacySkinTransformer.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs index c137e2f7cb..8aa0c85433 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs @@ -69,8 +69,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy private void sourceChanged() { - isLegacySkin = new Lazy(() => Source.FindProvider(s => s.GetConfig(LegacySkinConfiguration.LegacySetting.Version) != null) != null); - hasKeyTexture = new Lazy(() => Source.FindProvider(s => s.GetAnimation( + isLegacySkin = new Lazy(() => FindProvider(s => s.GetConfig(LegacySkinConfiguration.LegacySetting.Version) != null) != null); + hasKeyTexture = new Lazy(() => FindProvider(s => s.GetAnimation( this.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.KeyImage, 0)?.Value ?? "mania-key1", true, true) != null) != null); } diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs index 33dc59f30a..33693748d9 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy private void sourceChanged() { - hasHitCircle = new Lazy(Source.FindProvider(s => s.GetTexture("hitcircle") != null) != null); + hasHitCircle = new Lazy(() => FindProvider(s => s.GetTexture("hitcircle") != null) != null); } public override Drawable GetDrawableComponent(ISkinComponent component) From 69c4ccad05297772db8bc7cabc5dae759f789d92 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 31 May 2021 21:27:11 +0900 Subject: [PATCH 601/839] Fix weird taiko logic failing for weird reasons that probably should not have been a thing --- osu.Game/Skinning/LegacySkin.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 92944a9c93..f3f3e67eef 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -563,7 +563,11 @@ namespace osu.Game.Skinning // Fall back to using the last piece for components coming from lazer (e.g. "Gameplay/osu/approachcircle" -> "approachcircle"). string lastPiece = componentName.Split('/').Last(); - yield return componentName.StartsWith("Gameplay/taiko/", StringComparison.Ordinal) ? "taiko-" + lastPiece : lastPiece; + + if (componentName.StartsWith("Gameplay/taiko/", StringComparison.Ordinal)) + yield return "taiko-" + lastPiece; + + yield return lastPiece; } protected override void Dispose(bool isDisposing) From 86d1ba7ef29c16408c2726f822a28c1e5fb84ba5 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 31 May 2021 23:17:26 +0900 Subject: [PATCH 602/839] Remove unused `IScrollingHitObject` interface --- .../Objects/Drawables/IScrollingHitObject.cs | 31 ------------------- 1 file changed, 31 deletions(-) delete mode 100644 osu.Game/Rulesets/Objects/Drawables/IScrollingHitObject.cs diff --git a/osu.Game/Rulesets/Objects/Drawables/IScrollingHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/IScrollingHitObject.cs deleted file mode 100644 index 48fcfabc2f..0000000000 --- a/osu.Game/Rulesets/Objects/Drawables/IScrollingHitObject.cs +++ /dev/null @@ -1,31 +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.Bindables; -using osu.Framework.Graphics; - -namespace osu.Game.Rulesets.Objects.Drawables -{ - /// - /// An interface that exposes properties required for scrolling hit objects to be properly displayed. - /// - internal interface IScrollingHitObject : IDrawable - { - /// - /// Time offset before the hit object start time at which this becomes visible and the time offset - /// after the hit object's end time after which it expires. - /// - /// - /// This provides only a default life time range, however classes inheriting from should override - /// their life times if more tight control is desired. - /// - /// - BindableDouble LifetimeOffset { get; } - - /// - /// Axes which this will scroll through. - /// This is set by the container which this scrolls through. - /// - Axes ScrollingAxes { set; } - } -} From d5714e63b9611f2743130f5c689e5b95a6f4f53b Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 1 Jun 2021 13:17:30 +0900 Subject: [PATCH 603/839] Apply code styling suggestions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- .../Pooling/PoolableDrawableWithLifetime.cs | 26 +++++++------------ 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs b/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs index 66d4421cdb..5125243bfc 100644 --- a/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs +++ b/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs @@ -37,8 +37,7 @@ namespace osu.Game.Rulesets.Objects.Pooling if (Entry == null) return; - Entry.LifetimeStart = value; - base.LifetimeStart = Entry.LifetimeStart; + base.LifetimeStart = Entry.LifetimeStart = value; } } @@ -52,8 +51,7 @@ namespace osu.Game.Rulesets.Objects.Pooling if (Entry == null) return; - Entry.LifetimeEnd = value; - base.LifetimeEnd = Entry.LifetimeEnd; + base.LifetimeEnd = Entry.LifetimeEnd = value; } } @@ -84,8 +82,8 @@ namespace osu.Game.Rulesets.Objects.Pooling free(); Entry = entry; - entry.LifetimeChanged += entryLifetimeChanged; - setLifetimeFromEntry(); + entry.LifetimeChanged += setLifetimeFromEntry; + setLifetimeFromEntry(entry); OnApply(entry); @@ -121,23 +119,19 @@ namespace osu.Game.Rulesets.Objects.Pooling OnFree(Entry); - Entry.LifetimeChanged -= entryLifetimeChanged; + Entry.LifetimeChanged -= setLifetimeFromEntry; Entry = null; - setLifetimeFromEntry(); + base.LifetimeStart = double.MinValue; + base.LifetimeEnd = double.MaxValue; HasEntryApplied = false; } - private void entryLifetimeChanged(LifetimeEntry entry) + private void setLifetimeFromEntry(LifetimeEntry entry) { Debug.Assert(entry == Entry); - setLifetimeFromEntry(); - } - - private void setLifetimeFromEntry() - { - base.LifetimeStart = Entry?.LifetimeStart ?? double.MinValue; - base.LifetimeEnd = Entry?.LifetimeEnd ?? double.MaxValue; + base.LifetimeStart = entry.LifetimeStart; + base.LifetimeEnd = entry.LifetimeEnd; } } } From 1a05a5d2f02dca6d74be4c8af2a33e111379d8f1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 13:50:04 +0900 Subject: [PATCH 604/839] Add test covering failure to resolve relative URLs --- osu.Game.Tests/Chat/MessageFormatterTests.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game.Tests/Chat/MessageFormatterTests.cs b/osu.Game.Tests/Chat/MessageFormatterTests.cs index b80da928c8..6f9007d670 100644 --- a/osu.Game.Tests/Chat/MessageFormatterTests.cs +++ b/osu.Game.Tests/Chat/MessageFormatterTests.cs @@ -489,5 +489,14 @@ namespace osu.Game.Tests.Chat Assert.AreEqual(result.Links[2].Url, "\uD83D\uDE00"); Assert.AreEqual(result.Links[3].Url, "\uD83D\uDE20"); } + + [Test] + public void TestRelativeExternalLinks() + { + LinkDetails result = MessageFormatter.GetLinkDetails("/relative"); + + Assert.AreEqual(LinkAction.External, result.Action); + Assert.AreEqual("/relative", result.Argument); + } } } From 111bfd4d88abe484931b8789d36653832b005264 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 13:50:20 +0900 Subject: [PATCH 605/839] Fix relative URLs having a null argument after resolution to `LinkDetails` --- osu.Game/Online/Chat/MessageFormatter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs index b80720a0aa..54d7030457 100644 --- a/osu.Game/Online/Chat/MessageFormatter.cs +++ b/osu.Game/Online/Chat/MessageFormatter.cs @@ -217,7 +217,7 @@ namespace osu.Game.Online.Chat return new LinkDetails(LinkAction.JoinMultiplayerMatch, args[1]); } - return new LinkDetails(LinkAction.External, null); + return new LinkDetails(LinkAction.External, url); } private static MessageFormatterResult format(string toFormat, int startIndex = 0, int space = 3) From 3ff97f787aaf9a72ffec5f271c81fe2abbf81eb0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 13:32:52 +0900 Subject: [PATCH 606/839] Localise all URL generation for now to avoid weird `Schedule` logic --- .../Containers/Markdown/OsuMarkdownContainer.cs | 12 ------------ .../Overlays/Wiki/Markdown/WikiMarkdownContainer.cs | 7 ++++++- osu.Game/Overlays/Wiki/WikiPanelContainer.cs | 4 +++- osu.Game/Overlays/WikiOverlay.cs | 2 +- 4 files changed, 10 insertions(+), 15 deletions(-) diff --git a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownContainer.cs b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownContainer.cs index ad11a9625e..6facf4e26c 100644 --- a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownContainer.cs +++ b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownContainer.cs @@ -6,13 +6,11 @@ using Markdig.Extensions.AutoIdentifiers; using Markdig.Extensions.Tables; using Markdig.Extensions.Yaml; using Markdig.Syntax; -using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers.Markdown; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics.Sprites; -using osu.Game.Online.API; namespace osu.Game.Graphics.Containers.Markdown { @@ -23,16 +21,6 @@ namespace osu.Game.Graphics.Containers.Markdown LineSpacing = 21; } - protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) - { - var api = parent.Get(); - - // needs to be set before the base BDL call executes to avoid invalidating any already populated markdown content. - DocumentUrl = api.WebsiteRootUrl; - - return base.CreateChildDependencies(parent); - } - protected override void AddMarkdownComponent(IMarkdownObject markdownObject, FillFlowContainer container, int level) { switch (markdownObject) diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs index 1c5fd99ce3..acaaa523a2 100644 --- a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs +++ b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs @@ -5,17 +5,22 @@ using System.Linq; using Markdig.Extensions.Yaml; using Markdig.Syntax; using Markdig.Syntax.Inlines; +using osu.Framework.Allocation; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers.Markdown; using osu.Game.Graphics.Containers.Markdown; +using osu.Game.Online.API; namespace osu.Game.Overlays.Wiki.Markdown { public class WikiMarkdownContainer : OsuMarkdownContainer { + [Resolved] + private IAPIProvider api { get; set; } + public string CurrentPath { - set => Schedule(() => DocumentUrl = $"{DocumentUrl}wiki/{value}"); + set => DocumentUrl = value; } protected override void AddMarkdownComponent(IMarkdownObject markdownObject, FillFlowContainer container, int level) diff --git a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs index db213e4951..e1c00a955b 100644 --- a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs +++ b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs @@ -13,6 +13,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Graphics.Containers.Markdown; +using osu.Game.Online.API; using osu.Game.Overlays.Wiki.Markdown; using osuTK; using osuTK.Graphics; @@ -37,7 +38,7 @@ namespace osu.Game.Overlays.Wiki } [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) + private void load(OverlayColourProvider colourProvider, IAPIProvider api) { Children = new Drawable[] { @@ -61,6 +62,7 @@ namespace osu.Game.Overlays.Wiki }, panelContainer = new WikiPanelMarkdownContainer(isFullWidth) { + CurrentPath = $@"{api.WebsiteRootUrl}/wiki/", Text = text, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs index 040e608574..a87a592b56 100644 --- a/osu.Game/Overlays/WikiOverlay.cs +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -73,7 +73,7 @@ namespace osu.Game.Overlays { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - CurrentPath = $"{path.Value}/", + CurrentPath = $@"{api.WebsiteRootUrl}/wiki/{path.Value}/", Text = response.Markdown, DocumentMargin = new MarginPadding(0), DocumentPadding = new MarginPadding From 6e4730652e6829a6b54014aebd6dc5a5ad5823d3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 14:01:08 +0900 Subject: [PATCH 607/839] Push private methods down --- osu.Game/Overlays/WikiOverlay.cs | 92 ++++++++++++++++---------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs index a87a592b56..8468a48bf5 100644 --- a/osu.Game/Overlays/WikiOverlay.cs +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -36,6 +36,52 @@ namespace osu.Game.Overlays { } + public void ShowPage(string pagePath = index_path) + { + path.Value = pagePath.Trim('/'); + Show(); + } + + protected override WikiHeader CreateHeader() => new WikiHeader + { + ShowIndexPage = () => ShowPage(), + ShowParentPage = showParentPage, + }; + + protected override void LoadComplete() + { + base.LoadComplete(); + path.BindValueChanged(onPathChanged); + wikiData.BindTo(Header.WikiPageData); + } + + protected override void PopIn() + { + base.PopIn(); + + if (displayUpdateRequired) + { + path.TriggerChange(); + displayUpdateRequired = false; + } + } + + protected override void PopOutComplete() + { + base.PopOutComplete(); + displayUpdateRequired = true; + } + + protected void LoadDisplay(Drawable display) + { + ScrollFlow.ScrollToStart(); + LoadComponentAsync(display, loaded => + { + Child = loaded; + Loading.Hide(); + }, (cancellationToken = new CancellationTokenSource()).Token); + } + private void onPathChanged(ValueChangedEvent e) { cancellationToken?.Cancel(); @@ -92,52 +138,6 @@ namespace osu.Game.Overlays ShowPage(parentPath); } - public void ShowPage(string pagePath = index_path) - { - path.Value = pagePath.Trim('/'); - Show(); - } - - protected override WikiHeader CreateHeader() => new WikiHeader - { - ShowIndexPage = () => ShowPage(), - ShowParentPage = showParentPage, - }; - - protected override void LoadComplete() - { - base.LoadComplete(); - path.BindValueChanged(onPathChanged); - wikiData.BindTo(Header.WikiPageData); - } - - protected override void PopIn() - { - base.PopIn(); - - if (displayUpdateRequired) - { - path.TriggerChange(); - displayUpdateRequired = false; - } - } - - protected override void PopOutComplete() - { - base.PopOutComplete(); - displayUpdateRequired = true; - } - - protected void LoadDisplay(Drawable display) - { - ScrollFlow.ScrollToStart(); - LoadComponentAsync(display, loaded => - { - Child = loaded; - Loading.Hide(); - }, (cancellationToken = new CancellationTokenSource()).Token); - } - protected override void Dispose(bool isDisposing) { cancellationToken?.Cancel(); From 27b703952059d79c50f3563b04a85db77b73e74f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 14:02:32 +0900 Subject: [PATCH 608/839] Use constant --- osu.Game/Overlays/WikiOverlay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs index 8468a48bf5..af7bc40f17 100644 --- a/osu.Game/Overlays/WikiOverlay.cs +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -16,7 +16,7 @@ namespace osu.Game.Overlays { public class WikiOverlay : OnlineOverlay { - private const string index_path = "Main_Page"; + private const string index_path = @"main_page"; private readonly Bindable path = new Bindable(index_path); @@ -101,7 +101,7 @@ namespace osu.Game.Overlays { wikiData.Value = response; - if (response.Layout == "main_page") + if (response.Layout == index_path) { LoadDisplay(new WikiMainPage { From a9f4bc6285dcc84bd9cf4582b6bc2b7557815908 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 14:09:35 +0900 Subject: [PATCH 609/839] Never return a null argument Enable nullable --- osu.Game.Tests/Chat/MessageFormatterTests.cs | 13 +++++++++++-- osu.Game/Online/Chat/MessageFormatter.cs | 8 +++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Chat/MessageFormatterTests.cs b/osu.Game.Tests/Chat/MessageFormatterTests.cs index 6f9007d670..ecb37706b0 100644 --- a/osu.Game.Tests/Chat/MessageFormatterTests.cs +++ b/osu.Game.Tests/Chat/MessageFormatterTests.cs @@ -24,10 +24,10 @@ namespace osu.Game.Tests.Chat [TestCase(LinkAction.OpenBeatmap, "456", "https://dev.ppy.sh/beatmapsets/123#osu/456")] [TestCase(LinkAction.OpenBeatmap, "456", "https://dev.ppy.sh/beatmapsets/123#osu/456?whatever")] [TestCase(LinkAction.OpenBeatmap, "456", "https://dev.ppy.sh/beatmapsets/123/456")] - [TestCase(LinkAction.External, null, "https://dev.ppy.sh/beatmapsets/abc/def")] + [TestCase(LinkAction.External, "https://dev.ppy.sh/beatmapsets/abc/def", "https://dev.ppy.sh/beatmapsets/abc/def")] [TestCase(LinkAction.OpenBeatmapSet, "123", "https://dev.ppy.sh/beatmapsets/123")] [TestCase(LinkAction.OpenBeatmapSet, "123", "https://dev.ppy.sh/beatmapsets/123/whatever")] - [TestCase(LinkAction.External, null, "https://dev.ppy.sh/beatmapsets/abc")] + [TestCase(LinkAction.External, "https://dev.ppy.sh/beatmapsets/abc", "https://dev.ppy.sh/beatmapsets/abc")] public void TestBeatmapLinks(LinkAction expectedAction, string expectedArg, string link) { MessageFormatter.WebsiteRootUrl = "dev.ppy.sh"; @@ -490,6 +490,15 @@ namespace osu.Game.Tests.Chat Assert.AreEqual(result.Links[3].Url, "\uD83D\uDE20"); } + [Test] + public void TestAbsoluteExternalLinks() + { + LinkDetails result = MessageFormatter.GetLinkDetails("https://google.com"); + + Assert.AreEqual(LinkAction.External, result.Action); + Assert.AreEqual("https://google.com", result.Argument); + } + [Test] public void TestRelativeExternalLinks() { diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs index 54d7030457..e08cb1b35f 100644 --- a/osu.Game/Online/Chat/MessageFormatter.cs +++ b/osu.Game/Online/Chat/MessageFormatter.cs @@ -6,6 +6,8 @@ using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; +#nullable enable + namespace osu.Game.Online.Chat { public static class MessageFormatter @@ -61,7 +63,7 @@ namespace osu.Game.Online.Chat private static string websiteRootUrl = "osu.ppy.sh"; - private static void handleMatches(Regex regex, string display, string link, MessageFormatterResult result, int startIndex = 0, LinkAction? linkActionOverride = null, char[] escapeChars = null) + private static void handleMatches(Regex regex, string display, string link, MessageFormatterResult result, int startIndex = 0, LinkAction? linkActionOverride = null, char[]? escapeChars = null) { int captureOffset = 0; @@ -170,12 +172,12 @@ namespace osu.Game.Online.Chat } } - return new LinkDetails(LinkAction.External, null); + break; case "osu": // every internal link also needs some kind of argument if (args.Length < 3) - return new LinkDetails(LinkAction.External, null); + break; LinkAction linkType; From 9221213fe527bdc4e33188a1a9a7642edd240ab4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 14:19:13 +0900 Subject: [PATCH 610/839] Fix potential nullref is beatmap load failed --- osu.Game/Screens/Play/ReplayPlayer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs index 07c3d197da..f26675cc64 100644 --- a/osu.Game/Screens/Play/ReplayPlayer.cs +++ b/osu.Game/Screens/Play/ReplayPlayer.cs @@ -36,6 +36,8 @@ namespace osu.Game.Screens.Play [BackgroundDependencyLoader] private void load() { + if (!LoadedBeatmapSuccessfully) return; + Score = createScore(GameplayBeatmap, Mods.Value); } From cbf3ef5400bbc7ea2e75f532845175f99a002341 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 14:22:16 +0900 Subject: [PATCH 611/839] Create replay via the `ICreateReplay` interface instead of explicitly `ModAutoplay` --- osu.Game/Screens/Select/PlaySongSelect.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 357222c109..418cf23ce7 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -111,9 +111,9 @@ namespace osu.Game.Screens.Select Player createPlayer() { - var autoplayMod = Mods.Value.OfType().FirstOrDefault(); - if (autoplayMod != null) - return new ReplayPlayer((beatmap, mods) => autoplayMod.CreateReplayScore(beatmap, mods)); + var replayGeneratingMod = Mods.Value.OfType().FirstOrDefault(); + if (replayGeneratingMod != null) + return new ReplayPlayer((beatmap, mods) => replayGeneratingMod.CreateReplayScore(beatmap, mods)); return new SoloPlayer(); } From 86020adf6463efc2565a5ffb518270eb91ce5285 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 1 Jun 2021 14:22:12 +0900 Subject: [PATCH 612/839] Revert invalid code transformation --- .../Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs b/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs index 5125243bfc..b1515e2ac2 100644 --- a/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs +++ b/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs @@ -37,6 +37,8 @@ namespace osu.Game.Rulesets.Objects.Pooling if (Entry == null) return; + // Cannot write it as `base.LifetimeStart = Entry.LifetimeStart = value` because the change may be blocked (when `HitObjectLifetimeEntry.KeepAlive` is true). + Entry.LifetimeStart = value; base.LifetimeStart = Entry.LifetimeStart = value; } } @@ -51,7 +53,8 @@ namespace osu.Game.Rulesets.Objects.Pooling if (Entry == null) return; - base.LifetimeEnd = Entry.LifetimeEnd = value; + Entry.LifetimeEnd = value; + base.LifetimeEnd = Entry.LifetimeEnd; } } From 977d44df87a22eeed43851a89b86525523c8d51c Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 1 Jun 2021 14:25:32 +0900 Subject: [PATCH 613/839] Add test catching lifetime change while KeepAlive is true --- osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs b/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs index 5dfc9a2786..b5b3cec15d 100644 --- a/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs +++ b/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs @@ -117,8 +117,12 @@ namespace osu.Game.Tests.Gameplay AddStep("Modify start time", () => entry.HitObject.StartTime = 100); AddAssert("Drawable lifetime is correct", () => dho.LifetimeStart == double.MinValue); + AddStep("Set LifetimeEnd", () => dho.LifetimeEnd = 999); + AddAssert("Lifetime change is blocked", () => dho.LifetimeEnd == double.MaxValue); + AddStep("KeepAlive = false", () => entry.KeepAlive = false); - AddAssert("Drawable lifetime is restored", () => dho.LifetimeStart == 100 - TestLifetimeEntry.INITIAL_LIFETIME_OFFSET); + AddAssert("Drawable lifetime is restored", () => + dho.LifetimeStart == 100 - TestLifetimeEntry.INITIAL_LIFETIME_OFFSET && dho.LifetimeEnd == 999); } private class TestDrawableHitObject : DrawableHitObject From 6ef9b346e12e7b1f5f63177990947efb5c5eae8b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 14:33:21 +0900 Subject: [PATCH 614/839] Fix newly found inspections from 2021.1EAP1 --- .../TestSceneMultiplayerRoomManager.cs | 21 ++++++++++++------- .../Screens/TestSceneTeamIntroScreen.cs | 11 +++++----- .../Lounge/Components/DrawableRoom.cs | 8 +++---- .../OnlinePlay/Multiplayer/Multiplayer.cs | 12 +++++------ .../OnlinePlay/OnlinePlaySongSelect.cs | 18 +++++++++------- .../Beatmaps/DifficultyCalculatorTest.cs | 12 +++++++---- 6 files changed, 49 insertions(+), 33 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs index 91c15de69f..c008771fd9 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs @@ -141,14 +141,21 @@ namespace osu.Game.Tests.Visual.Multiplayer private Room createRoom(Action initFunc = null) { - var room = new Room(); - - room.Name.Value = "test room"; - room.Playlist.Add(new PlaylistItem + var room = new Room { - Beatmap = { Value = new TestBeatmap(Ruleset.Value).BeatmapInfo }, - Ruleset = { Value = Ruleset.Value } - }); + Name = + { + Value = "test room" + }, + Playlist = + { + new PlaylistItem + { + Beatmap = { Value = new TestBeatmap(Ruleset.Value).BeatmapInfo }, + Ruleset = { Value = Ruleset.Value } + } + } + }; initFunc?.Invoke(room); return room; diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneTeamIntroScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneTeamIntroScreen.cs index b3f78c92d9..e89aac73fa 100644 --- a/osu.Game.Tournament.Tests/Screens/TestSceneTeamIntroScreen.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneTeamIntroScreen.cs @@ -17,11 +17,12 @@ namespace osu.Game.Tournament.Tests.Screens [BackgroundDependencyLoader] private void load() { - var match = new TournamentMatch(); - match.Team1.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym.Value == "USA"); - match.Team2.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym.Value == "JPN"); - match.Round.Value = Ladder.Rounds.FirstOrDefault(g => g.Name.Value == "Finals"); - ladder.CurrentMatch.Value = match; + ladder.CurrentMatch.Value = new TournamentMatch + { + Team1 = { Value = Ladder.Teams.FirstOrDefault(t => t.Acronym.Value == "USA") }, + Team2 = { Value = Ladder.Teams.FirstOrDefault(t => t.Acronym.Value == "JPN") }, + Round = { Value = Ladder.Rounds.FirstOrDefault(g => g.Name.Value == "Finals") } + }; Add(new TeamIntroScreen { diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 0a7198a7fa..35782c6104 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -39,7 +39,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components public event Action StateChanged; private readonly Box selectionBox; - private CachedModelDependencyContainer dependencies; [Resolved(canBeNull: true)] private OnlinePlayScreen parentScreen { get; set; } @@ -209,9 +208,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { - dependencies = new CachedModelDependencyContainer(base.CreateChildDependencies(parent)); - dependencies.Model.Value = Room; - return dependencies; + return new CachedModelDependencyContainer(base.CreateChildDependencies(parent)) + { + Model = { Value = Room } + }; } protected override void LoadComplete() diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs index a065d04f64..dbac826954 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs @@ -54,12 +54,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer Logger.Log($"Polling adjusted (listing: {multiplayerRoomManager.TimeBetweenListingPolls.Value}, selection: {multiplayerRoomManager.TimeBetweenSelectionPolls.Value})"); } - protected override Room CreateNewRoom() - { - var room = new Room { Name = { Value = $"{API.LocalUser}'s awesome room" } }; - room.Category.Value = RoomCategory.Realtime; - return room; - } + protected override Room CreateNewRoom() => + new Room + { + Name = { Value = $"{API.LocalUser}'s awesome room" }, + Category = { Value = RoomCategory.Realtime } + }; protected override string ScreenTitle => "Multiplayer"; diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs index 3f30ef1176..3e7e557aad 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs @@ -96,15 +96,19 @@ namespace osu.Game.Screens.OnlinePlay { itemSelected = true; - var item = new PlaylistItem(); + var item = new PlaylistItem + { + Beatmap = + { + Value = Beatmap.Value.BeatmapInfo + }, + Ruleset = + { + Value = Ruleset.Value + } + }; - item.Beatmap.Value = Beatmap.Value.BeatmapInfo; - item.Ruleset.Value = Ruleset.Value; - - item.RequiredMods.Clear(); item.RequiredMods.AddRange(Mods.Value.Select(m => m.CreateCopy())); - - item.AllowedMods.Clear(); item.AllowedMods.AddRange(FreeMods.Value.Select(m => m.CreateCopy())); SelectItem(item); diff --git a/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs b/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs index e10bf08da4..76f229a799 100644 --- a/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs +++ b/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs @@ -31,12 +31,16 @@ namespace osu.Game.Tests.Beatmaps using (var stream = new LineBufferedReader(resStream)) { var decoder = Decoder.GetDecoder(stream); + ((LegacyBeatmapDecoder)decoder).ApplyOffsets = false; - var working = new TestWorkingBeatmap(decoder.Decode(stream)); - working.BeatmapInfo.Ruleset = CreateRuleset().RulesetInfo; - - return working; + return new TestWorkingBeatmap(decoder.Decode(stream)) + { + BeatmapInfo = + { + Ruleset = CreateRuleset().RulesetInfo + } + }; } } From 0f381f7758d567f4dc4796beceec10a02ba67db1 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 1 Jun 2021 14:38:02 +0900 Subject: [PATCH 615/839] Fix wrong code --- osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs | 6 ++++-- .../Objects/Pooling/PoolableDrawableWithLifetime.cs | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs b/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs index b5b3cec15d..da0d57f9d1 100644 --- a/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs +++ b/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs @@ -117,12 +117,14 @@ namespace osu.Game.Tests.Gameplay AddStep("Modify start time", () => entry.HitObject.StartTime = 100); AddAssert("Drawable lifetime is correct", () => dho.LifetimeStart == double.MinValue); + AddStep("Set LifetimeStart", () => dho.LifetimeStart = 666); + AddAssert("Lifetime change is blocked", () => dho.LifetimeStart == double.MinValue); + AddStep("Set LifetimeEnd", () => dho.LifetimeEnd = 999); AddAssert("Lifetime change is blocked", () => dho.LifetimeEnd == double.MaxValue); AddStep("KeepAlive = false", () => entry.KeepAlive = false); - AddAssert("Drawable lifetime is restored", () => - dho.LifetimeStart == 100 - TestLifetimeEntry.INITIAL_LIFETIME_OFFSET && dho.LifetimeEnd == 999); + AddAssert("Drawable lifetime is restored", () => dho.LifetimeStart == 666 && dho.LifetimeEnd == 999); } private class TestDrawableHitObject : DrawableHitObject diff --git a/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs b/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs index b1515e2ac2..fb1baf1d65 100644 --- a/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs +++ b/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs @@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Objects.Pooling // Cannot write it as `base.LifetimeStart = Entry.LifetimeStart = value` because the change may be blocked (when `HitObjectLifetimeEntry.KeepAlive` is true). Entry.LifetimeStart = value; - base.LifetimeStart = Entry.LifetimeStart = value; + base.LifetimeStart = Entry.LifetimeStart; } } From 00ffea5e2c2b7bfa666d1da7c57e18785a5f6aa1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 14:38:30 +0900 Subject: [PATCH 616/839] Update tests to specify full absolute path --- .../Visual/Online/TestSceneWikiMarkdownContainer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs index 1e19af933a..9d8f07969c 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs @@ -48,7 +48,7 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestLink() { - AddStep("set current path", () => markdownContainer.CurrentPath = "Article_styling_criteria/"); + AddStep("set current path", () => markdownContainer.CurrentPath = $"{API.WebsiteRootUrl}/wiki/Article_styling_criteria/"); AddStep("set '/wiki/Main_Page''", () => markdownContainer.Text = "[wiki main page](/wiki/Main_Page)"); AddAssert("check url", () => markdownContainer.Link.Url == $"{API.WebsiteRootUrl}/wiki/Main_Page"); @@ -113,7 +113,7 @@ needs_cleanup: true AddStep("Add relative image", () => { markdownContainer.DocumentUrl = "https://dev.ppy.sh"; - markdownContainer.CurrentPath = "Interface/"; + markdownContainer.CurrentPath = $"{API.WebsiteRootUrl}/wiki/Interface/"; markdownContainer.Text = "![intro](img/intro-screen.jpg)"; }); } @@ -124,7 +124,7 @@ needs_cleanup: true AddStep("Add paragraph with block image", () => { markdownContainer.DocumentUrl = "https://dev.ppy.sh"; - markdownContainer.CurrentPath = "Interface/"; + markdownContainer.CurrentPath = $"{API.WebsiteRootUrl}/wiki/Interface/"; markdownContainer.Text = @"Line before image ![play menu](img/play-menu.jpg ""Main Menu in osu!"") From 40949f6c1b0c1088f4c28b94b8f9c30084952f69 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 1 Jun 2021 14:46:43 +0900 Subject: [PATCH 617/839] Simplify lifetime setter Setting entry lifetime will cause `LifetimeChanged` event and `base.LifetimeStart`/`End` will be modified in the callback. --- .../Objects/Pooling/PoolableDrawableWithLifetime.cs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs b/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs index fb1baf1d65..4440ca8d21 100644 --- a/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs +++ b/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs @@ -35,11 +35,8 @@ namespace osu.Game.Rulesets.Objects.Pooling if (Entry == null && LifetimeStart != value) throw new InvalidOperationException($"Cannot modify lifetime of {nameof(PoolableDrawableWithLifetime)} when entry is not set"); - if (Entry == null) return; - - // Cannot write it as `base.LifetimeStart = Entry.LifetimeStart = value` because the change may be blocked (when `HitObjectLifetimeEntry.KeepAlive` is true). - Entry.LifetimeStart = value; - base.LifetimeStart = Entry.LifetimeStart; + if (Entry != null) + Entry.LifetimeStart = value; } } @@ -51,10 +48,8 @@ namespace osu.Game.Rulesets.Objects.Pooling if (Entry == null && LifetimeEnd != value) throw new InvalidOperationException($"Cannot modify lifetime of {nameof(PoolableDrawableWithLifetime)} when entry is not set"); - if (Entry == null) return; - - Entry.LifetimeEnd = value; - base.LifetimeEnd = Entry.LifetimeEnd; + if (Entry != null) + Entry.LifetimeEnd = value; } } From 240f7facba9c365d1d3850f314ce2c4e34a24254 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 15:39:02 +0900 Subject: [PATCH 618/839] Add local concessions for autoplay test handling --- osu.Game/Tests/Visual/TestPlayer.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/osu.Game/Tests/Visual/TestPlayer.cs b/osu.Game/Tests/Visual/TestPlayer.cs index 0addc9de75..99308f8d75 100644 --- a/osu.Game/Tests/Visual/TestPlayer.cs +++ b/osu.Game/Tests/Visual/TestPlayer.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Rulesets.Judgements; @@ -48,6 +49,20 @@ namespace osu.Game.Tests.Visual PauseOnFocusLost = pauseOnFocusLost; } + protected override void PrepareReplay() + { + var replayGeneratingMod = Mods.Value.OfType().FirstOrDefault(); + + if (replayGeneratingMod != null) + { + // This logic should really not exist (and tests should be instantiating a ReplayPlayer), but a lot of base work is required to make that happen. + DrawableRuleset?.SetReplayScore(replayGeneratingMod.CreateReplayScore(GameplayBeatmap.PlayableBeatmap, Mods.Value)); + return; + } + + base.PrepareReplay(); + } + [BackgroundDependencyLoader] private void load() { From 3ba0d29108fc250e9c29ea37ec14a9a900741ebf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 15:44:24 +0900 Subject: [PATCH 619/839] Fix incorrect beatmap being parsed down for autoplay generation --- osu.Game/Screens/Play/ReplayPlayer.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs index f26675cc64..91236ad607 100644 --- a/osu.Game/Screens/Play/ReplayPlayer.cs +++ b/osu.Game/Screens/Play/ReplayPlayer.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Input.Bindings; +using osu.Game.Beatmaps; using osu.Game.Input.Bindings; using osu.Game.Rulesets.Mods; using osu.Game.Scoring; @@ -17,7 +18,7 @@ namespace osu.Game.Screens.Play { protected Score Score { get; private set; } - private readonly Func, Score> createScore; + private readonly Func, Score> createScore; // Disallow replays from failing. (see https://github.com/ppy/osu/issues/6108) protected override bool CheckModsAllowFailure() => false; @@ -27,7 +28,7 @@ namespace osu.Game.Screens.Play { } - public ReplayPlayer(Func, Score> createScore, PlayerConfiguration configuration = null) + public ReplayPlayer(Func, Score> createScore, PlayerConfiguration configuration = null) : base(configuration) { this.createScore = createScore; @@ -38,7 +39,7 @@ namespace osu.Game.Screens.Play { if (!LoadedBeatmapSuccessfully) return; - Score = createScore(GameplayBeatmap, Mods.Value); + Score = createScore(GameplayBeatmap.PlayableBeatmap, Mods.Value); } protected override void PrepareReplay() From cd8e3f3a0430402c4bbd9bdb93782a04aa4626ab Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 1 Jun 2021 09:57:43 +0300 Subject: [PATCH 620/839] Revert "Group all skinnable test scenes to one `TestSceneSkinnableHUDComponents`" This reverts commit d1272d5e13b22f4118aa7a5820bc72cc319b7312. --- .../Visual/Gameplay/TestSceneComboCounter.cs | 35 +++++++++ .../TestSceneSkinnableAccuracyCounter.cs | 36 +++++++++ .../TestSceneSkinnableHUDComponents.cs | 76 ------------------- .../TestSceneSkinnableHealthDisplay.cs | 57 ++++++++++++++ .../TestSceneSkinnableScoreCounter.cs | 41 ++++++++++ 5 files changed, 169 insertions(+), 76 deletions(-) create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneComboCounter.cs create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableAccuracyCounter.cs delete mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDComponents.cs create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableScoreCounter.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneComboCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneComboCounter.cs new file mode 100644 index 0000000000..b22af0f7ac --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneComboCounter.cs @@ -0,0 +1,35 @@ +// 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.Framework.Allocation; +using osu.Framework.Testing; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Scoring; +using osu.Game.Skinning; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public class TestSceneComboCounter : SkinnableTestScene + { + protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); + + [Cached] + private ScoreProcessor scoreProcessor = new ScoreProcessor(); + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("Create combo counters", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.ComboCounter)))); + } + + [Test] + public void TestComboCounterIncrementing() + { + AddRepeatStep("increase combo", () => scoreProcessor.Combo.Value++, 10); + + AddStep("reset combo", () => scoreProcessor.Combo.Value = 0); + } + } +} diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableAccuracyCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableAccuracyCounter.cs new file mode 100644 index 0000000000..6f4e6a2420 --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableAccuracyCounter.cs @@ -0,0 +1,36 @@ +// 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.Framework.Allocation; +using osu.Framework.Testing; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Scoring; +using osu.Game.Skinning; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public class TestSceneSkinnableAccuracyCounter : SkinnableTestScene + { + protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); + + [Cached] + private ScoreProcessor scoreProcessor = new ScoreProcessor(); + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("Set initial accuracy", () => scoreProcessor.Accuracy.Value = 1); + AddStep("Create accuracy counters", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.AccuracyCounter)))); + } + + [Test] + public void TestChangingAccuracy() + { + AddStep(@"Reset all", () => scoreProcessor.Accuracy.Value = 1); + + AddStep(@"Miss :(", () => scoreProcessor.Accuracy.Value -= 0.023); + } + } +} diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDComponents.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDComponents.cs deleted file mode 100644 index 1c2f572d9e..0000000000 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDComponents.cs +++ /dev/null @@ -1,76 +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 NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Osu; -using osu.Game.Rulesets.Osu.Judgements; -using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Scoring; -using osu.Game.Skinning; - -namespace osu.Game.Tests.Visual.Gameplay -{ - public class TestSceneSkinnableHUDComponents : SkinnableTestScene - { - protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); - - [Cached] - private ScoreProcessor scoreProcessor = new ScoreProcessor(); - - [Cached(typeof(HealthProcessor))] - private HealthProcessor healthProcessor = new DrainingHealthProcessor(0); - - [BackgroundDependencyLoader] - private void load() - { - SetContents(() => new SkinnableTargetContainer(SkinnableTarget.MainHUDComponents) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - }); - } - - [Test] - public void TestScoreCounter() - { - AddStep(@"reset total score", () => scoreProcessor.TotalScore.Value = 0); - AddStep(@"increment total score", () => scoreProcessor.TotalScore.Value += 300); - AddStep(@"set large score", () => scoreProcessor.TotalScore.Value = 1_000_000_000); - } - - [Test] - public void TestComboCounter() - { - AddStep(@"reset combo", () => scoreProcessor.Combo.Value = 0); - AddRepeatStep(@"increase combo", () => scoreProcessor.Combo.Value++, 10); - } - - [Test] - public void TestAccuracyCounter() - { - AddStep(@"reset accuracy", () => scoreProcessor.Accuracy.Value = 1); - AddStep(@"decrease accuracy", () => scoreProcessor.Accuracy.Value -= 0.023); - } - - [Test] - public void TestHealthDisplay() - { - AddStep(@"reset health", () => healthProcessor.Health.Value = 1); - AddRepeatStep(@"decrease hp", () => healthProcessor.Health.Value -= 0.08f, 10); - AddRepeatStep(@"decrease hp without flash", () => healthProcessor.Health.Value += 0.1f, 3); - AddRepeatStep(@"increase hp with flash", () => - { - healthProcessor.Health.Value += 0.1f; - healthProcessor.ApplyResult(new JudgementResult(new HitCircle(), new OsuJudgement()) - { - Type = HitResult.Perfect - }); - }, 3); - } - } -} diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs new file mode 100644 index 0000000000..ead27bf017 --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.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 NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Testing; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Judgements; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Scoring; +using osu.Game.Skinning; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public class TestSceneSkinnableHealthDisplay : SkinnableTestScene + { + protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); + + [Cached(typeof(HealthProcessor))] + private HealthProcessor healthProcessor = new DrainingHealthProcessor(0); + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("Create health displays", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.HealthDisplay)))); + AddStep(@"Reset all", delegate + { + healthProcessor.Health.Value = 1; + }); + } + + [Test] + public void TestHealthDisplayIncrementing() + { + AddRepeatStep(@"decrease hp", delegate + { + healthProcessor.Health.Value -= 0.08f; + }, 10); + + AddRepeatStep(@"increase hp without flash", delegate + { + healthProcessor.Health.Value += 0.1f; + }, 3); + + AddRepeatStep(@"increase hp with flash", delegate + { + healthProcessor.Health.Value += 0.1f; + healthProcessor.ApplyResult(new JudgementResult(new HitCircle(), new OsuJudgement()) + { + Type = HitResult.Perfect + }); + }, 3); + } + } +} diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableScoreCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableScoreCounter.cs new file mode 100644 index 0000000000..8d633c3ca2 --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableScoreCounter.cs @@ -0,0 +1,41 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Testing; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Scoring; +using osu.Game.Skinning; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public class TestSceneSkinnableScoreCounter : SkinnableTestScene + { + protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); + + [Cached] + private ScoreProcessor scoreProcessor = new ScoreProcessor(); + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("Create score counters", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.ScoreCounter)))); + } + + [Test] + public void TestScoreCounterIncrementing() + { + AddStep(@"Reset all", () => scoreProcessor.TotalScore.Value = 0); + + AddStep(@"Hit! :D", () => scoreProcessor.TotalScore.Value += 300); + } + + [Test] + public void TestVeryLargeScore() + { + AddStep("set large score", () => scoreProcessor.TotalScore.Value = 1_000_000_000); + } + } +} From 1babb05fc7ee51aef25d53468ae7b2d1129a485e Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 1 Jun 2021 14:03:05 +0700 Subject: [PATCH 621/839] add OsuMarkdownInlineCode --- .../Markdown/OsuMarkdownTextFlowContainer.cs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs index c3527fa99a..267a5befb5 100644 --- a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs +++ b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs @@ -4,7 +4,9 @@ using Markdig.Syntax.Inlines; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers.Markdown; +using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Game.Overlays; @@ -24,5 +26,32 @@ namespace osu.Game.Graphics.Containers.Markdown protected override SpriteText CreateEmphasisedSpriteText(bool bold, bool italic) => CreateSpriteText().With(t => t.Font = t.Font.With(weight: bold ? FontWeight.Bold : FontWeight.Regular, italics: italic)); + + private class OsuMarkdownInlineCode : Container + { + [Resolved] + private IMarkdownTextComponent parentTextComponent { get; set; } + + public string Text; + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + AutoSizeAxes = Axes.Both; + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background6, + }, + parentTextComponent.CreateSpriteText().With(t => + { + t.Colour = colourProvider.Light1; + t.Text = Text; + }), + }; + } + } } } From 5108dadfbc0d7fb8aeb0139a993bd40bf05f7ff2 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 1 Jun 2021 14:03:57 +0700 Subject: [PATCH 622/839] use inline code in markdown text flow --- .../Markdown/OsuMarkdownTextFlowContainer.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs index 267a5befb5..b9c2b20bb6 100644 --- a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs +++ b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs @@ -14,15 +14,14 @@ namespace osu.Game.Graphics.Containers.Markdown { public class OsuMarkdownTextFlowContainer : MarkdownTextFlowContainer { - [Resolved] - private OverlayColourProvider colourProvider { get; set; } - protected override void AddLinkText(string text, LinkInline linkInline) => AddDrawable(new OsuMarkdownLinkText(text, linkInline)); - // TODO : Add background (colour B6) and change font to monospace - protected override void AddCodeInLine(CodeInline codeInline) - => AddText(codeInline.Content, t => { t.Colour = colourProvider.Light1; }); + // TODO : Change font to monospace + protected override void AddCodeInLine(CodeInline codeInline) => AddDrawable(new OsuMarkdownInlineCode + { + Text = codeInline.Content + }); protected override SpriteText CreateEmphasisedSpriteText(bool bold, bool italic) => CreateSpriteText().With(t => t.Font = t.Font.With(weight: bold ? FontWeight.Bold : FontWeight.Regular, italics: italic)); From b4dd93553892db0e43c52a6c3b7235f2aba8c41c Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 1 Jun 2021 14:14:12 +0700 Subject: [PATCH 623/839] add corner radius and padding --- .../Containers/Markdown/OsuMarkdownTextFlowContainer.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs index b9c2b20bb6..f3308019ce 100644 --- a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs +++ b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs @@ -37,6 +37,8 @@ namespace osu.Game.Graphics.Containers.Markdown private void load(OverlayColourProvider colourProvider) { AutoSizeAxes = Axes.Both; + CornerRadius = 4; + Masking = true; Children = new Drawable[] { new Box @@ -48,6 +50,11 @@ namespace osu.Game.Graphics.Containers.Markdown { t.Colour = colourProvider.Light1; t.Text = Text; + t.Padding = new MarginPadding + { + Vertical = 1, + Horizontal = 4, + }; }), }; } From c090110ae2c4d828311728b25592861afce8bffc Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 1 Jun 2021 10:13:56 +0300 Subject: [PATCH 624/839] Provide cell skin on content creation --- osu.Game/Tests/Visual/SkinnableTestScene.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Tests/Visual/SkinnableTestScene.cs b/osu.Game/Tests/Visual/SkinnableTestScene.cs index 3d2c68c2ad..b1287fd012 100644 --- a/osu.Game/Tests/Visual/SkinnableTestScene.cs +++ b/osu.Game/Tests/Visual/SkinnableTestScene.cs @@ -52,7 +52,9 @@ namespace osu.Game.Tests.Visual private readonly List createdDrawables = new List(); - public void SetContents(Func creationFunction) + public void SetContents(Func creationFunction) => SetContents(_ => creationFunction?.Invoke()); + + public void SetContents(Func creationFunction) { createdDrawables.Clear(); @@ -67,9 +69,9 @@ namespace osu.Game.Tests.Visual protected IEnumerable CreatedDrawables => createdDrawables; - private Drawable createProvider(Skin skin, Func creationFunction, IBeatmap beatmap) + private Drawable createProvider(Skin skin, Func creationFunction, IBeatmap beatmap) { - var created = creationFunction(); + var created = creationFunction(skin); createdDrawables.Add(created); From cb38abab3566d304a239826a7feb276f500a087b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 1 Jun 2021 10:16:01 +0300 Subject: [PATCH 625/839] Add local logic for creating default/legacy implementation based on cell skin --- .../SkinnableHUDComponentTestScene.cs | 33 +++++++++++++++++++ .../TestSceneSkinnableAccuracyCounter.cs | 12 +++---- ...r.cs => TestSceneSkinnableComboCounter.cs} | 17 +++------- .../TestSceneSkinnableHealthDisplay.cs | 12 +++---- .../TestSceneSkinnableScoreCounter.cs | 16 +++------ 5 files changed, 55 insertions(+), 35 deletions(-) create mode 100644 osu.Game.Tests/Visual/Gameplay/SkinnableHUDComponentTestScene.cs rename osu.Game.Tests/Visual/Gameplay/{TestSceneComboCounter.cs => TestSceneSkinnableComboCounter.cs} (56%) diff --git a/osu.Game.Tests/Visual/Gameplay/SkinnableHUDComponentTestScene.cs b/osu.Game.Tests/Visual/Gameplay/SkinnableHUDComponentTestScene.cs new file mode 100644 index 0000000000..6de9d7c478 --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/SkinnableHUDComponentTestScene.cs @@ -0,0 +1,33 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Osu; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public abstract class SkinnableHUDComponentTestScene : SkinnableTestScene + { + protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); + + [SetUp] + public void SetUp() => Schedule(() => + { + SetContents(skin => + { + var implementation = skin != null + ? CreateLegacyImplementation() + : CreateDefaultImplementation(); + + implementation.Anchor = Anchor.Centre; + implementation.Origin = Anchor.Centre; + return implementation; + }); + }); + + protected abstract Drawable CreateDefaultImplementation(); + protected abstract Drawable CreateLegacyImplementation(); + } +} diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableAccuracyCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableAccuracyCounter.cs index 6f4e6a2420..80eb887894 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableAccuracyCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableAccuracyCounter.cs @@ -3,26 +3,26 @@ using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Framework.Testing; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Scoring; +using osu.Game.Screens.Play.HUD; using osu.Game.Skinning; namespace osu.Game.Tests.Visual.Gameplay { - public class TestSceneSkinnableAccuracyCounter : SkinnableTestScene + public class TestSceneSkinnableAccuracyCounter : SkinnableHUDComponentTestScene { - protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); - [Cached] private ScoreProcessor scoreProcessor = new ScoreProcessor(); + protected override Drawable CreateDefaultImplementation() => new DefaultAccuracyCounter(); + protected override Drawable CreateLegacyImplementation() => new LegacyAccuracyCounter(); + [SetUpSteps] public void SetUpSteps() { AddStep("Set initial accuracy", () => scoreProcessor.Accuracy.Value = 1); - AddStep("Create accuracy counters", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.AccuracyCounter)))); } [Test] diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneComboCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableComboCounter.cs similarity index 56% rename from osu.Game.Tests/Visual/Gameplay/TestSceneComboCounter.cs rename to osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableComboCounter.cs index b22af0f7ac..1c5a05dd1d 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneComboCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableComboCounter.cs @@ -3,26 +3,19 @@ using NUnit.Framework; using osu.Framework.Allocation; -using osu.Framework.Testing; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Osu; +using osu.Framework.Graphics; using osu.Game.Rulesets.Scoring; -using osu.Game.Skinning; +using osu.Game.Screens.Play.HUD; namespace osu.Game.Tests.Visual.Gameplay { - public class TestSceneComboCounter : SkinnableTestScene + public class TestSceneSkinnableComboCounter : SkinnableHUDComponentTestScene { - protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); - [Cached] private ScoreProcessor scoreProcessor = new ScoreProcessor(); - [SetUpSteps] - public void SetUpSteps() - { - AddStep("Create combo counters", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.ComboCounter)))); - } + protected override Drawable CreateDefaultImplementation() => new DefaultComboCounter(); + protected override Drawable CreateLegacyImplementation() => new LegacyComboCounter(); [Test] public void TestComboCounterIncrementing() diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs index ead27bf017..057798c922 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs @@ -3,28 +3,28 @@ using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Framework.Testing; -using osu.Game.Rulesets; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Scoring; +using osu.Game.Screens.Play.HUD; using osu.Game.Skinning; namespace osu.Game.Tests.Visual.Gameplay { - public class TestSceneSkinnableHealthDisplay : SkinnableTestScene + public class TestSceneSkinnableHealthDisplay : SkinnableHUDComponentTestScene { - protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); - [Cached(typeof(HealthProcessor))] private HealthProcessor healthProcessor = new DrainingHealthProcessor(0); + protected override Drawable CreateDefaultImplementation() => new DefaultHealthDisplay(); + protected override Drawable CreateLegacyImplementation() => new LegacyHealthDisplay(); + [SetUpSteps] public void SetUpSteps() { - AddStep("Create health displays", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.HealthDisplay)))); AddStep(@"Reset all", delegate { healthProcessor.Health.Value = 1; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableScoreCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableScoreCounter.cs index 8d633c3ca2..1700886263 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableScoreCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableScoreCounter.cs @@ -3,26 +3,20 @@ using NUnit.Framework; using osu.Framework.Allocation; -using osu.Framework.Testing; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Osu; +using osu.Framework.Graphics; using osu.Game.Rulesets.Scoring; +using osu.Game.Screens.Play.HUD; using osu.Game.Skinning; namespace osu.Game.Tests.Visual.Gameplay { - public class TestSceneSkinnableScoreCounter : SkinnableTestScene + public class TestSceneSkinnableScoreCounter : SkinnableHUDComponentTestScene { - protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); - [Cached] private ScoreProcessor scoreProcessor = new ScoreProcessor(); - [SetUpSteps] - public void SetUpSteps() - { - AddStep("Create score counters", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.ScoreCounter)))); - } + protected override Drawable CreateDefaultImplementation() => new DefaultScoreCounter(); + protected override Drawable CreateLegacyImplementation() => new LegacyScoreCounter(); [Test] public void TestScoreCounterIncrementing() From 790f1dacc9839d0267797821bb59042c884b549d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 16:24:38 +0900 Subject: [PATCH 626/839] Ensure `ScoreProcessor` is still hooked up in special case --- osu.Game/Tests/Visual/TestPlayer.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/osu.Game/Tests/Visual/TestPlayer.cs b/osu.Game/Tests/Visual/TestPlayer.cs index 99308f8d75..b7e1c68c89 100644 --- a/osu.Game/Tests/Visual/TestPlayer.cs +++ b/osu.Game/Tests/Visual/TestPlayer.cs @@ -51,12 +51,16 @@ namespace osu.Game.Tests.Visual protected override void PrepareReplay() { - var replayGeneratingMod = Mods.Value.OfType().FirstOrDefault(); + var autoplayMod = Mods.Value.OfType().FirstOrDefault(); - if (replayGeneratingMod != null) + // This logic should really not exist (and tests should be instantiating a ReplayPlayer), but a lot of base work is required to make that happen. + if (autoplayMod != null) { - // This logic should really not exist (and tests should be instantiating a ReplayPlayer), but a lot of base work is required to make that happen. - DrawableRuleset?.SetReplayScore(replayGeneratingMod.CreateReplayScore(GameplayBeatmap.PlayableBeatmap, Mods.Value)); + var replayScore = autoplayMod.CreateReplayScore(GameplayBeatmap.PlayableBeatmap, Mods.Value); + + DrawableRuleset?.SetReplayScore(replayScore); + + ScoreProcessor.NewJudgement += result => ScoreProcessor.PopulateScore(replayScore.ScoreInfo); return; } From 6e861a9b7fcb107e2f12887ee3f4b93e253beedd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 16:24:38 +0900 Subject: [PATCH 627/839] Revert incorrect `ScoreProcessor` change --- osu.Game/Tests/Visual/TestPlayer.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Tests/Visual/TestPlayer.cs b/osu.Game/Tests/Visual/TestPlayer.cs index b7e1c68c89..ceb886f9c4 100644 --- a/osu.Game/Tests/Visual/TestPlayer.cs +++ b/osu.Game/Tests/Visual/TestPlayer.cs @@ -56,11 +56,7 @@ namespace osu.Game.Tests.Visual // This logic should really not exist (and tests should be instantiating a ReplayPlayer), but a lot of base work is required to make that happen. if (autoplayMod != null) { - var replayScore = autoplayMod.CreateReplayScore(GameplayBeatmap.PlayableBeatmap, Mods.Value); - - DrawableRuleset?.SetReplayScore(replayScore); - - ScoreProcessor.NewJudgement += result => ScoreProcessor.PopulateScore(replayScore.ScoreInfo); + DrawableRuleset?.SetReplayScore(autoplayMod.CreateReplayScore(GameplayBeatmap.PlayableBeatmap, Mods.Value)); return; } From 145e42928b08e22902fe74c9e8559e82a39c649f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 16:46:29 +0900 Subject: [PATCH 628/839] Fix remaining null checks --- osu.Game/Graphics/Containers/LinkFlowContainer.cs | 4 ++-- osu.Game/OsuGame.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs index 054febeec3..f89d7fd779 100644 --- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs +++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs @@ -43,7 +43,7 @@ namespace osu.Game.Graphics.Containers AddText(text[previousLinkEnd..link.Index]); string displayText = text.Substring(link.Index, link.Length); - string linkArgument = link.Argument ?? link.Url; + string linkArgument = link.Argument; string tooltip = displayText == link.Url ? null : link.Url; AddLink(displayText, link.Action, linkArgument, tooltip); @@ -57,7 +57,7 @@ namespace osu.Game.Graphics.Containers createLink(AddText(text, creationParameters), new LinkDetails(LinkAction.External, url), url); public void AddLink(string text, Action action, string tooltipText = null, Action creationParameters = null) - => createLink(AddText(text, creationParameters), new LinkDetails(LinkAction.Custom, null), tooltipText, action); + => createLink(AddText(text, creationParameters), new LinkDetails(LinkAction.Custom, @"action"), tooltipText, action); public void AddLink(string text, LinkAction action, string argument, string tooltipText = null, Action creationParameters = null) => createLink(AddText(text, creationParameters), new LinkDetails(action, argument), tooltipText); diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index b3b0773eff..c51624341e 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -277,7 +277,7 @@ namespace osu.Game { case LinkAction.OpenBeatmap: // TODO: proper query params handling - if (link.Argument != null && int.TryParse(link.Argument.Contains('?') ? link.Argument.Split('?')[0] : link.Argument, out int beatmapId)) + if (int.TryParse(link.Argument.Contains('?') ? link.Argument.Split('?')[0] : link.Argument, out int beatmapId)) ShowBeatmap(beatmapId); break; From 7a71cc1e821e0346e1d9c19fc4506388928b9ee3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 16:54:29 +0900 Subject: [PATCH 629/839] Fix actually incorrect navigation test (can no longer retry from autoplay results) --- osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index 253e448bb4..dd05ce9b7e 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -95,7 +95,7 @@ namespace osu.Game.Tests.Visual.Navigation AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault); - AddStep("set autoplay", () => Game.SelectedMods.Value = new[] { new OsuModAutoplay() }); + AddStep("set nofail", () => Game.SelectedMods.Value = new[] { new OsuModNoFail() }); AddStep("press enter", () => InputManager.Key(Key.Enter)); AddUntilStep("wait for player", () => (player = Game.ScreenStack.CurrentScreen as Player) != null); From ff815cb4b4f461f55d6154682ccd5ce0213ce80d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 16:57:40 +0900 Subject: [PATCH 630/839] Fix incorrect xmldoc --- osu.Game/Skinning/ISkin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/ISkin.cs b/osu.Game/Skinning/ISkin.cs index 1c3598abb4..09e79a5ff5 100644 --- a/osu.Game/Skinning/ISkin.cs +++ b/osu.Game/Skinning/ISkin.cs @@ -60,7 +60,7 @@ namespace osu.Game.Skinning IBindable GetConfig(TLookup lookup); /// - /// For the specified texture, find any potential skin that can fulfill the lookup. + /// Find the first (if any) skin that can fulfill the lookup. /// This should be used for cases where subsequent lookups (for related components) need to occur on the same skin. /// /// The skin to be used for subsequent lookups, or null if none is available. From 3668f1861f3369a3819da6d44dd52c648f54c494 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 17:10:09 +0900 Subject: [PATCH 631/839] Fix one more null issue --- osu.Game/Graphics/Containers/LinkFlowContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs index f89d7fd779..458bc8876f 100644 --- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs +++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs @@ -70,7 +70,7 @@ namespace osu.Game.Graphics.Containers createLink(spriteText.Yield(), new LinkDetails(action, argument), tooltipText); } - public void AddLink(IEnumerable text, LinkAction action = LinkAction.External, string linkArgument = null, string tooltipText = null) + public void AddLink(IEnumerable text, LinkAction action, string linkArgument, string tooltipText = null) { foreach (var t in text) AddArbitraryDrawable(t); From df0a5689e462c31046a917ac3f44e57505becdc1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 17:13:13 +0900 Subject: [PATCH 632/839] Revert "Fix weird taiko logic failing for weird reasons that probably should not have been a thing" This reverts commit 69c4ccad05297772db8bc7cabc5dae759f789d92. --- osu.Game/Skinning/LegacySkin.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index f3f3e67eef..92944a9c93 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -563,11 +563,7 @@ namespace osu.Game.Skinning // Fall back to using the last piece for components coming from lazer (e.g. "Gameplay/osu/approachcircle" -> "approachcircle"). string lastPiece = componentName.Split('/').Last(); - - if (componentName.StartsWith("Gameplay/taiko/", StringComparison.Ordinal)) - yield return "taiko-" + lastPiece; - - yield return lastPiece; + yield return componentName.StartsWith("Gameplay/taiko/", StringComparison.Ordinal) ? "taiko-" + lastPiece : lastPiece; } protected override void Dispose(bool isDisposing) From f14c0eae9956641f8c77179f165b059abc98914d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 17:28:41 +0900 Subject: [PATCH 633/839] Fix editor no longer creating autoplay frames --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index a2dade2627..0667145ffb 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -349,6 +349,9 @@ namespace osu.Game.Rulesets.UI foreach (var mod in mods.OfType>()) mod.ApplyToDrawableRuleset(this); + foreach (var mod in mods.OfType()) + SetReplayScore(mod.CreateReplayScore(Beatmap, mods)); + foreach (var mod in mods.OfType()) mod.ReadFromConfig(config); } From 83bfd36498dcb30d2ad8e22184ab56504a9968d4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 17:56:12 +0900 Subject: [PATCH 634/839] Disable broken taiko hitsound fallback tests for now --- .../TestSceneTaikoHitObjectSamples.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectSamples.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectSamples.cs index 221d715a35..7318a6d929 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectSamples.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectSamples.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Taiko.Tests [TestCase("taiko-normal-hitnormal")] [TestCase("normal-hitnormal")] - [TestCase("hitnormal")] + // [TestCase("hitnormal")] intentionally broken (will play classic default instead). public void TestDefaultCustomSampleFromBeatmap(string expectedSample) { SetupSkins(expectedSample, expectedSample); @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Taiko.Tests [TestCase("taiko-normal-hitnormal")] [TestCase("normal-hitnormal")] - [TestCase("hitnormal")] + // [TestCase("hitnormal")] intentionally broken (will play classic default instead). public void TestDefaultCustomSampleFromUserSkinFallback(string expectedSample) { SetupSkins(string.Empty, expectedSample); From a837fc9e3ba99c315e3fd29c13692c7c159965c9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 18:00:24 +0900 Subject: [PATCH 635/839] Remove duplicated taiko fallback --- osu.Game/Skinning/LegacySkin.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 92944a9c93..98cc5c8fd8 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -562,8 +562,7 @@ namespace osu.Game.Skinning yield return componentName; // Fall back to using the last piece for components coming from lazer (e.g. "Gameplay/osu/approachcircle" -> "approachcircle"). - string lastPiece = componentName.Split('/').Last(); - yield return componentName.StartsWith("Gameplay/taiko/", StringComparison.Ordinal) ? "taiko-" + lastPiece : lastPiece; + yield return componentName.Split('/').Last(); } protected override void Dispose(bool isDisposing) From ea4644be905f602ae2dd9ed068808f568b900363 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 17:13:13 +0900 Subject: [PATCH 636/839] Revert "Fix weird taiko logic failing for weird reasons that probably should not have been a thing" This reverts commit 69c4ccad05297772db8bc7cabc5dae759f789d92. --- osu.Game/Skinning/LegacySkin.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index f3f3e67eef..92944a9c93 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -563,11 +563,7 @@ namespace osu.Game.Skinning // Fall back to using the last piece for components coming from lazer (e.g. "Gameplay/osu/approachcircle" -> "approachcircle"). string lastPiece = componentName.Split('/').Last(); - - if (componentName.StartsWith("Gameplay/taiko/", StringComparison.Ordinal)) - yield return "taiko-" + lastPiece; - - yield return lastPiece; + yield return componentName.StartsWith("Gameplay/taiko/", StringComparison.Ordinal) ? "taiko-" + lastPiece : lastPiece; } protected override void Dispose(bool isDisposing) From dd006401b3d1bcbe41f56b907178ba4d42a691ae Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 17:56:12 +0900 Subject: [PATCH 637/839] Disable broken taiko hitsound fallback tests for now --- .../TestSceneTaikoHitObjectSamples.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectSamples.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectSamples.cs index 221d715a35..7318a6d929 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectSamples.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectSamples.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Taiko.Tests [TestCase("taiko-normal-hitnormal")] [TestCase("normal-hitnormal")] - [TestCase("hitnormal")] + // [TestCase("hitnormal")] intentionally broken (will play classic default instead). public void TestDefaultCustomSampleFromBeatmap(string expectedSample) { SetupSkins(expectedSample, expectedSample); @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Taiko.Tests [TestCase("taiko-normal-hitnormal")] [TestCase("normal-hitnormal")] - [TestCase("hitnormal")] + // [TestCase("hitnormal")] intentionally broken (will play classic default instead). public void TestDefaultCustomSampleFromUserSkinFallback(string expectedSample) { SetupSkins(string.Empty, expectedSample); From 3a6d081d82c20fc587a529783c1495b662d2d8c0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 18:00:24 +0900 Subject: [PATCH 638/839] Remove duplicated taiko fallback --- osu.Game/Skinning/LegacySkin.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 92944a9c93..98cc5c8fd8 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -562,8 +562,7 @@ namespace osu.Game.Skinning yield return componentName; // Fall back to using the last piece for components coming from lazer (e.g. "Gameplay/osu/approachcircle" -> "approachcircle"). - string lastPiece = componentName.Split('/').Last(); - yield return componentName.StartsWith("Gameplay/taiko/", StringComparison.Ordinal) ? "taiko-" + lastPiece : lastPiece; + yield return componentName.Split('/').Last(); } protected override void Dispose(bool isDisposing) From 54338bdcc5666a60395ee478a817d4aa145a7a18 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 18:29:15 +0900 Subject: [PATCH 639/839] Add test ensuring correct osu! ruleset sample lookups --- ...u-hitobject-beatmap-custom-sample-bank.osu | 10 ++++ .../TestSceneOsuHitObjectSamples.cs | 49 +++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/SampleLookups/osu-hitobject-beatmap-custom-sample-bank.osu create mode 100644 osu.Game.Rulesets.Osu.Tests/TestSceneOsuHitObjectSamples.cs diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/SampleLookups/osu-hitobject-beatmap-custom-sample-bank.osu b/osu.Game.Rulesets.Osu.Tests/Resources/SampleLookups/osu-hitobject-beatmap-custom-sample-bank.osu new file mode 100644 index 0000000000..a84fc08bb8 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/Resources/SampleLookups/osu-hitobject-beatmap-custom-sample-bank.osu @@ -0,0 +1,10 @@ +osu file format v14 + +[General] +Mode: 0 + +[TimingPoints] +0,300,4,1,2,100,1,0 + +[HitObjects] +444,320,1000,5,0,0:0:0:0: diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuHitObjectSamples.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuHitObjectSamples.cs new file mode 100644 index 0000000000..e8d98ce3b8 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuHitObjectSamples.cs @@ -0,0 +1,49 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Reflection; +using NUnit.Framework; +using osu.Framework.IO.Stores; +using osu.Game.Tests.Beatmaps; + +namespace osu.Game.Rulesets.Osu.Tests +{ + public class TestSceneOsuHitObjectSamples : HitObjectSampleTest + { + protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); + + protected override IResourceStore RulesetResources => new DllResourceStore(Assembly.GetAssembly(typeof(TestSceneOsuHitObjectSamples))); + + [TestCase("normal-hitnormal")] + [TestCase("hitnormal")] + public void TestDefaultCustomSampleFromBeatmap(string expectedSample) + { + SetupSkins(expectedSample, expectedSample); + + CreateTestWithBeatmap("osu-hitobject-beatmap-custom-sample-bank.osu"); + + AssertBeatmapLookup(expectedSample); + } + + [TestCase("normal-hitnormal")] + [TestCase("hitnormal")] + public void TestDefaultCustomSampleFromUserSkinFallback(string expectedSample) + { + SetupSkins(string.Empty, expectedSample); + + CreateTestWithBeatmap("osu-hitobject-beatmap-custom-sample-bank.osu"); + + AssertUserLookup(expectedSample); + } + + [TestCase("normal-hitnormal2")] + public void TestUserSkinLookupIgnoresSampleBank(string unwantedSample) + { + SetupSkins(string.Empty, unwantedSample); + + CreateTestWithBeatmap("osu-hitobject-beatmap-custom-sample-bank.osu"); + + AssertNoLookup(unwantedSample); + } + } +} From 2e2281c7d22345164d314464341138d3d544f250 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 18:56:22 +0900 Subject: [PATCH 640/839] Revert disabling taiko sample tests and fix logic --- .../TestSceneTaikoHitObjectSamples.cs | 4 ++-- .../Legacy/TaikoLegacySkinTransformer.cs | 23 +++++++++++-------- osu.Game/Skinning/LegacySkin.cs | 2 ++ 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectSamples.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectSamples.cs index 7318a6d929..221d715a35 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectSamples.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectSamples.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Taiko.Tests [TestCase("taiko-normal-hitnormal")] [TestCase("normal-hitnormal")] - // [TestCase("hitnormal")] intentionally broken (will play classic default instead). + [TestCase("hitnormal")] public void TestDefaultCustomSampleFromBeatmap(string expectedSample) { SetupSkins(expectedSample, expectedSample); @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Taiko.Tests [TestCase("taiko-normal-hitnormal")] [TestCase("normal-hitnormal")] - // [TestCase("hitnormal")] intentionally broken (will play classic default instead). + [TestCase("hitnormal")] public void TestDefaultCustomSampleFromUserSkinFallback(string expectedSample) { SetupSkins(string.Empty, expectedSample); diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs index e0557c8617..7ce0f6b93b 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs @@ -152,32 +152,35 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy throw new ArgumentOutOfRangeException(nameof(component), $"Invalid component type: {component}"); } - public override ISample GetSample(ISampleInfo sampleInfo) => Source.GetSample(new LegacyTaikoSampleInfo(sampleInfo)); + public override ISample GetSample(ISampleInfo sampleInfo) + { + if (sampleInfo is HitSampleInfo hitSampleInfo) + return Source.GetSample(new LegacyTaikoSampleInfo(hitSampleInfo)); + + return base.GetSample(sampleInfo); + } public override IBindable GetConfig(TLookup lookup) => Source.GetConfig(lookup); - private class LegacyTaikoSampleInfo : ISampleInfo + private class LegacyTaikoSampleInfo : HitSampleInfo { - private readonly ISampleInfo source; + public LegacyTaikoSampleInfo(HitSampleInfo sampleInfo) + : base(sampleInfo.Name, sampleInfo.Bank, sampleInfo.Suffix, sampleInfo.Volume) - public LegacyTaikoSampleInfo(ISampleInfo source) { - this.source = source; } - public IEnumerable LookupNames + public override IEnumerable LookupNames { get { - foreach (var name in source.LookupNames) + foreach (var name in base.LookupNames) yield return name.Insert(name.LastIndexOf('/') + 1, "taiko-"); - foreach (var name in source.LookupNames) + foreach (var name in base.LookupNames) yield return name; } } - - public int Volume => source.Volume; } } } diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 98cc5c8fd8..474ac1a794 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -519,7 +519,9 @@ namespace osu.Game.Skinning var sample = Samples?.Get(lookup); if (sample != null) + { return sample; + } } return legacyDefaultFallback?.GetSample(sampleInfo); From 92ffc4627f8bf90f3320dd71a6b20495e071ba1c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 23:24:49 +0900 Subject: [PATCH 641/839] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 3d51357d8b..e95c7e6619 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index d299ba4fda..c9bfb4a8eb 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -34,7 +34,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 9e178b267a..70bec9b894 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 97d2ac19cfe679a27368b90d9e3510a214af01f0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Jun 2021 19:59:09 +0000 Subject: [PATCH 642/839] Bump BenchmarkDotNet from 0.12.1 to 0.13.0 Bumps [BenchmarkDotNet](https://github.com/dotnet/BenchmarkDotNet) from 0.12.1 to 0.13.0. - [Release notes](https://github.com/dotnet/BenchmarkDotNet/releases) - [Commits](https://github.com/dotnet/BenchmarkDotNet/compare/v0.12.1...v0.13.0) --- updated-dependencies: - dependency-name: BenchmarkDotNet dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- osu.Game.Benchmarks/osu.Game.Benchmarks.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj b/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj index bfcf4ef35e..7a74563b2b 100644 --- a/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj +++ b/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj @@ -7,7 +7,7 @@ - + From 069baab8839c5da5f710f0d50e2cfc8eefaff870 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Jun 2021 19:59:27 +0000 Subject: [PATCH 643/839] Bump Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson Bumps [Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson](https://github.com/dotnet/aspnetcore) from 5.0.5 to 5.0.6. - [Release notes](https://github.com/dotnet/aspnetcore/releases) - [Commits](https://github.com/dotnet/aspnetcore/compare/v5.0.5...v5.0.6) --- updated-dependencies: - dependency-name: Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- 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 c9bfb4a8eb..169c28480f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -24,7 +24,7 @@ - + From ea9919e8fe023f4b90d101d69ddd1eff96b2992b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Jun 2021 19:59:27 +0000 Subject: [PATCH 644/839] Bump Microsoft.AspNetCore.SignalR.Client from 5.0.5 to 5.0.6 Bumps [Microsoft.AspNetCore.SignalR.Client](https://github.com/dotnet/aspnetcore) from 5.0.5 to 5.0.6. - [Release notes](https://github.com/dotnet/aspnetcore/releases) - [Commits](https://github.com/dotnet/aspnetcore/compare/v5.0.5...v5.0.6) --- updated-dependencies: - dependency-name: Microsoft.AspNetCore.SignalR.Client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- 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 c9bfb4a8eb..fb675da847 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -22,7 +22,7 @@ - + From 967a7c3db5918d10223728d0a5b646ab7b6c8392 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Jun 2021 19:59:27 +0000 Subject: [PATCH 645/839] Bump Microsoft.NET.Test.Sdk from 16.9.4 to 16.10.0 Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 16.9.4 to 16.10.0. - [Release notes](https://github.com/microsoft/vstest/releases) - [Commits](https://github.com/microsoft/vstest/compare/v16.9.4...v16.10.0) --- updated-dependencies: - dependency-name: Microsoft.NET.Test.Sdk dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .../osu.Game.Rulesets.EmptyFreeform.Tests.csproj | 2 +- .../osu.Game.Rulesets.Pippidon.Tests.csproj | 2 +- .../osu.Game.Rulesets.EmptyScrolling.Tests.csproj | 2 +- .../osu.Game.Rulesets.Pippidon.Tests.csproj | 2 +- .../osu.Game.Rulesets.Catch.Tests.csproj | 2 +- .../osu.Game.Rulesets.Mania.Tests.csproj | 2 +- osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj | 2 +- .../osu.Game.Rulesets.Taiko.Tests.csproj | 2 +- osu.Game.Tests/osu.Game.Tests.csproj | 2 +- osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj index 992f954a3a..5eb5efa54c 100644 --- a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj +++ b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj index 7571d1827a..d7c116411a 100644 --- a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj +++ b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj index 1c8ed54440..89b551286b 100644 --- a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj +++ b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj index 7571d1827a..d7c116411a 100644 --- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj +++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj index 77e9d672e3..83d0744588 100644 --- a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj +++ b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj @@ -2,7 +2,7 @@ - + diff --git a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj index 8f8b99b092..b2a0912d19 100644 --- a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj +++ b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj @@ -2,7 +2,7 @@ - + diff --git a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj index e01e858873..ebe642803b 100644 --- a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj +++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj @@ -2,7 +2,7 @@ - + diff --git a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj index 2dfa1dfbb7..8fb167ba10 100644 --- a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj +++ b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj @@ -2,7 +2,7 @@ - + diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 895518e1b9..35d3c7f202 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -3,7 +3,7 @@ - + diff --git a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj index d5dda39aa5..2084be765a 100644 --- a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj +++ b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj @@ -5,7 +5,7 @@ - + From bd8db2ce63869e243cccd2efae812891f075e020 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Jun 2021 19:59:28 +0000 Subject: [PATCH 646/839] Bump ppy.LocalisationAnalyser from 2021.524.0 to 2021.525.0 Bumps [ppy.LocalisationAnalyser](https://github.com/ppy/osu-localisation-analyser) from 2021.524.0 to 2021.525.0. - [Release notes](https://github.com/ppy/osu-localisation-analyser/releases) - [Commits](https://github.com/ppy/osu-localisation-analyser/compare/2021.524.0...2021.525.0) --- updated-dependencies: - dependency-name: ppy.LocalisationAnalyser dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- 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 c9bfb4a8eb..c8eea7c4f3 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -30,7 +30,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive From deaa381440f0f8e02dd3809da69efa4948975b50 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Jun 2021 19:59:30 +0000 Subject: [PATCH 647/839] Bump Sentry from 3.3.4 to 3.4.0 Bumps [Sentry](https://github.com/getsentry/sentry-dotnet) from 3.3.4 to 3.4.0. - [Release notes](https://github.com/getsentry/sentry-dotnet/releases) - [Changelog](https://github.com/getsentry/sentry-dotnet/blob/main/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-dotnet/compare/3.3.4...3.4.0) --- updated-dependencies: - dependency-name: Sentry dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- 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 c9bfb4a8eb..967f0af1df 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ - + From a10ef2ba6513cb617886270b2bd6792d7b06819a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Jun 2021 19:59:31 +0000 Subject: [PATCH 648/839] Bump Humanizer from 2.8.26 to 2.10.1 Bumps [Humanizer](https://github.com/Humanizr/Humanizer) from 2.8.26 to 2.10.1. - [Release notes](https://github.com/Humanizr/Humanizer/releases) - [Changelog](https://github.com/Humanizr/Humanizer/blob/main/release_notes.md) - [Commits](https://github.com/Humanizr/Humanizer/compare/v2.8.26...v2.10.1) --- updated-dependencies: - dependency-name: Humanizer dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index c9bfb4a8eb..28b77a32ec 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -20,7 +20,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 70bec9b894..c1d63f3236 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -89,7 +89,7 @@ - + From a758b382784ac346764d3b5d803044fc96cf2f78 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Jun 2021 19:59:32 +0000 Subject: [PATCH 649/839] Bump Microsoft.AspNetCore.SignalR.Protocols.MessagePack Bumps [Microsoft.AspNetCore.SignalR.Protocols.MessagePack](https://github.com/dotnet/aspnetcore) from 5.0.5 to 5.0.6. - [Release notes](https://github.com/dotnet/aspnetcore/releases) - [Commits](https://github.com/dotnet/aspnetcore/compare/v5.0.5...v5.0.6) --- updated-dependencies: - dependency-name: Microsoft.AspNetCore.SignalR.Protocols.MessagePack dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- 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 c9bfb4a8eb..12f8163bfa 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -23,7 +23,7 @@ - + From 8a76d97b63a65fe4e303525e4bd09d809cafa2f1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 2 Jun 2021 11:06:30 +0900 Subject: [PATCH 650/839] Remove replay logic from `DrawableRuleset` (and implement in `DrawableEditorRulesetWrapper`) --- .../Rulesets/Edit/DrawableEditorRulesetWrapper.cs | 14 +++++++++++--- osu.Game/Rulesets/UI/DrawableRuleset.cs | 12 +----------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/osu.Game/Rulesets/Edit/DrawableEditorRulesetWrapper.cs b/osu.Game/Rulesets/Edit/DrawableEditorRulesetWrapper.cs index 8166e6b8ce..071f01ca00 100644 --- a/osu.Game/Rulesets/Edit/DrawableEditorRulesetWrapper.cs +++ b/osu.Game/Rulesets/Edit/DrawableEditorRulesetWrapper.cs @@ -1,9 +1,11 @@ // 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.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.UI; using osu.Game.Screens.Edit; @@ -52,15 +54,21 @@ namespace osu.Game.Rulesets.Edit if (changeHandler != null) { // for now only regenerate replay on a finalised state change, not HitObjectUpdated. - changeHandler.OnStateChange += updateReplay; + changeHandler.OnStateChange += () => Scheduler.AddOnce(regenerateAutoplay); } else { - beatmap.HitObjectUpdated += _ => updateReplay(); + beatmap.HitObjectUpdated += _ => Scheduler.AddOnce(regenerateAutoplay); } + + Scheduler.AddOnce(regenerateAutoplay); } - private void updateReplay() => Scheduler.AddOnce(drawableRuleset.RegenerateAutoplay); + private void regenerateAutoplay() + { + var autoplayMod = drawableRuleset.Mods.OfType().Single(); + drawableRuleset.SetReplayScore(autoplayMod.CreateReplayScore(drawableRuleset.Beatmap, drawableRuleset.Mods)); + } private void addHitObject(HitObject hitObject) { diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 0667145ffb..16a411d478 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -182,18 +182,11 @@ namespace osu.Game.Rulesets.UI .WithChild(ResumeOverlay))); } - RegenerateAutoplay(); + applyRulesetMods(Mods, config); loadObjects(cancellationToken ?? default); } - public void RegenerateAutoplay() - { - // for now this is applying mods which aren't just autoplay. - // we'll need to reconsider this flow in the future. - applyRulesetMods(Mods, config); - } - /// /// Creates and adds drawable representations of hit objects to the play field. /// @@ -349,9 +342,6 @@ namespace osu.Game.Rulesets.UI foreach (var mod in mods.OfType>()) mod.ApplyToDrawableRuleset(this); - foreach (var mod in mods.OfType()) - SetReplayScore(mod.CreateReplayScore(Beatmap, mods)); - foreach (var mod in mods.OfType()) mod.ReadFromConfig(config); } From 911256603bf0ba6d83c05694c6d20ecdbafa041b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 2 Jun 2021 11:10:02 +0900 Subject: [PATCH 651/839] Rewrite comment to hopefully be more informative --- osu.Game/Tests/Visual/TestPlayer.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Tests/Visual/TestPlayer.cs b/osu.Game/Tests/Visual/TestPlayer.cs index ceb886f9c4..09da4db952 100644 --- a/osu.Game/Tests/Visual/TestPlayer.cs +++ b/osu.Game/Tests/Visual/TestPlayer.cs @@ -51,9 +51,15 @@ namespace osu.Game.Tests.Visual protected override void PrepareReplay() { + // Generally, replay generation is handled by whatever is constructing the player. + // This is implemented locally here to ease migration of test scenes that have some executions + // running with autoplay and some not, but are not written in a way that lends to instantiating + // different `Player` types. + // + // Eventually we will want to remove this and update all test usages which rely on autoplay to use + // a `TestReplayPlayer`. var autoplayMod = Mods.Value.OfType().FirstOrDefault(); - // This logic should really not exist (and tests should be instantiating a ReplayPlayer), but a lot of base work is required to make that happen. if (autoplayMod != null) { DrawableRuleset?.SetReplayScore(autoplayMod.CreateReplayScore(GameplayBeatmap.PlayableBeatmap, Mods.Value)); From d3d8941ec8559a173c23ff63da47554681124e31 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 2 Jun 2021 11:11:41 +0900 Subject: [PATCH 652/839] Make method internal --- osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs b/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs index 5ad0d6246e..096dad88bd 100644 --- a/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs +++ b/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs @@ -94,6 +94,6 @@ namespace osu.Game.Rulesets.Objects /// /// Set using . /// - public void SetInitialLifetime() => LifetimeStart = HitObject.StartTime - InitialLifetimeOffset; + internal void SetInitialLifetime() => LifetimeStart = HitObject.StartTime - InitialLifetimeOffset; } } From b82190e157deb303bf15ba69b56fb5a55ae4584d Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 2 Jun 2021 11:32:24 +0900 Subject: [PATCH 653/839] Fix hit circle animation reset when skin is changed The transforms applied in `Animate` call was not applied because the piece is recreated. --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs | 2 -- osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs | 4 ++++ .../Skinning/Legacy/LegacyMainCirclePiece.cs | 4 ++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index 1bf9e76d7d..236af4b3f1 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -182,8 +182,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables // todo: temporary / arbitrary, used for lifetime optimisation. this.Delay(800).FadeOut(); - (CirclePiece.Drawable as IMainCirclePiece)?.Animate(state); - switch (state) { case ArmedState.Idle: diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs index b46baa00ba..9af9d2ea95 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs @@ -42,6 +42,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default private readonly IBindable accentColour = new Bindable(); private readonly IBindable indexInCurrentCombo = new Bindable(); + private readonly IBindable armedState = new Bindable(); [Resolved] private DrawableHitObject drawableObject { get; set; } @@ -53,6 +54,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default accentColour.BindTo(drawableObject.AccentColour); indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable); + armedState.BindTo(drawableObject.State); } protected override void LoadComplete() @@ -67,6 +69,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default }, true); indexInCurrentCombo.BindValueChanged(index => number.Text = (index.NewValue + 1).ToString(), true); + + armedState.BindValueChanged(state => Animate(state.NewValue), true); } public void Animate(ArmedState state) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs index cf62165929..57af247562 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs @@ -41,6 +41,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy private readonly Bindable accentColour = new Bindable(); private readonly IBindable indexInCurrentCombo = new Bindable(); + private readonly IBindable armedState = new Bindable(); [Resolved] private DrawableHitObject drawableObject { get; set; } @@ -115,6 +116,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy accentColour.BindTo(drawableObject.AccentColour); indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable); + armedState.BindTo(drawableObject.State); Texture getTextureWithFallback(string name) { @@ -139,6 +141,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy accentColour.BindValueChanged(colour => hitCircleSprite.Colour = LegacyColourCompatibility.DisallowZeroAlpha(colour.NewValue), true); if (hasNumber) indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true); + + armedState.BindValueChanged(state => Animate(state.NewValue), true); } public void Animate(ArmedState state) From c06ff3c623321a15ad318fafb89b7d3d5c5632ce Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 2 Jun 2021 11:46:28 +0900 Subject: [PATCH 654/839] Remove defunct `Animate` calls --- .../Objects/Drawables/DrawableSliderRepeat.cs | 2 -- .../Objects/Drawables/DrawableSliderTail.cs | 2 -- .../Skinning/Default/IMainCirclePiece.cs | 17 ----------------- .../Skinning/Default/MainCirclePiece.cs | 12 +++++++----- .../Skinning/Legacy/LegacyMainCirclePiece.cs | 13 +++++++------ 5 files changed, 14 insertions(+), 32 deletions(-) delete mode 100644 osu.Game.Rulesets.Osu/Skinning/Default/IMainCirclePiece.cs diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs index 7b4188edab..b7458b5695 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs @@ -97,8 +97,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { base.UpdateHitStateTransforms(state); - (CirclePiece.Drawable as IMainCirclePiece)?.Animate(state); - switch (state) { case ArmedState.Idle: diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs index cd6bf1d8d2..ec1387eb54 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs @@ -87,8 +87,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Debug.Assert(HitObject.HitWindows != null); - (CirclePiece.Drawable as IMainCirclePiece)?.Animate(state); - switch (state) { case ArmedState.Idle: diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/IMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Default/IMainCirclePiece.cs deleted file mode 100644 index 17a1e29094..0000000000 --- a/osu.Game.Rulesets.Osu/Skinning/Default/IMainCirclePiece.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. - -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Osu.Objects.Drawables; - -namespace osu.Game.Rulesets.Osu.Skinning.Default -{ - public interface IMainCirclePiece - { - /// - /// Begins animating this . - /// - /// The of the related . - void Animate(ArmedState state); - } -} diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs index 9af9d2ea95..b52dc749f0 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs @@ -13,7 +13,7 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Skinning.Default { - public class MainCirclePiece : CompositeDrawable, IMainCirclePiece + public class MainCirclePiece : CompositeDrawable { private readonly CirclePiece circle; private readonly RingPiece ring; @@ -70,17 +70,19 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default indexInCurrentCombo.BindValueChanged(index => number.Text = (index.NewValue + 1).ToString(), true); - armedState.BindValueChanged(state => Animate(state.NewValue), true); + armedState.BindValueChanged(animate, true); } - public void Animate(ArmedState state) + private void animate(ValueChangedEvent state) { + ClearTransforms(true); + using (BeginAbsoluteSequence(drawableObject.StateUpdateTime)) glow.FadeOut(400); using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime)) { - switch (state) + switch (state.NewValue) { case ArmedState.Hit: const double flash_in = 40; @@ -93,7 +95,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default explode.FadeIn(flash_in); this.ScaleTo(1.5f, 400, Easing.OutQuad); - using (BeginDelayedSequence(flash_in, true)) + using (BeginDelayedSequence(flash_in)) { // after the flash, we can hide some elements that were behind it ring.FadeOut(); diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs index 57af247562..ffbeea5e0e 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs @@ -12,7 +12,6 @@ using osu.Game.Graphics.Sprites; 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.Skinning; using osuTK; using osuTK.Graphics; @@ -20,7 +19,7 @@ using static osu.Game.Skinning.LegacySkinConfiguration; namespace osu.Game.Rulesets.Osu.Skinning.Legacy { - public class LegacyMainCirclePiece : CompositeDrawable, IMainCirclePiece + public class LegacyMainCirclePiece : CompositeDrawable { private readonly string priorityLookup; private readonly bool hasNumber; @@ -142,16 +141,18 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy if (hasNumber) indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true); - armedState.BindValueChanged(state => Animate(state.NewValue), true); + armedState.BindValueChanged(animate, true); } - public void Animate(ArmedState state) + private void animate(ValueChangedEvent state) { const double legacy_fade_duration = 240; - using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime, true)) + ClearTransforms(true); + + using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime)) { - switch (state) + switch (state.NewValue) { case ArmedState.Hit: circleSprites.FadeOut(legacy_fade_duration, Easing.Out); From 45984f035b731ff0d39510b22df7375455fafc39 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 2 Jun 2021 11:33:43 +0900 Subject: [PATCH 655/839] Make autoplay tests test via the `ReplayPlayer` code path --- .../Visual/Gameplay/TestSceneAutoplay.cs | 4 +- osu.Game/Tests/Visual/TestReplayPlayer.cs | 75 +++++++++++++++++++ 2 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 osu.Game/Tests/Visual/TestReplayPlayer.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs index e47c782bca..bc7cf8eee2 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs @@ -18,12 +18,12 @@ namespace osu.Game.Tests.Visual.Gameplay [Description("Player instantiated with an autoplay mod.")] public class TestSceneAutoplay : TestSceneAllRulesetPlayers { - protected new TestPlayer Player => (TestPlayer)base.Player; + protected new TestReplayPlayer Player => (TestReplayPlayer)base.Player; protected override Player CreatePlayer(Ruleset ruleset) { SelectedMods.Value = new[] { ruleset.GetAutoplayMod() }; - return new TestPlayer(false); + return new TestReplayPlayer(false); } protected override void AddCheckSteps() diff --git a/osu.Game/Tests/Visual/TestReplayPlayer.cs b/osu.Game/Tests/Visual/TestReplayPlayer.cs new file mode 100644 index 0000000000..ac47d186eb --- /dev/null +++ b/osu.Game/Tests/Visual/TestReplayPlayer.cs @@ -0,0 +1,75 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; +using osu.Game.Scoring; +using osu.Game.Screens.Play; + +namespace osu.Game.Tests.Visual +{ + /// + /// A player that exposes many components that would otherwise not be available, for testing purposes. + /// + public class TestReplayPlayer : ReplayPlayer + { + protected override bool PauseOnFocusLost { get; } + + public new DrawableRuleset DrawableRuleset => base.DrawableRuleset; + + /// + /// Mods from *player* (not OsuScreen). + /// + public new Bindable> Mods => base.Mods; + + public new HUDOverlay HUDOverlay => base.HUDOverlay; + + public new GameplayClockContainer GameplayClockContainer => base.GameplayClockContainer; + + public new ScoreProcessor ScoreProcessor => base.ScoreProcessor; + + public new HealthProcessor HealthProcessor => base.HealthProcessor; + + public new bool PauseCooldownActive => base.PauseCooldownActive; + + public readonly List Results = new List(); + + /// + /// Instantiate a replay player that renders an autoplay mod. + /// + public TestReplayPlayer(bool allowPause = true, bool showResults = true, bool pauseOnFocusLost = false) + : base((beatmap, mods) => mods.OfType().First().CreateReplayScore(beatmap, mods), new PlayerConfiguration + { + AllowPause = allowPause, + ShowResults = showResults + }) + { + PauseOnFocusLost = pauseOnFocusLost; + } + + /// + /// Instantiate a replay player that renders the provided replay. + /// + public TestReplayPlayer(Score score, bool allowPause = true, bool showResults = true, bool pauseOnFocusLost = false) + : base(score, new PlayerConfiguration + { + AllowPause = allowPause, + ShowResults = showResults + }) + { + PauseOnFocusLost = pauseOnFocusLost; + } + + [BackgroundDependencyLoader] + private void load() + { + ScoreProcessor.NewJudgement += r => Results.Add(r); + } + } +} From 82c1a9ae4c6bfdbbd95945f33b823faaf33354fa Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Tue, 1 Jun 2021 18:59:31 -0700 Subject: [PATCH 656/839] Remove windows-only note from InspectCode section of readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index eb790ca18f..3054f19e79 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ Before committing your code, please run a code formatter. This can be achieved b We have adopted some cross-platform, compiler integrated analyzers. They can provide warnings when you are editing, building inside IDE or from command line, as-if they are provided by the compiler itself. -JetBrains ReSharper InspectCode is also used for wider rule sets. You can run it from PowerShell with `.\InspectCode.ps1`, which is [only supported on Windows](https://youtrack.jetbrains.com/issue/RSRP-410004). Alternatively, you can install ReSharper or use Rider to get inline support in your IDE of choice. +JetBrains ReSharper InspectCode is also used for wider rule sets. You can run it from PowerShell with `.\InspectCode.ps1`. Alternatively, you can install ReSharper or use Rider to get inline support in your IDE of choice. ## Contributing From ac761bb0755bb0f377a1c57a09688354b6fea5a1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 2 Jun 2021 14:43:35 +0900 Subject: [PATCH 657/839] Use `string.Empty` instead of arbitrary string --- osu.Game/Graphics/Containers/LinkFlowContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs index 458bc8876f..5ff2fdf6b2 100644 --- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs +++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs @@ -57,7 +57,7 @@ namespace osu.Game.Graphics.Containers createLink(AddText(text, creationParameters), new LinkDetails(LinkAction.External, url), url); public void AddLink(string text, Action action, string tooltipText = null, Action creationParameters = null) - => createLink(AddText(text, creationParameters), new LinkDetails(LinkAction.Custom, @"action"), tooltipText, action); + => createLink(AddText(text, creationParameters), new LinkDetails(LinkAction.Custom, string.Empty), tooltipText, action); public void AddLink(string text, LinkAction action, string argument, string tooltipText = null, Action creationParameters = null) => createLink(AddText(text, creationParameters), new LinkDetails(action, argument), tooltipText); From bf216687e1b74be0623d1a7cf9af698a1ebba37c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 2 Jun 2021 15:05:06 +0900 Subject: [PATCH 658/839] Fix osu!stable directory selection failing if no `Songs` folder is present at install location --- osu.Desktop/OsuGameDesktop.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 4a28ab3722..a66c7e6d3b 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -57,7 +57,7 @@ namespace osu.Desktop private string getStableInstallPath() { - static bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs")); + static bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs")) || File.Exists("osu!.cfg"); string stableInstallPath; From d5d5a4d0d799700f7805df49ef86322e9d6a33a2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 2 Jun 2021 15:13:21 +0900 Subject: [PATCH 659/839] Actually combine the path --- osu.Desktop/OsuGameDesktop.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index a66c7e6d3b..4de1e84fbf 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -57,7 +57,7 @@ namespace osu.Desktop private string getStableInstallPath() { - static bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs")) || File.Exists("osu!.cfg"); + static bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs")) || File.Exists(Path.Combine(p, "osu!.cfg")); string stableInstallPath; From a15cac6f5386651adf0af91d990f94525da98b99 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 2 Jun 2021 15:44:04 +0900 Subject: [PATCH 660/839] Change the way `Score` is initialised in `Player` to better lend to population of metadata --- .../Spectate/MultiSpectatorPlayer.cs | 4 +- .../OnlinePlay/Playlists/PlaylistsPlayer.cs | 8 +- osu.Game/Screens/Play/Player.cs | 73 ++++++++----------- osu.Game/Screens/Play/ReplayPlayer.cs | 24 +----- osu.Game/Screens/Play/SpectatorPlayer.cs | 12 +-- 5 files changed, 45 insertions(+), 76 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs index 0fe9e01d9d..2c157b0564 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs @@ -17,7 +17,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate public class MultiSpectatorPlayer : SpectatorPlayer { private readonly Bindable waitingOnFrames = new Bindable(true); - private readonly Score score; private readonly ISpectatorPlayerClock spectatorPlayerClock; /// @@ -28,7 +27,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate public MultiSpectatorPlayer([NotNull] Score score, [NotNull] ISpectatorPlayerClock spectatorPlayerClock) : base(score) { - this.score = score; this.spectatorPlayerClock = spectatorPlayerClock; } @@ -43,7 +41,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate base.UpdateAfterChildren(); // This is required because the frame stable clock is set to WaitingOnFrames = false for one frame. - waitingOnFrames.Value = DrawableRuleset.FrameStableClock.WaitingOnFrames.Value || score.Replay.Frames.Count == 0; + waitingOnFrames.Value = DrawableRuleset.FrameStableClock.WaitingOnFrames.Value || Score.Replay.Frames.Count == 0; } protected override GameplayClockContainer CreateGameplayClockContainer(WorkingBeatmap beatmap, double gameplayStart) diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs index 260d4961ff..a2ef715367 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs @@ -54,11 +54,11 @@ namespace osu.Game.Screens.OnlinePlay.Playlists return new PlaylistsResultsScreen(score, RoomId.Value.Value, PlaylistItem, true); } - protected override Score CreateScore() + protected override void PrepareScoreForResults() { - var score = base.CreateScore(); - score.ScoreInfo.TotalScore = (int)Math.Round(ScoreProcessor.GetStandardisedScore()); - return score; + base.PrepareScoreForResults(); + + Score.ScoreInfo.TotalScore = (int)Math.Round(ScoreProcessor.GetStandardisedScore()); } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 99570f6c5e..0d1ebd30fc 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; @@ -24,10 +23,8 @@ using osu.Game.IO.Archives; using osu.Game.Online.API; using osu.Game.Online.Spectator; using osu.Game.Overlays; -using osu.Game.Replays; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Scoring; @@ -137,6 +134,8 @@ namespace osu.Game.Screens.Play public readonly PlayerConfiguration Configuration; + protected Score Score { get; private set; } + /// /// Create a new player instance. /// @@ -161,22 +160,32 @@ namespace osu.Game.Screens.Play if (!LoadedBeatmapSuccessfully) return; + Score = CreateScore(); + + // ensure the score is in a consistent state with the current player. + Score.ScoreInfo.Beatmap = Beatmap.Value.BeatmapInfo; + Score.ScoreInfo.Ruleset = rulesetInfo; + Score.ScoreInfo.Mods = Mods.Value.ToArray(); + PrepareReplay(); + ScoreProcessor.NewJudgement += result => ScoreProcessor.PopulateScore(Score.ScoreInfo); + gameActive.BindValueChanged(_ => updatePauseOnFocusLostState(), true); } - [CanBeNull] - private Score recordingScore; - /// /// Run any recording / playback setup for replays. /// protected virtual void PrepareReplay() { - DrawableRuleset.SetRecordTarget(recordingScore = new Score()); + DrawableRuleset.SetRecordTarget(Score); + } - ScoreProcessor.NewJudgement += result => ScoreProcessor.PopulateScore(recordingScore.ScoreInfo); + protected virtual void PrepareScoreForResults() + { + // perform one final population to ensure everything is up-to-date. + ScoreProcessor.PopulateScore(Score.ScoreInfo); } [BackgroundDependencyLoader(true)] @@ -631,11 +640,11 @@ namespace osu.Game.Screens.Play prepareScoreForDisplayTask ??= Task.Run(async () => { - var score = CreateScore(); + PrepareScoreForResults(); try { - await PrepareScoreForResultsAsync(score).ConfigureAwait(false); + await PrepareScoreForResultsAsync(Score).ConfigureAwait(false); } catch (Exception ex) { @@ -644,14 +653,14 @@ namespace osu.Game.Screens.Play try { - await ImportScore(score).ConfigureAwait(false); + await ImportScore(Score).ConfigureAwait(false); } catch (Exception ex) { Logger.Error(ex, "Score import failed!"); } - return score.ScoreInfo; + return Score.ScoreInfo; }); if (skipStoryboardOutro) @@ -903,41 +912,19 @@ namespace osu.Game.Screens.Play } /// - /// Creates the player's . + /// Creates the player's . /// - /// The . - protected virtual Score CreateScore() - { - var score = new Score + /// The . + protected virtual Score CreateScore() => + new Score { - ScoreInfo = new ScoreInfo - { - Beatmap = Beatmap.Value.BeatmapInfo, - Ruleset = rulesetInfo, - Mods = Mods.Value.ToArray(), - } + ScoreInfo = new ScoreInfo { User = api.LocalUser.Value }, }; - if (DrawableRuleset.ReplayScore != null) - { - score.ScoreInfo.User = DrawableRuleset.ReplayScore.ScoreInfo?.User ?? new GuestUser(); - score.Replay = DrawableRuleset.ReplayScore.Replay; - } - else - { - score.ScoreInfo.User = api.LocalUser.Value; - score.Replay = new Replay { Frames = recordingScore?.Replay.Frames.ToList() ?? new List() }; - } - - ScoreProcessor.PopulateScore(score.ScoreInfo); - - return score; - } - /// - /// Imports the player's to the local database. + /// Imports the player's to the local database. /// - /// The to import. + /// The to import. /// The imported score. protected virtual async Task ImportScore(Score score) { @@ -968,9 +955,9 @@ namespace osu.Game.Screens.Play } /// - /// Prepare the for display at results. + /// Prepare the for display at results. /// - /// The to prepare. + /// The to prepare. /// A task that prepares the provided score. On completion, the score is assumed to be ready for display. protected virtual Task PrepareScoreForResultsAsync(Score score) => Task.CompletedTask; diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs index 91236ad607..e440e7d34e 100644 --- a/osu.Game/Screens/Play/ReplayPlayer.cs +++ b/osu.Game/Screens/Play/ReplayPlayer.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using osu.Framework.Allocation; using osu.Framework.Input.Bindings; using osu.Game.Beatmaps; using osu.Game.Input.Bindings; @@ -16,8 +15,6 @@ namespace osu.Game.Screens.Play { public class ReplayPlayer : Player, IKeyBindingHandler { - protected Score Score { get; private set; } - private readonly Func, Score> createScore; // Disallow replays from failing. (see https://github.com/ppy/osu/issues/6108) @@ -34,28 +31,15 @@ namespace osu.Game.Screens.Play this.createScore = createScore; } - [BackgroundDependencyLoader] - private void load() - { - if (!LoadedBeatmapSuccessfully) return; - - Score = createScore(GameplayBeatmap.PlayableBeatmap, Mods.Value); - } - protected override void PrepareReplay() { DrawableRuleset?.SetReplayScore(Score); + + // todo: move to base class along with Score? + ScoreProcessor.NewJudgement += result => ScoreProcessor.PopulateScore(Score.ScoreInfo); } - protected override Score CreateScore() - { - var baseScore = base.CreateScore(); - - // Since the replay score doesn't contain statistics, we'll pass them through here. - Score.ScoreInfo.HitEvents = baseScore.ScoreInfo.HitEvents; - - return Score; - } + protected override Score CreateScore() => createScore(GameplayBeatmap.PlayableBeatmap, Mods.Value); // Don't re-import replay scores as they're already present in the database. protected override Task ImportScore(Score score) => Task.CompletedTask; diff --git a/osu.Game/Screens/Play/SpectatorPlayer.cs b/osu.Game/Screens/Play/SpectatorPlayer.cs index a8125dfded..67471dff90 100644 --- a/osu.Game/Screens/Play/SpectatorPlayer.cs +++ b/osu.Game/Screens/Play/SpectatorPlayer.cs @@ -18,6 +18,9 @@ namespace osu.Game.Screens.Play { private readonly Score score; + [Resolved] + private SpectatorClient spectatorClient { get; set; } + protected override bool CheckModsAllowFailure() => false; // todo: better support starting mid-way through beatmap public SpectatorPlayer(Score score) @@ -25,13 +28,10 @@ namespace osu.Game.Screens.Play this.score = score; } - protected override ResultsScreen CreateResults(ScoreInfo score) - { - return new SpectatorResultsScreen(score); - } + protected override Score CreateScore() => score; - [Resolved] - private SpectatorClient spectatorClient { get; set; } + protected override ResultsScreen CreateResults(ScoreInfo score) + => new SpectatorResultsScreen(score); [BackgroundDependencyLoader] private void load() From f8ae70e562c7d59bcf3b4c04f57dfe09e7d20e0d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 2 Jun 2021 16:04:53 +0900 Subject: [PATCH 661/839] Update existing function type rather than adding an override --- .editorconfig | 10 +-- .../TestSceneCatcherArea.cs | 2 +- .../TestSceneComboCounter.cs | 2 +- .../TestSceneFruitObjects.cs | 24 +++--- .../TestSceneFruitVisualChange.cs | 4 +- .../Skinning/ManiaHitObjectTestScene.cs | 2 +- .../Skinning/TestSceneColumnBackground.cs | 2 +- .../Skinning/TestSceneColumnHitObjectArea.cs | 2 +- .../Skinning/TestSceneDrawableJudgement.cs | 2 +- .../Skinning/TestSceneHitExplosion.cs | 2 +- .../Skinning/TestSceneKeyArea.cs | 2 +- .../Skinning/TestScenePlayfield.cs | 4 +- .../Skinning/TestSceneStage.cs | 2 +- .../Skinning/TestSceneStageBackground.cs | 2 +- .../Skinning/TestSceneStageForeground.cs | 2 +- .../TestSceneDrawableJudgement.cs | 2 +- .../TestSceneGameplayCursor.cs | 2 +- .../TestSceneHitCircle.cs | 24 +++--- .../TestSceneSlider.cs | 78 +++++++++---------- .../TestSceneSpinner.cs | 12 +-- .../Skinning/TestSceneDrawableBarLine.cs | 4 +- .../Skinning/TestSceneDrawableDrumRoll.cs | 4 +- .../Skinning/TestSceneDrawableHit.cs | 8 +- .../Skinning/TestSceneDrawableTaikoMascot.cs | 14 ++-- .../Skinning/TestSceneHitExplosion.cs | 8 +- .../Skinning/TestSceneInputDrum.cs | 2 +- .../Skinning/TestSceneKiaiHitExplosion.cs | 4 +- .../Skinning/TestSceneTaikoPlayfield.cs | 2 +- .../Skinning/TestSceneTaikoScroller.cs | 2 +- .../TestSceneDrawableStoryboardSprite.cs | 6 +- .../TestSceneSkinEditorComponentsList.cs | 2 +- .../TestSceneSkinEditorMultipleSkins.cs | 2 +- .../Gameplay/TestSceneSkinnableHUDOverlay.cs | 2 +- osu.Game/Tests/Visual/SkinnableTestScene.cs | 4 +- 34 files changed, 117 insertions(+), 129 deletions(-) diff --git a/.editorconfig b/.editorconfig index 0cdf3b92d3..f4d7e08d08 100644 --- a/.editorconfig +++ b/.editorconfig @@ -10,14 +10,6 @@ trim_trailing_whitespace = true #Roslyn naming styles -#PascalCase for public and protected members -dotnet_naming_style.pascalcase.capitalization = pascal_case -dotnet_naming_symbols.public_members.applicable_accessibilities = public,internal,protected,protected_internal,private_protected -dotnet_naming_symbols.public_members.applicable_kinds = property,method,field,event -dotnet_naming_rule.public_members_pascalcase.severity = error -dotnet_naming_rule.public_members_pascalcase.symbols = public_members -dotnet_naming_rule.public_members_pascalcase.style = pascalcase - #camelCase for private members dotnet_naming_style.camelcase.capitalization = camel_case @@ -197,4 +189,4 @@ dotnet_diagnostic.IDE0069.severity = none dotnet_diagnostic.CA2225.severity = none # Banned APIs -dotnet_diagnostic.RS0030.severity = error \ No newline at end of file +dotnet_diagnostic.RS0030.severity = error diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs index ad404e1f63..4af5098451 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs @@ -95,7 +95,7 @@ namespace osu.Game.Rulesets.Catch.Tests CircleSize = circleSize }; - SetContents(() => + SetContents(_ => { var droppedObjectContainer = new Container { diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneComboCounter.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneComboCounter.cs index c7b322c8a0..064a84cb98 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneComboCounter.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneComboCounter.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Catch.Tests { scoreProcessor = new ScoreProcessor(); - SetContents(() => new CatchComboDisplay + SetContents(_ => new CatchComboDisplay { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneFruitObjects.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneFruitObjects.cs index 3a651605d3..943adbef52 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneFruitObjects.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneFruitObjects.cs @@ -19,22 +19,22 @@ namespace osu.Game.Rulesets.Catch.Tests { base.LoadComplete(); - AddStep("show pear", () => SetContents(() => createDrawableFruit(0))); - AddStep("show grape", () => SetContents(() => createDrawableFruit(1))); - AddStep("show pineapple / apple", () => SetContents(() => createDrawableFruit(2))); - AddStep("show raspberry / orange", () => SetContents(() => createDrawableFruit(3))); + AddStep("show pear", () => SetContents(_ => createDrawableFruit(0))); + AddStep("show grape", () => SetContents(_ => createDrawableFruit(1))); + AddStep("show pineapple / apple", () => SetContents(_ => createDrawableFruit(2))); + AddStep("show raspberry / orange", () => SetContents(_ => createDrawableFruit(3))); - AddStep("show banana", () => SetContents(createDrawableBanana)); + AddStep("show banana", () => SetContents(_ => createDrawableBanana())); - AddStep("show droplet", () => SetContents(() => createDrawableDroplet())); - AddStep("show tiny droplet", () => SetContents(createDrawableTinyDroplet)); + AddStep("show droplet", () => SetContents(_ => createDrawableDroplet())); + AddStep("show tiny droplet", () => SetContents(_ => createDrawableTinyDroplet())); - AddStep("show hyperdash pear", () => SetContents(() => createDrawableFruit(0, true))); - AddStep("show hyperdash grape", () => SetContents(() => createDrawableFruit(1, true))); - AddStep("show hyperdash pineapple / apple", () => SetContents(() => createDrawableFruit(2, true))); - AddStep("show hyperdash raspberry / orange", () => SetContents(() => createDrawableFruit(3, true))); + AddStep("show hyperdash pear", () => SetContents(_ => createDrawableFruit(0, true))); + AddStep("show hyperdash grape", () => SetContents(_ => createDrawableFruit(1, true))); + AddStep("show hyperdash pineapple / apple", () => SetContents(_ => createDrawableFruit(2, true))); + AddStep("show hyperdash raspberry / orange", () => SetContents(_ => createDrawableFruit(3, true))); - AddStep("show hyperdash droplet", () => SetContents(() => createDrawableDroplet(true))); + AddStep("show hyperdash droplet", () => SetContents(_ => createDrawableDroplet(true))); } private Drawable createDrawableFruit(int indexInBeatmap, bool hyperdash = false) => diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneFruitVisualChange.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneFruitVisualChange.cs index 125e0c674c..9446e864a1 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneFruitVisualChange.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneFruitVisualChange.cs @@ -14,13 +14,13 @@ namespace osu.Game.Rulesets.Catch.Tests protected override void LoadComplete() { - AddStep("fruit changes visual and hyper", () => SetContents(() => new TestDrawableCatchHitObjectSpecimen(new DrawableFruit(new Fruit + AddStep("fruit changes visual and hyper", () => SetContents(_ => new TestDrawableCatchHitObjectSpecimen(new DrawableFruit(new Fruit { IndexInBeatmapBindable = { BindTarget = indexInBeatmap }, HyperDashBindable = { BindTarget = hyperDash }, })))); - AddStep("droplet changes hyper", () => SetContents(() => new TestDrawableCatchHitObjectSpecimen(new DrawableDroplet(new Droplet + AddStep("droplet changes hyper", () => SetContents(_ => new TestDrawableCatchHitObjectSpecimen(new DrawableDroplet(new Droplet { HyperDashBindable = { BindTarget = hyperDash }, })))); diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaHitObjectTestScene.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaHitObjectTestScene.cs index 96444fd316..b7d7af6b8c 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaHitObjectTestScene.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaHitObjectTestScene.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning [SetUp] public void SetUp() => Schedule(() => { - SetContents(() => new FillFlowContainer + SetContents(_ => new FillFlowContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneColumnBackground.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneColumnBackground.cs index ca323b5911..106b2d188d 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneColumnBackground.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneColumnBackground.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning [BackgroundDependencyLoader] private void load() { - SetContents(() => new FillFlowContainer + SetContents(_ => new FillFlowContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneColumnHitObjectArea.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneColumnHitObjectArea.cs index 4392666cb7..215f8fb1d5 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneColumnHitObjectArea.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneColumnHitObjectArea.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning [BackgroundDependencyLoader] private void load() { - SetContents(() => new FillFlowContainer + SetContents(_ => new FillFlowContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneDrawableJudgement.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneDrawableJudgement.cs index dcb25f21ba..75a5495078 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneDrawableJudgement.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneDrawableJudgement.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning { if (hitWindows.IsHitResultAllowed(result)) { - AddStep("Show " + result.GetDescription(), () => SetContents(() => + AddStep("Show " + result.GetDescription(), () => SetContents(_ => new DrawableManiaJudgement(new JudgementResult(new HitObject { StartTime = Time.Current }, new Judgement()) { Type = result diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneHitExplosion.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneHitExplosion.cs index 4dc6700786..004793e1e5 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneHitExplosion.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneHitExplosion.cs @@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning [BackgroundDependencyLoader] private void load() { - SetContents(() => + SetContents(_ => { var pool = new DrawablePool(5); hitExplosionPools.Add(pool); diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneKeyArea.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneKeyArea.cs index c58c07c83b..7564bd84ad 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneKeyArea.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneKeyArea.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning [BackgroundDependencyLoader] private void load() { - SetContents(() => new FillFlowContainer + SetContents(_ => new FillFlowContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestScenePlayfield.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestScenePlayfield.cs index 161eda650e..c7dc5fc8b5 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/TestScenePlayfield.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestScenePlayfield.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning new StageDefinition { Columns = 2 } }; - SetContents(() => new ManiaPlayfield(stageDefinitions)); + SetContents(_ => new ManiaPlayfield(stageDefinitions)); }); } @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning new StageDefinition { Columns = 2 } }; - SetContents(() => new ManiaPlayfield(stageDefinitions)); + SetContents(_ => new ManiaPlayfield(stageDefinitions)); }); } diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStage.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStage.cs index 37b97a444a..7804261906 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStage.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStage.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning [BackgroundDependencyLoader] private void load() { - SetContents(() => + SetContents(_ => { ManiaAction normalAction = ManiaAction.Key1; ManiaAction specialAction = ManiaAction.Special1; diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStageBackground.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStageBackground.cs index a15fb392d6..410a43fc73 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStageBackground.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStageBackground.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning [BackgroundDependencyLoader] private void load() { - SetContents(() => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageBackground, stageDefinition: new StageDefinition { Columns = 4 }), + SetContents(_ => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageBackground, stageDefinition: new StageDefinition { Columns = 4 }), _ => new DefaultStageBackground()) { Anchor = Anchor.Centre, diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStageForeground.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStageForeground.cs index bceee1c599..27e97152bc 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStageForeground.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStageForeground.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning [BackgroundDependencyLoader] private void load() { - SetContents(() => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageForeground, stageDefinition: new StageDefinition { Columns = 4 }), _ => null) + SetContents(_ => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageForeground, stageDefinition: new StageDefinition { Columns = 4 }), _ => null) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs index 4395ca6281..7821ae9cf0 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs @@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Osu.Tests { int poolIndex = 0; - SetContents(() => + SetContents(_ => { DrawablePool pool; diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs index 9a77292aff..a95159ce4c 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs @@ -97,7 +97,7 @@ namespace osu.Game.Rulesets.Osu.Tests private void loadContent(bool automated = true, Func skinProvider = null) { - SetContents(() => + SetContents(_ => { var inputManager = automated ? (InputManager)new MovingCursorInputManager() : new OsuInputManager(new OsuRuleset().RulesetInfo); var skinContainer = skinProvider?.Invoke() ?? new SkinProvidingContainer(null); diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs index 1278a0ff2d..58e46b6687 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs @@ -23,18 +23,18 @@ namespace osu.Game.Rulesets.Osu.Tests [Test] public void TestVariousHitCircles() { - AddStep("Miss Big Single", () => SetContents(() => testSingle(2))); - AddStep("Miss Medium Single", () => SetContents(() => testSingle(5))); - AddStep("Miss Small Single", () => SetContents(() => testSingle(7))); - AddStep("Hit Big Single", () => SetContents(() => testSingle(2, true))); - AddStep("Hit Medium Single", () => SetContents(() => testSingle(5, true))); - AddStep("Hit Small Single", () => SetContents(() => testSingle(7, true))); - AddStep("Miss Big Stream", () => SetContents(() => testStream(2))); - AddStep("Miss Medium Stream", () => SetContents(() => testStream(5))); - AddStep("Miss Small Stream", () => SetContents(() => testStream(7))); - AddStep("Hit Big Stream", () => SetContents(() => testStream(2, true))); - AddStep("Hit Medium Stream", () => SetContents(() => testStream(5, true))); - AddStep("Hit Small Stream", () => SetContents(() => testStream(7, true))); + AddStep("Miss Big Single", () => SetContents(_ => testSingle(2))); + AddStep("Miss Medium Single", () => SetContents(_ => testSingle(5))); + AddStep("Miss Small Single", () => SetContents(_ => testSingle(7))); + AddStep("Hit Big Single", () => SetContents(_ => testSingle(2, true))); + AddStep("Hit Medium Single", () => SetContents(_ => testSingle(5, true))); + AddStep("Hit Small Single", () => SetContents(_ => testSingle(7, true))); + AddStep("Miss Big Stream", () => SetContents(_ => testStream(2))); + AddStep("Miss Medium Stream", () => SetContents(_ => testStream(5))); + AddStep("Miss Small Stream", () => SetContents(_ => testStream(7))); + AddStep("Hit Big Stream", () => SetContents(_ => testStream(2, true))); + AddStep("Hit Medium Stream", () => SetContents(_ => testStream(5, true))); + AddStep("Hit Small Stream", () => SetContents(_ => testStream(7, true))); } private Drawable testSingle(float circleSize, bool auto = false, double timeOffset = 0, Vector2? positionOffset = null) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs index d40484f5ed..fc5fcf2358 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs @@ -30,54 +30,54 @@ namespace osu.Game.Rulesets.Osu.Tests [Test] public void TestVariousSliders() { - AddStep("Big Single", () => SetContents(() => testSimpleBig())); - AddStep("Medium Single", () => SetContents(() => testSimpleMedium())); - AddStep("Small Single", () => SetContents(() => testSimpleSmall())); - AddStep("Big 1 Repeat", () => SetContents(() => testSimpleBig(1))); - AddStep("Medium 1 Repeat", () => SetContents(() => testSimpleMedium(1))); - AddStep("Small 1 Repeat", () => SetContents(() => testSimpleSmall(1))); - AddStep("Big 2 Repeats", () => SetContents(() => testSimpleBig(2))); - AddStep("Medium 2 Repeats", () => SetContents(() => testSimpleMedium(2))); - AddStep("Small 2 Repeats", () => SetContents(() => testSimpleSmall(2))); + AddStep("Big Single", () => SetContents(_ => testSimpleBig())); + AddStep("Medium Single", () => SetContents(_ => testSimpleMedium())); + AddStep("Small Single", () => SetContents(_ => testSimpleSmall())); + AddStep("Big 1 Repeat", () => SetContents(_ => testSimpleBig(1))); + AddStep("Medium 1 Repeat", () => SetContents(_ => testSimpleMedium(1))); + AddStep("Small 1 Repeat", () => SetContents(_ => testSimpleSmall(1))); + AddStep("Big 2 Repeats", () => SetContents(_ => testSimpleBig(2))); + AddStep("Medium 2 Repeats", () => SetContents(_ => testSimpleMedium(2))); + AddStep("Small 2 Repeats", () => SetContents(_ => testSimpleSmall(2))); - AddStep("Slow Slider", () => SetContents(testSlowSpeed)); // slow long sliders take ages already so no repeat steps - AddStep("Slow Short Slider", () => SetContents(() => testShortSlowSpeed())); - AddStep("Slow Short Slider 1 Repeats", () => SetContents(() => testShortSlowSpeed(1))); - AddStep("Slow Short Slider 2 Repeats", () => SetContents(() => testShortSlowSpeed(2))); + AddStep("Slow Slider", () => SetContents(_ => testSlowSpeed())); // slow long sliders take ages already so no repeat steps + AddStep("Slow Short Slider", () => SetContents(_ => testShortSlowSpeed())); + AddStep("Slow Short Slider 1 Repeats", () => SetContents(_ => testShortSlowSpeed(1))); + AddStep("Slow Short Slider 2 Repeats", () => SetContents(_ => testShortSlowSpeed(2))); - AddStep("Fast Slider", () => SetContents(() => testHighSpeed())); - AddStep("Fast Slider 1 Repeat", () => SetContents(() => testHighSpeed(1))); - AddStep("Fast Slider 2 Repeats", () => SetContents(() => testHighSpeed(2))); - AddStep("Fast Short Slider", () => SetContents(() => testShortHighSpeed())); - AddStep("Fast Short Slider 1 Repeat", () => SetContents(() => testShortHighSpeed(1))); - AddStep("Fast Short Slider 2 Repeats", () => SetContents(() => testShortHighSpeed(2))); - AddStep("Fast Short Slider 6 Repeats", () => SetContents(() => testShortHighSpeed(6))); + AddStep("Fast Slider", () => SetContents(_ => testHighSpeed())); + AddStep("Fast Slider 1 Repeat", () => SetContents(_ => testHighSpeed(1))); + AddStep("Fast Slider 2 Repeats", () => SetContents(_ => testHighSpeed(2))); + AddStep("Fast Short Slider", () => SetContents(_ => testShortHighSpeed())); + AddStep("Fast Short Slider 1 Repeat", () => SetContents(_ => testShortHighSpeed(1))); + AddStep("Fast Short Slider 2 Repeats", () => SetContents(_ => testShortHighSpeed(2))); + AddStep("Fast Short Slider 6 Repeats", () => SetContents(_ => testShortHighSpeed(6))); - AddStep("Perfect Curve", () => SetContents(() => testPerfect())); - AddStep("Perfect Curve 1 Repeat", () => SetContents(() => testPerfect(1))); - AddStep("Perfect Curve 2 Repeats", () => SetContents(() => testPerfect(2))); + AddStep("Perfect Curve", () => SetContents(_ => testPerfect())); + AddStep("Perfect Curve 1 Repeat", () => SetContents(_ => testPerfect(1))); + AddStep("Perfect Curve 2 Repeats", () => SetContents(_ => testPerfect(2))); - AddStep("Linear Slider", () => SetContents(() => testLinear())); - AddStep("Linear Slider 1 Repeat", () => SetContents(() => testLinear(1))); - AddStep("Linear Slider 2 Repeats", () => SetContents(() => testLinear(2))); + AddStep("Linear Slider", () => SetContents(_ => testLinear())); + AddStep("Linear Slider 1 Repeat", () => SetContents(_ => testLinear(1))); + AddStep("Linear Slider 2 Repeats", () => SetContents(_ => testLinear(2))); - AddStep("Bezier Slider", () => SetContents(() => testBezier())); - AddStep("Bezier Slider 1 Repeat", () => SetContents(() => testBezier(1))); - AddStep("Bezier Slider 2 Repeats", () => SetContents(() => testBezier(2))); + AddStep("Bezier Slider", () => SetContents(_ => testBezier())); + AddStep("Bezier Slider 1 Repeat", () => SetContents(_ => testBezier(1))); + AddStep("Bezier Slider 2 Repeats", () => SetContents(_ => testBezier(2))); - AddStep("Linear Overlapping", () => SetContents(() => testLinearOverlapping())); - AddStep("Linear Overlapping 1 Repeat", () => SetContents(() => testLinearOverlapping(1))); - AddStep("Linear Overlapping 2 Repeats", () => SetContents(() => testLinearOverlapping(2))); + AddStep("Linear Overlapping", () => SetContents(_ => testLinearOverlapping())); + AddStep("Linear Overlapping 1 Repeat", () => SetContents(_ => testLinearOverlapping(1))); + AddStep("Linear Overlapping 2 Repeats", () => SetContents(_ => testLinearOverlapping(2))); - AddStep("Catmull Slider", () => SetContents(() => testCatmull())); - AddStep("Catmull Slider 1 Repeat", () => SetContents(() => testCatmull(1))); - AddStep("Catmull Slider 2 Repeats", () => SetContents(() => testCatmull(2))); + AddStep("Catmull Slider", () => SetContents(_ => testCatmull())); + AddStep("Catmull Slider 1 Repeat", () => SetContents(_ => testCatmull(1))); + AddStep("Catmull Slider 2 Repeats", () => SetContents(_ => testCatmull(2))); - AddStep("Big Single, Large StackOffset", () => SetContents(() => testSimpleBigLargeStackOffset())); - AddStep("Big 1 Repeat, Large StackOffset", () => SetContents(() => testSimpleBigLargeStackOffset(1))); + AddStep("Big Single, Large StackOffset", () => SetContents(_ => testSimpleBigLargeStackOffset())); + AddStep("Big 1 Repeat, Large StackOffset", () => SetContents(_ => testSimpleBigLargeStackOffset(1))); - AddStep("Distance Overflow", () => SetContents(() => testDistanceOverflow())); - AddStep("Distance Overflow 1 Repeat", () => SetContents(() => testDistanceOverflow(1))); + AddStep("Distance Overflow", () => SetContents(_ => testDistanceOverflow())); + AddStep("Distance Overflow 1 Repeat", () => SetContents(_ => testDistanceOverflow(1))); } [Test] diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs index 0a7ef443b1..b21b7a6f4a 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs @@ -29,15 +29,15 @@ namespace osu.Game.Rulesets.Osu.Tests public void TestVariousSpinners(bool autoplay) { string term = autoplay ? "Hit" : "Miss"; - AddStep($"{term} Big", () => SetContents(() => testSingle(2, autoplay))); - AddStep($"{term} Medium", () => SetContents(() => testSingle(5, autoplay))); - AddStep($"{term} Small", () => SetContents(() => testSingle(7, autoplay))); + AddStep($"{term} Big", () => SetContents(_ => testSingle(2, autoplay))); + AddStep($"{term} Medium", () => SetContents(_ => testSingle(5, autoplay))); + AddStep($"{term} Small", () => SetContents(_ => testSingle(7, autoplay))); } [Test] public void TestSpinningSamplePitchShift() { - AddStep("Add spinner", () => SetContents(() => testSingle(5, true, 4000))); + AddStep("Add spinner", () => SetContents(_ => testSingle(5, true, 4000))); AddUntilStep("Pitch starts low", () => getSpinningSample().Frequency.Value < 0.8); AddUntilStep("Pitch increases", () => getSpinningSample().Frequency.Value > 0.8); @@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Osu.Tests [TestCase(true)] public void TestLongSpinner(bool autoplay) { - AddStep("Very long spinner", () => SetContents(() => testSingle(5, autoplay, 4000))); + AddStep("Very long spinner", () => SetContents(_ => testSingle(5, autoplay, 4000))); AddUntilStep("Wait for completion", () => drawableSpinner.Result.HasResult); AddUntilStep("Check correct progress", () => drawableSpinner.Progress == (autoplay ? 1 : 0)); } @@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.Tests [TestCase(true)] public void TestSuperShortSpinner(bool autoplay) { - AddStep("Very short spinner", () => SetContents(() => testSingle(5, autoplay, 200))); + AddStep("Very short spinner", () => SetContents(_ => testSingle(5, autoplay, 200))); AddUntilStep("Wait for completion", () => drawableSpinner.Result.HasResult); AddUntilStep("Short spinner implicitly completes", () => drawableSpinner.Progress == 1); } diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.cs index ff309f278e..f9b8e9a985 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning [BackgroundDependencyLoader] private void load() { - AddStep("Bar line", () => SetContents(() => + AddStep("Bar line", () => SetContents(_ => { ScrollingHitObjectContainer hoc; @@ -54,7 +54,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning return cont; })); - AddStep("Bar line (major)", () => SetContents(() => + AddStep("Bar line (major)", () => SetContents(_ => { ScrollingHitObjectContainer hoc; diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableDrumRoll.cs index 44646e5fc9..26a4e85fe5 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableDrumRoll.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning [BackgroundDependencyLoader] private void load() { - AddStep("Drum roll", () => SetContents(() => + AddStep("Drum roll", () => SetContents(_ => { var hoc = new ScrollingHitObjectContainer(); @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning return hoc; })); - AddStep("Drum roll (strong)", () => SetContents(() => + AddStep("Drum roll (strong)", () => SetContents(_ => { var hoc = new ScrollingHitObjectContainer(); diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableHit.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableHit.cs index 9930d97d31..c4ee68206c 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableHit.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableHit.cs @@ -17,25 +17,25 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning [BackgroundDependencyLoader] private void load() { - AddStep("Centre hit", () => SetContents(() => new DrawableHit(createHitAtCurrentTime()) + AddStep("Centre hit", () => SetContents(_ => new DrawableHit(createHitAtCurrentTime()) { Anchor = Anchor.Centre, Origin = Anchor.Centre, })); - AddStep("Centre hit (strong)", () => SetContents(() => new DrawableHit(createHitAtCurrentTime(true)) + AddStep("Centre hit (strong)", () => SetContents(_ => new DrawableHit(createHitAtCurrentTime(true)) { Anchor = Anchor.Centre, Origin = Anchor.Centre, })); - AddStep("Rim hit", () => SetContents(() => new DrawableHit(createHitAtCurrentTime()) + AddStep("Rim hit", () => SetContents(_ => new DrawableHit(createHitAtCurrentTime()) { Anchor = Anchor.Centre, Origin = Anchor.Centre, })); - AddStep("Rim hit (strong)", () => SetContents(() => new DrawableHit(createHitAtCurrentTime(true)) + AddStep("Rim hit (strong)", () => SetContents(_ => new DrawableHit(createHitAtCurrentTime(true)) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs index 8d1aafdcc2..b976735223 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs @@ -54,16 +54,16 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning { AddStep("set beatmap", () => setBeatmap()); - AddStep("clear state", () => SetContents(() => new TaikoMascotAnimation(TaikoMascotAnimationState.Clear))); - AddStep("idle state", () => SetContents(() => new TaikoMascotAnimation(TaikoMascotAnimationState.Idle))); - AddStep("kiai state", () => SetContents(() => new TaikoMascotAnimation(TaikoMascotAnimationState.Kiai))); - AddStep("fail state", () => SetContents(() => new TaikoMascotAnimation(TaikoMascotAnimationState.Fail))); + AddStep("clear state", () => SetContents(_ => new TaikoMascotAnimation(TaikoMascotAnimationState.Clear))); + AddStep("idle state", () => SetContents(_ => new TaikoMascotAnimation(TaikoMascotAnimationState.Idle))); + AddStep("kiai state", () => SetContents(_ => new TaikoMascotAnimation(TaikoMascotAnimationState.Kiai))); + AddStep("fail state", () => SetContents(_ => new TaikoMascotAnimation(TaikoMascotAnimationState.Fail))); } [Test] public void TestInitialState() { - AddStep("create mascot", () => SetContents(() => new DrawableTaikoMascot { RelativeSizeAxes = Axes.Both })); + AddStep("create mascot", () => SetContents(_ => new DrawableTaikoMascot { RelativeSizeAxes = Axes.Both })); AddAssert("mascot initially idle", () => allMascotsIn(TaikoMascotAnimationState.Idle)); } @@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning { AddStep("set beatmap", () => setBeatmap()); - AddStep("create mascot", () => SetContents(() => new DrawableTaikoMascot { RelativeSizeAxes = Axes.Both })); + AddStep("create mascot", () => SetContents(_ => new DrawableTaikoMascot { RelativeSizeAxes = Axes.Both })); AddStep("set clear state", () => mascots.ForEach(mascot => mascot.State.Value = TaikoMascotAnimationState.Clear)); AddStep("miss", () => mascots.ForEach(mascot => mascot.LastResult.Value = new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Miss })); @@ -181,7 +181,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning { Beatmap.Value.Track.Start(); - SetContents(() => + SetContents(_ => { var ruleset = new TaikoRuleset(); return new DrawableTaikoRuleset(ruleset, Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo)); diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs index 61ea8b664d..1cba6c9008 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs @@ -22,16 +22,16 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning [Test] public void TestNormalHit() { - AddStep("Great", () => SetContents(() => getContentFor(createHit(HitResult.Great)))); - AddStep("Ok", () => SetContents(() => getContentFor(createHit(HitResult.Ok)))); - AddStep("Miss", () => SetContents(() => getContentFor(createHit(HitResult.Miss)))); + AddStep("Great", () => SetContents(_ => getContentFor(createHit(HitResult.Great)))); + AddStep("Ok", () => SetContents(_ => getContentFor(createHit(HitResult.Ok)))); + AddStep("Miss", () => SetContents(_ => getContentFor(createHit(HitResult.Miss)))); } [TestCase(HitResult.Great)] [TestCase(HitResult.Ok)] public void TestStrongHit(HitResult type) { - AddStep("create hit", () => SetContents(() => getContentFor(createStrongHit(type)))); + AddStep("create hit", () => SetContents(_ => getContentFor(createStrongHit(type)))); AddStep("visualise second hit", () => this.ChildrenOfType() .ForEach(e => e.VisualiseSecondHit(new JudgementResult(new HitObject { StartTime = Time.Current }, new Judgement())))); diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneInputDrum.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneInputDrum.cs index 9b36b064bc..055a292fe8 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneInputDrum.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneInputDrum.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning [BackgroundDependencyLoader] private void load() { - SetContents(() => new TaikoInputManager(new TaikoRuleset().RulesetInfo) + SetContents(_ => new TaikoInputManager(new TaikoRuleset().RulesetInfo) { RelativeSizeAxes = Axes.Both, Child = new Container diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneKiaiHitExplosion.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneKiaiHitExplosion.cs index b558709592..419e100296 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneKiaiHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneKiaiHitExplosion.cs @@ -15,8 +15,8 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning [Test] public void TestKiaiHits() { - AddStep("rim hit", () => SetContents(() => getContentFor(createHit(HitType.Rim)))); - AddStep("centre hit", () => SetContents(() => getContentFor(createHit(HitType.Centre)))); + AddStep("rim hit", () => SetContents(_ => getContentFor(createHit(HitType.Rim)))); + AddStep("centre hit", () => SetContents(_ => getContentFor(createHit(HitType.Centre)))); } private Drawable getContentFor(DrawableTestHit hit) diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs index 7b7e2c43d1..f96297a06d 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning Beatmap.Value.Track.Start(); }); - AddStep("Load playfield", () => SetContents(() => new TaikoPlayfield(new ControlPointInfo()) + AddStep("Load playfield", () => SetContents(_ => new TaikoPlayfield(new ControlPointInfo()) { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs index 14c3599fcd..9882c7bc90 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning public TestSceneTaikoScroller() { - AddStep("Load scroller", () => SetContents(() => + AddStep("Load scroller", () => SetContents(_ => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.Scroller), _ => Empty()) { Clock = new FramedClock(clock), diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs index 9501026edc..52bedc328d 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs @@ -29,8 +29,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("allow skin lookup", () => storyboard.UseSkinSprites = false); - AddStep("create sprites", () => SetContents( - () => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero))); + AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero))); assertSpritesFromSkin(false); } @@ -42,8 +41,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("allow skin lookup", () => storyboard.UseSkinSprites = true); - AddStep("create sprites", () => SetContents( - () => createSprite(lookup_name, Anchor.Centre, Vector2.Zero))); + AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.Centre, Vector2.Zero))); assertSpritesFromSkin(true); } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorComponentsList.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorComponentsList.cs index 14bd62b98a..58c89411c0 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorComponentsList.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorComponentsList.cs @@ -14,7 +14,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestToggleEditor() { - AddStep("show available components", () => SetContents(() => new SkinComponentToolbox(300) + AddStep("show available components", () => SetContents(_ => new SkinComponentToolbox(300) { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs index 245e190b1f..856747ad19 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs @@ -27,7 +27,7 @@ namespace osu.Game.Tests.Visual.Gameplay { AddStep("create editor overlay", () => { - SetContents(() => + SetContents(_ => { var ruleset = new OsuRuleset(); var mods = new[] { ruleset.GetAutoplayMod() }; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs index c92e9dcfd5..723e35ed55 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs @@ -77,7 +77,7 @@ namespace osu.Game.Tests.Visual.Gameplay { AddStep("create overlay", () => { - SetContents(() => + SetContents(_ => { hudOverlay = new HUDOverlay(null, Array.Empty()); diff --git a/osu.Game/Tests/Visual/SkinnableTestScene.cs b/osu.Game/Tests/Visual/SkinnableTestScene.cs index b04d4f3170..ef44d0df24 100644 --- a/osu.Game/Tests/Visual/SkinnableTestScene.cs +++ b/osu.Game/Tests/Visual/SkinnableTestScene.cs @@ -52,9 +52,7 @@ namespace osu.Game.Tests.Visual private readonly List createdDrawables = new List(); - public void SetContents(Func creationFunction) => SetContents(_ => creationFunction?.Invoke()); - - public void SetContents(Func creationFunction) + protected void SetContents(Func creationFunction) { createdDrawables.Clear(); From e76e540ac11338cb5a83b87c04c835db5c4fb1e7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 2 Jun 2021 16:45:25 +0900 Subject: [PATCH 662/839] Fix iOS props Newtonsoft.Json version not matching osu.Game --- osu.iOS.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.iOS.props b/osu.iOS.props index c1d63f3236..cbb6a21fd1 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -92,7 +92,7 @@ - + From 5b436ee436a58d3a8d83898f5613fcdf18222268 Mon Sep 17 00:00:00 2001 From: Nathan Alo Date: Wed, 2 Jun 2021 15:50:33 +0800 Subject: [PATCH 663/839] add beatmap with storyboard source --- osu.Game/Configuration/BackgroundSource.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Configuration/BackgroundSource.cs b/osu.Game/Configuration/BackgroundSource.cs index 5726e96eb1..18e0603860 100644 --- a/osu.Game/Configuration/BackgroundSource.cs +++ b/osu.Game/Configuration/BackgroundSource.cs @@ -1,11 +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.ComponentModel; + namespace osu.Game.Configuration { public enum BackgroundSource { Skin, - Beatmap + Beatmap, + + [Description("Beatmap (with storyboard / video)")] + BeatmapWithStoryboard, } } From dec18ef826defe08cce89cc5b92b01b30c4fef81 Mon Sep 17 00:00:00 2001 From: Nathan Alo Date: Wed, 2 Jun 2021 15:50:58 +0800 Subject: [PATCH 664/839] implement BeatmapBackgroundWithStoryboard --- .../BeatmapBackgroundWithStoryboard.cs | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs diff --git a/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs b/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs new file mode 100644 index 0000000000..20bf15af22 --- /dev/null +++ b/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs @@ -0,0 +1,40 @@ +// 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.Timing; +using osu.Game.Beatmaps; +using osu.Game.Storyboards.Drawables; + +namespace osu.Game.Graphics.Backgrounds +{ + public class BeatmapBackgroundWithStoryboard : BeatmapBackground + { + private DrawableStoryboard storyboard; + + public BeatmapBackgroundWithStoryboard(WorkingBeatmap beatmap, string fallbackTextureName = "Backgrounds/bg1") + : base(beatmap, fallbackTextureName) + { + } + + [BackgroundDependencyLoader] + private void load() + { + var clock = new InterpolatingFramedClock(Beatmap.Track); + LoadComponentAsync(new DrawableStoryboard(Beatmap.Storyboard) + { + Alpha = 0, + Clock = clock, + }, + loaded => + { + AddInternal(storyboard = loaded); + storyboard.FadeIn(300, Easing.OutQuint); + + if (Beatmap.Storyboard.ReplacesBackground) + Sprite.FadeOut(300, Easing.OutQuint); + }); + } + } +} From dde64adcb5102d0ea358e9eae8d9106a4b33b316 Mon Sep 17 00:00:00 2001 From: Nathan Alo Date: Wed, 2 Jun 2021 15:51:43 +0800 Subject: [PATCH 665/839] add new background type in BackgroundScreenDefault --- osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs index bd4577fd57..81968db320 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs @@ -110,6 +110,10 @@ namespace osu.Game.Screens.Backgrounds newBackground = new BeatmapBackground(beatmap.Value, backgroundName); break; + case BackgroundSource.BeatmapWithStoryboard: + newBackground = new BeatmapBackgroundWithStoryboard(beatmap.Value, backgroundName); + break; + default: newBackground = new SkinnedBackground(skin.Value, backgroundName); break; From e66f6e8f91cd6783544d2bb1ea501d3f297e39aa Mon Sep 17 00:00:00 2001 From: Nathan Alo Date: Wed, 2 Jun 2021 16:12:41 +0800 Subject: [PATCH 666/839] fix inspect code issues and cleanup code --- .../BeatmapBackgroundWithStoryboard.cs | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs b/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs index 20bf15af22..0afdc52e49 100644 --- a/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs +++ b/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs @@ -11,8 +11,6 @@ namespace osu.Game.Graphics.Backgrounds { public class BeatmapBackgroundWithStoryboard : BeatmapBackground { - private DrawableStoryboard storyboard; - public BeatmapBackgroundWithStoryboard(WorkingBeatmap beatmap, string fallbackTextureName = "Backgrounds/bg1") : base(beatmap, fallbackTextureName) { @@ -21,20 +19,19 @@ namespace osu.Game.Graphics.Backgrounds [BackgroundDependencyLoader] private void load() { - var clock = new InterpolatingFramedClock(Beatmap.Track); LoadComponentAsync(new DrawableStoryboard(Beatmap.Storyboard) - { - Alpha = 0, - Clock = clock, - }, - loaded => - { - AddInternal(storyboard = loaded); - storyboard.FadeIn(300, Easing.OutQuint); + { + Alpha = 0, + Clock = new InterpolatingFramedClock(Beatmap.Track), + }, + loaded => + { + AddInternal(loaded); + loaded.FadeIn(300, Easing.OutQuint); - if (Beatmap.Storyboard.ReplacesBackground) - Sprite.FadeOut(300, Easing.OutQuint); - }); + if (Beatmap.Storyboard.ReplacesBackground) + Sprite.FadeOut(300, Easing.OutQuint); + }); } } } From 3c3ef1363209db1ac261f37f35f5fba7f5a041ba Mon Sep 17 00:00:00 2001 From: Nathan Alo Date: Wed, 2 Jun 2021 16:28:22 +0800 Subject: [PATCH 667/839] remove fade --- .../Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs b/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs index 0afdc52e49..21c536ee30 100644 --- a/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs +++ b/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs @@ -21,14 +21,11 @@ namespace osu.Game.Graphics.Backgrounds { LoadComponentAsync(new DrawableStoryboard(Beatmap.Storyboard) { - Alpha = 0, Clock = new InterpolatingFramedClock(Beatmap.Track), }, loaded => { AddInternal(loaded); - loaded.FadeIn(300, Easing.OutQuint); - if (Beatmap.Storyboard.ReplacesBackground) Sprite.FadeOut(300, Easing.OutQuint); }); From e0eb0adb0af1aa7b30b875fa7aa8e5df756af96d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 2 Jun 2021 18:32:14 +0900 Subject: [PATCH 668/839] Remove unnecessary bind in `ReplayPlayer` --- osu.Game/Screens/Play/ReplayPlayer.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs index e440e7d34e..f70c05c2ff 100644 --- a/osu.Game/Screens/Play/ReplayPlayer.cs +++ b/osu.Game/Screens/Play/ReplayPlayer.cs @@ -34,9 +34,6 @@ namespace osu.Game.Screens.Play protected override void PrepareReplay() { DrawableRuleset?.SetReplayScore(Score); - - // todo: move to base class along with Score? - ScoreProcessor.NewJudgement += result => ScoreProcessor.PopulateScore(Score.ScoreInfo); } protected override Score CreateScore() => createScore(GameplayBeatmap.PlayableBeatmap, Mods.Value); From cde8de154d10e5470dac1f974938e861d2b8db72 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 2 Jun 2021 19:11:09 +0900 Subject: [PATCH 669/839] Remove unused test property for now --- osu.Game/Tests/Visual/TestReplayPlayer.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/osu.Game/Tests/Visual/TestReplayPlayer.cs b/osu.Game/Tests/Visual/TestReplayPlayer.cs index ac47d186eb..da302d018d 100644 --- a/osu.Game/Tests/Visual/TestReplayPlayer.cs +++ b/osu.Game/Tests/Visual/TestReplayPlayer.cs @@ -3,9 +3,7 @@ using System.Collections.Generic; using System.Linq; -using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; @@ -38,8 +36,6 @@ namespace osu.Game.Tests.Visual public new bool PauseCooldownActive => base.PauseCooldownActive; - public readonly List Results = new List(); - /// /// Instantiate a replay player that renders an autoplay mod. /// @@ -65,11 +61,5 @@ namespace osu.Game.Tests.Visual { PauseOnFocusLost = pauseOnFocusLost; } - - [BackgroundDependencyLoader] - private void load() - { - ScoreProcessor.NewJudgement += r => Results.Add(r); - } } } From 277545bb0652c8e2e56c474293200b5b2c1755da Mon Sep 17 00:00:00 2001 From: Nathan Alo Date: Wed, 2 Jun 2021 20:27:12 +0800 Subject: [PATCH 670/839] refactor BeatmapBackgroundWithStoryboard to reduce overhead This avoids loading the sprite if its not needed and instead of hiding it, it is removed when the storyboard replaces the background or there is a video. This also only initializes DrawableStoryboard if there are any elements in any layer. --- .../Graphics/Backgrounds/BeatmapBackground.cs | 11 +++++-- .../BeatmapBackgroundWithStoryboard.cs | 29 ++++++++++--------- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/osu.Game/Graphics/Backgrounds/BeatmapBackground.cs b/osu.Game/Graphics/Backgrounds/BeatmapBackground.cs index 058d2ed0f9..2a4c5e194b 100644 --- a/osu.Game/Graphics/Backgrounds/BeatmapBackground.cs +++ b/osu.Game/Graphics/Backgrounds/BeatmapBackground.cs @@ -13,16 +13,21 @@ namespace osu.Game.Graphics.Backgrounds private readonly string fallbackTextureName; + [Resolved] + private LargeTextureStore textures { get; set; } + public BeatmapBackground(WorkingBeatmap beatmap, string fallbackTextureName = @"Backgrounds/bg1") { Beatmap = beatmap; this.fallbackTextureName = fallbackTextureName; } - [BackgroundDependencyLoader] - private void load(LargeTextureStore textures) + protected override void LoadComplete() { - Sprite.Texture = Beatmap?.Background ?? textures.Get(fallbackTextureName); + base.LoadComplete(); + Initialize(); } + + protected virtual void Initialize() => Sprite.Texture = Beatmap?.Background ?? textures.Get(fallbackTextureName); } } diff --git a/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs b/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs index 21c536ee30..1925cb927e 100644 --- a/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs +++ b/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs @@ -1,8 +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 osu.Framework.Allocation; -using osu.Framework.Graphics; +using System.Linq; using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Storyboards.Drawables; @@ -16,19 +15,21 @@ namespace osu.Game.Graphics.Backgrounds { } - [BackgroundDependencyLoader] - private void load() + protected override void Initialize() { - LoadComponentAsync(new DrawableStoryboard(Beatmap.Storyboard) - { - Clock = new InterpolatingFramedClock(Beatmap.Track), - }, - loaded => - { - AddInternal(loaded); - if (Beatmap.Storyboard.ReplacesBackground) - Sprite.FadeOut(300, Easing.OutQuint); - }); + if (Beatmap.Storyboard.HasDrawable) + { + LoadComponentAsync(new DrawableStoryboard(Beatmap.Storyboard) { Clock = new InterpolatingFramedClock(Beatmap.Track) }, AddInternal); + } + + if (Beatmap.Storyboard.ReplacesBackground || Beatmap.Storyboard.Layers.First(l => l.Name == "Video").Elements.Any()) + { + Sprite.Expire(); + } + else + { + base.Initialize(); + } } } } From d6656047e3e46a16abf8c2647237acb9e3b97e22 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 3 Jun 2021 01:58:50 +0900 Subject: [PATCH 671/839] Fix beatmap statistics with value of zero not displaying correctly at song select Closes #13307. --- osu.Game/Screens/Select/Details/AdvancedStats.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index b0084735b1..53e30fd9ca 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -186,11 +186,11 @@ namespace osu.Game.Screens.Select.Details set => name.Text = value; } - private (float baseValue, float? adjustedValue) value; + private (float baseValue, float? adjustedValue)? value; public (float baseValue, float? adjustedValue) Value { - get => value; + get => value ?? (0, null); set { if (value == this.value) From a62dd7cca089b65990f273d185bd561699552a2a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 3 Jun 2021 12:33:16 +0900 Subject: [PATCH 672/839] Revert "refactor BeatmapBackgroundWithStoryboard to reduce overhead" This reverts commit 277545bb0652c8e2e56c474293200b5b2c1755da. --- .../Graphics/Backgrounds/BeatmapBackground.cs | 11 ++----- .../BeatmapBackgroundWithStoryboard.cs | 29 +++++++++---------- 2 files changed, 17 insertions(+), 23 deletions(-) diff --git a/osu.Game/Graphics/Backgrounds/BeatmapBackground.cs b/osu.Game/Graphics/Backgrounds/BeatmapBackground.cs index 2a4c5e194b..058d2ed0f9 100644 --- a/osu.Game/Graphics/Backgrounds/BeatmapBackground.cs +++ b/osu.Game/Graphics/Backgrounds/BeatmapBackground.cs @@ -13,21 +13,16 @@ namespace osu.Game.Graphics.Backgrounds private readonly string fallbackTextureName; - [Resolved] - private LargeTextureStore textures { get; set; } - public BeatmapBackground(WorkingBeatmap beatmap, string fallbackTextureName = @"Backgrounds/bg1") { Beatmap = beatmap; this.fallbackTextureName = fallbackTextureName; } - protected override void LoadComplete() + [BackgroundDependencyLoader] + private void load(LargeTextureStore textures) { - base.LoadComplete(); - Initialize(); + Sprite.Texture = Beatmap?.Background ?? textures.Get(fallbackTextureName); } - - protected virtual void Initialize() => Sprite.Texture = Beatmap?.Background ?? textures.Get(fallbackTextureName); } } diff --git a/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs b/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs index 1925cb927e..21c536ee30 100644 --- a/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs +++ b/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs @@ -1,7 +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.Linq; +using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Storyboards.Drawables; @@ -15,21 +16,19 @@ namespace osu.Game.Graphics.Backgrounds { } - protected override void Initialize() + [BackgroundDependencyLoader] + private void load() { - if (Beatmap.Storyboard.HasDrawable) - { - LoadComponentAsync(new DrawableStoryboard(Beatmap.Storyboard) { Clock = new InterpolatingFramedClock(Beatmap.Track) }, AddInternal); - } - - if (Beatmap.Storyboard.ReplacesBackground || Beatmap.Storyboard.Layers.First(l => l.Name == "Video").Elements.Any()) - { - Sprite.Expire(); - } - else - { - base.Initialize(); - } + LoadComponentAsync(new DrawableStoryboard(Beatmap.Storyboard) + { + Clock = new InterpolatingFramedClock(Beatmap.Track), + }, + loaded => + { + AddInternal(loaded); + if (Beatmap.Storyboard.ReplacesBackground) + Sprite.FadeOut(300, Easing.OutQuint); + }); } } } From d00fb2118840e324cae17f52b481d81e0a7b9012 Mon Sep 17 00:00:00 2001 From: Nathan Alo Date: Thu, 3 Jun 2021 13:24:21 +0800 Subject: [PATCH 673/839] prevent scaling container from creating a storyboard background --- osu.Game/Graphics/Containers/ScalingContainer.cs | 2 ++ osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/ScalingContainer.cs b/osu.Game/Graphics/Containers/ScalingContainer.cs index 2488fd14d0..d2b1e5e523 100644 --- a/osu.Game/Graphics/Containers/ScalingContainer.cs +++ b/osu.Game/Graphics/Containers/ScalingContainer.cs @@ -172,6 +172,8 @@ namespace osu.Game.Graphics.Containers private class ScalingBackgroundScreen : BackgroundScreenDefault { + protected override bool AllowStoryboardBackground => false; + public override void OnEntering(IScreen last) { this.FadeInFromZero(4000, Easing.OutQuint); diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs index 81968db320..b02e7ddb0d 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs @@ -31,6 +31,8 @@ namespace osu.Game.Screens.Backgrounds [Resolved] private IBindable beatmap { get; set; } + protected virtual bool AllowStoryboardBackground => true; + public BackgroundScreenDefault(bool animateOnEnter = true) : base(animateOnEnter) { @@ -111,7 +113,9 @@ namespace osu.Game.Screens.Backgrounds break; case BackgroundSource.BeatmapWithStoryboard: - newBackground = new BeatmapBackgroundWithStoryboard(beatmap.Value, backgroundName); + newBackground = AllowStoryboardBackground + ? new BeatmapBackgroundWithStoryboard(beatmap.Value, backgroundName) + : new BeatmapBackground(beatmap.Value, backgroundName); break; default: From 62b07fb9ceadace33eb2801e0ff7461089d4ebea Mon Sep 17 00:00:00 2001 From: Nathan Alo Date: Thu, 3 Jun 2021 13:27:00 +0800 Subject: [PATCH 674/839] apply suggestions - Replace the sprite with a solid black box when a storyboard requests it. - Create a new storyboard instance and exclude the fail layer as well as strip all samples from it - Do not attempt in creating the storyboard when it isn't needed --- .../BeatmapBackgroundWithStoryboard.cs | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs b/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs index 21c536ee30..1936482c90 100644 --- a/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs +++ b/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs @@ -1,10 +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 System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Textures; using osu.Framework.Timing; using osu.Game.Beatmaps; +using osu.Game.Storyboards; using osu.Game.Storyboards.Drawables; namespace osu.Game.Graphics.Backgrounds @@ -19,16 +22,23 @@ namespace osu.Game.Graphics.Backgrounds [BackgroundDependencyLoader] private void load() { - LoadComponentAsync(new DrawableStoryboard(Beatmap.Storyboard) - { - Clock = new InterpolatingFramedClock(Beatmap.Track), - }, - loaded => - { - AddInternal(loaded); - if (Beatmap.Storyboard.ReplacesBackground) - Sprite.FadeOut(300, Easing.OutQuint); - }); + if (!Beatmap.Storyboard.HasDrawable) + return; + + var storyboard = new Storyboard { BeatmapInfo = Beatmap.BeatmapInfo }; + foreach (var layer in storyboard.Layers) + { + if (layer.Name != "Fail") + layer.Elements = Beatmap.Storyboard.GetLayer(layer.Name).Elements.Where(e => !(e is StoryboardSampleInfo)).ToList(); + } + + if (storyboard.ReplacesBackground) + { + Sprite.Texture = Texture.WhitePixel; + Sprite.Colour = Colour4.Black; + } + + LoadComponentAsync(new DrawableStoryboard(storyboard) { Clock = new InterpolatingFramedClock(Beatmap.Track) }, AddInternal); } } } From 66dd7b77055cfe297273148bf308372a025f17a8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 3 Jun 2021 14:37:38 +0900 Subject: [PATCH 675/839] Update test logic to allow gameplay to properly continue --- .../Visual/Navigation/TestSceneScreenNavigation.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index dd05ce9b7e..0308d74aa4 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -12,6 +12,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; using osu.Game.Overlays.Mods; using osu.Game.Overlays.Toolbar; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Screens.Play; using osu.Game.Screens.Ranking; @@ -95,11 +96,12 @@ namespace osu.Game.Tests.Visual.Navigation AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault); - AddStep("set nofail", () => Game.SelectedMods.Value = new[] { new OsuModNoFail() }); + AddStep("set mods", () => Game.SelectedMods.Value = new Mod[] { new OsuModNoFail(), new OsuModDoubleTime { SpeedChange = { Value = 2 } } }); AddStep("press enter", () => InputManager.Key(Key.Enter)); AddUntilStep("wait for player", () => (player = Game.ScreenStack.CurrentScreen as Player) != null); - AddStep("seek to end", () => player.ChildrenOfType().First().Seek(beatmap().Track.Length)); + AddUntilStep("wait for track playing", () => beatmap().Track.IsRunning); + AddStep("seek to near end", () => player.ChildrenOfType().First().Seek(beatmap().Beatmap.HitObjects[^1].StartTime - 1000)); AddUntilStep("wait for pass", () => (results = Game.ScreenStack.CurrentScreen as ResultsScreen) != null && results.IsLoaded); AddStep("attempt to retry", () => results.ChildrenOfType().First().Action()); AddUntilStep("wait for player", () => Game.ScreenStack.CurrentScreen != player && Game.ScreenStack.CurrentScreen is Player); From 94701b77cb261963a66106c0b4270a59de6dfc24 Mon Sep 17 00:00:00 2001 From: Samuel Cattini-Schultz Date: Thu, 3 Jun 2021 15:44:28 +1000 Subject: [PATCH 676/839] Add TODO for variable clockrate support in catch difficulty calculator --- osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs index 7d61be7bb1..83db9216ed 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs @@ -39,6 +39,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills // In catch, rate adjustment mods do not only affect the timings of hitobjects, // but also the speed of the player's catcher, which has an impact on difficulty + // TODO: Support variable clockrates caused by mods such as ModTimeRamp + // (perhaps by using IApplicableToRate within the CatchDifficultyHitObject constructor to set a catcher speed for each object before processing) var rateAdjustMod = mods.FirstOrDefault(m => m is ModRateAdjust); catcherSpeedMultiplier = (rateAdjustMod as ModRateAdjust)?.SpeedChange.Value ?? 1; } From d7d0dde5d27c84cfb2e7380158d1c4e26b1cd40a Mon Sep 17 00:00:00 2001 From: Nathan Alo Date: Thu, 3 Jun 2021 13:56:14 +0800 Subject: [PATCH 677/839] use created storyboard to check for drawables instead --- .../Backgrounds/BeatmapBackgroundWithStoryboard.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs b/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs index 1936482c90..9695e93f5d 100644 --- a/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs +++ b/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs @@ -22,16 +22,17 @@ namespace osu.Game.Graphics.Backgrounds [BackgroundDependencyLoader] private void load() { - if (!Beatmap.Storyboard.HasDrawable) - return; - var storyboard = new Storyboard { BeatmapInfo = Beatmap.BeatmapInfo }; + foreach (var layer in storyboard.Layers) { if (layer.Name != "Fail") layer.Elements = Beatmap.Storyboard.GetLayer(layer.Name).Elements.Where(e => !(e is StoryboardSampleInfo)).ToList(); } + if (!storyboard.HasDrawable) + return; + if (storyboard.ReplacesBackground) { Sprite.Texture = Texture.WhitePixel; From a6cc37eb3b7a84de7159ea1e4f270f3f157419a0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 3 Jun 2021 14:56:21 +0900 Subject: [PATCH 678/839] Mark fields `readonly` --- osu.Game/Online/Chat/ChannelManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index e5e38e425f..f6804fdbf7 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -587,8 +587,8 @@ namespace osu.Game.Online.Chat /// public class ClosedChannel { - public ChannelType Type; - public long Id; + public readonly ChannelType Type; + public readonly long Id; public ClosedChannel(ChannelType type, long id) { From a91015302e5de78319fcddad63ad6888def53076 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 3 Jun 2021 14:56:29 +0900 Subject: [PATCH 679/839] Replace second usage of new function --- osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs index c314e10aff..c0de093425 100644 --- a/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs +++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs @@ -81,9 +81,11 @@ namespace osu.Game.Overlays.Chat.Tabs RemoveItem(channel); if (SelectedTab == null) - SelectTab(selectorTab); + SelectChannelSelectorTab(); } + public void SelectChannelSelectorTab() => SelectTab(selectorTab); + protected override void SelectTab(TabItem tab) { if (tab is ChannelSelectorTabItem) @@ -96,11 +98,6 @@ namespace osu.Game.Overlays.Chat.Tabs selectorTab.Active.Value = false; } - public void SelectChannelSelectorTab() - { - SelectTab(selectorTab); - } - protected override TabFillFlowContainer CreateTabFlow() => new ChannelTabFillFlowContainer { Direction = FillDirection.Full, From fe2934db1daa6681bae7342782fc8b7635b4cec8 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 31 May 2021 22:57:43 +0900 Subject: [PATCH 680/839] Factor out lifetime management logic of HitObjectContainer --- .../PooledDrawableWithLifetimeContainer.cs | 157 ++++++++++++++++++ osu.Game/Rulesets/UI/HitObjectContainer.cs | 94 ++--------- 2 files changed, 175 insertions(+), 76 deletions(-) create mode 100644 osu.Game/Rulesets/Objects/Pooling/PooledDrawableWithLifetimeContainer.cs diff --git a/osu.Game/Rulesets/Objects/Pooling/PooledDrawableWithLifetimeContainer.cs b/osu.Game/Rulesets/Objects/Pooling/PooledDrawableWithLifetimeContainer.cs new file mode 100644 index 0000000000..656e4a9dd6 --- /dev/null +++ b/osu.Game/Rulesets/Objects/Pooling/PooledDrawableWithLifetimeContainer.cs @@ -0,0 +1,157 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Performance; + +namespace osu.Game.Rulesets.Objects.Pooling +{ + /// + /// A container of s dynamically added/removed by model s. + /// When an entry became alive, a drawable corresponding to the entry is obtained (potentially pooled), and added to this container. + /// The drawable is removed when the entry became dead. + /// + /// The type of entries managed by this container. + /// The type of drawables corresponding to the entries. + public abstract class PooledDrawableWithLifetimeContainer : CompositeDrawable + where TEntry : LifetimeEntry + where TDrawable : Drawable + { + /// + /// All entries added to this container, including dead entries. + /// + /// + /// The enumeration order is undefined. + /// + public IEnumerable Entries => allEntries; + + /// + /// All alive entries and drawables corresponding to the entries. + /// + /// + /// The enumeration order is undefined. + /// + public IEnumerable<(TEntry Entry, TDrawable Drawable)> AliveEntries => aliveDrawableMap.Select(x => (x.Key, x.Value)); + + /// + /// The amount of time prior to the current time within which entries should be considered alive. + /// + internal double PastLifetimeExtension { get; set; } + + /// + /// The amount of time after the current time within which entries should be considered alive. + /// + internal double FutureLifetimeExtension { get; set; } + + private readonly Dictionary aliveDrawableMap = new Dictionary(); + private readonly HashSet allEntries = new HashSet(); + + private readonly LifetimeEntryManager lifetimeManager = new LifetimeEntryManager(); + + protected PooledDrawableWithLifetimeContainer() + { + lifetimeManager.EntryBecameAlive += entryBecameAlive; + lifetimeManager.EntryBecameDead += entryBecameDead; + lifetimeManager.EntryCrossedBoundary += entryCrossedBoundary; + } + + /// + /// Add a to be managed by this container. + /// + /// + /// The aliveness of the entry is not updated until . + /// + public virtual void Add(TEntry entry) + { + allEntries.Add(entry); + lifetimeManager.AddEntry(entry); + } + + /// + /// Remove a from this container. + /// + /// + /// If the entry was alive, the corresponding drawable is removed. + /// + /// Whether the entry was in this container. + public virtual bool Remove(TEntry entry) + { + if (!lifetimeManager.RemoveEntry(entry)) return false; + + allEntries.Remove(entry); + return true; + } + + /// + /// Initialize new corresponding . + /// + /// The corresponding to the entry. + protected abstract TDrawable GetDrawable(TEntry entry); + + private void entryBecameAlive(LifetimeEntry lifetimeEntry) + { + var entry = (TEntry)lifetimeEntry; + Debug.Assert(!aliveDrawableMap.ContainsKey(entry)); + + TDrawable drawable = GetDrawable(entry); + aliveDrawableMap[entry] = drawable; + AddDrawable(entry, drawable); + } + + /// + /// Add a corresponding to to this container. + /// + /// + /// Invoked when the entry became alive and a is obtained by . + /// + protected virtual void AddDrawable(TEntry entry, TDrawable drawable) => AddInternal(drawable); + + private void entryBecameDead(LifetimeEntry lifetimeEntry) + { + var entry = (TEntry)lifetimeEntry; + Debug.Assert(aliveDrawableMap.ContainsKey(entry)); + + TDrawable drawable = aliveDrawableMap[entry]; + aliveDrawableMap.Remove(entry); + RemoveDrawable(entry, drawable); + } + + /// + /// Remove a corresponding to from this container. + /// + /// + /// Invoked when the entry became dead. + /// + protected virtual void RemoveDrawable(TEntry entry, TDrawable drawable) => RemoveInternal(drawable); + + private void entryCrossedBoundary(LifetimeEntry lifetimeEntry, LifetimeBoundaryKind kind, LifetimeBoundaryCrossingDirection direction) => + OnEntryCrossedBoundary((TEntry)lifetimeEntry, kind, direction); + + protected virtual void OnEntryCrossedBoundary(TEntry entry, LifetimeBoundaryKind kind, LifetimeBoundaryCrossingDirection direction) + { + } + + /// + /// Remove all s. + /// + public virtual void Clear() + { + foreach (var entry in Entries.ToArray()) + Remove(entry); + Debug.Assert(aliveDrawableMap.Count == 0); + } + + protected override bool CheckChildrenLife() + { + bool aliveChanged = base.CheckChildrenLife(); + aliveChanged |= lifetimeManager.Update(Time.Current - PastLifetimeExtension, Time.Current + FutureLifetimeExtension); + return aliveChanged; + } + } +} diff --git a/osu.Game/Rulesets/UI/HitObjectContainer.cs b/osu.Game/Rulesets/UI/HitObjectContainer.cs index 83033b2dd5..ff7a368c0c 100644 --- a/osu.Game/Rulesets/UI/HitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/HitObjectContainer.cs @@ -14,24 +14,15 @@ using osu.Framework.Graphics.Performance; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Objects.Pooling; namespace osu.Game.Rulesets.UI { - public class HitObjectContainer : CompositeDrawable, IHitObjectContainer + public class HitObjectContainer : PooledDrawableWithLifetimeContainer, IHitObjectContainer { - /// - /// All entries in this including dead entries. - /// - public IEnumerable Entries => allEntries; - - /// - /// All alive entries and s used by the entries. - /// - public IEnumerable<(HitObjectLifetimeEntry Entry, DrawableHitObject Drawable)> AliveEntries => aliveDrawableMap.Select(x => (x.Key, x.Value)); - public IEnumerable Objects => InternalChildren.Cast().OrderBy(h => h.HitObject.StartTime); - public IEnumerable AliveObjects => AliveInternalChildren.Cast().OrderBy(h => h.HitObject.StartTime); + public IEnumerable AliveObjects => AliveEntries.Select(pair => pair.Drawable).OrderBy(h => h.HitObject.StartTime); /// /// Invoked when a is judged. @@ -59,34 +50,16 @@ namespace osu.Game.Rulesets.UI /// internal event Action HitObjectUsageFinished; - /// - /// The amount of time prior to the current time within which s should be considered alive. - /// - internal double PastLifetimeExtension { get; set; } - - /// - /// The amount of time after the current time within which s should be considered alive. - /// - internal double FutureLifetimeExtension { get; set; } - private readonly Dictionary startTimeMap = new Dictionary(); - private readonly Dictionary aliveDrawableMap = new Dictionary(); private readonly Dictionary nonPooledDrawableMap = new Dictionary(); - private readonly LifetimeEntryManager lifetimeManager = new LifetimeEntryManager(); - private readonly HashSet allEntries = new HashSet(); - [Resolved(CanBeNull = true)] private IPooledHitObjectProvider pooledObjectProvider { get; set; } public HitObjectContainer() { RelativeSizeAxes = Axes.Both; - - lifetimeManager.EntryBecameAlive += entryBecameAlive; - lifetimeManager.EntryBecameDead += entryBecameDead; - lifetimeManager.EntryCrossedBoundary += entryCrossedBoundary; } protected override void LoadAsyncComplete() @@ -99,37 +72,29 @@ namespace osu.Game.Rulesets.UI #region Pooling support - public void Add(HitObjectLifetimeEntry entry) + public override bool Remove(HitObjectLifetimeEntry entry) { - allEntries.Add(entry); - lifetimeManager.AddEntry(entry); - } - - public bool Remove(HitObjectLifetimeEntry entry) - { - if (!lifetimeManager.RemoveEntry(entry)) return false; + if (!base.Remove(entry)) return false; // This logic is not in `Remove(DrawableHitObject)` because a non-pooled drawable may be removed by specifying its entry. if (nonPooledDrawableMap.Remove(entry, out var drawable)) removeDrawable(drawable); - allEntries.Remove(entry); return true; } - private void entryBecameAlive(LifetimeEntry lifetimeEntry) + protected sealed override DrawableHitObject GetDrawable(HitObjectLifetimeEntry entry) { - var entry = (HitObjectLifetimeEntry)lifetimeEntry; - Debug.Assert(!aliveDrawableMap.ContainsKey(entry)); + if (nonPooledDrawableMap.TryGetValue(entry, out var drawable)) + return drawable; - bool isPooled = !nonPooledDrawableMap.TryGetValue(entry, out var drawable); - drawable ??= pooledObjectProvider?.GetPooledDrawableRepresentation(entry.HitObject, null); - if (drawable == null) - throw new InvalidOperationException($"A drawable representation could not be retrieved for hitobject type: {entry.HitObject.GetType().ReadableName()}."); + return pooledObjectProvider?.GetPooledDrawableRepresentation(entry.HitObject, null) ?? + throw new InvalidOperationException($"A drawable representation could not be retrieved for hitobject type: {entry.HitObject.GetType().ReadableName()}."); + } - aliveDrawableMap[entry] = drawable; - - if (isPooled) + protected override void AddDrawable(HitObjectLifetimeEntry entry, DrawableHitObject drawable) + { + if (!nonPooledDrawableMap.ContainsKey(entry)) { addDrawable(drawable); HitObjectUsageBegan?.Invoke(entry.HitObject); @@ -138,18 +103,11 @@ namespace osu.Game.Rulesets.UI OnAdd(drawable); } - private void entryBecameDead(LifetimeEntry lifetimeEntry) + protected override void RemoveDrawable(HitObjectLifetimeEntry entry, DrawableHitObject drawable) { - var entry = (HitObjectLifetimeEntry)lifetimeEntry; - Debug.Assert(aliveDrawableMap.ContainsKey(entry)); - - var drawable = aliveDrawableMap[entry]; - bool isPooled = !nonPooledDrawableMap.ContainsKey(entry); - drawable.OnKilled(); - aliveDrawableMap.Remove(entry); - if (isPooled) + if (!nonPooledDrawableMap.ContainsKey(entry)) { removeDrawable(drawable); HitObjectUsageFinished?.Invoke(entry.HitObject); @@ -201,9 +159,9 @@ namespace osu.Game.Rulesets.UI public int IndexOf(DrawableHitObject hitObject) => IndexOfInternal(hitObject); - private void entryCrossedBoundary(LifetimeEntry entry, LifetimeBoundaryKind kind, LifetimeBoundaryCrossingDirection direction) + protected override void OnEntryCrossedBoundary(HitObjectLifetimeEntry entry, LifetimeBoundaryKind kind, LifetimeBoundaryCrossingDirection direction) { - if (nonPooledDrawableMap.TryGetValue((HitObjectLifetimeEntry)entry, out var drawable)) + if (nonPooledDrawableMap.TryGetValue(entry, out var drawable)) OnChildLifetimeBoundaryCrossed(new LifetimeBoundaryCrossedEvent(drawable, kind, direction)); } @@ -228,22 +186,6 @@ namespace osu.Game.Rulesets.UI { } - public virtual void Clear() - { - lifetimeManager.ClearEntries(); - foreach (var drawable in nonPooledDrawableMap.Values) - removeDrawable(drawable); - nonPooledDrawableMap.Clear(); - Debug.Assert(InternalChildren.Count == 0 && startTimeMap.Count == 0 && aliveDrawableMap.Count == 0, "All hit objects should have been removed"); - } - - protected override bool CheckChildrenLife() - { - bool aliveChanged = base.CheckChildrenLife(); - aliveChanged |= lifetimeManager.Update(Time.Current - PastLifetimeExtension, Time.Current + FutureLifetimeExtension); - return aliveChanged; - } - private void onNewResult(DrawableHitObject d, JudgementResult r) => NewResult?.Invoke(d, r); private void onRevertResult(DrawableHitObject d, JudgementResult r) => RevertResult?.Invoke(d, r); From 2c9e5b6c7e7b965ac08325b76345522777442b49 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 31 May 2021 23:00:12 +0900 Subject: [PATCH 681/839] Replace `EntryCrossedBoundary` with more useful `RemoveRewoundEntry` property It can be used for dynamically added entries. --- .../UI/DrumRollHitContainer.cs | 17 +++++------------ .../PooledDrawableWithLifetimeContainer.cs | 13 +++++++++---- osu.Game/Rulesets/UI/HitObjectContainer.cs | 12 ------------ 3 files changed, 14 insertions(+), 28 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/DrumRollHitContainer.cs b/osu.Game.Rulesets.Taiko/UI/DrumRollHitContainer.cs index 9bfb6aa839..263454c78a 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrumRollHitContainer.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrumRollHitContainer.cs @@ -1,9 +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 osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Performance; -using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Taiko.Objects.Drawables; using osu.Game.Rulesets.UI.Scrolling; @@ -11,6 +8,11 @@ namespace osu.Game.Rulesets.Taiko.UI { internal class DrumRollHitContainer : ScrollingHitObjectContainer { + // TODO: this usage is buggy. + // Because `LifetimeStart` is set based on scrolling, lifetime is not same as the time when the object is created. + // If the `Update` override is removed, it breaks in an obscure way. + protected override bool RemoveRewoundEntry => true; + protected override void Update() { base.Update(); @@ -23,14 +25,5 @@ namespace osu.Game.Rulesets.Taiko.UI Remove(flyingHit); } } - - protected override void OnChildLifetimeBoundaryCrossed(LifetimeBoundaryCrossedEvent e) - { - base.OnChildLifetimeBoundaryCrossed(e); - - // ensure all old hits are removed on becoming alive (may miss being in the AliveInternalChildren list above). - if (e.Kind == LifetimeBoundaryKind.Start && e.Direction == LifetimeBoundaryCrossingDirection.Backward) - Remove((DrawableHitObject)e.Child); - } } } diff --git a/osu.Game/Rulesets/Objects/Pooling/PooledDrawableWithLifetimeContainer.cs b/osu.Game/Rulesets/Objects/Pooling/PooledDrawableWithLifetimeContainer.cs index 656e4a9dd6..5fe9d889a1 100644 --- a/osu.Game/Rulesets/Objects/Pooling/PooledDrawableWithLifetimeContainer.cs +++ b/osu.Game/Rulesets/Objects/Pooling/PooledDrawableWithLifetimeContainer.cs @@ -39,6 +39,12 @@ namespace osu.Game.Rulesets.Objects.Pooling /// public IEnumerable<(TEntry Entry, TDrawable Drawable)> AliveEntries => aliveDrawableMap.Select(x => (x.Key, x.Value)); + /// + /// Whether to remove an entry when clock goes backward and crossed its . + /// Used when entries are dynamically added at its to prevent duplicated entries. + /// + protected virtual bool RemoveRewoundEntry => false; + /// /// The amount of time prior to the current time within which entries should be considered alive. /// @@ -130,11 +136,10 @@ namespace osu.Game.Rulesets.Objects.Pooling /// protected virtual void RemoveDrawable(TEntry entry, TDrawable drawable) => RemoveInternal(drawable); - private void entryCrossedBoundary(LifetimeEntry lifetimeEntry, LifetimeBoundaryKind kind, LifetimeBoundaryCrossingDirection direction) => - OnEntryCrossedBoundary((TEntry)lifetimeEntry, kind, direction); - - protected virtual void OnEntryCrossedBoundary(TEntry entry, LifetimeBoundaryKind kind, LifetimeBoundaryCrossingDirection direction) + private void entryCrossedBoundary(LifetimeEntry lifetimeEntry, LifetimeBoundaryKind kind, LifetimeBoundaryCrossingDirection direction) { + if (RemoveRewoundEntry && kind == LifetimeBoundaryKind.Start && direction == LifetimeBoundaryCrossingDirection.Backward) + Remove((TEntry)lifetimeEntry); } /// diff --git a/osu.Game/Rulesets/UI/HitObjectContainer.cs b/osu.Game/Rulesets/UI/HitObjectContainer.cs index ff7a368c0c..917aa54673 100644 --- a/osu.Game/Rulesets/UI/HitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/HitObjectContainer.cs @@ -9,8 +9,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.TypeExtensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Performance; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; @@ -159,16 +157,6 @@ namespace osu.Game.Rulesets.UI public int IndexOf(DrawableHitObject hitObject) => IndexOfInternal(hitObject); - protected override void OnEntryCrossedBoundary(HitObjectLifetimeEntry entry, LifetimeBoundaryKind kind, LifetimeBoundaryCrossingDirection direction) - { - if (nonPooledDrawableMap.TryGetValue(entry, out var drawable)) - OnChildLifetimeBoundaryCrossed(new LifetimeBoundaryCrossedEvent(drawable, kind, direction)); - } - - protected virtual void OnChildLifetimeBoundaryCrossed(LifetimeBoundaryCrossedEvent e) - { - } - #endregion /// From 0ce7baa3f3e80b640cf6c8d07400bc3465509368 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 31 May 2021 23:01:54 +0900 Subject: [PATCH 682/839] Make `HitObjectContainer.Clear` non-virtual It just call `Remove` for all entries. --- .../Objects/Pooling/PooledDrawableWithLifetimeContainer.cs | 3 ++- .../Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs | 7 ------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Pooling/PooledDrawableWithLifetimeContainer.cs b/osu.Game/Rulesets/Objects/Pooling/PooledDrawableWithLifetimeContainer.cs index 5fe9d889a1..d35933dba8 100644 --- a/osu.Game/Rulesets/Objects/Pooling/PooledDrawableWithLifetimeContainer.cs +++ b/osu.Game/Rulesets/Objects/Pooling/PooledDrawableWithLifetimeContainer.cs @@ -145,10 +145,11 @@ namespace osu.Game.Rulesets.Objects.Pooling /// /// Remove all s. /// - public virtual void Clear() + public void Clear() { foreach (var entry in Entries.ToArray()) Remove(entry); + Debug.Assert(aliveDrawableMap.Count == 0); } diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index cde4182f2d..b60aabe0e6 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -45,13 +45,6 @@ namespace osu.Game.Rulesets.UI.Scrolling timeRange.ValueChanged += _ => layoutCache.Invalidate(); } - public override void Clear() - { - base.Clear(); - - layoutComputed.Clear(); - } - /// /// Given a position in screen space, return the time within this column. /// From b321b20e9d5087bd5b0a0a208d616d751dc9abc6 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 31 May 2021 23:07:32 +0900 Subject: [PATCH 683/839] Remove `OnAdd`/`OnRemove` of `HitObjectContainer` Instead, override `AddDrawable`/`RemoveDrawable`. --- osu.Game/Rulesets/UI/HitObjectContainer.cs | 35 ++++--------------- .../Scrolling/ScrollingHitObjectContainer.cs | 20 +++++++---- 2 files changed, 20 insertions(+), 35 deletions(-) diff --git a/osu.Game/Rulesets/UI/HitObjectContainer.cs b/osu.Game/Rulesets/UI/HitObjectContainer.cs index 917aa54673..fee77af0ba 100644 --- a/osu.Game/Rulesets/UI/HitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/HitObjectContainer.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -92,26 +91,19 @@ namespace osu.Game.Rulesets.UI protected override void AddDrawable(HitObjectLifetimeEntry entry, DrawableHitObject drawable) { - if (!nonPooledDrawableMap.ContainsKey(entry)) - { - addDrawable(drawable); - HitObjectUsageBegan?.Invoke(entry.HitObject); - } + if (nonPooledDrawableMap.ContainsKey(entry)) return; - OnAdd(drawable); + addDrawable(drawable); + HitObjectUsageBegan?.Invoke(entry.HitObject); } protected override void RemoveDrawable(HitObjectLifetimeEntry entry, DrawableHitObject drawable) { drawable.OnKilled(); + if (nonPooledDrawableMap.ContainsKey(entry)) return; - if (!nonPooledDrawableMap.ContainsKey(entry)) - { - removeDrawable(drawable); - HitObjectUsageFinished?.Invoke(entry.HitObject); - } - - OnRemove(drawable); + removeDrawable(drawable); + HitObjectUsageFinished?.Invoke(entry.HitObject); } private void addDrawable(DrawableHitObject drawable) @@ -159,21 +151,6 @@ namespace osu.Game.Rulesets.UI #endregion - /// - /// Invoked after a is added to this container. - /// - protected virtual void OnAdd(DrawableHitObject drawableHitObject) - { - Debug.Assert(drawableHitObject.LoadState >= LoadState.Ready); - } - - /// - /// Invoked after a is removed from this container. - /// - protected virtual void OnRemove(DrawableHitObject drawableHitObject) - { - } - private void onNewResult(DrawableHitObject d, JudgementResult r) => NewResult?.Invoke(d, r); private void onRevertResult(DrawableHitObject d, JudgementResult r) => RevertResult?.Invoke(d, r); diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index b60aabe0e6..f478e37e3e 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -2,10 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Layout; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; using osuTK; @@ -140,17 +142,20 @@ namespace osu.Game.Rulesets.UI.Scrolling } } - protected override void OnAdd(DrawableHitObject drawableHitObject) + protected override void AddDrawable(HitObjectLifetimeEntry entry, DrawableHitObject drawable) { - invalidateHitObject(drawableHitObject); - drawableHitObject.DefaultsApplied += invalidateHitObject; + base.AddDrawable(entry, drawable); + + invalidateHitObject(drawable); + drawable.DefaultsApplied += invalidateHitObject; } - protected override void OnRemove(DrawableHitObject drawableHitObject) + protected override void RemoveDrawable(HitObjectLifetimeEntry entry, DrawableHitObject drawable) { - layoutComputed.Remove(drawableHitObject); + base.RemoveDrawable(entry, drawable); - drawableHitObject.DefaultsApplied -= invalidateHitObject; + drawable.DefaultsApplied -= invalidateHitObject; + layoutComputed.Remove(drawable); } private void invalidateHitObject(DrawableHitObject hitObject) @@ -199,6 +204,9 @@ namespace osu.Game.Rulesets.UI.Scrolling private double computeOriginAdjustedLifetimeStart(DrawableHitObject hitObject) { + // Origin position may be relative to the parent size + Debug.Assert(hitObject.Parent != null); + float originAdjustment = 0.0f; // calculate the dimension of the part of the hitobject that should already be visible From e74fe68c9656947476ed88e6ef859b7a75de8c60 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 3 Jun 2021 15:04:07 +0900 Subject: [PATCH 684/839] Use new platform actions instead of hardcoded keys --- osu.Game/Overlays/ChatOverlay.cs | 50 ++++++++++++++++---------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 84653226cc..0aa6108815 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -24,13 +24,15 @@ using osu.Game.Overlays.Chat.Tabs; using osuTK.Input; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; +using osu.Framework.Input; +using osu.Framework.Input.Bindings; using osu.Framework.Localisation; using osu.Game.Localisation; using osu.Game.Online; namespace osu.Game.Overlays { - public class ChatOverlay : OsuFocusedOverlayContainer, INamedOverlayComponent + public class ChatOverlay : OsuFocusedOverlayContainer, INamedOverlayComponent, IKeyBindingHandler { public string IconTexture => "Icons/Hexacons/messaging"; public LocalisableString Title => ChatStrings.HeaderTitle; @@ -367,33 +369,31 @@ namespace osu.Game.Overlays } } - if (e.ControlPressed) - { - if (e.ShiftPressed) - { - switch (e.Key) - { - case Key.T: - channelManager.JoinLastClosedChannel(); - return true; - } - } - else - { - switch (e.Key) - { - case Key.W: - channelManager.LeaveChannel(channelManager.CurrentChannel.Value); - return true; + return base.OnKeyDown(e); + } - case Key.T: - ChannelTabControl.SelectChannelSelectorTab(); - return true; - } - } + public bool OnPressed(PlatformAction action) + { + switch (action.ActionType) + { + case PlatformActionType.TabNew: + ChannelTabControl.SelectChannelSelectorTab(); + return true; + + case PlatformActionType.TabRestore: + channelManager.JoinLastClosedChannel(); + return true; + + case PlatformActionType.DocumentClose: + channelManager.LeaveChannel(channelManager.CurrentChannel.Value); + return true; } - return base.OnKeyDown(e); + return false; + } + + public void OnReleased(PlatformAction action) + { } public override bool AcceptsFocus => true; From f51413ead9a0c1afba047526ff9fae183ff1c9db Mon Sep 17 00:00:00 2001 From: Samuel Cattini-Schultz Date: Thu, 3 Jun 2021 16:09:37 +1000 Subject: [PATCH 685/839] Refactor to pass clockrate in constructor rather than deriving from mods --- .../EmptyFreeformDifficultyCalculator.cs | 2 +- .../PippidonDifficultyCalculator.cs | 2 +- .../EmptyScrollingDifficultyCalculator.cs | 2 +- .../PippidonDifficultyCalculator.cs | 2 +- .../Difficulty/CatchDifficultyCalculator.cs | 4 ++-- osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs | 8 +++----- .../Difficulty/ManiaDifficultyCalculator.cs | 2 +- .../Difficulty/OsuDifficultyCalculator.cs | 2 +- .../Difficulty/TaikoDifficultyCalculator.cs | 2 +- .../NonVisual/DifficultyAdjustmentModCombinationsTest.cs | 2 +- osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs | 5 +++-- 11 files changed, 16 insertions(+), 17 deletions(-) diff --git a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformDifficultyCalculator.cs b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformDifficultyCalculator.cs index 59a68245a6..a80f1178b6 100644 --- a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformDifficultyCalculator.cs +++ b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformDifficultyCalculator.cs @@ -25,6 +25,6 @@ namespace osu.Game.Rulesets.EmptyFreeform protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty(); - protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[0]; + protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[0]; } } diff --git a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs index f6340f6c25..290148d14b 100644 --- a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs +++ b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs @@ -25,6 +25,6 @@ namespace osu.Game.Rulesets.Pippidon protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty(); - protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[0]; + protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[0]; } } diff --git a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingDifficultyCalculator.cs b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingDifficultyCalculator.cs index 7f29c4e712..f557a4c754 100644 --- a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingDifficultyCalculator.cs +++ b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingDifficultyCalculator.cs @@ -25,6 +25,6 @@ namespace osu.Game.Rulesets.EmptyScrolling protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty(); - protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[0]; + protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[0]; } } diff --git a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs index f6340f6c25..290148d14b 100644 --- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs +++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs @@ -25,6 +25,6 @@ namespace osu.Game.Rulesets.Pippidon protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty(); - protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[0]; + protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[0]; } } diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index f5cce47186..9feaa55051 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty } } - protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) + protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) { halfCatcherWidth = Catcher.CalculateCatchWidth(beatmap.BeatmapInfo.BaseDifficulty) * 0.5f; @@ -76,7 +76,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty return new Skill[] { - new Movement(mods, halfCatcherWidth), + new Movement(mods, halfCatcherWidth, clockRate), }; } diff --git a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs index 849af75228..4372ed938c 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Linq; using osu.Game.Rulesets.Catch.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; @@ -34,17 +33,16 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills /// private readonly double catcherSpeedMultiplier; - public Movement(Mod[] mods, float halfCatcherWidth) + public Movement(Mod[] mods, float halfCatcherWidth, double clockRate) : base(mods) { HalfCatcherWidth = halfCatcherWidth; - // In catch, rate adjustment mods do not only affect the timings of hitobjects, + // In catch, clockrate adjustments do not only affect the timings of hitobjects, // but also the speed of the player's catcher, which has an impact on difficulty // TODO: Support variable clockrates caused by mods such as ModTimeRamp // (perhaps by using IApplicableToRate within the CatchDifficultyHitObject constructor to set a catcher speed for each object before processing) - var rateAdjustMod = mods.FirstOrDefault(m => m is ModRateAdjust); - catcherSpeedMultiplier = (rateAdjustMod as ModRateAdjust)?.SpeedChange.Value ?? 1; + catcherSpeedMultiplier = clockRate; } protected override double StrainValueOf(DifficultyHitObject current) diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs index 8c0b9ed8b7..a7a6677b68 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs @@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty // Sorting is done in CreateDifficultyHitObjects, since the full list of hitobjects is required. protected override IEnumerable SortObjects(IEnumerable input) => input; - protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[] + protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[] { new Strain(mods, ((ManiaBeatmap)beatmap).TotalColumns) }; diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 75d6786d95..e47f82fb39 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -79,7 +79,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty } } - protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[] + protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[] { new Aim(mods), new Speed(mods) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index 6b3e31c5d5..18d06c069f 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty { } - protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[] + protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[] { new Colour(mods), new Rhythm(mods), diff --git a/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs b/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs index 16c1004f37..e458e66ab7 100644 --- a/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs +++ b/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs @@ -217,7 +217,7 @@ namespace osu.Game.Tests.NonVisual throw new NotImplementedException(); } - protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) + protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) { throw new NotImplementedException(); } diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index 5780fe39fa..3cc69bd85b 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Difficulty private DifficultyAttributes calculate(IBeatmap beatmap, Mod[] mods, double clockRate) { - var skills = CreateSkills(beatmap, mods); + var skills = CreateSkills(beatmap, mods, clockRate); if (!beatmap.HitObjects.Any()) return CreateDifficultyAttributes(beatmap, mods, skills, clockRate); @@ -180,7 +180,8 @@ namespace osu.Game.Rulesets.Difficulty /// /// The whose difficulty will be calculated. /// Mods to calculate difficulty with. + /// Clockrate to calculate difficulty with. /// The s. - protected abstract Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods); + protected abstract Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate); } } From b917d6d80c2edf191a6548762aca01e288e2562a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 3 Jun 2021 15:34:06 +0900 Subject: [PATCH 686/839] Update tests to check for correct platform action keys --- .../Visual/Online/TestSceneChatOverlay.cs | 55 +++++++++---------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index 8dbccde3d6..c40b60c35d 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -9,6 +9,8 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input; +using osu.Framework.Platform; using osu.Framework.Testing; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; @@ -38,6 +40,9 @@ namespace osu.Game.Tests.Visual.Online private Channel channel2 => channels[1]; private Channel channel3 => channels[2]; + [Resolved] + private GameHost host { get; set; } + public TestSceneChatOverlay() { channels = Enumerable.Range(1, 10) @@ -231,7 +236,7 @@ namespace osu.Game.Tests.Visual.Online } [Test] - public void TestCtrlWShortcut() + public void TestCloseTabShortcut() { AddStep("Join 2 channels", () => { @@ -241,7 +246,7 @@ namespace osu.Game.Tests.Visual.Online // Want to close channel 2 AddStep("Select channel 2", () => clickDrawable(chatOverlay.TabMap[channel2])); - AddStep("Press Ctrl + W", pressControlW); + AddStep("Press keys", pressCloseDocumentKeys); // Channel 2 should be closed AddAssert("Channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); @@ -250,13 +255,13 @@ namespace osu.Game.Tests.Visual.Online // Want to close channel 1 AddStep("Select channel 1", () => clickDrawable(chatOverlay.TabMap[channel1])); - AddStep("Press Ctrl + W", pressControlW); + AddStep("Press keys", pressCloseDocumentKeys); // Channel 1 and channel 2 should be closed AddAssert("All channels closed", () => !channelManager.JoinedChannels.Any()); } [Test] - public void TestCtrlTShortcut() + public void TestNewTabShortcut() { AddStep("Join 2 channels", () => { @@ -265,14 +270,14 @@ namespace osu.Game.Tests.Visual.Online }); // Want to join another channel - AddStep("Press Ctrl + T", pressControlT); + AddStep("Press keys", pressNewTabKeys); // Selector should be visible AddAssert("Selector is visible", () => chatOverlay.SelectionOverlayState == Visibility.Visible); } [Test] - public void TestCtrlShiftTShortcut() + public void TestRestoreTabShortcut() { AddStep("Join 3 channels", () => { @@ -282,7 +287,7 @@ namespace osu.Game.Tests.Visual.Online }); // Should do nothing - AddStep("Press Ctrl + Shift + T", pressControlShiftT); + AddStep("Press keys", pressRestoreTabKeys); AddAssert("All channels still open", () => channelManager.JoinedChannels.Count == 3); // Close channel 1 @@ -292,7 +297,7 @@ namespace osu.Game.Tests.Visual.Online AddAssert("Other channels still open", () => channelManager.JoinedChannels.Count == 2); // Reopen channel 1 - AddStep("Press Ctrl + Shift + T", pressControlShiftT); + AddStep("Press keys", pressRestoreTabKeys); AddAssert("All channels now open", () => channelManager.JoinedChannels.Count == 3); AddAssert("Current channel is channel 1", () => currentChannel == channel1); @@ -305,13 +310,13 @@ namespace osu.Game.Tests.Visual.Online AddAssert("Current channel is channel 3", () => currentChannel == channel3); // Should first re-open channel 2 - AddStep("Press Ctrl + Shift + T", pressControlShiftT); + AddStep("Press keys", pressRestoreTabKeys); AddAssert("Channel 1 still closed", () => !channelManager.JoinedChannels.Contains(channel1)); AddAssert("Channel 2 now open", () => channelManager.JoinedChannels.Contains(channel2)); AddAssert("Current channel is channel 2", () => currentChannel == channel2); // Should then re-open channel 1 - AddStep("Press Ctrl + Shift + T", pressControlShiftT); + AddStep("Press keys", pressRestoreTabKeys); AddAssert("All channels now open", () => channelManager.JoinedChannels.Count == 3); AddAssert("Current channel is channel 1", () => currentChannel == channel1); } @@ -324,27 +329,21 @@ namespace osu.Game.Tests.Visual.Online InputManager.ReleaseKey(Key.AltLeft); } - private void pressControlW() - { - InputManager.PressKey(Key.ControlLeft); - InputManager.Key(Key.W); - InputManager.ReleaseKey(Key.ControlLeft); - } + private void pressCloseDocumentKeys() => pressKeysFor(PlatformActionType.DocumentClose); - private void pressControlT() - { - InputManager.PressKey(Key.ControlLeft); - InputManager.Key(Key.T); - InputManager.ReleaseKey(Key.ControlLeft); - } + private void pressNewTabKeys() => pressKeysFor(PlatformActionType.TabNew); - private void pressControlShiftT() + private void pressRestoreTabKeys() => pressKeysFor(PlatformActionType.TabRestore); + + private void pressKeysFor(PlatformActionType type) { - InputManager.PressKey(Key.ControlLeft); - InputManager.PressKey(Key.ShiftLeft); - InputManager.Key(Key.T); - InputManager.ReleaseKey(Key.ShiftLeft); - InputManager.ReleaseKey(Key.ControlLeft); + var binding = host.PlatformKeyBindings.First(b => ((PlatformAction)b.Action).ActionType == type); + + foreach (var k in binding.KeyCombination.Keys) + InputManager.PressKey((Key)k); + + foreach (var k in binding.KeyCombination.Keys) + InputManager.ReleaseKey((Key)k); } private void clickDrawable(Drawable d) From be91203c92ba7004f0f03b32878b3a4182092584 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 3 Jun 2021 15:35:12 +0900 Subject: [PATCH 687/839] Add nested `PlatformActionContainer` to allow testing of platform actions in visual tests --- osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs b/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs index 01dd7a25c8..c7edc0174a 100644 --- a/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs +++ b/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs @@ -4,6 +4,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; using osu.Framework.Testing.Input; using osu.Game.Graphics.Cursor; using osu.Game.Graphics.Sprites; @@ -48,7 +49,7 @@ namespace osu.Game.Tests.Visual InputManager = new ManualInputManager { UseParentInput = true, - Child = mainContent + Child = new PlatformActionContainer().WithChild(mainContent) }, new Container { From 420df124b5fba03eaf59196b5a2d70f2c973f4c8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 3 Jun 2021 17:27:21 +0900 Subject: [PATCH 688/839] Add framestable-bypassing seek for spectator --- .../Visual/Gameplay/TestSceneHitErrorMeter.cs | 1 + osu.Game/Rulesets/UI/DrawableRuleset.cs | 4 ++- osu.Game/Screens/Play/Player.cs | 18 ++++++++++++ osu.Game/Screens/Play/SpectatorPlayer.cs | 28 ++++++++++--------- 4 files changed, 37 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs index 2c5443fe08..ab13095af4 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs @@ -177,6 +177,7 @@ namespace osu.Game.Tests.Visual.Gameplay public override Container Overlays { get; } public override Container FrameStableComponents { get; } public override IFrameStableClock FrameStableClock { get; } + internal override bool FrameStablePlayback { get; set; } public override IReadOnlyList Mods { get; } public override double GameplayStartTime { get; } diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index a2dade2627..0cd5804fd0 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -71,7 +71,7 @@ namespace osu.Game.Rulesets.UI /// /// Whether to enable frame-stable playback. /// - internal bool FrameStablePlayback + internal override bool FrameStablePlayback { get => frameStablePlayback; set @@ -432,6 +432,8 @@ namespace osu.Game.Rulesets.UI /// public abstract IFrameStableClock FrameStableClock { get; } + internal abstract bool FrameStablePlayback { get; set; } + /// /// The mods which are to be applied. /// diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 2258d509b7..f8f9103c89 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -576,6 +576,24 @@ namespace osu.Game.Screens.Play /// The destination time to seek to. public void Seek(double time) => GameplayClockContainer.Seek(time); + /// + /// Seeks to a specific time in gameplay, bypassing frame stability. + /// + /// + /// Intermediate hitobject judgements may not be applied or reverted correctly during this seek. + /// + /// The destination time to seek to. + public void NonFrameStableSeek(double time) + { + bool wasFrameStable = DrawableRuleset.FrameStablePlayback; + DrawableRuleset.FrameStablePlayback = false; + + Seek(time); + + // Delay resetting frame-stable playback for one frame to give the FrameStabilityContainer a chance to seek. + ScheduleAfterChildren(() => DrawableRuleset.FrameStablePlayback = wasFrameStable); + } + /// /// Restart gameplay via a parent . /// This can be called from a child screen in order to trigger the restart process. diff --git a/osu.Game/Screens/Play/SpectatorPlayer.cs b/osu.Game/Screens/Play/SpectatorPlayer.cs index c5e26bdef6..605702c8e2 100644 --- a/osu.Game/Screens/Play/SpectatorPlayer.cs +++ b/osu.Game/Screens/Play/SpectatorPlayer.cs @@ -1,11 +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.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Screens; -using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Online.Spectator; @@ -50,6 +48,7 @@ namespace osu.Game.Screens.Play base.LoadComplete(); spectatorClient.OnNewFrames += userSentFrames; + seekToGameplay(); } private void userSentFrames(int userId, FrameDataBundle bundle) @@ -73,6 +72,20 @@ namespace osu.Game.Screens.Play score.Replay.Frames.Add(convertedFrame); } + + seekToGameplay(); + } + + private bool seekedToGameplay; + + private void seekToGameplay() + { + if (seekedToGameplay || score.Replay.Frames.Count == 0) + return; + + NonFrameStableSeek(score.Replay.Frames[0].Time); + + seekedToGameplay = true; } protected override ResultsScreen CreateResults(ScoreInfo score) @@ -85,17 +98,6 @@ namespace osu.Game.Screens.Play DrawableRuleset?.SetReplayScore(score); } - protected override GameplayClockContainer CreateGameplayClockContainer(WorkingBeatmap beatmap, double gameplayStart) - { - // if we already have frames, start gameplay at the point in time they exist, should they be too far into the beatmap. - double? firstFrameTime = score.Replay.Frames.FirstOrDefault()?.Time; - - if (firstFrameTime == null || firstFrameTime <= gameplayStart + 5000) - return base.CreateGameplayClockContainer(beatmap, gameplayStart); - - return new MasterGameplayClockContainer(beatmap, firstFrameTime.Value, true); - } - public override bool OnExiting(IScreen next) { spectatorClient.OnUserBeganPlaying -= userBeganPlaying; From 3bc81fbb068b478abb16ae49670056a818079844 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 3 Jun 2021 17:27:24 +0900 Subject: [PATCH 689/839] Fix spectator tests --- osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs index 9606d8c828..6eeb3596a8 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs @@ -76,9 +76,9 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("screen hasn't changed", () => Stack.CurrentScreen is SoloSpectator); start(); - sendFrames(); - waitForPlayer(); + + sendFrames(); AddAssert("ensure frames arrived", () => replayHandler.HasFrames); AddUntilStep("wait for frame starvation", () => replayHandler.WaitingForFrame); @@ -116,12 +116,11 @@ namespace osu.Game.Tests.Visual.Gameplay start(); loadSpectatingScreen(); + waitForPlayer(); AddStep("advance frame count", () => nextFrame = 300); sendFrames(); - waitForPlayer(); - AddUntilStep("playing from correct point in time", () => player.ChildrenOfType().First().FrameStableClock.CurrentTime > 30000); } From 662bbed5d1206ca918d54a803ea52825ade0432d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 3 Jun 2021 17:38:51 +0900 Subject: [PATCH 690/839] Fix seeking to gameplay too soon --- osu.Game/Screens/Play/SpectatorPlayer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/SpectatorPlayer.cs b/osu.Game/Screens/Play/SpectatorPlayer.cs index 605702c8e2..1e6becff40 100644 --- a/osu.Game/Screens/Play/SpectatorPlayer.cs +++ b/osu.Game/Screens/Play/SpectatorPlayer.cs @@ -43,9 +43,9 @@ namespace osu.Game.Screens.Play }); } - protected override void LoadComplete() + protected override void StartGameplay() { - base.LoadComplete(); + base.StartGameplay(); spectatorClient.OnNewFrames += userSentFrames; seekToGameplay(); From be03a2d7d2dfa19a9f8fc1fe43aeb1c00f7c59f5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 3 Jun 2021 17:47:22 +0900 Subject: [PATCH 691/839] Fix multiple calls to seek method potentially not working --- osu.Game/Screens/Play/Player.cs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 64d5aa6ef7..985eb32cca 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -583,6 +583,8 @@ namespace osu.Game.Screens.Play /// The destination time to seek to. public void Seek(double time) => GameplayClockContainer.Seek(time); + private ScheduledDelegate frameStablePlaybackResetDelegate; + /// /// Seeks to a specific time in gameplay, bypassing frame stability. /// @@ -592,13 +594,16 @@ namespace osu.Game.Screens.Play /// The destination time to seek to. public void NonFrameStableSeek(double time) { + if (frameStablePlaybackResetDelegate?.Cancelled == false && !frameStablePlaybackResetDelegate.Completed) + frameStablePlaybackResetDelegate.RunTask(); + bool wasFrameStable = DrawableRuleset.FrameStablePlayback; DrawableRuleset.FrameStablePlayback = false; Seek(time); // Delay resetting frame-stable playback for one frame to give the FrameStabilityContainer a chance to seek. - ScheduleAfterChildren(() => DrawableRuleset.FrameStablePlayback = wasFrameStable); + frameStablePlaybackResetDelegate = ScheduleAfterChildren(() => DrawableRuleset.FrameStablePlayback = wasFrameStable); } /// @@ -931,11 +936,10 @@ namespace osu.Game.Screens.Play /// Creates the player's . /// /// The . - protected virtual Score CreateScore() => - new Score - { - ScoreInfo = new ScoreInfo { User = api.LocalUser.Value }, - }; + protected virtual Score CreateScore() => new Score + { + ScoreInfo = new ScoreInfo { User = api.LocalUser.Value }, + }; /// /// Imports the player's to the local database. From e887807ae7f186b03dad7525aa97b56ab1f586bb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 3 Jun 2021 18:32:05 +0900 Subject: [PATCH 692/839] Apply review fixes --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 6 +++--- osu.Game/Screens/Play/Player.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index bd6da52654..2d171eb533 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -68,9 +68,6 @@ namespace osu.Game.Rulesets.UI private bool frameStablePlayback = true; - /// - /// Whether to enable frame-stable playback. - /// internal override bool FrameStablePlayback { get => frameStablePlayback; @@ -425,6 +422,9 @@ namespace osu.Game.Rulesets.UI /// public abstract IFrameStableClock FrameStableClock { get; } + /// + /// Whether to enable frame-stable playback. + /// internal abstract bool FrameStablePlayback { get; set; } /// diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 985eb32cca..a4aaeb5b1f 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -592,7 +592,7 @@ namespace osu.Game.Screens.Play /// Intermediate hitobject judgements may not be applied or reverted correctly during this seek. /// /// The destination time to seek to. - public void NonFrameStableSeek(double time) + internal void NonFrameStableSeek(double time) { if (frameStablePlaybackResetDelegate?.Cancelled == false && !frameStablePlaybackResetDelegate.Completed) frameStablePlaybackResetDelegate.RunTask(); From 8193691cbcaed3f84383e595b9428dd395e3348e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 3 Jun 2021 13:58:19 +0200 Subject: [PATCH 693/839] Invert condition to reduce nesting --- osu.Game/Online/Chat/ChannelManager.cs | 42 +++++++++++++------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index f6804fdbf7..cf0fd2cbe6 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -471,29 +471,29 @@ namespace osu.Game.Online.Chat ClosedChannel lastClosedChannel = closedChannels.Last(); closedChannels.RemoveAt(closedChannels.Count - 1); - // If the user hasn't already joined the channel, try to join it - if (joinedChannels.FirstOrDefault(lastClosedChannel.IsEqual) == null) + // If the user has already joined the channel, try the next one + if (joinedChannels.FirstOrDefault(lastClosedChannel.IsEqual) != null) + continue; + + Channel lastChannel = AvailableChannels.FirstOrDefault(lastClosedChannel.IsEqual); + + if (lastChannel != null) { - Channel lastChannel = AvailableChannels.FirstOrDefault(lastClosedChannel.IsEqual); - - if (lastChannel != null) - { - // Channel exists as an availaable channel, directly join it - CurrentChannel.Value = JoinChannel(lastChannel); - } - else if (lastClosedChannel.Type == ChannelType.PM) - { - // Try to get User to open PM chat - users.GetUserAsync((int)lastClosedChannel.Id).ContinueWith(u => - { - if (u.Result == null) return; - - Schedule(() => CurrentChannel.Value = JoinChannel(new Channel(u.Result))); - }); - } - - return; + // Channel exists as an availaable channel, directly join it + CurrentChannel.Value = JoinChannel(lastChannel); } + else if (lastClosedChannel.Type == ChannelType.PM) + { + // Try to get User to open PM chat + users.GetUserAsync((int)lastClosedChannel.Id).ContinueWith(u => + { + if (u.Result == null) return; + + Schedule(() => CurrentChannel.Value = JoinChannel(new Channel(u.Result))); + }); + } + + return; } } From e8c2483f19c17197552f67135773888870d0e294 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 3 Jun 2021 13:59:25 +0200 Subject: [PATCH 694/839] Use standard list instead of bindable list No reason to use a bindable list there, as `CollectionChanged` was never subscribed to. --- osu.Game/Online/Chat/ChannelManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index cf0fd2cbe6..617471f75f 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -37,7 +37,7 @@ namespace osu.Game.Online.Chat /// /// Keeps a stack of recently closed channels /// - private readonly BindableList closedChannels = new BindableList(); + private readonly List closedChannels = new List(); // For efficiency purposes, this constant bounds the number of closed channels we store. // This number is somewhat arbitrary; future developers are free to modify it. From b2cc2a51ec112ef2834b2f566d75375b3b4781ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 3 Jun 2021 14:13:01 +0200 Subject: [PATCH 695/839] Rename method to be less misleading Would rather avoid variations of Equals/Equal/IsEqual. There's not really much equality involved as the types are different. --- osu.Game/Online/Chat/ChannelManager.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 617471f75f..bdce366c06 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -472,10 +472,10 @@ namespace osu.Game.Online.Chat closedChannels.RemoveAt(closedChannels.Count - 1); // If the user has already joined the channel, try the next one - if (joinedChannels.FirstOrDefault(lastClosedChannel.IsEqual) != null) + if (joinedChannels.FirstOrDefault(lastClosedChannel.Matches) != null) continue; - Channel lastChannel = AvailableChannels.FirstOrDefault(lastClosedChannel.IsEqual); + Channel lastChannel = AvailableChannels.FirstOrDefault(lastClosedChannel.Matches); if (lastChannel != null) { @@ -596,7 +596,7 @@ namespace osu.Game.Online.Chat Id = id; } - public bool IsEqual(Channel channel) + public bool Matches(Channel channel) { if (channel.Type != Type) return false; From 5f5f3a8c5c17a3a634ff49e9ce0bfb086ec4e3c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 3 Jun 2021 14:20:52 +0200 Subject: [PATCH 696/839] General comment cleanups --- osu.Game/Online/Chat/ChannelManager.cs | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index bdce366c06..8507887357 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -441,7 +441,7 @@ namespace osu.Game.Online.Chat closedChannels.RemoveAt(0); } - // For PM channels, we store the user ID; else, we store the channel id + // For PM channels, we store the user ID; else, we store the channel ID closedChannels.Add(channel.Type == ChannelType.PM ? new ClosedChannel(ChannelType.PM, channel.Users.Single().Id) : new ClosedChannel(channel.Type, channel.Id)); @@ -454,18 +454,14 @@ namespace osu.Game.Online.Chat }); /// - /// Opens the most recently closed channel that has not - /// already been reopened + /// Opens the most recently closed channel that has not already been reopened, /// Works similarly to reopening the last closed tab on a web browser. /// public void JoinLastClosedChannel() { - // This loop could be eliminated if a check was added so that - // when the code opens a channel it removes from the closedChannel list. - // However, this would require adding an O(|closeChannels|) work operation - // every time the user joins a channel, which would make joining a channel - // slower. We wanted to centralize all major slowdowns so they - // can only occur if the user actually decides to use this feature. + // This loop could be eliminated if the join channel operation ensured that every channel joined + // is removed from the closedChannels list, but it'd require a linear scan of closed channels on every join. + // To keep the overhead of joining channels low, just lazily scan the list of closed channels locally. while (closedChannels.Count > 0) { ClosedChannel lastClosedChannel = closedChannels.Last(); @@ -479,12 +475,12 @@ namespace osu.Game.Online.Chat if (lastChannel != null) { - // Channel exists as an availaable channel, directly join it + // Channel exists as an available channel, directly join it CurrentChannel.Value = JoinChannel(lastChannel); } else if (lastClosedChannel.Type == ChannelType.PM) { - // Try to get User to open PM chat + // Try to get user in order to open PM chat users.GetUserAsync((int)lastClosedChannel.Id).ContinueWith(u => { if (u.Result == null) return; @@ -583,7 +579,7 @@ namespace osu.Game.Online.Chat } /// - /// Class that stores information about a closed channel + /// Stores information about a closed channel /// public class ClosedChannel { From e117f98bfaf099bde18bc72a66007d43348deee0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 3 Jun 2021 14:31:13 +0200 Subject: [PATCH 697/839] Rename test steps --- .../Visual/Online/TestSceneChatOverlay.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index c40b60c35d..3971146ff8 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -246,7 +246,7 @@ namespace osu.Game.Tests.Visual.Online // Want to close channel 2 AddStep("Select channel 2", () => clickDrawable(chatOverlay.TabMap[channel2])); - AddStep("Press keys", pressCloseDocumentKeys); + AddStep("Close tab via shortcut", pressCloseDocumentKeys); // Channel 2 should be closed AddAssert("Channel 1 open", () => channelManager.JoinedChannels.Contains(channel1)); @@ -255,7 +255,7 @@ namespace osu.Game.Tests.Visual.Online // Want to close channel 1 AddStep("Select channel 1", () => clickDrawable(chatOverlay.TabMap[channel1])); - AddStep("Press keys", pressCloseDocumentKeys); + AddStep("Close tab via shortcut", pressCloseDocumentKeys); // Channel 1 and channel 2 should be closed AddAssert("All channels closed", () => !channelManager.JoinedChannels.Any()); } @@ -270,7 +270,7 @@ namespace osu.Game.Tests.Visual.Online }); // Want to join another channel - AddStep("Press keys", pressNewTabKeys); + AddStep("Press new tab shortcut", pressNewTabKeys); // Selector should be visible AddAssert("Selector is visible", () => chatOverlay.SelectionOverlayState == Visibility.Visible); @@ -287,7 +287,7 @@ namespace osu.Game.Tests.Visual.Online }); // Should do nothing - AddStep("Press keys", pressRestoreTabKeys); + AddStep("Restore tab via shortcut", pressRestoreTabKeys); AddAssert("All channels still open", () => channelManager.JoinedChannels.Count == 3); // Close channel 1 @@ -297,7 +297,7 @@ namespace osu.Game.Tests.Visual.Online AddAssert("Other channels still open", () => channelManager.JoinedChannels.Count == 2); // Reopen channel 1 - AddStep("Press keys", pressRestoreTabKeys); + AddStep("Restore tab via shortcut", pressRestoreTabKeys); AddAssert("All channels now open", () => channelManager.JoinedChannels.Count == 3); AddAssert("Current channel is channel 1", () => currentChannel == channel1); @@ -310,13 +310,13 @@ namespace osu.Game.Tests.Visual.Online AddAssert("Current channel is channel 3", () => currentChannel == channel3); // Should first re-open channel 2 - AddStep("Press keys", pressRestoreTabKeys); + AddStep("Restore tab via shortcut", pressRestoreTabKeys); AddAssert("Channel 1 still closed", () => !channelManager.JoinedChannels.Contains(channel1)); AddAssert("Channel 2 now open", () => channelManager.JoinedChannels.Contains(channel2)); AddAssert("Current channel is channel 2", () => currentChannel == channel2); // Should then re-open channel 1 - AddStep("Press keys", pressRestoreTabKeys); + AddStep("Restore tab via shortcut", pressRestoreTabKeys); AddAssert("All channels now open", () => channelManager.JoinedChannels.Count == 3); AddAssert("Current channel is channel 1", () => currentChannel == channel1); } From 490ce0bbc542c4324c2902e7311fd9b8fc55424b Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 3 Jun 2021 19:40:54 +0700 Subject: [PATCH 698/839] load placeholder page when failed --- osu.Game/Overlays/WikiOverlay.cs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs index af7bc40f17..f26162151f 100644 --- a/osu.Game/Overlays/WikiOverlay.cs +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -92,7 +92,7 @@ namespace osu.Game.Overlays Loading.Show(); request.Success += response => Schedule(() => onSuccess(response)); - request.Failure += _ => Schedule(() => LoadDisplay(Empty())); + request.Failure += _ => Schedule(onFail); api.PerformAsync(request); } @@ -132,6 +132,24 @@ namespace osu.Game.Overlays } } + private void onFail() + { + LoadDisplay(new WikiMarkdownContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + CurrentPath = $@"{api.WebsiteRootUrl}/wiki/", + Text = "There is something wrong when fetching this page. [Back to main page.](Main_Page)", + DocumentMargin = new MarginPadding(0), + DocumentPadding = new MarginPadding + { + Vertical = 20, + Left = 30, + Right = 50, + }, + }); + } + private void showParentPage() { var parentPath = string.Join("/", path.Value.Split('/').SkipLast(1)); From b36c406a83b35721e72c37ecd3447f0ba5541daf Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 3 Jun 2021 19:48:04 +0700 Subject: [PATCH 699/839] add test scene for error page --- .../Visual/Online/TestSceneWikiOverlay.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs index da4bf82948..4d09ed21dc 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.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.Net; using NUnit.Framework; using osu.Game.Online.API; using osu.Game.Online.API.Requests; @@ -32,7 +33,14 @@ namespace osu.Game.Tests.Visual.Online AddStep("Show Article Page", () => wiki.ShowPage("Interface")); } - private void setUpWikiResponse(APIWikiPage r) + [Test] + public void TestErrorPage() + { + setUpWikiResponse(null, true); + AddStep("Show Error Page", () => wiki.ShowPage("Error")); + } + + private void setUpWikiResponse(APIWikiPage r, bool isFailed = false) => AddStep("set up response", () => { dummyAPI.HandleRequest = request => @@ -40,7 +48,11 @@ namespace osu.Game.Tests.Visual.Online if (!(request is GetWikiRequest getWikiRequest)) return false; - getWikiRequest.TriggerSuccess(r); + if (isFailed) + getWikiRequest.TriggerFailure(new WebException()); + else + getWikiRequest.TriggerSuccess(r); + return true; }; }); From c5fc155cc0dfd99010fe62ad57fb30e055ccddf8 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 3 Jun 2021 20:37:27 +0700 Subject: [PATCH 700/839] Change text wording MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Overlays/WikiOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs index f26162151f..2261f71c04 100644 --- a/osu.Game/Overlays/WikiOverlay.cs +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -139,7 +139,7 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, CurrentPath = $@"{api.WebsiteRootUrl}/wiki/", - Text = "There is something wrong when fetching this page. [Back to main page.](Main_Page)", + Text = $"Something went wrong when trying to fetch page \"{path.Value}\".\n\n[Go back to main page.](Main_Page)", DocumentMargin = new MarginPadding(0), DocumentPadding = new MarginPadding { From 7a4fc9ffc8501be688a533fc619198936c9ae8a2 Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Thu, 3 Jun 2021 18:16:11 +0200 Subject: [PATCH 701/839] Move seed to base class --- .../Mods/ManiaModRandom.cs | 6 +- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 90 ------------------ .../Mods/TaikoModRandom.cs | 5 +- osu.Game/Rulesets/Mods/ModRandom.cs | 91 +++++++++++++++++++ 4 files changed, 100 insertions(+), 92 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs index 699c58c373..6f2d4fe91e 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Linq; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Utils; @@ -17,8 +18,11 @@ namespace osu.Game.Rulesets.Mania.Mods public void ApplyToBeatmap(IBeatmap beatmap) { + Seed.Value ??= RNG.Next(); + var rng = new Random((int)Seed.Value); + var availableColumns = ((ManiaBeatmap)beatmap).TotalColumns; - var shuffledColumns = Enumerable.Range(0, availableColumns).OrderBy(item => RNG.Next()).ToList(); + var shuffledColumns = Enumerable.Range(0, availableColumns).OrderBy(item => rng.Next()).ToList(); beatmap.HitObjects.OfType().ForEach(h => h.Column = shuffledColumns[h.Column]); } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 7b28675511..4dfadbb835 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -3,15 +3,9 @@ using System; using System.Linq; -using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.UserInterface; using osu.Framework.Utils; using osu.Game.Beatmaps; -using osu.Game.Configuration; -using osu.Game.Graphics.UserInterface; -using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Beatmaps; @@ -42,13 +36,6 @@ namespace osu.Game.Rulesets.Osu.Mods private Random rng; - [SettingSource("Seed", "Use a custom seed instead of a random one", SettingControlType = typeof(OsuModRandomSettingsControl))] - public Bindable Seed { get; } = new Bindable - { - Default = null, - Value = null - }; - public void ApplyToBeatmap(IBeatmap beatmap) { if (!(beatmap is OsuBeatmap osuBeatmap)) @@ -289,82 +276,5 @@ namespace osu.Game.Rulesets.Osu.Mods AngleRad = 0; } } - - public class OsuModRandomSettingsControl : SettingsItem - { - protected override Drawable CreateControl() => new SeedControl - { - RelativeSizeAxes = Axes.X, - Margin = new MarginPadding { Top = 5 } - }; - - private sealed class SeedControl : CompositeDrawable, IHasCurrentValue - { - private readonly BindableWithCurrent current = new BindableWithCurrent(); - - public Bindable Current - { - get => current; - set - { - current.Current = value; - seedNumberBox.Text = value.Value.ToString(); - } - } - - private readonly OsuNumberBox seedNumberBox; - - public SeedControl() - { - AutoSizeAxes = Axes.Y; - - InternalChildren = new[] - { - new GridContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - ColumnDimensions = new[] - { - new Dimension(), - new Dimension(GridSizeMode.Absolute, 2), - new Dimension(GridSizeMode.Relative, 0.25f) - }, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize) - }, - Content = new[] - { - new Drawable[] - { - seedNumberBox = new OsuNumberBox - { - RelativeSizeAxes = Axes.X, - CommitOnFocusLost = true - } - } - } - } - }; - - seedNumberBox.Current.BindValueChanged(e => - { - int? value = null; - - if (int.TryParse(e.NewValue, out var intVal)) - value = intVal; - - current.Value = value; - }); - } - - protected override void Update() - { - if (current.Value == null) - seedNumberBox.Text = current.Current.Value.ToString(); - } - } - } } } diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModRandom.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModRandom.cs index a22f189d5e..307a37bf2e 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModRandom.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModRandom.cs @@ -20,10 +20,13 @@ namespace osu.Game.Rulesets.Taiko.Mods { var taikoBeatmap = (TaikoBeatmap)beatmap; + Seed.Value ??= RNG.Next(); + var rng = new Random((int)Seed.Value); + foreach (var obj in taikoBeatmap.HitObjects) { if (obj is Hit hit) - hit.Type = RNG.Next(2) == 0 ? HitType.Centre : HitType.Rim; + hit.Type = rng.Next(2) == 0 ? HitType.Centre : HitType.Rim; } } } diff --git a/osu.Game/Rulesets/Mods/ModRandom.cs b/osu.Game/Rulesets/Mods/ModRandom.cs index da55ab3fbf..3f14263420 100644 --- a/osu.Game/Rulesets/Mods/ModRandom.cs +++ b/osu.Game/Rulesets/Mods/ModRandom.cs @@ -1,8 +1,15 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Configuration; using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays.Settings; namespace osu.Game.Rulesets.Mods { @@ -13,5 +20,89 @@ namespace osu.Game.Rulesets.Mods public override ModType Type => ModType.Conversion; public override IconUsage? Icon => OsuIcon.Dice; public override double ScoreMultiplier => 1; + + [SettingSource("Seed", "Use a custom seed instead of a random one", SettingControlType = typeof(ModRandomSettingsControl))] + public Bindable Seed { get; } = new Bindable + { + Default = null, + Value = null + }; + + private class ModRandomSettingsControl : SettingsItem + { + protected override Drawable CreateControl() => new SeedControl + { + RelativeSizeAxes = Axes.X, + Margin = new MarginPadding { Top = 5 } + }; + + private sealed class SeedControl : CompositeDrawable, IHasCurrentValue + { + private readonly BindableWithCurrent current = new BindableWithCurrent(); + + public Bindable Current + { + get => current; + set + { + current.Current = value; + seedNumberBox.Text = value.Value.ToString(); + } + } + + private readonly OsuNumberBox seedNumberBox; + + public SeedControl() + { + AutoSizeAxes = Axes.Y; + + InternalChildren = new[] + { + new GridContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + ColumnDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.Absolute, 2), + new Dimension(GridSizeMode.Relative, 0.25f) + }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize) + }, + Content = new[] + { + new Drawable[] + { + seedNumberBox = new OsuNumberBox + { + RelativeSizeAxes = Axes.X, + CommitOnFocusLost = true + } + } + } + } + }; + + seedNumberBox.Current.BindValueChanged(e => + { + int? value = null; + + if (int.TryParse(e.NewValue, out var intVal)) + value = intVal; + + current.Value = value; + }); + } + + protected override void Update() + { + if (current.Value == null) + seedNumberBox.Text = current.Current.Value.ToString(); + } + } + } } } From d444fed46f1169eb20b5041f29b22ae39d867375 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 4 Jun 2021 01:59:56 +0900 Subject: [PATCH 702/839] Detach gameplay score from replay recorder before importing Closes #13320. --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 8 +++++++- osu.Game/Rulesets/UI/RulesetInputManager.cs | 6 ++++-- osu.Game/Screens/Play/Player.cs | 3 +++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 16a411d478..e2e3c22618 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -267,6 +267,12 @@ namespace osu.Game.Rulesets.UI if (!(KeyBindingInputManager is IHasRecordingHandler recordingInputManager)) throw new InvalidOperationException($"A {nameof(KeyBindingInputManager)} which supports recording is not available"); + if (score == null) + { + recordingInputManager.Recorder = null; + return; + } + var recorder = CreateReplayRecorder(score); if (recorder == null) @@ -511,7 +517,7 @@ namespace osu.Game.Rulesets.UI /// Sets a replay to be used to record gameplay. /// /// The target to be recorded to. - public abstract void SetRecordTarget(Score score); + public abstract void SetRecordTarget([CanBeNull] Score score); /// /// Invoked when the interactive user requests resuming from a paused state. diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index 75c3a4661c..e3b9ad5641 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -30,12 +30,14 @@ namespace osu.Game.Rulesets.UI { set { - if (recorder != null) + if (value != null && recorder != null) throw new InvalidOperationException("Cannot attach more than one recorder"); + recorder?.Expire(); recorder = value; - KeyBindingContainer.Add(recorder); + if (recorder != null) + KeyBindingContainer.Add(recorder); } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 0d1ebd30fc..20012d0282 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -636,6 +636,9 @@ namespace osu.Game.Screens.Play 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(async () => From 076e498a6378b07dcae67e4b06bfde1d0f6f565c Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 4 Jun 2021 00:12:13 +0700 Subject: [PATCH 703/839] create abstract class OverlaySidebar --- osu.Game/Overlays/OverlaySidebar.cs | 76 +++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 osu.Game/Overlays/OverlaySidebar.cs diff --git a/osu.Game/Overlays/OverlaySidebar.cs b/osu.Game/Overlays/OverlaySidebar.cs new file mode 100644 index 0000000000..468b5b6eb3 --- /dev/null +++ b/osu.Game/Overlays/OverlaySidebar.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 JetBrains.Annotations; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics.Containers; + +namespace osu.Game.Overlays +{ + public abstract class OverlaySidebar : CompositeDrawable + { + private readonly Box sidebarBackground; + private readonly Box scrollbarBackground; + + protected OverlaySidebar() + { + RelativeSizeAxes = Axes.Y; + Width = 250; + InternalChildren = new Drawable[] + { + sidebarBackground = new Box + { + RelativeSizeAxes = Axes.Both, + }, + scrollbarBackground = new Box + { + RelativeSizeAxes = Axes.Y, + Width = OsuScrollContainer.SCROLL_BAR_HEIGHT, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Alpha = 0.5f + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Right = -3 }, // Compensate for scrollbar margin + Child = new OsuScrollContainer + { + RelativeSizeAxes = Axes.Both, + Child = new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Right = 3 }, // Addeded 3px back + Child = new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding + { + Vertical = 20, + Left = 50, + Right = 30 + }, + Child = CreateContent() + } + } + } + } + }; + } + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + sidebarBackground.Colour = colourProvider.Background4; + scrollbarBackground.Colour = colourProvider.Background3; + } + + [NotNull] + protected virtual Drawable CreateContent() => Empty(); + } +} From 905472a20b5c5b1380efc4afb96b315939c8cf3a Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 4 Jun 2021 00:12:29 +0700 Subject: [PATCH 704/839] make NewsSidebar extends OverlaySidebar --- osu.Game/Overlays/News/Sidebar/NewsSidebar.cs | 81 ++++--------------- 1 file changed, 15 insertions(+), 66 deletions(-) diff --git a/osu.Game/Overlays/News/Sidebar/NewsSidebar.cs b/osu.Game/Overlays/News/Sidebar/NewsSidebar.cs index 9e397e78c8..35cd6eb03b 100644 --- a/osu.Game/Overlays/News/Sidebar/NewsSidebar.cs +++ b/osu.Game/Overlays/News/Sidebar/NewsSidebar.cs @@ -6,87 +6,36 @@ using osu.Framework.Bindables; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics; using osu.Game.Online.API.Requests.Responses; -using osu.Framework.Graphics.Shapes; using osuTK; using System.Linq; -using osu.Game.Graphics.Containers; namespace osu.Game.Overlays.News.Sidebar { - public class NewsSidebar : CompositeDrawable + public class NewsSidebar : OverlaySidebar { [Cached] public readonly Bindable Metadata = new Bindable(); private FillFlowContainer monthsFlow; - [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) + protected override Drawable CreateContent() => new FillFlowContainer { - RelativeSizeAxes = Axes.Y; - Width = 250; - InternalChildren = new Drawable[] + Direction = FillDirection.Vertical, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(0, 20), + Children = new Drawable[] { - new Box + new YearsPanel(), + monthsFlow = new FillFlowContainer { - RelativeSizeAxes = Axes.Both, - Colour = colourProvider.Background4 - }, - new Box - { - RelativeSizeAxes = Axes.Y, - Width = OsuScrollContainer.SCROLL_BAR_HEIGHT, - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Colour = colourProvider.Background3, - Alpha = 0.5f - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Right = -3 }, // Compensate for scrollbar margin - Child = new OsuScrollContainer - { - RelativeSizeAxes = Axes.Both, - Child = new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Right = 3 }, // Addeded 3px back - Child = new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding - { - Vertical = 20, - Left = 50, - Right = 30 - }, - Child = new FillFlowContainer - { - Direction = FillDirection.Vertical, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Spacing = new Vector2(0, 20), - Children = new Drawable[] - { - new YearsPanel(), - monthsFlow = new FillFlowContainer - { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 10) - } - } - } - } - } - } + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 10) } - }; - } + } + }; protected override void LoadComplete() { From beb0119dd54c03d0258a3f5174b7face1bf5b7b0 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 4 Jun 2021 09:28:31 +0700 Subject: [PATCH 705/839] initial wiki sidebar --- osu.Game/Overlays/Wiki/WikiSidebar.cs | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 osu.Game/Overlays/Wiki/WikiSidebar.cs diff --git a/osu.Game/Overlays/Wiki/WikiSidebar.cs b/osu.Game/Overlays/Wiki/WikiSidebar.cs new file mode 100644 index 0000000000..6d1f520135 --- /dev/null +++ b/osu.Game/Overlays/Wiki/WikiSidebar.cs @@ -0,0 +1,9 @@ +// 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.Overlays.Wiki +{ + public class WikiSidebar : OverlaySidebar + { + } +} From 791a9dd33a3a6754170457a9da701f776b4c7958 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 4 Jun 2021 09:29:10 +0700 Subject: [PATCH 706/839] add WikiArticlePage --- osu.Game/Overlays/Wiki/WikiArticlePage.cs | 54 +++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 osu.Game/Overlays/Wiki/WikiArticlePage.cs diff --git a/osu.Game/Overlays/Wiki/WikiArticlePage.cs b/osu.Game/Overlays/Wiki/WikiArticlePage.cs new file mode 100644 index 0000000000..c41ab8b250 --- /dev/null +++ b/osu.Game/Overlays/Wiki/WikiArticlePage.cs @@ -0,0 +1,54 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Overlays.Wiki.Markdown; + +namespace osu.Game.Overlays.Wiki +{ + public class WikiArticlePage : GridContainer + { + public Container SidebarContainer { get; } + + public WikiArticlePage(string currentPath, string markdown) + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + }; + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + new Dimension(), + }; + Content = new[] + { + new Drawable[] + { + SidebarContainer = new Container + { + AutoSizeAxes = Axes.X, + Child = new WikiSidebar(), + }, + new WikiMarkdownContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + CurrentPath = currentPath, + Text = markdown, + DocumentMargin = new MarginPadding(0), + DocumentPadding = new MarginPadding + { + Vertical = 20, + Left = 30, + Right = 50, + }, + } + }, + }; + } + } +} From 458910b7446e618ef132a1e5b0cf4b2e92d8b519 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 4 Jun 2021 09:29:36 +0700 Subject: [PATCH 707/839] use WikiArticlePage in WikiOverlay --- osu.Game/Overlays/WikiOverlay.cs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs index c5ba0eed66..4cfbbbbfe4 100644 --- a/osu.Game/Overlays/WikiOverlay.cs +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Linq; using System.Threading; using osu.Framework.Allocation; @@ -10,7 +11,6 @@ using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.Wiki; -using osu.Game.Overlays.Wiki.Markdown; namespace osu.Game.Overlays { @@ -31,6 +31,8 @@ namespace osu.Game.Overlays private bool displayUpdateRequired = true; + private WikiArticlePage articlePage; + public WikiOverlay() : base(OverlayColourScheme.Orange, false) { @@ -82,6 +84,17 @@ namespace osu.Game.Overlays }, (cancellationToken = new CancellationTokenSource()).Token); } + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + if (articlePage != null) + { + articlePage.SidebarContainer.Height = DrawHeight; + articlePage.SidebarContainer.Y = Math.Clamp(ScrollFlow.Current - Header.DrawHeight, 0, Math.Max(ScrollFlow.ScrollContent.DrawHeight - DrawHeight - Header.DrawHeight, 0)); + } + } + private void onPathChanged(ValueChangedEvent e) { cancellationToken?.Cancel(); @@ -115,20 +128,7 @@ namespace osu.Game.Overlays } else { - LoadDisplay(new WikiMarkdownContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - CurrentPath = $@"{api.WebsiteRootUrl}/wiki/{path.Value}/", - Text = response.Markdown, - DocumentMargin = new MarginPadding(0), - DocumentPadding = new MarginPadding - { - Vertical = 20, - Left = 30, - Right = 50, - }, - }); + LoadDisplay(articlePage = new WikiArticlePage($@"{api.WebsiteRootUrl}/wiki/{path.Value}/", response.Markdown)); } } From 34379b953af958c1a8ac2634cf1bdf9bb3fb72fb Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 4 Jun 2021 09:36:21 +0700 Subject: [PATCH 708/839] change test scene response --- osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs index 4d09ed21dc..3506d459ce 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs @@ -30,7 +30,7 @@ namespace osu.Game.Tests.Visual.Online public void TestArticlePage() { setUpWikiResponse(responseArticlePage); - AddStep("Show Article Page", () => wiki.ShowPage("Interface")); + AddStep("Show Article Page", () => wiki.ShowPage("Article_styling_criteria/Formatting")); } [Test] @@ -69,16 +69,16 @@ namespace osu.Game.Tests.Visual.Online "---\nlayout: main_page\n---\n\n\n\n
\nWelcome to the osu! wiki, a project containing a wide range of osu! related information.\n
\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# Game client\n\n[Interface](/wiki/Interface) • [Options](/wiki/Options) • [Visual settings](/wiki/Visual_Settings) • [Shortcut key reference](/wiki/Shortcut_key_reference) • [Configuration file](/wiki/osu!_Program_Files/User_Configuration_File) • [Program files](/wiki/osu!_Program_Files)\n\n[File formats](/wiki/osu!_File_Formats): [.osz](/wiki/osu!_File_Formats/Osz_(file_format)) • [.osk](/wiki/osu!_File_Formats/Osk_(file_format)) • [.osr](/wiki/osu!_File_Formats/Osr_(file_format)) • [.osu](/wiki/osu!_File_Formats/Osu_(file_format)) • [.osb](/wiki/osu!_File_Formats/Osb_(file_format)) • [.db](/wiki/osu!_File_Formats/Db_(file_format))\n\n
\n
\n\n# Gameplay\n\n[Game modes](/wiki/Game_mode): [osu!](/wiki/Game_mode/osu!) • [osu!taiko](/wiki/Game_mode/osu!taiko) • [osu!catch](/wiki/Game_mode/osu!catch) • [osu!mania](/wiki/Game_mode/osu!mania)\n\n[Beatmap](/wiki/Beatmap) • [Hit object](/wiki/Hit_object) • [Mods](/wiki/Game_modifier) • [Score](/wiki/Score) • [Replay](/wiki/Replay) • [Multi](/wiki/Multi)\n\n
\n
\n\n# [Beatmap editor](/wiki/Beatmap_Editor)\n\nSections: [Compose](/wiki/Beatmap_Editor/Compose) • [Design](/wiki/Beatmap_Editor/Design) • [Timing](/wiki/Beatmap_Editor/Timing) • [Song setup](/wiki/Beatmap_Editor/Song_Setup)\n\nComponents: [AiMod](/wiki/Beatmap_Editor/AiMod) • [Beat snap divisor](/wiki/Beatmap_Editor/Beat_Snap_Divisor) • [Distance snap](/wiki/Beatmap_Editor/Distance_Snap) • [Menu](/wiki/Beatmap_Editor/Menu) • [SB load](/wiki/Beatmap_Editor/SB_Load) • [Timelines](/wiki/Beatmap_Editor/Timelines)\n\n[Beatmapping](/wiki/Beatmapping) • [Difficulty](/wiki/Beatmap/Difficulty) • [Mapping techniques](/wiki/Mapping_Techniques) • [Storyboarding](/wiki/Storyboarding)\n\n
\n
\n\n# Beatmap submission and ranking\n\n[Submission](/wiki/Submission) • [Modding](/wiki/Modding) • [Ranking procedure](/wiki/Beatmap_ranking_procedure) • [Mappers' Guild](/wiki/Mappers_Guild) • [Project Loved](/wiki/Project_Loved)\n\n[Ranking criteria](/wiki/Ranking_Criteria): [osu!](/wiki/Ranking_Criteria/osu!) • [osu!taiko](/wiki/Ranking_Criteria/osu!taiko) • [osu!catch](/wiki/Ranking_Criteria/osu!catch) • [osu!mania](/wiki/Ranking_Criteria/osu!mania)\n\n
\n
\n\n# Community\n\n[Tournaments](/wiki/Tournaments) • [Skinning](/wiki/Skinning) • [Projects](/wiki/Projects) • [Guides](/wiki/Guides) • [osu!dev Discord server](/wiki/osu!dev_Discord_server) • [How you can help](/wiki/How_You_Can_Help!) • [Glossary](/wiki/Glossary)\n\n
\n
\n\n# People\n\n[The Team](/wiki/People/The_Team): [Developers](/wiki/People/The_Team/Developers) • [Global Moderation Team](/wiki/People/The_Team/Global_Moderation_Team) • [Support Team](/wiki/People/The_Team/Support_Team) • [Nomination Assessment Team](/wiki/People/The_Team/Nomination_Assessment_Team) • [Beatmap Nominators](/wiki/People/The_Team/Beatmap_Nominators) • [osu! Alumni](/wiki/People/The_Team/osu!_Alumni) • [Project Loved Team](/wiki/People/The_Team/Project_Loved_Team)\n\nOrganisations: [osu! UCI](/wiki/Organisations/osu!_UCI)\n\n[Community Contributors](/wiki/People/Community_Contributors) • [Users with unique titles](/wiki/People/Users_with_unique_titles)\n\n
\n
\n\n# For developers\n\n[API](/wiki/osu!api) • [Bot account](/wiki/Bot_account) • [Brand identity guidelines](/wiki/Brand_identity_guidelines)\n\n
\n
\n\n# About the wiki\n\n[Sitemap](/wiki/Sitemap) • [Contribution guide](/wiki/osu!_wiki_Contribution_Guide) • [Article styling criteria](/wiki/Article_Styling_Criteria) • [News styling criteria](/wiki/News_Styling_Criteria)\n\n
\n
\n", }; - // From https://osu.ppy.sh/api/v2/wiki/en/Interface + // From https://osu.ppy.sh/api/v2/wiki/en/Article_styling_criteria/Formatting private APIWikiPage responseArticlePage => new APIWikiPage { - Title = "Interface", + Title = "Formatting", Layout = "markdown_page", - Path = "Interface", + Path = "Article_styling_criteria/Formatting", Locale = "en", - Subtitle = null, + Subtitle = "Article styling criteria", Markdown = - "# Interface\n\n![](img/intro-screen.jpg \"Introduction screen\")\n\n## Main Menu\n\n![](img/main-menu.jpg \"Main Menu\")\n\nThe [osu!cookie](/wiki/Glossary#cookie) \\[1\\] pulses according to the [BPM](/wiki/Beatmapping/Beats_per_minute) of any song currently playing on the main menu. In addition, bars will extend out of the osu!cookie in accordance to the song's volume. If no song is playing, it pulses at a slow 60 BPM. The elements of the main menu are as follows:\n\n- \\[2\\] Click Play (`P`) or the logo to switch to the Solo mode song selection screen.\n- \\[3\\] Click Edit (`E`) to open the Editor mode song selection screen.\n- \\[4\\] Click Options (`O`) to go to the Options screen.\n- \\[5\\] Click Exit (`Esc`) to exit osu!.\n- \\[6\\] A random useful tip is displayed below the menu.\n- \\[7\\] In the lower-left is a link to the osu! website, as well as copyright information.\n- \\[8\\] Connection result to [Bancho](/wiki/Glossary#bancho)! In this picture it is not shown, but the connection result looks like a chain link.\n- \\[9\\] In the bottom right are the chat controls for the extended [chat window](/wiki/Chat_Console) (called \"Player List\" here) and the regular chat window (`F9` & `F8`, respectively).\n- \\[10\\] In the upper right is the osu! jukebox which plays the songs in random order. The top shows the song currently playing. The buttons, from left to right, do as follows:\n - Previous Track\n - Play\n - Pause\n - Stop (the difference between Play and Stop is that Stop will reset the song to the beginning, while Pause simply pauses it)\n - Next Track\n - View Song Info. This toggles the top bar showing the song info between being permanent and temporary. When permanent, the info bar will stay visible until it fades out with the rest of the UI. When temporary, it will disappear a little while after a song has been chosen. It will stay hidden until it is toggled again, or another song plays.\n- \\[11\\] The number of beatmaps you have available, how long your osu!client has been running, and your system clock.\n- \\[12\\] Your profile, click on it to display the User Options (see below).\n\n## User Options\n\n![](img/user-options.jpg \"User Options\")\n\nAccess this screen by clicking your profile at the top left of the main menu. You cannot access the Chat Consoles while viewing the user option screen. You can select any item by pressing the corresponding number on the option:\n\n1. `View Profile`: Opens up your profile page in your default web browser.\n2. `Sign Out`: Sign out of your account (after signing out, the [Options](/wiki/Options) sidebar will prompt you to sign in).\n3. `Change Avatar`: Open up the edit avatar page in your default web browser.\n4. `Close`: Close this dialog\n\n## Play Menu\n\n![](img/play-menu.jpg \"Play Menu\")\n\n- Click `Solo` (`P`) to play alone.\n- Click `Multi` (`M`) to play with other people. You will be directed to the [Multi](/wiki/Multi) Lobby (see below).\n- Click `Back` to return to the main menu.\n\n## Multi Lobby\n\n*Main page: [Multi](/wiki/Multi)*\n\n![](img/multi-lobby.jpg \"Multi Lobby\")\n\n![](img/multi-room.jpg \"Multi Host\")\n\n1. Your rank in the match. This is also shown next to your name.\n2. Your profile information.\n3. The jukebox.\n4. Player list - displays player names, their rank (host or player), their [mods](/wiki/Game_modifier) activated (if any, see \\#7), their osu! ranking, and their team (if applicable).\n5. The name of the match and the password settings.\n6. The beatmap selected. It shows the beatmap as it would in the solo song selection screen.\n7. The [mods](/wiki/Game_modifier) that you have activated (see #12), as well as the option to select them. The option marked \"Free Mods\" toggles whether or not players can select their own mods. If yes, they can pick any combination of mods *except for speed-altering mods like [Double Time](/wiki/Game_modifier/Double_Time)*. If no, the host decides what mods will be used. The host can pick speed-altering mods regardless of whether or not Free Mods is turned on.\n8. The team mode and win conditions.\n9. The ready button.\n10. The [chat console](/wiki/Chat_Console).\n11. The leave button.\n12. Where your activated mods appear.\n\n## Song Selection Screen\n\n![](img/song-selection.jpg \"Song Selection\")\n\nYou can identify the current mode selected by either looking at the icon in the bottom left, above Mode, or by looking at the transparent icon in the center of the screen. These are the four you will see:\n\n- ![](/wiki/shared/mode/osu.png) is [osu!](/wiki/Game_mode/osu!)\n- ![](/wiki/shared/mode/taiko.png) is [osu!taiko](/wiki/Game_mode/osu!taiko)\n- ![](/wiki/shared/mode/catch.png) is [osu!catch](/wiki/Game_mode/osu!catch)\n- ![](/wiki/shared/mode/mania.png) is [osu!mania](/wiki/Game_mode/osu!mania)\n\nBefore continuing on, this screen has too many elements to note with easily, noticeable numbers. The subsections below will focus on one part of the screen at a time, starting from the top down and left to right.\n\n### Beatmap Information\n\n![](img/metadata-comparison.jpg)\n\n![](img/beatmap-metadata.jpg)\n\nThis area displays **information on the beatmap difficulty currently selected.** By default, the beatmap whose song is heard in the osu! jukebox is selected when entering the selection screen. In the top left is the ranked status of the beatmap. The title is next. Normally, the romanised title is shown, but if you select `Prefer metadata in original language` in the [Options](/wiki/Options), it will show the Unicode title; this is shown in the upper picture. The beatmapper is also shown, and beatmap information is shown below. From left to right, the values are as follows:\n\n- **Length**: The total length of the beatmap, from start to finish and including breaks. Not to be confused with [drain time](/wiki/Glossary#drain-time).\n- **BPM**: The BPM of the beatmap. If (like in the lower picture) there are two BPMS and one in parentheses, this means that the BPM changes throughout the song. It shows the slowest and fastest BPMs, and the value in parentheses is the BPM at the start of the beatmap.\n- **Objects**: The total amount of [hit objects](/wiki/Hit_Objects) in the beatmap.\n- **Circles**: The total amount of hit circles in the beatmap.\n- **Sliders**: The total amount of sliders in the beatmap.\n- **Spinners**: The total amount of spinners in the beatmap.\n- **OD**: The Overall Difficulty of the beatmap.\n- **HP**: The drain rate of your HP. In osu!, this is how much of an HP loss you receive upon missing a note, how fast the life bar idly drains, and how much HP is received for hitting a note. In osu!mania, this is the same except there is no idle HP drain. In osu!taiko, this determines how slowly the HP bar fills and how much HP is lost when a note is missed. osu!catch is the same as osu!.\n- **Stars**: The star difficulty of the beatmap. This is graphically visible in the beatmap rectangle itself.\n\n### Group and Sort\n\n![](img/beatmap-filters.jpg)\n\nClick on one of the tabs to **sort your song list according to the selected criterion**.\n\n**Group** - Most options organize beatmaps into various expandable groups:\n\n- `No grouping` - Beatmaps will not be grouped but will still be sorted in the order specified by Sort.\n- `By Difficulty` - Beatmaps will be grouped by their star difficulty, rounded to the nearest whole number.\n- `By Artist` - Beatmaps will be grouped by the artist's first character of their name.\n- `Recently Played` - Beatmaps will be grouped by when you last played them.\n- `Collections` - This will show the collections you have created. *Note that this will hide beatmaps not listed in a collection!*\n- `By BPM` - Beatmaps will be grouped according to BPM in multiples of 60, starting at 120.\n- `By Creator` - Beatmaps will be grouped by the beatmap creator's name's first character.\n- `By Date Added` - Beatmaps will be grouped according to when they were added, from today to 4+ months ago.\n- `By Length` - Beatmaps will be grouped according to their length: 1 minute or less, 2 minutes or less, 3, 4, 5, and 10.\n- `By Mode` - Beatmaps will be grouped according to their game mode.\n- `By Rank Achieved` - Beatmaps will be sorted by the highest rank achieved on them.\n- `By Title` - Beatmaps will be grouped by the first letter of their title.\n- `Favourites` - Only beatmaps you have favorited online will be shown.\n- `My Maps` - Only beatmaps you have mapped (that is, whose creator matches your profile name) will be shown.\n- `Ranked Status` - Beatmaps will be grouped by their ranked status: ranked, pending, not submitted, unknown, or loved.\n\nThe first five groupings are available in tabs below Group and Sort.\n\n**Sort** - Sorts beatmaps in a certain order\n\n- `By Artist` - Beatmaps will be sorted alphabetically by the artist's name's first character.\n- `By BPM` - Beatmaps will be sorted lowest to highest by their BPM. For maps with multiple BPMs, the highest will be used.\n- `By Creator` - Beatmaps will be sorted alphabetically by the creator's name's first character.\n- `By Date Added` - Beatmaps will be sorted from oldest to newest by when they were added.\n- `By Difficulty` - Beatmaps will be sorted from easiest to hardest by star difficulty. *Note that this will split apart mapsets!*\n- `By Length` - Beatmaps will be sorted from shortest to longest by length.\n- `By Rank Achieved` - Beatmaps will be sorted from poorest to best by the highest rank achieved on them.\n- `By Title` - Beatmaps will be sorted alphabetically by the first character of their name.\n\n### Search\n\n![](img/search-bar.jpg)\n\n*Note: You cannot have the chat console or the options sidebar open if you want to search; otherwise, anything you type will be perceived as chat text or as an options search query.*\n\nOnly beatmaps that match the criteria of your search will be shown. By default, any search will be matched against the beatmaps' artists, titles, creators, and tags.\n\nIn addition to searching these fields, you can use filters to search through other metadata by combining one of the supported filters with a comparison to a value (for example, `ar=9`).\n\nSupported filters:\n\n- `artist`: Name of the artist\n- `creator`: Name of the beatmap creator\n- `ar`: Approach Rate\n- `cs`: Circle Size\n- `od`: Overall Difficulty\n- `hp`: HP Drain Rate\n- `keys`: Number of keys (osu!mania and converted beatmaps only)\n- `stars`: Star Difficulty\n- `bpm`: Beats per minute\n- `length`: Length in seconds\n- `drain`: Drain Time in seconds\n- `mode`: Mode. Value can be `osu`, `taiko`, `catchthebeat`, or `mania`, or `o`/`t`/`c`/`m` for short.\n- `status`: Ranked status. Value can be `ranked`, `approved`, `pending`, `notsubmitted`, `unknown`, or `loved`, or `r`/`a`/`p`/`n`/`u`/`l` for short.\n- `played`: Time since last played in days\n- `unplayed`: Shows only unplayed maps. A comparison with no set value must be used. The comparison itself is ignored.\n- `speed`: Saved osu!mania scroll speed. Always 0 for unplayed maps or if the [Remember osu!mania scroll speed per beatmap](/wiki/Options#gameplay) option is off\n\nSupported comparisons:\n\n- `=` or `==`: Equal to\n- `!=`: Not equal to\n- `<`: Less than\n- `>`: Greater than\n- `<=`: Less than or equal to\n- `>=`: Greater than or equal to\n\nYou may also enter a beatmap or beatmapset ID in your search to get a single result.\n\n### Rankings\n\n![](img/leaderboards.jpg)\n\n A variety of things can appear in this space:\n\n- A \"Not Submitted\" box denotes a beatmap that has not been uploaded to the osu! site using the Beatmap Submission System or was deleted by the mapper.\n- An \"Update to latest version\" box appears if there is a new version of the beatmap available for download. Click on the button to update.\n - **Note:** Once you update the beatmap, it cannot be reversed. If you want to preserve the older version for some reason (say, to keep scores), then do not update.\n- A \"Latest pending version\" box appears means that the beatmap has been uploaded to the osu!website but is not ranked yet.\n- If replays matching the view setting of the beatmap exist, they will be displayed instead of a box denoting the ranked/played status of the beatmap. This is shown in the above picture.\n - Under public rankings (e.g. Global, Friends, etc.), your high score will be shown at the bottom, as well as your rank on the leaderboard.\n- A \"No records set!\" box means that there are no replays for the current view setting (this is typically seen in the Local view setting if you just downloaded or edited the beatmap).\n - Note: Scores for Multi are not counted as records.\n\nThese are the view settings:\n\n- Local Ranking\n- Country Ranking\\*\n- Global Ranking\n- Global Ranking (Selected Mods)\\*\n- Friend Ranking\\*\n\n\\*Requires you to be an [osu!supporter](/wiki/osu!supporter) to access them.\n\nClick the word bubble icon to call up the **Quick Web Access** screen for the selected beatmap:\n\n- Press `1` or click the `Beatmap Listing/Scores` button and your default internet browser will pull up the Beatmap Listing and score page of the beatmap set the selected beatmap belongs to.\n- Press `2` or click `Beatmap Modding` and your default internet browser will pull up the modding page of the beatmap set the selected beatmap belongs to.\n- Press `3` or `Esc` or click `Cancel` to return to the Song Selection Screen.\n\nWhile you are on the Quick Web Access Screen, you cannot access the Chat and Extended Chat Consoles.\n\n### Song\n\n![](img/beatmap-cards.jpg)\n\nThe song list displays all available beatmaps. Different beatmaps may have different coloured boxes:\n\n- **Pink**: This beatmap has not been played yet.\n- **Orange**: At least one beatmap from the beatmapset has been completed.\n- **Light Blue**: Other beatmaps in the same set, shown when a mapset is expanded.\n- **White**: Currently selected beatmap.\n\nYou can navigate the beatmap list by using the mouse wheel, using the up and down arrow keys, dragging it while holding the left mouse button or clicking the right mouse button (previously known as Absolute Scrolling), which will move the scroll bar to your mouse's Y position. Click on a box to select that beatmap and display its information on the upper left, high scores (if any) on the left and, if you've cleared it, the letter grade of the highest score you've achieved. Click the box again, press `Enter` or click the osu!cookie at the lower right to begin playing the beatmap.\n\n### Gameplay toolbox\n\n![](img/game-mode-selector.jpg \"List of available game modes\")\n\n![](img/gameplay-toolbox.jpg)\n\nThis section can be called the gameplay toolbox. We will cover each button's use from left to right.\n\nPress `Esc` or click the `Back` button to return to main menu.\n\nClick on the `Mode` button to open up a list of gameplay modes available on osu!. Click on your desired gameplay mode and osu! will switch to that gameplay mode style - the scoreboard will change accordingly. Alternatively, you can press `Ctrl` and `1` (osu!), `2` (osu!taiko), `3` (osu!catch), or `4` (osu!mania) to change the gamemode.\n\nThe background transparent icon and the \"Mode\" box will change to depict what mode is currently selected.\n\n![](img/game-modifiers.jpg \"Mod Selection Screen\")\n\nClick the `Mods` button or press `F1` to open the **[Mod Selection Screen](/wiki/Game_modifier)**.\n\nIn this screen, you can apply modifications (\"mods\" for short) to gameplay. Some mods lower difficulty and apply a multiplier that lowers the score you achieve. Conversely, some mods increase the difficulty, but apply a multiplier that increases the score you achieve. Finally, some mods modify gameplay in a different way. [Relax](/wiki/Game_modifier/Relax) and [Auto Pilot](/wiki/Game_modifier/Autopilot) fall in that category.\n\nPlace your mouse on a mod's icon to see a short description of its effect. Click on an icon to select or deselect that mod. Some mods, like Double Time, have multiple variations; click on the mod again to cycle through. The score multiplier value displays the combined effect the multipliers of the mod(s) of you have selected will have on your score. Click `Reset all mods` or press `1` to deselect all currently selected mods. Click `Close` or press `2` or `Esc` to return to the Song Selection Screen.\n\nWhile you are on the Mod Selection Screen, you cannot access the Chat and Extended Chat Consoles. In addition, skins can alter the text and/or icon of the mods, but the effects will still be the same.\n\nClick the `Random` button or press `F2` to have the game **randomly scroll through all of your beatmaps and pick one.** You cannot select a beatmap yourself until it has finished scrolling.\n\n*Note: You can press `Shift` + the `Random` button or `F2` to go back to the beatmap you had selected before you randomized your selection.*\n\n![](img/beatmap-options.jpg \"Possible commands for a beatmap\")\n\nClick the `Beatmap Options` button, press `F3` or right-click your mouse while hovering over the beatmap to call up the **Beatmap Options Menu for options on the currently selected beatmap**.\n\n- Press `1` or click the `Manage Collections` button to bring up the Collections screen - here, you can manage pre-existing collections, as well as add or remove the currently selected beatmap or mapset to or from a collection.\n- Press `2` or click `Delete...` to delete the \\[1\\] currently selected beatmapset, \\[2\\] delete the currently selected beatmap, or \\[3\\] delete **all VISIBLE beatmaps**.\n - Note that deleted beatmaps are moved to the Recycle Bin.\n- Press `3` or click `Remove from Unplayed` to mark an unplayed beatmap as played (that is, change its box colour from pink to orange).\n- Press `4` or click `Clear local scores` to delete all records of the scores you have achieved in this beatmap.\n- Press `5` or click `Edit` to open the selected beatmap in osu!'s Editor.\n- Press `6` or `Esc` or click `Close` to return to the Song Selection Screen.\n\nClick on **your user panel** to access the **User Options Menu**.\n\nClick the **[osu!cookie](/wiki/Glossary#cookie)** to **start playing the selected beatmap**.\n\n## Results screen\n\n![](img/results-osu.jpg \"Accuracy in osu!\")\n\nThis is the results screen shown after you have successfully passed the beatmap. You can access your online results by scrolling down or pressing the obvious button.\n\n**Note:** The results screen may change depending on the used skin.\n\nBelow are the results screens of the other game modes.\n\n![](img/results-taiko.jpg \"Accuracy in osu!taiko\")\n\n![](img/results-mania.jpg \"Accuracy in osu!mania\")\n\n![](img/results-catch.jpg \"Accuracy in osu!catch\")\n\n### Online Leaderboard\n\n![](img/extended-results-screen.jpg \"An example of an osu!online score\")\n\nThis is your online leaderboard. You can go here by scrolling down from the results screen. Your Local Scoreboard will show your name and the score as usual.\n\n1. Your player bar. It shows your [PP](/wiki/Performance_Points), Global Rank, Total Score, Overall [Accuracy](/wiki/Accuracy), and level bar.\n2. `Save replay to Replays folder`: You can watch the replay later either by opening it from a local leaderboard, or by going to `Replays` directory and double clicking it.\n3. `Add as online favourite`: Include the beatmap into your list of favourites, which is located on your osu! profile page under the \"Beatmaps\" section.\n4. Local Leaderboard: All your results are stored on your computer. To see them, navigate to the [song selection screen](#song-selection-screen), then select `Local Rankings` from the drop-down menu on the left.\n5. `Beatmap Ranking` section. Available only for maps with online leaderboards ([qualified](/wiki/Beatmap/Category#qualified), [ranked](/wiki/Beatmap/Category#ranked), or [loved](/wiki/Beatmap/Category#loved)). You also need to be online to see this section.\n 1. `Overall`: Your position on the map's leaderboard, where you compete against players that used [mods](/wiki/Game_modifier), even if you didn't use any yourself.\n 2. `Accuracy`: How [precisely](/wiki/Accuracy) did you play the beatmap. Will only be counted when your old score is surpassed.\n 3. `Max Combo`: Your longest combo on the map you played.\n 4. `Ranked Score`: Your [best result](/wiki/Score#ranked-score) on the beatmap.\n 5. `Total Score`: Not taken into account, since it does not affect your position in online rankings.\n 6. `Performance`: The amount of [unweighted PP](/wiki/Performance_points#why-didnt-i-gain-the-full-amount-of-pp-from-a-map-i-played) you would receive for the play.\n6. `Overall Ranking` section. It's available only for beatmaps with online leaderboards. You also need to be online to see this section.\n 1. `Overall`: Your global ranking in the world.\n 2. `Accuracy`: Your average [accuracy](/wiki/Accuracy#accuracy) over all beatmaps you have played.\n 3. `Max Combo`: The longest combo over all beatmaps you have played.\n 4. [`Ranked Score`](/wiki/Score#ranked-score): The number of points earned from all ranked beatmaps that you have ever played, with every map being counted exactly once.\n 5. [`Total Score`](/wiki/Score#total-score): Same as ranked score, but it takes into account all beatmaps available on the osu! website, and also underplayed or failed beatmaps. This counts towards your level.\n 6. `Perfomance`: Displays your total amount of Performance Points, and also how many PP the submitted play was worth.\n7. Information about the beatmap with its playcount and pass rate.\n8. Beatmap rating. Use your personal discretion based on whether you enjoy the beatmap or not. Best left alone if you can't decide.\n9. Click here to return to the song selection screen.\n\n![](img/medal-unlock.jpg \"Unlocking a medal\")\n\nAbove is what it looks like to receive a medal.\n", + "# Formatting\n\n*For the writing standards, see: [Article style criteria/Writing](../Writing)*\n\n*Notice: This article uses [RFC 2119](https://tools.ietf.org/html/rfc2119 \"IETF Tools\") to describe requirement levels.*\n\n## Locales\n\nListed below are the properly-supported locales for the wiki:\n\n| File Name | Locale Name | Native Script |\n| :-- | :-- | :-- |\n| `en.md` | English | English |\n| `ar.md` | Arabic | اَلْعَرَبِيَّةُ |\n| `be.md` | Belarusian | Беларуская мова |\n| `bg.md` | Bulgarian | Български |\n| `cs.md` | Czech | Česky |\n| `da.md` | Danish | Dansk |\n| `de.md` | German | Deutsch |\n| `gr.md` | Greek | Ελληνικά |\n| `es.md` | Spanish | Español |\n| `fi.md` | Finnish | Suomi |\n| `fr.md` | French | Français |\n| `hu.md` | Hungarian | Magyar |\n| `id.md` | Indonesian | Bahasa Indonesia |\n| `it.md` | Italian | Italiano |\n| `ja.md` | Japanese | 日本語 |\n| `ko.md` | Korean | 한국어 |\n| `nl.md` | Dutch | Nederlands |\n| `no.md` | Norwegian | Norsk |\n| `pl.md` | Polish | Polski |\n| `pt.md` | Portuguese | Português |\n| `pt-br.md` | Brazilian Portuguese | Português (Brasil) |\n| `ro.md` | Romanian | Română |\n| `ru.md` | Russian | Русский |\n| `sk.md` | Slovak | Slovenčina |\n| `sv.md` | Swedish | Svenska |\n| `th.md` | Thai | ไทย |\n| `tr.md` | Turkish | Türkçe |\n| `uk.md` | Ukrainian | Українська мова |\n| `vi.md` | Vietnamese | Tiếng Việt |\n| `zh.md` | Chinese (Simplified) | 简体中文 |\n| `zh-tw.md` | Traditional Chinese (Taiwan) | 繁體中文(台灣) |\n\n*Note: The website will give readers their selected language's version of an article. If it is not available, the English version will be given.*\n\n### Content parity\n\nTranslations are subject to strict content parity with their English article, in the sense that they must have the same message, regardless of grammar and syntax. Any changes to the translations' meanings must be accompanied by equivalent changes to the English article.\n\nThere are some cases where the content is allowed to differ:\n\n- Articles originally written in a language other than English (in this case, English should act as the translation)\n- Explanations of English words that are common terms in the osu! community\n- External links\n- Tags\n- Subcommunity-specific explanations\n\n## Front matter\n\nFront matter must be placed at the very top of the file. It is written in [YAML](https://en.wikipedia.org/wiki/YAML#Example \"YAML Wikipedia article\") and describes additional information about the article. This must be surrounded by three hyphens (`---`) on the lines above and below it, and an empty line must follow it before the title heading.\n\n### Articles that need help\n\n*Note: Avoid translating English articles with this tag. In addition to this, this tag should be added when the translation needs its own clean up.*\n\nThe `needs_cleanup` tag may be added to articles that need rewriting or formatting help. It is also acceptable to open an issue on GitHub for this purpose. This tag must be written as shown below:\n\n```yaml\nneeds_cleanup: true\n```\n\nWhen adding this tag to an article, [comments](#comments) should also be added to explain what needs to be done to remove the tag.\n\n### Outdated articles\n\n*Note: Avoid translating English articles with this tag. If the English article has this tag, the translation must also have this tag.*\n\nTranslated articles that are outdated must use the `outdated` tag when the English variant is updated. English articles may also become outdated when the content they contain is misleading or no longer relevant. This tag must be written as shown below:\n\n```yaml\noutdated: true\n```\n\nWhen adding this tag to an article, [comments](#comments) should also be added to explain what needs to be updated to remove the tag.\n\n### Tagging articles\n\nTags help the website's search engine query articles better. Tags should be written in the same language as the article and include the original list of tags. Tags should use lowercase letters where applicable.\n\nFor example, an article called \"Beatmap discussion\" may include the following tags:\n\n```yaml\ntags:\n - beatmap discussions\n - modding V2\n - MV2\n```\n\n### Translations without reviews\n\n*Note: Wiki maintainers will determine and apply this mark prior to merging.*\n\nSometimes, translations are added to the wiki without review from other native speakers of the language. In this case, the `no_native_review` mark is added to let future translators know that it may need to be checked again. This tag must be written as shown below:\n\n```yaml\nno_native_review: true\n```\n\n## Article naming\n\n*See also: [Folder names](#folder-names) and [Titles](#titles)*\n\nArticle titles should be singular and use sentence case. See [Wikipedia's naming conventions article](https://en.wikipedia.org/wiki/Wikipedia:Naming_conventions_(plurals) \"Wikipedia\") for more details.\n\nArticle titles should match the folder name it is in (spaces may replace underscores (`_`) where appropriate). If the folder name changes, the article title should be changed to match it and vice versa.\n\n---\n\nContest and tournament articles are an exception. The folder name must use abbreviations, acronyms, or initialisms. The article's title must be the full name of the contest or tournament.\n\n## Folder and file structure\n\n### Folder names\n\n*See also: [Article naming](#article-naming)*\n\nFolder names must be in English and use sentence case.\n\nFolder names must only use these characters:\n\n- uppercase and lowercase letters\n- numbers\n- underscores (`_`)\n- hyphens (`-`)\n- exclamation marks (`!`)\n\n### Article file names\n\nThe file name of an article can be found in the `File Name` column of the [locales section](#locales). The location of a translated article must be placed in the same folder as the English article.\n\n### Index articles\n\nAn index article must be created if the folder is intended to only hold other articles. Index articles must contain a list of articles that are inside its own folder. They may also contain other information, such as a lead paragraph or descriptions of the linked articles.\n\n### Disambiguation articles\n\n[Disambiguation](/wiki/Disambiguation) articles must be placed in the `/wiki/Disambiguation` folder. The main page must be updated to include the disambiguation article. Refer to [Disambiguation/Mod](/wiki/Disambiguation/Mod) as an example.\n\nRedirects must be updated to have the ambiguous keyword(s) redirect to the disambiguation article.\n\nArticles linked from a disambiguation article must have a [For other uses](#for-other-uses) hatnote.\n\n## HTML\n\nHTML must not be used, with exception for [comments](#comments). The structure of the article must be redone if HTML is used.\n\n### Comments\n\nHTML comments should be used for marking to-dos, but may also be used to annotate text. They should be on their own line, but can be placed inline in a paragraph. If placed inline, the start of the comment must not have a space.\n\nBad example:\n\n```markdown\nHTML comments should be used for marking to-dos or annotate text.\n```\n\nGood example:\n\n```markdown\nHTML comments should be used for marking to-dos or annotate text.\n```\n\n## Editing\n\n### End of line sequence\n\n*Caution: Uploading Markdown files using `CRLF` (carriage return and line feed) via GitHub will result in those files using `CRLF`. To prevent this, set the line ending to `LF` (line feed) before uploading.*\n\nMarkdown files must be checked in using the `LF` end of line sequence.\n\n### Escaping\n\nMarkdown syntax should be escaped as needed. However, article titles are parsed as plain text and so must not be escaped.\n\n### Paragraphs\n\nEach paragraph must be followed by one empty line.\n\n### Line breaks\n\nLine breaks must use a backslash (`\\`).\n\nLine breaks must be used sparingly.\n\n## Hatnote\n\n*Not to be confused with [Notice](#notice).*\n\nHatnotes are short notes placed at the top of an article or section to help readers navigate to related articles or inform them about related topics.\n\nHatnotes must be italicised and be placed immediately after the heading. If multiple hatnotes are used, they must be on the same paragraph separated with a line break.\n\n### Main page\n\n*Main page* hatnotes direct the reader to the main article of a topic. When this hatnote is used, it implies that the section it is on is a summary of what the linked page is about. This hatnote should have only one link. These must be formatted as follows:\n\n```markdown\n*Main page: {article}*\n\n*Main pages: {article} and {article}*\n```\n\n### See also\n\n*See also* hatnotes suggest to readers other points of interest from a given article or section. These must be formatted as follows:\n\n```markdown\n*See also: {article}*\n\n*See also: {article} and {article}*\n```\n\n### For see\n\n*For see* hatnotes are similar to *see also* hatnotes, but are generally more descriptive and direct. This hatnote may use more than one link if necessary. These must be formatted as follows:\n\n```markdown\n*For {description}, see: {article}`*\n\n*For {description}, see: {article} and {article}`*\n```\n\n### Not to be confused with\n\n*Not to be confused with* hatnotes help distinguish ambiguous or misunderstood article titles or sections. This hatnote may use more than one link if necessary. These must be formatted as follows:\n\n```markdown\n*Not to be confused with {article}.*\n\n*Not to be confused with {article} or {article}.*\n```\n\n### For other uses\n\n*For other uses* hatnotes are similar to *not to be confused with* hatnotes, but links directly to the [disambiguation article](#disambiguation-articles). This hatnote must only link to the disambiguation article. These must be formatted as follows:\n\n```markdown\n*For other uses, see {disambiguation article}.*\n```\n\n## Notice\n\n*Not to be confused with [Hatnote](#hatnote).*\n\nA notice should be placed where appropriate in a section, but must start off the paragraph and use italics. Notices may contain bolding where appropriate, but should be kept to a minimum. Notices must be written as complete sentences. Thus, unlike most [hatnotes](#hatnotes), must use a full stop (`.`) or an exclamation mark (`!`) if appropriate. Anything within the same paragraph of a notice must also be italicised. These must be formatted as follows:\n\n```markdown\n*Note: {note}.*\n\n*Notice: {notice}.*\n\n*Caution: {caution}.*\n\n*Warning: {warning}.*\n```\n\n- `Note` should be used for factual or trivial details.\n- `Notice` should be used for reminders or to draw attention to something that the reader should be made aware of.\n- `Caution` should be used to warn the reader to avoid unintended consequences.\n- `Warning` should be used to warn the reader that action may be taken against them.\n\n## Emphasising\n\n### Bold\n\nBold must use double asterisks (`**`).\n\nLead paragraphs may bold the first occurrence of the article's title.\n\n### Italics\n\nItalics must use single asterisks (`*`).\n\nNames of work or video games should be italicised. osu!—the game—is exempt from this.\n\nThe first occurrence of an abbreviation, acronym, or initialism may be italicised.\n\nItalics may also be used to provide emphasis or help with readability.\n\n## Headings\n\nAll headings must use sentence case.\n\nHeadings must use the [ATX (hash) style](https://github.github.com/gfm/#atx-headings \"GitHub\") and must have an empty line before and after the heading. The title heading is an exception when it is on the first line. If this is the case, there only needs to be an empty line after the title heading.\n\nHeadings must not exceed a heading level of 5 and must not be used to style or format text.\n\n### Titles\n\n*See also: [Article naming](#article-naming)*\n\n*Caution: Titles are parsed as plain text; they must not be escaped.*\n\nThe first heading in all articles must be a level 1 heading, being the article's title. All headings afterwards must be [section headings](#sections). Titles must not contain formatting, links, or images.\n\nThe title heading must be on the first line, unless [front matter](#front-matter) is being used. If that is the case, the title heading must go after it and have an empty line before the title heading.\n\n### Sections\n\nSection headings must use levels 2 to 5. The section heading proceeding the [title heading](#titles) must be a level 2 heading. Unlike titles, section headings may have small image icons.\n\nSection headings must not skip a heading level (i.e. do not go from a level 2 heading to a level 4 heading) and must not contain formatting or links.\n\n*Notice: On the website, heading levels 4 and 5 will not appear in the table of contents. They cannot be linked to directly either.*\n\n## Lists\n\nLists should not go over 4 levels of indentation and should not have an empty line in between each item.\n\nFor nested lists, bullets or numbers must align with the item content of their parent lists.\n\nThe following example was done incorrectly (take note of the spacing before the bullet):\n\n```markdown\n1. Fly a kite\n - Don't fly a kite if it's raining\n```\n\nThe following example was done correctly:\n\n```markdown\n1. Fly a kite\n - Don't fly a kite if it's raining\n```\n\n### Bulleted\n\nBulleted lists must use a hyphen (`-`). These must then be followed by one space. (Example shown below.)\n\n```markdown\n- osu!\n - Hit circle\n - Combo number\n - Approach circle\n - Slider\n - Hit circles\n - Slider body\n - Slider ticks\n - Spinner\n- osu!taiko\n```\n\n### Numbered\n\nThe numbers in a numbered list must be incremented to represent their step.\n\n```markdown\n1. Download the osu! installer.\n2. Run the installer.\n 1. To change the installation location, click the text underneath the progression bar.\n 2. The installer will prompt for a new location, choose the installation folder.\n3. osu! will start up once installation is complete.\n4. Sign in.\n```\n\n### Mixed\n\nCombining both bulleted and numbered lists should be done sparingly.\n\n```markdown\n1. Download a skin from the forums.\n2. Load the skin file into osu!.\n - If the file is a `.zip`, unzip it and move the contents into the `Skins/` folder (found in your osu! installation folder).\n - If the file is a `.osk`, open it on your desktop or drag-and-drop it into the game client.\n3. Open osu!, if it is not opened, and select the skin in the options.\n - This may have been completed if you opened the `.osk` file or drag-and-dropped it into the game client.\n```\n\n## Code\n\nThe markup for code is a grave mark (`` ` ``). To put grave marks in code, use double grave marks instead. If the grave mark is at the start or end, pad it with one space. (Example shown below.)\n\n```markdown\n`` ` ``\n`` `Space` ``\n```\n\n### Keyboard keys\n\n*Notice: When denoting the letter itself, and not the keyboard key, use quotation marks instead.*\n\nWhen representing keyboard keys, use capital letters for single characters and title case for modifiers. Use the plus symbol (`+`) (without code) to represent key combinations. (Example shown below.)\n\n```markdown\npippi is spelt with a lowercase \"p\" like peppy.\n\nPress `Ctrl` + `O` to open the open dialog.\n```\n\nWhen representing a space or the spacebar, use `` `Space` ``.\n\n### Button and menu text\n\nWhen copying the text from a menu or button, the letter casing should be copied as it appears. (Example shown below.)\n\n```markdown\nThe `osu!direct` button is visible in the main menu on the right side, if you have an active osu!supporter tag.\n```\n\n### Folder and directory names\n\nWhen copying the name of a folder or directory, the letter casing should be copied as it appears, but prefer lowercased paths when possible. Directory paths must not be absolute (i.e. do not start the directory name from the drive letter or from the root folder). (Example shown below.)\n\n```markdown\nosu! is installed in the `AppData/Local` folder by default, unless specified otherwise during installation.\n```\n\n### Keywords and commands\n\nWhen copying a keyword or command, the letter casing should be copied as it appears or how someone normally would type it. If applicable, prefer lowercase letters. (Example shown below.)\n\n```markdown\nAs of now, the `Name` and `Author` commands in the skin configuration file (`skin.ini`) do nothing.\n```\n\n### File names\n\nWhen copying the name of a file, the letter casing should be copied as it appears. If applicable, prefer lowercase letters. (Example shown below.)\n\n```markdown\nTo play osu!, double click the `osu!.exe` icon.\n```\n\n### File extensions\n\n*Notice: File formats (not to be confused with file extensions) must be written in capital letters without the prefixed fullstop (`.`).*\n\nFile extensions must be prefixed with a fullstop (`.`) and be followed by the file extension in lowercase letters. (Example shown below.)\n\n```markdown\nThe JPG (or JPEG) file format has the `.jpg` (or `.jpeg`) extension.\n```\n\n### Chat channels\n\nWhen copying the name of a chat channel, start it with a hash (`#`), followed by the channel name in lowercase letters. (Example shown below.)\n\n```markdown\n`#lobby` is where you can advertise your multi room.\n```\n\n## Preformatted text (code blocks)\n\n*Notice: Syntax highlighting for preformatted text is not implemented on the website yet.*\n\nPreformatted text (also known as code blocks) must be fenced using three grave marks. They should set the language identifier for syntax highlighting.\n\n## Links\n\nThere are two types of links: inline and reference. Inline has two styles.\n\nThe following is an example of both inline styles:\n\n```markdown\n[Game Modifiers](/wiki/Game_Modifiers)\n\n\n```\n\nThe following is an example of the reference style:\n\n```markdown\n[Game Modifiers][game mods link]\n\n[game mods link]: /wiki/Game_Modifiers\n```\n\n---\n\nLinks must use the inline style if they are only referenced once. The inline angle brackets style should be avoided. References to reference links must be placed at the bottom of the article.\n\n### Internal links\n\n*Note: Internal links refer to links that stay inside the `https://osu.ppy.sh/` domain.*\n\n#### Wiki links\n\nAll links that point to an wiki article should start with `/wiki/` followed by the path to get to the article you are targeting. Relative links may also be used. Some examples include the following:\n\n```markdown\n[FAQ](/wiki/FAQ)\n[pippi](/wiki/Mascots#-pippi)\n[Beatmaps](../)\n[Pattern](./Pattern)\n```\n\nWiki links must not use redirects and must not have a trailing forward slash (`/`).\n\nBad examples include the following:\n\n```markdown\n[Article styling criteria](/wiki/ASC)\n[Developers](/wiki/Developers/)\n[Developers](/wiki/Developers/#game-client-developers)\n```\n\nGood examples include the following:\n\n```markdown\n[Article styling criteria](/wiki/Article_styling_criteria)\n[Developers](/wiki/Developers)\n[Developers](/wiki/Developers#game-client-developers)\n```\n\n##### Sub-article links\n\nWiki links that point to a sub-article should include the parent article's folder name in its link text. See the following example:\n\n```markdown\n*See also: [Beatmap Editor/Design](/wiki/Beatmap_Editor/Design)*\n```\n\n##### Section links\n\n*Notice: On the website, heading levels 4 and 5 are not given the id attribute. This means that they can not be linked to directly.*\n\nWiki links that point to a section of an article may use the section sign symbol (`§`). See the following example:\n\n```markdown\n*For timing rules, see: [Ranking Criteria § Timing](/wiki/Ranking_Criteria#timing)*\n```\n\n#### Other osu! links\n\nThe URL from the address bar of your web browser should be copied as it is when linking to other osu! web pages. The `https://osu.ppy.sh` part of the URL must be kept.\n\n##### User profiles\n\nAll usernames must be linked on first occurrence. Other occurrences are optional, but must be consistent throughout the entire article for all usernames. If it is difficult to determine the user's id, it may be skipped over.\n\nWhen linking to a user profile, the user's id number must be used. Use the new website (`https://osu.ppy.sh/users/{username})`) to get the user's id.\n\nThe link text of the user link should be the user's current name.\n\n##### Difficulties\n\nWhenever linking to a single difficulty, use this format as the link text:\n\n```\n{artist} - {title} ({creator}) [{difficuty_name}]\n```\n\nThe link must actually link to that difficulty. Beatmap difficulty URLs must be formatted as follows:\n\n```\nhttps://osu.ppy.sh/beatmapsets/{BeatmapSetID}#{mode}/{BeatmapID}\n```\n\nThe difficulty name may be left outside of the link text, but doing so must be consistent throughout the entire article.\n\n##### Beatmaps\n\nWhenever linking to a beatmap, use this format as the link text:\n\n```\n{artist} - {title} ({creator})\n```\n\nAll beatmap URLs must be formatted as follows:\n\n```\nhttps://osu.ppy.sh/beatmapsets/{BeatmapSetID}\n```\n\n### External links\n\n*Notice: External links refers to links that go outside the `https://osu.ppy.sh/` domain.*\n\nThe `https` protocol must be used, unless the site does not support it. External links must be a clean and direct link to a reputable source. The link text should be the title of the page it is linking to. The URL from the address bar of your web browser should be copied as it is when linking to other external pages.\n\nThere are no visual differences between external and osu! web links. Due to this, the website name should be included in the title text. See the following example:\n\n```markdown\n*For more information about music theory, see: [Music theory](https://en.wikipedia.org/wiki/Music_theory \"Wikipedia\")*\n```\n\n## Images\n\nThere are two types of image links: inline and reference. Examples:\n\n**Inline style:**\n\n```markdown\n![](/wiki/shared/flag/AU.gif)\n```\n\n**Reference style:**\n\n```markdown\n![][flag_AU]\n\n[flag_AU]: /wiki/shared/flag/AU.gif\n```\n\nImages should use the inline linking style. References to reference links must be placed at the bottom of the article.\n\nImages must be placed in a folder named `img`, located in the article's folder. Images that are used in multiple articles should be stored in the `/wiki/shared/` folder.\n\n### Image caching\n\nImages on the website are cached for up to 60 days. The cached image is matched with the image link's URL.\n\nWhen updating an image, either change the image's name or append a query string to the URL. In both cases, all translations linking to the updated image should also be updated.\n\n### Formats and quality\n\nImages should use the JPG format at quality 8 (80 or 80%, depending on the program). If the image contains transparency or has text that must be readable, use the PNG format instead. If the image contains an animation, the GIF format can be used; however, this should be used sparingly as these may take longer to load or can be bigger then the [max file size](#file-size).\n\n### File size\n\nImages must be under 1 megabyte, otherwise they will fail to load. Downscaling and using JPG at 80% is almost always under the size limit.\n\nAll images should be optimised as much as possible. Use [jpeg-archive](https://github.com/danielgtaylor/jpeg-archive \"GitHub\") to compress JPEG images. For consistency, use the following command for jpeg-archive:\n\n```sh\njpeg-recompress -am smallfry \n```\n\nWhere `` is the file name to be compressed and `` is the compressed file name.\n\n### File names\n\n*Notice: File extensions must use lowercase letters, otherwise they will fail to load!*\n\nUse hyphens (`-`) when spacing words. When naming an image, the file name should be meaningful or descriptive but short.\n\n### Formatting and positioning\n\n*Note: It is currently not possible to float an image or have text wrap around it.*\n\nImages on the website will be centred when it is on a single line, by themself. Otherwise, they will be positioned inline with the paragraph. The following example will place the image in the center:\n\n```markdown\nInstalling osu! is easy. First, download the installer from the download page.\n\n![](img/download-page.jpg)\n\nThen locate the installer and run it.\n```\n\n### Alt text\n\nImages should have alt text unless it is for decorative purposes.\n\n### Captions\n\nImages are given captions on the website if they fulfill these conditions:\n\n1. The image is by itself.\n2. The image is not inside a heading.\n3. The image has title text.\n\nCaptions are assumed via the title text, which must be in plain text. Images with captions are also centred with the image on the website.\n\n### Max image width\n\nThe website's max image width is the width of the article body. Images should be no wider than 800 pixels.\n\n### Annotating images\n\nWhen annotating images, use *Torus Regular*. For Chinese, Korean, Japanese characters, use *Microsoft YaHei*.\n\nAnnotating images should be avoided, as it is difficult for translators (and other editors) to edit them.\n\n#### Translating annotated images\n\nWhen translating annotated images, the localised image version must be placed in the same directory as the original version (i.e. the English version). The filename of a localised image version must start with the original version's name, followed by a hyphen, followed by the locale name (in capital letters). See the following examples:\n\n- `hardrock-mod-vs-easy-mod.jpg` for English\n- `hardrock-mod-vs-easy-mod-DE.jpg` for German\n- `hardrock-mod-vs-easy-mod-ZH-TW.jpg` for Traditional Chinese\n\n### Screenshots of gameplay\n\nAll screenshots of gameplay must be done in the stable build, unless it is for a specific feature that is unavailable in the stable build. You should use the in-game screenshot feature (`F12`).\n\n#### Game client settings\n\n*Note: If you do not want to change your current settings for the wiki, you can move your `osu!..cfg` out of the osu! folder and move it back later.*\n\nYou must set these settings before taking a screenshot of the game client (settings not stated below are assumed to be at their defaults):\n\n- Select language: `English`\n- Prefer metadata in original language: `Enabled`\n- Resolution: `1280x720`\n- Fullscreen mode: `Disabled`\n- Parallax: `Disabled`\n- Menu tips: `Disabled`\n- Seasonal backgrounds: `Never`\n- Always show key overlay: `Enabled`\n- Current skin: `Default` (first option)\n\n*Notice to translators: If you are translating an article containing screenshots of the game, you may set the game client's language to the language you are translating in.*\n\n### Image links\n\nImages must not be part of a link text.\n\nFlag icons next to user links must be separate from the link text. See the following example:\n\n```markdown\n![][flag_AU] [peppy](https://osu.ppy.sh/users/2)\n```\n\n### Flag icons\n\n*For a list of flag icons, see: [issue \\#328](https://github.com/ppy/osu-wiki/issues/328 \"GitHub\")*\n\nThe flag icons use the two letter code (in all capital letters) and end with `.gif`. When adding a flag inline, use this format:\n\n```markdown\n![](/wiki/shared/flag/xx.gif)\n```\n\nWhere `xx` is the [ISO 3166-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 \"Wikipedia\") two-lettered country code for the flag.\n\nThe full country name should be added in the title text. The country code in the alternate text is optional, but must be applied to all flag icons in the article.\n\n## Tables\n\nTables on the website only support headings along the first row.\n\nTables must not be beautified (do not pad cells with extra spaces to make their widths uniform). They must have a vertical bar (`|`) on the left and right sides and the text of each cell must be padded with one space on both sides. Empty cells must use a vertical bar (`|`) followed by two spaces then another vertical bar (`|`).\n\nThe delimiter row (the next line after the table heading) must use only three characters per column (and be padded with a space on both sides), which must look like one of the following:\n\n- `:--` (for left align)\n- `:-:` (for centre align)\n- `--:` (for right align)\n\n---\n\nThe following is an example of what a table should look like:\n\n```markdown\n| Team \"Picturesque\" Red | Score | Team \"Statuesque\" Blue | Average Beatmap Stars |\n| :-- | :-: | --: | :-- |\n| **peppy** | 5 - 2 | pippi | 9.3 stars |\n| Aiko | 1 - 6 | **Alisa** | 4.2 stars |\n| Ryūta | 3 - 4 | **Yuzu** | 5.1 stars |\n| **Taikonator** | 7 - 0 | Tama | 13.37 stars |\n| Maria | No Contest | Mocha | |\n```\n\n## Blockquotes\n\nThe blockquote is limited to quoting text from someone. It must not be used to format text otherwise.\n\n## Thematic breaks\n\nThe thematic break (also known as the horizontal rule or line) should be used sparingly. A few uses of the thematic break may include (but is not limited to):\n\n- separating images from text\n- separating multiple images that follow one another\n- shifting the topic within a section\n\nThese must have an empty line before and after the markup. Thematic breaks must use only three hyphens, as depicted below:\n\n```markdown\n---\n```\n" }; } } From eebd5745a823378432472cdca31e3e06904c8109 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 4 Jun 2021 11:44:43 +0900 Subject: [PATCH 709/839] Move full stop out of link and reword slightly --- osu.Game/Overlays/WikiOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs index 2261f71c04..c5ba0eed66 100644 --- a/osu.Game/Overlays/WikiOverlay.cs +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -139,7 +139,7 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, CurrentPath = $@"{api.WebsiteRootUrl}/wiki/", - Text = $"Something went wrong when trying to fetch page \"{path.Value}\".\n\n[Go back to main page.](Main_Page)", + Text = $"Something went wrong when trying to fetch page \"{path.Value}\".\n\n[Return to the main page](Main_Page).", DocumentMargin = new MarginPadding(0), DocumentPadding = new MarginPadding { From 7e781501443b62d2b3dc4102b2375c35252c7f42 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 4 Jun 2021 10:16:49 +0700 Subject: [PATCH 710/839] add basic content wiki sidebar --- osu.Game/Overlays/Wiki/WikiSidebar.cs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/osu.Game/Overlays/Wiki/WikiSidebar.cs b/osu.Game/Overlays/Wiki/WikiSidebar.cs index 6d1f520135..1f48d026ad 100644 --- a/osu.Game/Overlays/Wiki/WikiSidebar.cs +++ b/osu.Game/Overlays/Wiki/WikiSidebar.cs @@ -1,9 +1,36 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; + namespace osu.Game.Overlays.Wiki { public class WikiSidebar : OverlaySidebar { + private FillFlowContainer tableOfContents; + + protected override Drawable CreateContent() => new FillFlowContainer + { + Direction = FillDirection.Vertical, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new OsuSpriteText + { + Text = "CONTENTS", + Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold), + }, + tableOfContents = new FillFlowContainer + { + Direction = FillDirection.Vertical, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + } + }, + }; } } From b1b305c150aeb2bb5c6df0a7d62a9f72e5fcba3e Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 4 Jun 2021 10:17:38 +0700 Subject: [PATCH 711/839] add method AddToc --- osu.Game/Overlays/Wiki/WikiSidebar.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/osu.Game/Overlays/Wiki/WikiSidebar.cs b/osu.Game/Overlays/Wiki/WikiSidebar.cs index 1f48d026ad..0f4ae45650 100644 --- a/osu.Game/Overlays/Wiki/WikiSidebar.cs +++ b/osu.Game/Overlays/Wiki/WikiSidebar.cs @@ -3,6 +3,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Containers.Markdown; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; @@ -32,5 +33,19 @@ namespace osu.Game.Overlays.Wiki } }, }; + + public void AddToc(string title, MarkdownHeading heading, int level) + { + switch (level) + { + case 2: + tableOfContents.Add(new OsuSpriteText + { + Text = title, + Font = OsuFont.GetFont(size: 15), + }); + break; + } + } } } From abb522f084fa882fbf28dd858df0c1dcb0d8ecc6 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 4 Jun 2021 10:53:48 +0700 Subject: [PATCH 712/839] add missing using --- osu.Game/Overlays/WikiOverlay.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs index 4cfbbbbfe4..812f26e77d 100644 --- a/osu.Game/Overlays/WikiOverlay.cs +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -11,6 +11,7 @@ using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.Wiki; +using osu.Game.Overlays.Wiki.Markdown; namespace osu.Game.Overlays { From 59dbed64180576aca1f8a7eaec39bfb550ebeb91 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 4 Jun 2021 11:08:51 +0700 Subject: [PATCH 713/839] create ArticleMarkdownContainer in WikiArticlePage --- osu.Game/Overlays/Wiki/WikiArticlePage.cs | 25 +++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Wiki/WikiArticlePage.cs b/osu.Game/Overlays/Wiki/WikiArticlePage.cs index c41ab8b250..12a3ee386e 100644 --- a/osu.Game/Overlays/Wiki/WikiArticlePage.cs +++ b/osu.Game/Overlays/Wiki/WikiArticlePage.cs @@ -1,14 +1,19 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using Markdig.Syntax; +using Markdig.Syntax.Inlines; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Containers.Markdown; using osu.Game.Overlays.Wiki.Markdown; namespace osu.Game.Overlays.Wiki { public class WikiArticlePage : GridContainer { + private readonly WikiSidebar sidebar; public Container SidebarContainer { get; } public WikiArticlePage(string currentPath, string markdown) @@ -31,9 +36,9 @@ namespace osu.Game.Overlays.Wiki SidebarContainer = new Container { AutoSizeAxes = Axes.X, - Child = new WikiSidebar(), + Child = sidebar = new WikiSidebar(), }, - new WikiMarkdownContainer + new ArticleMarkdownContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, @@ -46,9 +51,25 @@ namespace osu.Game.Overlays.Wiki Left = 30, Right = 50, }, + OnAddHeading = sidebar.AddToc, } }, }; } + + private class ArticleMarkdownContainer : WikiMarkdownContainer + { + public Action OnAddHeading; + + protected override MarkdownHeading CreateHeading(HeadingBlock headingBlock) + { + var heading = base.CreateHeading(headingBlock); + var title = ((LiteralInline)headingBlock.Inline.FirstChild).Content.ToString(); + + OnAddHeading(title, heading, headingBlock.Level); + + return heading; + } + } } } From d8d4bf66b3fdfd7367f347325d8a03f44b841fd7 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 4 Jun 2021 11:09:20 +0700 Subject: [PATCH 714/839] create TocTitle in WikiSidebar --- osu.Game/Overlays/Wiki/WikiSidebar.cs | 34 +++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Wiki/WikiSidebar.cs b/osu.Game/Overlays/Wiki/WikiSidebar.cs index 0f4ae45650..aa521d4ff5 100644 --- a/osu.Game/Overlays/Wiki/WikiSidebar.cs +++ b/osu.Game/Overlays/Wiki/WikiSidebar.cs @@ -1,10 +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 System.Collections.Generic; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers.Markdown; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; namespace osu.Game.Overlays.Wiki @@ -39,13 +42,34 @@ namespace osu.Game.Overlays.Wiki switch (level) { case 2: - tableOfContents.Add(new OsuSpriteText - { - Text = title, - Font = OsuFont.GetFont(size: 15), - }); + tableOfContents.Add(new TocTitle(title)); break; } } + + private class TocTitle : OsuHoverContainer + { + private readonly OsuSpriteText spriteText; + + public TocTitle(string text) + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Child = spriteText = new OsuSpriteText + { + Text = text, + Font = OsuFont.GetFont(size: 15), + }; + } + + protected override IEnumerable EffectTargets => new Drawable[] { spriteText }; + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + IdleColour = colourProvider.Light2; + HoverColour = colourProvider.Light1; + } + } } } From 41ec531bab6c54a331ded55d8d86070f90e3ca35 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 4 Jun 2021 11:12:42 +0700 Subject: [PATCH 715/839] add subtitle toc --- osu.Game/Overlays/Wiki/WikiSidebar.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Wiki/WikiSidebar.cs b/osu.Game/Overlays/Wiki/WikiSidebar.cs index aa521d4ff5..c2e28f4a81 100644 --- a/osu.Game/Overlays/Wiki/WikiSidebar.cs +++ b/osu.Game/Overlays/Wiki/WikiSidebar.cs @@ -44,6 +44,10 @@ namespace osu.Game.Overlays.Wiki case 2: tableOfContents.Add(new TocTitle(title)); break; + + case 3: + tableOfContents.Add(new TocTitle(title, true)); + break; } } @@ -51,14 +55,14 @@ namespace osu.Game.Overlays.Wiki { private readonly OsuSpriteText spriteText; - public TocTitle(string text) + public TocTitle(string text, bool subtitle = false) { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; Child = spriteText = new OsuSpriteText { Text = text, - Font = OsuFont.GetFont(size: 15), + Font = OsuFont.GetFont(size: subtitle ? 12 : 15), }; } From 424d1b402587256e5c872ac38ea91e20262eacbb Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 4 Jun 2021 11:18:08 +0700 Subject: [PATCH 716/839] add margin padding spacing in toc title --- osu.Game/Overlays/Wiki/WikiSidebar.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/Wiki/WikiSidebar.cs b/osu.Game/Overlays/Wiki/WikiSidebar.cs index c2e28f4a81..e25e6189d7 100644 --- a/osu.Game/Overlays/Wiki/WikiSidebar.cs +++ b/osu.Game/Overlays/Wiki/WikiSidebar.cs @@ -64,6 +64,8 @@ namespace osu.Game.Overlays.Wiki Text = text, Font = OsuFont.GetFont(size: subtitle ? 12 : 15), }; + Margin = new MarginPadding { Top = subtitle ? 5 : 10 }; + Padding = new MarginPadding { Left = subtitle ? 10 : 0 }; } protected override IEnumerable EffectTargets => new Drawable[] { spriteText }; From 4e73d0254044741d8a34194dac3589566879c856 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 4 Jun 2021 11:35:23 +0700 Subject: [PATCH 717/839] move sidebar into local variable --- osu.Game/Overlays/Wiki/WikiArticlePage.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Wiki/WikiArticlePage.cs b/osu.Game/Overlays/Wiki/WikiArticlePage.cs index 12a3ee386e..99068d6919 100644 --- a/osu.Game/Overlays/Wiki/WikiArticlePage.cs +++ b/osu.Game/Overlays/Wiki/WikiArticlePage.cs @@ -13,7 +13,6 @@ namespace osu.Game.Overlays.Wiki { public class WikiArticlePage : GridContainer { - private readonly WikiSidebar sidebar; public Container SidebarContainer { get; } public WikiArticlePage(string currentPath, string markdown) @@ -29,6 +28,9 @@ namespace osu.Game.Overlays.Wiki new Dimension(GridSizeMode.AutoSize), new Dimension(), }; + + WikiSidebar sidebar; + Content = new[] { new Drawable[] From e28b38653be073deae56b4cb2e44f31f0e101a6a Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 4 Jun 2021 11:43:00 +0700 Subject: [PATCH 718/839] cache scroll container --- osu.Game/Overlays/WikiOverlay.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs index 812f26e77d..5beb5d50e6 100644 --- a/osu.Game/Overlays/WikiOverlay.cs +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -26,6 +26,9 @@ namespace osu.Game.Overlays [Resolved] private IAPIProvider api { get; set; } + [Cached] + private readonly OverlayScrollContainer scrollContainer; + private GetWikiRequest request; private CancellationTokenSource cancellationToken; @@ -37,6 +40,7 @@ namespace osu.Game.Overlays public WikiOverlay() : base(OverlayColourScheme.Orange, false) { + scrollContainer = ScrollFlow; } public void ShowPage(string pagePath = index_path) From 37ff6299c9c73df7c2eb374e3ab0acac57776e1d Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 4 Jun 2021 11:43:32 +0700 Subject: [PATCH 719/839] add target in toc title --- osu.Game/Overlays/Wiki/WikiSidebar.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Wiki/WikiSidebar.cs b/osu.Game/Overlays/Wiki/WikiSidebar.cs index e25e6189d7..b3e315fefc 100644 --- a/osu.Game/Overlays/Wiki/WikiSidebar.cs +++ b/osu.Game/Overlays/Wiki/WikiSidebar.cs @@ -42,21 +42,25 @@ namespace osu.Game.Overlays.Wiki switch (level) { case 2: - tableOfContents.Add(new TocTitle(title)); - break; - case 3: - tableOfContents.Add(new TocTitle(title, true)); + tableOfContents.Add(new TocTitle(title, heading, level == 3)); break; } } private class TocTitle : OsuHoverContainer { + [Resolved] + private OverlayScrollContainer scrollContainer { get; set; } + private readonly OsuSpriteText spriteText; - public TocTitle(string text, bool subtitle = false) + private readonly MarkdownHeading target; + + public TocTitle(string text, MarkdownHeading target, bool subtitle = false) { + this.target = target; + RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; Child = spriteText = new OsuSpriteText From 91e77ee4dea2649d541ad9d305ba40b53aa6b922 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 4 Jun 2021 11:43:49 +0700 Subject: [PATCH 720/839] add onclick in toc title --- osu.Game/Overlays/Wiki/WikiSidebar.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Overlays/Wiki/WikiSidebar.cs b/osu.Game/Overlays/Wiki/WikiSidebar.cs index b3e315fefc..1c5e11a7ca 100644 --- a/osu.Game/Overlays/Wiki/WikiSidebar.cs +++ b/osu.Game/Overlays/Wiki/WikiSidebar.cs @@ -6,6 +6,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers.Markdown; +using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -80,6 +81,12 @@ namespace osu.Game.Overlays.Wiki IdleColour = colourProvider.Light2; HoverColour = colourProvider.Light1; } + + protected override bool OnClick(ClickEvent e) + { + scrollContainer.ScrollTo(target); + return base.OnClick(e); + } } } } From 59536747373395b413d4fa79a43184a4ef0391ef Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 4 Jun 2021 14:06:08 +0900 Subject: [PATCH 721/839] Tidy up constants --- osu.Game.Rulesets.Osu/Skinning/Default/CirclePiece.cs | 1 - osu.Game.Rulesets.Osu/Skinning/Default/KiaiFlash.cs | 10 +++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/CirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Default/CirclePiece.cs index 0b43704439..cb68d4b7a7 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/CirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/CirclePiece.cs @@ -45,7 +45,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default new KiaiFlash { RelativeSizeAxes = Axes.Both, - FlashOpacity = 0.25f, }, triangles = new TrianglesPiece { diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/KiaiFlash.cs b/osu.Game.Rulesets.Osu/Skinning/Default/KiaiFlash.cs index a13bcba4c7..d49b1713f6 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/KiaiFlash.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/KiaiFlash.cs @@ -12,7 +12,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default { public class KiaiFlash : BeatSyncedContainer { - public float FlashOpacity = 1f; + private const double fade_length = 80; + + private const float flash_opacity = 0.25f; public KiaiFlash() { @@ -30,14 +32,12 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) { if (!effectPoint.KiaiMode) - { return; - } Child - .FadeTo(FlashOpacity, EarlyActivationMilliseconds, Easing.OutQuint) + .FadeTo(flash_opacity, EarlyActivationMilliseconds, Easing.OutQuint) .Then() - .FadeOut(timingPoint.BeatLength - 80, Easing.OutSine); + .FadeOut(timingPoint.BeatLength - fade_length, Easing.OutSine); } } } From 52557da335b1fd0374deb8c2447666d1a4153ef8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 4 Jun 2021 14:06:23 +0900 Subject: [PATCH 722/839] Add test coverage --- .../TestSceneHitCircleKiai.cs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleKiai.cs diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleKiai.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleKiai.cs new file mode 100644 index 0000000000..2bce8fa7f2 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleKiai.cs @@ -0,0 +1,30 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; + +namespace osu.Game.Rulesets.Osu.Tests +{ + [TestFixture] + public class TestSceneHitCircleKiai : TestSceneHitCircle + { + [SetUp] + public void SetUp() => Schedule(() => + { + var controlPointInfo = new ControlPointInfo(); + + controlPointInfo.Add(0, new TimingControlPoint { BeatLength = 500 }); + controlPointInfo.Add(0, new EffectControlPoint { KiaiMode = true }); + + Beatmap.Value = CreateWorkingBeatmap(new Beatmap + { + ControlPointInfo = controlPointInfo + }); + + // track needs to be playing for BeatSyncedContainer to work. + Beatmap.Value.Track.Start(); + }); + } +} From a706ff63eddec695d2eaa039a1279fcdf086283a Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 4 Jun 2021 12:50:03 +0700 Subject: [PATCH 723/839] change sprite text to text flow --- osu.Game/Overlays/Wiki/WikiSidebar.cs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Wiki/WikiSidebar.cs b/osu.Game/Overlays/Wiki/WikiSidebar.cs index 1c5e11a7ca..d91076e40a 100644 --- a/osu.Game/Overlays/Wiki/WikiSidebar.cs +++ b/osu.Game/Overlays/Wiki/WikiSidebar.cs @@ -54,26 +54,30 @@ namespace osu.Game.Overlays.Wiki [Resolved] private OverlayScrollContainer scrollContainer { get; set; } - private readonly OsuSpriteText spriteText; - private readonly MarkdownHeading target; + private readonly OsuTextFlowContainer textFlow; + public TocTitle(string text, MarkdownHeading target, bool subtitle = false) { this.target = target; RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; - Child = spriteText = new OsuSpriteText + Child = textFlow = new OsuTextFlowContainer(t => { - Text = text, - Font = OsuFont.GetFont(size: subtitle ? 12 : 15), - }; + t.Font = OsuFont.GetFont(size: subtitle ? 12 : 15); + }).With(f => + { + f.AddText(text); + f.RelativeSizeAxes = Axes.X; + f.AutoSizeAxes = Axes.Y; + }); Margin = new MarginPadding { Top = subtitle ? 5 : 10 }; Padding = new MarginPadding { Left = subtitle ? 10 : 0 }; } - protected override IEnumerable EffectTargets => new Drawable[] { spriteText }; + protected override IEnumerable EffectTargets => new Drawable[] { textFlow }; [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider) From f62a2747f67721cc333ca37c93bb8721287cdd7a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 4 Jun 2021 14:51:35 +0900 Subject: [PATCH 724/839] Add legacy implementation --- .../Skinning/Legacy/KiaiFlashingSprite.cs | 61 +++++++++++++++++++ .../Skinning/Legacy/LegacyMainCirclePiece.cs | 13 ++-- 2 files changed, 67 insertions(+), 7 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Skinning/Legacy/KiaiFlashingSprite.cs diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/KiaiFlashingSprite.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/KiaiFlashingSprite.cs new file mode 100644 index 0000000000..4a1d69ad41 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/KiaiFlashingSprite.cs @@ -0,0 +1,61 @@ +// 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.Track; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Graphics.Containers; + +namespace osu.Game.Rulesets.Osu.Skinning.Legacy +{ + internal class KiaiFlashingSprite : BeatSyncedContainer + { + private readonly Sprite mainSprite; + private readonly Sprite flashingSprite; + + public Texture Texture + { + set + { + mainSprite.Texture = value; + flashingSprite.Texture = value; + } + } + + private const float flash_opacity = 0.3f; + + public KiaiFlashingSprite() + { + AutoSizeAxes = Axes.Both; + + Children = new Drawable[] + { + mainSprite = new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + flashingSprite = new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Alpha = 0, + Blending = BlendingParameters.Additive, + } + }; + } + + protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) + { + if (!effectPoint.KiaiMode) + return; + + flashingSprite + .FadeTo(flash_opacity) + .Then() + .FadeOut(timingPoint.BeatLength * 0.75f); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs index ffbeea5e0e..822dad8523 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs @@ -5,7 +5,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.Textures; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; @@ -32,9 +31,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); } - private Container circleSprites; - private Sprite hitCircleSprite; - private Sprite hitCircleOverlay; + private Container circleSprites; + private Drawable hitCircleSprite; + private Drawable hitCircleOverlay; private SkinnableSpriteText hitCircleText; @@ -72,20 +71,20 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy InternalChildren = new Drawable[] { - circleSprites = new Container + circleSprites = new Container { Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, Children = new[] { - hitCircleSprite = new Sprite + hitCircleSprite = new KiaiFlashingSprite { Texture = baseTexture, Anchor = Anchor.Centre, Origin = Anchor.Centre, }, - hitCircleOverlay = new Sprite + hitCircleOverlay = new KiaiFlashingSprite { Texture = overlayTexture, Anchor = Anchor.Centre, From 996c15610668fedb30df9dd4d885b648a6fb82dc Mon Sep 17 00:00:00 2001 From: Nathan Alo Date: Fri, 4 Jun 2021 13:56:10 +0800 Subject: [PATCH 725/839] apply suggestions - apply 0 alpha to beatmap background if storyboard replaces it - use an AudioContainer to mute all samples coming from the storyboard --- .../BeatmapBackgroundWithStoryboard.cs | 27 +++++++------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs b/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs index 9695e93f5d..b9e5b3c08f 100644 --- a/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs +++ b/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs @@ -1,13 +1,11 @@ // 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.Graphics; -using osu.Framework.Graphics.Textures; +using osu.Framework.Graphics.Containers; using osu.Framework.Timing; using osu.Game.Beatmaps; -using osu.Game.Storyboards; using osu.Game.Storyboards.Drawables; namespace osu.Game.Graphics.Backgrounds @@ -22,24 +20,17 @@ namespace osu.Game.Graphics.Backgrounds [BackgroundDependencyLoader] private void load() { - var storyboard = new Storyboard { BeatmapInfo = Beatmap.BeatmapInfo }; - - foreach (var layer in storyboard.Layers) - { - if (layer.Name != "Fail") - layer.Elements = Beatmap.Storyboard.GetLayer(layer.Name).Elements.Where(e => !(e is StoryboardSampleInfo)).ToList(); - } - - if (!storyboard.HasDrawable) + if (!Beatmap.Storyboard.HasDrawable) return; - if (storyboard.ReplacesBackground) - { - Sprite.Texture = Texture.WhitePixel; - Sprite.Colour = Colour4.Black; - } + if (Beatmap.Storyboard.ReplacesBackground) + Sprite.Alpha = 0; - LoadComponentAsync(new DrawableStoryboard(storyboard) { Clock = new InterpolatingFramedClock(Beatmap.Track) }, AddInternal); + var audio = new AudioContainer { RelativeSizeAxes = Axes.Both }; + audio.Volume.Value = 0; + + AddInternal(audio); + LoadComponentAsync(new DrawableStoryboard(Beatmap.Storyboard) { Clock = new InterpolatingFramedClock(Beatmap.Track) }, audio.Add); } } } From 071c07586a42846447f135219f6f037e66de841c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 4 Jun 2021 15:00:53 +0900 Subject: [PATCH 726/839] Increase music volume back to 80% for the time being --- 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 7935815f38..9c3adba342 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -58,7 +58,7 @@ namespace osu.Game /// /// The maximum volume at which audio tracks should playback. This can be set lower than 1 to create some head-room for sound effects. /// - internal const double GLOBAL_TRACK_VOLUME_ADJUST = 0.5; + internal const double GLOBAL_TRACK_VOLUME_ADJUST = 0.8; public bool UseDevelopmentServer { get; } From 6c1fede18e0b0b133a89a7629f5aeb3db5439fe3 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 4 Jun 2021 13:11:37 +0700 Subject: [PATCH 727/839] add wiki sidebar test scene --- .../Visual/Online/TestSceneWikiSidebar.cs | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneWikiSidebar.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiSidebar.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiSidebar.cs new file mode 100644 index 0000000000..fd7dbbe3ff --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiSidebar.cs @@ -0,0 +1,59 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Markdig.Parsers; +using Markdig.Syntax; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Containers.Markdown; +using osu.Game.Overlays; +using osu.Game.Overlays.Wiki; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneWikiSidebar : OsuTestScene + { + [Cached] + private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Orange); + + [Cached] + private readonly OverlayScrollContainer scrollContainer = new OverlayScrollContainer(); + + private WikiSidebar sidebar; + + private readonly MarkdownHeading dummyHeading = new MarkdownHeading(new HeadingBlock(new HeadingBlockParser())); + + [SetUp] + public void SetUp() => Schedule(() => Child = sidebar = new WikiSidebar()); + + [Test] + public void TestNoContent() + { + AddStep("No Content", () => { }); + } + + [Test] + public void TestOnlyMainTitle() + { + AddStep("Add TOC", () => + { + for (var i = 0; i < 10; i++) + { + sidebar.AddToc($"This is a very long title {i + 1}", dummyHeading, 2); + } + }); + } + + [Test] + public void TestWithSubtitle() + { + AddStep("Add TOC", () => + { + for (var i = 0; i < 20; i++) + { + sidebar.AddToc($"This is a very long title {i + 1}", dummyHeading, i % 4 == 0 ? 2 : 3); + } + }); + } + } +} From 19a44d65c5df7a1c83dfe9ca3b73ee0fbeb63d7f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 4 Jun 2021 15:18:16 +0900 Subject: [PATCH 728/839] Tidy up code --- .../Backgrounds/BeatmapBackgroundWithStoryboard.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs b/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs index b9e5b3c08f..6a42e83305 100644 --- a/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs +++ b/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs @@ -26,11 +26,12 @@ namespace osu.Game.Graphics.Backgrounds if (Beatmap.Storyboard.ReplacesBackground) Sprite.Alpha = 0; - var audio = new AudioContainer { RelativeSizeAxes = Axes.Both }; - audio.Volume.Value = 0; - - AddInternal(audio); - LoadComponentAsync(new DrawableStoryboard(Beatmap.Storyboard) { Clock = new InterpolatingFramedClock(Beatmap.Track) }, audio.Add); + LoadComponentAsync(new AudioContainer + { + RelativeSizeAxes = Axes.Both, + Volume = { Value = 0 }, + Child = new DrawableStoryboard(Beatmap.Storyboard) { Clock = new InterpolatingFramedClock(Beatmap.Track) } + }, AddInternal); } } } From 37c8c63fc566c115aa3f974b6d23bb12f62caa05 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 4 Jun 2021 16:18:04 +0900 Subject: [PATCH 729/839] Ensure all frames in an animation are retrieved from the same skin --- osu.Game/Skinning/LegacySkinExtensions.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacySkinExtensions.cs b/osu.Game/Skinning/LegacySkinExtensions.cs index d8fb1fa664..6e8276c01e 100644 --- a/osu.Game/Skinning/LegacySkinExtensions.cs +++ b/osu.Game/Skinning/LegacySkinExtensions.cs @@ -54,9 +54,16 @@ namespace osu.Game.Skinning IEnumerable getTextures() { + ISkin lookupSource = null; + for (int i = 0; true; i++) { - if ((texture = source.GetTexture($"{componentName}{animationSeparator}{i}", wrapModeS, wrapModeT)) == null) + string frameName = $"{componentName}{animationSeparator}{i}"; + + // ensure all textures are retrieved from the same skin source. + lookupSource ??= source.FindProvider(s => s.GetTexture(frameName, wrapModeS, wrapModeT) != null); + + if ((texture = lookupSource?.GetTexture(frameName, wrapModeS, wrapModeT)) == null) break; yield return texture; From b373b120ff090d633863d312bc0b2cadf24d7255 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 4 Jun 2021 16:31:50 +0900 Subject: [PATCH 730/839] Use general lifetime container for follow point container --- .../Connections/FollowPointConnection.cs | 22 +++---- .../Connections/FollowPointRenderer.cs | 64 ++++--------------- 2 files changed, 20 insertions(+), 66 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs index cda4715280..7effc4cfa7 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs @@ -5,6 +5,7 @@ using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Pooling; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Pooling; using osuTK; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections @@ -12,38 +13,31 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections /// /// Visualises the s between two s. /// - public class FollowPointConnection : PoolableDrawable + public class FollowPointConnection : PoolableDrawableWithLifetime { // Todo: These shouldn't be constants public const int SPACING = 32; public const double PREEMPT = 800; - public FollowPointLifetimeEntry Entry; public DrawablePool Pool; - protected override void PrepareForUse() + protected override void OnApply(FollowPointLifetimeEntry entry) { - base.PrepareForUse(); - - Entry.Invalidated += onEntryInvalidated; + base.OnApply(entry); + entry.Invalidated += refreshPoints; refreshPoints(); } - protected override void FreeAfterUse() + protected override void OnFree(FollowPointLifetimeEntry entry) { - base.FreeAfterUse(); - - Entry.Invalidated -= onEntryInvalidated; + base.OnFree(entry); + entry.Invalidated -= refreshPoints; // Return points to the pool. ClearInternal(false); - - Entry = null; } - private void onEntryInvalidated() => Scheduler.AddOnce(refreshPoints); - private void refreshPoints() { ClearInternal(false); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs index 3e85e528e8..21e6619444 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs @@ -6,43 +6,32 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Performance; using osu.Framework.Graphics.Pooling; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Pooling; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections { /// /// Visualises connections between s. /// - public class FollowPointRenderer : CompositeDrawable + public class FollowPointRenderer : PooledDrawableWithLifetimeContainer { - public override bool RemoveCompletedTransforms => false; - - public IReadOnlyList Entries => lifetimeEntries; + public new IReadOnlyList Entries => lifetimeEntries; private DrawablePool connectionPool; private DrawablePool pointPool; private readonly List lifetimeEntries = new List(); - private readonly Dictionary connectionsInUse = new Dictionary(); private readonly Dictionary startTimeMap = new Dictionary(); - private readonly LifetimeEntryManager lifetimeManager = new LifetimeEntryManager(); - - public FollowPointRenderer() - { - lifetimeManager.EntryBecameAlive += onEntryBecameAlive; - lifetimeManager.EntryBecameDead += onEntryBecameDead; - } [BackgroundDependencyLoader] private void load() { InternalChildren = new Drawable[] { - connectionPool = new DrawablePoolNoLifetime(1, 200), - pointPool = new DrawablePoolNoLifetime(50, 1000) + connectionPool = new DrawablePool(1, 200), + pointPool = new DrawablePool(50, 1000) }; } @@ -107,7 +96,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections previousEntry.End = newEntry.Start; } - lifetimeManager.AddEntry(newEntry); + Add(newEntry); } private void removeEntry(OsuHitObject hitObject) @@ -118,7 +107,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections entry.UnbindEvents(); lifetimeEntries.RemoveAt(index); - lifetimeManager.RemoveEntry(entry); + Remove(entry); if (index > 0) { @@ -131,30 +120,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections } } - protected override bool CheckChildrenLife() + protected override FollowPointConnection GetDrawable(FollowPointLifetimeEntry entry) { - bool anyAliveChanged = base.CheckChildrenLife(); - anyAliveChanged |= lifetimeManager.Update(Time.Current); - return anyAliveChanged; - } - - private void onEntryBecameAlive(LifetimeEntry entry) - { - var connection = connectionPool.Get(c => - { - c.Entry = (FollowPointLifetimeEntry)entry; - c.Pool = pointPool; - }); - - connectionsInUse[entry] = connection; - - AddInternal(connection); - } - - private void onEntryBecameDead(LifetimeEntry entry) - { - RemoveInternal(connectionsInUse[entry]); - connectionsInUse.Remove(entry); + var connection = connectionPool.Get(); + connection.Pool = pointPool; + connection.Apply(entry); + return connection; } private void onStartTimeChanged(OsuHitObject hitObject) @@ -171,16 +142,5 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections entry.UnbindEvents(); lifetimeEntries.Clear(); } - - private class DrawablePoolNoLifetime : DrawablePool - where T : PoolableDrawable, new() - { - public override bool RemoveWhenNotAlive => false; - - public DrawablePoolNoLifetime(int initialSize, int? maximumSize = null) - : base(initialSize, maximumSize) - { - } - } } } From ae2165b3be51770a8fe8da1eaefb730e6a75e6de Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 16:57:40 +0900 Subject: [PATCH 731/839] Fix incorrect xmldoc --- osu.Game/Skinning/ISkin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/ISkin.cs b/osu.Game/Skinning/ISkin.cs index 1c3598abb4..09e79a5ff5 100644 --- a/osu.Game/Skinning/ISkin.cs +++ b/osu.Game/Skinning/ISkin.cs @@ -60,7 +60,7 @@ namespace osu.Game.Skinning IBindable GetConfig(TLookup lookup); /// - /// For the specified texture, find any potential skin that can fulfill the lookup. + /// Find the first (if any) skin that can fulfill the lookup. /// This should be used for cases where subsequent lookups (for related components) need to occur on the same skin. /// /// The skin to be used for subsequent lookups, or null if none is available. From 6d6c03eafe1980ee0c705ae4caf589bc4dc1c141 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 4 Jun 2021 14:45:06 +0700 Subject: [PATCH 732/839] use linq to find first literal inline --- osu.Game/Overlays/Wiki/WikiArticlePage.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Wiki/WikiArticlePage.cs b/osu.Game/Overlays/Wiki/WikiArticlePage.cs index 99068d6919..a60bebdc4b 100644 --- a/osu.Game/Overlays/Wiki/WikiArticlePage.cs +++ b/osu.Game/Overlays/Wiki/WikiArticlePage.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Linq; using Markdig.Syntax; using Markdig.Syntax.Inlines; using osu.Framework.Graphics; @@ -66,7 +67,7 @@ namespace osu.Game.Overlays.Wiki protected override MarkdownHeading CreateHeading(HeadingBlock headingBlock) { var heading = base.CreateHeading(headingBlock); - var title = ((LiteralInline)headingBlock.Inline.FirstChild).Content.ToString(); + var title = ((LiteralInline)headingBlock.Inline.First(i => i is LiteralInline)).Content.ToString(); OnAddHeading(title, heading, headingBlock.Level); From bc892086fea7f5dedb1ada1ae396e744502e98e2 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 4 Jun 2021 16:41:56 +0900 Subject: [PATCH 733/839] Resolve nullable inspection, enable nullable for `FollowPointLifetimeEntry` --- .../Connections/FollowPointConnection.cs | 12 +++++++----- .../Connections/FollowPointLifetimeEntry.cs | 17 ++++++++--------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs index 7effc4cfa7..55f494d2f9 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Diagnostics; using osu.Framework.Graphics; using osu.Framework.Graphics.Pooling; using osu.Game.Rulesets.Objects; @@ -26,7 +27,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections base.OnApply(entry); entry.Invalidated += refreshPoints; - refreshPoints(); + refreshPoints(entry); } protected override void OnFree(FollowPointLifetimeEntry entry) @@ -38,12 +39,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections ClearInternal(false); } - private void refreshPoints() + private void refreshPoints(FollowPointLifetimeEntry entry) { ClearInternal(false); - OsuHitObject start = Entry.Start; - OsuHitObject end = Entry.End; + OsuHitObject start = entry.Start; + OsuHitObject end = entry.End; + Debug.Assert(end != null, $"{nameof(FollowPointLifetimeEntry)} without end hit object should never be alive"); double startTime = start.GetEndTime(); @@ -88,7 +90,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections } // todo: use Expire() on FollowPoints and take lifetime from them when https://github.com/ppy/osu-framework/issues/3300 is fixed. - Entry.LifetimeEnd = finalTransformEndTime; + entry.LifetimeEnd = finalTransformEndTime; } /// diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointLifetimeEntry.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointLifetimeEntry.cs index a167cb2f0f..a44d07d92c 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointLifetimeEntry.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointLifetimeEntry.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. +#nullable enable + using System; using osu.Framework.Bindables; using osu.Framework.Graphics.Performance; @@ -11,7 +13,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections { public class FollowPointLifetimeEntry : LifetimeEntry { - public event Action Invalidated; + public event Action? Invalidated; public readonly OsuHitObject Start; public FollowPointLifetimeEntry(OsuHitObject start) @@ -22,9 +24,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections bindEvents(); } - private OsuHitObject end; + private OsuHitObject? end; - public OsuHitObject End + public OsuHitObject? End { get => end; set @@ -56,11 +58,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections public void UnbindEvents() { - if (Start != null) - { - Start.DefaultsApplied -= onDefaultsApplied; - Start.PositionBindable.ValueChanged -= onPositionChanged; - } + Start.DefaultsApplied -= onDefaultsApplied; + Start.PositionBindable.ValueChanged -= onPositionChanged; if (End != null) { @@ -92,7 +91,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections LifetimeStart = fadeInTime; LifetimeEnd = double.MaxValue; // This will be set by the connection. - Invalidated?.Invoke(); + Invalidated?.Invoke(this); } } } From 0098ac276090076b85faf01c8e934a358d9ee1f5 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 4 Jun 2021 16:53:27 +0900 Subject: [PATCH 734/839] Remove one TODO It can be removed because pooled drawables are always ready, and `FollowPointConnection` is also ready when applied. --- .../Objects/Drawables/Connections/FollowPointConnection.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs index 55f494d2f9..36a4d1abe3 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs @@ -83,13 +83,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections fp.FadeIn(end.TimeFadeIn); fp.ScaleTo(end.Scale, end.TimeFadeIn, Easing.Out); fp.MoveTo(pointEndPosition, end.TimeFadeIn, Easing.Out); - fp.Delay(fadeOutTime - fadeInTime).FadeOut(end.TimeFadeIn); + fp.Delay(fadeOutTime - fadeInTime).FadeOut(end.TimeFadeIn).Expire(); - finalTransformEndTime = fadeOutTime + end.TimeFadeIn; + finalTransformEndTime = fp.LifetimeEnd; } } - // todo: use Expire() on FollowPoints and take lifetime from them when https://github.com/ppy/osu-framework/issues/3300 is fixed. entry.LifetimeEnd = finalTransformEndTime; } From 8883d5e2d1839056234d402bfb9188d7d64eeb93 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 4 Jun 2021 15:21:44 +0700 Subject: [PATCH 735/839] use heading block to get title string --- osu.Game/Overlays/Wiki/WikiArticlePage.cs | 7 ++---- osu.Game/Overlays/Wiki/WikiSidebar.cs | 26 ++++++++++++++++++++--- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Wiki/WikiArticlePage.cs b/osu.Game/Overlays/Wiki/WikiArticlePage.cs index a60bebdc4b..83a61db88d 100644 --- a/osu.Game/Overlays/Wiki/WikiArticlePage.cs +++ b/osu.Game/Overlays/Wiki/WikiArticlePage.cs @@ -2,9 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Linq; using Markdig.Syntax; -using Markdig.Syntax.Inlines; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers.Markdown; @@ -62,14 +60,13 @@ namespace osu.Game.Overlays.Wiki private class ArticleMarkdownContainer : WikiMarkdownContainer { - public Action OnAddHeading; + public Action OnAddHeading; protected override MarkdownHeading CreateHeading(HeadingBlock headingBlock) { var heading = base.CreateHeading(headingBlock); - var title = ((LiteralInline)headingBlock.Inline.First(i => i is LiteralInline)).Content.ToString(); - OnAddHeading(title, heading, headingBlock.Level); + OnAddHeading(headingBlock, heading); return heading; } diff --git a/osu.Game/Overlays/Wiki/WikiSidebar.cs b/osu.Game/Overlays/Wiki/WikiSidebar.cs index d91076e40a..0db4d41b8b 100644 --- a/osu.Game/Overlays/Wiki/WikiSidebar.cs +++ b/osu.Game/Overlays/Wiki/WikiSidebar.cs @@ -2,6 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using Markdig.Syntax; +using Markdig.Syntax.Inlines; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -38,17 +40,35 @@ namespace osu.Game.Overlays.Wiki }, }; - public void AddToc(string title, MarkdownHeading heading, int level) + public void AddToc(HeadingBlock headingBlock, MarkdownHeading heading) { - switch (level) + switch (headingBlock.Level) { case 2: case 3: - tableOfContents.Add(new TocTitle(title, heading, level == 3)); + string title = getTitle(headingBlock.Inline); + tableOfContents.Add(new TocTitle(title, heading, headingBlock.Level == 3)); break; } } + private string getTitle(ContainerInline containerInline) + { + foreach (var inline in containerInline) + { + switch (inline) + { + case LiteralInline literalInline: + return literalInline.Content.ToString(); + + case LinkInline { IsImage: false } linkInline: + return getTitle(linkInline); + } + } + + return string.Empty; + } + private class TocTitle : OsuHoverContainer { [Resolved] From 3bf70dea60ed87d447cdbe64bc03cfc843bfc7b1 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 4 Jun 2021 15:51:23 +0700 Subject: [PATCH 736/839] fix test to using heading block --- .../Visual/Online/TestSceneWikiSidebar.cs | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiSidebar.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiSidebar.cs index fd7dbbe3ff..a04a3eef6b 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiSidebar.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiSidebar.cs @@ -3,9 +3,11 @@ using Markdig.Parsers; using Markdig.Syntax; +using Markdig.Syntax.Inlines; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics.Containers.Markdown; +using osu.Game.Graphics.Containers.Markdown; using osu.Game.Overlays; using osu.Game.Overlays.Wiki; @@ -21,8 +23,6 @@ namespace osu.Game.Tests.Visual.Online private WikiSidebar sidebar; - private readonly MarkdownHeading dummyHeading = new MarkdownHeading(new HeadingBlock(new HeadingBlockParser())); - [SetUp] public void SetUp() => Schedule(() => Child = sidebar = new WikiSidebar()); @@ -38,9 +38,7 @@ namespace osu.Game.Tests.Visual.Online AddStep("Add TOC", () => { for (var i = 0; i < 10; i++) - { - sidebar.AddToc($"This is a very long title {i + 1}", dummyHeading, 2); - } + addTitle($"This is a very long title {i + 1}"); }); } @@ -49,11 +47,23 @@ namespace osu.Game.Tests.Visual.Online { AddStep("Add TOC", () => { - for (var i = 0; i < 20; i++) - { - sidebar.AddToc($"This is a very long title {i + 1}", dummyHeading, i % 4 == 0 ? 2 : 3); - } + for (var i = 0; i < 10; i++) + addTitle($"This is a very long title {i + 1}", i % 4 != 0); }); } + + private void addTitle(string text, bool subtitle = false) + { + var headingBlock = createHeadingBlock(text, subtitle ? 3 : 2); + sidebar.AddToc(headingBlock, createHeading(headingBlock)); + } + + private HeadingBlock createHeadingBlock(string text, int level = 2) => new HeadingBlock(new HeadingBlockParser()) + { + Inline = new ContainerInline().AppendChild(new LiteralInline(text)), + Level = level, + }; + + private MarkdownHeading createHeading(HeadingBlock headingBlock) => new OsuMarkdownHeading(headingBlock); } } From a82eeb6daf15c2391a9c104ed2793c32958f20c7 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 4 Jun 2021 16:00:26 +0700 Subject: [PATCH 737/839] tidy up sidebar test --- .../Visual/Online/TestSceneWikiSidebar.cs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiSidebar.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiSidebar.cs index a04a3eef6b..bf96fc86d9 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiSidebar.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiSidebar.cs @@ -6,7 +6,6 @@ using Markdig.Syntax; using Markdig.Syntax.Inlines; using NUnit.Framework; using osu.Framework.Allocation; -using osu.Framework.Graphics.Containers.Markdown; using osu.Game.Graphics.Containers.Markdown; using osu.Game.Overlays; using osu.Game.Overlays.Wiki; @@ -54,16 +53,13 @@ namespace osu.Game.Tests.Visual.Online private void addTitle(string text, bool subtitle = false) { - var headingBlock = createHeadingBlock(text, subtitle ? 3 : 2); - sidebar.AddToc(headingBlock, createHeading(headingBlock)); + var headingBlock = new HeadingBlock(new HeadingBlockParser()) + { + Inline = new ContainerInline().AppendChild(new LiteralInline(text)), + Level = subtitle ? 3 : 2, + }; + var heading = new OsuMarkdownHeading(headingBlock); + sidebar.AddToc(headingBlock, heading); } - - private HeadingBlock createHeadingBlock(string text, int level = 2) => new HeadingBlock(new HeadingBlockParser()) - { - Inline = new ContainerInline().AppendChild(new LiteralInline(text)), - Level = level, - }; - - private MarkdownHeading createHeading(HeadingBlock headingBlock) => new OsuMarkdownHeading(headingBlock); } } From d7da66d876ec5a99b2effc438dabbba9dbd446bf Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 4 Jun 2021 18:41:02 +0900 Subject: [PATCH 738/839] Bring back scheduling of follow point update --- .../Connections/FollowPointConnection.cs | 15 +++++++++------ .../Connections/FollowPointLifetimeEntry.cs | 4 ++-- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs index 36a4d1abe3..001ea6c4ad 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Diagnostics; using osu.Framework.Graphics; using osu.Framework.Graphics.Pooling; using osu.Game.Rulesets.Objects; @@ -26,26 +25,30 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections { base.OnApply(entry); - entry.Invalidated += refreshPoints; - refreshPoints(entry); + entry.Invalidated += onEntryInvalidated; + refreshPoints(); } protected override void OnFree(FollowPointLifetimeEntry entry) { base.OnFree(entry); - entry.Invalidated -= refreshPoints; + entry.Invalidated -= onEntryInvalidated; // Return points to the pool. ClearInternal(false); } - private void refreshPoints(FollowPointLifetimeEntry entry) + private void onEntryInvalidated() => Scheduler.AddOnce(refreshPoints); + + private void refreshPoints() { ClearInternal(false); + var entry = Entry; + if (entry?.End == null) return; + OsuHitObject start = entry.Start; OsuHitObject end = entry.End; - Debug.Assert(end != null, $"{nameof(FollowPointLifetimeEntry)} without end hit object should never be alive"); double startTime = start.GetEndTime(); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointLifetimeEntry.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointLifetimeEntry.cs index a44d07d92c..82bca0a4e2 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointLifetimeEntry.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointLifetimeEntry.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections { public class FollowPointLifetimeEntry : LifetimeEntry { - public event Action? Invalidated; + public event Action? Invalidated; public readonly OsuHitObject Start; public FollowPointLifetimeEntry(OsuHitObject start) @@ -91,7 +91,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections LifetimeStart = fadeInTime; LifetimeEnd = double.MaxValue; // This will be set by the connection. - Invalidated?.Invoke(this); + Invalidated?.Invoke(); } } } From 181f1da3d3bb44c4c34da40c4f1b1b758707f815 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 4 Jun 2021 19:46:50 +0900 Subject: [PATCH 739/839] Maintain catch hit explosion by lifetime entries - Fix hit explosion not showing when a replay is rewound to a time after a hit object is caught --- .../TestSceneCatcher.cs | 2 +- osu.Game.Rulesets.Catch/UI/Catcher.cs | 18 ++------- osu.Game.Rulesets.Catch/UI/HitExplosion.cs | 39 ++++++++----------- .../UI/HitExplosionContainer.cs | 22 +++++++++++ .../UI/HitExplosionEntry.cs | 23 +++++++++++ 5 files changed, 66 insertions(+), 38 deletions(-) create mode 100644 osu.Game.Rulesets.Catch/UI/HitExplosionContainer.cs create mode 100644 osu.Game.Rulesets.Catch/UI/HitExplosionEntry.cs diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs index 517027a9fc..900691ecae 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs @@ -216,7 +216,7 @@ namespace osu.Game.Rulesets.Catch.Tests AddStep("enable hit lighting", () => config.SetValue(OsuSetting.HitLighting, true)); AddStep("catch fruit", () => attemptCatch(new Fruit())); AddAssert("correct hit lighting colour", () => - catcher.ChildrenOfType().First()?.ObjectColour == fruitColour); + catcher.ChildrenOfType().First()?.Entry?.ObjectColour == fruitColour); } [Test] diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 0d6a577d1e..75ed4c3c0f 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -126,8 +126,7 @@ namespace osu.Game.Rulesets.Catch.UI private float hyperDashTargetPosition; private Bindable hitLighting; - private readonly DrawablePool hitExplosionPool; - private readonly Container hitExplosionContainer; + private readonly HitExplosionContainer hitExplosionContainer; private readonly DrawablePool caughtFruitPool; private readonly DrawablePool caughtBananaPool; @@ -148,7 +147,6 @@ namespace osu.Game.Rulesets.Catch.UI InternalChildren = new Drawable[] { - hitExplosionPool = new DrawablePool(10), caughtFruitPool = new DrawablePool(50), caughtBananaPool = new DrawablePool(100), // less capacity is needed compared to fruit because droplet is not stacked @@ -173,7 +171,7 @@ namespace osu.Game.Rulesets.Catch.UI Anchor = Anchor.TopCentre, Alpha = 0, }, - hitExplosionContainer = new Container + hitExplosionContainer = new HitExplosionContainer { Anchor = Anchor.TopCentre, Origin = Anchor.BottomCentre, @@ -297,7 +295,6 @@ namespace osu.Game.Rulesets.Catch.UI caughtObjectContainer.RemoveAll(d => d.HitObject == drawableObject.HitObject); droppedObjectTarget.RemoveAll(d => d.HitObject == drawableObject.HitObject); - hitExplosionContainer.RemoveAll(d => d.HitObject == drawableObject.HitObject); } /// @@ -508,15 +505,8 @@ namespace osu.Game.Rulesets.Catch.UI return position; } - private void addLighting(CatchHitObject hitObject, float x, Color4 colour) - { - HitExplosion hitExplosion = hitExplosionPool.Get(); - hitExplosion.HitObject = hitObject; - hitExplosion.X = x; - hitExplosion.Scale = new Vector2(hitObject.Scale); - hitExplosion.ObjectColour = colour; - hitExplosionContainer.Add(hitExplosion); - } + private void addLighting(CatchHitObject hitObject, float x, Color4 colour) => + hitExplosionContainer.Add(new HitExplosionEntry(Time.Current, x, hitObject.Scale, colour)); private CaughtObject getCaughtObject(PalpableCatchHitObject source) { diff --git a/osu.Game.Rulesets.Catch/UI/HitExplosion.cs b/osu.Game.Rulesets.Catch/UI/HitExplosion.cs index 26627422e1..c1effa939e 100644 --- a/osu.Game.Rulesets.Catch/UI/HitExplosion.cs +++ b/osu.Game.Rulesets.Catch/UI/HitExplosion.cs @@ -5,31 +5,15 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; -using osu.Framework.Graphics.Pooling; using osu.Framework.Utils; -using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Objects.Pooling; using osuTK; using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.UI { - public class HitExplosion : PoolableDrawable + public class HitExplosion : PoolableDrawableWithLifetime { - private Color4 objectColour; - public CatchHitObject HitObject; - - public Color4 ObjectColour - { - get => objectColour; - set - { - if (objectColour == value) return; - - objectColour = value; - onColourChanged(); - } - } - private readonly CircularContainer largeFaint; private readonly CircularContainer smallFaint; private readonly CircularContainer directionalGlow1; @@ -83,9 +67,19 @@ namespace osu.Game.Rulesets.Catch.UI }; } - protected override void PrepareForUse() + protected override void OnApply(HitExplosionEntry entry) { - base.PrepareForUse(); + X = entry.Position; + Scale = new Vector2(entry.Scale); + setColour(entry.ObjectColour); + + using (BeginAbsoluteSequence(entry.LifetimeStart)) + applyTransforms(); + } + + private void applyTransforms() + { + ClearTransforms(true); const double duration = 400; @@ -99,11 +93,10 @@ namespace osu.Game.Rulesets.Catch.UI directionalGlow1.Rotation = RNG.NextSingle(-angle_variangle, angle_variangle); directionalGlow2.Rotation = RNG.NextSingle(-angle_variangle, angle_variangle); - this.FadeInFromZero(50).Then().FadeOut(duration, Easing.Out); - Expire(true); + this.FadeInFromZero(50).Then().FadeOut(duration, Easing.Out).Expire(); } - private void onColourChanged() + private void setColour(Color4 objectColour) { const float roundness = 100; diff --git a/osu.Game.Rulesets.Catch/UI/HitExplosionContainer.cs b/osu.Game.Rulesets.Catch/UI/HitExplosionContainer.cs new file mode 100644 index 0000000000..094d88243a --- /dev/null +++ b/osu.Game.Rulesets.Catch/UI/HitExplosionContainer.cs @@ -0,0 +1,22 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics.Pooling; +using osu.Game.Rulesets.Objects.Pooling; + +namespace osu.Game.Rulesets.Catch.UI +{ + public class HitExplosionContainer : PooledDrawableWithLifetimeContainer + { + protected override bool RemoveRewoundEntry => true; + + private readonly DrawablePool pool; + + public HitExplosionContainer() + { + AddInternal(pool = new DrawablePool(10)); + } + + protected override HitExplosion GetDrawable(HitExplosionEntry entry) => pool.Get(d => d.Apply(entry)); + } +} diff --git a/osu.Game.Rulesets.Catch/UI/HitExplosionEntry.cs b/osu.Game.Rulesets.Catch/UI/HitExplosionEntry.cs new file mode 100644 index 0000000000..d0dfdfcafd --- /dev/null +++ b/osu.Game.Rulesets.Catch/UI/HitExplosionEntry.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 osu.Framework.Graphics.Performance; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Catch.UI +{ + public class HitExplosionEntry : LifetimeEntry + { + public readonly float Position; + public readonly float Scale; + public readonly Color4 ObjectColour; + + public HitExplosionEntry(double startTime, float position, float scale, Color4 objectColour) + { + LifetimeStart = startTime; + Position = position; + Scale = scale; + ObjectColour = objectColour; + } + } +} From 5512231bf4be1108dd589cbda34caa6da4a1ef96 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 4 Jun 2021 19:52:12 +0900 Subject: [PATCH 740/839] Add `NextSingle` of version taking output range to `StatelessRNG` --- osu.Game/Utils/StatelessRNG.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Utils/StatelessRNG.cs b/osu.Game/Utils/StatelessRNG.cs index 118b08fe30..cd169229e3 100644 --- a/osu.Game/Utils/StatelessRNG.cs +++ b/osu.Game/Utils/StatelessRNG.cs @@ -75,5 +75,10 @@ namespace osu.Game.Utils /// public static float NextSingle(int seed, int series = 0) => (float)(NextULong(seed, series) & ((1 << 24) - 1)) / (1 << 24); // float has 24-bit precision + + /// + /// Compute a random floating point value between and from given seed and series number. + /// + public static float NextSingle(float min, float max, int seed, int series = 0) => min + NextSingle(seed, series) * (max - min); } } From 8e20f90ed5b836c79e4fd591f6d6a3c5d741d7cc Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 4 Jun 2021 19:54:46 +0900 Subject: [PATCH 741/839] Use seeded RNG for catch explosion animation The animation is always the same when a replay is rewound or a beatmap is played multiple times. --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 2 +- osu.Game.Rulesets.Catch/UI/HitExplosion.cs | 9 +++++---- osu.Game.Rulesets.Catch/UI/HitExplosionEntry.cs | 4 +++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 75ed4c3c0f..1c29e8b20c 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -506,7 +506,7 @@ namespace osu.Game.Rulesets.Catch.UI } private void addLighting(CatchHitObject hitObject, float x, Color4 colour) => - hitExplosionContainer.Add(new HitExplosionEntry(Time.Current, x, hitObject.Scale, colour)); + hitExplosionContainer.Add(new HitExplosionEntry(Time.Current, x, hitObject.Scale, colour, hitObject.RandomSeed)); private CaughtObject getCaughtObject(PalpableCatchHitObject source) { diff --git a/osu.Game.Rulesets.Catch/UI/HitExplosion.cs b/osu.Game.Rulesets.Catch/UI/HitExplosion.cs index c1effa939e..d9ab428231 100644 --- a/osu.Game.Rulesets.Catch/UI/HitExplosion.cs +++ b/osu.Game.Rulesets.Catch/UI/HitExplosion.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Utils; using osu.Game.Rulesets.Objects.Pooling; +using osu.Game.Utils; using osuTK; using osuTK.Graphics; @@ -74,10 +75,10 @@ namespace osu.Game.Rulesets.Catch.UI setColour(entry.ObjectColour); using (BeginAbsoluteSequence(entry.LifetimeStart)) - applyTransforms(); + applyTransforms(entry.RNGSeed); } - private void applyTransforms() + private void applyTransforms(int randomSeed) { ClearTransforms(true); @@ -90,8 +91,8 @@ namespace osu.Game.Rulesets.Catch.UI .FadeOut(duration * 2); const float angle_variangle = 15; // should be less than 45 - directionalGlow1.Rotation = RNG.NextSingle(-angle_variangle, angle_variangle); - directionalGlow2.Rotation = RNG.NextSingle(-angle_variangle, angle_variangle); + directionalGlow1.Rotation = StatelessRNG.NextSingle(-angle_variangle, angle_variangle, randomSeed, 4); + directionalGlow2.Rotation = StatelessRNG.NextSingle(-angle_variangle, angle_variangle, randomSeed, 5); this.FadeInFromZero(50).Then().FadeOut(duration, Easing.Out).Expire(); } diff --git a/osu.Game.Rulesets.Catch/UI/HitExplosionEntry.cs b/osu.Game.Rulesets.Catch/UI/HitExplosionEntry.cs index d0dfdfcafd..b142962a8a 100644 --- a/osu.Game.Rulesets.Catch/UI/HitExplosionEntry.cs +++ b/osu.Game.Rulesets.Catch/UI/HitExplosionEntry.cs @@ -11,13 +11,15 @@ namespace osu.Game.Rulesets.Catch.UI public readonly float Position; public readonly float Scale; public readonly Color4 ObjectColour; + public readonly int RNGSeed; - public HitExplosionEntry(double startTime, float position, float scale, Color4 objectColour) + public HitExplosionEntry(double startTime, float position, float scale, Color4 objectColour, int rngSeed) { LifetimeStart = startTime; Position = position; Scale = scale; ObjectColour = objectColour; + RNGSeed = rngSeed; } } } From 009aa994d09eca2508af2f3d4ec4fb73fae3a7f6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 4 Jun 2021 21:58:07 +0900 Subject: [PATCH 742/839] Fix potential race incorrectly pausing the source clock --- osu.Game/Screens/Play/MasterGameplayClockContainer.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs index fcbc6fae15..3fbb55872b 100644 --- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs +++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs @@ -100,7 +100,13 @@ namespace osu.Game.Screens.Play { // The source is stopped by a frequency fade first. if (isPaused.NewValue) - this.TransformBindableTo(pauseFreqAdjust, 0, 200, Easing.Out).OnComplete(_ => AdjustableSource.Stop()); + { + this.TransformBindableTo(pauseFreqAdjust, 0, 200, Easing.Out).OnComplete(_ => + { + if (IsPaused.Value == isPaused.NewValue) + AdjustableSource.Stop(); + }); + } else this.TransformBindableTo(pauseFreqAdjust, 1, 200, Easing.In); } From 9f2a9608f2acbb7c4169bdc6b0c2e9cc7c4aaa84 Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Fri, 4 Jun 2021 16:17:54 +0200 Subject: [PATCH 743/839] Rework slider positioning --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 87 ++++++++++++---------- 1 file changed, 47 insertions(+), 40 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 4dfadbb835..f8572cc28b 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -23,6 +23,8 @@ namespace osu.Game.Rulesets.Osu.Mods public override string Description => "It never gets boring!"; public override bool Ranked => false; + private const float slider_path_checking_rate = 10; + // The relative distance to the edge of the playfield before objects' positions should start to "turn around" and curve towards the middle. // The closer the hit objects draw to the border, the sharper the turn private const float playfield_edge_ratio = 0.375f; @@ -74,22 +76,8 @@ namespace osu.Game.Rulesets.Osu.Mods // update end position as it may have changed as a result of the position update. current.EndPositionRandomised = current.PositionRandomised; - switch (hitObject) - { - case Slider slider: - // Shift nested objects the same distance as the slider got shifted in the randomisation process - // so that moveSliderIntoPlayfield() can determine their relative distances to slider.Position and thus minMargin - shiftNestedObjects(slider, Vector2.Subtract(slider.Position, current.PositionOriginal)); - - var oldPos = new Vector2(slider.Position.X, slider.Position.Y); - - moveSliderIntoPlayfield(slider, current); - - // Shift them again to move them to their final position after the slider got moved into the playfield - shiftNestedObjects(slider, Vector2.Subtract(slider.Position, oldPos)); - - break; - } + if (hitObject is Slider slider) + moveSliderIntoPlayfield(slider, current); previous = current; } @@ -146,34 +134,53 @@ namespace osu.Game.Rulesets.Osu.Mods /// private void moveSliderIntoPlayfield(Slider slider, RandomObjectInfo currentObjectInfo) { - // Min. distances from the slider's position to the playfield border - var minMargin = new MarginPadding(); + var minMargin = getMinSliderMargin(slider); - foreach (var hitObject in slider.NestedHitObjects.Where(o => o is SliderTick || o is SliderEndCircle)) - { - if (!(hitObject is OsuHitObject osuHitObject)) - continue; - - var relativePos = Vector2.Subtract(osuHitObject.Position, slider.Position); - - minMargin.Left = Math.Max(minMargin.Left, -relativePos.X); - minMargin.Right = Math.Max(minMargin.Right, relativePos.X); - minMargin.Top = Math.Max(minMargin.Top, -relativePos.Y); - minMargin.Bottom = Math.Max(minMargin.Bottom, relativePos.Y); - } - - if (slider.Position.X < minMargin.Left) - slider.Position = new Vector2(minMargin.Left, slider.Position.Y); - else if (slider.Position.X + minMargin.Right > OsuPlayfield.BASE_SIZE.X) - slider.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - minMargin.Right, slider.Position.Y); - - if (slider.Position.Y < minMargin.Top) - slider.Position = new Vector2(slider.Position.X, minMargin.Top); - else if (slider.Position.Y + minMargin.Bottom > OsuPlayfield.BASE_SIZE.Y) - slider.Position = new Vector2(slider.Position.X, OsuPlayfield.BASE_SIZE.Y - minMargin.Bottom); + slider.Position = new Vector2( + Math.Clamp(slider.Position.X, minMargin.Left, OsuPlayfield.BASE_SIZE.X - minMargin.Right), + Math.Clamp(slider.Position.Y, minMargin.Top, OsuPlayfield.BASE_SIZE.Y - minMargin.Bottom) + ); currentObjectInfo.PositionRandomised = slider.Position; currentObjectInfo.EndPositionRandomised = slider.EndPosition; + + shiftNestedObjects(slider, Vector2.Subtract(currentObjectInfo.PositionRandomised, currentObjectInfo.PositionOriginal)); + } + + /// + /// Calculates the min. distances from the 's position to the playfield border for the slider to be fully inside of the playfield. + /// + private MarginPadding getMinSliderMargin(Slider slider) + { + var minMargin = new MarginPadding(); + Vector2 pos; + + for (double j = 0; j <= 1; j += 1 / (slider_path_checking_rate / 1000 * (slider.EndTime - slider.StartTime))) + { + pos = slider.Path.PositionAt(j); + updateMargin(); + } + + var repeat = (SliderRepeat)slider.NestedHitObjects.FirstOrDefault(o => o is SliderRepeat); + + if (repeat != null) + { + pos = repeat.Position - slider.Position; + updateMargin(); + } + + pos = slider.Path.PositionAt(1); + updateMargin(); + + return minMargin; + + void updateMargin() + { + minMargin.Left = Math.Max(minMargin.Left, -pos.X); + minMargin.Right = Math.Max(minMargin.Right, pos.X); + minMargin.Top = Math.Max(minMargin.Top, -pos.Y); + minMargin.Bottom = Math.Max(minMargin.Bottom, pos.Y); + } } /// From a0a6f3ef81021df70ccccbc792d6b2253b55e4f9 Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Fri, 4 Jun 2021 16:23:03 +0200 Subject: [PATCH 744/839] Replace `Vector2` methods with math operators --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index f8572cc28b..3525eddd2c 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Osu.Mods private static readonly float border_distance_x = OsuPlayfield.BASE_SIZE.X * playfield_edge_ratio; private static readonly float border_distance_y = OsuPlayfield.BASE_SIZE.Y * playfield_edge_ratio; - private static readonly Vector2 playfield_middle = Vector2.Divide(OsuPlayfield.BASE_SIZE, 2); + private static readonly Vector2 playfield_middle = OsuPlayfield.BASE_SIZE / 2; private static readonly float playfield_diagonal = OsuPlayfield.BASE_SIZE.LengthFast; @@ -119,7 +119,7 @@ namespace osu.Game.Rulesets.Osu.Mods current.AngleRad = (float)Math.Atan2(posRelativeToPrev.Y, posRelativeToPrev.X); - var position = Vector2.Add(previous.EndPositionRandomised, posRelativeToPrev); + var position = previous.EndPositionRandomised + posRelativeToPrev; // Move hit objects back into the playfield if they are outside of it, // which would sometimes happen during big jumps otherwise. @@ -144,7 +144,7 @@ namespace osu.Game.Rulesets.Osu.Mods currentObjectInfo.PositionRandomised = slider.Position; currentObjectInfo.EndPositionRandomised = slider.EndPosition; - shiftNestedObjects(slider, Vector2.Subtract(currentObjectInfo.PositionRandomised, currentObjectInfo.PositionOriginal)); + shiftNestedObjects(slider, currentObjectInfo.PositionRandomised - currentObjectInfo.PositionOriginal); } /// @@ -195,7 +195,7 @@ namespace osu.Game.Rulesets.Osu.Mods if (!(hitObject is OsuHitObject osuHitObject)) continue; - osuHitObject.Position = Vector2.Add(osuHitObject.Position, shift); + osuHitObject.Position += shift; } } From 6357d1363c36b2ece628648ea98d93c9b232ec13 Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Fri, 4 Jun 2021 16:26:40 +0200 Subject: [PATCH 745/839] Add comment for `slider_path_checking_rate` --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 3525eddd2c..e5c48ca96e 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -23,6 +23,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override string Description => "It never gets boring!"; public override bool Ranked => false; + // How often per second getMinSliderMargin() checks if the slider is outside of the playfield private const float slider_path_checking_rate = 10; // The relative distance to the edge of the playfield before objects' positions should start to "turn around" and curve towards the middle. From 32e41048ff0a482263f30a177ada14a8fe8925ae Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Fri, 4 Jun 2021 16:50:27 +0200 Subject: [PATCH 746/839] Fix `System.ArgumentException` caused by sliders bigger than the playfield --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index e5c48ca96e..6181b2257e 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -173,6 +173,9 @@ namespace osu.Game.Rulesets.Osu.Mods pos = slider.Path.PositionAt(1); updateMargin(); + minMargin.Left = Math.Min(minMargin.Left, OsuPlayfield.BASE_SIZE.X - minMargin.Right); + minMargin.Top = Math.Min(minMargin.Top, OsuPlayfield.BASE_SIZE.Y - minMargin.Bottom); + return minMargin; void updateMargin() From b4f190c6ff51a4e1b0fbd3d2e44606d24bb7a847 Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Fri, 4 Jun 2021 17:22:36 +0200 Subject: [PATCH 747/839] Rename iteration variable --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 6181b2257e..79f821a8ef 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -156,9 +156,9 @@ namespace osu.Game.Rulesets.Osu.Mods var minMargin = new MarginPadding(); Vector2 pos; - for (double j = 0; j <= 1; j += 1 / (slider_path_checking_rate / 1000 * (slider.EndTime - slider.StartTime))) + for (double i = 0; i <= 1; i += 1 / (slider_path_checking_rate / 1000 * (slider.EndTime - slider.StartTime))) { - pos = slider.Path.PositionAt(j); + pos = slider.Path.PositionAt(i); updateMargin(); } From 70c64af25e001ca8d097c2f12d4626e21bb39fdb Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 4 Jun 2021 23:31:51 +0700 Subject: [PATCH 748/839] rename toc entry --- osu.Game/Overlays/Wiki/WikiSidebar.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Wiki/WikiSidebar.cs b/osu.Game/Overlays/Wiki/WikiSidebar.cs index 0db4d41b8b..63a1ce3ffb 100644 --- a/osu.Game/Overlays/Wiki/WikiSidebar.cs +++ b/osu.Game/Overlays/Wiki/WikiSidebar.cs @@ -47,7 +47,7 @@ namespace osu.Game.Overlays.Wiki case 2: case 3: string title = getTitle(headingBlock.Inline); - tableOfContents.Add(new TocTitle(title, heading, headingBlock.Level == 3)); + tableOfContents.Add(new TableOfContentsEntry(title, heading, headingBlock.Level == 3)); break; } } @@ -69,7 +69,7 @@ namespace osu.Game.Overlays.Wiki return string.Empty; } - private class TocTitle : OsuHoverContainer + private class TableOfContentsEntry : OsuHoverContainer { [Resolved] private OverlayScrollContainer scrollContainer { get; set; } @@ -78,7 +78,7 @@ namespace osu.Game.Overlays.Wiki private readonly OsuTextFlowContainer textFlow; - public TocTitle(string text, MarkdownHeading target, bool subtitle = false) + public TableOfContentsEntry(string text, MarkdownHeading target, bool subtitle = false) { this.target = target; From 5febbe453086d874e0b893a8bca850867981492b Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 4 Jun 2021 23:32:42 +0700 Subject: [PATCH 749/839] rename method add entry --- osu.Game.Tests/Visual/Online/TestSceneWikiSidebar.cs | 2 +- osu.Game/Overlays/Wiki/WikiArticlePage.cs | 2 +- osu.Game/Overlays/Wiki/WikiSidebar.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiSidebar.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiSidebar.cs index bf96fc86d9..b4f1997bb0 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiSidebar.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiSidebar.cs @@ -59,7 +59,7 @@ namespace osu.Game.Tests.Visual.Online Level = subtitle ? 3 : 2, }; var heading = new OsuMarkdownHeading(headingBlock); - sidebar.AddToc(headingBlock, heading); + sidebar.AddEntry(headingBlock, heading); } } } diff --git a/osu.Game/Overlays/Wiki/WikiArticlePage.cs b/osu.Game/Overlays/Wiki/WikiArticlePage.cs index 83a61db88d..60982b0aa9 100644 --- a/osu.Game/Overlays/Wiki/WikiArticlePage.cs +++ b/osu.Game/Overlays/Wiki/WikiArticlePage.cs @@ -52,7 +52,7 @@ namespace osu.Game.Overlays.Wiki Left = 30, Right = 50, }, - OnAddHeading = sidebar.AddToc, + OnAddHeading = sidebar.AddEntry, } }, }; diff --git a/osu.Game/Overlays/Wiki/WikiSidebar.cs b/osu.Game/Overlays/Wiki/WikiSidebar.cs index 63a1ce3ffb..d4c484b0fd 100644 --- a/osu.Game/Overlays/Wiki/WikiSidebar.cs +++ b/osu.Game/Overlays/Wiki/WikiSidebar.cs @@ -40,7 +40,7 @@ namespace osu.Game.Overlays.Wiki }, }; - public void AddToc(HeadingBlock headingBlock, MarkdownHeading heading) + public void AddEntry(HeadingBlock headingBlock, MarkdownHeading heading) { switch (headingBlock.Level) { From a431ef6c4802c298c2b40bda1f4d658da7db1778 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 4 Jun 2021 23:43:00 +0700 Subject: [PATCH 750/839] keep colour change when entry is clicked --- osu.Game/Overlays/Wiki/WikiSidebar.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Wiki/WikiSidebar.cs b/osu.Game/Overlays/Wiki/WikiSidebar.cs index d4c484b0fd..f2cb2c3100 100644 --- a/osu.Game/Overlays/Wiki/WikiSidebar.cs +++ b/osu.Game/Overlays/Wiki/WikiSidebar.cs @@ -74,6 +74,9 @@ namespace osu.Game.Overlays.Wiki [Resolved] private OverlayScrollContainer scrollContainer { get; set; } + [Resolved] + private OverlayColourProvider colourProvider { get; set; } + private readonly MarkdownHeading target; private readonly OsuTextFlowContainer textFlow; @@ -100,7 +103,7 @@ namespace osu.Game.Overlays.Wiki protected override IEnumerable EffectTargets => new Drawable[] { textFlow }; [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) + private void load() { IdleColour = colourProvider.Light2; HoverColour = colourProvider.Light1; @@ -108,6 +111,7 @@ namespace osu.Game.Overlays.Wiki protected override bool OnClick(ClickEvent e) { + IdleColour = colourProvider.Light1; scrollContainer.ScrollTo(target); return base.OnClick(e); } From f07d4532d94f0f22f79408afdb517c3622c4d37e Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 4 Jun 2021 23:48:27 +0700 Subject: [PATCH 751/839] move scroll to into action --- osu.Game/Overlays/Wiki/WikiSidebar.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Wiki/WikiSidebar.cs b/osu.Game/Overlays/Wiki/WikiSidebar.cs index f2cb2c3100..3de2c8addf 100644 --- a/osu.Game/Overlays/Wiki/WikiSidebar.cs +++ b/osu.Game/Overlays/Wiki/WikiSidebar.cs @@ -71,9 +71,6 @@ namespace osu.Game.Overlays.Wiki private class TableOfContentsEntry : OsuHoverContainer { - [Resolved] - private OverlayScrollContainer scrollContainer { get; set; } - [Resolved] private OverlayColourProvider colourProvider { get; set; } @@ -103,16 +100,16 @@ namespace osu.Game.Overlays.Wiki protected override IEnumerable EffectTargets => new Drawable[] { textFlow }; [BackgroundDependencyLoader] - private void load() + private void load(OverlayScrollContainer scrollContainer) { IdleColour = colourProvider.Light2; HoverColour = colourProvider.Light1; + Action = () => scrollContainer.ScrollTo(target); } protected override bool OnClick(ClickEvent e) { IdleColour = colourProvider.Light1; - scrollContainer.ScrollTo(target); return base.OnClick(e); } } From 5ee77925e4ab345b93ccffde20a8e792d9fa4885 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 4 Jun 2021 23:54:50 +0700 Subject: [PATCH 752/839] change WikiArticlePage to extends CompositeDrawable --- osu.Game/Overlays/Wiki/WikiArticlePage.cs | 63 ++++++++++++----------- 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/osu.Game/Overlays/Wiki/WikiArticlePage.cs b/osu.Game/Overlays/Wiki/WikiArticlePage.cs index 60982b0aa9..0061bff8ea 100644 --- a/osu.Game/Overlays/Wiki/WikiArticlePage.cs +++ b/osu.Game/Overlays/Wiki/WikiArticlePage.cs @@ -10,7 +10,7 @@ using osu.Game.Overlays.Wiki.Markdown; namespace osu.Game.Overlays.Wiki { - public class WikiArticlePage : GridContainer + public class WikiArticlePage : CompositeDrawable { public Container SidebarContainer { get; } @@ -18,42 +18,47 @@ namespace osu.Game.Overlays.Wiki { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize), - }; - ColumnDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize), - new Dimension(), - }; WikiSidebar sidebar; - Content = new[] + InternalChild = new GridContainer { - new Drawable[] + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + RowDimensions = new[] { - SidebarContainer = new Container + new Dimension(GridSizeMode.AutoSize), + }, + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + new Dimension(), + }, + Content = new[] + { + new Drawable[] { - AutoSizeAxes = Axes.X, - Child = sidebar = new WikiSidebar(), - }, - new ArticleMarkdownContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - CurrentPath = currentPath, - Text = markdown, - DocumentMargin = new MarginPadding(0), - DocumentPadding = new MarginPadding + SidebarContainer = new Container { - Vertical = 20, - Left = 30, - Right = 50, + AutoSizeAxes = Axes.X, + Child = sidebar = new WikiSidebar(), }, - OnAddHeading = sidebar.AddEntry, - } + new ArticleMarkdownContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + CurrentPath = currentPath, + Text = markdown, + DocumentMargin = new MarginPadding(0), + DocumentPadding = new MarginPadding + { + Vertical = 20, + Left = 30, + Right = 50, + }, + OnAddHeading = sidebar.AddEntry, + } + }, }, }; } From 4cf3381d0bcbe0d195890a16406fe6eaf5b21a72 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 4 Jun 2021 23:59:11 +0700 Subject: [PATCH 753/839] use wiki article page when failed fetch --- osu.Game/Overlays/WikiOverlay.cs | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs index 5beb5d50e6..fc24820f3c 100644 --- a/osu.Game/Overlays/WikiOverlay.cs +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -11,7 +11,6 @@ using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.Wiki; -using osu.Game.Overlays.Wiki.Markdown; namespace osu.Game.Overlays { @@ -139,20 +138,8 @@ namespace osu.Game.Overlays private void onFail() { - LoadDisplay(new WikiMarkdownContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - CurrentPath = $@"{api.WebsiteRootUrl}/wiki/", - Text = $"Something went wrong when trying to fetch page \"{path.Value}\".\n\n[Return to the main page](Main_Page).", - DocumentMargin = new MarginPadding(0), - DocumentPadding = new MarginPadding - { - Vertical = 20, - Left = 30, - Right = 50, - }, - }); + LoadDisplay(articlePage = new WikiArticlePage($@"{api.WebsiteRootUrl}/wiki/", + $"Something went wrong when trying to fetch page \"{path.Value}\".\n\n[Return to the main page](Main_Page).")); } private void showParentPage() From 1084906d405ed33ad71eaac08dc94ac44556a33d Mon Sep 17 00:00:00 2001 From: Craftplacer Date: Sat, 5 Jun 2021 03:30:21 +0200 Subject: [PATCH 754/839] Add DummyAPIAccess one parent up --- osu.Game/Tests/Visual/OsuTestScene.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index 198d22fedd..b8a266fc98 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -118,7 +118,7 @@ namespace osu.Game.Tests.Visual { dummyAPI = new DummyAPIAccess(); Dependencies.CacheAs(dummyAPI); - Add(dummyAPI); + base.Content.Add(dummyAPI); } return Dependencies; From c099751ad189f119dee015720753cf911debb623 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sat, 5 Jun 2021 18:26:03 +0700 Subject: [PATCH 755/839] use plain if check in switch case --- osu.Game/Overlays/Wiki/WikiSidebar.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Wiki/WikiSidebar.cs b/osu.Game/Overlays/Wiki/WikiSidebar.cs index 3de2c8addf..c95ee3a8c3 100644 --- a/osu.Game/Overlays/Wiki/WikiSidebar.cs +++ b/osu.Game/Overlays/Wiki/WikiSidebar.cs @@ -61,8 +61,11 @@ namespace osu.Game.Overlays.Wiki case LiteralInline literalInline: return literalInline.Content.ToString(); - case LinkInline { IsImage: false } linkInline: - return getTitle(linkInline); + case LinkInline linkInline: + if (!linkInline.IsImage) + return getTitle(linkInline); + + break; } } From 958bddc8cb9d4443d729a6a54a0fb2bc5873e249 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sat, 5 Jun 2021 18:30:28 +0700 Subject: [PATCH 756/839] remove onclick in toc entry --- osu.Game/Overlays/Wiki/WikiSidebar.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/osu.Game/Overlays/Wiki/WikiSidebar.cs b/osu.Game/Overlays/Wiki/WikiSidebar.cs index c95ee3a8c3..b4e97e4a7b 100644 --- a/osu.Game/Overlays/Wiki/WikiSidebar.cs +++ b/osu.Game/Overlays/Wiki/WikiSidebar.cs @@ -8,7 +8,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers.Markdown; -using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -109,12 +108,6 @@ namespace osu.Game.Overlays.Wiki HoverColour = colourProvider.Light1; Action = () => scrollContainer.ScrollTo(target); } - - protected override bool OnClick(ClickEvent e) - { - IdleColour = colourProvider.Light1; - return base.OnClick(e); - } } } } From 55f3a328a4bb7cb2e460261e242734bb44ad5eaa Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sat, 5 Jun 2021 19:46:46 +0700 Subject: [PATCH 757/839] add WikiTableOfContents --- osu.Game/Overlays/Wiki/WikiTableOfContents.cs | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 osu.Game/Overlays/Wiki/WikiTableOfContents.cs diff --git a/osu.Game/Overlays/Wiki/WikiTableOfContents.cs b/osu.Game/Overlays/Wiki/WikiTableOfContents.cs new file mode 100644 index 0000000000..857ec27020 --- /dev/null +++ b/osu.Game/Overlays/Wiki/WikiTableOfContents.cs @@ -0,0 +1,108 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Containers.Markdown; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; + +namespace osu.Game.Overlays.Wiki +{ + public class WikiTableOfContents : CompositeDrawable + { + private readonly FillFlowContainer content; + + private FillFlowContainer lastItem; + + private FillFlowContainer lastSubsection; + + public WikiTableOfContents() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + InternalChild = content = new FillFlowContainer + { + Direction = FillDirection.Vertical, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + }; + } + + public void AddEntry(string title, MarkdownHeading target, bool subtitle = false) + { + var entry = new TableOfContentsEntry(title, target, subtitle); + + if (subtitle) + { + lastSubsection ??= new FillFlowContainer + { + Direction = FillDirection.Vertical, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Left = 10 }, + }; + + lastSubsection.Add(entry); + + return; + } + + if (lastSubsection != null) + { + lastItem.Add(lastSubsection); + lastItem.Margin = new MarginPadding { Bottom = 10 }; + lastSubsection = null; + } + + content.Add(lastItem = new FillFlowContainer + { + Direction = FillDirection.Vertical, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Margin = new MarginPadding { Bottom = 5 }, + Child = entry, + }); + } + + private class TableOfContentsEntry : OsuHoverContainer + { + [Resolved] + private OverlayColourProvider colourProvider { get; set; } + + private readonly MarkdownHeading target; + + private readonly OsuTextFlowContainer textFlow; + + public TableOfContentsEntry(string text, MarkdownHeading target, bool subtitle = false) + { + this.target = target; + + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Child = textFlow = new OsuTextFlowContainer(t => + { + t.Font = OsuFont.GetFont(size: subtitle ? 12 : 15); + }).With(f => + { + f.AddText(text); + f.RelativeSizeAxes = Axes.X; + f.AutoSizeAxes = Axes.Y; + }); + Margin = new MarginPadding { Bottom = 2 }; + } + + protected override IEnumerable EffectTargets => new Drawable[] { textFlow }; + + [BackgroundDependencyLoader] + private void load(OverlayScrollContainer scrollContainer) + { + IdleColour = colourProvider.Light2; + HoverColour = colourProvider.Light1; + Action = () => scrollContainer.ScrollTo(target); + } + } + } +} From 9f45a2862358e98b9ba819dea5faec24dcd431b2 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sat, 5 Jun 2021 19:47:00 +0700 Subject: [PATCH 758/839] use WikiTableOfContents in WikiSidebar --- osu.Game/Overlays/Wiki/WikiSidebar.cs | 55 ++------------------------- 1 file changed, 4 insertions(+), 51 deletions(-) diff --git a/osu.Game/Overlays/Wiki/WikiSidebar.cs b/osu.Game/Overlays/Wiki/WikiSidebar.cs index b4e97e4a7b..ee4e195f3f 100644 --- a/osu.Game/Overlays/Wiki/WikiSidebar.cs +++ b/osu.Game/Overlays/Wiki/WikiSidebar.cs @@ -1,22 +1,19 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Collections.Generic; using Markdig.Syntax; using Markdig.Syntax.Inlines; -using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers.Markdown; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; namespace osu.Game.Overlays.Wiki { public class WikiSidebar : OverlaySidebar { - private FillFlowContainer tableOfContents; + private WikiTableOfContents tableOfContents; protected override Drawable CreateContent() => new FillFlowContainer { @@ -29,13 +26,9 @@ namespace osu.Game.Overlays.Wiki { Text = "CONTENTS", Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold), + Margin = new MarginPadding { Bottom = 5 }, }, - tableOfContents = new FillFlowContainer - { - Direction = FillDirection.Vertical, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - } + tableOfContents = new WikiTableOfContents(), }, }; @@ -45,8 +38,7 @@ namespace osu.Game.Overlays.Wiki { case 2: case 3: - string title = getTitle(headingBlock.Inline); - tableOfContents.Add(new TableOfContentsEntry(title, heading, headingBlock.Level == 3)); + tableOfContents.AddEntry(getTitle(headingBlock.Inline), heading, headingBlock.Level == 3); break; } } @@ -70,44 +62,5 @@ namespace osu.Game.Overlays.Wiki return string.Empty; } - - private class TableOfContentsEntry : OsuHoverContainer - { - [Resolved] - private OverlayColourProvider colourProvider { get; set; } - - private readonly MarkdownHeading target; - - private readonly OsuTextFlowContainer textFlow; - - public TableOfContentsEntry(string text, MarkdownHeading target, bool subtitle = false) - { - this.target = target; - - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - Child = textFlow = new OsuTextFlowContainer(t => - { - t.Font = OsuFont.GetFont(size: subtitle ? 12 : 15); - }).With(f => - { - f.AddText(text); - f.RelativeSizeAxes = Axes.X; - f.AutoSizeAxes = Axes.Y; - }); - Margin = new MarginPadding { Top = subtitle ? 5 : 10 }; - Padding = new MarginPadding { Left = subtitle ? 10 : 0 }; - } - - protected override IEnumerable EffectTargets => new Drawable[] { textFlow }; - - [BackgroundDependencyLoader] - private void load(OverlayScrollContainer scrollContainer) - { - IdleColour = colourProvider.Light2; - HoverColour = colourProvider.Light1; - Action = () => scrollContainer.ScrollTo(target); - } - } } } From f59263932a1a74984658bd61df832b1724190c6d Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Sat, 5 Jun 2021 17:04:58 +0200 Subject: [PATCH 759/839] Use `SliderPath.GetPathToProgress` for getting the `SliderPath`'s positions --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 25 +++++++--------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 79f821a8ef..2d61c64fcb 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Linq; using osu.Framework.Graphics; using osu.Framework.Utils; @@ -23,9 +24,6 @@ namespace osu.Game.Rulesets.Osu.Mods public override string Description => "It never gets boring!"; public override bool Ranked => false; - // How often per second getMinSliderMargin() checks if the slider is outside of the playfield - private const float slider_path_checking_rate = 10; - // The relative distance to the edge of the playfield before objects' positions should start to "turn around" and curve towards the middle. // The closer the hit objects draw to the border, the sharper the turn private const float playfield_edge_ratio = 0.375f; @@ -154,31 +152,24 @@ namespace osu.Game.Rulesets.Osu.Mods private MarginPadding getMinSliderMargin(Slider slider) { var minMargin = new MarginPadding(); - Vector2 pos; - for (double i = 0; i <= 1; i += 1 / (slider_path_checking_rate / 1000 * (slider.EndTime - slider.StartTime))) - { - pos = slider.Path.PositionAt(i); - updateMargin(); - } + var pathPositions = new List(); + slider.Path.GetPathToProgress(pathPositions, 0, 1); + + foreach (var pos in pathPositions) + updateMargin(pos); var repeat = (SliderRepeat)slider.NestedHitObjects.FirstOrDefault(o => o is SliderRepeat); if (repeat != null) - { - pos = repeat.Position - slider.Position; - updateMargin(); - } - - pos = slider.Path.PositionAt(1); - updateMargin(); + updateMargin(repeat.Position - slider.Position); minMargin.Left = Math.Min(minMargin.Left, OsuPlayfield.BASE_SIZE.X - minMargin.Right); minMargin.Top = Math.Min(minMargin.Top, OsuPlayfield.BASE_SIZE.Y - minMargin.Bottom); return minMargin; - void updateMargin() + void updateMargin(Vector2 pos) { minMargin.Left = Math.Max(minMargin.Left, -pos.X); minMargin.Right = Math.Max(minMargin.Right, pos.X); From b214f2ae0e39a08e6763c8943d9e63991b996568 Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Sat, 5 Jun 2021 17:13:08 +0200 Subject: [PATCH 760/839] Remove `repeat` and simplify `getMinSliderMargin` --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 2d61c64fcb..c282a919ea 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -151,31 +151,23 @@ namespace osu.Game.Rulesets.Osu.Mods /// private MarginPadding getMinSliderMargin(Slider slider) { - var minMargin = new MarginPadding(); - var pathPositions = new List(); slider.Path.GetPathToProgress(pathPositions, 0, 1); + var minMargin = new MarginPadding(); + foreach (var pos in pathPositions) - updateMargin(pos); - - var repeat = (SliderRepeat)slider.NestedHitObjects.FirstOrDefault(o => o is SliderRepeat); - - if (repeat != null) - updateMargin(repeat.Position - slider.Position); - - minMargin.Left = Math.Min(minMargin.Left, OsuPlayfield.BASE_SIZE.X - minMargin.Right); - minMargin.Top = Math.Min(minMargin.Top, OsuPlayfield.BASE_SIZE.Y - minMargin.Bottom); - - return minMargin; - - void updateMargin(Vector2 pos) { minMargin.Left = Math.Max(minMargin.Left, -pos.X); minMargin.Right = Math.Max(minMargin.Right, pos.X); minMargin.Top = Math.Max(minMargin.Top, -pos.Y); minMargin.Bottom = Math.Max(minMargin.Bottom, pos.Y); } + + minMargin.Left = Math.Min(minMargin.Left, OsuPlayfield.BASE_SIZE.X - minMargin.Right); + minMargin.Top = Math.Min(minMargin.Top, OsuPlayfield.BASE_SIZE.Y - minMargin.Bottom); + + return minMargin; } /// From 525c16419a5587a19a6173034e5d30e15ad2143b Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sun, 6 Jun 2021 08:37:03 +0700 Subject: [PATCH 761/839] use container for main title and sub title table of contents --- osu.Game/Overlays/Wiki/WikiTableOfContents.cs | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/osu.Game/Overlays/Wiki/WikiTableOfContents.cs b/osu.Game/Overlays/Wiki/WikiTableOfContents.cs index 857ec27020..6c9f7d1536 100644 --- a/osu.Game/Overlays/Wiki/WikiTableOfContents.cs +++ b/osu.Game/Overlays/Wiki/WikiTableOfContents.cs @@ -15,9 +15,9 @@ namespace osu.Game.Overlays.Wiki { private readonly FillFlowContainer content; - private FillFlowContainer lastItem; + private Container lastMainTitle; - private FillFlowContainer lastSubsection; + private Container lastSubTitle; public WikiTableOfContents() { @@ -37,29 +37,26 @@ namespace osu.Game.Overlays.Wiki if (subtitle) { - lastSubsection ??= new FillFlowContainer + lastMainTitle.Margin = new MarginPadding(0); + + if (lastSubTitle != null) + lastSubTitle.Margin = new MarginPadding(0); + + content.Add(lastSubTitle = new Container { - Direction = FillDirection.Vertical, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Left = 10 }, - }; - - lastSubsection.Add(entry); + Margin = new MarginPadding { Bottom = 10 }, + Child = entry, + }); return; } - if (lastSubsection != null) - { - lastItem.Add(lastSubsection); - lastItem.Margin = new MarginPadding { Bottom = 10 }; - lastSubsection = null; - } + lastSubTitle = null; - content.Add(lastItem = new FillFlowContainer + content.Add(lastMainTitle = new Container { - Direction = FillDirection.Vertical, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Margin = new MarginPadding { Bottom = 5 }, @@ -92,6 +89,7 @@ namespace osu.Game.Overlays.Wiki f.AutoSizeAxes = Axes.Y; }); Margin = new MarginPadding { Bottom = 2 }; + Padding = new MarginPadding { Left = subtitle ? 10 : 0 }; } protected override IEnumerable EffectTargets => new Drawable[] { textFlow }; From 39f99bf785676c443f37248668eb9bf45006032e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 6 Jun 2021 11:08:54 +0900 Subject: [PATCH 762/839] Move `FindProvider` to `ISkinSource` --- osu.Game/Skinning/ISkin.cs | 9 ------- osu.Game/Skinning/ISkinSource.cs | 9 +++++++ osu.Game/Skinning/LegacySkin.cs | 27 +++++-------------- osu.Game/Skinning/Skin.cs | 2 -- osu.Game/Skinning/SkinManager.cs | 2 +- .../Beatmaps/LegacyBeatmapSkinColourTest.cs | 2 ++ 6 files changed, 18 insertions(+), 33 deletions(-) diff --git a/osu.Game/Skinning/ISkin.cs b/osu.Game/Skinning/ISkin.cs index 09e79a5ff5..73f7cf6d39 100644 --- a/osu.Game/Skinning/ISkin.cs +++ b/osu.Game/Skinning/ISkin.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 JetBrains.Annotations; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; @@ -58,13 +57,5 @@ namespace osu.Game.Skinning /// A matching value boxed in an , or null if unavailable. [CanBeNull] IBindable GetConfig(TLookup lookup); - - /// - /// Find the first (if any) skin that can fulfill the lookup. - /// This should be used for cases where subsequent lookups (for related components) need to occur on the same skin. - /// - /// The skin to be used for subsequent lookups, or null if none is available. - [CanBeNull] - ISkin FindProvider(Func lookupFunction); } } diff --git a/osu.Game/Skinning/ISkinSource.cs b/osu.Game/Skinning/ISkinSource.cs index 337d2a87a4..c7ebe91d64 100644 --- a/osu.Game/Skinning/ISkinSource.cs +++ b/osu.Game/Skinning/ISkinSource.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using JetBrains.Annotations; namespace osu.Game.Skinning { @@ -11,5 +12,13 @@ namespace osu.Game.Skinning public interface ISkinSource : ISkin { event Action SourceChanged; + + /// + /// Find the first (if any) skin that can fulfill the lookup. + /// This should be used for cases where subsequent lookups (for related components) need to occur on the same skin. + /// + /// The skin to be used for subsequent lookups, or null if none is available. + [CanBeNull] + ISkin FindProvider(Func lookupFunction); } } diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index d2d7cc4d86..8f1895883d 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -54,9 +54,6 @@ namespace osu.Game.Skinning private readonly Dictionary maniaConfigurations = new Dictionary(); - [CanBeNull] - private readonly DefaultLegacySkin legacyDefaultFallback; - [UsedImplicitly(ImplicitUseKindFlags.InstantiatedWithFixedConstructorSignature)] public LegacySkin(SkinInfo skin, IStorageResourceProvider resources) : this(skin, new LegacySkinResourceStore(skin, resources.Files), resources, "skin.ini") @@ -73,9 +70,6 @@ namespace osu.Game.Skinning protected LegacySkin(SkinInfo skin, [CanBeNull] IResourceStore storage, [CanBeNull] IStorageResourceProvider resources, string configurationFilename) : base(skin, resources) { - if (resources != null) - legacyDefaultFallback = CreateFallbackSkin(storage, resources); - using (var stream = storage?.GetStream(configurationFilename)) { if (stream != null) @@ -158,7 +152,7 @@ namespace osu.Game.Skinning return genericLookup(lookup); } - return legacyDefaultFallback?.GetConfig(lookup); + return null; } private IBindable lookupForMania(LegacyManiaSkinConfigurationLookup maniaLookup) @@ -335,7 +329,7 @@ namespace osu.Game.Skinning { } - return legacyDefaultFallback?.GetConfig(lookup); + return null; } public override Drawable GetDrawableComponent(ISkinComponent component) @@ -406,6 +400,7 @@ namespace osu.Game.Skinning return null; case GameplaySkinComponent resultComponent: + // TODO: this should be inside the judgement pieces. Func createDrawable = () => getJudgementAnimation(resultComponent.Component); // kind of wasteful that we throw this away, but should do for now. @@ -427,7 +422,7 @@ namespace osu.Game.Skinning if (animation != null) return animation; - return legacyDefaultFallback?.GetDrawableComponent(component); + return null; } private Texture getParticleTexture(HitResult result) @@ -487,7 +482,7 @@ namespace osu.Game.Skinning return texture; } - return legacyDefaultFallback?.GetTexture(componentName, wrapModeS, wrapModeT); + return null; } public override ISample GetSample(ISampleInfo sampleInfo) @@ -511,17 +506,7 @@ namespace osu.Game.Skinning } } - return legacyDefaultFallback?.GetSample(sampleInfo); - } - - public override ISkin FindProvider(Func lookupFunction) - { - var source = base.FindProvider(lookupFunction); - - if (source != null) - return source; - - return legacyDefaultFallback?.FindProvider(lookupFunction); + return null; } private IEnumerable getLegacyLookupNames(HitSampleInfo hitSample) diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index c12e9a64c2..b6cb8fc7a4 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -35,8 +35,6 @@ namespace osu.Game.Skinning public abstract IBindable GetConfig(TLookup lookup); - public virtual ISkin FindProvider(Func lookupFunction) => lookupFunction(this) ? this : null; - protected Skin(SkinInfo skin, IStorageResourceProvider resources) { SkinInfo = skin; diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index fa4f657882..d373618232 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -212,7 +212,7 @@ namespace osu.Game.Skinning public IBindable GetConfig(TLookup lookup) => CurrentSkin.Value.GetConfig(lookup); - public ISkin FindProvider(Func lookupFunction) => CurrentSkin.Value.FindProvider(lookupFunction); + public ISkin FindProvider(Func lookupFunction) => lookupFunction(CurrentSkin.Value) ? CurrentSkin.Value : null; #region IResourceStorageProvider diff --git a/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs b/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs index 0a7fb1483d..2540b6d7da 100644 --- a/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs +++ b/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs @@ -158,6 +158,8 @@ namespace osu.Game.Tests.Beatmaps add { } remove { } } + + public ISkin FindProvider(Func lookupFunction) => null; } } } From b87a5956dd62de3fb77532073022ee6ac0a08e21 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 6 Jun 2021 12:17:55 +0900 Subject: [PATCH 763/839] Add fallback logic to `SkinManager` --- osu.Game/Skinning/SkinManager.cs | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index d373618232..9aa2d90064 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -204,16 +204,34 @@ namespace osu.Game.Skinning public event Action SourceChanged; - public Drawable GetDrawableComponent(ISkinComponent component) => CurrentSkin.Value.GetDrawableComponent(component); + public Drawable GetDrawableComponent(ISkinComponent component) => lookupWithFallback(s => s.GetDrawableComponent(component)); - public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => CurrentSkin.Value.GetTexture(componentName, wrapModeS, wrapModeT); + public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => lookupWithFallback(s => s.GetTexture(componentName, wrapModeS, wrapModeT)); - public ISample GetSample(ISampleInfo sampleInfo) => CurrentSkin.Value.GetSample(sampleInfo); + public ISample GetSample(ISampleInfo sampleInfo) => lookupWithFallback(s => s.GetSample(sampleInfo)); - public IBindable GetConfig(TLookup lookup) => CurrentSkin.Value.GetConfig(lookup); + public IBindable GetConfig(TLookup lookup) => lookupWithFallback(s => s.GetConfig(lookup)); public ISkin FindProvider(Func lookupFunction) => lookupFunction(CurrentSkin.Value) ? CurrentSkin.Value : null; + private Skin defaultLegacySkin; + + private T lookupWithFallback(Func func) + where T : class + { + var selectedSkin = func(CurrentSkin.Value); + + if (selectedSkin != null) + return selectedSkin; + + defaultLegacySkin ??= new DefaultLegacySkin(this); + + if (CurrentSkin.Value is LegacySkin) + return func(defaultLegacySkin); + + return null; + } + #region IResourceStorageProvider AudioManager IStorageResourceProvider.AudioManager => audio; From b904fa6615ad210afc94e874a3861572e9bb0499 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 6 Jun 2021 12:37:42 +0900 Subject: [PATCH 764/839] Revert "Ensure all frames in an animation are retrieved from the same skin" This reverts commit 37c8c63fc566c115aa3f974b6d23bb12f62caa05. --- osu.Game/Skinning/LegacySkinExtensions.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/osu.Game/Skinning/LegacySkinExtensions.cs b/osu.Game/Skinning/LegacySkinExtensions.cs index 6e8276c01e..d8fb1fa664 100644 --- a/osu.Game/Skinning/LegacySkinExtensions.cs +++ b/osu.Game/Skinning/LegacySkinExtensions.cs @@ -54,16 +54,9 @@ namespace osu.Game.Skinning IEnumerable getTextures() { - ISkin lookupSource = null; - for (int i = 0; true; i++) { - string frameName = $"{componentName}{animationSeparator}{i}"; - - // ensure all textures are retrieved from the same skin source. - lookupSource ??= source.FindProvider(s => s.GetTexture(frameName, wrapModeS, wrapModeT) != null); - - if ((texture = lookupSource?.GetTexture(frameName, wrapModeS, wrapModeT)) == null) + if ((texture = source.GetTexture($"{componentName}{animationSeparator}{i}", wrapModeS, wrapModeT)) == null) break; yield return texture; From ed733ee648b91223a3d758d94bdaf90b3a2a6b11 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sun, 6 Jun 2021 20:19:39 +0700 Subject: [PATCH 765/839] directly using table of content entry in wiki table of contents --- osu.Game/Overlays/Wiki/WikiTableOfContents.cs | 22 +++++-------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/osu.Game/Overlays/Wiki/WikiTableOfContents.cs b/osu.Game/Overlays/Wiki/WikiTableOfContents.cs index 6c9f7d1536..77441c26ed 100644 --- a/osu.Game/Overlays/Wiki/WikiTableOfContents.cs +++ b/osu.Game/Overlays/Wiki/WikiTableOfContents.cs @@ -15,9 +15,9 @@ namespace osu.Game.Overlays.Wiki { private readonly FillFlowContainer content; - private Container lastMainTitle; + private TableOfContentsEntry lastMainTitle; - private Container lastSubTitle; + private TableOfContentsEntry lastSubTitle; public WikiTableOfContents() { @@ -42,26 +42,14 @@ namespace osu.Game.Overlays.Wiki if (lastSubTitle != null) lastSubTitle.Margin = new MarginPadding(0); - content.Add(lastSubTitle = new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Margin = new MarginPadding { Bottom = 10 }, - Child = entry, - }); + content.Add(lastSubTitle = entry.With(d => d.Margin = new MarginPadding { Bottom = 10 })); return; } lastSubTitle = null; - content.Add(lastMainTitle = new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Margin = new MarginPadding { Bottom = 5 }, - Child = entry, - }); + content.Add(lastMainTitle = entry.With(d => d.Margin = new MarginPadding { Bottom = 5 })); } private class TableOfContentsEntry : OsuHoverContainer @@ -87,8 +75,8 @@ namespace osu.Game.Overlays.Wiki f.AddText(text); f.RelativeSizeAxes = Axes.X; f.AutoSizeAxes = Axes.Y; + f.Margin = new MarginPadding { Bottom = 2 }; }); - Margin = new MarginPadding { Bottom = 2 }; Padding = new MarginPadding { Left = subtitle ? 10 : 0 }; } From 9ebafb1ec0dcde3756fb681d826d85a465775a95 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 6 Jun 2021 22:26:27 +0900 Subject: [PATCH 766/839] Fix cursor trail logic --- .../TestSceneCursorTrail.cs | 18 ++++++++++++++---- .../Skinning/Legacy/LegacyCursor.cs | 6 ++++-- .../Skinning/Legacy/LegacyCursorTrail.cs | 8 +++++++- .../Legacy/OsuLegacySkinTransformer.cs | 12 ++++++++---- 4 files changed, 33 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs index 9997660c2d..46274e779b 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs @@ -39,18 +39,28 @@ namespace osu.Game.Rulesets.Osu.Tests [Test] public void TestLegacySmoothCursorTrail() { - createTest(() => new LegacySkinContainer(false) + createTest(() => { - Child = new LegacyCursorTrail() + var skinContainer = new LegacySkinContainer(false); + var legacyCursorTrail = new LegacyCursorTrail(skinContainer); + + skinContainer.Child = legacyCursorTrail; + + return skinContainer; }); } [Test] public void TestLegacyDisjointCursorTrail() { - createTest(() => new LegacySkinContainer(true) + createTest(() => { - Child = new LegacyCursorTrail() + var skinContainer = new LegacySkinContainer(true); + var legacyCursorTrail = new LegacyCursorTrail(skinContainer); + + skinContainer.Child = legacyCursorTrail; + + return skinContainer; }); } diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursor.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursor.cs index 7a8555d991..b2ffc171be 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursor.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursor.cs @@ -11,10 +11,12 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { public class LegacyCursor : OsuCursorSprite { + private readonly ISkin skin; private bool spin; - public LegacyCursor() + public LegacyCursor(ISkin skin) { + this.skin = skin; Size = new Vector2(50); Anchor = Anchor.Centre; @@ -22,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy } [BackgroundDependencyLoader] - private void load(ISkinSource skin) + private void load() { bool centre = skin.GetConfig(OsuSkinConfiguration.CursorCentre)?.Value ?? true; spin = skin.GetConfig(OsuSkinConfiguration.CursorRotate)?.Value ?? true; diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs index 0025576325..f6fd3e36ab 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs @@ -14,14 +14,20 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { public class LegacyCursorTrail : CursorTrail { + private readonly ISkin skin; private const double disjoint_trail_time_separation = 1000 / 60.0; private bool disjointTrail; private double lastTrailTime; private IBindable cursorSize; + public LegacyCursorTrail(ISkin skin) + { + this.skin = skin; + } + [BackgroundDependencyLoader] - private void load(ISkinSource skin, OsuConfigManager config) + private void load(OsuConfigManager config) { Texture = skin.GetTexture("cursortrail"); disjointTrail = skin.GetTexture("cursormiddle") == null; diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs index 33693748d9..e3f32fb76f 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs @@ -84,14 +84,18 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy return null; case OsuSkinComponents.Cursor: - if (Source.GetTexture("cursor") != null) - return new LegacyCursor(); + var cursorProvider = Source.FindProvider(s => s.GetTexture("cursor") != null); + + if (cursorProvider != null) + return new LegacyCursor(cursorProvider); return null; case OsuSkinComponents.CursorTrail: - if (Source.GetTexture("cursortrail") != null) - return new LegacyCursorTrail(); + var trailProvider = Source.FindProvider(s => s.GetTexture("cursortrail") != null); + + if (trailProvider != null) + return new LegacyCursorTrail(trailProvider); return null; From b5f145cfa92e08b26c7d6fb976fbe011ce7167cb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 6 Jun 2021 23:01:37 +0900 Subject: [PATCH 767/839] Use null propagation for animation lookups --- osu.Game/Skinning/LegacySkin.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 8f1895883d..337acee9e8 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -417,12 +417,7 @@ namespace osu.Game.Skinning break; } - var animation = this.GetAnimation(component.LookupName, false, false); - - if (animation != null) - return animation; - - return null; + return this.GetAnimation(component.LookupName, false, false); } private Texture getParticleTexture(HitResult result) From e10dfab2e85190328a3e6621d54b4adab1e72f9a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 6 Jun 2021 23:23:35 +0900 Subject: [PATCH 768/839] Ensure scorebar marker lookup is performed on the source the background is retrieved from --- osu.Game/Skinning/LegacyHealthDisplay.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/LegacyHealthDisplay.cs b/osu.Game/Skinning/LegacyHealthDisplay.cs index c601adc3a0..5a5c7f11ea 100644 --- a/osu.Game/Skinning/LegacyHealthDisplay.cs +++ b/osu.Game/Skinning/LegacyHealthDisplay.cs @@ -35,7 +35,10 @@ namespace osu.Game.Skinning { AutoSizeAxes = Axes.Both; - isNewStyle = getTexture(skin, "marker") != null; + var backgroundSource = skin.FindProvider(s => getTexture(s, "bg") != null); + + // the marker lookup to decide which display style must be performed on the source of the bg, which is the most common element. + isNewStyle = getTexture(backgroundSource, "marker") != null; // background implementation is the same for both versions. AddInternal(new Sprite { Texture = getTexture(skin, "bg") }); @@ -76,7 +79,7 @@ namespace osu.Game.Skinning protected override void Flash(JudgementResult result) => marker.Flash(result); - private static Texture getTexture(ISkinSource skin, string name) => skin.GetTexture($"scorebar-{name}"); + private static Texture getTexture(ISkin skin, string name) => skin?.GetTexture($"scorebar-{name}"); private static Color4 getFillColour(double hp) { From 166e4565be07b82a3ced961432053d969e90368f Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 7 Jun 2021 13:59:17 +0900 Subject: [PATCH 769/839] Move `FruitVisualRepresentation` namespace --- .../Objects/Drawables/DrawableFruit.cs | 8 -------- .../Objects/FruitVisualRepresentation.cs | 13 +++++++++++++ .../Skinning/Default/FruitPiece.cs | 1 + .../Skinning/Default/FruitPulpFormation.cs | 2 +- .../Skinning/Legacy/LegacyFruitPiece.cs | 1 + 5 files changed, 16 insertions(+), 9 deletions(-) create mode 100644 osu.Game.Rulesets.Catch/Objects/FruitVisualRepresentation.cs diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs index 0b89c46480..5b55036627 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs @@ -44,12 +44,4 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables ScalingContainer.RotateTo((RandomSingle(1) - 0.5f) * 40); } } - - public enum FruitVisualRepresentation - { - Pear, - Grape, - Pineapple, - Raspberry, - } } diff --git a/osu.Game.Rulesets.Catch/Objects/FruitVisualRepresentation.cs b/osu.Game.Rulesets.Catch/Objects/FruitVisualRepresentation.cs new file mode 100644 index 0000000000..7ec7050245 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Objects/FruitVisualRepresentation.cs @@ -0,0 +1,13 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Rulesets.Catch.Objects +{ + public enum FruitVisualRepresentation + { + Pear, + Grape, + Pineapple, + Raspberry, + } +} diff --git a/osu.Game.Rulesets.Catch/Skinning/Default/FruitPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Default/FruitPiece.cs index 49f128c960..14c94022f2 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Default/FruitPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Default/FruitPiece.cs @@ -3,6 +3,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects.Drawables; namespace osu.Game.Rulesets.Catch.Skinning.Default diff --git a/osu.Game.Rulesets.Catch/Skinning/Default/FruitPulpFormation.cs b/osu.Game.Rulesets.Catch/Skinning/Default/FruitPulpFormation.cs index 88e0b5133a..f097361d2a 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Default/FruitPulpFormation.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Default/FruitPulpFormation.cs @@ -2,7 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Bindables; -using osu.Game.Rulesets.Catch.Objects.Drawables; +using osu.Game.Rulesets.Catch.Objects; using osuTK; namespace osu.Game.Rulesets.Catch.Skinning.Default diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyFruitPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyFruitPiece.cs index 969cc38e5b..bceb3bab42 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyFruitPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyFruitPiece.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Bindables; +using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects.Drawables; namespace osu.Game.Rulesets.Catch.Skinning.Legacy From ac5c55bd2cf1c5e7bf565cef6c451a31894d54a3 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 7 Jun 2021 14:49:37 +0900 Subject: [PATCH 770/839] Remove "fruit visual representation" state from `DrawableFruit` Instead, skin pieces compute visual representation from `IndexInBeatmap`. --- .../Objects/Drawables/CaughtFruit.cs | 13 +------------ .../Objects/Drawables/CaughtObject.cs | 2 ++ .../Objects/Drawables/DrawableFruit.cs | 10 +--------- .../Objects/Drawables/IHasCatchObjectState.cs | 2 ++ .../Objects/Drawables/IHasFruitState.cs | 15 --------------- osu.Game.Rulesets.Catch/Objects/Fruit.cs | 2 ++ .../Skinning/Default/CatchHitObjectPiece.cs | 2 ++ .../Skinning/Default/FruitPiece.cs | 7 ++++--- .../Skinning/Legacy/LegacyFruitPiece.cs | 12 ++++-------- .../Skinning/LegacyCatchHitObjectPiece.cs | 2 ++ 10 files changed, 20 insertions(+), 47 deletions(-) delete mode 100644 osu.Game.Rulesets.Catch/Objects/Drawables/IHasFruitState.cs diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtFruit.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtFruit.cs index 140b411c88..7c88090a20 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtFruit.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtFruit.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 osu.Framework.Bindables; using osu.Game.Rulesets.Catch.Skinning.Default; namespace osu.Game.Rulesets.Catch.Objects.Drawables @@ -9,21 +8,11 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables /// /// Represents a caught by the catcher. /// - public class CaughtFruit : CaughtObject, IHasFruitState + public class CaughtFruit : CaughtObject { - public Bindable VisualRepresentation { get; } = new Bindable(); - public CaughtFruit() : base(CatchSkinComponents.Fruit, _ => new FruitPiece()) { } - - public override void CopyStateFrom(IHasCatchObjectState objectState) - { - base.CopyStateFrom(objectState); - - var fruitState = (IHasFruitState)objectState; - VisualRepresentation.Value = fruitState.VisualRepresentation.Value; - } } } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs index 524505d588..d8bce9bb6d 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs @@ -20,6 +20,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables public PalpableCatchHitObject HitObject { get; private set; } public Bindable AccentColour { get; } = new Bindable(); public Bindable HyperDash { get; } = new Bindable(); + public Bindable IndexInBeatmap { get; } = new Bindable(); public Vector2 DisplaySize => Size * Scale; @@ -51,6 +52,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables Rotation = objectState.DisplayRotation; AccentColour.Value = objectState.AccentColour.Value; HyperDash.Value = objectState.HyperDash.Value; + IndexInBeatmap.Value = objectState.IndexInBeatmap.Value; } protected override void FreeAfterUse() diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs index 5b55036627..0af7ee6c30 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs @@ -3,17 +3,14 @@ using JetBrains.Annotations; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Rulesets.Catch.Skinning.Default; using osu.Game.Skinning; namespace osu.Game.Rulesets.Catch.Objects.Drawables { - public class DrawableFruit : DrawablePalpableCatchHitObject, IHasFruitState + public class DrawableFruit : DrawablePalpableCatchHitObject { - public Bindable VisualRepresentation { get; } = new Bindable(); - public DrawableFruit() : this(null) { @@ -27,11 +24,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables [BackgroundDependencyLoader] private void load() { - IndexInBeatmap.BindValueChanged(change => - { - VisualRepresentation.Value = (FruitVisualRepresentation)(change.NewValue % 4); - }, true); - ScalingContainer.Child = new SkinnableDrawable( new CatchSkinComponent(CatchSkinComponents.Fruit), _ => new FruitPiece()); diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs index 81b61f0959..be0ee2821e 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs @@ -18,6 +18,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables Bindable HyperDash { get; } + Bindable IndexInBeatmap { get; } + Vector2 DisplaySize { get; } float DisplayRotation { get; } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/IHasFruitState.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasFruitState.cs deleted file mode 100644 index 2d4de543c3..0000000000 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/IHasFruitState.cs +++ /dev/null @@ -1,15 +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.Bindables; - -namespace osu.Game.Rulesets.Catch.Objects.Drawables -{ - /// - /// Provides a visual state of a . - /// - public interface IHasFruitState : IHasCatchObjectState - { - Bindable VisualRepresentation { get; } - } -} diff --git a/osu.Game.Rulesets.Catch/Objects/Fruit.cs b/osu.Game.Rulesets.Catch/Objects/Fruit.cs index 43486796ad..4818fe2cad 100644 --- a/osu.Game.Rulesets.Catch/Objects/Fruit.cs +++ b/osu.Game.Rulesets.Catch/Objects/Fruit.cs @@ -9,5 +9,7 @@ namespace osu.Game.Rulesets.Catch.Objects public class Fruit : PalpableCatchHitObject { public override Judgement CreateJudgement() => new CatchJudgement(); + + public static FruitVisualRepresentation GetVisualRepresentation(int indexInBeatmap) => (FruitVisualRepresentation)(indexInBeatmap % 4); } } diff --git a/osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs index 51c06c8e37..2db3bae034 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs @@ -15,6 +15,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default { public readonly Bindable AccentColour = new Bindable(); public readonly Bindable HyperDash = new Bindable(); + public readonly Bindable IndexInBeatmap = new Bindable(); [Resolved] protected IHasCatchObjectState ObjectState { get; private set; } @@ -37,6 +38,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default AccentColour.BindTo(ObjectState.AccentColour); HyperDash.BindTo(ObjectState.HyperDash); + IndexInBeatmap.BindTo(ObjectState.IndexInBeatmap); HyperDash.BindValueChanged(hyper => { diff --git a/osu.Game.Rulesets.Catch/Skinning/Default/FruitPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Default/FruitPiece.cs index 14c94022f2..cfe0df0c97 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Default/FruitPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Default/FruitPiece.cs @@ -4,7 +4,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Rulesets.Catch.Objects; -using osu.Game.Rulesets.Catch.Objects.Drawables; namespace osu.Game.Rulesets.Catch.Skinning.Default { @@ -40,8 +39,10 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default { base.LoadComplete(); - var fruitState = (IHasFruitState)ObjectState; - VisualRepresentation.BindTo(fruitState.VisualRepresentation); + IndexInBeatmap.BindValueChanged(index => + { + VisualRepresentation.Value = Fruit.GetVisualRepresentation(index.NewValue); + }, true); } } } diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyFruitPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyFruitPiece.cs index bceb3bab42..f002bab219 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyFruitPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyFruitPiece.cs @@ -1,24 +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 osu.Framework.Bindables; using osu.Game.Rulesets.Catch.Objects; -using osu.Game.Rulesets.Catch.Objects.Drawables; namespace osu.Game.Rulesets.Catch.Skinning.Legacy { internal class LegacyFruitPiece : LegacyCatchHitObjectPiece { - public readonly Bindable VisualRepresentation = new Bindable(); - protected override void LoadComplete() { base.LoadComplete(); - var fruitState = (IHasFruitState)ObjectState; - VisualRepresentation.BindTo(fruitState.VisualRepresentation); - - VisualRepresentation.BindValueChanged(visual => setTexture(visual.NewValue), true); + IndexInBeatmap.BindValueChanged(index => + { + setTexture(Fruit.GetVisualRepresentation(index.NewValue)); + }, true); } private void setTexture(FruitVisualRepresentation visualRepresentation) diff --git a/osu.Game.Rulesets.Catch/Skinning/LegacyCatchHitObjectPiece.cs b/osu.Game.Rulesets.Catch/Skinning/LegacyCatchHitObjectPiece.cs index 4b1f5a4724..8c1ba014cf 100644 --- a/osu.Game.Rulesets.Catch/Skinning/LegacyCatchHitObjectPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/LegacyCatchHitObjectPiece.cs @@ -19,6 +19,7 @@ namespace osu.Game.Rulesets.Catch.Skinning { public readonly Bindable AccentColour = new Bindable(); public readonly Bindable HyperDash = new Bindable(); + public readonly Bindable IndexInBeatmap = new Bindable(); private readonly Sprite colouredSprite; private readonly Sprite overlaySprite; @@ -64,6 +65,7 @@ namespace osu.Game.Rulesets.Catch.Skinning AccentColour.BindTo(ObjectState.AccentColour); HyperDash.BindTo(ObjectState.HyperDash); + IndexInBeatmap.BindTo(ObjectState.IndexInBeatmap); hyperSprite.Colour = Skin.GetConfig(CatchSkinColour.HyperDashFruit)?.Value ?? Skin.GetConfig(CatchSkinColour.HyperDash)?.Value ?? From bb02c35f2de27409182ca79270f7744da282b356 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 7 Jun 2021 15:10:47 +0900 Subject: [PATCH 771/839] Move all osu!catch legacy skin piece files to the correct location --- .../Skinning/{ => Legacy}/LegacyBananaPiece.cs | 2 +- .../Skinning/{ => Legacy}/LegacyCatchHitObjectPiece.cs | 2 +- .../Skinning/{ => Legacy}/LegacyDropletPiece.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename osu.Game.Rulesets.Catch/Skinning/{ => Legacy}/LegacyBananaPiece.cs (91%) rename osu.Game.Rulesets.Catch/Skinning/{ => Legacy}/LegacyCatchHitObjectPiece.cs (98%) rename osu.Game.Rulesets.Catch/Skinning/{ => Legacy}/LegacyDropletPiece.cs (93%) diff --git a/osu.Game.Rulesets.Catch/Skinning/LegacyBananaPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyBananaPiece.cs similarity index 91% rename from osu.Game.Rulesets.Catch/Skinning/LegacyBananaPiece.cs rename to osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyBananaPiece.cs index f80e50c8c0..5bd5b0d4bb 100644 --- a/osu.Game.Rulesets.Catch/Skinning/LegacyBananaPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyBananaPiece.cs @@ -3,7 +3,7 @@ using osu.Framework.Graphics.Textures; -namespace osu.Game.Rulesets.Catch.Skinning +namespace osu.Game.Rulesets.Catch.Skinning.Legacy { public class LegacyBananaPiece : LegacyCatchHitObjectPiece { diff --git a/osu.Game.Rulesets.Catch/Skinning/LegacyCatchHitObjectPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatchHitObjectPiece.cs similarity index 98% rename from osu.Game.Rulesets.Catch/Skinning/LegacyCatchHitObjectPiece.cs rename to osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatchHitObjectPiece.cs index 4b1f5a4724..2e772df551 100644 --- a/osu.Game.Rulesets.Catch/Skinning/LegacyCatchHitObjectPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatchHitObjectPiece.cs @@ -13,7 +13,7 @@ using osu.Game.Skinning; using osuTK; using osuTK.Graphics; -namespace osu.Game.Rulesets.Catch.Skinning +namespace osu.Game.Rulesets.Catch.Skinning.Legacy { public abstract class LegacyCatchHitObjectPiece : PoolableDrawable { diff --git a/osu.Game.Rulesets.Catch/Skinning/LegacyDropletPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyDropletPiece.cs similarity index 93% rename from osu.Game.Rulesets.Catch/Skinning/LegacyDropletPiece.cs rename to osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyDropletPiece.cs index 8f4331d2a3..2c5cbe1e41 100644 --- a/osu.Game.Rulesets.Catch/Skinning/LegacyDropletPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyDropletPiece.cs @@ -4,7 +4,7 @@ using osu.Framework.Graphics.Textures; using osuTK; -namespace osu.Game.Rulesets.Catch.Skinning +namespace osu.Game.Rulesets.Catch.Skinning.Legacy { public class LegacyDropletPiece : LegacyCatchHitObjectPiece { From aa700702fe730763aa1630985e1ab35922501332 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 7 Jun 2021 15:48:45 +0900 Subject: [PATCH 772/839] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index e95c7e6619..1216b1772f 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 49b86ad56e..21a890014a 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -34,7 +34,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index cbb6a21fd1..bf080e4def 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 277eb9fa6ed825c53ccefe87e641e186ff998bd3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 7 Jun 2021 15:58:41 +0900 Subject: [PATCH 773/839] Fix slider repeat arrow not updating rotation immediately while paused in editor A bit of a local solution, but not sure there's a better way to handle this. Closes #13342. --- .../Objects/Drawables/DrawableSliderRepeat.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs index b7458b5695..4a2a18ffd6 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs @@ -152,7 +152,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables while (Math.Abs(aimRotation - Arrow.Rotation) > 180) aimRotation += aimRotation < Arrow.Rotation ? 360 : -360; - if (!hasRotation) + // The clock may be paused in a scenario like the editor. + if (!hasRotation || !Clock.IsRunning) { Arrow.Rotation = aimRotation; hasRotation = true; From e8d41477731d41e2aa0adfc6a688ac131cc54785 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 7 Jun 2021 16:08:44 +0900 Subject: [PATCH 774/839] Add missing null handling for never `Markdig` version --- .../Graphics/Containers/Markdown/OsuMarkdownContainer.cs | 7 +++++-- osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownContainer.cs b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownContainer.cs index 6facf4e26c..81f30bd406 100644 --- a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownContainer.cs +++ b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownContainer.cs @@ -30,9 +30,12 @@ namespace osu.Game.Graphics.Containers.Markdown break; case ListItemBlock listItemBlock: - var isOrdered = ((ListBlock)listItemBlock.Parent).IsOrdered; - var childContainer = CreateListItem(listItemBlock, level, isOrdered); + bool isOrdered = ((ListBlock)listItemBlock.Parent)?.IsOrdered == true; + + OsuMarkdownListItem childContainer = CreateListItem(listItemBlock, level, isOrdered); + container.Add(childContainer); + foreach (var single in listItemBlock) base.AddMarkdownComponent(single, childContainer.Content, level); break; diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs index acaaa523a2..6f0b433acb 100644 --- a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs +++ b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs @@ -33,7 +33,7 @@ namespace osu.Game.Overlays.Wiki.Markdown case ParagraphBlock paragraphBlock: // Check if paragraph only contains an image - if (paragraphBlock.Inline.Count() == 1 && paragraphBlock.Inline.FirstChild is LinkInline { IsImage: true } linkInline) + if (paragraphBlock.Inline?.Count() == 1 && paragraphBlock.Inline.FirstChild is LinkInline { IsImage: true } linkInline) { container.Add(new WikiMarkdownImageBlock(linkInline)); return; From f677f9b5f44a36fd4dc880c55bd7c998fb5b98db Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 7 Jun 2021 17:22:30 +0900 Subject: [PATCH 775/839] Stop `BackgroundScreenDefault` from reloading beatmap background when already correct --- .../TestSceneBackgroundScreenDefault.cs | 59 ++++++++++ .../Backgrounds/BackgroundScreenDefault.cs | 103 ++++++++++-------- 2 files changed, 115 insertions(+), 47 deletions(-) create mode 100644 osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs diff --git a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs new file mode 100644 index 0000000000..ef50f866d5 --- /dev/null +++ b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs @@ -0,0 +1,59 @@ +// 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 NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Screens; +using osu.Framework.Testing; +using osu.Game.Configuration; +using osu.Game.Graphics.Backgrounds; +using osu.Game.Online.API; +using osu.Game.Screens; +using osu.Game.Screens.Backgrounds; +using osu.Game.Users; + +namespace osu.Game.Tests.Visual.Background +{ + [TestFixture] + public class TestSceneBackgroundScreenDefault : OsuTestScene + { + private BackgroundScreenStack stack; + private BackgroundScreenDefault screen; + + private Graphics.Backgrounds.Background getCurrentBackground() => screen.ChildrenOfType().FirstOrDefault(); + + [Resolved] + private OsuConfigManager config { get; set; } + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("create background stack", () => Child = stack = new BackgroundScreenStack()); + AddStep("push default screen", () => stack.Push(screen = new BackgroundScreenDefault(false))); + AddUntilStep("wait for screen to load", () => screen.IsCurrentScreen()); + } + + [Test] + public void TestBeatmapDoesntReloadOnNoChange() + { + BeatmapBackground last = null; + + setSourceMode(BackgroundSource.Beatmap); + setSupporter(true); + + AddUntilStep("wait for beatmap background to be loaded", () => (last = getCurrentBackground() as BeatmapBackground) != null); + AddAssert("next doesn't load new background", () => screen.Next() == false); + + // doesn't really need to be checked but might as well. + AddWaitStep("wait a bit", 5); + AddUntilStep("ensure same background instance", () => last == getCurrentBackground()); + } + + private void setSourceMode(BackgroundSource source) => + AddStep("set background mode to beatmap", () => config.SetValue(OsuSetting.MenuBackgroundSource, source)); + + private void setSupporter(bool isSupporter) => + AddStep("set supporter", () => ((DummyAPIAccess)API).LocalUser.Value = new User { IsSupporter = isSupporter }); + } +} diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs index b02e7ddb0d..dc27514459 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs @@ -5,8 +5,8 @@ using System.Threading; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Utils; using osu.Framework.Threading; +using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Graphics.Backgrounds; @@ -53,14 +53,41 @@ namespace osu.Game.Screens.Backgrounds mode.ValueChanged += _ => Next(); beatmap.ValueChanged += _ => Next(); introSequence.ValueChanged += _ => Next(); - seasonalBackgroundLoader.SeasonalBackgroundChanged += Next; + seasonalBackgroundLoader.SeasonalBackgroundChanged += () => Next(); currentDisplay = RNG.Next(0, background_count); Next(); } - private void display(Background newBackground) + private ScheduledDelegate nextTask; + private CancellationTokenSource cancellationTokenSource; + + /// + /// Request loading the next background. + /// + /// Whether a new background was queued for load. May return false if the current background is still valid. + public bool Next() + { + var nextBackground = createBackground(); + + // in the case that the background hasn't changed, we want to avoid cancelling any tasks that could still be loading. + if (nextBackground == background) + return false; + + cancellationTokenSource?.Cancel(); + cancellationTokenSource = new CancellationTokenSource(); + + nextTask?.Cancel(); + nextTask = Scheduler.AddDelayed(() => + { + LoadComponentAsync(nextBackground, displayNext, cancellationTokenSource.Token); + }, 100); + + return true; + } + + private void displayNext(Background newBackground) { background?.FadeOut(800, Easing.InOutSine); background?.Expire(); @@ -69,68 +96,50 @@ namespace osu.Game.Screens.Backgrounds currentDisplay++; } - private ScheduledDelegate nextTask; - private CancellationTokenSource cancellationTokenSource; - - public void Next() - { - nextTask?.Cancel(); - cancellationTokenSource?.Cancel(); - cancellationTokenSource = new CancellationTokenSource(); - nextTask = Scheduler.AddDelayed(() => LoadComponentAsync(createBackground(), display, cancellationTokenSource.Token), 100); - } - private Background createBackground() { - Background newBackground; - string backgroundName; + // seasonal background loading gets highest priority. + Background newBackground = seasonalBackgroundLoader.LoadNextBackground(); - var seasonalBackground = seasonalBackgroundLoader.LoadNextBackground(); - - if (seasonalBackground != null) - { - seasonalBackground.Depth = currentDisplay; - return seasonalBackground; - } - - switch (introSequence.Value) - { - case IntroSequence.Welcome: - backgroundName = "Intro/Welcome/menu-background"; - break; - - default: - backgroundName = $@"Menu/menu-background-{currentDisplay % background_count + 1}"; - break; - } - - if (user.Value?.IsSupporter ?? false) + if (newBackground == null && user.Value?.IsSupporter == true) { switch (mode.Value) { case BackgroundSource.Beatmap: - newBackground = new BeatmapBackground(beatmap.Value, backgroundName); - break; - case BackgroundSource.BeatmapWithStoryboard: - newBackground = AllowStoryboardBackground - ? new BeatmapBackgroundWithStoryboard(beatmap.Value, backgroundName) - : new BeatmapBackground(beatmap.Value, backgroundName); - break; + { + // this method is called in many cases where the beatmap hasn't changed (ie. on screen transitions). + // if a background is already displayed for the requested beatmap, we don't want to load it again. + if ((background as BeatmapBackground)?.Beatmap == beatmap.Value) + return background; - default: - newBackground = new SkinnedBackground(skin.Value, backgroundName); + if (mode.Value == BackgroundSource.BeatmapWithStoryboard && AllowStoryboardBackground) + newBackground = new BeatmapBackgroundWithStoryboard(beatmap.Value, getBackgroundTextureName()); + + newBackground ??= new BeatmapBackground(beatmap.Value, getBackgroundTextureName()); break; + } } } - else - newBackground = new Background(backgroundName); + newBackground ??= new Background(getBackgroundTextureName()); newBackground.Depth = currentDisplay; return newBackground; } + private string getBackgroundTextureName() + { + switch (introSequence.Value) + { + case IntroSequence.Welcome: + return @"Intro/Welcome/menu-background"; + + default: + return $@"Menu/menu-background-{currentDisplay % background_count + 1}"; + } + } + private class SkinnedBackground : Background { private readonly Skin skin; From 59130be99cce706979294681a0aa2cd0f159c7c1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 7 Jun 2021 17:32:04 +0900 Subject: [PATCH 776/839] Fix switching storyboard mode not triggering a reload --- .../Screens/Backgrounds/BackgroundScreenDefault.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs index dc27514459..6bcfaac907 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs @@ -108,15 +108,16 @@ namespace osu.Game.Screens.Backgrounds case BackgroundSource.Beatmap: case BackgroundSource.BeatmapWithStoryboard: { - // this method is called in many cases where the beatmap hasn't changed (ie. on screen transitions). - // if a background is already displayed for the requested beatmap, we don't want to load it again. - if ((background as BeatmapBackground)?.Beatmap == beatmap.Value) - return background; - if (mode.Value == BackgroundSource.BeatmapWithStoryboard && AllowStoryboardBackground) newBackground = new BeatmapBackgroundWithStoryboard(beatmap.Value, getBackgroundTextureName()); - newBackground ??= new BeatmapBackground(beatmap.Value, getBackgroundTextureName()); + + // this method is called in many cases where the beatmap hasn't changed (ie. on screen transitions). + // if a background is already displayed for the requested beatmap, we don't want to load it again. + if (background?.GetType() == newBackground.GetType() && + (background as BeatmapBackground)?.Beatmap == beatmap.Value) + return background; + break; } } From 729e05241f22e096682e550ae6ab502e32b58c2b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 7 Jun 2021 17:32:10 +0900 Subject: [PATCH 777/839] Add more test coverage --- .../TestSceneBackgroundScreenDefault.cs | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs index ef50f866d5..09fe9b3767 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs @@ -34,6 +34,33 @@ namespace osu.Game.Tests.Visual.Background AddUntilStep("wait for screen to load", () => screen.IsCurrentScreen()); } + [Test] + public void TestTogglingStoryboardSwitchesBackgroundType() + { + setSupporter(true); + + setSourceMode(BackgroundSource.Beatmap); + AddUntilStep("is beatmap background", () => getCurrentBackground() is BeatmapBackground); + + setSourceMode(BackgroundSource.BeatmapWithStoryboard); + AddUntilStep("is storyboard background", () => getCurrentBackground() is BeatmapBackgroundWithStoryboard); + } + + [Test] + public void TestTogglingSupporterTogglesBeatmapBackground() + { + setSourceMode(BackgroundSource.Beatmap); + + setSupporter(true); + AddUntilStep("is beatmap background", () => getCurrentBackground() is BeatmapBackground); + + setSupporter(false); + AddUntilStep("is default background", () => !(getCurrentBackground() is BeatmapBackground)); + + setSupporter(true); + AddUntilStep("is beatmap background", () => getCurrentBackground() is BeatmapBackground); + } + [Test] public void TestBeatmapDoesntReloadOnNoChange() { @@ -54,6 +81,10 @@ namespace osu.Game.Tests.Visual.Background AddStep("set background mode to beatmap", () => config.SetValue(OsuSetting.MenuBackgroundSource, source)); private void setSupporter(bool isSupporter) => - AddStep("set supporter", () => ((DummyAPIAccess)API).LocalUser.Value = new User { IsSupporter = isSupporter }); + AddStep($"set supporter {isSupporter}", () => ((DummyAPIAccess)API).LocalUser.Value = new User + { + IsSupporter = isSupporter, + Id = API.LocalUser.Value.Id + 1, + }); } } From e606bf249afa83a73ebd9924886bced36afd9222 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 7 Jun 2021 11:05:21 +0200 Subject: [PATCH 778/839] Move dependency specification to BDL As it is not used anywhere else. --- osu.Game/Overlays/Wiki/WikiTableOfContents.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Wiki/WikiTableOfContents.cs b/osu.Game/Overlays/Wiki/WikiTableOfContents.cs index 77441c26ed..c0615dce1f 100644 --- a/osu.Game/Overlays/Wiki/WikiTableOfContents.cs +++ b/osu.Game/Overlays/Wiki/WikiTableOfContents.cs @@ -54,9 +54,6 @@ namespace osu.Game.Overlays.Wiki private class TableOfContentsEntry : OsuHoverContainer { - [Resolved] - private OverlayColourProvider colourProvider { get; set; } - private readonly MarkdownHeading target; private readonly OsuTextFlowContainer textFlow; @@ -83,7 +80,7 @@ namespace osu.Game.Overlays.Wiki protected override IEnumerable EffectTargets => new Drawable[] { textFlow }; [BackgroundDependencyLoader] - private void load(OverlayScrollContainer scrollContainer) + private void load(OverlayColourProvider colourProvider, OverlayScrollContainer scrollContainer) { IdleColour = colourProvider.Light2; HoverColour = colourProvider.Light1; From a0bda9ad595e48aabe2141359e2c38fdc3822188 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 7 Jun 2021 11:18:18 +0200 Subject: [PATCH 779/839] Hoist scroll cache declaration to original place of definition --- osu.Game/Overlays/OnlineOverlay.cs | 3 +++ osu.Game/Overlays/WikiOverlay.cs | 4 ---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/OnlineOverlay.cs b/osu.Game/Overlays/OnlineOverlay.cs index de33e4a1bc..a610511398 100644 --- a/osu.Game/Overlays/OnlineOverlay.cs +++ b/osu.Game/Overlays/OnlineOverlay.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 osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.UserInterface; @@ -13,7 +14,9 @@ namespace osu.Game.Overlays { protected override Container Content => content; + [Cached] protected readonly OverlayScrollContainer ScrollFlow; + protected readonly LoadingLayer Loading; private readonly Container content; diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs index fc24820f3c..bde73b6180 100644 --- a/osu.Game/Overlays/WikiOverlay.cs +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -25,9 +25,6 @@ namespace osu.Game.Overlays [Resolved] private IAPIProvider api { get; set; } - [Cached] - private readonly OverlayScrollContainer scrollContainer; - private GetWikiRequest request; private CancellationTokenSource cancellationToken; @@ -39,7 +36,6 @@ namespace osu.Game.Overlays public WikiOverlay() : base(OverlayColourScheme.Orange, false) { - scrollContainer = ScrollFlow; } public void ShowPage(string pagePath = index_path) From a0fbf29b987da2eead9141b45bf0e7c4fd6c8d68 Mon Sep 17 00:00:00 2001 From: Susko3 <16479013+Susko3@users.noreply.github.com> Date: Mon, 7 Jun 2021 11:24:48 +0200 Subject: [PATCH 780/839] add `application/x-osu-archive` mime type to Android `IntentFilter`s --- osu.Android/OsuGameActivity.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Android/OsuGameActivity.cs b/osu.Android/OsuGameActivity.cs index cffcea22c2..063e02d349 100644 --- a/osu.Android/OsuGameActivity.cs +++ b/osu.Android/OsuGameActivity.cs @@ -20,7 +20,8 @@ namespace osu.Android [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullUser, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = false, LaunchMode = LaunchMode.SingleInstance, Exported = true)] [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osz", DataHost = "*", DataMimeType = "*/*")] [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osk", DataHost = "*", DataMimeType = "*/*")] - [IntentFilter(new[] { Intent.ActionSend, Intent.ActionSendMultiple }, Categories = new[] { Intent.CategoryDefault }, DataMimeTypes = new[] { "application/zip", "application/octet-stream", "application/download", "application/x-zip", "application/x-zip-compressed" })] + [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataMimeType = "application/x-osu-archive")] + [IntentFilter(new[] { Intent.ActionSend, Intent.ActionSendMultiple }, Categories = new[] { Intent.CategoryDefault }, DataMimeTypes = new[] { "application/zip", "application/octet-stream", "application/download", "application/x-zip", "application/x-zip-compressed", "application/x-osu-archive" })] [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryBrowsable, Intent.CategoryDefault }, DataSchemes = new[] { "osu", "osump" })] public class OsuGameActivity : AndroidGameActivity { From 122a624b7fb68d8f5bb55ad30b96cd38be8e9a3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 7 Jun 2021 12:15:37 +0200 Subject: [PATCH 781/839] Remove bogus `CatchHitWindows` `CatchHitWindows` were a vestige from the past, and were not actually used anywhere except for the hit error meter test, giving off an appearance that the hit error meter was working properly. `CatchHitObject` actually specifies empty hit windows. --- .../Scoring/CatchHitWindows.cs | 22 ------------------- .../Visual/Gameplay/TestSceneHitErrorMeter.cs | 6 ++--- 2 files changed, 2 insertions(+), 26 deletions(-) delete mode 100644 osu.Game.Rulesets.Catch/Scoring/CatchHitWindows.cs diff --git a/osu.Game.Rulesets.Catch/Scoring/CatchHitWindows.cs b/osu.Game.Rulesets.Catch/Scoring/CatchHitWindows.cs deleted file mode 100644 index 0a444d923e..0000000000 --- a/osu.Game.Rulesets.Catch/Scoring/CatchHitWindows.cs +++ /dev/null @@ -1,22 +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.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Catch.Scoring -{ - public class CatchHitWindows : HitWindows - { - public override bool IsHitResultAllowed(HitResult result) - { - switch (result) - { - case HitResult.Great: - case HitResult.Miss: - return true; - } - - return false; - } - } -} diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs index ab13095af4..acc7287b5a 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs @@ -11,7 +11,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Threading; using osu.Framework.Utils; using osu.Game.Graphics.Sprites; -using osu.Game.Rulesets.Catch.Scoring; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mania.Scoring; using osu.Game.Rulesets.Mods; @@ -84,10 +83,9 @@ namespace osu.Game.Tests.Visual.Gameplay } [Test] - public void TestCatch() + public void TestEmpty() { - AddStep("OD 1", () => recreateDisplay(new CatchHitWindows(), 1)); - AddStep("OD 10", () => recreateDisplay(new CatchHitWindows(), 10)); + AddStep("empty windows", () => recreateDisplay(HitWindows.Empty, 5)); } private void recreateDisplay(HitWindows hitWindows, float overallDifficulty) From 37d062c7cd736563d2760ac5a9f4b154a74c1c02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 7 Jun 2021 12:25:48 +0200 Subject: [PATCH 782/839] Add failing assertions to hit error meter test --- .../Visual/Gameplay/TestSceneHitErrorMeter.cs | 18 ++++++++++++++++-- .../HUD/HitErrorMeters/BarHitErrorMeter.cs | 2 +- .../HUD/HitErrorMeters/ColourHitErrorMeter.cs | 2 +- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs index acc7287b5a..d2e25d1f9f 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs @@ -4,10 +4,12 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; using osu.Framework.Threading; using osu.Framework.Utils; using osu.Game.Graphics.Sprites; @@ -86,6 +88,18 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestEmpty() { AddStep("empty windows", () => recreateDisplay(HitWindows.Empty, 5)); + + AddStep("hit", () => newJudgement()); + AddAssert("no bars added", () => !this.ChildrenOfType().Any()); + AddAssert("circle added", () => + this.ChildrenOfType().All( + meter => meter.ChildrenOfType().Count() == 1)); + + AddStep("miss", () => newJudgement(50, HitResult.Miss)); + AddAssert("no bars added", () => !this.ChildrenOfType().Any()); + AddAssert("circle added", () => + this.ChildrenOfType().All( + meter => meter.ChildrenOfType().Count() == 2)); } private void recreateDisplay(HitWindows hitWindows, float overallDifficulty) @@ -152,12 +166,12 @@ namespace osu.Game.Tests.Visual.Gameplay }); } - private void newJudgement(double offset = 0) + private void newJudgement(double offset = 0, HitResult result = HitResult.Perfect) { scoreProcessor.ApplyResult(new JudgementResult(new HitCircle { HitWindows = drawableRuleset.HitWindows }, new Judgement()) { TimeOffset = offset == 0 ? RNG.Next(-150, 150) : offset, - Type = HitResult.Perfect, + Type = result, }); } diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs index 5d0263772d..5a8a65acfd 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs @@ -244,7 +244,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters private float getRelativeJudgementPosition(double value) => Math.Clamp((float)((value / maxHitWindow) + 1) / 2, 0, 1); - private class JudgementLine : CompositeDrawable + internal class JudgementLine : CompositeDrawable { private const int judgement_fade_duration = 5000; diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs index e9ccbcdae2..86c0de8855 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs @@ -53,7 +53,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters } } - private class HitErrorCircle : Container + internal class HitErrorCircle : Container { public bool IsRemoved { get; private set; } From 0531c2dcd9a5072f684541f039f8a1dfd1a1468e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 7 Jun 2021 12:31:24 +0200 Subject: [PATCH 783/839] Move empty window check to bar error meter It's not valid in the base `HitErrorMeter`, as the colour meter only displays colour for a given judgement, so it is still valid to add new items to it even if the hit window is 0, as misses are still possible. --- .../Play/HUD/HitErrorMeters/BarHitErrorMeter.cs | 2 +- .../Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs | 12 ++---------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs index 5a8a65acfd..0412085d1d 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs @@ -214,7 +214,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters protected override void OnNewJudgement(JudgementResult judgement) { - if (!judgement.IsHit) + if (!judgement.IsHit || judgement.HitObject.HitWindows?.WindowFor(HitResult.Miss) == 0) return; if (judgementsContainer.Count > max_concurrent_judgements) diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs index b0f9928b13..17a6e772fb 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs @@ -32,15 +32,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters { base.LoadComplete(); - processor.NewJudgement += onNewJudgement; - } - - private void onNewJudgement(JudgementResult result) - { - if (result.HitObject.HitWindows?.WindowFor(HitResult.Miss) == 0) - return; - - OnNewJudgement(result); + processor.NewJudgement += OnNewJudgement; } protected abstract void OnNewJudgement(JudgementResult judgement); @@ -74,7 +66,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters base.Dispose(isDisposing); if (processor != null) - processor.NewJudgement -= onNewJudgement; + processor.NewJudgement -= OnNewJudgement; } } } From 1b4771655aa694dabdfdb9a78e3d063e32d34b09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 7 Jun 2021 13:13:24 +0200 Subject: [PATCH 784/839] Adjust test scene to avoid cross-test interference * Move steps from ctor to a separate basic test. * Wait for barrage to complete in basic test, as not doing so polluted state of other tests. * Reset score processor after every test. --- .../Visual/Gameplay/TestSceneHitErrorMeter.cs | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs index d2e25d1f9f..2a12577ad8 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs @@ -30,15 +30,22 @@ namespace osu.Game.Tests.Visual.Gameplay { public class TestSceneHitErrorMeter : OsuTestScene { - [Cached] - private ScoreProcessor scoreProcessor = new ScoreProcessor(); + [Cached(typeof(ScoreProcessor))] + private TestScoreProcessor scoreProcessor = new TestScoreProcessor(); [Cached(typeof(DrawableRuleset))] private TestDrawableRuleset drawableRuleset = new TestDrawableRuleset(); - public TestSceneHitErrorMeter() + [SetUpSteps] + public void SetUp() { - recreateDisplay(new OsuHitWindows(), 5); + AddStep("reset score processor", () => scoreProcessor.Reset()); + } + + [Test] + public void TestBasic() + { + AddStep("create display", () => recreateDisplay(new OsuHitWindows(), 5)); AddRepeatStep("New random judgement", () => newJudgement(), 40); @@ -46,12 +53,11 @@ namespace osu.Game.Tests.Visual.Gameplay AddRepeatStep("New max positive", () => newJudgement(drawableRuleset.HitWindows.WindowFor(HitResult.Meh)), 20); AddStep("New fixed judgement (50ms)", () => newJudgement(50)); + ScheduledDelegate del = null; AddStep("Judgement barrage", () => { int runCount = 0; - ScheduledDelegate del = null; - del = Scheduler.AddDelayed(() => { newJudgement(runCount++ / 10f); @@ -61,6 +67,7 @@ namespace osu.Game.Tests.Visual.Gameplay del?.Cancel(); }, 10, true); }); + AddUntilStep("wait for barrage", () => del.Cancelled); } [Test] @@ -211,5 +218,10 @@ namespace osu.Game.Tests.Visual.Gameplay public override void CancelResume() => throw new NotImplementedException(); } + + private class TestScoreProcessor : ScoreProcessor + { + public void Reset() => base.Reset(false); + } } } From 08701b5eab700ea2944cf358cd60616d8fe9c96a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 7 Jun 2021 23:23:10 +0900 Subject: [PATCH 785/839] Ensure all lookups in `LegacyHealthDisplay` use the found provider Not actually needed to fix the remaining issue but does feel better --- osu.Game/Skinning/LegacyHealthDisplay.cs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/osu.Game/Skinning/LegacyHealthDisplay.cs b/osu.Game/Skinning/LegacyHealthDisplay.cs index 5a5c7f11ea..9d3bafd0b1 100644 --- a/osu.Game/Skinning/LegacyHealthDisplay.cs +++ b/osu.Game/Skinning/LegacyHealthDisplay.cs @@ -20,9 +20,6 @@ namespace osu.Game.Skinning { private const double epic_cutoff = 0.5; - [Resolved] - private ISkinSource skin { get; set; } - private LegacyHealthPiece fill; private LegacyHealthPiece marker; @@ -31,14 +28,14 @@ namespace osu.Game.Skinning private bool isNewStyle; [BackgroundDependencyLoader] - private void load() + private void load(ISkinSource source) { AutoSizeAxes = Axes.Both; - var backgroundSource = skin.FindProvider(s => getTexture(s, "bg") != null); + var skin = source.FindProvider(s => getTexture(s, "bg") != null); // the marker lookup to decide which display style must be performed on the source of the bg, which is the most common element. - isNewStyle = getTexture(backgroundSource, "marker") != null; + isNewStyle = getTexture(skin, "marker") != null; // background implementation is the same for both versions. AddInternal(new Sprite { Texture = getTexture(skin, "bg") }); @@ -98,7 +95,7 @@ namespace osu.Game.Skinning private readonly Texture dangerTexture; private readonly Texture superDangerTexture; - public LegacyOldStyleMarker(ISkinSource skin) + public LegacyOldStyleMarker(ISkin skin) { normalTexture = getTexture(skin, "ki"); dangerTexture = getTexture(skin, "kidanger"); @@ -129,9 +126,9 @@ namespace osu.Game.Skinning public class LegacyNewStyleMarker : LegacyMarker { - private readonly ISkinSource skin; + private readonly ISkin skin; - public LegacyNewStyleMarker(ISkinSource skin) + public LegacyNewStyleMarker(ISkin skin) { this.skin = skin; } @@ -153,7 +150,7 @@ namespace osu.Game.Skinning internal class LegacyOldStyleFill : LegacyHealthPiece { - public LegacyOldStyleFill(ISkinSource skin) + public LegacyOldStyleFill(ISkin skin) { // required for sizing correctly.. var firstFrame = getTexture(skin, "colour-0"); @@ -176,7 +173,7 @@ namespace osu.Game.Skinning internal class LegacyNewStyleFill : LegacyHealthPiece { - public LegacyNewStyleFill(ISkinSource skin) + public LegacyNewStyleFill(ISkin skin) { InternalChild = new Sprite { From c0305343bc9312333957362d124c906f2febd82b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 7 Jun 2021 23:23:44 +0900 Subject: [PATCH 786/839] Fix `FindProvider` incorrectly returning `LegacySkinTransformer` itself --- osu.Game/Skinning/LegacySkinTransformer.cs | 8 +++++++- osu.Game/Skinning/SkinProvidingContainer.cs | 12 ++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/osu.Game/Skinning/LegacySkinTransformer.cs b/osu.Game/Skinning/LegacySkinTransformer.cs index cace4acf6c..651fdddb1b 100644 --- a/osu.Game/Skinning/LegacySkinTransformer.cs +++ b/osu.Game/Skinning/LegacySkinTransformer.cs @@ -16,7 +16,7 @@ namespace osu.Game.Skinning /// /// Transformer used to handle support of legacy features for individual rulesets. /// - public abstract class LegacySkinTransformer : ISkin + public abstract class LegacySkinTransformer : ISkinSource { /// /// Source of the which is being transformed. @@ -50,5 +50,11 @@ namespace osu.Game.Skinning public abstract IBindable GetConfig(TLookup lookup); public ISkin FindProvider(Func lookupFunction) => Source.FindProvider(lookupFunction); + + public event Action SourceChanged + { + add { throw new NotSupportedException(); } + remove { } + } } } diff --git a/osu.Game/Skinning/SkinProvidingContainer.cs b/osu.Game/Skinning/SkinProvidingContainer.cs index 863b5f5a24..0e16cf43ee 100644 --- a/osu.Game/Skinning/SkinProvidingContainer.cs +++ b/osu.Game/Skinning/SkinProvidingContainer.cs @@ -46,8 +46,16 @@ namespace osu.Game.Skinning public ISkin FindProvider(Func lookupFunction) { - if (skin != null && lookupFunction(skin)) - return skin; + if (skin is ISkinSource source) + { + if (source.FindProvider(lookupFunction) is ISkin found) + return found; + } + else if (skin != null) + { + if (lookupFunction(skin)) + return skin; + } return fallbackSource?.FindProvider(lookupFunction); } From 6d56e02ddbe825599c3aaf1c09310c08858c8158 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Jun 2021 00:02:57 +0900 Subject: [PATCH 787/839] Add back incorrectly reverted animation handling logic This reverts commit b904fa6615ad210afc94e874a3861572e9bb0499. --- osu.Game/Skinning/LegacySkinExtensions.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacySkinExtensions.cs b/osu.Game/Skinning/LegacySkinExtensions.cs index d8fb1fa664..051bcf4a8e 100644 --- a/osu.Game/Skinning/LegacySkinExtensions.cs +++ b/osu.Game/Skinning/LegacySkinExtensions.cs @@ -54,9 +54,16 @@ namespace osu.Game.Skinning IEnumerable getTextures() { + ISkin lookupSource = null; + for (int i = 0; true; i++) { - if ((texture = source.GetTexture($"{componentName}{animationSeparator}{i}", wrapModeS, wrapModeT)) == null) + string frameName = $"{componentName}{animationSeparator}{i}"; + + // ensure all textures are retrieved from the same skin source. + lookupSource ??= (source as ISkinSource)?.FindProvider(s => s.GetTexture(frameName, wrapModeS, wrapModeT) != null) ?? source; + + if ((texture = lookupSource?.GetTexture(frameName, wrapModeS, wrapModeT)) == null) break; yield return texture; From 273d66a0e0a76ca45e5188601bd03a34e9804011 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Jun 2021 00:42:34 +0900 Subject: [PATCH 788/839] Fix `TaikoMascot` texture animation lookups --- osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs b/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs index 9c76aea54c..3706acbe23 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs @@ -85,8 +85,12 @@ namespace osu.Game.Rulesets.Taiko.UI } [BackgroundDependencyLoader] - private void load(ISkinSource skin) + private void load(ISkinSource source) { + ISkin skin = source.FindProvider(s => getAnimationFrame(s, state, 0) != null); + + if (skin == null) return; + for (int frameIndex = 0; true; frameIndex++) { var texture = getAnimationFrame(skin, state, frameIndex); @@ -112,8 +116,12 @@ namespace osu.Game.Rulesets.Taiko.UI } [BackgroundDependencyLoader] - private void load(ISkinSource skin) + private void load(ISkinSource source) { + ISkin skin = source.FindProvider(s => getAnimationFrame(s, TaikoMascotAnimationState.Clear, 0) != null); + + if (skin == null) return; + foreach (var frameIndex in clear_animation_sequence) { var texture = getAnimationFrame(skin, TaikoMascotAnimationState.Clear, frameIndex); From e7e9197f03d2247fb2dfbb599401dff62a8160ec Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Jun 2021 00:42:50 +0900 Subject: [PATCH 789/839] Fix `FindProvider` not correctly checking legacy default in `SkinManager` --- osu.Game/Skinning/SkinManager.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 9aa2d90064..ea4e1232c3 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -48,6 +48,8 @@ namespace osu.Game.Skinning protected override string ImportFromStablePath => "Skins"; + private readonly Skin defaultLegacySkin; + public SkinManager(Storage storage, DatabaseContextFactory contextFactory, GameHost host, IResourceStore resources, AudioManager audio) : base(storage, contextFactory, new SkinStore(contextFactory, storage), host) { @@ -55,6 +57,8 @@ namespace osu.Game.Skinning this.host = host; this.resources = resources; + defaultLegacySkin = new DefaultLegacySkin(this); + CurrentSkinInfo.ValueChanged += skin => CurrentSkin.Value = GetSkin(skin.NewValue); CurrentSkin.ValueChanged += skin => { @@ -212,9 +216,16 @@ namespace osu.Game.Skinning public IBindable GetConfig(TLookup lookup) => lookupWithFallback(s => s.GetConfig(lookup)); - public ISkin FindProvider(Func lookupFunction) => lookupFunction(CurrentSkin.Value) ? CurrentSkin.Value : null; + public ISkin FindProvider(Func lookupFunction) + { + if (lookupFunction(CurrentSkin.Value)) + return CurrentSkin.Value; - private Skin defaultLegacySkin; + if (CurrentSkin.Value is LegacySkin && lookupFunction(defaultLegacySkin)) + return defaultLegacySkin; + + return null; + } private T lookupWithFallback(Func func) where T : class @@ -224,8 +235,6 @@ namespace osu.Game.Skinning if (selectedSkin != null) return selectedSkin; - defaultLegacySkin ??= new DefaultLegacySkin(this); - if (CurrentSkin.Value is LegacySkin) return func(defaultLegacySkin); From 2c1f22d7ae914e84a6263d4ae7231d2f0f2a3bb7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Jun 2021 01:17:20 +0900 Subject: [PATCH 790/839] Refactor animation lookup to properly handle skins providing non-animated resources --- osu.Game/Skinning/LegacySkinExtensions.cs | 27 ++++++++++++++--------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/osu.Game/Skinning/LegacySkinExtensions.cs b/osu.Game/Skinning/LegacySkinExtensions.cs index 051bcf4a8e..ec25268be4 100644 --- a/osu.Game/Skinning/LegacySkinExtensions.cs +++ b/osu.Game/Skinning/LegacySkinExtensions.cs @@ -27,6 +27,18 @@ namespace osu.Game.Skinning { Texture texture; + // find the first source which provides either the animated or non-animated version. + ISkin skin = (source as ISkinSource)?.FindProvider(s => + { + if (animatable && s.GetTexture(getFrameName(0)) != null) + return true; + + return s.GetTexture(componentName, wrapModeS, wrapModeT) != null; + }) ?? source; + + if (skin == null) + return null; + if (animatable) { var textures = getTextures().ToArray(); @@ -35,7 +47,7 @@ namespace osu.Game.Skinning { var animation = new SkinnableTextureAnimation(startAtCurrentTime) { - DefaultFrameLength = frameLength ?? getFrameLength(source, applyConfigFrameRate, textures), + DefaultFrameLength = frameLength ?? getFrameLength(skin, applyConfigFrameRate, textures), Loop = looping, }; @@ -47,28 +59,23 @@ namespace osu.Game.Skinning } // if an animation was not allowed or not found, fall back to a sprite retrieval. - if ((texture = source.GetTexture(componentName, wrapModeS, wrapModeT)) != null) + if ((texture = skin.GetTexture(componentName, wrapModeS, wrapModeT)) != null) return new Sprite { Texture = texture }; return null; IEnumerable getTextures() { - ISkin lookupSource = null; - for (int i = 0; true; i++) { - string frameName = $"{componentName}{animationSeparator}{i}"; - - // ensure all textures are retrieved from the same skin source. - lookupSource ??= (source as ISkinSource)?.FindProvider(s => s.GetTexture(frameName, wrapModeS, wrapModeT) != null) ?? source; - - if ((texture = lookupSource?.GetTexture(frameName, wrapModeS, wrapModeT)) == null) + if ((texture = skin.GetTexture(getFrameName(i), wrapModeS, wrapModeT)) == null) break; yield return texture; } } + + string getFrameName(int frameIndex) => $"{componentName}{animationSeparator}{frameIndex}"; } public static bool HasFont(this ISkin source, LegacyFont font) From 06840d78cc4df29d7b5f8a58c0b7cdb44f83a9ec Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Jun 2021 12:04:29 +0900 Subject: [PATCH 791/839] Remove now unused method --- osu.Game/Skinning/DefaultLegacySkin.cs | 2 -- osu.Game/Skinning/LegacyBeatmapSkin.cs | 8 -------- osu.Game/Skinning/LegacySkin.cs | 3 --- 3 files changed, 13 deletions(-) diff --git a/osu.Game/Skinning/DefaultLegacySkin.cs b/osu.Game/Skinning/DefaultLegacySkin.cs index 1d17b5ce20..30192182f3 100644 --- a/osu.Game/Skinning/DefaultLegacySkin.cs +++ b/osu.Game/Skinning/DefaultLegacySkin.cs @@ -31,8 +31,6 @@ namespace osu.Game.Skinning Configuration.LegacyVersion = 2.7m; } - protected override DefaultLegacySkin CreateFallbackSkin(IResourceStore storage, IStorageResourceProvider resources) => null; - public static SkinInfo Info { get; } = new SkinInfo { ID = SkinInfo.CLASSIC_SKIN, // this is temporary until database storage is decided upon. diff --git a/osu.Game/Skinning/LegacyBeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs index 2374cb976b..caf37e5bc9 100644 --- a/osu.Game/Skinning/LegacyBeatmapSkin.cs +++ b/osu.Game/Skinning/LegacyBeatmapSkin.cs @@ -70,14 +70,6 @@ namespace osu.Game.Skinning return base.GetSample(sampleInfo); } - protected override DefaultLegacySkin CreateFallbackSkin(IResourceStore storage, IStorageResourceProvider resources) - { - // for simplicity, beatmap skins don't do lookups on the default skin. - // this will mean that fallback always occurs to the user (then default) skin. - // this may not offer perfect behaviour, but helps keep things simple. - return null; - } - private static SkinInfo createSkinInfo(BeatmapInfo beatmap) => new SkinInfo { Name = beatmap.ToString(), Creator = beatmap.Metadata?.AuthorString }; } diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 337acee9e8..e255fbae81 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -109,9 +109,6 @@ namespace osu.Game.Skinning true) != null); } - [CanBeNull] - protected virtual DefaultLegacySkin CreateFallbackSkin(IResourceStore storage, IStorageResourceProvider resources) => new DefaultLegacySkin(resources); - public override IBindable GetConfig(TLookup lookup) { switch (lookup) From 88b87b98a850b89b4e6f52e47791ba5c60da9f00 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Jun 2021 12:10:14 +0900 Subject: [PATCH 792/839] Fix slider ball layer sources --- osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBall.cs | 7 +++++-- .../Skinning/Legacy/OsuLegacySkinTransformer.cs | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBall.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBall.cs index 1a8c5ada1b..e4e1483665 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBall.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBall.cs @@ -14,18 +14,21 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { private readonly Drawable animationContent; + private readonly ISkin skin; + private Sprite layerNd; private Sprite layerSpec; - public LegacySliderBall(Drawable animationContent) + public LegacySliderBall(Drawable animationContent, ISkin skin) { this.animationContent = animationContent; + this.skin = skin; AutoSizeAxes = Axes.Both; } [BackgroundDependencyLoader] - private void load(ISkinSource skin) + private void load() { var ballColour = skin.GetConfig(OsuSkinColour.SliderBall)?.Value ?? Color4.White; diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs index e3f32fb76f..3267b48ebf 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs @@ -49,13 +49,16 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy return followCircle; case OsuSkinComponents.SliderBall: - var sliderBallContent = this.GetAnimation("sliderb", true, true, animationSeparator: ""); + // specular and nd layers must come from the same source as the ball texure. + var ballProvider = Source.FindProvider(s => s.GetTexture("sliderb") != null || s.GetTexture("sliderb0") != null); + + var sliderBallContent = ballProvider.GetAnimation("sliderb", true, true, animationSeparator: ""); // todo: slider ball has a custom frame delay based on velocity // Math.Max((150 / Velocity) * GameBase.SIXTY_FRAME_TIME, GameBase.SIXTY_FRAME_TIME); if (sliderBallContent != null) - return new LegacySliderBall(sliderBallContent); + return new LegacySliderBall(sliderBallContent, ballProvider); return null; From 27e3de3ea33a99abaf453a3eac542221be6c1e61 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Jun 2021 12:12:14 +0900 Subject: [PATCH 793/839] Add TODO about beatmap skin fallback support --- osu.Game/Skinning/SkinManager.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index ea4e1232c3..25cd909035 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -235,6 +235,9 @@ namespace osu.Game.Skinning if (selectedSkin != null) return selectedSkin; + // TODO: we also want to return a DefaultLegacySkin here if the current *beatmap* is providing any skinned elements. + // When attempting to address this, we may want to move the full DefaultLegacySkin fallback logic to within Player itself (to better allow + // for beatmap skin visibility). if (CurrentSkin.Value is LegacySkin) return func(defaultLegacySkin); From 7341e474f1405dde31bd1082cdf7d725d99c61ac Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 8 Jun 2021 14:25:39 +0900 Subject: [PATCH 794/839] Attempt to safeguard against collections database corruptions --- osu.Game/Collections/CollectionManager.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/osu.Game/Collections/CollectionManager.cs b/osu.Game/Collections/CollectionManager.cs index 086cc573d5..b53cc659f7 100644 --- a/osu.Game/Collections/CollectionManager.cs +++ b/osu.Game/Collections/CollectionManager.cs @@ -264,14 +264,18 @@ namespace osu.Game.Collections using (var sw = new SerializationWriter(storage.GetStream(database_name, FileAccess.Write))) { sw.Write(database_version); - sw.Write(Collections.Count); - foreach (var c in Collections) + var collectionsCopy = Collections.ToArray(); + sw.Write(collectionsCopy.Length); + + foreach (var c in collectionsCopy) { sw.Write(c.Name.Value); - sw.Write(c.Beatmaps.Count); - foreach (var b in c.Beatmaps) + var beatmapsCopy = c.Beatmaps.ToArray(); + sw.Write(beatmapsCopy.Length); + + foreach (var b in beatmapsCopy) sw.Write(b.MD5Hash); } } From f3f634e969607be08ba6e592d4b7454a2e53692f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Jun 2021 15:05:14 +0900 Subject: [PATCH 795/839] Clean up previous sample immediately on skin source change to avoid `Play` after disposal This seems to be the simplest way to avoid calls to `Play` after the underlying sample may have been disposed. As per the issue thread, a local workaround is acceptable here. Closes #13223. --- osu.Game/Skinning/PoolableSkinnableSample.cs | 44 +++++++++++++++++--- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/osu.Game/Skinning/PoolableSkinnableSample.cs b/osu.Game/Skinning/PoolableSkinnableSample.cs index b04158a58f..7565417b7f 100644 --- a/osu.Game/Skinning/PoolableSkinnableSample.cs +++ b/osu.Game/Skinning/PoolableSkinnableSample.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -70,22 +71,48 @@ 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, bool allowFallback) { base.SkinChanged(skin, allowFallback); 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(); - Sample = null; - var sample = CurrentSkin.GetSample(sampleInfo); if (sample == null && AllowDefaultFallback) @@ -155,6 +182,13 @@ namespace osu.Game.Skinning } } + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + CurrentSkin.SourceChanged -= skinChangedImmediate; + } + #region Re-expose AudioContainer public BindableNumber Volume => sampleContainer.Volume; From e388a896e87e6ad03701bedd286a751b9f965408 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Jun 2021 16:02:25 +0900 Subject: [PATCH 796/839] Don't apply visibility increase to first object in osu!catch The goal of the visibility increase is to help in cases where timing is an issue (by showing the approach circle etc.). This doesn't need to apply to catch. @smoogipoo interested as to whether you agree with this one. Visually it looks better to me but it does change the behaviour for only osu!catch, so I'm not 100% confident on it. Closes #13367. --- osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs | 8 -------- osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs | 3 +-- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs index 1248409b2a..09362929d2 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs @@ -4,10 +4,8 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; -using osu.Framework.Allocation; using osu.Framework.Testing; using osu.Game.Beatmaps; -using osu.Game.Configuration; using osu.Game.Rulesets.Catch.Mods; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects.Drawables; @@ -21,12 +19,6 @@ namespace osu.Game.Rulesets.Catch.Tests { public class TestSceneCatchModHidden : ModTestScene { - [BackgroundDependencyLoader] - private void load() - { - LocalConfig.SetValue(OsuSetting.IncreaseFirstObjectVisibility, false); - } - [Test] public void TestJuiceStream() { diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs b/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs index 7bad4c79cb..f9e106f097 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs @@ -29,8 +29,7 @@ namespace osu.Game.Rulesets.Catch.Mods } protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) - { - } + => ApplyNormalVisibilityState(hitObject, state); protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) { From 67135ce3dbaa2ea8a55abba016fd525d16f1ad2a Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 8 Jun 2021 16:15:17 +0900 Subject: [PATCH 797/839] Add null check --- osu.Game/Skinning/PoolableSkinnableSample.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/PoolableSkinnableSample.cs b/osu.Game/Skinning/PoolableSkinnableSample.cs index 7565417b7f..9acc1f1a9d 100644 --- a/osu.Game/Skinning/PoolableSkinnableSample.cs +++ b/osu.Game/Skinning/PoolableSkinnableSample.cs @@ -186,7 +186,8 @@ namespace osu.Game.Skinning { base.Dispose(isDisposing); - CurrentSkin.SourceChanged -= skinChangedImmediate; + if (CurrentSkin != null) + CurrentSkin.SourceChanged -= skinChangedImmediate; } #region Re-expose AudioContainer From 89895f6ce40ab080b0d5cce81763b3a63310ecf3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Jun 2021 16:24:00 +0900 Subject: [PATCH 798/839] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 1216b1772f..395470824f 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 21a890014a..9ecab1ee48 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -34,7 +34,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index bf080e4def..e66f125985 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 7fa0ac6ed720b32bcb3c52b05f3cf8352c69927a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 8 Jun 2021 17:03:46 +0900 Subject: [PATCH 799/839] Fix possible nullref when exiting song select too fast --- .../Visual/Navigation/TestSceneScreenNavigation.cs | 8 ++++++++ osu.Game/Screens/Select/SongSelect.cs | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index 0308d74aa4..3694d3afce 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -152,6 +152,14 @@ namespace osu.Game.Tests.Visual.Navigation AddUntilStep("wait for track", () => !Game.MusicController.CurrentTrack.IsDummyDevice && Game.MusicController.IsPlaying); } + [Test] + public void TestPushSongSelectAndPressBackButtonImmediately() + { + AddStep("push song select", () => Game.ScreenStack.Push(new TestPlaySongSelect())); + AddStep("press back button", () => Game.ChildrenOfType().First().Action()); + AddWaitStep("wait two frame", 2); + } + [Test] public void TestExitSongSelectWithClick() { diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 270addc8e6..c697c64c3f 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -665,7 +665,7 @@ namespace osu.Game.Screens.Select public override bool OnBackButton() { - if (ModSelect.State.Value == Visibility.Visible) + if (ModSelect?.State.Value == Visibility.Visible) { ModSelect.Hide(); return true; From 490ab9e96a1fed83ed169d9084b095e6af1e9e96 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 8 Jun 2021 17:09:03 +0900 Subject: [PATCH 800/839] Fix typo --- osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index 3694d3afce..6fc19b95b9 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -157,7 +157,7 @@ namespace osu.Game.Tests.Visual.Navigation { AddStep("push song select", () => Game.ScreenStack.Push(new TestPlaySongSelect())); AddStep("press back button", () => Game.ChildrenOfType().First().Action()); - AddWaitStep("wait two frame", 2); + AddWaitStep("wait two frames", 2); } [Test] From 860f1aebb385ab9bccdad07848a72a418f500914 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 8 Jun 2021 17:38:12 +0900 Subject: [PATCH 801/839] Only call OnBackButton() if the screen has finished loading --- osu.Game/OsuGame.cs | 5 +++-- osu.Game/Screens/IOsuScreen.cs | 3 +++ osu.Game/Screens/Select/SongSelect.cs | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index c51624341e..8d87baa363 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -651,9 +651,10 @@ namespace osu.Game Origin = Anchor.BottomLeft, Action = () => { - var currentScreen = ScreenStack.CurrentScreen as IOsuScreen; + if (!(ScreenStack.CurrentScreen is IOsuScreen currentScreen)) + return; - if (currentScreen?.AllowBackButton == true && !currentScreen.OnBackButton()) + if (!((Drawable)currentScreen).IsLoaded || currentScreen.AllowBackButton && !currentScreen.OnBackButton()) ScreenStack.Exit(); } }, diff --git a/osu.Game/Screens/IOsuScreen.cs b/osu.Game/Screens/IOsuScreen.cs index cc8778d9ae..0434135547 100644 --- a/osu.Game/Screens/IOsuScreen.cs +++ b/osu.Game/Screens/IOsuScreen.cs @@ -67,8 +67,11 @@ namespace osu.Game.Screens /// Invoked when the back button has been pressed to close any overlays before exiting this . /// /// + /// If this has not yet finished loading, the exit will occur immediately without this method being invoked. + /// /// Return true to block this from being exited after closing an overlay. /// Return false if this should continue exiting. + /// /// bool OnBackButton(); } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index c697c64c3f..270addc8e6 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -665,7 +665,7 @@ namespace osu.Game.Screens.Select public override bool OnBackButton() { - if (ModSelect?.State.Value == Visibility.Visible) + if (ModSelect.State.Value == Visibility.Visible) { ModSelect.Hide(); return true; From ab9290772b46ac2087eeb9f9e5fdfaf848fa03a5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 8 Jun 2021 17:54:54 +0900 Subject: [PATCH 802/839] Fix a similar case with online play sub-screens --- .../Navigation/TestSceneScreenNavigation.cs | 28 +++++++++++++++++++ .../Screens/OnlinePlay/OnlinePlayScreen.cs | 5 +++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index 6fc19b95b9..52401d32e5 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -9,16 +9,19 @@ using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; +using osu.Game.Online.Multiplayer; using osu.Game.Overlays; using osu.Game.Overlays.Mods; using osu.Game.Overlays.Toolbar; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.Play; using osu.Game.Screens.Ranking; using osu.Game.Screens.Select; using osu.Game.Screens.Select.Options; using osu.Game.Tests.Beatmaps.IO; +using osu.Game.Tests.Visual.Multiplayer; using osuTK; using osuTK.Input; @@ -306,6 +309,18 @@ namespace osu.Game.Tests.Visual.Navigation AddAssert("Toolbar is hidden", () => Game.Toolbar.State.Value == Visibility.Hidden); } + [Test] + public void TestPushMatchSubScreenAndPressBackButtonImmediately() + { + TestMultiplayer multiplayer = null; + + PushAndConfirm(() => multiplayer = new TestMultiplayer()); + + AddStep("open room", () => multiplayer.OpenNewRoom()); + AddStep("press back button", () => Game.ChildrenOfType().First().Action()); + AddWaitStep("wait two frames", 2); + } + private void pushEscape() => AddStep("Press escape", () => InputManager.Key(Key.Escape)); @@ -330,5 +345,18 @@ namespace osu.Game.Tests.Visual.Navigation protected override bool DisplayStableImportPrompt => false; } + + private class TestMultiplayer : Screens.OnlinePlay.Multiplayer.Multiplayer + { + [Cached(typeof(MultiplayerClient))] + public readonly TestMultiplayerClient Client; + + public TestMultiplayer() + { + Client = new TestMultiplayerClient((TestMultiplayerRoomManager)RoomManager); + } + + protected override RoomManager CreateRoomManager() => new TestMultiplayerRoomManager(); + } } } diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index 90e499c67f..e418d36d40 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -253,7 +253,10 @@ namespace osu.Game.Screens.OnlinePlay public override bool OnBackButton() { - if ((screenStack.CurrentScreen as IOnlinePlaySubScreen)?.OnBackButton() == true) + if (!(screenStack.CurrentScreen is IOnlinePlaySubScreen onlineSubScreen)) + return false; + + if (((Drawable)onlineSubScreen).IsLoaded && onlineSubScreen.AllowBackButton && onlineSubScreen.OnBackButton()) return true; if (screenStack.CurrentScreen != null && !(screenStack.CurrentScreen is LoungeSubScreen)) From 4d9fffc01bbcc2863386f0d32f38840cb0077c91 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Jun 2021 17:58:57 +0900 Subject: [PATCH 803/839] Update score encoder version to be higher than any existing stable version --- osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs index f8dd6953ad..cd77fbbdd8 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs @@ -15,7 +15,10 @@ namespace osu.Game.Scoring.Legacy { public class LegacyScoreEncoder { - public const int LATEST_VERSION = 128; + /// + /// Database version in stable-compatible YYYYMMDD format. + /// + public const int LATEST_VERSION = 30000000; private readonly Score score; private readonly IBeatmap beatmap; From 061e3d7f26c22df9da73b81a07cb9c75835a2281 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Jun 2021 18:00:09 +0900 Subject: [PATCH 804/839] Move legacy `ScoreInfo` to be completely based on presence of classic mod --- .../Requests/Responses/APILegacyScoreInfo.cs | 8 +++++-- osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs | 4 ++++ osu.Game/Scoring/ScoreInfo.cs | 23 +------------------ 3 files changed, 11 insertions(+), 24 deletions(-) diff --git a/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs b/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs index 3d3c07a5ad..1b394185fd 100644 --- a/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs +++ b/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs @@ -21,7 +21,12 @@ namespace osu.Game.Online.API.Requests.Responses { var ruleset = rulesets.GetRuleset(OnlineRulesetID); - var mods = Mods != null ? ruleset.CreateInstance().GetAllMods().Where(mod => Mods.Contains(mod.Acronym)).ToArray() : Array.Empty(); + var rulesetInstance = ruleset.CreateInstance(); + + var mods = Mods != null ? rulesetInstance.GetAllMods().Where(mod => Mods.Contains(mod.Acronym)).ToArray() : Array.Empty(); + + // all API scores provided by this class are considered to be legacy. + mods = mods.Append(rulesetInstance.GetAllMods().OfType().Single()).ToArray(); var scoreInfo = new ScoreInfo { @@ -38,7 +43,6 @@ namespace osu.Game.Online.API.Requests.Responses Rank = Rank, Ruleset = ruleset, Mods = mods, - IsLegacyScore = true }; if (Statistics != null) diff --git a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs index 97cb5ca7ab..4b93530e45 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs @@ -65,6 +65,10 @@ namespace osu.Game.Scoring.Legacy scoreInfo.Mods = currentRuleset.ConvertFromLegacyMods((LegacyMods)sr.ReadInt32()).ToArray(); + // lazer replays get a really high version number. + if (version < 30000000) + scoreInfo.Mods = scoreInfo.Mods.Append(currentRuleset.GetAllMods().OfType().Single()).ToArray(); + currentBeatmap = workingBeatmap.GetPlayableBeatmap(currentRuleset.RulesetInfo, scoreInfo.Mods); scoreInfo.Beatmap = currentBeatmap.BeatmapInfo; diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index a6faaf6379..801175d90d 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -201,33 +201,12 @@ namespace osu.Game.Scoring [JsonProperty("position")] public int? Position { get; set; } - private bool isLegacyScore; - /// /// Whether this represents a legacy (osu!stable) score. /// [JsonIgnore] [NotMapped] - public bool IsLegacyScore - { - get - { - if (isLegacyScore) - return true; - - // The above check will catch legacy online scores that have an appropriate UserString + UserId. - // For non-online scores such as those imported in, a heuristic is used based on the following table: - // - // Mode | UserString | UserId - // --------------- | ---------- | --------- - // stable | | 1 - // lazer | | - // lazer (offline) | Guest | 1 - - return ID > 0 && UserID == 1 && UserString != "Guest"; - } - set => isLegacyScore = value; - } + public bool IsLegacyScore => mods.OfType().Any(); public IEnumerable GetStatisticsForDisplay() { From b287366c8bed5e6f51e98409c6eb3cc6a41150e4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Jun 2021 18:09:57 +0900 Subject: [PATCH 805/839] Remove forgotten classic mod addition --- osu.Game/Scoring/ScoreInfo.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 801175d90d..755dab17f9 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -76,9 +76,6 @@ namespace osu.Game.Scoring else if (localAPIMods != null) scoreMods = apiMods.Select(m => m.ToMod(rulesetInstance)).ToArray(); - if (IsLegacyScore) - scoreMods = scoreMods.Append(rulesetInstance.GetAllMods().OfType().Single()).ToArray(); - return scoreMods; } set From d31e3e8f1c5d1f41a620f8f9e582a5553d13c1b5 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 8 Jun 2021 18:23:03 +0900 Subject: [PATCH 806/839] Fix nullref --- 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 755dab17f9..3944c1d3de 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -203,7 +203,7 @@ namespace osu.Game.Scoring /// [JsonIgnore] [NotMapped] - public bool IsLegacyScore => mods.OfType().Any(); + public bool IsLegacyScore => Mods.OfType().Any(); public IEnumerable GetStatisticsForDisplay() { From 4ee7721c51ae5300f80a71f0f58de993530c2211 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Jun 2021 18:38:47 +0900 Subject: [PATCH 807/839] Extract first version out to constant --- osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs | 2 +- osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs index 4b93530e45..2f17167297 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs @@ -66,7 +66,7 @@ namespace osu.Game.Scoring.Legacy scoreInfo.Mods = currentRuleset.ConvertFromLegacyMods((LegacyMods)sr.ReadInt32()).ToArray(); // lazer replays get a really high version number. - if (version < 30000000) + if (version < LegacyScoreEncoder.FIRST_LAZER_VERSION) scoreInfo.Mods = scoreInfo.Mods.Append(currentRuleset.GetAllMods().OfType().Single()).ToArray(); currentBeatmap = workingBeatmap.GetPlayableBeatmap(currentRuleset.RulesetInfo, scoreInfo.Mods); diff --git a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs index cd77fbbdd8..288552879c 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs @@ -17,8 +17,14 @@ namespace osu.Game.Scoring.Legacy { /// /// Database version in stable-compatible YYYYMMDD format. + /// Should be incremented if any changes are made to the format/usage. /// - public const int LATEST_VERSION = 30000000; + public const int LATEST_VERSION = FIRST_LAZER_VERSION; + + /// + /// The first stable-compatible YYYYMMDD format version given to lazer usage of replays. + /// + public const int FIRST_LAZER_VERSION = 30000000; private readonly Score score; private readonly IBeatmap beatmap; From 0b9916b266a9be0eb888cb0484ea2d05216ed71d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 8 Jun 2021 18:39:52 +0900 Subject: [PATCH 808/839] Add parens to declare operator precedence --- osu.Game/OsuGame.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 8d87baa363..2dca91cbf3 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -654,7 +654,7 @@ namespace osu.Game if (!(ScreenStack.CurrentScreen is IOsuScreen currentScreen)) return; - if (!((Drawable)currentScreen).IsLoaded || currentScreen.AllowBackButton && !currentScreen.OnBackButton()) + if (!((Drawable)currentScreen).IsLoaded || (currentScreen.AllowBackButton && !currentScreen.OnBackButton())) ScreenStack.Exit(); } }, From c3ea1b26e1b519bb876b13841e9557ced5ed2e5e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 8 Jun 2021 22:48:07 +0900 Subject: [PATCH 809/839] Fix DT being doubled in multiplayer spectator --- .../OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs index 277aa5d772..983daac909 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs @@ -22,6 +22,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate // Isolates beatmap/ruleset to this screen. public override bool DisallowExternalBeatmapRulesetChanges => true; + // We are managing our own adjustments. For now, this happens inside the Player instances themselves. + public override bool AllowRateAdjustments => false; + /// /// Whether all spectating players have finished loading. /// From c8e14d771033917af1b42339af7bdba0647e3fb1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 8 Jun 2021 23:09:18 +0900 Subject: [PATCH 810/839] Ignore non-scorable and bonus judgements --- .../Visual/Gameplay/TestSceneHitErrorMeter.cs | 28 +++++++++++++++++++ .../HUD/HitErrorMeters/BarHitErrorMeter.cs | 3 ++ .../HUD/HitErrorMeters/ColourHitErrorMeter.cs | 9 +++++- 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs index 2a12577ad8..7accaef818 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs @@ -109,6 +109,34 @@ namespace osu.Game.Tests.Visual.Gameplay meter => meter.ChildrenOfType().Count() == 2)); } + [Test] + public void TestBonus() + { + AddStep("OD 1", () => recreateDisplay(new OsuHitWindows(), 1)); + + AddStep("small bonus", () => newJudgement(result: HitResult.SmallBonus)); + AddAssert("no bars added", () => !this.ChildrenOfType().Any()); + AddAssert("no circle added", () => !this.ChildrenOfType().Any()); + + AddStep("large bonus", () => newJudgement(result: HitResult.LargeBonus)); + AddAssert("no bars added", () => !this.ChildrenOfType().Any()); + AddAssert("no circle added", () => !this.ChildrenOfType().Any()); + } + + [Test] + public void TestIgnore() + { + AddStep("OD 1", () => recreateDisplay(new OsuHitWindows(), 1)); + + AddStep("ignore hit", () => newJudgement(result: HitResult.IgnoreHit)); + AddAssert("no bars added", () => !this.ChildrenOfType().Any()); + AddAssert("no circle added", () => !this.ChildrenOfType().Any()); + + AddStep("ignore miss", () => newJudgement(result: HitResult.IgnoreMiss)); + AddAssert("no bars added", () => !this.ChildrenOfType().Any()); + AddAssert("no circle added", () => !this.ChildrenOfType().Any()); + } + private void recreateDisplay(HitWindows hitWindows, float overallDifficulty) { hitWindows?.SetDifficulty(overallDifficulty); diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs index 0412085d1d..89f61785e8 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs @@ -217,6 +217,9 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters if (!judgement.IsHit || judgement.HitObject.HitWindows?.WindowFor(HitResult.Miss) == 0) return; + if (!judgement.Type.IsScorable() || judgement.Type.IsBonus()) + return; + if (judgementsContainer.Count > max_concurrent_judgements) { const double quick_fade_time = 100; diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs index 86c0de8855..dda2a6da95 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Scoring; using osuTK; using osuTK.Graphics; @@ -24,7 +25,13 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters InternalChild = judgementsFlow = new JudgementFlow(); } - protected override void OnNewJudgement(JudgementResult judgement) => judgementsFlow.Push(GetColourForHitResult(judgement.Type)); + protected override void OnNewJudgement(JudgementResult judgement) + { + if (!judgement.Type.IsScorable() || judgement.Type.IsBonus()) + return; + + judgementsFlow.Push(GetColourForHitResult(judgement.Type)); + } private class JudgementFlow : FillFlowContainer { From 00efed2c39d67d0d5f4c5b0d21509ebea2fef205 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 8 Jun 2021 23:10:21 +0900 Subject: [PATCH 811/839] Add colours for tick judgements --- osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs index 17a6e772fb..9844b9f10d 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs @@ -41,6 +41,8 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters { switch (result) { + case HitResult.SmallTickMiss: + case HitResult.LargeTickMiss: case HitResult.Miss: return colours.Red; @@ -53,6 +55,8 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters case HitResult.Good: return colours.GreenLight; + case HitResult.SmallTickHit: + case HitResult.LargeTickHit: case HitResult.Great: return colours.Blue; From 88266eac63b54be330837c4b2c77bca522012aa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 8 Jun 2021 16:33:56 +0200 Subject: [PATCH 812/839] Add option to fix label width of a `LabelledDrawable` --- .../TestSceneLabelledDrawable.cs | 42 +++++++++++++++ .../UserInterfaceV2/LabelledDrawable.cs | 51 +++++++++++++++++-- 2 files changed, 90 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledDrawable.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledDrawable.cs index 8179f92ffc..fe312ccc8f 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledDrawable.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledDrawable.cs @@ -2,11 +2,14 @@ // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Testing; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterfaceV2; +using osuTK; using osuTK.Graphics; namespace osu.Game.Tests.Visual.UserInterface @@ -21,6 +24,45 @@ namespace osu.Game.Tests.Visual.UserInterface [TestCase(true)] public void TestNonPadded(bool hasDescription) => createPaddedComponent(hasDescription, false); + [Test] + public void TestFixedWidth() + { + const float label_width = 200; + + AddStep("create components", () => Child = new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 10), + Children = new Drawable[] + { + new NonPaddedLabelledDrawable + { + Label = "short", + FixedLabelWidth = label_width + }, + new NonPaddedLabelledDrawable + { + Label = "very very very very very very very very very very very long", + FixedLabelWidth = label_width + }, + new PaddedLabelledDrawable + { + Label = "short", + FixedLabelWidth = label_width + }, + new PaddedLabelledDrawable + { + Label = "very very very very very very very very very very very long", + FixedLabelWidth = label_width + } + } + }); + + AddStep("unset label width", () => this.ChildrenOfType>().ForEach(d => d.FixedLabelWidth = null)); + AddStep("reset label width", () => this.ChildrenOfType>().ForEach(d => d.FixedLabelWidth = label_width)); + } + private void createPaddedComponent(bool hasDescription = false, bool padded = true) { AddStep("create component", () => diff --git a/osu.Game/Graphics/UserInterfaceV2/LabelledDrawable.cs b/osu.Game/Graphics/UserInterfaceV2/LabelledDrawable.cs index ec68223a3d..5a697623c9 100644 --- a/osu.Game/Graphics/UserInterfaceV2/LabelledDrawable.cs +++ b/osu.Game/Graphics/UserInterfaceV2/LabelledDrawable.cs @@ -14,6 +14,27 @@ namespace osu.Game.Graphics.UserInterfaceV2 public abstract class LabelledDrawable : CompositeDrawable where T : Drawable { + private float? fixedLabelWidth; + + /// + /// The fixed width of the label of this . + /// If null, the label portion will auto-size to its content. + /// Can be used in layout scenarios where several labels must match in length for the components to be aligned properly. + /// + public float? FixedLabelWidth + { + get => fixedLabelWidth; + set + { + if (fixedLabelWidth == value) + return; + + fixedLabelWidth = value; + + updateLabelWidth(); + } + } + protected const float CONTENT_PADDING_VERTICAL = 10; protected const float CONTENT_PADDING_HORIZONTAL = 15; protected const float CORNER_RADIUS = 15; @@ -23,6 +44,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 /// protected readonly T Component; + private readonly GridContainer grid; private readonly OsuTextFlowContainer labelText; private readonly OsuTextFlowContainer descriptionText; @@ -56,7 +78,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 Spacing = new Vector2(0, 12), Children = new Drawable[] { - new GridContainer + grid = new GridContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, @@ -69,7 +91,13 @@ namespace osu.Game.Graphics.UserInterfaceV2 Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, AutoSizeAxes = Axes.Both, - Padding = new MarginPadding { Right = 20 } + Padding = new MarginPadding + { + Right = 20, + // ensure that the label is always vertically padded even if the component itself isn't. + // this may become an issue if the label is taller than the component. + Vertical = padded ? 0 : CONTENT_PADDING_VERTICAL + } }, new Container { @@ -87,7 +115,6 @@ namespace osu.Game.Graphics.UserInterfaceV2 }, }, RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) }, - ColumnDimensions = new[] { new Dimension(GridSizeMode.AutoSize) } }, descriptionText = new OsuTextFlowContainer(s => s.Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold, italics: true)) { @@ -99,6 +126,24 @@ namespace osu.Game.Graphics.UserInterfaceV2 } } }; + + updateLabelWidth(); + } + + private void updateLabelWidth() + { + if (fixedLabelWidth == null) + { + grid.ColumnDimensions = new[] { new Dimension(GridSizeMode.AutoSize) }; + labelText.RelativeSizeAxes = Axes.None; + labelText.AutoSizeAxes = Axes.Both; + } + else + { + grid.ColumnDimensions = new[] { new Dimension(GridSizeMode.Absolute, fixedLabelWidth.Value) }; + labelText.AutoSizeAxes = Axes.Y; + labelText.RelativeSizeAxes = Axes.X; + } } [BackgroundDependencyLoader] From 410cb16340fc7212070a456ff7412cfb9a2204af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 8 Jun 2021 17:14:26 +0200 Subject: [PATCH 813/839] Apply fixed label width to setup screen items --- osu.Game/Screens/Edit/Setup/ColoursSection.cs | 1 + osu.Game/Screens/Edit/Setup/DifficultySection.cs | 4 ++++ osu.Game/Screens/Edit/Setup/MetadataSection.cs | 4 ++++ osu.Game/Screens/Edit/Setup/ResourcesSection.cs | 2 ++ osu.Game/Screens/Edit/Setup/SetupSection.cs | 6 ++++++ 5 files changed, 17 insertions(+) diff --git a/osu.Game/Screens/Edit/Setup/ColoursSection.cs b/osu.Game/Screens/Edit/Setup/ColoursSection.cs index cb7deadcb7..4a81959a54 100644 --- a/osu.Game/Screens/Edit/Setup/ColoursSection.cs +++ b/osu.Game/Screens/Edit/Setup/ColoursSection.cs @@ -25,6 +25,7 @@ namespace osu.Game.Screens.Edit.Setup comboColours = new LabelledColourPalette { Label = "Hitcircle / Slider Combos", + FixedLabelWidth = LABEL_WIDTH, ColourNamePrefix = "Combo" } }; diff --git a/osu.Game/Screens/Edit/Setup/DifficultySection.cs b/osu.Game/Screens/Edit/Setup/DifficultySection.cs index 493d3ed20c..a8800d524f 100644 --- a/osu.Game/Screens/Edit/Setup/DifficultySection.cs +++ b/osu.Game/Screens/Edit/Setup/DifficultySection.cs @@ -28,6 +28,7 @@ namespace osu.Game.Screens.Edit.Setup circleSizeSlider = new LabelledSliderBar { Label = "Object Size", + FixedLabelWidth = LABEL_WIDTH, Description = "The size of all hit objects", Current = new BindableFloat(Beatmap.BeatmapInfo.BaseDifficulty.CircleSize) { @@ -40,6 +41,7 @@ namespace osu.Game.Screens.Edit.Setup healthDrainSlider = new LabelledSliderBar { Label = "Health Drain", + FixedLabelWidth = LABEL_WIDTH, Description = "The rate of passive health drain throughout playable time", Current = new BindableFloat(Beatmap.BeatmapInfo.BaseDifficulty.DrainRate) { @@ -52,6 +54,7 @@ namespace osu.Game.Screens.Edit.Setup approachRateSlider = new LabelledSliderBar { Label = "Approach Rate", + FixedLabelWidth = LABEL_WIDTH, Description = "The speed at which objects are presented to the player", Current = new BindableFloat(Beatmap.BeatmapInfo.BaseDifficulty.ApproachRate) { @@ -64,6 +67,7 @@ namespace osu.Game.Screens.Edit.Setup overallDifficultySlider = new LabelledSliderBar { Label = "Overall Difficulty", + FixedLabelWidth = LABEL_WIDTH, Description = "The harshness of hit windows and difficulty of special objects (ie. spinners)", Current = new BindableFloat(Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty) { diff --git a/osu.Game/Screens/Edit/Setup/MetadataSection.cs b/osu.Game/Screens/Edit/Setup/MetadataSection.cs index 889a5eab5e..c79888b9d1 100644 --- a/osu.Game/Screens/Edit/Setup/MetadataSection.cs +++ b/osu.Game/Screens/Edit/Setup/MetadataSection.cs @@ -27,24 +27,28 @@ namespace osu.Game.Screens.Edit.Setup artistTextBox = new LabelledTextBox { Label = "Artist", + FixedLabelWidth = LABEL_WIDTH, Current = { Value = Beatmap.Metadata.Artist }, TabbableContentContainer = this }, titleTextBox = new LabelledTextBox { Label = "Title", + FixedLabelWidth = LABEL_WIDTH, Current = { Value = Beatmap.Metadata.Title }, TabbableContentContainer = this }, creatorTextBox = new LabelledTextBox { Label = "Creator", + FixedLabelWidth = LABEL_WIDTH, Current = { Value = Beatmap.Metadata.AuthorString }, TabbableContentContainer = this }, difficultyTextBox = new LabelledTextBox { Label = "Difficulty Name", + FixedLabelWidth = LABEL_WIDTH, Current = { Value = Beatmap.BeatmapInfo.Version }, TabbableContentContainer = this }, diff --git a/osu.Game/Screens/Edit/Setup/ResourcesSection.cs b/osu.Game/Screens/Edit/Setup/ResourcesSection.cs index 12270f2aa4..ba22c82ecc 100644 --- a/osu.Game/Screens/Edit/Setup/ResourcesSection.cs +++ b/osu.Game/Screens/Edit/Setup/ResourcesSection.cs @@ -54,6 +54,7 @@ namespace osu.Game.Screens.Edit.Setup backgroundTextBox = new FileChooserLabelledTextBox(".jpg", ".jpeg", ".png") { Label = "Background", + FixedLabelWidth = LABEL_WIDTH, PlaceholderText = "Click to select a background image", Current = { Value = working.Value.Metadata.BackgroundFile }, Target = backgroundFileChooserContainer, @@ -72,6 +73,7 @@ namespace osu.Game.Screens.Edit.Setup audioTrackTextBox = new FileChooserLabelledTextBox(".mp3", ".ogg") { Label = "Audio Track", + FixedLabelWidth = LABEL_WIDTH, PlaceholderText = "Click to select a track", Current = { Value = working.Value.Metadata.AudioFile }, Target = audioTrackFileChooserContainer, diff --git a/osu.Game/Screens/Edit/Setup/SetupSection.cs b/osu.Game/Screens/Edit/Setup/SetupSection.cs index 8964e651df..560e6fff67 100644 --- a/osu.Game/Screens/Edit/Setup/SetupSection.cs +++ b/osu.Game/Screens/Edit/Setup/SetupSection.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterfaceV2; using osuTK; namespace osu.Game.Screens.Edit.Setup @@ -15,6 +16,11 @@ namespace osu.Game.Screens.Edit.Setup { private readonly FillFlowContainer flow; + /// + /// Used to align some of the child s together to achieve a grid-like look. + /// + protected const float LABEL_WIDTH = 160; + [Resolved] protected OsuColour Colours { get; private set; } From 5bf4dd63588324b8b8cc409d2940cfe43223c698 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 8 Jun 2021 21:57:08 +0200 Subject: [PATCH 814/839] Move skin background to separate file --- .../Graphics/Backgrounds/SkinBackground.cs | 25 +++++++++++++++++++ .../Backgrounds/BackgroundScreenDefault.cs | 17 ------------- 2 files changed, 25 insertions(+), 17 deletions(-) create mode 100644 osu.Game/Graphics/Backgrounds/SkinBackground.cs diff --git a/osu.Game/Graphics/Backgrounds/SkinBackground.cs b/osu.Game/Graphics/Backgrounds/SkinBackground.cs new file mode 100644 index 0000000000..8be017dc91 --- /dev/null +++ b/osu.Game/Graphics/Backgrounds/SkinBackground.cs @@ -0,0 +1,25 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Game.Skinning; + +namespace osu.Game.Graphics.Backgrounds +{ + internal class SkinBackground : Background + { + private readonly Skin skin; + + public SkinBackground(Skin skin, string fallbackTextureName) + : base(fallbackTextureName) + { + this.skin = skin; + } + + [BackgroundDependencyLoader] + private void load() + { + Sprite.Texture = skin.GetTexture("menu-background") ?? Sprite.Texture; + } + } +} diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs index 6bcfaac907..abfbb64f61 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs @@ -140,22 +140,5 @@ namespace osu.Game.Screens.Backgrounds return $@"Menu/menu-background-{currentDisplay % background_count + 1}"; } } - - private class SkinnedBackground : Background - { - private readonly Skin skin; - - public SkinnedBackground(Skin skin, string fallbackTextureName) - : base(fallbackTextureName) - { - this.skin = skin; - } - - [BackgroundDependencyLoader] - private void load() - { - Sprite.Texture = skin.GetTexture("menu-background") ?? Sprite.Texture; - } - } } } From d86ace4d11b10ce6af7ed61527007ea8c53c55dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 8 Jun 2021 21:58:44 +0200 Subject: [PATCH 815/839] Add test coverage for skin background source --- .../Visual/Background/TestSceneBackgroundScreenDefault.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs index 09fe9b3767..c574d89ad9 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs @@ -35,7 +35,7 @@ namespace osu.Game.Tests.Visual.Background } [Test] - public void TestTogglingStoryboardSwitchesBackgroundType() + public void TestBackgroundTypeSwitch() { setSupporter(true); @@ -44,6 +44,9 @@ namespace osu.Game.Tests.Visual.Background setSourceMode(BackgroundSource.BeatmapWithStoryboard); AddUntilStep("is storyboard background", () => getCurrentBackground() is BeatmapBackgroundWithStoryboard); + + setSourceMode(BackgroundSource.Skin); + AddUntilStep("is skin background", () => getCurrentBackground() is SkinBackground); } [Test] @@ -78,7 +81,7 @@ namespace osu.Game.Tests.Visual.Background } private void setSourceMode(BackgroundSource source) => - AddStep("set background mode to beatmap", () => config.SetValue(OsuSetting.MenuBackgroundSource, source)); + AddStep($"set background mode to {source}", () => config.SetValue(OsuSetting.MenuBackgroundSource, source)); private void setSupporter(bool isSupporter) => AddStep($"set supporter {isSupporter}", () => ((DummyAPIAccess)API).LocalUser.Value = new User From a98c302211d61d3139f0e981ebeb52e0c6680f64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 8 Jun 2021 22:04:59 +0200 Subject: [PATCH 816/839] Bring back skin background source --- osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs index abfbb64f61..46574ff8c8 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs @@ -120,6 +120,10 @@ namespace osu.Game.Screens.Backgrounds break; } + + case BackgroundSource.Skin: + newBackground = new SkinBackground(skin.Value, getBackgroundTextureName()); + break; } } From f628ec25ef9b80b1b88f5d380f4322328bbdafd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 8 Jun 2021 22:25:29 +0200 Subject: [PATCH 817/839] Add test coverage for keeping same background instance --- .../Background/TestSceneBackgroundScreenDefault.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs index c574d89ad9..135998695b 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; @@ -64,15 +65,17 @@ namespace osu.Game.Tests.Visual.Background AddUntilStep("is beatmap background", () => getCurrentBackground() is BeatmapBackground); } - [Test] - public void TestBeatmapDoesntReloadOnNoChange() + [TestCase(BackgroundSource.Beatmap, typeof(BeatmapBackground))] + [TestCase(BackgroundSource.BeatmapWithStoryboard, typeof(BeatmapBackgroundWithStoryboard))] + [TestCase(BackgroundSource.Skin, typeof(SkinBackground))] + public void TestBackgroundDoesntReloadOnNoChange(BackgroundSource source, Type backgroundType) { - BeatmapBackground last = null; + Graphics.Backgrounds.Background last = null; - setSourceMode(BackgroundSource.Beatmap); + setSourceMode(source); setSupporter(true); - AddUntilStep("wait for beatmap background to be loaded", () => (last = getCurrentBackground() as BeatmapBackground) != null); + AddUntilStep("wait for beatmap background to be loaded", () => (last = getCurrentBackground())?.GetType() == backgroundType); AddAssert("next doesn't load new background", () => screen.Next() == false); // doesn't really need to be checked but might as well. From 97204b6f278408f97fe3ab2c5cec289445c6810a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 8 Jun 2021 22:26:15 +0200 Subject: [PATCH 818/839] Reduce unnecessary background changes via `IEquatable` implementation --- osu.Game/Graphics/Backgrounds/Background.cs | 12 +++++++++++- osu.Game/Graphics/Backgrounds/BeatmapBackground.cs | 9 +++++++++ osu.Game/Graphics/Backgrounds/SkinBackground.cs | 9 +++++++++ .../Screens/Backgrounds/BackgroundScreenDefault.cs | 11 +++++------ 4 files changed, 34 insertions(+), 7 deletions(-) diff --git a/osu.Game/Graphics/Backgrounds/Background.cs b/osu.Game/Graphics/Backgrounds/Background.cs index c90b1e0e98..cfc1eb1806 100644 --- a/osu.Game/Graphics/Backgrounds/Background.cs +++ b/osu.Game/Graphics/Backgrounds/Background.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.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -14,7 +15,7 @@ namespace osu.Game.Graphics.Backgrounds /// /// A background which offers blurring via a on demand. /// - public class Background : CompositeDrawable + public class Background : CompositeDrawable, IEquatable { private const float blur_scale = 0.5f; @@ -71,5 +72,14 @@ namespace osu.Game.Graphics.Backgrounds bufferedContainer?.BlurTo(newBlurSigma * blur_scale, duration, easing); } + + public virtual bool Equals(Background other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + + return other.GetType() == GetType() + && other.textureName == textureName; + } } } diff --git a/osu.Game/Graphics/Backgrounds/BeatmapBackground.cs b/osu.Game/Graphics/Backgrounds/BeatmapBackground.cs index 058d2ed0f9..e0c15dd52a 100644 --- a/osu.Game/Graphics/Backgrounds/BeatmapBackground.cs +++ b/osu.Game/Graphics/Backgrounds/BeatmapBackground.cs @@ -24,5 +24,14 @@ namespace osu.Game.Graphics.Backgrounds { Sprite.Texture = Beatmap?.Background ?? textures.Get(fallbackTextureName); } + + public override bool Equals(Background other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + + return other.GetType() == GetType() + && ((BeatmapBackground)other).Beatmap == Beatmap; + } } } diff --git a/osu.Game/Graphics/Backgrounds/SkinBackground.cs b/osu.Game/Graphics/Backgrounds/SkinBackground.cs index 8be017dc91..9266e7b17b 100644 --- a/osu.Game/Graphics/Backgrounds/SkinBackground.cs +++ b/osu.Game/Graphics/Backgrounds/SkinBackground.cs @@ -21,5 +21,14 @@ namespace osu.Game.Graphics.Backgrounds { Sprite.Texture = skin.GetTexture("menu-background") ?? Sprite.Texture; } + + public override bool Equals(Background other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + + return other.GetType() == GetType() + && ((SkinBackground)other).skin.SkinInfo.Equals(skin.SkinInfo); + } } } diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs index 46574ff8c8..81b15570d2 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs @@ -112,12 +112,6 @@ namespace osu.Game.Screens.Backgrounds newBackground = new BeatmapBackgroundWithStoryboard(beatmap.Value, getBackgroundTextureName()); newBackground ??= new BeatmapBackground(beatmap.Value, getBackgroundTextureName()); - // this method is called in many cases where the beatmap hasn't changed (ie. on screen transitions). - // if a background is already displayed for the requested beatmap, we don't want to load it again. - if (background?.GetType() == newBackground.GetType() && - (background as BeatmapBackground)?.Beatmap == beatmap.Value) - return background; - break; } @@ -127,6 +121,11 @@ namespace osu.Game.Screens.Backgrounds } } + // this method is called in many cases where the background might not necessarily need to change. + // if an equivalent background is currently being shown, we don't want to load it again. + if (newBackground?.Equals(background) == true) + return background; + newBackground ??= new Background(getBackgroundTextureName()); newBackground.Depth = currentDisplay; From 4707918c6afedf0cff2111dbbdfc2833aba75b11 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 9 Jun 2021 10:53:52 +0900 Subject: [PATCH 819/839] Fix hit circle animation when a replay is rewound --- osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs | 2 ++ osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs index b52dc749f0..fece3494e6 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs @@ -15,6 +15,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default { public class MainCirclePiece : CompositeDrawable { + public override bool RemoveCompletedTransforms => false; + private readonly CirclePiece circle; private readonly RingPiece ring; private readonly FlashPiece flash; diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs index 822dad8523..e5200ac248 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs @@ -20,6 +20,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { public class LegacyMainCirclePiece : CompositeDrawable { + public override bool RemoveCompletedTransforms => false; + private readonly string priorityLookup; private readonly bool hasNumber; From 555ab8fccd61f3aff1ee9237e9da873d02a9b500 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 9 Jun 2021 12:31:30 +0900 Subject: [PATCH 820/839] Fix event not unregistered on dispose --- osu.Game.Rulesets.Osu/Skinning/Default/DefaultSpinner.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSpinner.cs index ae8c03dad1..df33bf52be 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSpinner.cs @@ -128,5 +128,13 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default spmContainer.FadeIn(drawableSpinner.HitObject.TimeFadeIn); } } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (drawableSpinner != null) + drawableSpinner.ApplyCustomUpdateState -= updateStateTransforms; + } } } From a87226ab10a241f52bb10129622d9e3420bc44fa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 9 Jun 2021 14:08:27 +0900 Subject: [PATCH 821/839] Remove obsoleted `DrawableJudgement` methods Undated, but change was made on 2020-11-18. --- osu.Game/Rulesets/Judgements/DrawableJudgement.cs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs index feeafb7151..8a57b4af91 100644 --- a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs +++ b/osu.Game/Rulesets/Judgements/DrawableJudgement.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.Diagnostics; using JetBrains.Annotations; using osu.Framework.Allocation; @@ -32,18 +31,6 @@ namespace osu.Game.Rulesets.Judgements private readonly Container aboveHitObjectsContent; - /// - /// Duration of initial fade in. - /// - [Obsolete("Apply any animations manually via ApplyHitAnimations / ApplyMissAnimations. Defaults were moved inside skinned components.")] - protected virtual double FadeInDuration => 100; - - /// - /// Duration to wait until fade out begins. Defaults to . - /// - [Obsolete("Apply any animations manually via ApplyHitAnimations / ApplyMissAnimations. Defaults were moved inside skinned components.")] - protected virtual double FadeOutDelay => FadeInDuration; - /// /// Creates a drawable which visualises a . /// From f41e34ae2c840adadc16dffb04994b1da815a4ed Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 9 Jun 2021 14:09:28 +0900 Subject: [PATCH 822/839] Remove more obsoleted members --- osu.Game/Graphics/Backgrounds/Triangles.cs | 6 ---- osu.Game/Rulesets/Judgements/Judgement.cs | 9 ----- .../Objects/Drawables/DrawableHitObject.cs | 36 ++----------------- 3 files changed, 2 insertions(+), 49 deletions(-) diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index 67cee883c8..269360c492 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -57,12 +57,6 @@ namespace osu.Game.Graphics.Backgrounds } } - /// - /// Whether we want to expire triangles as they exit our draw area completely. - /// - [Obsolete("Unused.")] // Can be removed 20210518 - protected virtual bool ExpireOffScreenTriangles => true; - /// /// Whether we should create new triangles as others expire. /// diff --git a/osu.Game/Rulesets/Judgements/Judgement.cs b/osu.Game/Rulesets/Judgements/Judgement.cs index be69db5ca8..fd576e9b9f 100644 --- a/osu.Game/Rulesets/Judgements/Judgement.cs +++ b/osu.Game/Rulesets/Judgements/Judgement.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.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; @@ -69,14 +68,6 @@ namespace osu.Game.Rulesets.Judgements /// public double MaxHealthIncrease => HealthIncreaseFor(MaxResult); - /// - /// Retrieves the numeric score representation of a . - /// - /// The to find the numeric score representation for. - /// The numeric score representation of . - [Obsolete("Has no effect. Use ToNumericResult(HitResult) (standardised across all rulesets).")] // Can be made non-virtual 20210328 - protected virtual int NumericResultFor(HitResult result) => ToNumericResult(result); - /// /// Retrieves the numeric score representation of a . /// diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 6c688c1625..ef4fc1bc15 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -409,11 +409,6 @@ namespace osu.Game.Rulesets.Objects.Drawables using (BeginAbsoluteSequence(StateUpdateTime, true)) UpdateStartTimeStateTransforms(); -#pragma warning disable 618 - using (BeginAbsoluteSequence(StateUpdateTime + (Result?.TimeOffset ?? 0), true)) - UpdateStateTransforms(newState); -#pragma warning restore 618 - using (BeginAbsoluteSequence(HitStateUpdateTime, true)) UpdateHitStateTransforms(newState); @@ -447,7 +442,7 @@ namespace osu.Game.Rulesets.Objects.Drawables /// By default, this will fade in the object from zero with no duration. ///
/// - /// This is called once before every . This is to ensure a good state in the case + /// This is called once before every . This is to ensure a good state in the case /// the was negative and potentially altered the pre-hit transforms. /// protected virtual void UpdateInitialTransforms() @@ -455,16 +450,6 @@ namespace osu.Game.Rulesets.Objects.Drawables this.FadeInFromZero(); } - /// - /// Apply transforms based on the current . Previous states are automatically cleared. - /// In the case of a non-idle , and if was not set during this call, will be invoked. - /// - /// The new armed state. - [Obsolete("Use UpdateStartTimeStateTransforms and UpdateHitStateTransforms instead")] // Can be removed 20210504 - protected virtual void UpdateStateTransforms(ArmedState state) - { - } - /// /// Apply passive transforms at the 's StartTime. /// This is called each time changes. @@ -520,23 +505,6 @@ namespace osu.Game.Rulesets.Objects.Drawables AccentColour.Value = combo.GetComboColour(comboColours); } - /// - /// Called to retrieve the combo colour. Automatically assigned to . - /// Defaults to using to decide on a colour. - /// - /// - /// This will only be called if the implements . - /// - /// A list of combo colours provided by the beatmap or skin. Can be null if not available. - [Obsolete("Unused. Implement IHasComboInformation and IHasComboInformation.GetComboColour() on the HitObject model instead.")] // Can be removed 20210527 - protected virtual Color4 GetComboColour(IReadOnlyList comboColours) - { - if (!(HitObject is IHasComboInformation combo)) - throw new InvalidOperationException($"{nameof(HitObject)} must implement {nameof(IHasComboInformation)}"); - - return comboColours?.Count > 0 ? comboColours[combo.ComboIndex % comboColours.Count] : Color4.White; - } - /// /// Called when a change is made to the skin. /// @@ -630,7 +598,7 @@ namespace osu.Game.Rulesets.Objects.Drawables /// /// The time at which state transforms should be applied that line up to 's StartTime. - /// This is used to offset calls to . + /// This is used to offset calls to . /// public double StateUpdateTime => HitObject.StartTime; From 62199a38a80716aa2617259b051f4d29996ab8d2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 9 Jun 2021 14:11:50 +0900 Subject: [PATCH 823/839] Add one missing obsoletion removal date --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index ef4fc1bc15..8818e4c14a 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -192,7 +192,7 @@ namespace osu.Game.Rulesets.Objects.Drawables /// /// Applies a hit object to be represented by this . /// - [Obsolete("Use either overload of Apply that takes a single argument of type HitObject or HitObjectLifetimeEntry")] + [Obsolete("Use either overload of Apply that takes a single argument of type HitObject or HitObjectLifetimeEntry")] // Can be removed 20211021. public void Apply([NotNull] HitObject hitObject, [CanBeNull] HitObjectLifetimeEntry lifetimeEntry) { if (lifetimeEntry != null) From b8df3fff9e26a4454240c2c562c6598d42cdd9bd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 9 Jun 2021 14:20:01 +0900 Subject: [PATCH 824/839] Fix incorrect method referenced in xmldco Co-authored-by: ekrctb <32995012+ekrctb@users.noreply.github.com> --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 8818e4c14a..5fd2b2493e 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -598,7 +598,7 @@ namespace osu.Game.Rulesets.Objects.Drawables /// /// The time at which state transforms should be applied that line up to 's StartTime. - /// This is used to offset calls to . + /// This is used to offset calls to . /// public double StateUpdateTime => HitObject.StartTime; From 5487012060e239cbcfca3028adc71f5a03cec6c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 9 Jun 2021 07:48:16 +0200 Subject: [PATCH 825/839] Add test coverage for default skin background cycling --- .../TestSceneBackgroundScreenDefault.cs | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs index 135998695b..f7d42a2ee6 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs @@ -12,6 +12,7 @@ using osu.Game.Graphics.Backgrounds; using osu.Game.Online.API; using osu.Game.Screens; using osu.Game.Screens.Backgrounds; +using osu.Game.Skinning; using osu.Game.Users; namespace osu.Game.Tests.Visual.Background @@ -24,6 +25,9 @@ namespace osu.Game.Tests.Visual.Background private Graphics.Backgrounds.Background getCurrentBackground() => screen.ChildrenOfType().FirstOrDefault(); + [Resolved] + private SkinManager skins { get; set; } + [Resolved] private OsuConfigManager config { get; set; } @@ -47,6 +51,9 @@ namespace osu.Game.Tests.Visual.Background AddUntilStep("is storyboard background", () => getCurrentBackground() is BeatmapBackgroundWithStoryboard); setSourceMode(BackgroundSource.Skin); + AddUntilStep("is default background", () => getCurrentBackground().GetType() == typeof(Graphics.Backgrounds.Background)); + + setCustomSkin(); AddUntilStep("is skin background", () => getCurrentBackground() is SkinBackground); } @@ -74,6 +81,8 @@ namespace osu.Game.Tests.Visual.Background setSourceMode(source); setSupporter(true); + if (source == BackgroundSource.Skin) + setCustomSkin(); AddUntilStep("wait for beatmap background to be loaded", () => (last = getCurrentBackground())?.GetType() == backgroundType); AddAssert("next doesn't load new background", () => screen.Next() == false); @@ -83,6 +92,23 @@ namespace osu.Game.Tests.Visual.Background AddUntilStep("ensure same background instance", () => last == getCurrentBackground()); } + [Test] + public void TestBackgroundCyclingOnDefaultSkin([Values] bool supporter) + { + Graphics.Backgrounds.Background last = null; + + setSourceMode(BackgroundSource.Skin); + setSupporter(supporter); + setDefaultSkin(); + + AddUntilStep("wait for beatmap background to be loaded", () => (last = getCurrentBackground())?.GetType() == typeof(Graphics.Backgrounds.Background)); + AddAssert("next cycles background", () => screen.Next()); + + // doesn't really need to be checked but might as well. + AddWaitStep("wait a bit", 5); + AddUntilStep("ensure different background instance", () => last != getCurrentBackground()); + } + private void setSourceMode(BackgroundSource source) => AddStep($"set background mode to {source}", () => config.SetValue(OsuSetting.MenuBackgroundSource, source)); @@ -92,5 +118,16 @@ namespace osu.Game.Tests.Visual.Background IsSupporter = isSupporter, Id = API.LocalUser.Value.Id + 1, }); + + private void setCustomSkin() + { + // feign a skin switch. this doesn't do anything except force CurrentSkin to become a LegacySkin. + AddStep("set custom skin", () => skins.CurrentSkinInfo.Value = new SkinInfo { ID = 5 }); + } + + private void setDefaultSkin() => AddStep("set default skin", () => skins.CurrentSkinInfo.SetDefault()); + + [TearDownSteps] + public void TearDown() => setDefaultSkin(); } } From a801a9a14d7b52fbafb3f88a43180f53af53552a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 9 Jun 2021 07:59:47 +0200 Subject: [PATCH 826/839] Ensure background rotation on default skins --- osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs index 81b15570d2..f0c90cc409 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs @@ -116,6 +116,10 @@ namespace osu.Game.Screens.Backgrounds } case BackgroundSource.Skin: + // default skins should use the default background rotation, which won't be the case if a SkinBackground is created for them. + if (skin.Value is DefaultSkin || skin.Value is DefaultLegacySkin) + break; + newBackground = new SkinBackground(skin.Value, getBackgroundTextureName()); break; } From d248bbd4c8dff39fafc7c316f37033d6a563e490 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 9 Jun 2021 15:00:55 +0900 Subject: [PATCH 827/839] Use candidate skin for mania skin key lookup rather than `this` --- .../Skinning/Legacy/ManiaLegacySkinTransformer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs index 8aa0c85433..962a13ebea 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs @@ -71,7 +71,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy { isLegacySkin = new Lazy(() => FindProvider(s => s.GetConfig(LegacySkinConfiguration.LegacySetting.Version) != null) != null); hasKeyTexture = new Lazy(() => FindProvider(s => s.GetAnimation( - this.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.KeyImage, 0)?.Value + s.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.KeyImage, 0)?.Value ?? "mania-key1", true, true) != null) != null); } From 610cdaea98bb1fb9720035cfdbdaa113c542bed8 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 9 Jun 2021 16:14:55 +0900 Subject: [PATCH 828/839] Fix circle piece animation is sometimes not playing when a replay is rewound --- .../Skinning/Default/MainCirclePiece.cs | 21 +++++++++++-------- .../Skinning/Legacy/LegacyMainCirclePiece.cs | 17 ++++++++++----- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs index fece3494e6..d7ebe9333d 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs @@ -15,8 +15,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default { public class MainCirclePiece : CompositeDrawable { - public override bool RemoveCompletedTransforms => false; - private readonly CirclePiece circle; private readonly RingPiece ring; private readonly FlashPiece flash; @@ -44,7 +42,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default private readonly IBindable accentColour = new Bindable(); private readonly IBindable indexInCurrentCombo = new Bindable(); - private readonly IBindable armedState = new Bindable(); [Resolved] private DrawableHitObject drawableObject { get; set; } @@ -56,7 +53,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default accentColour.BindTo(drawableObject.AccentColour); indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable); - armedState.BindTo(drawableObject.State); } protected override void LoadComplete() @@ -72,19 +68,18 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default indexInCurrentCombo.BindValueChanged(index => number.Text = (index.NewValue + 1).ToString(), true); - armedState.BindValueChanged(animate, true); + drawableObject.ApplyCustomUpdateState += updateStateTransforms; + updateStateTransforms(drawableObject, drawableObject.State.Value); } - private void animate(ValueChangedEvent state) + private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state) { - ClearTransforms(true); - using (BeginAbsoluteSequence(drawableObject.StateUpdateTime)) glow.FadeOut(400); using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime)) { - switch (state.NewValue) + switch (state) { case ArmedState.Hit: const double flash_in = 40; @@ -111,5 +106,13 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default } } } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (drawableObject != null) + drawableObject.ApplyCustomUpdateState -= updateStateTransforms; + } } } diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs index e5200ac248..bb7a335efe 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs @@ -41,7 +41,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy private readonly Bindable accentColour = new Bindable(); private readonly IBindable indexInCurrentCombo = new Bindable(); - private readonly IBindable armedState = new Bindable(); [Resolved] private DrawableHitObject drawableObject { get; set; } @@ -116,7 +115,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy accentColour.BindTo(drawableObject.AccentColour); indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable); - armedState.BindTo(drawableObject.State); Texture getTextureWithFallback(string name) { @@ -142,10 +140,11 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy if (hasNumber) indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true); - armedState.BindValueChanged(animate, true); + drawableObject.ApplyCustomUpdateState += updateStateTransforms; + updateStateTransforms(drawableObject, drawableObject.State.Value); } - private void animate(ValueChangedEvent state) + private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state) { const double legacy_fade_duration = 240; @@ -153,7 +152,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime)) { - switch (state.NewValue) + switch (state) { case ArmedState.Hit: circleSprites.FadeOut(legacy_fade_duration, Easing.Out); @@ -178,5 +177,13 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy } } } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (drawableObject != null) + drawableObject.ApplyCustomUpdateState -= updateStateTransforms; + } } } From 5418e895ae93b31f9515e7100a4a8136cd04756f Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 9 Jun 2021 16:50:13 +0900 Subject: [PATCH 829/839] Remove useless `ClearTransforms` The transforms are cleared by DHO before `ApplyCustomUpdateState` is invoked. --- osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs index bb7a335efe..7a210324d7 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs @@ -148,8 +148,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { const double legacy_fade_duration = 240; - ClearTransforms(true); - using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime)) { switch (state) From a7ef0173e9a20f8827e2b9ede18221985fca0a3a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 9 Jun 2021 17:07:28 +0900 Subject: [PATCH 830/839] Add safety to ensure background is correct tint when entering gameplay --- 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 94e67107c9..f9036780aa 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -32,6 +32,7 @@ using osu.Game.Scoring.Legacy; using osu.Game.Screens.Ranking; using osu.Game.Skinning; using osu.Game.Users; +using osuTK.Graphics; namespace osu.Game.Screens.Play { @@ -856,6 +857,7 @@ namespace osu.Game.Screens.Play { b.IgnoreUserSettings.Value = false; b.BlurAmount.Value = 0; + b.FadeColour(Color4.White, 250); // bind component bindables. b.IsBreakTime.BindTo(breakTracker.IsBreakTime); From 258d05d1e0c8029cb8a28773804f828b0570f599 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 9 Jun 2021 17:17:39 +0900 Subject: [PATCH 831/839] Ensure `PlayerLoader` restores the background colour to its own value on resume --- osu.Game/Screens/Play/PlayerLoader.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index ce580e2b53..5f6b4ca2b0 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -184,8 +184,6 @@ namespace osu.Game.Screens.Play { if (epilepsyWarning != null) epilepsyWarning.DimmableBackground = b; - - b?.FadeColour(Color4.White, 800, Easing.OutQuint); }); Beatmap.Value.Track.AddAdjustment(AdjustableProperty.Volume, volumeAdjustment); @@ -334,6 +332,8 @@ namespace osu.Game.Screens.Play content.FadeInFromZero(400); content.ScaleTo(1, 650, Easing.OutQuint).Then().Schedule(prepareNewPlayer); + + ApplyToBackground(b => b?.FadeColour(Color4.White, 800, Easing.OutQuint)); } private void contentOut() From 7b0c5e9d32528db538ee66775d40e96c2ca2b4d3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 9 Jun 2021 17:18:52 +0900 Subject: [PATCH 832/839] Fix results screen changing applied colour to background on exit The general rule is that screens should only apply colours and the likes on enter / resume, and leave the outwards transition to whatever screen is coming next. --- osu.Game/Screens/Ranking/ResultsScreen.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index a0ea27b640..e460ee77f4 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -13,6 +13,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Bindings; using osu.Framework.Screens; using osu.Game.Audio; +using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; @@ -257,7 +258,7 @@ namespace osu.Game.Screens.Ranking ApplyToBackground(b => { b.BlurAmount.Value = BACKGROUND_BLUR; - b.FadeTo(0.5f, 250); + b.FadeColour(OsuColour.Gray(0.5f), 250); }); bottomPanel.FadeTo(1, 250); @@ -265,7 +266,6 @@ namespace osu.Game.Screens.Ranking public override bool OnExiting(IScreen next) { - ApplyToBackground(b => b.FadeTo(1, 250)); return base.OnExiting(next); } From a65b76bdbf3a360c7a9cb3c9d2f53dbbae35ad65 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 9 Jun 2021 17:19:36 +0900 Subject: [PATCH 833/839] Add a simple fade to the results screen Stops it from immediately disappearing. --- osu.Game/Screens/Ranking/ResultsScreen.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index e460ee77f4..c44ce63ccb 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -266,8 +266,11 @@ namespace osu.Game.Screens.Ranking public override bool OnExiting(IScreen next) { + if (base.OnExiting(next)) + return true; - return base.OnExiting(next); + this.FadeOut(100); + return false; } public override bool OnBackButton() From f113c095ce55640dabac6b1c4ffc956b28a39839 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 9 Jun 2021 20:29:06 +0900 Subject: [PATCH 834/839] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 395470824f..cfa3bc836f 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 9ecab1ee48..be10764c6a 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -34,7 +34,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index e66f125985..751f60912a 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From e3f3c379535d078d4c7e0003c555a20afe64b5b1 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Wed, 9 Jun 2021 17:03:46 -0700 Subject: [PATCH 835/839] Add ability to navigate score panels with left/right arrows --- osu.Game/Screens/Ranking/ScorePanelList.cs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/osu.Game/Screens/Ranking/ScorePanelList.cs b/osu.Game/Screens/Ranking/ScorePanelList.cs index 441c9e048a..e170241ede 100644 --- a/osu.Game/Screens/Ranking/ScorePanelList.cs +++ b/osu.Game/Screens/Ranking/ScorePanelList.cs @@ -8,9 +8,11 @@ using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Input.Events; using osu.Game.Graphics.Containers; using osu.Game.Scoring; using osuTK; +using osuTK.Input; namespace osu.Game.Screens.Ranking { @@ -263,6 +265,26 @@ namespace osu.Game.Screens.Ranking container.Attach(); } + protected override bool OnKeyDown(KeyDownEvent e) + { + var expandedPanelIndex = flow.GetPanelIndex(expandedPanel.Score); + + switch (e.Key) + { + case Key.Left: + if (expandedPanelIndex > 0) + SelectedScore.Value = flow.Children[expandedPanelIndex - 1].Panel.Score; + return true; + + case Key.Right: + if (expandedPanelIndex < flow.Count - 1) + SelectedScore.Value = flow.Children[expandedPanelIndex + 1].Panel.Score; + return true; + } + + return base.OnKeyDown(e); + } + private class Flow : FillFlowContainer { public override IEnumerable FlowingChildren => applySorting(AliveInternalChildren); From 39e1f77d537c0fc3df62dbde6b5bc9be13a1c9a5 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 10 Jun 2021 09:37:33 +0700 Subject: [PATCH 836/839] add image table with image content test --- .../Online/TestSceneWikiMarkdownContainer.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs index 9d8f07969c..b6dce2c398 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs @@ -143,6 +143,25 @@ Line after image"; }); } + [Test] + public void TestTableWithImageContent() + { + AddStep("Add Table", () => + { + markdownContainer.DocumentUrl = "https://dev.ppy.sh"; + markdownContainer.Text = @" +| Image | Name | Effect | +| :-: | :-: | :-- | +| ![](/wiki/Skinning/Interface/img/hit300.png ""300"") | 300 | A possible score when tapping a hit circle precisely on time, completing a Slider and keeping the cursor over every tick, or completing a Spinner with the Spinner Metre full. A score of 300 appears in an blue score by default. Scoring nothing except 300s in a beatmap will award the player with the SS or SSH grade. | +| ![](/wiki/Skinning/Interface/img/hit300g.png ""Geki"") | (激) Geki | A term from Ouendan, called Elite Beat! in EBA. Appears when playing the last element in a combo in which the player has scored only 300s. Getting a Geki will give a sizable boost to the Life Bar. By default, it is blue. | +| ![](/wiki/Skinning/Interface/img/hit100.png ""100"") | 100 | A possible score one can get when tapping a Hit Object slightly late or early, completing a Slider and missing a number of ticks, or completing a Spinner with the Spinner Meter almost full. A score of 100 appears in a green score by default. When very skilled players test a beatmap and they get a lot of 100s, this may mean that the beatmap does not have correct timing. | +| ![](/wiki/Skinning/Interface/img/hit300k.png ""300 Katu"") ![](/wiki/Skinning/Interface/img/hit100k.png ""100 Katu"") | (喝) Katu or Katsu | A term from Ouendan, called Beat! in EBA. Appears when playing the last element in a combo in which the player has scored at least one 100, but no 50s or misses. Getting a Katu will give a small boost to the Life Bar. By default, it is coloured green or blue depending on whether the Katu itself is a 100 or a 300. | +| ![](/wiki/Skinning/Interface/img/hit50.png ""50"") | 50 | A possible score one can get when tapping a hit circle rather early or late but not early or late enough to cause a miss, completing a Slider and missing a lot of ticks, or completing a Spinner with the Spinner Metre close to full. A score of 50 appears in a orange score by default. Scoring a 50 in a combo will prevent the appearance of a Katu or a Geki at the combo's end. | +| ![](/wiki/Skinning/Interface/img/hit0.png ""Miss"") | Miss | A possible score one can get when not tapping a hit circle or too early (based on OD and AR, it may *shake* instead), not tapping or holding the Slider at least once, or completing a Spinner with low Spinner Metre fill. Scoring a Miss will reset the current combo to 0 and will prevent the appearance of a Katu or a Geki at the combo's end. | +"; + }); + } + private class TestMarkdownContainer : WikiMarkdownContainer { public LinkInline Link; From 28d7b069085aa9721fc9d55b20963a34753606a1 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 10 Jun 2021 09:38:07 +0700 Subject: [PATCH 837/839] create OsuMarkdownImage --- .../Containers/Markdown/OsuMarkdownImage.cs | 20 +++++++++++++++++++ .../Markdown/OsuMarkdownTextFlowContainer.cs | 2 ++ 2 files changed, 22 insertions(+) create mode 100644 osu.Game/Graphics/Containers/Markdown/OsuMarkdownImage.cs diff --git a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownImage.cs b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownImage.cs new file mode 100644 index 0000000000..75c73af0ce --- /dev/null +++ b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownImage.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 Markdig.Syntax.Inlines; +using osu.Framework.Graphics.Containers.Markdown; +using osu.Framework.Graphics.Cursor; + +namespace osu.Game.Graphics.Containers.Markdown +{ + public class OsuMarkdownImage : MarkdownImage, IHasTooltip + { + public string TooltipText { get; } + + public OsuMarkdownImage(LinkInline linkInline) + : base(linkInline.Url) + { + TooltipText = linkInline.Title; + } + } +} diff --git a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs index f3308019ce..36b48b7769 100644 --- a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs +++ b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs @@ -17,6 +17,8 @@ namespace osu.Game.Graphics.Containers.Markdown protected override void AddLinkText(string text, LinkInline linkInline) => AddDrawable(new OsuMarkdownLinkText(text, linkInline)); + protected override void AddImage(LinkInline linkInline) => AddDrawable(new OsuMarkdownImage(linkInline)); + // TODO : Change font to monospace protected override void AddCodeInLine(CodeInline codeInline) => AddDrawable(new OsuMarkdownInlineCode { From 05cb935a940315ad6ff3bfdbed05c010ee09388c Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 10 Jun 2021 09:38:48 +0700 Subject: [PATCH 838/839] change WikiMarkdownImage to extend OsuMarkdownImage --- osu.Game/Overlays/Wiki/Markdown/WikiMarkdownImage.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownImage.cs b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownImage.cs index c2115efeb5..27d1fe9b2f 100644 --- a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownImage.cs +++ b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownImage.cs @@ -2,19 +2,15 @@ // See the LICENCE file in the repository root for full licence text. using Markdig.Syntax.Inlines; -using osu.Framework.Graphics.Containers.Markdown; -using osu.Framework.Graphics.Cursor; +using osu.Game.Graphics.Containers.Markdown; namespace osu.Game.Overlays.Wiki.Markdown { - public class WikiMarkdownImage : MarkdownImage, IHasTooltip + public class WikiMarkdownImage : OsuMarkdownImage { - public string TooltipText { get; } - public WikiMarkdownImage(LinkInline linkInline) - : base(linkInline.Url) + : base(linkInline) { - TooltipText = linkInline.Title; } protected override ImageContainer CreateImageContainer(string url) From cc38556f619c652d4b9bbc84d4948a059fe9aaf1 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Wed, 9 Jun 2021 23:26:57 -0700 Subject: [PATCH 839/839] Fix background being dimmed forever after toggling statistics in results screen --- osu.Game/Screens/Ranking/ResultsScreen.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index c44ce63ccb..9f7d0935d5 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -318,7 +318,7 @@ namespace osu.Game.Screens.Ranking ScorePanelList.HandleInput = false; // Dim background. - ApplyToBackground(b => b.FadeTo(0.1f, 150)); + ApplyToBackground(b => b.FadeColour(OsuColour.Gray(0.1f), 150)); detachedPanel = expandedPanel; } @@ -342,7 +342,7 @@ namespace osu.Game.Screens.Ranking ScorePanelList.HandleInput = true; // Un-dim background. - ApplyToBackground(b => b.FadeTo(0.5f, 150)); + ApplyToBackground(b => b.FadeColour(OsuColour.Gray(0.5f), 150)); detachedPanel = null; }