From 668f89d8b230103d8daee7fd639fde069baa4d99 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Tue, 22 Dec 2020 17:33:11 +0200 Subject: [PATCH 001/121] Copy test from #11019 --- .../TestSceneSectionsContainer.cs | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneSectionsContainer.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneSectionsContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneSectionsContainer.cs new file mode 100644 index 0000000000..5c2e6e457d --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneSectionsContainer.cs @@ -0,0 +1,122 @@ +// 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.Graphics.Colour; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics.Containers; +using osuTK.Graphics; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneSectionsContainer : OsuManualInputManagerTestScene + { + private readonly SectionsContainer container; + private float custom; + private const float header_height = 100; + + public TestSceneSectionsContainer() + { + container = new SectionsContainer + { + RelativeSizeAxes = Axes.Y, + Width = 300, + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + FixedHeader = new Box + { + Alpha = 0.5f, + Width = 300, + Height = header_height, + Colour = Color4.Red + } + }; + container.SelectedSection.ValueChanged += section => + { + if (section.OldValue != null) + section.OldValue.Selected = false; + if (section.NewValue != null) + section.NewValue.Selected = true; + }; + Add(container); + } + + [Test] + public void TestSelection() + { + AddStep("clear", () => container.Clear()); + AddStep("add 1/8th", () => append(1 / 8.0f)); + AddStep("add third", () => append(1 / 3.0f)); + AddStep("add half", () => append(1 / 2.0f)); + AddStep("add full", () => append(1)); + AddSliderStep("set custom", 0.1f, 1.1f, 0.5f, i => custom = i); + AddStep("add custom", () => append(custom)); + AddStep("scroll to previous", () => container.ScrollTo( + container.Children.Reverse().SkipWhile(s => s != container.SelectedSection.Value).Skip(1).FirstOrDefault() ?? container.Children.First() + )); + AddStep("scroll to next", () => container.ScrollTo( + container.Children.SkipWhile(s => s != container.SelectedSection.Value).Skip(1).FirstOrDefault() ?? container.Children.Last() + )); + AddStep("scroll up", () => triggerUserScroll(1)); + AddStep("scroll down", () => triggerUserScroll(-1)); + } + + [Test] + public void TestCorrectSectionSelected() + { + const int sections_count = 11; + float[] alternating = { 0.07f, 0.33f, 0.16f, 0.33f }; + AddStep("clear", () => container.Clear()); + AddStep("fill with sections", () => + { + for (int i = 0; i < sections_count; i++) + append(alternating[i % alternating.Length]); + }); + + void step(int scrollIndex) + { + AddStep($"scroll to section {scrollIndex + 1}", () => container.ScrollTo(container.Children[scrollIndex])); + AddUntilStep("correct section selected", () => container.SelectedSection.Value == container.Children[scrollIndex]); + } + + for (int i = 1; i < sections_count; i++) + step(i); + for (int i = sections_count - 2; i >= 0; i--) + step(i); + + AddStep("scroll almost to end", () => container.ScrollTo(container.Children[sections_count - 2])); + AddUntilStep("correct section selected", () => container.SelectedSection.Value == container.Children[sections_count - 2]); + AddStep("scroll down", () => triggerUserScroll(-1)); + AddUntilStep("correct section selected", () => container.SelectedSection.Value == container.Children[sections_count - 1]); + } + + private static readonly ColourInfo selected_colour = ColourInfo.GradientVertical(Color4.Yellow, Color4.Gold); + private static readonly ColourInfo default_colour = ColourInfo.GradientVertical(Color4.White, Color4.DarkGray); + + private void append(float multiplier) + { + container.Add(new TestSection + { + Width = 300, + Height = (container.ChildSize.Y - header_height) * multiplier, + Colour = default_colour + }); + } + + private void triggerUserScroll(float direction) + { + InputManager.MoveMouseTo(container); + InputManager.ScrollVerticalBy(direction); + } + + private class TestSection : Box + { + public bool Selected + { + set => Colour = value ? selected_colour : default_colour; + } + } + } +} From 78c14fd69693f688f40595699eba849486f49463 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Tue, 22 Dec 2020 17:36:44 +0200 Subject: [PATCH 002/121] Refactor code into UserTrackingScrollContainer --- .../Graphics/Containers/SectionsContainer.cs | 4 +- .../Containers/UserTrackingScrollContainer.cs | 49 +++++++++++++++++++ osu.Game/Overlays/OverlayScrollContainer.cs | 4 +- osu.Game/Overlays/UserProfileOverlay.cs | 2 +- osu.Game/Screens/Select/BeatmapCarousel.cs | 20 +------- 5 files changed, 55 insertions(+), 24 deletions(-) create mode 100644 osu.Game/Graphics/Containers/UserTrackingScrollContainer.cs diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 81968de304..6e9520ef8f 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -95,7 +95,7 @@ namespace osu.Game.Graphics.Containers protected override Container Content => scrollContentContainer; - private readonly OsuScrollContainer scrollContainer; + private readonly UserTrackingScrollContainer scrollContainer; private readonly Container headerBackgroundContainer; private readonly MarginPadding originalSectionsMargin; private Drawable expandableHeader, fixedHeader, footer, headerBackground; @@ -139,7 +139,7 @@ namespace osu.Game.Graphics.Containers public void ScrollToTop() => scrollContainer.ScrollTo(0); [NotNull] - protected virtual OsuScrollContainer CreateScrollContainer() => new OsuScrollContainer(); + protected virtual UserTrackingScrollContainer CreateScrollContainer() => new UserTrackingScrollContainer(); [NotNull] protected virtual FlowContainer CreateScrollContentContainer() => diff --git a/osu.Game/Graphics/Containers/UserTrackingScrollContainer.cs b/osu.Game/Graphics/Containers/UserTrackingScrollContainer.cs new file mode 100644 index 0000000000..b8ce34b204 --- /dev/null +++ b/osu.Game/Graphics/Containers/UserTrackingScrollContainer.cs @@ -0,0 +1,49 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; + +namespace osu.Game.Graphics.Containers +{ + public class UserTrackingScrollContainer : UserTrackingScrollContainer + { + public UserTrackingScrollContainer() + { + } + + public UserTrackingScrollContainer(Direction direction) + : base(direction) + { + } + } + + public class UserTrackingScrollContainer : OsuScrollContainer + where T : Drawable + { + /// + /// Whether the last scroll event was user triggered, directly on the scroll container. + /// + public bool UserScrolling { get; private set; } + + public UserTrackingScrollContainer() + { + } + + public UserTrackingScrollContainer(Direction direction) + : base(direction) + { + } + + protected override void OnUserScroll(float value, bool animated = true, double? distanceDecay = default) + { + UserScrolling = true; + base.OnUserScroll(value, animated, distanceDecay); + } + + public new void ScrollTo(float value, bool animated = true, double? distanceDecay = null) + { + UserScrolling = false; + base.ScrollTo(value, animated, distanceDecay); + } + } +} diff --git a/osu.Game/Overlays/OverlayScrollContainer.cs b/osu.Game/Overlays/OverlayScrollContainer.cs index b67d5db1a4..0004719b87 100644 --- a/osu.Game/Overlays/OverlayScrollContainer.cs +++ b/osu.Game/Overlays/OverlayScrollContainer.cs @@ -17,9 +17,9 @@ using osuTK.Graphics; namespace osu.Game.Overlays { /// - /// which provides . Mostly used in . + /// which provides . Mostly used in . /// - public class OverlayScrollContainer : OsuScrollContainer + public class OverlayScrollContainer : UserTrackingScrollContainer { /// /// Scroll position at which the will be shown. diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index 81027667fa..7f29545c2e 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -202,7 +202,7 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.Both; } - protected override OsuScrollContainer CreateScrollContainer() => new OverlayScrollContainer(); + protected override UserTrackingScrollContainer CreateScrollContainer() => new OverlayScrollContainer(); protected override FlowContainer CreateScrollContentContainer() => new FillFlowContainer { diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index d76f0abb9e..e9a8d28316 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -901,15 +901,10 @@ namespace osu.Game.Screens.Select } } - protected class CarouselScrollContainer : OsuScrollContainer + protected class CarouselScrollContainer : UserTrackingScrollContainer { private bool rightMouseScrollBlocked; - /// - /// Whether the last scroll event was user triggered, directly on the scroll container. - /// - public bool UserScrolling { get; private set; } - public CarouselScrollContainer() { // size is determined by the carousel itself, due to not all content necessarily being loaded. @@ -919,19 +914,6 @@ namespace osu.Game.Screens.Select Masking = false; } - // ReSharper disable once OptionalParameterHierarchyMismatch 2020.3 EAP4 bug. (https://youtrack.jetbrains.com/issue/RSRP-481535?p=RIDER-51910) - protected override void OnUserScroll(float value, bool animated = true, double? distanceDecay = default) - { - UserScrolling = true; - base.OnUserScroll(value, animated, distanceDecay); - } - - public new void ScrollTo(float value, bool animated = true, double? distanceDecay = null) - { - UserScrolling = false; - base.ScrollTo(value, animated, distanceDecay); - } - protected override bool OnMouseDown(MouseDownEvent e) { if (e.Button == MouseButton.Right) From 2cf76ebc75c6e0dc4bfbe428a9ee099c21a5eeb9 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Tue, 22 Dec 2020 17:51:12 +0200 Subject: [PATCH 003/121] Scroll to 20% and select section intersecting below there --- .../Graphics/Containers/SectionsContainer.cs | 43 +++++++++++++++---- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 6e9520ef8f..b5f81c516a 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -9,6 +9,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Layout; +using osu.Framework.Utils; namespace osu.Game.Graphics.Containers { @@ -20,6 +21,8 @@ namespace osu.Game.Graphics.Containers where T : Drawable { public Bindable SelectedSection { get; } = new Bindable(); + private Drawable lastClickedSection; + private T smallestSection; public Drawable ExpandableHeader { @@ -131,10 +134,21 @@ namespace osu.Game.Graphics.Containers lastKnownScroll = float.NaN; headerHeight = float.NaN; footerHeight = float.NaN; + + if (drawable == null) + return; + + if (smallestSection == null || smallestSection.Height > drawable.Height) + smallestSection = drawable; } - public void ScrollTo(Drawable section) => - scrollContainer.ScrollTo(scrollContainer.GetChildPosInContent(section) - (FixedHeader?.BoundingBox.Height ?? 0)); + private const float scroll_target_multiplier = 0.2f; + + public void ScrollTo(Drawable section) + { + lastClickedSection = section; + scrollContainer.ScrollTo(scrollContainer.GetChildPosInContent(section) - scrollContainer.DisplayableContent * scroll_target_multiplier - (FixedHeader?.BoundingBox.Height ?? 0)); + } public void ScrollToTop() => scrollContainer.ScrollTo(0); @@ -183,6 +197,10 @@ namespace osu.Game.Graphics.Containers { lastKnownScroll = currentScroll; + // reset last clicked section because user started scrolling themselves + if (scrollContainer.UserScrolling) + lastClickedSection = null; + if (ExpandableHeader != null && FixedHeader != null) { float offset = Math.Min(ExpandableHeader.LayoutSize.Y, currentScroll); @@ -194,18 +212,27 @@ namespace osu.Game.Graphics.Containers headerBackgroundContainer.Height = (ExpandableHeader?.LayoutSize.Y ?? 0) + (FixedHeader?.LayoutSize.Y ?? 0); headerBackgroundContainer.Y = ExpandableHeader?.Y ?? 0; - float scrollOffset = FixedHeader?.LayoutSize.Y ?? 0; + // scroll offset is our fixed header height if we have it plus 20% of content height + // plus 5% to fix floating point errors and to not have a section instantly unselect when scrolling upwards + // but the 5% can't be bigger than our smallest section height, otherwise it won't get selected correctly + float sectionOrContent = Math.Min(smallestSection?.Height / 2.0f ?? 0, scrollContainer.DisplayableContent * 0.05f); + float scrollOffset = (FixedHeader?.LayoutSize.Y ?? 0) + scrollContainer.DisplayableContent * scroll_target_multiplier + sectionOrContent; Func diff = section => scrollContainer.GetChildPosInContent(section) - currentScroll - scrollOffset; - if (scrollContainer.IsScrolledToEnd()) + if (Precision.AlmostBigger(0, scrollContainer.Current)) { - SelectedSection.Value = Children.LastOrDefault(); + SelectedSection.Value = lastClickedSection as T ?? Children.FirstOrDefault(); + return; } - else + + if (Precision.AlmostBigger(scrollContainer.Current, scrollContainer.ScrollableExtent)) { - SelectedSection.Value = Children.TakeWhile(section => diff(section) <= 0).LastOrDefault() - ?? Children.FirstOrDefault(); + SelectedSection.Value = lastClickedSection as T ?? Children.LastOrDefault(); + return; } + + SelectedSection.Value = Children.TakeWhile(section => diff(section) <= 0).LastOrDefault() + ?? Children.FirstOrDefault(); } } From 013b9b62a1ff7dfd22db139d0571259bcd89de43 Mon Sep 17 00:00:00 2001 From: Firmatorenio Date: Tue, 29 Dec 2020 20:22:56 +0600 Subject: [PATCH 004/121] add SV multipliers to taiko difficulty mods --- osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs | 9 +++++++++ osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs | 9 +++++++++ osu.Game/Rulesets/Mods/ModHardRock.cs | 2 +- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs index d1ad4c9d8d..5ff91eec9f 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.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.Game.Beatmaps; using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Taiko.Mods @@ -8,5 +9,13 @@ namespace osu.Game.Rulesets.Taiko.Mods public class TaikoModEasy : ModEasy { public override string Description => @"Beats move slower, and less accuracy required!"; + + private const double slider_multiplier = 0.8; + + public override void ApplyToDifficulty(BeatmapDifficulty difficulty) + { + base.ApplyToDifficulty(difficulty); + difficulty.SliderMultiplier *= slider_multiplier; + } } } diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs index 49d225cdb5..37c8dab2de 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.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.Game.Beatmaps; using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Taiko.Mods @@ -9,5 +10,13 @@ namespace osu.Game.Rulesets.Taiko.Mods { public override double ScoreMultiplier => 1.06; public override bool Ranked => true; + + private const double slider_multiplier = 1.87; + + public override void ApplyToDifficulty(BeatmapDifficulty difficulty) + { + base.ApplyToDifficulty(difficulty); + difficulty.SliderMultiplier *= slider_multiplier; + } } } diff --git a/osu.Game/Rulesets/Mods/ModHardRock.cs b/osu.Game/Rulesets/Mods/ModHardRock.cs index 0e589735c1..4edcb0b074 100644 --- a/osu.Game/Rulesets/Mods/ModHardRock.cs +++ b/osu.Game/Rulesets/Mods/ModHardRock.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Mods { } - public void ApplyToDifficulty(BeatmapDifficulty difficulty) + public virtual void ApplyToDifficulty(BeatmapDifficulty difficulty) { const float ratio = 1.4f; difficulty.CircleSize = Math.Min(difficulty.CircleSize * 1.3f, 10.0f); // CS uses a custom 1.3 ratio. From 669c42a38d7f3ac2d015754166b7af7aacbd13c9 Mon Sep 17 00:00:00 2001 From: Firmatorenio Date: Wed, 30 Dec 2020 20:57:41 +0600 Subject: [PATCH 005/121] add remarks explaining HR SV multiplier --- osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs | 3 +++ osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs | 10 +++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs index 5ff91eec9f..ad6fdf59e2 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs @@ -10,6 +10,9 @@ namespace osu.Game.Rulesets.Taiko.Mods { public override string Description => @"Beats move slower, and less accuracy required!"; + /// + /// Multiplier factor added to the scrolling speed. + /// private const double slider_multiplier = 0.8; public override void ApplyToDifficulty(BeatmapDifficulty difficulty) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs index 37c8dab2de..a5a8b75f80 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs @@ -11,7 +11,15 @@ namespace osu.Game.Rulesets.Taiko.Mods public override double ScoreMultiplier => 1.06; public override bool Ranked => true; - private const double slider_multiplier = 1.87; + /// + /// Multiplier factor added to the scrolling speed. + /// + /// + /// This factor is made up of two parts: the base part (1.4) and the aspect ratio adjustment (4/3). + /// Stable applies the latter by dividing the width of the user's display by the width of a display with the same height, but 4:3 aspect ratio. + /// TODO: Revisit if taiko playfield ever changes away from a hard-coded 16:9 (see https://github.com/ppy/osu/issues/5685). + /// + private const double slider_multiplier = 1.4 * 4 / 3; public override void ApplyToDifficulty(BeatmapDifficulty difficulty) { From 04fa32bc34852ba79a249529f627b9a6f8aa6dd5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Jan 2021 16:14:21 +0900 Subject: [PATCH 006/121] Rename and add xmldoc for smooth seeking method --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 2 +- .../Timelines/Summary/Parts/MarkerPart.cs | 2 +- .../Compose/Components/BlueprintContainer.cs | 2 +- osu.Game/Screens/Edit/EditorClock.cs | 33 +++++++++++-------- .../Screens/Edit/Timing/ControlPointTable.cs | 2 +- 5 files changed, 24 insertions(+), 17 deletions(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 35852f60ea..e927951d0a 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -332,7 +332,7 @@ namespace osu.Game.Rulesets.Edit EditorBeatmap.Add(hitObject); if (EditorClock.CurrentTime < hitObject.StartTime) - EditorClock.SeekTo(hitObject.StartTime); + EditorClock.SeekSmoothlyTo(hitObject.StartTime); } } diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs index 9e9ac93d23..5a2214509c 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs @@ -58,7 +58,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts return; float markerPos = Math.Clamp(ToLocalSpace(screenPosition).X, 0, DrawWidth); - editorClock.SeekTo(markerPos / DrawWidth * editorClock.TrackLength); + editorClock.SeekSmoothlyTo(markerPos / DrawWidth * editorClock.TrackLength); }); } diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 0b45bd5597..5371beac60 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -170,7 +170,7 @@ namespace osu.Game.Screens.Edit.Compose.Components if (clickedBlueprint == null || SelectionHandler.SelectedBlueprints.FirstOrDefault(b => b.IsHovered) != clickedBlueprint) return false; - EditorClock?.SeekTo(clickedBlueprint.HitObject.StartTime); + EditorClock?.SeekSmoothlyTo(clickedBlueprint.HitObject.StartTime); return true; } diff --git a/osu.Game/Screens/Edit/EditorClock.cs b/osu.Game/Screens/Edit/EditorClock.cs index 148eef6c93..c651d6a7c4 100644 --- a/osu.Game/Screens/Edit/EditorClock.cs +++ b/osu.Game/Screens/Edit/EditorClock.cs @@ -111,7 +111,7 @@ namespace osu.Game.Screens.Edit if (!snapped || ControlPointInfo.TimingPoints.Count == 0) { - SeekTo(seekTime); + SeekSmoothlyTo(seekTime); return; } @@ -145,11 +145,11 @@ namespace osu.Game.Screens.Edit // Ensure the sought point is within the boundaries seekTime = Math.Clamp(seekTime, 0, TrackLength); - SeekTo(seekTime); + SeekSmoothlyTo(seekTime); } /// - /// The current time of this clock, include any active transform seeks performed via . + /// The current time of this clock, include any active transform seeks performed via . /// public double CurrentTimeAccurate => Transforms.OfType().FirstOrDefault()?.EndValue ?? CurrentTime; @@ -182,6 +182,23 @@ namespace osu.Game.Screens.Edit return underlyingClock.Seek(position); } + /// + /// Seek smoothly to the provided destination. + /// Use to perform an immediate seek. + /// + /// + public void SeekSmoothlyTo(double seekDestination) + { + seekingOrStopped.Value = true; + + if (IsRunning) + Seek(seekDestination); + else + { + transformSeekTo(seekDestination, transform_time, Easing.OutQuint); + } + } + public void ResetSpeedAdjustments() => underlyingClock.ResetSpeedAdjustments(); double IAdjustableClock.Rate @@ -243,16 +260,6 @@ namespace osu.Game.Screens.Edit } } - public void SeekTo(double seekDestination) - { - seekingOrStopped.Value = true; - - if (IsRunning) - Seek(seekDestination); - else - transformSeekTo(seekDestination, transform_time, Easing.OutQuint); - } - private void transformSeekTo(double seek, double duration = 0, Easing easing = Easing.None) => this.TransformTo(this.PopulateTransform(new TransformSeek(), seek, duration, easing)); diff --git a/osu.Game/Screens/Edit/Timing/ControlPointTable.cs b/osu.Game/Screens/Edit/Timing/ControlPointTable.cs index 89d3c36250..e4b9150df1 100644 --- a/osu.Game/Screens/Edit/Timing/ControlPointTable.cs +++ b/osu.Game/Screens/Edit/Timing/ControlPointTable.cs @@ -206,7 +206,7 @@ namespace osu.Game.Screens.Edit.Timing Action = () => { selectedGroup.Value = controlGroup; - clock.SeekTo(controlGroup.Time); + clock.SeekSmoothlyTo(controlGroup.Time); }; } From 831c06a3c7c02549fe9bab75ff83afaaf30fa1ec Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Jan 2021 16:14:38 +0900 Subject: [PATCH 007/121] Expose and consume boolean covering whether an ongoing smooth seek is running --- .../Edit/Compose/Components/Timeline/Timeline.cs | 10 ++++++---- osu.Game/Screens/Edit/EditorClock.cs | 11 +++++++++-- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index 20836c0e68..7df4f1ae7d 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -146,12 +146,14 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline seekTrackToCurrent(); else if (!editorClock.IsRunning) { - // The track isn't running. There are two cases we have to be wary of: - // 1) The user flick-drags on this timeline: We want the track to follow us - // 2) The user changes the track time through some other means (scrolling in the editor or overview timeline): We want to follow the track time + // The track isn't running. There are three cases we have to be wary of: + // 1) The user flick-drags on this timeline and we are applying an interpolated seek on the clock, until interrupted by 2 or 3. + // 2) The user changes the track time through some other means (scrolling in the editor or overview timeline; clicking a hitobject etc.). We want the timeline to track the clock's time. + // 3) An ongoing seek transform is running from an external seek. We want the timeline to track the clock's time. // The simplest way to cover both cases is by checking whether the scroll position has changed and the audio hasn't been changed externally - if (Current != lastScrollPosition && editorClock.CurrentTime == lastTrackTime) + // Checking IsSeeking covers the third case, where the transform may not have been applied yet. + if (Current != lastScrollPosition && editorClock.CurrentTime == lastTrackTime && !editorClock.IsSeeking) seekTrackToCurrent(); else scrollToTrackTime(); diff --git a/osu.Game/Screens/Edit/EditorClock.cs b/osu.Game/Screens/Edit/EditorClock.cs index c651d6a7c4..ec0f5d7154 100644 --- a/osu.Game/Screens/Edit/EditorClock.cs +++ b/osu.Game/Screens/Edit/EditorClock.cs @@ -35,6 +35,11 @@ namespace osu.Game.Screens.Edit private readonly Bindable seekingOrStopped = new Bindable(true); + /// + /// Whether a seek is currently in progress. True for the duration of a seek performed via . + /// + public bool IsSeeking { get; private set; } + public EditorClock(WorkingBeatmap beatmap, BindableBeatDivisor beatDivisor) : this(beatmap.Beatmap.ControlPointInfo, beatmap.Track.Length, beatDivisor) { @@ -176,7 +181,7 @@ namespace osu.Game.Screens.Edit public bool Seek(double position) { - seekingOrStopped.Value = true; + seekingOrStopped.Value = IsSeeking = true; ClearTransforms(); return underlyingClock.Seek(position); @@ -246,6 +251,8 @@ namespace osu.Game.Screens.Edit { if (seekingOrStopped.Value) { + IsSeeking &= Transforms.Any(); + if (track.Value?.IsRunning != true) { // seeking in the editor can happen while the track isn't running. @@ -256,7 +263,7 @@ namespace osu.Game.Screens.Edit // we are either running a seek tween or doing an immediate seek. // in the case of an immediate seek the seeking bool will be set to false after one update. // this allows for silencing hit sounds and the likes. - seekingOrStopped.Value = Transforms.Any(); + seekingOrStopped.Value = IsSeeking; } } From b5e784ed427a47c93eae96a2a459b6a4b9dc224c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Jan 2021 16:34:28 +0900 Subject: [PATCH 008/121] Fix possibility of crash when selecting a random skin during skin import --- osu.Game/Overlays/Settings/Sections/SkinSection.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 5898482e4a..123cecb0cd 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -91,7 +91,7 @@ namespace osu.Game.Overlays.Settings.Sections if (skinDropdown.Items.All(s => s.ID != configBindable.Value)) configBindable.Value = 0; - configBindable.BindValueChanged(id => dropdownBindable.Value = skinDropdown.Items.Single(s => s.ID == id.NewValue), true); + configBindable.BindValueChanged(id => Scheduler.AddOnce(updateSelectedSkinFromConfig), true); dropdownBindable.BindValueChanged(skin => { if (skin.NewValue == random_skin_info) @@ -104,6 +104,8 @@ namespace osu.Game.Overlays.Settings.Sections }); } + private void updateSelectedSkinFromConfig() => dropdownBindable.Value = skinDropdown.Items.Single(s => s.ID == configBindable.Value); + private void updateItems() { skinItems = skins.GetAllUsableSkins(); From d6e6b4bbeea9ec532b7d927fa3d38c7e1efbf49f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Jan 2021 17:34:01 +0900 Subject: [PATCH 009/121] Revert forced cloning of ControlPointInfo This reverts commit 3c3e860dbc34d37855b79786a1abb754af1667e8. Closes https://github.com/ppy/osu/issues/11491. --- osu.Game/Beatmaps/Beatmap.cs | 10 +--------- osu.Game/Beatmaps/IBeatmap.cs | 2 +- osu.Game/Beatmaps/WorkingBeatmap.cs | 2 ++ osu.Game/Screens/Edit/EditorBeatmap.cs | 6 +++++- osu.Game/Screens/Play/GameplayBeatmap.cs | 6 +++++- 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index be2006e67a..5435e86dfd 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -50,15 +50,7 @@ namespace osu.Game.Beatmaps IBeatmap IBeatmap.Clone() => Clone(); - public Beatmap Clone() - { - var clone = (Beatmap)MemberwiseClone(); - - clone.ControlPointInfo = ControlPointInfo.CreateCopy(); - // todo: deep clone other elements as required. - - return clone; - } + public Beatmap Clone() => (Beatmap)MemberwiseClone(); } public class Beatmap : Beatmap diff --git a/osu.Game/Beatmaps/IBeatmap.cs b/osu.Game/Beatmaps/IBeatmap.cs index 8f27e0b0e9..7dd85e1232 100644 --- a/osu.Game/Beatmaps/IBeatmap.cs +++ b/osu.Game/Beatmaps/IBeatmap.cs @@ -24,7 +24,7 @@ namespace osu.Game.Beatmaps /// /// The control points in this beatmap. /// - ControlPointInfo ControlPointInfo { get; } + ControlPointInfo ControlPointInfo { get; set; } /// /// The breaks in this beatmap. diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 30382c444f..d25adca92b 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -111,6 +111,8 @@ namespace osu.Game.Beatmaps // Convert IBeatmap converted = converter.Convert(cancellationSource.Token); + converted.ControlPointInfo = converted.ControlPointInfo.CreateCopy(); + // Apply conversion mods to the result foreach (var mod in mods.OfType()) { diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs index 165d2ba278..a54a95f59d 100644 --- a/osu.Game/Screens/Edit/EditorBeatmap.cs +++ b/osu.Game/Screens/Edit/EditorBeatmap.cs @@ -74,7 +74,11 @@ namespace osu.Game.Screens.Edit public BeatmapMetadata Metadata => PlayableBeatmap.Metadata; - public ControlPointInfo ControlPointInfo => PlayableBeatmap.ControlPointInfo; + public ControlPointInfo ControlPointInfo + { + get => PlayableBeatmap.ControlPointInfo; + set => PlayableBeatmap.ControlPointInfo = value; + } public List Breaks => PlayableBeatmap.Breaks; diff --git a/osu.Game/Screens/Play/GameplayBeatmap.cs b/osu.Game/Screens/Play/GameplayBeatmap.cs index 64894544f4..565595656f 100644 --- a/osu.Game/Screens/Play/GameplayBeatmap.cs +++ b/osu.Game/Screens/Play/GameplayBeatmap.cs @@ -29,7 +29,11 @@ namespace osu.Game.Screens.Play public BeatmapMetadata Metadata => PlayableBeatmap.Metadata; - public ControlPointInfo ControlPointInfo => PlayableBeatmap.ControlPointInfo; + public ControlPointInfo ControlPointInfo + { + get => PlayableBeatmap.ControlPointInfo; + set => PlayableBeatmap.ControlPointInfo = value; + } public List Breaks => PlayableBeatmap.Breaks; From 0b165dce4bbbb0647ccbdae48b8d2621173655f6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Jan 2021 17:50:32 +0900 Subject: [PATCH 010/121] Fix multiplayer mod select showing autoplay as a choice --- osu.Game/OsuGameBase.cs | 1 + osu.Game/Overlays/Mods/ModSelectOverlay.cs | 7 +++++-- .../OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs | 5 +++++ osu.Game/Screens/Select/SongSelect.cs | 8 +++----- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 1f8ae54e55..20d88d33f2 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -327,6 +327,7 @@ namespace osu.Game if (!SelectedMods.Disabled) SelectedMods.Value = Array.Empty(); + AvailableMods.Value = dict; } diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 0c8245bebe..b93602116b 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -30,6 +30,7 @@ namespace osu.Game.Overlays.Mods { public class ModSelectOverlay : WaveOverlayContainer { + private readonly Func isValidMod; public const float HEIGHT = 510; protected readonly TriangleButton DeselectAllButton; @@ -60,8 +61,10 @@ namespace osu.Game.Overlays.Mods private SampleChannel sampleOn, sampleOff; - public ModSelectOverlay() + public ModSelectOverlay(Func isValidMod = null) { + this.isValidMod = isValidMod ?? (m => true); + Waves.FirstWaveColour = Color4Extensions.FromHex(@"19b0e2"); Waves.SecondWaveColour = Color4Extensions.FromHex(@"2280a2"); Waves.ThirdWaveColour = Color4Extensions.FromHex(@"005774"); @@ -403,7 +406,7 @@ namespace osu.Game.Overlays.Mods if (mods.NewValue == null) return; foreach (var section in ModSectionsContainer.Children) - section.Mods = mods.NewValue[section.ModType]; + section.Mods = mods.NewValue[section.ModType].Where(isValidMod); } private void selectedModsChanged(ValueChangedEvent> mods) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs index 36dbb9e792..ebc06d2445 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs @@ -13,6 +13,7 @@ using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; +using osu.Game.Overlays.Mods; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Select; @@ -109,5 +110,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer } protected override BeatmapDetailArea CreateBeatmapDetailArea() => new PlayBeatmapDetailArea(); + + protected override ModSelectOverlay CreateModSelectOverlay() => new ModSelectOverlay(isValidMod); + + private bool isValidMod(Mod mod) => !(mod is ModAutoplay) && (mod as MultiMod)?.Mods.Any(mm => mm is ModAutoplay) != true; } } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 6c0bd3a228..4fca77a176 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -251,11 +251,7 @@ namespace osu.Game.Screens.Select Children = new Drawable[] { BeatmapOptions = new BeatmapOptionsOverlay(), - ModSelect = new ModSelectOverlay - { - Origin = Anchor.BottomCentre, - Anchor = Anchor.BottomCentre, - } + ModSelect = CreateModSelectOverlay() } } } @@ -305,6 +301,8 @@ namespace osu.Game.Screens.Select } } + protected virtual ModSelectOverlay CreateModSelectOverlay() => new ModSelectOverlay(); + protected virtual void ApplyFilterToCarousel(FilterCriteria criteria) { // if not the current screen, we want to get carousel in a good presentation state before displaying (resume or enter). From 7476cb3047b400489d39af6494c782de531313ed Mon Sep 17 00:00:00 2001 From: rednir Date: Mon, 18 Jan 2021 19:51:42 +0000 Subject: [PATCH 011/121] Sort SkinSection in alphabetical order --- .../Overlays/Settings/Sections/SkinSection.cs | 35 ++++++++++++++----- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 5898482e4a..0bfa0ba4f0 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -107,21 +107,20 @@ namespace osu.Game.Overlays.Settings.Sections private void updateItems() { skinItems = skins.GetAllUsableSkins(); - - // insert after lazer built-in skins - int firstNonDefault = skinItems.FindIndex(s => s.ID > 0); - if (firstNonDefault < 0) - firstNonDefault = skinItems.Count; - - skinItems.Insert(firstNonDefault, random_skin_info); - + skinItems = sortList(skinItems); + skinDropdown.Items = skinItems; } private void itemUpdated(ValueChangedEvent> weakItem) { if (weakItem.NewValue.TryGetTarget(out var item)) - Schedule(() => skinDropdown.Items = skinDropdown.Items.Where(i => !i.Equals(item)).Append(item).ToArray()); + { + List newDropdownItems = skinDropdown.Items.ToList(); + newDropdownItems.Add(item); + newDropdownItems = sortList(newDropdownItems); + Schedule(() => skinDropdown.Items = newDropdownItems.ToArray()); + } } private void itemRemoved(ValueChangedEvent> weakItem) @@ -130,6 +129,24 @@ namespace osu.Game.Overlays.Settings.Sections Schedule(() => skinDropdown.Items = skinDropdown.Items.Where(i => i.ID != item.ID).ToArray()); } + private List sortList(List skinsList) + { + skinsList.Sort((a, b) => String.Compare(a.Name, b.Name, StringComparison.Ordinal)); + for (int i = 0; i < skinsList.Count; i++) + { + // insert lazer built-in skins before user skins + if (skinsList[i].ID <= 0) { + var itemToMove = skinsList[i]; + skinsList.RemoveAt(i); + skinsList.Insert(0, itemToMove); + } + } + skinsList.RemoveAll(s => s.ID == SkinInfo.RANDOM_SKIN); + skinsList.Insert(0, random_skin_info); + + return skinsList; + } + private class SkinSettingsDropdown : SettingsDropdown { protected override OsuDropdown CreateDropdown() => new SkinDropdownControl(); From 0b65c0cd25a727fbfa121395e515fe9e8bd295b1 Mon Sep 17 00:00:00 2001 From: rednir Date: Mon, 18 Jan 2021 20:17:42 +0000 Subject: [PATCH 012/121] Remove whitespace --- osu.Game/Overlays/Settings/Sections/SkinSection.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 8e78940ac2..20953b7a9b 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -113,7 +113,6 @@ namespace osu.Game.Overlays.Settings.Sections { skinItems = skins.GetAllUsableSkins(); skinItems = sortList(skinItems); - skinDropdown.Items = skinItems; } From 5233a0449a63b3db599d11535b3484c8ebc50f47 Mon Sep 17 00:00:00 2001 From: Mysfit Date: Mon, 18 Jan 2021 16:08:06 -0500 Subject: [PATCH 013/121] Hide main room subscreen on initial mp room creation. Toggle mp room subscreen visibility based on settings overlay visibility before room is created. --- .../Multiplayer/MultiplayerMatchSubScreen.cs | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 80991569dc..df61a0ad21 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -39,6 +39,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private MultiplayerMatchSettingsOverlay settingsOverlay; + private readonly Bindable settingsOverlayVisibility = new Bindable(); + + private GridContainer subScreenContainer; + private IBindable isConnected; [CanBeNull] @@ -55,7 +59,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { InternalChildren = new Drawable[] { - new GridContainer + subScreenContainer = new GridContainer { RelativeSizeAxes = Axes.Both, Content = new[] @@ -178,6 +182,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer State = { Value = client.Room == null ? Visibility.Visible : Visibility.Hidden } } }; + + subScreenContainer.Hide(); + settingsOverlayVisibility.BindTo(settingsOverlay.State); + settingsOverlayVisibility.ValueChanged += settingsOverlayVisibilityChanged; } protected override void LoadComplete() @@ -258,6 +266,22 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer readyClickOperation = null; } + private void settingsOverlayVisibilityChanged(ValueChangedEvent settingsOverlayVisibilityChangedEvent) + { + if (client.Room != null) + { + subScreenContainer.Show(); + settingsOverlayVisibility.ValueChanged -= settingsOverlayVisibilityChanged; + } + else + { + if (settingsOverlayVisibilityChangedEvent.NewValue == Visibility.Visible) + subScreenContainer.Hide(); + else + subScreenContainer.Show(); + } + } + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); From cbfb999c2845074d799d1126296584898d9be52b Mon Sep 17 00:00:00 2001 From: Mysfit Date: Mon, 18 Jan 2021 17:13:24 -0500 Subject: [PATCH 014/121] Use the client.RoomUpdated action instead of binding the value of the settings overlay visibility and creating an event from it based on its ValueChanged action. --- .../Multiplayer/MultiplayerMatchSubScreen.cs | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index df61a0ad21..38c2ca7e0c 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -39,8 +39,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private MultiplayerMatchSettingsOverlay settingsOverlay; - private readonly Bindable settingsOverlayVisibility = new Bindable(); - private GridContainer subScreenContainer; private IBindable isConnected; @@ -184,8 +182,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer }; subScreenContainer.Hide(); - settingsOverlayVisibility.BindTo(settingsOverlay.State); - settingsOverlayVisibility.ValueChanged += settingsOverlayVisibilityChanged; + client.RoomUpdated += roomUpdated; } protected override void LoadComplete() @@ -266,19 +263,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer readyClickOperation = null; } - private void settingsOverlayVisibilityChanged(ValueChangedEvent settingsOverlayVisibilityChangedEvent) + private void roomUpdated() { if (client.Room != null) { + // If the room is updated and is not null, show the room sub screen container and unsubscribe. subScreenContainer.Show(); - settingsOverlayVisibility.ValueChanged -= settingsOverlayVisibilityChanged; - } - else - { - if (settingsOverlayVisibilityChangedEvent.NewValue == Visibility.Visible) - subScreenContainer.Hide(); - else - subScreenContainer.Show(); + client.RoomUpdated -= roomUpdated; } } From f0add0a7cff7b6fe63ca0ccbf0de4caa130fd30d Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 19 Jan 2021 01:34:37 +0300 Subject: [PATCH 015/121] Make BeatmapSetOverlay use OverlayHeader --- .../Online/TestSceneBeatmapSetOverlay.cs | 2 +- .../Overlays/BeatmapSet/BeatmapSetHeader.cs | 300 ++++++++++++++++- osu.Game/Overlays/BeatmapSet/Header.cs | 313 ------------------ osu.Game/Overlays/BeatmapSetOverlay.cs | 9 +- 4 files changed, 302 insertions(+), 322 deletions(-) delete mode 100644 osu.Game/Overlays/BeatmapSet/Header.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs index 689321698a..7ff978c7ca 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs @@ -315,7 +315,7 @@ namespace osu.Game.Tests.Visual.Online private class TestBeatmapSetOverlay : BeatmapSetOverlay { - public new Header Header => base.Header; + public new BeatmapSetHeader Header => base.Header; } } } diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs index 6511b15fc8..bc9008d1f5 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs @@ -1,23 +1,319 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; +using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; +using osu.Framework.Graphics.Shapes; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Drawables; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online; +using osu.Game.Online.API; +using osu.Game.Overlays.BeatmapListing.Panels; +using osu.Game.Overlays.BeatmapSet.Buttons; using osu.Game.Rulesets; +using osuTK; +using osuTK.Graphics; namespace osu.Game.Overlays.BeatmapSet { public class BeatmapSetHeader : OverlayHeader { - public readonly Bindable Ruleset = new Bindable(); + private const float transition_duration = 200; + private const float buttons_height = 45; + private const float buttons_spacing = 5; + + public readonly Bindable BeatmapSet = new Bindable(); + + public bool DownloadButtonsVisible => downloadButtonsContainer.Any(); + + public BeatmapPicker Picker { get; private set; } public BeatmapRulesetSelector RulesetSelector { get; private set; } + private IBindable state => downloadTracker.State; + + [Cached(typeof(IBindable))] + private readonly Bindable ruleset = new Bindable(); + + [Resolved] + private IAPIProvider api { get; set; } + + private readonly DownloadTracker downloadTracker; + private OsuSpriteText title, artist; + private AuthorInfo author; + private ExplicitContentBeatmapPill explicitContentPill; + private FillFlowContainer downloadButtonsContainer; + private BeatmapAvailability beatmapAvailability; + private BeatmapSetOnlineStatusPill onlineStatusPill; + private ExternalLinkButton externalLink; + private UpdateableBeatmapSetCover cover; + private Box coverGradient; + private FillFlowContainer fadeContent; + private FavouriteButton favouriteButton; + private LoadingSpinner loading; + private Details details; + + public BeatmapSetHeader() + { + Masking = true; + + EdgeEffect = new EdgeEffectParameters + { + Colour = Color4.Black.Opacity(0.25f), + Type = EdgeEffectType.Shadow, + Radius = 3, + Offset = new Vector2(0f, 1f), + }; + + AddInternal(downloadTracker = new DownloadTracker + { + BeatmapSet = { BindTarget = BeatmapSet } + }); + } + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + Picker.Beatmap.ValueChanged += b => + { + details.Beatmap = b.NewValue; + externalLink.Link = $@"{api.WebsiteRootUrl}/beatmapsets/{BeatmapSet.Value?.OnlineBeatmapSetID}#{b.NewValue?.Ruleset.ShortName}/{b.NewValue?.OnlineBeatmapID}"; + }; + + coverGradient.Colour = ColourInfo.GradientVertical(colourProvider.Background6.Opacity(0.3f), colourProvider.Background6.Opacity(0.8f)); + onlineStatusPill.BackgroundColour = colourProvider.Background6; + + state.BindValueChanged(_ => updateDownloadButtons()); + + BeatmapSet.BindValueChanged(setInfo => + { + Picker.BeatmapSet = RulesetSelector.BeatmapSet = author.BeatmapSet = beatmapAvailability.BeatmapSet = details.BeatmapSet = setInfo.NewValue; + cover.BeatmapSet = setInfo.NewValue; + + if (setInfo.NewValue == null) + { + onlineStatusPill.FadeTo(0.5f, 500, Easing.OutQuint); + fadeContent.Hide(); + + loading.Show(); + + downloadButtonsContainer.FadeOut(transition_duration); + favouriteButton.FadeOut(transition_duration); + } + else + { + fadeContent.FadeIn(500, Easing.OutQuint); + + loading.Hide(); + + title.Text = setInfo.NewValue.Metadata.Title ?? string.Empty; + artist.Text = setInfo.NewValue.Metadata.Artist ?? string.Empty; + + explicitContentPill.Alpha = setInfo.NewValue.OnlineInfo.HasExplicitContent ? 1 : 0; + + onlineStatusPill.FadeIn(500, Easing.OutQuint); + onlineStatusPill.Status = setInfo.NewValue.OnlineInfo.Status; + + downloadButtonsContainer.FadeIn(transition_duration); + favouriteButton.FadeIn(transition_duration); + + updateDownloadButtons(); + } + }, true); + } + + protected override Drawable CreateContent() => new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + cover = new UpdateableBeatmapSetCover + { + RelativeSizeAxes = Axes.Both, + Masking = true, + }, + coverGradient = new Box + { + RelativeSizeAxes = Axes.Both + }, + }, + }, + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding + { + Vertical = BeatmapSetOverlay.Y_PADDING, + Left = BeatmapSetOverlay.X_PADDING, + Right = BeatmapSetOverlay.X_PADDING + BeatmapSetOverlay.RIGHT_WIDTH, + }, + Children = new Drawable[] + { + fadeContent = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Child = Picker = new BeatmapPicker(), + }, + new FillFlowContainer + { + Direction = FillDirection.Horizontal, + AutoSizeAxes = Axes.Both, + Margin = new MarginPadding { Top = 15 }, + Children = new Drawable[] + { + title = new OsuSpriteText + { + Font = OsuFont.GetFont(size: 30, weight: FontWeight.SemiBold, italics: true) + }, + externalLink = new ExternalLinkButton + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Margin = new MarginPadding { Left = 5, Bottom = 4 }, // To better lineup with the font + }, + explicitContentPill = new ExplicitContentBeatmapPill + { + Alpha = 0f, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Margin = new MarginPadding { Left = 10, Bottom = 4 }, + } + } + }, + artist = new OsuSpriteText + { + Font = OsuFont.GetFont(size: 20, weight: FontWeight.Medium, italics: true), + Margin = new MarginPadding { Bottom = 20 } + }, + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Child = author = new AuthorInfo(), + }, + beatmapAvailability = new BeatmapAvailability(), + new Container + { + RelativeSizeAxes = Axes.X, + Height = buttons_height, + Margin = new MarginPadding { Top = 10 }, + Children = new Drawable[] + { + favouriteButton = new FavouriteButton + { + BeatmapSet = { BindTarget = BeatmapSet } + }, + downloadButtonsContainer = new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Left = buttons_height + buttons_spacing }, + Spacing = new Vector2(buttons_spacing), + }, + }, + }, + }, + }, + } + }, + loading = new LoadingSpinner + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(1.5f), + }, + new FillFlowContainer + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + AutoSizeAxes = Axes.Both, + Margin = new MarginPadding { Top = BeatmapSetOverlay.Y_PADDING, Right = BeatmapSetOverlay.X_PADDING }, + Direction = FillDirection.Vertical, + Spacing = new Vector2(10), + Children = new Drawable[] + { + onlineStatusPill = new BeatmapSetOnlineStatusPill + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + TextSize = 14, + TextPadding = new MarginPadding { Horizontal = 35, Vertical = 10 } + }, + details = new Details(), + }, + }, + } + }; + + private void updateDownloadButtons() + { + if (BeatmapSet.Value == null) return; + + if ((BeatmapSet.Value.OnlineInfo.Availability?.DownloadDisabled ?? false) && state.Value != DownloadState.LocallyAvailable) + { + downloadButtonsContainer.Clear(); + return; + } + + switch (state.Value) + { + case DownloadState.LocallyAvailable: + // temporary for UX until new design is implemented. + downloadButtonsContainer.Child = new BeatmapPanelDownloadButton(BeatmapSet.Value) + { + Width = 50, + RelativeSizeAxes = Axes.Y, + SelectedBeatmap = { BindTarget = Picker.Beatmap } + }; + break; + + case DownloadState.Downloading: + case DownloadState.Importing: + // temporary to avoid showing two buttons for maps with novideo. will be fixed in new beatmap overlay design. + downloadButtonsContainer.Child = new HeaderDownloadButton(BeatmapSet.Value); + break; + + default: + downloadButtonsContainer.Child = new HeaderDownloadButton(BeatmapSet.Value); + if (BeatmapSet.Value.OnlineInfo.HasVideo) + downloadButtonsContainer.Add(new HeaderDownloadButton(BeatmapSet.Value, true)); + break; + } + } + + private class DownloadTracker : BeatmapDownloadTrackingComposite + { + public new Bindable State => base.State; + } + protected override OverlayTitle CreateTitle() => new BeatmapHeaderTitle(); protected override Drawable CreateTitleContent() => RulesetSelector = new BeatmapRulesetSelector { - Current = Ruleset + Current = ruleset }; private class BeatmapHeaderTitle : OverlayTitle diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs deleted file mode 100644 index 916c21c010..0000000000 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ /dev/null @@ -1,313 +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 osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Effects; -using osu.Framework.Graphics.Shapes; -using osu.Game.Beatmaps.Drawables; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; -using osu.Game.Online; -using osu.Game.Online.API; -using osu.Game.Overlays.BeatmapListing.Panels; -using osu.Game.Overlays.BeatmapSet.Buttons; -using osu.Game.Rulesets; -using osuTK; -using osuTK.Graphics; - -namespace osu.Game.Overlays.BeatmapSet -{ - public class Header : BeatmapDownloadTrackingComposite - { - private const float transition_duration = 200; - private const float buttons_height = 45; - private const float buttons_spacing = 5; - - private readonly UpdateableBeatmapSetCover cover; - private readonly Box coverGradient; - private readonly OsuSpriteText title, artist; - private readonly AuthorInfo author; - private readonly ExplicitContentBeatmapPill explicitContentPill; - private readonly FillFlowContainer downloadButtonsContainer; - private readonly BeatmapAvailability beatmapAvailability; - private readonly BeatmapSetOnlineStatusPill onlineStatusPill; - public Details Details; - - public bool DownloadButtonsVisible => downloadButtonsContainer.Any(); - - [Resolved] - private IAPIProvider api { get; set; } - - public BeatmapRulesetSelector RulesetSelector => beatmapSetHeader.RulesetSelector; - public readonly BeatmapPicker Picker; - - private readonly FavouriteButton favouriteButton; - private readonly FillFlowContainer fadeContent; - private readonly LoadingSpinner loading; - private readonly BeatmapSetHeader beatmapSetHeader; - - [Cached(typeof(IBindable))] - private readonly Bindable ruleset = new Bindable(); - - public Header() - { - ExternalLinkButton externalLink; - - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - Masking = true; - - EdgeEffect = new EdgeEffectParameters - { - Colour = Color4.Black.Opacity(0.25f), - Type = EdgeEffectType.Shadow, - Radius = 3, - Offset = new Vector2(0f, 1f), - }; - - InternalChild = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - beatmapSetHeader = new BeatmapSetHeader - { - Ruleset = { BindTarget = ruleset }, - }, - new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - cover = new UpdateableBeatmapSetCover - { - RelativeSizeAxes = Axes.Both, - Masking = true, - }, - coverGradient = new Box - { - RelativeSizeAxes = Axes.Both - }, - }, - }, - new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding - { - Vertical = BeatmapSetOverlay.Y_PADDING, - Left = BeatmapSetOverlay.X_PADDING, - Right = BeatmapSetOverlay.X_PADDING + BeatmapSetOverlay.RIGHT_WIDTH, - }, - Children = new Drawable[] - { - fadeContent = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Child = Picker = new BeatmapPicker(), - }, - new FillFlowContainer - { - Direction = FillDirection.Horizontal, - AutoSizeAxes = Axes.Both, - Margin = new MarginPadding { Top = 15 }, - Children = new Drawable[] - { - title = new OsuSpriteText - { - Font = OsuFont.GetFont(size: 30, weight: FontWeight.SemiBold, italics: true) - }, - externalLink = new ExternalLinkButton - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Margin = new MarginPadding { Left = 5, Bottom = 4 }, // To better lineup with the font - }, - explicitContentPill = new ExplicitContentBeatmapPill - { - Alpha = 0f, - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Margin = new MarginPadding { Left = 10, Bottom = 4 }, - } - } - }, - artist = new OsuSpriteText - { - Font = OsuFont.GetFont(size: 20, weight: FontWeight.Medium, italics: true), - Margin = new MarginPadding { Bottom = 20 } - }, - new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Child = author = new AuthorInfo(), - }, - beatmapAvailability = new BeatmapAvailability(), - new Container - { - RelativeSizeAxes = Axes.X, - Height = buttons_height, - Margin = new MarginPadding { Top = 10 }, - Children = new Drawable[] - { - favouriteButton = new FavouriteButton - { - BeatmapSet = { BindTarget = BeatmapSet } - }, - downloadButtonsContainer = new FillFlowContainer - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Left = buttons_height + buttons_spacing }, - Spacing = new Vector2(buttons_spacing), - }, - }, - }, - }, - }, - } - }, - loading = new LoadingSpinner - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Scale = new Vector2(1.5f), - }, - new FillFlowContainer - { - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - AutoSizeAxes = Axes.Both, - Margin = new MarginPadding { Top = BeatmapSetOverlay.Y_PADDING, Right = BeatmapSetOverlay.X_PADDING }, - Direction = FillDirection.Vertical, - Spacing = new Vector2(10), - Children = new Drawable[] - { - onlineStatusPill = new BeatmapSetOnlineStatusPill - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - TextSize = 14, - TextPadding = new MarginPadding { Horizontal = 35, Vertical = 10 } - }, - Details = new Details(), - }, - }, - }, - }, - } - }; - - Picker.Beatmap.ValueChanged += b => - { - Details.Beatmap = b.NewValue; - externalLink.Link = $@"{api.WebsiteRootUrl}/beatmapsets/{BeatmapSet.Value?.OnlineBeatmapSetID}#{b.NewValue?.Ruleset.ShortName}/{b.NewValue?.OnlineBeatmapID}"; - }; - } - - [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) - { - coverGradient.Colour = ColourInfo.GradientVertical(colourProvider.Background6.Opacity(0.3f), colourProvider.Background6.Opacity(0.8f)); - onlineStatusPill.BackgroundColour = colourProvider.Background6; - - State.BindValueChanged(_ => updateDownloadButtons()); - - BeatmapSet.BindValueChanged(setInfo => - { - Picker.BeatmapSet = RulesetSelector.BeatmapSet = author.BeatmapSet = beatmapAvailability.BeatmapSet = Details.BeatmapSet = setInfo.NewValue; - cover.BeatmapSet = setInfo.NewValue; - - if (setInfo.NewValue == null) - { - onlineStatusPill.FadeTo(0.5f, 500, Easing.OutQuint); - fadeContent.Hide(); - - loading.Show(); - - downloadButtonsContainer.FadeOut(transition_duration); - favouriteButton.FadeOut(transition_duration); - } - else - { - fadeContent.FadeIn(500, Easing.OutQuint); - - loading.Hide(); - - title.Text = setInfo.NewValue.Metadata.Title ?? string.Empty; - artist.Text = setInfo.NewValue.Metadata.Artist ?? string.Empty; - - explicitContentPill.Alpha = setInfo.NewValue.OnlineInfo.HasExplicitContent ? 1 : 0; - - onlineStatusPill.FadeIn(500, Easing.OutQuint); - onlineStatusPill.Status = setInfo.NewValue.OnlineInfo.Status; - - downloadButtonsContainer.FadeIn(transition_duration); - favouriteButton.FadeIn(transition_duration); - - updateDownloadButtons(); - } - }, true); - } - - private void updateDownloadButtons() - { - if (BeatmapSet.Value == null) return; - - if ((BeatmapSet.Value.OnlineInfo.Availability?.DownloadDisabled ?? false) && State.Value != DownloadState.LocallyAvailable) - { - downloadButtonsContainer.Clear(); - return; - } - - switch (State.Value) - { - case DownloadState.LocallyAvailable: - // temporary for UX until new design is implemented. - downloadButtonsContainer.Child = new BeatmapPanelDownloadButton(BeatmapSet.Value) - { - Width = 50, - RelativeSizeAxes = Axes.Y, - SelectedBeatmap = { BindTarget = Picker.Beatmap } - }; - break; - - case DownloadState.Downloading: - case DownloadState.Importing: - // temporary to avoid showing two buttons for maps with novideo. will be fixed in new beatmap overlay design. - downloadButtonsContainer.Child = new HeaderDownloadButton(BeatmapSet.Value); - break; - - default: - downloadButtonsContainer.Child = new HeaderDownloadButton(BeatmapSet.Value); - if (BeatmapSet.Value.OnlineInfo.HasVideo) - downloadButtonsContainer.Add(new HeaderDownloadButton(BeatmapSet.Value, true)); - break; - } - } - } -} diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index bbec62a85a..86f0f4f614 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -19,15 +19,12 @@ using osuTK; namespace osu.Game.Overlays { - public class BeatmapSetOverlay : FullscreenOverlay // we don't provide a standard header for now. + public class BeatmapSetOverlay : FullscreenOverlay { public const float X_PADDING = 40; public const float Y_PADDING = 25; public const float RIGHT_WIDTH = 275; - //todo: should be an OverlayHeader? or maybe not? - protected new readonly Header Header; - [Resolved] private RulesetStore rulesets { get; set; } @@ -39,7 +36,7 @@ namespace osu.Game.Overlays private readonly Box background; public BeatmapSetOverlay() - : base(OverlayColourScheme.Blue, null) + : base(OverlayColourScheme.Blue, new BeatmapSetHeader()) { OverlayScrollContainer scroll; Info info; @@ -72,7 +69,7 @@ namespace osu.Game.Overlays Direction = FillDirection.Vertical, Children = new Drawable[] { - Header = new Header(), + Header, info = new Info() } }, From 3a7608275d5b0188c3ab70df2ae9482fad252392 Mon Sep 17 00:00:00 2001 From: Mysfit Date: Tue, 19 Jan 2021 00:35:56 -0500 Subject: [PATCH 016/121] Use fades instead of event listening. Fixed same issue in the playlist room creation. --- .../Multiplayer/MultiplayerMatchSubScreen.cs | 45 ++++++++++++++----- .../Playlists/PlaylistsRoomSubScreen.cs | 38 +++++++++++++++- 2 files changed, 70 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 38c2ca7e0c..03a76c66f2 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -180,9 +180,40 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer State = { Value = client.Room == null ? Visibility.Visible : Visibility.Hidden } } }; + } - subScreenContainer.Hide(); - client.RoomUpdated += roomUpdated; + public override void OnEntering(IScreen last) + { + base.OnEntering(last); + + subScreenContainer.FadeOut().Delay(1000).FadeIn(500); + } + + public override bool OnExiting(IScreen next) + { + if (base.OnExiting(next)) + return true; + + subScreenContainer.FadeOut(); + + return false; + } + + public override void OnResuming(IScreen last) + { + base.OnResuming(last); + + if (client.Room == null) + subScreenContainer.FadeOut().Delay(1000).FadeIn(500); + else + subScreenContainer.FadeInFromZero(); + } + + public override void OnSuspending(IScreen next) + { + subScreenContainer.FadeOut(); + + base.OnSuspending(next); } protected override void LoadComplete() @@ -263,16 +294,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer readyClickOperation = null; } - private void roomUpdated() - { - if (client.Room != null) - { - // If the room is updated and is not null, show the room sub screen container and unsubscribe. - subScreenContainer.Show(); - client.RoomUpdated -= roomUpdated; - } - } - protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs index e76ca995bf..583956b3f1 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs @@ -33,6 +33,8 @@ namespace osu.Game.Screens.OnlinePlay.Playlists private OverlinedHeader participantsHeader; + private GridContainer subScreenContainer; + public PlaylistsRoomSubScreen(Room room) { Title = room.RoomID.Value == null ? "New playlist" : room.Name.Value; @@ -44,7 +46,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists { InternalChildren = new Drawable[] { - new GridContainer + subScreenContainer = new GridContainer { RelativeSizeAxes = Axes.Both, Content = new[] @@ -195,6 +197,40 @@ namespace osu.Game.Screens.OnlinePlay.Playlists [Resolved] private IAPIProvider api { get; set; } + public override void OnEntering(IScreen last) + { + base.OnEntering(last); + + subScreenContainer.FadeOut().Delay(1000).FadeIn(500); + } + + public override bool OnExiting(IScreen next) + { + if (base.OnExiting(next)) + return true; + + subScreenContainer.FadeOut(); + + return false; + } + + public override void OnResuming(IScreen last) + { + base.OnResuming(last); + + if (roomId.Value == null) + subScreenContainer.FadeOut().Delay(1000).FadeIn(500); + else + subScreenContainer.FadeInFromZero(); + } + + public override void OnSuspending(IScreen next) + { + subScreenContainer.FadeOut(); + + base.OnSuspending(next); + } + protected override void LoadComplete() { base.LoadComplete(); From f1894a8bacb83c8e35c62422447d2d7cd78a3aa9 Mon Sep 17 00:00:00 2001 From: rednir Date: Tue, 19 Jan 2021 12:17:56 +0000 Subject: [PATCH 017/121] fixed itemUpdated() --- osu.Game/Overlays/Settings/Sections/SkinSection.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 0bfa0ba4f0..ba92ed5b04 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -116,8 +116,7 @@ namespace osu.Game.Overlays.Settings.Sections { if (weakItem.NewValue.TryGetTarget(out var item)) { - List newDropdownItems = skinDropdown.Items.ToList(); - newDropdownItems.Add(item); + List newDropdownItems = skinDropdown.Items.Where(i => !i.Equals(item)).Append(item).ToList(); newDropdownItems = sortList(newDropdownItems); Schedule(() => skinDropdown.Items = newDropdownItems.ToArray()); } From 9b7187e3c855fa50dfc8331f43730a113ed95059 Mon Sep 17 00:00:00 2001 From: Mysfit Date: Tue, 19 Jan 2021 08:23:31 -0500 Subject: [PATCH 018/121] Revert "Use fades instead of event listening. Fixed same issue in the playlist room creation." This reverts commit 3a7608275d5b0188c3ab70df2ae9482fad252392. --- .../Multiplayer/MultiplayerMatchSubScreen.cs | 45 +++++-------------- .../Playlists/PlaylistsRoomSubScreen.cs | 38 +--------------- 2 files changed, 13 insertions(+), 70 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 03a76c66f2..38c2ca7e0c 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -180,40 +180,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer State = { Value = client.Room == null ? Visibility.Visible : Visibility.Hidden } } }; - } - public override void OnEntering(IScreen last) - { - base.OnEntering(last); - - subScreenContainer.FadeOut().Delay(1000).FadeIn(500); - } - - public override bool OnExiting(IScreen next) - { - if (base.OnExiting(next)) - return true; - - subScreenContainer.FadeOut(); - - return false; - } - - public override void OnResuming(IScreen last) - { - base.OnResuming(last); - - if (client.Room == null) - subScreenContainer.FadeOut().Delay(1000).FadeIn(500); - else - subScreenContainer.FadeInFromZero(); - } - - public override void OnSuspending(IScreen next) - { - subScreenContainer.FadeOut(); - - base.OnSuspending(next); + subScreenContainer.Hide(); + client.RoomUpdated += roomUpdated; } protected override void LoadComplete() @@ -294,6 +263,16 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer readyClickOperation = null; } + private void roomUpdated() + { + if (client.Room != null) + { + // If the room is updated and is not null, show the room sub screen container and unsubscribe. + subScreenContainer.Show(); + client.RoomUpdated -= roomUpdated; + } + } + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs index 583956b3f1..e76ca995bf 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs @@ -33,8 +33,6 @@ namespace osu.Game.Screens.OnlinePlay.Playlists private OverlinedHeader participantsHeader; - private GridContainer subScreenContainer; - public PlaylistsRoomSubScreen(Room room) { Title = room.RoomID.Value == null ? "New playlist" : room.Name.Value; @@ -46,7 +44,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists { InternalChildren = new Drawable[] { - subScreenContainer = new GridContainer + new GridContainer { RelativeSizeAxes = Axes.Both, Content = new[] @@ -197,40 +195,6 @@ namespace osu.Game.Screens.OnlinePlay.Playlists [Resolved] private IAPIProvider api { get; set; } - public override void OnEntering(IScreen last) - { - base.OnEntering(last); - - subScreenContainer.FadeOut().Delay(1000).FadeIn(500); - } - - public override bool OnExiting(IScreen next) - { - if (base.OnExiting(next)) - return true; - - subScreenContainer.FadeOut(); - - return false; - } - - public override void OnResuming(IScreen last) - { - base.OnResuming(last); - - if (roomId.Value == null) - subScreenContainer.FadeOut().Delay(1000).FadeIn(500); - else - subScreenContainer.FadeInFromZero(); - } - - public override void OnSuspending(IScreen next) - { - subScreenContainer.FadeOut(); - - base.OnSuspending(next); - } - protected override void LoadComplete() { base.LoadComplete(); From 6d1d488831f1992957b6817eaa590114e6db60b5 Mon Sep 17 00:00:00 2001 From: Mysfit Date: Tue, 19 Jan 2021 08:24:14 -0500 Subject: [PATCH 019/121] Revert "Use the client.RoomUpdated action instead of binding the value of the settings overlay visibility and creating an event from it based on its ValueChanged action." This reverts commit cbfb999c2845074d799d1126296584898d9be52b. --- .../Multiplayer/MultiplayerMatchSubScreen.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 38c2ca7e0c..df61a0ad21 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -39,6 +39,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private MultiplayerMatchSettingsOverlay settingsOverlay; + private readonly Bindable settingsOverlayVisibility = new Bindable(); + private GridContainer subScreenContainer; private IBindable isConnected; @@ -182,7 +184,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer }; subScreenContainer.Hide(); - client.RoomUpdated += roomUpdated; + settingsOverlayVisibility.BindTo(settingsOverlay.State); + settingsOverlayVisibility.ValueChanged += settingsOverlayVisibilityChanged; } protected override void LoadComplete() @@ -263,13 +266,19 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer readyClickOperation = null; } - private void roomUpdated() + private void settingsOverlayVisibilityChanged(ValueChangedEvent settingsOverlayVisibilityChangedEvent) { if (client.Room != null) { - // If the room is updated and is not null, show the room sub screen container and unsubscribe. subScreenContainer.Show(); - client.RoomUpdated -= roomUpdated; + settingsOverlayVisibility.ValueChanged -= settingsOverlayVisibilityChanged; + } + else + { + if (settingsOverlayVisibilityChangedEvent.NewValue == Visibility.Visible) + subScreenContainer.Hide(); + else + subScreenContainer.Show(); } } From 33677f57702f2b17dd6dd9e385a4553aa66bdd9a Mon Sep 17 00:00:00 2001 From: Mysfit Date: Tue, 19 Jan 2021 08:52:43 -0500 Subject: [PATCH 020/121] Use BindValueChanged to show main content for new multiplayer and playlist rooms when the settings overlay is hidden. --- .../Multiplayer/MultiplayerMatchSubScreen.cs | 37 +++++++------------ .../Playlists/PlaylistsRoomSubScreen.cs | 15 +++++++- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index df61a0ad21..0ec43c2b10 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -39,15 +39,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private MultiplayerMatchSettingsOverlay settingsOverlay; - private readonly Bindable settingsOverlayVisibility = new Bindable(); - - private GridContainer subScreenContainer; - private IBindable isConnected; [CanBeNull] private IDisposable readyClickOperation; + private GridContainer mainContent; + public MultiplayerMatchSubScreen(Room room) { Title = room.RoomID.Value == null ? "New room" : room.Name.Value; @@ -59,7 +57,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { InternalChildren = new Drawable[] { - subScreenContainer = new GridContainer + mainContent = new GridContainer { RelativeSizeAxes = Axes.Both, Content = new[] @@ -183,9 +181,16 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer } }; - subScreenContainer.Hide(); - settingsOverlayVisibility.BindTo(settingsOverlay.State); - settingsOverlayVisibility.ValueChanged += settingsOverlayVisibilityChanged; + if (client.Room == null) + { + mainContent.Hide(); + + settingsOverlay.State.BindValueChanged(visibility => + { + if (visibility.NewValue == Visibility.Hidden) + mainContent.Show(); + }, true); + } } protected override void LoadComplete() @@ -266,22 +271,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer readyClickOperation = null; } - private void settingsOverlayVisibilityChanged(ValueChangedEvent settingsOverlayVisibilityChangedEvent) - { - if (client.Room != null) - { - subScreenContainer.Show(); - settingsOverlayVisibility.ValueChanged -= settingsOverlayVisibilityChanged; - } - else - { - if (settingsOverlayVisibilityChangedEvent.NewValue == Visibility.Visible) - subScreenContainer.Hide(); - else - subScreenContainer.Show(); - } - } - protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs index e76ca995bf..b8d9d258f5 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs @@ -33,6 +33,8 @@ namespace osu.Game.Screens.OnlinePlay.Playlists private OverlinedHeader participantsHeader; + private GridContainer mainContent; + public PlaylistsRoomSubScreen(Room room) { Title = room.RoomID.Value == null ? "New playlist" : room.Name.Value; @@ -44,7 +46,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists { InternalChildren = new Drawable[] { - new GridContainer + mainContent = new GridContainer { RelativeSizeAxes = Axes.Both, Content = new[] @@ -190,6 +192,17 @@ namespace osu.Game.Screens.OnlinePlay.Playlists State = { Value = roomId.Value == null ? Visibility.Visible : Visibility.Hidden } } }; + + if (roomId.Value == null) + { + mainContent.Hide(); + + settingsOverlay.State.BindValueChanged(visibility => + { + if (visibility.NewValue == Visibility.Hidden) + mainContent.Show(); + }, true); + } } [Resolved] From 31e61326e1ea4d4f3b3e1e8450c8fdc385939d0d Mon Sep 17 00:00:00 2001 From: rednir Date: Tue, 19 Jan 2021 14:00:17 +0000 Subject: [PATCH 021/121] Only user skins are sorted --- .../Overlays/Settings/Sections/SkinSection.cs | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index ba92ed5b04..1dcc5d824d 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -38,6 +38,8 @@ namespace osu.Game.Overlays.Settings.Sections private List skinItems; + private int firstNonDefault; + [Resolved] private SkinManager skins { get; set; } @@ -107,8 +109,10 @@ namespace osu.Game.Overlays.Settings.Sections private void updateItems() { skinItems = skins.GetAllUsableSkins(); + firstNonDefault = skinItems.FindIndex(s => s.ID > 0); + + skinItems.Insert(firstNonDefault, random_skin_info); skinItems = sortList(skinItems); - skinDropdown.Items = skinItems; } @@ -130,19 +134,12 @@ namespace osu.Game.Overlays.Settings.Sections private List sortList(List skinsList) { - skinsList.Sort((a, b) => String.Compare(a.Name, b.Name, StringComparison.Ordinal)); - for (int i = 0; i < skinsList.Count; i++) - { - // insert lazer built-in skins before user skins - if (skinsList[i].ID <= 0) { - var itemToMove = skinsList[i]; - skinsList.RemoveAt(i); - skinsList.Insert(0, itemToMove); - } - } - skinsList.RemoveAll(s => s.ID == SkinInfo.RANDOM_SKIN); - skinsList.Insert(0, random_skin_info); - + // Sort user skins seperate from built-in skins + List userSkinsList = skinsList.GetRange(firstNonDefault + 1, skinsList.Count - (firstNonDefault + 1)); + skinsList.RemoveRange(firstNonDefault + 1, skinsList.Count - (firstNonDefault + 1)); + userSkinsList.Sort((a, b) => String.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase)); + + skinsList.AddRange(userSkinsList); return skinsList; } From c5c5fdca454cd9f1a036eba686d33673d074c038 Mon Sep 17 00:00:00 2001 From: rednir Date: Tue, 19 Jan 2021 14:10:08 +0000 Subject: [PATCH 022/121] Remove another whitespace --- osu.Game/Overlays/Settings/Sections/SkinSection.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index b42091ce30..9ca8ec9051 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -143,7 +143,6 @@ namespace osu.Game.Overlays.Settings.Sections List userSkinsList = skinsList.GetRange(firstNonDefault + 1, skinsList.Count - (firstNonDefault + 1)); skinsList.RemoveRange(firstNonDefault + 1, skinsList.Count - (firstNonDefault + 1)); userSkinsList.Sort((a, b) => String.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase)); - skinsList.AddRange(userSkinsList); return skinsList; } From b265d2dab499f4cdf75fadbc1acc20acb2f21ad3 Mon Sep 17 00:00:00 2001 From: rednir Date: Tue, 19 Jan 2021 14:16:22 +0000 Subject: [PATCH 023/121] Remove another whitespace --- osu.Game/Overlays/Settings/Sections/SkinSection.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index b42091ce30..9ca8ec9051 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -143,7 +143,6 @@ namespace osu.Game.Overlays.Settings.Sections List userSkinsList = skinsList.GetRange(firstNonDefault + 1, skinsList.Count - (firstNonDefault + 1)); skinsList.RemoveRange(firstNonDefault + 1, skinsList.Count - (firstNonDefault + 1)); userSkinsList.Sort((a, b) => String.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase)); - skinsList.AddRange(userSkinsList); return skinsList; } From 052e9eef02661abf004152ac29c015a329ad20d3 Mon Sep 17 00:00:00 2001 From: Mysfit Date: Tue, 19 Jan 2021 09:16:39 -0500 Subject: [PATCH 024/121] Added inline comments --- .../Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs | 2 ++ osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 0ec43c2b10..7c4b6d18ec 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -183,6 +183,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer if (client.Room == null) { + // A new room is being created. + // The main content should be hidden until the settings overlay is hidden, signaling the room is ready to be displayed. mainContent.Hide(); settingsOverlay.State.BindValueChanged(visibility => diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs index b8d9d258f5..22580f0537 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs @@ -195,6 +195,8 @@ namespace osu.Game.Screens.OnlinePlay.Playlists if (roomId.Value == null) { + // A new room is being created. + // The main content should be hidden until the settings overlay is hidden, signaling the room is ready to be displayed. mainContent.Hide(); settingsOverlay.State.BindValueChanged(visibility => From 14ff2af00ef868bd1ff65dd22f7eed242d7c2e8b Mon Sep 17 00:00:00 2001 From: rednir Date: Tue, 19 Jan 2021 15:37:59 +0000 Subject: [PATCH 025/121] Satisfy AppVeyor --- osu.Game/Overlays/Settings/Sections/SkinSection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 9ca8ec9051..7023702d0e 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -142,7 +142,7 @@ namespace osu.Game.Overlays.Settings.Sections // Sort user skins seperate from built-in skins List userSkinsList = skinsList.GetRange(firstNonDefault + 1, skinsList.Count - (firstNonDefault + 1)); skinsList.RemoveRange(firstNonDefault + 1, skinsList.Count - (firstNonDefault + 1)); - userSkinsList.Sort((a, b) => String.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase)); + userSkinsList.Sort((a, b) => System.String.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase)); skinsList.AddRange(userSkinsList); return skinsList; } From a880b8d21dae103d8e3340678384356b9218b4f2 Mon Sep 17 00:00:00 2001 From: rednir Date: Tue, 19 Jan 2021 16:11:16 +0000 Subject: [PATCH 026/121] Satisfy AppVeyor --- osu.Game/Overlays/Settings/Sections/SkinSection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 9ca8ec9051..c39f95d93a 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -142,7 +142,7 @@ namespace osu.Game.Overlays.Settings.Sections // Sort user skins seperate from built-in skins List userSkinsList = skinsList.GetRange(firstNonDefault + 1, skinsList.Count - (firstNonDefault + 1)); skinsList.RemoveRange(firstNonDefault + 1, skinsList.Count - (firstNonDefault + 1)); - userSkinsList.Sort((a, b) => String.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase)); + userSkinsList.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase)); skinsList.AddRange(userSkinsList); return skinsList; } From 206a0b8bace7733f6ac6f12909adcaecb6472d08 Mon Sep 17 00:00:00 2001 From: rednir Date: Tue, 19 Jan 2021 16:55:50 +0000 Subject: [PATCH 027/121] Fix firstNonDefault staying as -1 --- osu.Game/Overlays/Settings/Sections/SkinSection.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index c39f95d93a..a3e472a9f9 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -115,7 +115,8 @@ namespace osu.Game.Overlays.Settings.Sections { skinItems = skins.GetAllUsableSkins(); firstNonDefault = skinItems.FindIndex(s => s.ID > 0); - + if (firstNonDefault < 0) + firstNonDefault = skinItems.Count; skinItems.Insert(firstNonDefault, random_skin_info); skinItems = sortList(skinItems); skinDropdown.Items = skinItems; From b00c6a1d60723622101512b13168214ff61696aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 19 Jan 2021 18:29:55 +0100 Subject: [PATCH 028/121] Make first non-default skin index a property The previous code was very brittle - it was not always updating properly, and seems to have worked either by a carefully crafted set of circumstances, or just plain coincidence. Having this be a get-only property avoids potential error in the future caused by not updating the index properly, at the expense of an added linear lookup. --- .../Overlays/Settings/Sections/SkinSection.cs | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index a3e472a9f9..bbb4c50f6b 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -38,7 +38,17 @@ namespace osu.Game.Overlays.Settings.Sections private List skinItems; - private int firstNonDefault; + private int firstNonDefaultSkinIndex + { + get + { + var index = skinItems.FindIndex(s => s.ID > 0); + if (index < 0) + index = skinItems.Count; + + return index; + } + } [Resolved] private SkinManager skins { get; set; } @@ -114,10 +124,7 @@ namespace osu.Game.Overlays.Settings.Sections private void updateItems() { skinItems = skins.GetAllUsableSkins(); - firstNonDefault = skinItems.FindIndex(s => s.ID > 0); - if (firstNonDefault < 0) - firstNonDefault = skinItems.Count; - skinItems.Insert(firstNonDefault, random_skin_info); + skinItems.Insert(firstNonDefaultSkinIndex, random_skin_info); skinItems = sortList(skinItems); skinDropdown.Items = skinItems; } @@ -141,8 +148,8 @@ namespace osu.Game.Overlays.Settings.Sections private List sortList(List skinsList) { // Sort user skins seperate from built-in skins - List userSkinsList = skinsList.GetRange(firstNonDefault + 1, skinsList.Count - (firstNonDefault + 1)); - skinsList.RemoveRange(firstNonDefault + 1, skinsList.Count - (firstNonDefault + 1)); + List userSkinsList = skinsList.GetRange(firstNonDefaultSkinIndex, skinsList.Count - firstNonDefaultSkinIndex); + skinsList.RemoveRange(firstNonDefaultSkinIndex, skinsList.Count - firstNonDefaultSkinIndex); userSkinsList.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase)); skinsList.AddRange(userSkinsList); return skinsList; From 78e590d25da4b570447d8fb3b8462f56fa52f50c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 19 Jan 2021 18:36:42 +0100 Subject: [PATCH 029/121] Refactor skin sorting method * Rename to `sortUserSkins` to convey meaning better. * Sort in-place instead of slicing the list. * Change to `void` to avoid misleading users that the method returns a new list instance. * Fix typo in comment. --- .../Overlays/Settings/Sections/SkinSection.cs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index bbb4c50f6b..75c0324408 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -125,7 +125,7 @@ namespace osu.Game.Overlays.Settings.Sections { skinItems = skins.GetAllUsableSkins(); skinItems.Insert(firstNonDefaultSkinIndex, random_skin_info); - skinItems = sortList(skinItems); + sortUserSkins(skinItems); skinDropdown.Items = skinItems; } @@ -134,7 +134,7 @@ namespace osu.Game.Overlays.Settings.Sections if (weakItem.NewValue.TryGetTarget(out var item)) { List newDropdownItems = skinDropdown.Items.Where(i => !i.Equals(item)).Append(item).ToList(); - newDropdownItems = sortList(newDropdownItems); + sortUserSkins(newDropdownItems); Schedule(() => skinDropdown.Items = newDropdownItems.ToArray()); } } @@ -145,14 +145,11 @@ namespace osu.Game.Overlays.Settings.Sections Schedule(() => skinDropdown.Items = skinDropdown.Items.Where(i => i.ID != item.ID).ToArray()); } - private List sortList(List skinsList) + private void sortUserSkins(List skinsList) { - // Sort user skins seperate from built-in skins - List userSkinsList = skinsList.GetRange(firstNonDefaultSkinIndex, skinsList.Count - firstNonDefaultSkinIndex); - skinsList.RemoveRange(firstNonDefaultSkinIndex, skinsList.Count - firstNonDefaultSkinIndex); - userSkinsList.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase)); - skinsList.AddRange(userSkinsList); - return skinsList; + // Sort user skins separately from built-in skins + skinsList.Sort(firstNonDefaultSkinIndex, skinsList.Count - firstNonDefaultSkinIndex, + Comparer.Create((a, b) => string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase))); } private class SkinSettingsDropdown : SettingsDropdown From 3b49b7461ef628031ba3440ea795cf5a42073a2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 19 Jan 2021 18:46:21 +0100 Subject: [PATCH 030/121] Schedule entire operation for safety Also removes a redundant list copy. --- osu.Game/Overlays/Settings/Sections/SkinSection.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 75c0324408..4cfd801caf 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -133,9 +133,12 @@ namespace osu.Game.Overlays.Settings.Sections { if (weakItem.NewValue.TryGetTarget(out var item)) { - List newDropdownItems = skinDropdown.Items.Where(i => !i.Equals(item)).Append(item).ToList(); - sortUserSkins(newDropdownItems); - Schedule(() => skinDropdown.Items = newDropdownItems.ToArray()); + Schedule(() => + { + List newDropdownItems = skinDropdown.Items.Where(i => !i.Equals(item)).Append(item).ToList(); + sortUserSkins(newDropdownItems); + skinDropdown.Items = newDropdownItems; + }); } } From 2ca3ccad0647c28f20b9aedaf0d560baf98b9de9 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 20 Jan 2021 04:56:46 +0300 Subject: [PATCH 031/121] Move all the content to BeatmapSetHeaderContent drawable --- .../Online/TestSceneBeatmapSetOverlay.cs | 6 +- .../Overlays/BeatmapSet/BeatmapSetHeader.cs | 278 +---------------- .../BeatmapSet/BeatmapSetHeaderContent.cs | 283 ++++++++++++++++++ osu.Game/Overlays/BeatmapSetOverlay.cs | 6 +- 4 files changed, 295 insertions(+), 278 deletions(-) create mode 100644 osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs index 7ff978c7ca..edc1696456 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs @@ -231,8 +231,8 @@ namespace osu.Game.Tests.Visual.Online }); }); - AddAssert("shown beatmaps of current ruleset", () => overlay.Header.Picker.Difficulties.All(b => b.Beatmap.Ruleset.Equals(overlay.Header.RulesetSelector.Current.Value))); - AddAssert("left-most beatmap selected", () => overlay.Header.Picker.Difficulties.First().State == BeatmapPicker.DifficultySelectorState.Selected); + AddAssert("shown beatmaps of current ruleset", () => overlay.Header.HeaderContent.Picker.Difficulties.All(b => b.Beatmap.Ruleset.Equals(overlay.Header.RulesetSelector.Current.Value))); + AddAssert("left-most beatmap selected", () => overlay.Header.HeaderContent.Picker.Difficulties.First().State == BeatmapPicker.DifficultySelectorState.Selected); } [Test] @@ -310,7 +310,7 @@ namespace osu.Game.Tests.Visual.Online private void downloadAssert(bool shown) { - AddAssert($"is download button {(shown ? "shown" : "hidden")}", () => overlay.Header.DownloadButtonsVisible == shown); + AddAssert($"is download button {(shown ? "shown" : "hidden")}", () => overlay.Header.HeaderContent.DownloadButtonsVisible == shown); } private class TestBeatmapSetOverlay : BeatmapSetOverlay diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs index bc9008d1f5..4b26b02a8e 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs @@ -1,24 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; -using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; -using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Drawables; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; -using osu.Game.Online; -using osu.Game.Online.API; -using osu.Game.Overlays.BeatmapListing.Panels; -using osu.Game.Overlays.BeatmapSet.Buttons; using osu.Game.Rulesets; using osuTK; using osuTK.Graphics; @@ -27,41 +15,16 @@ namespace osu.Game.Overlays.BeatmapSet { public class BeatmapSetHeader : OverlayHeader { - private const float transition_duration = 200; - private const float buttons_height = 45; - private const float buttons_spacing = 5; - public readonly Bindable BeatmapSet = new Bindable(); - public bool DownloadButtonsVisible => downloadButtonsContainer.Any(); - - public BeatmapPicker Picker { get; private set; } + public BeatmapSetHeaderContent HeaderContent { get; private set; } + [Cached] public BeatmapRulesetSelector RulesetSelector { get; private set; } - private IBindable state => downloadTracker.State; - [Cached(typeof(IBindable))] private readonly Bindable ruleset = new Bindable(); - [Resolved] - private IAPIProvider api { get; set; } - - private readonly DownloadTracker downloadTracker; - private OsuSpriteText title, artist; - private AuthorInfo author; - private ExplicitContentBeatmapPill explicitContentPill; - private FillFlowContainer downloadButtonsContainer; - private BeatmapAvailability beatmapAvailability; - private BeatmapSetOnlineStatusPill onlineStatusPill; - private ExternalLinkButton externalLink; - private UpdateableBeatmapSetCover cover; - private Box coverGradient; - private FillFlowContainer fadeContent; - private FavouriteButton favouriteButton; - private LoadingSpinner loading; - private Details details; - public BeatmapSetHeader() { Masking = true; @@ -73,249 +36,20 @@ namespace osu.Game.Overlays.BeatmapSet Radius = 3, Offset = new Vector2(0f, 1f), }; - - AddInternal(downloadTracker = new DownloadTracker - { - BeatmapSet = { BindTarget = BeatmapSet } - }); } - [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) + protected override Drawable CreateContent() => HeaderContent = new BeatmapSetHeaderContent { - Picker.Beatmap.ValueChanged += b => - { - details.Beatmap = b.NewValue; - externalLink.Link = $@"{api.WebsiteRootUrl}/beatmapsets/{BeatmapSet.Value?.OnlineBeatmapSetID}#{b.NewValue?.Ruleset.ShortName}/{b.NewValue?.OnlineBeatmapID}"; - }; - - coverGradient.Colour = ColourInfo.GradientVertical(colourProvider.Background6.Opacity(0.3f), colourProvider.Background6.Opacity(0.8f)); - onlineStatusPill.BackgroundColour = colourProvider.Background6; - - state.BindValueChanged(_ => updateDownloadButtons()); - - BeatmapSet.BindValueChanged(setInfo => - { - Picker.BeatmapSet = RulesetSelector.BeatmapSet = author.BeatmapSet = beatmapAvailability.BeatmapSet = details.BeatmapSet = setInfo.NewValue; - cover.BeatmapSet = setInfo.NewValue; - - if (setInfo.NewValue == null) - { - onlineStatusPill.FadeTo(0.5f, 500, Easing.OutQuint); - fadeContent.Hide(); - - loading.Show(); - - downloadButtonsContainer.FadeOut(transition_duration); - favouriteButton.FadeOut(transition_duration); - } - else - { - fadeContent.FadeIn(500, Easing.OutQuint); - - loading.Hide(); - - title.Text = setInfo.NewValue.Metadata.Title ?? string.Empty; - artist.Text = setInfo.NewValue.Metadata.Artist ?? string.Empty; - - explicitContentPill.Alpha = setInfo.NewValue.OnlineInfo.HasExplicitContent ? 1 : 0; - - onlineStatusPill.FadeIn(500, Easing.OutQuint); - onlineStatusPill.Status = setInfo.NewValue.OnlineInfo.Status; - - downloadButtonsContainer.FadeIn(transition_duration); - favouriteButton.FadeIn(transition_duration); - - updateDownloadButtons(); - } - }, true); - } - - protected override Drawable CreateContent() => new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - cover = new UpdateableBeatmapSetCover - { - RelativeSizeAxes = Axes.Both, - Masking = true, - }, - coverGradient = new Box - { - RelativeSizeAxes = Axes.Both - }, - }, - }, - new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding - { - Vertical = BeatmapSetOverlay.Y_PADDING, - Left = BeatmapSetOverlay.X_PADDING, - Right = BeatmapSetOverlay.X_PADDING + BeatmapSetOverlay.RIGHT_WIDTH, - }, - Children = new Drawable[] - { - fadeContent = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Child = Picker = new BeatmapPicker(), - }, - new FillFlowContainer - { - Direction = FillDirection.Horizontal, - AutoSizeAxes = Axes.Both, - Margin = new MarginPadding { Top = 15 }, - Children = new Drawable[] - { - title = new OsuSpriteText - { - Font = OsuFont.GetFont(size: 30, weight: FontWeight.SemiBold, italics: true) - }, - externalLink = new ExternalLinkButton - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Margin = new MarginPadding { Left = 5, Bottom = 4 }, // To better lineup with the font - }, - explicitContentPill = new ExplicitContentBeatmapPill - { - Alpha = 0f, - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Margin = new MarginPadding { Left = 10, Bottom = 4 }, - } - } - }, - artist = new OsuSpriteText - { - Font = OsuFont.GetFont(size: 20, weight: FontWeight.Medium, italics: true), - Margin = new MarginPadding { Bottom = 20 } - }, - new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Child = author = new AuthorInfo(), - }, - beatmapAvailability = new BeatmapAvailability(), - new Container - { - RelativeSizeAxes = Axes.X, - Height = buttons_height, - Margin = new MarginPadding { Top = 10 }, - Children = new Drawable[] - { - favouriteButton = new FavouriteButton - { - BeatmapSet = { BindTarget = BeatmapSet } - }, - downloadButtonsContainer = new FillFlowContainer - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Left = buttons_height + buttons_spacing }, - Spacing = new Vector2(buttons_spacing), - }, - }, - }, - }, - }, - } - }, - loading = new LoadingSpinner - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Scale = new Vector2(1.5f), - }, - new FillFlowContainer - { - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - AutoSizeAxes = Axes.Both, - Margin = new MarginPadding { Top = BeatmapSetOverlay.Y_PADDING, Right = BeatmapSetOverlay.X_PADDING }, - Direction = FillDirection.Vertical, - Spacing = new Vector2(10), - Children = new Drawable[] - { - onlineStatusPill = new BeatmapSetOnlineStatusPill - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - TextSize = 14, - TextPadding = new MarginPadding { Horizontal = 35, Vertical = 10 } - }, - details = new Details(), - }, - }, - } + BeatmapSet = { BindTarget = BeatmapSet } }; - private void updateDownloadButtons() - { - if (BeatmapSet.Value == null) return; - - if ((BeatmapSet.Value.OnlineInfo.Availability?.DownloadDisabled ?? false) && state.Value != DownloadState.LocallyAvailable) - { - downloadButtonsContainer.Clear(); - return; - } - - switch (state.Value) - { - case DownloadState.LocallyAvailable: - // temporary for UX until new design is implemented. - downloadButtonsContainer.Child = new BeatmapPanelDownloadButton(BeatmapSet.Value) - { - Width = 50, - RelativeSizeAxes = Axes.Y, - SelectedBeatmap = { BindTarget = Picker.Beatmap } - }; - break; - - case DownloadState.Downloading: - case DownloadState.Importing: - // temporary to avoid showing two buttons for maps with novideo. will be fixed in new beatmap overlay design. - downloadButtonsContainer.Child = new HeaderDownloadButton(BeatmapSet.Value); - break; - - default: - downloadButtonsContainer.Child = new HeaderDownloadButton(BeatmapSet.Value); - if (BeatmapSet.Value.OnlineInfo.HasVideo) - downloadButtonsContainer.Add(new HeaderDownloadButton(BeatmapSet.Value, true)); - break; - } - } - - private class DownloadTracker : BeatmapDownloadTrackingComposite - { - public new Bindable State => base.State; - } - - protected override OverlayTitle CreateTitle() => new BeatmapHeaderTitle(); - protected override Drawable CreateTitleContent() => RulesetSelector = new BeatmapRulesetSelector { Current = ruleset }; + protected override OverlayTitle CreateTitle() => new BeatmapHeaderTitle(); + private class BeatmapHeaderTitle : OverlayTitle { public BeatmapHeaderTitle() diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs new file mode 100644 index 0000000000..153aa41582 --- /dev/null +++ b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs @@ -0,0 +1,283 @@ +// 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.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Beatmaps.Drawables; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online; +using osu.Game.Online.API; +using osu.Game.Overlays.BeatmapListing.Panels; +using osu.Game.Overlays.BeatmapSet.Buttons; +using osuTK; + +namespace osu.Game.Overlays.BeatmapSet +{ + public class BeatmapSetHeaderContent : BeatmapDownloadTrackingComposite + { + private const float transition_duration = 200; + private const float buttons_height = 45; + private const float buttons_spacing = 5; + + public bool DownloadButtonsVisible => downloadButtonsContainer.Any(); + + public readonly Details Details; + public readonly BeatmapPicker Picker; + + private readonly UpdateableBeatmapSetCover cover; + private readonly Box coverGradient; + private readonly OsuSpriteText title, artist; + private readonly AuthorInfo author; + private readonly ExplicitContentBeatmapPill explicitContentPill; + private readonly FillFlowContainer downloadButtonsContainer; + private readonly BeatmapAvailability beatmapAvailability; + private readonly BeatmapSetOnlineStatusPill onlineStatusPill; + private readonly FavouriteButton favouriteButton; + private readonly FillFlowContainer fadeContent; + private readonly LoadingSpinner loading; + + [Resolved] + private IAPIProvider api { get; set; } + + [Resolved] + private BeatmapRulesetSelector rulesetSelector { get; set; } + + public BeatmapSetHeaderContent() + { + ExternalLinkButton externalLink; + + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + InternalChild = new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + cover = new UpdateableBeatmapSetCover + { + RelativeSizeAxes = Axes.Both, + Masking = true, + }, + coverGradient = new Box + { + RelativeSizeAxes = Axes.Both + }, + }, + }, + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding + { + Vertical = BeatmapSetOverlay.Y_PADDING, + Left = BeatmapSetOverlay.X_PADDING, + Right = BeatmapSetOverlay.X_PADDING + BeatmapSetOverlay.RIGHT_WIDTH, + }, + Children = new Drawable[] + { + fadeContent = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Child = Picker = new BeatmapPicker(), + }, + new FillFlowContainer + { + Direction = FillDirection.Horizontal, + AutoSizeAxes = Axes.Both, + Margin = new MarginPadding { Top = 15 }, + Children = new Drawable[] + { + title = new OsuSpriteText + { + Font = OsuFont.GetFont(size: 30, weight: FontWeight.SemiBold, italics: true) + }, + externalLink = new ExternalLinkButton + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Margin = new MarginPadding { Left = 5, Bottom = 4 }, // To better lineup with the font + }, + explicitContentPill = new ExplicitContentBeatmapPill + { + Alpha = 0f, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Margin = new MarginPadding { Left = 10, Bottom = 4 }, + } + } + }, + artist = new OsuSpriteText + { + Font = OsuFont.GetFont(size: 20, weight: FontWeight.Medium, italics: true), + Margin = new MarginPadding { Bottom = 20 } + }, + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Child = author = new AuthorInfo(), + }, + beatmapAvailability = new BeatmapAvailability(), + new Container + { + RelativeSizeAxes = Axes.X, + Height = buttons_height, + Margin = new MarginPadding { Top = 10 }, + Children = new Drawable[] + { + favouriteButton = new FavouriteButton + { + BeatmapSet = { BindTarget = BeatmapSet } + }, + downloadButtonsContainer = new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Left = buttons_height + buttons_spacing }, + Spacing = new Vector2(buttons_spacing), + }, + }, + }, + }, + }, + } + }, + loading = new LoadingSpinner + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(1.5f), + }, + new FillFlowContainer + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + AutoSizeAxes = Axes.Both, + Margin = new MarginPadding { Top = BeatmapSetOverlay.Y_PADDING, Right = BeatmapSetOverlay.X_PADDING }, + Direction = FillDirection.Vertical, + Spacing = new Vector2(10), + Children = new Drawable[] + { + onlineStatusPill = new BeatmapSetOnlineStatusPill + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + TextSize = 14, + TextPadding = new MarginPadding { Horizontal = 35, Vertical = 10 } + }, + Details = new Details(), + }, + }, + } + }; + + Picker.Beatmap.ValueChanged += b => + { + Details.Beatmap = b.NewValue; + externalLink.Link = $@"{api.WebsiteRootUrl}/beatmapsets/{BeatmapSet.Value?.OnlineBeatmapSetID}#{b.NewValue?.Ruleset.ShortName}/{b.NewValue?.OnlineBeatmapID}"; + }; + } + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + coverGradient.Colour = ColourInfo.GradientVertical(colourProvider.Background6.Opacity(0.3f), colourProvider.Background6.Opacity(0.8f)); + onlineStatusPill.BackgroundColour = colourProvider.Background6; + + State.BindValueChanged(_ => updateDownloadButtons()); + + BeatmapSet.BindValueChanged(setInfo => + { + Picker.BeatmapSet = rulesetSelector.BeatmapSet = author.BeatmapSet = beatmapAvailability.BeatmapSet = Details.BeatmapSet = setInfo.NewValue; + cover.BeatmapSet = setInfo.NewValue; + + if (setInfo.NewValue == null) + { + onlineStatusPill.FadeTo(0.5f, 500, Easing.OutQuint); + fadeContent.Hide(); + + loading.Show(); + + downloadButtonsContainer.FadeOut(transition_duration); + favouriteButton.FadeOut(transition_duration); + } + else + { + fadeContent.FadeIn(500, Easing.OutQuint); + + loading.Hide(); + + title.Text = setInfo.NewValue.Metadata.Title ?? string.Empty; + artist.Text = setInfo.NewValue.Metadata.Artist ?? string.Empty; + + explicitContentPill.Alpha = setInfo.NewValue.OnlineInfo.HasExplicitContent ? 1 : 0; + + onlineStatusPill.FadeIn(500, Easing.OutQuint); + onlineStatusPill.Status = setInfo.NewValue.OnlineInfo.Status; + + downloadButtonsContainer.FadeIn(transition_duration); + favouriteButton.FadeIn(transition_duration); + + updateDownloadButtons(); + } + }, true); + } + + private void updateDownloadButtons() + { + if (BeatmapSet.Value == null) return; + + if ((BeatmapSet.Value.OnlineInfo.Availability?.DownloadDisabled ?? false) && State.Value != DownloadState.LocallyAvailable) + { + downloadButtonsContainer.Clear(); + return; + } + + switch (State.Value) + { + case DownloadState.LocallyAvailable: + // temporary for UX until new design is implemented. + downloadButtonsContainer.Child = new BeatmapPanelDownloadButton(BeatmapSet.Value) + { + Width = 50, + RelativeSizeAxes = Axes.Y, + SelectedBeatmap = { BindTarget = Picker.Beatmap } + }; + break; + + case DownloadState.Downloading: + case DownloadState.Importing: + // temporary to avoid showing two buttons for maps with novideo. will be fixed in new beatmap overlay design. + downloadButtonsContainer.Child = new HeaderDownloadButton(BeatmapSet.Value); + break; + + default: + downloadButtonsContainer.Child = new HeaderDownloadButton(BeatmapSet.Value); + if (BeatmapSet.Value.OnlineInfo.HasVideo) + downloadButtonsContainer.Add(new HeaderDownloadButton(BeatmapSet.Value, true)); + break; + } + } + } +} diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index 86f0f4f614..c16ec339bb 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -76,7 +76,7 @@ namespace osu.Game.Overlays }, new ScoresContainer { - Beatmap = { BindTarget = Header.Picker.Beatmap } + Beatmap = { BindTarget = Header.HeaderContent.Picker.Beatmap } }, comments = new CommentsSection() }, @@ -88,7 +88,7 @@ namespace osu.Game.Overlays info.BeatmapSet.BindTo(beatmapSet); comments.BeatmapSet.BindTo(beatmapSet); - Header.Picker.Beatmap.ValueChanged += b => + Header.HeaderContent.Picker.Beatmap.ValueChanged += b => { info.Beatmap = b.NewValue; @@ -122,7 +122,7 @@ namespace osu.Game.Overlays req.Success += res => { beatmapSet.Value = res.ToBeatmapSet(rulesets); - Header.Picker.Beatmap.Value = Header.BeatmapSet.Value.Beatmaps.First(b => b.OnlineBeatmapID == beatmapId); + Header.HeaderContent.Picker.Beatmap.Value = Header.BeatmapSet.Value.Beatmaps.First(b => b.OnlineBeatmapID == beatmapId); }; API.Queue(req); From eb85efcea2d79eb8b1750b9d703692e6fb305280 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 20 Jan 2021 20:59:28 +0900 Subject: [PATCH 032/121] Add check to playlists too --- osu.Game/Screens/Select/MatchSongSelect.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Screens/Select/MatchSongSelect.cs b/osu.Game/Screens/Select/MatchSongSelect.cs index 0948a4d19a..ed47b5d5ac 100644 --- a/osu.Game/Screens/Select/MatchSongSelect.cs +++ b/osu.Game/Screens/Select/MatchSongSelect.cs @@ -10,6 +10,8 @@ using osu.Framework.Graphics; using osu.Framework.Screens; using osu.Game.Beatmaps; using osu.Game.Online.Rooms; +using osu.Game.Overlays.Mods; +using osu.Game.Rulesets.Mods; using osu.Game.Screens.OnlinePlay; using osu.Game.Screens.OnlinePlay.Components; @@ -78,5 +80,9 @@ namespace osu.Game.Screens.Select item.RequiredMods.Clear(); item.RequiredMods.AddRange(Mods.Value.Select(m => m.CreateCopy())); } + + protected override ModSelectOverlay CreateModSelectOverlay() => new ModSelectOverlay(isValidMod); + + private bool isValidMod(Mod mod) => !(mod is ModAutoplay) && (mod as MultiMod)?.Mods.Any(mm => mm is ModAutoplay) != true; } } From bba182a02d10f283d85a876c89530c55247359b6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 20 Jan 2021 21:27:16 +0900 Subject: [PATCH 033/121] Fix test failure --- .../Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs index a4c87d3ace..319c2bc6fd 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs @@ -11,12 +11,10 @@ using osu.Framework.Platform; using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Beatmaps; -using osu.Game.Graphics.UserInterface; using osu.Game.Online.Rooms; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; using osu.Game.Screens.OnlinePlay; -using osu.Game.Screens.OnlinePlay.Match.Components; using osu.Game.Screens.OnlinePlay.Playlists; using osu.Game.Tests.Beatmaps; using osu.Game.Users; @@ -85,8 +83,7 @@ namespace osu.Game.Tests.Visual.Playlists AddStep("move mouse to create button", () => { - var footer = match.ChildrenOfType
().Single(); - InputManager.MoveMouseTo(footer.ChildrenOfType().Single()); + InputManager.MoveMouseTo(this.ChildrenOfType().Single()); }); AddStep("click", () => InputManager.Click(MouseButton.Left)); From 7abe33ad0e2df62efe586788e8963b0fe10f869d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 20 Jan 2021 23:23:12 +0100 Subject: [PATCH 034/121] Add failing test case --- .../Gameplay/TestScenePoolingRuleset.cs | 75 +++++++++++++++++-- 1 file changed, 69 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePoolingRuleset.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePoolingRuleset.cs index cd7d692b0a..17a009a2ce 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePoolingRuleset.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePoolingRuleset.cs @@ -17,10 +17,12 @@ using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Rulesets; using osu.Game.Rulesets.Difficulty; +using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Legacy; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.UI; using osuTK; using osuTK.Graphics; @@ -129,6 +131,31 @@ namespace osu.Game.Tests.Visual.Gameplay AddUntilStep("no DHOs shown", () => !this.ChildrenOfType().Any()); } + [Test] + public void TestApplyHitResultOnKilled() + { + ManualClock clock = null; + bool anyJudged = false; + + void onNewResult(JudgementResult _) => anyJudged = true; + + var beatmap = new Beatmap(); + beatmap.HitObjects.Add(new TestKilledHitObject { Duration = 20 }); + + createTest(beatmap, 10, () => new FramedClock(clock = new ManualClock())); + + AddStep("subscribe to new result", () => + { + anyJudged = false; + drawableRuleset.NewResult += onNewResult; + }); + AddStep("skip past object", () => clock.CurrentTime = beatmap.HitObjects[0].GetEndTime() + 1000); + + AddAssert("object judged", () => anyJudged); + + AddStep("clean up", () => drawableRuleset.NewResult -= onNewResult); + } + private void createTest(IBeatmap beatmap, int poolSize, Func createClock = null) => AddStep("create test", () => { var ruleset = new TestPoolingRuleset(); @@ -192,6 +219,7 @@ namespace osu.Game.Tests.Visual.Gameplay private void load() { RegisterPool(poolSize); + RegisterPool(poolSize); } protected override HitObjectLifetimeEntry CreateLifetimeEntry(HitObject hitObject) => new TestHitObjectLifetimeEntry(hitObject); @@ -220,19 +248,30 @@ namespace osu.Game.Tests.Visual.Gameplay protected override IEnumerable ConvertHitObject(HitObject original, IBeatmap beatmap, CancellationToken cancellationToken) { - yield return new TestHitObject + switch (original) { - StartTime = original.StartTime, - Duration = 250 - }; + case TestKilledHitObject h: + yield return h; + + break; + + default: + yield return new TestHitObject + { + StartTime = original.StartTime, + Duration = 250 + }; + + break; + } } } #endregion - #region HitObject + #region HitObjects - private class TestHitObject : ConvertHitObject + private class TestHitObject : ConvertHitObject, IHasDuration { public double EndTime => StartTime + Duration; @@ -287,6 +326,30 @@ namespace osu.Game.Tests.Visual.Gameplay } } + private class TestKilledHitObject : TestHitObject + { + } + + private class DrawableTestKilledHitObject : DrawableHitObject + { + public DrawableTestKilledHitObject() + : base(null) + { + } + + protected override void UpdateHitStateTransforms(ArmedState state) + { + base.UpdateHitStateTransforms(state); + Expire(); + } + + public override void OnKilled() + { + base.OnKilled(); + ApplyResult(r => r.Type = r.Judgement.MinResult); + } + } + #endregion } } From 1d9aaac2c221a4e507df4019076f080e74f1a0b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 20 Jan 2021 23:55:54 +0100 Subject: [PATCH 035/121] Fix HOC not propagating DHO results applied on kill `DrawableHitObject.OnKilled()` calls `UpdateResult()` to clean up a hitobject's state definitively with regards to the judgement result before returning the DHO back to the pool. As it turns out, if a consumer was relying on this code path (as taiko was in the case of nested strong hit objects), it would not work properly with pooling, due to `HitObjectContainer` unsubscribing from `On{New,Revert}Result` *before* calling the DHO's `OnKilled()`. This in turn would lead to users potentially getting stuck in gameplay, due to `ScoreProcessor` not receiving all results via that event path. To resolve, change the call ordering to allow hit result changes applied in `OnKilled()` to propagate normally. --- osu.Game/Rulesets/UI/HitObjectContainer.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/HitObjectContainer.cs b/osu.Game/Rulesets/UI/HitObjectContainer.cs index 12e39d4fbf..1972043ccb 100644 --- a/osu.Game/Rulesets/UI/HitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/HitObjectContainer.cs @@ -124,9 +124,11 @@ namespace osu.Game.Rulesets.UI 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; - drawable.OnKilled(); drawableMap.Remove(entry); From 163a937e415b21b4f92d84675aae2a27f57e70c9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 21 Jan 2021 13:30:53 +0900 Subject: [PATCH 036/121] Fix mod test failing intermittently --- .../Visual/UserInterface/TestSceneModSelectOverlay.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 0d0acbb8f4..bb72226750 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -134,6 +134,8 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestExternallySetCustomizedMod() { + changeRuleset(0); + AddStep("set customized mod externally", () => SelectedMods.Value = new[] { new OsuModDoubleTime { SpeedChange = { Value = 1.01 } } }); AddAssert("ensure button is selected and customized accordingly", () => From 54dbb43f79a758e73e4782c1364e2405384a82c4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 21 Jan 2021 14:03:35 +0900 Subject: [PATCH 037/121] Fix more potential failures --- osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index bb72226750..bd4010a7f3 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -40,6 +40,7 @@ namespace osu.Game.Tests.Visual.UserInterface [SetUp] public void SetUp() => Schedule(() => { + SelectedMods.Value = Array.Empty(); Children = new Drawable[] { modSelect = new TestModSelectOverlay From 0fcf61d352197ef5f4d8e10d8bf1d19d55c266b0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Jan 2021 14:07:02 +0900 Subject: [PATCH 038/121] Replace null check with assert --- osu.Game/Graphics/Containers/SectionsContainer.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index b5f81c516a..d378b67acf 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Diagnostics; using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; @@ -131,13 +132,13 @@ namespace osu.Game.Graphics.Containers public override void Add(T drawable) { base.Add(drawable); + + Debug.Assert(drawable != null); + lastKnownScroll = float.NaN; headerHeight = float.NaN; footerHeight = float.NaN; - if (drawable == null) - return; - if (smallestSection == null || smallestSection.Height > drawable.Height) smallestSection = drawable; } From 8f9089d1aefa26c90a883e226dcce91b9cabfd8e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Jan 2021 14:30:22 +0900 Subject: [PATCH 039/121] Move constant to a better place --- osu.Game/Graphics/Containers/SectionsContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index d378b67acf..fd8b98e767 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -109,6 +109,8 @@ namespace osu.Game.Graphics.Containers private float lastKnownScroll; + private const float scroll_target_multiplier = 0.2f; + public SectionsContainer() { AddRangeInternal(new Drawable[] @@ -143,8 +145,6 @@ namespace osu.Game.Graphics.Containers smallestSection = drawable; } - private const float scroll_target_multiplier = 0.2f; - public void ScrollTo(Drawable section) { lastClickedSection = section; From 555abcdc3695c437efe3277fc738e54b637fe235 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Jan 2021 14:31:31 +0900 Subject: [PATCH 040/121] Replace nan usage with nullable float --- .../Graphics/Containers/SectionsContainer.cs | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index fd8b98e767..2140e251f8 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -40,7 +40,7 @@ namespace osu.Game.Graphics.Containers if (value == null) return; AddInternal(expandableHeader); - lastKnownScroll = float.NaN; + lastKnownScroll = null; } } @@ -56,7 +56,7 @@ namespace osu.Game.Graphics.Containers if (value == null) return; AddInternal(fixedHeader); - lastKnownScroll = float.NaN; + lastKnownScroll = null; } } @@ -75,7 +75,7 @@ namespace osu.Game.Graphics.Containers footer.Anchor |= Anchor.y2; footer.Origin |= Anchor.y2; scrollContainer.Add(footer); - lastKnownScroll = float.NaN; + lastKnownScroll = null; } } @@ -93,7 +93,7 @@ namespace osu.Game.Graphics.Containers headerBackgroundContainer.Add(headerBackground); - lastKnownScroll = float.NaN; + lastKnownScroll = null; } } @@ -105,9 +105,9 @@ namespace osu.Game.Graphics.Containers private Drawable expandableHeader, fixedHeader, footer, headerBackground; private FlowContainer scrollContentContainer; - private float headerHeight, footerHeight; + private float? headerHeight, footerHeight; - private float lastKnownScroll; + private float? lastKnownScroll; private const float scroll_target_multiplier = 0.2f; @@ -137,9 +137,9 @@ namespace osu.Game.Graphics.Containers Debug.Assert(drawable != null); - lastKnownScroll = float.NaN; - headerHeight = float.NaN; - footerHeight = float.NaN; + lastKnownScroll = null; + headerHeight = null; + footerHeight = null; if (smallestSection == null || smallestSection.Height > drawable.Height) smallestSection = drawable; @@ -171,7 +171,7 @@ namespace osu.Game.Graphics.Containers if (source == InvalidationSource.Child && (invalidation & Invalidation.DrawSize) != 0) { - lastKnownScroll = -1; + lastKnownScroll = null; result = true; } @@ -242,8 +242,9 @@ namespace osu.Game.Graphics.Containers if (!Children.Any()) return; var newMargin = originalSectionsMargin; - newMargin.Top += headerHeight; - newMargin.Bottom += footerHeight; + + newMargin.Top += (headerHeight ?? 0); + newMargin.Bottom += (footerHeight ?? 0); scrollContentContainer.Margin = newMargin; } From 6d167b7865688b4f73cb94106fbbe233e739ce40 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Jan 2021 14:40:55 +0900 Subject: [PATCH 041/121] Remove the need to store the smallest section --- osu.Game/Graphics/Containers/SectionsContainer.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 2140e251f8..6607193cc6 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -23,7 +23,6 @@ namespace osu.Game.Graphics.Containers { public Bindable SelectedSection { get; } = new Bindable(); private Drawable lastClickedSection; - private T smallestSection; public Drawable ExpandableHeader { @@ -140,9 +139,6 @@ namespace osu.Game.Graphics.Containers lastKnownScroll = null; headerHeight = null; footerHeight = null; - - if (smallestSection == null || smallestSection.Height > drawable.Height) - smallestSection = drawable; } public void ScrollTo(Drawable section) @@ -213,10 +209,12 @@ namespace osu.Game.Graphics.Containers headerBackgroundContainer.Height = (ExpandableHeader?.LayoutSize.Y ?? 0) + (FixedHeader?.LayoutSize.Y ?? 0); headerBackgroundContainer.Y = ExpandableHeader?.Y ?? 0; + var smallestSectionHeight = Children.Count > 0 ? Children.Min(d => d.Height) : 0; + // scroll offset is our fixed header height if we have it plus 20% of content height // plus 5% to fix floating point errors and to not have a section instantly unselect when scrolling upwards // but the 5% can't be bigger than our smallest section height, otherwise it won't get selected correctly - float sectionOrContent = Math.Min(smallestSection?.Height / 2.0f ?? 0, scrollContainer.DisplayableContent * 0.05f); + float sectionOrContent = Math.Min(smallestSectionHeight / 2.0f, scrollContainer.DisplayableContent * 0.05f); float scrollOffset = (FixedHeader?.LayoutSize.Y ?? 0) + scrollContainer.DisplayableContent * scroll_target_multiplier + sectionOrContent; Func diff = section => scrollContainer.GetChildPosInContent(section) - currentScroll - scrollOffset; From e5eec27e9574037e13287b11438c044d8cc1c0da Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Jan 2021 14:44:47 +0900 Subject: [PATCH 042/121] Simplify selected section resolution --- osu.Game/Graphics/Containers/SectionsContainer.cs | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 6607193cc6..cae2dd7e37 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -215,23 +215,16 @@ namespace osu.Game.Graphics.Containers // plus 5% to fix floating point errors and to not have a section instantly unselect when scrolling upwards // but the 5% can't be bigger than our smallest section height, otherwise it won't get selected correctly float sectionOrContent = Math.Min(smallestSectionHeight / 2.0f, scrollContainer.DisplayableContent * 0.05f); + float scrollOffset = (FixedHeader?.LayoutSize.Y ?? 0) + scrollContainer.DisplayableContent * scroll_target_multiplier + sectionOrContent; Func diff = section => scrollContainer.GetChildPosInContent(section) - currentScroll - scrollOffset; if (Precision.AlmostBigger(0, scrollContainer.Current)) - { SelectedSection.Value = lastClickedSection as T ?? Children.FirstOrDefault(); - return; - } - - if (Precision.AlmostBigger(scrollContainer.Current, scrollContainer.ScrollableExtent)) - { + else if (Precision.AlmostBigger(scrollContainer.Current, scrollContainer.ScrollableExtent)) SelectedSection.Value = lastClickedSection as T ?? Children.LastOrDefault(); - return; - } - - SelectedSection.Value = Children.TakeWhile(section => diff(section) <= 0).LastOrDefault() - ?? Children.FirstOrDefault(); + else + SelectedSection.Value = Children.TakeWhile(section => diff(section) <= 0).LastOrDefault() ?? Children.FirstOrDefault(); } } From a85f952a381ca32869e06a16a23c149955a88dbc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Jan 2021 14:46:35 +0900 Subject: [PATCH 043/121] Inline single use function --- osu.Game/Graphics/Containers/SectionsContainer.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index cae2dd7e37..cf361abadc 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -217,14 +217,17 @@ namespace osu.Game.Graphics.Containers float sectionOrContent = Math.Min(smallestSectionHeight / 2.0f, scrollContainer.DisplayableContent * 0.05f); float scrollOffset = (FixedHeader?.LayoutSize.Y ?? 0) + scrollContainer.DisplayableContent * scroll_target_multiplier + sectionOrContent; - Func diff = section => scrollContainer.GetChildPosInContent(section) - currentScroll - scrollOffset; if (Precision.AlmostBigger(0, scrollContainer.Current)) SelectedSection.Value = lastClickedSection as T ?? Children.FirstOrDefault(); else if (Precision.AlmostBigger(scrollContainer.Current, scrollContainer.ScrollableExtent)) SelectedSection.Value = lastClickedSection as T ?? Children.LastOrDefault(); else - SelectedSection.Value = Children.TakeWhile(section => diff(section) <= 0).LastOrDefault() ?? Children.FirstOrDefault(); + { + SelectedSection.Value = Children + .TakeWhile(section => scrollContainer.GetChildPosInContent(section) - currentScroll - scrollOffset <= 0) + .LastOrDefault() ?? Children.FirstOrDefault(); + } } } From 9daf29fedcb0b4e74a7a3d17579557c68fd4723a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Jan 2021 14:52:41 +0900 Subject: [PATCH 044/121] Extract out commonly used variables --- osu.Game/Graphics/Containers/SectionsContainer.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index cf361abadc..5afe74db18 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -178,7 +178,10 @@ namespace osu.Game.Graphics.Containers { base.UpdateAfterChildren(); - float headerH = (ExpandableHeader?.LayoutSize.Y ?? 0) + (FixedHeader?.LayoutSize.Y ?? 0); + float fixedHeaderSize = (FixedHeader?.LayoutSize.Y ?? 0); + float expandableHeaderSize = ExpandableHeader?.LayoutSize.Y ?? 0; + + float headerH = expandableHeaderSize + fixedHeaderSize; float footerH = Footer?.LayoutSize.Y ?? 0; if (headerH != headerHeight || footerH != footerHeight) @@ -200,13 +203,13 @@ namespace osu.Game.Graphics.Containers if (ExpandableHeader != null && FixedHeader != null) { - float offset = Math.Min(ExpandableHeader.LayoutSize.Y, currentScroll); + float offset = Math.Min(expandableHeaderSize, currentScroll); ExpandableHeader.Y = -offset; - FixedHeader.Y = -offset + ExpandableHeader.LayoutSize.Y; + FixedHeader.Y = -offset + expandableHeaderSize; } - headerBackgroundContainer.Height = (ExpandableHeader?.LayoutSize.Y ?? 0) + (FixedHeader?.LayoutSize.Y ?? 0); + headerBackgroundContainer.Height = expandableHeaderSize + fixedHeaderSize; headerBackgroundContainer.Y = ExpandableHeader?.Y ?? 0; var smallestSectionHeight = Children.Count > 0 ? Children.Min(d => d.Height) : 0; @@ -216,7 +219,7 @@ namespace osu.Game.Graphics.Containers // but the 5% can't be bigger than our smallest section height, otherwise it won't get selected correctly float sectionOrContent = Math.Min(smallestSectionHeight / 2.0f, scrollContainer.DisplayableContent * 0.05f); - float scrollOffset = (FixedHeader?.LayoutSize.Y ?? 0) + scrollContainer.DisplayableContent * scroll_target_multiplier + sectionOrContent; + float scrollOffset = fixedHeaderSize + scrollContainer.DisplayableContent * scroll_target_multiplier + sectionOrContent; if (Precision.AlmostBigger(0, scrollContainer.Current)) SelectedSection.Value = lastClickedSection as T ?? Children.FirstOrDefault(); From c650cbd2a70f1f333d2fc3b159a1ca10ace61fca Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Jan 2021 14:56:10 +0900 Subject: [PATCH 045/121] Rename variable to something slightly better --- osu.Game/Graphics/Containers/SectionsContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 5afe74db18..87c3007ca4 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -217,9 +217,9 @@ namespace osu.Game.Graphics.Containers // scroll offset is our fixed header height if we have it plus 20% of content height // plus 5% to fix floating point errors and to not have a section instantly unselect when scrolling upwards // but the 5% can't be bigger than our smallest section height, otherwise it won't get selected correctly - float sectionOrContent = Math.Min(smallestSectionHeight / 2.0f, scrollContainer.DisplayableContent * 0.05f); + float scrollIntoSectionAmount = Math.Min(smallestSectionHeight / 2.0f, scrollContainer.DisplayableContent * 0.05f); - float scrollOffset = fixedHeaderSize + scrollContainer.DisplayableContent * scroll_target_multiplier + sectionOrContent; + float scrollOffset = fixedHeaderSize + scrollContainer.DisplayableContent * scroll_target_multiplier + scrollIntoSectionAmount; if (Precision.AlmostBigger(0, scrollContainer.Current)) SelectedSection.Value = lastClickedSection as T ?? Children.FirstOrDefault(); From 8853ac04d97e2419da6b4f2db2a464d680173d3b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Jan 2021 15:08:36 +0900 Subject: [PATCH 046/121] Rename some variable and add xmldoc for scroll centre position --- osu.Game/Graphics/Containers/SectionsContainer.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 87c3007ca4..6ed161fa77 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -108,7 +108,10 @@ namespace osu.Game.Graphics.Containers private float? lastKnownScroll; - private const float scroll_target_multiplier = 0.2f; + /// + /// The percentage of the container to consider the centre-point for deciding the active section (and scrolling to a requested section). + /// + private const float scroll_y_centre = 0.2f; public SectionsContainer() { @@ -144,7 +147,7 @@ namespace osu.Game.Graphics.Containers public void ScrollTo(Drawable section) { lastClickedSection = section; - scrollContainer.ScrollTo(scrollContainer.GetChildPosInContent(section) - scrollContainer.DisplayableContent * scroll_target_multiplier - (FixedHeader?.BoundingBox.Height ?? 0)); + scrollContainer.ScrollTo(scrollContainer.GetChildPosInContent(section) - scrollContainer.DisplayableContent * scroll_y_centre - (FixedHeader?.BoundingBox.Height ?? 0)); } public void ScrollToTop() => scrollContainer.ScrollTo(0); @@ -217,9 +220,9 @@ namespace osu.Game.Graphics.Containers // scroll offset is our fixed header height if we have it plus 20% of content height // plus 5% to fix floating point errors and to not have a section instantly unselect when scrolling upwards // but the 5% can't be bigger than our smallest section height, otherwise it won't get selected correctly - float scrollIntoSectionAmount = Math.Min(smallestSectionHeight / 2.0f, scrollContainer.DisplayableContent * 0.05f); + float selectionLenienceAboveSection = Math.Min(smallestSectionHeight / 2.0f, scrollContainer.DisplayableContent * 0.05f); - float scrollOffset = fixedHeaderSize + scrollContainer.DisplayableContent * scroll_target_multiplier + scrollIntoSectionAmount; + float scrollCentre = fixedHeaderSize + scrollContainer.DisplayableContent * scroll_y_centre + selectionLenienceAboveSection; if (Precision.AlmostBigger(0, scrollContainer.Current)) SelectedSection.Value = lastClickedSection as T ?? Children.FirstOrDefault(); @@ -228,7 +231,7 @@ namespace osu.Game.Graphics.Containers else { SelectedSection.Value = Children - .TakeWhile(section => scrollContainer.GetChildPosInContent(section) - currentScroll - scrollOffset <= 0) + .TakeWhile(section => scrollContainer.GetChildPosInContent(section) - currentScroll - scrollCentre <= 0) .LastOrDefault() ?? Children.FirstOrDefault(); } } From e6980688f60fd9d6b9e6489f2879eb397a50218f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Jan 2021 15:42:23 +0900 Subject: [PATCH 047/121] Leave the multiplayer channel when leaving multiplayer --- osu.Game/Online/Chat/ChannelManager.cs | 10 +++++++--- .../OnlinePlay/Match/Components/MatchChatDisplay.cs | 6 ++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 62ae507419..036ec4d0f3 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -339,7 +339,7 @@ namespace osu.Game.Online.Chat } /// - /// Joins a channel if it has not already been joined. + /// Joins a channel if it has not already been joined. Must be called from the update thread. /// /// The channel to join. /// The joined channel. Note that this may not match the parameter channel as it is a backed object. @@ -399,7 +399,11 @@ namespace osu.Game.Online.Chat return channel; } - public void LeaveChannel(Channel channel) + /// + /// Leave the specified channel. Can be called from any thread. + /// + /// The channel to leave. + public void LeaveChannel(Channel channel) => Schedule(() => { if (channel == null) return; @@ -413,7 +417,7 @@ namespace osu.Game.Online.Chat api.Queue(new LeaveChannelRequest(channel)); channel.Joined.Value = false; } - } + }); private long lastMessageId; diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/MatchChatDisplay.cs b/osu.Game/Screens/OnlinePlay/Match/Components/MatchChatDisplay.cs index 8800215c2e..6da2866236 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/MatchChatDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/MatchChatDisplay.cs @@ -38,5 +38,11 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components Channel.Value = channelManager?.JoinChannel(new Channel { Id = channelId.Value, Type = ChannelType.Multiplayer, Name = $"#lazermp_{roomId.Value}" }); } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + channelManager?.LeaveChannel(Channel.Value); + } } } From 9eb74e86edacd10d0f8fa8facf4f7f0045c4ca5c Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 21 Jan 2021 17:40:15 +0900 Subject: [PATCH 048/121] Apply comment suggestion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index 7df4f1ae7d..e215ecc17a 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -151,7 +151,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline // 2) The user changes the track time through some other means (scrolling in the editor or overview timeline; clicking a hitobject etc.). We want the timeline to track the clock's time. // 3) An ongoing seek transform is running from an external seek. We want the timeline to track the clock's time. - // The simplest way to cover both cases is by checking whether the scroll position has changed and the audio hasn't been changed externally + // The simplest way to cover the first two cases is by checking whether the scroll position has changed and the audio hasn't been changed externally // Checking IsSeeking covers the third case, where the transform may not have been applied yet. if (Current != lastScrollPosition && editorClock.CurrentTime == lastTrackTime && !editorClock.IsSeeking) seekTrackToCurrent(); From 05d3914fee2d9365577a2bf2fb8cd25fc0e11a17 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 21 Jan 2021 21:26:33 +0300 Subject: [PATCH 049/121] Rename friends tooltip to followers --- osu.Game/Overlays/Profile/Header/Components/AddFriendButton.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/AddFriendButton.cs b/osu.Game/Overlays/Profile/Header/Components/AddFriendButton.cs index 6c2b2dc16a..7080a578d0 100644 --- a/osu.Game/Overlays/Profile/Header/Components/AddFriendButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/AddFriendButton.cs @@ -17,7 +17,7 @@ namespace osu.Game.Overlays.Profile.Header.Components { public readonly Bindable User = new Bindable(); - public override string TooltipText => "friends"; + public override string TooltipText => "followers"; private OsuSpriteText followerText; From 2aa1df9ea4b5d95bdaa44b956d55f8de2eecf9ea Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 21 Jan 2021 21:38:52 +0300 Subject: [PATCH 050/121] Implement ProfileHeaderStatisticsButton component --- .../Header/Components/AddFriendButton.cs | 38 ++------------ .../ProfileHeaderStatisticsButton.cs | 52 +++++++++++++++++++ 2 files changed, 55 insertions(+), 35 deletions(-) create mode 100644 osu.Game/Overlays/Profile/Header/Components/ProfileHeaderStatisticsButton.cs diff --git a/osu.Game/Overlays/Profile/Header/Components/AddFriendButton.cs b/osu.Game/Overlays/Profile/Header/Components/AddFriendButton.cs index 7080a578d0..ac05511132 100644 --- a/osu.Game/Overlays/Profile/Header/Components/AddFriendButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/AddFriendButton.cs @@ -3,58 +3,26 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; using osu.Game.Users; -using osuTK; namespace osu.Game.Overlays.Profile.Header.Components { - public class AddFriendButton : ProfileHeaderButton + public class AddFriendButton : ProfileHeaderStatisticsButton { public readonly Bindable User = new Bindable(); public override string TooltipText => "followers"; - private OsuSpriteText followerText; + protected override IconUsage CreateIcon() => FontAwesome.Solid.User; [BackgroundDependencyLoader] private void load() { - Child = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Direction = FillDirection.Horizontal, - Padding = new MarginPadding { Right = 10 }, - Children = new Drawable[] - { - new SpriteIcon - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Icon = FontAwesome.Solid.User, - FillMode = FillMode.Fit, - Size = new Vector2(50, 14) - }, - followerText = new OsuSpriteText - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Font = OsuFont.GetFont(weight: FontWeight.Bold) - } - } - }; - // todo: when friending/unfriending is implemented, the APIAccess.Friends list should be updated accordingly. - User.BindValueChanged(user => updateFollowers(user.NewValue), true); } - private void updateFollowers(User user) => followerText.Text = user?.FollowerCount.ToString("#,##0"); + private void updateFollowers(User user) => SetValue(user?.FollowerCount.ToString("#,##0")); } } diff --git a/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderStatisticsButton.cs b/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderStatisticsButton.cs new file mode 100644 index 0000000000..7c1da503d1 --- /dev/null +++ b/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderStatisticsButton.cs @@ -0,0 +1,52 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using JetBrains.Annotations; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osuTK; + +namespace osu.Game.Overlays.Profile.Header.Components +{ + public abstract class ProfileHeaderStatisticsButton : ProfileHeaderButton + { + private readonly OsuSpriteText drawableText; + + protected ProfileHeaderStatisticsButton() + { + Child = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Direction = FillDirection.Horizontal, + Padding = new MarginPadding { Right = 10 }, + Children = new Drawable[] + { + new SpriteIcon + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Icon = CreateIcon(), + FillMode = FillMode.Fit, + Size = new Vector2(50, 14) + }, + drawableText = new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Font = OsuFont.GetFont(weight: FontWeight.Bold) + } + } + }; + } + + [NotNull] + protected abstract IconUsage CreateIcon(); + + protected void SetValue(string value) => drawableText.Text = value; + } +} From 966440f109a452b46fa99b3fd2d3845650e02467 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 21 Jan 2021 22:02:19 +0300 Subject: [PATCH 051/121] Add MappingFollowerCount field to User --- osu.Game/Users/User.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Users/User.cs b/osu.Game/Users/User.cs index d7e78d5b35..518236755d 100644 --- a/osu.Game/Users/User.cs +++ b/osu.Game/Users/User.cs @@ -126,6 +126,9 @@ namespace osu.Game.Users [JsonProperty(@"follower_count")] public int FollowerCount; + [JsonProperty(@"mapping_follower_count")] + public int MappingFollowerCount; + [JsonProperty(@"favourite_beatmapset_count")] public int FavouriteBeatmapsetCount; From a7c22ebe88812ba1cf33bb931eceabcb11e83105 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 21 Jan 2021 22:02:54 +0300 Subject: [PATCH 052/121] Implement MappingSubscribersButton component --- .../Profile/Header/CentreHeaderContainer.cs | 7 ++++-- .../{AddFriendButton.cs => FriendsButton.cs} | 6 ++--- .../Components/MappingSubscribersButton.cs | 25 +++++++++++++++++++ .../ProfileHeaderStatisticsButton.cs | 14 +++++------ 4 files changed, 39 insertions(+), 13 deletions(-) rename osu.Game/Overlays/Profile/Header/Components/{AddFriendButton.cs => FriendsButton.cs} (75%) create mode 100644 osu.Game/Overlays/Profile/Header/Components/MappingSubscribersButton.cs diff --git a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs index 658cdb8ce3..1849b6d88a 100644 --- a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs @@ -49,9 +49,12 @@ namespace osu.Game.Overlays.Profile.Header Spacing = new Vector2(10, 0), Children = new Drawable[] { - new AddFriendButton + new FriendsButton + { + User = { BindTarget = User } + }, + new MappingSubscribersButton { - RelativeSizeAxes = Axes.Y, User = { BindTarget = User } }, new MessageUserButton diff --git a/osu.Game/Overlays/Profile/Header/Components/AddFriendButton.cs b/osu.Game/Overlays/Profile/Header/Components/FriendsButton.cs similarity index 75% rename from osu.Game/Overlays/Profile/Header/Components/AddFriendButton.cs rename to osu.Game/Overlays/Profile/Header/Components/FriendsButton.cs index ac05511132..f369874586 100644 --- a/osu.Game/Overlays/Profile/Header/Components/AddFriendButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/FriendsButton.cs @@ -8,7 +8,7 @@ using osu.Game.Users; namespace osu.Game.Overlays.Profile.Header.Components { - public class AddFriendButton : ProfileHeaderStatisticsButton + public class FriendsButton : ProfileHeaderStatisticsButton { public readonly Bindable User = new Bindable(); @@ -20,9 +20,7 @@ namespace osu.Game.Overlays.Profile.Header.Components private void load() { // todo: when friending/unfriending is implemented, the APIAccess.Friends list should be updated accordingly. - User.BindValueChanged(user => updateFollowers(user.NewValue), true); + User.BindValueChanged(user => SetValue(user.NewValue?.FollowerCount ?? 0), true); } - - private void updateFollowers(User user) => SetValue(user?.FollowerCount.ToString("#,##0")); } } diff --git a/osu.Game/Overlays/Profile/Header/Components/MappingSubscribersButton.cs b/osu.Game/Overlays/Profile/Header/Components/MappingSubscribersButton.cs new file mode 100644 index 0000000000..2fb53a0b9a --- /dev/null +++ b/osu.Game/Overlays/Profile/Header/Components/MappingSubscribersButton.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.Bindables; +using osu.Framework.Graphics.Sprites; +using osu.Game.Users; + +namespace osu.Game.Overlays.Profile.Header.Components +{ + public class MappingSubscribersButton : ProfileHeaderStatisticsButton + { + public readonly Bindable User = new Bindable(); + + public override string TooltipText => "mapping subscribers"; + + protected override IconUsage CreateIcon() => FontAwesome.Solid.Bell; + + [BackgroundDependencyLoader] + private void load() + { + User.BindValueChanged(user => SetValue(user.NewValue?.MappingFollowerCount ?? 0), true); + } + } +} diff --git a/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderStatisticsButton.cs b/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderStatisticsButton.cs index 7c1da503d1..84a6e351ea 100644 --- a/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderStatisticsButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderStatisticsButton.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 JetBrains.Annotations; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; @@ -17,13 +16,14 @@ namespace osu.Game.Overlays.Profile.Header.Components protected ProfileHeaderStatisticsButton() { + RelativeSizeAxes = Axes.Y; Child = new FillFlowContainer { - AutoSizeAxes = Axes.Both, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, Direction = FillDirection.Horizontal, - Padding = new MarginPadding { Right = 10 }, Children = new Drawable[] { new SpriteIcon @@ -38,15 +38,15 @@ namespace osu.Game.Overlays.Profile.Header.Components { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, + Margin = new MarginPadding { Right = 10 }, Font = OsuFont.GetFont(weight: FontWeight.Bold) } } }; } - [NotNull] protected abstract IconUsage CreateIcon(); - protected void SetValue(string value) => drawableText.Text = value; + protected void SetValue(int value) => drawableText.Text = value.ToString("#,##0"); } } From 343166f158d09e74a9fee1df901989a80252c7be Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 21 Jan 2021 22:47:44 +0300 Subject: [PATCH 053/121] Make CreateIcon a property --- osu.Game/Overlays/Profile/Header/Components/FriendsButton.cs | 2 +- .../Profile/Header/Components/MappingSubscribersButton.cs | 2 +- .../Header/Components/ProfileHeaderStatisticsButton.cs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/FriendsButton.cs b/osu.Game/Overlays/Profile/Header/Components/FriendsButton.cs index f369874586..e0930b6a65 100644 --- a/osu.Game/Overlays/Profile/Header/Components/FriendsButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/FriendsButton.cs @@ -14,7 +14,7 @@ namespace osu.Game.Overlays.Profile.Header.Components public override string TooltipText => "followers"; - protected override IconUsage CreateIcon() => FontAwesome.Solid.User; + protected override IconUsage CreateIcon => FontAwesome.Solid.User; [BackgroundDependencyLoader] private void load() diff --git a/osu.Game/Overlays/Profile/Header/Components/MappingSubscribersButton.cs b/osu.Game/Overlays/Profile/Header/Components/MappingSubscribersButton.cs index 2fb53a0b9a..ef290676d9 100644 --- a/osu.Game/Overlays/Profile/Header/Components/MappingSubscribersButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/MappingSubscribersButton.cs @@ -14,7 +14,7 @@ namespace osu.Game.Overlays.Profile.Header.Components public override string TooltipText => "mapping subscribers"; - protected override IconUsage CreateIcon() => FontAwesome.Solid.Bell; + protected override IconUsage CreateIcon => FontAwesome.Solid.Bell; [BackgroundDependencyLoader] private void load() diff --git a/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderStatisticsButton.cs b/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderStatisticsButton.cs index 84a6e351ea..ff315727fa 100644 --- a/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderStatisticsButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderStatisticsButton.cs @@ -30,7 +30,7 @@ namespace osu.Game.Overlays.Profile.Header.Components { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Icon = CreateIcon(), + Icon = CreateIcon, FillMode = FillMode.Fit, Size = new Vector2(50, 14) }, @@ -45,7 +45,7 @@ namespace osu.Game.Overlays.Profile.Header.Components }; } - protected abstract IconUsage CreateIcon(); + protected abstract IconUsage CreateIcon { get; } protected void SetValue(int value) => drawableText.Text = value.ToString("#,##0"); } From e87197c7fc80b9d40e85829b2120cfbbf8cca87d Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 21 Jan 2021 22:48:31 +0300 Subject: [PATCH 054/121] Adjust text size --- .../Profile/Header/Components/ProfileHeaderStatisticsButton.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderStatisticsButton.cs b/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderStatisticsButton.cs index ff315727fa..118ea4c6aa 100644 --- a/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderStatisticsButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderStatisticsButton.cs @@ -39,7 +39,7 @@ namespace osu.Game.Overlays.Profile.Header.Components Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, Margin = new MarginPadding { Right = 10 }, - Font = OsuFont.GetFont(weight: FontWeight.Bold) + Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold) } } }; From 4555b9ff704bad005b094d0e5997ed1b29e9606c Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 21 Jan 2021 22:56:12 +0300 Subject: [PATCH 055/121] Make ProfileHeaderButton height defined --- osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs | 1 - osu.Game/Overlays/Profile/Header/Components/MessageUserButton.cs | 1 - .../Overlays/Profile/Header/Components/ProfileHeaderButton.cs | 1 + .../Profile/Header/Components/ProfileHeaderStatisticsButton.cs | 1 - 4 files changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs index 1849b6d88a..8f940cd0cc 100644 --- a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs @@ -72,7 +72,6 @@ namespace osu.Game.Overlays.Profile.Header Width = UserProfileOverlay.CONTENT_X_MARGIN, Child = new ExpandDetailsButton { - RelativeSizeAxes = Axes.Y, Anchor = Anchor.Centre, Origin = Anchor.Centre, DetailsVisible = { BindTarget = DetailsVisible } diff --git a/osu.Game/Overlays/Profile/Header/Components/MessageUserButton.cs b/osu.Game/Overlays/Profile/Header/Components/MessageUserButton.cs index cc6edcdd6a..228765ee1a 100644 --- a/osu.Game/Overlays/Profile/Header/Components/MessageUserButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/MessageUserButton.cs @@ -33,7 +33,6 @@ namespace osu.Game.Overlays.Profile.Header.Components public MessageUserButton() { Content.Alpha = 0; - RelativeSizeAxes = Axes.Y; Child = new SpriteIcon { diff --git a/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderButton.cs b/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderButton.cs index e14d73dd98..cea63574cf 100644 --- a/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderButton.cs @@ -22,6 +22,7 @@ namespace osu.Game.Overlays.Profile.Header.Components protected ProfileHeaderButton() { AutoSizeAxes = Axes.X; + Height = 40; base.Content.Add(new CircularContainer { diff --git a/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderStatisticsButton.cs b/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderStatisticsButton.cs index 118ea4c6aa..0b8f0b4d25 100644 --- a/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderStatisticsButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderStatisticsButton.cs @@ -16,7 +16,6 @@ namespace osu.Game.Overlays.Profile.Header.Components protected ProfileHeaderStatisticsButton() { - RelativeSizeAxes = Axes.Y; Child = new FillFlowContainer { AutoSizeAxes = Axes.X, From c631354b57d39f165332f6482cce23fb1702c576 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 21 Jan 2021 23:39:19 +0300 Subject: [PATCH 056/121] Rename property --- osu.Game/Overlays/Profile/Header/Components/FriendsButton.cs | 2 +- .../Profile/Header/Components/MappingSubscribersButton.cs | 2 +- .../Header/Components/ProfileHeaderStatisticsButton.cs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/FriendsButton.cs b/osu.Game/Overlays/Profile/Header/Components/FriendsButton.cs index e0930b6a65..09916997a4 100644 --- a/osu.Game/Overlays/Profile/Header/Components/FriendsButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/FriendsButton.cs @@ -14,7 +14,7 @@ namespace osu.Game.Overlays.Profile.Header.Components public override string TooltipText => "followers"; - protected override IconUsage CreateIcon => FontAwesome.Solid.User; + protected override IconUsage Icon => FontAwesome.Solid.User; [BackgroundDependencyLoader] private void load() diff --git a/osu.Game/Overlays/Profile/Header/Components/MappingSubscribersButton.cs b/osu.Game/Overlays/Profile/Header/Components/MappingSubscribersButton.cs index ef290676d9..b4d7c9a05c 100644 --- a/osu.Game/Overlays/Profile/Header/Components/MappingSubscribersButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/MappingSubscribersButton.cs @@ -14,7 +14,7 @@ namespace osu.Game.Overlays.Profile.Header.Components public override string TooltipText => "mapping subscribers"; - protected override IconUsage CreateIcon => FontAwesome.Solid.Bell; + protected override IconUsage Icon => FontAwesome.Solid.Bell; [BackgroundDependencyLoader] private void load() diff --git a/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderStatisticsButton.cs b/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderStatisticsButton.cs index 0b8f0b4d25..b65d5e2329 100644 --- a/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderStatisticsButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderStatisticsButton.cs @@ -29,7 +29,7 @@ namespace osu.Game.Overlays.Profile.Header.Components { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Icon = CreateIcon, + Icon = Icon, FillMode = FillMode.Fit, Size = new Vector2(50, 14) }, @@ -44,7 +44,7 @@ namespace osu.Game.Overlays.Profile.Header.Components }; } - protected abstract IconUsage CreateIcon { get; } + protected abstract IconUsage Icon { get; } protected void SetValue(int value) => drawableText.Text = value.ToString("#,##0"); } From 2eba2a9abf2b56f2637843846fab1988ea10aeb3 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 21 Jan 2021 23:40:23 +0300 Subject: [PATCH 057/121] Rename FriendsButton to FollowersButton --- osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs | 2 +- .../Header/Components/{FriendsButton.cs => FollowersButton.cs} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename osu.Game/Overlays/Profile/Header/Components/{FriendsButton.cs => FollowersButton.cs} (92%) diff --git a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs index 8f940cd0cc..04a1040e06 100644 --- a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs @@ -49,7 +49,7 @@ namespace osu.Game.Overlays.Profile.Header Spacing = new Vector2(10, 0), Children = new Drawable[] { - new FriendsButton + new FollowersButton { User = { BindTarget = User } }, diff --git a/osu.Game/Overlays/Profile/Header/Components/FriendsButton.cs b/osu.Game/Overlays/Profile/Header/Components/FollowersButton.cs similarity index 92% rename from osu.Game/Overlays/Profile/Header/Components/FriendsButton.cs rename to osu.Game/Overlays/Profile/Header/Components/FollowersButton.cs index 09916997a4..bd8aa7b3bd 100644 --- a/osu.Game/Overlays/Profile/Header/Components/FriendsButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/FollowersButton.cs @@ -8,7 +8,7 @@ using osu.Game.Users; namespace osu.Game.Overlays.Profile.Header.Components { - public class FriendsButton : ProfileHeaderStatisticsButton + public class FollowersButton : ProfileHeaderStatisticsButton { public readonly Bindable User = new Bindable(); From 65b70759844c37ee4cdf723c615781934da9059e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Jan 2021 15:37:50 +0900 Subject: [PATCH 058/121] Limit the effect of parallax when outside the bounds of the ParallaxContainer This fixes the visual issues that still remain when mouse confining fails. I think it also feels more correct in general. --- .../Graphics/Containers/ParallaxContainer.cs | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/osu.Game/Graphics/Containers/ParallaxContainer.cs b/osu.Game/Graphics/Containers/ParallaxContainer.cs index 4cd3934cde..b501e68ba1 100644 --- a/osu.Game/Graphics/Containers/ParallaxContainer.cs +++ b/osu.Game/Graphics/Containers/ParallaxContainer.cs @@ -24,6 +24,10 @@ namespace osu.Game.Graphics.Containers private Bindable parallaxEnabled; + private const float parallax_duration = 100; + + private bool firstUpdate = true; + public ParallaxContainer() { RelativeSizeAxes = Axes.Both; @@ -60,17 +64,27 @@ namespace osu.Game.Graphics.Containers input = GetContainingInputManager(); } - private bool firstUpdate = true; - protected override void Update() { base.Update(); if (parallaxEnabled.Value) { - Vector2 offset = (input.CurrentState.Mouse == null ? Vector2.Zero : ToLocalSpace(input.CurrentState.Mouse.Position) - DrawSize / 2) * ParallaxAmount; + Vector2 offset = Vector2.Zero; - const float parallax_duration = 100; + if (input.CurrentState.Mouse != null) + { + var sizeDiv2 = DrawSize / 2; + + Vector2 relativeAmount = ToLocalSpace(input.CurrentState.Mouse.Position) - sizeDiv2; + + const float base_factor = 0.999f; + + relativeAmount.X = (float)(Math.Sign(relativeAmount.X) * Interpolation.Damp(0, 1, base_factor, Math.Abs(relativeAmount.X))); + relativeAmount.Y = (float)(Math.Sign(relativeAmount.Y) * Interpolation.Damp(0, 1, base_factor, Math.Abs(relativeAmount.Y))); + + offset = relativeAmount * sizeDiv2 * ParallaxAmount; + } double elapsed = Math.Clamp(Clock.ElapsedFrameTime, 0, parallax_duration); From fca6b15d2fa3fcfeec85cd3336aa181be6e1ffbb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Jan 2021 16:05:45 +0900 Subject: [PATCH 059/121] Fix local echo messages remaining permanently dimmed when chatting via multiplayer --- osu.Game/Overlays/Chat/ChatLine.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChatLine.cs b/osu.Game/Overlays/Chat/ChatLine.cs index 4eb348ae33..f43420e35e 100644 --- a/osu.Game/Overlays/Chat/ChatLine.cs +++ b/osu.Game/Overlays/Chat/ChatLine.cs @@ -190,13 +190,13 @@ namespace osu.Game.Overlays.Chat } } }; - - updateMessageContent(); } protected override void LoadComplete() { base.LoadComplete(); + + updateMessageContent(); FinishTransforms(true); } From bfabb1fdea25412c24088185fc0ed2c3e8a7f40c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Jan 2021 16:50:22 +0900 Subject: [PATCH 060/121] Change offset value to 10% --- osu.Game/Graphics/Containers/SectionsContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 6ed161fa77..16bc6e4fc5 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -110,8 +110,8 @@ namespace osu.Game.Graphics.Containers /// /// The percentage of the container to consider the centre-point for deciding the active section (and scrolling to a requested section). - /// - private const float scroll_y_centre = 0.2f; + /// + private const float scroll_y_centre = 0.1f; public SectionsContainer() { From a5f7ca485bfeeb38d88f113c6afe1bba6c49ba3c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Jan 2021 16:53:31 +0900 Subject: [PATCH 061/121] Fix unintended xmldoc tag edit --- osu.Game/Graphics/Containers/SectionsContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 16bc6e4fc5..741fc17695 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -110,7 +110,7 @@ namespace osu.Game.Graphics.Containers /// /// The percentage of the container to consider the centre-point for deciding the active section (and scrolling to a requested section). - /// + /// private const float scroll_y_centre = 0.1f; public SectionsContainer() From 6379381f957c9194050f9ae5f47d15edba39f802 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 22 Jan 2021 20:46:20 +0300 Subject: [PATCH 062/121] Make VotePill background transparent for own comments --- .../Visual/Online/TestSceneVotePill.cs | 17 +++++++++++++++-- osu.Game/Overlays/Comments/VotePill.cs | 14 ++++++++++---- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneVotePill.cs b/osu.Game.Tests/Visual/Online/TestSceneVotePill.cs index 9bb29541ec..420f6b1ab6 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneVotePill.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneVotePill.cs @@ -7,6 +7,7 @@ using osu.Game.Overlays.Comments; using osu.Game.Online.API.Requests.Responses; using osu.Framework.Allocation; using osu.Game.Overlays; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Tests.Visual.Online { @@ -16,13 +17,14 @@ namespace osu.Game.Tests.Visual.Online [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); - private VotePill votePill; + private TestPill votePill; [Test] public void TestUserCommentPill() { AddStep("Log in", logIn); AddStep("User comment", () => addVotePill(getUserComment())); + AddAssert("Background is transparent", () => votePill.Background.Alpha == 0); AddStep("Click", () => votePill.Click()); AddAssert("Not loading", () => !votePill.IsLoading); } @@ -32,6 +34,7 @@ namespace osu.Game.Tests.Visual.Online { AddStep("Log in", logIn); AddStep("Random comment", () => addVotePill(getRandomComment())); + AddAssert("Background is not transparent", () => votePill.Background.Alpha == 1); AddStep("Click", () => votePill.Click()); AddAssert("Loading", () => votePill.IsLoading); } @@ -64,11 +67,21 @@ namespace osu.Game.Tests.Visual.Online private void addVotePill(Comment comment) { Clear(); - Add(votePill = new VotePill(comment) + Add(votePill = new TestPill(comment) { Anchor = Anchor.Centre, Origin = Anchor.Centre, }); } + + private class TestPill : VotePill + { + public new Box Background => base.Background; + + public TestPill(Comment comment) + : base(comment) + { + } + } } } diff --git a/osu.Game/Overlays/Comments/VotePill.cs b/osu.Game/Overlays/Comments/VotePill.cs index aa9723ea85..b6e6aa82c7 100644 --- a/osu.Game/Overlays/Comments/VotePill.cs +++ b/osu.Game/Overlays/Comments/VotePill.cs @@ -36,8 +36,10 @@ namespace osu.Game.Overlays.Comments [Resolved] private OverlayColourProvider colourProvider { get; set; } + protected Box Background { get; private set; } + private readonly Comment comment; - private Box background; + private Box hoverLayer; private CircularContainer borderContainer; private SpriteText sideNumber; @@ -62,8 +64,12 @@ namespace osu.Game.Overlays.Comments AccentColour = borderContainer.BorderColour = sideNumber.Colour = colours.GreenLight; hoverLayer.Colour = Color4.Black.Opacity(0.5f); - if (api.IsLoggedIn && api.LocalUser.Value.Id != comment.UserId) + var ownComment = api.LocalUser.Value.Id == comment.UserId; + + if (api.IsLoggedIn && !ownComment) Action = onAction; + + Background.Alpha = ownComment ? 0 : 1; } protected override void LoadComplete() @@ -71,7 +77,7 @@ namespace osu.Game.Overlays.Comments base.LoadComplete(); isVoted.Value = comment.IsVoted; votesCount.Value = comment.VotesCount; - isVoted.BindValueChanged(voted => background.Colour = voted.NewValue ? AccentColour : colourProvider.Background6, true); + isVoted.BindValueChanged(voted => Background.Colour = voted.NewValue ? AccentColour : colourProvider.Background6, true); votesCount.BindValueChanged(count => votesCounter.Text = $"+{count.NewValue}", true); } @@ -102,7 +108,7 @@ namespace osu.Game.Overlays.Comments Masking = true, Children = new Drawable[] { - background = new Box + Background = new Box { RelativeSizeAxes = Axes.Both }, From 61fcb486a8c7aea90c40b2c9f881fcc295f7b8de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 22 Jan 2021 19:47:38 +0100 Subject: [PATCH 063/121] Trim unnecessary parentheses --- osu.Game/Graphics/Containers/SectionsContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 741fc17695..624ec70cb6 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -181,7 +181,7 @@ namespace osu.Game.Graphics.Containers { base.UpdateAfterChildren(); - float fixedHeaderSize = (FixedHeader?.LayoutSize.Y ?? 0); + float fixedHeaderSize = FixedHeader?.LayoutSize.Y ?? 0; float expandableHeaderSize = ExpandableHeader?.LayoutSize.Y ?? 0; float headerH = expandableHeaderSize + fixedHeaderSize; From 20161aea6a472320f34ff0e8057d08a0cdea527d Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 22 Jan 2021 21:47:53 +0300 Subject: [PATCH 064/121] Show LoginOverlay if not logged-in when clicking on a pill --- .../Visual/Online/TestSceneVotePill.cs | 32 ++++++++++++++++--- osu.Game/Overlays/Comments/VotePill.cs | 11 ++++++- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneVotePill.cs b/osu.Game.Tests/Visual/Online/TestSceneVotePill.cs index 420f6b1ab6..e9e826e62f 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneVotePill.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneVotePill.cs @@ -8,6 +8,7 @@ using osu.Game.Online.API.Requests.Responses; using osu.Framework.Allocation; using osu.Game.Overlays; using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Containers; namespace osu.Game.Tests.Visual.Online { @@ -17,11 +18,30 @@ namespace osu.Game.Tests.Visual.Online [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); + [Cached] + private LoginOverlay login; + private TestPill votePill; + private readonly Container pillContainer; + + public TestSceneVotePill() + { + AddRange(new Drawable[] + { + pillContainer = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both + }, + login = new LoginOverlay() + }); + } [Test] public void TestUserCommentPill() { + AddStep("Hide login overlay", () => login.Hide()); AddStep("Log in", logIn); AddStep("User comment", () => addVotePill(getUserComment())); AddAssert("Background is transparent", () => votePill.Background.Alpha == 0); @@ -32,9 +52,10 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestRandomCommentPill() { + AddStep("Hide login overlay", () => login.Hide()); AddStep("Log in", logIn); AddStep("Random comment", () => addVotePill(getRandomComment())); - AddAssert("Background is not transparent", () => votePill.Background.Alpha == 1); + AddAssert("Background is visible", () => votePill.Background.Alpha == 1); AddStep("Click", () => votePill.Click()); AddAssert("Loading", () => votePill.IsLoading); } @@ -42,10 +63,11 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestOfflineRandomCommentPill() { + AddStep("Hide login overlay", () => login.Hide()); AddStep("Log out", API.Logout); AddStep("Random comment", () => addVotePill(getRandomComment())); AddStep("Click", () => votePill.Click()); - AddAssert("Not loading", () => !votePill.IsLoading); + AddAssert("Login overlay is visible", () => login.State.Value == Visibility.Visible); } private void logIn() => API.Login("localUser", "password"); @@ -66,12 +88,12 @@ namespace osu.Game.Tests.Visual.Online private void addVotePill(Comment comment) { - Clear(); - Add(votePill = new TestPill(comment) + pillContainer.Clear(); + pillContainer.Child = votePill = new TestPill(comment) { Anchor = Anchor.Centre, Origin = Anchor.Centre, - }); + }; } private class TestPill : VotePill diff --git a/osu.Game/Overlays/Comments/VotePill.cs b/osu.Game/Overlays/Comments/VotePill.cs index b6e6aa82c7..cf3c470f96 100644 --- a/osu.Game/Overlays/Comments/VotePill.cs +++ b/osu.Game/Overlays/Comments/VotePill.cs @@ -33,6 +33,9 @@ namespace osu.Game.Overlays.Comments [Resolved] private IAPIProvider api { get; set; } + [Resolved(canBeNull: true)] + private LoginOverlay login { get; set; } + [Resolved] private OverlayColourProvider colourProvider { get; set; } @@ -66,7 +69,7 @@ namespace osu.Game.Overlays.Comments var ownComment = api.LocalUser.Value.Id == comment.UserId; - if (api.IsLoggedIn && !ownComment) + if (!ownComment) Action = onAction; Background.Alpha = ownComment ? 0 : 1; @@ -83,6 +86,12 @@ namespace osu.Game.Overlays.Comments private void onAction() { + if (!api.IsLoggedIn) + { + login?.Show(); + return; + } + request = new CommentVoteRequest(comment.Id, isVoted.Value ? CommentVoteAction.UnVote : CommentVoteAction.Vote); request.Success += onSuccess; api.Queue(request); From f3192877fee57db6f29d84faa208d10d1bdf41b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 22 Jan 2021 19:48:33 +0100 Subject: [PATCH 065/121] Update outdated comment --- osu.Game/Graphics/Containers/SectionsContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 624ec70cb6..8ab146efe7 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -217,7 +217,7 @@ namespace osu.Game.Graphics.Containers var smallestSectionHeight = Children.Count > 0 ? Children.Min(d => d.Height) : 0; - // scroll offset is our fixed header height if we have it plus 20% of content height + // scroll offset is our fixed header height if we have it plus 10% of content height // plus 5% to fix floating point errors and to not have a section instantly unselect when scrolling upwards // but the 5% can't be bigger than our smallest section height, otherwise it won't get selected correctly float selectionLenienceAboveSection = Math.Min(smallestSectionHeight / 2.0f, scrollContainer.DisplayableContent * 0.05f); From 3d42cc1f9199715990aa25c234a15885a87ec09a Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 22 Jan 2021 22:27:26 +0300 Subject: [PATCH 066/121] Minor refactoring --- .../Visual/Online/TestSceneVotePill.cs | 19 ++----- osu.Game/Overlays/Comments/VotePill.cs | 53 ++++++++----------- 2 files changed, 26 insertions(+), 46 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneVotePill.cs b/osu.Game.Tests/Visual/Online/TestSceneVotePill.cs index e9e826e62f..6334c014c8 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneVotePill.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneVotePill.cs @@ -7,7 +7,6 @@ using osu.Game.Overlays.Comments; using osu.Game.Online.API.Requests.Responses; using osu.Framework.Allocation; using osu.Game.Overlays; -using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Containers; namespace osu.Game.Tests.Visual.Online @@ -21,7 +20,7 @@ namespace osu.Game.Tests.Visual.Online [Cached] private LoginOverlay login; - private TestPill votePill; + private VotePill votePill; private readonly Container pillContainer; public TestSceneVotePill() @@ -44,7 +43,7 @@ namespace osu.Game.Tests.Visual.Online AddStep("Hide login overlay", () => login.Hide()); AddStep("Log in", logIn); AddStep("User comment", () => addVotePill(getUserComment())); - AddAssert("Background is transparent", () => votePill.Background.Alpha == 0); + AddAssert("Is disabled", () => !votePill.Enabled.Value); AddStep("Click", () => votePill.Click()); AddAssert("Not loading", () => !votePill.IsLoading); } @@ -55,7 +54,7 @@ namespace osu.Game.Tests.Visual.Online AddStep("Hide login overlay", () => login.Hide()); AddStep("Log in", logIn); AddStep("Random comment", () => addVotePill(getRandomComment())); - AddAssert("Background is visible", () => votePill.Background.Alpha == 1); + AddAssert("Is enabled", () => votePill.Enabled.Value); AddStep("Click", () => votePill.Click()); AddAssert("Loading", () => votePill.IsLoading); } @@ -89,21 +88,11 @@ namespace osu.Game.Tests.Visual.Online private void addVotePill(Comment comment) { pillContainer.Clear(); - pillContainer.Child = votePill = new TestPill(comment) + pillContainer.Child = votePill = new VotePill(comment) { Anchor = Anchor.Centre, Origin = Anchor.Centre, }; } - - private class TestPill : VotePill - { - public new Box Background => base.Background; - - public TestPill(Comment comment) - : base(comment) - { - } - } } } diff --git a/osu.Game/Overlays/Comments/VotePill.cs b/osu.Game/Overlays/Comments/VotePill.cs index cf3c470f96..04a0508f3d 100644 --- a/osu.Game/Overlays/Comments/VotePill.cs +++ b/osu.Game/Overlays/Comments/VotePill.cs @@ -39,10 +39,9 @@ namespace osu.Game.Overlays.Comments [Resolved] private OverlayColourProvider colourProvider { get; set; } - protected Box Background { get; private set; } - + private bool isOwnComment; private readonly Comment comment; - + private Box background; private Box hoverLayer; private CircularContainer borderContainer; private SpriteText sideNumber; @@ -64,15 +63,14 @@ namespace osu.Game.Overlays.Comments [BackgroundDependencyLoader] private void load(OsuColour colours) { + isOwnComment = api.LocalUser.Value.Id == comment.UserId; + Action = onAction; + AccentColour = borderContainer.BorderColour = sideNumber.Colour = colours.GreenLight; hoverLayer.Colour = Color4.Black.Opacity(0.5f); + background.Alpha = isOwnComment ? 0 : 1; - var ownComment = api.LocalUser.Value.Id == comment.UserId; - - if (!ownComment) - Action = onAction; - - Background.Alpha = ownComment ? 0 : 1; + Enabled.Value = !isOwnComment; } protected override void LoadComplete() @@ -80,7 +78,7 @@ namespace osu.Game.Overlays.Comments base.LoadComplete(); isVoted.Value = comment.IsVoted; votesCount.Value = comment.VotesCount; - isVoted.BindValueChanged(voted => Background.Colour = voted.NewValue ? AccentColour : colourProvider.Background6, true); + isVoted.BindValueChanged(voted => background.Colour = voted.NewValue ? AccentColour : colourProvider.Background6, true); votesCount.BindValueChanged(count => votesCounter.Text = $"+{count.NewValue}", true); } @@ -117,7 +115,7 @@ namespace osu.Game.Overlays.Comments Masking = true, Children = new Drawable[] { - Background = new Box + background = new Box { RelativeSizeAxes = Axes.Both }, @@ -151,55 +149,48 @@ namespace osu.Game.Overlays.Comments protected override void OnLoadStarted() { votesCounter.FadeOut(duration, Easing.OutQuint); - updateDisplay(); + updateDisplay(false); } protected override void OnLoadFinished() { votesCounter.FadeIn(duration, Easing.OutQuint); - - if (IsHovered) - onHoverAction(); + updateDisplay(IsHovered); } protected override bool OnHover(HoverEvent e) { - onHoverAction(); + if (!isOwnComment && !IsLoading) + updateDisplay(true); + return base.OnHover(e); } protected override void OnHoverLost(HoverLostEvent e) { - updateDisplay(); + if (!isOwnComment && !IsLoading) + updateDisplay(false); + base.OnHoverLost(e); } - private void updateDisplay() + private void updateDisplay(bool isHovered) { - if (Action == null) - return; - if (isVoted.Value) { - hoverLayer.FadeTo(IsHovered ? 1 : 0); + hoverLayer.FadeTo(isHovered ? 1 : 0); sideNumber.Hide(); } else - sideNumber.FadeTo(IsHovered ? 1 : 0); + sideNumber.FadeTo(isHovered ? 1 : 0); - borderContainer.BorderThickness = IsHovered ? 3 : 0; - } - - private void onHoverAction() - { - if (!IsLoading) - updateDisplay(); + borderContainer.BorderThickness = isHovered ? 3 : 0; } protected override void Dispose(bool isDisposing) { - base.Dispose(isDisposing); request?.Cancel(); + base.Dispose(isDisposing); } } } From e9d10bb6e7bf9d9e48d25263cdc7c8f696b9a27a Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 22 Jan 2021 22:49:49 +0300 Subject: [PATCH 067/121] Revert "Minor refactoring" This reverts commit 3d42cc1f9199715990aa25c234a15885a87ec09a. --- .../Visual/Online/TestSceneVotePill.cs | 19 +++++-- osu.Game/Overlays/Comments/VotePill.cs | 53 +++++++++++-------- 2 files changed, 46 insertions(+), 26 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneVotePill.cs b/osu.Game.Tests/Visual/Online/TestSceneVotePill.cs index 6334c014c8..e9e826e62f 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneVotePill.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneVotePill.cs @@ -7,6 +7,7 @@ using osu.Game.Overlays.Comments; using osu.Game.Online.API.Requests.Responses; using osu.Framework.Allocation; using osu.Game.Overlays; +using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Containers; namespace osu.Game.Tests.Visual.Online @@ -20,7 +21,7 @@ namespace osu.Game.Tests.Visual.Online [Cached] private LoginOverlay login; - private VotePill votePill; + private TestPill votePill; private readonly Container pillContainer; public TestSceneVotePill() @@ -43,7 +44,7 @@ namespace osu.Game.Tests.Visual.Online AddStep("Hide login overlay", () => login.Hide()); AddStep("Log in", logIn); AddStep("User comment", () => addVotePill(getUserComment())); - AddAssert("Is disabled", () => !votePill.Enabled.Value); + AddAssert("Background is transparent", () => votePill.Background.Alpha == 0); AddStep("Click", () => votePill.Click()); AddAssert("Not loading", () => !votePill.IsLoading); } @@ -54,7 +55,7 @@ namespace osu.Game.Tests.Visual.Online AddStep("Hide login overlay", () => login.Hide()); AddStep("Log in", logIn); AddStep("Random comment", () => addVotePill(getRandomComment())); - AddAssert("Is enabled", () => votePill.Enabled.Value); + AddAssert("Background is visible", () => votePill.Background.Alpha == 1); AddStep("Click", () => votePill.Click()); AddAssert("Loading", () => votePill.IsLoading); } @@ -88,11 +89,21 @@ namespace osu.Game.Tests.Visual.Online private void addVotePill(Comment comment) { pillContainer.Clear(); - pillContainer.Child = votePill = new VotePill(comment) + pillContainer.Child = votePill = new TestPill(comment) { Anchor = Anchor.Centre, Origin = Anchor.Centre, }; } + + private class TestPill : VotePill + { + public new Box Background => base.Background; + + public TestPill(Comment comment) + : base(comment) + { + } + } } } diff --git a/osu.Game/Overlays/Comments/VotePill.cs b/osu.Game/Overlays/Comments/VotePill.cs index 04a0508f3d..cf3c470f96 100644 --- a/osu.Game/Overlays/Comments/VotePill.cs +++ b/osu.Game/Overlays/Comments/VotePill.cs @@ -39,9 +39,10 @@ namespace osu.Game.Overlays.Comments [Resolved] private OverlayColourProvider colourProvider { get; set; } - private bool isOwnComment; + protected Box Background { get; private set; } + private readonly Comment comment; - private Box background; + private Box hoverLayer; private CircularContainer borderContainer; private SpriteText sideNumber; @@ -63,14 +64,15 @@ namespace osu.Game.Overlays.Comments [BackgroundDependencyLoader] private void load(OsuColour colours) { - isOwnComment = api.LocalUser.Value.Id == comment.UserId; - Action = onAction; - AccentColour = borderContainer.BorderColour = sideNumber.Colour = colours.GreenLight; hoverLayer.Colour = Color4.Black.Opacity(0.5f); - background.Alpha = isOwnComment ? 0 : 1; - Enabled.Value = !isOwnComment; + var ownComment = api.LocalUser.Value.Id == comment.UserId; + + if (!ownComment) + Action = onAction; + + Background.Alpha = ownComment ? 0 : 1; } protected override void LoadComplete() @@ -78,7 +80,7 @@ namespace osu.Game.Overlays.Comments base.LoadComplete(); isVoted.Value = comment.IsVoted; votesCount.Value = comment.VotesCount; - isVoted.BindValueChanged(voted => background.Colour = voted.NewValue ? AccentColour : colourProvider.Background6, true); + isVoted.BindValueChanged(voted => Background.Colour = voted.NewValue ? AccentColour : colourProvider.Background6, true); votesCount.BindValueChanged(count => votesCounter.Text = $"+{count.NewValue}", true); } @@ -115,7 +117,7 @@ namespace osu.Game.Overlays.Comments Masking = true, Children = new Drawable[] { - background = new Box + Background = new Box { RelativeSizeAxes = Axes.Both }, @@ -149,48 +151,55 @@ namespace osu.Game.Overlays.Comments protected override void OnLoadStarted() { votesCounter.FadeOut(duration, Easing.OutQuint); - updateDisplay(false); + updateDisplay(); } protected override void OnLoadFinished() { votesCounter.FadeIn(duration, Easing.OutQuint); - updateDisplay(IsHovered); + + if (IsHovered) + onHoverAction(); } protected override bool OnHover(HoverEvent e) { - if (!isOwnComment && !IsLoading) - updateDisplay(true); - + onHoverAction(); return base.OnHover(e); } protected override void OnHoverLost(HoverLostEvent e) { - if (!isOwnComment && !IsLoading) - updateDisplay(false); - + updateDisplay(); base.OnHoverLost(e); } - private void updateDisplay(bool isHovered) + private void updateDisplay() { + if (Action == null) + return; + if (isVoted.Value) { - hoverLayer.FadeTo(isHovered ? 1 : 0); + hoverLayer.FadeTo(IsHovered ? 1 : 0); sideNumber.Hide(); } else - sideNumber.FadeTo(isHovered ? 1 : 0); + sideNumber.FadeTo(IsHovered ? 1 : 0); - borderContainer.BorderThickness = isHovered ? 3 : 0; + borderContainer.BorderThickness = IsHovered ? 3 : 0; + } + + private void onHoverAction() + { + if (!IsLoading) + updateDisplay(); } protected override void Dispose(bool isDisposing) { - request?.Cancel(); base.Dispose(isDisposing); + request?.Cancel(); } } } From adcef19ab25953f003bc150654798f82e739a7d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 23 Jan 2021 15:44:30 +0100 Subject: [PATCH 068/121] Add coverage for operation tracker with failing tests --- .../NonVisual/OngoingOperationTrackerTest.cs | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 osu.Game.Tests/NonVisual/OngoingOperationTrackerTest.cs diff --git a/osu.Game.Tests/NonVisual/OngoingOperationTrackerTest.cs b/osu.Game.Tests/NonVisual/OngoingOperationTrackerTest.cs new file mode 100644 index 0000000000..b2be83d1f9 --- /dev/null +++ b/osu.Game.Tests/NonVisual/OngoingOperationTrackerTest.cs @@ -0,0 +1,62 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using NUnit.Framework; +using osu.Framework.Bindables; +using osu.Framework.Testing; +using osu.Game.Screens.OnlinePlay; +using osu.Game.Tests.Visual; + +namespace osu.Game.Tests.NonVisual +{ + [HeadlessTest] + public class OngoingOperationTrackerTest : OsuTestScene + { + private OngoingOperationTracker tracker; + private IBindable operationInProgress; + + [SetUpSteps] + public void SetUp() + { + AddStep("create tracker", () => Child = tracker = new OngoingOperationTracker()); + AddStep("bind to operation status", () => operationInProgress = tracker.InProgress.GetBoundCopy()); + } + + [Test] + public void TestOperationTracking() + { + IDisposable firstOperation = null; + IDisposable secondOperation = null; + + AddStep("begin first operation", () => firstOperation = tracker.BeginOperation()); + AddAssert("operation in progress", () => operationInProgress.Value); + + AddStep("cannot start another operation", + () => Assert.Throws(() => tracker.BeginOperation())); + + AddStep("end first operation", () => firstOperation.Dispose()); + AddAssert("operation is ended", () => !operationInProgress.Value); + + AddStep("start second operation", () => secondOperation = tracker.BeginOperation()); + AddAssert("operation in progress", () => operationInProgress.Value); + + AddStep("dispose first operation again", () => firstOperation.Dispose()); + AddAssert("operation in progress", () => operationInProgress.Value); + + AddStep("dispose second operation", () => secondOperation.Dispose()); + AddAssert("operation is ended", () => !operationInProgress.Value); + } + + [Test] + public void TestOperationDisposalAfterTracker() + { + IDisposable operation = null; + + AddStep("begin operation", () => operation = tracker.BeginOperation()); + AddStep("dispose tracker", () => tracker.Expire()); + AddStep("end operation", () => operation.Dispose()); + AddAssert("operation is ended", () => !operationInProgress.Value); + } + } +} From 18b309a195e9aea1890a726dc1c77ed1f2e7afdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 23 Jan 2021 16:02:51 +0100 Subject: [PATCH 069/121] Make disposal of tracker operation idempotent --- osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs b/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs index 5c9e9ce90b..b834d4fa25 100644 --- a/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs +++ b/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs @@ -49,10 +49,7 @@ namespace osu.Game.Screens.OnlinePlay private void endOperation() { - if (leasedInProgress == null) - throw new InvalidOperationException("Cannot end operation multiple times."); - - leasedInProgress.Return(); + leasedInProgress?.Return(); leasedInProgress = null; } } From 7f89d9117d98f593209bc90271dd099a2febbcfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 23 Jan 2021 16:04:12 +0100 Subject: [PATCH 070/121] Make disposal of tracker idempotent for operations --- osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs b/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs index b834d4fa25..5d171e6e43 100644 --- a/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs +++ b/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs @@ -52,5 +52,13 @@ namespace osu.Game.Screens.OnlinePlay leasedInProgress?.Return(); leasedInProgress = null; } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + // base call does an UnbindAllBindables(). + // clean up the leased reference here so that it doesn't get returned twice. + leasedInProgress = null; + } } } From d22f557a3bd074ff4ee29128f7d8a3375dece37d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 23 Jan 2021 16:14:58 +0100 Subject: [PATCH 071/121] Remove possibility of double-disposal interference --- .../OnlinePlay/OngoingOperationTracker.cs | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs b/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs index 5d171e6e43..060f1d7b91 100644 --- a/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs +++ b/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs @@ -44,7 +44,7 @@ namespace osu.Game.Screens.OnlinePlay leasedInProgress.Value = true; // for extra safety, marshal the end of operation back to the update thread if necessary. - return new InvokeOnDisposal(() => Scheduler.Add(endOperation, false)); + return new OngoingOperation(() => Scheduler.Add(endOperation, false)); } private void endOperation() @@ -60,5 +60,26 @@ namespace osu.Game.Screens.OnlinePlay // clean up the leased reference here so that it doesn't get returned twice. leasedInProgress = null; } + + private class OngoingOperation : InvokeOnDisposal + { + private bool isDisposed; + + public OngoingOperation(Action action) + : base(action) + { + } + + public override void Dispose() + { + // base class does not check disposal state for performance reasons which aren't relevant here. + // track locally, to avoid interfering with other operations in case of a potential double-disposal. + if (isDisposed) + return; + + base.Dispose(); + isDisposed = true; + } + } } } From c30b700b3a320d070ceebac14e3f43cdd770661c Mon Sep 17 00:00:00 2001 From: yhsphd Date: Sun, 24 Jan 2021 00:26:52 +0900 Subject: [PATCH 072/121] "started" for past matches fixes grammar error at 'coming up next' section in schedule screen which displays schedule like "starting an hour ago" for past matches --- osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs index 88289ad6bd..b3fa9dc91c 100644 --- a/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs +++ b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs @@ -194,7 +194,7 @@ namespace osu.Game.Tournament.Screens.Schedule { new TournamentSpriteText { - Text = "Starting ", + Text = match.NewValue.Date.Value.CompareTo(DateTimeOffset.Now) > 0 ? "Starting " : "Started ", Font = OsuFont.Torus.With(size: 24, weight: FontWeight.Regular) }, new DrawableDate(match.NewValue.Date.Value) From 899942611fcb085fc09226c84a7bbbd47500450f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 23 Jan 2021 17:01:47 +0100 Subject: [PATCH 073/121] Add tests for time display --- .../Screens/TestSceneScheduleScreen.cs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneScheduleScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneScheduleScreen.cs index b240ef3ae5..0da8d1eb4a 100644 --- a/osu.Game.Tournament.Tests/Screens/TestSceneScheduleScreen.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneScheduleScreen.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 NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Tournament.Components; @@ -16,5 +18,23 @@ namespace osu.Game.Tournament.Tests.Screens Add(new TourneyVideo("main") { RelativeSizeAxes = Axes.Both }); Add(new ScheduleScreen()); } + + [Test] + public void TestCurrentMatchTime() + { + setMatchDate(TimeSpan.FromDays(-1)); + setMatchDate(TimeSpan.FromSeconds(5)); + setMatchDate(TimeSpan.FromMinutes(4)); + setMatchDate(TimeSpan.FromHours(3)); + } + + private void setMatchDate(TimeSpan relativeTime) + // Humanizer cannot handle negative timespans. + => AddStep($"start time is {relativeTime}", () => + { + var match = CreateSampleMatch(); + match.Date.Value = DateTimeOffset.Now + relativeTime; + Ladder.CurrentMatch.Value = match; + }); } } From a8fa09103cc0573dff3c0a3f2681293f40f45ab7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 23 Jan 2021 17:16:13 +0100 Subject: [PATCH 074/121] Update match start text prefix in real time --- .../Screens/Schedule/ScheduleScreen.cs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs index b3fa9dc91c..c1d8c8ddd3 100644 --- a/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs +++ b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs @@ -192,12 +192,7 @@ namespace osu.Game.Tournament.Screens.Schedule Origin = Anchor.CentreLeft, Children = new Drawable[] { - new TournamentSpriteText - { - Text = match.NewValue.Date.Value.CompareTo(DateTimeOffset.Now) > 0 ? "Starting " : "Started ", - Font = OsuFont.Torus.With(size: 24, weight: FontWeight.Regular) - }, - new DrawableDate(match.NewValue.Date.Value) + new ScheduleMatchDate(match.NewValue.Date.Value) { Font = OsuFont.Torus.With(size: 24, weight: FontWeight.Regular) } @@ -251,6 +246,18 @@ namespace osu.Game.Tournament.Screens.Schedule } } + public class ScheduleMatchDate : DrawableDate + { + public ScheduleMatchDate(DateTimeOffset date, float textSize = OsuFont.DEFAULT_FONT_SIZE, bool italic = true) + : base(date, textSize, italic) + { + } + + protected override string Format() => Date < DateTimeOffset.Now + ? $"Started {base.Format()}" + : $"Starting {base.Format()}"; + } + public class ScheduleContainer : Container { protected override Container Content => content; From eaa1519710577df734eb51e003777980fa434c81 Mon Sep 17 00:00:00 2001 From: Shivam Date: Sun, 24 Jan 2021 18:41:45 +0100 Subject: [PATCH 075/121] Implement native osu!lazer mod icons for tournament --- .../Components/TournamentBeatmapPanel.cs | 98 ++++++++++++++++--- 1 file changed, 86 insertions(+), 12 deletions(-) diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index 477bf4bd63..7fac2bac71 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -16,6 +16,9 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Tournament.Models; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Screens.Play.HUD; using osuTK.Graphics; namespace osu.Game.Tournament.Components @@ -124,21 +127,11 @@ namespace osu.Game.Tournament.Components if (!string.IsNullOrEmpty(mods)) { - AddInternal(new Container + AddInternal(new ModSprite { - RelativeSizeAxes = Axes.Y, - Width = 60, Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, - Margin = new MarginPadding(10), - Child = new Sprite - { - FillMode = FillMode.Fit, - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Texture = textures.Get($"mods/{mods}"), - } + Mod = mods }); } } @@ -192,5 +185,86 @@ namespace osu.Game.Tournament.Components Alpha = 1; } } + + private class ModSprite : Container + { + public string Mod; + + public ModSprite() + { + Margin = new MarginPadding(10); + Width = 60; + RelativeSizeAxes = Axes.Y; + } + + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + var texture = textures.Get($"mods/{Mod}"); + + if (texture != null) + { + Child = new Sprite + { + FillMode = FillMode.Fit, + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Texture = texture + }; + } + else + { + Mod selectedMod = null; + + switch (Mod) + { + case "DT": + selectedMod = new OsuModDoubleTime(); + break; + + case "FL": + selectedMod = new OsuModFlashlight(); + break; + + case "HT": + selectedMod = new OsuModHalfTime(); + break; + + case "HD": + selectedMod = new OsuModHidden(); + break; + + case "HR": + selectedMod = new OsuModHardRock(); + break; + + case "NF": + selectedMod = new OsuModNoFail(); + break; + + case "EZ": + selectedMod = new OsuModEasy(); + break; + } + + if (selectedMod != null) + { + Child = new ModDisplay + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Current = + { + Value = new[] + { + selectedMod + } + } + }; + } + } + } + } } } From d38db6eace2c6b00f5a3b92fe0f603302184f993 Mon Sep 17 00:00:00 2001 From: Shivam Date: Sun, 24 Jan 2021 23:29:05 +0100 Subject: [PATCH 076/121] Change ModSprite to use ruleset's mods directly. --- .../Components/TournamentBeatmapPanel.cs | 66 +++++-------------- 1 file changed, 16 insertions(+), 50 deletions(-) diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index 7fac2bac71..8fc52f8b4b 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -15,10 +15,9 @@ using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; -using osu.Game.Tournament.Models; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Rulesets; using osu.Game.Screens.Play.HUD; +using osu.Game.Tournament.Models; using osuTK.Graphics; namespace osu.Game.Tournament.Components @@ -190,6 +189,12 @@ namespace osu.Game.Tournament.Components { public string Mod; + [Resolved] + private LadderInfo ladderInfo { get; set; } + + [Resolved] + private RulesetStore rulesets { get; set; } + public ModSprite() { Margin = new MarginPadding(10); @@ -198,7 +203,7 @@ namespace osu.Game.Tournament.Components } [BackgroundDependencyLoader] - private void load(TextureStore textures) + private void load(TextureStore textures, IBindable ruleset) { var texture = textures.Get($"mods/{Mod}"); @@ -215,54 +220,15 @@ namespace osu.Game.Tournament.Components } else { - Mod selectedMod = null; - - switch (Mod) + Child = new ModDisplay { - case "DT": - selectedMod = new OsuModDoubleTime(); - break; - - case "FL": - selectedMod = new OsuModFlashlight(); - break; - - case "HT": - selectedMod = new OsuModHalfTime(); - break; - - case "HD": - selectedMod = new OsuModHidden(); - break; - - case "HR": - selectedMod = new OsuModHardRock(); - break; - - case "NF": - selectedMod = new OsuModNoFail(); - break; - - case "EZ": - selectedMod = new OsuModEasy(); - break; - } - - if (selectedMod != null) - { - Child = new ModDisplay + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Current = { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Current = - { - Value = new[] - { - selectedMod - } - } - }; - } + Value = rulesets.GetRuleset(ladderInfo.Ruleset.Value.ID ?? 0).CreateInstance().GetAllMods().Where(mod => mod.Acronym == Mod).ToArray() + } + }; } } } From c6d46129ad25567e2a3c9736de085e3669c78557 Mon Sep 17 00:00:00 2001 From: Shivam Date: Sun, 24 Jan 2021 23:33:02 +0100 Subject: [PATCH 077/121] Remove unneccessary ruleset parameter --- osu.Game.Tournament/Components/TournamentBeatmapPanel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index 8fc52f8b4b..92ac123097 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -203,7 +203,7 @@ namespace osu.Game.Tournament.Components } [BackgroundDependencyLoader] - private void load(TextureStore textures, IBindable ruleset) + private void load(TextureStore textures) { var texture = textures.Get($"mods/{Mod}"); From bb8113fb5136b1c235693899ea11b58c649f289c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 Jan 2021 14:47:47 +0900 Subject: [PATCH 078/121] Fix mod select footer not animating correctly on first reveal --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index b93602116b..1258ba719d 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -216,9 +216,9 @@ namespace osu.Game.Overlays.Mods }, new Drawable[] { - // Footer new Container { + Name = "Footer content", RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Origin = Anchor.TopCentre, @@ -237,10 +237,9 @@ namespace osu.Game.Overlays.Mods Anchor = Anchor.BottomCentre, AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, + RelativePositionAxes = Axes.X, Width = content_width, Spacing = new Vector2(footer_button_spacing, footer_button_spacing / 2), - LayoutDuration = 100, - LayoutEasing = Easing.OutQuint, Padding = new MarginPadding { Vertical = 15, @@ -354,7 +353,7 @@ namespace osu.Game.Overlays.Mods { base.PopOut(); - footerContainer.MoveToX(footerContainer.DrawSize.X, WaveContainer.DISAPPEAR_DURATION, Easing.InSine); + footerContainer.MoveToX(content_width, WaveContainer.DISAPPEAR_DURATION, Easing.InSine); footerContainer.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.InSine); foreach (var section in ModSectionsContainer.Children) From 366f074f86eab5f0a665fe70843720b03fc9fb5b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 Jan 2021 16:53:38 +0900 Subject: [PATCH 079/121] Better describe test steps to discern on failures --- .../NonVisual/OngoingOperationTrackerTest.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/NonVisual/OngoingOperationTrackerTest.cs b/osu.Game.Tests/NonVisual/OngoingOperationTrackerTest.cs index b2be83d1f9..eef9582af9 100644 --- a/osu.Game.Tests/NonVisual/OngoingOperationTrackerTest.cs +++ b/osu.Game.Tests/NonVisual/OngoingOperationTrackerTest.cs @@ -30,22 +30,22 @@ namespace osu.Game.Tests.NonVisual IDisposable secondOperation = null; AddStep("begin first operation", () => firstOperation = tracker.BeginOperation()); - AddAssert("operation in progress", () => operationInProgress.Value); + AddAssert("first operation in progress", () => operationInProgress.Value); AddStep("cannot start another operation", () => Assert.Throws(() => tracker.BeginOperation())); AddStep("end first operation", () => firstOperation.Dispose()); - AddAssert("operation is ended", () => !operationInProgress.Value); + AddAssert("first operation is ended", () => !operationInProgress.Value); AddStep("start second operation", () => secondOperation = tracker.BeginOperation()); - AddAssert("operation in progress", () => operationInProgress.Value); + AddAssert("second operation in progress", () => operationInProgress.Value); AddStep("dispose first operation again", () => firstOperation.Dispose()); - AddAssert("operation in progress", () => operationInProgress.Value); + AddAssert("second operation still in progress", () => operationInProgress.Value); AddStep("dispose second operation", () => secondOperation.Dispose()); - AddAssert("operation is ended", () => !operationInProgress.Value); + AddAssert("second operation is ended", () => !operationInProgress.Value); } [Test] From 10e8b7082e5affb83670333a6115af16c34ee9c8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 Jan 2021 16:53:58 +0900 Subject: [PATCH 080/121] Rework logic to avoid custom disposal early return handling --- .../OnlinePlay/OngoingOperationTracker.cs | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs b/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs index 060f1d7b91..b7ee84eb9e 100644 --- a/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs +++ b/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs @@ -2,7 +2,6 @@ // 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; @@ -43,42 +42,45 @@ namespace osu.Game.Screens.OnlinePlay leasedInProgress = inProgress.BeginLease(true); leasedInProgress.Value = true; - // for extra safety, marshal the end of operation back to the update thread if necessary. - return new OngoingOperation(() => Scheduler.Add(endOperation, false)); + return new OngoingOperation(this, leasedInProgress); } - private void endOperation() + private void endOperationWithKnownLease(LeasedBindable lease) { - leasedInProgress?.Return(); - leasedInProgress = null; + if (lease != leasedInProgress) + return; + + // for extra safety, marshal the end of operation back to the update thread if necessary. + Scheduler.Add(() => + { + leasedInProgress?.Return(); + leasedInProgress = null; + }, false); } protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); + // base call does an UnbindAllBindables(). // clean up the leased reference here so that it doesn't get returned twice. leasedInProgress = null; } - private class OngoingOperation : InvokeOnDisposal + private class OngoingOperation : IDisposable { - private bool isDisposed; + private readonly OngoingOperationTracker tracker; + private readonly LeasedBindable lease; - public OngoingOperation(Action action) - : base(action) + public OngoingOperation(OngoingOperationTracker tracker, LeasedBindable lease) { + this.tracker = tracker; + this.lease = lease; } - public override void Dispose() + public void Dispose() { - // base class does not check disposal state for performance reasons which aren't relevant here. - // track locally, to avoid interfering with other operations in case of a potential double-disposal. - if (isDisposed) - return; - - base.Dispose(); - isDisposed = true; + tracker.endOperationWithKnownLease(lease); } } } From 91ce3df3a944244d73e18630f3dd26e3709df282 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 Jan 2021 17:44:01 +0900 Subject: [PATCH 081/121] Bind MultiplayerGameplayLeaderboard to player updates later in load process --- osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index d4ce542a67..a3d27c4e71 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -53,8 +53,6 @@ namespace osu.Game.Screens.Play.HUD [BackgroundDependencyLoader] private void load(OsuConfigManager config, IAPIProvider api) { - streamingClient.OnNewFrames += handleIncomingFrames; - foreach (var userId in playingUsers) { streamingClient.WatchUser(userId); @@ -90,6 +88,9 @@ namespace osu.Game.Screens.Play.HUD playingUsers.BindTo(multiplayerClient.CurrentMatchPlayingUserIds); playingUsers.BindCollectionChanged(usersChanged); + + // this leaderboard should be guaranteed to be completely loaded before the gameplay starts (is a prerequisite in MultiplayerPlayer). + streamingClient.OnNewFrames += handleIncomingFrames; } private void usersChanged(object sender, NotifyCollectionChangedEventArgs e) From 4ac362ee1acc899e1593ef548b981962e00916ec Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 Jan 2021 18:29:00 +0900 Subject: [PATCH 082/121] Move cloning local to editor --- osu.Game/Beatmaps/WorkingBeatmap.cs | 2 -- osu.Game/Screens/Edit/Editor.cs | 4 ++++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index d25adca92b..30382c444f 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -111,8 +111,6 @@ namespace osu.Game.Beatmaps // Convert IBeatmap converted = converter.Convert(cancellationSource.Token); - converted.ControlPointInfo = converted.ControlPointInfo.CreateCopy(); - // Apply conversion mods to the result foreach (var mod in mods.OfType()) { diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index b7ebf0c0a4..0e04d1ea12 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -131,6 +131,10 @@ namespace osu.Game.Screens.Edit try { playableBeatmap = Beatmap.Value.GetPlayableBeatmap(Beatmap.Value.BeatmapInfo.Ruleset); + + // clone these locally for now to avoid incurring overhead on GetPlayableBeatmap usages. + // eventually we will want to improve how/where this is done as there are issues with *not* cloning it in all cases. + playableBeatmap.ControlPointInfo = playableBeatmap.ControlPointInfo.CreateCopy(); } catch (Exception e) { From b489e92c9e1042e0d9254158f7da75a339142436 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 Jan 2021 18:43:36 +0900 Subject: [PATCH 083/121] Fix TimelineParts not using correct beatmap --- .../Timelines/Summary/Parts/BookmarkPart.cs | 3 +-- .../Timelines/Summary/Parts/BreakPart.cs | 5 ++--- .../Summary/Parts/ControlPointPart.cs | 5 ++--- .../Timelines/Summary/Parts/MarkerPart.cs | 8 ++------ .../Timelines/Summary/Parts/TimelinePart.cs | 18 +++++++++++------- .../Timeline/TimelineControlPointDisplay.cs | 5 ++--- 6 files changed, 20 insertions(+), 24 deletions(-) diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/BookmarkPart.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/BookmarkPart.cs index 103e39e78a..8298cf4773 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/BookmarkPart.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/BookmarkPart.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations; @@ -13,7 +12,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts ///
public class BookmarkPart : TimelinePart { - protected override void LoadBeatmap(WorkingBeatmap beatmap) + protected override void LoadBeatmap(EditorBeatmap beatmap) { base.LoadBeatmap(beatmap); foreach (int bookmark in beatmap.BeatmapInfo.Bookmarks) diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/BreakPart.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/BreakPart.cs index ceccbffc9c..e8a4b5c8c7 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/BreakPart.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/BreakPart.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Game.Beatmaps; using osu.Game.Beatmaps.Timing; using osu.Game.Graphics; using osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations; @@ -14,10 +13,10 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts ///
public class BreakPart : TimelinePart { - protected override void LoadBeatmap(WorkingBeatmap beatmap) + protected override void LoadBeatmap(EditorBeatmap beatmap) { base.LoadBeatmap(beatmap); - foreach (var breakPeriod in beatmap.Beatmap.Breaks) + foreach (var breakPeriod in beatmap.Breaks) Add(new BreakVisualisation(breakPeriod)); } diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/ControlPointPart.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/ControlPointPart.cs index e76ab71e54..70afc1e308 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/ControlPointPart.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/ControlPointPart.cs @@ -4,7 +4,6 @@ using System.Collections.Specialized; using System.Linq; using osu.Framework.Bindables; -using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts @@ -16,12 +15,12 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts { private readonly IBindableList controlPointGroups = new BindableList(); - protected override void LoadBeatmap(WorkingBeatmap beatmap) + protected override void LoadBeatmap(EditorBeatmap beatmap) { base.LoadBeatmap(beatmap); controlPointGroups.UnbindAll(); - controlPointGroups.BindTo(beatmap.Beatmap.ControlPointInfo.Groups); + controlPointGroups.BindTo(beatmap.ControlPointInfo.Groups); controlPointGroups.BindCollectionChanged((sender, args) => { switch (args.Action) diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs index 5a2214509c..d551333616 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs @@ -2,15 +2,14 @@ // See the LICENCE file in the repository root for full licence text. using System; -using osuTK; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Framework.Threading; -using osu.Game.Beatmaps; using osu.Game.Graphics; +using osuTK; namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts { @@ -54,9 +53,6 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts scheduledSeek?.Cancel(); scheduledSeek = Schedule(() => { - if (Beatmap.Value == null) - return; - float markerPos = Math.Clamp(ToLocalSpace(screenPosition).X, 0, DrawWidth); editorClock.SeekSmoothlyTo(markerPos / DrawWidth * editorClock.TrackLength); }); @@ -68,7 +64,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts marker.X = (float)editorClock.CurrentTime; } - protected override void LoadBeatmap(WorkingBeatmap beatmap) + protected override void LoadBeatmap(EditorBeatmap beatmap) { // block base call so we don't clear our marker (can be reused on beatmap change). } diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs index 5b8f7c747b..5aba81aa7d 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs @@ -21,7 +21,10 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts /// public class TimelinePart : Container where T : Drawable { - protected readonly IBindable Beatmap = new Bindable(); + private readonly IBindable beatmap = new Bindable(); + + [Resolved] + protected EditorBeatmap EditorBeatmap { get; private set; } protected readonly IBindable Track = new Bindable(); @@ -33,10 +36,9 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts { AddInternal(this.content = content ?? new Container { RelativeSizeAxes = Axes.Both }); - Beatmap.ValueChanged += b => + beatmap.ValueChanged += b => { updateRelativeChildSize(); - LoadBeatmap(b.NewValue); }; Track.ValueChanged += _ => updateRelativeChildSize(); @@ -45,24 +47,26 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts [BackgroundDependencyLoader] private void load(IBindable beatmap, EditorClock clock) { - Beatmap.BindTo(beatmap); + this.beatmap.BindTo(beatmap); + LoadBeatmap(EditorBeatmap); + Track.BindTo(clock.Track); } private void updateRelativeChildSize() { // the track may not be loaded completely (only has a length once it is). - if (!Beatmap.Value.Track.IsLoaded) + if (!beatmap.Value.Track.IsLoaded) { content.RelativeChildSize = Vector2.One; Schedule(updateRelativeChildSize); return; } - content.RelativeChildSize = new Vector2((float)Math.Max(1, Beatmap.Value.Track.Length), 1); + content.RelativeChildSize = new Vector2((float)Math.Max(1, beatmap.Value.Track.Length), 1); } - protected virtual void LoadBeatmap(WorkingBeatmap beatmap) + protected virtual void LoadBeatmap(EditorBeatmap beatmap) { content.Clear(); } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineControlPointDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineControlPointDisplay.cs index 13191df13c..18600bcdee 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineControlPointDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineControlPointDisplay.cs @@ -5,7 +5,6 @@ using System.Collections.Specialized; using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts; @@ -23,12 +22,12 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline RelativeSizeAxes = Axes.Both; } - protected override void LoadBeatmap(WorkingBeatmap beatmap) + protected override void LoadBeatmap(EditorBeatmap beatmap) { base.LoadBeatmap(beatmap); controlPointGroups.UnbindAll(); - controlPointGroups.BindTo(beatmap.Beatmap.ControlPointInfo.Groups); + controlPointGroups.BindTo(beatmap.ControlPointInfo.Groups); controlPointGroups.BindCollectionChanged((sender, args) => { switch (args.Action) From f3061a8e837e4a59522d9a4faeac1b0e9f98aa00 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 Jan 2021 18:47:41 +0900 Subject: [PATCH 084/121] Update squirrel to fix incorrect desktop icon creation on install --- osu.Desktop/osu.Desktop.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 4554f8b83a..e201b250d4 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -25,7 +25,7 @@ - + From 439f03e3b3d78dbdbc8ba3d6db559c13043f1e86 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 Jan 2021 19:25:38 +0900 Subject: [PATCH 085/121] Fix failing test due to missing dependency --- .../Visual/Editing/TestSceneEditorSummaryTimeline.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorSummaryTimeline.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorSummaryTimeline.cs index 3adc1bd425..94a9fd7b35 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorSummaryTimeline.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorSummaryTimeline.cs @@ -5,6 +5,8 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Beatmaps; +using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Components.Timelines.Summary; using osuTK; @@ -13,6 +15,9 @@ namespace osu.Game.Tests.Visual.Editing [TestFixture] public class TestSceneEditorSummaryTimeline : EditorClockTestScene { + [Cached(typeof(EditorBeatmap))] + private readonly EditorBeatmap editorBeatmap = new EditorBeatmap(new OsuBeatmap()); + [BackgroundDependencyLoader] private void load() { From f89eb7d75db7982d3cdc6200b4737c630094eb87 Mon Sep 17 00:00:00 2001 From: Shivam Date: Mon, 25 Jan 2021 13:22:37 +0100 Subject: [PATCH 086/121] Split and rename TournamentModDisplay component --- .../Components/TournamentBeatmapPanel.cs | 64 +++---------------- .../Components/TournamentModDisplay.cs | 56 ++++++++++++++++ 2 files changed, 64 insertions(+), 56 deletions(-) create mode 100644 osu.Game.Tournament/Components/TournamentModDisplay.cs diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index 92ac123097..2ed99d2fb5 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -9,14 +9,11 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; -using osu.Game.Rulesets; -using osu.Game.Screens.Play.HUD; using osu.Game.Tournament.Models; using osuTK.Graphics; @@ -25,7 +22,7 @@ namespace osu.Game.Tournament.Components public class TournamentBeatmapPanel : CompositeDrawable { public readonly BeatmapInfo Beatmap; - private readonly string mods; + private readonly string mod; private const float horizontal_padding = 10; private const float vertical_padding = 10; @@ -40,7 +37,7 @@ namespace osu.Game.Tournament.Components if (beatmap == null) throw new ArgumentNullException(nameof(beatmap)); Beatmap = beatmap; - this.mods = mods; + this.mod = mods; Width = 400; Height = HEIGHT; } @@ -124,13 +121,16 @@ namespace osu.Game.Tournament.Components }, }); - if (!string.IsNullOrEmpty(mods)) + if (!string.IsNullOrEmpty(mod)) { - AddInternal(new ModSprite + AddInternal(new TournamentModDisplay { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, - Mod = mods + Margin = new MarginPadding(10), + Width = 60, + RelativeSizeAxes = Axes.Y, + ModAcronym = mod }); } } @@ -184,53 +184,5 @@ namespace osu.Game.Tournament.Components Alpha = 1; } } - - private class ModSprite : Container - { - public string Mod; - - [Resolved] - private LadderInfo ladderInfo { get; set; } - - [Resolved] - private RulesetStore rulesets { get; set; } - - public ModSprite() - { - Margin = new MarginPadding(10); - Width = 60; - RelativeSizeAxes = Axes.Y; - } - - [BackgroundDependencyLoader] - private void load(TextureStore textures) - { - var texture = textures.Get($"mods/{Mod}"); - - if (texture != null) - { - Child = new Sprite - { - FillMode = FillMode.Fit, - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Texture = texture - }; - } - else - { - Child = new ModDisplay - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Current = - { - Value = rulesets.GetRuleset(ladderInfo.Ruleset.Value.ID ?? 0).CreateInstance().GetAllMods().Where(mod => mod.Acronym == Mod).ToArray() - } - }; - } - } - } } } diff --git a/osu.Game.Tournament/Components/TournamentModDisplay.cs b/osu.Game.Tournament/Components/TournamentModDisplay.cs new file mode 100644 index 0000000000..a22969c20d --- /dev/null +++ b/osu.Game.Tournament/Components/TournamentModDisplay.cs @@ -0,0 +1,56 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Game.Rulesets; +using osu.Game.Rulesets.UI; +using osu.Game.Tournament.Models; +using osuTK; + +namespace osu.Game.Tournament.Components +{ + public class TournamentModDisplay : CompositeDrawable + { + public string ModAcronym; + + [Resolved] + private LadderInfo ladderInfo { get; set; } + + [Resolved] + private RulesetStore rulesets { get; set; } + + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + var texture = textures.Get($"mods/{ModAcronym}"); + + if (texture != null) + { + AddInternal(new Sprite + { + FillMode = FillMode.Fit, + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Texture = texture + }); + } + else + { + var mod = rulesets.GetRuleset(ladderInfo.Ruleset.Value.ID ?? 0).CreateInstance().GetAllMods().FirstOrDefault(mod => mod.Acronym == ModAcronym); + + AddInternal(new ModIcon(mod) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(0.5f) + }); + } + } + } +} From 74310da7cf550af33367a4dd0e3d49eebacf32a5 Mon Sep 17 00:00:00 2001 From: Shivam Date: Mon, 25 Jan 2021 13:24:43 +0100 Subject: [PATCH 087/121] Change parameter to be singular mod instead of plural --- osu.Game.Tournament/Components/TournamentBeatmapPanel.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index 2ed99d2fb5..e02709a045 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -32,12 +32,12 @@ namespace osu.Game.Tournament.Components private readonly Bindable currentMatch = new Bindable(); private Box flash; - public TournamentBeatmapPanel(BeatmapInfo beatmap, string mods = null) + public TournamentBeatmapPanel(BeatmapInfo beatmap, string mod = null) { if (beatmap == null) throw new ArgumentNullException(nameof(beatmap)); Beatmap = beatmap; - this.mod = mods; + this.mod = mod; Width = 400; Height = HEIGHT; } From ca08a19c409e4a454a0d6a66d7ce57d2ea845228 Mon Sep 17 00:00:00 2001 From: Shivam Date: Mon, 25 Jan 2021 13:28:46 +0100 Subject: [PATCH 088/121] Rename mod to modIcon --- osu.Game.Tournament/Components/TournamentModDisplay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament/Components/TournamentModDisplay.cs b/osu.Game.Tournament/Components/TournamentModDisplay.cs index a22969c20d..827b3d6a69 100644 --- a/osu.Game.Tournament/Components/TournamentModDisplay.cs +++ b/osu.Game.Tournament/Components/TournamentModDisplay.cs @@ -42,9 +42,9 @@ namespace osu.Game.Tournament.Components } else { - var mod = rulesets.GetRuleset(ladderInfo.Ruleset.Value.ID ?? 0).CreateInstance().GetAllMods().FirstOrDefault(mod => mod.Acronym == ModAcronym); + var modIcon = rulesets.GetRuleset(ladderInfo.Ruleset.Value.ID ?? 0).CreateInstance().GetAllMods().FirstOrDefault(mod => mod.Acronym == ModAcronym); - AddInternal(new ModIcon(mod) + AddInternal(new ModIcon(modIcon) { Anchor = Anchor.Centre, Origin = Anchor.Centre, From 07bd9013585ebae924cf44c501b079bb9d9b01b0 Mon Sep 17 00:00:00 2001 From: Shivam Date: Mon, 25 Jan 2021 14:20:58 +0100 Subject: [PATCH 089/121] Add visual test for Tournament Mod Display --- .../TestSceneTournamentModDisplay.cs | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.cs diff --git a/osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.cs b/osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.cs new file mode 100644 index 0000000000..9689ecd4ae --- /dev/null +++ b/osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.cs @@ -0,0 +1,66 @@ +// 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.Game.Beatmaps; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Rulesets; +using osu.Game.Tournament.Components; + +namespace osu.Game.Tournament.Tests.Components +{ + public class TestSceneTournamentModDisplay : TournamentTestScene + { + [Resolved] + private IAPIProvider api { get; set; } + + [Resolved] + private RulesetStore rulesets { get; set; } + + private FillFlowContainer fillFlow; + + private BeatmapInfo beatmap; + + [BackgroundDependencyLoader] + private void load() + { + var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = 490154 }); + req.Success += success; + api.Queue(req); + + Add(fillFlow = new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Direction = FillDirection.Full, + Spacing = new osuTK.Vector2(10) + }); + } + + [Test] + public void TestModDisplay() + { + AddUntilStep("beatmap is available", () => beatmap != null); + AddStep("add maps with available mods for ruleset", () => displayForRuleset(Ladder.Ruleset.Value.ID ?? 0)); + } + + private void displayForRuleset(int rulesetId) + { + fillFlow.Clear(); + var mods = rulesets.GetRuleset(rulesetId).CreateInstance().GetAllMods(); + + foreach (var mod in mods) + { + fillFlow.Add(new TournamentBeatmapPanel(beatmap, mod.Acronym)); + } + } + + private void success(APIBeatmap apiBeatmap) => beatmap = apiBeatmap.ToBeatmap(rulesets); + } +} From 5e0ccb6c91d3e1d55968882b80d3ba0eca1971a0 Mon Sep 17 00:00:00 2001 From: Shivam Date: Mon, 25 Jan 2021 14:21:22 +0100 Subject: [PATCH 090/121] Remove unncessary test step --- .../TestSceneTournamentModDisplay.cs | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.cs b/osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.cs index 9689ecd4ae..b4d9fa4222 100644 --- a/osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.cs +++ b/osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.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; @@ -43,24 +42,19 @@ namespace osu.Game.Tournament.Tests.Components }); } - [Test] - public void TestModDisplay() + private void success(APIBeatmap apiBeatmap) { - AddUntilStep("beatmap is available", () => beatmap != null); - AddStep("add maps with available mods for ruleset", () => displayForRuleset(Ladder.Ruleset.Value.ID ?? 0)); - } - - private void displayForRuleset(int rulesetId) - { - fillFlow.Clear(); - var mods = rulesets.GetRuleset(rulesetId).CreateInstance().GetAllMods(); + beatmap = apiBeatmap.ToBeatmap(rulesets); + var mods = rulesets.GetRuleset(Ladder.Ruleset.Value.ID ?? 0).CreateInstance().GetAllMods(); foreach (var mod in mods) { - fillFlow.Add(new TournamentBeatmapPanel(beatmap, mod.Acronym)); + fillFlow.Add(new TournamentBeatmapPanel(beatmap, mod.Acronym) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre + }); } } - - private void success(APIBeatmap apiBeatmap) => beatmap = apiBeatmap.ToBeatmap(rulesets); } } From 6a85f5ca8bf2a9506ed55d9a60f870b820326e2b Mon Sep 17 00:00:00 2001 From: Shivam Date: Mon, 25 Jan 2021 14:21:53 +0100 Subject: [PATCH 091/121] Add null checks to prevent nullrefexception in automated test --- osu.Game.Tournament/Components/TournamentModDisplay.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Components/TournamentModDisplay.cs b/osu.Game.Tournament/Components/TournamentModDisplay.cs index 827b3d6a69..e91c27345e 100644 --- a/osu.Game.Tournament/Components/TournamentModDisplay.cs +++ b/osu.Game.Tournament/Components/TournamentModDisplay.cs @@ -42,7 +42,15 @@ namespace osu.Game.Tournament.Components } else { - var modIcon = rulesets.GetRuleset(ladderInfo.Ruleset.Value.ID ?? 0).CreateInstance().GetAllMods().FirstOrDefault(mod => mod.Acronym == ModAcronym); + var ruleset = rulesets.AvailableRulesets.FirstOrDefault(r => r == ladderInfo.Ruleset.Value); + + if (ruleset == null) + return; + + var modIcon = ruleset.CreateInstance().GetAllMods().FirstOrDefault(mod => mod.Acronym == ModAcronym); + + if (modIcon == null) + return; AddInternal(new ModIcon(modIcon) { From a741d91aed91f338ffecd3a4596b551c8804b52e Mon Sep 17 00:00:00 2001 From: Shivam Date: Mon, 25 Jan 2021 14:57:35 +0100 Subject: [PATCH 092/121] use null propragtor for Ruleset.Value and rulset instead of null checks --- .../Components/TournamentModDisplay.cs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tournament/Components/TournamentModDisplay.cs b/osu.Game.Tournament/Components/TournamentModDisplay.cs index e91c27345e..369a58858e 100644 --- a/osu.Game.Tournament/Components/TournamentModDisplay.cs +++ b/osu.Game.Tournament/Components/TournamentModDisplay.cs @@ -18,14 +18,11 @@ namespace osu.Game.Tournament.Components { public string ModAcronym; - [Resolved] - private LadderInfo ladderInfo { get; set; } - [Resolved] private RulesetStore rulesets { get; set; } [BackgroundDependencyLoader] - private void load(TextureStore textures) + private void load(TextureStore textures, LadderInfo ladderInfo) { var texture = textures.Get($"mods/{ModAcronym}"); @@ -42,12 +39,8 @@ namespace osu.Game.Tournament.Components } else { - var ruleset = rulesets.AvailableRulesets.FirstOrDefault(r => r == ladderInfo.Ruleset.Value); - - if (ruleset == null) - return; - - var modIcon = ruleset.CreateInstance().GetAllMods().FirstOrDefault(mod => mod.Acronym == ModAcronym); + var ruleset = rulesets.GetRuleset(ladderInfo.Ruleset.Value?.ID ?? 0); + var modIcon = ruleset?.CreateInstance().GetAllMods().FirstOrDefault(mod => mod.Acronym == ModAcronym); if (modIcon == null) return; From b036f0165a077bc49cb0fd9d7e8713fb6a18fd89 Mon Sep 17 00:00:00 2001 From: Shivam Date: Mon, 25 Jan 2021 15:47:31 +0100 Subject: [PATCH 093/121] move value set to constructor and make private readonly --- .../Components/TournamentBeatmapPanel.cs | 3 +-- .../Components/TournamentModDisplay.cs | 11 ++++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index e02709a045..8cc4566c08 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -123,14 +123,13 @@ namespace osu.Game.Tournament.Components if (!string.IsNullOrEmpty(mod)) { - AddInternal(new TournamentModDisplay + AddInternal(new TournamentModDisplay(mod) { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, Margin = new MarginPadding(10), Width = 60, RelativeSizeAxes = Axes.Y, - ModAcronym = mod }); } } diff --git a/osu.Game.Tournament/Components/TournamentModDisplay.cs b/osu.Game.Tournament/Components/TournamentModDisplay.cs index 369a58858e..3df8550667 100644 --- a/osu.Game.Tournament/Components/TournamentModDisplay.cs +++ b/osu.Game.Tournament/Components/TournamentModDisplay.cs @@ -16,15 +16,20 @@ namespace osu.Game.Tournament.Components { public class TournamentModDisplay : CompositeDrawable { - public string ModAcronym; + private readonly string modAcronym; [Resolved] private RulesetStore rulesets { get; set; } + public TournamentModDisplay(string mod) + { + modAcronym = mod; + } + [BackgroundDependencyLoader] private void load(TextureStore textures, LadderInfo ladderInfo) { - var texture = textures.Get($"mods/{ModAcronym}"); + var texture = textures.Get($"mods/{modAcronym}"); if (texture != null) { @@ -40,7 +45,7 @@ namespace osu.Game.Tournament.Components else { var ruleset = rulesets.GetRuleset(ladderInfo.Ruleset.Value?.ID ?? 0); - var modIcon = ruleset?.CreateInstance().GetAllMods().FirstOrDefault(mod => mod.Acronym == ModAcronym); + var modIcon = ruleset?.CreateInstance().GetAllMods().FirstOrDefault(mod => mod.Acronym == modAcronym); if (modIcon == null) return; From 9efce5717f40e2ade16c4dee2cb89501d6c27c0d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 25 Jan 2021 22:11:50 +0300 Subject: [PATCH 094/121] Fix beatmap listing placeholder disappearing on second time display --- osu.Game/Overlays/BeatmapListingOverlay.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index 2f7f21e403..b65eaad0a2 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -176,6 +176,9 @@ namespace osu.Game.Overlays loadingLayer.Hide(); lastFetchDisplayedTime = Time.Current; + if (content == currentContent) + return; + var lastContent = currentContent; if (lastContent != null) From 9312de7c2387f4534090304550f3910c54b98e39 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 25 Jan 2021 23:40:26 +0300 Subject: [PATCH 095/121] Move online beatmap listing overlay to separate test scene --- ...ingOverlay.cs => TestSceneOnlineBeatmapListingOverlay.cs} | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) rename osu.Game.Tests/Visual/Online/{TestSceneBeatmapListingOverlay.cs => TestSceneOnlineBeatmapListingOverlay.cs} (80%) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneOnlineBeatmapListingOverlay.cs similarity index 80% rename from osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs rename to osu.Game.Tests/Visual/Online/TestSceneOnlineBeatmapListingOverlay.cs index 6cb1687d1f..fe1701a554 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneOnlineBeatmapListingOverlay.cs @@ -6,13 +6,14 @@ using NUnit.Framework; namespace osu.Game.Tests.Visual.Online { - public class TestSceneBeatmapListingOverlay : OsuTestScene + [Description("uses online API")] + public class TestSceneOnlineBeatmapListingOverlay : OsuTestScene { protected override bool UseOnlineAPI => true; private readonly BeatmapListingOverlay overlay; - public TestSceneBeatmapListingOverlay() + public TestSceneOnlineBeatmapListingOverlay() { Add(overlay = new BeatmapListingOverlay()); } From 75d6dbdbb7290c215505e408f15781b96128d929 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 25 Jan 2021 22:18:23 +0300 Subject: [PATCH 096/121] Fix beatmap listing placeholder potentially getting disposed --- osu.Game/Overlays/BeatmapListingOverlay.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index 0c9c995dd6..2f7f21e403 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -180,18 +180,24 @@ namespace osu.Game.Overlays if (lastContent != null) { - lastContent.FadeOut(100, Easing.OutQuint).Expire(); + lastContent.FadeOut(100, Easing.OutQuint); // Consider the case when the new content is smaller than the last content. // If the auto-size computation is delayed until fade out completes, the background remain high for too long making the resulting transition to the smaller height look weird. // At the same time, if the last content's height is bypassed immediately, there is a period where the new content is at Alpha = 0 when the auto-sized height will be 0. // To resolve both of these issues, the bypass is delayed until a point when the content transitions (fade-in and fade-out) overlap and it looks good to do so. - lastContent.Delay(25).Schedule(() => lastContent.BypassAutoSizeAxes = Axes.Y).Then().Schedule(() => panelTarget.Remove(lastContent)); + lastContent.Delay(25).Schedule(() => lastContent.BypassAutoSizeAxes = Axes.Y).Then().Schedule(() => + { + panelTarget.Remove(lastContent); + + // the content may be reused again (e.g. notFoundContent), clear Y-axis bypass for displaying back properly. + lastContent.BypassAutoSizeAxes = Axes.None; + }); } if (!content.IsAlive) panelTarget.Add(content); - content.FadeIn(200, Easing.OutQuint); + content.FadeInFromZero(200, Easing.OutQuint); currentContent = content; } From c317d6016967a321c70d02ee81d012129e8b5767 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 25 Jan 2021 23:41:05 +0300 Subject: [PATCH 097/121] Add offline test scene for beatmap listing overlay --- .../Online/TestSceneBeatmapListingOverlay.cs | 81 +++++++++++++++++++ .../API/Requests/Responses/APIBeatmapSet.cs | 2 +- osu.Game/Overlays/BeatmapListingOverlay.cs | 2 +- 3 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs new file mode 100644 index 0000000000..1349264bf9 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs @@ -0,0 +1,81 @@ +// 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.Graphics.Containers; +using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Overlays; +using osu.Game.Overlays.BeatmapListing; +using osu.Game.Rulesets; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneBeatmapListingOverlay : OsuTestScene + { + private readonly List setsForResponse = new List(); + + private BeatmapListingOverlay overlay; + + [BackgroundDependencyLoader] + private void load() + { + Child = overlay = new BeatmapListingOverlay { State = { Value = Visibility.Visible } }; + + ((DummyAPIAccess)API).HandleRequest = req => + { + if (req is SearchBeatmapSetsRequest searchBeatmapSetsRequest) + { + searchBeatmapSetsRequest.TriggerSuccess(new SearchBeatmapSetsResponse + { + BeatmapSets = setsForResponse, + }); + } + }; + } + + [Test] + public void TestNoBeatmapsPlaceholder() + { + AddStep("fetch for 0 beatmaps", () => fetchFor()); + AddUntilStep("placeholder shown", () => overlay.ChildrenOfType().SingleOrDefault()?.IsPresent == true); + + AddStep("fetch for 1 beatmap", () => fetchFor(CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet)); + AddUntilStep("placeholder hidden", () => !overlay.ChildrenOfType().Any()); + + AddStep("fetch for 0 beatmaps", () => fetchFor()); + AddUntilStep("placeholder shown", () => overlay.ChildrenOfType().SingleOrDefault()?.IsPresent == true); + + // fetch once more to ensure nothing happens in displaying placeholder again when it already is present. + AddStep("fetch for 0 beatmaps again", () => fetchFor()); + AddUntilStep("placeholder shown", () => overlay.ChildrenOfType().SingleOrDefault()?.IsPresent == true); + } + + private void fetchFor(params BeatmapSetInfo[] beatmaps) + { + setsForResponse.Clear(); + setsForResponse.AddRange(beatmaps.Select(b => new TestAPIBeatmapSet(b))); + + // trigger arbitrary change for fetching. + overlay.ChildrenOfType().Single().Query.TriggerChange(); + } + + private class TestAPIBeatmapSet : APIBeatmapSet + { + private readonly BeatmapSetInfo beatmapSet; + + public TestAPIBeatmapSet(BeatmapSetInfo beatmapSet) + { + this.beatmapSet = beatmapSet; + } + + public override BeatmapSetInfo ToBeatmapSet(RulesetStore rulesets) => beatmapSet; + } + } +} diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs index bd1800e9f7..45d9c9405f 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs @@ -81,7 +81,7 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"beatmaps")] private IEnumerable beatmaps { get; set; } - public BeatmapSetInfo ToBeatmapSet(RulesetStore rulesets) + public virtual BeatmapSetInfo ToBeatmapSet(RulesetStore rulesets) { var beatmapSet = new BeatmapSetInfo { diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index b65eaad0a2..c5cc0a9c85 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -211,7 +211,7 @@ namespace osu.Game.Overlays base.Dispose(isDisposing); } - private class NotFoundDrawable : CompositeDrawable + public class NotFoundDrawable : CompositeDrawable { public NotFoundDrawable() { From 0d8d0d685219e162e385f0bb506735d7fedcb2e0 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 26 Jan 2021 01:03:29 +0300 Subject: [PATCH 098/121] Apply alternative way of fixing --- osu.Game/Overlays/BeatmapListingOverlay.cs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index 2f7f21e403..fc90968ec4 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -178,21 +178,18 @@ namespace osu.Game.Overlays var lastContent = currentContent; - if (lastContent != null) + // "not found" placeholder is reused, only remove without disposing through expire. + if (lastContent == notFoundContent) + lastContent.FadeOut(100, Easing.OutQuint).Schedule(() => panelTarget.Remove(lastContent)); + else if (lastContent != null) { - lastContent.FadeOut(100, Easing.OutQuint); + lastContent.FadeOut(100, Easing.OutQuint).Expire(); // Consider the case when the new content is smaller than the last content. // If the auto-size computation is delayed until fade out completes, the background remain high for too long making the resulting transition to the smaller height look weird. // At the same time, if the last content's height is bypassed immediately, there is a period where the new content is at Alpha = 0 when the auto-sized height will be 0. // To resolve both of these issues, the bypass is delayed until a point when the content transitions (fade-in and fade-out) overlap and it looks good to do so. - lastContent.Delay(25).Schedule(() => lastContent.BypassAutoSizeAxes = Axes.Y).Then().Schedule(() => - { - panelTarget.Remove(lastContent); - - // the content may be reused again (e.g. notFoundContent), clear Y-axis bypass for displaying back properly. - lastContent.BypassAutoSizeAxes = Axes.None; - }); + lastContent.Delay(25).Schedule(() => lastContent.BypassAutoSizeAxes = Axes.Y).Then().Schedule(() => panelTarget.Remove(lastContent)); } if (!content.IsAlive) From ca0242debef0c77bde87d7f4fecdc84f3b65a8b5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Jan 2021 15:42:48 +0900 Subject: [PATCH 099/121] Tidy up logic to correctly expire in cases where expiry is expected --- osu.Game/Overlays/BeatmapListingOverlay.cs | 27 +++++++++++++--------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index fc90968ec4..de566c92cb 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -178,24 +178,29 @@ namespace osu.Game.Overlays var lastContent = currentContent; - // "not found" placeholder is reused, only remove without disposing through expire. - if (lastContent == notFoundContent) - lastContent.FadeOut(100, Easing.OutQuint).Schedule(() => panelTarget.Remove(lastContent)); - else if (lastContent != null) + if (lastContent != null) { - lastContent.FadeOut(100, Easing.OutQuint).Expire(); + var transform = lastContent.FadeOut(100, Easing.OutQuint); - // Consider the case when the new content is smaller than the last content. - // If the auto-size computation is delayed until fade out completes, the background remain high for too long making the resulting transition to the smaller height look weird. - // At the same time, if the last content's height is bypassed immediately, there is a period where the new content is at Alpha = 0 when the auto-sized height will be 0. - // To resolve both of these issues, the bypass is delayed until a point when the content transitions (fade-in and fade-out) overlap and it looks good to do so. - lastContent.Delay(25).Schedule(() => lastContent.BypassAutoSizeAxes = Axes.Y).Then().Schedule(() => panelTarget.Remove(lastContent)); + if (lastContent == notFoundContent) + { + // not found display may be used multiple times, so don't expire/dispose it. + transform.Schedule(() => panelTarget.Remove(lastContent)); + } + else + { + // Consider the case when the new content is smaller than the last content. + // If the auto-size computation is delayed until fade out completes, the background remain high for too long making the resulting transition to the smaller height look weird. + // At the same time, if the last content's height is bypassed immediately, there is a period where the new content is at Alpha = 0 when the auto-sized height will be 0. + // To resolve both of these issues, the bypass is delayed until a point when the content transitions (fade-in and fade-out) overlap and it looks good to do so. + lastContent.Delay(25).Schedule(() => lastContent.BypassAutoSizeAxes = Axes.Y).Then().Schedule(() => lastContent.Expire()); + } } if (!content.IsAlive) panelTarget.Add(content); - content.FadeInFromZero(200, Easing.OutQuint); + content.FadeInFromZero(200, Easing.OutQuint); currentContent = content; } From 60ae87ec383645e61194bf51f19e94d55a342023 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Jan 2021 16:25:49 +0900 Subject: [PATCH 100/121] Add MessagePack package --- 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 2b8f81532d..3e971d9d4f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -22,6 +22,7 @@ + From e4fc6041635c4aebfbb791c6e671324ad9156abf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Jan 2021 16:26:03 +0900 Subject: [PATCH 101/121] Setup all multiplayer model classes for MessagePack support --- osu.Game/Online/API/APIMod.cs | 7 ++++++- osu.Game/Online/Multiplayer/MultiplayerRoom.cs | 10 +++++++++- osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs | 7 +++++++ osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs | 8 +++++++- osu.Game/Online/Rooms/BeatmapAvailability.cs | 6 +++++- 5 files changed, 34 insertions(+), 4 deletions(-) diff --git a/osu.Game/Online/API/APIMod.cs b/osu.Game/Online/API/APIMod.cs index c8b76b9685..69ce3825ee 100644 --- a/osu.Game/Online/API/APIMod.cs +++ b/osu.Game/Online/API/APIMod.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using Humanizer; +using MessagePack; using Newtonsoft.Json; using osu.Framework.Bindables; using osu.Game.Configuration; @@ -13,16 +14,20 @@ using osu.Game.Rulesets.Mods; namespace osu.Game.Online.API { + [MessagePackObject] public class APIMod : IMod { [JsonProperty("acronym")] + [Key(0)] public string Acronym { get; set; } [JsonProperty("settings")] + [Key(1)] public Dictionary Settings { get; set; } = new Dictionary(); [JsonConstructor] - private APIMod() + [SerializationConstructor] + public APIMod() { } diff --git a/osu.Game/Online/Multiplayer/MultiplayerRoom.cs b/osu.Game/Online/Multiplayer/MultiplayerRoom.cs index 12fcf25ace..c5fa6253ed 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerRoom.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerRoom.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; +using MessagePack; using Newtonsoft.Json; namespace osu.Game.Online.Multiplayer @@ -13,35 +14,42 @@ namespace osu.Game.Online.Multiplayer /// A multiplayer room. /// [Serializable] + [MessagePackObject] public class MultiplayerRoom { /// /// The ID of the room, used for database persistence. /// + [Key(0)] public readonly long RoomID; /// /// The current state of the room (ie. whether it is in progress or otherwise). /// + [Key(1)] public MultiplayerRoomState State { get; set; } /// /// All currently enforced game settings for this room. /// + [Key(2)] public MultiplayerRoomSettings Settings { get; set; } = new MultiplayerRoomSettings(); /// /// All users currently in this room. /// + [Key(3)] public List Users { get; set; } = new List(); /// /// The host of this room, in control of changing room settings. /// + [Key(4)] public MultiplayerRoomUser? Host { get; set; } [JsonConstructor] - public MultiplayerRoom(in long roomId) + [SerializationConstructor] + public MultiplayerRoom(long roomId) { RoomID = roomId; } diff --git a/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs b/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs index 857b38ea60..0ead5db84c 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs @@ -7,22 +7,29 @@ using System; using System.Collections.Generic; using System.Linq; using JetBrains.Annotations; +using MessagePack; using osu.Game.Online.API; namespace osu.Game.Online.Multiplayer { [Serializable] + [MessagePackObject] public class MultiplayerRoomSettings : IEquatable { + [Key(0)] public int BeatmapID { get; set; } + [Key(1)] public int RulesetID { get; set; } + [Key(2)] public string BeatmapChecksum { get; set; } = string.Empty; + [Key(3)] public string Name { get; set; } = "Unnamed room"; [NotNull] + [Key(4)] public IEnumerable Mods { get; set; } = Enumerable.Empty(); public bool Equals(MultiplayerRoomSettings other) diff --git a/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs b/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs index 2590acbc81..b300be9f60 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs @@ -4,6 +4,7 @@ #nullable enable using System; +using MessagePack; using Newtonsoft.Json; using osu.Game.Online.Rooms; using osu.Game.Users; @@ -11,21 +12,26 @@ using osu.Game.Users; namespace osu.Game.Online.Multiplayer { [Serializable] + [MessagePackObject] public class MultiplayerRoomUser : IEquatable { + [Key(0)] public readonly int UserID; + [Key(1)] public MultiplayerUserState State { get; set; } = MultiplayerUserState.Idle; /// /// The availability state of the current beatmap. /// + [Key(2)] public BeatmapAvailability BeatmapAvailability { get; set; } = BeatmapAvailability.LocallyAvailable(); + [IgnoreMember] public User? User { get; set; } [JsonConstructor] - public MultiplayerRoomUser(in int userId) + public MultiplayerRoomUser(int userId) { UserID = userId; } diff --git a/osu.Game/Online/Rooms/BeatmapAvailability.cs b/osu.Game/Online/Rooms/BeatmapAvailability.cs index e7dbc5f436..38bd236718 100644 --- a/osu.Game/Online/Rooms/BeatmapAvailability.cs +++ b/osu.Game/Online/Rooms/BeatmapAvailability.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using MessagePack; using Newtonsoft.Json; namespace osu.Game.Online.Rooms @@ -9,20 +10,23 @@ namespace osu.Game.Online.Rooms /// /// The local availability information about a certain beatmap for the client. /// + [MessagePackObject] public class BeatmapAvailability : IEquatable { /// /// The beatmap's availability state. /// + [Key(0)] public readonly DownloadState State; /// /// The beatmap's downloading progress, null when not in state. /// + [Key(1)] public readonly double? DownloadProgress; [JsonConstructor] - private BeatmapAvailability(DownloadState state, double? downloadProgress = null) + public BeatmapAvailability(DownloadState state, double? downloadProgress = null) { State = state; DownloadProgress = downloadProgress; From 9537090d28bb29994e5a2902c4091e69bc15856b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Jan 2021 16:39:35 +0900 Subject: [PATCH 102/121] Setup all spectator model classes for MessagePack --- osu.Game/Online/Spectator/FrameDataBundle.cs | 4 ++++ osu.Game/Online/Spectator/FrameHeader.cs | 10 +++++++++- osu.Game/Online/Spectator/SpectatorState.cs | 5 +++++ osu.Game/Replays/Legacy/LegacyReplayFrame.cs | 13 +++++++++++++ osu.Game/Rulesets/Replays/ReplayFrame.cs | 4 ++++ 5 files changed, 35 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Spectator/FrameDataBundle.cs b/osu.Game/Online/Spectator/FrameDataBundle.cs index a8d0434324..0e59cdf4ce 100644 --- a/osu.Game/Online/Spectator/FrameDataBundle.cs +++ b/osu.Game/Online/Spectator/FrameDataBundle.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; +using MessagePack; using Newtonsoft.Json; using osu.Game.Replays.Legacy; using osu.Game.Scoring; @@ -12,10 +13,13 @@ using osu.Game.Scoring; namespace osu.Game.Online.Spectator { [Serializable] + [MessagePackObject] public class FrameDataBundle { + [Key(0)] public FrameHeader Header { get; set; } + [Key(1)] public IEnumerable Frames { get; set; } public FrameDataBundle(ScoreInfo score, IEnumerable frames) diff --git a/osu.Game/Online/Spectator/FrameHeader.cs b/osu.Game/Online/Spectator/FrameHeader.cs index 135b356eda..adfcbcd95a 100644 --- a/osu.Game/Online/Spectator/FrameHeader.cs +++ b/osu.Game/Online/Spectator/FrameHeader.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; +using MessagePack; using Newtonsoft.Json; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; @@ -12,31 +13,37 @@ using osu.Game.Scoring; namespace osu.Game.Online.Spectator { [Serializable] + [MessagePackObject] public class FrameHeader { /// /// The current accuracy of the score. /// + [Key(0)] public double Accuracy { get; set; } /// /// The current combo of the score. /// + [Key(1)] public int Combo { get; set; } /// /// The maximum combo achieved up to the current point in time. /// + [Key(2)] public int MaxCombo { get; set; } /// /// Cumulative hit statistics. /// + [Key(3)] public Dictionary Statistics { get; set; } /// /// The time at which this frame was received by the server. /// + [Key(4)] public DateTimeOffset ReceivedTime { get; set; } /// @@ -54,7 +61,8 @@ namespace osu.Game.Online.Spectator } [JsonConstructor] - public FrameHeader(int combo, int maxCombo, double accuracy, Dictionary statistics, DateTimeOffset receivedTime) + [SerializationConstructor] + public FrameHeader(double accuracy, int combo, int maxCombo, Dictionary statistics, DateTimeOffset receivedTime) { Combo = combo; MaxCombo = maxCombo; diff --git a/osu.Game/Online/Spectator/SpectatorState.cs b/osu.Game/Online/Spectator/SpectatorState.cs index 101ce3d5d5..96a875bc14 100644 --- a/osu.Game/Online/Spectator/SpectatorState.cs +++ b/osu.Game/Online/Spectator/SpectatorState.cs @@ -5,18 +5,23 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; +using MessagePack; using osu.Game.Online.API; namespace osu.Game.Online.Spectator { [Serializable] + [MessagePackObject] public class SpectatorState : IEquatable { + [Key(0)] public int? BeatmapID { get; set; } + [Key(1)] public int? RulesetID { get; set; } [NotNull] + [Key(2)] public IEnumerable Mods { get; set; } = Enumerable.Empty(); public bool Equals(SpectatorState other) => BeatmapID == other?.BeatmapID && Mods.SequenceEqual(other?.Mods) && RulesetID == other?.RulesetID; diff --git a/osu.Game/Replays/Legacy/LegacyReplayFrame.cs b/osu.Game/Replays/Legacy/LegacyReplayFrame.cs index 74bacae9e1..ab9ccda9b9 100644 --- a/osu.Game/Replays/Legacy/LegacyReplayFrame.cs +++ b/osu.Game/Replays/Legacy/LegacyReplayFrame.cs @@ -1,38 +1,51 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using MessagePack; using Newtonsoft.Json; using osu.Game.Rulesets.Replays; using osuTK; namespace osu.Game.Replays.Legacy { + [MessagePackObject] public class LegacyReplayFrame : ReplayFrame { [JsonIgnore] + [IgnoreMember] public Vector2 Position => new Vector2(MouseX ?? 0, MouseY ?? 0); + [Key(1)] public float? MouseX; + + [Key(2)] public float? MouseY; [JsonIgnore] + [IgnoreMember] public bool MouseLeft => MouseLeft1 || MouseLeft2; [JsonIgnore] + [IgnoreMember] public bool MouseRight => MouseRight1 || MouseRight2; [JsonIgnore] + [IgnoreMember] public bool MouseLeft1 => ButtonState.HasFlag(ReplayButtonState.Left1); [JsonIgnore] + [IgnoreMember] public bool MouseRight1 => ButtonState.HasFlag(ReplayButtonState.Right1); [JsonIgnore] + [IgnoreMember] public bool MouseLeft2 => ButtonState.HasFlag(ReplayButtonState.Left2); [JsonIgnore] + [IgnoreMember] public bool MouseRight2 => ButtonState.HasFlag(ReplayButtonState.Right2); + [Key(3)] public ReplayButtonState ButtonState; public LegacyReplayFrame(double time, float? mouseX, float? mouseY, ReplayButtonState buttonState) diff --git a/osu.Game/Rulesets/Replays/ReplayFrame.cs b/osu.Game/Rulesets/Replays/ReplayFrame.cs index 85e068ae79..7de53211a2 100644 --- a/osu.Game/Rulesets/Replays/ReplayFrame.cs +++ b/osu.Game/Rulesets/Replays/ReplayFrame.cs @@ -1,10 +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 MessagePack; + namespace osu.Game.Rulesets.Replays { + [MessagePackObject] public class ReplayFrame { + [Key(0)] public double Time; public ReplayFrame() From 20cfa991bffbe1cb4255b6cc45cda39b16cc28f9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Jan 2021 17:41:21 +0900 Subject: [PATCH 103/121] Switch clients to MessagePack mode --- osu.Game/Online/Multiplayer/MultiplayerClient.cs | 2 +- osu.Game/Online/Spectator/SpectatorStreamingClient.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 50dc8f661c..3221456e75 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -70,7 +70,7 @@ namespace osu.Game.Online.Multiplayer { options.Headers.Add("Authorization", $"Bearer {api.AccessToken}"); }) - .AddNewtonsoftJsonProtocol(options => { options.PayloadSerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; }) + .AddMessagePackProtocol() .Build(); // this is kind of SILLY diff --git a/osu.Game/Online/Spectator/SpectatorStreamingClient.cs b/osu.Game/Online/Spectator/SpectatorStreamingClient.cs index 344b73f3d9..cc866b7ad9 100644 --- a/osu.Game/Online/Spectator/SpectatorStreamingClient.cs +++ b/osu.Game/Online/Spectator/SpectatorStreamingClient.cs @@ -121,7 +121,7 @@ namespace osu.Game.Online.Spectator { options.Headers.Add("Authorization", $"Bearer {api.AccessToken}"); }) - .AddNewtonsoftJsonProtocol(options => { options.PayloadSerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; }) + .AddMessagePackProtocol() .Build(); // until strong typed client support is added, each method must be manually bound (see https://github.com/dotnet/aspnetcore/issues/15198) From 15885c17af58234c63ea154178f2156854e95bd5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Jan 2021 18:07:43 +0900 Subject: [PATCH 104/121] Remove unused usings --- osu.Game/Online/Multiplayer/MultiplayerClient.cs | 1 - osu.Game/Online/Spectator/SpectatorStreamingClient.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 3221456e75..0d779232d0 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -9,7 +9,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.SignalR.Client; using Microsoft.Extensions.DependencyInjection; -using Newtonsoft.Json; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Logging; diff --git a/osu.Game/Online/Spectator/SpectatorStreamingClient.cs b/osu.Game/Online/Spectator/SpectatorStreamingClient.cs index cc866b7ad9..dac2131035 100644 --- a/osu.Game/Online/Spectator/SpectatorStreamingClient.cs +++ b/osu.Game/Online/Spectator/SpectatorStreamingClient.cs @@ -9,7 +9,6 @@ using System.Threading.Tasks; using JetBrains.Annotations; using Microsoft.AspNetCore.SignalR.Client; using Microsoft.Extensions.DependencyInjection; -using Newtonsoft.Json; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; From a5f3418e561efcf05800d4d2d61f9182091467a9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Jan 2021 19:11:19 +0900 Subject: [PATCH 105/121] Avoid tooltip display --- .../Components/TournamentModDisplay.cs | 2 +- osu.Game/Overlays/Mods/ModButton.cs | 16 +++------------- osu.Game/Rulesets/UI/ModIcon.cs | 14 ++++++++++++-- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/osu.Game.Tournament/Components/TournamentModDisplay.cs b/osu.Game.Tournament/Components/TournamentModDisplay.cs index 3df8550667..fa9ee7edff 100644 --- a/osu.Game.Tournament/Components/TournamentModDisplay.cs +++ b/osu.Game.Tournament/Components/TournamentModDisplay.cs @@ -50,7 +50,7 @@ namespace osu.Game.Tournament.Components if (modIcon == null) return; - AddInternal(new ModIcon(modIcon) + AddInternal(new ModIcon(modIcon, false) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index ab8efdabcc..8e0d1f5bbd 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -236,13 +236,13 @@ namespace osu.Game.Overlays.Mods { iconsContainer.AddRange(new[] { - backgroundIcon = new PassThroughTooltipModIcon(Mods[1]) + backgroundIcon = new ModIcon(Mods[1], false) { Origin = Anchor.BottomRight, Anchor = Anchor.BottomRight, Position = new Vector2(1.5f), }, - foregroundIcon = new PassThroughTooltipModIcon(Mods[0]) + foregroundIcon = new ModIcon(Mods[0], false) { Origin = Anchor.BottomRight, Anchor = Anchor.BottomRight, @@ -252,7 +252,7 @@ namespace osu.Game.Overlays.Mods } else { - iconsContainer.Add(foregroundIcon = new PassThroughTooltipModIcon(Mod) + iconsContainer.Add(foregroundIcon = new ModIcon(Mod, false) { Origin = Anchor.Centre, Anchor = Anchor.Centre, @@ -297,15 +297,5 @@ namespace osu.Game.Overlays.Mods Mod = mod; } - - private class PassThroughTooltipModIcon : ModIcon - { - public override string TooltipText => null; - - public PassThroughTooltipModIcon(Mod mod) - : base(mod) - { - } - } } } diff --git a/osu.Game/Rulesets/UI/ModIcon.cs b/osu.Game/Rulesets/UI/ModIcon.cs index 8ea6c74349..04a2e052fa 100644 --- a/osu.Game/Rulesets/UI/ModIcon.cs +++ b/osu.Game/Rulesets/UI/ModIcon.cs @@ -16,6 +16,9 @@ using osu.Framework.Bindables; namespace osu.Game.Rulesets.UI { + /// + /// Display the specified mod at a fixed size. + /// public class ModIcon : Container, IHasTooltip { public readonly BindableBool Selected = new BindableBool(); @@ -28,9 +31,10 @@ namespace osu.Game.Rulesets.UI private readonly ModType type; - public virtual string TooltipText => mod.IconTooltip; + public virtual string TooltipText => showTooltip ? mod.IconTooltip : null; private Mod mod; + private readonly bool showTooltip; public Mod Mod { @@ -42,9 +46,15 @@ namespace osu.Game.Rulesets.UI } } - public ModIcon(Mod mod) + /// + /// Construct a new instance. + /// + /// The mod to be displayed + /// Whether a tooltip describing the mod should display on hover. + public ModIcon(Mod mod, bool showTooltip = true) { this.mod = mod ?? throw new ArgumentNullException(nameof(mod)); + this.showTooltip = showTooltip; type = mod.Type; From 64a3c712aa8be74bb2d32fddeb55fc364a3cb0fb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Jan 2021 19:15:19 +0900 Subject: [PATCH 106/121] Rename class and add xmldoc --- osu.Game.Tournament/Components/TournamentBeatmapPanel.cs | 2 +- .../{TournamentModDisplay.cs => TournamentModIcon.cs} | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) rename osu.Game.Tournament/Components/{TournamentModDisplay.cs => TournamentModIcon.cs} (86%) diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index 8cc4566c08..d1197b1a61 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -123,7 +123,7 @@ namespace osu.Game.Tournament.Components if (!string.IsNullOrEmpty(mod)) { - AddInternal(new TournamentModDisplay(mod) + AddInternal(new TournamentModIcon(mod) { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, diff --git a/osu.Game.Tournament/Components/TournamentModDisplay.cs b/osu.Game.Tournament/Components/TournamentModIcon.cs similarity index 86% rename from osu.Game.Tournament/Components/TournamentModDisplay.cs rename to osu.Game.Tournament/Components/TournamentModIcon.cs index fa9ee7edff..b53ecc02f8 100644 --- a/osu.Game.Tournament/Components/TournamentModDisplay.cs +++ b/osu.Game.Tournament/Components/TournamentModIcon.cs @@ -14,16 +14,19 @@ using osuTK; namespace osu.Game.Tournament.Components { - public class TournamentModDisplay : CompositeDrawable + /// + /// Mod icon displayed in tournament usages, allowing user overridden graphics. + /// + public class TournamentModIcon : CompositeDrawable { private readonly string modAcronym; [Resolved] private RulesetStore rulesets { get; set; } - public TournamentModDisplay(string mod) + public TournamentModIcon(string modAcronym) { - modAcronym = mod; + this.modAcronym = modAcronym; } [BackgroundDependencyLoader] From 81ab82fafe13df7aa513c5d64f3bb205feac6102 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Jan 2021 19:16:38 +0900 Subject: [PATCH 107/121] Tidy up nesting --- .../Components/TournamentModIcon.cs | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/osu.Game.Tournament/Components/TournamentModIcon.cs b/osu.Game.Tournament/Components/TournamentModIcon.cs index b53ecc02f8..43ac92d285 100644 --- a/osu.Game.Tournament/Components/TournamentModIcon.cs +++ b/osu.Game.Tournament/Components/TournamentModIcon.cs @@ -32,9 +32,9 @@ namespace osu.Game.Tournament.Components [BackgroundDependencyLoader] private void load(TextureStore textures, LadderInfo ladderInfo) { - var texture = textures.Get($"mods/{modAcronym}"); + var customTexture = textures.Get($"mods/{modAcronym}"); - if (texture != null) + if (customTexture != null) { AddInternal(new Sprite { @@ -42,24 +42,24 @@ namespace osu.Game.Tournament.Components RelativeSizeAxes = Axes.Both, Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, - Texture = texture + Texture = customTexture }); + + return; } - else + + var ruleset = rulesets.GetRuleset(ladderInfo.Ruleset.Value?.ID ?? 0); + var modIcon = ruleset?.CreateInstance().GetAllMods().FirstOrDefault(mod => mod.Acronym == modAcronym); + + if (modIcon == null) + return; + + AddInternal(new ModIcon(modIcon, false) { - var ruleset = rulesets.GetRuleset(ladderInfo.Ruleset.Value?.ID ?? 0); - var modIcon = ruleset?.CreateInstance().GetAllMods().FirstOrDefault(mod => mod.Acronym == modAcronym); - - if (modIcon == null) - return; - - AddInternal(new ModIcon(modIcon, false) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Scale = new Vector2(0.5f) - }); - } + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(0.5f) + }); } } } From 45395cb5e8fcf22d495908f7c77a3b5d90624d98 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Jan 2021 23:00:14 +0900 Subject: [PATCH 108/121] 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 9ad5946311..f8ce6befd4 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 3e971d9d4f..97f4320c95 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -27,7 +27,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 4732620085..301e7378a6 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -88,7 +88,7 @@ - + From 63f057a525e01e3c58ede431293b124c8b14d70d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 27 Jan 2021 20:45:48 +0300 Subject: [PATCH 109/121] Fix dotnet run/publish with runtime specified not working again --- osu.Desktop/osu.Desktop.csproj | 5 +---- osu.Game/osu.Game.csproj | 1 + 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index e201b250d4..cce7907c6c 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -24,16 +24,13 @@ + - - - - diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 97f4320c95..eb541c9de5 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -26,6 +26,7 @@ + From 2c08ce05fa18828248c1fe45aea502059917c0bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 27 Jan 2021 22:01:56 +0100 Subject: [PATCH 110/121] Remove game-local enum [Order] attribute In favour of the newly-added framework one. --- .../BeatmapListing/BeatmapSearchFilterRow.cs | 4 +- .../Overlays/BeatmapListing/SearchLanguage.cs | 2 +- .../Overlays/BeatmapSet/Scores/ScoreTable.cs | 4 +- osu.Game/Rulesets/Ruleset.cs | 6 +-- osu.Game/Rulesets/Scoring/HitResult.cs | 2 +- osu.Game/Utils/OrderAttribute.cs | 52 ------------------- 6 files changed, 9 insertions(+), 61 deletions(-) delete mode 100644 osu.Game/Utils/OrderAttribute.cs diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapSearchFilterRow.cs b/osu.Game/Overlays/BeatmapListing/BeatmapSearchFilterRow.cs index b429a5277b..01bcbd3244 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapSearchFilterRow.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapSearchFilterRow.cs @@ -12,7 +12,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osuTK; using Humanizer; -using osu.Game.Utils; +using osu.Framework.Extensions.EnumExtensions; namespace osu.Game.Overlays.BeatmapListing { @@ -80,7 +80,7 @@ namespace osu.Game.Overlays.BeatmapListing if (typeof(T).IsEnum) { - foreach (var val in OrderAttributeUtils.GetValuesInOrder()) + foreach (var val in EnumExtensions.GetValuesInOrder()) AddItem(val); } } diff --git a/osu.Game/Overlays/BeatmapListing/SearchLanguage.cs b/osu.Game/Overlays/BeatmapListing/SearchLanguage.cs index eee5d8f7e1..015cee8ce3 100644 --- a/osu.Game/Overlays/BeatmapListing/SearchLanguage.cs +++ b/osu.Game/Overlays/BeatmapListing/SearchLanguage.cs @@ -1,7 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Game.Utils; +using osu.Framework.Utils; namespace osu.Game.Overlays.BeatmapListing { diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs index 324299ccba..ddd1dfa6cd 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Extensions; +using osu.Framework.Extensions.EnumExtensions; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -15,7 +16,6 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Scoring; using osu.Game.Users.Drawables; -using osu.Game.Utils; using osuTK; using osuTK.Graphics; @@ -105,7 +105,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores var ruleset = scores.First().Ruleset.CreateInstance(); - foreach (var result in OrderAttributeUtils.GetValuesInOrder()) + foreach (var result in EnumExtensions.GetValuesInOrder()) { if (!allScoreStatistics.Contains(result)) continue; diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index b3b3d11ab3..dbc2bd4d01 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -24,9 +24,9 @@ using osu.Game.Skinning; using osu.Game.Users; using JetBrains.Annotations; using osu.Framework.Extensions; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Testing; using osu.Game.Screens.Ranking.Statistics; -using osu.Game.Utils; namespace osu.Game.Rulesets { @@ -272,7 +272,7 @@ namespace osu.Game.Rulesets var validResults = GetValidHitResults(); // enumerate over ordered list to guarantee return order is stable. - foreach (var result in OrderAttributeUtils.GetValuesInOrder()) + foreach (var result in EnumExtensions.GetValuesInOrder()) { switch (result) { @@ -298,7 +298,7 @@ namespace osu.Game.Rulesets /// /// is implicitly included. Special types like are ignored even when specified. /// - protected virtual IEnumerable GetValidHitResults() => OrderAttributeUtils.GetValuesInOrder(); + protected virtual IEnumerable GetValidHitResults() => EnumExtensions.GetValuesInOrder(); /// /// Get a display friendly name for the specified result type. diff --git a/osu.Game/Rulesets/Scoring/HitResult.cs b/osu.Game/Rulesets/Scoring/HitResult.cs index 6a3a034fc1..eaa1f95744 100644 --- a/osu.Game/Rulesets/Scoring/HitResult.cs +++ b/osu.Game/Rulesets/Scoring/HitResult.cs @@ -3,7 +3,7 @@ using System.ComponentModel; using System.Diagnostics; -using osu.Game.Utils; +using osu.Framework.Utils; namespace osu.Game.Rulesets.Scoring { diff --git a/osu.Game/Utils/OrderAttribute.cs b/osu.Game/Utils/OrderAttribute.cs deleted file mode 100644 index aded7f9814..0000000000 --- a/osu.Game/Utils/OrderAttribute.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Collections.Generic; -using System.Linq; - -namespace osu.Game.Utils -{ - public static class OrderAttributeUtils - { - /// - /// Get values of an enum in order. Supports custom ordering via . - /// - public static IEnumerable GetValuesInOrder() - { - var type = typeof(T); - - if (!type.IsEnum) - throw new InvalidOperationException("T must be an enum"); - - IEnumerable items = (T[])Enum.GetValues(type); - - if (Attribute.GetCustomAttribute(type, typeof(HasOrderedElementsAttribute)) == null) - return items; - - return items.OrderBy(i => - { - if (type.GetField(i.ToString()).GetCustomAttributes(typeof(OrderAttribute), false).FirstOrDefault() is OrderAttribute attr) - return attr.Order; - - throw new ArgumentException($"Not all values of {nameof(T)} have {nameof(OrderAttribute)} specified."); - }); - } - } - - [AttributeUsage(AttributeTargets.Field)] - public class OrderAttribute : Attribute - { - public readonly int Order; - - public OrderAttribute(int order) - { - Order = order; - } - } - - [AttributeUsage(AttributeTargets.Enum)] - public class HasOrderedElementsAttribute : Attribute - { - } -} From 90a82f986bcd2ba59f0ebc3ccc95e942a3e5665a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Jan 2021 16:20:16 +0900 Subject: [PATCH 111/121] Fallback to using json for signalr communication if JIT is unavailable --- .../Online/Multiplayer/MultiplayerClient.cs | 22 +++++++++++++------ .../Spectator/SpectatorStreamingClient.cs | 21 ++++++++++++------ 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 0d779232d0..c10c4dd1b0 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -9,6 +9,8 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.SignalR.Client; using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json; +using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Logging; @@ -64,13 +66,19 @@ namespace osu.Game.Online.Multiplayer if (connection != null) return; - connection = new HubConnectionBuilder() - .WithUrl(endpoint, options => - { - options.Headers.Add("Authorization", $"Bearer {api.AccessToken}"); - }) - .AddMessagePackProtocol() - .Build(); + var builder = new HubConnectionBuilder() + .WithUrl(endpoint, options => { options.Headers.Add("Authorization", $"Bearer {api.AccessToken}"); }); + + if (RuntimeInfo.SupportsJIT) + builder.AddMessagePackProtocol(); + else + { + // eventuall we will precompile resolvers for messagepack, but this isn't working currently + // see https://github.com/neuecc/MessagePack-CSharp/issues/780#issuecomment-768794308. + builder.AddNewtonsoftJsonProtocol(options => { options.PayloadSerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; }); + } + + connection = builder.Build(); // this is kind of SILLY // https://github.com/dotnet/aspnetcore/issues/15198 diff --git a/osu.Game/Online/Spectator/SpectatorStreamingClient.cs b/osu.Game/Online/Spectator/SpectatorStreamingClient.cs index dac2131035..7a28c179a8 100644 --- a/osu.Game/Online/Spectator/SpectatorStreamingClient.cs +++ b/osu.Game/Online/Spectator/SpectatorStreamingClient.cs @@ -9,6 +9,8 @@ using System.Threading.Tasks; using JetBrains.Annotations; using Microsoft.AspNetCore.SignalR.Client; using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json; +using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -115,14 +117,19 @@ namespace osu.Game.Online.Spectator if (connection != null) return; - connection = new HubConnectionBuilder() - .WithUrl(endpoint, options => - { - options.Headers.Add("Authorization", $"Bearer {api.AccessToken}"); - }) - .AddMessagePackProtocol() - .Build(); + var builder = new HubConnectionBuilder() + .WithUrl(endpoint, options => { options.Headers.Add("Authorization", $"Bearer {api.AccessToken}"); }); + if (RuntimeInfo.SupportsJIT) + builder.AddMessagePackProtocol(); + else + { + // eventuall we will precompile resolvers for messagepack, but this isn't working currently + // see https://github.com/neuecc/MessagePack-CSharp/issues/780#issuecomment-768794308. + builder.AddNewtonsoftJsonProtocol(options => { options.PayloadSerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; }); + } + + connection = builder.Build(); // 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); From c3d40440170e98e026bfceaf3dbd25901704e7ca Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Jan 2021 16:53:56 +0900 Subject: [PATCH 112/121] Avoid using Dapper to fix iOS compatibility of beatmap lookup cache --- ...BeatmapManager_BeatmapOnlineLookupQueue.cs | 32 ++++++++++++------- osu.Game/osu.Game.csproj | 1 - 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs b/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs index e90ccbb805..ea91f2d2e0 100644 --- a/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs +++ b/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs @@ -7,7 +7,6 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Dapper; using Microsoft.Data.Sqlite; using osu.Framework.Development; using osu.Framework.IO.Network; @@ -154,20 +153,31 @@ namespace osu.Game.Beatmaps { using (var db = new SqliteConnection(storage.GetDatabaseConnectionString("online"))) { - var found = db.QuerySingleOrDefault( - "SELECT * FROM osu_beatmaps WHERE checksum = @MD5Hash OR beatmap_id = @OnlineBeatmapID OR filename = @Path", beatmap); + db.Open(); - if (found != null) + using (var cmd = db.CreateCommand()) { - var status = (BeatmapSetOnlineStatus)found.approved; + cmd.CommandText = "SELECT beatmapset_id, beatmap_id, approved FROM osu_beatmaps WHERE checksum = @MD5Hash OR beatmap_id = @OnlineBeatmapID OR filename = @Path"; - beatmap.Status = status; - beatmap.BeatmapSet.Status = status; - beatmap.BeatmapSet.OnlineBeatmapSetID = found.beatmapset_id; - beatmap.OnlineBeatmapID = found.beatmap_id; + cmd.Parameters.Add(new SqliteParameter("@MD5Hash", beatmap.MD5Hash)); + cmd.Parameters.Add(new SqliteParameter("@OnlineBeatmapID", beatmap.OnlineBeatmapID)); + cmd.Parameters.Add(new SqliteParameter("@Path", beatmap.Path)); - LogForModel(set, $"Cached local retrieval for {beatmap}."); - return true; + using (var reader = cmd.ExecuteReader()) + { + if (reader.Read()) + { + var status = (BeatmapSetOnlineStatus)reader.GetByte(2); + + beatmap.Status = status; + beatmap.BeatmapSet.Status = status; + beatmap.BeatmapSet.OnlineBeatmapSetID = reader.GetInt32(0); + beatmap.OnlineBeatmapID = reader.GetInt32(1); + + LogForModel(set, $"Cached local retrieval for {beatmap}."); + return true; + } + } } } } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 97f4320c95..bfc5ff302e 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -18,7 +18,6 @@ - From a616688a47a1d2d1c952eb8fe173cd88f8a64c37 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Jan 2021 23:55:03 +0900 Subject: [PATCH 113/121] 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 f8ce6befd4..7060e88026 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 97f4320c95..a18eef15fc 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -27,7 +27,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 301e7378a6..48dc01f5de 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -88,7 +88,7 @@ - + From 386f9f78423f205490c817cab468d09a32d12339 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 28 Jan 2021 22:36:07 +0100 Subject: [PATCH 114/121] Fix typos in comments --- osu.Game/Online/Multiplayer/MultiplayerClient.cs | 2 +- osu.Game/Online/Spectator/SpectatorStreamingClient.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index c10c4dd1b0..b13d4fa899 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -73,7 +73,7 @@ namespace osu.Game.Online.Multiplayer builder.AddMessagePackProtocol(); else { - // eventuall we will precompile resolvers for messagepack, but this isn't working currently + // eventually we will precompile resolvers for messagepack, but this isn't working currently // see https://github.com/neuecc/MessagePack-CSharp/issues/780#issuecomment-768794308. builder.AddNewtonsoftJsonProtocol(options => { options.PayloadSerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; }); } diff --git a/osu.Game/Online/Spectator/SpectatorStreamingClient.cs b/osu.Game/Online/Spectator/SpectatorStreamingClient.cs index 7a28c179a8..b95e3f1297 100644 --- a/osu.Game/Online/Spectator/SpectatorStreamingClient.cs +++ b/osu.Game/Online/Spectator/SpectatorStreamingClient.cs @@ -124,7 +124,7 @@ namespace osu.Game.Online.Spectator builder.AddMessagePackProtocol(); else { - // eventuall we will precompile resolvers for messagepack, but this isn't working currently + // eventually we will precompile resolvers for messagepack, but this isn't working currently // see https://github.com/neuecc/MessagePack-CSharp/issues/780#issuecomment-768794308. builder.AddNewtonsoftJsonProtocol(options => { options.PayloadSerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; }); } From da4c207a73b4beb25a59237a2e43ce674ed9ec99 Mon Sep 17 00:00:00 2001 From: Corentin PALLARD Date: Fri, 29 Jan 2021 02:53:26 +0100 Subject: [PATCH 115/121] Fix the ctb auto mod speedup in some occasions --- osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs index 32e8ab5da7..ae6868aea5 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs @@ -45,6 +45,9 @@ namespace osu.Game.Rulesets.Catch.Replays float positionChange = Math.Abs(lastPosition - h.EffectiveX); double timeAvailable = h.StartTime - lastTime; + if (timeAvailable < 0) + return; + // So we can either make it there without a dash or not. // If positionChange is 0, we don't need to move, so speedRequired should also be 0 (could be NaN if timeAvailable is 0 too) // The case where positionChange > 0 and timeAvailable == 0 results in PositiveInfinity which provides expected beheaviour. From d168de0ae32f0877d225efc13942b96a57a9bba2 Mon Sep 17 00:00:00 2001 From: Corentin PALLARD Date: Fri, 29 Jan 2021 03:03:23 +0100 Subject: [PATCH 116/121] Formatting --- osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs index ae6868aea5..64ded8e94f 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs @@ -46,7 +46,9 @@ namespace osu.Game.Rulesets.Catch.Replays double timeAvailable = h.StartTime - lastTime; if (timeAvailable < 0) + { return; + } // So we can either make it there without a dash or not. // If positionChange is 0, we don't need to move, so speedRequired should also be 0 (could be NaN if timeAvailable is 0 too) From 449f883be15956273c5271eef5236dbd95776792 Mon Sep 17 00:00:00 2001 From: Firmatorenio Date: Fri, 29 Jan 2021 11:48:51 +0600 Subject: [PATCH 117/121] add SV multiplier adjustment to TaikoModDifficultyAdjust --- .../Mods/TaikoModDifficultyAdjust.cs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs index 56a73ad7df..1d1773fcbb 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs @@ -1,11 +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.Linq; +using osu.Framework.Bindables; +using osu.Game.Beatmaps; +using osu.Game.Configuration; using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Taiko.Mods { public class TaikoModDifficultyAdjust : ModDifficultyAdjust { + [SettingSource("Slider Velocity", "Adjust a beatmap's set SV", LAST_SETTING_ORDER + 1)] + public BindableNumber SliderVelocity { get; } = new BindableFloat + { + Precision = 0.05f, + MinValue = 0.25f, + MaxValue = 4, + Default = 1, + Value = 1, + }; + + public override string SettingDescription + { + get + { + string sliderVelocity = SliderVelocity.IsDefault ? string.Empty : $"SV {SliderVelocity.Value:N1}"; + + return string.Join(", ", new[] + { + base.SettingDescription, + sliderVelocity + }.Where(s => !string.IsNullOrEmpty(s))); + } + } + + protected override void ApplySettings(BeatmapDifficulty difficulty) + { + base.ApplySettings(difficulty); + + ApplySetting(SliderVelocity, sv => difficulty.SliderMultiplier *= sv); + } } } From a61444690ea8324719e4f04f1cb6e02b3706bb19 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Jan 2021 16:32:28 +0900 Subject: [PATCH 118/121] Remove all usage of CatchUnobservedExceptions This should no longer be required with the recent framework side change that stops a game from crashing on unobserved exceptions (https://github.com/ppy/osu-framework/pull/4171). --- osu.Game/Extensions/TaskExtensions.cs | 36 ------------------- .../Multiplayer/StatefulMultiplayerClient.cs | 3 +- .../OnlinePlay/Multiplayer/Multiplayer.cs | 3 +- .../Multiplayer/MultiplayerMatchSubScreen.cs | 8 +---- .../Multiplayer/MultiplayerRoomManager.cs | 3 +- .../Participants/ParticipantPanel.cs | 3 +- 6 files changed, 5 insertions(+), 51 deletions(-) delete mode 100644 osu.Game/Extensions/TaskExtensions.cs diff --git a/osu.Game/Extensions/TaskExtensions.cs b/osu.Game/Extensions/TaskExtensions.cs deleted file mode 100644 index 4138c2757a..0000000000 --- a/osu.Game/Extensions/TaskExtensions.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. - -#nullable enable - -using System; -using System.Threading.Tasks; -using osu.Framework.Extensions.ExceptionExtensions; -using osu.Framework.Logging; - -namespace osu.Game.Extensions -{ - public static class TaskExtensions - { - /// - /// Denote a task which is to be run without local error handling logic, where failure is not catastrophic. - /// Avoids unobserved exceptions from being fired. - /// - /// The task. - /// - /// Whether errors should be logged as errors visible to users, or as debug messages. - /// Logging as debug will essentially silence the errors on non-release builds. - /// - public static void CatchUnobservedExceptions(this Task task, bool logAsError = false) - { - task.ContinueWith(t => - { - Exception? exception = t.Exception?.AsSingular(); - if (logAsError) - Logger.Error(exception, $"Error running task: {exception?.Message ?? "(unknown)"}", LoggingTarget.Runtime, true); - else - Logger.Log($"Error running task: {exception}", LoggingTarget.Runtime, LogLevel.Debug); - }, TaskContinuationOptions.NotOnRanToCompletion); - } - } -} diff --git a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs index f0e11b2b8b..48194d1f0f 100644 --- a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs @@ -15,7 +15,6 @@ using osu.Framework.Graphics; using osu.Framework.Logging; using osu.Game.Beatmaps; using osu.Game.Database; -using osu.Game.Extensions; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; @@ -104,7 +103,7 @@ namespace osu.Game.Online.Multiplayer if (!connected.NewValue && Room != null) { Logger.Log("Connection to multiplayer server was lost.", LoggingTarget.Runtime, LogLevel.Important); - LeaveRoom().CatchUnobservedExceptions(); + LeaveRoom(); } }); } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs index 76f5c74433..ae22e1fcec 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs @@ -4,7 +4,6 @@ using osu.Framework.Allocation; using osu.Framework.Logging; using osu.Framework.Screens; -using osu.Game.Extensions; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; @@ -23,7 +22,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer base.OnResuming(last); if (client.Room != null) - client.ChangeState(MultiplayerUserState.Idle).CatchUnobservedExceptions(true); + client.ChangeState(MultiplayerUserState.Idle); } protected override void UpdatePollingRate(bool isIdle) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 7c4b6d18ec..c071637b9b 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -11,7 +11,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Screens; -using osu.Game.Extensions; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; using osu.Game.Screens.OnlinePlay.Components; @@ -237,7 +236,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer // accessing Exception here silences any potential errors from the antecedent task if (t.Exception != null) { - t.CatchUnobservedExceptions(true); // will run immediately. // gameplay was not started due to an exception; unblock button. endOperation(); } @@ -248,11 +246,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer } client.ToggleReady() - .ContinueWith(t => - { - t.CatchUnobservedExceptions(true); // will run immediately. - endOperation(); - }); + .ContinueWith(t => endOperation()); void endOperation() { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs index 61d8896732..65d112a032 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs @@ -9,7 +9,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.ExceptionExtensions; using osu.Framework.Logging; -using osu.Game.Extensions; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; using osu.Game.Online.Rooms.RoomStatuses; @@ -69,7 +68,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer base.PartRoom(); - multiplayerClient.LeaveRoom().CatchUnobservedExceptions(); + multiplayerClient.LeaveRoom(); // Todo: This is not the way to do this. Basically when we're the only participant and the room closes, there's no way to know if this is actually the case. // This is delayed one frame because upon exiting the match subscreen, multiplayer updates the polling rate and messes with polling. diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs index f99655e305..b5533f49cc 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs @@ -10,7 +10,6 @@ using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; -using osu.Game.Extensions; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -176,7 +175,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants if (Room.Host?.UserID != api.LocalUser.Value.Id) return; - Client.TransferHost(targetUser).CatchUnobservedExceptions(true); + Client.TransferHost(targetUser); }) }; } From 37ef5c70729c6f99cf51cc6841eb4833a728e51c Mon Sep 17 00:00:00 2001 From: Firmatorenio Date: Fri, 29 Jan 2021 15:04:55 +0600 Subject: [PATCH 119/121] rename SliderVelocity to ScrollSpeed --- .../Mods/TaikoModDifficultyAdjust.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs index 1d1773fcbb..4006652bd5 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs @@ -11,8 +11,8 @@ namespace osu.Game.Rulesets.Taiko.Mods { public class TaikoModDifficultyAdjust : ModDifficultyAdjust { - [SettingSource("Slider Velocity", "Adjust a beatmap's set SV", LAST_SETTING_ORDER + 1)] - public BindableNumber SliderVelocity { get; } = new BindableFloat + [SettingSource("Scroll Speed", "Adjust a beatmap's set scroll speed", LAST_SETTING_ORDER + 1)] + public BindableNumber ScrollSpeed { get; } = new BindableFloat { Precision = 0.05f, MinValue = 0.25f, @@ -25,12 +25,12 @@ namespace osu.Game.Rulesets.Taiko.Mods { get { - string sliderVelocity = SliderVelocity.IsDefault ? string.Empty : $"SV {SliderVelocity.Value:N1}"; + string scrollSpeed = ScrollSpeed.IsDefault ? string.Empty : $"Scroll x{ScrollSpeed.Value:N1}"; return string.Join(", ", new[] { base.SettingDescription, - sliderVelocity + scrollSpeed }.Where(s => !string.IsNullOrEmpty(s))); } } @@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Taiko.Mods { base.ApplySettings(difficulty); - ApplySetting(SliderVelocity, sv => difficulty.SliderMultiplier *= sv); + ApplySetting(ScrollSpeed, scroll => difficulty.SliderMultiplier *= scroll); } } } From 18e6afbec06a2111275ba6415eb7d132f52f9ce2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Jan 2021 19:16:10 +0900 Subject: [PATCH 120/121] Ensure the item is present before trying to select it --- .../Overlays/Settings/Sections/SkinSection.cs | 33 ++++++++++++++----- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 56c677d5b5..7c8309fd56 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -121,7 +121,22 @@ namespace osu.Game.Overlays.Settings.Sections }); } - private void updateSelectedSkinFromConfig() => dropdownBindable.Value = skinDropdown.Items.Single(s => s.ID == configBindable.Value); + private void updateSelectedSkinFromConfig() + { + int id = configBindable.Value; + + var skin = skinDropdown.Items.FirstOrDefault(s => s.ID == id); + + if (skin == null) + { + // there may be a thread race condition where an item is selected that hasn't yet been added to the dropdown. + // to avoid adding complexity, let's just ensure the item is added so we can perform the selection. + skin = skins.Query(s => s.ID == id); + addItem(skin); + } + + dropdownBindable.Value = skin; + } private void updateItems() { @@ -134,14 +149,14 @@ namespace osu.Game.Overlays.Settings.Sections private void itemUpdated(ValueChangedEvent> weakItem) { if (weakItem.NewValue.TryGetTarget(out var item)) - { - Schedule(() => - { - List newDropdownItems = skinDropdown.Items.Where(i => !i.Equals(item)).Append(item).ToList(); - sortUserSkins(newDropdownItems); - skinDropdown.Items = newDropdownItems; - }); - } + Schedule(() => addItem(item)); + } + + private void addItem(SkinInfo item) + { + List newDropdownItems = skinDropdown.Items.Where(i => !i.Equals(item)).Append(item).ToList(); + sortUserSkins(newDropdownItems); + skinDropdown.Items = newDropdownItems; } private void itemRemoved(ValueChangedEvent> weakItem) From 16f3d1815f27a344ca2e9a662f960f97ef4db666 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Jan 2021 19:53:56 +0900 Subject: [PATCH 121/121] Fix SQLite exception thrown is a beatmap lookup is attempted without an OnlineBeatmapID present MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It turns out the SQLite API isn't smart enough to handle nullables directly, so we need to help it out a bit. Stops the following from being thrown: ``` System.InvalidOperationException: Value must be set. at Microsoft.Data.Sqlite.SqliteParameter.Bind(sqlite3_stmt stmt) = 3 at at Microsoft.Data.Sqlite.SqliteParameterCollection.Bind(sqlite3_stmt stmt) = 3 at at Microsoft.Data.Sqlite.SqliteCommand.ExecuteReader(CommandBehavior behavior) at Microsoft.Data.Sqlite.SqliteCommand.ExecuteReader() at osu.Game.Beatmaps.BeatmapManager.BeatmapOnlineLookupQueue.checkLocalCache(BeatmapSetInfo set, BeatmapInfo beatmap) in /Users/dean/Projects/osu/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs:line 166 = 166 ``` --- osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs b/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs index ea91f2d2e0..7c4b344c9e 100644 --- a/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs +++ b/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs @@ -160,7 +160,7 @@ namespace osu.Game.Beatmaps cmd.CommandText = "SELECT beatmapset_id, beatmap_id, approved FROM osu_beatmaps WHERE checksum = @MD5Hash OR beatmap_id = @OnlineBeatmapID OR filename = @Path"; cmd.Parameters.Add(new SqliteParameter("@MD5Hash", beatmap.MD5Hash)); - cmd.Parameters.Add(new SqliteParameter("@OnlineBeatmapID", beatmap.OnlineBeatmapID)); + cmd.Parameters.Add(new SqliteParameter("@OnlineBeatmapID", beatmap.OnlineBeatmapID ?? (object)DBNull.Value)); cmd.Parameters.Add(new SqliteParameter("@Path", beatmap.Path)); using (var reader = cmd.ExecuteReader())