From d467dd57472ebc001ee28eb6f01d6b468702e735 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Mon, 29 Jul 2019 06:51:44 +0300 Subject: [PATCH 001/818] Add simple implementation of in-game leaderboard --- osu.Game/Screens/Play/InGameLeaderboard.cs | 42 ++++ osu.Game/Screens/Play/InGameScoreContainer.cs | 206 ++++++++++++++++++ 2 files changed, 248 insertions(+) create mode 100644 osu.Game/Screens/Play/InGameLeaderboard.cs create mode 100644 osu.Game/Screens/Play/InGameScoreContainer.cs diff --git a/osu.Game/Screens/Play/InGameLeaderboard.cs b/osu.Game/Screens/Play/InGameLeaderboard.cs new file mode 100644 index 0000000000..c8f5cf5fd7 --- /dev/null +++ b/osu.Game/Screens/Play/InGameLeaderboard.cs @@ -0,0 +1,42 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Users; + +namespace osu.Game.Screens.Play +{ + public class InGameLeaderboard : CompositeDrawable + { + protected readonly InGameScoreContainer ScoresContainer; + + public readonly BindableDouble PlayerCurrentScore = new BindableDouble(); + + private bool playerItemCreated; + private User playerUser; + + public User PlayerUser + { + get => playerUser; + set + { + playerUser = value; + + if (playerItemCreated) + return; + + ScoresContainer.AddRealTimePlayer(PlayerCurrentScore, playerUser); + playerItemCreated = true; + } + } + + public InGameLeaderboard() + { + AutoSizeAxes = Axes.Y; + + InternalChild = ScoresContainer = new InGameScoreContainer(); + } + } +} diff --git a/osu.Game/Screens/Play/InGameScoreContainer.cs b/osu.Game/Screens/Play/InGameScoreContainer.cs new file mode 100644 index 0000000000..eb4a0a2f38 --- /dev/null +++ b/osu.Game/Screens/Play/InGameScoreContainer.cs @@ -0,0 +1,206 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Linq; +using Humanizer; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Scoring; +using osu.Game.Users; +using osuTK; + +namespace osu.Game.Screens.Play +{ + public class InGameScoreContainer : FillFlowContainer + { + /// + /// Whether to declare a new position for un-positioned players. + /// Must be disabled for online leaderboards with top 50 scores only. + /// + public bool DeclareNewPosition = true; + + public InGameScoreContainer() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Direction = FillDirection.Vertical; + Spacing = new Vector2(2.5f); + LayoutDuration = 500; + LayoutEasing = Easing.OutQuint; + } + + public void AddRealTimePlayer(BindableDouble currentScore, User user = null) + { + if (currentScore == null) + return; + + var scoreItem = addScore(currentScore.Value, user); + currentScore.ValueChanged += s => scoreItem.TotalScore = s.NewValue; + } + + public void AddScore(ScoreInfo score, int? position = null) + { + if (score != null) + addScore(score.TotalScore, score.User, position); + } + + private int maxPosition => this.Where(i => i.ScorePosition.HasValue).Max(i => i.ScorePosition) ?? 0; + + private InGameScoreItem addScore(double totalScore, User user = null, int? position = null) + { + var scoreItem = new InGameScoreItem + { + User = user, + TotalScore = totalScore, + ScorePosition = position, + OnScoreChange = updateScores, + }; + + Add(scoreItem); + SetLayoutPosition(scoreItem, position ?? maxPosition + 1); + + updateScores(); + + return scoreItem; + } + + private void updateScores() + { + var orderedByScore = this.OrderByDescending(i => i.TotalScore).ToList(); + var orderedPositions = this.OrderByDescending(i => i.ScorePosition.HasValue).ThenBy(i => i.ScorePosition).Select(i => i.ScorePosition).ToList(); + + for (int i = 0; i < Count; i++) + { + int newPosition = orderedPositions[i] ?? maxPosition + 1; + + SetLayoutPosition(orderedByScore[i], newPosition); + orderedByScore[i].ScorePosition = DeclareNewPosition ? newPosition : orderedPositions[i]; + } + } + } + + public class InGameScoreItem : CompositeDrawable + { + private readonly OsuSpriteText positionText, positionSymbol, userString; + private readonly GlowingSpriteText scoreText; + + public Action OnScoreChange; + + private int? scorePosition; + + public int? ScorePosition + { + get => scorePosition; + set + { + scorePosition = value; + + if (scorePosition.HasValue) + positionText.Text = $"#{scorePosition.Value.ToMetric(decimals: scorePosition < 100000 ? 1 : 0)}"; + + positionText.FadeTo(scorePosition.HasValue ? 1 : 0, 100); + positionSymbol.FadeTo(scorePosition.HasValue ? 1 : 0, 100); + } + } + + private double totalScore; + + public double TotalScore + { + get => totalScore; + set + { + totalScore = value; + scoreText.Text = totalScore.ToString("N0"); + + OnScoreChange?.Invoke(); + } + } + + private User user; + + public User User + { + get => user; + set + { + user = value; + userString.Text = user?.Username; + } + } + + public InGameScoreItem() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + InternalChild = new Container + { + Masking = true, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + new FillFlowContainer + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopRight, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Margin = new MarginPadding { Right = 2.5f }, + Spacing = new Vector2(2.5f), + Children = new[] + { + positionText = new OsuSpriteText + { + Alpha = 0, + Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold), + }, + positionSymbol = new OsuSpriteText + { + Alpha = 0, + Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold), + Text = ">", + }, + } + }, + new FillFlowContainer + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopLeft, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Margin = new MarginPadding { Left = 2.5f }, + Spacing = new Vector2(2.5f), + Children = new Drawable[] + { + userString = new OsuSpriteText + { + Size = new Vector2(80, 16), + Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold, italics: true), + }, + scoreText = new GlowingSpriteText + { + GlowColour = OsuColour.FromHex(@"83ccfa"), + Font = OsuFont.Numeric.With(size: 14), + } + } + }, + }, + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + positionText.Colour = colours.YellowLight; + positionSymbol.Colour = colours.Yellow; + } + } +} From 6233dc22e0b76bda53722e0aca270d67e0e39c8c Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Mon, 29 Jul 2019 06:52:42 +0300 Subject: [PATCH 002/818] Add simple tests for current implementation --- .../Gameplay/TestSceneInGameLeaderboard.cs | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneInGameLeaderboard.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneInGameLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneInGameLeaderboard.cs new file mode 100644 index 0000000000..36ed8f9b4c --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneInGameLeaderboard.cs @@ -0,0 +1,79 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Game.Screens.Play; +using osu.Game.Users; +using osuTK; + +namespace osu.Game.Tests.Visual.Gameplay +{ + [TestFixture] + public class TestSceneInGameLeaderboard : OsuTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(InGameLeaderboard), + typeof(InGameScoreContainer), + }; + + private readonly TestInGameLeaderboard leaderboard; + private readonly BindableDouble playerScore; + + public TestSceneInGameLeaderboard() + { + Add(leaderboard = new TestInGameLeaderboard + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(2), + RelativeSizeAxes = Axes.X, + PlayerCurrentScore = { BindTarget = playerScore = new BindableDouble(1222333) } + }); + + AddStep("add player user", () => leaderboard.PlayerUser = new User { Username = "You" }); + AddSliderStep("set player score", 50, 5000000, 1222333, v => playerScore.Value = v); + } + + [Test] + public void TestPlayerScore() + { + var player2Score = new BindableDouble(1234567); + var player3Score = new BindableDouble(1111111); + + AddStep("add player 2", () => leaderboard.AddDummyPlayer(player2Score, "Player 2")); + AddStep("add player 3", () => leaderboard.AddDummyPlayer(player3Score, "Player 3")); + + AddAssert("is player 2 position #1", () => leaderboard.CheckPositionByUsername("Player 2", 1)); + AddAssert("is player position #2", () => leaderboard.CheckPositionByUsername("You", 2)); + AddAssert("is player 3 position #3", () => leaderboard.CheckPositionByUsername("Player 3", 3)); + + AddStep("set score above player 3", () => player2Score.Value = playerScore.Value - 500); + AddAssert("is player position #1", () => leaderboard.CheckPositionByUsername("You", 1)); + AddAssert("is player 2 position #2", () => leaderboard.CheckPositionByUsername("Player 2", 2)); + AddAssert("is player 3 position #3", () => leaderboard.CheckPositionByUsername("Player 3", 3)); + + AddStep("set score below players", () => player2Score.Value = playerScore.Value - 123456); + AddAssert("is player position #1", () => leaderboard.CheckPositionByUsername("You", 1)); + AddAssert("is player 3 position #2", () => leaderboard.CheckPositionByUsername("Player 3", 2)); + AddAssert("is player 2 position #3", () => leaderboard.CheckPositionByUsername("Player 2", 3)); + } + + private class TestInGameLeaderboard : InGameLeaderboard + { + public bool CheckPositionByUsername(string username, int? estimatedPosition) + { + var scoreItem = ScoresContainer.Where(i => i.User.Username == username).FirstOrDefault(); + + return scoreItem != null && scoreItem.ScorePosition == estimatedPosition; + } + + public void AddDummyPlayer(BindableDouble currentScore, string username) => ScoresContainer.AddRealTimePlayer(currentScore, new User { Username = username }); + } + } +} From 4874746525615cb5aaffbe6195d8038b7766543b Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Tue, 30 Jul 2019 12:32:12 +0300 Subject: [PATCH 003/818] Add OnScoreChange event For use in scrolling system on a later PR --- osu.Game/Screens/Play/InGameScoreContainer.cs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/InGameScoreContainer.cs b/osu.Game/Screens/Play/InGameScoreContainer.cs index eb4a0a2f38..985e867510 100644 --- a/osu.Game/Screens/Play/InGameScoreContainer.cs +++ b/osu.Game/Screens/Play/InGameScoreContainer.cs @@ -18,6 +18,12 @@ namespace osu.Game.Screens.Play { public class InGameScoreContainer : FillFlowContainer { + /// + /// Called once an item's score has changed. + /// Useful for doing calculations on what score to show or hide next. (scrolling system) + /// + public event Action OnScoreChange; + /// /// Whether to declare a new position for un-positioned players. /// Must be disabled for online leaderboards with top 50 scores only. @@ -64,12 +70,12 @@ namespace osu.Game.Screens.Play Add(scoreItem); SetLayoutPosition(scoreItem, position ?? maxPosition + 1); - updateScores(); + reorderPositions(); return scoreItem; } - private void updateScores() + private void reorderPositions() { var orderedByScore = this.OrderByDescending(i => i.TotalScore).ToList(); var orderedPositions = this.OrderByDescending(i => i.ScorePosition.HasValue).ThenBy(i => i.ScorePosition).Select(i => i.ScorePosition).ToList(); @@ -82,6 +88,13 @@ namespace osu.Game.Screens.Play orderedByScore[i].ScorePosition = DeclareNewPosition ? newPosition : orderedPositions[i]; } } + + private void updateScores() + { + reorderPositions(); + + OnScoreChange?.Invoke(); + } } public class InGameScoreItem : CompositeDrawable From 32498d5d5088f4b009eec5d7304ea10f96dbb515 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Tue, 30 Jul 2019 12:35:49 +0300 Subject: [PATCH 004/818] Add xmldocs and return score items. Return for usage in later PR --- osu.Game/Screens/Play/InGameScoreContainer.cs | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Play/InGameScoreContainer.cs b/osu.Game/Screens/Play/InGameScoreContainer.cs index 985e867510..0ebf475e18 100644 --- a/osu.Game/Screens/Play/InGameScoreContainer.cs +++ b/osu.Game/Screens/Play/InGameScoreContainer.cs @@ -40,20 +40,30 @@ namespace osu.Game.Screens.Play LayoutEasing = Easing.OutQuint; } - public void AddRealTimePlayer(BindableDouble currentScore, User user = null) + /// + /// Adds a real-time player score item whose score is updated via a . + /// + /// The bindable current score of the player. + /// The player user. + /// Returns the drawable score item of that player. + public InGameScoreItem AddRealTimePlayer(BindableDouble currentScore, User user = null) { if (currentScore == null) - return; + return null; var scoreItem = addScore(currentScore.Value, user); currentScore.ValueChanged += s => scoreItem.TotalScore = s.NewValue; + + return scoreItem; } - public void AddScore(ScoreInfo score, int? position = null) - { - if (score != null) - addScore(score.TotalScore, score.User, position); - } + /// + /// Adds a score item based off a with an initial position. + /// + /// The score info to use for this item. + /// The initial position of this item. + /// Returns the drawable score item of that player. + public InGameScoreItem AddScore(ScoreInfo score, int? position = null) => score != null ? addScore(score.TotalScore, score.User, position) : null; private int maxPosition => this.Where(i => i.ScorePosition.HasValue).Max(i => i.ScorePosition) ?? 0; From 77aa3a9fe56437c3b4aca91b12e87ee5880a8b39 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Tue, 30 Jul 2019 12:37:01 +0300 Subject: [PATCH 005/818] Clear previous score items on SetUp --- osu.Game.Tests/Visual/Gameplay/TestSceneInGameLeaderboard.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneInGameLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneInGameLeaderboard.cs index 36ed8f9b4c..4a861428de 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneInGameLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneInGameLeaderboard.cs @@ -40,6 +40,9 @@ namespace osu.Game.Tests.Visual.Gameplay AddSliderStep("set player score", 50, 5000000, 1222333, v => playerScore.Value = v); } + [SetUp] + public void SetUp() => leaderboard.ClearScores(); + [Test] public void TestPlayerScore() { @@ -66,6 +69,8 @@ namespace osu.Game.Tests.Visual.Gameplay private class TestInGameLeaderboard : InGameLeaderboard { + public void ClearScores() => ScoresContainer.RemoveAll(s => s.User.Username != PlayerUser.Username); + public bool CheckPositionByUsername(string username, int? estimatedPosition) { var scoreItem = ScoresContainer.Where(i => i.User.Username == username).FirstOrDefault(); From 7675c679db84f0fa422b6f9034407527f36484af Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Wed, 31 Jul 2019 16:09:40 +0300 Subject: [PATCH 006/818] Fix reordering not declaring new position properly Small bug fix --- osu.Game/Screens/Play/InGameScoreContainer.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/InGameScoreContainer.cs b/osu.Game/Screens/Play/InGameScoreContainer.cs index 0ebf475e18..edccf6c45b 100644 --- a/osu.Game/Screens/Play/InGameScoreContainer.cs +++ b/osu.Game/Screens/Play/InGameScoreContainer.cs @@ -90,9 +90,11 @@ namespace osu.Game.Screens.Play var orderedByScore = this.OrderByDescending(i => i.TotalScore).ToList(); var orderedPositions = this.OrderByDescending(i => i.ScorePosition.HasValue).ThenBy(i => i.ScorePosition).Select(i => i.ScorePosition).ToList(); + var newDeclaredPosition = maxPosition + 1; + for (int i = 0; i < Count; i++) { - int newPosition = orderedPositions[i] ?? maxPosition + 1; + int newPosition = orderedPositions[i] ?? newDeclaredPosition; SetLayoutPosition(orderedByScore[i], newPosition); orderedByScore[i].ScorePosition = DeclareNewPosition ? newPosition : orderedPositions[i]; From 5e4e15033025258a2ec6212c782c8cd72380d5f9 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sun, 4 Aug 2019 18:20:45 +0300 Subject: [PATCH 007/818] Use normal Action --- osu.Game/Screens/Play/InGameScoreContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/InGameScoreContainer.cs b/osu.Game/Screens/Play/InGameScoreContainer.cs index edccf6c45b..2d45158095 100644 --- a/osu.Game/Screens/Play/InGameScoreContainer.cs +++ b/osu.Game/Screens/Play/InGameScoreContainer.cs @@ -22,7 +22,7 @@ namespace osu.Game.Screens.Play /// Called once an item's score has changed. /// Useful for doing calculations on what score to show or hide next. (scrolling system) /// - public event Action OnScoreChange; + public Action OnScoreChange; /// /// Whether to declare a new position for un-positioned players. From 9325c024bb08dcd468cc41a0f70d3df7f09f5d78 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sun, 4 Aug 2019 18:26:01 +0300 Subject: [PATCH 008/818] Add InitialPosition field --- osu.Game/Screens/Play/InGameScoreContainer.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/InGameScoreContainer.cs b/osu.Game/Screens/Play/InGameScoreContainer.cs index 2d45158095..36dbec7fce 100644 --- a/osu.Game/Screens/Play/InGameScoreContainer.cs +++ b/osu.Game/Screens/Play/InGameScoreContainer.cs @@ -61,19 +61,18 @@ namespace osu.Game.Screens.Play /// Adds a score item based off a with an initial position. /// /// The score info to use for this item. - /// The initial position of this item. + /// The initial position of this item. /// Returns the drawable score item of that player. - public InGameScoreItem AddScore(ScoreInfo score, int? position = null) => score != null ? addScore(score.TotalScore, score.User, position) : null; + public InGameScoreItem AddScore(ScoreInfo score, int? initialPosition = null) => score != null ? addScore(score.TotalScore, score.User, initialPosition) : null; private int maxPosition => this.Where(i => i.ScorePosition.HasValue).Max(i => i.ScorePosition) ?? 0; private InGameScoreItem addScore(double totalScore, User user = null, int? position = null) { - var scoreItem = new InGameScoreItem + var scoreItem = new InGameScoreItem(position) { User = user, TotalScore = totalScore, - ScorePosition = position, OnScoreChange = updateScores, }; @@ -117,6 +116,7 @@ namespace osu.Game.Screens.Play public Action OnScoreChange; private int? scorePosition; + public int? InitialPosition; public int? ScorePosition { @@ -159,7 +159,7 @@ namespace osu.Game.Screens.Play } } - public InGameScoreItem() + public InGameScoreItem(int? initialPosition) { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; @@ -219,6 +219,8 @@ namespace osu.Game.Screens.Play }, }, }; + + InitialPosition = ScorePosition = initialPosition; } [BackgroundDependencyLoader] From 91f35dde58a3815ba25d731c6ed5fad8da1e98ab Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sun, 4 Aug 2019 18:27:49 +0300 Subject: [PATCH 009/818] Actual fix of position declaring bug --- osu.Game/Screens/Play/InGameScoreContainer.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/InGameScoreContainer.cs b/osu.Game/Screens/Play/InGameScoreContainer.cs index 36dbec7fce..3949307dca 100644 --- a/osu.Game/Screens/Play/InGameScoreContainer.cs +++ b/osu.Game/Screens/Play/InGameScoreContainer.cs @@ -65,7 +65,7 @@ namespace osu.Game.Screens.Play /// Returns the drawable score item of that player. public InGameScoreItem AddScore(ScoreInfo score, int? initialPosition = null) => score != null ? addScore(score.TotalScore, score.User, initialPosition) : null; - private int maxPosition => this.Where(i => i.ScorePosition.HasValue).Max(i => i.ScorePosition) ?? 0; + private int maxPosition => this.Max(i => this.Any(item => item.InitialPosition.HasValue) ? i.InitialPosition : i.ScorePosition) ?? 0; private InGameScoreItem addScore(double totalScore, User user = null, int? position = null) { @@ -87,13 +87,11 @@ namespace osu.Game.Screens.Play private void reorderPositions() { var orderedByScore = this.OrderByDescending(i => i.TotalScore).ToList(); - var orderedPositions = this.OrderByDescending(i => i.ScorePosition.HasValue).ThenBy(i => i.ScorePosition).Select(i => i.ScorePosition).ToList(); - - var newDeclaredPosition = maxPosition + 1; + var orderedPositions = this.Select(i => this.Any(item => item.InitialPosition.HasValue) ? i.InitialPosition : i.ScorePosition).OrderByDescending(p => p.HasValue).ThenBy(p => p).ToList(); for (int i = 0; i < Count; i++) { - int newPosition = orderedPositions[i] ?? newDeclaredPosition; + int newPosition = orderedPositions[i] ?? maxPosition + 1; SetLayoutPosition(orderedByScore[i], newPosition); orderedByScore[i].ScorePosition = DeclareNewPosition ? newPosition : orderedPositions[i]; From f5daf98aa530173f9e83dfc5a1b90b1fb2fd1591 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sun, 4 Aug 2019 18:28:40 +0300 Subject: [PATCH 010/818] Reset player state on setup --- .../Visual/Gameplay/TestSceneInGameLeaderboard.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneInGameLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneInGameLeaderboard.cs index 4a861428de..096b29d496 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneInGameLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneInGameLeaderboard.cs @@ -41,7 +41,12 @@ namespace osu.Game.Tests.Visual.Gameplay } [SetUp] - public void SetUp() => leaderboard.ClearScores(); + public void SetUp() + { + leaderboard.ClearScores(); + leaderboard.PlayerPosition = 1; + playerScore.Value = 1222333; + } [Test] public void TestPlayerScore() From 598c02f8b9f266c1dbd3d0d2ee196a2ee7a28d73 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sun, 4 Aug 2019 18:31:32 +0300 Subject: [PATCH 011/818] Shorten Where().FirstOrDefault() --- osu.Game.Tests/Visual/Gameplay/TestSceneInGameLeaderboard.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneInGameLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneInGameLeaderboard.cs index 096b29d496..24b8033fbc 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneInGameLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneInGameLeaderboard.cs @@ -78,7 +78,7 @@ namespace osu.Game.Tests.Visual.Gameplay public bool CheckPositionByUsername(string username, int? estimatedPosition) { - var scoreItem = ScoresContainer.Where(i => i.User.Username == username).FirstOrDefault(); + var scoreItem = ScoresContainer.FirstOrDefault(i => i.User.Username == username); return scoreItem != null && scoreItem.ScorePosition == estimatedPosition; } From fdd00c0820355b10b8450e449512cd8067fb77ff Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sun, 4 Aug 2019 18:35:53 +0300 Subject: [PATCH 012/818] Remove position fading Unnecessary effect --- osu.Game/Screens/Play/InGameScoreContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/InGameScoreContainer.cs b/osu.Game/Screens/Play/InGameScoreContainer.cs index 3949307dca..f548b3de3f 100644 --- a/osu.Game/Screens/Play/InGameScoreContainer.cs +++ b/osu.Game/Screens/Play/InGameScoreContainer.cs @@ -126,8 +126,8 @@ namespace osu.Game.Screens.Play if (scorePosition.HasValue) positionText.Text = $"#{scorePosition.Value.ToMetric(decimals: scorePosition < 100000 ? 1 : 0)}"; - positionText.FadeTo(scorePosition.HasValue ? 1 : 0, 100); - positionSymbol.FadeTo(scorePosition.HasValue ? 1 : 0, 100); + positionText.FadeTo(scorePosition.HasValue ? 1 : 0); + positionSymbol.FadeTo(scorePosition.HasValue ? 1 : 0); } } From ca7a812e1cc822152ceff303d1323a608b6e141a Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sun, 4 Aug 2019 18:39:42 +0300 Subject: [PATCH 013/818] Add PlayerPosition property --- osu.Game.Tests/Visual/Gameplay/TestSceneInGameLeaderboard.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneInGameLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneInGameLeaderboard.cs index 24b8033fbc..cd211d06ec 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneInGameLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneInGameLeaderboard.cs @@ -75,6 +75,11 @@ namespace osu.Game.Tests.Visual.Gameplay private class TestInGameLeaderboard : InGameLeaderboard { public void ClearScores() => ScoresContainer.RemoveAll(s => s.User.Username != PlayerUser.Username); + public int? PlayerPosition + { + get => PlayerScoreItem.ScorePosition; + set => PlayerScoreItem.ScorePosition = value; + } public bool CheckPositionByUsername(string username, int? estimatedPosition) { From 472af16015c40745f59fa759e362f637f57e0f44 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Sun, 4 Aug 2019 19:17:45 +0300 Subject: [PATCH 014/818] Revert unintended change --- .../Visual/Gameplay/TestSceneInGameLeaderboard.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneInGameLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneInGameLeaderboard.cs index cd211d06ec..0019212dfa 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneInGameLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneInGameLeaderboard.cs @@ -44,7 +44,6 @@ namespace osu.Game.Tests.Visual.Gameplay public void SetUp() { leaderboard.ClearScores(); - leaderboard.PlayerPosition = 1; playerScore.Value = 1222333; } @@ -75,11 +74,6 @@ namespace osu.Game.Tests.Visual.Gameplay private class TestInGameLeaderboard : InGameLeaderboard { public void ClearScores() => ScoresContainer.RemoveAll(s => s.User.Username != PlayerUser.Username); - public int? PlayerPosition - { - get => PlayerScoreItem.ScorePosition; - set => PlayerScoreItem.ScorePosition = value; - } public bool CheckPositionByUsername(string username, int? estimatedPosition) { From dd6c9173da8b4d44f001cb057efa67c71e2d14a6 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Tue, 14 Apr 2020 18:42:00 +0300 Subject: [PATCH 015/818] Move DifficultyRecommender to OsuGameBase --- osu.Game/OsuGameBase.cs | 5 +++++ osu.Game/Screens/Select/SongSelect.cs | 5 +---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 5487bd9320..4b79e9f24c 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -33,6 +33,7 @@ using osu.Game.Resources; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Scoring; +using osu.Game.Screens.Select; using osu.Game.Skinning; using osuTK.Input; @@ -240,6 +241,10 @@ namespace osu.Game dependencies.Cache(previewTrackManager = new PreviewTrackManager()); Add(previewTrackManager); + DifficultyRecommender difficultyRecommender; + dependencies.Cache(difficultyRecommender = new DifficultyRecommender()); + Add(difficultyRecommender); + Ruleset.BindValueChanged(onRulesetChanged); } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index f164056ede..7f35011379 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -81,8 +81,6 @@ namespace osu.Game.Screens.Select protected BeatmapCarousel Carousel { get; private set; } - private DifficultyRecommender recommender; - private BeatmapInfoWedge beatmapInfoWedge; private DialogOverlay dialogOverlay; @@ -104,14 +102,13 @@ namespace osu.Game.Screens.Select private MusicController music { get; set; } [BackgroundDependencyLoader(true)] - private void load(AudioManager audio, DialogOverlay dialog, OsuColour colours, SkinManager skins, ScoreManager scores) + private void load(AudioManager audio, DialogOverlay dialog, OsuColour colours, SkinManager skins, ScoreManager scores, DifficultyRecommender recommender) { // initial value transfer is required for FilterControl (it uses our re-cached bindables in its async load for the initial filter). transferRulesetValue(); AddRangeInternal(new Drawable[] { - recommender = new DifficultyRecommender(), new ResetScrollContainer(() => Carousel.ScrollToSelected()) { RelativeSizeAxes = Axes.Y, From 00d1cf1ce2b693348633919d649a15388f955072 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Tue, 14 Apr 2020 18:42:18 +0300 Subject: [PATCH 016/818] Recommend from all rulesets --- .../Screens/Select/DifficultyRecommender.cs | 37 +++++++++++++++++-- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Select/DifficultyRecommender.cs b/osu.Game/Screens/Select/DifficultyRecommender.cs index 20cdca858a..76b1188298 100644 --- a/osu.Game/Screens/Select/DifficultyRecommender.cs +++ b/osu.Game/Screens/Select/DifficultyRecommender.cs @@ -44,16 +44,27 @@ namespace osu.Game.Screens.Select /// The recommended difficulty, or null if a recommendation could not be provided. public BeatmapInfo GetRecommendedBeatmap(IEnumerable beatmaps) { - if (recommendedStarDifficulty.TryGetValue(ruleset.Value, out var stars)) + if (!recommendedStarDifficulty.Any()) + return null; + + BeatmapInfo beatmap = null; + + foreach (var r in getBestRulesetOrder()) { - return beatmaps.OrderBy(b => + if (!recommendedStarDifficulty.TryGetValue(ruleset.Value, out var stars)) + break; + + beatmap = beatmaps.Where(b => b.Ruleset.Equals(r)).OrderBy(b => { var difference = b.StarDifficulty - stars; return difference >= 0 ? difference * 2 : difference * -1; // prefer easier over harder }).FirstOrDefault(); + + if (beatmap != null) + break; } - return null; + return beatmap; } private void calculateRecommendedDifficulties() @@ -72,6 +83,26 @@ namespace osu.Game.Screens.Select }); } + private IEnumerable bestRulesetOrder; + + private IEnumerable getBestRulesetOrder() + { + if (bestRulesetOrder != null) + return bestRulesetOrder; + + var otherRulesets = recommendedStarDifficulty.ToList() + .Where(pair => !pair.Key.Equals(ruleset.Value)) + .OrderBy(pair => pair.Value) + .Select(pair => pair.Key) + .Reverse(); + + var rulesetList = new List(new[] { ruleset.Value }); + rulesetList.AddRange(otherRulesets); + + bestRulesetOrder = rulesetList; + return rulesetList; + } + public void APIStateChanged(IAPIProvider api, APIState state) { switch (state) From bbef94b4df15fd3e1b2f0f51f5c16e2243920a45 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Tue, 14 Apr 2020 18:56:20 +0300 Subject: [PATCH 017/818] Reset best order on ruleset change --- osu.Game/Screens/Select/DifficultyRecommender.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Select/DifficultyRecommender.cs b/osu.Game/Screens/Select/DifficultyRecommender.cs index 76b1188298..bdc81ad066 100644 --- a/osu.Game/Screens/Select/DifficultyRecommender.cs +++ b/osu.Game/Screens/Select/DifficultyRecommender.cs @@ -32,6 +32,8 @@ namespace osu.Game.Screens.Select private void load() { api.Register(this); + + ruleset.ValueChanged += _ => bestRulesetOrder = null; } /// From 872551733f3b4c9e8fe4c7f21a4d0b8edf4ae3bb Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Tue, 14 Apr 2020 19:39:14 +0300 Subject: [PATCH 018/818] Present recommended beatmaps --- osu.Game/OsuGame.cs | 14 +++++++++----- osu.Game/OsuGameBase.cs | 7 ++++--- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 5e93d760e3..64fe0f6733 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -322,7 +322,7 @@ namespace osu.Game /// public void PresentBeatmap(BeatmapSetInfo beatmap, Predicate difficultyCriteria = null) { - difficultyCriteria ??= b => b.Ruleset.Equals(Ruleset.Value); + difficultyCriteria ??= _ => true; var databasedSet = beatmap.OnlineBeatmapSetID != null ? BeatmapManager.QueryBeatmapSet(s => s.OnlineBeatmapSetID == beatmap.OnlineBeatmapSetID) @@ -346,11 +346,15 @@ namespace osu.Game return; } - // Find first beatmap that matches our predicate. - var first = databasedSet.Beatmaps.Find(difficultyCriteria) ?? databasedSet.Beatmaps.First(); + // Find beatmaps that match our predicate. + var beatmaps = databasedSet.Beatmaps.Where(b => difficultyCriteria(b)); + if (!beatmaps.Any()) + beatmaps = databasedSet.Beatmaps; - Ruleset.Value = first.Ruleset; - Beatmap.Value = BeatmapManager.GetWorkingBeatmap(first); + var selection = DifficultyRecommender.GetRecommendedBeatmap(beatmaps); + + Ruleset.Value = selection.Ruleset; + Beatmap.Value = BeatmapManager.GetWorkingBeatmap(selection); }, validScreens: new[] { typeof(PlaySongSelect) }); } diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 4b79e9f24c..0c86017974 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -100,6 +100,9 @@ namespace osu.Game public bool IsDeployedBuild => AssemblyVersion.Major > 0; + [Cached] + protected readonly DifficultyRecommender DifficultyRecommender = new DifficultyRecommender(); + public virtual string Version { get @@ -241,9 +244,7 @@ namespace osu.Game dependencies.Cache(previewTrackManager = new PreviewTrackManager()); Add(previewTrackManager); - DifficultyRecommender difficultyRecommender; - dependencies.Cache(difficultyRecommender = new DifficultyRecommender()); - Add(difficultyRecommender); + Add(DifficultyRecommender); Ruleset.BindValueChanged(onRulesetChanged); } From 80949e89b934f0eaee7705da8e8f62f0dc47b6b3 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Tue, 14 Apr 2020 19:49:42 +0300 Subject: [PATCH 019/818] Offline fallback and commenting --- osu.Game/OsuGame.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 64fe0f6733..e64ca3ad87 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -316,8 +316,8 @@ namespace osu.Game /// /// The beatmap to select. /// - /// Optional predicate used to try and find a difficulty to select. - /// If omitted, this will try to present the first beatmap from the current ruleset. + /// Optional predicate used to filter which difficulties to select. + /// If omitted, this will try to present a recommended beatmap from the current ruleset. /// In case of failure the first difficulty of the set will be presented, ignoring the predicate. /// public void PresentBeatmap(BeatmapSetInfo beatmap, Predicate difficultyCriteria = null) @@ -351,7 +351,10 @@ namespace osu.Game if (!beatmaps.Any()) beatmaps = databasedSet.Beatmaps; - var selection = DifficultyRecommender.GetRecommendedBeatmap(beatmaps); + var selection = DifficultyRecommender.GetRecommendedBeatmap(beatmaps) ?? ( + // fallback if a difficulty can't be recommended, maybe we are offline + databasedSet.Beatmaps.Find(b => b.Ruleset.Equals(Ruleset.Value)) ?? databasedSet.Beatmaps.First() + ); Ruleset.Value = selection.Ruleset; Beatmap.Value = BeatmapManager.GetWorkingBeatmap(selection); From 58e122a7cb1c0b47365e1fe28c16f24d85ce0681 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Tue, 14 Apr 2020 19:56:41 +0300 Subject: [PATCH 020/818] Better fallback logic --- osu.Game/OsuGame.cs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index e64ca3ad87..fea89155f5 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -322,8 +322,6 @@ namespace osu.Game /// public void PresentBeatmap(BeatmapSetInfo beatmap, Predicate difficultyCriteria = null) { - difficultyCriteria ??= _ => true; - var databasedSet = beatmap.OnlineBeatmapSetID != null ? BeatmapManager.QueryBeatmapSet(s => s.OnlineBeatmapSetID == beatmap.OnlineBeatmapSetID) : BeatmapManager.QueryBeatmapSet(s => s.Hash == beatmap.Hash); @@ -347,14 +345,20 @@ namespace osu.Game } // Find beatmaps that match our predicate. - var beatmaps = databasedSet.Beatmaps.Where(b => difficultyCriteria(b)); + var beatmaps = databasedSet.Beatmaps.Where(b => difficultyCriteria?.Invoke(b) ?? true); if (!beatmaps.Any()) beatmaps = databasedSet.Beatmaps; - var selection = DifficultyRecommender.GetRecommendedBeatmap(beatmaps) ?? ( - // fallback if a difficulty can't be recommended, maybe we are offline - databasedSet.Beatmaps.Find(b => b.Ruleset.Equals(Ruleset.Value)) ?? databasedSet.Beatmaps.First() - ); + var selection = DifficultyRecommender.GetRecommendedBeatmap(beatmaps); + + // fallback if a difficulty can't be recommended, maybe we are offline + if (selection == null) + { + if (difficultyCriteria != null) + selection = beatmaps.First(); + else + selection = databasedSet.Beatmaps.Find(b => b.Ruleset.Equals(Ruleset.Value)) ?? databasedSet.Beatmaps.First(); + } Ruleset.Value = selection.Ruleset; Beatmap.Value = BeatmapManager.GetWorkingBeatmap(selection); From 47187ec14cbbcfe9ebb8157150cae30e1346c2aa Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Wed, 15 Apr 2020 18:04:23 +0300 Subject: [PATCH 021/818] Simplify recommended beatmap presenting --- osu.Game/OsuGame.cs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index fea89155f5..68bf9c822f 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -346,19 +346,18 @@ namespace osu.Game // Find beatmaps that match our predicate. var beatmaps = databasedSet.Beatmaps.Where(b => difficultyCriteria?.Invoke(b) ?? true); + + // Use all beatmaps if predicate matched nothing if (!beatmaps.Any()) beatmaps = databasedSet.Beatmaps; - var selection = DifficultyRecommender.GetRecommendedBeatmap(beatmaps); - - // fallback if a difficulty can't be recommended, maybe we are offline - if (selection == null) - { - if (difficultyCriteria != null) - selection = beatmaps.First(); - else - selection = databasedSet.Beatmaps.Find(b => b.Ruleset.Equals(Ruleset.Value)) ?? databasedSet.Beatmaps.First(); - } + // Try to select recommended beatmap + // This should give us a beatmap from current ruleset if there are any in our matched beatmaps + var selection = DifficultyRecommender.GetRecommendedBeatmap(beatmaps) ?? ( + // Fallback if a difficulty can't be recommended, maybe we are offline + // First try to find a beatmap in current ruleset, otherwise use first beatmap + beatmaps.FirstOrDefault(b => b.Ruleset.Equals(Ruleset.Value)) ?? beatmaps.First() + ); Ruleset.Value = selection.Ruleset; Beatmap.Value = BeatmapManager.GetWorkingBeatmap(selection); From b5c1752f0a40a731bdefc4c328c2b9cb05a953bf Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Wed, 15 Apr 2020 18:14:51 +0300 Subject: [PATCH 022/818] Calculate best ruleset order only once --- .../Screens/Select/DifficultyRecommender.cs | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/Select/DifficultyRecommender.cs b/osu.Game/Screens/Select/DifficultyRecommender.cs index bdc81ad066..e7536db356 100644 --- a/osu.Game/Screens/Select/DifficultyRecommender.cs +++ b/osu.Game/Screens/Select/DifficultyRecommender.cs @@ -32,8 +32,6 @@ namespace osu.Game.Screens.Select private void load() { api.Register(this); - - ruleset.ValueChanged += _ => bestRulesetOrder = null; } /// @@ -90,19 +88,22 @@ namespace osu.Game.Screens.Select private IEnumerable getBestRulesetOrder() { if (bestRulesetOrder != null) - return bestRulesetOrder; + return moveCurrentRulesetToFirst(); - var otherRulesets = recommendedStarDifficulty.ToList() - .Where(pair => !pair.Key.Equals(ruleset.Value)) - .OrderBy(pair => pair.Value) - .Select(pair => pair.Key) - .Reverse(); + bestRulesetOrder = recommendedStarDifficulty.ToList() + .OrderBy(pair => pair.Value) + .Select(pair => pair.Key) + .Reverse(); - var rulesetList = new List(new[] { ruleset.Value }); - rulesetList.AddRange(otherRulesets); + return moveCurrentRulesetToFirst(); + } - bestRulesetOrder = rulesetList; - return rulesetList; + private IEnumerable moveCurrentRulesetToFirst() + { + var orderedRulesets = bestRulesetOrder.ToList(); + orderedRulesets.Remove(ruleset.Value); + orderedRulesets.Insert(0, ruleset.Value); + return orderedRulesets; } public void APIStateChanged(IAPIProvider api, APIState state) From da9bd74e2eef2f08b85e6e8e15501f70299c4218 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Wed, 15 Apr 2020 20:19:17 +0300 Subject: [PATCH 023/818] Very basic testing --- .../TestSceneBeatmapRecommendations.cs | 130 ++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs new file mode 100644 index 0000000000..80a00ac9a1 --- /dev/null +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs @@ -0,0 +1,130 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Logging; +using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Osu; +using osu.Game.Screens.Select; +using osu.Game.Tests.Visual.Navigation; +using osu.Game.Users; + +namespace osu.Game.Tests.Visual.SongSelect +{ + public class TestSceneBeatmapRecommendations : OsuGameTestScene + { + [Resolved] + private DifficultyRecommender recommender { get; set; } + + [SetUpSteps] + public new void SetUpSteps() + { + AddStep("register request handling", () => + { + Logger.Log($"Registering request handling for {(DummyAPIAccess)API}"); + ((DummyAPIAccess)API).HandleRequest = req => + { + Logger.Log($"New request {req}"); + + switch (req) + { + case GetUserRequest userRequest: + userRequest.TriggerSuccess(new User + { + Username = @"Dummy", + Id = 1001, + Statistics = new UserStatistics + { + PP = 928 // Expected recommended star difficulty is 2.999 + } + }); + break; + } + }; + // Force recommender to calculate its star ratings again + recommender.APIStateChanged(API, APIState.Online); + }); + } + + [Test] + public void TestPresentedBeatmapIsRecommended() + { + var importFunctions = importBeatmaps(5); + + for (int i = 0; i < 5; i++) + { + presentAndConfirm(importFunctions[i], i); + } + } + + private List> importBeatmaps(int amount, RulesetInfo ruleset = null) + { + var importFunctions = new List>(); + + for (int i = 0; i < amount; i++) + { + importFunctions.Add(importBeatmap(i, ruleset)); + } + + return importFunctions; + } + + private Func importBeatmap(int i, RulesetInfo ruleset = null) + { + BeatmapSetInfo imported = null; + AddStep($"import beatmap {i * 1000}", () => + { + var difficulty = new BeatmapDifficulty(); + var metadata = new BeatmapMetadata + { + Artist = "SomeArtist", + AuthorString = "SomeAuthor", + Title = $"import {i * 1000}" + }; + + var beatmaps = new List(); + + for (int j = 1; j <= 5; j++) + { + beatmaps.Add(new BeatmapInfo + { + OnlineBeatmapID = j * 1024 + i * 5, + Metadata = metadata, + BaseDifficulty = difficulty, + Ruleset = ruleset ?? new OsuRuleset().RulesetInfo, + StarDifficulty = j, + }); + } + + imported = Game.BeatmapManager.Import(new BeatmapSetInfo + { + Hash = Guid.NewGuid().ToString(), + OnlineBeatmapSetID = i, + Metadata = metadata, + Beatmaps = beatmaps, + }).Result; + }); + + AddAssert($"import {i * 1000} succeeded", () => imported != null); + + return () => imported; + } + + private void presentAndConfirm(Func getImport, int importedID) + { + AddStep("present beatmap", () => Game.PresentBeatmap(getImport())); + + AddUntilStep("wait for song select", () => Game.ScreenStack.CurrentScreen is Screens.Select.SongSelect); + AddUntilStep("recommended beatmap displayed", () => Game.Beatmap.Value.BeatmapInfo.OnlineBeatmapID == importedID * 5 + 1024 * 3); + AddAssert("correct ruleset selected", () => Game.Ruleset.Value.ID == getImport().Beatmaps.First().Ruleset.ID); + } + } +} From 1ac9ee599088d893211bd6dbcdba16662275e745 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Fri, 17 Apr 2020 18:15:11 +0300 Subject: [PATCH 024/818] Optimize recommender (for custom rulesets) --- .../Screens/Select/DifficultyRecommender.cs | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/Select/DifficultyRecommender.cs b/osu.Game/Screens/Select/DifficultyRecommender.cs index e7536db356..07dfc3a85e 100644 --- a/osu.Game/Screens/Select/DifficultyRecommender.cs +++ b/osu.Game/Screens/Select/DifficultyRecommender.cs @@ -51,8 +51,7 @@ namespace osu.Game.Screens.Select foreach (var r in getBestRulesetOrder()) { - if (!recommendedStarDifficulty.TryGetValue(ruleset.Value, out var stars)) - break; + recommendedStarDifficulty.TryGetValue(ruleset.Value, out var stars); beatmap = beatmaps.Where(b => b.Ruleset.Equals(r)).OrderBy(b => { @@ -75,6 +74,7 @@ namespace osu.Game.Screens.Select req.Success += result => { + bestRulesetOrder = null; // algorithm taken from https://github.com/ppy/osu-web/blob/e6e2825516449e3d0f3f5e1852c6bdd3428c3437/app/Models/User.php#L1505 recommendedStarDifficulty[rulesetInfo] = Math.Pow((double)(result.Statistics.PP ?? 0), 0.4) * 0.195; }; @@ -87,22 +87,30 @@ namespace osu.Game.Screens.Select private IEnumerable getBestRulesetOrder() { - if (bestRulesetOrder != null) - return moveCurrentRulesetToFirst(); - - bestRulesetOrder = recommendedStarDifficulty.ToList() - .OrderBy(pair => pair.Value) - .Select(pair => pair.Key) - .Reverse(); + bestRulesetOrder ??= recommendedStarDifficulty.ToList() + .OrderBy(pair => pair.Value) + .Select(pair => pair.Key) + .Reverse(); return moveCurrentRulesetToFirst(); } private IEnumerable moveCurrentRulesetToFirst() { - var orderedRulesets = bestRulesetOrder.ToList(); - orderedRulesets.Remove(ruleset.Value); - orderedRulesets.Insert(0, ruleset.Value); + List orderedRulesets = null; + + if (bestRulesetOrder.Contains(ruleset.Value)) + { + orderedRulesets = bestRulesetOrder.ToList(); + orderedRulesets.Remove(ruleset.Value); + orderedRulesets.Insert(0, ruleset.Value); + } + else + { + orderedRulesets = new List { ruleset.Value }; + orderedRulesets.AddRange(bestRulesetOrder); + } + return orderedRulesets; } From 90fa58b3b65c7221990bfbc12237577a489c4741 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Fri, 17 Apr 2020 19:55:51 +0300 Subject: [PATCH 025/818] More testing --- .../TestSceneBeatmapRecommendations.cs | 96 +++++++++++++------ .../Online/API/Requests/GetUserRequest.cs | 6 +- 2 files changed, 70 insertions(+), 32 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs index 80a00ac9a1..ec5fe65fdd 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs @@ -6,44 +6,53 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; -using osu.Framework.Logging; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Rulesets; -using osu.Game.Rulesets.Osu; using osu.Game.Screens.Select; using osu.Game.Tests.Visual.Navigation; using osu.Game.Users; namespace osu.Game.Tests.Visual.SongSelect { + [HeadlessTest] public class TestSceneBeatmapRecommendations : OsuGameTestScene { [Resolved] private DifficultyRecommender recommender { get; set; } + [Resolved] + private RulesetStore rulesets { get; set; } + [SetUpSteps] public new void SetUpSteps() { AddStep("register request handling", () => { - Logger.Log($"Registering request handling for {(DummyAPIAccess)API}"); ((DummyAPIAccess)API).HandleRequest = req => { - Logger.Log($"New request {req}"); - switch (req) { case GetUserRequest userRequest: + + decimal pp = userRequest.Ruleset.ID switch + { + 0 => 336, // Expected recommended star difficulty 2* + 1 => 928, // Expected recommended star difficulty 3* + 2 => 1905, // Expected recommended star difficulty 4* + 3 => 3329, // Expected recommended star difficulty 5* + _ => 0 + }; + userRequest.TriggerSuccess(new User { Username = @"Dummy", Id = 1001, Statistics = new UserStatistics { - PP = 928 // Expected recommended star difficulty is 2.999 + PP = pp } }); break; @@ -57,74 +66,103 @@ namespace osu.Game.Tests.Visual.SongSelect [Test] public void TestPresentedBeatmapIsRecommended() { - var importFunctions = importBeatmaps(5); + var importFunctions = new List>(); for (int i = 0; i < 5; i++) { - presentAndConfirm(importFunctions[i], i); + importFunctions.Add(importBeatmap(i, new List { null, null, null, null, null })); } - } - private List> importBeatmaps(int amount, RulesetInfo ruleset = null) - { - var importFunctions = new List>(); - - for (int i = 0; i < amount; i++) + for (int i = 0; i < 5; i++) { - importFunctions.Add(importBeatmap(i, ruleset)); + presentAndConfirm(importFunctions[i], i, 2); } - - return importFunctions; } - private Func importBeatmap(int i, RulesetInfo ruleset = null) + [Test] + public void TestBestRulesetIsRecommended() + { + var osuRuleset = rulesets.AvailableRulesets.First(r => r.ID == 0); + var taikoRuleset = rulesets.AvailableRulesets.First(r => r.ID == 1); + var catchRuleset = rulesets.AvailableRulesets.First(r => r.ID == 2); + var maniaRuleset = rulesets.AvailableRulesets.First(r => r.ID == 3); + + var osuImport = importBeatmap(0, new List { osuRuleset }); + var mixedImport = importBeatmap(1, new List { taikoRuleset, catchRuleset, maniaRuleset }); + + // Make sure we are on standard ruleset + presentAndConfirm(osuImport, 0, 1); + + // Present mixed difficulty set, expect ruleset with highest star difficulty + presentAndConfirm(mixedImport, 1, 3); + } + + [Test] + public void TestSecondBestRulesetIsRecommended() + { + var osuRuleset = rulesets.AvailableRulesets.First(r => r.ID == 0); + var taikoRuleset = rulesets.AvailableRulesets.First(r => r.ID == 1); + var catchRuleset = rulesets.AvailableRulesets.First(r => r.ID == 2); + + var osuImport = importBeatmap(0, new List { osuRuleset }); + var mixedImport = importBeatmap(1, new List { taikoRuleset, catchRuleset, taikoRuleset }); + + // Make sure we are on standard ruleset + presentAndConfirm(osuImport, 0, 1); + + // Present mixed difficulty set, expect ruleset with highest star difficulty + presentAndConfirm(mixedImport, 1, 2); + } + + private Func importBeatmap(int importID, List rulesets) { BeatmapSetInfo imported = null; - AddStep($"import beatmap {i * 1000}", () => + AddStep($"import beatmap {importID}", () => { var difficulty = new BeatmapDifficulty(); var metadata = new BeatmapMetadata { Artist = "SomeArtist", AuthorString = "SomeAuthor", - Title = $"import {i * 1000}" + Title = $"import {importID}" }; var beatmaps = new List(); + int difficultyID = 1; - for (int j = 1; j <= 5; j++) + foreach (RulesetInfo r in rulesets) { beatmaps.Add(new BeatmapInfo { - OnlineBeatmapID = j * 1024 + i * 5, + OnlineBeatmapID = importID + 1024 * difficultyID, Metadata = metadata, BaseDifficulty = difficulty, - Ruleset = ruleset ?? new OsuRuleset().RulesetInfo, - StarDifficulty = j, + Ruleset = r ?? rulesets.First(), + StarDifficulty = difficultyID, }); + difficultyID++; } imported = Game.BeatmapManager.Import(new BeatmapSetInfo { Hash = Guid.NewGuid().ToString(), - OnlineBeatmapSetID = i, + OnlineBeatmapSetID = importID, Metadata = metadata, Beatmaps = beatmaps, }).Result; }); - AddAssert($"import {i * 1000} succeeded", () => imported != null); + AddAssert($"import {importID} succeeded", () => imported != null); return () => imported; } - private void presentAndConfirm(Func getImport, int importedID) + private void presentAndConfirm(Func getImport, int importedID, int expextedDiff) { AddStep("present beatmap", () => Game.PresentBeatmap(getImport())); AddUntilStep("wait for song select", () => Game.ScreenStack.CurrentScreen is Screens.Select.SongSelect); - AddUntilStep("recommended beatmap displayed", () => Game.Beatmap.Value.BeatmapInfo.OnlineBeatmapID == importedID * 5 + 1024 * 3); - AddAssert("correct ruleset selected", () => Game.Ruleset.Value.ID == getImport().Beatmaps.First().Ruleset.ID); + AddUntilStep("recommended beatmap displayed", () => Game.Beatmap.Value.BeatmapInfo.OnlineBeatmapID == importedID + 1024 * expextedDiff); } } } diff --git a/osu.Game/Online/API/Requests/GetUserRequest.cs b/osu.Game/Online/API/Requests/GetUserRequest.cs index 31b7e95b39..42aad6f9eb 100644 --- a/osu.Game/Online/API/Requests/GetUserRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserRequest.cs @@ -9,14 +9,14 @@ namespace osu.Game.Online.API.Requests public class GetUserRequest : APIRequest { private readonly long? userId; - private readonly RulesetInfo ruleset; + public readonly RulesetInfo Ruleset; public GetUserRequest(long? userId = null, RulesetInfo ruleset = null) { this.userId = userId; - this.ruleset = ruleset; + Ruleset = ruleset; } - protected override string Target => userId.HasValue ? $@"users/{userId}/{ruleset?.ShortName}" : $@"me/{ruleset?.ShortName}"; + protected override string Target => userId.HasValue ? $@"users/{userId}/{Ruleset?.ShortName}" : $@"me/{Ruleset?.ShortName}"; } } From 4aaa00e3219611c387dcdf20dc4f04103bb7af07 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Fri, 17 Apr 2020 20:33:12 +0300 Subject: [PATCH 026/818] Fix CI complaints --- osu.Game/Screens/Select/DifficultyRecommender.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/DifficultyRecommender.cs b/osu.Game/Screens/Select/DifficultyRecommender.cs index 07dfc3a85e..4d48cc3fe7 100644 --- a/osu.Game/Screens/Select/DifficultyRecommender.cs +++ b/osu.Game/Screens/Select/DifficultyRecommender.cs @@ -97,7 +97,7 @@ namespace osu.Game.Screens.Select private IEnumerable moveCurrentRulesetToFirst() { - List orderedRulesets = null; + List orderedRulesets; if (bestRulesetOrder.Contains(ruleset.Value)) { From ce47b7ca932f85b2ac323b0c220da2eaa88dd3ef Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Sat, 25 Apr 2020 10:21:01 +0300 Subject: [PATCH 027/818] Unnest in SetUpSteps --- .../TestSceneBeatmapRecommendations.cs | 59 ++++++++++++------- 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs index ec5fe65fdd..3f2117a4f8 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs @@ -36,31 +36,48 @@ namespace osu.Game.Tests.Visual.SongSelect switch (req) { case GetUserRequest userRequest: - - decimal pp = userRequest.Ruleset.ID switch - { - 0 => 336, // Expected recommended star difficulty 2* - 1 => 928, // Expected recommended star difficulty 3* - 2 => 1905, // Expected recommended star difficulty 4* - 3 => 3329, // Expected recommended star difficulty 5* - _ => 0 - }; - - userRequest.TriggerSuccess(new User - { - Username = @"Dummy", - Id = 1001, - Statistics = new UserStatistics - { - PP = pp - } - }); + userRequest.TriggerSuccess(getUser(userRequest.Ruleset.ID)); break; } }; - // Force recommender to calculate its star ratings again - recommender.APIStateChanged(API, APIState.Online); }); + + // Force recommender to calculate its star ratings again + AddStep("calculate recommended SRs", () => recommender.APIStateChanged(API, APIState.Online)); + + User getUser(int? rulesetID) + { + return new User + { + Username = @"Dummy", + Id = 1001, + Statistics = new UserStatistics + { + PP = getNecessaryPP(rulesetID) + } + }; + } + + decimal getNecessaryPP(int? rulesetID) + { + switch (rulesetID) + { + case 0: + return 336; + + case 1: + return 928; + + case 2: + return 1905; + + case 3: + return 3329; + + default: + return 0; + } + } } [Test] From 52416ea90a49f2632a2173ab1c55ca458883b4bc Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Sat, 25 Apr 2020 10:22:19 +0300 Subject: [PATCH 028/818] Use GetRuleset --- .../SongSelect/TestSceneBeatmapRecommendations.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs index 3f2117a4f8..aed1729d7d 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs @@ -99,10 +99,10 @@ namespace osu.Game.Tests.Visual.SongSelect [Test] public void TestBestRulesetIsRecommended() { - var osuRuleset = rulesets.AvailableRulesets.First(r => r.ID == 0); - var taikoRuleset = rulesets.AvailableRulesets.First(r => r.ID == 1); - var catchRuleset = rulesets.AvailableRulesets.First(r => r.ID == 2); - var maniaRuleset = rulesets.AvailableRulesets.First(r => r.ID == 3); + var osuRuleset = rulesets.GetRuleset(0); + var taikoRuleset = rulesets.GetRuleset(1); + var catchRuleset = rulesets.GetRuleset(2); + var maniaRuleset = rulesets.GetRuleset(3); var osuImport = importBeatmap(0, new List { osuRuleset }); var mixedImport = importBeatmap(1, new List { taikoRuleset, catchRuleset, maniaRuleset }); @@ -117,9 +117,9 @@ namespace osu.Game.Tests.Visual.SongSelect [Test] public void TestSecondBestRulesetIsRecommended() { - var osuRuleset = rulesets.AvailableRulesets.First(r => r.ID == 0); - var taikoRuleset = rulesets.AvailableRulesets.First(r => r.ID == 1); - var catchRuleset = rulesets.AvailableRulesets.First(r => r.ID == 2); + var osuRuleset = rulesets.GetRuleset(0); + var taikoRuleset = rulesets.GetRuleset(1); + var catchRuleset = rulesets.GetRuleset(2); var osuImport = importBeatmap(0, new List { osuRuleset }); var mixedImport = importBeatmap(1, new List { taikoRuleset, catchRuleset, taikoRuleset }); From e906ec4d92d8d75467684630a80c606053ff7484 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Sat, 25 Apr 2020 10:25:07 +0300 Subject: [PATCH 029/818] Fix typo --- .../Visual/SongSelect/TestSceneBeatmapRecommendations.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs index aed1729d7d..f3a118572f 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs @@ -174,12 +174,12 @@ namespace osu.Game.Tests.Visual.SongSelect return () => imported; } - private void presentAndConfirm(Func getImport, int importedID, int expextedDiff) + private void presentAndConfirm(Func getImport, int importedID, int expectedDiff) { AddStep("present beatmap", () => Game.PresentBeatmap(getImport())); AddUntilStep("wait for song select", () => Game.ScreenStack.CurrentScreen is Screens.Select.SongSelect); - AddUntilStep("recommended beatmap displayed", () => Game.Beatmap.Value.BeatmapInfo.OnlineBeatmapID == importedID + 1024 * expextedDiff); + AddUntilStep("recommended beatmap displayed", () => Game.Beatmap.Value.BeatmapInfo.OnlineBeatmapID == importedID + 1024 * expectedDiff); } } } From e65acc34018df7f77594ae8fff8c369a62a01ef8 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Sat, 25 Apr 2020 10:36:19 +0300 Subject: [PATCH 030/818] Other review suggestions --- .../TestSceneBeatmapRecommendations.cs | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs index f3a118572f..f49dae4033 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs @@ -87,12 +87,12 @@ namespace osu.Game.Tests.Visual.SongSelect for (int i = 0; i < 5; i++) { - importFunctions.Add(importBeatmap(i, new List { null, null, null, null, null })); + importFunctions.Add(importBeatmap(i, Enumerable.Repeat(rulesets.GetRuleset(0), 5))); } for (int i = 0; i < 5; i++) { - presentAndConfirm(importFunctions[i], i, 2); + presentAndConfirm(importFunctions[i], 2); } } @@ -108,10 +108,10 @@ namespace osu.Game.Tests.Visual.SongSelect var mixedImport = importBeatmap(1, new List { taikoRuleset, catchRuleset, maniaRuleset }); // Make sure we are on standard ruleset - presentAndConfirm(osuImport, 0, 1); + presentAndConfirm(osuImport, 1); // Present mixed difficulty set, expect ruleset with highest star difficulty - presentAndConfirm(mixedImport, 1, 3); + presentAndConfirm(mixedImport, 3); } [Test] @@ -125,13 +125,13 @@ namespace osu.Game.Tests.Visual.SongSelect var mixedImport = importBeatmap(1, new List { taikoRuleset, catchRuleset, taikoRuleset }); // Make sure we are on standard ruleset - presentAndConfirm(osuImport, 0, 1); + presentAndConfirm(osuImport, 1); // Present mixed difficulty set, expect ruleset with highest star difficulty - presentAndConfirm(mixedImport, 1, 2); + presentAndConfirm(mixedImport, 2); } - private Func importBeatmap(int importID, List rulesets) + private Func importBeatmap(int importID, IEnumerable rulesetEnumerable) { BeatmapSetInfo imported = null; AddStep($"import beatmap {importID}", () => @@ -147,14 +147,14 @@ namespace osu.Game.Tests.Visual.SongSelect var beatmaps = new List(); int difficultyID = 1; - foreach (RulesetInfo r in rulesets) + foreach (RulesetInfo r in rulesetEnumerable) { beatmaps.Add(new BeatmapInfo { OnlineBeatmapID = importID + 1024 * difficultyID, Metadata = metadata, BaseDifficulty = difficulty, - Ruleset = r ?? rulesets.First(), + Ruleset = r ?? rulesets.AvailableRulesets.First(), StarDifficulty = difficultyID, }); difficultyID++; @@ -174,12 +174,16 @@ namespace osu.Game.Tests.Visual.SongSelect return () => imported; } - private void presentAndConfirm(Func getImport, int importedID, int expectedDiff) + private void presentAndConfirm(Func getImport, int expectedDiff) { AddStep("present beatmap", () => Game.PresentBeatmap(getImport())); AddUntilStep("wait for song select", () => Game.ScreenStack.CurrentScreen is Screens.Select.SongSelect); - AddUntilStep("recommended beatmap displayed", () => Game.Beatmap.Value.BeatmapInfo.OnlineBeatmapID == importedID + 1024 * expectedDiff); + AddUntilStep("recommended beatmap displayed", () => + { + int? expectedID = getImport().Beatmaps[expectedDiff - 1].OnlineBeatmapID; + return Game.Beatmap.Value.BeatmapInfo.OnlineBeatmapID == expectedID; + }); } } } From f68a7401b9577ca357ac931129133306b632a159 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Sat, 25 Apr 2020 10:37:18 +0300 Subject: [PATCH 031/818] Fix comment --- .../Visual/SongSelect/TestSceneBeatmapRecommendations.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs index f49dae4033..a6e3e0c1c6 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs @@ -127,7 +127,7 @@ namespace osu.Game.Tests.Visual.SongSelect // Make sure we are on standard ruleset presentAndConfirm(osuImport, 1); - // Present mixed difficulty set, expect ruleset with highest star difficulty + // Present mixed difficulty set, expect ruleset with second highest star difficulty presentAndConfirm(mixedImport, 2); } From cea582992fa9075899d36d0a4792c7fe13db4bce Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Sat, 25 Apr 2020 10:47:12 +0300 Subject: [PATCH 032/818] Fix early return check --- osu.Game/OsuGame.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index eb86ef4116..9707475cc7 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -339,7 +339,7 @@ namespace osu.Game menuScreen.LoadToSolo(); // we might even already be at the song - if (Beatmap.Value.BeatmapSetInfo.Hash == databasedSet.Hash && difficultyCriteria(Beatmap.Value.BeatmapInfo)) + if (Beatmap.Value.BeatmapSetInfo.Hash == databasedSet.Hash && (difficultyCriteria?.Invoke(Beatmap.Value.BeatmapInfo) ?? true)) { return; } From 16f53991a898e4b7aae75c8bbfc4935b6876cf57 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Sat, 25 Apr 2020 10:50:00 +0300 Subject: [PATCH 033/818] Test presenting same beatmap more than once --- osu.Game.Tests/Visual/Navigation/TestScenePresentBeatmap.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePresentBeatmap.cs b/osu.Game.Tests/Visual/Navigation/TestScenePresentBeatmap.cs index 27f5b29738..eb73fded2f 100644 --- a/osu.Game.Tests/Visual/Navigation/TestScenePresentBeatmap.cs +++ b/osu.Game.Tests/Visual/Navigation/TestScenePresentBeatmap.cs @@ -55,8 +55,14 @@ namespace osu.Game.Tests.Visual.Navigation var secondimport = importBeatmap(3); presentAndConfirm(secondimport); + // Test presenting same beatmap more than once + presentAndConfirm(secondimport); + presentSecondDifficultyAndConfirm(firstImport, 1); presentSecondDifficultyAndConfirm(secondimport, 3); + + // Test presenting same beatmap more than once + presentSecondDifficultyAndConfirm(secondimport, 3); } [Test] From 2913a8183538f823858aace50834f9e38a74b726 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 1 May 2020 16:59:45 +0200 Subject: [PATCH 034/818] Improve test code quality & safety --- .../Visual/Navigation/OsuGameTestScene.cs | 2 +- .../TestSceneBeatmapRecommendations.cs | 122 +++++++++--------- 2 files changed, 60 insertions(+), 64 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs b/osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs index 31afce86ae..a3ef33b916 100644 --- a/osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs +++ b/osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs @@ -46,7 +46,7 @@ namespace osu.Game.Tests.Visual.Navigation } [SetUpSteps] - public void SetUpSteps() + public virtual void SetUpSteps() { AddStep("Create new game instance", () => { diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs index a6e3e0c1c6..5fb4e80b51 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs @@ -11,23 +11,25 @@ using osu.Game.Beatmaps; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Rulesets; +using osu.Game.Rulesets.Catch; +using osu.Game.Rulesets.Mania; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Taiko; using osu.Game.Screens.Select; using osu.Game.Tests.Visual.Navigation; using osu.Game.Users; namespace osu.Game.Tests.Visual.SongSelect { - [HeadlessTest] public class TestSceneBeatmapRecommendations : OsuGameTestScene { + protected override bool UseOnlineAPI => false; + [Resolved] private DifficultyRecommender recommender { get; set; } - [Resolved] - private RulesetStore rulesets { get; set; } - [SetUpSteps] - public new void SetUpSteps() + public override void SetUpSteps() { AddStep("register request handling", () => { @@ -42,6 +44,8 @@ namespace osu.Game.Tests.Visual.SongSelect }; }); + base.SetUpSteps(); + // Force recommender to calculate its star ratings again AddStep("calculate recommended SRs", () => recommender.APIStateChanged(API, APIState.Online)); @@ -83,97 +87,89 @@ namespace osu.Game.Tests.Visual.SongSelect [Test] public void TestPresentedBeatmapIsRecommended() { - var importFunctions = new List>(); + List beatmapSets = null; + const int import_count = 5; - for (int i = 0; i < 5; i++) + AddStep("import 5 maps", () => { - importFunctions.Add(importBeatmap(i, Enumerable.Repeat(rulesets.GetRuleset(0), 5))); - } + beatmapSets = new List(); - for (int i = 0; i < 5; i++) - { - presentAndConfirm(importFunctions[i], 2); - } + for (int i = 0; i < import_count; ++i) + { + beatmapSets.Add(importBeatmapSet(i, Enumerable.Repeat(new OsuRuleset().RulesetInfo, 5))); + } + }); + + AddAssert("all sets imported", () => ensureAllBeatmapSetsImported(beatmapSets)); + + presentAndConfirm(() => beatmapSets[3], 2); } [Test] public void TestBestRulesetIsRecommended() { - var osuRuleset = rulesets.GetRuleset(0); - var taikoRuleset = rulesets.GetRuleset(1); - var catchRuleset = rulesets.GetRuleset(2); - var maniaRuleset = rulesets.GetRuleset(3); + BeatmapSetInfo osuSet = null, mixedSet = null; - var osuImport = importBeatmap(0, new List { osuRuleset }); - var mixedImport = importBeatmap(1, new List { taikoRuleset, catchRuleset, maniaRuleset }); + AddStep("create osu! beatmapset", () => osuSet = importBeatmapSet(0, new[] { new OsuRuleset().RulesetInfo })); + AddStep("create mixed beatmapset", () => mixedSet = importBeatmapSet(1, + new[] { new TaikoRuleset().RulesetInfo, new CatchRuleset().RulesetInfo, new ManiaRuleset().RulesetInfo })); + + AddAssert("all sets imported", () => ensureAllBeatmapSetsImported(new[] { osuSet, mixedSet })); // Make sure we are on standard ruleset - presentAndConfirm(osuImport, 1); + presentAndConfirm(() => osuSet, 1); // Present mixed difficulty set, expect ruleset with highest star difficulty - presentAndConfirm(mixedImport, 3); + presentAndConfirm(() => mixedSet, 3); } [Test] public void TestSecondBestRulesetIsRecommended() { - var osuRuleset = rulesets.GetRuleset(0); - var taikoRuleset = rulesets.GetRuleset(1); - var catchRuleset = rulesets.GetRuleset(2); + BeatmapSetInfo osuSet = null, mixedSet = null; - var osuImport = importBeatmap(0, new List { osuRuleset }); - var mixedImport = importBeatmap(1, new List { taikoRuleset, catchRuleset, taikoRuleset }); + AddStep("create osu! beatmapset", () => osuSet = importBeatmapSet(0, new[] { new OsuRuleset().RulesetInfo })); + AddStep("create mixed beatmapset", () => mixedSet = importBeatmapSet(1, + new[] { new TaikoRuleset().RulesetInfo, new CatchRuleset().RulesetInfo, new TaikoRuleset().RulesetInfo })); + + AddAssert("all sets imported", () => ensureAllBeatmapSetsImported(new[] { osuSet, mixedSet })); // Make sure we are on standard ruleset - presentAndConfirm(osuImport, 1); + presentAndConfirm(() => osuSet, 1); // Present mixed difficulty set, expect ruleset with second highest star difficulty - presentAndConfirm(mixedImport, 2); + presentAndConfirm(() => mixedSet, 2); } - private Func importBeatmap(int importID, IEnumerable rulesetEnumerable) + private BeatmapSetInfo importBeatmapSet(int importID, IEnumerable difficultyRulesets) { - BeatmapSetInfo imported = null; - AddStep($"import beatmap {importID}", () => + var metadata = new BeatmapMetadata { - var difficulty = new BeatmapDifficulty(); - var metadata = new BeatmapMetadata - { - Artist = "SomeArtist", - AuthorString = "SomeAuthor", - Title = $"import {importID}" - }; + Artist = "SomeArtist", + AuthorString = "SomeAuthor", + Title = $"import {importID}" + }; - var beatmaps = new List(); - int difficultyID = 1; - - foreach (RulesetInfo r in rulesetEnumerable) + var beatmapSet = new BeatmapSetInfo + { + Hash = Guid.NewGuid().ToString(), + OnlineBeatmapSetID = importID, + Metadata = metadata, + Beatmaps = difficultyRulesets.Select((ruleset, difficultyIndex) => new BeatmapInfo { - beatmaps.Add(new BeatmapInfo - { - OnlineBeatmapID = importID + 1024 * difficultyID, - Metadata = metadata, - BaseDifficulty = difficulty, - Ruleset = r ?? rulesets.AvailableRulesets.First(), - StarDifficulty = difficultyID, - }); - difficultyID++; - } - - imported = Game.BeatmapManager.Import(new BeatmapSetInfo - { - Hash = Guid.NewGuid().ToString(), - OnlineBeatmapSetID = importID, + OnlineBeatmapID = importID * 1024 + difficultyIndex, Metadata = metadata, - Beatmaps = beatmaps, - }).Result; - }); + BaseDifficulty = new BeatmapDifficulty(), + Ruleset = ruleset, + StarDifficulty = difficultyIndex + 1 + }).ToList() + }; - AddAssert($"import {importID} succeeded", () => imported != null); - - return () => imported; + return Game.BeatmapManager.Import(beatmapSet).Result; } + private bool ensureAllBeatmapSetsImported(IEnumerable beatmapSets) => beatmapSets.All(set => set != null); + private void presentAndConfirm(Func getImport, int expectedDiff) { AddStep("present beatmap", () => Game.PresentBeatmap(getImport())); From 9f091f3a5635cdca80c15511a7e888c70f0536f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 1 May 2020 17:02:28 +0200 Subject: [PATCH 035/818] Do not query API for custom rulesets --- osu.Game/Screens/Select/DifficultyRecommender.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/DifficultyRecommender.cs b/osu.Game/Screens/Select/DifficultyRecommender.cs index 4d48cc3fe7..e9c7f6c464 100644 --- a/osu.Game/Screens/Select/DifficultyRecommender.cs +++ b/osu.Game/Screens/Select/DifficultyRecommender.cs @@ -68,7 +68,8 @@ namespace osu.Game.Screens.Select private void calculateRecommendedDifficulties() { - rulesets.AvailableRulesets.ForEach(rulesetInfo => + // only query API for built-in rulesets + rulesets.AvailableRulesets.Where(ruleset => ruleset.ID <= 3).ForEach(rulesetInfo => { var req = new GetUserRequest(api.LocalUser.Value.Id, rulesetInfo); From 623611d9dc03e210f75615a260cde554aaef2a3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 1 May 2020 17:15:35 +0200 Subject: [PATCH 036/818] Simplify ruleset ordering --- osu.Game/Screens/Select/DifficultyRecommender.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Select/DifficultyRecommender.cs b/osu.Game/Screens/Select/DifficultyRecommender.cs index e9c7f6c464..0dc4ff95ca 100644 --- a/osu.Game/Screens/Select/DifficultyRecommender.cs +++ b/osu.Game/Screens/Select/DifficultyRecommender.cs @@ -88,10 +88,9 @@ namespace osu.Game.Screens.Select private IEnumerable getBestRulesetOrder() { - bestRulesetOrder ??= recommendedStarDifficulty.ToList() - .OrderBy(pair => pair.Value) + bestRulesetOrder ??= recommendedStarDifficulty.OrderByDescending(pair => pair.Value) .Select(pair => pair.Key) - .Reverse(); + .ToList(); return moveCurrentRulesetToFirst(); } From 3cf60e6e00483b9fd39c001073e29517f2e2ed9e Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Fri, 1 May 2020 19:08:56 +0300 Subject: [PATCH 037/818] Add failing test --- .../TestSceneBeatmapRecommendations.cs | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs index 5fb4e80b51..68f31c5c73 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs @@ -141,6 +141,23 @@ namespace osu.Game.Tests.Visual.SongSelect presentAndConfirm(() => mixedSet, 2); } + [Test] + public void TestCorrectStarRatingIsUsed() + { + BeatmapSetInfo osuSet = null, maniaSet = null; + + AddStep("create osu! beatmapset", () => osuSet = importBeatmapSet(0, new[] { new OsuRuleset().RulesetInfo })); + AddStep("create mania beatmapset", () => maniaSet = importBeatmapSet(1, Enumerable.Repeat(new ManiaRuleset().RulesetInfo, 10))); + + AddAssert("all sets imported", () => ensureAllBeatmapSetsImported(new[] { osuSet, maniaSet })); + + // Make sure we are on standard ruleset + presentAndConfirm(() => osuSet, 1); + + // Present mania set, expect the difficulty that matches recommended mania star rating + presentAndConfirm(() => maniaSet, 5); + } + private BeatmapSetInfo importBeatmapSet(int importID, IEnumerable difficultyRulesets) { var metadata = new BeatmapMetadata @@ -161,7 +178,8 @@ namespace osu.Game.Tests.Visual.SongSelect Metadata = metadata, BaseDifficulty = new BeatmapDifficulty(), Ruleset = ruleset, - StarDifficulty = difficultyIndex + 1 + StarDifficulty = difficultyIndex + 1, + Version = $"SR{difficultyIndex + 1}" }).ToList() }; From 1c04d58d6e55f2a09a237c53a4a2b700ecf0d74d Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Fri, 1 May 2020 19:44:35 +0300 Subject: [PATCH 038/818] Fix recommender's incorrect usage of current ruleset --- osu.Game/Screens/Select/DifficultyRecommender.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/DifficultyRecommender.cs b/osu.Game/Screens/Select/DifficultyRecommender.cs index 0dc4ff95ca..0753bbc5bd 100644 --- a/osu.Game/Screens/Select/DifficultyRecommender.cs +++ b/osu.Game/Screens/Select/DifficultyRecommender.cs @@ -51,7 +51,7 @@ namespace osu.Game.Screens.Select foreach (var r in getBestRulesetOrder()) { - recommendedStarDifficulty.TryGetValue(ruleset.Value, out var stars); + recommendedStarDifficulty.TryGetValue(r, out var stars); beatmap = beatmaps.Where(b => b.Ruleset.Equals(r)).OrderBy(b => { From d30e4061cce9ef69ffeb08b342a9f77109b8935e Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Fri, 1 May 2020 19:46:49 +0300 Subject: [PATCH 039/818] Add clarifying comment about pp choice --- .../Visual/SongSelect/TestSceneBeatmapRecommendations.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs index 68f31c5c73..fc14af3ab5 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs @@ -67,16 +67,16 @@ namespace osu.Game.Tests.Visual.SongSelect switch (rulesetID) { case 0: - return 336; + return 336; // recommended star rating of 2 case 1: - return 928; + return 928; // SR 3 case 2: - return 1905; + return 1905; // SR 4 case 3: - return 3329; + return 3329; // SR 5 default: return 0; From 809338a28002c1bed4223b890560dd50662f0f37 Mon Sep 17 00:00:00 2001 From: PercyDan54 <50285552+PercyDan54@users.noreply.github.com> Date: Tue, 17 Nov 2020 20:19:12 +0800 Subject: [PATCH 040/818] Init Import screen --- osu.Game/Screens/Import/FileImportScreen.cs | 318 ++++++++++++++++++++ osu.Game/Screens/Menu/ButtonSystem.cs | 2 + osu.Game/Screens/Menu/MainMenu.cs | 4 +- 3 files changed, 323 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Screens/Import/FileImportScreen.cs diff --git a/osu.Game/Screens/Import/FileImportScreen.cs b/osu.Game/Screens/Import/FileImportScreen.cs new file mode 100644 index 0000000000..669c26101c --- /dev/null +++ b/osu.Game/Screens/Import/FileImportScreen.cs @@ -0,0 +1,318 @@ +using System.IO; +using System.Threading.Tasks; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Platform; +using osu.Framework.Screens; +using osu.Game.Graphics; +using osu.Game.Graphics.UserInterfaceV2; +using osuTK; +using osu.Game.Overlays.Settings; +using osu.Game.Configuration; +using osu.Game.Overlays; +using osu.Game.Graphics.UserInterface; +using osu.Game.Graphics.Containers; +using osuTK.Graphics; + +namespace osu.Game.Screens.Import +{ + public class FileImportScreen : OsuScreen + { + private Container contentContainer; + private FileSelector fileSelector; + private Container fileSelectContainer; + + public override bool HideOverlaysOnEnter => true; + + private string[] fileExtensions = { ".foo" }; + private string defaultPath; + + private readonly Bindable currentFile = new Bindable(); + private readonly IBindable currentDirectory = new Bindable(); + private readonly Bindable filterType = new Bindable(FileFilterType.All); + private TextFlowContainer currentFileText; + private OsuScrollContainer fileNameScroll; + private readonly OverlayColourProvider overlayColourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); + + [Resolved] + private OsuGameBase gameBase { get; set; } + + [Resolved] + private DialogOverlay dialogOverlay { get; set; } + + [BackgroundDependencyLoader(true)] + private void load(Storage storage) + { + storage.GetStorageForDirectory("imports"); + var originalPath = storage.GetFullPath("imports", true); + + defaultPath = originalPath; + + InternalChild = contentContainer = new Container + { + Masking = true, + CornerRadius = 10, + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(0.9f, 0.8f), + Children = new Drawable[] + { + new Box + { + Colour = overlayColourProvider.Background5, + RelativeSizeAxes = Axes.Both, + }, + fileSelectContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Width = 0.65f, + Anchor = Anchor.TopLeft, + Origin = Anchor.TopLeft, + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Width = 0.35f, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Masking = true, + CornerRadius = 10, + Children = new Drawable[] + { + new GridContainer + { + RelativeSizeAxes = Axes.Both, + RowDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.AutoSize), + }, + Content = new[] + { + new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Children = new Drawable[] + { + new Box + { + Colour = overlayColourProvider.Background3, + RelativeSizeAxes = Axes.Both + }, + fileNameScroll = new OsuScrollContainer + { + Masking = false, + RelativeSizeAxes = Axes.Both, + Child = currentFileText = new TextFlowContainer(t => t.Font = OsuFont.Default.With(size: 30)) + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + TextAnchor = Anchor.Centre + }, + }, + } + }, + }, + new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new Box + { + Colour = overlayColourProvider.Background4, + RelativeSizeAxes = Axes.Both + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(10), + Children = new Drawable[] + { + new SettingsEnumDropdown + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + LabelText = "File Type", + Current = filterType, + Margin = new MarginPadding { Bottom = 15 } + }, + new GridContainer + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Margin = new MarginPadding { Top = 15 }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize) + }, + Content = new[] + { + new Drawable[] + { + new TriangleButton + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + RelativeSizeAxes = Axes.X, + Height = 50, + Width = 0.9f, + Text = "Refresh", + Action = refresh + }, + new TriangleButton + { + Text = "Import", + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + RelativeSizeAxes = Axes.X, + Height = 50, + Width = 0.9f, + Action = () => + { + var d = currentFile.Value?.FullName; + if (d != null) + startImport(d); + else + currentFileText.FlashColour(Color4.Red, 500); + }, + } + }, + } + }, + } + }, + } + } + } + } + }, + } + } + } + }; + + fileNameScroll.ScrollContent.Anchor = Anchor.Centre; + fileNameScroll.ScrollContent.Origin = Anchor.Centre; + + currentFile.BindValueChanged(updateFileSelectionText, true); + currentDirectory.BindValueChanged(_ => + { + currentFile.Value = null; + }); + + filterType.BindValueChanged(onFilterTypeChanged, true); + } + + private void onFilterTypeChanged(ValueChangedEvent v) + { + switch (v.NewValue) + { + case FileFilterType.Beatmap: + fileExtensions = new string[] { ".osz" }; + break; + + case FileFilterType.Skin: + fileExtensions = new string[] { ".osk" }; + break; + + case FileFilterType.Replay: + fileExtensions = new string[] { ".osr" }; + break; + + default: + case FileFilterType.All: + fileExtensions = new string[] { ".osk", ".osr", ".osz" }; + break; + } + + refresh(); + } + + private void refresh() + { + currentFile.UnbindBindings(); + currentDirectory.UnbindBindings(); + + fileSelector?.Expire(); + + var directory = currentDirectory.Value?.FullName ?? defaultPath; + fileSelector = new FileSelector(initialPath: directory, validFileExtensions: fileExtensions) + { + RelativeSizeAxes = Axes.Both + }; + + currentDirectory.BindTo(fileSelector.CurrentPath); + currentFile.BindTo(fileSelector.CurrentFile); + + fileSelectContainer.Add(fileSelector); + } + + private void updateFileSelectionText(ValueChangedEvent v) + { + currentFileText.Text = v.NewValue?.Name ?? "Select a file"; + } + + public override void OnEntering(IScreen last) + { + base.OnEntering(last); + + contentContainer.FadeOut().Then().ScaleTo(0.8f).RotateTo(-15).MoveToX(300) + .Then() + .ScaleTo(1, 1500, Easing.OutElastic) + .FadeIn(500) + .MoveToX(0, 500, Easing.OutQuint) + .RotateTo(0, 500, Easing.OutQuint); + } + + public override bool OnExiting(IScreen next) + { + contentContainer.ScaleTo(0.8f, 500, Easing.OutExpo).RotateTo(-15, 500, Easing.OutExpo).MoveToX(300, 500, Easing.OutQuint).FadeOut(500); + this.FadeOut(500, Easing.OutExpo); + + return base.OnExiting(next); + } + + private void startImport(string path) + { + if (string.IsNullOrEmpty(path)) + return; + + if (!File.Exists(path)) + { + refresh(); + currentFileText.Text = "File not exist"; + currentFileText.FlashColour(Color4.Red, 500); + return; + } + + string[] paths = { path }; + + Task.Factory.StartNew(() => gameBase.Import(paths), TaskCreationOptions.LongRunning); + } + + public enum FileFilterType + { + Skin, + Beatmap, + Replay, + All + } + } +} diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 4becdd58cd..1e0d27ac6a 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -41,6 +41,7 @@ namespace osu.Game.Screens.Menu public Action OnExit; public Action OnBeatmapListing; public Action OnSolo; + public Action OnImportButton; public Action OnSettings; public Action OnMulti; public Action OnChart; @@ -131,6 +132,7 @@ namespace osu.Game.Screens.Menu buttonsTopLevel.Add(new Button(@"play", @"button-play-select", OsuIcon.Logo, new Color4(102, 68, 204, 255), () => State = ButtonSystemState.Play, WEDGE_WIDTH, Key.P)); buttonsTopLevel.Add(new Button(@"osu!editor", @"button-generic-select", OsuIcon.EditCircle, new Color4(238, 170, 0, 255), () => OnEdit?.Invoke(), 0, Key.E)); buttonsTopLevel.Add(new Button(@"osu!direct", @"button-direct-select", OsuIcon.ChevronDownCircle, new Color4(165, 204, 0, 255), () => OnBeatmapListing?.Invoke(), 0, Key.D)); + buttonsTopLevel.Add(new Button(@"Import File", @"button-generic-select", FontAwesome.Solid.File, new Color4(0, 86, 73, 255), () => OnImportButton?.Invoke())); if (host.CanExit) buttonsTopLevel.Add(new Button(@"exit", string.Empty, OsuIcon.CrossCircle, new Color4(238, 51, 153, 255), () => OnExit?.Invoke(), 0, Key.Q)); diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index c3ecd75963..69e4ea487e 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -19,6 +19,7 @@ using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Edit; using osu.Game.Screens.Multi; using osu.Game.Screens.Select; +using osu.Game.Screens.Import; namespace osu.Game.Screens.Menu { @@ -105,6 +106,7 @@ namespace osu.Game.Screens.Menu }, OnSolo = onSolo, OnMulti = delegate { this.Push(new Multiplayer()); }, + OnImportButton = onImport, OnExit = confirmAndExit, } } @@ -144,7 +146,7 @@ namespace osu.Game.Screens.Menu [Resolved(canBeNull: true)] private OsuGame game { get; set; } - + private void onImport() => this.Push(new FileImportScreen()); private void confirmAndExit() { if (exitConfirmed) return; From dcaebd2621a34055c4d8a68e27e21c7a0056c04d Mon Sep 17 00:00:00 2001 From: PercyDan54 <50285552+PercyDan54@users.noreply.github.com> Date: Tue, 17 Nov 2020 21:39:29 +0800 Subject: [PATCH 041/818] Add license text --- osu.Game/Screens/Import/FileImportScreen.cs | 4 +++- osu.Game/Screens/Menu/ButtonSystem.cs | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Import/FileImportScreen.cs b/osu.Game/Screens/Import/FileImportScreen.cs index 669c26101c..bcbc87e8cc 100644 --- a/osu.Game/Screens/Import/FileImportScreen.cs +++ b/osu.Game/Screens/Import/FileImportScreen.cs @@ -1,3 +1,6 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + using System.IO; using System.Threading.Tasks; using osu.Framework.Allocation; @@ -11,7 +14,6 @@ using osu.Game.Graphics; using osu.Game.Graphics.UserInterfaceV2; using osuTK; using osu.Game.Overlays.Settings; -using osu.Game.Configuration; using osu.Game.Overlays; using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.Containers; diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 1e0d27ac6a..1722329c21 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -132,7 +132,10 @@ namespace osu.Game.Screens.Menu buttonsTopLevel.Add(new Button(@"play", @"button-play-select", OsuIcon.Logo, new Color4(102, 68, 204, 255), () => State = ButtonSystemState.Play, WEDGE_WIDTH, Key.P)); buttonsTopLevel.Add(new Button(@"osu!editor", @"button-generic-select", OsuIcon.EditCircle, new Color4(238, 170, 0, 255), () => OnEdit?.Invoke(), 0, Key.E)); buttonsTopLevel.Add(new Button(@"osu!direct", @"button-direct-select", OsuIcon.ChevronDownCircle, new Color4(165, 204, 0, 255), () => OnBeatmapListing?.Invoke(), 0, Key.D)); - buttonsTopLevel.Add(new Button(@"Import File", @"button-generic-select", FontAwesome.Solid.File, new Color4(0, 86, 73, 255), () => OnImportButton?.Invoke())); + if(RuntimeInfo.OS == RuntimeInfo.Platform.Android) + { + buttonsTopLevel.Add(new Button(@"Import File", @"button-generic-select", FontAwesome.Solid.File, new Color4(0, 86, 73, 255), () => OnImportButton?.Invoke())); + } if (host.CanExit) buttonsTopLevel.Add(new Button(@"exit", string.Empty, OsuIcon.CrossCircle, new Color4(238, 51, 153, 255), () => OnExit?.Invoke(), 0, Key.Q)); From 9a3dd12f309d2cccb3b50dfc92d2369f62b0f21d Mon Sep 17 00:00:00 2001 From: PercyDan54 <50285552+PercyDan54@users.noreply.github.com> Date: Wed, 18 Nov 2020 18:15:56 +0800 Subject: [PATCH 042/818] Move to debug settings --- .../Overlays/Settings/Sections/Debug/GeneralSettings.cs | 9 ++++++++- osu.Game/Screens/Menu/ButtonSystem.cs | 5 ----- osu.Game/Screens/Menu/MainMenu.cs | 4 +--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs index f05b876d8f..22674f0879 100644 --- a/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs @@ -4,6 +4,8 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; +using osu.Framework.Screens; +using osu.Game.Screens.Import; namespace osu.Game.Overlays.Settings.Sections.Debug { @@ -12,7 +14,7 @@ namespace osu.Game.Overlays.Settings.Sections.Debug protected override string Header => "General"; [BackgroundDependencyLoader] - private void load(FrameworkDebugConfigManager config, FrameworkConfigManager frameworkConfig) + private void load(FrameworkDebugConfigManager config, FrameworkConfigManager frameworkConfig, OsuGame game) { Children = new Drawable[] { @@ -27,6 +29,11 @@ namespace osu.Game.Overlays.Settings.Sections.Debug Current = config.GetBindable(DebugSetting.BypassFrontToBackPass) } }; + Add(new SettingsButton + { + Text = "Import files", + Action = () => game?.PerformFromScreen(menu => menu.Push(new FileImportScreen())) + }); } } } diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 1722329c21..4becdd58cd 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -41,7 +41,6 @@ namespace osu.Game.Screens.Menu public Action OnExit; public Action OnBeatmapListing; public Action OnSolo; - public Action OnImportButton; public Action OnSettings; public Action OnMulti; public Action OnChart; @@ -132,10 +131,6 @@ namespace osu.Game.Screens.Menu buttonsTopLevel.Add(new Button(@"play", @"button-play-select", OsuIcon.Logo, new Color4(102, 68, 204, 255), () => State = ButtonSystemState.Play, WEDGE_WIDTH, Key.P)); buttonsTopLevel.Add(new Button(@"osu!editor", @"button-generic-select", OsuIcon.EditCircle, new Color4(238, 170, 0, 255), () => OnEdit?.Invoke(), 0, Key.E)); buttonsTopLevel.Add(new Button(@"osu!direct", @"button-direct-select", OsuIcon.ChevronDownCircle, new Color4(165, 204, 0, 255), () => OnBeatmapListing?.Invoke(), 0, Key.D)); - if(RuntimeInfo.OS == RuntimeInfo.Platform.Android) - { - buttonsTopLevel.Add(new Button(@"Import File", @"button-generic-select", FontAwesome.Solid.File, new Color4(0, 86, 73, 255), () => OnImportButton?.Invoke())); - } if (host.CanExit) buttonsTopLevel.Add(new Button(@"exit", string.Empty, OsuIcon.CrossCircle, new Color4(238, 51, 153, 255), () => OnExit?.Invoke(), 0, Key.Q)); diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 69e4ea487e..c3ecd75963 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -19,7 +19,6 @@ using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Edit; using osu.Game.Screens.Multi; using osu.Game.Screens.Select; -using osu.Game.Screens.Import; namespace osu.Game.Screens.Menu { @@ -106,7 +105,6 @@ namespace osu.Game.Screens.Menu }, OnSolo = onSolo, OnMulti = delegate { this.Push(new Multiplayer()); }, - OnImportButton = onImport, OnExit = confirmAndExit, } } @@ -146,7 +144,7 @@ namespace osu.Game.Screens.Menu [Resolved(canBeNull: true)] private OsuGame game { get; set; } - private void onImport() => this.Push(new FileImportScreen()); + private void confirmAndExit() { if (exitConfirmed) return; From 6c5a6b42e59b53825fa73f29fb8de2a7eda42f08 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Sat, 21 Nov 2020 14:09:32 +0200 Subject: [PATCH 043/818] Only calculate recommended SR once --- osu.Game/Screens/Select/DifficultyRecommender.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/DifficultyRecommender.cs b/osu.Game/Screens/Select/DifficultyRecommender.cs index 2baabebdad..0e02b45650 100644 --- a/osu.Game/Screens/Select/DifficultyRecommender.cs +++ b/osu.Game/Screens/Select/DifficultyRecommender.cs @@ -69,8 +69,11 @@ namespace osu.Game.Screens.Select return beatmap; } - private void calculateRecommendedDifficulties() + private void calculateRecommendedDifficulties(bool onlyIfNoPreviousValues = false) { + if (recommendedStarDifficulty.Any() && onlyIfNoPreviousValues) + return; + // only query API for built-in rulesets rulesets.AvailableRulesets.Where(ruleset => ruleset.ID <= 3).ForEach(rulesetInfo => { @@ -122,7 +125,7 @@ namespace osu.Game.Screens.Select switch (state.NewValue) { case APIState.Online: - calculateRecommendedDifficulties(); + calculateRecommendedDifficulties(true); break; } }); From 72d9da5fac22583df2f275b1d5af65b1deb6e505 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Sat, 21 Nov 2020 14:26:09 +0200 Subject: [PATCH 044/818] Apply review suggestions --- osu.Game/OsuGame.cs | 25 +++++++++++-------- .../Screens/Select/DifficultyRecommender.cs | 14 +++-------- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index e5a299d4b0..acc42bb660 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -335,11 +335,15 @@ namespace osu.Game /// The user should have already requested this interactively. /// /// The beatmap to select. - /// - /// Optional predicate used to filter which difficulties to select. - /// If omitted, this will try to present a recommended beatmap from the current ruleset. - /// In case of failure the first difficulty of the set will be presented, ignoring the predicate. - /// + /// Optional predicate used to narrow the set of difficulties to select from when presenting. + /// + /// Among items satisfying the predicate, the order of preference is: + /// + /// beatmap with recommended difficulty, as provided by , + /// first beatmap from the current ruleset, + /// first beatmap from any ruleset. + /// + /// public void PresentBeatmap(BeatmapSetInfo beatmap, Predicate difficultyCriteria = null) { var databasedSet = beatmap.OnlineBeatmapSetID != null @@ -373,11 +377,12 @@ namespace osu.Game // Try to select recommended beatmap // This should give us a beatmap from current ruleset if there are any in our matched beatmaps - var selection = DifficultyRecommender.GetRecommendedBeatmap(beatmaps) ?? ( - // Fallback if a difficulty can't be recommended, maybe we are offline - // First try to find a beatmap in current ruleset, otherwise use first beatmap - beatmaps.FirstOrDefault(b => b.Ruleset.Equals(Ruleset.Value)) ?? beatmaps.First() - ); + var selection = DifficultyRecommender.GetRecommendedBeatmap(beatmaps); + // Fallback if a difficulty can't be recommended, maybe we are offline + // First try to find a beatmap in current ruleset + selection ??= beatmaps.FirstOrDefault(b => b.Ruleset.Equals(Ruleset.Value)); + // Otherwise use first beatmap + selection ??= beatmaps.First(); Ruleset.Value = selection.Ruleset; Beatmap.Value = BeatmapManager.GetWorkingBeatmap(selection); diff --git a/osu.Game/Screens/Select/DifficultyRecommender.cs b/osu.Game/Screens/Select/DifficultyRecommender.cs index 0e02b45650..ab64513ecb 100644 --- a/osu.Game/Screens/Select/DifficultyRecommender.cs +++ b/osu.Game/Screens/Select/DifficultyRecommender.cs @@ -81,7 +81,6 @@ namespace osu.Game.Screens.Select req.Success += result => { - bestRulesetOrder = null; // algorithm taken from https://github.com/ppy/osu-web/blob/e6e2825516449e3d0f3f5e1852c6bdd3428c3437/app/Models/User.php#L1505 recommendedStarDifficulty[rulesetInfo] = Math.Pow((double)(result.Statistics.PP ?? 0), 0.4) * 0.195; }; @@ -90,19 +89,12 @@ namespace osu.Game.Screens.Select }); } - private IEnumerable bestRulesetOrder; - private IEnumerable getBestRulesetOrder() { - bestRulesetOrder ??= recommendedStarDifficulty.OrderByDescending(pair => pair.Value) - .Select(pair => pair.Key) - .ToList(); + IEnumerable bestRulesetOrder = recommendedStarDifficulty.OrderByDescending(pair => pair.Value) + .Select(pair => pair.Key) + .ToList(); - return moveCurrentRulesetToFirst(); - } - - private IEnumerable moveCurrentRulesetToFirst() - { List orderedRulesets; if (bestRulesetOrder.Contains(ruleset.Value)) From 07db977af5a61d0f45f80352f183f8db50f616c4 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Sat, 21 Nov 2020 14:36:43 +0200 Subject: [PATCH 045/818] Remove no longer necessary force calculation Is no longer necessary because recommender uses bindable state value changes, compared to when the test was written, and runs the state change once on load --- .../Visual/SongSelect/TestSceneBeatmapRecommendations.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs index fc14af3ab5..8a4914a31b 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs @@ -46,9 +46,6 @@ namespace osu.Game.Tests.Visual.SongSelect base.SetUpSteps(); - // Force recommender to calculate its star ratings again - AddStep("calculate recommended SRs", () => recommender.APIStateChanged(API, APIState.Online)); - User getUser(int? rulesetID) { return new User From 07ee36a4ef2dde5ae2b524f672a124d3a8942efa Mon Sep 17 00:00:00 2001 From: PercyDan54 <50285552+PercyDan54@users.noreply.github.com> Date: Tue, 24 Nov 2020 17:18:19 +0800 Subject: [PATCH 046/818] Simplify code --- osu.Game/Screens/Import/FileImportScreen.cs | 118 ++++---------------- 1 file changed, 19 insertions(+), 99 deletions(-) diff --git a/osu.Game/Screens/Import/FileImportScreen.cs b/osu.Game/Screens/Import/FileImportScreen.cs index bcbc87e8cc..2f2548c15a 100644 --- a/osu.Game/Screens/Import/FileImportScreen.cs +++ b/osu.Game/Screens/Import/FileImportScreen.cs @@ -13,7 +13,6 @@ using osu.Framework.Screens; using osu.Game.Graphics; using osu.Game.Graphics.UserInterfaceV2; using osuTK; -using osu.Game.Overlays.Settings; using osu.Game.Overlays; using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.Containers; @@ -29,12 +28,11 @@ namespace osu.Game.Screens.Import public override bool HideOverlaysOnEnter => true; - private string[] fileExtensions = { ".foo" }; + private string[] fileExtensions; private string defaultPath; private readonly Bindable currentFile = new Bindable(); private readonly IBindable currentDirectory = new Bindable(); - private readonly Bindable filterType = new Bindable(FileFilterType.All); private TextFlowContainer currentFileText; private OsuScrollContainer fileNameScroll; private readonly OverlayColourProvider overlayColourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); @@ -42,15 +40,12 @@ namespace osu.Game.Screens.Import [Resolved] private OsuGameBase gameBase { get; set; } - [Resolved] - private DialogOverlay dialogOverlay { get; set; } - [BackgroundDependencyLoader(true)] private void load(Storage storage) { storage.GetStorageForDirectory("imports"); var originalPath = storage.GetFullPath("imports", true); - + fileExtensions = new string[] { ".osk", ".osr", ".osz" }; defaultPath = originalPath; InternalChild = contentContainer = new Container @@ -131,85 +126,43 @@ namespace osu.Game.Screens.Import { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, + Margin = new MarginPadding { Bottom = 15, Top =15 }, Children = new Drawable[] { - new Box - { - Colour = overlayColourProvider.Background4, - RelativeSizeAxes = Axes.Both - }, new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Spacing = new Vector2(10), Children = new Drawable[] { - new SettingsEnumDropdown - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - LabelText = "File Type", - Current = filterType, - Margin = new MarginPadding { Bottom = 15 } - }, - new GridContainer - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Margin = new MarginPadding { Top = 15 }, - RowDimensions = new[] + new TriangleButton { - new Dimension(GridSizeMode.AutoSize) - }, - Content = new[] - { - new Drawable[] + Text = "Import", + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + RelativeSizeAxes = Axes.X, + Height = 50, + Width = 0.9f, + Action = () => { - new TriangleButton - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - RelativeSizeAxes = Axes.X, - Height = 50, - Width = 0.9f, - Text = "Refresh", - Action = refresh - }, - new TriangleButton - { - Text = "Import", - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - RelativeSizeAxes = Axes.X, - Height = 50, - Width = 0.9f, - Action = () => - { - var d = currentFile.Value?.FullName; - if (d != null) - startImport(d); - else - currentFileText.FlashColour(Color4.Red, 500); - }, - } - }, + var d = currentFile.Value?.FullName; + if (d != null) + startImport(d); + else + currentFileText.FlashColour(Color4.Red, 500); + } } - }, } - }, + } } } } } - }, + } } } } }; - fileNameScroll.ScrollContent.Anchor = Anchor.Centre; fileNameScroll.ScrollContent.Origin = Anchor.Centre; @@ -218,37 +171,6 @@ namespace osu.Game.Screens.Import { currentFile.Value = null; }); - - filterType.BindValueChanged(onFilterTypeChanged, true); - } - - private void onFilterTypeChanged(ValueChangedEvent v) - { - switch (v.NewValue) - { - case FileFilterType.Beatmap: - fileExtensions = new string[] { ".osz" }; - break; - - case FileFilterType.Skin: - fileExtensions = new string[] { ".osk" }; - break; - - case FileFilterType.Replay: - fileExtensions = new string[] { ".osr" }; - break; - - default: - case FileFilterType.All: - fileExtensions = new string[] { ".osk", ".osr", ".osz" }; - break; - } - - refresh(); - } - - private void refresh() - { currentFile.UnbindBindings(); currentDirectory.UnbindBindings(); @@ -265,7 +187,6 @@ namespace osu.Game.Screens.Import fileSelectContainer.Add(fileSelector); } - private void updateFileSelectionText(ValueChangedEvent v) { currentFileText.Text = v.NewValue?.Name ?? "Select a file"; @@ -298,7 +219,6 @@ namespace osu.Game.Screens.Import if (!File.Exists(path)) { - refresh(); currentFileText.Text = "File not exist"; currentFileText.FlashColour(Color4.Red, 500); return; From 0b735b84cf205bb96f0e4d5308a64abf92ee9e63 Mon Sep 17 00:00:00 2001 From: PercyDan54 <50285552+PercyDan54@users.noreply.github.com> Date: Tue, 24 Nov 2020 17:23:53 +0800 Subject: [PATCH 047/818] Remove unused enum --- osu.Game/Screens/Import/FileImportScreen.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/osu.Game/Screens/Import/FileImportScreen.cs b/osu.Game/Screens/Import/FileImportScreen.cs index 2f2548c15a..f53ea70f45 100644 --- a/osu.Game/Screens/Import/FileImportScreen.cs +++ b/osu.Game/Screens/Import/FileImportScreen.cs @@ -228,13 +228,5 @@ namespace osu.Game.Screens.Import Task.Factory.StartNew(() => gameBase.Import(paths), TaskCreationOptions.LongRunning); } - - public enum FileFilterType - { - Skin, - Beatmap, - Replay, - All - } } } From 7ab27399bf148eda81aba3e30c7e88d3d0aa7143 Mon Sep 17 00:00:00 2001 From: PercyDan54 <50285552+PercyDan54@users.noreply.github.com> Date: Tue, 24 Nov 2020 18:04:34 +0800 Subject: [PATCH 048/818] Fix CI errors --- osu.Game/Screens/Import/FileImportScreen.cs | 37 ++++++++++----------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/osu.Game/Screens/Import/FileImportScreen.cs b/osu.Game/Screens/Import/FileImportScreen.cs index f53ea70f45..88dcd6623f 100644 --- a/osu.Game/Screens/Import/FileImportScreen.cs +++ b/osu.Game/Screens/Import/FileImportScreen.cs @@ -28,9 +28,7 @@ namespace osu.Game.Screens.Import public override bool HideOverlaysOnEnter => true; - private string[] fileExtensions; private string defaultPath; - private readonly Bindable currentFile = new Bindable(); private readonly IBindable currentDirectory = new Bindable(); private TextFlowContainer currentFileText; @@ -45,7 +43,7 @@ namespace osu.Game.Screens.Import { storage.GetStorageForDirectory("imports"); var originalPath = storage.GetFullPath("imports", true); - fileExtensions = new string[] { ".osk", ".osr", ".osz" }; + string[] fileExtensions = { ".osk", ".osr", ".osz" }; defaultPath = originalPath; InternalChild = contentContainer = new Container @@ -126,7 +124,7 @@ namespace osu.Game.Screens.Import { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Margin = new MarginPadding { Bottom = 15, Top =15 }, + Margin = new MarginPadding { Bottom = 15, Top =15 } , Children = new Drawable[] { new FillFlowContainer @@ -135,21 +133,21 @@ namespace osu.Game.Screens.Import AutoSizeAxes = Axes.Y, Children = new Drawable[] { - new TriangleButton - { - Text = "Import", - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - RelativeSizeAxes = Axes.X, - Height = 50, - Width = 0.9f, - Action = () => - { - var d = currentFile.Value?.FullName; - if (d != null) - startImport(d); - else - currentFileText.FlashColour(Color4.Red, 500); + new TriangleButton + { + Text = "Import", + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + RelativeSizeAxes = Axes.X, + Height = 50, + Width = 0.9f, + Action = () => + { + var d = currentFile.Value?.FullName; + if (d != null) + startImport(d); + else + currentFileText.FlashColour(Color4.Red, 500); } } } @@ -187,6 +185,7 @@ namespace osu.Game.Screens.Import fileSelectContainer.Add(fileSelector); } + private void updateFileSelectionText(ValueChangedEvent v) { currentFileText.Text = v.NewValue?.Name ?? "Select a file"; From 739c18a34d20eca3b542bbe2aad8e15d793e05e8 Mon Sep 17 00:00:00 2001 From: PercyDan54 <50285552+PercyDan54@users.noreply.github.com> Date: Tue, 24 Nov 2020 18:25:38 +0800 Subject: [PATCH 049/818] dotnet format --- osu.Game/Screens/Import/FileImportScreen.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Import/FileImportScreen.cs b/osu.Game/Screens/Import/FileImportScreen.cs index 88dcd6623f..39fd67c4e1 100644 --- a/osu.Game/Screens/Import/FileImportScreen.cs +++ b/osu.Game/Screens/Import/FileImportScreen.cs @@ -135,14 +135,14 @@ namespace osu.Game.Screens.Import { new TriangleButton { - Text = "Import", - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - RelativeSizeAxes = Axes.X, - Height = 50, - Width = 0.9f, - Action = () => - { + Text = "Import", + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + RelativeSizeAxes = Axes.X, + Height = 50, + Width = 0.9f, + Action = () => + { var d = currentFile.Value?.FullName; if (d != null) startImport(d); From 0009724c1a36914287df4c7b835078101b85520c Mon Sep 17 00:00:00 2001 From: PercyDan54 <50285552+PercyDan54@users.noreply.github.com> Date: Tue, 24 Nov 2020 18:48:32 +0800 Subject: [PATCH 050/818] Fix CI --- osu.Game/Screens/Import/FileImportScreen.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Import/FileImportScreen.cs b/osu.Game/Screens/Import/FileImportScreen.cs index 39fd67c4e1..a59688a2a9 100644 --- a/osu.Game/Screens/Import/FileImportScreen.cs +++ b/osu.Game/Screens/Import/FileImportScreen.cs @@ -124,7 +124,7 @@ namespace osu.Game.Screens.Import { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Margin = new MarginPadding { Bottom = 15, Top =15 } , + Margin = new MarginPadding { Bottom = 15, Top = 15 }, Children = new Drawable[] { new FillFlowContainer @@ -148,8 +148,8 @@ namespace osu.Game.Screens.Import startImport(d); else currentFileText.FlashColour(Color4.Red, 500); - } } + } } } } From 579e61eab9502a205ed0a85a678e5d980d647bd2 Mon Sep 17 00:00:00 2001 From: PercyDan <50285552+PercyDan54@users.noreply.github.com> Date: Sat, 28 Nov 2020 16:31:24 +0800 Subject: [PATCH 051/818] Allow null --- osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs index 22674f0879..4a9c9bd8a2 100644 --- a/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs @@ -13,7 +13,7 @@ namespace osu.Game.Overlays.Settings.Sections.Debug { protected override string Header => "General"; - [BackgroundDependencyLoader] + [BackgroundDependencyLoader(true)] private void load(FrameworkDebugConfigManager config, FrameworkConfigManager frameworkConfig, OsuGame game) { Children = new Drawable[] From 2e5a40eddf839b6ce4cfbca727db92828e504562 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Tue, 1 Dec 2020 20:12:09 +0100 Subject: [PATCH 052/818] Add an IntentFilter to handle osu! files. --- osu.Android/OsuGameActivity.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Android/OsuGameActivity.cs b/osu.Android/OsuGameActivity.cs index 7e250dce0e..b6e5742332 100644 --- a/osu.Android/OsuGameActivity.cs +++ b/osu.Android/OsuGameActivity.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using Android.App; +using Android.Content; using Android.Content.PM; using Android.OS; using Android.Views; @@ -9,7 +10,9 @@ using osu.Framework.Android; namespace osu.Android { + [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullUser, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = false)] + [IntentFilter(new[] { Intent.ActionDefault }, Categories = new[] { Intent.CategoryDefault, Intent.CategoryBrowsable, Intent.CategoryAppFiles }, DataSchemes = new[] { "content" }, DataPathPatterns = new[] { ".*\\.osz", ".*\\.osk" }, DataMimeType = "application/*")] public class OsuGameActivity : AndroidGameActivity { protected override Framework.Game CreateGame() => new OsuGameAndroid(this); From 005fa3a7eed9230828d7337cc200a405984bab6a Mon Sep 17 00:00:00 2001 From: Lucas A Date: Tue, 1 Dec 2020 20:28:15 +0100 Subject: [PATCH 053/818] Add ability to import files from a stream. --- osu.Game/Database/ArchiveModelManager.cs | 8 ++++++++ osu.Game/Database/ICanAcceptFiles.cs | 9 +++++++++ osu.Game/OsuGameBase.cs | 11 +++++++++++ osu.Game/Screens/Edit/Setup/ResourcesSection.cs | 2 ++ 4 files changed, 30 insertions(+) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 8bdc804311..9a7aa7b039 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -212,6 +212,14 @@ namespace osu.Game.Database return import; } + /// + /// Import one from a . + /// + /// The stream to import files from. + /// The filename of the archive being imported. + public async Task Import(Stream stream, string filename) + => await Import(new ZipArchiveReader(stream, filename)); // we need to keep around the filename as some model managers (namely SkinManager) use the archive name to populate skin info + /// /// Fired when the user requests to view the resulting import. /// diff --git a/osu.Game/Database/ICanAcceptFiles.cs b/osu.Game/Database/ICanAcceptFiles.cs index e4d92d957c..111701f0a4 100644 --- a/osu.Game/Database/ICanAcceptFiles.cs +++ b/osu.Game/Database/ICanAcceptFiles.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.IO; using System.Threading.Tasks; namespace osu.Game.Database @@ -17,6 +18,14 @@ namespace osu.Game.Database /// The files which should be imported. Task Import(params string[] paths); + /// + /// Import the specified stream. + /// + /// The stream to import files from. + /// The filename of the archive being imported. + /// + Task Import(Stream stream, string filename); + /// /// An array of accepted file extensions (in the standard format of ".abc"). /// diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index e7b5d3304d..0fc2b8d1d7 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -395,6 +395,17 @@ namespace osu.Game } } + public async Task Import(Stream stream, string filename) + { + var extension = Path.GetExtension(filename)?.ToLowerInvariant(); + + foreach (var importer in fileImporters) + { + if (importer.HandledExtensions.Contains(extension)) + await importer.Import(stream, Path.GetFileNameWithoutExtension(filename)); + } + } + public IEnumerable HandledExtensions => fileImporters.SelectMany(i => i.HandledExtensions); protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Screens/Edit/Setup/ResourcesSection.cs b/osu.Game/Screens/Edit/Setup/ResourcesSection.cs index 17ecfdd52e..1527f240cb 100644 --- a/osu.Game/Screens/Edit/Setup/ResourcesSection.cs +++ b/osu.Game/Screens/Edit/Setup/ResourcesSection.cs @@ -99,6 +99,8 @@ namespace osu.Game.Screens.Edit.Setup return Task.CompletedTask; } + Task ICanAcceptFiles.Import(Stream stream, string filename) => Task.CompletedTask; + protected override void LoadComplete() { base.LoadComplete(); From 827e957568d6efc53564193ae9196bbb13cc29bc Mon Sep 17 00:00:00 2001 From: Lucas A Date: Wed, 2 Dec 2020 18:03:49 +0100 Subject: [PATCH 054/818] Allow importing osz files / osk files from Downloads directory. --- osu.Android/OsuGameActivity.cs | 23 +++++++++++++++++++++-- osu.Game/OsuGame.cs | 16 ++++++++-------- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/osu.Android/OsuGameActivity.cs b/osu.Android/OsuGameActivity.cs index b6e5742332..c41323b97f 100644 --- a/osu.Android/OsuGameActivity.cs +++ b/osu.Android/OsuGameActivity.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.Linq; +using System.Threading.Tasks; using Android.App; using Android.Content; using Android.Content.PM; @@ -12,10 +14,12 @@ namespace osu.Android { [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullUser, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = false)] - [IntentFilter(new[] { Intent.ActionDefault }, Categories = new[] { Intent.CategoryDefault, Intent.CategoryBrowsable, Intent.CategoryAppFiles }, DataSchemes = new[] { "content" }, DataPathPatterns = new[] { ".*\\.osz", ".*\\.osk" }, DataMimeType = "application/*")] + [IntentFilter(new[] { Intent.ActionDefault }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPatterns = new[] { ".*\\.osz", ".*\\.osk" }, DataMimeType = "application/*")] public class OsuGameActivity : AndroidGameActivity { - protected override Framework.Game CreateGame() => new OsuGameAndroid(this); + private OsuGameAndroid game; + + protected override Framework.Game CreateGame() => game = new OsuGameAndroid(this); protected override void OnCreate(Bundle savedInstanceState) { @@ -26,8 +30,23 @@ namespace osu.Android base.OnCreate(savedInstanceState); + OnNewIntent(Intent); + Window.AddFlags(WindowManagerFlags.Fullscreen); Window.AddFlags(WindowManagerFlags.KeepScreenOn); } + + protected override void OnNewIntent(Intent intent) + { + if (intent.Action == Intent.ActionView) + { + var filename = intent.Data.Path.Split('/').Last(); + var stream = ContentResolver.OpenInputStream(intent.Data); + if (stream != null) + // intent handler may run before the game has even loaded so we need to wait for the file importers to load before launching import + game.WaitForReady(() => game, _ => Task.Run(() => game.Import(stream, filename))); + } + base.OnNewIntent(intent); + } } } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index bb638bcf3a..5b6b90fba5 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -267,7 +267,7 @@ namespace osu.Game case LinkAction.OpenEditorTimestamp: case LinkAction.JoinMultiplayerMatch: case LinkAction.Spectate: - waitForReady(() => notifications, _ => notifications.Post(new SimpleNotification + WaitForReady(() => notifications, _ => notifications.Post(new SimpleNotification { Text = @"This link type is not yet supported!", Icon = FontAwesome.Solid.LifeRing, @@ -288,7 +288,7 @@ namespace osu.Game } }); - public void OpenUrlExternally(string url) => waitForReady(() => externalLinkOpener, _ => + public void OpenUrlExternally(string url) => WaitForReady(() => externalLinkOpener, _ => { if (url.StartsWith('/')) url = $"{API.Endpoint}{url}"; @@ -300,7 +300,7 @@ namespace osu.Game /// Open a specific channel in chat. /// /// The channel to display. - public void ShowChannel(string channel) => waitForReady(() => channelManager, _ => + public void ShowChannel(string channel) => WaitForReady(() => channelManager, _ => { try { @@ -316,19 +316,19 @@ namespace osu.Game /// Show a beatmap set as an overlay. /// /// The set to display. - public void ShowBeatmapSet(int setId) => waitForReady(() => beatmapSetOverlay, _ => beatmapSetOverlay.FetchAndShowBeatmapSet(setId)); + public void ShowBeatmapSet(int setId) => WaitForReady(() => beatmapSetOverlay, _ => beatmapSetOverlay.FetchAndShowBeatmapSet(setId)); /// /// Show a user's profile as an overlay. /// /// The user to display. - public void ShowUser(int userId) => waitForReady(() => userProfile, _ => userProfile.ShowUser(userId)); + public void ShowUser(int userId) => WaitForReady(() => userProfile, _ => userProfile.ShowUser(userId)); /// /// Show a beatmap's set as an overlay, displaying the given beatmap. /// /// The beatmap to show. - public void ShowBeatmap(int beatmapId) => waitForReady(() => beatmapSetOverlay, _ => beatmapSetOverlay.FetchAndShowBeatmap(beatmapId)); + public void ShowBeatmap(int beatmapId) => WaitForReady(() => beatmapSetOverlay, _ => beatmapSetOverlay.FetchAndShowBeatmap(beatmapId)); /// /// Present a beatmap at song select immediately. @@ -483,13 +483,13 @@ namespace osu.Game /// A function to retrieve a (potentially not-yet-constructed) target instance. /// The action to perform on the instance when load is confirmed. /// The type of the target instance. - private void waitForReady(Func retrieveInstance, Action action) + public void WaitForReady(Func retrieveInstance, Action action) where T : Drawable { var instance = retrieveInstance(); if (ScreenStack == null || ScreenStack.CurrentScreen is StartupScreen || instance?.IsLoaded != true) - Schedule(() => waitForReady(retrieveInstance, action)); + Schedule(() => WaitForReady(retrieveInstance, action)); else action(instance); } From 48dad61a46494a588da1078e39295648b3c84dc0 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Thu, 3 Dec 2020 19:38:45 +0200 Subject: [PATCH 055/818] Apply review suggestions --- .../SongSelect/TestSceneBeatmapRecommendations.cs | 2 -- osu.Game/Rulesets/ILegacyRuleset.cs | 2 ++ osu.Game/Screens/Select/DifficultyRecommender.cs | 12 ++++++++---- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs index 8a4914a31b..6c19206802 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs @@ -23,8 +23,6 @@ namespace osu.Game.Tests.Visual.SongSelect { public class TestSceneBeatmapRecommendations : OsuGameTestScene { - protected override bool UseOnlineAPI => false; - [Resolved] private DifficultyRecommender recommender { get; set; } diff --git a/osu.Game/Rulesets/ILegacyRuleset.cs b/osu.Game/Rulesets/ILegacyRuleset.cs index 06a85b5261..f4b03baccd 100644 --- a/osu.Game/Rulesets/ILegacyRuleset.cs +++ b/osu.Game/Rulesets/ILegacyRuleset.cs @@ -5,6 +5,8 @@ namespace osu.Game.Rulesets { public interface ILegacyRuleset { + const int MAX_LEGACY_RULESET_ID = 3; + /// /// Identifies the server-side ID of a legacy ruleset. /// diff --git a/osu.Game/Screens/Select/DifficultyRecommender.cs b/osu.Game/Screens/Select/DifficultyRecommender.cs index ab64513ecb..f8aaf5c0fd 100644 --- a/osu.Game/Screens/Select/DifficultyRecommender.cs +++ b/osu.Game/Screens/Select/DifficultyRecommender.cs @@ -26,6 +26,8 @@ namespace osu.Game.Screens.Select [Resolved] private Bindable ruleset { get; set; } + private int storedUserId; + private readonly Dictionary recommendedStarDifficulty = new Dictionary(); private readonly IBindable apiState = new Bindable(); @@ -69,13 +71,15 @@ namespace osu.Game.Screens.Select return beatmap; } - private void calculateRecommendedDifficulties(bool onlyIfNoPreviousValues = false) + private void calculateRecommendedDifficulties() { - if (recommendedStarDifficulty.Any() && onlyIfNoPreviousValues) + if (recommendedStarDifficulty.Any() && api.LocalUser.Value.Id == storedUserId) return; + storedUserId = api.LocalUser.Value.Id; + // only query API for built-in rulesets - rulesets.AvailableRulesets.Where(ruleset => ruleset.ID <= 3).ForEach(rulesetInfo => + rulesets.AvailableRulesets.Where(ruleset => ruleset.ID <= ILegacyRuleset.MAX_LEGACY_RULESET_ID).ForEach(rulesetInfo => { var req = new GetUserRequest(api.LocalUser.Value.Id, rulesetInfo); @@ -117,7 +121,7 @@ namespace osu.Game.Screens.Select switch (state.NewValue) { case APIState.Online: - calculateRecommendedDifficulties(true); + calculateRecommendedDifficulties(); break; } }); From 4cd2e207ac8b8353fd46019d0f446e8e258faf54 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Thu, 3 Dec 2020 19:53:06 +0200 Subject: [PATCH 056/818] Document getBestRulesetOrder --- osu.Game/Screens/Select/DifficultyRecommender.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Select/DifficultyRecommender.cs b/osu.Game/Screens/Select/DifficultyRecommender.cs index f8aaf5c0fd..21e6629add 100644 --- a/osu.Game/Screens/Select/DifficultyRecommender.cs +++ b/osu.Game/Screens/Select/DifficultyRecommender.cs @@ -93,6 +93,9 @@ namespace osu.Game.Screens.Select }); } + /// + /// Rulesets ordered by highest recommended star difficulty, except currently selected ruleset first + /// private IEnumerable getBestRulesetOrder() { IEnumerable bestRulesetOrder = recommendedStarDifficulty.OrderByDescending(pair => pair.Value) From 49be4978bd400fc4ffb1036eb4f9cc23cfd79a07 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Thu, 3 Dec 2020 19:53:41 +0200 Subject: [PATCH 057/818] Avoid calling ToList twice --- osu.Game/Screens/Select/DifficultyRecommender.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/DifficultyRecommender.cs b/osu.Game/Screens/Select/DifficultyRecommender.cs index 21e6629add..fa48316e9d 100644 --- a/osu.Game/Screens/Select/DifficultyRecommender.cs +++ b/osu.Game/Screens/Select/DifficultyRecommender.cs @@ -99,8 +99,7 @@ namespace osu.Game.Screens.Select private IEnumerable getBestRulesetOrder() { IEnumerable bestRulesetOrder = recommendedStarDifficulty.OrderByDescending(pair => pair.Value) - .Select(pair => pair.Key) - .ToList(); + .Select(pair => pair.Key); List orderedRulesets; From e792f070840bc3857467a3a3a74b00e0e7a075d3 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Thu, 3 Dec 2020 20:07:42 +0200 Subject: [PATCH 058/818] Add test for recommending current ruleset --- .../TestSceneBeatmapRecommendations.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs index 6c19206802..75a33af247 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs @@ -100,6 +100,24 @@ namespace osu.Game.Tests.Visual.SongSelect presentAndConfirm(() => beatmapSets[3], 2); } + [Test] + public void TestCurrentRulesetIsRecommended() + { + BeatmapSetInfo catchSet = null, mixedSet = null; + + AddStep("create catch beatmapset", () => catchSet = importBeatmapSet(0, new[] { new CatchRuleset().RulesetInfo })); + AddStep("create mixed beatmapset", () => mixedSet = importBeatmapSet(1, + new[] { new TaikoRuleset().RulesetInfo, new CatchRuleset().RulesetInfo, new ManiaRuleset().RulesetInfo })); + + AddAssert("all sets imported", () => ensureAllBeatmapSetsImported(new[] { catchSet, mixedSet })); + + // Switch to catch + presentAndConfirm(() => catchSet, 1); + + // Present mixed difficulty set, expect current ruleset to be selected + presentAndConfirm(() => mixedSet, 2); + } + [Test] public void TestBestRulesetIsRecommended() { From abe5a67bc5329702dd7b0176655b8796c1419c0b Mon Sep 17 00:00:00 2001 From: MATRIX-feather Date: Fri, 4 Dec 2020 03:14:07 +0800 Subject: [PATCH 059/818] Simplify implementation --- osu.Game/Screens/Import/FileImportScreen.cs | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/Import/FileImportScreen.cs b/osu.Game/Screens/Import/FileImportScreen.cs index a59688a2a9..211605e5c9 100644 --- a/osu.Game/Screens/Import/FileImportScreen.cs +++ b/osu.Game/Screens/Import/FileImportScreen.cs @@ -45,6 +45,7 @@ namespace osu.Game.Screens.Import var originalPath = storage.GetFullPath("imports", true); string[] fileExtensions = { ".osk", ".osr", ".osz" }; defaultPath = originalPath; + var directory = currentDirectory.Value?.FullName ?? defaultPath; InternalChild = contentContainer = new Container { @@ -67,6 +68,10 @@ namespace osu.Game.Screens.Import Width = 0.65f, Anchor = Anchor.TopLeft, Origin = Anchor.TopLeft, + Child = fileSelector = new FileSelector(initialPath: directory, validFileExtensions: fileExtensions) + { + RelativeSizeAxes = Axes.Both + } }, new Container { @@ -169,21 +174,9 @@ namespace osu.Game.Screens.Import { currentFile.Value = null; }); - currentFile.UnbindBindings(); - currentDirectory.UnbindBindings(); - - fileSelector?.Expire(); - - var directory = currentDirectory.Value?.FullName ?? defaultPath; - fileSelector = new FileSelector(initialPath: directory, validFileExtensions: fileExtensions) - { - RelativeSizeAxes = Axes.Both - }; currentDirectory.BindTo(fileSelector.CurrentPath); currentFile.BindTo(fileSelector.CurrentFile); - - fileSelectContainer.Add(fileSelector); } private void updateFileSelectionText(ValueChangedEvent v) From 85de1a1d20cc5299a3c9443ff422da320d92d384 Mon Sep 17 00:00:00 2001 From: MATRIX-feather Date: Fri, 4 Dec 2020 03:19:20 +0800 Subject: [PATCH 060/818] Animation changes --- osu.Game/Screens/Import/FileImportScreen.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Import/FileImportScreen.cs b/osu.Game/Screens/Import/FileImportScreen.cs index 211605e5c9..9fb8e22977 100644 --- a/osu.Game/Screens/Import/FileImportScreen.cs +++ b/osu.Game/Screens/Import/FileImportScreen.cs @@ -188,17 +188,16 @@ namespace osu.Game.Screens.Import { base.OnEntering(last); - contentContainer.FadeOut().Then().ScaleTo(0.8f).RotateTo(-15).MoveToX(300) + contentContainer.FadeOut().Then().ScaleTo(0.95f) .Then() - .ScaleTo(1, 1500, Easing.OutElastic) - .FadeIn(500) - .MoveToX(0, 500, Easing.OutQuint) - .RotateTo(0, 500, Easing.OutQuint); + .ScaleTo(1, 300, Easing.OutQuint) + .FadeIn(300); } public override bool OnExiting(IScreen next) { - contentContainer.ScaleTo(0.8f, 500, Easing.OutExpo).RotateTo(-15, 500, Easing.OutExpo).MoveToX(300, 500, Easing.OutQuint).FadeOut(500); + contentContainer.ScaleTo(0.95f, 300, Easing.OutQuint) + .FadeOut(300); this.FadeOut(500, Easing.OutExpo); return base.OnExiting(next); From fb080284d2a03c3861d8c2430436c8a30c48d9a2 Mon Sep 17 00:00:00 2001 From: MATRIX-feather Date: Fri, 4 Dec 2020 04:10:08 +0800 Subject: [PATCH 061/818] Simplify UI implementation --- osu.Game/Screens/Import/FileImportScreen.cs | 153 +++++++------------- 1 file changed, 54 insertions(+), 99 deletions(-) diff --git a/osu.Game/Screens/Import/FileImportScreen.cs b/osu.Game/Screens/Import/FileImportScreen.cs index 9fb8e22977..9af87887b2 100644 --- a/osu.Game/Screens/Import/FileImportScreen.cs +++ b/osu.Game/Screens/Import/FileImportScreen.cs @@ -13,7 +13,6 @@ using osu.Framework.Screens; using osu.Game.Graphics; using osu.Game.Graphics.UserInterfaceV2; using osuTK; -using osu.Game.Overlays; using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.Containers; using osuTK.Graphics; @@ -22,30 +21,32 @@ namespace osu.Game.Screens.Import { public class FileImportScreen : OsuScreen { - private Container contentContainer; - private FileSelector fileSelector; - private Container fileSelectContainer; - public override bool HideOverlaysOnEnter => true; - private string defaultPath; private readonly Bindable currentFile = new Bindable(); private readonly IBindable currentDirectory = new Bindable(); + + private FileSelector fileSelector; + private Container contentContainer; private TextFlowContainer currentFileText; private OsuScrollContainer fileNameScroll; - private readonly OverlayColourProvider overlayColourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); + + private const float duration = 300; + private const float button_height = 50; + private const float button_vertical_margin = 15; [Resolved] private OsuGameBase gameBase { get; set; } + [Resolved] + private OsuColour colours { get; set; } + [BackgroundDependencyLoader(true)] private void load(Storage storage) { storage.GetStorageForDirectory("imports"); var originalPath = storage.GetFullPath("imports", true); string[] fileExtensions = { ".osk", ".osr", ".osz" }; - defaultPath = originalPath; - var directory = currentDirectory.Value?.FullName ?? defaultPath; InternalChild = contentContainer = new Container { @@ -59,19 +60,13 @@ namespace osu.Game.Screens.Import { new Box { - Colour = overlayColourProvider.Background5, + Colour = colours.GreySeafoamDark, RelativeSizeAxes = Axes.Both, }, - fileSelectContainer = new Container + fileSelector = new FileSelector(initialPath: originalPath, validFileExtensions: fileExtensions) { RelativeSizeAxes = Axes.Both, - Width = 0.65f, - Anchor = Anchor.TopLeft, - Origin = Anchor.TopLeft, - Child = fileSelector = new FileSelector(initialPath: directory, validFileExtensions: fileExtensions) - { - RelativeSizeAxes = Axes.Both - } + Width = 0.65f }, new Container { @@ -79,87 +74,48 @@ namespace osu.Game.Screens.Import Width = 0.35f, Anchor = Anchor.TopRight, Origin = Anchor.TopRight, - Masking = true, - CornerRadius = 10, Children = new Drawable[] { - new GridContainer + new Box + { + Colour = colours.GreySeafoamDarker, + RelativeSizeAxes = Axes.Both + }, + new Container { RelativeSizeAxes = Axes.Both, - RowDimensions = new[] + Padding = new MarginPadding { Bottom = button_height + button_vertical_margin * 2 }, + Child = fileNameScroll = new OsuScrollContainer { - new Dimension(), - new Dimension(GridSizeMode.AutoSize), - }, - Content = new[] - { - new Drawable[] + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Child = currentFileText = new TextFlowContainer(t => t.Font = OsuFont.Default.With(size: 30)) { - new Container - { - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Children = new Drawable[] - { - new Box - { - Colour = overlayColourProvider.Background3, - RelativeSizeAxes = Axes.Both - }, - fileNameScroll = new OsuScrollContainer - { - Masking = false, - RelativeSizeAxes = Axes.Both, - Child = currentFileText = new TextFlowContainer(t => t.Font = OsuFont.Default.With(size: 30)) - { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - TextAnchor = Anchor.Centre - }, - }, - } - }, + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + TextAnchor = Anchor.Centre }, - new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Margin = new MarginPadding { Bottom = 15, Top = 15 }, - Children = new Drawable[] - { - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] - { - new TriangleButton - { - Text = "Import", - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - RelativeSizeAxes = Axes.X, - Height = 50, - Width = 0.9f, - Action = () => - { - var d = currentFile.Value?.FullName; - if (d != null) - startImport(d); - else - currentFileText.FlashColour(Color4.Red, 500); - } - } - } - } - } - } - } + }, + }, + new TriangleButton + { + Text = "Import", + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + RelativeSizeAxes = Axes.X, + Height = button_height, + Width = 0.9f, + Margin = new MarginPadding { Vertical = button_vertical_margin }, + Action = () => + { + var d = currentFile.Value?.FullName; + if (d != null) + startImport(d); + else + currentFileText.FlashColour(Color4.Red, 500); } } } @@ -190,15 +146,14 @@ namespace osu.Game.Screens.Import contentContainer.FadeOut().Then().ScaleTo(0.95f) .Then() - .ScaleTo(1, 300, Easing.OutQuint) - .FadeIn(300); + .ScaleTo(1, duration, Easing.OutQuint) + .FadeIn(duration); } public override bool OnExiting(IScreen next) { - contentContainer.ScaleTo(0.95f, 300, Easing.OutQuint) - .FadeOut(300); - this.FadeOut(500, Easing.OutExpo); + contentContainer.ScaleTo(0.95f, duration, Easing.OutQuint); + this.FadeOut(duration, Easing.OutQuint); return base.OnExiting(next); } @@ -210,8 +165,8 @@ namespace osu.Game.Screens.Import if (!File.Exists(path)) { - currentFileText.Text = "File not exist"; - currentFileText.FlashColour(Color4.Red, 500); + currentFileText.Text = "No such file"; + currentFileText.FlashColour(colours.Red, duration); return; } From 7104230ae3645392e051e1b998fb6c4bb809aabb Mon Sep 17 00:00:00 2001 From: MATRIX-feather Date: Fri, 4 Dec 2020 19:52:25 +0800 Subject: [PATCH 062/818] Add "onCurrentDirectoryChanged" --- osu.Game/Screens/Import/FileImportScreen.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Import/FileImportScreen.cs b/osu.Game/Screens/Import/FileImportScreen.cs index 9af87887b2..e2e2b699f5 100644 --- a/osu.Game/Screens/Import/FileImportScreen.cs +++ b/osu.Game/Screens/Import/FileImportScreen.cs @@ -126,15 +126,17 @@ namespace osu.Game.Screens.Import fileNameScroll.ScrollContent.Origin = Anchor.Centre; currentFile.BindValueChanged(updateFileSelectionText, true); - currentDirectory.BindValueChanged(_ => - { - currentFile.Value = null; - }); + currentDirectory.BindValueChanged(onCurrentDirectoryChanged); currentDirectory.BindTo(fileSelector.CurrentPath); currentFile.BindTo(fileSelector.CurrentFile); } + private void onCurrentDirectoryChanged(ValueChangedEvent v) + { + currentFile.Value = null; + } + private void updateFileSelectionText(ValueChangedEvent v) { currentFileText.Text = v.NewValue?.Name ?? "Select a file"; From 825120fed3b3111b3dcd836a7ef18e4716160515 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 4 Dec 2020 18:49:01 +0100 Subject: [PATCH 063/818] Display import state in a notification. --- osu.Game/Database/ArchiveModelManager.cs | 37 +++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 9a7aa7b039..7b59726cd0 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -218,7 +218,42 @@ namespace osu.Game.Database /// The stream to import files from. /// The filename of the archive being imported. public async Task Import(Stream stream, string filename) - => await Import(new ZipArchiveReader(stream, filename)); // we need to keep around the filename as some model managers (namely SkinManager) use the archive name to populate skin info + { + var notification = new ProgressNotification + { + Progress = 0, + State = ProgressNotificationState.Active, + Text = $"{HumanisedModelName.Humanize(LetterCasing.Title)} import is initialising...", + }; + + PostNotification.Invoke(notification); + + try + { + // we need to keep around the filename as some model managers (namely SkinManager) use the archive name to populate skin info + var imported = await Import(new ZipArchiveReader(stream, filename), notification.CancellationToken); + + notification.CompletionText = $"Imported {imported}! Click to view."; + notification.CompletionClickAction += () => + { + PresentImport?.Invoke(new[] { imported }); + return true; + }; + notification.State = ProgressNotificationState.Completed; + } + catch (TaskCanceledException) + { + throw; + } + catch (Exception e) + { + notification.Text = $"{HumanisedModelName.Humanize(LetterCasing.Title)} import failed!"; + notification.State = ProgressNotificationState.Cancelled; + Logger.Error(e, $@"Could not import ({filename})", LoggingTarget.Database); + } + + return; + } /// /// Fired when the user requests to view the resulting import. From dd21de0cd526fb81c4d80beea034e343744a740a Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 4 Dec 2020 22:07:45 +0100 Subject: [PATCH 064/818] Fix code inspections. --- osu.Android/OsuGameActivity.cs | 4 ++-- osu.Game/Database/ArchiveModelManager.cs | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Android/OsuGameActivity.cs b/osu.Android/OsuGameActivity.cs index c41323b97f..2b25c37d58 100644 --- a/osu.Android/OsuGameActivity.cs +++ b/osu.Android/OsuGameActivity.cs @@ -12,7 +12,6 @@ using osu.Framework.Android; namespace osu.Android { - [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullUser, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = false)] [IntentFilter(new[] { Intent.ActionDefault }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPatterns = new[] { ".*\\.osz", ".*\\.osk" }, DataMimeType = "application/*")] public class OsuGameActivity : AndroidGameActivity @@ -44,8 +43,9 @@ namespace osu.Android var stream = ContentResolver.OpenInputStream(intent.Data); if (stream != null) // intent handler may run before the game has even loaded so we need to wait for the file importers to load before launching import - game.WaitForReady(() => game, _ => Task.Run(() => game.Import(stream, filename))); + game.WaitForReady(() => game, _ => Task.Run(() => game.Import(stream, filename))); } + base.OnNewIntent(intent); } } diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 7b59726cd0..f4208671d7 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -251,8 +251,6 @@ namespace osu.Game.Database notification.State = ProgressNotificationState.Cancelled; Logger.Error(e, $@"Could not import ({filename})", LoggingTarget.Database); } - - return; } /// From 280abdc473e8bd5e512c1456b906d757e98d6345 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 5 Dec 2020 11:50:08 +0100 Subject: [PATCH 065/818] Use ContentResolver for getting filename. --- osu.Android/OsuGameActivity.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Android/OsuGameActivity.cs b/osu.Android/OsuGameActivity.cs index 2b25c37d58..917a39148d 100644 --- a/osu.Android/OsuGameActivity.cs +++ b/osu.Android/OsuGameActivity.cs @@ -1,12 +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 System.Threading.Tasks; using Android.App; using Android.Content; using Android.Content.PM; using Android.OS; +using Android.Provider; using Android.Views; using osu.Framework.Android; @@ -39,11 +39,14 @@ namespace osu.Android { if (intent.Action == Intent.ActionView) { - var filename = intent.Data.Path.Split('/').Last(); + var cursor = ContentResolver.Query(intent.Data, null, null, null); + var filename_column = cursor.GetColumnIndex(OpenableColumns.DisplayName); + cursor.MoveToFirst(); + var stream = ContentResolver.OpenInputStream(intent.Data); if (stream != null) // intent handler may run before the game has even loaded so we need to wait for the file importers to load before launching import - game.WaitForReady(() => game, _ => Task.Run(() => game.Import(stream, filename))); + game.WaitForReady(() => game, _ => Task.Run(() => game.Import(stream, cursor.GetString(filename_column)))); } base.OnNewIntent(intent); From 0266410368a9ed8c62265ac67a21f8ef7e7d6593 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 5 Dec 2020 18:30:40 +0100 Subject: [PATCH 066/818] Allow importing files through the android share sheet. --- osu.Android/OsuGameActivity.cs | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/osu.Android/OsuGameActivity.cs b/osu.Android/OsuGameActivity.cs index 917a39148d..b331f3d734 100644 --- a/osu.Android/OsuGameActivity.cs +++ b/osu.Android/OsuGameActivity.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using Android.App; using Android.Content; using Android.Content.PM; +using Android.Net; using Android.OS; using Android.Provider; using Android.Views; @@ -13,7 +14,7 @@ using osu.Framework.Android; namespace osu.Android { [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullUser, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = false)] - [IntentFilter(new[] { Intent.ActionDefault }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPatterns = new[] { ".*\\.osz", ".*\\.osk" }, DataMimeType = "application/*")] + [IntentFilter(new[] { Intent.ActionDefault, Intent.ActionSend }, Categories = new[] { Intent.CategoryDefault }, DataPathPatterns = new[] { ".*\\.osz", ".*\\.osk" }, DataMimeType = "application/*")] public class OsuGameActivity : AndroidGameActivity { private OsuGameAndroid game; @@ -37,19 +38,29 @@ namespace osu.Android protected override void OnNewIntent(Intent intent) { - if (intent.Action == Intent.ActionView) + if (intent.Action == Intent.ActionDefault) { - var cursor = ContentResolver.Query(intent.Data, null, null, null); - var filename_column = cursor.GetColumnIndex(OpenableColumns.DisplayName); - cursor.MoveToFirst(); - - var stream = ContentResolver.OpenInputStream(intent.Data); - if (stream != null) - // intent handler may run before the game has even loaded so we need to wait for the file importers to load before launching import - game.WaitForReady(() => game, _ => Task.Run(() => game.Import(stream, cursor.GetString(filename_column)))); + if (intent.Scheme == ContentResolver.SchemeContent) + handleImportFromUri(intent.Data); } - base.OnNewIntent(intent); + if (intent.Action == Intent.ActionSend) + { + var content = intent.ClipData.GetItemAt(0); + handleImportFromUri(content.Uri); + } + } + + private void handleImportFromUri(Uri uri) + { + var cursor = ContentResolver.Query(uri, null, null, null); + var filename_column = cursor.GetColumnIndex(OpenableColumns.DisplayName); + cursor.MoveToFirst(); + + var stream = ContentResolver.OpenInputStream(uri); + if (stream != null) + // intent handler may run before the game has even loaded so we need to wait for the file importers to load before launching import + game.WaitForReady(() => game, _ => Task.Run(() => game.Import(stream, cursor.GetString(filename_column)))); } } } From 2ea8b105d51a42491b7663c2f5b6c01d6a467843 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 5 Dec 2020 20:42:07 +0100 Subject: [PATCH 067/818] Apply review suggestions --- osu.Android/OsuGameActivity.cs | 11 ++++------- osu.Android/OsuGameAndroid.cs | 9 +++++++++ osu.Game/OsuGame.cs | 2 +- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/osu.Android/OsuGameActivity.cs b/osu.Android/OsuGameActivity.cs index b331f3d734..a56206c969 100644 --- a/osu.Android/OsuGameActivity.cs +++ b/osu.Android/OsuGameActivity.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Threading.Tasks; using Android.App; using Android.Content; using Android.Content.PM; @@ -38,10 +37,9 @@ namespace osu.Android protected override void OnNewIntent(Intent intent) { - if (intent.Action == Intent.ActionDefault) + if (intent.Action == Intent.ActionDefault && intent.Scheme == ContentResolver.SchemeContent) { - if (intent.Scheme == ContentResolver.SchemeContent) - handleImportFromUri(intent.Data); + handleImportFromUri(intent.Data); } if (intent.Action == Intent.ActionSend) @@ -53,14 +51,13 @@ namespace osu.Android private void handleImportFromUri(Uri uri) { - var cursor = ContentResolver.Query(uri, null, null, null); + var cursor = ContentResolver.Query(uri, new[] { OpenableColumns.DisplayName }, null, null); var filename_column = cursor.GetColumnIndex(OpenableColumns.DisplayName); cursor.MoveToFirst(); var stream = ContentResolver.OpenInputStream(uri); if (stream != null) - // intent handler may run before the game has even loaded so we need to wait for the file importers to load before launching import - game.WaitForReady(() => game, _ => Task.Run(() => game.Import(stream, cursor.GetString(filename_column)))); + game.ScheduleImport(stream, cursor.GetString(filename_column)); } } } diff --git a/osu.Android/OsuGameAndroid.cs b/osu.Android/OsuGameAndroid.cs index 21d6336b2c..81945ee083 100644 --- a/osu.Android/OsuGameAndroid.cs +++ b/osu.Android/OsuGameAndroid.cs @@ -2,6 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.IO; +using System.Threading.Tasks; using Android.App; using Android.OS; using osu.Framework.Allocation; @@ -65,6 +67,13 @@ namespace osu.Android } } + /// + /// Schedules a file to be imported once the game is loaded. + /// + /// A stream to the file to import. + /// The filename of the file to import. + public void ScheduleImport(Stream stream, string filename) => WaitForReady(() => this, _ => Task.Run(() => Import(stream, filename))); + protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 5b6b90fba5..2c1db67d24 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -483,7 +483,7 @@ namespace osu.Game /// A function to retrieve a (potentially not-yet-constructed) target instance. /// The action to perform on the instance when load is confirmed. /// The type of the target instance. - public void WaitForReady(Func retrieveInstance, Action action) + protected void WaitForReady(Func retrieveInstance, Action action) where T : Drawable { var instance = retrieveInstance(); From eda6e1fbddb46b9eec3a33a1417581154645ff8a Mon Sep 17 00:00:00 2001 From: Shivam Date: Mon, 7 Dec 2020 13:11:13 +0100 Subject: [PATCH 068/818] Add tournament switching in the UI --- osu.Game.Tournament/IO/TournamentStorage.cs | 18 ++++++++++++++++++ osu.Game.Tournament/Screens/SetupScreen.cs | 13 +++++++++++++ 2 files changed, 31 insertions(+) diff --git a/osu.Game.Tournament/IO/TournamentStorage.cs b/osu.Game.Tournament/IO/TournamentStorage.cs index 2e8a6ce667..66e27ddbb5 100644 --- a/osu.Game.Tournament/IO/TournamentStorage.cs +++ b/osu.Game.Tournament/IO/TournamentStorage.cs @@ -1,10 +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 osu.Framework.Bindables; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game.IO; using System.IO; +using System.Collections.Generic; using osu.Game.Tournament.Configuration; namespace osu.Game.Tournament.IO @@ -13,12 +15,15 @@ namespace osu.Game.Tournament.IO { private const string default_tournament = "default"; private readonly Storage storage; + private readonly Storage allTournaments; private readonly TournamentStorageManager storageConfig; + public readonly Bindable CurrentTournament; public TournamentStorage(Storage storage) : base(storage.GetStorageForDirectory("tournaments"), string.Empty) { this.storage = storage; + allTournaments = UnderlyingStorage; storageConfig = new TournamentStorageManager(storage); @@ -29,9 +34,22 @@ namespace osu.Game.Tournament.IO else Migrate(UnderlyingStorage.GetStorageForDirectory(default_tournament)); + CurrentTournament = new Bindable(storageConfig.Get(StorageConfig.CurrentTournament)); Logger.Log("Using tournament storage: " + GetFullPath(string.Empty)); + + CurrentTournament.BindValueChanged(updateTournament, false); } + private void updateTournament(ValueChangedEvent newTournament) + { + ChangeTargetStorage(allTournaments.GetStorageForDirectory(newTournament.NewValue)); + Logger.Log("Changing tournament storage: " + GetFullPath(string.Empty)); + storageConfig.Set(StorageConfig.CurrentTournament, newTournament.NewValue); + storageConfig.Save(); + } + + public IEnumerable ListTournaments() => allTournaments.GetDirectories(string.Empty); + public override void Migrate(Storage newStorage) { // this migration only happens once on moving to the per-tournament storage system. diff --git a/osu.Game.Tournament/Screens/SetupScreen.cs b/osu.Game.Tournament/Screens/SetupScreen.cs index e78d3a9e83..9f34c36e64 100644 --- a/osu.Game.Tournament/Screens/SetupScreen.cs +++ b/osu.Game.Tournament/Screens/SetupScreen.cs @@ -9,8 +9,10 @@ using osu.Framework.Bindables; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Platform; using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Tournament.IO; using osu.Game.Online.API; using osu.Game.Overlays; using osu.Game.Rulesets; @@ -40,6 +42,9 @@ namespace osu.Game.Tournament.Screens [Resolved] private RulesetStore rulesets { get; set; } + [Resolved] + private Storage storage { get; set; } + [Resolved(canBeNull: true)] private TournamentSceneManager sceneManager { get; set; } @@ -70,6 +75,7 @@ namespace osu.Game.Tournament.Screens private void reload() { var fileBasedIpc = ipc as FileBasedIPC; + var tourneyStorage = storage as TournamentStorage; fillFlow.Children = new Drawable[] { new ActionableInfo @@ -111,6 +117,13 @@ namespace osu.Game.Tournament.Screens Items = rulesets.AvailableRulesets, Current = LadderInfo.Ruleset, }, + new LabelledDropdown + { + Label = "Current tournament", + Description = "Changes the background videos and bracket to match the selected tournament. This requires a restart after selecting to apply changes.", + Items = tourneyStorage?.ListTournaments(), + Current = tourneyStorage?.CurrentTournament, + }, resolution = new ResolutionSelector { Label = "Stream area resolution", From 191f863a494dcbdab5aabb92c0686ddded29fabf Mon Sep 17 00:00:00 2001 From: Shivam Date: Mon, 7 Dec 2020 13:14:59 +0100 Subject: [PATCH 069/818] Remove unncessary words from the description --- osu.Game.Tournament/Screens/SetupScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Screens/SetupScreen.cs b/osu.Game.Tournament/Screens/SetupScreen.cs index 9f34c36e64..f22cd13cea 100644 --- a/osu.Game.Tournament/Screens/SetupScreen.cs +++ b/osu.Game.Tournament/Screens/SetupScreen.cs @@ -120,7 +120,7 @@ namespace osu.Game.Tournament.Screens new LabelledDropdown { Label = "Current tournament", - Description = "Changes the background videos and bracket to match the selected tournament. This requires a restart after selecting to apply changes.", + Description = "Changes the background videos and bracket to match the selected tournament. This requires a restart to apply changes.", Items = tourneyStorage?.ListTournaments(), Current = tourneyStorage?.CurrentTournament, }, From c778646f101d9f73709c5b76ae339b4ffeaef7c2 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Mon, 7 Dec 2020 18:01:57 +0100 Subject: [PATCH 070/818] Add support for importing replay files. --- osu.Android/OsuGameActivity.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Android/OsuGameActivity.cs b/osu.Android/OsuGameActivity.cs index a56206c969..541455277d 100644 --- a/osu.Android/OsuGameActivity.cs +++ b/osu.Android/OsuGameActivity.cs @@ -13,7 +13,7 @@ using osu.Framework.Android; namespace osu.Android { [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullUser, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = false)] - [IntentFilter(new[] { Intent.ActionDefault, Intent.ActionSend }, Categories = new[] { Intent.CategoryDefault }, DataPathPatterns = new[] { ".*\\.osz", ".*\\.osk" }, DataMimeType = "application/*")] + [IntentFilter(new[] { Intent.ActionDefault, Intent.ActionSend }, Categories = new[] { Intent.CategoryDefault }, DataPathPatterns = new[] { ".*\\.osz", ".*\\.osk", ".*\\.osr" }, DataMimeType = "application/*")] public class OsuGameActivity : AndroidGameActivity { private OsuGameAndroid game; From f980f413244c3252890868df1f32c2f1c88d3f81 Mon Sep 17 00:00:00 2001 From: Shivam Date: Mon, 7 Dec 2020 21:38:15 +0100 Subject: [PATCH 071/818] Address review --- osu.Game.Tournament/IO/TournamentStorage.cs | 6 ++---- osu.Game.Tournament/Screens/SetupScreen.cs | 12 +++++------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tournament/IO/TournamentStorage.cs b/osu.Game.Tournament/IO/TournamentStorage.cs index 66e27ddbb5..cf54c1f5ef 100644 --- a/osu.Game.Tournament/IO/TournamentStorage.cs +++ b/osu.Game.Tournament/IO/TournamentStorage.cs @@ -34,18 +34,16 @@ namespace osu.Game.Tournament.IO else Migrate(UnderlyingStorage.GetStorageForDirectory(default_tournament)); - CurrentTournament = new Bindable(storageConfig.Get(StorageConfig.CurrentTournament)); + CurrentTournament = storageConfig.GetBindable(StorageConfig.CurrentTournament); Logger.Log("Using tournament storage: " + GetFullPath(string.Empty)); - CurrentTournament.BindValueChanged(updateTournament, false); + CurrentTournament.BindValueChanged(updateTournament); } private void updateTournament(ValueChangedEvent newTournament) { ChangeTargetStorage(allTournaments.GetStorageForDirectory(newTournament.NewValue)); Logger.Log("Changing tournament storage: " + GetFullPath(string.Empty)); - storageConfig.Set(StorageConfig.CurrentTournament, newTournament.NewValue); - storageConfig.Save(); } public IEnumerable ListTournaments() => allTournaments.GetDirectories(string.Empty); diff --git a/osu.Game.Tournament/Screens/SetupScreen.cs b/osu.Game.Tournament/Screens/SetupScreen.cs index f22cd13cea..3c9d3c949b 100644 --- a/osu.Game.Tournament/Screens/SetupScreen.cs +++ b/osu.Game.Tournament/Screens/SetupScreen.cs @@ -42,16 +42,14 @@ namespace osu.Game.Tournament.Screens [Resolved] private RulesetStore rulesets { get; set; } - [Resolved] - private Storage storage { get; set; } - [Resolved(canBeNull: true)] private TournamentSceneManager sceneManager { get; set; } private Bindable windowSize; + private TournamentStorage storage; [BackgroundDependencyLoader] - private void load(FrameworkConfigManager frameworkConfig) + private void load(FrameworkConfigManager frameworkConfig, Storage storage) { windowSize = frameworkConfig.GetBindable(FrameworkSetting.WindowedSize); @@ -66,6 +64,7 @@ namespace osu.Game.Tournament.Screens api.LocalUser.BindValueChanged(_ => Schedule(reload)); stableInfo.OnStableInfoSaved += () => Schedule(reload); + this.storage = (TournamentStorage)storage; reload(); } @@ -75,7 +74,6 @@ namespace osu.Game.Tournament.Screens private void reload() { var fileBasedIpc = ipc as FileBasedIPC; - var tourneyStorage = storage as TournamentStorage; fillFlow.Children = new Drawable[] { new ActionableInfo @@ -121,8 +119,8 @@ namespace osu.Game.Tournament.Screens { Label = "Current tournament", Description = "Changes the background videos and bracket to match the selected tournament. This requires a restart to apply changes.", - Items = tourneyStorage?.ListTournaments(), - Current = tourneyStorage?.CurrentTournament, + Items = storage.ListTournaments(), + Current = storage.CurrentTournament, }, resolution = new ResolutionSelector { From 6002014f952ee71992f54c267ed4c9967e5d5639 Mon Sep 17 00:00:00 2001 From: Shivam Date: Mon, 7 Dec 2020 22:07:54 +0100 Subject: [PATCH 072/818] Change underlyingstorage to alltournaments for clarity --- osu.Game.Tournament/IO/TournamentStorage.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament/IO/TournamentStorage.cs b/osu.Game.Tournament/IO/TournamentStorage.cs index cf54c1f5ef..2ba1b6be8f 100644 --- a/osu.Game.Tournament/IO/TournamentStorage.cs +++ b/osu.Game.Tournament/IO/TournamentStorage.cs @@ -29,10 +29,10 @@ namespace osu.Game.Tournament.IO if (storage.Exists("tournament.ini")) { - ChangeTargetStorage(UnderlyingStorage.GetStorageForDirectory(storageConfig.Get(StorageConfig.CurrentTournament))); + ChangeTargetStorage(allTournaments.GetStorageForDirectory(storageConfig.Get(StorageConfig.CurrentTournament))); } else - Migrate(UnderlyingStorage.GetStorageForDirectory(default_tournament)); + Migrate(allTournaments.GetStorageForDirectory(default_tournament)); CurrentTournament = storageConfig.GetBindable(StorageConfig.CurrentTournament); Logger.Log("Using tournament storage: " + GetFullPath(string.Empty)); From 56721a6fa9aec7d9ae55091931e474186862d6b7 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 8 Dec 2020 12:48:13 +0900 Subject: [PATCH 073/818] Compute object position in stack via a pure function --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index a806e623af..76c5afeda7 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -447,8 +447,10 @@ namespace osu.Game.Rulesets.Catch.UI if (caughtObject == null) return; + var positionInStack = computePositionInStack(new Vector2(source.X - X, 0), caughtObject.DisplayRadius); + caughtObject.RelativePositionAxes = Axes.None; - caughtObject.X = source.X - X; + caughtObject.Position = positionInStack; caughtObject.IsOnPlate = true; caughtObject.Anchor = Anchor.TopCentre; @@ -457,8 +459,6 @@ namespace osu.Game.Rulesets.Catch.UI caughtObject.LifetimeStart = source.StartTime; caughtObject.LifetimeEnd = double.MaxValue; - adjustPositionInStack(caughtObject); - caughtFruitContainer.Add(caughtObject); addLighting(caughtObject); @@ -467,22 +467,22 @@ namespace osu.Game.Rulesets.Catch.UI removeFromPlate(caughtObject, DroppedObjectAnimation.Explode); } - private void adjustPositionInStack(DrawablePalpableCatchHitObject caughtObject) + private Vector2 computePositionInStack(Vector2 position, float displayRadius) { const float radius_div_2 = CatchHitObject.OBJECT_RADIUS / 2; const float allowance = 10; - float caughtObjectRadius = caughtObject.DisplayRadius; - - while (caughtFruitContainer.Any(f => Vector2Extensions.Distance(f.Position, caughtObject.Position) < (caughtObjectRadius + radius_div_2) / (allowance / 2))) + while (caughtFruitContainer.Any(f => Vector2Extensions.Distance(f.Position, position) < (displayRadius + radius_div_2) / (allowance / 2))) { - float diff = (caughtObjectRadius + radius_div_2) / allowance; + float diff = (displayRadius + radius_div_2) / allowance; - caughtObject.X += (RNG.NextSingle() - 0.5f) * diff * 2; - caughtObject.Y -= RNG.NextSingle() * diff; + position.X += (RNG.NextSingle() - 0.5f) * diff * 2; + position.Y -= RNG.NextSingle() * diff; } - caughtObject.X = Math.Clamp(caughtObject.X, -CatcherArea.CATCHER_SIZE / 2, CatcherArea.CATCHER_SIZE / 2); + position.X = Math.Clamp(position.X, -CatcherArea.CATCHER_SIZE / 2, CatcherArea.CATCHER_SIZE / 2); + + return position; } private void addLighting(DrawablePalpableCatchHitObject caughtObject) From 004c705aa9da1024078af02f8ef6c87c8468d804 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 8 Dec 2020 20:05:18 +0900 Subject: [PATCH 074/818] Remove ScaleContainer and flatten the Drawable tree of catch DHO --- .../TestSceneFruitRandomness.cs | 47 +++++-------------- .../Objects/Drawables/DrawableBanana.cs | 14 +++--- .../Objects/Drawables/DrawableDroplet.cs | 6 +-- .../Objects/Drawables/DrawableFruit.cs | 6 +-- .../DrawablePalpableCatchHitObject.cs | 14 +----- 5 files changed, 28 insertions(+), 59 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneFruitRandomness.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneFruitRandomness.cs index 2ffebb7de1..e1d817b314 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneFruitRandomness.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneFruitRandomness.cs @@ -11,13 +11,13 @@ namespace osu.Game.Rulesets.Catch.Tests { public class TestSceneFruitRandomness : OsuTestScene { - private readonly TestDrawableFruit drawableFruit; - private readonly TestDrawableBanana drawableBanana; + private readonly DrawableFruit drawableFruit; + private readonly DrawableBanana drawableBanana; public TestSceneFruitRandomness() { - drawableFruit = new TestDrawableFruit(new Fruit()); - drawableBanana = new TestDrawableBanana(new Banana()); + drawableFruit = new DrawableFruit(new Fruit()); + drawableBanana = new DrawableBanana(new Banana()); Add(new TestDrawableCatchHitObjectSpecimen(drawableFruit) { X = -200 }); Add(new TestDrawableCatchHitObjectSpecimen(drawableBanana)); @@ -44,9 +44,9 @@ namespace osu.Game.Rulesets.Catch.Tests { drawableFruit.HitObject.StartTime = drawableBanana.HitObject.StartTime = initial_start_time; - fruitRotation = drawableFruit.InnerRotation; - bananaRotation = drawableBanana.InnerRotation; - bananaScale = drawableBanana.InnerScale; + fruitRotation = drawableFruit.Rotation; + bananaRotation = drawableBanana.Rotation; + bananaScale = drawableBanana.Scale.X; bananaColour = drawableBanana.AccentColour.Value; }); @@ -55,9 +55,9 @@ namespace osu.Game.Rulesets.Catch.Tests drawableFruit.HitObject.StartTime = drawableBanana.HitObject.StartTime = another_start_time; }); - AddAssert("fruit rotation is changed", () => drawableFruit.InnerRotation != fruitRotation); - AddAssert("banana rotation is changed", () => drawableBanana.InnerRotation != bananaRotation); - AddAssert("banana scale is changed", () => drawableBanana.InnerScale != bananaScale); + AddAssert("fruit rotation is changed", () => drawableFruit.Rotation != fruitRotation); + AddAssert("banana rotation is changed", () => drawableBanana.Rotation != bananaRotation); + AddAssert("banana scale is changed", () => drawableBanana.Scale.X != bananaScale); AddAssert("banana colour is changed", () => drawableBanana.AccentColour.Value != bananaColour); AddStep("reset start time", () => @@ -66,31 +66,10 @@ namespace osu.Game.Rulesets.Catch.Tests }); AddAssert("rotation and scale restored", () => - drawableFruit.InnerRotation == fruitRotation && - drawableBanana.InnerRotation == bananaRotation && - drawableBanana.InnerScale == bananaScale && + drawableFruit.Rotation == fruitRotation && + drawableBanana.Rotation == bananaRotation && + drawableBanana.Scale.X == bananaScale && drawableBanana.AccentColour.Value == bananaColour); } - - private class TestDrawableFruit : DrawableFruit - { - public float InnerRotation => ScaleContainer.Rotation; - - public TestDrawableFruit(Fruit h) - : base(h) - { - } - } - - private class TestDrawableBanana : DrawableBanana - { - public float InnerRotation => ScaleContainer.Rotation; - public float InnerScale => ScaleContainer.Scale.X; - - public TestDrawableBanana(Banana h) - : base(h) - { - } - } } } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs index 2a543a0e04..4bc5da2396 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs @@ -24,9 +24,9 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables [BackgroundDependencyLoader] private void load() { - ScaleContainer.Child = new SkinnableDrawable( + AddInternal(new SkinnableDrawable( new CatchSkinComponent(CatchSkinComponents.Banana), - _ => new BananaPiece()); + _ => new BananaPiece())); } protected override void LoadComplete() @@ -44,12 +44,12 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables const float end_scale = 0.6f; const float random_scale_range = 1.6f; - ScaleContainer.ScaleTo(HitObject.Scale * (end_scale + random_scale_range * RandomSingle(3))) - .Then().ScaleTo(HitObject.Scale * end_scale, HitObject.TimePreempt); + this.ScaleTo(HitObject.Scale * (end_scale + random_scale_range * RandomSingle(3))) + .Then().ScaleTo(HitObject.Scale * end_scale, HitObject.TimePreempt); - ScaleContainer.RotateTo(getRandomAngle(1)) - .Then() - .RotateTo(getRandomAngle(2), HitObject.TimePreempt); + this.RotateTo(getRandomAngle(1)) + .Then() + .RotateTo(getRandomAngle(2), HitObject.TimePreempt); float getRandomAngle(int series) => 180 * (RandomSingle(series) * 2 - 1); } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs index 81c8de2e59..d96b1c8902 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs @@ -26,9 +26,9 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables [BackgroundDependencyLoader] private void load() { - ScaleContainer.Child = new SkinnableDrawable( + AddInternal(new SkinnableDrawable( new CatchSkinComponent(CatchSkinComponents.Droplet), - _ => new DropletPiece()); + _ => new DropletPiece())); } protected override void UpdateInitialTransforms() @@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables float startRotation = RandomSingle(1) * 20; double duration = HitObject.TimePreempt + 2000; - ScaleContainer.RotateTo(startRotation).RotateTo(startRotation + 720, duration); + this.RotateTo(startRotation).RotateTo(startRotation + 720, duration); } } } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs index 0fcd319a93..8135a31b43 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs @@ -32,16 +32,16 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables VisualRepresentation.Value = (FruitVisualRepresentation)(change.NewValue % 4); }, true); - ScaleContainer.Child = new SkinnableDrawable( + AddInternal(new SkinnableDrawable( new CatchSkinComponent(CatchSkinComponents.Fruit), - _ => new FruitPiece()); + _ => new FruitPiece())); } protected override void UpdateInitialTransforms() { base.UpdateInitialTransforms(); - ScaleContainer.RotateTo((RandomSingle(1) - 0.5f) * 40); + this.RotateTo((RandomSingle(1) - 0.5f) * 40); } } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs index 0877b5e248..b99710ae8e 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs @@ -5,7 +5,6 @@ using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osuTK; namespace osu.Game.Rulesets.Catch.Objects.Drawables @@ -21,7 +20,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables public readonly Bindable IndexInBeatmap = new Bindable(); /// - /// The multiplicative factor applied to scale relative to scale. + /// The multiplicative factor applied to relative to scale. /// protected virtual float ScaleFactor => 1; @@ -32,20 +31,11 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables public float DisplayRadius => CatchHitObject.OBJECT_RADIUS * HitObject.Scale * ScaleFactor; - protected readonly Container ScaleContainer; - protected DrawablePalpableCatchHitObject([CanBeNull] CatchHitObject h) : base(h) { Origin = Anchor.Centre; Size = new Vector2(CatchHitObject.OBJECT_RADIUS * 2); - - AddInternal(ScaleContainer = new Container - { - RelativeSizeAxes = Axes.Both, - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - }); } [BackgroundDependencyLoader] @@ -58,7 +48,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables ScaleBindable.BindValueChanged(scale => { - ScaleContainer.Scale = new Vector2(scale.NewValue * ScaleFactor); + Scale = new Vector2(scale.NewValue * ScaleFactor); }, true); IndexInBeatmap.BindValueChanged(_ => UpdateComboColour()); From 94a59ac3b240c75a00e440f2062ddbd7d9cec6e5 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 8 Dec 2020 20:41:26 +0900 Subject: [PATCH 075/818] Make catch hit lighting logic not dependent on caught object --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 33 +++++++++++++++------------ 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 76c5afeda7..ac0f25e21b 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -215,10 +215,19 @@ namespace osu.Game.Rulesets.Catch.UI catchResult.CatcherAnimationState = CurrentState; catchResult.CatcherHyperDash = HyperDashing; - if (!(drawableObject.HitObject is PalpableCatchHitObject hitObject)) return; + if (!(drawableObject is DrawablePalpableCatchHitObject palpableObject)) return; + + var hitObject = palpableObject.HitObject; if (result.IsHit) - placeCaughtObject(hitObject); + { + var positionInStack = computePositionInStack(new Vector2(palpableObject.X - X, 0), palpableObject.DisplayRadius); + + placeCaughtObject(hitObject, positionInStack); + + if (hitLighting.Value) + addLighting(hitObject, positionInStack.X, drawableObject.AccentColour.Value); + } // droplet doesn't affect the catcher state if (hitObject is TinyDroplet) return; @@ -441,16 +450,14 @@ namespace osu.Game.Rulesets.Catch.UI updateCatcher(); } - private void placeCaughtObject(PalpableCatchHitObject source) + private void placeCaughtObject(PalpableCatchHitObject source, Vector2 position) { var caughtObject = createCaughtObject(source); if (caughtObject == null) return; - var positionInStack = computePositionInStack(new Vector2(source.X - X, 0), caughtObject.DisplayRadius); - caughtObject.RelativePositionAxes = Axes.None; - caughtObject.Position = positionInStack; + caughtObject.Position = position; caughtObject.IsOnPlate = true; caughtObject.Anchor = Anchor.TopCentre; @@ -461,8 +468,6 @@ namespace osu.Game.Rulesets.Catch.UI caughtFruitContainer.Add(caughtObject); - addLighting(caughtObject); - if (!caughtObject.StaysOnPlate) removeFromPlate(caughtObject, DroppedObjectAnimation.Explode); } @@ -485,15 +490,13 @@ namespace osu.Game.Rulesets.Catch.UI return position; } - private void addLighting(DrawablePalpableCatchHitObject caughtObject) + private void addLighting(CatchHitObject hitObject, float x, Color4 colour) { - if (!hitLighting.Value) return; - HitExplosion hitExplosion = hitExplosionPool.Get(); - hitExplosion.HitObject = caughtObject.HitObject; - hitExplosion.X = caughtObject.X; - hitExplosion.Scale = new Vector2(caughtObject.HitObject.Scale); - hitExplosion.ObjectColour = caughtObject.AccentColour.Value; + hitExplosion.HitObject = hitObject; + hitExplosion.X = x; + hitExplosion.Scale = new Vector2(hitObject.Scale); + hitExplosion.ObjectColour = colour; hitExplosionContainer.Add(hitExplosion); } From be4a668e0b83025c6c7d87f5964ea4887e62d606 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 4 Dec 2020 15:34:31 +0900 Subject: [PATCH 076/818] Add basic structure for multiplayer state and model components --- .../RealtimeMultiplayer/ISpectatorClient.cs | 19 +++++++++++++++++++ .../RealtimeMultiplayer/ISpectatorServer.cs | 13 +++++++++++++ .../RealtimeMultiplayer/MultiplayerRoom.cs | 15 +++++++++++++++ .../MultiplayerRoomState.cs | 17 +++++++++++++++++ 4 files changed, 64 insertions(+) create mode 100644 osu.Game/Online/RealtimeMultiplayer/ISpectatorClient.cs create mode 100644 osu.Game/Online/RealtimeMultiplayer/ISpectatorServer.cs create mode 100644 osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs create mode 100644 osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomState.cs diff --git a/osu.Game/Online/RealtimeMultiplayer/ISpectatorClient.cs b/osu.Game/Online/RealtimeMultiplayer/ISpectatorClient.cs new file mode 100644 index 0000000000..8054c489c5 --- /dev/null +++ b/osu.Game/Online/RealtimeMultiplayer/ISpectatorClient.cs @@ -0,0 +1,19 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Threading.Tasks; + +namespace osu.Game.Online.RealtimeMultiplayer +{ + /// + /// An interface defining a spectator client instance. + /// + public interface IMultiplayerClient + { + /// + /// Signals that the room has changed state. + /// + /// The state of the room. + Task RoomStateChanged(MultiplayerRoomState state); + } +} diff --git a/osu.Game/Online/RealtimeMultiplayer/ISpectatorServer.cs b/osu.Game/Online/RealtimeMultiplayer/ISpectatorServer.cs new file mode 100644 index 0000000000..3d00eaaf6a --- /dev/null +++ b/osu.Game/Online/RealtimeMultiplayer/ISpectatorServer.cs @@ -0,0 +1,13 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Online.RealtimeMultiplayer +{ + /// + /// An interface defining the spectator server instance. + /// + public interface IMultiplayerServer + { + // TODO: implement + } +} diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs new file mode 100644 index 0000000000..ee1f42f7bf --- /dev/null +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs @@ -0,0 +1,15 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; + +namespace osu.Game.Online.RealtimeMultiplayer +{ + [Serializable] + public class MultiplayerRoom + { + public long RoomID { get; set; } + + public MultiplayerRoomState State { get; set; } + } +} diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomState.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomState.cs new file mode 100644 index 0000000000..60e47e9027 --- /dev/null +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomState.cs @@ -0,0 +1,17 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Online.RealtimeMultiplayer +{ + /// + /// The current overall state of a realtime multiplayer room. + /// + public enum MultiplayerRoomState + { + Open, + WaitingForLoad, + Playing, + WaitingForResults, + Closed + } +} From daed27460c8f2b29349401d59ac4026f2fcf8e48 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 4 Dec 2020 17:23:57 +0900 Subject: [PATCH 077/818] Add simple user state class --- .../RealtimeMultiplayer/MultiplayerClientState.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 osu.Game/Online/RealtimeMultiplayer/MultiplayerClientState.cs diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerClientState.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerClientState.cs new file mode 100644 index 0000000000..ac76f8999a --- /dev/null +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerClientState.cs @@ -0,0 +1,15 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Online.RealtimeMultiplayer +{ + public class MultiplayerClientState + { + public MultiplayerClientState(in long roomId) + { + CurrentRoomID = roomId; + } + + public long CurrentRoomID { get; } + } +} From f4ccbbd09261ac4ba63d4633d3d884841b4139c9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 4 Dec 2020 18:18:41 +0900 Subject: [PATCH 078/818] Add basic server implementation --- .../RealtimeMultiplayer/ISpectatorServer.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/ISpectatorServer.cs b/osu.Game/Online/RealtimeMultiplayer/ISpectatorServer.cs index 3d00eaaf6a..ba7d7d3832 100644 --- a/osu.Game/Online/RealtimeMultiplayer/ISpectatorServer.cs +++ b/osu.Game/Online/RealtimeMultiplayer/ISpectatorServer.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.Threading.Tasks; + namespace osu.Game.Online.RealtimeMultiplayer { /// @@ -8,6 +10,17 @@ namespace osu.Game.Online.RealtimeMultiplayer /// public interface IMultiplayerServer { - // TODO: implement + /// + /// Request to join a multiplayer room. + /// + /// The databased room ID. + /// Whether the room could be joined. + Task JoinRoom(long roomId); + + /// + /// Request to leave the currently joined room. + /// + /// The databased room ID. + Task LeaveRoom(long roomId); } } From 8ebdb5723b19827960f08a60e3e0c252fb188208 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 7 Dec 2020 18:50:02 +0900 Subject: [PATCH 079/818] Add models for users and rooms --- .../RealtimeMultiplayer/MultiplayerRoom.cs | 9 ++++++++ .../MultiplayerRoomUser.cs | 21 +++++++++++++++++++ .../MultiplayerUserState.cs | 13 ++++++++++++ 3 files changed, 43 insertions(+) create mode 100644 osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomUser.cs create mode 100644 osu.Game/Online/RealtimeMultiplayer/MultiplayerUserState.cs diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs index ee1f42f7bf..f42f8d7f5f 100644 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; namespace osu.Game.Online.RealtimeMultiplayer { @@ -11,5 +12,13 @@ namespace osu.Game.Online.RealtimeMultiplayer public long RoomID { get; set; } public MultiplayerRoomState State { get; set; } + + public IReadOnlyList Users => users; + + private List users = new List(); + + public void Join(int user) => users.Add(new MultiplayerRoomUser(user)); + + public void Leave(int user) => users.RemoveAll(u => u.UserID == user); } } diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomUser.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomUser.cs new file mode 100644 index 0000000000..c5fed1629c --- /dev/null +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomUser.cs @@ -0,0 +1,21 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Users; + +namespace osu.Game.Online.RealtimeMultiplayer +{ + public class MultiplayerRoomUser + { + public MultiplayerRoomUser(in int userId) + { + UserID = userId; + } + + public long UserID { get; set; } + + public MultiplayerUserState State { get; set; } + + public User User { get; set; } + } +} diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerUserState.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerUserState.cs new file mode 100644 index 0000000000..6d6baa8017 --- /dev/null +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerUserState.cs @@ -0,0 +1,13 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Online.RealtimeMultiplayer +{ + public enum MultiplayerUserState + { + Watching, + Ready, + Playing, + Results, + } +} From fdf025942b526ad34ce1a8cd25bd55f0acb6980c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Dec 2020 01:24:38 +0900 Subject: [PATCH 080/818] Ensure room is locked when mutating users --- .../RealtimeMultiplayer/MultiplayerRoom.cs | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs index f42f8d7f5f..1a02dab5e0 100644 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs @@ -9,16 +9,33 @@ namespace osu.Game.Online.RealtimeMultiplayer [Serializable] public class MultiplayerRoom { + private object writeLock = new object(); + public long RoomID { get; set; } public MultiplayerRoomState State { get; set; } - public IReadOnlyList Users => users; - private List users = new List(); - public void Join(int user) => users.Add(new MultiplayerRoomUser(user)); + public IReadOnlyList Users + { + get + { + lock (writeLock) + return users.ToArray(); + } + } - public void Leave(int user) => users.RemoveAll(u => u.UserID == user); + public void Join(int user) + { + lock (writeLock) + users.Add(new MultiplayerRoomUser(user)); + } + + public void Leave(int user) + { + lock (writeLock) + users.RemoveAll(u => u.UserID == user); + } } } From ca86524c922012b32e7a1420c54b49d96a6df3d6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Dec 2020 01:35:29 +0900 Subject: [PATCH 081/818] Add locking on join/leave operations --- .../RealtimeMultiplayer/MultiplayerRoom.cs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs index 1a02dab5e0..db3993255a 100644 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs @@ -26,16 +26,25 @@ namespace osu.Game.Online.RealtimeMultiplayer } } - public void Join(int user) + public MultiplayerRoomUser Join(int userId) { - lock (writeLock) - users.Add(new MultiplayerRoomUser(user)); + var user = new MultiplayerRoomUser(userId); + lock (writeLock) users.Add(user); + return user; } - public void Leave(int user) + public MultiplayerRoomUser Leave(int userId) { lock (writeLock) - users.RemoveAll(u => u.UserID == user); + { + var user = users.Find(u => u.UserID == userId); + + if (user == null) + return null; + + users.Remove(user); + return user; + } } } } From ff52a5ddc6bc5cbb4d5dfdf52fed460b6e660826 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Dec 2020 01:35:42 +0900 Subject: [PATCH 082/818] Add callbacks for join/leave events to notify other room occupants --- .../Online/RealtimeMultiplayer/ISpectatorClient.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/osu.Game/Online/RealtimeMultiplayer/ISpectatorClient.cs b/osu.Game/Online/RealtimeMultiplayer/ISpectatorClient.cs index 8054c489c5..d5f6baad43 100644 --- a/osu.Game/Online/RealtimeMultiplayer/ISpectatorClient.cs +++ b/osu.Game/Online/RealtimeMultiplayer/ISpectatorClient.cs @@ -15,5 +15,17 @@ namespace osu.Game.Online.RealtimeMultiplayer /// /// The state of the room. Task RoomStateChanged(MultiplayerRoomState state); + + /// + /// Signals that a user has joined the room. + /// + /// The user. + Task UserJoined(MultiplayerRoomUser user); + + /// + /// Signals that a user has left the room. + /// + /// The user. + Task UserLeft(MultiplayerRoomUser user); } } From 6e5846d91b7389575a91691851f6311de8a5cddc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Dec 2020 14:33:01 +0900 Subject: [PATCH 083/818] Fix serialization failure due to missing set --- osu.Game/Online/RealtimeMultiplayer/MultiplayerClientState.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerClientState.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerClientState.cs index ac76f8999a..17c209ba26 100644 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerClientState.cs +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerClientState.cs @@ -5,11 +5,11 @@ namespace osu.Game.Online.RealtimeMultiplayer { public class MultiplayerClientState { + public long CurrentRoomID { get; set; } + public MultiplayerClientState(in long roomId) { CurrentRoomID = roomId; } - - public long CurrentRoomID { get; } } } From baf16cfbc3a30082996acb9bde002b58ff815105 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Dec 2020 14:33:38 +0900 Subject: [PATCH 084/818] Add room settings related model and event flow --- .../RealtimeMultiplayer/ISpectatorClient.cs | 6 +++++ .../RealtimeMultiplayer/MultiplayerRoom.cs | 2 ++ .../MultiplayerRoomSettings.cs | 25 +++++++++++++++++++ 3 files changed, 33 insertions(+) create mode 100644 osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomSettings.cs diff --git a/osu.Game/Online/RealtimeMultiplayer/ISpectatorClient.cs b/osu.Game/Online/RealtimeMultiplayer/ISpectatorClient.cs index d5f6baad43..1e6832e728 100644 --- a/osu.Game/Online/RealtimeMultiplayer/ISpectatorClient.cs +++ b/osu.Game/Online/RealtimeMultiplayer/ISpectatorClient.cs @@ -27,5 +27,11 @@ namespace osu.Game.Online.RealtimeMultiplayer /// /// The user. Task UserLeft(MultiplayerRoomUser user); + + /// + /// Signals that the settings for this room have changed. + /// + /// The updated room settings. + Task SettingsChanged(MultiplayerRoomSettings newSettings); } } diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs index db3993255a..61320b9d73 100644 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs @@ -15,6 +15,8 @@ namespace osu.Game.Online.RealtimeMultiplayer public MultiplayerRoomState State { get; set; } + public MultiplayerRoomSettings Settings { get; set; } + private List users = new List(); public IReadOnlyList Users diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomSettings.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomSettings.cs new file mode 100644 index 0000000000..4c2c014cd9 --- /dev/null +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomSettings.cs @@ -0,0 +1,25 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using JetBrains.Annotations; +using osu.Game.Online.API; + +namespace osu.Game.Online.RealtimeMultiplayer +{ + public class MultiplayerRoomSettings : IEquatable + { + public int? BeatmapID { get; set; } + + public int? RulesetID { get; set; } + + [NotNull] + public IEnumerable Mods { get; set; } = Enumerable.Empty(); + + public bool Equals(MultiplayerRoomSettings other) => BeatmapID == other?.BeatmapID && Mods.SequenceEqual(other?.Mods) && RulesetID == other?.RulesetID; + + public override string ToString() => $"Beatmap:{BeatmapID} Mods:{string.Join(',', Mods)} Ruleset:{RulesetID}"; + } +} From 882ace6efea12be861efc400d5b61e0a8dd48a0d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Dec 2020 14:33:47 +0900 Subject: [PATCH 085/818] Make MultiplayerRoomUser equatable --- .../MultiplayerRoomUser.cs | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomUser.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomUser.cs index c5fed1629c..3daebe3dda 100644 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomUser.cs +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomUser.cs @@ -1,21 +1,41 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Game.Users; namespace osu.Game.Online.RealtimeMultiplayer { - public class MultiplayerRoomUser + public class MultiplayerRoomUser : IEquatable { public MultiplayerRoomUser(in int userId) { UserID = userId; } - public long UserID { get; set; } + public long UserID { get; } public MultiplayerUserState State { get; set; } public User User { get; set; } + + public bool Equals(MultiplayerRoomUser other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + + return UserID == other.UserID; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + + return Equals((MultiplayerRoomUser)obj); + } + + public override int GetHashCode() => UserID.GetHashCode(); } } From e193f8214df514f770d3bcad9ef41ce053255ed8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Dec 2020 14:39:26 +0900 Subject: [PATCH 086/818] Remove unnecessary room id from leave room request --- osu.Game/Online/RealtimeMultiplayer/ISpectatorServer.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/ISpectatorServer.cs b/osu.Game/Online/RealtimeMultiplayer/ISpectatorServer.cs index ba7d7d3832..053ffa0462 100644 --- a/osu.Game/Online/RealtimeMultiplayer/ISpectatorServer.cs +++ b/osu.Game/Online/RealtimeMultiplayer/ISpectatorServer.cs @@ -20,7 +20,6 @@ namespace osu.Game.Online.RealtimeMultiplayer /// /// Request to leave the currently joined room. /// - /// The databased room ID. - Task LeaveRoom(long roomId); + Task LeaveRoom(); } } From b3bdaaa7b59dc6a58f4448a7a236e0b8f2756f6f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Dec 2020 14:49:36 +0900 Subject: [PATCH 087/818] Move exceptions to common code --- .../RealtimeMultiplayer/InvalidStateException.cs | 15 +++++++++++++++ .../RealtimeMultiplayer/NotJoinedRoomException.cs | 12 ++++++++++++ .../UserAlreadyInMultiplayerRoom.cs | 15 +++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 osu.Game/Online/RealtimeMultiplayer/InvalidStateException.cs create mode 100644 osu.Game/Online/RealtimeMultiplayer/NotJoinedRoomException.cs create mode 100644 osu.Game/Online/RealtimeMultiplayer/UserAlreadyInMultiplayerRoom.cs diff --git a/osu.Game/Online/RealtimeMultiplayer/InvalidStateException.cs b/osu.Game/Online/RealtimeMultiplayer/InvalidStateException.cs new file mode 100644 index 0000000000..8393e7e925 --- /dev/null +++ b/osu.Game/Online/RealtimeMultiplayer/InvalidStateException.cs @@ -0,0 +1,15 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; + +namespace osu.Game.Online.RealtimeMultiplayer +{ + public class InvalidStateException : Exception + { + public InvalidStateException(string message) + : base(message) + { + } + } +} diff --git a/osu.Game/Online/RealtimeMultiplayer/NotJoinedRoomException.cs b/osu.Game/Online/RealtimeMultiplayer/NotJoinedRoomException.cs new file mode 100644 index 0000000000..1b264d1ac5 --- /dev/null +++ b/osu.Game/Online/RealtimeMultiplayer/NotJoinedRoomException.cs @@ -0,0 +1,12 @@ +using System; + +namespace osu.Game.Online.RealtimeMultiplayer +{ + public class NotJoinedRoomException : Exception + { + public NotJoinedRoomException() + : base("This user has not yet joined a multiplayer room.") + { + } + } +} \ No newline at end of file diff --git a/osu.Game/Online/RealtimeMultiplayer/UserAlreadyInMultiplayerRoom.cs b/osu.Game/Online/RealtimeMultiplayer/UserAlreadyInMultiplayerRoom.cs new file mode 100644 index 0000000000..9a2090c710 --- /dev/null +++ b/osu.Game/Online/RealtimeMultiplayer/UserAlreadyInMultiplayerRoom.cs @@ -0,0 +1,15 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; + +namespace osu.Game.Online.RealtimeMultiplayer +{ + public class UserAlreadyInMultiplayerRoom : Exception + { + public UserAlreadyInMultiplayerRoom() + : base("This user is already in a room.") + { + } + } +} From 327799c263bd923f4b7b51b8e675914aabb1d74f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Dec 2020 14:51:48 +0900 Subject: [PATCH 088/818] Rename multiplayer server file to match class --- .../{ISpectatorServer.cs => IMultiplayerServer.cs} | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) rename osu.Game/Online/RealtimeMultiplayer/{ISpectatorServer.cs => IMultiplayerServer.cs} (66%) diff --git a/osu.Game/Online/RealtimeMultiplayer/ISpectatorServer.cs b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs similarity index 66% rename from osu.Game/Online/RealtimeMultiplayer/ISpectatorServer.cs rename to osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs index 053ffa0462..f6eb3b8ff5 100644 --- a/osu.Game/Online/RealtimeMultiplayer/ISpectatorServer.cs +++ b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs @@ -1,6 +1,3 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - using System.Threading.Tasks; namespace osu.Game.Online.RealtimeMultiplayer @@ -14,12 +11,12 @@ namespace osu.Game.Online.RealtimeMultiplayer /// Request to join a multiplayer room. /// /// The databased room ID. - /// Whether the room could be joined. - Task JoinRoom(long roomId); + /// If the user is already in the requested (or another) room. + Task JoinRoom(long roomId); /// /// Request to leave the currently joined room. /// Task LeaveRoom(); } -} +} \ No newline at end of file From 5a231cef1507289a2da1c4a719e26b195edccf01 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Dec 2020 15:46:11 +0900 Subject: [PATCH 089/818] Add thread safety for external operations on MultiplayerRoom --- .../RealtimeMultiplayer/MultiplayerRoom.cs | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs index 61320b9d73..7c3724915f 100644 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs @@ -9,8 +9,6 @@ namespace osu.Game.Online.RealtimeMultiplayer [Serializable] public class MultiplayerRoom { - private object writeLock = new object(); - public long RoomID { get; set; } public MultiplayerRoomState State { get; set; } @@ -31,22 +29,34 @@ namespace osu.Game.Online.RealtimeMultiplayer public MultiplayerRoomUser Join(int userId) { var user = new MultiplayerRoomUser(userId); - lock (writeLock) users.Add(user); + PerformUpdate(_ => users.Add(user)); return user; } public MultiplayerRoomUser Leave(int userId) { - lock (writeLock) + MultiplayerRoomUser user = null; + + PerformUpdate(_ => { - var user = users.Find(u => u.UserID == userId); + user = users.Find(u => u.UserID == userId); - if (user == null) - return null; + if (user != null) + users.Remove(user); + }); - users.Remove(user); - return user; - } + return user; + } + + private object writeLock = new object(); + + /// + /// Perform an update on this room in a thread-safe manner. + /// + /// The action to perform. + public void PerformUpdate(Action action) + { + lock (writeLock) action(this); } } } From 4f449ba8217fa7ce930d2f608f55464d9da90ff0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Dec 2020 15:49:08 +0900 Subject: [PATCH 090/818] Rename idle state --- osu.Game/Online/RealtimeMultiplayer/MultiplayerUserState.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerUserState.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerUserState.cs index 6d6baa8017..47a1226e29 100644 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerUserState.cs +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerUserState.cs @@ -5,7 +5,7 @@ namespace osu.Game.Online.RealtimeMultiplayer { public enum MultiplayerUserState { - Watching, + Idle, Ready, Playing, Results, From 42b1e9d6a4185cea488227a159de1d256456bb4b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Dec 2020 15:54:52 +0900 Subject: [PATCH 091/818] Add xmldoc coverage of MultiplayerRoom --- .../RealtimeMultiplayer/MultiplayerRoom.cs | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs index 7c3724915f..4bd8411344 100644 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs @@ -9,14 +9,28 @@ namespace osu.Game.Online.RealtimeMultiplayer [Serializable] public class MultiplayerRoom { + /// + /// The ID of the room, used for database persistence. + /// public long RoomID { get; set; } + /// + /// The current state of the room (ie. whether it is in progress or otherwise). + /// public MultiplayerRoomState State { get; set; } + /// + /// All currently enforced game settings for this room. + /// public MultiplayerRoomSettings Settings { get; set; } private List users = new List(); + private object writeLock = new object(); + + /// + /// All users which are currently in this room, in any state. + /// public IReadOnlyList Users { get @@ -26,6 +40,9 @@ namespace osu.Game.Online.RealtimeMultiplayer } } + /// + /// Join a new user to this room. + /// public MultiplayerRoomUser Join(int userId) { var user = new MultiplayerRoomUser(userId); @@ -33,6 +50,9 @@ namespace osu.Game.Online.RealtimeMultiplayer return user; } + /// + /// Remove a user from this room. + /// public MultiplayerRoomUser Leave(int userId) { MultiplayerRoomUser user = null; @@ -48,8 +68,6 @@ namespace osu.Game.Online.RealtimeMultiplayer return user; } - private object writeLock = new object(); - /// /// Perform an update on this room in a thread-safe manner. /// From 2365d656104a66233ffe983907db64c5515b108c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Dec 2020 16:02:35 +0900 Subject: [PATCH 092/818] Move business logic out of MultiplayerRoom --- .../RealtimeMultiplayer/MultiplayerRoom.cs | 48 +++---------------- 1 file changed, 7 insertions(+), 41 deletions(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs index 4bd8411344..dc7ced7a9a 100644 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs @@ -6,6 +6,9 @@ using System.Collections.Generic; namespace osu.Game.Online.RealtimeMultiplayer { + /// + /// A multiplayer room. + /// [Serializable] public class MultiplayerRoom { @@ -24,50 +27,13 @@ namespace osu.Game.Online.RealtimeMultiplayer /// public MultiplayerRoomSettings Settings { get; set; } - private List users = new List(); + /// + /// All users currently in this room. + /// + public List Users { get; set; } = new List(); private object writeLock = new object(); - /// - /// All users which are currently in this room, in any state. - /// - public IReadOnlyList Users - { - get - { - lock (writeLock) - return users.ToArray(); - } - } - - /// - /// Join a new user to this room. - /// - public MultiplayerRoomUser Join(int userId) - { - var user = new MultiplayerRoomUser(userId); - PerformUpdate(_ => users.Add(user)); - return user; - } - - /// - /// Remove a user from this room. - /// - public MultiplayerRoomUser Leave(int userId) - { - MultiplayerRoomUser user = null; - - PerformUpdate(_ => - { - user = users.Find(u => u.UserID == userId); - - if (user != null) - users.Remove(user); - }); - - return user; - } - /// /// Perform an update on this room in a thread-safe manner. /// From 5d2ca7fc39be724fb6098b2c36076131220406c5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Dec 2020 16:15:51 +0900 Subject: [PATCH 093/818] Make remaining model classes nullable and serializable --- .../MultiplayerClientState.cs | 2 ++ .../RealtimeMultiplayer/MultiplayerRoom.cs | 9 ++++++++- .../MultiplayerRoomSettings.cs | 7 ++++++- .../MultiplayerRoomState.cs | 2 ++ .../MultiplayerRoomUser.cs | 19 ++++++++++--------- 5 files changed, 28 insertions(+), 11 deletions(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerClientState.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerClientState.cs index 17c209ba26..8c440abba3 100644 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerClientState.cs +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerClientState.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + namespace osu.Game.Online.RealtimeMultiplayer { public class MultiplayerClientState diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs index dc7ced7a9a..e70e12c89e 100644 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + using System; using System.Collections.Generic; @@ -25,13 +27,18 @@ namespace osu.Game.Online.RealtimeMultiplayer /// /// All currently enforced game settings for this room. /// - public MultiplayerRoomSettings Settings { get; set; } + public MultiplayerRoomSettings Settings { get; set; } = MultiplayerRoomSettings.Empty(); /// /// All users currently in this room. /// public List Users { get; set; } = new List(); + /// + /// The host of this room, in control of changing room settings. + /// + public MultiplayerRoomUser? Host { get; set; } + private object writeLock = new object(); /// diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomSettings.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomSettings.cs index 4c2c014cd9..3137afa8a4 100644 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomSettings.cs +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomSettings.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + using System; using System.Collections.Generic; using System.Linq; @@ -9,6 +11,7 @@ using osu.Game.Online.API; namespace osu.Game.Online.RealtimeMultiplayer { + [Serializable] public class MultiplayerRoomSettings : IEquatable { public int? BeatmapID { get; set; } @@ -18,8 +21,10 @@ namespace osu.Game.Online.RealtimeMultiplayer [NotNull] public IEnumerable Mods { get; set; } = Enumerable.Empty(); - public bool Equals(MultiplayerRoomSettings other) => BeatmapID == other?.BeatmapID && Mods.SequenceEqual(other?.Mods) && RulesetID == other?.RulesetID; + public bool Equals(MultiplayerRoomSettings other) => BeatmapID == other.BeatmapID && Mods.SequenceEqual(other.Mods) && RulesetID == other.RulesetID; public override string ToString() => $"Beatmap:{BeatmapID} Mods:{string.Join(',', Mods)} Ruleset:{RulesetID}"; + + public static MultiplayerRoomSettings Empty() => new MultiplayerRoomSettings(); } } diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomState.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomState.cs index 60e47e9027..9f76cd945e 100644 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomState.cs +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomState.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + namespace osu.Game.Online.RealtimeMultiplayer { /// diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomUser.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomUser.cs index 3daebe3dda..17aca4458a 100644 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomUser.cs +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomUser.cs @@ -1,27 +1,29 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + using System; using osu.Game.Users; namespace osu.Game.Online.RealtimeMultiplayer { + [Serializable] public class MultiplayerRoomUser : IEquatable { + public readonly long UserID; + + public MultiplayerUserState State { get; set; } = MultiplayerUserState.Idle; + + public User? User { get; set; } + public MultiplayerRoomUser(in int userId) { UserID = userId; } - public long UserID { get; } - - public MultiplayerUserState State { get; set; } - - public User User { get; set; } - public bool Equals(MultiplayerRoomUser other) { - if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; return UserID == other.UserID; @@ -29,9 +31,8 @@ namespace osu.Game.Online.RealtimeMultiplayer public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; + if (obj.GetType() != GetType()) return false; return Equals((MultiplayerRoomUser)obj); } From 5f5c0d55336a2228becd3021e2ee348c56c55d58 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Dec 2020 16:15:58 +0900 Subject: [PATCH 094/818] Return room model when joining --- osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs index f6eb3b8ff5..a05a4c1e11 100644 --- a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs +++ b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs @@ -12,7 +12,7 @@ namespace osu.Game.Online.RealtimeMultiplayer /// /// The databased room ID. /// If the user is already in the requested (or another) room. - Task JoinRoom(long roomId); + Task JoinRoom(long roomId); /// /// Request to leave the currently joined room. From 71de7ce0a3abc0af75d210d54ceafca9c1f0f5b2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Dec 2020 16:32:45 +0900 Subject: [PATCH 095/818] Add missing methods to server interface --- .../RealtimeMultiplayer/IMultiplayerServer.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs index a05a4c1e11..44d7ecc2e9 100644 --- a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs +++ b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs @@ -18,5 +18,17 @@ namespace osu.Game.Online.RealtimeMultiplayer /// Request to leave the currently joined room. /// Task LeaveRoom(); + + /// + /// Transfer the host of the currently joined room to another user in the room. + /// + /// The new user which is to become host. + Task TransferHost(long userId); + + /// + /// As the host, update the settings of the currently joined room. + /// + /// The new settings to apply. + Task ChangeSettings(MultiplayerRoomSettings settings); } -} \ No newline at end of file +} From a4ca8d2998dc9fa222aceeb09ae90bf013bdf542 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Dec 2020 16:44:47 +0900 Subject: [PATCH 096/818] Ensure multiplayer rooms are instantiated with a room ID --- osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs index e70e12c89e..8a88d8fc90 100644 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs @@ -17,7 +17,7 @@ namespace osu.Game.Online.RealtimeMultiplayer /// /// The ID of the room, used for database persistence. /// - public long RoomID { get; set; } + public readonly long RoomID; /// /// The current state of the room (ie. whether it is in progress or otherwise). @@ -41,6 +41,11 @@ namespace osu.Game.Online.RealtimeMultiplayer private object writeLock = new object(); + public MultiplayerRoom(in long roomId) + { + RoomID = roomId; + } + /// /// Perform an update on this room in a thread-safe manner. /// From dbe048cdc6b6d893e92109d50fd3fa5732fa294c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Dec 2020 17:03:44 +0900 Subject: [PATCH 097/818] Add client method for notifying about host changes --- osu.Game/Online/RealtimeMultiplayer/ISpectatorClient.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Online/RealtimeMultiplayer/ISpectatorClient.cs b/osu.Game/Online/RealtimeMultiplayer/ISpectatorClient.cs index 1e6832e728..f3dff0de86 100644 --- a/osu.Game/Online/RealtimeMultiplayer/ISpectatorClient.cs +++ b/osu.Game/Online/RealtimeMultiplayer/ISpectatorClient.cs @@ -28,6 +28,12 @@ namespace osu.Game.Online.RealtimeMultiplayer /// The user. Task UserLeft(MultiplayerRoomUser user); + /// + /// Signal that the host of the room has changed. + /// + /// The user ID of the new host. + Task HostChanged(long userId); + /// /// Signals that the settings for this room have changed. /// From 11a7057289b20a195bd1be91e1a11681d2f29eb2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Dec 2020 17:41:56 +0900 Subject: [PATCH 098/818] Add notification flow for user state changes in room --- .../RealtimeMultiplayer/IMultiplayerServer.cs | 7 +++++++ .../RealtimeMultiplayer/ISpectatorClient.cs | 7 +++++++ .../RealtimeMultiplayer/InvalidStateChange.cs | 15 +++++++++++++++ 3 files changed, 29 insertions(+) create mode 100644 osu.Game/Online/RealtimeMultiplayer/InvalidStateChange.cs diff --git a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs index 44d7ecc2e9..ad4aa5d2c2 100644 --- a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs +++ b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs @@ -30,5 +30,12 @@ namespace osu.Game.Online.RealtimeMultiplayer /// /// The new settings to apply. Task ChangeSettings(MultiplayerRoomSettings settings); + + /// + /// Change the local user state in the currently joined room. + /// + /// The proposed new state. + /// If the state change requested is not valid, given the previous state or room state. + Task ChangeState(MultiplayerUserState newState); } } diff --git a/osu.Game/Online/RealtimeMultiplayer/ISpectatorClient.cs b/osu.Game/Online/RealtimeMultiplayer/ISpectatorClient.cs index f3dff0de86..fa06652474 100644 --- a/osu.Game/Online/RealtimeMultiplayer/ISpectatorClient.cs +++ b/osu.Game/Online/RealtimeMultiplayer/ISpectatorClient.cs @@ -39,5 +39,12 @@ namespace osu.Game.Online.RealtimeMultiplayer /// /// The updated room settings. Task SettingsChanged(MultiplayerRoomSettings newSettings); + + /// + /// Signals that a user in this room changed their state. + /// + /// The ID of the user performing a state change. + /// The new state of the user. + Task UserStateChanged(long userId, MultiplayerUserState state); } } diff --git a/osu.Game/Online/RealtimeMultiplayer/InvalidStateChange.cs b/osu.Game/Online/RealtimeMultiplayer/InvalidStateChange.cs new file mode 100644 index 0000000000..d1016e95cb --- /dev/null +++ b/osu.Game/Online/RealtimeMultiplayer/InvalidStateChange.cs @@ -0,0 +1,15 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; + +namespace osu.Game.Online.RealtimeMultiplayer +{ + public class InvalidStateChange : Exception + { + public InvalidStateChange(MultiplayerUserState oldState, MultiplayerUserState newState) + : base($"Cannot change from {oldState} to {newState}") + { + } + } +} From 345352be6757ab9225df004307e07a48de2523dd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Dec 2020 17:42:08 +0900 Subject: [PATCH 099/818] Mark PerformUpdate as an instant handle method (doesn't really help with anything) --- osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs index 8a88d8fc90..3fbc01a2f5 100644 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; +using JetBrains.Annotations; namespace osu.Game.Online.RealtimeMultiplayer { @@ -50,7 +51,7 @@ namespace osu.Game.Online.RealtimeMultiplayer /// Perform an update on this room in a thread-safe manner. /// /// The action to perform. - public void PerformUpdate(Action action) + public void PerformUpdate([InstantHandle] Action action) { lock (writeLock) action(this); } From 8eccfa476cbbff5a3e6ceb1e0cef9b2c0e74a683 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Dec 2020 18:02:14 +0900 Subject: [PATCH 100/818] Add loading states --- osu.Game/Online/RealtimeMultiplayer/MultiplayerUserState.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerUserState.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerUserState.cs index 47a1226e29..948ed50cca 100644 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerUserState.cs +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerUserState.cs @@ -7,6 +7,8 @@ namespace osu.Game.Online.RealtimeMultiplayer { Idle, Ready, + WaitingForLoad, + Loaded, Playing, Results, } From 60550b73f77d0ec625c070fcd6eee395e8397149 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Dec 2020 18:18:20 +0900 Subject: [PATCH 101/818] Add missing states and xmldoc for all states' purposes --- .../MultiplayerUserState.cs | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerUserState.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerUserState.cs index 948ed50cca..ed799f5252 100644 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerUserState.cs +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerUserState.cs @@ -5,11 +5,51 @@ namespace osu.Game.Online.RealtimeMultiplayer { public enum MultiplayerUserState { + /// + /// The user is idle and waiting for something to happen (or watching the match but not participating). + /// Idle, + + /// + /// The user has marked themselves as ready to participate and should be considered for the next game start. + /// Ready, + + /// + /// The server is waiting for this user to finish loading. This is a reserved state, and is set by the server. + /// + /// + /// All users in state when the game start will be transitioned to this state. + /// All users in this state need to transition to before the game can start. + /// WaitingForLoad, + + /// + /// The user's client has marked itself as loaded and ready to begin gameplay. + /// Loaded, + + /// + /// The user is currently playing in a game. This is a reserved state, and is set by the server. + /// + /// + /// Once there are no remaining users, all users in state will be transitioned to this state. + /// At this point the game will start for all users. + /// Playing, + + /// + /// The user has finished playing and is ready to view results. + /// + /// + /// Once all users transition from to this state, the game will end and results will be distributed. + /// All users will be transitioned to the state. + /// + FinishedPlay, + + /// + /// The user is currently viewing results. This is a reserved state, and is set by the server. + /// Results, } } From 147db0abe22642c8f449b3f121cc8040c98f17f4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Dec 2020 18:28:31 +0900 Subject: [PATCH 102/818] Fix client naming and xmldoc --- .../{ISpectatorClient.cs => IMultiplayerClient.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename osu.Game/Online/RealtimeMultiplayer/{ISpectatorClient.cs => IMultiplayerClient.cs} (96%) diff --git a/osu.Game/Online/RealtimeMultiplayer/ISpectatorClient.cs b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerClient.cs similarity index 96% rename from osu.Game/Online/RealtimeMultiplayer/ISpectatorClient.cs rename to osu.Game/Online/RealtimeMultiplayer/IMultiplayerClient.cs index fa06652474..e772ef39f9 100644 --- a/osu.Game/Online/RealtimeMultiplayer/ISpectatorClient.cs +++ b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerClient.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; namespace osu.Game.Online.RealtimeMultiplayer { /// - /// An interface defining a spectator client instance. + /// An interface defining a multiplayer client instance. /// public interface IMultiplayerClient { From 2aedd82e27a490a09c5efe526e2e45a7120b12e0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Dec 2020 18:30:58 +0900 Subject: [PATCH 103/818] Document room states and remove unnecessary WaitingForResults state --- .../RealtimeMultiplayer/MultiplayerRoomState.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomState.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomState.cs index 9f76cd945e..69c04b09a8 100644 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomState.cs +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomState.cs @@ -10,10 +10,24 @@ namespace osu.Game.Online.RealtimeMultiplayer /// public enum MultiplayerRoomState { + /// + /// The room is open and accepting new players. + /// Open, + + /// + /// A game start has been triggered but players have not finished loading. + /// WaitingForLoad, + + /// + /// A game is currently ongoing. + /// Playing, - WaitingForResults, + + /// + /// The room has been disbanded and closed. + /// Closed } } From 2433838d5887e84089496713857dbd8f12f797aa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Dec 2020 18:42:08 +0900 Subject: [PATCH 104/818] Add methods covering match start / end --- .../RealtimeMultiplayer/IMultiplayerClient.cs | 15 +++++++++++++++ .../RealtimeMultiplayer/IMultiplayerServer.cs | 5 +++++ 2 files changed, 20 insertions(+) diff --git a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerClient.cs b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerClient.cs index e772ef39f9..1e59b62a9d 100644 --- a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerClient.cs +++ b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerClient.cs @@ -46,5 +46,20 @@ namespace osu.Game.Online.RealtimeMultiplayer /// The ID of the user performing a state change. /// The new state of the user. Task UserStateChanged(long userId, MultiplayerUserState state); + + /// + /// Signals that a match is to be started. Users in the state should begin loading gameplay at this point. + /// + Task LoadRequested(); + + /// + /// Signals that a match has started. All loaded users' clients should now start gameplay as soon as possible. + /// + Task MatchStarted(); + + /// + /// Signals that the match has ended, all players have finished and results are ready to be displayed. + /// + Task ResultsReady(); } } diff --git a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs index ad4aa5d2c2..eb9eddbdf5 100644 --- a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs +++ b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs @@ -37,5 +37,10 @@ namespace osu.Game.Online.RealtimeMultiplayer /// The proposed new state. /// If the state change requested is not valid, given the previous state or room state. Task ChangeState(MultiplayerUserState newState); + + /// + /// As the host of a room, start the match. + /// + Task StartMatch(); } } From df908f90b2fe4ed8eab7804dbd32c0191d16c4ac Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Dec 2020 19:06:08 +0900 Subject: [PATCH 105/818] Add exception to be thrown when an operation is requested requiring host when not host --- .../RealtimeMultiplayer/IMultiplayerServer.cs | 2 ++ .../RealtimeMultiplayer/NotHostException.cs | 15 +++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 osu.Game/Online/RealtimeMultiplayer/NotHostException.cs diff --git a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs index eb9eddbdf5..565c07d0ec 100644 --- a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs +++ b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs @@ -23,6 +23,7 @@ namespace osu.Game.Online.RealtimeMultiplayer /// Transfer the host of the currently joined room to another user in the room. /// /// The new user which is to become host. + /// A user other than the current host is attempting to transfer host. Task TransferHost(long userId); /// @@ -41,6 +42,7 @@ namespace osu.Game.Online.RealtimeMultiplayer /// /// As the host of a room, start the match. /// + /// A user other than the current host is attempting to start the game. Task StartMatch(); } } diff --git a/osu.Game/Online/RealtimeMultiplayer/NotHostException.cs b/osu.Game/Online/RealtimeMultiplayer/NotHostException.cs new file mode 100644 index 0000000000..e421c6ae28 --- /dev/null +++ b/osu.Game/Online/RealtimeMultiplayer/NotHostException.cs @@ -0,0 +1,15 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; + +namespace osu.Game.Online.RealtimeMultiplayer +{ + public class NotHostException : Exception + { + public NotHostException() + : base("User is attempting to perform a host level operation while not the host") + { + } + } +} From 021a11609326c10de152d53963c04bebc2a77e21 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Dec 2020 19:06:27 +0900 Subject: [PATCH 106/818] Add extra xmldoc covering the fact that MatchStarted is received by all users --- osu.Game/Online/RealtimeMultiplayer/IMultiplayerClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerClient.cs b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerClient.cs index 1e59b62a9d..c89d99dd1c 100644 --- a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerClient.cs +++ b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerClient.cs @@ -53,7 +53,7 @@ namespace osu.Game.Online.RealtimeMultiplayer Task LoadRequested(); /// - /// Signals that a match has started. All loaded users' clients should now start gameplay as soon as possible. + /// Signals that a match has started. All user in the state should begin gameplay as soon as possible. /// Task MatchStarted(); From d76fabedf90c4c45eda7e0ead815170f3b3012e6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Dec 2020 19:44:22 +0900 Subject: [PATCH 107/818] Add note about LoadRequested only being sent to a subset of room users --- osu.Game/Online/RealtimeMultiplayer/IMultiplayerClient.cs | 2 +- osu.Game/Online/RealtimeMultiplayer/MultiplayerUserState.cs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerClient.cs b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerClient.cs index c89d99dd1c..e75b0da207 100644 --- a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerClient.cs +++ b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerClient.cs @@ -48,7 +48,7 @@ namespace osu.Game.Online.RealtimeMultiplayer Task UserStateChanged(long userId, MultiplayerUserState state); /// - /// Signals that a match is to be started. Users in the state should begin loading gameplay at this point. + /// Signals that a match is to be started. This will *only* be sent to clients which are to begin loading at this point. /// Task LoadRequested(); diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerUserState.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerUserState.cs index ed799f5252..ed9acd146e 100644 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerUserState.cs +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerUserState.cs @@ -13,6 +13,10 @@ namespace osu.Game.Online.RealtimeMultiplayer /// /// The user has marked themselves as ready to participate and should be considered for the next game start. /// + /// + /// Clients in this state will receive gameplay channel messages. + /// As a client the only thing to look for in this state is a call. + /// Ready, /// From aa68ae4ff27713f25ad42545baa905d83e29de4f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Dec 2020 20:10:23 +0900 Subject: [PATCH 108/818] Change locking mechanism for multiplayer rooms to use using-disposal pattern Was required to lock over `await` calls server-side. --- .../RealtimeMultiplayer/LockUntilDisposal.cs | 24 +++++++++++++++++++ .../RealtimeMultiplayer/MultiplayerRoom.cs | 9 ++----- 2 files changed, 26 insertions(+), 7 deletions(-) create mode 100644 osu.Game/Online/RealtimeMultiplayer/LockUntilDisposal.cs diff --git a/osu.Game/Online/RealtimeMultiplayer/LockUntilDisposal.cs b/osu.Game/Online/RealtimeMultiplayer/LockUntilDisposal.cs new file mode 100644 index 0000000000..cd16fce2ee --- /dev/null +++ b/osu.Game/Online/RealtimeMultiplayer/LockUntilDisposal.cs @@ -0,0 +1,24 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Threading; + +namespace osu.Game.Online.RealtimeMultiplayer +{ + public readonly struct LockUntilDisposal : IDisposable + { + private readonly object lockTarget; + + public LockUntilDisposal(object lockTarget) + { + this.lockTarget = lockTarget; + Monitor.Enter(lockTarget); + } + + public void Dispose() + { + Monitor.Exit(lockTarget); + } + } +} diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs index 3fbc01a2f5..0f18ab6c89 100644 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; -using JetBrains.Annotations; namespace osu.Game.Online.RealtimeMultiplayer { @@ -48,12 +47,8 @@ namespace osu.Game.Online.RealtimeMultiplayer } /// - /// Perform an update on this room in a thread-safe manner. + /// Request a lock on this room to perform a thread-safe update. /// - /// The action to perform. - public void PerformUpdate([InstantHandle] Action action) - { - lock (writeLock) action(this); - } + public LockUntilDisposal LockForUpdate() => new LockUntilDisposal(writeLock); } } From ed50fd445e81589df6375b284fbb003617bd129a Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 8 Dec 2020 21:07:12 +0900 Subject: [PATCH 109/818] Fix hit lighting colour not applied in TestSceneCatcher --- .../TestSceneCatcher.cs | 59 +++++++++++++------ 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs index cf6011d721..94cd0ec8a7 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs @@ -18,6 +18,7 @@ using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects.Drawables; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; +using osu.Game.Skinning; using osu.Game.Tests.Visual; namespace osu.Game.Rulesets.Catch.Tests @@ -65,13 +66,11 @@ namespace osu.Game.Rulesets.Catch.Tests JudgementResult result2 = null; AddStep("catch hyper fruit", () => { - drawableObject1 = createDrawableObject(new Fruit { HyperDashTarget = new Fruit { X = 100 } }); - result1 = attemptCatch(drawableObject1); + attemptCatch(new Fruit { HyperDashTarget = new Fruit { X = 100 } }, out drawableObject1, out result1); }); AddStep("catch normal fruit", () => { - drawableObject2 = createDrawableObject(new Fruit()); - result2 = attemptCatch(drawableObject2); + attemptCatch(new Fruit(), out drawableObject2, out result2); }); AddStep("revert second result", () => { @@ -92,8 +91,7 @@ namespace osu.Game.Rulesets.Catch.Tests JudgementResult result = null; AddStep("catch kiai fruit", () => { - drawableObject = createDrawableObject(new TestKiaiFruit()); - result = attemptCatch(drawableObject); + attemptCatch(new TestKiaiFruit(), out drawableObject, out result); }); checkState(CatcherAnimationState.Kiai); AddStep("revert result", () => @@ -200,13 +198,22 @@ namespace osu.Game.Rulesets.Catch.Tests AddAssert("fruits are dropped", () => !catcher.CaughtObjects.Any() && droppedObjectContainer.Count == 10); } - [TestCase(true)] - [TestCase(false)] - public void TestHitLighting(bool enabled) + [Test] + public void TestHitLightingColour() { - AddStep($"{(enabled ? "enable" : "disable")} hit lighting", () => config.Set(OsuSetting.HitLighting, enabled)); + var fruitColour = SkinConfiguration.DefaultComboColours[1]; + AddStep("enable hit lighting", () => config.Set(OsuSetting.HitLighting, true)); AddStep("catch fruit", () => attemptCatch(new Fruit())); - AddAssert("check hit lighting", () => catcher.ChildrenOfType().Any() == enabled); + AddAssert("correct hit lighting colour", () => + catcher.ChildrenOfType().First()?.ObjectColour == fruitColour); + } + + [Test] + public void TestHitLightingDisabled() + { + AddStep("disable hit lighting", () => config.Set(OsuSetting.HitLighting, false)); + AddStep("catch fruit", () => attemptCatch(new Fruit())); + AddAssert("no hit lighting", () => !catcher.ChildrenOfType().Any()); } private void checkPlate(int count) => AddAssert($"{count} objects on the plate", () => catcher.CaughtObjects.Count() == count); @@ -218,18 +225,34 @@ namespace osu.Game.Rulesets.Catch.Tests private void attemptCatch(CatchHitObject hitObject, int count = 1) { for (var i = 0; i < count; i++) - attemptCatch(createDrawableObject(hitObject)); + attemptCatch(hitObject, out _, out _); } - private JudgementResult attemptCatch(DrawableCatchHitObject drawableObject) + private void attemptCatch(CatchHitObject hitObject, out DrawableCatchHitObject drawableObject, out JudgementResult result) { - drawableObject.HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); - var result = new CatchJudgementResult(drawableObject.HitObject, drawableObject.HitObject.CreateJudgement()) + hitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + drawableObject = createDrawableObject(hitObject); + result = createResult(hitObject); + applyResult(drawableObject, result); + } + + private void applyResult(DrawableCatchHitObject drawableObject, JudgementResult result) + { + // Load DHO to set colour of hit explosion correctly + Add(drawableObject); + drawableObject.OnLoadComplete += _ => { - Type = catcher.CanCatch(drawableObject.HitObject) ? HitResult.Great : HitResult.Miss + catcher.OnNewResult(drawableObject, result); + drawableObject.Expire(); + }; + } + + private JudgementResult createResult(CatchHitObject hitObject) + { + return new CatchJudgementResult(hitObject, hitObject.CreateJudgement()) + { + Type = catcher.CanCatch(hitObject) ? HitResult.Great : HitResult.Miss }; - catcher.OnNewResult(drawableObject, result); - return result; } private DrawableCatchHitObject createDrawableObject(CatchHitObject hitObject) From c301223d8cf12897e6d0327b11c505b59798be3d Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 8 Dec 2020 20:34:08 +0900 Subject: [PATCH 110/818] Make object on the catcher plate separate CaughtObject class --- .../TestSceneCatcher.cs | 2 +- .../Objects/Drawables/CaughtObject.cs | 83 +++++++++++++++++++ .../Drawables/DrawableCatchHitObject.cs | 4 - .../Objects/Drawables/DrawableDroplet.cs | 2 - .../DrawablePalpableCatchHitObject.cs | 7 +- .../Skinning/Default/CatchHitObjectPiece.cs | 16 +++- .../Skinning/Default/FruitPiece.cs | 4 + .../Skinning/Legacy/LegacyFruitPiece.cs | 4 + .../Skinning/LegacyCatchHitObjectPiece.cs | 7 ++ osu.Game.Rulesets.Catch/UI/Catcher.cs | 76 ++++++----------- 10 files changed, 141 insertions(+), 64 deletions(-) create mode 100644 osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs index 94cd0ec8a7..2747524503 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs @@ -275,7 +275,7 @@ namespace osu.Game.Rulesets.Catch.Tests public class TestCatcher : Catcher { - public IEnumerable CaughtObjects => this.ChildrenOfType(); + public IEnumerable CaughtObjects => this.ChildrenOfType(); public TestCatcher(Container trailsTarget, Container droppedObjectTarget, BeatmapDifficulty difficulty) : base(trailsTarget, droppedObjectTarget, difficulty) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs new file mode 100644 index 0000000000..0f3a143553 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs @@ -0,0 +1,83 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Catch.Skinning.Default; +using osu.Game.Skinning; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Catch.Objects.Drawables +{ + [Cached(typeof(CaughtObject))] + public abstract class CaughtObject : SkinnableDrawable + { + public readonly Bindable AccentColour = new Bindable(); + + public CatchHitObject HitObject { get; private set; } + + /// + /// Whether this hit object should stay on the catcher plate when the object is caught by the catcher. + /// + public virtual bool StaysOnPlate => true; + + public override bool RemoveWhenNotAlive => true; + + protected CaughtObject(CatchSkinComponents skinComponent, Func defaultImplementation) + : base(new CatchSkinComponent(skinComponent), defaultImplementation) + { + Anchor = Anchor.TopCentre; + Origin = Anchor.Centre; + + RelativeSizeAxes = Axes.None; + Size = new Vector2(CatchHitObject.OBJECT_RADIUS * 2); + } + + public virtual void CopyFrom(DrawablePalpableCatchHitObject drawableObject) + { + HitObject = drawableObject.HitObject; + Scale = drawableObject.Scale / 2; + Rotation = drawableObject.Rotation; + AccentColour.Value = drawableObject.AccentColour.Value; + } + } + + public class CaughtFruit : CaughtObject + { + public readonly Bindable VisualRepresentation = new Bindable(); + + public CaughtFruit() + : base(CatchSkinComponents.Fruit, _ => new FruitPiece()) + { + } + + public override void CopyFrom(DrawablePalpableCatchHitObject drawableObject) + { + base.CopyFrom(drawableObject); + + var drawableFruit = (DrawableFruit)drawableObject; + VisualRepresentation.Value = drawableFruit.VisualRepresentation.Value; + } + } + + public class CaughtBanana : CaughtObject + { + public CaughtBanana() + : base(CatchSkinComponents.Banana, _ => new BananaPiece()) + { + } + } + + public class CaughtDroplet : CaughtObject + { + public override bool StaysOnPlate => false; + + public CaughtDroplet() + : base(CatchSkinComponents.Droplet, _ => new DropletPiece()) + { + } + } +} diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs index 70efe9cf29..bfd124c691 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs @@ -50,10 +50,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables public Func CheckPosition; - public bool IsOnPlate; - - public override bool RemoveWhenNotAlive => IsOnPlate; - protected override JudgementResult CreateResult(Judgement judgement) => new CatchJudgementResult(HitObject, judgement); protected override void CheckForResult(bool userTriggered, double timeOffset) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs index d96b1c8902..c5f0bb8b18 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs @@ -11,8 +11,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables { public class DrawableDroplet : DrawablePalpableCatchHitObject { - public override bool StaysOnPlate => false; - public DrawableDroplet() : this(null) { diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs index b99710ae8e..34d9cf820e 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs @@ -24,11 +24,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables /// protected virtual float ScaleFactor => 1; - /// - /// Whether this hit object should stay on the catcher plate when the object is caught by the catcher. - /// - public virtual bool StaysOnPlate => true; - public float DisplayRadius => CatchHitObject.OBJECT_RADIUS * HitObject.Scale * ScaleFactor; protected DrawablePalpableCatchHitObject([CanBeNull] CatchHitObject h) @@ -43,7 +38,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables { XBindable.BindValueChanged(x => { - if (!IsOnPlate) X = x.NewValue; + X = x.NewValue; }, true); ScaleBindable.BindValueChanged(scale => diff --git a/osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs index d59b6cc0de..cb326b77e2 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs @@ -21,6 +21,10 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default [CanBeNull] protected DrawableHitObject DrawableHitObject { get; private set; } + [Resolved(canBeNull: true)] + [CanBeNull] + protected CaughtObject CaughtObject { get; private set; } + /// /// A part of this piece that will be faded out while falling in the playfield. /// @@ -45,6 +49,9 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default HyperDash.BindTo(hitObject.HyperDash); } + if (CaughtObject != null) + AccentColour.BindTo(CaughtObject.AccentColour); + HyperDash.BindValueChanged(hyper => { if (HyperBorderPiece != null) @@ -54,8 +61,13 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default protected override void Update() { - if (BorderPiece != null && DrawableHitObject?.HitObject != null) - BorderPiece.Alpha = (float)Math.Clamp((DrawableHitObject.HitObject.StartTime - Time.Current) / 500, 0, 1); + if (BorderPiece != null) + { + if (DrawableHitObject?.HitObject != null) + BorderPiece.Alpha = (float)Math.Clamp((DrawableHitObject.HitObject.StartTime - Time.Current) / 500, 0, 1); + else + BorderPiece.Alpha = 0; + } } } } diff --git a/osu.Game.Rulesets.Catch/Skinning/Default/FruitPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Default/FruitPiece.cs index 2e3803a31a..8c705cec4f 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Default/FruitPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Default/FruitPiece.cs @@ -43,6 +43,10 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default if (fruit != null) VisualRepresentation.BindTo(fruit.VisualRepresentation); + + var caughtFruit = (CaughtFruit)CaughtObject; + if (caughtFruit != null) + VisualRepresentation.BindTo(caughtFruit.VisualRepresentation); } } } diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyFruitPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyFruitPiece.cs index 6f93e68594..5140c62d3e 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyFruitPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyFruitPiece.cs @@ -19,6 +19,10 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy if (fruit != null) VisualRepresentation.BindTo(fruit.VisualRepresentation); + var caughtFruit = (CaughtFruit)CaughtObject; + if (caughtFruit != null) + VisualRepresentation.BindTo(caughtFruit.VisualRepresentation); + VisualRepresentation.BindValueChanged(visual => setTexture(visual.NewValue), true); } diff --git a/osu.Game.Rulesets.Catch/Skinning/LegacyCatchHitObjectPiece.cs b/osu.Game.Rulesets.Catch/Skinning/LegacyCatchHitObjectPiece.cs index 1e68439402..9527f15ff1 100644 --- a/osu.Game.Rulesets.Catch/Skinning/LegacyCatchHitObjectPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/LegacyCatchHitObjectPiece.cs @@ -33,6 +33,10 @@ namespace osu.Game.Rulesets.Catch.Skinning [CanBeNull] protected DrawableHitObject DrawableHitObject { get; private set; } + [Resolved(canBeNull: true)] + [CanBeNull] + protected CaughtObject CaughtObject { get; private set; } + protected LegacyCatchHitObjectPiece() { RelativeSizeAxes = Axes.Both; @@ -73,6 +77,9 @@ namespace osu.Game.Rulesets.Catch.Skinning HyperDash.BindTo(hitObject.HyperDash); } + if (CaughtObject != null) + AccentColour.BindTo(CaughtObject.AccentColour); + hyperSprite.Colour = Skin.GetConfig(CatchSkinColour.HyperDashFruit)?.Value ?? Skin.GetConfig(CatchSkinColour.HyperDash)?.Value ?? Catcher.DEFAULT_HYPER_DASH_COLOUR; diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index ac0f25e21b..756582e3f2 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Catch.UI private readonly Container droppedObjectTarget; - private readonly Container caughtFruitContainer; + private readonly Container caughtFruitContainer; public CatcherAnimationState CurrentState { get; private set; } @@ -124,7 +124,7 @@ namespace osu.Game.Rulesets.Catch.UI InternalChildren = new Drawable[] { hitExplosionPool = new DrawablePool(10), - caughtFruitContainer = new Container + caughtFruitContainer = new Container { Anchor = Anchor.TopCentre, Origin = Anchor.BottomCentre, @@ -223,7 +223,7 @@ namespace osu.Game.Rulesets.Catch.UI { var positionInStack = computePositionInStack(new Vector2(palpableObject.X - X, 0), palpableObject.DisplayRadius); - placeCaughtObject(hitObject, positionInStack); + placeCaughtObject(palpableObject, positionInStack); if (hitLighting.Value) addLighting(hitObject, positionInStack.X, drawableObject.AccentColour.Value); @@ -450,21 +450,14 @@ namespace osu.Game.Rulesets.Catch.UI updateCatcher(); } - private void placeCaughtObject(PalpableCatchHitObject source, Vector2 position) + private void placeCaughtObject(DrawablePalpableCatchHitObject drawableObject, Vector2 position) { - var caughtObject = createCaughtObject(source); + var caughtObject = createCaughtObject(drawableObject.HitObject); if (caughtObject == null) return; - caughtObject.RelativePositionAxes = Axes.None; + caughtObject.CopyFrom(drawableObject); caughtObject.Position = position; - caughtObject.IsOnPlate = true; - - caughtObject.Anchor = Anchor.TopCentre; - caughtObject.Origin = Anchor.Centre; - caughtObject.Scale *= 0.5f; - caughtObject.LifetimeStart = source.StartTime; - caughtObject.LifetimeEnd = double.MaxValue; caughtFruitContainer.Add(caughtObject); @@ -500,21 +493,18 @@ namespace osu.Game.Rulesets.Catch.UI hitExplosionContainer.Add(hitExplosion); } - private DrawablePalpableCatchHitObject createCaughtObject(PalpableCatchHitObject source) + private CaughtObject createCaughtObject(PalpableCatchHitObject source) { switch (source) { - case Banana banana: - return new DrawableBanana(banana); + case Fruit _: + return new CaughtFruit(); - case Fruit fruit: - return new DrawableFruit(fruit); + case Banana _: + return new CaughtBanana(); - case TinyDroplet tiny: - return new DrawableTinyDroplet(tiny); - - case Droplet droplet: - return new DrawableDroplet(droplet); + case Droplet _: + return new CaughtDroplet(); default: return null; @@ -532,7 +522,7 @@ namespace osu.Game.Rulesets.Catch.UI drop(caughtObject, animation); } - private void removeFromPlate(DrawablePalpableCatchHitObject caughtObject, DroppedObjectAnimation animation) + private void removeFromPlate(CaughtObject caughtObject, DroppedObjectAnimation animation) { if (!caughtFruitContainer.Remove(caughtObject)) throw new InvalidOperationException("Can only drop a caught object on the plate"); @@ -542,40 +532,28 @@ namespace osu.Game.Rulesets.Catch.UI drop(caughtObject, animation); } - private void drop(DrawablePalpableCatchHitObject d, DroppedObjectAnimation animation) + private void drop(CaughtObject d, DroppedObjectAnimation animation) { var originalX = d.X * Scale.X; - var startTime = Clock.CurrentTime; d.Anchor = Anchor.TopLeft; d.Position = caughtFruitContainer.ToSpaceOfOtherDrawable(d.DrawPosition, droppedObjectTarget); - // we cannot just apply the transforms because DHO clears transforms when state is updated - d.ApplyCustomUpdateState += (o, state) => animate(o, animation, originalX, startTime); - if (d.IsLoaded) - animate(d, animation, originalX, startTime); - } - - private void animate(Drawable d, DroppedObjectAnimation animation, float originalX, double startTime) - { - using (d.BeginAbsoluteSequence(startTime)) + switch (animation) { - switch (animation) - { - case DroppedObjectAnimation.Drop: - d.MoveToY(d.Y + 75, 750, Easing.InSine); - d.FadeOut(750); - break; + case DroppedObjectAnimation.Drop: + d.MoveToY(d.Y + 75, 750, Easing.InSine); + d.FadeOut(750); + break; - case DroppedObjectAnimation.Explode: - d.MoveToY(d.Y - 50, 250, Easing.OutSine).Then().MoveToY(d.Y + 50, 500, Easing.InSine); - d.MoveToX(d.X + originalX * 6, 1000); - d.FadeOut(750); - break; - } - - d.Expire(); + case DroppedObjectAnimation.Explode: + d.MoveToY(d.Y - 50, 250, Easing.OutSine).Then().MoveToY(d.Y + 50, 500, Easing.InSine); + d.MoveToX(d.X + originalX * 6, 1000); + d.FadeOut(750); + break; } + + d.Expire(); } private enum DroppedObjectAnimation From 02f5fda330306b6ae9b3a67dad6f50b2373562c6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Dec 2020 21:15:10 +0900 Subject: [PATCH 111/818] Add missing final newline in file --- osu.Game/Online/RealtimeMultiplayer/NotJoinedRoomException.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/NotJoinedRoomException.cs b/osu.Game/Online/RealtimeMultiplayer/NotJoinedRoomException.cs index 1b264d1ac5..ca4a54f6d7 100644 --- a/osu.Game/Online/RealtimeMultiplayer/NotJoinedRoomException.cs +++ b/osu.Game/Online/RealtimeMultiplayer/NotJoinedRoomException.cs @@ -9,4 +9,4 @@ namespace osu.Game.Online.RealtimeMultiplayer { } } -} \ No newline at end of file +} From a32dac00ddf388f2359dd97ebd23fc7faa68d687 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 8 Dec 2020 21:29:03 +0900 Subject: [PATCH 112/818] Introduce IHasCatchObjectState implemented by DHO and CaughtObject --- .../Objects/Drawables/CaughtObject.cs | 31 ++++++++++--------- .../Objects/Drawables/DrawableBanana.cs | 2 +- .../Objects/Drawables/DrawableDroplet.cs | 2 +- .../Objects/Drawables/DrawableFruit.cs | 4 +-- ...s => DrawablePalpableHasCatchHitObject.cs} | 14 ++++++--- .../Objects/Drawables/IHasCatchObjectState.cs | 24 ++++++++++++++ .../Skinning/Default/CatchHitObjectPiece.cs | 31 ++++--------------- .../Skinning/Default/FruitPiece.cs | 10 ++---- .../Skinning/Legacy/LegacyFruitPiece.cs | 10 ++---- .../Skinning/LegacyCatchHitObjectPiece.cs | 23 +++----------- osu.Game.Rulesets.Catch/UI/Catcher.cs | 5 +-- 11 files changed, 70 insertions(+), 86 deletions(-) rename osu.Game.Rulesets.Catch/Objects/Drawables/{DrawablePalpableCatchHitObject.cs => DrawablePalpableHasCatchHitObject.cs} (78%) create mode 100644 osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs index 0f3a143553..d3555ea771 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs @@ -12,12 +12,12 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.Objects.Drawables { - [Cached(typeof(CaughtObject))] - public abstract class CaughtObject : SkinnableDrawable + [Cached(typeof(IHasCatchObjectState))] + public abstract class CaughtObject : SkinnableDrawable, IHasCatchObjectState { - public readonly Bindable AccentColour = new Bindable(); - public CatchHitObject HitObject { get; private set; } + public Bindable AccentColour { get; } = new Bindable(); + public Bindable HyperDash { get; } = new Bindable(); /// /// Whether this hit object should stay on the catcher plate when the object is caught by the catcher. @@ -36,30 +36,31 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables Size = new Vector2(CatchHitObject.OBJECT_RADIUS * 2); } - public virtual void CopyFrom(DrawablePalpableCatchHitObject drawableObject) + public virtual void CopyFrom(IHasCatchObjectState objectState) { - HitObject = drawableObject.HitObject; - Scale = drawableObject.Scale / 2; - Rotation = drawableObject.Rotation; - AccentColour.Value = drawableObject.AccentColour.Value; + HitObject = objectState.HitObject; + Scale = objectState.Scale; + Rotation = objectState.Rotation; + AccentColour.Value = objectState.AccentColour.Value; + HyperDash.Value = objectState.HyperDash.Value; } } - public class CaughtFruit : CaughtObject + public class CaughtFruit : CaughtObject, IHasFruitState { - public readonly Bindable VisualRepresentation = new Bindable(); + public Bindable VisualRepresentation { get; } = new Bindable(); public CaughtFruit() : base(CatchSkinComponents.Fruit, _ => new FruitPiece()) { } - public override void CopyFrom(DrawablePalpableCatchHitObject drawableObject) + public override void CopyFrom(IHasCatchObjectState objectState) { - base.CopyFrom(drawableObject); + base.CopyFrom(objectState); - var drawableFruit = (DrawableFruit)drawableObject; - VisualRepresentation.Value = drawableFruit.VisualRepresentation.Value; + var fruitState = (IHasFruitState)objectState; + VisualRepresentation.Value = fruitState.VisualRepresentation.Value; } } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs index 4bc5da2396..34d89eb188 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs @@ -9,7 +9,7 @@ using osu.Game.Skinning; namespace osu.Game.Rulesets.Catch.Objects.Drawables { - public class DrawableBanana : DrawablePalpableCatchHitObject + public class DrawableBanana : DrawablePalpableHasCatchHitObject { public DrawableBanana() : this(null) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs index c5f0bb8b18..acdb3bb38c 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs @@ -9,7 +9,7 @@ using osu.Game.Skinning; namespace osu.Game.Rulesets.Catch.Objects.Drawables { - public class DrawableDroplet : DrawablePalpableCatchHitObject + public class DrawableDroplet : DrawablePalpableHasCatchHitObject { public DrawableDroplet() : this(null) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs index 8135a31b43..6ec46f6535 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs @@ -10,9 +10,9 @@ using osu.Game.Skinning; namespace osu.Game.Rulesets.Catch.Objects.Drawables { - public class DrawableFruit : DrawablePalpableCatchHitObject + public class DrawableFruit : DrawablePalpableHasCatchHitObject, IHasFruitState { - public readonly Bindable VisualRepresentation = new Bindable(); + public Bindable VisualRepresentation { get; } = new Bindable(); public DrawableFruit() : this(null) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableHasCatchHitObject.cs similarity index 78% rename from osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs rename to osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableHasCatchHitObject.cs index 34d9cf820e..ee893f7880 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableHasCatchHitObject.cs @@ -6,18 +6,22 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osuTK; +using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.Objects.Drawables { - public abstract class DrawablePalpableCatchHitObject : DrawableCatchHitObject + [Cached(typeof(IHasCatchObjectState))] + public abstract class DrawablePalpableHasCatchHitObject : DrawableCatchHitObject, IHasCatchObjectState { public new PalpableCatchHitObject HitObject => (PalpableCatchHitObject)base.HitObject; - public readonly Bindable HyperDash = new Bindable(); + Bindable IHasCatchObjectState.AccentColour => AccentColour; - public readonly Bindable ScaleBindable = new Bindable(1); + public Bindable HyperDash { get; } = new Bindable(); - public readonly Bindable IndexInBeatmap = new Bindable(); + public Bindable ScaleBindable { get; } = new Bindable(1); + + public Bindable IndexInBeatmap { get; } = new Bindable(); /// /// The multiplicative factor applied to relative to scale. @@ -26,7 +30,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables public float DisplayRadius => CatchHitObject.OBJECT_RADIUS * HitObject.Scale * ScaleFactor; - protected DrawablePalpableCatchHitObject([CanBeNull] CatchHitObject h) + protected DrawablePalpableHasCatchHitObject([CanBeNull] CatchHitObject h) : base(h) { Origin = Anchor.Centre; diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs new file mode 100644 index 0000000000..01d833b61a --- /dev/null +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs @@ -0,0 +1,24 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Bindables; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Catch.Objects.Drawables +{ + public interface IHasCatchObjectState + { + CatchHitObject HitObject { get; } + Bindable AccentColour { get; } + Bindable HyperDash { get; } + + float Rotation { get; } + Vector2 Scale { get; } + } + + public interface IHasFruitState : IHasCatchObjectState + { + Bindable VisualRepresentation { get; } + } +} diff --git a/osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs index cb326b77e2..b105fb4034 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs @@ -7,7 +7,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Catch.Objects.Drawables; -using osu.Game.Rulesets.Objects.Drawables; using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.Skinning.Default @@ -17,13 +16,8 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default public readonly Bindable AccentColour = new Bindable(); public readonly Bindable HyperDash = new Bindable(); - [Resolved(canBeNull: true)] - [CanBeNull] - protected DrawableHitObject DrawableHitObject { get; private set; } - - [Resolved(canBeNull: true)] - [CanBeNull] - protected CaughtObject CaughtObject { get; private set; } + [Resolved] + protected IHasCatchObjectState ObjectState { get; private set; } /// /// A part of this piece that will be faded out while falling in the playfield. @@ -41,16 +35,8 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default { base.LoadComplete(); - var hitObject = (DrawablePalpableCatchHitObject)DrawableHitObject; - - if (hitObject != null) - { - AccentColour.BindTo(hitObject.AccentColour); - HyperDash.BindTo(hitObject.HyperDash); - } - - if (CaughtObject != null) - AccentColour.BindTo(CaughtObject.AccentColour); + AccentColour.BindTo(ObjectState.AccentColour); + HyperDash.BindTo(ObjectState.HyperDash); HyperDash.BindValueChanged(hyper => { @@ -61,13 +47,8 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default protected override void Update() { - if (BorderPiece != null) - { - if (DrawableHitObject?.HitObject != null) - BorderPiece.Alpha = (float)Math.Clamp((DrawableHitObject.HitObject.StartTime - Time.Current) / 500, 0, 1); - else - BorderPiece.Alpha = 0; - } + if (BorderPiece != null && ObjectState?.HitObject != null) + BorderPiece.Alpha = (float)Math.Clamp((ObjectState.HitObject.StartTime - Time.Current) / 500, 0, 1); } } } diff --git a/osu.Game.Rulesets.Catch/Skinning/Default/FruitPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Default/FruitPiece.cs index 8c705cec4f..49f128c960 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Default/FruitPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Default/FruitPiece.cs @@ -39,14 +39,8 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default { base.LoadComplete(); - var fruit = (DrawableFruit)DrawableHitObject; - - if (fruit != null) - VisualRepresentation.BindTo(fruit.VisualRepresentation); - - var caughtFruit = (CaughtFruit)CaughtObject; - if (caughtFruit != null) - VisualRepresentation.BindTo(caughtFruit.VisualRepresentation); + var fruitState = (IHasFruitState)ObjectState; + VisualRepresentation.BindTo(fruitState.VisualRepresentation); } } } diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyFruitPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyFruitPiece.cs index 5140c62d3e..969cc38e5b 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyFruitPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyFruitPiece.cs @@ -14,14 +14,8 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy { base.LoadComplete(); - var fruit = (DrawableFruit)DrawableHitObject; - - if (fruit != null) - VisualRepresentation.BindTo(fruit.VisualRepresentation); - - var caughtFruit = (CaughtFruit)CaughtObject; - if (caughtFruit != null) - VisualRepresentation.BindTo(caughtFruit.VisualRepresentation); + var fruitState = (IHasFruitState)ObjectState; + VisualRepresentation.BindTo(fruitState.VisualRepresentation); VisualRepresentation.BindValueChanged(visual => setTexture(visual.NewValue), true); } diff --git a/osu.Game.Rulesets.Catch/Skinning/LegacyCatchHitObjectPiece.cs b/osu.Game.Rulesets.Catch/Skinning/LegacyCatchHitObjectPiece.cs index 9527f15ff1..4b1f5a4724 100644 --- a/osu.Game.Rulesets.Catch/Skinning/LegacyCatchHitObjectPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/LegacyCatchHitObjectPiece.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.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -10,7 +9,6 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Game.Rulesets.Catch.Objects.Drawables; using osu.Game.Rulesets.Catch.UI; -using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Skinning; using osuTK; using osuTK.Graphics; @@ -29,13 +27,8 @@ namespace osu.Game.Rulesets.Catch.Skinning [Resolved] protected ISkinSource Skin { get; private set; } - [Resolved(canBeNull: true)] - [CanBeNull] - protected DrawableHitObject DrawableHitObject { get; private set; } - - [Resolved(canBeNull: true)] - [CanBeNull] - protected CaughtObject CaughtObject { get; private set; } + [Resolved] + protected IHasCatchObjectState ObjectState { get; private set; } protected LegacyCatchHitObjectPiece() { @@ -69,16 +62,8 @@ namespace osu.Game.Rulesets.Catch.Skinning { base.LoadComplete(); - var hitObject = (DrawablePalpableCatchHitObject)DrawableHitObject; - - if (hitObject != null) - { - AccentColour.BindTo(hitObject.AccentColour); - HyperDash.BindTo(hitObject.HyperDash); - } - - if (CaughtObject != null) - AccentColour.BindTo(CaughtObject.AccentColour); + AccentColour.BindTo(ObjectState.AccentColour); + HyperDash.BindTo(ObjectState.HyperDash); hyperSprite.Colour = Skin.GetConfig(CatchSkinColour.HyperDashFruit)?.Value ?? Skin.GetConfig(CatchSkinColour.HyperDash)?.Value ?? diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 756582e3f2..4d540bce03 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -215,7 +215,7 @@ namespace osu.Game.Rulesets.Catch.UI catchResult.CatcherAnimationState = CurrentState; catchResult.CatcherHyperDash = HyperDashing; - if (!(drawableObject is DrawablePalpableCatchHitObject palpableObject)) return; + if (!(drawableObject is DrawablePalpableHasCatchHitObject palpableObject)) return; var hitObject = palpableObject.HitObject; @@ -450,7 +450,7 @@ namespace osu.Game.Rulesets.Catch.UI updateCatcher(); } - private void placeCaughtObject(DrawablePalpableCatchHitObject drawableObject, Vector2 position) + private void placeCaughtObject(DrawablePalpableHasCatchHitObject drawableObject, Vector2 position) { var caughtObject = createCaughtObject(drawableObject.HitObject); @@ -458,6 +458,7 @@ namespace osu.Game.Rulesets.Catch.UI caughtObject.CopyFrom(drawableObject); caughtObject.Position = position; + caughtObject.Scale /= 2; caughtFruitContainer.Add(caughtObject); From 749d5380ca5a1c5bd118307e4f9459cd7df00130 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 8 Dec 2020 22:38:10 +0900 Subject: [PATCH 113/818] Pool caught objects and dropped objects --- .../TestSceneCatcherArea.cs | 5 +- .../Objects/Drawables/CaughtObject.cs | 14 ++++- .../Objects/Drawables/IHasCatchObjectState.cs | 2 +- osu.Game.Rulesets.Catch/UI/Catcher.cs | 52 +++++++++++++------ 4 files changed, 53 insertions(+), 20 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs index 8602c7aad1..3d5e44476e 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs @@ -73,7 +73,10 @@ namespace osu.Game.Rulesets.Catch.Tests SetContents(() => { - var droppedObjectContainer = new Container(); + var droppedObjectContainer = new Container + { + RelativeSizeAxes = Axes.Both + }; return new CatchInputManager(catchRuleset) { diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs index d3555ea771..f36d287126 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables [Cached(typeof(IHasCatchObjectState))] public abstract class CaughtObject : SkinnableDrawable, IHasCatchObjectState { - public CatchHitObject HitObject { get; private set; } + public PalpableCatchHitObject HitObject { get; private set; } public Bindable AccentColour { get; } = new Bindable(); public Bindable HyperDash { get; } = new Bindable(); @@ -29,7 +29,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables protected CaughtObject(CatchSkinComponents skinComponent, Func defaultImplementation) : base(new CatchSkinComponent(skinComponent), defaultImplementation) { - Anchor = Anchor.TopCentre; Origin = Anchor.Centre; RelativeSizeAxes = Axes.None; @@ -44,6 +43,17 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables AccentColour.Value = objectState.AccentColour.Value; HyperDash.Value = objectState.HyperDash.Value; } + + protected override void FreeAfterUse() + { + ClearTransforms(); + + Alpha = 1; + LifetimeStart = double.MinValue; + LifetimeEnd = double.MaxValue; + + base.FreeAfterUse(); + } } public class CaughtFruit : CaughtObject, IHasFruitState diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs index 01d833b61a..a282bc5da0 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs @@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables { public interface IHasCatchObjectState { - CatchHitObject HitObject { get; } + PalpableCatchHitObject HitObject { get; } Bindable AccentColour { get; } Bindable HyperDash { get; } diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 4d540bce03..7aecd95efd 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -108,6 +108,10 @@ namespace osu.Game.Rulesets.Catch.UI private readonly DrawablePool hitExplosionPool; private readonly Container hitExplosionContainer; + private readonly DrawablePool caughtFruitPool; + private readonly DrawablePool caughtBananaPool; + private readonly DrawablePool caughtDropletPool; + public Catcher([NotNull] Container trailsTarget, [NotNull] Container droppedObjectTarget, BeatmapDifficulty difficulty = null) { this.trailsTarget = trailsTarget; @@ -124,6 +128,10 @@ namespace osu.Game.Rulesets.Catch.UI InternalChildren = new Drawable[] { hitExplosionPool = new DrawablePool(10), + caughtFruitPool = new DrawablePool(50), + caughtBananaPool = new DrawablePool(100), + // less capacity is needed compared to fruit because droplet is not stacked + caughtDropletPool = new DrawablePool(25), caughtFruitContainer = new Container { Anchor = Anchor.TopCentre, @@ -452,11 +460,12 @@ namespace osu.Game.Rulesets.Catch.UI private void placeCaughtObject(DrawablePalpableHasCatchHitObject drawableObject, Vector2 position) { - var caughtObject = createCaughtObject(drawableObject.HitObject); + var caughtObject = getCaughtObject(drawableObject.HitObject); if (caughtObject == null) return; caughtObject.CopyFrom(drawableObject); + caughtObject.Anchor = Anchor.TopCentre; caughtObject.Position = position; caughtObject.Scale /= 2; @@ -494,52 +503,62 @@ namespace osu.Game.Rulesets.Catch.UI hitExplosionContainer.Add(hitExplosion); } - private CaughtObject createCaughtObject(PalpableCatchHitObject source) + private CaughtObject getCaughtObject(PalpableCatchHitObject source) { switch (source) { case Fruit _: - return new CaughtFruit(); + return caughtFruitPool.Get(); case Banana _: - return new CaughtBanana(); + return caughtBananaPool.Get(); case Droplet _: - return new CaughtDroplet(); + return caughtDropletPool.Get(); default: return null; } } + private CaughtObject getDroppedObject(CaughtObject caughtObject) + { + var droppedObject = getCaughtObject(caughtObject.HitObject); + + droppedObject.CopyFrom(caughtObject); + droppedObject.Anchor = Anchor.TopLeft; + droppedObject.Position = caughtFruitContainer.ToSpaceOfOtherDrawable(caughtObject.DrawPosition, droppedObjectTarget); + + return droppedObject; + } + private void clearPlate(DroppedObjectAnimation animation) { var caughtObjects = caughtFruitContainer.Children.ToArray(); + var droppedObjects = caughtObjects.Select(getDroppedObject).ToArray(); + caughtFruitContainer.Clear(false); - droppedObjectTarget.AddRange(caughtObjects); + droppedObjectTarget.AddRange(droppedObjects); - foreach (var caughtObject in caughtObjects) - drop(caughtObject, animation); + foreach (var droppedObject in droppedObjects) + applyDropAnimation(droppedObject, animation); } private void removeFromPlate(CaughtObject caughtObject, DroppedObjectAnimation animation) { + var droppedObject = getDroppedObject(caughtObject); + if (!caughtFruitContainer.Remove(caughtObject)) throw new InvalidOperationException("Can only drop a caught object on the plate"); - droppedObjectTarget.Add(caughtObject); + droppedObjectTarget.Add(droppedObject); - drop(caughtObject, animation); + applyDropAnimation(droppedObject, animation); } - private void drop(CaughtObject d, DroppedObjectAnimation animation) + private void applyDropAnimation(Drawable d, DroppedObjectAnimation animation) { - var originalX = d.X * Scale.X; - - d.Anchor = Anchor.TopLeft; - d.Position = caughtFruitContainer.ToSpaceOfOtherDrawable(d.DrawPosition, droppedObjectTarget); - switch (animation) { case DroppedObjectAnimation.Drop: @@ -548,6 +567,7 @@ namespace osu.Game.Rulesets.Catch.UI break; case DroppedObjectAnimation.Explode: + var originalX = droppedObjectTarget.ToSpaceOfOtherDrawable(d.DrawPosition, caughtFruitContainer).X * Scale.X; d.MoveToY(d.Y - 50, 250, Easing.OutSine).Then().MoveToY(d.Y + 50, 500, Easing.InSine); d.MoveToX(d.X + originalX * 6, 1000); d.FadeOut(750); From 1f36bbecd180049026e62f592201434c3cf2c283 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 8 Dec 2020 23:07:30 +0900 Subject: [PATCH 114/818] Fix dropped objects not removed on revert result --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 7aecd95efd..d2930af1ca 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -274,7 +274,7 @@ namespace osu.Game.Rulesets.Catch.UI } caughtFruitContainer.RemoveAll(d => d.HitObject == drawableObject.HitObject); - droppedObjectTarget.RemoveAll(d => (d as DrawableCatchHitObject)?.HitObject == drawableObject.HitObject); + droppedObjectTarget.RemoveAll(d => (d as CaughtObject)?.HitObject == drawableObject.HitObject); hitExplosionContainer.RemoveAll(d => d.HitObject == drawableObject.HitObject); } From 5ca98b00334946cf1f0e0a876cd268374f3a7267 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 8 Dec 2020 23:11:22 +0900 Subject: [PATCH 115/818] Add doc comments a bit --- osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs | 3 +++ .../Objects/Drawables/IHasCatchObjectState.cs | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs index f36d287126..01bf943e1a 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs @@ -12,6 +12,9 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.Objects.Drawables { + /// + /// Represents a caught by the catcher. + /// [Cached(typeof(IHasCatchObjectState))] public abstract class CaughtObject : SkinnableDrawable, IHasCatchObjectState { diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs index a282bc5da0..0a75fb2224 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs @@ -7,6 +7,9 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.Objects.Drawables { + /// + /// Provides a visual state of a . + /// public interface IHasCatchObjectState { PalpableCatchHitObject HitObject { get; } @@ -17,6 +20,9 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables Vector2 Scale { get; } } + /// + /// Provides a visual state of a . + /// public interface IHasFruitState : IHasCatchObjectState { Bindable VisualRepresentation { get; } From 1212ffd24f7c6aae776b796fdab47dec3673d2e8 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 8 Dec 2020 23:35:24 +0900 Subject: [PATCH 116/818] Rename to CopyStateFrom, and add comment --- .../Objects/Drawables/CaughtObject.cs | 9 ++++++--- osu.Game.Rulesets.Catch/UI/Catcher.cs | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs index 01bf943e1a..87319a4498 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs @@ -38,7 +38,10 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables Size = new Vector2(CatchHitObject.OBJECT_RADIUS * 2); } - public virtual void CopyFrom(IHasCatchObjectState objectState) + /// + /// Copies the hit object visual state from another object. + /// + public virtual void CopyStateFrom(IHasCatchObjectState objectState) { HitObject = objectState.HitObject; Scale = objectState.Scale; @@ -68,9 +71,9 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables { } - public override void CopyFrom(IHasCatchObjectState objectState) + public override void CopyStateFrom(IHasCatchObjectState objectState) { - base.CopyFrom(objectState); + base.CopyStateFrom(objectState); var fruitState = (IHasFruitState)objectState; VisualRepresentation.Value = fruitState.VisualRepresentation.Value; diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index d2930af1ca..c9580ca5f8 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -464,7 +464,7 @@ namespace osu.Game.Rulesets.Catch.UI if (caughtObject == null) return; - caughtObject.CopyFrom(drawableObject); + caughtObject.CopyStateFrom(drawableObject); caughtObject.Anchor = Anchor.TopCentre; caughtObject.Position = position; caughtObject.Scale /= 2; @@ -525,7 +525,7 @@ namespace osu.Game.Rulesets.Catch.UI { var droppedObject = getCaughtObject(caughtObject.HitObject); - droppedObject.CopyFrom(caughtObject); + droppedObject.CopyStateFrom(caughtObject); droppedObject.Anchor = Anchor.TopLeft; droppedObject.Position = caughtFruitContainer.ToSpaceOfOtherDrawable(caughtObject.DrawPosition, droppedObjectTarget); From d8838ddbfbda709e1b92e10e6914d8f492a23623 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Tue, 8 Dec 2020 18:48:50 +0100 Subject: [PATCH 117/818] Remove duplicated overload. --- osu.Game/Database/ArchiveModelManager.cs | 43 +----------------------- 1 file changed, 1 insertion(+), 42 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 3ec3b96579..6766ca2b77 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -218,48 +218,7 @@ namespace osu.Game.Database } return import; - } - - /// - /// Import one from a . - /// - /// The stream to import files from. - /// The filename of the archive being imported. - public async Task Import(Stream stream, string filename) - { - var notification = new ProgressNotification - { - Progress = 0, - State = ProgressNotificationState.Active, - Text = $"{HumanisedModelName.Humanize(LetterCasing.Title)} import is initialising...", - }; - - PostNotification.Invoke(notification); - - try - { - // we need to keep around the filename as some model managers (namely SkinManager) use the archive name to populate skin info - var imported = await Import(new ZipArchiveReader(stream, filename), notification.CancellationToken); - - notification.CompletionText = $"Imported {imported}! Click to view."; - notification.CompletionClickAction += () => - { - PresentImport?.Invoke(new[] { imported }); - return true; - }; - notification.State = ProgressNotificationState.Completed; - } - catch (TaskCanceledException) - { - throw; - } - catch (Exception e) - { - notification.Text = $"{HumanisedModelName.Humanize(LetterCasing.Title)} import failed!"; - notification.State = ProgressNotificationState.Cancelled; - Logger.Error(e, $@"Could not import ({filename})", LoggingTarget.Database); - } - } + } /// /// Fired when the user requests to view the resulting import. From aa7d22460d2b7e77bae25f7edd030fbef3cb71ba Mon Sep 17 00:00:00 2001 From: Lucas A Date: Tue, 8 Dec 2020 19:46:55 +0100 Subject: [PATCH 118/818] Override Import() instead. --- osu.Android/OsuGameActivity.cs | 3 ++- osu.Android/OsuGameAndroid.cs | 11 +++++------ osu.Game/Database/ArchiveModelManager.cs | 2 +- osu.Game/OsuGameBase.cs | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Android/OsuGameActivity.cs b/osu.Android/OsuGameActivity.cs index 541455277d..eb9df24bf7 100644 --- a/osu.Android/OsuGameActivity.cs +++ b/osu.Android/OsuGameActivity.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Threading.Tasks; using Android.App; using Android.Content; using Android.Content.PM; @@ -57,7 +58,7 @@ namespace osu.Android var stream = ContentResolver.OpenInputStream(uri); if (stream != null) - game.ScheduleImport(stream, cursor.GetString(filename_column)); + Task.Factory.StartNew(() => game.Import(stream, cursor.GetString(filename_column)), TaskCreationOptions.LongRunning); } } } diff --git a/osu.Android/OsuGameAndroid.cs b/osu.Android/OsuGameAndroid.cs index 81945ee083..c9b27a16d6 100644 --- a/osu.Android/OsuGameAndroid.cs +++ b/osu.Android/OsuGameAndroid.cs @@ -67,12 +67,11 @@ namespace osu.Android } } - /// - /// Schedules a file to be imported once the game is loaded. - /// - /// A stream to the file to import. - /// The filename of the file to import. - public void ScheduleImport(Stream stream, string filename) => WaitForReady(() => this, _ => Task.Run(() => Import(stream, filename))); + public override Task Import(Stream stream, string filename) + { + WaitForReady(() => this, _ => Task.Run(() => base.Import(stream, filename))); + return Task.CompletedTask; + } protected override void LoadComplete() { diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 6766ca2b77..36cc4cce39 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -218,7 +218,7 @@ namespace osu.Game.Database } return import; - } + } /// /// Fired when the user requests to view the resulting import. diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 0fc2b8d1d7..150569f1dd 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -395,7 +395,7 @@ namespace osu.Game } } - public async Task Import(Stream stream, string filename) + public virtual async Task Import(Stream stream, string filename) { var extension = Path.GetExtension(filename)?.ToLowerInvariant(); From 748035e80a4550305cc2b294c2ced3d254829c6e Mon Sep 17 00:00:00 2001 From: Xexxar Date: Tue, 8 Dec 2020 16:53:52 -0600 Subject: [PATCH 119/818] changes to acc scaling curve for speed and aim pp --- .../Difficulty/OsuPerformanceCalculator.cs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 0ebe0ddc2d..f239289d7f 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -121,10 +121,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty : 0.0); } - // Scale the aim value with accuracy _slightly_ - aimValue *= 0.5 + accuracy / 2.0; - // It is important to also consider accuracy difficulty when doing that - aimValue *= 0.98 + Math.Pow(Attributes.OverallDifficulty, 2) / 2500; + // Scale the speed value with accuracy _alot_ + if (accuracy > .8) + aimValue *= 0.5 + Math.Pow(Math.Sin(2.5 * (accuracy - .8) * Math.PI), 2) / 2; + else + aimValue *= accuracy * (.5 / .8); return aimValue; } @@ -154,8 +155,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (mods.Any(m => m is OsuModHidden)) speedValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate); - // Scale the speed value with accuracy _slightly_ - speedValue *= 0.02 + accuracy; + // Scale the speed value with accuracy _alot_ + if (accuracy > .8) + speedValue *= 0.5 + Math.Pow(Math.Sin(2.5 * (accuracy - .8) * Math.PI), 2) / 2; + else + speedValue *= accuracy * (.5 / .8); // It is important to also consider accuracy difficulty when doing that speedValue *= 0.96 + Math.Pow(Attributes.OverallDifficulty, 2) / 1600; From b80204642e52a31163805731db903d1327419a49 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 9 Dec 2020 10:25:35 +0900 Subject: [PATCH 120/818] Revert rename error --- osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs | 2 +- osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs | 2 +- osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs | 2 +- ...HasCatchHitObject.cs => DrawablePalpableCatchHitObject.cs} | 4 ++-- osu.Game.Rulesets.Catch/UI/Catcher.cs | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) rename osu.Game.Rulesets.Catch/Objects/Drawables/{DrawablePalpableHasCatchHitObject.cs => DrawablePalpableCatchHitObject.cs} (92%) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs index 34d89eb188..4bc5da2396 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs @@ -9,7 +9,7 @@ using osu.Game.Skinning; namespace osu.Game.Rulesets.Catch.Objects.Drawables { - public class DrawableBanana : DrawablePalpableHasCatchHitObject + public class DrawableBanana : DrawablePalpableCatchHitObject { public DrawableBanana() : this(null) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs index acdb3bb38c..c5f0bb8b18 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs @@ -9,7 +9,7 @@ using osu.Game.Skinning; namespace osu.Game.Rulesets.Catch.Objects.Drawables { - public class DrawableDroplet : DrawablePalpableHasCatchHitObject + public class DrawableDroplet : DrawablePalpableCatchHitObject { public DrawableDroplet() : this(null) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs index 6ec46f6535..57f27d01b8 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs @@ -10,7 +10,7 @@ using osu.Game.Skinning; namespace osu.Game.Rulesets.Catch.Objects.Drawables { - public class DrawableFruit : DrawablePalpableHasCatchHitObject, IHasFruitState + public class DrawableFruit : DrawablePalpableCatchHitObject, IHasFruitState { public Bindable VisualRepresentation { get; } = new Bindable(); diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableHasCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs similarity index 92% rename from osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableHasCatchHitObject.cs rename to osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs index ee893f7880..aea9d2c082 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableHasCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs @@ -11,7 +11,7 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.Objects.Drawables { [Cached(typeof(IHasCatchObjectState))] - public abstract class DrawablePalpableHasCatchHitObject : DrawableCatchHitObject, IHasCatchObjectState + public abstract class DrawablePalpableCatchHitObject : DrawableCatchHitObject, IHasCatchObjectState { public new PalpableCatchHitObject HitObject => (PalpableCatchHitObject)base.HitObject; @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables public float DisplayRadius => CatchHitObject.OBJECT_RADIUS * HitObject.Scale * ScaleFactor; - protected DrawablePalpableHasCatchHitObject([CanBeNull] CatchHitObject h) + protected DrawablePalpableCatchHitObject([CanBeNull] CatchHitObject h) : base(h) { Origin = Anchor.Centre; diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index c9580ca5f8..30a5832c4f 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -223,7 +223,7 @@ namespace osu.Game.Rulesets.Catch.UI catchResult.CatcherAnimationState = CurrentState; catchResult.CatcherHyperDash = HyperDashing; - if (!(drawableObject is DrawablePalpableHasCatchHitObject palpableObject)) return; + if (!(drawableObject is DrawablePalpableCatchHitObject palpableObject)) return; var hitObject = palpableObject.HitObject; @@ -458,7 +458,7 @@ namespace osu.Game.Rulesets.Catch.UI updateCatcher(); } - private void placeCaughtObject(DrawablePalpableHasCatchHitObject drawableObject, Vector2 position) + private void placeCaughtObject(DrawablePalpableCatchHitObject drawableObject, Vector2 position) { var caughtObject = getCaughtObject(drawableObject.HitObject); From df9de7a8ddee1986df712deb138c7d4238f41bed Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 9 Dec 2020 10:28:42 +0900 Subject: [PATCH 121/818] Remove null check that is not required anymore --- osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs index b105fb4034..51c06c8e37 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs @@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default protected override void Update() { - if (BorderPiece != null && ObjectState?.HitObject != null) + if (BorderPiece != null) BorderPiece.Alpha = (float)Math.Clamp((ObjectState.HitObject.StartTime - Time.Current) / 500, 0, 1); } } From ccca7e0b25a97f52d9ef6d754d2f39fe6132a65b Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 9 Dec 2020 10:35:01 +0900 Subject: [PATCH 122/818] more specific type droppedObjectContainer --- osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs | 6 +++--- .../TestSceneCatcherArea.cs | 4 ++-- .../TestSceneHyperDashColouring.cs | 2 +- osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 2 +- osu.Game.Rulesets.Catch/UI/Catcher.cs | 14 ++++++++++---- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 2 +- 6 files changed, 18 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs index 2747524503..e8bb57cdf3 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Catch.Tests [Resolved] private OsuConfigManager config { get; set; } - private Container droppedObjectContainer; + private Container droppedObjectContainer; private TestCatcher catcher; @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Catch.Tests }; var trailContainer = new Container(); - droppedObjectContainer = new Container(); + droppedObjectContainer = new Container(); catcher = new TestCatcher(trailContainer, droppedObjectContainer, difficulty); Child = new Container @@ -277,7 +277,7 @@ namespace osu.Game.Rulesets.Catch.Tests { public IEnumerable CaughtObjects => this.ChildrenOfType(); - public TestCatcher(Container trailsTarget, Container droppedObjectTarget, BeatmapDifficulty difficulty) + public TestCatcher(Container trailsTarget, Container droppedObjectTarget, BeatmapDifficulty difficulty) : base(trailsTarget, droppedObjectTarget, difficulty) { } diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs index 3d5e44476e..31c285ef22 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs @@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Catch.Tests SetContents(() => { - var droppedObjectContainer = new Container + var droppedObjectContainer = new Container { RelativeSizeAxes = Axes.Both }; @@ -102,7 +102,7 @@ namespace osu.Game.Rulesets.Catch.Tests private class TestCatcherArea : CatcherArea { - public TestCatcherArea(Container droppedObjectContainer, BeatmapDifficulty beatmapDifficulty) + public TestCatcherArea(Container droppedObjectContainer, BeatmapDifficulty beatmapDifficulty) : base(droppedObjectContainer, beatmapDifficulty) { } diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs index d78dc2d2b5..683a776dcc 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs @@ -118,7 +118,7 @@ namespace osu.Game.Rulesets.Catch.Tests AddStep("create hyper-dashing catcher", () => { - Child = setupSkinHierarchy(catcherArea = new CatcherArea(new Container()) + Child = setupSkinHierarchy(catcherArea = new CatcherArea(new Container()) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index fdc12bf088..73420a9eda 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Catch.UI public CatchPlayfield(BeatmapDifficulty difficulty, Func> createDrawableRepresentation) { - var droppedObjectContainer = new Container + var droppedObjectContainer = new Container { RelativeSizeAxes = Axes.Both, }; diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 30a5832c4f..2893fcee92 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -53,10 +53,16 @@ namespace osu.Game.Rulesets.Catch.UI private CatcherTrailDisplay trails; - private readonly Container droppedObjectTarget; - + /// + /// Contains caught objects on the plate. + /// private readonly Container caughtFruitContainer; + /// + /// Contains objects dropped from the plate. + /// + private readonly Container droppedObjectTarget; + public CatcherAnimationState CurrentState { get; private set; } /// @@ -112,7 +118,7 @@ namespace osu.Game.Rulesets.Catch.UI private readonly DrawablePool caughtBananaPool; private readonly DrawablePool caughtDropletPool; - public Catcher([NotNull] Container trailsTarget, [NotNull] Container droppedObjectTarget, BeatmapDifficulty difficulty = null) + public Catcher([NotNull] Container trailsTarget, [NotNull] Container droppedObjectTarget, BeatmapDifficulty difficulty = null) { this.trailsTarget = trailsTarget; this.droppedObjectTarget = droppedObjectTarget; @@ -274,7 +280,7 @@ namespace osu.Game.Rulesets.Catch.UI } caughtFruitContainer.RemoveAll(d => d.HitObject == drawableObject.HitObject); - droppedObjectTarget.RemoveAll(d => (d as CaughtObject)?.HitObject == drawableObject.HitObject); + droppedObjectTarget.RemoveAll(d => d.HitObject == drawableObject.HitObject); hitExplosionContainer.RemoveAll(d => d.HitObject == drawableObject.HitObject); } diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 857d9141c9..44adbd5512 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Catch.UI public readonly Catcher MovableCatcher; private readonly CatchComboDisplay comboDisplay; - public CatcherArea(Container droppedObjectContainer, BeatmapDifficulty difficulty = null) + public CatcherArea(Container droppedObjectContainer, BeatmapDifficulty difficulty = null) { Size = new Vector2(CatchPlayfield.WIDTH, CATCHER_SIZE); Children = new Drawable[] From c8b0934573c029e7568d63927533dcbddcde9099 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 9 Dec 2020 10:35:36 +0900 Subject: [PATCH 123/818] Rename caughtFruitContainer -> caughtObjectContainer --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 2893fcee92..475fca7f1c 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Catch.UI /// /// Contains caught objects on the plate. /// - private readonly Container caughtFruitContainer; + private readonly Container caughtObjectContainer; /// /// Contains objects dropped from the plate. @@ -138,7 +138,7 @@ namespace osu.Game.Rulesets.Catch.UI caughtBananaPool = new DrawablePool(100), // less capacity is needed compared to fruit because droplet is not stacked caughtDropletPool = new DrawablePool(25), - caughtFruitContainer = new Container + caughtObjectContainer = new Container { Anchor = Anchor.TopCentre, Origin = Anchor.BottomCentre, @@ -186,7 +186,7 @@ namespace osu.Game.Rulesets.Catch.UI /// /// Creates proxied content to be displayed beneath hitobjects. /// - public Drawable CreateProxiedContent() => caughtFruitContainer.CreateProxy(); + public Drawable CreateProxiedContent() => caughtObjectContainer.CreateProxy(); /// /// Calculates the scale of the catcher based off the provided beatmap difficulty. @@ -279,7 +279,7 @@ namespace osu.Game.Rulesets.Catch.UI SetHyperDashState(); } - caughtFruitContainer.RemoveAll(d => d.HitObject == drawableObject.HitObject); + caughtObjectContainer.RemoveAll(d => d.HitObject == drawableObject.HitObject); droppedObjectTarget.RemoveAll(d => d.HitObject == drawableObject.HitObject); hitExplosionContainer.RemoveAll(d => d.HitObject == drawableObject.HitObject); } @@ -475,7 +475,7 @@ namespace osu.Game.Rulesets.Catch.UI caughtObject.Position = position; caughtObject.Scale /= 2; - caughtFruitContainer.Add(caughtObject); + caughtObjectContainer.Add(caughtObject); if (!caughtObject.StaysOnPlate) removeFromPlate(caughtObject, DroppedObjectAnimation.Explode); @@ -486,7 +486,7 @@ namespace osu.Game.Rulesets.Catch.UI const float radius_div_2 = CatchHitObject.OBJECT_RADIUS / 2; const float allowance = 10; - while (caughtFruitContainer.Any(f => Vector2Extensions.Distance(f.Position, position) < (displayRadius + radius_div_2) / (allowance / 2))) + while (caughtObjectContainer.Any(f => Vector2Extensions.Distance(f.Position, position) < (displayRadius + radius_div_2) / (allowance / 2))) { float diff = (displayRadius + radius_div_2) / allowance; @@ -533,17 +533,17 @@ namespace osu.Game.Rulesets.Catch.UI droppedObject.CopyStateFrom(caughtObject); droppedObject.Anchor = Anchor.TopLeft; - droppedObject.Position = caughtFruitContainer.ToSpaceOfOtherDrawable(caughtObject.DrawPosition, droppedObjectTarget); + droppedObject.Position = caughtObjectContainer.ToSpaceOfOtherDrawable(caughtObject.DrawPosition, droppedObjectTarget); return droppedObject; } private void clearPlate(DroppedObjectAnimation animation) { - var caughtObjects = caughtFruitContainer.Children.ToArray(); + var caughtObjects = caughtObjectContainer.Children.ToArray(); var droppedObjects = caughtObjects.Select(getDroppedObject).ToArray(); - caughtFruitContainer.Clear(false); + caughtObjectContainer.Clear(false); droppedObjectTarget.AddRange(droppedObjects); @@ -555,7 +555,7 @@ namespace osu.Game.Rulesets.Catch.UI { var droppedObject = getDroppedObject(caughtObject); - if (!caughtFruitContainer.Remove(caughtObject)) + if (!caughtObjectContainer.Remove(caughtObject)) throw new InvalidOperationException("Can only drop a caught object on the plate"); droppedObjectTarget.Add(droppedObject); @@ -573,7 +573,7 @@ namespace osu.Game.Rulesets.Catch.UI break; case DroppedObjectAnimation.Explode: - var originalX = droppedObjectTarget.ToSpaceOfOtherDrawable(d.DrawPosition, caughtFruitContainer).X * Scale.X; + var originalX = droppedObjectTarget.ToSpaceOfOtherDrawable(d.DrawPosition, caughtObjectContainer).X * Scale.X; d.MoveToY(d.Y - 50, 250, Easing.OutSine).Then().MoveToY(d.Y + 50, 500, Easing.InSine); d.MoveToX(d.X + originalX * 6, 1000); d.FadeOut(750); From 86445e7c2304eb54193c2278ac306e938b0a525e Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 9 Dec 2020 10:36:54 +0900 Subject: [PATCH 124/818] Remove unnecessary copy --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 475fca7f1c..e885881c42 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -540,8 +540,7 @@ namespace osu.Game.Rulesets.Catch.UI private void clearPlate(DroppedObjectAnimation animation) { - var caughtObjects = caughtObjectContainer.Children.ToArray(); - var droppedObjects = caughtObjects.Select(getDroppedObject).ToArray(); + var droppedObjects = caughtObjectContainer.Children.Select(getDroppedObject).ToArray(); caughtObjectContainer.Clear(false); From b52e279702a879eebd333efa734aae9b3b1a2820 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 9 Dec 2020 10:38:11 +0900 Subject: [PATCH 125/818] Reword exception message --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index e885881c42..125f735a60 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -555,7 +555,7 @@ namespace osu.Game.Rulesets.Catch.UI var droppedObject = getDroppedObject(caughtObject); if (!caughtObjectContainer.Remove(caughtObject)) - throw new InvalidOperationException("Can only drop a caught object on the plate"); + throw new InvalidOperationException("Can only drop objects that were previously caught on the plate"); droppedObjectTarget.Add(droppedObject); From da2f3d4473a0583b53cfc76671b48e209f21f321 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 9 Dec 2020 10:40:42 +0900 Subject: [PATCH 126/818] Move classes to separate files --- .../Objects/Drawables/CaughtBanana.cs | 18 +++++++++ .../Objects/Drawables/CaughtDroplet.cs | 20 ++++++++++ .../Objects/Drawables/CaughtFruit.cs | 29 +++++++++++++++ .../Objects/Drawables/CaughtObject.cs | 37 ------------------- .../Objects/Drawables/IHasCatchObjectState.cs | 8 ---- .../Objects/Drawables/IHasFruitState.cs | 15 ++++++++ 6 files changed, 82 insertions(+), 45 deletions(-) create mode 100644 osu.Game.Rulesets.Catch/Objects/Drawables/CaughtBanana.cs create mode 100644 osu.Game.Rulesets.Catch/Objects/Drawables/CaughtDroplet.cs create mode 100644 osu.Game.Rulesets.Catch/Objects/Drawables/CaughtFruit.cs create mode 100644 osu.Game.Rulesets.Catch/Objects/Drawables/IHasFruitState.cs diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtBanana.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtBanana.cs new file mode 100644 index 0000000000..8a91f82437 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtBanana.cs @@ -0,0 +1,18 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Rulesets.Catch.Skinning.Default; + +namespace osu.Game.Rulesets.Catch.Objects.Drawables +{ + /// + /// Represents a caught by the catcher. + /// + public class CaughtBanana : CaughtObject + { + public CaughtBanana() + : base(CatchSkinComponents.Banana, _ => new BananaPiece()) + { + } + } +} diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtDroplet.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtDroplet.cs new file mode 100644 index 0000000000..4a3397feff --- /dev/null +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtDroplet.cs @@ -0,0 +1,20 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Rulesets.Catch.Skinning.Default; + +namespace osu.Game.Rulesets.Catch.Objects.Drawables +{ + /// + /// Represents a caught by the catcher. + /// + public class CaughtDroplet : CaughtObject + { + public override bool StaysOnPlate => false; + + public CaughtDroplet() + : base(CatchSkinComponents.Droplet, _ => new DropletPiece()) + { + } + } +} diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtFruit.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtFruit.cs new file mode 100644 index 0000000000..140b411c88 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtFruit.cs @@ -0,0 +1,29 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Bindables; +using osu.Game.Rulesets.Catch.Skinning.Default; + +namespace osu.Game.Rulesets.Catch.Objects.Drawables +{ + /// + /// Represents a caught by the catcher. + /// + public class CaughtFruit : CaughtObject, IHasFruitState + { + public Bindable VisualRepresentation { get; } = new Bindable(); + + public CaughtFruit() + : base(CatchSkinComponents.Fruit, _ => new FruitPiece()) + { + } + + public override void CopyStateFrom(IHasCatchObjectState objectState) + { + base.CopyStateFrom(objectState); + + var fruitState = (IHasFruitState)objectState; + VisualRepresentation.Value = fruitState.VisualRepresentation.Value; + } + } +} diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs index 87319a4498..e597d1a4cd 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs @@ -5,7 +5,6 @@ using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Game.Rulesets.Catch.Skinning.Default; using osu.Game.Skinning; using osuTK; using osuTK.Graphics; @@ -61,40 +60,4 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables base.FreeAfterUse(); } } - - public class CaughtFruit : CaughtObject, IHasFruitState - { - public Bindable VisualRepresentation { get; } = new Bindable(); - - public CaughtFruit() - : base(CatchSkinComponents.Fruit, _ => new FruitPiece()) - { - } - - public override void CopyStateFrom(IHasCatchObjectState objectState) - { - base.CopyStateFrom(objectState); - - var fruitState = (IHasFruitState)objectState; - VisualRepresentation.Value = fruitState.VisualRepresentation.Value; - } - } - - public class CaughtBanana : CaughtObject - { - public CaughtBanana() - : base(CatchSkinComponents.Banana, _ => new BananaPiece()) - { - } - } - - public class CaughtDroplet : CaughtObject - { - public override bool StaysOnPlate => false; - - public CaughtDroplet() - : base(CatchSkinComponents.Droplet, _ => new DropletPiece()) - { - } - } } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs index 0a75fb2224..55ca502877 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs @@ -19,12 +19,4 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables float Rotation { get; } Vector2 Scale { get; } } - - /// - /// Provides a visual state of a . - /// - public interface IHasFruitState : IHasCatchObjectState - { - Bindable VisualRepresentation { get; } - } } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/IHasFruitState.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasFruitState.cs new file mode 100644 index 0000000000..2d4de543c3 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasFruitState.cs @@ -0,0 +1,15 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Bindables; + +namespace osu.Game.Rulesets.Catch.Objects.Drawables +{ + /// + /// Provides a visual state of a . + /// + public interface IHasFruitState : IHasCatchObjectState + { + Bindable VisualRepresentation { get; } + } +} From 775c4bad973c3f9afba809851b0b63add97929a1 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 9 Dec 2020 10:47:04 +0900 Subject: [PATCH 127/818] Remove unneeded lifetime assignment --- osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs index e597d1a4cd..fb83b73212 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs @@ -52,10 +52,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables protected override void FreeAfterUse() { ClearTransforms(); - Alpha = 1; - LifetimeStart = double.MinValue; - LifetimeEnd = double.MaxValue; base.FreeAfterUse(); } From a8e2f35b622a5797837115eeef7f2b96fd4a9169 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 9 Dec 2020 10:50:35 +0900 Subject: [PATCH 128/818] Remove unneeded check of caught object removal The logic was public but now it is private so the condition is ensured by the caller --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 125f735a60..64dacd09e6 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -554,8 +554,7 @@ namespace osu.Game.Rulesets.Catch.UI { var droppedObject = getDroppedObject(caughtObject); - if (!caughtObjectContainer.Remove(caughtObject)) - throw new InvalidOperationException("Can only drop objects that were previously caught on the plate"); + caughtObjectContainer.Remove(caughtObject); droppedObjectTarget.Add(droppedObject); From ff5150a14d152242026cdd6be462088f2a15ad03 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 9 Dec 2020 12:03:52 +0900 Subject: [PATCH 129/818] Fix typo in IMultiplayerClient xmldoc --- osu.Game/Online/RealtimeMultiplayer/IMultiplayerClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerClient.cs b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerClient.cs index e75b0da207..c162f066d4 100644 --- a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerClient.cs +++ b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerClient.cs @@ -53,7 +53,7 @@ namespace osu.Game.Online.RealtimeMultiplayer Task LoadRequested(); /// - /// Signals that a match has started. All user in the state should begin gameplay as soon as possible. + /// Signals that a match has started. All users in the state should begin gameplay as soon as possible. /// Task MatchStarted(); From 2046cbe2d987fa12b46f6acfcd40a91a5cb283f1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 9 Dec 2020 12:05:50 +0900 Subject: [PATCH 130/818] Add missing exceptions to server xmldoc --- osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs index 565c07d0ec..3c92f70a01 100644 --- a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs +++ b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs @@ -17,6 +17,7 @@ namespace osu.Game.Online.RealtimeMultiplayer /// /// Request to leave the currently joined room. /// + /// If the user is not in a room. Task LeaveRoom(); /// @@ -24,12 +25,15 @@ namespace osu.Game.Online.RealtimeMultiplayer /// /// The new user which is to become host. /// A user other than the current host is attempting to transfer host. + /// If the user is not in a room. Task TransferHost(long userId); /// /// As the host, update the settings of the currently joined room. /// /// The new settings to apply. + /// A user other than the current host is attempting to transfer host. + /// If the user is not in a room. Task ChangeSettings(MultiplayerRoomSettings settings); /// @@ -37,12 +41,14 @@ namespace osu.Game.Online.RealtimeMultiplayer /// /// The proposed new state. /// If the state change requested is not valid, given the previous state or room state. + /// If the user is not in a room. Task ChangeState(MultiplayerUserState newState); /// /// As the host of a room, start the match. /// /// A user other than the current host is attempting to start the game. + /// If the user is not in a room. Task StartMatch(); } } From fd4fa963ac6e86265ad9db5e5335040ae1de5291 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 9 Dec 2020 12:07:19 +0900 Subject: [PATCH 131/818] Standardise exception naming --- ...lreadyInMultiplayerRoom.cs => AlreadyInRoomException.cs} | 6 +++--- osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs | 4 ++-- ...InvalidStateChange.cs => InvalidStateChangeException.cs} | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) rename osu.Game/Online/RealtimeMultiplayer/{UserAlreadyInMultiplayerRoom.cs => AlreadyInRoomException.cs} (60%) rename osu.Game/Online/RealtimeMultiplayer/{InvalidStateChange.cs => InvalidStateChangeException.cs} (65%) diff --git a/osu.Game/Online/RealtimeMultiplayer/UserAlreadyInMultiplayerRoom.cs b/osu.Game/Online/RealtimeMultiplayer/AlreadyInRoomException.cs similarity index 60% rename from osu.Game/Online/RealtimeMultiplayer/UserAlreadyInMultiplayerRoom.cs rename to osu.Game/Online/RealtimeMultiplayer/AlreadyInRoomException.cs index 9a2090c710..f99bea651c 100644 --- a/osu.Game/Online/RealtimeMultiplayer/UserAlreadyInMultiplayerRoom.cs +++ b/osu.Game/Online/RealtimeMultiplayer/AlreadyInRoomException.cs @@ -5,10 +5,10 @@ using System; namespace osu.Game.Online.RealtimeMultiplayer { - public class UserAlreadyInMultiplayerRoom : Exception + public class AlreadyInRoomException : Exception { - public UserAlreadyInMultiplayerRoom() - : base("This user is already in a room.") + public AlreadyInRoomException() + : base("This user is already in a multiplayer room.") { } } diff --git a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs index 3c92f70a01..1bb6331c0a 100644 --- a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs +++ b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs @@ -11,7 +11,7 @@ namespace osu.Game.Online.RealtimeMultiplayer /// Request to join a multiplayer room. /// /// The databased room ID. - /// If the user is already in the requested (or another) room. + /// If the user is already in the requested (or another) room. Task JoinRoom(long roomId); /// @@ -40,7 +40,7 @@ namespace osu.Game.Online.RealtimeMultiplayer /// Change the local user state in the currently joined room. /// /// The proposed new state. - /// If the state change requested is not valid, given the previous state or room state. + /// If the state change requested is not valid, given the previous state or room state. /// If the user is not in a room. Task ChangeState(MultiplayerUserState newState); diff --git a/osu.Game/Online/RealtimeMultiplayer/InvalidStateChange.cs b/osu.Game/Online/RealtimeMultiplayer/InvalidStateChangeException.cs similarity index 65% rename from osu.Game/Online/RealtimeMultiplayer/InvalidStateChange.cs rename to osu.Game/Online/RealtimeMultiplayer/InvalidStateChangeException.cs index d1016e95cb..1e33f55491 100644 --- a/osu.Game/Online/RealtimeMultiplayer/InvalidStateChange.cs +++ b/osu.Game/Online/RealtimeMultiplayer/InvalidStateChangeException.cs @@ -5,9 +5,9 @@ using System; namespace osu.Game.Online.RealtimeMultiplayer { - public class InvalidStateChange : Exception + public class InvalidStateChangeException : Exception { - public InvalidStateChange(MultiplayerUserState oldState, MultiplayerUserState newState) + public InvalidStateChangeException(MultiplayerUserState oldState, MultiplayerUserState newState) : base($"Cannot change from {oldState} to {newState}") { } From 1013749a83365c54d7585e1a4289c54113754e52 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 9 Dec 2020 12:10:47 +0900 Subject: [PATCH 132/818] Change user id type to int --- osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomUser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomUser.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomUser.cs index 17aca4458a..60f1c9e42e 100644 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomUser.cs +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomUser.cs @@ -11,7 +11,7 @@ namespace osu.Game.Online.RealtimeMultiplayer [Serializable] public class MultiplayerRoomUser : IEquatable { - public readonly long UserID; + public readonly int UserID; public MultiplayerUserState State { get; set; } = MultiplayerUserState.Idle; From 0eb5b16454647d0f1fa7de484b2f5eb15d6d37fc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 9 Dec 2020 12:12:03 +0900 Subject: [PATCH 133/818] Remove Empty() implementation for RoomSettings until otherwise necessary --- osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs | 2 +- osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomSettings.cs | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs index 0f18ab6c89..24c20bded4 100644 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs @@ -27,7 +27,7 @@ namespace osu.Game.Online.RealtimeMultiplayer /// /// All currently enforced game settings for this room. /// - public MultiplayerRoomSettings Settings { get; set; } = MultiplayerRoomSettings.Empty(); + public MultiplayerRoomSettings Settings { get; set; } = new MultiplayerRoomSettings(); /// /// All users currently in this room. diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomSettings.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomSettings.cs index 3137afa8a4..8f0f4febd1 100644 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomSettings.cs +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomSettings.cs @@ -24,7 +24,5 @@ namespace osu.Game.Online.RealtimeMultiplayer public bool Equals(MultiplayerRoomSettings other) => BeatmapID == other.BeatmapID && Mods.SequenceEqual(other.Mods) && RulesetID == other.RulesetID; public override string ToString() => $"Beatmap:{BeatmapID} Mods:{string.Join(',', Mods)} Ruleset:{RulesetID}"; - - public static MultiplayerRoomSettings Empty() => new MultiplayerRoomSettings(); } } From 427d41bab5a08b998f6b8c3349e402f0c4315890 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 9 Dec 2020 12:17:37 +0900 Subject: [PATCH 134/818] Add missing licence headers --- osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs | 3 +++ osu.Game/Online/RealtimeMultiplayer/NotJoinedRoomException.cs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs index 1bb6331c0a..836260a26d 100644 --- a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs +++ b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs @@ -1,3 +1,6 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + using System.Threading.Tasks; namespace osu.Game.Online.RealtimeMultiplayer diff --git a/osu.Game/Online/RealtimeMultiplayer/NotJoinedRoomException.cs b/osu.Game/Online/RealtimeMultiplayer/NotJoinedRoomException.cs index ca4a54f6d7..d71200a086 100644 --- a/osu.Game/Online/RealtimeMultiplayer/NotJoinedRoomException.cs +++ b/osu.Game/Online/RealtimeMultiplayer/NotJoinedRoomException.cs @@ -1,3 +1,6 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + using System; namespace osu.Game.Online.RealtimeMultiplayer From 48129c52d676d8e33d63852031e530fe4cef6b32 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 9 Dec 2020 12:38:24 +0900 Subject: [PATCH 135/818] Change get-only property for now --- .../Online/RealtimeMultiplayer/MultiplayerClientState.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerClientState.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerClientState.cs index 8c440abba3..27fa0e9ac9 100644 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerClientState.cs +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerClientState.cs @@ -1,13 +1,16 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; + #nullable enable namespace osu.Game.Online.RealtimeMultiplayer { + [Serializable] public class MultiplayerClientState { - public long CurrentRoomID { get; set; } + public readonly long CurrentRoomID; public MultiplayerClientState(in long roomId) { From c92c2cbfc01716b553369e376b9754d9f70c5262 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 9 Dec 2020 14:46:43 +0900 Subject: [PATCH 136/818] Change exceptions which should be returned to the user to HubException type See https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.signalr.hubexception?view=aspnetcore-5.0. --- .../RealtimeMultiplayer/AlreadyInRoomException.cs | 10 +++++++++- .../RealtimeMultiplayer/InvalidStateChangeException.cs | 10 +++++++++- .../Online/RealtimeMultiplayer/NotHostException.cs | 10 +++++++++- .../RealtimeMultiplayer/NotJoinedRoomException.cs | 10 +++++++++- 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/AlreadyInRoomException.cs b/osu.Game/Online/RealtimeMultiplayer/AlreadyInRoomException.cs index f99bea651c..7f3c2b339a 100644 --- a/osu.Game/Online/RealtimeMultiplayer/AlreadyInRoomException.cs +++ b/osu.Game/Online/RealtimeMultiplayer/AlreadyInRoomException.cs @@ -2,14 +2,22 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Runtime.Serialization; +using Microsoft.AspNetCore.SignalR; namespace osu.Game.Online.RealtimeMultiplayer { - public class AlreadyInRoomException : Exception + [Serializable] + public class AlreadyInRoomException : HubException { public AlreadyInRoomException() : base("This user is already in a multiplayer room.") { } + + protected AlreadyInRoomException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } } } diff --git a/osu.Game/Online/RealtimeMultiplayer/InvalidStateChangeException.cs b/osu.Game/Online/RealtimeMultiplayer/InvalidStateChangeException.cs index 1e33f55491..d9a276fc19 100644 --- a/osu.Game/Online/RealtimeMultiplayer/InvalidStateChangeException.cs +++ b/osu.Game/Online/RealtimeMultiplayer/InvalidStateChangeException.cs @@ -2,14 +2,22 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Runtime.Serialization; +using Microsoft.AspNetCore.SignalR; namespace osu.Game.Online.RealtimeMultiplayer { - public class InvalidStateChangeException : Exception + [Serializable] + public class InvalidStateChangeException : HubException { public InvalidStateChangeException(MultiplayerUserState oldState, MultiplayerUserState newState) : base($"Cannot change from {oldState} to {newState}") { } + + protected InvalidStateChangeException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } } } diff --git a/osu.Game/Online/RealtimeMultiplayer/NotHostException.cs b/osu.Game/Online/RealtimeMultiplayer/NotHostException.cs index e421c6ae28..56095043f0 100644 --- a/osu.Game/Online/RealtimeMultiplayer/NotHostException.cs +++ b/osu.Game/Online/RealtimeMultiplayer/NotHostException.cs @@ -2,14 +2,22 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Runtime.Serialization; +using Microsoft.AspNetCore.SignalR; namespace osu.Game.Online.RealtimeMultiplayer { - public class NotHostException : Exception + [Serializable] + public class NotHostException : HubException { public NotHostException() : base("User is attempting to perform a host level operation while not the host") { } + + protected NotHostException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } } } diff --git a/osu.Game/Online/RealtimeMultiplayer/NotJoinedRoomException.cs b/osu.Game/Online/RealtimeMultiplayer/NotJoinedRoomException.cs index d71200a086..7a6e089d0b 100644 --- a/osu.Game/Online/RealtimeMultiplayer/NotJoinedRoomException.cs +++ b/osu.Game/Online/RealtimeMultiplayer/NotJoinedRoomException.cs @@ -2,14 +2,22 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Runtime.Serialization; +using Microsoft.AspNetCore.SignalR; namespace osu.Game.Online.RealtimeMultiplayer { - public class NotJoinedRoomException : Exception + [Serializable] + public class NotJoinedRoomException : HubException { public NotJoinedRoomException() : base("This user has not yet joined a multiplayer room.") { } + + protected NotJoinedRoomException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } } } From e09715d71efe23bdedf5a6b7718e6bdbe4ef2258 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 9 Dec 2020 14:47:26 +0900 Subject: [PATCH 137/818] Add ToString implementation to MultiplayerRoom for easier debug --- osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs index 24c20bded4..1ba8d0c487 100644 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs @@ -50,5 +50,7 @@ namespace osu.Game.Online.RealtimeMultiplayer /// Request a lock on this room to perform a thread-safe update. /// public LockUntilDisposal LockForUpdate() => new LockUntilDisposal(writeLock); + + public override string ToString() => $"RoomID:{RoomID} Host:{Host?.UserID} Users:{Users.Count} State:{State} Settings: [{Settings}]"; } } From bef52af1da4da012d37eb78dc2ac62fa138a371e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 9 Dec 2020 15:05:32 +0900 Subject: [PATCH 138/818] Move client state class to server implementation Never used by client. --- .../MultiplayerClientState.cs | 20 ------------------- 1 file changed, 20 deletions(-) delete mode 100644 osu.Game/Online/RealtimeMultiplayer/MultiplayerClientState.cs diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerClientState.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerClientState.cs deleted file mode 100644 index 27fa0e9ac9..0000000000 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerClientState.cs +++ /dev/null @@ -1,20 +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; - -#nullable enable - -namespace osu.Game.Online.RealtimeMultiplayer -{ - [Serializable] - public class MultiplayerClientState - { - public readonly long CurrentRoomID; - - public MultiplayerClientState(in long roomId) - { - CurrentRoomID = roomId; - } - } -} From ab00a15555a31394e23b9dd6277fc02aa25f9239 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 9 Dec 2020 15:05:57 +0900 Subject: [PATCH 139/818] Add JsonConstructor specs to allow for correct deserialization of readonly fields --- osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs | 2 ++ osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomUser.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs index 1ba8d0c487..5704ddd675 100644 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; +using Newtonsoft.Json; namespace osu.Game.Online.RealtimeMultiplayer { @@ -41,6 +42,7 @@ namespace osu.Game.Online.RealtimeMultiplayer private object writeLock = new object(); + [JsonConstructor] public MultiplayerRoom(in long roomId) { RoomID = roomId; diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomUser.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomUser.cs index 60f1c9e42e..caf1a70197 100644 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomUser.cs +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomUser.cs @@ -4,6 +4,7 @@ #nullable enable using System; +using Newtonsoft.Json; using osu.Game.Users; namespace osu.Game.Online.RealtimeMultiplayer @@ -17,6 +18,7 @@ namespace osu.Game.Online.RealtimeMultiplayer public User? User { get; set; } + [JsonConstructor] public MultiplayerRoomUser(in int userId) { UserID = userId; From 578e5cb92e66a62bd81b08d2cb0c944cedca5030 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 9 Dec 2020 15:59:13 +0900 Subject: [PATCH 140/818] Also make InvalidStateException serializable --- .../RealtimeMultiplayer/InvalidStateException.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/InvalidStateException.cs b/osu.Game/Online/RealtimeMultiplayer/InvalidStateException.cs index 8393e7e925..7791bfc69f 100644 --- a/osu.Game/Online/RealtimeMultiplayer/InvalidStateException.cs +++ b/osu.Game/Online/RealtimeMultiplayer/InvalidStateException.cs @@ -2,14 +2,22 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Runtime.Serialization; +using Microsoft.AspNetCore.SignalR; namespace osu.Game.Online.RealtimeMultiplayer { - public class InvalidStateException : Exception + [Serializable] + public class InvalidStateException : HubException { public InvalidStateException(string message) : base(message) { } + + protected InvalidStateException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } } } From 1e08b298b865cac8f05e13c4941a23a7b0131fa6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 9 Dec 2020 16:01:09 +0900 Subject: [PATCH 141/818] Remove unnecessary exception type --- .../AlreadyInRoomException.cs | 23 ------------------- 1 file changed, 23 deletions(-) delete mode 100644 osu.Game/Online/RealtimeMultiplayer/AlreadyInRoomException.cs diff --git a/osu.Game/Online/RealtimeMultiplayer/AlreadyInRoomException.cs b/osu.Game/Online/RealtimeMultiplayer/AlreadyInRoomException.cs deleted file mode 100644 index 7f3c2b339a..0000000000 --- a/osu.Game/Online/RealtimeMultiplayer/AlreadyInRoomException.cs +++ /dev/null @@ -1,23 +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.Runtime.Serialization; -using Microsoft.AspNetCore.SignalR; - -namespace osu.Game.Online.RealtimeMultiplayer -{ - [Serializable] - public class AlreadyInRoomException : HubException - { - public AlreadyInRoomException() - : base("This user is already in a multiplayer room.") - { - } - - protected AlreadyInRoomException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } - } -} From bb97eae8b1b46df5162a6502a282b0915527dd6b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 9 Dec 2020 16:02:37 +0900 Subject: [PATCH 142/818] Update outdated exception references in xmldoc --- osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs index 836260a26d..e2e7b6b991 100644 --- a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs +++ b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs @@ -14,7 +14,7 @@ namespace osu.Game.Online.RealtimeMultiplayer /// Request to join a multiplayer room. /// /// The databased room ID. - /// If the user is already in the requested (or another) room. + /// If the user is already in the requested (or another) room. Task JoinRoom(long roomId); /// @@ -52,6 +52,7 @@ namespace osu.Game.Online.RealtimeMultiplayer /// /// A user other than the current host is attempting to start the game. /// If the user is not in a room. + /// If an attempt to start the game occurs when the game's (or users') state disallows it. Task StartMatch(); } } From c8e3c7e77bb9b00356cd6b020ac212941d1b5fa5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 9 Dec 2020 17:45:07 +0900 Subject: [PATCH 143/818] Add stateful client interface --- .../IStatefulMultiplayerClient.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 osu.Game/Online/RealtimeMultiplayer/IStatefulMultiplayerClient.cs diff --git a/osu.Game/Online/RealtimeMultiplayer/IStatefulMultiplayerClient.cs b/osu.Game/Online/RealtimeMultiplayer/IStatefulMultiplayerClient.cs new file mode 100644 index 0000000000..578092662a --- /dev/null +++ b/osu.Game/Online/RealtimeMultiplayer/IStatefulMultiplayerClient.cs @@ -0,0 +1,17 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +namespace osu.Game.Online.RealtimeMultiplayer +{ + /// + /// A multiplayer client which maintains local room and user state. Also provides a proxy to access the . + /// + public interface IStatefulMultiplayerClient : IMultiplayerClient, IMultiplayerServer + { + MultiplayerUserState State { get; } + + MultiplayerRoom? Room { get; } + } +} From 6da854e37c3eb3b8f426904895f6ece374bea498 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Wed, 9 Dec 2020 13:32:59 +0100 Subject: [PATCH 144/818] Move scheduled import logic to OsuGame. --- osu.Android/OsuGameAndroid.cs | 6 ------ osu.Game/OsuGame.cs | 7 +++++++ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Android/OsuGameAndroid.cs b/osu.Android/OsuGameAndroid.cs index c9b27a16d6..57512012f9 100644 --- a/osu.Android/OsuGameAndroid.cs +++ b/osu.Android/OsuGameAndroid.cs @@ -67,12 +67,6 @@ namespace osu.Android } } - public override Task Import(Stream stream, string filename) - { - WaitForReady(() => this, _ => Task.Run(() => base.Import(stream, filename))); - return Task.CompletedTask; - } - protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 2c1db67d24..0ad58b2438 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -52,6 +52,7 @@ using osu.Game.Updater; using osu.Game.Utils; using LogLevel = osu.Framework.Logging.LogLevel; using osu.Game.Users; +using System.IO; namespace osu.Game { @@ -426,6 +427,12 @@ namespace osu.Game }, validScreens: new[] { typeof(PlaySongSelect) }); } + public override Task Import(Stream stream, string filename) + { + WaitForReady(() => this, _ => Task.Run(() => base.Import(stream, filename))); + return Task.CompletedTask; + } + protected virtual Loader CreateLoader() => new Loader(); protected virtual UpdateManager CreateUpdateManager() => new UpdateManager(); From 7e3fcfe43721ef34a2619084a9007f67de79ad92 Mon Sep 17 00:00:00 2001 From: Xexxar Date: Wed, 9 Dec 2020 10:35:48 -0600 Subject: [PATCH 145/818] fixed issue with comment --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index f239289d7f..c35b739f81 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -121,7 +121,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty : 0.0); } - // Scale the speed value with accuracy _alot_ + // Scale the aim value with accuracy _alot_ if (accuracy > .8) aimValue *= 0.5 + Math.Pow(Math.Sin(2.5 * (accuracy - .8) * Math.PI), 2) / 2; else From cfc34a63bd478b914ebf98f47eef25a08879c3d6 Mon Sep 17 00:00:00 2001 From: Xexxar Date: Wed, 9 Dec 2020 11:21:03 -0600 Subject: [PATCH 146/818] realized i accidently deleted the OD scaling --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index c35b739f81..306c491210 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -126,6 +126,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty aimValue *= 0.5 + Math.Pow(Math.Sin(2.5 * (accuracy - .8) * Math.PI), 2) / 2; else aimValue *= accuracy * (.5 / .8); + // It is important to also consider accuracy difficulty when doing that + aimValue *= 0.975 + Math.Pow(Attributes.OverallDifficulty, 2) / 2000; return aimValue; } @@ -161,7 +163,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty else speedValue *= accuracy * (.5 / .8); // It is important to also consider accuracy difficulty when doing that - speedValue *= 0.96 + Math.Pow(Attributes.OverallDifficulty, 2) / 1600; + speedValue *= 0.95 + Math.Pow(Attributes.OverallDifficulty, 2) / 1000; return speedValue; } From 146e6a61935bb71a7dec3dac37b1c4791c88a061 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 9 Dec 2020 18:30:52 +0100 Subject: [PATCH 147/818] Fix formatting issues --- .../Difficulty/OsuPerformanceCalculator.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 306c491210..0306ef3ca0 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -123,9 +123,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty // Scale the aim value with accuracy _alot_ if (accuracy > .8) - aimValue *= 0.5 + Math.Pow(Math.Sin(2.5 * (accuracy - .8) * Math.PI), 2) / 2; + aimValue *= 0.5 + Math.Pow(Math.Sin(2.5 * (accuracy - .8) * Math.PI), 2) / 2; else - aimValue *= accuracy * (.5 / .8); + aimValue *= accuracy * (.5 / .8); // It is important to also consider accuracy difficulty when doing that aimValue *= 0.975 + Math.Pow(Attributes.OverallDifficulty, 2) / 2000; @@ -159,9 +159,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty // Scale the speed value with accuracy _alot_ if (accuracy > .8) - speedValue *= 0.5 + Math.Pow(Math.Sin(2.5 * (accuracy - .8) * Math.PI), 2) / 2; + speedValue *= 0.5 + Math.Pow(Math.Sin(2.5 * (accuracy - .8) * Math.PI), 2) / 2; else - speedValue *= accuracy * (.5 / .8); + speedValue *= accuracy * (.5 / .8); // It is important to also consider accuracy difficulty when doing that speedValue *= 0.95 + Math.Pow(Attributes.OverallDifficulty, 2) / 1000; From 051afe5a7a6838b40be5cea60d2d8d0af777ef43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 9 Dec 2020 18:31:51 +0100 Subject: [PATCH 148/818] Fix typos in comments --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 0306ef3ca0..477028b02b 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -121,7 +121,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty : 0.0); } - // Scale the aim value with accuracy _alot_ + // Scale the aim value with accuracy _a lot_ if (accuracy > .8) aimValue *= 0.5 + Math.Pow(Math.Sin(2.5 * (accuracy - .8) * Math.PI), 2) / 2; else @@ -157,7 +157,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (mods.Any(m => m is OsuModHidden)) speedValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate); - // Scale the speed value with accuracy _alot_ + // Scale the speed value with accuracy _a lot_ if (accuracy > .8) speedValue *= 0.5 + Math.Pow(Math.Sin(2.5 * (accuracy - .8) * Math.PI), 2) / 2; else From 05ad9aae8d221e0caf59ce282771d6996fb50abb Mon Sep 17 00:00:00 2001 From: Xexxar Date: Wed, 9 Dec 2020 11:57:01 -0600 Subject: [PATCH 149/818] changed curve to linear OD + acc based curve --- .../Difficulty/OsuPerformanceCalculator.cs | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 306c491210..87a6fbb23a 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -121,13 +121,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty : 0.0); } - // Scale the aim value with accuracy _alot_ - if (accuracy > .8) - aimValue *= 0.5 + Math.Pow(Math.Sin(2.5 * (accuracy - .8) * Math.PI), 2) / 2; - else - aimValue *= accuracy * (.5 / .8); - // It is important to also consider accuracy difficulty when doing that - aimValue *= 0.975 + Math.Pow(Attributes.OverallDifficulty, 2) / 2000; + // Scale the aim value with accuracy and OD + aimValue *= .975 + Math.Pow(Attributes.OverallDifficulty, 2) / 1500 + 200 * (accuracy - 1) * Math.Pow(1 / Attributes.OverallDifficulty, 2); return aimValue; } @@ -157,13 +152,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (mods.Any(m => m is OsuModHidden)) speedValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate); - // Scale the speed value with accuracy _alot_ - if (accuracy > .8) - speedValue *= 0.5 + Math.Pow(Math.Sin(2.5 * (accuracy - .8) * Math.PI), 2) / 2; - else - speedValue *= accuracy * (.5 / .8); - // It is important to also consider accuracy difficulty when doing that - speedValue *= 0.95 + Math.Pow(Attributes.OverallDifficulty, 2) / 1000; + // Scale the speed value with accuracy and OD + speedValue *= .95 + Math.Pow(Attributes.OverallDifficulty, 2) / 750 + 200 * (accuracy - 1) * Math.Pow(1 / Attributes.OverallDifficulty, 2); return speedValue; } From d604c51cbd50ef2b72e831d23937373df55b6a0f Mon Sep 17 00:00:00 2001 From: Xexxar Date: Wed, 9 Dec 2020 13:04:14 -0600 Subject: [PATCH 150/818] capped scaling at OD 8 to prevent overscaling --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 87a6fbb23a..fce17071e4 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -122,7 +122,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty } // Scale the aim value with accuracy and OD - aimValue *= .975 + Math.Pow(Attributes.OverallDifficulty, 2) / 1500 + 200 * (accuracy - 1) * Math.Pow(1 / Attributes.OverallDifficulty, 2); + aimValue *= Math.Max(0, .975 + Math.Pow(Attributes.OverallDifficulty, 2) / 1500 + 200 * (accuracy - 1) * Math.Pow(1 / Math.Max(8, Attributes.OverallDifficulty), 2)); return aimValue; } @@ -153,7 +153,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty speedValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate); // Scale the speed value with accuracy and OD - speedValue *= .95 + Math.Pow(Attributes.OverallDifficulty, 2) / 750 + 200 * (accuracy - 1) * Math.Pow(1 / Attributes.OverallDifficulty, 2); + speedValue *= Math.Max(0, .95 + Math.Pow(Attributes.OverallDifficulty, 2) / 750 + 200 * (accuracy - 1) * Math.Pow(1 / Math.Max(8, Attributes.OverallDifficulty), 2)); return speedValue; } From 54abc3bd4dd121fab5986b4a6c0f35bec5e0c037 Mon Sep 17 00:00:00 2001 From: Xexxar Date: Wed, 9 Dec 2020 20:07:52 -0600 Subject: [PATCH 151/818] revert aim curve and add new 50s nerf --- .../Difficulty/OsuPerformanceCalculator.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index ddb5cb506d..2b9c817094 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -121,9 +121,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty : 0.0); } - // Scale the aim value with accuracy and OD - aimValue *= Math.Max(0, .975 + Math.Pow(Attributes.OverallDifficulty, 2) / 1500 + 200 * (accuracy - 1) * Math.Pow(1 / Math.Max(8, Attributes.OverallDifficulty), 2)); - + // Scale the aim value with accuracy _slightly_ + aimValue *= 0.5 + accuracy / 2.0; + // It is important to also consider accuracy difficulty when doing that + aimValue *= 0.98 + Math.Pow(Attributes.OverallDifficulty, 2) / 2500; + return aimValue; } @@ -153,7 +155,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty speedValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate); // Scale the speed value with accuracy and OD - speedValue *= Math.Max(0, .95 + Math.Pow(Attributes.OverallDifficulty, 2) / 750 + 200 * (accuracy - 1) * Math.Pow(1 / Math.Max(8, Attributes.OverallDifficulty), 2)); + speedValue *= (.95 + Math.Pow(Attributes.OverallDifficulty, 2) / 750) * Math.Pow(accuracy, (14.5 - Math.Max(Attributes.OverallDifficulty, 8)) / 2); + // Scale the speed value with # of 50s to punish doubletapping. + speedValue *= Math.Pow(0.98, countMeh < totalHits / 500.0 ? 0.5 * countMeh : countMeh - totalHits / 500.0 * 0.5); return speedValue; } From cc996ec7fcbe360b451ec786c81f6a243703af7d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 10 Dec 2020 16:32:14 +0900 Subject: [PATCH 152/818] Ensure player is consumed at the point of scheduled push running the first time --- osu.Game/Screens/Play/PlayerLoader.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 42074ac241..c26195b09c 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -274,6 +274,13 @@ namespace osu.Game.Screens.Play } } + private Player consumePlayer() + { + var consumed = player; + player = null; + return consumed; + } + private void prepareNewPlayer() { if (!this.IsCurrentScreen()) @@ -331,6 +338,8 @@ namespace osu.Game.Screens.Play scheduledPushPlayer = Scheduler.AddDelayed(() => { contentOut(); + // ensure that once we have reached this "point of no return", readyForPush will be false for all future checks (until a new player instance is prepared). + var consumedPlayer = consumePlayer(); TransformSequence pushSequence = this.Delay(250); @@ -362,8 +371,6 @@ namespace osu.Game.Screens.Play // Note that this may change if the player we load requested a re-run. ValidForResume = false; - if (player.LoadedBeatmapSuccessfully) - this.Push(player); else this.Exit(); }); @@ -373,6 +380,8 @@ namespace osu.Game.Screens.Play { Schedule(pushWhenLoaded); } + if (consumedPlayer.LoadedBeatmapSuccessfully) + this.Push(consumedPlayer); } private void cancelLoad() From 491ab7405942009e17012d326aa90ae88fea46ff Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 10 Dec 2020 16:33:28 +0900 Subject: [PATCH 153/818] Schedule pushWhenLoaded once ever Previously it was being scheduled another time each OnResume, resulting in more and more calls as a user retries the same beatmap multiple times. To simplify things I've decided to just schedule once ever. This means that on resuming there's no 400ms delay any more, but in testing this isn't really an issue (load time is still high enough that it will never really be below that anyway). Even if gameplay was to load faster, the animation should gracefully proceed. --- osu.Game/Screens/Play/PlayerLoader.cs | 95 +++++++++++++-------------- 1 file changed, 46 insertions(+), 49 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index c26195b09c..d9c7afdc3c 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -179,7 +179,10 @@ namespace osu.Game.Screens.Play contentIn(); MetadataInfo.Delay(750).FadeIn(500); - this.Delay(1800).Schedule(pushWhenLoaded); + + // after an initial delay, start the debounced load check. + // this will continue to execute even after resuming back on restart. + Scheduler.Add(new ScheduledDelegate(pushWhenLoaded, 1800, 0)); showMuteWarningIfNeeded(); } @@ -189,8 +192,6 @@ namespace osu.Game.Screens.Play base.OnResuming(last); contentIn(); - - this.Delay(400).Schedule(pushWhenLoaded); } public override void OnSuspending(IScreen next) @@ -322,66 +323,62 @@ namespace osu.Game.Screens.Play { if (!this.IsCurrentScreen()) return; - try + if (!readyForPush) { - if (!readyForPush) - { - // as the pushDebounce below has a delay, we need to keep checking and cancel a future debounce - // if we become unready for push during the delay. - cancelLoad(); - return; - } + // as the pushDebounce below has a delay, we need to keep checking and cancel a future debounce + // if we become unready for push during the delay. + cancelLoad(); + return; + } - if (scheduledPushPlayer != null) - return; + // if a push has already been scheduled, no further action is required. + // this value is reset via cancelLoad() to allow a second usage of the same PlayerLoader screen. + if (scheduledPushPlayer != null) + return; - scheduledPushPlayer = Scheduler.AddDelayed(() => - { - contentOut(); + scheduledPushPlayer = Scheduler.AddDelayed(() => + { // ensure that once we have reached this "point of no return", readyForPush will be false for all future checks (until a new player instance is prepared). var consumedPlayer = consumePlayer(); - TransformSequence pushSequence = this.Delay(250); + contentOut(); - // only show if the warning was created (i.e. the beatmap needs it) - // and this is not a restart of the map (the warning expires after first load). - if (epilepsyWarning?.IsAlive == true) - { - const double epilepsy_display_length = 3000; + TransformSequence pushSequence = this.Delay(250); - pushSequence - .Schedule(() => epilepsyWarning.State.Value = Visibility.Visible) - .TransformBindableTo(volumeAdjustment, 0.25, EpilepsyWarning.FADE_DURATION, Easing.OutQuint) - .Delay(epilepsy_display_length) - .Schedule(() => - { - epilepsyWarning.Hide(); - epilepsyWarning.Expire(); - }) - .Delay(EpilepsyWarning.FADE_DURATION); - } + // only show if the warning was created (i.e. the beatmap needs it) + // and this is not a restart of the map (the warning expires after first load). + if (epilepsyWarning?.IsAlive == true) + { + const double epilepsy_display_length = 3000; - pushSequence.Schedule(() => - { - if (!this.IsCurrentScreen()) return; + pushSequence + .Schedule(() => epilepsyWarning.State.Value = Visibility.Visible) + .TransformBindableTo(volumeAdjustment, 0.25, EpilepsyWarning.FADE_DURATION, Easing.OutQuint) + .Delay(epilepsy_display_length) + .Schedule(() => + { + epilepsyWarning.Hide(); + epilepsyWarning.Expire(); + }) + .Delay(EpilepsyWarning.FADE_DURATION); + } - LoadTask = null; + pushSequence.Schedule(() => + { + if (!this.IsCurrentScreen()) return; - // By default, we want to load the player and never be returned to. - // Note that this may change if the player we load requested a re-run. - ValidForResume = false; + LoadTask = null; + + // By default, we want to load the player and never be returned to. + // Note that this may change if the player we load requested a re-run. + ValidForResume = false; - else - this.Exit(); - }); - }, 500); - } - finally - { - Schedule(pushWhenLoaded); - } if (consumedPlayer.LoadedBeatmapSuccessfully) this.Push(consumedPlayer); + else + this.Exit(); + }); + }, 500); } private void cancelLoad() From 67dd7be71a549822b47562be9a4cfc106b338a12 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 10 Dec 2020 16:34:58 +0900 Subject: [PATCH 154/818] Move cancelLoad call to OnResuming This has no real effect; it just feels more readable to me. --- osu.Game/Screens/Play/PlayerLoader.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index d9c7afdc3c..f3575b01b4 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -191,6 +191,7 @@ namespace osu.Game.Screens.Play { base.OnResuming(last); + cancelLoad(); contentIn(); } @@ -198,8 +199,6 @@ namespace osu.Game.Screens.Play { base.OnSuspending(next); - cancelLoad(); - BackgroundBrightnessReduction = false; // we're moving to player, so a period of silence is upcoming. From 437c0506cec8ee2b2ba1ee3ed71bb57ea50dd04b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 10 Dec 2020 16:56:56 +0900 Subject: [PATCH 155/818] Refactor to allow for special disposal handling to still work --- osu.Game/Screens/Play/PlayerLoader.cs | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index f3575b01b4..729119fa36 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.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 System.Threading.Tasks; using JetBrains.Annotations; @@ -71,8 +72,9 @@ namespace osu.Game.Screens.Play } private bool readyForPush => + !playerConsumed // don't push unless the player is completely loaded - player?.LoadState == LoadState.Ready + && player?.LoadState == LoadState.Ready // don't push if the user is hovering one of the panes, unless they are idle. && (IsHovered || idleTracker.IsIdle.Value) // don't push if the user is dragging a slider or otherwise. @@ -84,6 +86,11 @@ namespace osu.Game.Screens.Play private Player player; + /// + /// Whether the curent player instance has been consumed via . + /// + private bool playerConsumed; + private LogoTrackingContainer content; private bool hideOverlays; @@ -191,7 +198,11 @@ namespace osu.Game.Screens.Play { base.OnResuming(last); + // prepare for a retry. + player = null; + playerConsumed = false; cancelLoad(); + contentIn(); } @@ -276,9 +287,10 @@ namespace osu.Game.Screens.Play private Player consumePlayer() { - var consumed = player; - player = null; - return consumed; + Debug.Assert(!playerConsumed); + + playerConsumed = true; + return player; } private void prepareNewPlayer() @@ -395,7 +407,7 @@ namespace osu.Game.Screens.Play if (isDisposing) { // if the player never got pushed, we should explicitly dispose it. - DisposalTask = LoadTask?.ContinueWith(_ => player.Dispose()); + DisposalTask = LoadTask?.ContinueWith(_ => player?.Dispose()); } } From 7c2f506b7902ec2c77048ccd058ec205fedd1570 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 10 Dec 2020 17:10:29 +0900 Subject: [PATCH 156/818] Port StanR's NF multiplier changes --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 030b0cf6d1..12192e36d4 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty double multiplier = 1.12; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things if (mods.Any(m => m is OsuModNoFail)) - multiplier *= 0.90; + multiplier *= Math.Max(0.90, 1.0 - 0.02 * countMiss); if (mods.Any(m => m is OsuModSpunOut)) multiplier *= 1.0 - Math.Pow((double)Attributes.SpinnerCount / totalHits, 0.85); From 679a550d833c9d6e12160c4a77ac81fa2662cca5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 10 Dec 2020 17:42:28 +0900 Subject: [PATCH 157/818] Fix single threaded seeking not working due to unnecessary seek call --- osu.Game/Screens/Play/GameplayClockContainer.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 2c83161614..5d11cdf21d 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -154,13 +154,16 @@ namespace osu.Game.Screens.Play public void Start() { - // Seeking the decoupled clock to its current time ensures that its source clock will be seeked to the same time - // This accounts for the audio clock source potentially taking time to enter a completely stopped state - Seek(GameplayClock.CurrentTime); - adjustableClock.Start(); - IsPaused.Value = false; + if (!adjustableClock.IsRunning) + { + // Seeking the decoupled clock to its current time ensures that its source clock will be seeked to the same time + // This accounts for the audio clock source potentially taking time to enter a completely stopped state + Seek(GameplayClock.CurrentTime); this.TransformBindableTo(pauseFreqAdjust, 1, 200, Easing.In); + adjustableClock.Start(); + } + IsPaused.Value = false; } /// From 01bd765384e9f9d2b61118fcab7e75132a7c3cf0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 10 Dec 2020 17:42:47 +0900 Subject: [PATCH 158/818] Simplify pause handling by moving transform logic to bindable change event --- osu.Game/Screens/Play/GameplayClockContainer.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 5d11cdf21d..0bbdc980db 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -95,6 +95,16 @@ namespace osu.Game.Screens.Play localGameplayClock = new LocalGameplayClock(userOffsetClock); GameplayClock.IsPaused.BindTo(IsPaused); + + IsPaused.BindValueChanged(onPaused); + } + + private void onPaused(ValueChangedEvent isPaused) + { + if (isPaused.NewValue) + this.TransformBindableTo(pauseFreqAdjust, 0, 200, Easing.Out).OnComplete(_ => adjustableClock.Stop()); + else + this.TransformBindableTo(pauseFreqAdjust, 1, 200, Easing.In); } private double totalOffset => userOffsetClock.Offset + platformOffsetClock.Offset; @@ -160,9 +170,9 @@ namespace osu.Game.Screens.Play // This accounts for the audio clock source potentially taking time to enter a completely stopped state Seek(GameplayClock.CurrentTime); - this.TransformBindableTo(pauseFreqAdjust, 1, 200, Easing.In); adjustableClock.Start(); } + IsPaused.Value = false; } @@ -202,8 +212,6 @@ namespace osu.Game.Screens.Play public void Stop() { - this.TransformBindableTo(pauseFreqAdjust, 0, 200, Easing.Out).OnComplete(_ => adjustableClock.Stop()); - IsPaused.Value = true; } From e097b6e61c2796f0c5071704d90b7119134fca0c Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 10 Dec 2020 19:42:01 +0900 Subject: [PATCH 159/818] Add ScalingContainer back Don't want to set DHO.Scale or DHO.Rotation because because DHO may be transformed by mods. DHO.Size is also assigned for drawable visualizer --- .../Objects/Drawables/CaughtObject.cs | 4 +++- .../Objects/Drawables/DrawableBanana.cs | 14 ++++++------- .../Objects/Drawables/DrawableDroplet.cs | 6 +++--- .../Objects/Drawables/DrawableFruit.cs | 6 +++--- .../DrawablePalpableCatchHitObject.cs | 20 ++++++++++++++++++- .../Objects/Drawables/IHasCatchObjectState.cs | 3 +-- 6 files changed, 36 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs index fb83b73212..22fb338229 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs @@ -21,6 +21,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables public Bindable AccentColour { get; } = new Bindable(); public Bindable HyperDash { get; } = new Bindable(); + float IHasCatchObjectState.Scale => Scale.X; + /// /// Whether this hit object should stay on the catcher plate when the object is caught by the catcher. /// @@ -43,7 +45,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables public virtual void CopyStateFrom(IHasCatchObjectState objectState) { HitObject = objectState.HitObject; - Scale = objectState.Scale; + Scale = new Vector2(objectState.Scale); Rotation = objectState.Rotation; AccentColour.Value = objectState.AccentColour.Value; HyperDash.Value = objectState.HyperDash.Value; diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs index 4bc5da2396..c1b41a7afc 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs @@ -24,9 +24,9 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables [BackgroundDependencyLoader] private void load() { - AddInternal(new SkinnableDrawable( + ScalingContainer.Child = new SkinnableDrawable( new CatchSkinComponent(CatchSkinComponents.Banana), - _ => new BananaPiece())); + _ => new BananaPiece()); } protected override void LoadComplete() @@ -44,12 +44,12 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables const float end_scale = 0.6f; const float random_scale_range = 1.6f; - this.ScaleTo(HitObject.Scale * (end_scale + random_scale_range * RandomSingle(3))) - .Then().ScaleTo(HitObject.Scale * end_scale, HitObject.TimePreempt); + ScalingContainer.ScaleTo(HitObject.Scale * (end_scale + random_scale_range * RandomSingle(3))) + .Then().ScaleTo(HitObject.Scale * end_scale, HitObject.TimePreempt); - this.RotateTo(getRandomAngle(1)) - .Then() - .RotateTo(getRandomAngle(2), HitObject.TimePreempt); + ScalingContainer.RotateTo(getRandomAngle(1)) + .Then() + .RotateTo(getRandomAngle(2), HitObject.TimePreempt); float getRandomAngle(int series) => 180 * (RandomSingle(series) * 2 - 1); } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs index c5f0bb8b18..2dce9507a5 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs @@ -24,9 +24,9 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables [BackgroundDependencyLoader] private void load() { - AddInternal(new SkinnableDrawable( + ScalingContainer.Child = new SkinnableDrawable( new CatchSkinComponent(CatchSkinComponents.Droplet), - _ => new DropletPiece())); + _ => new DropletPiece()); } protected override void UpdateInitialTransforms() @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables float startRotation = RandomSingle(1) * 20; double duration = HitObject.TimePreempt + 2000; - this.RotateTo(startRotation).RotateTo(startRotation + 720, duration); + ScalingContainer.RotateTo(startRotation).RotateTo(startRotation + 720, duration); } } } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs index 57f27d01b8..0b89c46480 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs @@ -32,16 +32,16 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables VisualRepresentation.Value = (FruitVisualRepresentation)(change.NewValue % 4); }, true); - AddInternal(new SkinnableDrawable( + ScalingContainer.Child = new SkinnableDrawable( new CatchSkinComponent(CatchSkinComponents.Fruit), - _ => new FruitPiece())); + _ => new FruitPiece()); } protected override void UpdateInitialTransforms() { base.UpdateInitialTransforms(); - this.RotateTo((RandomSingle(1) - 0.5f) * 40); + ScalingContainer.RotateTo((RandomSingle(1) - 0.5f) * 40); } } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs index aea9d2c082..9ddf56a9cd 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs @@ -5,6 +5,7 @@ using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osuTK; using osuTK.Graphics; @@ -30,11 +31,27 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables public float DisplayRadius => CatchHitObject.OBJECT_RADIUS * HitObject.Scale * ScaleFactor; + /// + /// The container internal transforms (such as scaling based on the circle size) are applied to. + /// + protected readonly Container ScalingContainer; + + float IHasCatchObjectState.Scale => HitObject.Scale * ScaleFactor; + + float IHasCatchObjectState.Rotation => ScalingContainer.Rotation; + protected DrawablePalpableCatchHitObject([CanBeNull] CatchHitObject h) : base(h) { Origin = Anchor.Centre; Size = new Vector2(CatchHitObject.OBJECT_RADIUS * 2); + + AddInternal(ScalingContainer = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(CatchHitObject.OBJECT_RADIUS * 2) + }); } [BackgroundDependencyLoader] @@ -47,7 +64,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables ScaleBindable.BindValueChanged(scale => { - Scale = new Vector2(scale.NewValue * ScaleFactor); + ScalingContainer.Scale = new Vector2(scale.NewValue * ScaleFactor); + Size = ScalingContainer.Size * ScalingContainer.Scale; }, true); IndexInBeatmap.BindValueChanged(_ => UpdateComboColour()); diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs index 55ca502877..fac0249985 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Bindables; -using osuTK; using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.Objects.Drawables @@ -17,6 +16,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables Bindable HyperDash { get; } float Rotation { get; } - Vector2 Scale { get; } + float Scale { get; } } } From 2634c6b8d9dc18f34ae7eb81dd114d36a71adb21 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 10 Dec 2020 20:43:01 +0900 Subject: [PATCH 160/818] Combine DisplayRadius and Scale to DisplaySize --- .../TestSceneFruitRandomness.cs | 23 ++++++++++--------- .../Objects/Drawables/CaughtObject.cs | 8 ++++--- .../DrawablePalpableCatchHitObject.cs | 8 +++---- .../Objects/Drawables/IHasCatchObjectState.cs | 5 ++-- osu.Game.Rulesets.Catch/UI/Catcher.cs | 2 +- 5 files changed, 24 insertions(+), 22 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneFruitRandomness.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneFruitRandomness.cs index e1d817b314..c888dc0a65 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneFruitRandomness.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneFruitRandomness.cs @@ -5,6 +5,7 @@ using NUnit.Framework; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects.Drawables; using osu.Game.Tests.Visual; +using osuTK; using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.Tests @@ -37,16 +38,16 @@ namespace osu.Game.Rulesets.Catch.Tests float fruitRotation = 0; float bananaRotation = 0; - float bananaScale = 0; + Vector2 bananaSize = new Vector2(); Color4 bananaColour = new Color4(); AddStep("Initialize start time", () => { drawableFruit.HitObject.StartTime = drawableBanana.HitObject.StartTime = initial_start_time; - fruitRotation = drawableFruit.Rotation; - bananaRotation = drawableBanana.Rotation; - bananaScale = drawableBanana.Scale.X; + fruitRotation = drawableFruit.DisplayRotation; + bananaRotation = drawableBanana.DisplayRotation; + bananaSize = drawableBanana.DisplaySize; bananaColour = drawableBanana.AccentColour.Value; }); @@ -55,9 +56,9 @@ namespace osu.Game.Rulesets.Catch.Tests drawableFruit.HitObject.StartTime = drawableBanana.HitObject.StartTime = another_start_time; }); - AddAssert("fruit rotation is changed", () => drawableFruit.Rotation != fruitRotation); - AddAssert("banana rotation is changed", () => drawableBanana.Rotation != bananaRotation); - AddAssert("banana scale is changed", () => drawableBanana.Scale.X != bananaScale); + AddAssert("fruit rotation is changed", () => drawableFruit.DisplayRotation != fruitRotation); + AddAssert("banana rotation is changed", () => drawableBanana.DisplayRotation != bananaRotation); + AddAssert("banana size is changed", () => drawableBanana.DisplaySize != bananaSize); AddAssert("banana colour is changed", () => drawableBanana.AccentColour.Value != bananaColour); AddStep("reset start time", () => @@ -65,10 +66,10 @@ namespace osu.Game.Rulesets.Catch.Tests drawableFruit.HitObject.StartTime = drawableBanana.HitObject.StartTime = initial_start_time; }); - AddAssert("rotation and scale restored", () => - drawableFruit.Rotation == fruitRotation && - drawableBanana.Rotation == bananaRotation && - drawableBanana.Scale.X == bananaScale && + AddAssert("rotation and size restored", () => + drawableFruit.DisplayRotation == fruitRotation && + drawableBanana.DisplayRotation == bananaRotation && + drawableBanana.DisplaySize == bananaSize && drawableBanana.AccentColour.Value == bananaColour); } } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs index 22fb338229..524505d588 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs @@ -21,7 +21,9 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables public Bindable AccentColour { get; } = new Bindable(); public Bindable HyperDash { get; } = new Bindable(); - float IHasCatchObjectState.Scale => Scale.X; + public Vector2 DisplaySize => Size * Scale; + + public float DisplayRotation => Rotation; /// /// Whether this hit object should stay on the catcher plate when the object is caught by the catcher. @@ -45,8 +47,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables public virtual void CopyStateFrom(IHasCatchObjectState objectState) { HitObject = objectState.HitObject; - Scale = new Vector2(objectState.Scale); - Rotation = objectState.Rotation; + Scale = Vector2.Divide(objectState.DisplaySize, Size); + Rotation = objectState.DisplayRotation; AccentColour.Value = objectState.AccentColour.Value; HyperDash.Value = objectState.HyperDash.Value; } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs index 9ddf56a9cd..7df06bd92d 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs @@ -29,16 +29,14 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables /// protected virtual float ScaleFactor => 1; - public float DisplayRadius => CatchHitObject.OBJECT_RADIUS * HitObject.Scale * ScaleFactor; - /// /// The container internal transforms (such as scaling based on the circle size) are applied to. /// protected readonly Container ScalingContainer; - float IHasCatchObjectState.Scale => HitObject.Scale * ScaleFactor; + public Vector2 DisplaySize => ScalingContainer.Size * ScalingContainer.Scale; - float IHasCatchObjectState.Rotation => ScalingContainer.Rotation; + public float DisplayRotation => ScalingContainer.Rotation; protected DrawablePalpableCatchHitObject([CanBeNull] CatchHitObject h) : base(h) @@ -65,7 +63,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables ScaleBindable.BindValueChanged(scale => { ScalingContainer.Scale = new Vector2(scale.NewValue * ScaleFactor); - Size = ScalingContainer.Size * ScalingContainer.Scale; + Size = DisplaySize; }, true); IndexInBeatmap.BindValueChanged(_ => UpdateComboColour()); diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs index fac0249985..d68e32aca9 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Bindables; +using osuTK; using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.Objects.Drawables @@ -15,7 +16,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables Bindable AccentColour { get; } Bindable HyperDash { get; } - float Rotation { get; } - float Scale { get; } + Vector2 DisplaySize { get; } + float DisplayRotation { get; } } } diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 64dacd09e6..f164c2655a 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -235,7 +235,7 @@ namespace osu.Game.Rulesets.Catch.UI if (result.IsHit) { - var positionInStack = computePositionInStack(new Vector2(palpableObject.X - X, 0), palpableObject.DisplayRadius); + var positionInStack = computePositionInStack(new Vector2(palpableObject.X - X, 0), palpableObject.DisplaySize.X / 2); placeCaughtObject(palpableObject, positionInStack); From b3d834731586f16cff8ea513ab1a361283b38078 Mon Sep 17 00:00:00 2001 From: Firmatorenio Date: Thu, 10 Dec 2020 20:11:08 +0600 Subject: [PATCH 161/818] add support for ScorePosition into LegacyManiaSkin --- osu.Game/Skinning/LegacyManiaSkinConfiguration.cs | 1 + osu.Game/Skinning/LegacyManiaSkinConfigurationLookup.cs | 1 + osu.Game/Skinning/LegacyManiaSkinDecoder.cs | 4 ++++ 3 files changed, 6 insertions(+) diff --git a/osu.Game/Skinning/LegacyManiaSkinConfiguration.cs b/osu.Game/Skinning/LegacyManiaSkinConfiguration.cs index 35a6140cbc..a8225a5bea 100644 --- a/osu.Game/Skinning/LegacyManiaSkinConfiguration.cs +++ b/osu.Game/Skinning/LegacyManiaSkinConfiguration.cs @@ -35,6 +35,7 @@ namespace osu.Game.Skinning public float HitPosition = (480 - 402) * POSITION_SCALE_FACTOR; public float LightPosition = (480 - 413) * POSITION_SCALE_FACTOR; + public float ScorePosition = (480 - 402 + 150) * POSITION_SCALE_FACTOR; public bool ShowJudgementLine = true; public bool KeysUnderNotes; diff --git a/osu.Game/Skinning/LegacyManiaSkinConfigurationLookup.cs b/osu.Game/Skinning/LegacyManiaSkinConfigurationLookup.cs index a99710ea96..9db6c8bf66 100644 --- a/osu.Game/Skinning/LegacyManiaSkinConfigurationLookup.cs +++ b/osu.Game/Skinning/LegacyManiaSkinConfigurationLookup.cs @@ -25,6 +25,7 @@ namespace osu.Game.Skinning LeftLineWidth, RightLineWidth, HitPosition, + ScorePosition, LightPosition, HitTargetImage, ShowJudgementLine, diff --git a/osu.Game/Skinning/LegacyManiaSkinDecoder.cs b/osu.Game/Skinning/LegacyManiaSkinDecoder.cs index 3dbec23194..49feb9c3f0 100644 --- a/osu.Game/Skinning/LegacyManiaSkinDecoder.cs +++ b/osu.Game/Skinning/LegacyManiaSkinDecoder.cs @@ -94,6 +94,10 @@ namespace osu.Game.Skinning currentConfig.LightPosition = (480 - float.Parse(pair.Value, CultureInfo.InvariantCulture)) * LegacyManiaSkinConfiguration.POSITION_SCALE_FACTOR; break; + case "ScorePosition": + currentConfig.ScorePosition = (480 - float.Parse(pair.Value, CultureInfo.InvariantCulture)) * LegacyManiaSkinConfiguration.POSITION_SCALE_FACTOR; + break; + case "JudgementLine": currentConfig.ShowJudgementLine = pair.Value == "1"; break; From cc5639d2b42cfd9b05272fb659a72c879cd1e3c5 Mon Sep 17 00:00:00 2001 From: Xexxar Date: Thu, 10 Dec 2020 09:48:40 -0600 Subject: [PATCH 162/818] added unneeded whitespace --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 2b9c817094..5a827eef96 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -125,7 +125,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty aimValue *= 0.5 + accuracy / 2.0; // It is important to also consider accuracy difficulty when doing that aimValue *= 0.98 + Math.Pow(Attributes.OverallDifficulty, 2) / 2500; - + return aimValue; } From 1f2946d64c988362c528b2401091252416a99cc2 Mon Sep 17 00:00:00 2001 From: Xexxar Date: Thu, 10 Dec 2020 14:21:06 -0600 Subject: [PATCH 163/818] changed miss penalty curve to scale with totalhits --- .../Difficulty/OsuPerformanceCalculator.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 0ebe0ddc2d..8a641992d3 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -93,7 +93,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty aimValue *= lengthBonus; // Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available - aimValue *= Math.Pow(0.97, countMiss); + if (countMiss > 0) + aimValue *= .97 * Math.Pow(1 - Math.Pow((double)countMiss / (double)totalHits, .775), Math.Pow(countMiss, .75)); // Combo scaling if (Attributes.MaxCombo > 0) @@ -139,7 +140,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty speedValue *= lengthBonus; // Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available - speedValue *= Math.Pow(0.97, countMiss); + if (countMiss > 0) + speedValue *= .97 * Math.Pow(1 - Math.Pow((double)countMiss / (double)totalHits, .775), Math.Pow(countMiss, 1)); // Combo scaling if (Attributes.MaxCombo > 0) From fd0d793c6966beb9d93cffbbef9cc809e76e9d73 Mon Sep 17 00:00:00 2001 From: Xexxar Date: Thu, 10 Dec 2020 18:51:54 -0600 Subject: [PATCH 164/818] changed the comment to reflect the change --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 8a641992d3..66ebc63243 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -92,7 +92,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty aimValue *= lengthBonus; - // Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available + // Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses. if (countMiss > 0) aimValue *= .97 * Math.Pow(1 - Math.Pow((double)countMiss / (double)totalHits, .775), Math.Pow(countMiss, .75)); @@ -139,7 +139,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty (totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0); speedValue *= lengthBonus; - // Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available + // Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses. if (countMiss > 0) speedValue *= .97 * Math.Pow(1 - Math.Pow((double)countMiss / (double)totalHits, .775), Math.Pow(countMiss, 1)); From 3fb41a20b55aa7e2d5270195e1a182dec18ed80b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 11 Dec 2020 13:27:52 +0900 Subject: [PATCH 165/818] Add room name to settings --- .../Online/RealtimeMultiplayer/MultiplayerRoomSettings.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomSettings.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomSettings.cs index 8f0f4febd1..a5dcafefcd 100644 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomSettings.cs +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomSettings.cs @@ -18,11 +18,13 @@ namespace osu.Game.Online.RealtimeMultiplayer public int? RulesetID { get; set; } + public string Name { get; set; } = "Unnamed room"; + [NotNull] public IEnumerable Mods { get; set; } = Enumerable.Empty(); - public bool Equals(MultiplayerRoomSettings other) => BeatmapID == other.BeatmapID && Mods.SequenceEqual(other.Mods) && RulesetID == other.RulesetID; + public bool Equals(MultiplayerRoomSettings other) => BeatmapID == other.BeatmapID && Mods.SequenceEqual(other.Mods) && RulesetID == other.RulesetID && Name.Equals(other.Name, StringComparison.Ordinal); - public override string ToString() => $"Beatmap:{BeatmapID} Mods:{string.Join(',', Mods)} Ruleset:{RulesetID}"; + public override string ToString() => $"Name:{Name} Beatmap:{BeatmapID} Mods:{string.Join(',', Mods)} Ruleset:{RulesetID}"; } } From c1c0b9a9db1c9a35114fd0535fc49ed3b5671e9d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 11 Dec 2020 14:10:45 +0900 Subject: [PATCH 166/818] Add realtime to room categories --- osu.Game/Online/Multiplayer/RoomCategory.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Multiplayer/RoomCategory.cs b/osu.Game/Online/Multiplayer/RoomCategory.cs index 636a73a3e9..d6786a72fe 100644 --- a/osu.Game/Online/Multiplayer/RoomCategory.cs +++ b/osu.Game/Online/Multiplayer/RoomCategory.cs @@ -6,6 +6,7 @@ namespace osu.Game.Online.Multiplayer public enum RoomCategory { Normal, - Spotlight + Spotlight, + Realtime, } } From 719b08b22fc0f078194b3fa54c64c78f88786666 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 11 Dec 2020 14:11:42 +0900 Subject: [PATCH 167/818] Make room setting's BeatmapID non-nullable --- osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomSettings.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomSettings.cs index a5dcafefcd..3934b8c1d7 100644 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomSettings.cs +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomSettings.cs @@ -14,7 +14,7 @@ namespace osu.Game.Online.RealtimeMultiplayer [Serializable] public class MultiplayerRoomSettings : IEquatable { - public int? BeatmapID { get; set; } + public int BeatmapID { get; set; } public int? RulesetID { get; set; } From d3b2e2b36eebcb6913244df225f4007bd014708b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 11 Dec 2020 14:43:17 +0900 Subject: [PATCH 168/818] Change locking method to better allow cross-thread locking --- .../RealtimeMultiplayer/LockUntilDisposal.cs | 24 ------------------- .../RealtimeMultiplayer/MultiplayerRoom.cs | 20 +++++++++++++++- 2 files changed, 19 insertions(+), 25 deletions(-) delete mode 100644 osu.Game/Online/RealtimeMultiplayer/LockUntilDisposal.cs diff --git a/osu.Game/Online/RealtimeMultiplayer/LockUntilDisposal.cs b/osu.Game/Online/RealtimeMultiplayer/LockUntilDisposal.cs deleted file mode 100644 index cd16fce2ee..0000000000 --- a/osu.Game/Online/RealtimeMultiplayer/LockUntilDisposal.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Threading; - -namespace osu.Game.Online.RealtimeMultiplayer -{ - public readonly struct LockUntilDisposal : IDisposable - { - private readonly object lockTarget; - - public LockUntilDisposal(object lockTarget) - { - this.lockTarget = lockTarget; - Monitor.Enter(lockTarget); - } - - public void Dispose() - { - Monitor.Exit(lockTarget); - } - } -} diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs index 5704ddd675..e009a34707 100644 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs @@ -5,7 +5,9 @@ using System; using System.Collections.Generic; +using System.Threading; using Newtonsoft.Json; +using osu.Framework.Allocation; namespace osu.Game.Online.RealtimeMultiplayer { @@ -48,10 +50,26 @@ namespace osu.Game.Online.RealtimeMultiplayer RoomID = roomId; } + private object updateLock = new object(); + + private ManualResetEventSlim freeForWrite = new ManualResetEventSlim(true); + /// /// Request a lock on this room to perform a thread-safe update. /// - public LockUntilDisposal LockForUpdate() => new LockUntilDisposal(writeLock); + public IDisposable LockForUpdate() + { + // ReSharper disable once InconsistentlySynchronizedField + freeForWrite.Wait(); + + lock (updateLock) + { + freeForWrite.Wait(); + freeForWrite.Reset(); + + return new ValueInvokeOnDisposal(this, r => freeForWrite.Set()); + } + } public override string ToString() => $"RoomID:{RoomID} Host:{Host?.UserID} Users:{Users.Count} State:{State} Settings: [{Settings}]"; } From 2dd591125673b4b4d6b130204995bfe41cf54de9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 11 Dec 2020 14:44:01 +0900 Subject: [PATCH 169/818] Rename method to better match purpose --- osu.Game/Screens/Play/GameplayClockContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 0bbdc980db..0248432917 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -96,10 +96,10 @@ namespace osu.Game.Screens.Play GameplayClock.IsPaused.BindTo(IsPaused); - IsPaused.BindValueChanged(onPaused); + IsPaused.BindValueChanged(onPauseChanged); } - private void onPaused(ValueChangedEvent isPaused) + private void onPauseChanged(ValueChangedEvent isPaused) { if (isPaused.NewValue) this.TransformBindableTo(pauseFreqAdjust, 0, 200, Easing.Out).OnComplete(_ => adjustableClock.Stop()); From c0d20d8ce430a2ee7257c7ac8cf34eeefd54489f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 11 Dec 2020 16:43:00 +0900 Subject: [PATCH 170/818] Add some spacing to interface class --- .../Objects/Drawables/IHasCatchObjectState.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs index d68e32aca9..81b61f0959 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs @@ -13,10 +13,13 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables public interface IHasCatchObjectState { PalpableCatchHitObject HitObject { get; } + Bindable AccentColour { get; } + Bindable HyperDash { get; } Vector2 DisplaySize { get; } + float DisplayRotation { get; } } } From a35060ea7a3c7c825b826b4ad7a65a898249d8bb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 11 Dec 2020 17:56:00 +0900 Subject: [PATCH 171/818] Add a simple cache-busting query string to online.db retrieval As we are finally pushing updates for this database, this adds a minimum level of guarantee that a client will request a new version (without having to worry about multiple levels of server-side caching). --- 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 c4563d5844..e90ccbb805 100644 --- a/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs +++ b/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs @@ -104,7 +104,7 @@ namespace osu.Game.Beatmaps string cacheFilePath = storage.GetFullPath(cache_database_name); string compressedCacheFilePath = $"{cacheFilePath}.bz2"; - cacheDownloadRequest = new FileWebRequest(compressedCacheFilePath, $"https://assets.ppy.sh/client-resources/{cache_database_name}.bz2"); + cacheDownloadRequest = new FileWebRequest(compressedCacheFilePath, $"https://assets.ppy.sh/client-resources/{cache_database_name}.bz2?{DateTimeOffset.UtcNow:yyyyMMdd}"); cacheDownloadRequest.Failed += ex => { From b5f6baf3412128bc142d966affffc433abfbe490 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 11 Dec 2020 18:03:48 +0900 Subject: [PATCH 172/818] 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 9a3d42d6b7..e0bf1867b9 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 9d37ceee6c..bd8a7c73b2 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -26,7 +26,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index ab03393836..c5e7c45eda 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -88,7 +88,7 @@ - + From aedb18b9f21e0e98af5f3720369314658565a2aa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 11 Dec 2020 18:14:33 +0900 Subject: [PATCH 173/818] Make RulesetID non-nullable --- osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomSettings.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomSettings.cs index 3934b8c1d7..d2f64235c3 100644 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomSettings.cs +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomSettings.cs @@ -16,7 +16,7 @@ namespace osu.Game.Online.RealtimeMultiplayer { public int BeatmapID { get; set; } - public int? RulesetID { get; set; } + public int RulesetID { get; set; } public string Name { get; set; } = "Unnamed room"; From 544160798b6448df40d9c2a9d85ca210f14fa3d4 Mon Sep 17 00:00:00 2001 From: Xexxar Date: Fri, 11 Dec 2020 08:01:45 -0600 Subject: [PATCH 174/818] cleaned up mistakes and made quality changes --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 66ebc63243..2c41959d3f 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -94,7 +94,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty // Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses. if (countMiss > 0) - aimValue *= .97 * Math.Pow(1 - Math.Pow((double)countMiss / (double)totalHits, .775), Math.Pow(countMiss, .75)); + aimValue *= 0.97 * Math.Pow(1 - Math.Pow((double)countMiss / (double)totalHits, 0.775), countMiss); // Combo scaling if (Attributes.MaxCombo > 0) @@ -141,7 +141,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty // Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses. if (countMiss > 0) - speedValue *= .97 * Math.Pow(1 - Math.Pow((double)countMiss / (double)totalHits, .775), Math.Pow(countMiss, 1)); + speedValue *= 0.97 * Math.Pow(1 - Math.Pow((double)countMiss / (double)totalHits, 0.775), countMiss); // Combo scaling if (Attributes.MaxCombo > 0) From abc8a2982c68163e29a80736c5227d51d01d0417 Mon Sep 17 00:00:00 2001 From: Xexxar Date: Fri, 11 Dec 2020 08:20:56 -0600 Subject: [PATCH 175/818] swapped ^.75 buff onto speed instead of aim --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 2c41959d3f..5d3552798c 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -94,7 +94,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty // Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses. if (countMiss > 0) - aimValue *= 0.97 * Math.Pow(1 - Math.Pow((double)countMiss / (double)totalHits, 0.775), countMiss); + aimValue *= 0.97 * Math.Pow(1 - Math.Pow((double)countMiss / totalHits, 0.775), countMiss); // Combo scaling if (Attributes.MaxCombo > 0) @@ -141,7 +141,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty // Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses. if (countMiss > 0) - speedValue *= 0.97 * Math.Pow(1 - Math.Pow((double)countMiss / (double)totalHits, 0.775), countMiss); + speedValue *= 0.97 * Math.Pow(1 - Math.Pow((double)countMiss / totalHits, 0.775), Math.Pow(countMiss, .75)); // Combo scaling if (Attributes.MaxCombo > 0) From 3acaa63373f1465249b537a956b0b9901936cedd Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 11 Dec 2020 23:24:38 +0900 Subject: [PATCH 176/818] CI fixes --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 2c41959d3f..ebcebeae7c 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -94,7 +94,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty // Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses. if (countMiss > 0) - aimValue *= 0.97 * Math.Pow(1 - Math.Pow((double)countMiss / (double)totalHits, 0.775), countMiss); + aimValue *= 0.97 * Math.Pow(1 - Math.Pow((double)countMiss / totalHits, 0.775), countMiss); // Combo scaling if (Attributes.MaxCombo > 0) @@ -141,7 +141,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty // Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses. if (countMiss > 0) - speedValue *= 0.97 * Math.Pow(1 - Math.Pow((double)countMiss / (double)totalHits, 0.775), countMiss); + speedValue *= 0.97 * Math.Pow(1 - Math.Pow((double)countMiss / totalHits, 0.775), countMiss); // Combo scaling if (Attributes.MaxCombo > 0) From b7872a54b885a2572ec439e8e2a8c0cb2f47dccf Mon Sep 17 00:00:00 2001 From: Xexxar Date: Fri, 11 Dec 2020 10:48:53 -0600 Subject: [PATCH 177/818] small factor rebalance --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 5d3552798c..a43c78f93f 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -141,7 +141,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty // Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses. if (countMiss > 0) - speedValue *= 0.97 * Math.Pow(1 - Math.Pow((double)countMiss / totalHits, 0.775), Math.Pow(countMiss, .75)); + speedValue *= 0.97 * Math.Pow(1 - Math.Pow((double)countMiss / totalHits, 0.775), Math.Pow(countMiss, .875)); // Combo scaling if (Attributes.MaxCombo > 0) From f20c5a2bda5f3d4a199af516e22b8caf1e97526a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 12 Dec 2020 15:29:21 +0900 Subject: [PATCH 178/818] Update framework (again) --- 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 e0bf1867b9..eaedcb7bc3 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 bd8a7c73b2..b4c7dca12f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -26,7 +26,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index c5e7c45eda..7542aded86 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -88,7 +88,7 @@ - + From 08b79bb92126925a538eb8ff62a7c7d058f6c574 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 12 Dec 2020 17:12:15 +0100 Subject: [PATCH 179/818] Store and return unstarted task for consumers to await on. --- osu.Game/OsuGame.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 0ad58b2438..6c32e2e94c 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -429,8 +429,9 @@ namespace osu.Game public override Task Import(Stream stream, string filename) { - WaitForReady(() => this, _ => Task.Run(() => base.Import(stream, filename))); - return Task.CompletedTask; + var importTask = new Task(async () => await base.Import(stream, filename)); + WaitForReady(() => this, _ => importTask.Start()); + return importTask; } protected virtual Loader CreateLoader() => new Loader(); From 8292c746ea82e35412f717fe8d6db6921661e54e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 12 Dec 2020 22:42:40 +0100 Subject: [PATCH 180/818] Leverage hitobject model for strong hit instead of creating own --- osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs index e4c0766844..c1b422452e 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Utils; @@ -9,7 +10,6 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Judgements; using osu.Game.Rulesets.Taiko.Objects; @@ -126,7 +126,7 @@ namespace osu.Game.Rulesets.Taiko.Tests var cpi = new ControlPointInfo(); cpi.Add(0, new EffectControlPoint { KiaiMode = kiai }); - Hit hit = new Hit(); + Hit hit = new Hit { IsStrong = true }; hit.ApplyDefaults(cpi, new BeatmapDifficulty()); var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Ok ? -0.1f : -0.05f, hitResult == HitResult.Ok ? 0.1f : 0.05f) }; @@ -134,7 +134,7 @@ namespace osu.Game.Rulesets.Taiko.Tests DrawableRuleset.Playfield.Add(h); ((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h, new JudgementResult(new HitObject(), new TaikoJudgement()) { Type = hitResult }); - ((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(new TestStrongNestedHit(h), new JudgementResult(new HitObject(), new TaikoStrongJudgement()) { Type = HitResult.Great }); + ((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h.NestedHitObjects.Single(), new JudgementResult(new HitObject(), new TaikoStrongJudgement()) { Type = HitResult.Great }); } private void addMissJudgement() @@ -210,15 +210,5 @@ namespace osu.Game.Rulesets.Taiko.Tests DrawableRuleset.Playfield.Add(new DrawableHit(h)); } - - private class TestStrongNestedHit : DrawableStrongNestedHit - { - public TestStrongNestedHit(DrawableHitObject mainObject) - : base(new StrongHitObject { StartTime = mainObject.HitObject.StartTime }, mainObject) - { - } - - public override bool OnPressed(TaikoAction action) => false; - } } } From 34e7a36b382ba88d4e2ae74fceb8ad7833133090 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 12 Dec 2020 22:48:22 +0100 Subject: [PATCH 181/818] Fix kiai hit steps not working correctly --- osu.Game.Rulesets.Taiko.Tests/DrawableTestHit.cs | 7 +++++-- osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs | 14 ++------------ 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/DrawableTestHit.cs b/osu.Game.Rulesets.Taiko.Tests/DrawableTestHit.cs index fb0917341e..f048cad18c 100644 --- a/osu.Game.Rulesets.Taiko.Tests/DrawableTestHit.cs +++ b/osu.Game.Rulesets.Taiko.Tests/DrawableTestHit.cs @@ -13,12 +13,15 @@ namespace osu.Game.Rulesets.Taiko.Tests { public readonly HitResult Type; - public DrawableTestHit(Hit hit, HitResult type = HitResult.Great) + public DrawableTestHit(Hit hit, HitResult type = HitResult.Great, bool kiai = false) : base(hit) { Type = type; - HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + var controlPoints = new ControlPointInfo(); + controlPoints.Add(0, new EffectControlPoint { KiaiMode = kiai }); + + HitObject.ApplyDefaults(controlPoints, new BeatmapDifficulty()); } protected override void UpdateInitialTransforms() diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs index c1b422452e..14a127ac82 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs @@ -106,13 +106,8 @@ namespace osu.Game.Rulesets.Taiko.Tests { HitResult hitResult = RNG.Next(2) == 0 ? HitResult.Ok : HitResult.Great; - var cpi = new ControlPointInfo(); - cpi.Add(0, new EffectControlPoint { KiaiMode = kiai }); - Hit hit = new Hit(); - hit.ApplyDefaults(cpi, new BeatmapDifficulty()); - - var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Ok ? -0.1f : -0.05f, hitResult == HitResult.Ok ? 0.1f : 0.05f) }; + var h = new DrawableTestHit(hit, kiai: kiai) { X = RNG.NextSingle(hitResult == HitResult.Ok ? -0.1f : -0.05f, hitResult == HitResult.Ok ? 0.1f : 0.05f) }; DrawableRuleset.Playfield.Add(h); @@ -123,13 +118,8 @@ namespace osu.Game.Rulesets.Taiko.Tests { HitResult hitResult = RNG.Next(2) == 0 ? HitResult.Ok : HitResult.Great; - var cpi = new ControlPointInfo(); - cpi.Add(0, new EffectControlPoint { KiaiMode = kiai }); - Hit hit = new Hit { IsStrong = true }; - hit.ApplyDefaults(cpi, new BeatmapDifficulty()); - - var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Ok ? -0.1f : -0.05f, hitResult == HitResult.Ok ? 0.1f : 0.05f) }; + var h = new DrawableTestHit(hit, kiai: kiai) { X = RNG.NextSingle(hitResult == HitResult.Ok ? -0.1f : -0.05f, hitResult == HitResult.Ok ? 0.1f : 0.05f) }; DrawableRuleset.Playfield.Add(h); From 76193e221754455ac9638f7974437c2259b9fc5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 12 Dec 2020 22:52:01 +0100 Subject: [PATCH 182/818] Fix miss step not working --- osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs index 14a127ac82..d257712553 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs @@ -130,8 +130,11 @@ namespace osu.Game.Rulesets.Taiko.Tests private void addMissJudgement() { DrawableTestHit h; - DrawableRuleset.Playfield.Add(h = new DrawableTestHit(new Hit(), HitResult.Miss)); - ((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h, new JudgementResult(new HitObject(), new TaikoJudgement()) { Type = HitResult.Miss }); + DrawableRuleset.Playfield.Add(h = new DrawableTestHit(new Hit { StartTime = DrawableRuleset.Playfield.Time.Current }, HitResult.Miss) + { + Alpha = 0 + }); + ((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h, new JudgementResult(h.HitObject, new TaikoJudgement()) { Type = HitResult.Miss }); } private void addBarLine(bool major, double delay = scroll_time) From 43c0e2191deaad07687704619d69b2c10056c0a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 12 Dec 2020 23:50:12 +0100 Subject: [PATCH 183/818] Apply local fix for strong/colour not being applied correctly --- .../TestSceneHits.cs | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs index d257712553..1d51020a6f 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs @@ -2,10 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Utils; +using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Judgements; @@ -118,7 +120,11 @@ namespace osu.Game.Rulesets.Taiko.Tests { HitResult hitResult = RNG.Next(2) == 0 ? HitResult.Ok : HitResult.Great; - Hit hit = new Hit { IsStrong = true }; + Hit hit = new Hit + { + IsStrong = true, + Samples = createSamples(strong: true) + }; var h = new DrawableTestHit(hit, kiai: kiai) { X = RNG.NextSingle(hitResult == HitResult.Ok ? -0.1f : -0.05f, hitResult == HitResult.Ok ? 0.1f : 0.05f) }; DrawableRuleset.Playfield.Add(h); @@ -166,6 +172,7 @@ namespace osu.Game.Rulesets.Taiko.Tests { StartTime = DrawableRuleset.Playfield.Time.Current + scroll_time, IsStrong = strong, + Samples = createSamples(strong: strong), Duration = duration, TickRate = 8, }; @@ -183,7 +190,8 @@ namespace osu.Game.Rulesets.Taiko.Tests Hit h = new Hit { StartTime = DrawableRuleset.Playfield.Time.Current + scroll_time, - IsStrong = strong + IsStrong = strong, + Samples = createSamples(HitType.Centre, strong) }; h.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); @@ -196,12 +204,27 @@ namespace osu.Game.Rulesets.Taiko.Tests Hit h = new Hit { StartTime = DrawableRuleset.Playfield.Time.Current + scroll_time, - IsStrong = strong + IsStrong = strong, + Samples = createSamples(HitType.Rim, strong) }; h.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); DrawableRuleset.Playfield.Add(new DrawableHit(h)); } + + // TODO: can be removed if a better way of handling colour/strong type and samples is developed + private IList createSamples(HitType? hitType = null, bool strong = false) + { + var samples = new List(); + + if (hitType == HitType.Rim) + samples.Add(new HitSampleInfo(HitSampleInfo.HIT_CLAP)); + + if (strong) + samples.Add(new HitSampleInfo(HitSampleInfo.HIT_FINISH)); + + return samples; + } } } From 3a3b32186ee62acc40a66f9047a777d4d2b72b2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 13 Dec 2020 00:00:37 +0100 Subject: [PATCH 184/818] Make height test steps work better --- .../DrawableTaikoRulesetTestScene.cs | 8 +++++--- osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/DrawableTaikoRulesetTestScene.cs b/osu.Game.Rulesets.Taiko.Tests/DrawableTaikoRulesetTestScene.cs index d1c4a1c56d..783636a62d 100644 --- a/osu.Game.Rulesets.Taiko.Tests/DrawableTaikoRulesetTestScene.cs +++ b/osu.Game.Rulesets.Taiko.Tests/DrawableTaikoRulesetTestScene.cs @@ -16,6 +16,8 @@ namespace osu.Game.Rulesets.Taiko.Tests { public abstract class DrawableTaikoRulesetTestScene : OsuTestScene { + protected const int DEFAULT_PLAYFIELD_CONTAINER_HEIGHT = 768; + protected DrawableTaikoRuleset DrawableRuleset { get; private set; } protected Container PlayfieldContainer { get; private set; } @@ -44,10 +46,10 @@ namespace osu.Game.Rulesets.Taiko.Tests Add(PlayfieldContainer = new Container { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, RelativeSizeAxes = Axes.X, - Height = 768, + Height = DEFAULT_PLAYFIELD_CONTAINER_HEIGHT, Children = new[] { DrawableRuleset = new DrawableTaikoRuleset(new TaikoRuleset(), beatmap.GetPlayableBeatmap(new TaikoRuleset().RulesetInfo)) } }); } diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs index 1d51020a6f..c3fa03d404 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs @@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.Taiko.Tests break; case 6: - PlayfieldContainer.Delay(delay).ResizeTo(new Vector2(1, TaikoPlayfield.DEFAULT_HEIGHT), 500); + PlayfieldContainer.Delay(delay).ResizeTo(new Vector2(1, DEFAULT_PLAYFIELD_CONTAINER_HEIGHT), 500); break; } } From 1ddc896b765a72c5135e2bd10e8759293dcce73b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 13 Dec 2020 12:18:29 +0100 Subject: [PATCH 185/818] Rename Strong{-> Nested}HitObject --- .../Skinning/TestSceneDrawableTaikoMascot.cs | 2 +- .../Objects/Drawables/DrawableDrumRoll.cs | 6 +++--- .../Objects/Drawables/DrawableDrumRollTick.cs | 6 +++--- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs | 6 +++--- .../Objects/Drawables/DrawableStrongNestedHit.cs | 4 ++-- .../Objects/Drawables/DrawableTaikoHitObject.cs | 6 +++--- .../{StrongHitObject.cs => StrongNestedHitObject.cs} | 2 +- osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs | 2 +- 8 files changed, 17 insertions(+), 17 deletions(-) rename osu.Game.Rulesets.Taiko/Objects/{StrongHitObject.cs => StrongNestedHitObject.cs} (89%) diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs index 99e103da3b..e62841b2c4 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs @@ -92,7 +92,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning createDrawableRuleset(); assertStateAfterResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Great }, TaikoMascotAnimationState.Idle); - assertStateAfterResult(new JudgementResult(new StrongHitObject(), new TaikoStrongJudgement()) { Type = HitResult.IgnoreMiss }, TaikoMascotAnimationState.Idle); + assertStateAfterResult(new JudgementResult(new StrongNestedHitObject(), new TaikoStrongJudgement()) { Type = HitResult.IgnoreMiss }, TaikoMascotAnimationState.Idle); } [Test] diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs index 4ead4982a1..4330e426c1 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs @@ -154,7 +154,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables Content.X = DrawHeight / 2; } - protected override DrawableStrongNestedHit CreateStrongHit(StrongHitObject hitObject) => new StrongNestedHit(hitObject, this); + protected override DrawableStrongNestedHit CreateStrongHit(StrongNestedHitObject hitObject) => new StrongNestedHit(hitObject, this); private void updateColour() { @@ -164,8 +164,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables private class StrongNestedHit : DrawableStrongNestedHit { - public StrongNestedHit(StrongHitObject strong, DrawableDrumRoll drumRoll) - : base(strong, drumRoll) + public StrongNestedHit(StrongNestedHitObject nestedHit, DrawableDrumRoll drumRoll) + : base(nestedHit, drumRoll) { } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs index e68e40ae1c..c458a620bd 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs @@ -61,12 +61,12 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables return UpdateResult(true); } - protected override DrawableStrongNestedHit CreateStrongHit(StrongHitObject hitObject) => new StrongNestedHit(hitObject, this); + protected override DrawableStrongNestedHit CreateStrongHit(StrongNestedHitObject hitObject) => new StrongNestedHit(hitObject, this); private class StrongNestedHit : DrawableStrongNestedHit { - public StrongNestedHit(StrongHitObject strong, DrawableDrumRollTick tick) - : base(strong, tick) + public StrongNestedHit(StrongNestedHitObject nestedHit, DrawableDrumRollTick tick) + : base(nestedHit, tick) { } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index d1751d8a75..5837a391b4 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -228,7 +228,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables } } - protected override DrawableStrongNestedHit CreateStrongHit(StrongHitObject hitObject) => new StrongNestedHit(hitObject, this); + protected override DrawableStrongNestedHit CreateStrongHit(StrongNestedHitObject hitObject) => new StrongNestedHit(hitObject, this); private class StrongNestedHit : DrawableStrongNestedHit { @@ -240,8 +240,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables public new DrawableHit MainObject => (DrawableHit)base.MainObject; - public StrongNestedHit(StrongHitObject strong, DrawableHit hit) - : base(strong, hit) + public StrongNestedHit(StrongNestedHitObject nestedHit, DrawableHit hit) + : base(nestedHit, hit) { } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableStrongNestedHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableStrongNestedHit.cs index 108e42eea5..e1da357c47 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableStrongNestedHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableStrongNestedHit.cs @@ -13,8 +13,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { public readonly DrawableHitObject MainObject; - protected DrawableStrongNestedHit(StrongHitObject strong, DrawableHitObject mainObject) - : base(strong) + protected DrawableStrongNestedHit(StrongNestedHitObject nestedHit, DrawableHitObject mainObject) + : base(nestedHit) { MainObject = mainObject; } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs index ff5b221273..42f24f1f52 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs @@ -208,7 +208,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { switch (hitObject) { - case StrongHitObject strong: + case StrongNestedHitObject strong: return CreateStrongHit(strong); } @@ -221,11 +221,11 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables protected abstract SkinnableDrawable CreateMainPiece(); /// - /// Creates the handler for this 's . + /// Creates the handler for this 's . /// This is only invoked if is true for . /// /// The strong hitobject. /// The strong hitobject handler. - protected virtual DrawableStrongNestedHit CreateStrongHit(StrongHitObject hitObject) => null; + protected virtual DrawableStrongNestedHit CreateStrongHit(StrongNestedHitObject hitObject) => null; } } diff --git a/osu.Game.Rulesets.Taiko/Objects/StrongHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/StrongNestedHitObject.cs similarity index 89% rename from osu.Game.Rulesets.Taiko/Objects/StrongHitObject.cs rename to osu.Game.Rulesets.Taiko/Objects/StrongNestedHitObject.cs index 72a04698be..b2def4c6e5 100644 --- a/osu.Game.Rulesets.Taiko/Objects/StrongHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/StrongNestedHitObject.cs @@ -7,7 +7,7 @@ using osu.Game.Rulesets.Taiko.Judgements; namespace osu.Game.Rulesets.Taiko.Objects { - public class StrongHitObject : TaikoHitObject + public class StrongNestedHitObject : TaikoHitObject { public override Judgement CreateJudgement() => new TaikoStrongJudgement(); diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs index d2c37d965c..2c1e3f32f3 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs @@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Taiko.Objects base.CreateNestedHitObjects(cancellationToken); if (IsStrong) - AddNested(new StrongHitObject { StartTime = this.GetEndTime() }); + AddNested(new StrongNestedHitObject { StartTime = this.GetEndTime() }); } public override Judgement CreateJudgement() => new TaikoJudgement(); From f74567e8eb393d351039c0208700cc703bfc3e3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 13 Dec 2020 12:36:39 +0100 Subject: [PATCH 186/818] Introduce base class for hitobjects that can be strong --- .../TaikoBeatmapConversionTest.cs | 2 +- .../Beatmaps/TaikoBeatmapConverter.cs | 4 +- .../Edit/TaikoSelectionHandler.cs | 2 +- .../Drawables/DrawableTaikoHitObject.cs | 2 +- osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs | 2 +- .../Objects/DrumRollTick.cs | 2 +- osu.Game.Rulesets.Taiko/Objects/Hit.cs | 2 +- osu.Game.Rulesets.Taiko/Objects/Swell.cs | 2 - .../Objects/TaikoHitObject.cs | 44 ------------------- .../Objects/TaikoStrongHitObject.cs | 42 ++++++++++++++++++ .../Replays/TaikoAutoGenerator.cs | 4 +- osu.Game.Rulesets.Taiko/UI/HitExplosion.cs | 2 +- osu.Game.Rulesets.Taiko/UI/TaikoHitTarget.cs | 6 +-- 13 files changed, 56 insertions(+), 60 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko/Objects/TaikoStrongHitObject.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs index 5e550a5d03..126af1a6ad 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Taiko.Tests IsCentre = (hitObject as Hit)?.Type == HitType.Centre, IsDrumRoll = hitObject is DrumRoll, IsSwell = hitObject is Swell, - IsStrong = ((TaikoHitObject)hitObject).IsStrong + IsStrong = (hitObject as TaikoStrongHitObject)?.IsStrong == true }; } diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 607eaf5dbd..78a0d1fdff 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -65,8 +65,8 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps converted.HitObjects = converted.HitObjects.GroupBy(t => t.StartTime).Select(x => { TaikoHitObject first = x.First(); - if (x.Skip(1).Any() && first.CanBeStrong) - first.IsStrong = true; + if (x.Skip(1).Any() && first is TaikoStrongHitObject strong) + strong.IsStrong = true; return first; }).ToList(); } diff --git a/osu.Game.Rulesets.Taiko/Edit/TaikoSelectionHandler.cs b/osu.Game.Rulesets.Taiko/Edit/TaikoSelectionHandler.cs index a05de1f217..7de920ca8c 100644 --- a/osu.Game.Rulesets.Taiko/Edit/TaikoSelectionHandler.cs +++ b/osu.Game.Rulesets.Taiko/Edit/TaikoSelectionHandler.cs @@ -96,7 +96,7 @@ namespace osu.Game.Rulesets.Taiko.Edit base.UpdateTernaryStates(); selectionRimState.Value = GetStateFromSelection(EditorBeatmap.SelectedHitObjects.OfType(), h => h.Type == HitType.Rim); - selectionStrongState.Value = GetStateFromSelection(EditorBeatmap.SelectedHitObjects.OfType(), h => h.IsStrong); + selectionStrongState.Value = GetStateFromSelection(EditorBeatmap.SelectedHitObjects.OfType(), h => h.IsStrong); } } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs index 42f24f1f52..1e39e7e97e 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs @@ -158,7 +158,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { base.LoadSamples(); - if (HitObject.CanBeStrong) + if (HitObject is TaikoStrongHitObject) isStrong.Value = getStrongSamples().Any(); } diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs index 5f52160be1..471e1a7b2f 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs @@ -15,7 +15,7 @@ using osuTK; namespace osu.Game.Rulesets.Taiko.Objects { - public class DrumRoll : TaikoHitObject, IHasPath + public class DrumRoll : TaikoStrongHitObject, IHasPath { /// /// Drum roll distance that results in a duration of 1 speed-adjusted beat length. diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs index 8a8be3e38d..5c36a3203c 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs @@ -7,7 +7,7 @@ using osu.Game.Rulesets.Taiko.Judgements; namespace osu.Game.Rulesets.Taiko.Objects { - public class DrumRollTick : TaikoHitObject + public class DrumRollTick : TaikoStrongHitObject { /// /// Whether this is the first (initial) tick of the slider. diff --git a/osu.Game.Rulesets.Taiko/Objects/Hit.cs b/osu.Game.Rulesets.Taiko/Objects/Hit.cs index 68cc8d0ead..d6385c810d 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Hit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Hit.cs @@ -5,7 +5,7 @@ using osu.Framework.Bindables; namespace osu.Game.Rulesets.Taiko.Objects { - public class Hit : TaikoHitObject + public class Hit : TaikoStrongHitObject { public readonly Bindable TypeBindable = new Bindable(); diff --git a/osu.Game.Rulesets.Taiko/Objects/Swell.cs b/osu.Game.Rulesets.Taiko/Objects/Swell.cs index bf8b7bc178..eeae6e79f8 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Swell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Swell.cs @@ -17,8 +17,6 @@ namespace osu.Game.Rulesets.Taiko.Objects set => Duration = value - StartTime; } - public override bool CanBeStrong => false; - public double Duration { get; set; } /// diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs index 2c1e3f32f3..f047c03f4b 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs @@ -1,9 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; -using System.Threading; -using osu.Framework.Bindables; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; @@ -19,47 +16,6 @@ namespace osu.Game.Rulesets.Taiko.Objects /// public const float DEFAULT_SIZE = 0.45f; - /// - /// Scale multiplier for a strong drawable taiko hit object. - /// - public const float STRONG_SCALE = 1.4f; - - /// - /// Default size of a strong drawable taiko hit object. - /// - public const float DEFAULT_STRONG_SIZE = DEFAULT_SIZE * STRONG_SCALE; - - public readonly Bindable IsStrongBindable = new BindableBool(); - - /// - /// Whether this can be made a "strong" (large) hit. - /// - public virtual bool CanBeStrong => true; - - /// - /// Whether this HitObject is a "strong" type. - /// Strong hit objects give more points for hitting the hit object with both keys. - /// - public bool IsStrong - { - get => IsStrongBindable.Value; - set - { - if (value && !CanBeStrong) - throw new InvalidOperationException($"Object of type {GetType()} cannot be strong"); - - IsStrongBindable.Value = value; - } - } - - protected override void CreateNestedHitObjects(CancellationToken cancellationToken) - { - base.CreateNestedHitObjects(cancellationToken); - - if (IsStrong) - AddNested(new StrongNestedHitObject { StartTime = this.GetEndTime() }); - } - public override Judgement CreateJudgement() => new TaikoJudgement(); protected override HitWindows CreateHitWindows() => new TaikoHitWindows(); diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoStrongHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoStrongHitObject.cs new file mode 100644 index 0000000000..a8d00b7681 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoStrongHitObject.cs @@ -0,0 +1,42 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Threading; +using osu.Framework.Bindables; +using osu.Game.Rulesets.Objects; + +namespace osu.Game.Rulesets.Taiko.Objects +{ + public abstract class TaikoStrongHitObject : TaikoHitObject + { + /// + /// Scale multiplier for a strong drawable taiko hit object. + /// + public const float STRONG_SCALE = 1.4f; + + /// + /// Default size of a strong drawable taiko hit object. + /// + public const float DEFAULT_STRONG_SIZE = DEFAULT_SIZE * STRONG_SCALE; + + public readonly Bindable IsStrongBindable = new BindableBool(); + + /// + /// Whether this HitObject is a "strong" type. + /// Strong hit objects give more points for hitting the hit object with both keys. + /// + public bool IsStrong + { + get => IsStrongBindable.Value; + set => IsStrongBindable.Value = value; + } + + protected override void CreateNestedHitObjects(CancellationToken cancellationToken) + { + base.CreateNestedHitObjects(cancellationToken); + + if (IsStrong) + AddNested(new StrongNestedHitObject { StartTime = this.GetEndTime() }); + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs index db2e5948f5..a3dbe672a4 100644 --- a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs +++ b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs @@ -102,13 +102,13 @@ namespace osu.Game.Rulesets.Taiko.Replays if (hit.Type == HitType.Centre) { - actions = h.IsStrong + actions = hit.IsStrong ? new[] { TaikoAction.LeftCentre, TaikoAction.RightCentre } : new[] { hitButton ? TaikoAction.LeftCentre : TaikoAction.RightCentre }; } else { - actions = h.IsStrong + actions = hit.IsStrong ? new[] { TaikoAction.LeftRim, TaikoAction.RightRim } : new[] { hitButton ? TaikoAction.LeftRim : TaikoAction.RightRim }; } diff --git a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs index 247c0dde73..05408e1049 100644 --- a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs @@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Taiko.UI /// public void VisualiseSecondHit() { - this.ResizeTo(new Vector2(TaikoHitObject.DEFAULT_STRONG_SIZE), 50); + this.ResizeTo(new Vector2(TaikoStrongHitObject.DEFAULT_STRONG_SIZE), 50); } } } diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoHitTarget.cs b/osu.Game.Rulesets.Taiko/UI/TaikoHitTarget.cs index caddc8b122..5d8145391d 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoHitTarget.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoHitTarget.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Taiko.UI Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, RelativeSizeAxes = Axes.Y, - Size = new Vector2(border_thickness, (1 - TaikoHitObject.DEFAULT_STRONG_SIZE) / 2f), + Size = new Vector2(border_thickness, (1 - TaikoStrongHitObject.DEFAULT_STRONG_SIZE) / 2f), Alpha = 0.1f }, new CircularContainer @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Taiko.UI Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, - Size = new Vector2(TaikoHitObject.DEFAULT_STRONG_SIZE), + Size = new Vector2(TaikoStrongHitObject.DEFAULT_STRONG_SIZE), Masking = true, BorderColour = Color4.White, BorderThickness = border_thickness, @@ -83,7 +83,7 @@ namespace osu.Game.Rulesets.Taiko.UI Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, RelativeSizeAxes = Axes.Y, - Size = new Vector2(border_thickness, (1 - TaikoHitObject.DEFAULT_STRONG_SIZE) / 2f), + Size = new Vector2(border_thickness, (1 - TaikoStrongHitObject.DEFAULT_STRONG_SIZE) / 2f), Alpha = 0.1f }, }; From b1635ecd166456bfa0667c80af75f1ea642f603c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 13 Dec 2020 12:51:10 +0100 Subject: [PATCH 187/818] Reflect can-be-strong split in DHO structure --- .../Objects/Drawables/DrawableDrumRoll.cs | 2 +- .../Objects/Drawables/DrawableDrumRollTick.cs | 2 +- .../Objects/Drawables/DrawableHit.cs | 2 +- .../Drawables/DrawableTaikoHitObject.cs | 82 +------------ .../Drawables/DrawableTaikoStrongHitObject.cs | 110 ++++++++++++++++++ .../Skinning/Legacy/LegacyCirclePiece.cs | 4 +- 6 files changed, 116 insertions(+), 86 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongHitObject.cs diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs index 4330e426c1..3f8970e506 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs @@ -19,7 +19,7 @@ using osuTK; namespace osu.Game.Rulesets.Taiko.Objects.Drawables { - public class DrawableDrumRoll : DrawableTaikoHitObject + public class DrawableDrumRoll : DrawableTaikoStrongHitObject { /// /// Number of rolling hits required to reach the dark/final colour. diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs index c458a620bd..c21b7983a0 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs @@ -9,7 +9,7 @@ using osu.Game.Skinning; namespace osu.Game.Rulesets.Taiko.Objects.Drawables { - public class DrawableDrumRollTick : DrawableTaikoHitObject + public class DrawableDrumRollTick : DrawableTaikoStrongHitObject { /// /// The hit type corresponding to the that the user pressed to hit this . diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index 5837a391b4..c82ac8498e 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -16,7 +16,7 @@ using osu.Game.Skinning; namespace osu.Game.Rulesets.Taiko.Objects.Drawables { - public class DrawableHit : DrawableTaikoHitObject + public class DrawableHit : DrawableTaikoStrongHitObject { /// /// A list of keys which can result in hits for this HitObject. diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs index 1e39e7e97e..cf3aa69b6f 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs @@ -4,13 +4,11 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; using osu.Framework.Input.Bindings; using osu.Game.Audio; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Skinning; using osuTK; @@ -120,112 +118,34 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables protected Vector2 BaseSize; protected SkinnableDrawable MainPiece; - private readonly Bindable isStrong; - - private readonly Container strongHitContainer; - protected DrawableTaikoHitObject(TObject hitObject) : base(hitObject) { HitObject = hitObject; - isStrong = HitObject.IsStrongBindable.GetBoundCopy(); Anchor = Anchor.CentreLeft; Origin = Anchor.Custom; RelativeSizeAxes = Axes.Both; - - AddInternal(strongHitContainer = new Container()); } [BackgroundDependencyLoader] private void load() { - isStrong.BindValueChanged(_ => - { - // will overwrite samples, should only be called on change. - updateSamplesFromStrong(); - - RecreatePieces(); - }); - RecreatePieces(); } - private HitSampleInfo[] getStrongSamples() => HitObject.Samples.Where(s => s.Name == HitSampleInfo.HIT_FINISH).ToArray(); - - protected override void LoadSamples() - { - base.LoadSamples(); - - if (HitObject is TaikoStrongHitObject) - isStrong.Value = getStrongSamples().Any(); - } - - private void updateSamplesFromStrong() - { - var strongSamples = getStrongSamples(); - - if (isStrong.Value != strongSamples.Any()) - { - if (isStrong.Value) - HitObject.Samples.Add(new HitSampleInfo(HitSampleInfo.HIT_FINISH)); - else - { - foreach (var sample in strongSamples) - HitObject.Samples.Remove(sample); - } - } - } - protected virtual void RecreatePieces() { - Size = BaseSize = new Vector2(HitObject.IsStrong ? TaikoHitObject.DEFAULT_STRONG_SIZE : TaikoHitObject.DEFAULT_SIZE); + Size = BaseSize = new Vector2(TaikoHitObject.DEFAULT_SIZE); MainPiece?.Expire(); Content.Add(MainPiece = CreateMainPiece()); } - protected override void AddNestedHitObject(DrawableHitObject hitObject) - { - base.AddNestedHitObject(hitObject); - - switch (hitObject) - { - case DrawableStrongNestedHit strong: - strongHitContainer.Add(strong); - break; - } - } - - protected override void ClearNestedHitObjects() - { - base.ClearNestedHitObjects(); - strongHitContainer.Clear(); - } - - protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject) - { - switch (hitObject) - { - case StrongNestedHitObject strong: - return CreateStrongHit(strong); - } - - return base.CreateNestedHitObject(hitObject); - } - // Most osu!taiko hitsounds are managed by the drum (see DrumSampleMapping). public override IEnumerable GetSamples() => Enumerable.Empty(); protected abstract SkinnableDrawable CreateMainPiece(); - - /// - /// Creates the handler for this 's . - /// This is only invoked if is true for . - /// - /// The strong hitobject. - /// The strong hitobject handler. - protected virtual DrawableStrongNestedHit CreateStrongHit(StrongNestedHitObject hitObject) => null; } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongHitObject.cs new file mode 100644 index 0000000000..875612c8a1 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongHitObject.cs @@ -0,0 +1,110 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics.Containers; +using osu.Game.Audio; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Drawables; +using osuTK; + +namespace osu.Game.Rulesets.Taiko.Objects.Drawables +{ + public abstract class DrawableTaikoStrongHitObject : DrawableTaikoHitObject + where TObject : TaikoStrongHitObject + { + private readonly Bindable isStrong; + + private readonly Container strongHitContainer; + + protected DrawableTaikoStrongHitObject(TObject hitObject) + : base(hitObject) + { + isStrong = HitObject.IsStrongBindable.GetBoundCopy(); + + AddInternal(strongHitContainer = new Container()); + } + + [BackgroundDependencyLoader] + private void load() + { + isStrong.BindValueChanged(_ => + { + // will overwrite samples, should only be called on change. + updateSamplesFromStrong(); + + RecreatePieces(); + }); + } + + private HitSampleInfo[] getStrongSamples() => HitObject.Samples.Where(s => s.Name == HitSampleInfo.HIT_FINISH).ToArray(); + + protected override void LoadSamples() + { + base.LoadSamples(); + isStrong.Value = getStrongSamples().Any(); + } + + private void updateSamplesFromStrong() + { + var strongSamples = getStrongSamples(); + + if (isStrong.Value != strongSamples.Any()) + { + if (isStrong.Value) + HitObject.Samples.Add(new HitSampleInfo(HitSampleInfo.HIT_FINISH)); + else + { + foreach (var sample in strongSamples) + HitObject.Samples.Remove(sample); + } + } + } + + protected override void RecreatePieces() + { + base.RecreatePieces(); + if (HitObject.IsStrong) + Size = BaseSize = new Vector2(TaikoStrongHitObject.DEFAULT_STRONG_SIZE); + } + + protected override void AddNestedHitObject(DrawableHitObject hitObject) + { + base.AddNestedHitObject(hitObject); + + switch (hitObject) + { + case DrawableStrongNestedHit strong: + strongHitContainer.Add(strong); + break; + } + } + + protected override void ClearNestedHitObjects() + { + base.ClearNestedHitObjects(); + strongHitContainer.Clear(); + } + + protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject) + { + switch (hitObject) + { + case StrongNestedHitObject strong: + return CreateStrongHit(strong); + } + + return base.CreateNestedHitObject(hitObject); + } + + /// + /// Creates the handler for this 's . + /// This is only invoked if is true for . + /// + /// The strong hitobject. + /// The strong hitobject handler. + protected abstract DrawableStrongNestedHit CreateStrongHit(StrongNestedHitObject hitObject); + } +} diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyCirclePiece.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyCirclePiece.cs index 821ddc3c04..52c9080633 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyCirclePiece.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyCirclePiece.cs @@ -7,7 +7,7 @@ using osu.Framework.Graphics.Animations; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Taiko.Objects.Drawables; +using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Skinning; using osuTK; using osuTK.Graphics; @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy const string normal_hit = "taikohit"; const string big_hit = "taikobig"; - string prefix = ((drawableHitObject as DrawableTaikoHitObject)?.HitObject.IsStrong ?? false) ? big_hit : normal_hit; + string prefix = ((drawableHitObject.HitObject as TaikoStrongHitObject)?.IsStrong ?? false) ? big_hit : normal_hit; return skin.GetAnimation($"{prefix}{lookup}", true, false) ?? // fallback to regular size if "big" version doesn't exist. From 4d444df6b3979852403e46e37c5a1c29237cfb21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 13 Dec 2020 12:53:42 +0100 Subject: [PATCH 188/818] Rename DHO CreateStrong{-> Nested}Hit --- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs | 2 +- .../Objects/Drawables/DrawableDrumRollTick.cs | 2 +- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs | 2 +- .../Objects/Drawables/DrawableTaikoStrongHitObject.cs | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs index 3f8970e506..3a7b644158 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs @@ -154,7 +154,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables Content.X = DrawHeight / 2; } - protected override DrawableStrongNestedHit CreateStrongHit(StrongNestedHitObject hitObject) => new StrongNestedHit(hitObject, this); + protected override DrawableStrongNestedHit CreateStrongNestedHit(StrongNestedHitObject hitObject) => new StrongNestedHit(hitObject, this); private void updateColour() { diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs index c21b7983a0..bc83fc538c 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs @@ -61,7 +61,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables return UpdateResult(true); } - protected override DrawableStrongNestedHit CreateStrongHit(StrongNestedHitObject hitObject) => new StrongNestedHit(hitObject, this); + protected override DrawableStrongNestedHit CreateStrongNestedHit(StrongNestedHitObject hitObject) => new StrongNestedHit(hitObject, this); private class StrongNestedHit : DrawableStrongNestedHit { diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index c82ac8498e..3928c4bb5e 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -228,7 +228,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables } } - protected override DrawableStrongNestedHit CreateStrongHit(StrongNestedHitObject hitObject) => new StrongNestedHit(hitObject, this); + protected override DrawableStrongNestedHit CreateStrongNestedHit(StrongNestedHitObject hitObject) => new StrongNestedHit(hitObject, this); private class StrongNestedHit : DrawableStrongNestedHit { diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongHitObject.cs index 875612c8a1..0aa8be2da5 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongHitObject.cs @@ -93,7 +93,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables switch (hitObject) { case StrongNestedHitObject strong: - return CreateStrongHit(strong); + return CreateStrongNestedHit(strong); } return base.CreateNestedHitObject(hitObject); @@ -105,6 +105,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables /// /// The strong hitobject. /// The strong hitobject handler. - protected abstract DrawableStrongNestedHit CreateStrongHit(StrongNestedHitObject hitObject); + protected abstract DrawableStrongNestedHit CreateStrongNestedHit(StrongNestedHitObject hitObject); } } From 61c488cd5eb33314611e87035073112b6a5444b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 13 Dec 2020 12:59:46 +0100 Subject: [PATCH 189/818] Create HO-specific nested hit types --- .../Skinning/TestSceneDrawableTaikoMascot.cs | 2 +- osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs | 6 ++++++ osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs | 6 ++++++ osu.Game.Rulesets.Taiko/Objects/Hit.cs | 6 ++++++ osu.Game.Rulesets.Taiko/Objects/StrongNestedHitObject.cs | 2 +- osu.Game.Rulesets.Taiko/Objects/TaikoStrongHitObject.cs | 9 ++++++++- 6 files changed, 28 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs index e62841b2c4..8d1aafdcc2 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs @@ -92,7 +92,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning createDrawableRuleset(); assertStateAfterResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Great }, TaikoMascotAnimationState.Idle); - assertStateAfterResult(new JudgementResult(new StrongNestedHitObject(), new TaikoStrongJudgement()) { Type = HitResult.IgnoreMiss }, TaikoMascotAnimationState.Idle); + assertStateAfterResult(new JudgementResult(new Hit.StrongNestedHit(), new TaikoStrongJudgement()) { Type = HitResult.IgnoreMiss }, TaikoMascotAnimationState.Idle); } [Test] diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs index 471e1a7b2f..93f95d6446 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs @@ -109,6 +109,12 @@ namespace osu.Game.Rulesets.Taiko.Objects protected override HitWindows CreateHitWindows() => HitWindows.Empty; + protected override StrongNestedHitObject CreateStrongNestedHit(double startTime) => new StrongNestedHit { StartTime = startTime }; + + public class StrongNestedHit : StrongNestedHitObject + { + } + #region LegacyBeatmapEncoder double IHasDistance.Distance => Duration * Velocity; diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs index 5c36a3203c..6b6ffa8668 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs @@ -28,5 +28,11 @@ namespace osu.Game.Rulesets.Taiko.Objects public override Judgement CreateJudgement() => new TaikoDrumRollTickJudgement(); protected override HitWindows CreateHitWindows() => HitWindows.Empty; + + protected override StrongNestedHitObject CreateStrongNestedHit(double startTime) => new StrongNestedHit { StartTime = startTime }; + + public class StrongNestedHit : StrongNestedHitObject + { + } } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Hit.cs b/osu.Game.Rulesets.Taiko/Objects/Hit.cs index d6385c810d..6bdde376f6 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Hit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Hit.cs @@ -17,5 +17,11 @@ namespace osu.Game.Rulesets.Taiko.Objects get => TypeBindable.Value; set => TypeBindable.Value = value; } + + protected override StrongNestedHitObject CreateStrongNestedHit(double startTime) => new StrongNestedHit { StartTime = startTime }; + + public class StrongNestedHit : StrongNestedHitObject + { + } } } diff --git a/osu.Game.Rulesets.Taiko/Objects/StrongNestedHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/StrongNestedHitObject.cs index b2def4c6e5..a0b1d3ef9b 100644 --- a/osu.Game.Rulesets.Taiko/Objects/StrongNestedHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/StrongNestedHitObject.cs @@ -7,7 +7,7 @@ using osu.Game.Rulesets.Taiko.Judgements; namespace osu.Game.Rulesets.Taiko.Objects { - public class StrongNestedHitObject : TaikoHitObject + public abstract class StrongNestedHitObject : TaikoHitObject { public override Judgement CreateJudgement() => new TaikoStrongJudgement(); diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoStrongHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoStrongHitObject.cs index a8d00b7681..6a8e33e718 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoStrongHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoStrongHitObject.cs @@ -36,7 +36,14 @@ namespace osu.Game.Rulesets.Taiko.Objects base.CreateNestedHitObjects(cancellationToken); if (IsStrong) - AddNested(new StrongNestedHitObject { StartTime = this.GetEndTime() }); + AddNested(CreateStrongNestedHit(this.GetEndTime())); } + + /// + /// Creates a representing a second hit on this object. + /// This is only called if is true. + /// + /// The start time of the nested hit. + protected abstract StrongNestedHitObject CreateStrongNestedHit(double startTime); } } From 60379b09dbc22a1b8229c45a5a74a8f384a2bb39 Mon Sep 17 00:00:00 2001 From: Firmatorenio Date: Sun, 13 Dec 2020 18:14:41 +0600 Subject: [PATCH 190/818] added a container for the judgements to move up or down --- ...awableManiaJudgementAdjustmentContainer.cs | 23 +++++++++++++++++++ osu.Game.Rulesets.Mania/UI/Stage.cs | 8 +------ 2 files changed, 24 insertions(+), 7 deletions(-) create mode 100644 osu.Game.Rulesets.Mania/UI/DrawableManiaJudgementAdjustmentContainer.cs diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgementAdjustmentContainer.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgementAdjustmentContainer.cs new file mode 100644 index 0000000000..b5abd9fb10 --- /dev/null +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgementAdjustmentContainer.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; +using osu.Game.Rulesets.UI; +using osu.Framework.Graphics; + +namespace osu.Game.Rulesets.Mania.UI +{ + public class DrawableManiaJudgementAdjustmentContainer : JudgementContainer + { + private float scorePosition => 0; + public DrawableManiaJudgementAdjustmentContainer(float hitTargetPosition) + { + Anchor = Anchor.TopCentre; + Origin = Anchor.Centre; + RelativeSizeAxes = Axes.Both; + Y = hitTargetPosition + 150; + } + + public DrawableManiaJudgementAdjustmentContainer() + : this(110) { } + } +} diff --git a/osu.Game.Rulesets.Mania/UI/Stage.cs b/osu.Game.Rulesets.Mania/UI/Stage.cs index 3d7960ffe3..73af205a37 100644 --- a/osu.Game.Rulesets.Mania/UI/Stage.cs +++ b/osu.Game.Rulesets.Mania/UI/Stage.cs @@ -101,13 +101,7 @@ namespace osu.Game.Rulesets.Mania.UI { RelativeSizeAxes = Axes.Both }, - judgements = new JudgementContainer - { - Anchor = Anchor.TopCentre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Y = HIT_TARGET_POSITION + 150, - }, + judgements = new DrawableManiaJudgementAdjustmentContainer(HIT_TARGET_POSITION), topLevelContainer = new Container { RelativeSizeAxes = Axes.Both } } } From 7d2b77cdbdf2f09b5c4938d3c87fe5b181682ab8 Mon Sep 17 00:00:00 2001 From: Graham Johnson Date: Sun, 13 Dec 2020 07:58:58 -0500 Subject: [PATCH 191/818] improve selection box rotation UX --- .../Screens/Edit/Compose/Components/SelectionBox.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs index 742d433760..347d9e3ba7 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs @@ -191,7 +191,7 @@ namespace osu.Game.Screens.Edit.Compose.Components { Anchor = Anchor.TopCentre, Y = -separation, - HandleDrag = e => OnRotation?.Invoke(e.Delta.X), + HandleDrag = e => OnRotation?.Invoke(convertDragEventToAngleOfRotation(e)), OperationStarted = operationStarted, OperationEnded = operationEnded } @@ -242,6 +242,15 @@ namespace osu.Game.Screens.Edit.Compose.Components private int activeOperations; + private float convertDragEventToAngleOfRotation(DragEvent e) + { + // Adjust coordinate system to the center of SelectionBox + float startAngle = MathF.Atan2(e.LastMousePosition.Y - DrawHeight / 2, e.LastMousePosition.X - DrawWidth / 2); + float endAngle = MathF.Atan2(e.MousePosition.Y - DrawHeight / 2, e.MousePosition.X - DrawWidth / 2); + + return (endAngle - startAngle) * 180 / MathF.PI; + } + private void operationEnded() { if (--activeOperations == 0) From 091b08b5070e4abac6999cf7e7a9c2ef4c71d560 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 13 Dec 2020 14:38:16 +0100 Subject: [PATCH 192/818] Scope drawable nested hits more closely to models --- .../Objects/Drawables/DrawableDrumRoll.cs | 6 +++--- .../Objects/Drawables/DrawableDrumRollTick.cs | 6 +++--- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs | 6 +++--- .../Objects/Drawables/DrawableTaikoStrongHitObject.cs | 7 ++++--- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs index 3a7b644158..0c6d28472d 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs @@ -19,7 +19,7 @@ using osuTK; namespace osu.Game.Rulesets.Taiko.Objects.Drawables { - public class DrawableDrumRoll : DrawableTaikoStrongHitObject + public class DrawableDrumRoll : DrawableTaikoStrongHitObject { /// /// Number of rolling hits required to reach the dark/final colour. @@ -154,7 +154,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables Content.X = DrawHeight / 2; } - protected override DrawableStrongNestedHit CreateStrongNestedHit(StrongNestedHitObject hitObject) => new StrongNestedHit(hitObject, this); + protected override DrawableStrongNestedHit CreateStrongNestedHit(DrumRoll.StrongNestedHit hitObject) => new StrongNestedHit(hitObject, this); private void updateColour() { @@ -164,7 +164,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables private class StrongNestedHit : DrawableStrongNestedHit { - public StrongNestedHit(StrongNestedHitObject nestedHit, DrawableDrumRoll drumRoll) + public StrongNestedHit(DrumRoll.StrongNestedHit nestedHit, DrawableDrumRoll drumRoll) : base(nestedHit, drumRoll) { } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs index bc83fc538c..c26e20f92c 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs @@ -9,7 +9,7 @@ using osu.Game.Skinning; namespace osu.Game.Rulesets.Taiko.Objects.Drawables { - public class DrawableDrumRollTick : DrawableTaikoStrongHitObject + public class DrawableDrumRollTick : DrawableTaikoStrongHitObject { /// /// The hit type corresponding to the that the user pressed to hit this . @@ -61,11 +61,11 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables return UpdateResult(true); } - protected override DrawableStrongNestedHit CreateStrongNestedHit(StrongNestedHitObject hitObject) => new StrongNestedHit(hitObject, this); + protected override DrawableStrongNestedHit CreateStrongNestedHit(DrumRollTick.StrongNestedHit hitObject) => new StrongNestedHit(hitObject, this); private class StrongNestedHit : DrawableStrongNestedHit { - public StrongNestedHit(StrongNestedHitObject nestedHit, DrawableDrumRollTick tick) + public StrongNestedHit(DrumRollTick.StrongNestedHit nestedHit, DrawableDrumRollTick tick) : base(nestedHit, tick) { } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index 3928c4bb5e..31cb3d764d 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -16,7 +16,7 @@ using osu.Game.Skinning; namespace osu.Game.Rulesets.Taiko.Objects.Drawables { - public class DrawableHit : DrawableTaikoStrongHitObject + public class DrawableHit : DrawableTaikoStrongHitObject { /// /// A list of keys which can result in hits for this HitObject. @@ -228,7 +228,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables } } - protected override DrawableStrongNestedHit CreateStrongNestedHit(StrongNestedHitObject hitObject) => new StrongNestedHit(hitObject, this); + protected override DrawableStrongNestedHit CreateStrongNestedHit(Hit.StrongNestedHit hitObject) => new StrongNestedHit(hitObject, this); private class StrongNestedHit : DrawableStrongNestedHit { @@ -240,7 +240,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables public new DrawableHit MainObject => (DrawableHit)base.MainObject; - public StrongNestedHit(StrongNestedHitObject nestedHit, DrawableHit hit) + public StrongNestedHit(Hit.StrongNestedHit nestedHit, DrawableHit hit) : base(nestedHit, hit) { } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongHitObject.cs index 0aa8be2da5..04e584e15d 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongHitObject.cs @@ -12,8 +12,9 @@ using osuTK; namespace osu.Game.Rulesets.Taiko.Objects.Drawables { - public abstract class DrawableTaikoStrongHitObject : DrawableTaikoHitObject + public abstract class DrawableTaikoStrongHitObject : DrawableTaikoHitObject where TObject : TaikoStrongHitObject + where TStrongNestedObject : StrongNestedHitObject { private readonly Bindable isStrong; @@ -92,7 +93,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { switch (hitObject) { - case StrongNestedHitObject strong: + case TStrongNestedObject strong: return CreateStrongNestedHit(strong); } @@ -105,6 +106,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables /// /// The strong hitobject. /// The strong hitobject handler. - protected abstract DrawableStrongNestedHit CreateStrongNestedHit(StrongNestedHitObject hitObject); + protected abstract DrawableStrongNestedHit CreateStrongNestedHit(TStrongNestedObject hitObject); } } From 080f7a3e32d9d19bca0b25ebb184c8402d155d1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 13 Dec 2020 13:04:44 +0100 Subject: [PATCH 193/818] Add/fix up xmldocs with clarifications --- .../Objects/Drawables/DrawableStrongNestedHit.cs | 2 +- osu.Game.Rulesets.Taiko/Objects/StrongNestedHitObject.cs | 4 ++++ osu.Game.Rulesets.Taiko/Objects/TaikoStrongHitObject.cs | 3 +++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableStrongNestedHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableStrongNestedHit.cs index e1da357c47..d735d258b3 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableStrongNestedHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableStrongNestedHit.cs @@ -7,7 +7,7 @@ using osu.Game.Rulesets.Taiko.Judgements; namespace osu.Game.Rulesets.Taiko.Objects.Drawables { /// - /// Used as a nested hitobject to provide s for s. + /// Used as a nested hitobject to provide s for s. /// public abstract class DrawableStrongNestedHit : DrawableTaikoHitObject { diff --git a/osu.Game.Rulesets.Taiko/Objects/StrongNestedHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/StrongNestedHitObject.cs index a0b1d3ef9b..2e8989bc79 100644 --- a/osu.Game.Rulesets.Taiko/Objects/StrongNestedHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/StrongNestedHitObject.cs @@ -7,6 +7,10 @@ using osu.Game.Rulesets.Taiko.Judgements; namespace osu.Game.Rulesets.Taiko.Objects { + /// + /// Base type for nested strong hits. + /// Used by s to represent their strong bonus scoring portions. + /// public abstract class StrongNestedHitObject : TaikoHitObject { public override Judgement CreateJudgement() => new TaikoStrongJudgement(); diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoStrongHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoStrongHitObject.cs index 6a8e33e718..861de95bae 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoStrongHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoStrongHitObject.cs @@ -7,6 +7,9 @@ using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Taiko.Objects { + /// + /// Base class for taiko hitobjects that can become strong (large). + /// public abstract class TaikoStrongHitObject : TaikoHitObject { /// From acd07017d1cf3b9c3497b73915363ed3be109725 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 13 Dec 2020 16:36:09 +0100 Subject: [PATCH 194/818] Replace now-default SDL run config with legacy osuTK config --- .../{osu_SDL.xml => osu___legacy_osuTK_.xml} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename .idea/.idea.osu.Desktop/.idea/runConfigurations/{osu_SDL.xml => osu___legacy_osuTK_.xml} (79%) diff --git a/.idea/.idea.osu.Desktop/.idea/runConfigurations/osu_SDL.xml b/.idea/.idea.osu.Desktop/.idea/runConfigurations/osu___legacy_osuTK_.xml similarity index 79% rename from .idea/.idea.osu.Desktop/.idea/runConfigurations/osu_SDL.xml rename to .idea/.idea.osu.Desktop/.idea/runConfigurations/osu___legacy_osuTK_.xml index 31f1fda09d..811fd9cc6d 100644 --- a/.idea/.idea.osu.Desktop/.idea/runConfigurations/osu_SDL.xml +++ b/.idea/.idea.osu.Desktop/.idea/runConfigurations/osu___legacy_osuTK_.xml @@ -1,7 +1,7 @@ - + /// /// This value is the original value specified in the beatmap, not affected by beatmap processing. + /// It should be used instead of when working on a beatmap, not a gameplay. /// public float OriginalX { @@ -32,6 +33,9 @@ namespace osu.Game.Rulesets.Catch.Objects float IHasXPosition.X => OriginalX; + /// + /// An alias of setter. + /// public float X { set => OriginalX = value; From 7cbbd74df247626ae917f766bcc6f03cd5e37a27 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 14 Dec 2020 11:38:07 +0900 Subject: [PATCH 215/818] Remove X setter from CatchHitObject --- .../Mods/TestSceneCatchModRelax.cs | 8 +++---- .../TestSceneAutoJuiceStream.cs | 2 +- .../TestSceneCatchModHidden.cs | 2 +- .../TestSceneCatchStacker.cs | 2 +- .../TestSceneCatcher.cs | 22 +++++++++---------- .../TestSceneCatcherArea.cs | 2 +- .../TestSceneDrawableHitObjects.cs | 6 ++--- .../TestSceneHyperDash.cs | 14 ++++++------ .../TestSceneJuiceStream.cs | 4 ++-- .../Beatmaps/CatchBeatmapConverter.cs | 4 ++-- .../Objects/CatchHitObject.cs | 8 ------- .../Objects/JuiceStream.cs | 6 ++--- 12 files changed, 36 insertions(+), 44 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModRelax.cs b/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModRelax.cs index c01aff0aa0..da4834aa73 100644 --- a/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModRelax.cs +++ b/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModRelax.cs @@ -32,22 +32,22 @@ namespace osu.Game.Rulesets.Catch.Tests.Mods { new Fruit { - X = CatchPlayfield.CENTER_X, + OriginalX = CatchPlayfield.CENTER_X, StartTime = 0 }, new Fruit { - X = 0, + OriginalX = 0, StartTime = 1000 }, new Fruit { - X = CatchPlayfield.WIDTH, + OriginalX = CatchPlayfield.WIDTH, StartTime = 2000 }, new JuiceStream { - X = CatchPlayfield.CENTER_X, + OriginalX = CatchPlayfield.CENTER_X, StartTime = 3000, Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, Vector2.UnitY * 200 }) } diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs index f552c3c27b..45cf5095f6 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Catch.Tests beatmap.HitObjects.Add(new JuiceStream { - X = CatchPlayfield.CENTER_X - width / 2, + OriginalX = CatchPlayfield.CENTER_X - width / 2, Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs index f15da29993..6af9c88088 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Catch.Tests { StartTime = 1000, Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(0, -192) }), - X = CatchPlayfield.WIDTH / 2 + OriginalX = CatchPlayfield.WIDTH / 2 } } }, diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchStacker.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchStacker.cs index 1ff31697b8..d7835bd8c4 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchStacker.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchStacker.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Catch.Tests { beatmap.HitObjects.Add(new Fruit { - X = (0.5f + i / 2048f * (i % 10 - 5)) * CatchPlayfield.WIDTH, + OriginalX = (0.5f + i / 2048f * (i % 10 - 5)) * CatchPlayfield.WIDTH, StartTime = i * 100, NewCombo = i % 8 == 0 }); diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs index e8bb57cdf3..d57e8e027e 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs @@ -66,7 +66,7 @@ namespace osu.Game.Rulesets.Catch.Tests JudgementResult result2 = null; AddStep("catch hyper fruit", () => { - attemptCatch(new Fruit { HyperDashTarget = new Fruit { X = 100 } }, out drawableObject1, out result1); + attemptCatch(new Fruit { HyperDashTarget = new Fruit { OriginalX = 100 } }, out drawableObject1, out result1); }); AddStep("catch normal fruit", () => { @@ -107,14 +107,14 @@ namespace osu.Game.Rulesets.Catch.Tests var halfWidth = Catcher.CalculateCatchWidth(new BeatmapDifficulty { CircleSize = 0 }) / 2; AddStep("catch fruit", () => { - attemptCatch(new Fruit { X = -halfWidth + 1 }); - attemptCatch(new Fruit { X = halfWidth - 1 }); + attemptCatch(new Fruit { OriginalX = -halfWidth + 1 }); + attemptCatch(new Fruit { OriginalX = halfWidth - 1 }); }); checkPlate(2); AddStep("miss fruit", () => { - attemptCatch(new Fruit { X = -halfWidth - 1 }); - attemptCatch(new Fruit { X = halfWidth + 1 }); + attemptCatch(new Fruit { OriginalX = -halfWidth - 1 }); + attemptCatch(new Fruit { OriginalX = halfWidth + 1 }); }); checkPlate(2); } @@ -122,7 +122,7 @@ namespace osu.Game.Rulesets.Catch.Tests [Test] public void TestFruitChangesCatcherState() { - AddStep("miss fruit", () => attemptCatch(new Fruit { X = 100 })); + AddStep("miss fruit", () => attemptCatch(new Fruit { OriginalX = 100 })); checkState(CatcherAnimationState.Fail); AddStep("catch fruit", () => attemptCatch(new Fruit())); checkState(CatcherAnimationState.Idle); @@ -135,7 +135,7 @@ namespace osu.Game.Rulesets.Catch.Tests { AddStep("catch hyper fruit", () => attemptCatch(new Fruit { - HyperDashTarget = new Fruit { X = 100 } + HyperDashTarget = new Fruit { OriginalX = 100 } })); checkHyperDash(true); AddStep("catch normal fruit", () => attemptCatch(new Fruit())); @@ -147,10 +147,10 @@ namespace osu.Game.Rulesets.Catch.Tests { AddStep("catch hyper kiai fruit", () => attemptCatch(new TestKiaiFruit { - HyperDashTarget = new Fruit { X = 100 } + HyperDashTarget = new Fruit { OriginalX = 100 } })); AddStep("catch tiny droplet", () => attemptCatch(new TinyDroplet())); - AddStep("miss tiny droplet", () => attemptCatch(new TinyDroplet { X = 100 })); + AddStep("miss tiny droplet", () => attemptCatch(new TinyDroplet { OriginalX = 100 })); // catcher state and hyper dash state is preserved checkState(CatcherAnimationState.Kiai); checkHyperDash(true); @@ -161,9 +161,9 @@ namespace osu.Game.Rulesets.Catch.Tests { AddStep("catch hyper kiai fruit", () => attemptCatch(new TestKiaiFruit { - HyperDashTarget = new Fruit { X = 100 } + HyperDashTarget = new Fruit { OriginalX = 100 } })); - AddStep("miss banana", () => attemptCatch(new Banana { X = 100 })); + AddStep("miss banana", () => attemptCatch(new Banana { OriginalX = 100 })); // catcher state is preserved but hyper dash state is reset checkState(CatcherAnimationState.Kiai); checkHyperDash(false); diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs index 5079e57e5e..423c3b7a13 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs @@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Catch.Tests AddStep("catch fruit", () => attemptCatch(new Fruit())); AddStep("catch fruit last in combo", () => attemptCatch(new Fruit { LastInCombo = true })); AddStep("catch kiai fruit", () => attemptCatch(new TestSceneCatcher.TestKiaiFruit())); - AddStep("miss last in combo", () => attemptCatch(new Fruit { X = 100, LastInCombo = true })); + AddStep("miss last in combo", () => attemptCatch(new Fruit { OriginalX = 100, LastInCombo = true })); } private void attemptCatch(Fruit fruit) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs index 3e4995482d..2db534e8c9 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs @@ -105,7 +105,7 @@ namespace osu.Game.Rulesets.Catch.Tests { var fruit = new Fruit { - X = getXCoords(hit), + OriginalX = getXCoords(hit), LastInCombo = i % 4 == 0, StartTime = playfieldTime + 800 + (200 * i) }; @@ -122,7 +122,7 @@ namespace osu.Game.Rulesets.Catch.Tests var juice = new JuiceStream { - X = xCoords, + OriginalX = xCoords, StartTime = playfieldTime + 1000, Path = new SliderPath(PathType.Linear, new[] { @@ -145,7 +145,7 @@ namespace osu.Game.Rulesets.Catch.Tests { var banana = new Banana { - X = getXCoords(hit), + OriginalX = getXCoords(hit), LastInCombo = i % 4 == 0, StartTime = playfieldTime + 800 + (200 * i) }; diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs index db09b2bc6b..67af3c4420 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs @@ -70,20 +70,20 @@ namespace osu.Game.Rulesets.Catch.Tests beatmap.ControlPointInfo.Add(0, new TimingControlPoint()); // Should produce a hyper-dash (edge case test) - beatmap.HitObjects.Add(new Fruit { StartTime = 1816, X = 56, NewCombo = true }); - beatmap.HitObjects.Add(new Fruit { StartTime = 2008, X = 308, NewCombo = true }); + beatmap.HitObjects.Add(new Fruit { StartTime = 1816, OriginalX = 56, NewCombo = true }); + beatmap.HitObjects.Add(new Fruit { StartTime = 2008, OriginalX = 308, NewCombo = true }); double startTime = 3000; const float left_x = 0.02f * CatchPlayfield.WIDTH; const float right_x = 0.98f * CatchPlayfield.WIDTH; - createObjects(() => new Fruit { X = left_x }); + createObjects(() => new Fruit { OriginalX = left_x }); createObjects(() => new TestJuiceStream(right_x), 1); createObjects(() => new TestJuiceStream(left_x), 1); - createObjects(() => new Fruit { X = right_x }); - createObjects(() => new Fruit { X = left_x }); - createObjects(() => new Fruit { X = right_x }); + createObjects(() => new Fruit { OriginalX = right_x }); + createObjects(() => new Fruit { OriginalX = left_x }); + createObjects(() => new Fruit { OriginalX = right_x }); createObjects(() => new TestJuiceStream(left_x), 1); beatmap.ControlPointInfo.Add(startTime, new TimingControlPoint @@ -121,7 +121,7 @@ namespace osu.Game.Rulesets.Catch.Tests { public TestJuiceStream(float x) { - X = x; + OriginalX = x; Path = new SliderPath(new[] { diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneJuiceStream.cs index 269e783899..dbcf382d62 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneJuiceStream.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneJuiceStream.cs @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Catch.Tests { new JuiceStream { - X = CatchPlayfield.CENTER_X, + OriginalX = CatchPlayfield.CENTER_X, Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Catch.Tests }, new Banana { - X = CatchPlayfield.CENTER_X, + OriginalX = CatchPlayfield.CENTER_X, StartTime = 1000, NewCombo = true } diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs index 34964fc4ae..55e86a7be2 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs @@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps Path = curveData.Path, NodeSamples = curveData.NodeSamples, RepeatCount = curveData.RepeatCount, - X = positionData?.X ?? 0, + OriginalX = positionData?.X ?? 0, NewCombo = comboData?.NewCombo ?? false, ComboOffset = comboData?.ComboOffset ?? 0, LegacyLastTickOffset = (obj as IHasLegacyLastTickOffset)?.LegacyLastTickOffset ?? 0 @@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps Samples = obj.Samples, NewCombo = comboData?.NewCombo ?? false, ComboOffset = comboData?.ComboOffset ?? 0, - X = positionData?.X ?? 0 + OriginalX = positionData?.X ?? 0 }.Yield(); } } diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index e689d6a178..ebbbd44960 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -33,14 +33,6 @@ namespace osu.Game.Rulesets.Catch.Objects float IHasXPosition.X => OriginalX; - /// - /// An alias of setter. - /// - public float X - { - set => OriginalX = value; - } - public readonly Bindable EffectiveXBindable = new Bindable(); /// diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index 35fd58826e..bda0457c83 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -75,7 +75,7 @@ namespace osu.Game.Rulesets.Catch.Objects AddNested(new TinyDroplet { StartTime = t + lastEvent.Value.Time, - X = OriginalX + Path.PositionAt( + OriginalX = OriginalX + Path.PositionAt( lastEvent.Value.PathProgress + (t / sinceLastTick) * (e.PathProgress - lastEvent.Value.PathProgress)).X, }); } @@ -93,7 +93,7 @@ namespace osu.Game.Rulesets.Catch.Objects { Samples = dropletSamples, StartTime = e.Time, - X = OriginalX + Path.PositionAt(e.PathProgress).X, + OriginalX = OriginalX + Path.PositionAt(e.PathProgress).X, }); break; @@ -104,7 +104,7 @@ namespace osu.Game.Rulesets.Catch.Objects { Samples = this.GetNodeSamples(nodeIndex++), StartTime = e.Time, - X = OriginalX + Path.PositionAt(e.PathProgress).X, + OriginalX = OriginalX + Path.PositionAt(e.PathProgress).X, }); break; } From 1794bfeddba4b8533d10f9854f06a0f5e9731cc7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 14 Dec 2020 13:07:55 +0900 Subject: [PATCH 216/818] Move offset into legacy mania judgement --- .../Legacy/LegacyManiaJudgementPiece.cs | 84 +++++++++++++++++++ .../Legacy/ManiaLegacySkinTransformer.cs | 3 +- .../UI/DrawableManiaJudgement.cs | 53 +++++------- osu.Game.Rulesets.Mania/UI/Stage.cs | 8 +- .../UI/Scrolling/ScrollingPlayfield.cs | 9 -- 5 files changed, 108 insertions(+), 49 deletions(-) create mode 100644 osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaJudgementPiece.cs diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaJudgementPiece.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaJudgementPiece.cs new file mode 100644 index 0000000000..464d754205 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaJudgementPiece.cs @@ -0,0 +1,84 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Animations; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Mania.UI; +using osu.Game.Rulesets.Scoring; +using osu.Game.Skinning; +using osuTK; + +namespace osu.Game.Rulesets.Mania.Skinning.Legacy +{ + public class LegacyManiaJudgementPiece : CompositeDrawable, IAnimatableJudgement + { + private readonly HitResult result; + private readonly Drawable animation; + + public LegacyManiaJudgementPiece(HitResult result, Drawable animation) + { + this.result = result; + this.animation = animation; + + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + AutoSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load(ISkinSource skin) + { + float? scorePosition = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.ScorePosition)?.Value; + + if (scorePosition != null) + scorePosition -= Stage.HIT_TARGET_POSITION + 150; + + Y = scorePosition ?? 0; + + if (animation != null) + InternalChild = animation; + } + + public void PlayAnimation() + { + if (animation == null) + return; + + (animation as IFramedAnimation)?.GotoFrame(0); + + switch (result) + { + case HitResult.None: + break; + + case HitResult.Miss: + animation.ScaleTo(1.6f); + animation.ScaleTo(1, 100, Easing.In); + + animation.MoveTo(Vector2.Zero); + animation.MoveToOffset(new Vector2(0, 100), 800, Easing.InQuint); + + animation.RotateTo(0); + animation.RotateTo(40, 800, Easing.InQuint); + + this.FadeOutFromOne(800); + break; + + default: + animation.ScaleTo(0.8f); + animation.ScaleTo(1, 250, Easing.OutElastic); + + animation.Delay(50).ScaleTo(0.75f, 250); + + this.Delay(50).FadeOut(200); + break; + } + } + + public Drawable GetAboveHitObjectsProxiedContent() => null; + } +} diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs index 89f639e2fe..7e2a8823b6 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs @@ -136,7 +136,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy string filename = this.GetManiaSkinConfig(hitresult_mapping[result])?.Value ?? default_hitresult_skin_filenames[result]; - return this.GetAnimation(filename, true, true); + var animation = this.GetAnimation(filename, true, true); + return animation == null ? null : new LegacyManiaJudgementPiece(result, animation); } public override SampleChannel GetSample(ISampleInfo sampleInfo) diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs index a3dcd0e57f..34d972e60f 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs @@ -5,7 +5,6 @@ using osu.Framework.Graphics; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Scoring; -using osuTK; namespace osu.Game.Rulesets.Mania.UI { @@ -20,37 +19,6 @@ namespace osu.Game.Rulesets.Mania.UI { } - protected override void ApplyMissAnimations() - { - if (!(JudgementBody.Drawable is DefaultManiaJudgementPiece)) - { - // this is temporary logic until mania's skin transformer returns IAnimatableJudgements - JudgementBody.ScaleTo(1.6f); - JudgementBody.ScaleTo(1, 100, Easing.In); - - JudgementBody.MoveTo(Vector2.Zero); - JudgementBody.MoveToOffset(new Vector2(0, 100), 800, Easing.InQuint); - - JudgementBody.RotateTo(0); - JudgementBody.RotateTo(40, 800, Easing.InQuint); - JudgementBody.FadeOutFromOne(800); - - LifetimeEnd = JudgementBody.LatestTransformEndTime; - } - - base.ApplyMissAnimations(); - } - - protected override void ApplyHitAnimations() - { - JudgementBody.ScaleTo(0.8f); - JudgementBody.ScaleTo(1, 250, Easing.OutElastic); - - JudgementBody.Delay(50) - .ScaleTo(0.75f, 250) - .FadeOut(200); - } - protected override Drawable CreateDefaultJudgement(HitResult result) => new DefaultManiaJudgementPiece(result); private class DefaultManiaJudgementPiece : DefaultJudgementPiece @@ -66,6 +34,27 @@ namespace osu.Game.Rulesets.Mania.UI JudgementText.Font = JudgementText.Font.With(size: 25); } + + public override void PlayAnimation() + { + base.PlayAnimation(); + + switch (Result) + { + case HitResult.None: + case HitResult.Miss: + break; + + default: + this.ScaleTo(0.8f); + this.ScaleTo(1, 250, Easing.OutElastic); + + this.Delay(50) + .ScaleTo(0.75f, 250) + .FadeOut(200); + break; + } + } } } } diff --git a/osu.Game.Rulesets.Mania/UI/Stage.cs b/osu.Game.Rulesets.Mania/UI/Stage.cs index d2f5e6902a..dc34bffab1 100644 --- a/osu.Game.Rulesets.Mania/UI/Stage.cs +++ b/osu.Game.Rulesets.Mania/UI/Stage.cs @@ -10,7 +10,6 @@ using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects.Drawables; -using osu.Game.Rulesets.Mania.Skinning; using osu.Game.Rulesets.Mania.UI.Components; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI; @@ -107,6 +106,7 @@ namespace osu.Game.Rulesets.Mania.UI Anchor = Anchor.TopCentre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, + Y = HIT_TARGET_POSITION + 150 }, topLevelContainer = new Container { RelativeSizeAxes = Axes.Both } } @@ -181,12 +181,6 @@ namespace osu.Game.Rulesets.Mania.UI })); } - protected override void OnSkinChanged() - { - judgements.Y = CurrentSkin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.ScorePosition) - ?.Value ?? HIT_TARGET_POSITION + 150; - } - protected override void Update() { // Due to masking differences, it is not possible to get the width of the columns container automatically diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs index 4f76198b9f..844a249769 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs @@ -20,20 +20,11 @@ namespace osu.Game.Rulesets.UI.Scrolling [Resolved] protected IScrollingInfo ScrollingInfo { get; private set; } - protected ISkinSource CurrentSkin { get; private set; } [BackgroundDependencyLoader] private void load(ISkinSource skin) { Direction.BindTo(ScrollingInfo.Direction); - CurrentSkin = skin; - - skin.SourceChanged += OnSkinChanged; - OnSkinChanged(); - } - - protected virtual void OnSkinChanged() - { } /// From 028909353c8dc5b027a05ae5fb995ee278db9ede Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 14 Dec 2020 13:15:52 +0900 Subject: [PATCH 217/818] Revert one more change --- osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs index 844a249769..475300c483 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.UI.Scrolling protected IScrollingInfo ScrollingInfo { get; private set; } [BackgroundDependencyLoader] - private void load(ISkinSource skin) + private void load() { Direction.BindTo(ScrollingInfo.Direction); } From d96399ea42130f3727d0188aa1ec8a0855c0b4b7 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 14 Dec 2020 13:18:14 +0900 Subject: [PATCH 218/818] Revert "Remove X setter from CatchHitObject" This reverts commit 7cbbd74d --- .../Mods/TestSceneCatchModRelax.cs | 8 +++---- .../TestSceneAutoJuiceStream.cs | 2 +- .../TestSceneCatchModHidden.cs | 2 +- .../TestSceneCatchStacker.cs | 2 +- .../TestSceneCatcher.cs | 22 +++++++++---------- .../TestSceneCatcherArea.cs | 2 +- .../TestSceneDrawableHitObjects.cs | 6 ++--- .../TestSceneHyperDash.cs | 14 ++++++------ .../TestSceneJuiceStream.cs | 4 ++-- .../Beatmaps/CatchBeatmapConverter.cs | 4 ++-- .../Objects/CatchHitObject.cs | 8 +++++++ .../Objects/JuiceStream.cs | 6 ++--- 12 files changed, 44 insertions(+), 36 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModRelax.cs b/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModRelax.cs index da4834aa73..c01aff0aa0 100644 --- a/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModRelax.cs +++ b/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModRelax.cs @@ -32,22 +32,22 @@ namespace osu.Game.Rulesets.Catch.Tests.Mods { new Fruit { - OriginalX = CatchPlayfield.CENTER_X, + X = CatchPlayfield.CENTER_X, StartTime = 0 }, new Fruit { - OriginalX = 0, + X = 0, StartTime = 1000 }, new Fruit { - OriginalX = CatchPlayfield.WIDTH, + X = CatchPlayfield.WIDTH, StartTime = 2000 }, new JuiceStream { - OriginalX = CatchPlayfield.CENTER_X, + X = CatchPlayfield.CENTER_X, StartTime = 3000, Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, Vector2.UnitY * 200 }) } diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs index 45cf5095f6..f552c3c27b 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Catch.Tests beatmap.HitObjects.Add(new JuiceStream { - OriginalX = CatchPlayfield.CENTER_X - width / 2, + X = CatchPlayfield.CENTER_X - width / 2, Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs index 6af9c88088..f15da29993 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Catch.Tests { StartTime = 1000, Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(0, -192) }), - OriginalX = CatchPlayfield.WIDTH / 2 + X = CatchPlayfield.WIDTH / 2 } } }, diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchStacker.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchStacker.cs index d7835bd8c4..1ff31697b8 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchStacker.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchStacker.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Catch.Tests { beatmap.HitObjects.Add(new Fruit { - OriginalX = (0.5f + i / 2048f * (i % 10 - 5)) * CatchPlayfield.WIDTH, + X = (0.5f + i / 2048f * (i % 10 - 5)) * CatchPlayfield.WIDTH, StartTime = i * 100, NewCombo = i % 8 == 0 }); diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs index d57e8e027e..e8bb57cdf3 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs @@ -66,7 +66,7 @@ namespace osu.Game.Rulesets.Catch.Tests JudgementResult result2 = null; AddStep("catch hyper fruit", () => { - attemptCatch(new Fruit { HyperDashTarget = new Fruit { OriginalX = 100 } }, out drawableObject1, out result1); + attemptCatch(new Fruit { HyperDashTarget = new Fruit { X = 100 } }, out drawableObject1, out result1); }); AddStep("catch normal fruit", () => { @@ -107,14 +107,14 @@ namespace osu.Game.Rulesets.Catch.Tests var halfWidth = Catcher.CalculateCatchWidth(new BeatmapDifficulty { CircleSize = 0 }) / 2; AddStep("catch fruit", () => { - attemptCatch(new Fruit { OriginalX = -halfWidth + 1 }); - attemptCatch(new Fruit { OriginalX = halfWidth - 1 }); + attemptCatch(new Fruit { X = -halfWidth + 1 }); + attemptCatch(new Fruit { X = halfWidth - 1 }); }); checkPlate(2); AddStep("miss fruit", () => { - attemptCatch(new Fruit { OriginalX = -halfWidth - 1 }); - attemptCatch(new Fruit { OriginalX = halfWidth + 1 }); + attemptCatch(new Fruit { X = -halfWidth - 1 }); + attemptCatch(new Fruit { X = halfWidth + 1 }); }); checkPlate(2); } @@ -122,7 +122,7 @@ namespace osu.Game.Rulesets.Catch.Tests [Test] public void TestFruitChangesCatcherState() { - AddStep("miss fruit", () => attemptCatch(new Fruit { OriginalX = 100 })); + AddStep("miss fruit", () => attemptCatch(new Fruit { X = 100 })); checkState(CatcherAnimationState.Fail); AddStep("catch fruit", () => attemptCatch(new Fruit())); checkState(CatcherAnimationState.Idle); @@ -135,7 +135,7 @@ namespace osu.Game.Rulesets.Catch.Tests { AddStep("catch hyper fruit", () => attemptCatch(new Fruit { - HyperDashTarget = new Fruit { OriginalX = 100 } + HyperDashTarget = new Fruit { X = 100 } })); checkHyperDash(true); AddStep("catch normal fruit", () => attemptCatch(new Fruit())); @@ -147,10 +147,10 @@ namespace osu.Game.Rulesets.Catch.Tests { AddStep("catch hyper kiai fruit", () => attemptCatch(new TestKiaiFruit { - HyperDashTarget = new Fruit { OriginalX = 100 } + HyperDashTarget = new Fruit { X = 100 } })); AddStep("catch tiny droplet", () => attemptCatch(new TinyDroplet())); - AddStep("miss tiny droplet", () => attemptCatch(new TinyDroplet { OriginalX = 100 })); + AddStep("miss tiny droplet", () => attemptCatch(new TinyDroplet { X = 100 })); // catcher state and hyper dash state is preserved checkState(CatcherAnimationState.Kiai); checkHyperDash(true); @@ -161,9 +161,9 @@ namespace osu.Game.Rulesets.Catch.Tests { AddStep("catch hyper kiai fruit", () => attemptCatch(new TestKiaiFruit { - HyperDashTarget = new Fruit { OriginalX = 100 } + HyperDashTarget = new Fruit { X = 100 } })); - AddStep("miss banana", () => attemptCatch(new Banana { OriginalX = 100 })); + AddStep("miss banana", () => attemptCatch(new Banana { X = 100 })); // catcher state is preserved but hyper dash state is reset checkState(CatcherAnimationState.Kiai); checkHyperDash(false); diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs index 423c3b7a13..5079e57e5e 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs @@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Catch.Tests AddStep("catch fruit", () => attemptCatch(new Fruit())); AddStep("catch fruit last in combo", () => attemptCatch(new Fruit { LastInCombo = true })); AddStep("catch kiai fruit", () => attemptCatch(new TestSceneCatcher.TestKiaiFruit())); - AddStep("miss last in combo", () => attemptCatch(new Fruit { OriginalX = 100, LastInCombo = true })); + AddStep("miss last in combo", () => attemptCatch(new Fruit { X = 100, LastInCombo = true })); } private void attemptCatch(Fruit fruit) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs index 2db534e8c9..3e4995482d 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs @@ -105,7 +105,7 @@ namespace osu.Game.Rulesets.Catch.Tests { var fruit = new Fruit { - OriginalX = getXCoords(hit), + X = getXCoords(hit), LastInCombo = i % 4 == 0, StartTime = playfieldTime + 800 + (200 * i) }; @@ -122,7 +122,7 @@ namespace osu.Game.Rulesets.Catch.Tests var juice = new JuiceStream { - OriginalX = xCoords, + X = xCoords, StartTime = playfieldTime + 1000, Path = new SliderPath(PathType.Linear, new[] { @@ -145,7 +145,7 @@ namespace osu.Game.Rulesets.Catch.Tests { var banana = new Banana { - OriginalX = getXCoords(hit), + X = getXCoords(hit), LastInCombo = i % 4 == 0, StartTime = playfieldTime + 800 + (200 * i) }; diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs index 67af3c4420..db09b2bc6b 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs @@ -70,20 +70,20 @@ namespace osu.Game.Rulesets.Catch.Tests beatmap.ControlPointInfo.Add(0, new TimingControlPoint()); // Should produce a hyper-dash (edge case test) - beatmap.HitObjects.Add(new Fruit { StartTime = 1816, OriginalX = 56, NewCombo = true }); - beatmap.HitObjects.Add(new Fruit { StartTime = 2008, OriginalX = 308, NewCombo = true }); + beatmap.HitObjects.Add(new Fruit { StartTime = 1816, X = 56, NewCombo = true }); + beatmap.HitObjects.Add(new Fruit { StartTime = 2008, X = 308, NewCombo = true }); double startTime = 3000; const float left_x = 0.02f * CatchPlayfield.WIDTH; const float right_x = 0.98f * CatchPlayfield.WIDTH; - createObjects(() => new Fruit { OriginalX = left_x }); + createObjects(() => new Fruit { X = left_x }); createObjects(() => new TestJuiceStream(right_x), 1); createObjects(() => new TestJuiceStream(left_x), 1); - createObjects(() => new Fruit { OriginalX = right_x }); - createObjects(() => new Fruit { OriginalX = left_x }); - createObjects(() => new Fruit { OriginalX = right_x }); + createObjects(() => new Fruit { X = right_x }); + createObjects(() => new Fruit { X = left_x }); + createObjects(() => new Fruit { X = right_x }); createObjects(() => new TestJuiceStream(left_x), 1); beatmap.ControlPointInfo.Add(startTime, new TimingControlPoint @@ -121,7 +121,7 @@ namespace osu.Game.Rulesets.Catch.Tests { public TestJuiceStream(float x) { - OriginalX = x; + X = x; Path = new SliderPath(new[] { diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneJuiceStream.cs index dbcf382d62..269e783899 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneJuiceStream.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneJuiceStream.cs @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Catch.Tests { new JuiceStream { - OriginalX = CatchPlayfield.CENTER_X, + X = CatchPlayfield.CENTER_X, Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Catch.Tests }, new Banana { - OriginalX = CatchPlayfield.CENTER_X, + X = CatchPlayfield.CENTER_X, StartTime = 1000, NewCombo = true } diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs index 55e86a7be2..34964fc4ae 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs @@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps Path = curveData.Path, NodeSamples = curveData.NodeSamples, RepeatCount = curveData.RepeatCount, - OriginalX = positionData?.X ?? 0, + X = positionData?.X ?? 0, NewCombo = comboData?.NewCombo ?? false, ComboOffset = comboData?.ComboOffset ?? 0, LegacyLastTickOffset = (obj as IHasLegacyLastTickOffset)?.LegacyLastTickOffset ?? 0 @@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps Samples = obj.Samples, NewCombo = comboData?.NewCombo ?? false, ComboOffset = comboData?.ComboOffset ?? 0, - OriginalX = positionData?.X ?? 0 + X = positionData?.X ?? 0 }.Yield(); } } diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index ebbbd44960..e689d6a178 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -33,6 +33,14 @@ namespace osu.Game.Rulesets.Catch.Objects float IHasXPosition.X => OriginalX; + /// + /// An alias of setter. + /// + public float X + { + set => OriginalX = value; + } + public readonly Bindable EffectiveXBindable = new Bindable(); /// diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index bda0457c83..35fd58826e 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -75,7 +75,7 @@ namespace osu.Game.Rulesets.Catch.Objects AddNested(new TinyDroplet { StartTime = t + lastEvent.Value.Time, - OriginalX = OriginalX + Path.PositionAt( + X = OriginalX + Path.PositionAt( lastEvent.Value.PathProgress + (t / sinceLastTick) * (e.PathProgress - lastEvent.Value.PathProgress)).X, }); } @@ -93,7 +93,7 @@ namespace osu.Game.Rulesets.Catch.Objects { Samples = dropletSamples, StartTime = e.Time, - OriginalX = OriginalX + Path.PositionAt(e.PathProgress).X, + X = OriginalX + Path.PositionAt(e.PathProgress).X, }); break; @@ -104,7 +104,7 @@ namespace osu.Game.Rulesets.Catch.Objects { Samples = this.GetNodeSamples(nodeIndex++), StartTime = e.Time, - OriginalX = OriginalX + Path.PositionAt(e.PathProgress).X, + X = OriginalX + Path.PositionAt(e.PathProgress).X, }); break; } From 0ad256a7626c6aa28c97f64ecc9bc9a1940c2f87 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 14 Dec 2020 13:18:32 +0900 Subject: [PATCH 219/818] Fix comment --- osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index e689d6a178..6267eca7de 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Catch.Objects private float xOffset; /// - /// A random offset applied to the horizontal value, set by the . + /// A random offset applied to the horizontal position, set by the . /// internal float XOffset { From 2e88e283d84c0547fd2f8272be87de6ccd8375d2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 14 Dec 2020 13:20:51 +0900 Subject: [PATCH 220/818] Remove unused using --- osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs index 475300c483..2b75f93f9e 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs @@ -4,7 +4,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Skinning; using osuTK; namespace osu.Game.Rulesets.UI.Scrolling From 5b5e883904298074b80671e87e0b88eca883127c Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 14 Dec 2020 13:39:07 +0900 Subject: [PATCH 221/818] Remove EffectiveXBindable (setting Value was not handled) And use orthogonal `OriginalXBindable` and `XOffsetBindable`. --- .../TestSceneCatcherArea.cs | 2 +- .../Objects/CatchHitObject.cs | 52 +++++++------------ .../Drawables/DrawableCatchHitObject.cs | 9 ++-- .../DrawablePalpableCatchHitObject.cs | 6 +-- 4 files changed, 29 insertions(+), 40 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs index 5079e57e5e..1cbfa6338e 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs @@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Catch.Tests private void attemptCatch(Fruit fruit) { - fruit.OriginalX += catcher.X; + fruit.X = fruit.OriginalX + catcher.X; fruit.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { CircleSize = circleSize diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index 6267eca7de..ae45182960 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -4,7 +4,6 @@ using osu.Framework.Bindables; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Rulesets.Catch.Beatmaps; using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; @@ -21,46 +20,40 @@ namespace osu.Game.Rulesets.Catch.Objects /// /// The horizontal position of the hit object between 0 and . /// - /// - /// This value is the original value specified in the beatmap, not affected by beatmap processing. - /// It should be used instead of when working on a beatmap, not a gameplay. - /// - public float OriginalX + public float X { - get => OriginalXBindable.Value; set => OriginalXBindable.Value = value; } - float IHasXPosition.X => OriginalX; + float IHasXPosition.X => OriginalXBindable.Value; + + public readonly Bindable XOffsetBindable = new Bindable(); /// - /// An alias of setter. + /// A random offset applied to the horizontal position, set by the beatmap processing. /// - public float X + public float XOffset { - set => OriginalX = value; + set => XOffsetBindable.Value = value; } - public readonly Bindable EffectiveXBindable = new Bindable(); + /// + /// The horizontal position of the hit object between 0 and . + /// + /// + /// This value is the original value specified in the beatmap, not affected by the beatmap processing. + /// Use for a gameplay. + /// + public float OriginalX => OriginalXBindable.Value; /// /// The effective horizontal position of the hit object between 0 and . /// - public float EffectiveX => EffectiveXBindable.Value; - - private float xOffset; - - /// - /// A random offset applied to the horizontal position, set by the . - /// - internal float XOffset - { - set - { - xOffset = value; - EffectiveXBindable.Value = OriginalX + xOffset; - } - } + /// + /// This value is the original value plus the offset applied by the beatmap processing. + /// Use if a value not affected by the offset is desired. + /// + public float EffectiveX => OriginalXBindable.Value + XOffsetBindable.Value; public double TimePreempt = 1000; @@ -127,10 +120,5 @@ namespace osu.Game.Rulesets.Catch.Objects } protected override HitWindows CreateHitWindows() => HitWindows.Empty; - - protected CatchHitObject() - { - OriginalXBindable.BindValueChanged(change => EffectiveXBindable.Value = change.NewValue + xOffset); - } } } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs index edd607a443..0c065948ef 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs @@ -15,7 +15,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables { public abstract class DrawableCatchHitObject : DrawableHitObject { - public readonly Bindable EffectiveXBindable = new Bindable(); + public readonly Bindable OriginalXBindable = new Bindable(); + public readonly Bindable XOffsetBindable = new Bindable(); protected override double InitialLifetimeOffset => HitObject.TimePreempt; @@ -38,14 +39,16 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables { base.OnApply(); - EffectiveXBindable.BindTo(HitObject.EffectiveXBindable); + OriginalXBindable.BindTo(HitObject.OriginalXBindable); + XOffsetBindable.BindTo(HitObject.XOffsetBindable); } protected override void OnFree() { base.OnFree(); - EffectiveXBindable.UnbindFrom(HitObject.EffectiveXBindable); + OriginalXBindable.UnbindFrom(HitObject.OriginalXBindable); + XOffsetBindable.UnbindFrom(HitObject.XOffsetBindable); } public Func CheckPosition; diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs index d3fa43c6b6..84af7922f9 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs @@ -55,10 +55,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables [BackgroundDependencyLoader] private void load() { - EffectiveXBindable.BindValueChanged(x => - { - X = x.NewValue; - }, true); + OriginalXBindable.BindValueChanged(_ => X = OriginalXBindable.Value + XOffsetBindable.Value); + XOffsetBindable.BindValueChanged(_ => X = OriginalXBindable.Value + XOffsetBindable.Value, true); ScaleBindable.BindValueChanged(scale => { From b81dbfc1921dabe9b846d752c7b4ca2a2a347c6d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 14 Dec 2020 13:56:46 +0900 Subject: [PATCH 222/818] Move shared implementation to a named function --- .../Objects/Drawables/DrawablePalpableCatchHitObject.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs index 84af7922f9..27cd7ed2bc 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs @@ -55,8 +55,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables [BackgroundDependencyLoader] private void load() { - OriginalXBindable.BindValueChanged(_ => X = OriginalXBindable.Value + XOffsetBindable.Value); - XOffsetBindable.BindValueChanged(_ => X = OriginalXBindable.Value + XOffsetBindable.Value, true); + OriginalXBindable.BindValueChanged(updateXPosition); + XOffsetBindable.BindValueChanged(updateXPosition, true); ScaleBindable.BindValueChanged(scale => { @@ -67,6 +67,11 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables IndexInBeatmap.BindValueChanged(_ => UpdateComboColour()); } + private void updateXPosition(ValueChangedEvent _) + { + X = OriginalXBindable.Value + XOffsetBindable.Value; + } + protected override void OnApply() { base.OnApply(); From a835ca9612e29fa71d66c852a0e4f5401bd3a465 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 14 Dec 2020 14:20:43 +0900 Subject: [PATCH 223/818] Fix anchors/origins for legacy pieces --- .../Skinning/Legacy/LegacyManiaJudgementPiece.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaJudgementPiece.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaJudgementPiece.cs index 464d754205..9684cbb167 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaJudgementPiece.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaJudgementPiece.cs @@ -40,7 +40,13 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy Y = scorePosition ?? 0; if (animation != null) - InternalChild = animation; + { + InternalChild = animation.With(d => + { + d.Anchor = Anchor.Centre; + d.Origin = Anchor.Centre; + }); + } } public void PlayAnimation() From 0d7f53b0b9b8d83835ac16aaf32d032c15b68dc9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 14 Dec 2020 14:21:21 +0900 Subject: [PATCH 224/818] Fix gameplay loading too fast the first time entering a beatmap --- osu.Game/Screens/Play/PlayerLoader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 729119fa36..f59b36bc42 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -189,7 +189,7 @@ namespace osu.Game.Screens.Play // after an initial delay, start the debounced load check. // this will continue to execute even after resuming back on restart. - Scheduler.Add(new ScheduledDelegate(pushWhenLoaded, 1800, 0)); + Scheduler.Add(new ScheduledDelegate(pushWhenLoaded, Clock.CurrentTime + 1800, 0)); showMuteWarningIfNeeded(); } From 704150306324135bbb6ba4957627619fa294548c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 14 Dec 2020 15:34:32 +0900 Subject: [PATCH 225/818] Avoid intermediary delegate --- .../Objects/Drawables/DrawableBarLine.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs index e7dd9a18c2..9e50faabc1 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs @@ -111,13 +111,13 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables protected override void LoadComplete() { base.LoadComplete(); - major.BindValueChanged(majorChanged => updateMajor(majorChanged.NewValue), true); + major.BindValueChanged(updateMajor); } - private void updateMajor(bool major) + private void updateMajor(ValueChangedEvent major) { - line.Alpha = major ? 1f : 0.75f; - triangleContainer.Alpha = major ? 1 : 0; + line.Alpha = major.NewValue ? 1f : 0.75f; + triangleContainer.Alpha = major.NewValue ? 1 : 0; } protected override void OnApply() From 51e8a05f181de7f34ef7b2f4290a3693069119e9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 14 Dec 2020 16:44:29 +0900 Subject: [PATCH 226/818] Seal SetRecordTarget method to simplify modification --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index c1a601eaae..b66a09aef1 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -268,7 +268,7 @@ namespace osu.Game.Rulesets.UI return false; } - public override void SetRecordTarget(Replay recordingReplay) + public sealed override void SetRecordTarget(Replay recordingReplay) { if (!(KeyBindingInputManager is IHasRecordingHandler recordingInputManager)) throw new InvalidOperationException($"A {nameof(KeyBindingInputManager)} which supports recording is not available"); From 1793385e96270a9b07050845b37b14fde04fe5c5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 14 Dec 2020 16:52:14 +0900 Subject: [PATCH 227/818] Pass a score to the replay recorder to allow reading more general scoring data --- osu.Game.Rulesets.Catch/UI/CatchReplayRecorder.cs | 4 ++-- osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs | 3 ++- osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs | 3 ++- osu.Game.Rulesets.Mania/UI/ManiaReplayRecorder.cs | 6 +++--- osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs | 3 ++- osu.Game.Rulesets.Osu/UI/OsuReplayRecorder.cs | 6 +++--- osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs | 3 ++- osu.Game.Rulesets.Taiko/UI/TaikoReplayRecorder.cs | 6 +++--- .../Visual/Gameplay/TestSceneReplayRecorder.cs | 5 +++-- .../Visual/Gameplay/TestSceneReplayRecording.cs | 5 +++-- .../Visual/Gameplay/TestSceneSpectatorPlayback.cs | 3 ++- osu.Game/Rulesets/UI/DrawableRuleset.cs | 10 +++++----- osu.Game/Rulesets/UI/ReplayRecorder.cs | 10 +++++----- osu.Game/Screens/Play/Player.cs | 8 ++++---- 14 files changed, 41 insertions(+), 34 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatchReplayRecorder.cs b/osu.Game.Rulesets.Catch/UI/CatchReplayRecorder.cs index 9a4d1f9585..1ddb5ac630 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchReplayRecorder.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchReplayRecorder.cs @@ -2,10 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using osu.Game.Replays; using osu.Game.Rulesets.Catch.Replays; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.UI; +using osu.Game.Scoring; using osuTK; namespace osu.Game.Rulesets.Catch.UI @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Catch.UI { private readonly CatchPlayfield playfield; - public CatchReplayRecorder(Replay target, CatchPlayfield playfield) + public CatchReplayRecorder(Score target, CatchPlayfield playfield) : base(target) { this.playfield = playfield; diff --git a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs index 46733181e3..9389fa803b 100644 --- a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs @@ -13,6 +13,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; +using osu.Game.Scoring; namespace osu.Game.Rulesets.Catch.UI { @@ -31,7 +32,7 @@ namespace osu.Game.Rulesets.Catch.UI protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay); - protected override ReplayRecorder CreateReplayRecorder(Replay replay) => new CatchReplayRecorder(replay, (CatchPlayfield)Playfield); + protected override ReplayRecorder CreateReplayRecorder(Score score) => new CatchReplayRecorder(score, (CatchPlayfield)Playfield); protected override Playfield CreatePlayfield() => new CatchPlayfield(Beatmap.BeatmapInfo.BaseDifficulty, CreateDrawableRepresentation); diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs index 7f5b9a6ee0..941ac9816c 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs @@ -23,6 +23,7 @@ using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; +using osu.Game.Scoring; namespace osu.Game.Rulesets.Mania.UI { @@ -132,6 +133,6 @@ namespace osu.Game.Rulesets.Mania.UI protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay); - protected override ReplayRecorder CreateReplayRecorder(Replay replay) => new ManiaReplayRecorder(replay); + protected override ReplayRecorder CreateReplayRecorder(Score score) => new ManiaReplayRecorder(score); } } diff --git a/osu.Game.Rulesets.Mania/UI/ManiaReplayRecorder.cs b/osu.Game.Rulesets.Mania/UI/ManiaReplayRecorder.cs index 18275000a2..b502d1f9e5 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaReplayRecorder.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaReplayRecorder.cs @@ -2,18 +2,18 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using osu.Game.Replays; using osu.Game.Rulesets.Mania.Replays; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.UI; +using osu.Game.Scoring; using osuTK; namespace osu.Game.Rulesets.Mania.UI { public class ManiaReplayRecorder : ReplayRecorder { - public ManiaReplayRecorder(Replay replay) - : base(replay) + public ManiaReplayRecorder(Score score) + : base(score) { } diff --git a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs index 69179137a6..df3f7c64e4 100644 --- a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs @@ -14,6 +14,7 @@ using osu.Game.Rulesets.Osu.Configuration; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Replays; using osu.Game.Rulesets.UI; +using osu.Game.Scoring; using osu.Game.Screens.Play; using osuTK; @@ -44,7 +45,7 @@ namespace osu.Game.Rulesets.Osu.UI protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new OsuFramedReplayInputHandler(replay); - protected override ReplayRecorder CreateReplayRecorder(Replay replay) => new OsuReplayRecorder(replay); + protected override ReplayRecorder CreateReplayRecorder(Score score) => new OsuReplayRecorder(score); public override double GameplayStartTime { diff --git a/osu.Game.Rulesets.Osu/UI/OsuReplayRecorder.cs b/osu.Game.Rulesets.Osu/UI/OsuReplayRecorder.cs index b68ea136d5..1304dfe416 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuReplayRecorder.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuReplayRecorder.cs @@ -2,18 +2,18 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using osu.Game.Replays; using osu.Game.Rulesets.Osu.Replays; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.UI; +using osu.Game.Scoring; using osuTK; namespace osu.Game.Rulesets.Osu.UI { public class OsuReplayRecorder : ReplayRecorder { - public OsuReplayRecorder(Replay replay) - : base(replay) + public OsuReplayRecorder(Score score) + : base(score) { } diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs index bbf8cb8de0..9cf931ee0a 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs @@ -17,6 +17,7 @@ using osu.Game.Replays; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.UI.Scrolling; +using osu.Game.Scoring; using osu.Game.Skinning; using osuTK; @@ -82,6 +83,6 @@ namespace osu.Game.Rulesets.Taiko.UI protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new TaikoFramedReplayInputHandler(replay); - protected override ReplayRecorder CreateReplayRecorder(Replay replay) => new TaikoReplayRecorder(replay); + protected override ReplayRecorder CreateReplayRecorder(Score score) => new TaikoReplayRecorder(score); } } diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoReplayRecorder.cs b/osu.Game.Rulesets.Taiko/UI/TaikoReplayRecorder.cs index 1859dabf03..e6391d1386 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoReplayRecorder.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoReplayRecorder.cs @@ -2,18 +2,18 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using osu.Game.Replays; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Taiko.Replays; using osu.Game.Rulesets.UI; +using osu.Game.Scoring; using osuTK; namespace osu.Game.Rulesets.Taiko.UI { public class TaikoReplayRecorder : ReplayRecorder { - public TaikoReplayRecorder(Replay replay) - : base(replay) + public TaikoReplayRecorder(Score score) + : base(score) { } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs index b72960931f..b2ad7ca5b4 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs @@ -19,6 +19,7 @@ using osu.Game.Replays; using osu.Game.Rulesets; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.UI; +using osu.Game.Scoring; using osu.Game.Screens.Play; using osu.Game.Tests.Visual.UserInterface; using osuTK; @@ -53,7 +54,7 @@ namespace osu.Game.Tests.Visual.Gameplay { recordingManager = new TestRulesetInputManager(new TestSceneModSettings.TestRulesetInfo(), 0, SimultaneousBindingMode.Unique) { - Recorder = recorder = new TestReplayRecorder(replay) + Recorder = recorder = new TestReplayRecorder(new Score { Replay = replay }) { ScreenSpaceToGamefield = pos => recordingManager.ToLocalSpace(pos), }, @@ -271,7 +272,7 @@ namespace osu.Game.Tests.Visual.Gameplay internal class TestReplayRecorder : ReplayRecorder { - public TestReplayRecorder(Replay target) + public TestReplayRecorder(Score target) : base(target) { } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs index 6872b6a669..40c4214749 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs @@ -15,6 +15,7 @@ using osu.Game.Replays; using osu.Game.Rulesets; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.UI; +using osu.Game.Scoring; using osu.Game.Screens.Play; using osu.Game.Tests.Visual.UserInterface; using osuTK; @@ -44,7 +45,7 @@ namespace osu.Game.Tests.Visual.Gameplay { recordingManager = new TestRulesetInputManager(new TestSceneModSettings.TestRulesetInfo(), 0, SimultaneousBindingMode.Unique) { - Recorder = new TestReplayRecorder(replay) + Recorder = new TestReplayRecorder(new Score { Replay = replay }) { ScreenSpaceToGamefield = pos => recordingManager.ToLocalSpace(pos) }, @@ -206,7 +207,7 @@ namespace osu.Game.Tests.Visual.Gameplay internal class TestReplayRecorder : ReplayRecorder { - public TestReplayRecorder(Replay target) + public TestReplayRecorder(Score target) : base(target) { } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs index 35473ee76c..e148fa381c 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs @@ -27,6 +27,7 @@ using osu.Game.Rulesets; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Replays.Types; using osu.Game.Rulesets.UI; +using osu.Game.Scoring; using osu.Game.Screens.Play; using osu.Game.Tests.Visual.UserInterface; using osuTK; @@ -348,7 +349,7 @@ namespace osu.Game.Tests.Visual.Gameplay internal class TestReplayRecorder : ReplayRecorder { public TestReplayRecorder() - : base(new Replay()) + : base(new Score()) { } diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index b66a09aef1..6940e43e5b 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -268,12 +268,12 @@ namespace osu.Game.Rulesets.UI return false; } - public sealed override void SetRecordTarget(Replay recordingReplay) + public sealed override void SetRecordTarget(Score score) { if (!(KeyBindingInputManager is IHasRecordingHandler recordingInputManager)) throw new InvalidOperationException($"A {nameof(KeyBindingInputManager)} which supports recording is not available"); - var recorder = CreateReplayRecorder(recordingReplay); + var recorder = CreateReplayRecorder(score); if (recorder == null) return; @@ -327,7 +327,7 @@ namespace osu.Game.Rulesets.UI protected virtual ReplayInputHandler CreateReplayInputHandler(Replay replay) => null; - protected virtual ReplayRecorder CreateReplayRecorder(Replay replay) => null; + protected virtual ReplayRecorder CreateReplayRecorder(Score score) => null; /// /// Creates a Playfield. @@ -516,8 +516,8 @@ namespace osu.Game.Rulesets.UI /// /// Sets a replay to be used to record gameplay. /// - /// The target to be recorded to. - public abstract void SetRecordTarget(Replay recordingReplay); + /// The target to be recorded to. + public abstract void SetRecordTarget(Score score); /// /// Invoked when the interactive user requests resuming from a paused state. diff --git a/osu.Game/Rulesets/UI/ReplayRecorder.cs b/osu.Game/Rulesets/UI/ReplayRecorder.cs index 1438ebd37a..2918a3b445 100644 --- a/osu.Game/Rulesets/UI/ReplayRecorder.cs +++ b/osu.Game/Rulesets/UI/ReplayRecorder.cs @@ -10,8 +10,8 @@ using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Game.Online.Spectator; -using osu.Game.Replays; using osu.Game.Rulesets.Replays; +using osu.Game.Scoring; using osu.Game.Screens.Play; using osuTK; @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.UI public abstract class ReplayRecorder : ReplayRecorder, IKeyBindingHandler where T : struct { - private readonly Replay target; + private readonly Score target; private readonly List pressedActions = new List(); @@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.UI [Resolved] private GameplayBeatmap gameplayBeatmap { get; set; } - protected ReplayRecorder(Replay target) + protected ReplayRecorder(Score target) { this.target = target; @@ -79,7 +79,7 @@ namespace osu.Game.Rulesets.UI private void recordFrame(bool important) { - var last = target.Frames.LastOrDefault(); + var last = target.Replay.Frames.LastOrDefault(); if (!important && last != null && Time.Current - last.Time < (1000d / RecordFrameRate)) return; @@ -90,7 +90,7 @@ namespace osu.Game.Rulesets.UI if (frame != null) { - target.Frames.Add(frame); + target.Replay.Frames.Add(frame); spectatorStreaming?.HandleFrame(frame); } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 7979b635aa..f7491ddfba 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -157,14 +157,14 @@ namespace osu.Game.Screens.Play PrepareReplay(); } - private Replay recordingReplay; + private Score recordingScore; /// /// Run any recording / playback setup for replays. /// protected virtual void PrepareReplay() { - DrawableRuleset.SetRecordTarget(recordingReplay = new Replay()); + DrawableRuleset.SetRecordTarget(recordingScore = new Score { Replay = new Replay() }); } [BackgroundDependencyLoader(true)] @@ -758,9 +758,9 @@ namespace osu.Game.Screens.Play var score = new Score { ScoreInfo = CreateScore() }; - if (recordingReplay?.Frames.Count > 0) + if (recordingScore?.Replay.Frames.Count > 0) { - score.Replay = recordingReplay; + score.Replay = recordingScore.Replay; using (var stream = new MemoryStream()) { From 64a2526678c07acd9b568ae11ff682f197306717 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 14 Dec 2020 17:33:23 +0900 Subject: [PATCH 228/818] Add header class and basic flow for propagating data updates --- .../Visual/Gameplay/TestSceneSpectator.cs | 3 +- osu.Game/Online/Spectator/FrameDataBundle.cs | 8 ++++- osu.Game/Online/Spectator/FrameHeader.cs | 35 +++++++++++++++++++ .../Spectator/SpectatorStreamingClient.cs | 12 +++++-- osu.Game/Rulesets/UI/ReplayRecorder.cs | 2 +- 5 files changed, 55 insertions(+), 5 deletions(-) create mode 100644 osu.Game/Online/Spectator/FrameHeader.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs index 72c6fd8d44..3e5b561a6f 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs @@ -17,6 +17,7 @@ using osu.Game.Replays.Legacy; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Replays; using osu.Game.Rulesets.UI; +using osu.Game.Scoring; using osu.Game.Screens.Play; using osu.Game.Tests.Beatmaps.IO; using osu.Game.Users; @@ -272,7 +273,7 @@ namespace osu.Game.Tests.Visual.Gameplay frames.Add(new LegacyReplayFrame(i * 100, RNG.Next(0, 512), RNG.Next(0, 512), buttonState)); } - var bundle = new FrameDataBundle(frames); + var bundle = new FrameDataBundle(new ScoreInfo(), frames); ((ISpectatorClient)this).UserSentFrames(StreamingUser.Id, bundle); if (!sentState) diff --git a/osu.Game/Online/Spectator/FrameDataBundle.cs b/osu.Game/Online/Spectator/FrameDataBundle.cs index 5281e61f9c..fecf88de22 100644 --- a/osu.Game/Online/Spectator/FrameDataBundle.cs +++ b/osu.Game/Online/Spectator/FrameDataBundle.cs @@ -1,20 +1,26 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + using System; using System.Collections.Generic; using osu.Game.Replays.Legacy; +using osu.Game.Scoring; namespace osu.Game.Online.Spectator { [Serializable] public class FrameDataBundle { + public FrameHeader Header { get; set; } + public IEnumerable Frames { get; set; } - public FrameDataBundle(IEnumerable frames) + public FrameDataBundle(ScoreInfo score, IEnumerable frames) { Frames = frames; + Header = new FrameHeader(score); } } } diff --git a/osu.Game/Online/Spectator/FrameHeader.cs b/osu.Game/Online/Spectator/FrameHeader.cs new file mode 100644 index 0000000000..0940eefa40 --- /dev/null +++ b/osu.Game/Online/Spectator/FrameHeader.cs @@ -0,0 +1,35 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +using System; +using System.Collections.Generic; +using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; + +namespace osu.Game.Online.Spectator +{ + [Serializable] + public class FrameHeader + { + public int Combo { get; set; } + + public int MaxCombo { get; set; } + + public Dictionary Statistics = new Dictionary(); + + /// + /// Construct header summary information from a point-in-time reference to a score which is actively being played. + /// + /// The score for reference. + public FrameHeader(ScoreInfo score) + { + Combo = score.Combo; + MaxCombo = score.MaxCombo; + + foreach (var kvp in score.Statistics) + Statistics[kvp.Key] = kvp.Value; + } + } +} diff --git a/osu.Game/Online/Spectator/SpectatorStreamingClient.cs b/osu.Game/Online/Spectator/SpectatorStreamingClient.cs index 08b524087a..0167a5d025 100644 --- a/osu.Game/Online/Spectator/SpectatorStreamingClient.cs +++ b/osu.Game/Online/Spectator/SpectatorStreamingClient.cs @@ -21,6 +21,7 @@ using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Replays.Types; +using osu.Game.Scoring; using osu.Game.Screens.Play; namespace osu.Game.Online.Spectator @@ -52,6 +53,9 @@ namespace osu.Game.Online.Spectator [CanBeNull] private IBeatmap currentBeatmap; + [CanBeNull] + private Score currentScore; + [Resolved] private IBindable currentRuleset { get; set; } @@ -203,7 +207,7 @@ namespace osu.Game.Online.Spectator return Task.CompletedTask; } - public void BeginPlaying(GameplayBeatmap beatmap) + public void BeginPlaying(GameplayBeatmap beatmap, Score score) { if (isPlaying) throw new InvalidOperationException($"Cannot invoke {nameof(BeginPlaying)} when already playing"); @@ -216,6 +220,8 @@ namespace osu.Game.Online.Spectator currentState.Mods = currentMods.Value.Select(m => new APIMod(m)); currentBeatmap = beatmap.PlayableBeatmap; + currentScore = score; + beginPlaying(); } @@ -308,7 +314,9 @@ namespace osu.Game.Online.Spectator pendingFrames.Clear(); - SendFrames(new FrameDataBundle(frames)); + Debug.Assert(currentScore != null); + + SendFrames(new FrameDataBundle(currentScore.ScoreInfo, frames)); lastSendTime = Time.Current; } diff --git a/osu.Game/Rulesets/UI/ReplayRecorder.cs b/osu.Game/Rulesets/UI/ReplayRecorder.cs index 2918a3b445..a4d46e3888 100644 --- a/osu.Game/Rulesets/UI/ReplayRecorder.cs +++ b/osu.Game/Rulesets/UI/ReplayRecorder.cs @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.UI inputManager = GetContainingInputManager(); - spectatorStreaming?.BeginPlaying(gameplayBeatmap); + spectatorStreaming?.BeginPlaying(gameplayBeatmap, target); } protected override void Dispose(bool isDisposing) From ae22f75406970fb5521ac70ad756ae378be59d92 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 14 Dec 2020 17:33:33 +0900 Subject: [PATCH 229/818] Bind replay recording score to judgement changes --- osu.Game/Screens/Play/Player.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index f7491ddfba..f40a7ccda8 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -165,6 +165,8 @@ namespace osu.Game.Screens.Play protected virtual void PrepareReplay() { DrawableRuleset.SetRecordTarget(recordingScore = new Score { Replay = new Replay() }); + + ScoreProcessor.NewJudgement += result => ScoreProcessor.PopulateScore(recordingScore.ScoreInfo); } [BackgroundDependencyLoader(true)] From 79768f0aa412f00fa6d8e45101038cd8417e2c78 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 14 Dec 2020 17:52:38 +0900 Subject: [PATCH 230/818] 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 eaedcb7bc3..2a08cb7867 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 b4c7dca12f..960959f367 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -26,7 +26,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 7542aded86..a5bcb91c74 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -88,7 +88,7 @@ - + From f3e6c586f7765c2ade6bf446e0605f792ef49bf9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 14 Dec 2020 17:59:04 +0900 Subject: [PATCH 231/818] Change waitForReady back to private implementation --- osu.Game/OsuGame.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 6c32e2e94c..888fd8c803 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -268,7 +268,7 @@ namespace osu.Game case LinkAction.OpenEditorTimestamp: case LinkAction.JoinMultiplayerMatch: case LinkAction.Spectate: - WaitForReady(() => notifications, _ => notifications.Post(new SimpleNotification + waitForReady(() => notifications, _ => notifications.Post(new SimpleNotification { Text = @"This link type is not yet supported!", Icon = FontAwesome.Solid.LifeRing, @@ -289,7 +289,7 @@ namespace osu.Game } }); - public void OpenUrlExternally(string url) => WaitForReady(() => externalLinkOpener, _ => + public void OpenUrlExternally(string url) => waitForReady(() => externalLinkOpener, _ => { if (url.StartsWith('/')) url = $"{API.Endpoint}{url}"; @@ -301,7 +301,7 @@ namespace osu.Game /// Open a specific channel in chat. /// /// The channel to display. - public void ShowChannel(string channel) => WaitForReady(() => channelManager, _ => + public void ShowChannel(string channel) => waitForReady(() => channelManager, _ => { try { @@ -317,19 +317,19 @@ namespace osu.Game /// Show a beatmap set as an overlay. /// /// The set to display. - public void ShowBeatmapSet(int setId) => WaitForReady(() => beatmapSetOverlay, _ => beatmapSetOverlay.FetchAndShowBeatmapSet(setId)); + public void ShowBeatmapSet(int setId) => waitForReady(() => beatmapSetOverlay, _ => beatmapSetOverlay.FetchAndShowBeatmapSet(setId)); /// /// Show a user's profile as an overlay. /// /// The user to display. - public void ShowUser(int userId) => WaitForReady(() => userProfile, _ => userProfile.ShowUser(userId)); + public void ShowUser(int userId) => waitForReady(() => userProfile, _ => userProfile.ShowUser(userId)); /// /// Show a beatmap's set as an overlay, displaying the given beatmap. /// /// The beatmap to show. - public void ShowBeatmap(int beatmapId) => WaitForReady(() => beatmapSetOverlay, _ => beatmapSetOverlay.FetchAndShowBeatmap(beatmapId)); + public void ShowBeatmap(int beatmapId) => waitForReady(() => beatmapSetOverlay, _ => beatmapSetOverlay.FetchAndShowBeatmap(beatmapId)); /// /// Present a beatmap at song select immediately. @@ -430,7 +430,7 @@ namespace osu.Game public override Task Import(Stream stream, string filename) { var importTask = new Task(async () => await base.Import(stream, filename)); - WaitForReady(() => this, _ => importTask.Start()); + waitForReady(() => this, _ => importTask.Start()); return importTask; } @@ -491,13 +491,13 @@ namespace osu.Game /// A function to retrieve a (potentially not-yet-constructed) target instance. /// The action to perform on the instance when load is confirmed. /// The type of the target instance. - protected void WaitForReady(Func retrieveInstance, Action action) + private void waitForReady(Func retrieveInstance, Action action) where T : Drawable { var instance = retrieveInstance(); if (ScreenStack == null || ScreenStack.CurrentScreen is StartupScreen || instance?.IsLoaded != true) - Schedule(() => WaitForReady(retrieveInstance, action)); + Schedule(() => waitForReady(retrieveInstance, action)); else action(instance); } From c5112edd08b5fea1688bbc4e339536b725f8cd7e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 14 Dec 2020 18:03:01 +0900 Subject: [PATCH 232/818] Add comment regarding the reasoning for encapsulating the task in another --- osu.Game/OsuGame.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 888fd8c803..d67d790ce2 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -429,8 +429,11 @@ namespace osu.Game public override Task Import(Stream stream, string filename) { + // encapsulate task as we don't want to begin the import process until in a ready state. var importTask = new Task(async () => await base.Import(stream, filename)); + waitForReady(() => this, _ => importTask.Start()); + return importTask; } From 7bf04808485e78ea96c85176dcfdf8e3360cc89b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 14 Dec 2020 18:12:23 +0900 Subject: [PATCH 233/818] Tidy up android-side code quality --- osu.Android/OsuGameActivity.cs | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/osu.Android/OsuGameActivity.cs b/osu.Android/OsuGameActivity.cs index eb9df24bf7..9798d669d6 100644 --- a/osu.Android/OsuGameActivity.cs +++ b/osu.Android/OsuGameActivity.cs @@ -38,27 +38,38 @@ namespace osu.Android protected override void OnNewIntent(Intent intent) { - if (intent.Action == Intent.ActionDefault && intent.Scheme == ContentResolver.SchemeContent) + switch (intent.Action) { - handleImportFromUri(intent.Data); - } + case Intent.ActionDefault: + if (intent.Scheme == ContentResolver.SchemeContent) + handleImportFromUri(intent.Data); + break; - if (intent.Action == Intent.ActionSend) - { - var content = intent.ClipData.GetItemAt(0); - handleImportFromUri(content.Uri); + case Intent.ActionSend: + { + var content = intent.ClipData?.GetItemAt(0); + if (content != null) + handleImportFromUri(content.Uri); + break; + } } } private void handleImportFromUri(Uri uri) { - var cursor = ContentResolver.Query(uri, new[] { OpenableColumns.DisplayName }, null, null); - var filename_column = cursor.GetColumnIndex(OpenableColumns.DisplayName); + var cursor = ContentResolver?.Query(uri, new[] { OpenableColumns.DisplayName }, null, null); + + if (cursor == null) + return; + cursor.MoveToFirst(); + var filenameColumn = cursor.GetColumnIndex(OpenableColumns.DisplayName); + var stream = ContentResolver.OpenInputStream(uri); + if (stream != null) - Task.Factory.StartNew(() => game.Import(stream, cursor.GetString(filename_column)), TaskCreationOptions.LongRunning); + Task.Factory.StartNew(() => game.Import(stream, cursor.GetString(filenameColumn)), TaskCreationOptions.LongRunning); } } } From 38e0b4e64dd3fbf26b3e39364ef0d968f66c193f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 14 Dec 2020 18:13:32 +0900 Subject: [PATCH 234/818] Remove unused using statements --- osu.Android/OsuGameAndroid.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Android/OsuGameAndroid.cs b/osu.Android/OsuGameAndroid.cs index 57512012f9..21d6336b2c 100644 --- a/osu.Android/OsuGameAndroid.cs +++ b/osu.Android/OsuGameAndroid.cs @@ -2,8 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.IO; -using System.Threading.Tasks; using Android.App; using Android.OS; using osu.Framework.Allocation; From 0d9c1cb5d338fc77cbd13b1f1f27efe0450102c6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 14 Dec 2020 18:41:24 +0900 Subject: [PATCH 235/818] Fix issues with data serialisation --- osu.Game/Online/Spectator/FrameDataBundle.cs | 8 +++++++ osu.Game/Online/Spectator/FrameHeader.cs | 17 ++++++++++----- osu.Game/Online/Spectator/StatisticPair.cs | 23 ++++++++++++++++++++ 3 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 osu.Game/Online/Spectator/StatisticPair.cs diff --git a/osu.Game/Online/Spectator/FrameDataBundle.cs b/osu.Game/Online/Spectator/FrameDataBundle.cs index fecf88de22..a8d0434324 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 Newtonsoft.Json; using osu.Game.Replays.Legacy; using osu.Game.Scoring; @@ -22,5 +23,12 @@ namespace osu.Game.Online.Spectator Frames = frames; Header = new FrameHeader(score); } + + [JsonConstructor] + public FrameDataBundle(FrameHeader header, IEnumerable frames) + { + Header = header; + Frames = frames; + } } } diff --git a/osu.Game/Online/Spectator/FrameHeader.cs b/osu.Game/Online/Spectator/FrameHeader.cs index 0940eefa40..9b6cc615a4 100644 --- a/osu.Game/Online/Spectator/FrameHeader.cs +++ b/osu.Game/Online/Spectator/FrameHeader.cs @@ -4,8 +4,8 @@ #nullable enable using System; -using System.Collections.Generic; -using osu.Game.Rulesets.Scoring; +using System.Linq; +using Newtonsoft.Json; using osu.Game.Scoring; namespace osu.Game.Online.Spectator @@ -17,7 +17,7 @@ namespace osu.Game.Online.Spectator public int MaxCombo { get; set; } - public Dictionary Statistics = new Dictionary(); + public StatisticPair[] Statistics { get; set; } /// /// Construct header summary information from a point-in-time reference to a score which is actively being played. @@ -28,8 +28,15 @@ namespace osu.Game.Online.Spectator Combo = score.Combo; MaxCombo = score.MaxCombo; - foreach (var kvp in score.Statistics) - Statistics[kvp.Key] = kvp.Value; + Statistics = score.Statistics.Select(kvp => new StatisticPair(kvp.Key, kvp.Value)).ToArray(); + } + + [JsonConstructor] + public FrameHeader(int combo, int maxCombo, StatisticPair[] statistics) + { + Combo = combo; + MaxCombo = maxCombo; + Statistics = statistics; } } } diff --git a/osu.Game/Online/Spectator/StatisticPair.cs b/osu.Game/Online/Spectator/StatisticPair.cs new file mode 100644 index 0000000000..793143a64c --- /dev/null +++ b/osu.Game/Online/Spectator/StatisticPair.cs @@ -0,0 +1,23 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Online.Spectator +{ + [Serializable] + public struct StatisticPair + { + public HitResult Result; + public int Count; + + public StatisticPair(HitResult result, int count) + { + Result = result; + Count = count; + } + + public override string ToString() => $"{Result}=>{Count}"; + } +} From 54827d4e96c82beec3d8eccd650b074623ee775d Mon Sep 17 00:00:00 2001 From: Xexxar Date: Mon, 14 Dec 2020 12:41:24 -0600 Subject: [PATCH 236/818] fixed low 50s count still penalizing high obj count maps --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 60df173223..af7786c3fd 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -159,7 +159,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty // Scale the speed value with accuracy and OD speedValue *= (.95 + Math.Pow(Attributes.OverallDifficulty, 2) / 750) * Math.Pow(accuracy, (14.5 - Math.Max(Attributes.OverallDifficulty, 8)) / 2); // Scale the speed value with # of 50s to punish doubletapping. - speedValue *= Math.Pow(0.98, countMeh < totalHits / 500.0 ? 0.5 * countMeh : countMeh - totalHits / 500.0 * 0.5); + speedValue *= Math.Pow(0.98, countMeh < totalHits / 500.0 ? 0 : countMeh - totalHits / 500.0); return speedValue; } From a323c5ce580bee6ed909ee3bb84adc5baf5a9efe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 14 Dec 2020 20:01:28 +0100 Subject: [PATCH 237/818] Use most backwards-compatible overload for query --- osu.Android/OsuGameActivity.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Android/OsuGameActivity.cs b/osu.Android/OsuGameActivity.cs index 9798d669d6..fe9b292389 100644 --- a/osu.Android/OsuGameActivity.cs +++ b/osu.Android/OsuGameActivity.cs @@ -57,7 +57,9 @@ namespace osu.Android private void handleImportFromUri(Uri uri) { - var cursor = ContentResolver?.Query(uri, new[] { OpenableColumns.DisplayName }, null, null); + // there are more performant overloads of this method, but this one is the most backwards-compatible + // (dates back to API 1). + var cursor = ContentResolver?.Query(uri, null, null, null, null); if (cursor == null) return; From 1f6e5f4d329934d06b5423481806e1fc8c78ddb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 14 Dec 2020 20:11:53 +0100 Subject: [PATCH 238/818] Copy archive contents to memory stream --- osu.Android/OsuGameActivity.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/osu.Android/OsuGameActivity.cs b/osu.Android/OsuGameActivity.cs index fe9b292389..f531b79d92 100644 --- a/osu.Android/OsuGameActivity.cs +++ b/osu.Android/OsuGameActivity.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.IO; using System.Threading.Tasks; using Android.App; using Android.Content; @@ -69,9 +70,21 @@ namespace osu.Android var filenameColumn = cursor.GetColumnIndex(OpenableColumns.DisplayName); var stream = ContentResolver.OpenInputStream(uri); + string filename = cursor.GetString(filenameColumn); if (stream != null) - Task.Factory.StartNew(() => game.Import(stream, cursor.GetString(filenameColumn)), TaskCreationOptions.LongRunning); + Task.Factory.StartNew(() => runImport(stream, filename), TaskCreationOptions.LongRunning); + } + + private Task runImport(Stream stream, string filename) + { + // SharpCompress requires archive streams to be seekable, which the stream opened by + // OpenInputStream() seems to not necessarily be. + // copy to an arbitrary-access memory stream to be able to proceed with the import. + var copy = new MemoryStream(); + stream.CopyTo(copy); + + return game.Import(copy, filename); } } } From 3e3be56e468d5bdf8d6a3042d7d21159d7b7e538 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 14 Dec 2020 20:23:49 +0100 Subject: [PATCH 239/818] Touch up and clarify intent handling --- osu.Android/OsuGameActivity.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Android/OsuGameActivity.cs b/osu.Android/OsuGameActivity.cs index f531b79d92..28c9433095 100644 --- a/osu.Android/OsuGameActivity.cs +++ b/osu.Android/OsuGameActivity.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Diagnostics; using System.IO; using System.Threading.Tasks; using Android.App; @@ -31,13 +32,18 @@ namespace osu.Android base.OnCreate(savedInstanceState); - OnNewIntent(Intent); + // OnNewIntent() only fires for an activity if it's *re-launched* while it's on top of the activity stack. + // on first launch we still have to fire manually. + // reference: https://developer.android.com/reference/android/app/Activity#onNewIntent(android.content.Intent) + handleIntent(Intent); Window.AddFlags(WindowManagerFlags.Fullscreen); Window.AddFlags(WindowManagerFlags.KeepScreenOn); } - protected override void OnNewIntent(Intent intent) + protected override void OnNewIntent(Intent intent) => handleIntent(intent); + + private void handleIntent(Intent intent) { switch (intent.Action) { From f9d7945a6f96b9b7bf19c301a251372e6c3bb681 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 14 Dec 2020 21:03:49 +0100 Subject: [PATCH 240/818] Remove non-functional replay import for now --- osu.Android/OsuGameActivity.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Android/OsuGameActivity.cs b/osu.Android/OsuGameActivity.cs index 28c9433095..cf0179e2ac 100644 --- a/osu.Android/OsuGameActivity.cs +++ b/osu.Android/OsuGameActivity.cs @@ -16,7 +16,7 @@ using osu.Framework.Android; namespace osu.Android { [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullUser, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = false)] - [IntentFilter(new[] { Intent.ActionDefault, Intent.ActionSend }, Categories = new[] { Intent.CategoryDefault }, DataPathPatterns = new[] { ".*\\.osz", ".*\\.osk", ".*\\.osr" }, DataMimeType = "application/*")] + [IntentFilter(new[] { Intent.ActionDefault, Intent.ActionSend }, Categories = new[] { Intent.CategoryDefault }, DataPathPatterns = new[] { ".*\\.osz", ".*\\.osk" }, DataMimeType = "application/*")] public class OsuGameActivity : AndroidGameActivity { private OsuGameAndroid game; From 5af1ac1b537bfce3b993692dbdff1e01a4de7278 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 14 Dec 2020 21:46:02 +0100 Subject: [PATCH 241/818] Rename TaikoStrong{-> able}HitObject --- osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs | 2 +- osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs | 2 +- osu.Game.Rulesets.Taiko/Edit/TaikoSelectionHandler.cs | 2 +- .../Objects/Drawables/DrawableTaikoStrongHitObject.cs | 6 +++--- osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs | 2 +- osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs | 2 +- osu.Game.Rulesets.Taiko/Objects/Hit.cs | 2 +- osu.Game.Rulesets.Taiko/Objects/StrongNestedHitObject.cs | 2 +- ...{TaikoStrongHitObject.cs => TaikoStrongableHitObject.cs} | 2 +- .../Skinning/Legacy/LegacyCirclePiece.cs | 2 +- osu.Game.Rulesets.Taiko/UI/HitExplosion.cs | 2 +- osu.Game.Rulesets.Taiko/UI/TaikoHitTarget.cs | 6 +++--- 12 files changed, 16 insertions(+), 16 deletions(-) rename osu.Game.Rulesets.Taiko/Objects/{TaikoStrongHitObject.cs => TaikoStrongableHitObject.cs} (96%) diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs index 126af1a6ad..3d77fb05db 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Taiko.Tests IsCentre = (hitObject as Hit)?.Type == HitType.Centre, IsDrumRoll = hitObject is DrumRoll, IsSwell = hitObject is Swell, - IsStrong = (hitObject as TaikoStrongHitObject)?.IsStrong == true + IsStrong = (hitObject as TaikoStrongableHitObject)?.IsStrong == true }; } diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 78a0d1fdff..1214c594aa 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps converted.HitObjects = converted.HitObjects.GroupBy(t => t.StartTime).Select(x => { TaikoHitObject first = x.First(); - if (x.Skip(1).Any() && first is TaikoStrongHitObject strong) + if (x.Skip(1).Any() && first is TaikoStrongableHitObject strong) strong.IsStrong = true; return first; }).ToList(); diff --git a/osu.Game.Rulesets.Taiko/Edit/TaikoSelectionHandler.cs b/osu.Game.Rulesets.Taiko/Edit/TaikoSelectionHandler.cs index 7de920ca8c..3fbcee44af 100644 --- a/osu.Game.Rulesets.Taiko/Edit/TaikoSelectionHandler.cs +++ b/osu.Game.Rulesets.Taiko/Edit/TaikoSelectionHandler.cs @@ -96,7 +96,7 @@ namespace osu.Game.Rulesets.Taiko.Edit base.UpdateTernaryStates(); selectionRimState.Value = GetStateFromSelection(EditorBeatmap.SelectedHitObjects.OfType(), h => h.Type == HitType.Rim); - selectionStrongState.Value = GetStateFromSelection(EditorBeatmap.SelectedHitObjects.OfType(), h => h.IsStrong); + selectionStrongState.Value = GetStateFromSelection(EditorBeatmap.SelectedHitObjects.OfType(), h => h.IsStrong); } } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongHitObject.cs index 04e584e15d..614c12165e 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongHitObject.cs @@ -13,7 +13,7 @@ using osuTK; namespace osu.Game.Rulesets.Taiko.Objects.Drawables { public abstract class DrawableTaikoStrongHitObject : DrawableTaikoHitObject - where TObject : TaikoStrongHitObject + where TObject : TaikoStrongableHitObject where TStrongNestedObject : StrongNestedHitObject { private readonly Bindable isStrong; @@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { base.RecreatePieces(); if (HitObject.IsStrong) - Size = BaseSize = new Vector2(TaikoStrongHitObject.DEFAULT_STRONG_SIZE); + Size = BaseSize = new Vector2(TaikoStrongableHitObject.DEFAULT_STRONG_SIZE); } protected override void AddNestedHitObject(DrawableHitObject hitObject) @@ -102,7 +102,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables /// /// Creates the handler for this 's . - /// This is only invoked if is true for . + /// This is only invoked if is true for . /// /// The strong hitobject. /// The strong hitobject handler. diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs index 93f95d6446..c0377c67a5 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs @@ -15,7 +15,7 @@ using osuTK; namespace osu.Game.Rulesets.Taiko.Objects { - public class DrumRoll : TaikoStrongHitObject, IHasPath + public class DrumRoll : TaikoStrongableHitObject, IHasPath { /// /// Drum roll distance that results in a duration of 1 speed-adjusted beat length. diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs index 6b6ffa8668..9d0336441e 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs @@ -7,7 +7,7 @@ using osu.Game.Rulesets.Taiko.Judgements; namespace osu.Game.Rulesets.Taiko.Objects { - public class DrumRollTick : TaikoStrongHitObject + public class DrumRollTick : TaikoStrongableHitObject { /// /// Whether this is the first (initial) tick of the slider. diff --git a/osu.Game.Rulesets.Taiko/Objects/Hit.cs b/osu.Game.Rulesets.Taiko/Objects/Hit.cs index 6bdde376f6..1b51288605 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Hit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Hit.cs @@ -5,7 +5,7 @@ using osu.Framework.Bindables; namespace osu.Game.Rulesets.Taiko.Objects { - public class Hit : TaikoStrongHitObject + public class Hit : TaikoStrongableHitObject { public readonly Bindable TypeBindable = new Bindable(); diff --git a/osu.Game.Rulesets.Taiko/Objects/StrongNestedHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/StrongNestedHitObject.cs index 2e8989bc79..3b427e48c5 100644 --- a/osu.Game.Rulesets.Taiko/Objects/StrongNestedHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/StrongNestedHitObject.cs @@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Taiko.Objects { /// /// Base type for nested strong hits. - /// Used by s to represent their strong bonus scoring portions. + /// Used by s to represent their strong bonus scoring portions. /// public abstract class StrongNestedHitObject : TaikoHitObject { diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoStrongHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs similarity index 96% rename from osu.Game.Rulesets.Taiko/Objects/TaikoStrongHitObject.cs rename to osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs index 861de95bae..fcd055bcec 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoStrongHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs @@ -10,7 +10,7 @@ namespace osu.Game.Rulesets.Taiko.Objects /// /// Base class for taiko hitobjects that can become strong (large). /// - public abstract class TaikoStrongHitObject : TaikoHitObject + public abstract class TaikoStrongableHitObject : TaikoHitObject { /// /// Scale multiplier for a strong drawable taiko hit object. diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyCirclePiece.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyCirclePiece.cs index 52c9080633..2b6c14ca63 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyCirclePiece.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyCirclePiece.cs @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy const string normal_hit = "taikohit"; const string big_hit = "taikobig"; - string prefix = ((drawableHitObject.HitObject as TaikoStrongHitObject)?.IsStrong ?? false) ? big_hit : normal_hit; + string prefix = ((drawableHitObject.HitObject as TaikoStrongableHitObject)?.IsStrong ?? false) ? big_hit : normal_hit; return skin.GetAnimation($"{prefix}{lookup}", true, false) ?? // fallback to regular size if "big" version doesn't exist. diff --git a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs index 05408e1049..d1fb3348b9 100644 --- a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs @@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Taiko.UI /// public void VisualiseSecondHit() { - this.ResizeTo(new Vector2(TaikoStrongHitObject.DEFAULT_STRONG_SIZE), 50); + this.ResizeTo(new Vector2(TaikoStrongableHitObject.DEFAULT_STRONG_SIZE), 50); } } } diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoHitTarget.cs b/osu.Game.Rulesets.Taiko/UI/TaikoHitTarget.cs index 5d8145391d..6401c6d09f 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoHitTarget.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoHitTarget.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Taiko.UI Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, RelativeSizeAxes = Axes.Y, - Size = new Vector2(border_thickness, (1 - TaikoStrongHitObject.DEFAULT_STRONG_SIZE) / 2f), + Size = new Vector2(border_thickness, (1 - TaikoStrongableHitObject.DEFAULT_STRONG_SIZE) / 2f), Alpha = 0.1f }, new CircularContainer @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Taiko.UI Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, - Size = new Vector2(TaikoStrongHitObject.DEFAULT_STRONG_SIZE), + Size = new Vector2(TaikoStrongableHitObject.DEFAULT_STRONG_SIZE), Masking = true, BorderColour = Color4.White, BorderThickness = border_thickness, @@ -83,7 +83,7 @@ namespace osu.Game.Rulesets.Taiko.UI Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, RelativeSizeAxes = Axes.Y, - Size = new Vector2(border_thickness, (1 - TaikoStrongHitObject.DEFAULT_STRONG_SIZE) / 2f), + Size = new Vector2(border_thickness, (1 - TaikoStrongableHitObject.DEFAULT_STRONG_SIZE) / 2f), Alpha = 0.1f }, }; From c5a218f7c96afe0551d4f083075e9edb924a9405 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 14 Dec 2020 21:46:28 +0100 Subject: [PATCH 242/818] Add "strongable" to user dictionary --- osu.sln.DotSettings | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index 3ef419c572..22ea73858e 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -928,5 +928,6 @@ private void load() True True True + True True True From 512549b4ea796e4b8f7e026cc1d2ad58db43ef46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 14 Dec 2020 21:47:31 +0100 Subject: [PATCH 243/818] Rename DrawableTaikoStrong{-> able}HitObject --- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs | 2 +- .../Objects/Drawables/DrawableDrumRollTick.cs | 2 +- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs | 2 +- .../Objects/Drawables/DrawableStrongNestedHit.cs | 2 +- ...StrongHitObject.cs => DrawableTaikoStrongableHitObject.cs} | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) rename osu.Game.Rulesets.Taiko/Objects/Drawables/{DrawableTaikoStrongHitObject.cs => DrawableTaikoStrongableHitObject.cs} (94%) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs index 0c6d28472d..4925b6fdfc 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs @@ -19,7 +19,7 @@ using osuTK; namespace osu.Game.Rulesets.Taiko.Objects.Drawables { - public class DrawableDrumRoll : DrawableTaikoStrongHitObject + public class DrawableDrumRoll : DrawableTaikoStrongableHitObject { /// /// Number of rolling hits required to reach the dark/final colour. diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs index c26e20f92c..c6761de5e3 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs @@ -9,7 +9,7 @@ using osu.Game.Skinning; namespace osu.Game.Rulesets.Taiko.Objects.Drawables { - public class DrawableDrumRollTick : DrawableTaikoStrongHitObject + public class DrawableDrumRollTick : DrawableTaikoStrongableHitObject { /// /// The hit type corresponding to the that the user pressed to hit this . diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index 31cb3d764d..431f2980ec 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -16,7 +16,7 @@ using osu.Game.Skinning; namespace osu.Game.Rulesets.Taiko.Objects.Drawables { - public class DrawableHit : DrawableTaikoStrongHitObject + public class DrawableHit : DrawableTaikoStrongableHitObject { /// /// A list of keys which can result in hits for this HitObject. diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableStrongNestedHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableStrongNestedHit.cs index d735d258b3..d2e8888197 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableStrongNestedHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableStrongNestedHit.cs @@ -7,7 +7,7 @@ using osu.Game.Rulesets.Taiko.Judgements; namespace osu.Game.Rulesets.Taiko.Objects.Drawables { /// - /// Used as a nested hitobject to provide s for s. + /// Used as a nested hitobject to provide s for s. /// public abstract class DrawableStrongNestedHit : DrawableTaikoHitObject { diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongableHitObject.cs similarity index 94% rename from osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongHitObject.cs rename to osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongableHitObject.cs index 614c12165e..af3e94d9c6 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongableHitObject.cs @@ -12,7 +12,7 @@ using osuTK; namespace osu.Game.Rulesets.Taiko.Objects.Drawables { - public abstract class DrawableTaikoStrongHitObject : DrawableTaikoHitObject + public abstract class DrawableTaikoStrongableHitObject : DrawableTaikoHitObject where TObject : TaikoStrongableHitObject where TStrongNestedObject : StrongNestedHitObject { @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables private readonly Container strongHitContainer; - protected DrawableTaikoStrongHitObject(TObject hitObject) + protected DrawableTaikoStrongableHitObject(TObject hitObject) : base(hitObject) { isStrong = HitObject.IsStrongBindable.GetBoundCopy(); From 2051f49f78c98e79214cee4d3f5ae8f185545bc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 14 Dec 2020 21:58:13 +0100 Subject: [PATCH 244/818] Ensure correct initial state of taiko bar lines --- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs index 9e50faabc1..d653f01db6 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs @@ -111,7 +111,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables protected override void LoadComplete() { base.LoadComplete(); - major.BindValueChanged(updateMajor); + major.BindValueChanged(updateMajor, true); } private void updateMajor(ValueChangedEvent major) From 523e8034407435b3d4b1beeaea4a24a457bc9918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 15 Dec 2020 00:28:00 +0100 Subject: [PATCH 245/818] Fix swells crashing on rapid seeks in editor --- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs index 5c6278ed08..229d581d0c 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs @@ -168,7 +168,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables foreach (var t in ticks) { - if (!t.IsHit) + if (!t.Result.HasResult) { nextTick = t; break; @@ -208,7 +208,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables continue; } - tick.TriggerResult(false); + if (!tick.Result.HasResult) + tick.TriggerResult(false); } ApplyResult(r => r.Type = numHits > HitObject.RequiredHits / 2 ? HitResult.Ok : r.Judgement.MinResult); From cba4657021286be23f5c0d90a89f5dca0da7de80 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 15 Dec 2020 12:37:41 +0900 Subject: [PATCH 246/818] Get handled file extensions from game itself, rather than duplicating locally --- osu.Game/Screens/Import/FileImportScreen.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Import/FileImportScreen.cs b/osu.Game/Screens/Import/FileImportScreen.cs index e2e2b699f5..24a498e8cd 100644 --- a/osu.Game/Screens/Import/FileImportScreen.cs +++ b/osu.Game/Screens/Import/FileImportScreen.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.IO; +using System.Linq; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -36,7 +37,7 @@ namespace osu.Game.Screens.Import private const float button_vertical_margin = 15; [Resolved] - private OsuGameBase gameBase { get; set; } + private OsuGameBase game { get; set; } [Resolved] private OsuColour colours { get; set; } @@ -46,7 +47,6 @@ namespace osu.Game.Screens.Import { storage.GetStorageForDirectory("imports"); var originalPath = storage.GetFullPath("imports", true); - string[] fileExtensions = { ".osk", ".osr", ".osz" }; InternalChild = contentContainer = new Container { @@ -63,7 +63,7 @@ namespace osu.Game.Screens.Import Colour = colours.GreySeafoamDark, RelativeSizeAxes = Axes.Both, }, - fileSelector = new FileSelector(initialPath: originalPath, validFileExtensions: fileExtensions) + fileSelector = new FileSelector(originalPath, game.HandledExtensions.ToArray()) { RelativeSizeAxes = Axes.Both, Width = 0.65f @@ -174,7 +174,7 @@ namespace osu.Game.Screens.Import string[] paths = { path }; - Task.Factory.StartNew(() => gameBase.Import(paths), TaskCreationOptions.LongRunning); + Task.Factory.StartNew(() => game.Import(paths), TaskCreationOptions.LongRunning); } } } From 33f77f81f2d3af9dec7c1d6f9f94442e8211924c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 15 Dec 2020 12:43:27 +0900 Subject: [PATCH 247/818] Disable the import button when no file is selected, rather than weird flash logic --- osu.Game/Screens/Import/FileImportScreen.cs | 24 +++++++++------------ 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/osu.Game/Screens/Import/FileImportScreen.cs b/osu.Game/Screens/Import/FileImportScreen.cs index 24a498e8cd..da6d3f6622 100644 --- a/osu.Game/Screens/Import/FileImportScreen.cs +++ b/osu.Game/Screens/Import/FileImportScreen.cs @@ -32,6 +32,8 @@ namespace osu.Game.Screens.Import private TextFlowContainer currentFileText; private OsuScrollContainer fileNameScroll; + private TriangleButton importButton; + private const float duration = 300; private const float button_height = 50; private const float button_vertical_margin = 15; @@ -100,7 +102,7 @@ namespace osu.Game.Screens.Import }, }, }, - new TriangleButton + importButton = new TriangleButton { Text = "Import", Anchor = Anchor.BottomCentre, @@ -109,14 +111,7 @@ namespace osu.Game.Screens.Import Height = button_height, Width = 0.9f, Margin = new MarginPadding { Vertical = button_vertical_margin }, - Action = () => - { - var d = currentFile.Value?.FullName; - if (d != null) - startImport(d); - else - currentFileText.FlashColour(Color4.Red, 500); - } + Action = () => startImport(currentFile.Value?.FullName) } } } @@ -125,21 +120,22 @@ namespace osu.Game.Screens.Import fileNameScroll.ScrollContent.Anchor = Anchor.Centre; fileNameScroll.ScrollContent.Origin = Anchor.Centre; - currentFile.BindValueChanged(updateFileSelectionText, true); - currentDirectory.BindValueChanged(onCurrentDirectoryChanged); + currentFile.BindValueChanged(fileChanged, true); + currentDirectory.BindValueChanged(directoryChanged); currentDirectory.BindTo(fileSelector.CurrentPath); currentFile.BindTo(fileSelector.CurrentFile); } - private void onCurrentDirectoryChanged(ValueChangedEvent v) + private void directoryChanged(ValueChangedEvent v) { currentFile.Value = null; } - private void updateFileSelectionText(ValueChangedEvent v) + private void fileChanged(ValueChangedEvent selectedFile) { - currentFileText.Text = v.NewValue?.Name ?? "Select a file"; + importButton.Enabled.Value = selectedFile.NewValue != null; + currentFileText.Text = selectedFile.NewValue?.Name ?? "Select a file"; } public override void OnEntering(IScreen last) From cafe81ab97cdf5565a168fe9c757b833bec64b35 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 15 Dec 2020 12:52:38 +0900 Subject: [PATCH 248/818] Further refactoring and bindable simplification --- osu.Game/Screens/Import/FileImportScreen.cs | 37 +++++++++------------ 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/osu.Game/Screens/Import/FileImportScreen.cs b/osu.Game/Screens/Import/FileImportScreen.cs index da6d3f6622..72032c82b8 100644 --- a/osu.Game/Screens/Import/FileImportScreen.cs +++ b/osu.Game/Screens/Import/FileImportScreen.cs @@ -12,11 +12,10 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Platform; using osu.Framework.Screens; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; using osuTK; -using osu.Game.Graphics.UserInterface; -using osu.Game.Graphics.Containers; -using osuTK.Graphics; namespace osu.Game.Screens.Import { @@ -24,13 +23,9 @@ namespace osu.Game.Screens.Import { public override bool HideOverlaysOnEnter => true; - private readonly Bindable currentFile = new Bindable(); - private readonly IBindable currentDirectory = new Bindable(); - private FileSelector fileSelector; private Container contentContainer; private TextFlowContainer currentFileText; - private OsuScrollContainer fileNameScroll; private TriangleButton importButton; @@ -47,9 +42,6 @@ namespace osu.Game.Screens.Import [BackgroundDependencyLoader(true)] private void load(Storage storage) { - storage.GetStorageForDirectory("imports"); - var originalPath = storage.GetFullPath("imports", true); - InternalChild = contentContainer = new Container { Masking = true, @@ -65,7 +57,7 @@ namespace osu.Game.Screens.Import Colour = colours.GreySeafoamDark, RelativeSizeAxes = Axes.Both, }, - fileSelector = new FileSelector(originalPath, game.HandledExtensions.ToArray()) + fileSelector = new FileSelector(validFileExtensions: game.HandledExtensions.ToArray()) { RelativeSizeAxes = Axes.Both, Width = 0.65f @@ -87,7 +79,7 @@ namespace osu.Game.Screens.Import { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Bottom = button_height + button_vertical_margin * 2 }, - Child = fileNameScroll = new OsuScrollContainer + Child = new OsuScrollContainer { RelativeSizeAxes = Axes.Both, Anchor = Anchor.TopCentre, @@ -100,6 +92,11 @@ namespace osu.Game.Screens.Import Origin = Anchor.Centre, TextAnchor = Anchor.Centre }, + ScrollContent = + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } }, }, importButton = new TriangleButton @@ -111,25 +108,21 @@ namespace osu.Game.Screens.Import Height = button_height, Width = 0.9f, Margin = new MarginPadding { Vertical = button_vertical_margin }, - Action = () => startImport(currentFile.Value?.FullName) + Action = () => startImport(fileSelector.CurrentFile.Value?.FullName) } } } } }; - fileNameScroll.ScrollContent.Anchor = Anchor.Centre; - fileNameScroll.ScrollContent.Origin = Anchor.Centre; - currentFile.BindValueChanged(fileChanged, true); - currentDirectory.BindValueChanged(directoryChanged); - - currentDirectory.BindTo(fileSelector.CurrentPath); - currentFile.BindTo(fileSelector.CurrentFile); + fileSelector.CurrentFile.BindValueChanged(fileChanged, true); + fileSelector.CurrentPath.BindValueChanged(directoryChanged); } - private void directoryChanged(ValueChangedEvent v) + private void directoryChanged(ValueChangedEvent _) { - currentFile.Value = null; + // this should probably be done by the selector itself, but let's do it here for now. + fileSelector.CurrentFile.Value = null; } private void fileChanged(ValueChangedEvent selectedFile) From db4f2d5ffbbcf3ba90a4aa5c563314c40d5bacb1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 15 Dec 2020 12:52:45 +0900 Subject: [PATCH 249/818] Refresh view after import succeeds --- osu.Game/Screens/Import/FileImportScreen.cs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Import/FileImportScreen.cs b/osu.Game/Screens/Import/FileImportScreen.cs index 72032c82b8..3646dc819c 100644 --- a/osu.Game/Screens/Import/FileImportScreen.cs +++ b/osu.Game/Screens/Import/FileImportScreen.cs @@ -154,16 +154,17 @@ namespace osu.Game.Screens.Import if (string.IsNullOrEmpty(path)) return; - if (!File.Exists(path)) - { - currentFileText.Text = "No such file"; - currentFileText.FlashColour(colours.Red, duration); - return; - } + Task.Factory.StartNew(async () => await game.Import(path), TaskCreationOptions.LongRunning) + .ContinueWith(_ => + { + // some files will be deleted after successful import, so we want to refresh the view. - string[] paths = { path }; - - Task.Factory.StartNew(() => game.Import(paths), TaskCreationOptions.LongRunning); + Schedule(() => + { + // should probably be exposed as a refresh method. + fileSelector.CurrentPath.TriggerChange(); + }); + }); } } } From 0d4ac2f748b968cf972c8270b1dc2138857ced96 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 15 Dec 2020 12:57:28 +0900 Subject: [PATCH 250/818] Refresh view after import completes --- osu.Game/Screens/Import/FileImportScreen.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Import/FileImportScreen.cs b/osu.Game/Screens/Import/FileImportScreen.cs index 3646dc819c..5f7888d743 100644 --- a/osu.Game/Screens/Import/FileImportScreen.cs +++ b/osu.Game/Screens/Import/FileImportScreen.cs @@ -154,17 +154,17 @@ namespace osu.Game.Screens.Import if (string.IsNullOrEmpty(path)) return; - Task.Factory.StartNew(async () => await game.Import(path), TaskCreationOptions.LongRunning) - .ContinueWith(_ => - { - // some files will be deleted after successful import, so we want to refresh the view. + Task.Factory.StartNew(async () => + { + await game.Import(path); - Schedule(() => - { - // should probably be exposed as a refresh method. - fileSelector.CurrentPath.TriggerChange(); - }); + // some files will be deleted after successful import, so we want to refresh the view. + Schedule(() => + { + // should probably be exposed as a refresh method. + fileSelector.CurrentPath.TriggerChange(); }); + }, TaskCreationOptions.LongRunning); } } } From 10ee8ed8e86c392015a29ffa81c8798101066fd0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 15 Dec 2020 13:02:38 +0900 Subject: [PATCH 251/818] Reorder functions and simplify transform logic --- osu.Game/Screens/Import/FileImportScreen.cs | 34 ++++++++++----------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/osu.Game/Screens/Import/FileImportScreen.cs b/osu.Game/Screens/Import/FileImportScreen.cs index 5f7888d743..329623e03a 100644 --- a/osu.Game/Screens/Import/FileImportScreen.cs +++ b/osu.Game/Screens/Import/FileImportScreen.cs @@ -119,6 +119,22 @@ namespace osu.Game.Screens.Import fileSelector.CurrentPath.BindValueChanged(directoryChanged); } + public override void OnEntering(IScreen last) + { + base.OnEntering(last); + + contentContainer.ScaleTo(0.95f).ScaleTo(1, duration, Easing.OutQuint); + this.FadeInFromZero(duration); + } + + public override bool OnExiting(IScreen next) + { + contentContainer.ScaleTo(0.95f, duration, Easing.OutQuint); + this.FadeOut(duration, Easing.OutQuint); + + return base.OnExiting(next); + } + private void directoryChanged(ValueChangedEvent _) { // this should probably be done by the selector itself, but let's do it here for now. @@ -131,24 +147,6 @@ namespace osu.Game.Screens.Import currentFileText.Text = selectedFile.NewValue?.Name ?? "Select a file"; } - public override void OnEntering(IScreen last) - { - base.OnEntering(last); - - contentContainer.FadeOut().Then().ScaleTo(0.95f) - .Then() - .ScaleTo(1, duration, Easing.OutQuint) - .FadeIn(duration); - } - - public override bool OnExiting(IScreen next) - { - contentContainer.ScaleTo(0.95f, duration, Easing.OutQuint); - this.FadeOut(duration, Easing.OutQuint); - - return base.OnExiting(next); - } - private void startImport(string path) { if (string.IsNullOrEmpty(path)) From f0e6b6eaf81c540382f70dc39dcdad249acaad42 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Date: Tue, 15 Dec 2020 11:09:09 +0700 Subject: [PATCH 252/818] sort by ruleset id first then star diff --- osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs index 88c15776cd..1567e18caa 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs @@ -152,7 +152,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels } else { - foreach (var b in SetInfo.Beatmaps.OrderBy(beatmap => beatmap.StarDifficulty)) + foreach (var b in SetInfo.Beatmaps.OrderBy(beatmap => beatmap.Ruleset.ID).ThenBy(beatmap => beatmap.StarDifficulty)) icons.Add(new DifficultyIcon(b)); } From 8bdef0ff5573276ea4f2a7f6a833bd81098b84d9 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 15 Dec 2020 13:18:41 +0900 Subject: [PATCH 253/818] Code quality fix Co-authored-by: Dean Herbert --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index af7786c3fd..b235b59609 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -157,7 +157,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty speedValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate); // Scale the speed value with accuracy and OD - speedValue *= (.95 + Math.Pow(Attributes.OverallDifficulty, 2) / 750) * Math.Pow(accuracy, (14.5 - Math.Max(Attributes.OverallDifficulty, 8)) / 2); + speedValue *= (0.95 + Math.Pow(Attributes.OverallDifficulty, 2) / 750) * Math.Pow(accuracy, (14.5 - Math.Max(Attributes.OverallDifficulty, 8)) / 2); // Scale the speed value with # of 50s to punish doubletapping. speedValue *= Math.Pow(0.98, countMeh < totalHits / 500.0 ? 0 : countMeh - totalHits / 500.0); From 8362ad37e34c10d2ada7bd4468d851d6debdb45a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 15 Dec 2020 15:22:14 +0900 Subject: [PATCH 254/818] Bring up-to-date with code changes --- .../Gameplay/TestSceneInGameLeaderboard.cs | 26 ++++++++----------- osu.Game/Screens/Play/InGameScoreContainer.cs | 3 ++- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneInGameLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneInGameLeaderboard.cs index 0019212dfa..4944a6477d 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneInGameLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneInGameLeaderboard.cs @@ -1,12 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; -using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Testing; using osu.Game.Screens.Play; using osu.Game.Users; using osuTK; @@ -16,12 +15,6 @@ namespace osu.Game.Tests.Visual.Gameplay [TestFixture] public class TestSceneInGameLeaderboard : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(InGameLeaderboard), - typeof(InGameScoreContainer), - }; - private readonly TestInGameLeaderboard leaderboard; private readonly BindableDouble playerScore; @@ -35,18 +28,21 @@ namespace osu.Game.Tests.Visual.Gameplay RelativeSizeAxes = Axes.X, PlayerCurrentScore = { BindTarget = playerScore = new BindableDouble(1222333) } }); + } + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("reset leaderboard", () => + { + leaderboard.ClearScores(); + playerScore.Value = 1222333; + }); AddStep("add player user", () => leaderboard.PlayerUser = new User { Username = "You" }); AddSliderStep("set player score", 50, 5000000, 1222333, v => playerScore.Value = v); } - [SetUp] - public void SetUp() - { - leaderboard.ClearScores(); - playerScore.Value = 1222333; - } - [Test] public void TestPlayerScore() { diff --git a/osu.Game/Screens/Play/InGameScoreContainer.cs b/osu.Game/Screens/Play/InGameScoreContainer.cs index f548b3de3f..d3ec132351 100644 --- a/osu.Game/Screens/Play/InGameScoreContainer.cs +++ b/osu.Game/Screens/Play/InGameScoreContainer.cs @@ -6,6 +6,7 @@ using System.Linq; using Humanizer; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; @@ -210,7 +211,7 @@ namespace osu.Game.Screens.Play }, scoreText = new GlowingSpriteText { - GlowColour = OsuColour.FromHex(@"83ccfa"), + GlowColour = Color4Extensions.FromHex(@"83ccfa"), Font = OsuFont.Numeric.With(size: 14), } } From dd5572b20a9336562e30bcf14d4bc444d8d0f3be Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 15 Dec 2020 15:26:20 +0900 Subject: [PATCH 255/818] Remove unnecessary methods and event --- osu.Game/Screens/Play/InGameScoreContainer.cs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Play/InGameScoreContainer.cs b/osu.Game/Screens/Play/InGameScoreContainer.cs index d3ec132351..693020fc4c 100644 --- a/osu.Game/Screens/Play/InGameScoreContainer.cs +++ b/osu.Game/Screens/Play/InGameScoreContainer.cs @@ -19,12 +19,6 @@ namespace osu.Game.Screens.Play { public class InGameScoreContainer : FillFlowContainer { - /// - /// Called once an item's score has changed. - /// Useful for doing calculations on what score to show or hide next. (scrolling system) - /// - public Action OnScoreChange; - /// /// Whether to declare a new position for un-positioned players. /// Must be disabled for online leaderboards with top 50 scores only. @@ -101,9 +95,16 @@ namespace osu.Game.Screens.Play private void updateScores() { - reorderPositions(); + var orderedByScore = this.OrderByDescending(i => i.TotalScore).ToList(); + var orderedPositions = this.Select(i => this.Any(item => item.InitialPosition.HasValue) ? i.InitialPosition : i.ScorePosition).OrderByDescending(p => p.HasValue).ThenBy(p => p).ToList(); - OnScoreChange?.Invoke(); + for (int i = 0; i < Count; i++) + { + int newPosition = orderedPositions[i] ?? maxPosition + 1; + + SetLayoutPosition(orderedByScore[i], newPosition); + orderedByScore[i].ScorePosition = DeclareNewPosition ? newPosition : orderedPositions[i]; + } } } From 8b68ccc0ff96f2bd0ae9b3076cdc19e2bff3ac26 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 15 Dec 2020 15:27:26 +0900 Subject: [PATCH 256/818] Rename class and move inside HUD namespace --- .../Gameplay/TestSceneInGameLeaderboard.cs | 34 +++--- .../Screens/Play/HUD/GameplayLeaderboard.cs | 104 ++++++++++++++++++ .../GameplayLeaderboardScore.cs} | 100 +---------------- osu.Game/Screens/Play/InGameLeaderboard.cs | 42 ------- 4 files changed, 122 insertions(+), 158 deletions(-) create mode 100644 osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs rename osu.Game/Screens/Play/{InGameScoreContainer.cs => HUD/GameplayLeaderboardScore.cs} (52%) delete mode 100644 osu.Game/Screens/Play/InGameLeaderboard.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneInGameLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneInGameLeaderboard.cs index 4944a6477d..657eb7e8ba 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneInGameLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneInGameLeaderboard.cs @@ -6,27 +6,27 @@ using NUnit.Framework; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Testing; -using osu.Game.Screens.Play; +using osu.Game.Screens.Play.HUD; using osu.Game.Users; using osuTK; namespace osu.Game.Tests.Visual.Gameplay { [TestFixture] - public class TestSceneInGameLeaderboard : OsuTestScene + public class TestSceneGameplayLeaderboard : OsuTestScene { - private readonly TestInGameLeaderboard leaderboard; - private readonly BindableDouble playerScore; + private readonly TestGameplayLeaderboard leaderboard; - public TestSceneInGameLeaderboard() + private readonly BindableDouble playerScore = new BindableDouble(); + + public TestSceneGameplayLeaderboard() { - Add(leaderboard = new TestInGameLeaderboard + Add(leaderboard = new TestGameplayLeaderboard { Anchor = Anchor.Centre, Origin = Anchor.Centre, Scale = new Vector2(2), RelativeSizeAxes = Axes.X, - PlayerCurrentScore = { BindTarget = playerScore = new BindableDouble(1222333) } }); } @@ -35,11 +35,11 @@ namespace osu.Game.Tests.Visual.Gameplay { AddStep("reset leaderboard", () => { - leaderboard.ClearScores(); + leaderboard.Clear(); playerScore.Value = 1222333; }); - AddStep("add player user", () => leaderboard.PlayerUser = new User { Username = "You" }); + AddStep("add player user", () => leaderboard.AddRealTimePlayer(playerScore, new User { Username = "You" })); AddSliderStep("set player score", 50, 5000000, 1222333, v => playerScore.Value = v); } @@ -49,8 +49,8 @@ namespace osu.Game.Tests.Visual.Gameplay var player2Score = new BindableDouble(1234567); var player3Score = new BindableDouble(1111111); - AddStep("add player 2", () => leaderboard.AddDummyPlayer(player2Score, "Player 2")); - AddStep("add player 3", () => leaderboard.AddDummyPlayer(player3Score, "Player 3")); + AddStep("add player 2", () => leaderboard.AddRealTimePlayer(player2Score, new User { Username = "Player 2" })); + AddStep("add player 3", () => leaderboard.AddRealTimePlayer(player3Score, new User { Username = "Player 3" })); AddAssert("is player 2 position #1", () => leaderboard.CheckPositionByUsername("Player 2", 1)); AddAssert("is player position #2", () => leaderboard.CheckPositionByUsername("You", 2)); @@ -67,18 +67,14 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("is player 2 position #3", () => leaderboard.CheckPositionByUsername("Player 2", 3)); } - private class TestInGameLeaderboard : InGameLeaderboard + private class TestGameplayLeaderboard : GameplayLeaderboard { - public void ClearScores() => ScoresContainer.RemoveAll(s => s.User.Username != PlayerUser.Username); - - public bool CheckPositionByUsername(string username, int? estimatedPosition) + public bool CheckPositionByUsername(string username, int? expectedPosition) { - var scoreItem = ScoresContainer.FirstOrDefault(i => i.User.Username == username); + var scoreItem = this.FirstOrDefault(i => i.User.Username == username); - return scoreItem != null && scoreItem.ScorePosition == estimatedPosition; + return scoreItem != null && scoreItem.ScorePosition == expectedPosition; } - - public void AddDummyPlayer(BindableDouble currentScore, string username) => ScoresContainer.AddRealTimePlayer(currentScore, new User { Username = username }); } } } diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs new file mode 100644 index 0000000000..9e1b9a3958 --- /dev/null +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs @@ -0,0 +1,104 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Scoring; +using osu.Game.Users; +using osuTK; + +namespace osu.Game.Screens.Play.HUD +{ + public class GameplayLeaderboard : FillFlowContainer + { + /// + /// Whether to declare a new position for un-positioned players. + /// Must be disabled for online leaderboards with top 50 scores only. + /// + public bool DeclareNewPosition = true; + + public GameplayLeaderboard() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Direction = FillDirection.Vertical; + Spacing = new Vector2(2.5f); + LayoutDuration = 500; + LayoutEasing = Easing.OutQuint; + } + + /// + /// Adds a real-time player score item whose score is updated via a . + /// + /// The bindable current score of the player. + /// The player user. + /// Returns the drawable score item of that player. + public GameplayLeaderboardScore AddRealTimePlayer(BindableDouble currentScore, User user = null) + { + if (currentScore == null) + return null; + + var scoreItem = addScore(currentScore.Value, user); + currentScore.ValueChanged += s => scoreItem.TotalScore = s.NewValue; + + return scoreItem; + } + + /// + /// Adds a score item based off a with an initial position. + /// + /// The score info to use for this item. + /// The initial position of this item. + /// Returns the drawable score item of that player. + public GameplayLeaderboardScore AddScore(ScoreInfo score, int? initialPosition = null) => score != null ? addScore(score.TotalScore, score.User, initialPosition) : null; + + private int maxPosition => this.Max(i => this.Any(item => item.InitialPosition.HasValue) ? i.InitialPosition : i.ScorePosition) ?? 0; + + private GameplayLeaderboardScore addScore(double totalScore, User user = null, int? position = null) + { + var scoreItem = new GameplayLeaderboardScore(position) + { + User = user, + TotalScore = totalScore, + OnScoreChange = updateScores, + }; + + Add(scoreItem); + SetLayoutPosition(scoreItem, position ?? maxPosition + 1); + + reorderPositions(); + + return scoreItem; + } + + private void reorderPositions() + { + var orderedByScore = this.OrderByDescending(i => i.TotalScore).ToList(); + var orderedPositions = this.Select(i => this.Any(item => item.InitialPosition.HasValue) ? i.InitialPosition : i.ScorePosition).OrderByDescending(p => p.HasValue).ThenBy(p => p).ToList(); + + for (int i = 0; i < Count; i++) + { + int newPosition = orderedPositions[i] ?? maxPosition + 1; + + SetLayoutPosition(orderedByScore[i], newPosition); + orderedByScore[i].ScorePosition = DeclareNewPosition ? newPosition : orderedPositions[i]; + } + } + + private void updateScores() + { + var orderedByScore = this.OrderByDescending(i => i.TotalScore).ToList(); + var orderedPositions = this.Select(i => this.Any(item => item.InitialPosition.HasValue) ? i.InitialPosition : i.ScorePosition).OrderByDescending(p => p.HasValue).ThenBy(p => p).ToList(); + + for (int i = 0; i < Count; i++) + { + int newPosition = orderedPositions[i] ?? maxPosition + 1; + + SetLayoutPosition(orderedByScore[i], newPosition); + orderedByScore[i].ScorePosition = DeclareNewPosition ? newPosition : orderedPositions[i]; + } + } + } +} diff --git a/osu.Game/Screens/Play/InGameScoreContainer.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs similarity index 52% rename from osu.Game/Screens/Play/InGameScoreContainer.cs rename to osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index 693020fc4c..3af7345ae1 100644 --- a/osu.Game/Screens/Play/InGameScoreContainer.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -2,113 +2,19 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Linq; using Humanizer; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osu.Game.Scoring; using osu.Game.Users; using osuTK; -namespace osu.Game.Screens.Play +namespace osu.Game.Screens.Play.HUD { - public class InGameScoreContainer : FillFlowContainer - { - /// - /// Whether to declare a new position for un-positioned players. - /// Must be disabled for online leaderboards with top 50 scores only. - /// - public bool DeclareNewPosition = true; - - public InGameScoreContainer() - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - Direction = FillDirection.Vertical; - Spacing = new Vector2(2.5f); - LayoutDuration = 500; - LayoutEasing = Easing.OutQuint; - } - - /// - /// Adds a real-time player score item whose score is updated via a . - /// - /// The bindable current score of the player. - /// The player user. - /// Returns the drawable score item of that player. - public InGameScoreItem AddRealTimePlayer(BindableDouble currentScore, User user = null) - { - if (currentScore == null) - return null; - - var scoreItem = addScore(currentScore.Value, user); - currentScore.ValueChanged += s => scoreItem.TotalScore = s.NewValue; - - return scoreItem; - } - - /// - /// Adds a score item based off a with an initial position. - /// - /// The score info to use for this item. - /// The initial position of this item. - /// Returns the drawable score item of that player. - public InGameScoreItem AddScore(ScoreInfo score, int? initialPosition = null) => score != null ? addScore(score.TotalScore, score.User, initialPosition) : null; - - private int maxPosition => this.Max(i => this.Any(item => item.InitialPosition.HasValue) ? i.InitialPosition : i.ScorePosition) ?? 0; - - private InGameScoreItem addScore(double totalScore, User user = null, int? position = null) - { - var scoreItem = new InGameScoreItem(position) - { - User = user, - TotalScore = totalScore, - OnScoreChange = updateScores, - }; - - Add(scoreItem); - SetLayoutPosition(scoreItem, position ?? maxPosition + 1); - - reorderPositions(); - - return scoreItem; - } - - private void reorderPositions() - { - var orderedByScore = this.OrderByDescending(i => i.TotalScore).ToList(); - var orderedPositions = this.Select(i => this.Any(item => item.InitialPosition.HasValue) ? i.InitialPosition : i.ScorePosition).OrderByDescending(p => p.HasValue).ThenBy(p => p).ToList(); - - for (int i = 0; i < Count; i++) - { - int newPosition = orderedPositions[i] ?? maxPosition + 1; - - SetLayoutPosition(orderedByScore[i], newPosition); - orderedByScore[i].ScorePosition = DeclareNewPosition ? newPosition : orderedPositions[i]; - } - } - - private void updateScores() - { - var orderedByScore = this.OrderByDescending(i => i.TotalScore).ToList(); - var orderedPositions = this.Select(i => this.Any(item => item.InitialPosition.HasValue) ? i.InitialPosition : i.ScorePosition).OrderByDescending(p => p.HasValue).ThenBy(p => p).ToList(); - - for (int i = 0; i < Count; i++) - { - int newPosition = orderedPositions[i] ?? maxPosition + 1; - - SetLayoutPosition(orderedByScore[i], newPosition); - orderedByScore[i].ScorePosition = DeclareNewPosition ? newPosition : orderedPositions[i]; - } - } - } - - public class InGameScoreItem : CompositeDrawable + public class GameplayLeaderboardScore : CompositeDrawable { private readonly OsuSpriteText positionText, positionSymbol, userString; private readonly GlowingSpriteText scoreText; @@ -159,7 +65,7 @@ namespace osu.Game.Screens.Play } } - public InGameScoreItem(int? initialPosition) + public GameplayLeaderboardScore(int? initialPosition) { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; diff --git a/osu.Game/Screens/Play/InGameLeaderboard.cs b/osu.Game/Screens/Play/InGameLeaderboard.cs deleted file mode 100644 index c8f5cf5fd7..0000000000 --- a/osu.Game/Screens/Play/InGameLeaderboard.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Users; - -namespace osu.Game.Screens.Play -{ - public class InGameLeaderboard : CompositeDrawable - { - protected readonly InGameScoreContainer ScoresContainer; - - public readonly BindableDouble PlayerCurrentScore = new BindableDouble(); - - private bool playerItemCreated; - private User playerUser; - - public User PlayerUser - { - get => playerUser; - set - { - playerUser = value; - - if (playerItemCreated) - return; - - ScoresContainer.AddRealTimePlayer(PlayerCurrentScore, playerUser); - playerItemCreated = true; - } - } - - public InGameLeaderboard() - { - AutoSizeAxes = Axes.Y; - - InternalChild = ScoresContainer = new InGameScoreContainer(); - } - } -} From b5ab400ad78ac968b4b5a629bb5aacab39dd63eb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 15 Dec 2020 15:44:43 +0900 Subject: [PATCH 257/818] Fix test filename not matching updated class name --- ...InGameLeaderboard.cs => TestSceneGameplayLeaderboard.cs} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename osu.Game.Tests/Visual/Gameplay/{TestSceneInGameLeaderboard.cs => TestSceneGameplayLeaderboard.cs} (89%) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneInGameLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs similarity index 89% rename from osu.Game.Tests/Visual/Gameplay/TestSceneInGameLeaderboard.cs rename to osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs index 657eb7e8ba..df970c1c46 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneInGameLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs @@ -39,7 +39,7 @@ namespace osu.Game.Tests.Visual.Gameplay playerScore.Value = 1222333; }); - AddStep("add player user", () => leaderboard.AddRealTimePlayer(playerScore, new User { Username = "You" })); + AddStep("add player user", () => leaderboard.AddPlayer(playerScore, new User { Username = "You" })); AddSliderStep("set player score", 50, 5000000, 1222333, v => playerScore.Value = v); } @@ -49,8 +49,8 @@ namespace osu.Game.Tests.Visual.Gameplay var player2Score = new BindableDouble(1234567); var player3Score = new BindableDouble(1111111); - AddStep("add player 2", () => leaderboard.AddRealTimePlayer(player2Score, new User { Username = "Player 2" })); - AddStep("add player 3", () => leaderboard.AddRealTimePlayer(player3Score, new User { Username = "Player 3" })); + AddStep("add player 2", () => leaderboard.AddPlayer(player2Score, new User { Username = "Player 2" })); + AddStep("add player 3", () => leaderboard.AddPlayer(player3Score, new User { Username = "Player 3" })); AddAssert("is player 2 position #1", () => leaderboard.CheckPositionByUsername("Player 2", 1)); AddAssert("is player position #2", () => leaderboard.CheckPositionByUsername("You", 2)); From e37089af5e188e7dbb769ba4d891a25b48420412 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 15 Dec 2020 15:44:56 +0900 Subject: [PATCH 258/818] Further code cleanup --- .../Screens/Play/HUD/GameplayLeaderboard.cs | 65 ++++--------------- .../Play/HUD/GameplayLeaderboardScore.cs | 5 +- 2 files changed, 15 insertions(+), 55 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs index 9e1b9a3958..6ee654fb9e 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs @@ -1,11 +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; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Scoring; using osu.Game.Users; using osuTK; @@ -13,52 +14,33 @@ namespace osu.Game.Screens.Play.HUD { public class GameplayLeaderboard : FillFlowContainer { - /// - /// Whether to declare a new position for un-positioned players. - /// Must be disabled for online leaderboards with top 50 scores only. - /// - public bool DeclareNewPosition = true; - public GameplayLeaderboard() { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; + Direction = FillDirection.Vertical; + Spacing = new Vector2(2.5f); - LayoutDuration = 500; + + LayoutDuration = 250; LayoutEasing = Easing.OutQuint; } /// - /// Adds a real-time player score item whose score is updated via a . + /// Adds a player to the leaderboard. /// /// The bindable current score of the player. - /// The player user. - /// Returns the drawable score item of that player. - public GameplayLeaderboardScore AddRealTimePlayer(BindableDouble currentScore, User user = null) + /// The player. + public void AddPlayer([NotNull] BindableDouble currentScore, [NotNull] User user) { - if (currentScore == null) - return null; - var scoreItem = addScore(currentScore.Value, user); currentScore.ValueChanged += s => scoreItem.TotalScore = s.NewValue; - - return scoreItem; } - /// - /// Adds a score item based off a with an initial position. - /// - /// The score info to use for this item. - /// The initial position of this item. - /// Returns the drawable score item of that player. - public GameplayLeaderboardScore AddScore(ScoreInfo score, int? initialPosition = null) => score != null ? addScore(score.TotalScore, score.User, initialPosition) : null; - - private int maxPosition => this.Max(i => this.Any(item => item.InitialPosition.HasValue) ? i.InitialPosition : i.ScorePosition) ?? 0; - - private GameplayLeaderboardScore addScore(double totalScore, User user = null, int? position = null) + private GameplayLeaderboardScore addScore(double totalScore, User user) { - var scoreItem = new GameplayLeaderboardScore(position) + var scoreItem = new GameplayLeaderboardScore { User = user, TotalScore = totalScore, @@ -66,38 +48,19 @@ namespace osu.Game.Screens.Play.HUD }; Add(scoreItem); - SetLayoutPosition(scoreItem, position ?? maxPosition + 1); - - reorderPositions(); + updateScores(); return scoreItem; } - private void reorderPositions() - { - var orderedByScore = this.OrderByDescending(i => i.TotalScore).ToList(); - var orderedPositions = this.Select(i => this.Any(item => item.InitialPosition.HasValue) ? i.InitialPosition : i.ScorePosition).OrderByDescending(p => p.HasValue).ThenBy(p => p).ToList(); - - for (int i = 0; i < Count; i++) - { - int newPosition = orderedPositions[i] ?? maxPosition + 1; - - SetLayoutPosition(orderedByScore[i], newPosition); - orderedByScore[i].ScorePosition = DeclareNewPosition ? newPosition : orderedPositions[i]; - } - } - private void updateScores() { var orderedByScore = this.OrderByDescending(i => i.TotalScore).ToList(); - var orderedPositions = this.Select(i => this.Any(item => item.InitialPosition.HasValue) ? i.InitialPosition : i.ScorePosition).OrderByDescending(p => p.HasValue).ThenBy(p => p).ToList(); for (int i = 0; i < Count; i++) { - int newPosition = orderedPositions[i] ?? maxPosition + 1; - - SetLayoutPosition(orderedByScore[i], newPosition); - orderedByScore[i].ScorePosition = DeclareNewPosition ? newPosition : orderedPositions[i]; + SetLayoutPosition(orderedByScore[i], i); + orderedByScore[i].ScorePosition = i + 1; } } } diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index 3af7345ae1..4c75f422c9 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -22,7 +22,6 @@ namespace osu.Game.Screens.Play.HUD public Action OnScoreChange; private int? scorePosition; - public int? InitialPosition; public int? ScorePosition { @@ -65,7 +64,7 @@ namespace osu.Game.Screens.Play.HUD } } - public GameplayLeaderboardScore(int? initialPosition) + public GameplayLeaderboardScore() { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; @@ -125,8 +124,6 @@ namespace osu.Game.Screens.Play.HUD }, }, }; - - InitialPosition = ScorePosition = initialPosition; } [BackgroundDependencyLoader] From ea6c196f81e72ac967da65218c69f8bf37ce2f9a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 15 Dec 2020 16:03:18 +0900 Subject: [PATCH 259/818] Remove unused using statement --- osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs index 6ee654fb9e..e53c56b390 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.Linq; using JetBrains.Annotations; using osu.Framework.Bindables; From d0668192aad1d44fea6516ce0d108146177a7737 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 15 Dec 2020 21:25:53 +0100 Subject: [PATCH 260/818] Ensure stream disposal & move entire operation inside task --- osu.Android/OsuGameActivity.cs | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/osu.Android/OsuGameActivity.cs b/osu.Android/OsuGameActivity.cs index cf0179e2ac..e801c2ca6e 100644 --- a/osu.Android/OsuGameActivity.cs +++ b/osu.Android/OsuGameActivity.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Diagnostics; using System.IO; using System.Threading.Tasks; using Android.App; @@ -62,7 +61,7 @@ namespace osu.Android } } - private void handleImportFromUri(Uri uri) + private void handleImportFromUri(Uri uri) => Task.Factory.StartNew(async () => { // there are more performant overloads of this method, but this one is the most backwards-compatible // (dates back to API 1). @@ -74,23 +73,16 @@ namespace osu.Android cursor.MoveToFirst(); var filenameColumn = cursor.GetColumnIndex(OpenableColumns.DisplayName); - - var stream = ContentResolver.OpenInputStream(uri); string filename = cursor.GetString(filenameColumn); - if (stream != null) - Task.Factory.StartNew(() => runImport(stream, filename), TaskCreationOptions.LongRunning); - } - - private Task runImport(Stream stream, string filename) - { // SharpCompress requires archive streams to be seekable, which the stream opened by // OpenInputStream() seems to not necessarily be. // copy to an arbitrary-access memory stream to be able to proceed with the import. var copy = new MemoryStream(); - stream.CopyTo(copy); + using (var stream = ContentResolver.OpenInputStream(uri)) + await stream.CopyToAsync(copy); - return game.Import(copy, filename); - } + await game.Import(copy, filename); + }, TaskCreationOptions.LongRunning); } } From 4cd290af11eb9918848929b717e38d681bac7a3d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 16 Dec 2020 12:31:05 +0900 Subject: [PATCH 261/818] Split server interfaces --- .../IMultiplayerLoungeServer.cs | 20 ++++++++ .../IMultiplayerRoomServer.cs | 51 +++++++++++++++++++ .../RealtimeMultiplayer/IMultiplayerServer.cs | 50 +----------------- 3 files changed, 73 insertions(+), 48 deletions(-) create mode 100644 osu.Game/Online/RealtimeMultiplayer/IMultiplayerLoungeServer.cs create mode 100644 osu.Game/Online/RealtimeMultiplayer/IMultiplayerRoomServer.cs diff --git a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerLoungeServer.cs b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerLoungeServer.cs new file mode 100644 index 0000000000..eecb61bcb0 --- /dev/null +++ b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerLoungeServer.cs @@ -0,0 +1,20 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Threading.Tasks; + +namespace osu.Game.Online.RealtimeMultiplayer +{ + /// + /// Interface for an out-of-room multiplayer server. + /// + public interface IMultiplayerLoungeServer + { + /// + /// Request to join a multiplayer room. + /// + /// The databased room ID. + /// If the user is already in the requested (or another) room. + Task JoinRoom(long roomId); + } +} diff --git a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerRoomServer.cs b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerRoomServer.cs new file mode 100644 index 0000000000..f1b3daf7d3 --- /dev/null +++ b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerRoomServer.cs @@ -0,0 +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 System.Threading.Tasks; + +namespace osu.Game.Online.RealtimeMultiplayer +{ + /// + /// Interface for an in-room multiplayer server. + /// + public interface IMultiplayerRoomServer + { + /// + /// Request to leave the currently joined room. + /// + /// If the user is not in a room. + Task LeaveRoom(); + + /// + /// Transfer the host of the currently joined room to another user in the room. + /// + /// The new user which is to become host. + /// A user other than the current host is attempting to transfer host. + /// If the user is not in a room. + Task TransferHost(long userId); + + /// + /// As the host, update the settings of the currently joined room. + /// + /// The new settings to apply. + /// A user other than the current host is attempting to transfer host. + /// If the user is not in a room. + Task ChangeSettings(MultiplayerRoomSettings settings); + + /// + /// Change the local user state in the currently joined room. + /// + /// The proposed new state. + /// If the state change requested is not valid, given the previous state or room state. + /// If the user is not in a room. + Task ChangeState(MultiplayerUserState newState); + + /// + /// As the host of a room, start the match. + /// + /// A user other than the current host is attempting to start the game. + /// If the user is not in a room. + /// If an attempt to start the game occurs when the game's (or users') state disallows it. + Task StartMatch(); + } +} diff --git a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs index e2e7b6b991..1d093af743 100644 --- a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs +++ b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs @@ -1,58 +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.Threading.Tasks; - namespace osu.Game.Online.RealtimeMultiplayer { /// - /// An interface defining the spectator server instance. + /// An interface defining the multiplayer server instance. /// - public interface IMultiplayerServer + public interface IMultiplayerServer : IMultiplayerRoomServer, IMultiplayerLoungeServer { - /// - /// Request to join a multiplayer room. - /// - /// The databased room ID. - /// If the user is already in the requested (or another) room. - Task JoinRoom(long roomId); - - /// - /// Request to leave the currently joined room. - /// - /// If the user is not in a room. - Task LeaveRoom(); - - /// - /// Transfer the host of the currently joined room to another user in the room. - /// - /// The new user which is to become host. - /// A user other than the current host is attempting to transfer host. - /// If the user is not in a room. - Task TransferHost(long userId); - - /// - /// As the host, update the settings of the currently joined room. - /// - /// The new settings to apply. - /// A user other than the current host is attempting to transfer host. - /// If the user is not in a room. - Task ChangeSettings(MultiplayerRoomSettings settings); - - /// - /// Change the local user state in the currently joined room. - /// - /// The proposed new state. - /// If the state change requested is not valid, given the previous state or room state. - /// If the user is not in a room. - Task ChangeState(MultiplayerUserState newState); - - /// - /// As the host of a room, start the match. - /// - /// A user other than the current host is attempting to start the game. - /// If the user is not in a room. - /// If an attempt to start the game occurs when the game's (or users') state disallows it. - Task StartMatch(); } } From 31fe28b8b326f70180c31c94c5e46a36a95b99b2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 16 Dec 2020 12:32:53 +0900 Subject: [PATCH 262/818] Remove IStatefulMultiplayerClient --- .../IStatefulMultiplayerClient.cs | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 osu.Game/Online/RealtimeMultiplayer/IStatefulMultiplayerClient.cs diff --git a/osu.Game/Online/RealtimeMultiplayer/IStatefulMultiplayerClient.cs b/osu.Game/Online/RealtimeMultiplayer/IStatefulMultiplayerClient.cs deleted file mode 100644 index 578092662a..0000000000 --- a/osu.Game/Online/RealtimeMultiplayer/IStatefulMultiplayerClient.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -#nullable enable - -namespace osu.Game.Online.RealtimeMultiplayer -{ - /// - /// A multiplayer client which maintains local room and user state. Also provides a proxy to access the . - /// - public interface IStatefulMultiplayerClient : IMultiplayerClient, IMultiplayerServer - { - MultiplayerUserState State { get; } - - MultiplayerRoom? Room { get; } - } -} From 84a077078967aeb6bf8a5f526bdb7cd1288c876d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 16 Dec 2020 15:35:07 +0900 Subject: [PATCH 263/818] Change frame header to use dictionary for compatibility --- osu.Game/Online/Spectator/FrameHeader.cs | 10 ++++++---- osu.Game/Online/Spectator/StatisticPair.cs | 23 ---------------------- 2 files changed, 6 insertions(+), 27 deletions(-) delete mode 100644 osu.Game/Online/Spectator/StatisticPair.cs diff --git a/osu.Game/Online/Spectator/FrameHeader.cs b/osu.Game/Online/Spectator/FrameHeader.cs index 9b6cc615a4..f2dd30a002 100644 --- a/osu.Game/Online/Spectator/FrameHeader.cs +++ b/osu.Game/Online/Spectator/FrameHeader.cs @@ -4,8 +4,9 @@ #nullable enable using System; -using System.Linq; +using System.Collections.Generic; using Newtonsoft.Json; +using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; namespace osu.Game.Online.Spectator @@ -17,7 +18,7 @@ namespace osu.Game.Online.Spectator public int MaxCombo { get; set; } - public StatisticPair[] Statistics { get; set; } + public Dictionary Statistics { get; set; } /// /// Construct header summary information from a point-in-time reference to a score which is actively being played. @@ -28,11 +29,12 @@ namespace osu.Game.Online.Spectator Combo = score.Combo; MaxCombo = score.MaxCombo; - Statistics = score.Statistics.Select(kvp => new StatisticPair(kvp.Key, kvp.Value)).ToArray(); + // copy for safety + Statistics = new Dictionary(score.Statistics); } [JsonConstructor] - public FrameHeader(int combo, int maxCombo, StatisticPair[] statistics) + public FrameHeader(int combo, int maxCombo, Dictionary statistics) { Combo = combo; MaxCombo = maxCombo; diff --git a/osu.Game/Online/Spectator/StatisticPair.cs b/osu.Game/Online/Spectator/StatisticPair.cs deleted file mode 100644 index 793143a64c..0000000000 --- a/osu.Game/Online/Spectator/StatisticPair.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Online.Spectator -{ - [Serializable] - public struct StatisticPair - { - public HitResult Result; - public int Count; - - public StatisticPair(HitResult result, int count) - { - Result = result; - Count = count; - } - - public override string ToString() => $"{Result}=>{Count}"; - } -} From 72d296f4128066787bf0c098d52422aefe363622 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 16 Dec 2020 16:19:53 +0900 Subject: [PATCH 264/818] Add received timestamp and basic xmldoc for header class --- osu.Game/Online/Spectator/FrameHeader.cs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Spectator/FrameHeader.cs b/osu.Game/Online/Spectator/FrameHeader.cs index f2dd30a002..b4988fecf9 100644 --- a/osu.Game/Online/Spectator/FrameHeader.cs +++ b/osu.Game/Online/Spectator/FrameHeader.cs @@ -14,12 +14,26 @@ namespace osu.Game.Online.Spectator [Serializable] public class FrameHeader { + /// + /// The current combo of the score. + /// public int Combo { get; set; } + /// + /// The maximum combo achieved up to the current point in time. + /// public int MaxCombo { get; set; } + /// + /// Cumulative hit statistics. + /// public Dictionary Statistics { get; set; } + /// + /// The time at which this frame was received by the server. + /// + public DateTimeOffset ReceivedTime { get; set; } + /// /// Construct header summary information from a point-in-time reference to a score which is actively being played. /// @@ -34,11 +48,12 @@ namespace osu.Game.Online.Spectator } [JsonConstructor] - public FrameHeader(int combo, int maxCombo, Dictionary statistics) + public FrameHeader(int combo, int maxCombo, Dictionary statistics, DateTimeOffset receivedTime) { Combo = combo; MaxCombo = maxCombo; Statistics = statistics; + ReceivedTime = receivedTime; } } } From fb795f6bfdbb0638c21ae33e2f02e025ca38d780 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 16 Dec 2020 13:00:19 +0900 Subject: [PATCH 265/818] Add initial hook-up to spectator backend --- .../Visual/Gameplay/TestSceneSpectator.cs | 2 +- .../TestSceneSpectatorDrivenLeaderboard.cs | 74 +++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorDrivenLeaderboard.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs index 3e5b561a6f..1fdff99da6 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs @@ -232,7 +232,7 @@ namespace osu.Game.Tests.Visual.Gameplay public class TestSpectatorStreamingClient : SpectatorStreamingClient { - public readonly User StreamingUser = new User { Id = 1234, Username = "Test user" }; + public readonly User StreamingUser = new User { Id = 55, Username = "Test user" }; public new BindableList PlayingUsers => (BindableList)base.PlayingUsers; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorDrivenLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorDrivenLeaderboard.cs new file mode 100644 index 0000000000..7211755ba6 --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorDrivenLeaderboard.cs @@ -0,0 +1,74 @@ +// 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.Bindables; +using osu.Framework.Graphics; +using osu.Game.Database; +using osu.Game.Online.API; +using osu.Game.Online.Spectator; +using osu.Game.Rulesets.Osu.Scoring; +using osu.Game.Screens.Play.HUD; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public class TestSceneSpectatorDrivenLeaderboard : OsuTestScene + { + [Cached(typeof(SpectatorStreamingClient))] + private TestSceneSpectator.TestSpectatorStreamingClient testSpectatorStreamingClient = new TestSceneSpectator.TestSpectatorStreamingClient(); + + // used just to show beatmap card for the time being. + protected override bool UseOnlineAPI => true; + + [SetUp] + public void SetUp() => Schedule(() => + { + OsuScoreProcessor scoreProcessor; + + testSpectatorStreamingClient.StartPlay(55); + + Children = new Drawable[] + { + scoreProcessor = new OsuScoreProcessor(), + new MultiplayerGameplayLeaderboard(scoreProcessor) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } + }; + }); + } + + public class MultiplayerGameplayLeaderboard : GameplayLeaderboard + { + private readonly OsuScoreProcessor scoreProcessor; + + public MultiplayerGameplayLeaderboard(OsuScoreProcessor scoreProcessor) + { + this.scoreProcessor = scoreProcessor; + + AddPlayer(new BindableDouble(), new GuestUser()); + } + + [Resolved] + private SpectatorStreamingClient streamingClient { get; set; } + + [Resolved] + private UserLookupCache userLookupCache { get; set; } + + [BackgroundDependencyLoader] + private void load() + { + Console.WriteLine("got here"); + + foreach (var user in streamingClient.PlayingUsers) + { + streamingClient.WatchUser(user); + var resolvedUser = userLookupCache.GetUserAsync(user).Result; + AddPlayer(new BindableDouble(), resolvedUser); + } + } + } +} From 2954218897e90eb54a692063c0e4af5cf882d28c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 16 Dec 2020 15:25:20 +0900 Subject: [PATCH 266/818] Add method to ScoreProcessor to calculate score and accuracy from statistics --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 39 +++++++++++++++++++-- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 499673619f..b4f29d7a6e 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -68,7 +68,12 @@ namespace osu.Game.Rulesets.Scoring private readonly double comboPortion; private int maxAchievableCombo; - private double maxBaseScore; + + /// + /// The maximum achievable base score. + /// + public double MaxBaseScore { get; private set; } + private double rollingMaxBaseScore; private double baseScore; @@ -196,7 +201,7 @@ namespace osu.Game.Rulesets.Scoring private double getScore(ScoringMode mode) { return GetScore(mode, maxAchievableCombo, - maxBaseScore > 0 ? baseScore / maxBaseScore : 0, + MaxBaseScore > 0 ? baseScore / MaxBaseScore : 0, maxAchievableCombo > 0 ? (double)HighestCombo.Value / maxAchievableCombo : 1, scoreResultCounts); } @@ -227,6 +232,34 @@ namespace osu.Game.Rulesets.Scoring } } + /// + /// Given a minimal set of inputs, return the computed score and accuracy for the tracked beatmap / mods combination. + /// + /// The to compute the total score in. + /// The maximum combo achievable in the beatmap. + /// Statistics to be used for calculating accuracy, bonus score, etc. + /// The computed score and accuracy for provided inputs. + public (double score, double accuracy) GetScoreAndAccuracy(ScoringMode mode, int maxCombo, Dictionary statistics) + { + // calculate base score from statistics pairs + int computedBaseScore = 0; + + foreach (var pair in statistics) + { + if (!pair.Key.AffectsAccuracy()) + continue; + + computedBaseScore += Judgement.ToNumericResult(pair.Key) * pair.Value; + } + + double accuracy = MaxBaseScore > 0 ? computedBaseScore / MaxBaseScore : 0; + double comboRatio = maxAchievableCombo > 0 ? (double)HighestCombo.Value / maxAchievableCombo : 1; + + double score = GetScore(mode, maxAchievableCombo, accuracy, comboRatio, scoreResultCounts); + + return (score, accuracy); + } + private double getBonusScore(Dictionary statistics) => statistics.GetOrDefault(HitResult.SmallBonus) * Judgement.SMALL_BONUS_SCORE + statistics.GetOrDefault(HitResult.LargeBonus) * Judgement.LARGE_BONUS_SCORE; @@ -266,7 +299,7 @@ namespace osu.Game.Rulesets.Scoring if (storeResults) { maxAchievableCombo = HighestCombo.Value; - maxBaseScore = baseScore; + MaxBaseScore = baseScore; } baseScore = 0; From d009a0be51defe7890002ca501d666c40e29fbd9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 16 Dec 2020 15:25:27 +0900 Subject: [PATCH 267/818] Move class to final location --- .../HUD/MultiplayerGameplayLeaderboard.cs | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs new file mode 100644 index 0000000000..1d9fdd9ded --- /dev/null +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -0,0 +1,98 @@ +// 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 JetBrains.Annotations; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Game.Configuration; +using osu.Game.Database; +using osu.Game.Online.API; +using osu.Game.Online.Spectator; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Screens.Play.HUD +{ + public class MultiplayerGameplayLeaderboard : GameplayLeaderboard + { + private readonly ScoreProcessor scoreProcessor; + + /// + /// Construct a new leaderboard. + /// + /// A score processor instance to handle score calculation for scores of users in the match. + public MultiplayerGameplayLeaderboard(ScoreProcessor scoreProcessor) + { + this.scoreProcessor = scoreProcessor; + + AddPlayer(new BindableDouble(), new GuestUser()); + } + + [Resolved] + private SpectatorStreamingClient streamingClient { get; set; } + + [Resolved] + private UserLookupCache userLookupCache { get; set; } + + private readonly Dictionary userScores = new Dictionary(); + + private Bindable scoringMode; + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + streamingClient.OnNewFrames += handleIncomingFrames; + + foreach (var user in streamingClient.PlayingUsers) + { + streamingClient.WatchUser(user); + var resolvedUser = userLookupCache.GetUserAsync(user).Result; + + var trackedUser = new TrackedUserData(); + + userScores[user] = trackedUser; + AddPlayer(trackedUser.Score, resolvedUser); + } + + scoringMode = config.GetBindable(OsuSetting.ScoreDisplayMode); + scoringMode.BindValueChanged(updateAllScores, true); + } + + private void updateAllScores(ValueChangedEvent mode) + { + foreach (var trackedData in userScores.Values) + trackedData.UpdateScore(scoreProcessor, mode.NewValue); + } + + private void handleIncomingFrames(int userId, FrameDataBundle bundle) + { + if (userScores.TryGetValue(userId, out var trackedData)) + { + trackedData.LastHeader = bundle.Header; + trackedData.UpdateScore(scoreProcessor, scoringMode.Value); + } + } + + private class TrackedUserData + { + public readonly BindableDouble Score = new BindableDouble(); + + public readonly BindableDouble Accuracy = new BindableDouble(); + + public readonly BindableInt CurrentCombo = new BindableInt(); + + [CanBeNull] + public FrameHeader LastHeader; + + public void UpdateScore(ScoreProcessor processor, ScoringMode mode) + { + if (LastHeader == null) + return; + + (Score.Value, Accuracy.Value) = processor.GetScoreAndAccuracy(mode, LastHeader.MaxCombo, LastHeader.Statistics.ToDictionary(s => s.Result, s => s.Count)); + CurrentCombo.Value = LastHeader.Combo; + } + } + } +} From 09d0ceb7668e115170437c1d2063ed883897ed02 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 16 Dec 2020 15:26:03 +0900 Subject: [PATCH 268/818] Add testing setup to get a better visual idea of how scoreboard will work fixup! Add method to ScoreProcessor to calculate score and accuracy from statistics --- .../TestSceneSpectatorDrivenLeaderboard.cs | 109 +++++++++++++----- .../HUD/MultiplayerGameplayLeaderboard.cs | 4 +- 2 files changed, 85 insertions(+), 28 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorDrivenLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorDrivenLeaderboard.cs index 7211755ba6..e7ebf6e92c 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorDrivenLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorDrivenLeaderboard.cs @@ -2,14 +2,18 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; +using System.Threading.Tasks; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Game.Database; -using osu.Game.Online.API; +using osu.Framework.Utils; using osu.Game.Online.Spectator; +using osu.Game.Replays.Legacy; using osu.Game.Rulesets.Osu.Scoring; +using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; using osu.Game.Screens.Play.HUD; namespace osu.Game.Tests.Visual.Gameplay @@ -17,7 +21,7 @@ namespace osu.Game.Tests.Visual.Gameplay public class TestSceneSpectatorDrivenLeaderboard : OsuTestScene { [Cached(typeof(SpectatorStreamingClient))] - private TestSceneSpectator.TestSpectatorStreamingClient testSpectatorStreamingClient = new TestSceneSpectator.TestSpectatorStreamingClient(); + private TestMultiplayerStreaming streamingClient = new TestMultiplayerStreaming(16); // used just to show beatmap card for the time being. protected override bool UseOnlineAPI => true; @@ -27,7 +31,7 @@ namespace osu.Game.Tests.Visual.Gameplay { OsuScoreProcessor scoreProcessor; - testSpectatorStreamingClient.StartPlay(55); + streamingClient.Start(Beatmap.Value.BeatmapInfo.OnlineBeatmapID ?? 0); Children = new Drawable[] { @@ -38,37 +42,90 @@ namespace osu.Game.Tests.Visual.Gameplay Origin = Anchor.Centre, } }; + + Beatmap.Value = CreateWorkingBeatmap(Ruleset.Value); + + var playable = Beatmap.Value.GetPlayableBeatmap(Ruleset.Value); + + scoreProcessor.ApplyBeatmap(playable); }); - } - public class MultiplayerGameplayLeaderboard : GameplayLeaderboard - { - private readonly OsuScoreProcessor scoreProcessor; - - public MultiplayerGameplayLeaderboard(OsuScoreProcessor scoreProcessor) + [Test] + public void TestScoreUpdates() { - this.scoreProcessor = scoreProcessor; - - AddPlayer(new BindableDouble(), new GuestUser()); + AddRepeatStep("update state", () => streamingClient.RandomlyUpdateState(), 100); } - [Resolved] - private SpectatorStreamingClient streamingClient { get; set; } - - [Resolved] - private UserLookupCache userLookupCache { get; set; } - - [BackgroundDependencyLoader] - private void load() + public class TestMultiplayerStreaming : SpectatorStreamingClient { - Console.WriteLine("got here"); + public new BindableList PlayingUsers => (BindableList)base.PlayingUsers; - foreach (var user in streamingClient.PlayingUsers) + private readonly int totalUsers; + + public TestMultiplayerStreaming(int totalUsers) { - streamingClient.WatchUser(user); - var resolvedUser = userLookupCache.GetUserAsync(user).Result; - AddPlayer(new BindableDouble(), resolvedUser); + this.totalUsers = totalUsers; } + + public void Start(int beatmapId) + { + for (int i = 0; i < totalUsers; i++) + { + ((ISpectatorClient)this).UserBeganPlaying(i, new SpectatorState + { + BeatmapID = beatmapId, + RulesetID = 0, + }); + } + } + + private readonly Dictionary lastHeaders = new Dictionary(); + + public void RandomlyUpdateState() + { + foreach (var userId in PlayingUsers) + { + if (RNG.Next(0, 1) == 1) + continue; + + if (!lastHeaders.TryGetValue(userId, out var header)) + { + lastHeaders[userId] = header = new FrameHeader(new ScoreInfo + { + Statistics = new Dictionary(new[] + { + new KeyValuePair(HitResult.Miss, 0), + new KeyValuePair(HitResult.Meh, 0), + new KeyValuePair(HitResult.Great, 0) + }) + }); + } + + switch (RNG.Next(0, 3)) + { + case 0: + header.Combo = 0; + header.Statistics[HitResult.Miss]++; + break; + + case 1: + header.Combo++; + header.MaxCombo = Math.Max(header.MaxCombo, header.Combo); + header.Statistics[HitResult.Meh]++; + break; + + default: + header.Combo++; + header.MaxCombo = Math.Max(header.MaxCombo, header.Combo); + header.Statistics[HitResult.Great]++; + break; + } + + ((ISpectatorClient)this).UserSentFrames(userId, new FrameDataBundle(header, Array.Empty())); + } + } + + protected override Task Connect() => Task.CompletedTask; } } } diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index 1d9fdd9ded..b3621f42c2 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -90,7 +89,8 @@ namespace osu.Game.Screens.Play.HUD if (LastHeader == null) return; - (Score.Value, Accuracy.Value) = processor.GetScoreAndAccuracy(mode, LastHeader.MaxCombo, LastHeader.Statistics.ToDictionary(s => s.Result, s => s.Count)); + (Score.Value, Accuracy.Value) = processor.GetScoreAndAccuracy(mode, LastHeader.MaxCombo, LastHeader.Statistics); + CurrentCombo.Value = LastHeader.Combo; } } From c1ba0f46425e756177e6c383d7f2e42b9d176c80 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 16 Dec 2020 15:53:05 +0900 Subject: [PATCH 269/818] Use a local lookup cache for better usernames --- .../TestSceneSpectatorDrivenLeaderboard.cs | 7 +++++ .../TestSceneCurrentlyPlayingDisplay.cs | 28 ++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorDrivenLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorDrivenLeaderboard.cs index e7ebf6e92c..647d57b5fe 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorDrivenLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorDrivenLeaderboard.cs @@ -9,12 +9,14 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Utils; +using osu.Game.Database; using osu.Game.Online.Spectator; using osu.Game.Replays.Legacy; using osu.Game.Rulesets.Osu.Scoring; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Play.HUD; +using osu.Game.Tests.Visual.Online; namespace osu.Game.Tests.Visual.Gameplay { @@ -23,6 +25,9 @@ namespace osu.Game.Tests.Visual.Gameplay [Cached(typeof(SpectatorStreamingClient))] private TestMultiplayerStreaming streamingClient = new TestMultiplayerStreaming(16); + [Cached(typeof(UserLookupCache))] + private UserLookupCache lookupCache = new TestSceneCurrentlyPlayingDisplay.TestUserLookupCache(); + // used just to show beatmap card for the time being. protected override bool UseOnlineAPI => true; @@ -35,6 +40,8 @@ namespace osu.Game.Tests.Visual.Gameplay Children = new Drawable[] { + streamingClient, + lookupCache, scoreProcessor = new OsuScoreProcessor(), new MultiplayerGameplayLeaderboard(scoreProcessor) { diff --git a/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs index 7eba64f418..4f0ca67e64 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs @@ -69,8 +69,34 @@ namespace osu.Game.Tests.Visual.Online internal class TestUserLookupCache : UserLookupCache { + private static readonly string[] usernames = + { + "fieryrage", + "Kerensa", + "MillhioreF", + "Player01", + "smoogipoo", + "Ephemeral", + "BTMC", + "Cilvery", + "m980", + "HappyStick", + "LittleEndu", + "frenzibyte", + "Zallius", + "BanchoBot", + "rocketminer210", + "pishifat" + }; + + private int id; + protected override Task ComputeValueAsync(int lookup, CancellationToken token = default) - => Task.FromResult(new User { Username = "peppy", Id = 2 }); + => Task.FromResult(new User + { + Id = id++, + Username = usernames[id % usernames.Length], + }); } } } From 6e2131c164ad2a2d45ec7bab53d58deae3279de7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 16 Dec 2020 15:53:15 +0900 Subject: [PATCH 270/818] Don't track local user score in any special way --- osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index b3621f42c2..f8fce0825f 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -7,7 +7,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Configuration; using osu.Game.Database; -using osu.Game.Online.API; using osu.Game.Online.Spectator; using osu.Game.Rulesets.Scoring; @@ -24,8 +23,6 @@ namespace osu.Game.Screens.Play.HUD public MultiplayerGameplayLeaderboard(ScoreProcessor scoreProcessor) { this.scoreProcessor = scoreProcessor; - - AddPlayer(new BindableDouble(), new GuestUser()); } [Resolved] From 6bce587b599c04194589c8f3bd2716a44f886ea3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 16 Dec 2020 16:05:46 +0900 Subject: [PATCH 271/818] Pass users in via constructor and correctly unbind on disposal --- .../TestSceneSpectatorDrivenLeaderboard.cs | 3 +- .../HUD/MultiplayerGameplayLeaderboard.cs | 32 ++++++++++++++++--- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorDrivenLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorDrivenLeaderboard.cs index 647d57b5fe..ffd02d247a 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorDrivenLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorDrivenLeaderboard.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using NUnit.Framework; using osu.Framework.Allocation; @@ -43,7 +44,7 @@ namespace osu.Game.Tests.Visual.Gameplay streamingClient, lookupCache, scoreProcessor = new OsuScoreProcessor(), - new MultiplayerGameplayLeaderboard(scoreProcessor) + new MultiplayerGameplayLeaderboard(scoreProcessor, streamingClient.PlayingUsers.ToArray()) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index f8fce0825f..93f258c507 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -16,13 +16,22 @@ namespace osu.Game.Screens.Play.HUD { private readonly ScoreProcessor scoreProcessor; + private readonly int[] userIds; + + private readonly Dictionary userScores = new Dictionary(); + /// /// Construct a new leaderboard. /// /// A score processor instance to handle score calculation for scores of users in the match. - public MultiplayerGameplayLeaderboard(ScoreProcessor scoreProcessor) + /// IDs of all users in this match. + public MultiplayerGameplayLeaderboard(ScoreProcessor scoreProcessor, int[] userIds) { + // todo: this will eventually need to be created per user to support different mod combinations. this.scoreProcessor = scoreProcessor; + + // todo: this will likely be passed in as User instances. + this.userIds = userIds; } [Resolved] @@ -31,8 +40,6 @@ namespace osu.Game.Screens.Play.HUD [Resolved] private UserLookupCache userLookupCache { get; set; } - private readonly Dictionary userScores = new Dictionary(); - private Bindable scoringMode; [BackgroundDependencyLoader] @@ -40,9 +47,11 @@ namespace osu.Game.Screens.Play.HUD { streamingClient.OnNewFrames += handleIncomingFrames; - foreach (var user in streamingClient.PlayingUsers) + foreach (var user in userIds) { streamingClient.WatchUser(user); + + // probably won't be required in the final implementation. var resolvedUser = userLookupCache.GetUserAsync(user).Result; var trackedUser = new TrackedUserData(); @@ -70,6 +79,21 @@ namespace osu.Game.Screens.Play.HUD } } + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (streamingClient != null) + { + foreach (var user in userIds) + { + streamingClient.StopWatchingUser(user); + } + + streamingClient.OnNewFrames -= handleIncomingFrames; + } + } + private class TrackedUserData { public readonly BindableDouble Score = new BindableDouble(); From a01bb3d5a3a7ee33650de9b305addf61a1daa16f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 16 Dec 2020 16:08:44 +0900 Subject: [PATCH 272/818] Better limit bindable exposure of data class --- osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs | 2 +- .../Play/HUD/MultiplayerGameplayLeaderboard.cs | 16 +++++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs index e53c56b390..3934a99221 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs @@ -31,7 +31,7 @@ namespace osu.Game.Screens.Play.HUD /// /// The bindable current score of the player. /// The player. - public void AddPlayer([NotNull] BindableDouble currentScore, [NotNull] User user) + public void AddPlayer([NotNull] IBindableNumber currentScore, [NotNull] User user) { var scoreItem = addScore(currentScore.Value, user); currentScore.ValueChanged += s => scoreItem.TotalScore = s.NewValue; diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index 93f258c507..33bcf06aa7 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -96,11 +96,17 @@ namespace osu.Game.Screens.Play.HUD private class TrackedUserData { - public readonly BindableDouble Score = new BindableDouble(); + public IBindableNumber Score => score; - public readonly BindableDouble Accuracy = new BindableDouble(); + private readonly BindableDouble score = new BindableDouble(); - public readonly BindableInt CurrentCombo = new BindableInt(); + public IBindableNumber Accuracy => accuracy; + + private readonly BindableDouble accuracy = new BindableDouble(); + + public IBindableNumber CurrentCombo => currentCombo; + + private readonly BindableInt currentCombo = new BindableInt(); [CanBeNull] public FrameHeader LastHeader; @@ -110,9 +116,9 @@ namespace osu.Game.Screens.Play.HUD if (LastHeader == null) return; - (Score.Value, Accuracy.Value) = processor.GetScoreAndAccuracy(mode, LastHeader.MaxCombo, LastHeader.Statistics); + (score.Value, accuracy.Value) = processor.GetScoreAndAccuracy(mode, LastHeader.MaxCombo, LastHeader.Statistics); - CurrentCombo.Value = LastHeader.Combo; + currentCombo.Value = LastHeader.Combo; } } } From cda3bd2017180391786a46ef771c4b3e04dc4cb1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 16 Dec 2020 16:22:47 +0900 Subject: [PATCH 273/818] Rename test scene to match tested class name --- ...erboard.cs => TestSceneMultiplayerGameplayLeaderboard.cs} | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) rename osu.Game.Tests/Visual/Gameplay/{TestSceneSpectatorDrivenLeaderboard.cs => TestSceneMultiplayerGameplayLeaderboard.cs} (96%) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorDrivenLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs similarity index 96% rename from osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorDrivenLeaderboard.cs rename to osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs index ffd02d247a..78f9db57be 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorDrivenLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs @@ -21,7 +21,7 @@ using osu.Game.Tests.Visual.Online; namespace osu.Game.Tests.Visual.Gameplay { - public class TestSceneSpectatorDrivenLeaderboard : OsuTestScene + public class TestSceneMultiplayerGameplayLeaderboard : OsuTestScene { [Cached(typeof(SpectatorStreamingClient))] private TestMultiplayerStreaming streamingClient = new TestMultiplayerStreaming(16); @@ -29,9 +29,6 @@ namespace osu.Game.Tests.Visual.Gameplay [Cached(typeof(UserLookupCache))] private UserLookupCache lookupCache = new TestSceneCurrentlyPlayingDisplay.TestUserLookupCache(); - // used just to show beatmap card for the time being. - protected override bool UseOnlineAPI => true; - [SetUp] public void SetUp() => Schedule(() => { From c0021847686aa1c3022abef0dabaa858b2cc8464 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 16 Dec 2020 18:08:16 +0900 Subject: [PATCH 274/818] Clamp osu!mania's HitPosition offset to match osu-stable implementation Closes #11184. --- osu.Game/Skinning/LegacyManiaSkinDecoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacyManiaSkinDecoder.cs b/osu.Game/Skinning/LegacyManiaSkinDecoder.cs index 6a04a95040..0a1de461ea 100644 --- a/osu.Game/Skinning/LegacyManiaSkinDecoder.cs +++ b/osu.Game/Skinning/LegacyManiaSkinDecoder.cs @@ -87,7 +87,7 @@ namespace osu.Game.Skinning break; case "HitPosition": - currentConfig.HitPosition = (480 - float.Parse(pair.Value, CultureInfo.InvariantCulture)) * LegacyManiaSkinConfiguration.POSITION_SCALE_FACTOR; + currentConfig.HitPosition = (480 - Math.Clamp(float.Parse(pair.Value, CultureInfo.InvariantCulture), 240, 480)) * LegacyManiaSkinConfiguration.POSITION_SCALE_FACTOR; break; case "LightPosition": From 95711578415c95d6fcc52498e88c55da0712d40f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 16 Dec 2020 20:04:28 +0900 Subject: [PATCH 275/818] Use ints for userid parameters --- osu.Game/Online/RealtimeMultiplayer/IMultiplayerClient.cs | 4 ++-- osu.Game/Online/RealtimeMultiplayer/IMultiplayerRoomServer.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerClient.cs b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerClient.cs index c162f066d4..9af0047137 100644 --- a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerClient.cs +++ b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerClient.cs @@ -32,7 +32,7 @@ namespace osu.Game.Online.RealtimeMultiplayer /// Signal that the host of the room has changed. /// /// The user ID of the new host. - Task HostChanged(long userId); + Task HostChanged(int userId); /// /// Signals that the settings for this room have changed. @@ -45,7 +45,7 @@ namespace osu.Game.Online.RealtimeMultiplayer /// /// The ID of the user performing a state change. /// The new state of the user. - Task UserStateChanged(long userId, MultiplayerUserState state); + Task UserStateChanged(int userId, MultiplayerUserState state); /// /// Signals that a match is to be started. This will *only* be sent to clients which are to begin loading at this point. diff --git a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerRoomServer.cs b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerRoomServer.cs index f1b3daf7d3..12dfe481c4 100644 --- a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerRoomServer.cs +++ b/osu.Game/Online/RealtimeMultiplayer/IMultiplayerRoomServer.cs @@ -22,7 +22,7 @@ namespace osu.Game.Online.RealtimeMultiplayer /// The new user which is to become host. /// A user other than the current host is attempting to transfer host. /// If the user is not in a room. - Task TransferHost(long userId); + Task TransferHost(int userId); /// /// As the host, update the settings of the currently joined room. From 41d8b84bd7c7aff099593a034c8dd71ee1d0ae13 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 17 Dec 2020 15:47:20 +0900 Subject: [PATCH 276/818] Revert MaxBaseScore to being a private field (no longe required to be public) --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index b4f29d7a6e..f4850d3325 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Scoring /// /// The maximum achievable base score. /// - public double MaxBaseScore { get; private set; } + private double maxBaseScore; private double rollingMaxBaseScore; private double baseScore; @@ -201,7 +201,7 @@ namespace osu.Game.Rulesets.Scoring private double getScore(ScoringMode mode) { return GetScore(mode, maxAchievableCombo, - MaxBaseScore > 0 ? baseScore / MaxBaseScore : 0, + maxBaseScore > 0 ? baseScore / maxBaseScore : 0, maxAchievableCombo > 0 ? (double)HighestCombo.Value / maxAchievableCombo : 1, scoreResultCounts); } @@ -252,7 +252,7 @@ namespace osu.Game.Rulesets.Scoring computedBaseScore += Judgement.ToNumericResult(pair.Key) * pair.Value; } - double accuracy = MaxBaseScore > 0 ? computedBaseScore / MaxBaseScore : 0; + double accuracy = maxBaseScore > 0 ? computedBaseScore / maxBaseScore : 0; double comboRatio = maxAchievableCombo > 0 ? (double)HighestCombo.Value / maxAchievableCombo : 1; double score = GetScore(mode, maxAchievableCombo, accuracy, comboRatio, scoreResultCounts); @@ -299,7 +299,7 @@ namespace osu.Game.Rulesets.Scoring if (storeResults) { maxAchievableCombo = HighestCombo.Value; - MaxBaseScore = baseScore; + maxBaseScore = baseScore; } baseScore = 0; From de9c21e7d19e265fbf776430d2708f860ed16bbf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 17 Dec 2020 15:48:53 +0900 Subject: [PATCH 277/818] Tenatively mark leaderboard class as LongRunningLoad until final integration --- osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index 33bcf06aa7..b2dc47ce61 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -12,6 +12,7 @@ using osu.Game.Rulesets.Scoring; namespace osu.Game.Screens.Play.HUD { + [LongRunningLoad] public class MultiplayerGameplayLeaderboard : GameplayLeaderboard { private readonly ScoreProcessor scoreProcessor; From cc3dddf59fa432bad0fc2d36fe8810853223f48f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 17 Dec 2020 16:02:06 +0900 Subject: [PATCH 278/818] Fix test scene crashing on second run of SetUp Also correctly support LongRunningLoad --- ...TestSceneMultiplayerGameplayLeaderboard.cs | 53 +++++++++++++------ 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs index 78f9db57be..0c8d8ca165 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs @@ -9,6 +9,8 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Database; using osu.Game.Online.Spectator; @@ -29,31 +31,48 @@ namespace osu.Game.Tests.Visual.Gameplay [Cached(typeof(UserLookupCache))] private UserLookupCache lookupCache = new TestSceneCurrentlyPlayingDisplay.TestUserLookupCache(); - [SetUp] - public void SetUp() => Schedule(() => + private MultiplayerGameplayLeaderboard leaderboard; + + protected override Container Content { get; } = new Container { RelativeSizeAxes = Axes.Both }; + + public TestSceneMultiplayerGameplayLeaderboard() { - OsuScoreProcessor scoreProcessor; - - streamingClient.Start(Beatmap.Value.BeatmapInfo.OnlineBeatmapID ?? 0); - - Children = new Drawable[] + base.Content.Children = new Drawable[] { streamingClient, lookupCache, - scoreProcessor = new OsuScoreProcessor(), - new MultiplayerGameplayLeaderboard(scoreProcessor, streamingClient.PlayingUsers.ToArray()) + Content + }; + } + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("create leaderboard", () => + { + OsuScoreProcessor scoreProcessor; + Beatmap.Value = CreateWorkingBeatmap(Ruleset.Value); + + var playable = Beatmap.Value.GetPlayableBeatmap(Ruleset.Value); + + streamingClient.Start(Beatmap.Value.BeatmapInfo.OnlineBeatmapID ?? 0); + + Children = new Drawable[] + { + scoreProcessor = new OsuScoreProcessor(), + }; + + scoreProcessor.ApplyBeatmap(playable); + + LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(scoreProcessor, streamingClient.PlayingUsers.ToArray()) { Anchor = Anchor.Centre, Origin = Anchor.Centre, - } - }; + }, Add); + }); - Beatmap.Value = CreateWorkingBeatmap(Ruleset.Value); - - var playable = Beatmap.Value.GetPlayableBeatmap(Ruleset.Value); - - scoreProcessor.ApplyBeatmap(playable); - }); + AddUntilStep("wait for load", () => leaderboard.IsLoaded); + } [Test] public void TestScoreUpdates() From f13683dc908e2f42b19d2a1fb6b05f808eb01b36 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 17 Dec 2020 16:05:41 +0900 Subject: [PATCH 279/818] Correctly account for max combo of the input, rather than the global --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index f4850d3325..f5fb918ba3 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -253,7 +253,7 @@ namespace osu.Game.Rulesets.Scoring } double accuracy = maxBaseScore > 0 ? computedBaseScore / maxBaseScore : 0; - double comboRatio = maxAchievableCombo > 0 ? (double)HighestCombo.Value / maxAchievableCombo : 1; + double comboRatio = maxAchievableCombo > 0 ? (double)maxCombo / maxAchievableCombo : 1; double score = GetScore(mode, maxAchievableCombo, accuracy, comboRatio, scoreResultCounts); From 81b0db040179821af0928ae8389a85f5666f3f4a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 17 Dec 2020 16:14:41 +0900 Subject: [PATCH 280/818] Remove double construction of empty replay object --- osu.Game/Screens/Play/Player.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index f40a7ccda8..18950a9d0a 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -21,7 +21,6 @@ using osu.Game.Graphics.Containers; using osu.Game.IO.Archives; using osu.Game.Online.API; using osu.Game.Overlays; -using osu.Game.Replays; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; @@ -164,7 +163,7 @@ namespace osu.Game.Screens.Play /// protected virtual void PrepareReplay() { - DrawableRuleset.SetRecordTarget(recordingScore = new Score { Replay = new Replay() }); + DrawableRuleset.SetRecordTarget(recordingScore = new Score()); ScoreProcessor.NewJudgement += result => ScoreProcessor.PopulateScore(recordingScore.ScoreInfo); } From 3ff70d331adf2e87451b3bcb8306c39140d9a7e3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 17 Dec 2020 16:17:13 +0900 Subject: [PATCH 281/818] Mark recordingScore as nullable --- osu.Game/Screens/Play/Player.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 18950a9d0a..a54f9fc047 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; @@ -156,6 +157,7 @@ namespace osu.Game.Screens.Play PrepareReplay(); } + [CanBeNull] private Score recordingScore; /// From 78ce6f1cd21e81858f7f923dab907e52dde020e9 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 17 Dec 2020 13:30:55 +0300 Subject: [PATCH 282/818] Add friends list to API providers --- osu.Game/Online/API/APIAccess.cs | 14 ++++++++++++-- osu.Game/Online/API/DummyAPIAccess.cs | 7 +++++++ osu.Game/Online/API/IAPIProvider.cs | 6 ++++++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index b916339a53..f084f6f2c7 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -41,6 +41,8 @@ namespace osu.Game.Online.API public Bindable LocalUser { get; } = new Bindable(createGuestUser()); + public BindableList Friends { get; } = new BindableList(); + public Bindable Activity { get; } = new Bindable(); protected bool HasLogin => authentication.Token.Value != null || (!string.IsNullOrEmpty(ProvidedUsername) && !string.IsNullOrEmpty(password)); @@ -143,6 +145,10 @@ namespace osu.Game.Online.API failureCount = 0; + var friendsReq = new GetFriendsRequest(); + friendsReq.Success += f => Friends.AddRange(f); + handleRequest(friendsReq); + //we're connected! state.Value = APIState.Online; }; @@ -352,8 +358,12 @@ namespace osu.Game.Online.API password = null; authentication.Clear(); - // Scheduled prior to state change such that the state changed event is invoked with the correct user present - Schedule(() => LocalUser.Value = createGuestUser()); + // Scheduled prior to state change such that the state changed event is invoked with the correct user and their friends present + Schedule(() => + { + LocalUser.Value = createGuestUser(); + Friends.Clear(); + }); state.Value = APIState.Offline; } diff --git a/osu.Game/Online/API/DummyAPIAccess.cs b/osu.Game/Online/API/DummyAPIAccess.cs index e275676cea..7e5a6378ec 100644 --- a/osu.Game/Online/API/DummyAPIAccess.cs +++ b/osu.Game/Online/API/DummyAPIAccess.cs @@ -5,6 +5,7 @@ using System; using System.Threading; using System.Threading.Tasks; using osu.Framework.Bindables; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Game.Users; @@ -18,6 +19,12 @@ namespace osu.Game.Online.API Id = 1001, }); + public BindableList Friends { get; } = new BindableList(new User + { + Username = @"Dummy's friend", + Id = 2002, + }.Yield()); + public Bindable Activity { get; } = new Bindable(); public string AccessToken => "token"; diff --git a/osu.Game/Online/API/IAPIProvider.cs b/osu.Game/Online/API/IAPIProvider.cs index cadc806f4f..3f62b37a48 100644 --- a/osu.Game/Online/API/IAPIProvider.cs +++ b/osu.Game/Online/API/IAPIProvider.cs @@ -15,6 +15,12 @@ namespace osu.Game.Online.API /// Bindable LocalUser { get; } + /// + /// The user's friends. + /// This is not thread-safe and should be scheduled locally if consumed from a drawable component. + /// + BindableList Friends { get; } + /// /// The current user's activity. /// This is not thread-safe and should be scheduled locally if consumed from a drawable component. From 449b9a21aefe912280728fd621fe11c782fbdb28 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 17 Dec 2020 13:31:57 +0300 Subject: [PATCH 283/818] Allow OverlayView fetching with no API requests required --- osu.Game/Overlays/OverlayView.cs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/OverlayView.cs b/osu.Game/Overlays/OverlayView.cs index c254cdf290..f5ef183bd5 100644 --- a/osu.Game/Overlays/OverlayView.cs +++ b/osu.Game/Overlays/OverlayView.cs @@ -42,25 +42,29 @@ namespace osu.Game.Overlays /// /// Create the API request for fetching data. /// - protected abstract APIRequest CreateRequest(); + protected virtual APIRequest CreateRequest() => null; /// /// Fired when results arrive from the main API request. /// /// - protected abstract void OnSuccess(T response); + protected virtual void OnSuccess(T response) + { + } /// /// Force a re-request for data from the API. /// - protected void PerformFetch() + protected virtual void PerformFetch() { request?.Cancel(); - request = CreateRequest(); - request.Success += response => Schedule(() => OnSuccess(response)); - API.Queue(request); + if (request != null) + { + request.Success += response => Schedule(() => OnSuccess(response)); + API.Queue(request); + } } private void onlineStateChanged(ValueChangedEvent state) => Schedule(() => From 94175d053289d944765c24d3e486f402929692e0 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 17 Dec 2020 13:33:17 +0300 Subject: [PATCH 284/818] Use global friends list instead of always fetching --- osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs b/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs index 41b25ee1a5..8a8ac36e14 100644 --- a/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs +++ b/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs @@ -9,8 +9,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics.UserInterface; -using osu.Game.Online.API; -using osu.Game.Online.API.Requests; using osu.Game.Users; using osuTK; @@ -143,11 +141,11 @@ namespace osu.Game.Overlays.Dashboard.Friends userListToolbar.SortCriteria.BindValueChanged(_ => recreatePanels()); } - protected override APIRequest> CreateRequest() => new GetFriendsRequest(); - - protected override void OnSuccess(List response) + protected override void PerformFetch() { - Users = response; + base.PerformFetch(); + + Users = API.Friends.ToList(); } private void recreatePanels() From 904a4daa985b8f5e37573520a9147ad9adb80167 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 17 Dec 2020 13:33:49 +0300 Subject: [PATCH 285/818] Add todo comment reminding of updating friends list along --- osu.Game/Overlays/Profile/Header/Components/AddFriendButton.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/Profile/Header/Components/AddFriendButton.cs b/osu.Game/Overlays/Profile/Header/Components/AddFriendButton.cs index 6e1b6e2c7d..6c2b2dc16a 100644 --- a/osu.Game/Overlays/Profile/Header/Components/AddFriendButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/AddFriendButton.cs @@ -50,6 +50,8 @@ namespace osu.Game.Overlays.Profile.Header.Components } }; + // todo: when friending/unfriending is implemented, the APIAccess.Friends list should be updated accordingly. + User.BindValueChanged(user => updateFollowers(user.NewValue), true); } From 5d180753fa3cbc40971c867e5d1b36cdf1484306 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 17 Dec 2020 13:44:30 +0300 Subject: [PATCH 286/818] Complete connection once friends list is succesfully fetched --- osu.Game/Online/API/APIAccess.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index f084f6f2c7..806a42a38b 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -146,11 +146,15 @@ namespace osu.Game.Online.API failureCount = 0; var friendsReq = new GetFriendsRequest(); - friendsReq.Success += f => Friends.AddRange(f); - handleRequest(friendsReq); + friendsReq.Success += f => + { + Friends.AddRange(f); - //we're connected! - state.Value = APIState.Online; + //we're connected! + state.Value = APIState.Online; + }; + + handleRequest(friendsReq); }; if (!handleRequest(userReq)) From 0faf3fdfd3fc4f96d137d1e8824398383e441ace Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 17 Dec 2020 15:12:32 +0300 Subject: [PATCH 287/818] Update gameplay leaderboard scores with the new design --- .../Gameplay/TestSceneGameplayLeaderboard.cs | 11 +- .../Screens/Play/HUD/GameplayLeaderboard.cs | 47 +-- .../Play/HUD/GameplayLeaderboardScore.cs | 268 ++++++++++++------ 3 files changed, 215 insertions(+), 111 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs index df970c1c46..d0fdb3dd9c 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs @@ -24,9 +24,8 @@ namespace osu.Game.Tests.Visual.Gameplay Add(leaderboard = new TestGameplayLeaderboard { Anchor = Anchor.Centre, - Origin = Anchor.Centre, + Origin = Anchor.TopCentre, Scale = new Vector2(2), - RelativeSizeAxes = Axes.X, }); } @@ -39,7 +38,7 @@ namespace osu.Game.Tests.Visual.Gameplay playerScore.Value = 1222333; }); - AddStep("add player user", () => leaderboard.AddPlayer(playerScore, new User { Username = "You" })); + AddStep("add player user", () => leaderboard.AddLocalUser(playerScore, new User { Username = "You" })); AddSliderStep("set player score", 50, 5000000, 1222333, v => playerScore.Value = v); } @@ -75,6 +74,12 @@ namespace osu.Game.Tests.Visual.Gameplay return scoreItem != null && scoreItem.ScorePosition == expectedPosition; } + + public void AddPlayer(BindableDouble totalScore, User user) => + base.AddPlayer(totalScore, new BindableDouble(1f), new BindableInt(1), user, false); + + public void AddLocalUser(BindableDouble totalScore, User user) => + base.AddPlayer(totalScore, new BindableDouble(1f), new BindableInt(1), user, true); } } } diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs index e53c56b390..a2673e3097 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs @@ -15,8 +15,7 @@ namespace osu.Game.Screens.Play.HUD { public GameplayLeaderboard() { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; + AutoSizeAxes = Axes.Both; Direction = FillDirection.Vertical; @@ -29,32 +28,44 @@ namespace osu.Game.Screens.Play.HUD /// /// Adds a player to the leaderboard. /// - /// The bindable current score of the player. + /// A bindable of the player's total score. + /// A bindable of the player's accuracy. + /// A bindable of the player's current combo. /// The player. - public void AddPlayer([NotNull] BindableDouble currentScore, [NotNull] User user) + public void AddPlayer([NotNull] IBindableNumber totalScore, + [NotNull] IBindableNumber accuracy, + [NotNull] IBindableNumber combo, + [NotNull] User user) { - var scoreItem = addScore(currentScore.Value, user); - currentScore.ValueChanged += s => scoreItem.TotalScore = s.NewValue; + AddPlayer(totalScore, accuracy, combo, user, false); } - private GameplayLeaderboardScore addScore(double totalScore, User user) + /// + /// Adds a player to the leaderboard. + /// + /// A bindable of the player's total score. + /// A bindable of the player's accuracy. + /// A bindable of the player's current combo. + /// The player. + /// Whether the provided is the local user. + protected void AddPlayer([NotNull] IBindableNumber totalScore, + [NotNull] IBindableNumber accuracy, + [NotNull] IBindableNumber combo, + [NotNull] User user, bool localUser) => Schedule(() => { - var scoreItem = new GameplayLeaderboardScore + Add(new GameplayLeaderboardScore(user, localUser) { - User = user, - TotalScore = totalScore, - OnScoreChange = updateScores, - }; + TotalScore = { BindTarget = totalScore }, + Accuracy = { BindTarget = accuracy }, + Combo = { BindTarget = combo }, + }); - Add(scoreItem); - updateScores(); - - return scoreItem; - } + totalScore.BindValueChanged(_ => updateScores(), true); + }); private void updateScores() { - var orderedByScore = this.OrderByDescending(i => i.TotalScore).ToList(); + var orderedByScore = this.OrderByDescending(i => i.TotalScore.Value).ToList(); for (int i = 0; i < Count; i++) { diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index 4c75f422c9..62ea5782e8 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -1,25 +1,35 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; +using System.Linq; using Humanizer; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Online.API; using osu.Game.Users; +using osu.Game.Utils; using osuTK; +using osuTK.Graphics; namespace osu.Game.Screens.Play.HUD { public class GameplayLeaderboardScore : CompositeDrawable { - private readonly OsuSpriteText positionText, positionSymbol, userString; - private readonly GlowingSpriteText scoreText; + private const float regular_width = 215f; + private const float extended_width = 235f; - public Action OnScoreChange; + private const float panel_height = 35f; + + private OsuSpriteText positionText, scoreText, accuracyText, comboText; + public readonly BindableDouble TotalScore = new BindableDouble(); + public readonly BindableDouble Accuracy = new BindableDouble(); + public readonly BindableInt Combo = new BindableInt(); private int? scorePosition; @@ -34,103 +44,181 @@ namespace osu.Game.Screens.Play.HUD positionText.Text = $"#{scorePosition.Value.ToMetric(decimals: scorePosition < 100000 ? 1 : 0)}"; positionText.FadeTo(scorePosition.HasValue ? 1 : 0); - positionSymbol.FadeTo(scorePosition.HasValue ? 1 : 0); } } - private double totalScore; + public User User { get; } - public double TotalScore + private readonly bool localUser; + + public GameplayLeaderboardScore(User user, bool localUser) { - get => totalScore; - set - { - totalScore = value; - scoreText.Text = totalScore.ToString("N0"); + User = user; + this.localUser = localUser; - OnScoreChange?.Invoke(); - } - } + AutoSizeAxes = Axes.Both; - private User user; - - public User User - { - get => user; - set - { - user = value; - userString.Text = user?.Username; - } - } - - public GameplayLeaderboardScore() - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - - InternalChild = new Container - { - Masking = true, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - AutoSizeAxes = Axes.Both, - Children = new Drawable[] - { - new FillFlowContainer - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopRight, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Margin = new MarginPadding { Right = 2.5f }, - Spacing = new Vector2(2.5f), - Children = new[] - { - positionText = new OsuSpriteText - { - Alpha = 0, - Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold), - }, - positionSymbol = new OsuSpriteText - { - Alpha = 0, - Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold), - Text = ">", - }, - } - }, - new FillFlowContainer - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopLeft, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Margin = new MarginPadding { Left = 2.5f }, - Spacing = new Vector2(2.5f), - Children = new Drawable[] - { - userString = new OsuSpriteText - { - Size = new Vector2(80, 16), - Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold, italics: true), - }, - scoreText = new GlowingSpriteText - { - GlowColour = Color4Extensions.FromHex(@"83ccfa"), - Font = OsuFont.Numeric.With(size: 14), - } - } - }, - }, - }; + Anchor = Anchor.TopRight; + Origin = Anchor.TopRight; } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(IAPIProvider api) { - positionText.Colour = colours.YellowLight; - positionSymbol.Colour = colours.Yellow; + const float panel_shear = 0.15f; + const float shear_width = panel_height * panel_shear; + + Color4 panelColour, textColour; + float panelWidth; + + if (localUser) + { + panelWidth = extended_width; + panelColour = Color4Extensions.FromHex("7fcc33"); + textColour = Color4.White; + } + else if (api.Friends.Any(f => User.Equals(f))) + { + panelWidth = extended_width; + panelColour = Color4Extensions.FromHex("ffd966"); + textColour = Color4Extensions.FromHex("2e576b"); + } + else + { + panelWidth = regular_width; + panelColour = Color4Extensions.FromHex("3399cc"); + textColour = Color4.White; + } + + InternalChildren = new Drawable[] + { + new Container + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Masking = true, + CornerRadius = 5f, + Shear = new Vector2(panel_shear, 0f), + Size = new Vector2(panelWidth, panel_height), + Child = new Box + { + Alpha = 0.5f, + RelativeSizeAxes = Axes.Both, + Colour = panelColour, + } + }, + new GridContainer + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Size = new Vector2(regular_width, panel_height), + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.Absolute, 35f), + new Dimension(), + new Dimension(GridSizeMode.Absolute, 85f), + }, + Content = new[] + { + new Drawable[] + { + positionText = new OsuSpriteText + { + Padding = new MarginPadding { Right = shear_width / 2 }, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Colour = textColour, + Font = OsuFont.Torus.With(size: 14, weight: FontWeight.Bold), + Shadow = false, + }, + new Container + { + Padding = new MarginPadding { Horizontal = shear_width / 3 }, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Container + { + Masking = true, + CornerRadius = 5f, + Shear = new Vector2(panel_shear, 0f), + RelativeSizeAxes = Axes.Both, + Children = new[] + { + new Box + { + Alpha = 0.5f, + RelativeSizeAxes = Axes.Both, + Colour = panelColour, + }, + } + }, + new OsuSpriteText + { + Padding = new MarginPadding { Left = shear_width }, + RelativeSizeAxes = Axes.X, + Width = 0.8f, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Colour = textColour, + Font = OsuFont.Torus.With(size: 14, weight: FontWeight.SemiBold), + Text = User.Username, + Truncate = true, + Shadow = false, + } + } + }, + new Container + { + Padding = new MarginPadding { Top = 2f, Right = 17.5f, Bottom = 5f }, + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Colour = textColour, + Children = new Drawable[] + { + scoreText = new OsuSpriteText + { + Anchor = Anchor.TopLeft, + Origin = Anchor.TopLeft, + Spacing = new Vector2(0.5f, 0f), + Font = OsuFont.Torus.With(size: 16, weight: FontWeight.SemiBold), + Shadow = false, + }, + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Children = new Drawable[] + { + accuracyText = new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Font = OsuFont.Torus.With(size: 12, weight: FontWeight.SemiBold), + Shadow = false, + }, + comboText = new OsuSpriteText + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Font = OsuFont.Torus.With(size: 12, weight: FontWeight.SemiBold), + Shadow = false, + }, + } + } + }, + } + } + } + } + }; + + TotalScore.BindValueChanged(v => scoreText.Text = v.NewValue.ToString("N0"), true); + Accuracy.BindValueChanged(v => accuracyText.Text = v.NewValue.FormatAccuracy(), true); + Combo.BindValueChanged(v => comboText.Text = $"{v.NewValue}x", true); } } } From c15bb6b928a4130d1515e8db07dd2e9f071dccfb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 17 Dec 2020 21:47:57 +0900 Subject: [PATCH 288/818] Add beatmap hash to MultiplayerRoomSettings --- .../RealtimeMultiplayer/MultiplayerRoomSettings.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomSettings.cs b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomSettings.cs index d2f64235c3..60e0d1292e 100644 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomSettings.cs +++ b/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomSettings.cs @@ -18,13 +18,20 @@ namespace osu.Game.Online.RealtimeMultiplayer public int RulesetID { get; set; } + public string BeatmapChecksum { get; set; } = string.Empty; + public string Name { get; set; } = "Unnamed room"; [NotNull] public IEnumerable Mods { get; set; } = Enumerable.Empty(); - public bool Equals(MultiplayerRoomSettings other) => BeatmapID == other.BeatmapID && Mods.SequenceEqual(other.Mods) && RulesetID == other.RulesetID && Name.Equals(other.Name, StringComparison.Ordinal); + public bool Equals(MultiplayerRoomSettings other) + => BeatmapID == other.BeatmapID + && BeatmapChecksum == other.BeatmapChecksum + && Mods.SequenceEqual(other.Mods) + && RulesetID == other.RulesetID + && Name.Equals(other.Name, StringComparison.Ordinal); - public override string ToString() => $"Name:{Name} Beatmap:{BeatmapID} Mods:{string.Join(',', Mods)} Ruleset:{RulesetID}"; + public override string ToString() => $"Name:{Name} Beatmap:{BeatmapID} ({BeatmapChecksum}) Mods:{string.Join(',', Mods)} Ruleset:{RulesetID}"; } } From 5e4f667cffac3ce6f4bfb108b29697e8fea9ba40 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 18 Dec 2020 01:27:58 +0300 Subject: [PATCH 289/818] Revert "Allow OverlayView fetching with no API requests required" This reverts commit 449b9a21aefe912280728fd621fe11c782fbdb28. --- osu.Game/Overlays/OverlayView.cs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/OverlayView.cs b/osu.Game/Overlays/OverlayView.cs index f5ef183bd5..c254cdf290 100644 --- a/osu.Game/Overlays/OverlayView.cs +++ b/osu.Game/Overlays/OverlayView.cs @@ -42,29 +42,25 @@ namespace osu.Game.Overlays /// /// Create the API request for fetching data. /// - protected virtual APIRequest CreateRequest() => null; + protected abstract APIRequest CreateRequest(); /// /// Fired when results arrive from the main API request. /// /// - protected virtual void OnSuccess(T response) - { - } + protected abstract void OnSuccess(T response); /// /// Force a re-request for data from the API. /// - protected virtual void PerformFetch() + protected void PerformFetch() { request?.Cancel(); - request = CreateRequest(); - if (request != null) - { - request.Success += response => Schedule(() => OnSuccess(response)); - API.Queue(request); - } + request = CreateRequest(); + request.Success += response => Schedule(() => OnSuccess(response)); + + API.Queue(request); } private void onlineStateChanged(ValueChangedEvent state) => Schedule(() => From 9c22753f3fb23f94a19b3870d7cc1e46638e616e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 18 Dec 2020 02:51:19 +0300 Subject: [PATCH 290/818] Remove unnecessary inheritance to OverlayView --- .../Visual/Online/TestSceneFriendDisplay.cs | 11 +++----- .../Dashboard/Friends/FriendDisplay.cs | 26 ++++++++++++------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneFriendDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneFriendDisplay.cs index 0cc6e9f358..9bece39ca0 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneFriendDisplay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneFriendDisplay.cs @@ -20,7 +20,7 @@ namespace osu.Game.Tests.Visual.Online [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); - private TestFriendDisplay display; + private FriendDisplay display; [SetUp] public void Setup() => Schedule(() => @@ -28,7 +28,7 @@ namespace osu.Game.Tests.Visual.Online Child = new BasicScrollContainer { RelativeSizeAxes = Axes.Both, - Child = display = new TestFriendDisplay() + Child = display = new FriendDisplay() }; }); @@ -41,7 +41,7 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestOnline() { - AddStep("Fetch online", () => display?.Fetch()); + // No need to do anything, fetch is performed automatically. } private List getUsers() => new List @@ -76,10 +76,5 @@ namespace osu.Game.Tests.Visual.Online LastVisit = DateTimeOffset.Now } }; - - private class TestFriendDisplay : FriendDisplay - { - public void Fetch() => PerformFetch(); - } } } diff --git a/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs b/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs index 8a8ac36e14..cc26a11da1 100644 --- a/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs +++ b/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs @@ -5,16 +5,18 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API; using osu.Game.Users; using osuTK; namespace osu.Game.Overlays.Dashboard.Friends { - public class FriendDisplay : OverlayView> + public class FriendDisplay : CompositeDrawable { private List users = new List(); @@ -39,8 +41,16 @@ namespace osu.Game.Overlays.Dashboard.Friends private Container itemsPlaceholder; private LoadingLayer loading; - [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) + private readonly IBindableList apiFriends = new BindableList(); + + public FriendDisplay() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + } + + [BackgroundDependencyLoader(true)] + private void load(OverlayColourProvider colourProvider, IAPIProvider api) { InternalChild = new FillFlowContainer { @@ -130,6 +140,9 @@ namespace osu.Game.Overlays.Dashboard.Friends background.Colour = colourProvider.Background4; controlBackground.Colour = colourProvider.Background5; + + apiFriends.BindTo(api.Friends); + apiFriends.BindCollectionChanged((_, __) => Schedule(() => Users = apiFriends.ToList()), true); } protected override void LoadComplete() @@ -141,13 +154,6 @@ namespace osu.Game.Overlays.Dashboard.Friends userListToolbar.SortCriteria.BindValueChanged(_ => recreatePanels()); } - protected override void PerformFetch() - { - base.PerformFetch(); - - Users = API.Friends.ToList(); - } - private void recreatePanels() { if (!users.Any()) From 8a01e567a146d71405b8feeb7bfd7d532569b000 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 18 Dec 2020 03:06:28 +0300 Subject: [PATCH 291/818] Fix API potentially getting stuck in connecting state --- osu.Game/Online/API/APIAccess.cs | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 806a42a38b..b42c78e9cd 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -145,16 +145,14 @@ namespace osu.Game.Online.API failureCount = 0; - var friendsReq = new GetFriendsRequest(); - friendsReq.Success += f => + fetchFriends(() => { - Friends.AddRange(f); - //we're connected! state.Value = APIState.Online; - }; - - handleRequest(friendsReq); + }, () => + { + state.Value = APIState.Failing; + }); }; if (!handleRequest(userReq)) @@ -255,6 +253,19 @@ namespace osu.Game.Online.API return null; } + private void fetchFriends(Action onSuccess, Action onFail) + { + var friendsReq = new GetFriendsRequest(); + friendsReq.Success += res => + { + Friends.AddRange(res); + onSuccess?.Invoke(); + }; + + if (!handleRequest(friendsReq)) + onFail?.Invoke(); + } + /// /// Handle a single API request. /// Ensures all exceptions are caught and dealt with correctly. From a01ed1827a481fb8ac87aef1a1fda89ed5b33f4e Mon Sep 17 00:00:00 2001 From: Graham Johnson Date: Thu, 17 Dec 2020 19:34:16 -0500 Subject: [PATCH 292/818] Align the drag circles on the selction box in the editor to be on the center of the border --- .../Edit/Compose/Components/SelectionBox.cs | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs index 347d9e3ba7..e4feceb987 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs @@ -235,6 +235,8 @@ namespace osu.Game.Screens.Edit.Compose.Components private void addDragHandle(Anchor anchor) => AddInternal(new SelectionBoxDragHandle { Anchor = anchor, + Y = getAdjustmentToCenterCircleOnBorder(anchor).Y, + X = getAdjustmentToCenterCircleOnBorder(anchor).X, HandleDrag = e => OnScale?.Invoke(e.Delta, anchor), OperationStarted = operationStarted, OperationEnded = operationEnded @@ -251,6 +253,45 @@ namespace osu.Game.Screens.Edit.Compose.Components return (endAngle - startAngle) * 180 / MathF.PI; } + /// + /// Adjust Drag circle to be centered on the center of the border instead of on the edge. + /// + /// The part of the rectangle to be adjusted. + private Vector2 getAdjustmentToCenterCircleOnBorder(Anchor anchor) + { + Vector2 adjustment = Vector2.Zero; + + switch (anchor) + { + case Anchor.TopLeft: + case Anchor.CentreLeft: + case Anchor.BottomLeft: + adjustment.X = BORDER_RADIUS / 2; + break; + case Anchor.TopRight: + case Anchor.CentreRight: + case Anchor.BottomRight: + adjustment.X = -BORDER_RADIUS / 2; + break; + } + + switch (anchor) + { + case Anchor.TopLeft: + case Anchor.TopCentre: + case Anchor.TopRight: + adjustment.Y = BORDER_RADIUS / 2; + break; + case Anchor.BottomLeft: + case Anchor.BottomCentre: + case Anchor.BottomRight: + adjustment.Y = -BORDER_RADIUS / 2; + break; + } + + return adjustment; + } + private void operationEnded() { if (--activeOperations == 0) From a8abefcd665adf869b97e45d89c0d85a6d979a7b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 18 Dec 2020 03:34:33 +0300 Subject: [PATCH 293/818] Make GameplayLeaderboardScore a model class --- .../Gameplay/TestSceneGameplayLeaderboard.cs | 20 +++++---- .../Screens/Play/HUD/GameplayLeaderboard.cs | 41 ++----------------- .../Play/HUD/GameplayLeaderboardScore.cs | 5 ++- 3 files changed, 17 insertions(+), 49 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs index d0fdb3dd9c..12bc918d45 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs @@ -38,7 +38,7 @@ namespace osu.Game.Tests.Visual.Gameplay playerScore.Value = 1222333; }); - AddStep("add player user", () => leaderboard.AddLocalUser(playerScore, new User { Username = "You" })); + AddStep("add local player", () => leaderboard.Add(createLeaderboardScore(playerScore, "You", true))); AddSliderStep("set player score", 50, 5000000, 1222333, v => playerScore.Value = v); } @@ -48,8 +48,8 @@ namespace osu.Game.Tests.Visual.Gameplay var player2Score = new BindableDouble(1234567); var player3Score = new BindableDouble(1111111); - AddStep("add player 2", () => leaderboard.AddPlayer(player2Score, new User { Username = "Player 2" })); - AddStep("add player 3", () => leaderboard.AddPlayer(player3Score, new User { Username = "Player 3" })); + AddStep("add player 2", () => leaderboard.Add(createLeaderboardScore(player2Score, "Player 2"))); + AddStep("add player 3", () => leaderboard.Add(createLeaderboardScore(player3Score, "Player 3"))); AddAssert("is player 2 position #1", () => leaderboard.CheckPositionByUsername("Player 2", 1)); AddAssert("is player position #2", () => leaderboard.CheckPositionByUsername("You", 2)); @@ -66,6 +66,14 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("is player 2 position #3", () => leaderboard.CheckPositionByUsername("Player 2", 3)); } + private static GameplayLeaderboardScore createLeaderboardScore(BindableDouble score, string username, bool localOrReplayPlayer = false) + { + return new GameplayLeaderboardScore(new User { Username = username }, localOrReplayPlayer) + { + TotalScore = { BindTarget = score }, + }; + } + private class TestGameplayLeaderboard : GameplayLeaderboard { public bool CheckPositionByUsername(string username, int? expectedPosition) @@ -74,12 +82,6 @@ namespace osu.Game.Tests.Visual.Gameplay return scoreItem != null && scoreItem.ScorePosition == expectedPosition; } - - public void AddPlayer(BindableDouble totalScore, User user) => - base.AddPlayer(totalScore, new BindableDouble(1f), new BindableInt(1), user, false); - - public void AddLocalUser(BindableDouble totalScore, User user) => - base.AddPlayer(totalScore, new BindableDouble(1f), new BindableInt(1), user, true); } } } diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs index a2673e3097..573bf54b14 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs @@ -2,11 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using System.Linq; -using JetBrains.Annotations; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Users; using osuTK; namespace osu.Game.Screens.Play.HUD @@ -25,44 +22,12 @@ namespace osu.Game.Screens.Play.HUD LayoutEasing = Easing.OutQuint; } - /// - /// Adds a player to the leaderboard. - /// - /// A bindable of the player's total score. - /// A bindable of the player's accuracy. - /// A bindable of the player's current combo. - /// The player. - public void AddPlayer([NotNull] IBindableNumber totalScore, - [NotNull] IBindableNumber accuracy, - [NotNull] IBindableNumber combo, - [NotNull] User user) + public override void Add(GameplayLeaderboardScore drawable) { - AddPlayer(totalScore, accuracy, combo, user, false); + base.Add(drawable); + drawable?.TotalScore.BindValueChanged(_ => updateScores(), true); } - /// - /// Adds a player to the leaderboard. - /// - /// A bindable of the player's total score. - /// A bindable of the player's accuracy. - /// A bindable of the player's current combo. - /// The player. - /// Whether the provided is the local user. - protected void AddPlayer([NotNull] IBindableNumber totalScore, - [NotNull] IBindableNumber accuracy, - [NotNull] IBindableNumber combo, - [NotNull] User user, bool localUser) => Schedule(() => - { - Add(new GameplayLeaderboardScore(user, localUser) - { - TotalScore = { BindTarget = totalScore }, - Accuracy = { BindTarget = accuracy }, - Combo = { BindTarget = combo }, - }); - - totalScore.BindValueChanged(_ => updateScores(), true); - }); - private void updateScores() { var orderedByScore = this.OrderByDescending(i => i.TotalScore.Value).ToList(); diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index 62ea5782e8..f94d6bd01f 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -27,8 +27,9 @@ namespace osu.Game.Screens.Play.HUD private const float panel_height = 35f; private OsuSpriteText positionText, scoreText, accuracyText, comboText; - public readonly BindableDouble TotalScore = new BindableDouble(); - public readonly BindableDouble Accuracy = new BindableDouble(); + + public readonly BindableDouble TotalScore = new BindableDouble(1000000); + public readonly BindableDouble Accuracy = new BindableDouble(1); public readonly BindableInt Combo = new BindableInt(); private int? scorePosition; From 92bf74ba29b3bc6c8ec645fe54d9c218d76ff6cf Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 18 Dec 2020 03:37:24 +0300 Subject: [PATCH 294/818] localUser -> localOrReplayPlayer --- .../Screens/Play/HUD/GameplayLeaderboardScore.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index f94d6bd01f..285ed93341 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -50,12 +50,17 @@ namespace osu.Game.Screens.Play.HUD public User User { get; } - private readonly bool localUser; + private readonly bool localOrReplayPlayer; - public GameplayLeaderboardScore(User user, bool localUser) + /// + /// Creates a new . + /// + /// The score's player. + /// Whether the player is the local user or a replay player. + public GameplayLeaderboardScore(User user, bool localOrReplayPlayer) { User = user; - this.localUser = localUser; + this.localOrReplayPlayer = localOrReplayPlayer; AutoSizeAxes = Axes.Both; @@ -72,7 +77,7 @@ namespace osu.Game.Screens.Play.HUD Color4 panelColour, textColour; float panelWidth; - if (localUser) + if (localOrReplayPlayer) { panelWidth = extended_width; panelColour = Color4Extensions.FromHex("7fcc33"); From a0235a06e642319ce5108c21a3643e09bdc2031f Mon Sep 17 00:00:00 2001 From: Graham Johnson Date: Thu, 17 Dec 2020 19:40:21 -0500 Subject: [PATCH 295/818] update comment --- osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs index e4feceb987..4a0a925962 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs @@ -257,6 +257,7 @@ namespace osu.Game.Screens.Edit.Compose.Components /// Adjust Drag circle to be centered on the center of the border instead of on the edge. /// /// The part of the rectangle to be adjusted. + /// A 2d vector on how much to adjust the drag circle private Vector2 getAdjustmentToCenterCircleOnBorder(Anchor anchor) { Vector2 adjustment = Vector2.Zero; From 44f4ed4fd3001587378bae5ff90d4e4edfd5011a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Dec 2020 11:19:40 +0900 Subject: [PATCH 296/818] Fix spacing --- osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs index 4a0a925962..f50e599457 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs @@ -269,6 +269,7 @@ namespace osu.Game.Screens.Edit.Compose.Components case Anchor.BottomLeft: adjustment.X = BORDER_RADIUS / 2; break; + case Anchor.TopRight: case Anchor.CentreRight: case Anchor.BottomRight: @@ -283,6 +284,7 @@ namespace osu.Game.Screens.Edit.Compose.Components case Anchor.TopRight: adjustment.Y = BORDER_RADIUS / 2; break; + case Anchor.BottomLeft: case Anchor.BottomCentre: case Anchor.BottomRight: From 9079d33412207d9f3e18940d3d61c8d8f1a86137 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Dec 2020 11:20:21 +0900 Subject: [PATCH 297/818] X before Y for sanity --- osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs index f50e599457..8ccd4f71e9 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs @@ -235,8 +235,8 @@ namespace osu.Game.Screens.Edit.Compose.Components private void addDragHandle(Anchor anchor) => AddInternal(new SelectionBoxDragHandle { Anchor = anchor, - Y = getAdjustmentToCenterCircleOnBorder(anchor).Y, X = getAdjustmentToCenterCircleOnBorder(anchor).X, + Y = getAdjustmentToCenterCircleOnBorder(anchor).Y, HandleDrag = e => OnScale?.Invoke(e.Delta, anchor), OperationStarted = operationStarted, OperationEnded = operationEnded From 208a9e596ed91f212f37745f493c3416601c3ada Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 18 Dec 2020 14:58:58 +0900 Subject: [PATCH 298/818] Set new room for each test iteration --- .../Multiplayer/TestSceneLoungeRoomInfo.cs | 5 +--- .../TestSceneMatchBeatmapDetailArea.cs | 4 +--- .../Multiplayer/TestSceneMatchHeader.cs | 12 ++++++---- .../Multiplayer/TestSceneMatchLeaderboard.cs | 10 +++++--- .../TestSceneMatchSettingsOverlay.cs | 4 +--- .../Multiplayer/TestSceneMatchSongSelect.cs | 7 ------ .../Multiplayer/TestSceneMatchSubScreen.cs | 6 ----- .../TestSceneOverlinedParticipants.cs | 5 ++-- .../Multiplayer/TestSceneOverlinedPlaylist.cs | 23 +++++++++++-------- .../Multiplayer/TestSceneParticipantsList.cs | 13 +++++------ osu.Game/Tests/Visual/MultiplayerTestScene.cs | 15 +++++++----- 11 files changed, 49 insertions(+), 55 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomInfo.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomInfo.cs index cdad37a9ad..9baaa42c83 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomInfo.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomInfo.cs @@ -4,7 +4,6 @@ using System; using NUnit.Framework; using osu.Framework.Graphics; -using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer.RoomStatuses; using osu.Game.Screens.Multi.Lounge.Components; using osu.Game.Users; @@ -14,10 +13,8 @@ namespace osu.Game.Tests.Visual.Multiplayer public class TestSceneLoungeRoomInfo : MultiplayerTestScene { [SetUp] - public void Setup() => Schedule(() => + public new void Setup() => Schedule(() => { - Room = new Room(); - Child = new RoomInfo { Anchor = Anchor.Centre, diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.cs index 01cd26fbe5..6b1d90e06e 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.cs @@ -24,10 +24,8 @@ namespace osu.Game.Tests.Visual.Multiplayer private RulesetStore rulesetStore { get; set; } [SetUp] - public void Setup() => Schedule(() => + public new void Setup() => Schedule(() => { - Room = new Room(); - Child = new MatchBeatmapDetailArea { Anchor = Anchor.Centre, diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchHeader.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchHeader.cs index e5943105b7..ec5292e51e 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchHeader.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchHeader.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using NUnit.Framework; using osu.Game.Beatmaps; using osu.Game.Online.Multiplayer; using osu.Game.Rulesets.Osu; @@ -14,7 +15,12 @@ namespace osu.Game.Tests.Visual.Multiplayer { public TestSceneMatchHeader() { - Room = new Room(); + Child = new Header(); + } + + [SetUp] + public new void Setup() => Schedule(() => + { Room.Playlist.Add(new PlaylistItem { Beatmap = @@ -41,8 +47,6 @@ namespace osu.Game.Tests.Visual.Multiplayer Room.Name.Value = "A very awesome room"; Room.Host.Value = new User { Id = 2, Username = "peppy" }; - - Child = new Header(); - } + }); } } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs index c24c6c4ba3..a72f71d79c 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs @@ -3,10 +3,10 @@ using System.Collections.Generic; using Newtonsoft.Json; +using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Online.API; -using osu.Game.Online.Multiplayer; using osu.Game.Screens.Multi.Match.Components; using osu.Game.Users; using osuTK; @@ -19,8 +19,6 @@ namespace osu.Game.Tests.Visual.Multiplayer public TestSceneMatchLeaderboard() { - Room = new Room { RoomID = { Value = 3 } }; - Add(new MatchLeaderboard { Origin = Anchor.Centre, @@ -40,6 +38,12 @@ namespace osu.Game.Tests.Visual.Multiplayer api.Queue(req); } + [SetUp] + public new void Setup() => Schedule(() => + { + Room.RoomID.Value = 3; + }); + private class GetRoomScoresRequest : APIRequest> { protected override string Target => "rooms/3/leaderboard"; diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs index 07ff56b5c3..cbe8cc6137 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs @@ -23,10 +23,8 @@ namespace osu.Game.Tests.Visual.Multiplayer private TestRoomSettings settings; [SetUp] - public void Setup() => Schedule(() => + public new void Setup() => Schedule(() => { - Room = new Room(); - settings = new TestRoomSettings { RelativeSizeAxes = Axes.Both, diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSongSelect.cs index 55b8902d7b..4742fd0d84 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSongSelect.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSongSelect.cs @@ -14,7 +14,6 @@ using osu.Framework.Platform; using osu.Framework.Screens; using osu.Framework.Utils; using osu.Game.Beatmaps; -using osu.Game.Online.Multiplayer; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; @@ -94,12 +93,6 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("wait for present", () => songSelect.IsCurrentScreen()); } - [SetUp] - public void Setup() => Schedule(() => - { - Room = new Room(); - }); - [Test] public void TestItemAddedIfEmptyOnStart() { diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSubScreen.cs index 2e22317539..65e9893851 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSubScreen.cs @@ -45,12 +45,6 @@ namespace osu.Game.Tests.Visual.Multiplayer manager.Import(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapSet).Wait(); } - [SetUp] - public void Setup() => Schedule(() => - { - Room = new Room(); - }); - [SetUpSteps] public void SetupSteps() { diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneOverlinedParticipants.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneOverlinedParticipants.cs index b6bfa7c93a..481541a1af 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneOverlinedParticipants.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneOverlinedParticipants.cs @@ -3,7 +3,6 @@ using NUnit.Framework; using osu.Framework.Graphics; -using osu.Game.Online.Multiplayer; using osu.Game.Screens.Multi.Components; using osuTK; @@ -14,9 +13,9 @@ namespace osu.Game.Tests.Visual.Multiplayer protected override bool UseOnlineAPI => true; [SetUp] - public void Setup() => Schedule(() => + public new void Setup() => Schedule(() => { - Room = new Room { RoomID = { Value = 7 } }; + Room.RoomID.Value = 7; }); [Test] diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneOverlinedPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneOverlinedPlaylist.cs index 14984988cb..3dbc990f7a 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneOverlinedPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneOverlinedPlaylist.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using NUnit.Framework; using osu.Framework.Graphics; using osu.Game.Online.Multiplayer; using osu.Game.Rulesets.Osu; @@ -16,7 +17,18 @@ namespace osu.Game.Tests.Visual.Multiplayer public TestSceneOverlinedPlaylist() { - Room = new Room { RoomID = { Value = 7 } }; + Add(new DrawableRoomPlaylist(false, false) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(500), + }); + } + + [SetUp] + public new void Setup() => Schedule(() => + { + Room.RoomID.Value = 7; for (int i = 0; i < 10; i++) { @@ -27,13 +39,6 @@ namespace osu.Game.Tests.Visual.Multiplayer Ruleset = { Value = new OsuRuleset().RulesetInfo } }); } - - Add(new DrawableRoomPlaylist(false, false) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(500), - }); - } + }); } } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneParticipantsList.cs index f71c5fc5d2..1e5647f7f4 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneParticipantsList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneParticipantsList.cs @@ -3,7 +3,6 @@ using NUnit.Framework; using osu.Framework.Graphics; -using osu.Game.Online.Multiplayer; using osu.Game.Screens.Multi.Components; namespace osu.Game.Tests.Visual.Multiplayer @@ -12,15 +11,15 @@ namespace osu.Game.Tests.Visual.Multiplayer { protected override bool UseOnlineAPI => true; - [SetUp] - public void Setup() => Schedule(() => - { - Room = new Room { RoomID = { Value = 7 } }; - }); - public TestSceneParticipantsList() { Add(new ParticipantsList { RelativeSizeAxes = Axes.Both }); } + + [SetUp] + public new void Setup() => Schedule(() => + { + Room.RoomID.Value = 7; + }); } } diff --git a/osu.Game/Tests/Visual/MultiplayerTestScene.cs b/osu.Game/Tests/Visual/MultiplayerTestScene.cs index 4d073f16f4..dc614896c5 100644 --- a/osu.Game/Tests/Visual/MultiplayerTestScene.cs +++ b/osu.Game/Tests/Visual/MultiplayerTestScene.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Online.Multiplayer; @@ -10,13 +11,9 @@ namespace osu.Game.Tests.Visual public abstract class MultiplayerTestScene : ScreenTestScene { [Cached] - private readonly Bindable currentRoom = new Bindable(); + private readonly Bindable currentRoom = new Bindable(new Room()); - protected Room Room - { - get => currentRoom.Value; - set => currentRoom.Value = value; - } + protected Room Room => currentRoom.Value; private CachedModelDependencyContainer dependencies; @@ -26,5 +23,11 @@ namespace osu.Game.Tests.Visual dependencies.Model.BindTo(currentRoom); return dependencies; } + + [SetUp] + public void Setup() => Schedule(() => + { + Room.CopyFrom(new Room()); + }); } } From a4f7eb83c7baa4b847600fcca112b219abe71287 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 18 Dec 2020 15:07:39 +0900 Subject: [PATCH 299/818] Fix overlined participants test scene not working --- .../TestSceneOverlinedParticipants.cs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneOverlinedParticipants.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneOverlinedParticipants.cs index 481541a1af..88ecdb0aad 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneOverlinedParticipants.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneOverlinedParticipants.cs @@ -4,18 +4,25 @@ using NUnit.Framework; using osu.Framework.Graphics; using osu.Game.Screens.Multi.Components; -using osuTK; +using osu.Game.Users; namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneOverlinedParticipants : MultiplayerTestScene { - protected override bool UseOnlineAPI => true; - [SetUp] public new void Setup() => Schedule(() => { Room.RoomID.Value = 7; + + for (int i = 0; i < 50; i++) + { + Room.RecentParticipants.Add(new User + { + Username = "peppy", + Id = 2 + }); + } }); [Test] @@ -27,7 +34,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Width = 500, + Width = 0.2f, }; }); } @@ -41,7 +48,8 @@ namespace osu.Game.Tests.Visual.Multiplayer { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Size = new Vector2(500) + Width = 0.2f, + Height = 0.2f, }; }); } From f0e91ba43188c7cc3f7a0e660c13477551082f71 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 18 Dec 2020 15:09:11 +0900 Subject: [PATCH 300/818] Fix overlined playlist test scene not working --- osu.Game.Tests/Visual/Multiplayer/TestSceneOverlinedPlaylist.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneOverlinedPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneOverlinedPlaylist.cs index 3dbc990f7a..f8af67aa7f 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneOverlinedPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneOverlinedPlaylist.cs @@ -22,6 +22,7 @@ namespace osu.Game.Tests.Visual.Multiplayer Anchor = Anchor.Centre, Origin = Anchor.Centre, Size = new Vector2(500), + Items = { BindTarget = Room.Playlist } }); } From 8c5e25b990af1ea231617428af012ba516db95ee Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 18 Dec 2020 15:11:11 +0900 Subject: [PATCH 301/818] Remove overlined test scenes --- .../TestSceneOverlinedParticipants.cs | 57 ------------------- .../Multiplayer/TestSceneOverlinedPlaylist.cs | 45 --------------- .../Multiplayer/TestSceneParticipantsList.cs | 46 ++++++++++++--- 3 files changed, 39 insertions(+), 109 deletions(-) delete mode 100644 osu.Game.Tests/Visual/Multiplayer/TestSceneOverlinedParticipants.cs delete mode 100644 osu.Game.Tests/Visual/Multiplayer/TestSceneOverlinedPlaylist.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneOverlinedParticipants.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneOverlinedParticipants.cs deleted file mode 100644 index 88ecdb0aad..0000000000 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneOverlinedParticipants.cs +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using NUnit.Framework; -using osu.Framework.Graphics; -using osu.Game.Screens.Multi.Components; -using osu.Game.Users; - -namespace osu.Game.Tests.Visual.Multiplayer -{ - public class TestSceneOverlinedParticipants : MultiplayerTestScene - { - [SetUp] - public new void Setup() => Schedule(() => - { - Room.RoomID.Value = 7; - - for (int i = 0; i < 50; i++) - { - Room.RecentParticipants.Add(new User - { - Username = "peppy", - Id = 2 - }); - } - }); - - [Test] - public void TestHorizontalLayout() - { - AddStep("create component", () => - { - Child = new ParticipantsDisplay(Direction.Horizontal) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Width = 0.2f, - }; - }); - } - - [Test] - public void TestVerticalLayout() - { - AddStep("create component", () => - { - Child = new ParticipantsDisplay(Direction.Vertical) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Width = 0.2f, - Height = 0.2f, - }; - }); - } - } -} diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneOverlinedPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneOverlinedPlaylist.cs deleted file mode 100644 index f8af67aa7f..0000000000 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneOverlinedPlaylist.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using NUnit.Framework; -using osu.Framework.Graphics; -using osu.Game.Online.Multiplayer; -using osu.Game.Rulesets.Osu; -using osu.Game.Screens.Multi; -using osu.Game.Tests.Beatmaps; -using osuTK; - -namespace osu.Game.Tests.Visual.Multiplayer -{ - public class TestSceneOverlinedPlaylist : MultiplayerTestScene - { - protected override bool UseOnlineAPI => true; - - public TestSceneOverlinedPlaylist() - { - Add(new DrawableRoomPlaylist(false, false) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(500), - Items = { BindTarget = Room.Playlist } - }); - } - - [SetUp] - public new void Setup() => Schedule(() => - { - Room.RoomID.Value = 7; - - for (int i = 0; i < 10; i++) - { - Room.Playlist.Add(new PlaylistItem - { - ID = i, - Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo }, - Ruleset = { Value = new OsuRuleset().RulesetInfo } - }); - } - }); - } -} diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneParticipantsList.cs index 1e5647f7f4..7bbec7d30e 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneParticipantsList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneParticipantsList.cs @@ -4,22 +4,54 @@ using NUnit.Framework; using osu.Framework.Graphics; using osu.Game.Screens.Multi.Components; +using osu.Game.Users; namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneParticipantsList : MultiplayerTestScene { - protected override bool UseOnlineAPI => true; - - public TestSceneParticipantsList() - { - Add(new ParticipantsList { RelativeSizeAxes = Axes.Both }); - } - [SetUp] public new void Setup() => Schedule(() => { Room.RoomID.Value = 7; + + for (int i = 0; i < 50; i++) + { + Room.RecentParticipants.Add(new User + { + Username = "peppy", + Id = 2 + }); + } }); + + [Test] + public void TestHorizontalLayout() + { + AddStep("create component", () => + { + Child = new ParticipantsDisplay(Direction.Horizontal) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Width = 0.2f, + }; + }); + } + + [Test] + public void TestVerticalLayout() + { + AddStep("create component", () => + { + Child = new ParticipantsDisplay(Direction.Vertical) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Width = 0.2f, + Height = 0.2f, + }; + }); + } } } From 206bf3713ed2af58b97b55489783848728934dde Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Dec 2020 15:16:36 +0900 Subject: [PATCH 302/818] Make IAPIProvider read-only bindables into IBindables --- osu.Desktop/DiscordRichPresence.cs | 2 +- .../Visual/Menus/TestSceneDisclaimer.cs | 3 ++- .../Online/TestSceneAccountCreationOverlay.cs | 2 +- .../Online/TestSceneNowPlayingCommand.cs | 11 +++++--- osu.Game/Online/API/APIAccess.cs | 26 +++++++++++-------- osu.Game/Online/API/DummyAPIAccess.cs | 4 +++ osu.Game/Online/API/IAPIProvider.cs | 6 ++--- osu.Game/OsuGame.cs | 3 +-- .../BeatmapSet/Buttons/FavouriteButton.cs | 2 +- .../BeatmapSet/Scores/ScoresContainer.cs | 2 +- .../Overlays/Comments/CommentsContainer.cs | 2 +- .../Backgrounds/BackgroundScreenDefault.cs | 2 +- osu.Game/Screens/Menu/Disclaimer.cs | 2 +- .../Screens/Menu/MenuLogoVisualisation.cs | 2 +- osu.Game/Screens/Menu/MenuSideFlashes.cs | 2 +- 15 files changed, 41 insertions(+), 30 deletions(-) diff --git a/osu.Desktop/DiscordRichPresence.cs b/osu.Desktop/DiscordRichPresence.cs index 26d7402a5b..f1878d967d 100644 --- a/osu.Desktop/DiscordRichPresence.cs +++ b/osu.Desktop/DiscordRichPresence.cs @@ -26,7 +26,7 @@ namespace osu.Desktop [Resolved] private IBindable ruleset { get; set; } - private Bindable user; + private IBindable user; private readonly IBindable status = new Bindable(); private readonly IBindable activity = new Bindable(); diff --git a/osu.Game.Tests/Visual/Menus/TestSceneDisclaimer.cs b/osu.Game.Tests/Visual/Menus/TestSceneDisclaimer.cs index 49fab08ded..9cbdee3632 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneDisclaimer.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneDisclaimer.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Game.Online.API; using osu.Game.Screens.Menu; using osu.Game.Users; @@ -16,7 +17,7 @@ namespace osu.Game.Tests.Visual.Menus AddStep("toggle support", () => { - API.LocalUser.Value = new User + ((DummyAPIAccess)API).LocalUser.Value = new User { Username = API.LocalUser.Value.Username, Id = API.LocalUser.Value.Id + 1, diff --git a/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs index 6c8ec917ba..dcfe0432a8 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs @@ -14,7 +14,7 @@ namespace osu.Game.Tests.Visual.Online { private readonly Container userPanelArea; - private Bindable localUser; + private IBindable localUser; public TestSceneAccountCreationOverlay() { diff --git a/osu.Game.Tests/Visual/Online/TestSceneNowPlayingCommand.cs b/osu.Game.Tests/Visual/Online/TestSceneNowPlayingCommand.cs index 8e151a987a..0324da6cf5 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneNowPlayingCommand.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneNowPlayingCommand.cs @@ -6,6 +6,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Testing; using osu.Game.Beatmaps; +using osu.Game.Online.API; using osu.Game.Online.Chat; using osu.Game.Rulesets; using osu.Game.Users; @@ -18,6 +19,8 @@ namespace osu.Game.Tests.Visual.Online [Cached(typeof(IChannelPostTarget))] private PostTarget postTarget { get; set; } + private DummyAPIAccess api => (DummyAPIAccess)API; + public TestSceneNowPlayingCommand() { Add(postTarget = new PostTarget()); @@ -26,7 +29,7 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestGenericActivity() { - AddStep("Set activity", () => API.Activity.Value = new UserActivity.InLobby(null)); + AddStep("Set activity", () => api.Activity.Value = new UserActivity.InLobby(null)); AddStep("Run command", () => Add(new NowPlayingCommand())); @@ -36,7 +39,7 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestEditActivity() { - AddStep("Set activity", () => API.Activity.Value = new UserActivity.Editing(new BeatmapInfo())); + AddStep("Set activity", () => api.Activity.Value = new UserActivity.Editing(new BeatmapInfo())); AddStep("Run command", () => Add(new NowPlayingCommand())); @@ -46,7 +49,7 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestPlayActivity() { - AddStep("Set activity", () => API.Activity.Value = new UserActivity.SoloGame(new BeatmapInfo(), new RulesetInfo())); + AddStep("Set activity", () => api.Activity.Value = new UserActivity.SoloGame(new BeatmapInfo(), new RulesetInfo())); AddStep("Run command", () => Add(new NowPlayingCommand())); @@ -57,7 +60,7 @@ namespace osu.Game.Tests.Visual.Online [TestCase(false)] public void TestLinkPresence(bool hasOnlineId) { - AddStep("Set activity", () => API.Activity.Value = new UserActivity.InLobby(null)); + AddStep("Set activity", () => api.Activity.Value = new UserActivity.InLobby(null)); AddStep("Set beatmap", () => Beatmap.Value = new DummyWorkingBeatmap(Audio, null) { diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index b42c78e9cd..b96475d5bf 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -39,11 +39,15 @@ namespace osu.Game.Online.API private string password; - public Bindable LocalUser { get; } = new Bindable(createGuestUser()); + public IBindable LocalUser => localUser; + public IBindableList Friends => friends; + public IBindable Activity => activity; - public BindableList Friends { get; } = new BindableList(); + private Bindable localUser { get; } = new Bindable(createGuestUser()); - public Bindable Activity { get; } = new Bindable(); + private BindableList friends { get; } = new BindableList(); + + private Bindable activity { get; } = new Bindable(); protected bool HasLogin => authentication.Token.Value != null || (!string.IsNullOrEmpty(ProvidedUsername) && !string.IsNullOrEmpty(password)); @@ -63,10 +67,10 @@ namespace osu.Game.Online.API authentication.TokenString = config.Get(OsuSetting.Token); authentication.Token.ValueChanged += onTokenChanged; - LocalUser.BindValueChanged(u => + localUser.BindValueChanged(u => { - u.OldValue?.Activity.UnbindFrom(Activity); - u.NewValue.Activity.BindTo(Activity); + u.OldValue?.Activity.UnbindFrom(activity); + u.NewValue.Activity.BindTo(activity); }, true); var thread = new Thread(run) @@ -138,10 +142,10 @@ namespace osu.Game.Online.API var userReq = new GetUserRequest(); userReq.Success += u => { - LocalUser.Value = u; + localUser.Value = u; // todo: save/pull from settings - LocalUser.Value.Status.Value = new UserStatusOnline(); + localUser.Value.Status.Value = new UserStatusOnline(); failureCount = 0; @@ -343,7 +347,7 @@ namespace osu.Game.Online.API return true; } - public bool IsLoggedIn => LocalUser.Value.Id > 1; + public bool IsLoggedIn => localUser.Value.Id > 1; public void Queue(APIRequest request) { @@ -376,8 +380,8 @@ namespace osu.Game.Online.API // Scheduled prior to state change such that the state changed event is invoked with the correct user and their friends present Schedule(() => { - LocalUser.Value = createGuestUser(); - Friends.Clear(); + localUser.Value = createGuestUser(); + friends.Clear(); }); state.Value = APIState.Offline; diff --git a/osu.Game/Online/API/DummyAPIAccess.cs b/osu.Game/Online/API/DummyAPIAccess.cs index 7e5a6378ec..a0bc8dd9d6 100644 --- a/osu.Game/Online/API/DummyAPIAccess.cs +++ b/osu.Game/Online/API/DummyAPIAccess.cs @@ -93,5 +93,9 @@ namespace osu.Game.Online.API } public void SetState(APIState newState) => state.Value = newState; + + IBindable IAPIProvider.LocalUser => LocalUser; + IBindableList IAPIProvider.Friends => Friends; + IBindable IAPIProvider.Activity => Activity; } } diff --git a/osu.Game/Online/API/IAPIProvider.cs b/osu.Game/Online/API/IAPIProvider.cs index 3f62b37a48..3a444460f2 100644 --- a/osu.Game/Online/API/IAPIProvider.cs +++ b/osu.Game/Online/API/IAPIProvider.cs @@ -13,19 +13,19 @@ namespace osu.Game.Online.API /// The local user. /// This is not thread-safe and should be scheduled locally if consumed from a drawable component. /// - Bindable LocalUser { get; } + IBindable LocalUser { get; } /// /// The user's friends. /// This is not thread-safe and should be scheduled locally if consumed from a drawable component. /// - BindableList Friends { get; } + IBindableList Friends { get; } /// /// The current user's activity. /// This is not thread-safe and should be scheduled locally if consumed from a drawable component. /// - Bindable Activity { get; } + IBindable Activity { get; } /// /// Retrieve the OAuth access token. diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index d67d790ce2..1e342fa464 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -51,7 +51,6 @@ using osu.Game.Screens.Select; using osu.Game.Updater; using osu.Game.Utils; using LogLevel = osu.Framework.Logging.LogLevel; -using osu.Game.Users; using System.IO; namespace osu.Game @@ -976,7 +975,7 @@ namespace osu.Game if (newScreen is IOsuScreen newOsuScreen) { OverlayActivationMode.BindTo(newOsuScreen.OverlayActivationMode); - ((IBindable)API.Activity).BindTo(newOsuScreen.Activity); + API.Activity.BindTo(newOsuScreen.Activity); MusicController.AllowRateAdjustments = newOsuScreen.AllowRateAdjustments; diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs index 742b1055b2..c983b337b5 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs @@ -26,7 +26,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons private PostBeatmapFavouriteRequest request; private LoadingLayer loading; - private readonly Bindable localUser = new Bindable(); + private readonly IBindable localUser = new Bindable(); public string TooltipText { diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index a58d662de7..9a2dcd014a 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -26,7 +26,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores public readonly Bindable Beatmap = new Bindable(); private readonly Bindable ruleset = new Bindable(); private readonly Bindable scope = new Bindable(BeatmapLeaderboardScope.Global); - private readonly Bindable user = new Bindable(); + private readonly IBindable user = new Bindable(); private readonly Box background; private readonly ScoreTable scoreTable; diff --git a/osu.Game/Overlays/Comments/CommentsContainer.cs b/osu.Game/Overlays/Comments/CommentsContainer.cs index 2a78748be6..513fabf52a 100644 --- a/osu.Game/Overlays/Comments/CommentsContainer.cs +++ b/osu.Game/Overlays/Comments/CommentsContainer.cs @@ -25,7 +25,7 @@ namespace osu.Game.Overlays.Comments public readonly Bindable Sort = new Bindable(); public readonly BindableBool ShowDeleted = new BindableBool(); - protected readonly Bindable User = new Bindable(); + protected readonly IBindable User = new Bindable(); [Resolved] private IAPIProvider api { get; set; } diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs index 8beb955824..bd4577fd57 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs @@ -22,7 +22,7 @@ namespace osu.Game.Screens.Backgrounds private int currentDisplay; private const int background_count = 7; - private Bindable user; + private IBindable user; private Bindable skin; private Bindable mode; private Bindable introSequence; diff --git a/osu.Game/Screens/Menu/Disclaimer.cs b/osu.Game/Screens/Menu/Disclaimer.cs index 8368047d5a..ceec12c967 100644 --- a/osu.Game/Screens/Menu/Disclaimer.cs +++ b/osu.Game/Screens/Menu/Disclaimer.cs @@ -147,7 +147,7 @@ namespace osu.Game.Screens.Menu if (nextScreen != null) LoadComponentAsync(nextScreen); - currentUser.BindTo(api.LocalUser); + ((IBindable)currentUser).BindTo(api.LocalUser); } public override void OnEntering(IScreen last) diff --git a/osu.Game/Screens/Menu/MenuLogoVisualisation.cs b/osu.Game/Screens/Menu/MenuLogoVisualisation.cs index 92add458f9..c44beeffa5 100644 --- a/osu.Game/Screens/Menu/MenuLogoVisualisation.cs +++ b/osu.Game/Screens/Menu/MenuLogoVisualisation.cs @@ -12,7 +12,7 @@ namespace osu.Game.Screens.Menu { internal class MenuLogoVisualisation : LogoVisualisation { - private Bindable user; + private IBindable user; private Bindable skin; [BackgroundDependencyLoader] diff --git a/osu.Game/Screens/Menu/MenuSideFlashes.cs b/osu.Game/Screens/Menu/MenuSideFlashes.cs index 2ff8132d47..a1ae4555ed 100644 --- a/osu.Game/Screens/Menu/MenuSideFlashes.cs +++ b/osu.Game/Screens/Menu/MenuSideFlashes.cs @@ -35,7 +35,7 @@ namespace osu.Game.Screens.Menu private const double box_fade_in_time = 65; private const int box_width = 200; - private Bindable user; + private IBindable user; private Bindable skin; [Resolved] From d36169f697ce53809bbec26b44411b28c66f6b0a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Dec 2020 15:16:48 +0900 Subject: [PATCH 303/818] Move friend request to a more understandable place in connection flow --- osu.Game/Online/API/APIAccess.cs | 36 +++++++++++++------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index b96475d5bf..93d913dfad 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -140,6 +140,7 @@ namespace osu.Game.Online.API } var userReq = new GetUserRequest(); + userReq.Success += u => { localUser.Value = u; @@ -148,15 +149,6 @@ namespace osu.Game.Online.API localUser.Value.Status.Value = new UserStatusOnline(); failureCount = 0; - - fetchFriends(() => - { - //we're connected! - state.Value = APIState.Online; - }, () => - { - state.Value = APIState.Failing; - }); }; if (!handleRequest(userReq)) @@ -166,6 +158,19 @@ namespace osu.Game.Online.API continue; } + // getting user's friends is considered part of the connection process. + var friendsReq = new GetFriendsRequest(); + + friendsReq.Success += res => + { + friends.AddRange(res); + + //we're connected! + state.Value = APIState.Online; + }; + + if (!handleRequest(friendsReq)) + state.Value = APIState.Failing; // The Success callback event is fired on the main thread, so we should wait for that to run before proceeding. // Without this, we will end up circulating this Connecting loop multiple times and queueing up many web requests // before actually going online. @@ -257,19 +262,6 @@ namespace osu.Game.Online.API return null; } - private void fetchFriends(Action onSuccess, Action onFail) - { - var friendsReq = new GetFriendsRequest(); - friendsReq.Success += res => - { - Friends.AddRange(res); - onSuccess?.Invoke(); - }; - - if (!handleRequest(friendsReq)) - onFail?.Invoke(); - } - /// /// Handle a single API request. /// Ensures all exceptions are caught and dealt with correctly. From bdfeb55dec5614b535737fb22df80c59627973a4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 18 Dec 2020 15:18:06 +0900 Subject: [PATCH 304/818] Fix room status test scene not working --- .../Visual/Multiplayer/TestSceneRoomStatus.cs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs index 1925e0ef4f..c1dfb94464 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Online.Multiplayer; @@ -22,18 +23,21 @@ namespace osu.Game.Tests.Visual.Multiplayer new DrawableRoom(new Room { Name = { Value = "Room 1" }, - Status = { Value = new RoomStatusOpen() } - }), + Status = { Value = new RoomStatusOpen() }, + EndDate = { Value = DateTimeOffset.Now.AddDays(1) } + }) { MatchingFilter = true }, new DrawableRoom(new Room { Name = { Value = "Room 2" }, - Status = { Value = new RoomStatusPlaying() } - }), + Status = { Value = new RoomStatusPlaying() }, + EndDate = { Value = DateTimeOffset.Now.AddDays(1) } + }) { MatchingFilter = true }, new DrawableRoom(new Room { Name = { Value = "Room 3" }, - Status = { Value = new RoomStatusEnded() } - }), + Status = { Value = new RoomStatusEnded() }, + EndDate = { Value = DateTimeOffset.Now } + }) { MatchingFilter = true }, } }; } From 57c5d45c022844e729e11c4236d7fa0b8cc93ed9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Dec 2020 15:19:38 +0900 Subject: [PATCH 305/818] Standardise and extract common connection failure handling logic --- osu.Game/Online/API/APIAccess.cs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 93d913dfad..fe500b9548 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -153,8 +153,7 @@ namespace osu.Game.Online.API if (!handleRequest(userReq)) { - if (State.Value == APIState.Connecting) - state.Value = APIState.Failing; + failConnectionProcess(); continue; } @@ -170,7 +169,11 @@ namespace osu.Game.Online.API }; if (!handleRequest(friendsReq)) - state.Value = APIState.Failing; + { + failConnectionProcess(); + continue; + } + // The Success callback event is fired on the main thread, so we should wait for that to run before proceeding. // Without this, we will end up circulating this Connecting loop multiple times and queueing up many web requests // before actually going online. @@ -203,6 +206,13 @@ namespace osu.Game.Online.API Thread.Sleep(50); } + + void failConnectionProcess() + { + // if something went wrong during the connection process, we want to reset the state (but only if still connecting). + if (State.Value == APIState.Connecting) + state.Value = APIState.Failing; + } } public void Perform(APIRequest request) From 99b670627a847e42ca245923b6e89f70f3a1ea04 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Dec 2020 15:25:12 +0900 Subject: [PATCH 306/818] Remove unused placeholder friend in DummyAPI implementation --- osu.Game/Online/API/DummyAPIAccess.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Online/API/DummyAPIAccess.cs b/osu.Game/Online/API/DummyAPIAccess.cs index a0bc8dd9d6..4e1bc21df3 100644 --- a/osu.Game/Online/API/DummyAPIAccess.cs +++ b/osu.Game/Online/API/DummyAPIAccess.cs @@ -19,11 +19,7 @@ namespace osu.Game.Online.API Id = 1001, }); - public BindableList Friends { get; } = new BindableList(new User - { - Username = @"Dummy's friend", - Id = 2002, - }.Yield()); + public BindableList Friends { get; } = new BindableList(); public Bindable Activity { get; } = new Bindable(); From 4af508235ee855cd0ac9be9d5aed56d416651f95 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Dec 2020 15:35:18 +0900 Subject: [PATCH 307/818] Rename long variable --- osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index 285ed93341..1d548e8809 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -50,17 +50,17 @@ namespace osu.Game.Screens.Play.HUD public User User { get; } - private readonly bool localOrReplayPlayer; + private readonly bool trackedPlayer; /// /// Creates a new . /// /// The score's player. - /// Whether the player is the local user or a replay player. - public GameplayLeaderboardScore(User user, bool localOrReplayPlayer) + /// Whether the player is the local user or a replay player. + public GameplayLeaderboardScore(User user, bool trackedPlayer) { User = user; - this.localOrReplayPlayer = localOrReplayPlayer; + this.trackedPlayer = trackedPlayer; AutoSizeAxes = Axes.Both; @@ -77,7 +77,7 @@ namespace osu.Game.Screens.Play.HUD Color4 panelColour, textColour; float panelWidth; - if (localOrReplayPlayer) + if (trackedPlayer) { panelWidth = extended_width; panelColour = Color4Extensions.FromHex("7fcc33"); From c80ecec0b418eedfa54931ef34d496984655cec1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 18 Dec 2020 15:36:24 +0900 Subject: [PATCH 308/818] Reorder methods --- osu.Game/Screens/Play/Player.cs | 42 ++++++++++++++++----------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index a54f9fc047..92c76ec2d2 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -531,29 +531,8 @@ namespace osu.Game.Screens.Play completionProgressDelegate = Schedule(GotoRanking); } - protected virtual ScoreInfo CreateScore() - { - var score = new ScoreInfo - { - Beatmap = Beatmap.Value.BeatmapInfo, - Ruleset = rulesetInfo, - Mods = Mods.Value.ToArray(), - }; - - if (DrawableRuleset.ReplayScore != null) - score.User = DrawableRuleset.ReplayScore.ScoreInfo?.User ?? new GuestUser(); - else - score.User = api.LocalUser.Value; - - ScoreProcessor.PopulateScore(score); - - return score; - } - protected override bool OnScroll(ScrollEvent e) => mouseWheelDisabled.Value && !GameplayClockContainer.IsPaused.Value; - protected virtual ResultsScreen CreateResults(ScoreInfo score) => new SoloResultsScreen(score, true); - #region Fail Logic protected FailOverlay FailOverlay { get; private set; } @@ -748,6 +727,25 @@ namespace osu.Game.Screens.Play return base.OnExiting(next); } + protected virtual ScoreInfo CreateScore() + { + var score = new ScoreInfo + { + Beatmap = Beatmap.Value.BeatmapInfo, + Ruleset = rulesetInfo, + Mods = Mods.Value.ToArray(), + }; + + if (DrawableRuleset.ReplayScore != null) + score.User = DrawableRuleset.ReplayScore.ScoreInfo?.User ?? new GuestUser(); + else + score.User = api.LocalUser.Value; + + ScoreProcessor.PopulateScore(score); + + return score; + } + protected virtual void GotoRanking() { if (DrawableRuleset.ReplayScore != null) @@ -781,6 +779,8 @@ namespace osu.Game.Screens.Play })); } + protected virtual ResultsScreen CreateResults(ScoreInfo score) => new SoloResultsScreen(score, true); + private void fadeOut(bool instant = false) { float fadeOutDuration = instant ? 0 : 250; From a749dca20b14a247cb8828cc0171b99be5e6f141 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Dec 2020 15:43:15 +0900 Subject: [PATCH 309/818] Remove left over using statement --- osu.Game/Online/API/DummyAPIAccess.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Online/API/DummyAPIAccess.cs b/osu.Game/Online/API/DummyAPIAccess.cs index 4e1bc21df3..265298270c 100644 --- a/osu.Game/Online/API/DummyAPIAccess.cs +++ b/osu.Game/Online/API/DummyAPIAccess.cs @@ -5,7 +5,6 @@ using System; using System.Threading; using System.Threading.Tasks; using osu.Framework.Bindables; -using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Game.Users; From ceb2e4762d7c63efc3f9e044ae326447543c8528 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Dec 2020 16:20:42 +0900 Subject: [PATCH 310/818] Add test covering a more consistent spread of player scores --- .../Visual/Gameplay/TestSceneGameplayLeaderboard.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs index 12bc918d45..d596def98a 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs @@ -6,6 +6,7 @@ using NUnit.Framework; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Testing; +using osu.Framework.Utils; using osu.Game.Screens.Play.HUD; using osu.Game.Users; using osuTK; @@ -66,6 +67,13 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("is player 2 position #3", () => leaderboard.CheckPositionByUsername("Player 2", 3)); } + [Test] + public void TestRandomScores() + { + int playerNumber = 1; + AddRepeatStep("add player with random score", () => leaderboard.Add(createLeaderboardScore(new BindableDouble(RNG.Next(0, 5_000_000)), $"Player {playerNumber++}")), 10); + } + private static GameplayLeaderboardScore createLeaderboardScore(BindableDouble score, string username, bool localOrReplayPlayer = false) { return new GameplayLeaderboardScore(new User { Username = username }, localOrReplayPlayer) From c84807ed5c0f09e47993666920d5374d561c5f9b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Dec 2020 16:20:54 +0900 Subject: [PATCH 311/818] Refactor implementation --- .../Play/HUD/GameplayLeaderboardScore.cs | 123 +++++++++++------- 1 file changed, 73 insertions(+), 50 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index 1d548e8809..439210c944 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Linq; using Humanizer; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -11,7 +10,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osu.Game.Online.API; using osu.Game.Users; using osu.Game.Utils; using osuTK; @@ -26,7 +24,7 @@ namespace osu.Game.Screens.Play.HUD private const float panel_height = 35f; - private OsuSpriteText positionText, scoreText, accuracyText, comboText; + private OsuSpriteText positionText, scoreText, accuracyText, comboText, usernameText; public readonly BindableDouble TotalScore = new BindableDouble(1000000); public readonly BindableDouble Accuracy = new BindableDouble(1); @@ -45,6 +43,7 @@ namespace osu.Game.Screens.Play.HUD positionText.Text = $"#{scorePosition.Value.ToMetric(decimals: scorePosition < 100000 ? 1 : 0)}"; positionText.FadeTo(scorePosition.HasValue ? 1 : 0); + updateColour(); } } @@ -52,6 +51,9 @@ namespace osu.Game.Screens.Play.HUD private readonly bool trackedPlayer; + private Container mainFillContainer; + private Box centralFill; + /// /// Creates a new . /// @@ -62,62 +64,93 @@ namespace osu.Game.Screens.Play.HUD User = user; this.trackedPlayer = trackedPlayer; - AutoSizeAxes = Axes.Both; + AutoSizeAxes = Axes.X; + Height = panel_height; Anchor = Anchor.TopRight; Origin = Anchor.TopRight; } - [BackgroundDependencyLoader] - private void load(IAPIProvider api) + protected override void LoadComplete() { - const float panel_shear = 0.15f; - const float shear_width = panel_height * panel_shear; + base.LoadComplete(); - Color4 panelColour, textColour; - float panelWidth; + updateColour(); + FinishTransforms(true); + } - if (trackedPlayer) + private void updateColour() + { + if (scorePosition == 1) { - panelWidth = extended_width; + mainFillContainer.ResizeWidthTo(extended_width, 200, Easing.OutQuint); panelColour = Color4Extensions.FromHex("7fcc33"); textColour = Color4.White; } - else if (api.Friends.Any(f => User.Equals(f))) + else if (trackedPlayer) { - panelWidth = extended_width; + mainFillContainer.ResizeWidthTo(extended_width, 200, Easing.OutQuint); panelColour = Color4Extensions.FromHex("ffd966"); textColour = Color4Extensions.FromHex("2e576b"); } else { - panelWidth = regular_width; + mainFillContainer.ResizeWidthTo(regular_width, 200, Easing.OutQuint); panelColour = Color4Extensions.FromHex("3399cc"); textColour = Color4.White; } + } + + private Color4 panelColour + { + set + { + mainFillContainer.FadeColour(value, 200, Easing.OutQuint); + centralFill.FadeColour(value, 200, Easing.OutQuint); + } + } + + private Color4 textColour + { + set + { + scoreText.FadeColour(value, 200, Easing.OutQuint); + accuracyText.FadeColour(value, 200, Easing.OutQuint); + comboText.FadeColour(value, 200, Easing.OutQuint); + usernameText.FadeColour(value, 200, Easing.OutQuint); + positionText.FadeColour(value, 200, Easing.OutQuint); + } + } + + [BackgroundDependencyLoader] + private void load() + { + const float panel_shear = 0.15f; + const float shear_width = panel_height * panel_shear; InternalChildren = new Drawable[] { - new Container + mainFillContainer = new Container { + Width = regular_width, + RelativeSizeAxes = Axes.Y, Anchor = Anchor.TopRight, Origin = Anchor.TopRight, Masking = true, CornerRadius = 5f, Shear = new Vector2(panel_shear, 0f), - Size = new Vector2(panelWidth, panel_height), Child = new Box { Alpha = 0.5f, RelativeSizeAxes = Axes.Both, - Colour = panelColour, } }, new GridContainer { + Width = regular_width, + RelativeSizeAxes = Axes.Y, Anchor = Anchor.TopRight, Origin = Anchor.TopRight, - Size = new Vector2(regular_width, panel_height), ColumnDimensions = new[] { new Dimension(GridSizeMode.Absolute, 35f), @@ -133,7 +166,7 @@ namespace osu.Game.Screens.Play.HUD Padding = new MarginPadding { Right = shear_width / 2 }, Anchor = Anchor.Centre, Origin = Anchor.Centre, - Colour = textColour, + Colour = Color4.White, Font = OsuFont.Torus.With(size: 14, weight: FontWeight.Bold), Shadow = false, }, @@ -151,22 +184,22 @@ namespace osu.Game.Screens.Play.HUD RelativeSizeAxes = Axes.Both, Children = new[] { - new Box + centralFill = new Box { Alpha = 0.5f, RelativeSizeAxes = Axes.Both, - Colour = panelColour, + Colour = Color4Extensions.FromHex("3399cc"), }, } }, - new OsuSpriteText + usernameText = new OsuSpriteText { Padding = new MarginPadding { Left = shear_width }, RelativeSizeAxes = Axes.X, Width = 0.8f, Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Colour = textColour, + Colour = Color4.White, Font = OsuFont.Torus.With(size: 14, weight: FontWeight.SemiBold), Text = User.Username, Truncate = true, @@ -180,41 +213,31 @@ namespace osu.Game.Screens.Play.HUD RelativeSizeAxes = Axes.Both, Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Colour = textColour, + Colour = Color4.White, Children = new Drawable[] { scoreText = new OsuSpriteText { - Anchor = Anchor.TopLeft, - Origin = Anchor.TopLeft, - Spacing = new Vector2(0.5f, 0f), - Font = OsuFont.Torus.With(size: 16, weight: FontWeight.SemiBold), + Spacing = new Vector2(-1f, 0f), + Font = OsuFont.Torus.With(size: 16, weight: FontWeight.SemiBold, fixedWidth: true), Shadow = false, }, - new Container + accuracyText = new OsuSpriteText { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, - Children = new Drawable[] - { - accuracyText = new OsuSpriteText - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Font = OsuFont.Torus.With(size: 12, weight: FontWeight.SemiBold), - Shadow = false, - }, - comboText = new OsuSpriteText - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Font = OsuFont.Torus.With(size: 12, weight: FontWeight.SemiBold), - Shadow = false, - }, - } - } + Font = OsuFont.Torus.With(size: 12, weight: FontWeight.SemiBold, fixedWidth: true), + Spacing = new Vector2(-1f, 0f), + Shadow = false, + }, + comboText = new OsuSpriteText + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Spacing = new Vector2(-1f, 0f), + Font = OsuFont.Torus.With(size: 12, weight: FontWeight.SemiBold, fixedWidth: true), + Shadow = false, + }, }, } } From e6a38ffbce738d3c464647349b8e5b81b55b373f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Dec 2020 16:33:55 +0900 Subject: [PATCH 312/818] Fix test failure due to polluted bindable value from previous test --- osu.Game/Tests/Visual/MultiplayerTestScene.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Tests/Visual/MultiplayerTestScene.cs b/osu.Game/Tests/Visual/MultiplayerTestScene.cs index dc614896c5..6f24e00a92 100644 --- a/osu.Game/Tests/Visual/MultiplayerTestScene.cs +++ b/osu.Game/Tests/Visual/MultiplayerTestScene.cs @@ -11,7 +11,7 @@ namespace osu.Game.Tests.Visual public abstract class MultiplayerTestScene : ScreenTestScene { [Cached] - private readonly Bindable currentRoom = new Bindable(new Room()); + private readonly Bindable currentRoom = new Bindable(); protected Room Room => currentRoom.Value; @@ -27,7 +27,7 @@ namespace osu.Game.Tests.Visual [SetUp] public void Setup() => Schedule(() => { - Room.CopyFrom(new Room()); + currentRoom.Value = new Room(); }); } } From 07a8ffa4aaefeeaa2da30aea60a94381826f7c13 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Dec 2020 16:50:25 +0900 Subject: [PATCH 313/818] Fix failing tests due to ignoring the lookup ID --- .../Visual/Online/TestSceneCurrentlyPlayingDisplay.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs index 4f0ca67e64..eaa881b02c 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs @@ -89,13 +89,11 @@ namespace osu.Game.Tests.Visual.Online "pishifat" }; - private int id; - protected override Task ComputeValueAsync(int lookup, CancellationToken token = default) => Task.FromResult(new User { - Id = id++, - Username = usernames[id % usernames.Length], + Id = lookup++, + Username = usernames[lookup % usernames.Length], }); } } From 2db7433c0b11f5a90be103621af529a2c35f50e7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 18 Dec 2020 16:51:59 +0900 Subject: [PATCH 314/818] Refactor player score creation and submission process --- .../TestSceneCompletionCancellation.cs | 3 +- .../Screens/Multi/Play/TimeshiftPlayer.cs | 17 ++-- osu.Game/Screens/Play/Player.cs | 79 ++++++++++--------- osu.Game/Screens/Play/ReplayPlayer.cs | 13 +-- 4 files changed, 65 insertions(+), 47 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs index 6fd5511e5a..6e3b394057 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs @@ -10,6 +10,7 @@ using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Scoring; using osu.Game.Storyboards; using osuTK; @@ -117,7 +118,7 @@ namespace osu.Game.Tests.Visual.Gameplay { } - protected override void GotoRanking() + protected override void GotoRanking(ScoreInfo score) { GotoRankingInvoked = true; } diff --git a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs index 0efa9c5196..76e4a328e0 100644 --- a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs +++ b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs @@ -5,6 +5,7 @@ using System; using System.Diagnostics; using System.Linq; using System.Threading; +using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Logging; @@ -95,19 +96,25 @@ namespace osu.Game.Screens.Multi.Play return new TimeshiftResultsScreen(score, roomId.Value.Value, playlistItem, true); } - protected override ScoreInfo CreateScore() + protected override Score CreateScore() { var score = base.CreateScore(); - score.TotalScore = (int)Math.Round(ScoreProcessor.GetStandardisedScore()); + score.ScoreInfo.TotalScore = (int)Math.Round(ScoreProcessor.GetStandardisedScore()); + return score; + } + + protected override async Task SubmitScore(Score score) + { + await base.SubmitScore(score); Debug.Assert(token != null); - var request = new SubmitRoomScoreRequest(token.Value, roomId.Value ?? 0, playlistItem.ID, score); - request.Success += s => score.OnlineScoreID = s.ID; + var request = new SubmitRoomScoreRequest(token.Value, roomId.Value ?? 0, playlistItem.ID, score.ScoreInfo); + request.Success += s => score.ScoreInfo.OnlineScoreID = s.ID; request.Failure += e => Logger.Error(e, "Failed to submit score"); api.Queue(request); - return score; + return score.ScoreInfo; } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 92c76ec2d2..3fb680b9c9 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading.Tasks; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -527,8 +528,18 @@ namespace osu.Game.Screens.Play if (!showResults) return; - using (BeginDelayedSequence(RESULTS_DISPLAY_DELAY)) - completionProgressDelegate = Schedule(GotoRanking); + SubmitScore(CreateScore()).ContinueWith(t => Schedule(() => + { + using (BeginDelayedSequence(RESULTS_DISPLAY_DELAY)) + { + completionProgressDelegate = Schedule(() => + { + // screen may be in the exiting transition phase. + if (this.IsCurrentScreen()) + GotoRanking(t.Result); + }); + } + })); } protected override bool OnScroll(ScrollEvent e) => mouseWheelDisabled.Value && !GameplayClockContainer.IsPaused.Value; @@ -727,60 +738,56 @@ namespace osu.Game.Screens.Play return base.OnExiting(next); } - protected virtual ScoreInfo CreateScore() + protected virtual Score CreateScore() { - var score = new ScoreInfo + var score = new Score { - Beatmap = Beatmap.Value.BeatmapInfo, - Ruleset = rulesetInfo, - Mods = Mods.Value.ToArray(), + ScoreInfo = new ScoreInfo + { + Beatmap = Beatmap.Value.BeatmapInfo, + Ruleset = rulesetInfo, + Mods = Mods.Value.ToArray(), + } }; if (DrawableRuleset.ReplayScore != null) - score.User = DrawableRuleset.ReplayScore.ScoreInfo?.User ?? new GuestUser(); + { + score.ScoreInfo.User = DrawableRuleset.ReplayScore.ScoreInfo?.User ?? new GuestUser(); + score.Replay = DrawableRuleset.ReplayScore.Replay; + } else - score.User = api.LocalUser.Value; + { + score.ScoreInfo.User = api.LocalUser.Value; + if (recordingScore?.Replay.Frames.Count > 0) + score.Replay = recordingScore.Replay; + } - ScoreProcessor.PopulateScore(score); + ScoreProcessor.PopulateScore(score.ScoreInfo); return score; } - protected virtual void GotoRanking() + protected virtual async Task SubmitScore(Score score) { + // Replays are already populated and present in the game's database, so should not be re-imported. if (DrawableRuleset.ReplayScore != null) + return score.ScoreInfo; + + LegacyByteArrayReader replayReader; + + using (var stream = new MemoryStream()) { - // if a replay is present, we likely don't want to import into the local database. - this.Push(CreateResults(CreateScore())); - return; + new LegacyScoreEncoder(score, gameplayBeatmap.PlayableBeatmap).Encode(stream); + replayReader = new LegacyByteArrayReader(stream.ToArray(), "replay.osr"); } - LegacyByteArrayReader replayReader = null; - - var score = new Score { ScoreInfo = CreateScore() }; - - if (recordingScore?.Replay.Frames.Count > 0) - { - score.Replay = recordingScore.Replay; - - using (var stream = new MemoryStream()) - { - new LegacyScoreEncoder(score, gameplayBeatmap.PlayableBeatmap).Encode(stream); - replayReader = new LegacyByteArrayReader(stream.ToArray(), "replay.osr"); - } - } - - scoreManager.Import(score.ScoreInfo, replayReader) - .ContinueWith(imported => Schedule(() => - { - // screen may be in the exiting transition phase. - if (this.IsCurrentScreen()) - this.Push(CreateResults(imported.Result)); - })); + return await scoreManager.Import(score.ScoreInfo, replayReader); } protected virtual ResultsScreen CreateResults(ScoreInfo score) => new SoloResultsScreen(score, true); + protected virtual void GotoRanking(ScoreInfo score) => this.Push(CreateResults(score)); + private void fadeOut(bool instant = false) { float fadeOutDuration = instant ? 0 : 250; diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs index 294d116f51..390d1d1959 100644 --- a/osu.Game/Screens/Play/ReplayPlayer.cs +++ b/osu.Game/Screens/Play/ReplayPlayer.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Threading.Tasks; using osu.Framework.Input.Bindings; using osu.Game.Input.Bindings; using osu.Game.Scoring; @@ -26,18 +27,20 @@ namespace osu.Game.Screens.Play DrawableRuleset?.SetReplayScore(Score); } - protected override ResultsScreen CreateResults(ScoreInfo score) => new SoloResultsScreen(score, false); - - protected override ScoreInfo CreateScore() + protected override Score CreateScore() { var baseScore = base.CreateScore(); // Since the replay score doesn't contain statistics, we'll pass them through here. - Score.ScoreInfo.HitEvents = baseScore.HitEvents; + Score.ScoreInfo.HitEvents = baseScore.ScoreInfo.HitEvents; - return Score.ScoreInfo; + return Score; } + protected override Task SubmitScore(Score score) => Task.FromResult(score.ScoreInfo); + + protected override ResultsScreen CreateResults(ScoreInfo score) => new SoloResultsScreen(score, false); + public bool OnPressed(GlobalAction action) { switch (action) From 70cda680c0e868f41f27bd902cd2d0712d7ee4b6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Dec 2020 16:55:55 +0900 Subject: [PATCH 315/818] Update to match new implementation --- 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 b2dc47ce61..4d39d1a6b3 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -7,6 +7,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Configuration; using osu.Game.Database; +using osu.Game.Online.API; using osu.Game.Online.Spectator; using osu.Game.Rulesets.Scoring; @@ -44,7 +45,7 @@ namespace osu.Game.Screens.Play.HUD private Bindable scoringMode; [BackgroundDependencyLoader] - private void load(OsuConfigManager config) + private void load(OsuConfigManager config, IAPIProvider api) { streamingClient.OnNewFrames += handleIncomingFrames; @@ -58,7 +59,7 @@ namespace osu.Game.Screens.Play.HUD var trackedUser = new TrackedUserData(); userScores[user] = trackedUser; - AddPlayer(trackedUser.Score, resolvedUser); + Add(new GameplayLeaderboardScore(resolvedUser, resolvedUser.Id == api.LocalUser.Value.Id)); //trackedUser.Score, resolvedUser); } scoringMode = config.GetBindable(OsuSetting.ScoreDisplayMode); From 97ff500b0daa28063188e699411b99017e2241bb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 18 Dec 2020 16:56:22 +0900 Subject: [PATCH 316/818] Make timeshift wait on score submission --- .../Screens/Multi/Play/TimeshiftPlayer.cs | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs index 76e4a328e0..e106dc3a1c 100644 --- a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs +++ b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs @@ -105,16 +105,29 @@ namespace osu.Game.Screens.Multi.Play protected override async Task SubmitScore(Score score) { - await base.SubmitScore(score); - Debug.Assert(token != null); + bool completed = false; var request = new SubmitRoomScoreRequest(token.Value, roomId.Value ?? 0, playlistItem.ID, score.ScoreInfo); - request.Success += s => score.ScoreInfo.OnlineScoreID = s.ID; - request.Failure += e => Logger.Error(e, "Failed to submit score"); + + request.Success += s => + { + score.ScoreInfo.OnlineScoreID = s.ID; + completed = true; + }; + + request.Failure += e => + { + Logger.Error(e, "Failed to submit score"); + completed = true; + }; + api.Queue(request); - return score.ScoreInfo; + while (!completed) + await Task.Delay(100); + + return await base.SubmitScore(score); } protected override void Dispose(bool isDisposing) From 157a72ec5deb0f5176fd171c57b9e61d30daa958 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Dec 2020 17:07:38 +0900 Subject: [PATCH 317/818] Revert previous player add flow via interface --- .../Gameplay/TestSceneGameplayLeaderboard.cs | 16 +++++++--------- osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs | 16 +++++++++++++--- .../Screens/Play/HUD/GameplayLeaderboardScore.cs | 8 ++++---- osu.Game/Screens/Play/HUD/ILeaderboardScore.cs | 14 ++++++++++++++ 4 files changed, 38 insertions(+), 16 deletions(-) create mode 100644 osu.Game/Screens/Play/HUD/ILeaderboardScore.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs index d596def98a..6e9375f69c 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs @@ -39,7 +39,7 @@ namespace osu.Game.Tests.Visual.Gameplay playerScore.Value = 1222333; }); - AddStep("add local player", () => leaderboard.Add(createLeaderboardScore(playerScore, "You", true))); + AddStep("add local player", () => createLeaderboardScore(playerScore, "You", true)); AddSliderStep("set player score", 50, 5000000, 1222333, v => playerScore.Value = v); } @@ -49,8 +49,8 @@ namespace osu.Game.Tests.Visual.Gameplay var player2Score = new BindableDouble(1234567); var player3Score = new BindableDouble(1111111); - AddStep("add player 2", () => leaderboard.Add(createLeaderboardScore(player2Score, "Player 2"))); - AddStep("add player 3", () => leaderboard.Add(createLeaderboardScore(player3Score, "Player 3"))); + AddStep("add player 2", () => createLeaderboardScore(player2Score, "Player 2")); + AddStep("add player 3", () => createLeaderboardScore(player3Score, "Player 3")); AddAssert("is player 2 position #1", () => leaderboard.CheckPositionByUsername("Player 2", 1)); AddAssert("is player position #2", () => leaderboard.CheckPositionByUsername("You", 2)); @@ -71,15 +71,13 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestRandomScores() { int playerNumber = 1; - AddRepeatStep("add player with random score", () => leaderboard.Add(createLeaderboardScore(new BindableDouble(RNG.Next(0, 5_000_000)), $"Player {playerNumber++}")), 10); + AddRepeatStep("add player with random score", () => createLeaderboardScore(new BindableDouble(RNG.Next(0, 5_000_000)), $"Player {playerNumber++}"), 10); } - private static GameplayLeaderboardScore createLeaderboardScore(BindableDouble score, string username, bool localOrReplayPlayer = false) + private void createLeaderboardScore(BindableDouble score, string username, bool isTracked = false) { - return new GameplayLeaderboardScore(new User { Username = username }, localOrReplayPlayer) - { - TotalScore = { BindTarget = score }, - }; + var leaderboardScore = leaderboard.AddPlayer(new User { Username = username }, isTracked); + leaderboardScore.TotalScore.BindTo(score); } private class TestGameplayLeaderboard : GameplayLeaderboard diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs index 573bf54b14..e738477a80 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs @@ -1,9 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Users; using osuTK; namespace osu.Game.Screens.Play.HUD @@ -22,13 +24,21 @@ namespace osu.Game.Screens.Play.HUD LayoutEasing = Easing.OutQuint; } - public override void Add(GameplayLeaderboardScore drawable) + public ILeaderboardScore AddPlayer(User user, bool isTracked) { + var drawable = new GameplayLeaderboardScore(user, isTracked); base.Add(drawable); - drawable?.TotalScore.BindValueChanged(_ => updateScores(), true); + drawable.TotalScore.BindValueChanged(_ => Scheduler.AddOnce(sort), true); + + return drawable; } - private void updateScores() + public override void Add(GameplayLeaderboardScore drawable) + { + throw new InvalidOperationException($"Use {nameof(AddPlayer)} instead."); + } + + private void sort() { var orderedByScore = this.OrderByDescending(i => i.TotalScore.Value).ToList(); diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index 439210c944..d4d7c69f6b 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -17,7 +17,7 @@ using osuTK.Graphics; namespace osu.Game.Screens.Play.HUD { - public class GameplayLeaderboardScore : CompositeDrawable + public class GameplayLeaderboardScore : CompositeDrawable, ILeaderboardScore { private const float regular_width = 215f; private const float extended_width = 235f; @@ -26,9 +26,9 @@ namespace osu.Game.Screens.Play.HUD private OsuSpriteText positionText, scoreText, accuracyText, comboText, usernameText; - public readonly BindableDouble TotalScore = new BindableDouble(1000000); - public readonly BindableDouble Accuracy = new BindableDouble(1); - public readonly BindableInt Combo = new BindableInt(); + public BindableDouble TotalScore { get; } = new BindableDouble(); + public BindableDouble Accuracy { get; } = new BindableDouble(1); + public BindableInt Combo { get; } = new BindableInt(); private int? scorePosition; diff --git a/osu.Game/Screens/Play/HUD/ILeaderboardScore.cs b/osu.Game/Screens/Play/HUD/ILeaderboardScore.cs new file mode 100644 index 0000000000..bc1a03c5aa --- /dev/null +++ b/osu.Game/Screens/Play/HUD/ILeaderboardScore.cs @@ -0,0 +1,14 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Bindables; + +namespace osu.Game.Screens.Play.HUD +{ + public interface ILeaderboardScore + { + BindableDouble TotalScore { get; } + BindableDouble Accuracy { get; } + BindableInt Combo { get; } + } +} From cb3f89d0a5c73baa360413c9ef9cf08c8ea39617 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Dec 2020 17:13:51 +0900 Subject: [PATCH 318/818] Hook up with new leaderboard design --- osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index 4d39d1a6b3..c96f496cd0 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -59,7 +59,11 @@ namespace osu.Game.Screens.Play.HUD var trackedUser = new TrackedUserData(); userScores[user] = trackedUser; - Add(new GameplayLeaderboardScore(resolvedUser, resolvedUser.Id == api.LocalUser.Value.Id)); //trackedUser.Score, resolvedUser); + var leaderboardScore = AddPlayer(resolvedUser, resolvedUser.Id == api.LocalUser.Value.Id); + + ((IBindable)leaderboardScore.Accuracy).BindTo(trackedUser.Accuracy); + ((IBindable)leaderboardScore.TotalScore).BindTo(trackedUser.Score); + ((IBindable)leaderboardScore.Combo).BindTo(trackedUser.CurrentCombo); } scoringMode = config.GetBindable(OsuSetting.ScoreDisplayMode); From bca4d83af794a587eb02eba41357ce6e841381c1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Dec 2020 17:07:38 +0900 Subject: [PATCH 319/818] Revert previous player add flow via interface --- .../Gameplay/TestSceneGameplayLeaderboard.cs | 16 +++++++--------- osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs | 16 +++++++++++++--- .../Screens/Play/HUD/GameplayLeaderboardScore.cs | 8 ++++---- osu.Game/Screens/Play/HUD/ILeaderboardScore.cs | 14 ++++++++++++++ 4 files changed, 38 insertions(+), 16 deletions(-) create mode 100644 osu.Game/Screens/Play/HUD/ILeaderboardScore.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs index d596def98a..6e9375f69c 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs @@ -39,7 +39,7 @@ namespace osu.Game.Tests.Visual.Gameplay playerScore.Value = 1222333; }); - AddStep("add local player", () => leaderboard.Add(createLeaderboardScore(playerScore, "You", true))); + AddStep("add local player", () => createLeaderboardScore(playerScore, "You", true)); AddSliderStep("set player score", 50, 5000000, 1222333, v => playerScore.Value = v); } @@ -49,8 +49,8 @@ namespace osu.Game.Tests.Visual.Gameplay var player2Score = new BindableDouble(1234567); var player3Score = new BindableDouble(1111111); - AddStep("add player 2", () => leaderboard.Add(createLeaderboardScore(player2Score, "Player 2"))); - AddStep("add player 3", () => leaderboard.Add(createLeaderboardScore(player3Score, "Player 3"))); + AddStep("add player 2", () => createLeaderboardScore(player2Score, "Player 2")); + AddStep("add player 3", () => createLeaderboardScore(player3Score, "Player 3")); AddAssert("is player 2 position #1", () => leaderboard.CheckPositionByUsername("Player 2", 1)); AddAssert("is player position #2", () => leaderboard.CheckPositionByUsername("You", 2)); @@ -71,15 +71,13 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestRandomScores() { int playerNumber = 1; - AddRepeatStep("add player with random score", () => leaderboard.Add(createLeaderboardScore(new BindableDouble(RNG.Next(0, 5_000_000)), $"Player {playerNumber++}")), 10); + AddRepeatStep("add player with random score", () => createLeaderboardScore(new BindableDouble(RNG.Next(0, 5_000_000)), $"Player {playerNumber++}"), 10); } - private static GameplayLeaderboardScore createLeaderboardScore(BindableDouble score, string username, bool localOrReplayPlayer = false) + private void createLeaderboardScore(BindableDouble score, string username, bool isTracked = false) { - return new GameplayLeaderboardScore(new User { Username = username }, localOrReplayPlayer) - { - TotalScore = { BindTarget = score }, - }; + var leaderboardScore = leaderboard.AddPlayer(new User { Username = username }, isTracked); + leaderboardScore.TotalScore.BindTo(score); } private class TestGameplayLeaderboard : GameplayLeaderboard diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs index 573bf54b14..e738477a80 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs @@ -1,9 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Users; using osuTK; namespace osu.Game.Screens.Play.HUD @@ -22,13 +24,21 @@ namespace osu.Game.Screens.Play.HUD LayoutEasing = Easing.OutQuint; } - public override void Add(GameplayLeaderboardScore drawable) + public ILeaderboardScore AddPlayer(User user, bool isTracked) { + var drawable = new GameplayLeaderboardScore(user, isTracked); base.Add(drawable); - drawable?.TotalScore.BindValueChanged(_ => updateScores(), true); + drawable.TotalScore.BindValueChanged(_ => Scheduler.AddOnce(sort), true); + + return drawable; } - private void updateScores() + public override void Add(GameplayLeaderboardScore drawable) + { + throw new InvalidOperationException($"Use {nameof(AddPlayer)} instead."); + } + + private void sort() { var orderedByScore = this.OrderByDescending(i => i.TotalScore.Value).ToList(); diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index 439210c944..d4d7c69f6b 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -17,7 +17,7 @@ using osuTK.Graphics; namespace osu.Game.Screens.Play.HUD { - public class GameplayLeaderboardScore : CompositeDrawable + public class GameplayLeaderboardScore : CompositeDrawable, ILeaderboardScore { private const float regular_width = 215f; private const float extended_width = 235f; @@ -26,9 +26,9 @@ namespace osu.Game.Screens.Play.HUD private OsuSpriteText positionText, scoreText, accuracyText, comboText, usernameText; - public readonly BindableDouble TotalScore = new BindableDouble(1000000); - public readonly BindableDouble Accuracy = new BindableDouble(1); - public readonly BindableInt Combo = new BindableInt(); + public BindableDouble TotalScore { get; } = new BindableDouble(); + public BindableDouble Accuracy { get; } = new BindableDouble(1); + public BindableInt Combo { get; } = new BindableInt(); private int? scorePosition; diff --git a/osu.Game/Screens/Play/HUD/ILeaderboardScore.cs b/osu.Game/Screens/Play/HUD/ILeaderboardScore.cs new file mode 100644 index 0000000000..bc1a03c5aa --- /dev/null +++ b/osu.Game/Screens/Play/HUD/ILeaderboardScore.cs @@ -0,0 +1,14 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Bindables; + +namespace osu.Game.Screens.Play.HUD +{ + public interface ILeaderboardScore + { + BindableDouble TotalScore { get; } + BindableDouble Accuracy { get; } + BindableInt Combo { get; } + } +} From 4cf013c0058555776eea78a956203cfbbfe8d70a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Dec 2020 17:19:00 +0900 Subject: [PATCH 320/818] Fix animation replacing itself even when score position hasn't changed --- osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index d4d7c69f6b..086ac956e8 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -37,6 +37,9 @@ namespace osu.Game.Screens.Play.HUD get => scorePosition; set { + if (value == scorePosition) + return; + scorePosition = value; if (scorePosition.HasValue) From e82986b76328983a9c648ba274df7689115941af Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Dec 2020 17:19:36 +0900 Subject: [PATCH 321/818] Fix panel x positions getting weird duration relayouts Also adjust the transitions a bit to feel better. --- .../Play/HUD/GameplayLeaderboardScore.cs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index 086ac956e8..77e6d320ae 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -67,11 +67,7 @@ namespace osu.Game.Screens.Play.HUD User = user; this.trackedPlayer = trackedPlayer; - AutoSizeAxes = Axes.X; - Height = panel_height; - - Anchor = Anchor.TopRight; - Origin = Anchor.TopRight; + Size = new Vector2(extended_width, panel_height); } protected override void LoadComplete() @@ -82,23 +78,25 @@ namespace osu.Game.Screens.Play.HUD FinishTransforms(true); } + private const double transition_duration = 500; + private void updateColour() { if (scorePosition == 1) { - mainFillContainer.ResizeWidthTo(extended_width, 200, Easing.OutQuint); + mainFillContainer.ResizeWidthTo(extended_width, transition_duration, Easing.OutElastic); panelColour = Color4Extensions.FromHex("7fcc33"); textColour = Color4.White; } else if (trackedPlayer) { - mainFillContainer.ResizeWidthTo(extended_width, 200, Easing.OutQuint); + mainFillContainer.ResizeWidthTo(extended_width, transition_duration, Easing.OutElastic); panelColour = Color4Extensions.FromHex("ffd966"); textColour = Color4Extensions.FromHex("2e576b"); } else { - mainFillContainer.ResizeWidthTo(regular_width, 200, Easing.OutQuint); + mainFillContainer.ResizeWidthTo(regular_width, transition_duration, Easing.OutElastic); panelColour = Color4Extensions.FromHex("3399cc"); textColour = Color4.White; } @@ -108,8 +106,8 @@ namespace osu.Game.Screens.Play.HUD { set { - mainFillContainer.FadeColour(value, 200, Easing.OutQuint); - centralFill.FadeColour(value, 200, Easing.OutQuint); + mainFillContainer.FadeColour(value, transition_duration, Easing.OutQuint); + centralFill.FadeColour(value, transition_duration, Easing.OutQuint); } } From 668536ce562eb30088b66602f294672b5be58d58 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Dec 2020 17:25:48 +0900 Subject: [PATCH 322/818] Fix vertical size potentially changing during relayout --- .../Gameplay/TestSceneGameplayLeaderboard.cs | 2 +- osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs | 4 +++- .../Screens/Play/HUD/GameplayLeaderboardScore.cs | 15 ++++++++------- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs index 6e9375f69c..ff15e1d2dc 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs @@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual.Gameplay Add(leaderboard = new TestGameplayLeaderboard { Anchor = Anchor.Centre, - Origin = Anchor.TopCentre, + Origin = Anchor.Centre, Scale = new Vector2(2), }); } diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs index e738477a80..99319c0008 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs @@ -14,7 +14,7 @@ namespace osu.Game.Screens.Play.HUD { public GameplayLeaderboard() { - AutoSizeAxes = Axes.Both; + Width = GameplayLeaderboardScore.EXTENDED_WIDTH; Direction = FillDirection.Vertical; @@ -30,6 +30,8 @@ namespace osu.Game.Screens.Play.HUD base.Add(drawable); drawable.TotalScore.BindValueChanged(_ => Scheduler.AddOnce(sort), true); + Height = Count * (GameplayLeaderboardScore.PANEL_HEIGHT + Spacing.Y); + return drawable; } diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index 77e6d320ae..242e7ff6b9 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -19,10 +19,11 @@ namespace osu.Game.Screens.Play.HUD { public class GameplayLeaderboardScore : CompositeDrawable, ILeaderboardScore { - private const float regular_width = 215f; - private const float extended_width = 235f; + public const float EXTENDED_WIDTH = 235f; - private const float panel_height = 35f; + private const float regular_width = 215f; + + public const float PANEL_HEIGHT = 35f; private OsuSpriteText positionText, scoreText, accuracyText, comboText, usernameText; @@ -67,7 +68,7 @@ namespace osu.Game.Screens.Play.HUD User = user; this.trackedPlayer = trackedPlayer; - Size = new Vector2(extended_width, panel_height); + Size = new Vector2(EXTENDED_WIDTH, PANEL_HEIGHT); } protected override void LoadComplete() @@ -84,13 +85,13 @@ namespace osu.Game.Screens.Play.HUD { if (scorePosition == 1) { - mainFillContainer.ResizeWidthTo(extended_width, transition_duration, Easing.OutElastic); + mainFillContainer.ResizeWidthTo(EXTENDED_WIDTH, transition_duration, Easing.OutElastic); panelColour = Color4Extensions.FromHex("7fcc33"); textColour = Color4.White; } else if (trackedPlayer) { - mainFillContainer.ResizeWidthTo(extended_width, transition_duration, Easing.OutElastic); + mainFillContainer.ResizeWidthTo(EXTENDED_WIDTH, transition_duration, Easing.OutElastic); panelColour = Color4Extensions.FromHex("ffd966"); textColour = Color4Extensions.FromHex("2e576b"); } @@ -127,7 +128,7 @@ namespace osu.Game.Screens.Play.HUD private void load() { const float panel_shear = 0.15f; - const float shear_width = panel_height * panel_shear; + const float shear_width = PANEL_HEIGHT * panel_shear; InternalChildren = new Drawable[] { From 615352c1e41a13c9dc189003aeb4ee2b8a3f95df Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Dec 2020 17:30:11 +0900 Subject: [PATCH 323/818] Fix shear offset not being included in GameplayLeaderboard's own size --- osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs | 9 +++++++-- .../Screens/Play/HUD/GameplayLeaderboardScore.cs | 14 ++++++++------ 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs index 99319c0008..aa668cc4c8 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs @@ -14,7 +14,7 @@ namespace osu.Game.Screens.Play.HUD { public GameplayLeaderboard() { - Width = GameplayLeaderboardScore.EXTENDED_WIDTH; + Width = GameplayLeaderboardScore.EXTENDED_WIDTH + GameplayLeaderboardScore.SHEAR_WIDTH; Direction = FillDirection.Vertical; @@ -26,7 +26,12 @@ namespace osu.Game.Screens.Play.HUD public ILeaderboardScore AddPlayer(User user, bool isTracked) { - var drawable = new GameplayLeaderboardScore(user, isTracked); + var drawable = new GameplayLeaderboardScore(user, isTracked) + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + }; + base.Add(drawable); drawable.TotalScore.BindValueChanged(_ => Scheduler.AddOnce(sort), true); diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index 242e7ff6b9..5c4b4406cc 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -16,6 +16,7 @@ using osuTK; using osuTK.Graphics; namespace osu.Game.Screens.Play.HUD + { public class GameplayLeaderboardScore : CompositeDrawable, ILeaderboardScore { @@ -25,6 +26,10 @@ namespace osu.Game.Screens.Play.HUD public const float PANEL_HEIGHT = 35f; + public const float SHEAR_WIDTH = PANEL_HEIGHT * panel_shear; + + private const float panel_shear = 0.15f; + private OsuSpriteText positionText, scoreText, accuracyText, comboText, usernameText; public BindableDouble TotalScore { get; } = new BindableDouble(); @@ -127,9 +132,6 @@ namespace osu.Game.Screens.Play.HUD [BackgroundDependencyLoader] private void load() { - const float panel_shear = 0.15f; - const float shear_width = PANEL_HEIGHT * panel_shear; - InternalChildren = new Drawable[] { mainFillContainer = new Container @@ -165,7 +167,7 @@ namespace osu.Game.Screens.Play.HUD { positionText = new OsuSpriteText { - Padding = new MarginPadding { Right = shear_width / 2 }, + Padding = new MarginPadding { Right = SHEAR_WIDTH / 2 }, Anchor = Anchor.Centre, Origin = Anchor.Centre, Colour = Color4.White, @@ -174,7 +176,7 @@ namespace osu.Game.Screens.Play.HUD }, new Container { - Padding = new MarginPadding { Horizontal = shear_width / 3 }, + Padding = new MarginPadding { Horizontal = SHEAR_WIDTH / 3 }, RelativeSizeAxes = Axes.Both, Children = new Drawable[] { @@ -196,7 +198,7 @@ namespace osu.Game.Screens.Play.HUD }, usernameText = new OsuSpriteText { - Padding = new MarginPadding { Left = shear_width }, + Padding = new MarginPadding { Left = SHEAR_WIDTH }, RelativeSizeAxes = Axes.X, Width = 0.8f, Anchor = Anchor.CentreLeft, From fdad5e86d3baa4adb991717e8408afae2da02bb0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Dec 2020 17:33:18 +0900 Subject: [PATCH 324/818] Remove stray newline --- osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index 5c4b4406cc..0b58cf76a2 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -16,7 +16,6 @@ using osuTK; using osuTK.Graphics; namespace osu.Game.Screens.Play.HUD - { public class GameplayLeaderboardScore : CompositeDrawable, ILeaderboardScore { From 2958cab239027632f0b9b5c61109bbbd90bfa8b4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 18 Dec 2020 17:47:33 +0900 Subject: [PATCH 325/818] Remove GotoRanking --- .../TestSceneCompletionCancellation.cs | 13 ++++++++----- osu.Game/Screens/Play/Player.cs | 18 +++++++++++++++--- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs index 6e3b394057..4ee48fd853 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs @@ -11,6 +11,7 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Scoring; +using osu.Game.Screens.Ranking; using osu.Game.Storyboards; using osuTK; @@ -51,7 +52,7 @@ namespace osu.Game.Tests.Visual.Gameplay cancel(); complete(); - AddUntilStep("attempted to push ranking", () => ((FakeRankingPushPlayer)Player).GotoRankingInvoked); + AddUntilStep("attempted to push ranking", () => ((FakeRankingPushPlayer)Player).ResultsCreated); } /// @@ -85,7 +86,7 @@ namespace osu.Game.Tests.Visual.Gameplay { // wait to ensure there was no attempt of pushing the results screen. AddWaitStep("wait", resultsDisplayWaitCount); - AddAssert("no attempt to push ranking", () => !((FakeRankingPushPlayer)Player).GotoRankingInvoked); + AddAssert("no attempt to push ranking", () => !((FakeRankingPushPlayer)Player).ResultsCreated); } protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) @@ -111,16 +112,18 @@ namespace osu.Game.Tests.Visual.Gameplay public class FakeRankingPushPlayer : TestPlayer { - public bool GotoRankingInvoked; + public bool ResultsCreated { get; private set; } public FakeRankingPushPlayer() : base(true, true) { } - protected override void GotoRanking(ScoreInfo score) + protected override ResultsScreen CreateResults(ScoreInfo score) { - GotoRankingInvoked = true; + var results = base.CreateResults(score); + ResultsCreated = true; + return results; } } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 3fb680b9c9..cc5f32d300 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -536,7 +536,7 @@ namespace osu.Game.Screens.Play { // screen may be in the exiting transition phase. if (this.IsCurrentScreen()) - GotoRanking(t.Result); + this.Push(CreateResults(t.Result)); }); } })); @@ -738,6 +738,10 @@ namespace osu.Game.Screens.Play return base.OnExiting(next); } + /// + /// Creates the player's . + /// + /// The . protected virtual Score CreateScore() { var score = new Score @@ -767,6 +771,11 @@ namespace osu.Game.Screens.Play return score; } + /// + /// Submits the player's . + /// + /// The to submit. + /// The submitted score. protected virtual async Task SubmitScore(Score score) { // Replays are already populated and present in the game's database, so should not be re-imported. @@ -784,10 +793,13 @@ namespace osu.Game.Screens.Play return await scoreManager.Import(score.ScoreInfo, replayReader); } + /// + /// Creates the for a . + /// + /// The to be displayed in the results screen. + /// The . protected virtual ResultsScreen CreateResults(ScoreInfo score) => new SoloResultsScreen(score, true); - protected virtual void GotoRanking(ScoreInfo score) => this.Push(CreateResults(score)); - private void fadeOut(bool instant = false) { float fadeOutDuration = instant ? 0 : 250; From 1369b75a86b93cac21c22c0d4a9305b9864f9258 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 18 Dec 2020 17:48:42 +0900 Subject: [PATCH 326/818] Fix potential multiple submission --- osu.Game/Screens/Play/Player.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index cc5f32d300..94c595908e 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -502,6 +502,7 @@ namespace osu.Game.Screens.Play } private ScheduledDelegate completionProgressDelegate; + private Task scoreSubmissionTask; private void updateCompletionState(ValueChangedEvent completionState) { @@ -528,7 +529,8 @@ namespace osu.Game.Screens.Play if (!showResults) return; - SubmitScore(CreateScore()).ContinueWith(t => Schedule(() => + scoreSubmissionTask ??= SubmitScore(CreateScore()); + scoreSubmissionTask.ContinueWith(t => Schedule(() => { using (BeginDelayedSequence(RESULTS_DISPLAY_DELAY)) { From 8826d0155908750dfcc99df83bed41e548bb3391 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 18 Dec 2020 18:20:36 +0900 Subject: [PATCH 327/818] Create completion progress delegate immediately --- osu.Game/Screens/Play/Player.cs | 37 ++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 94c595908e..b79b8eeae8 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -529,21 +529,38 @@ namespace osu.Game.Screens.Play if (!showResults) return; - scoreSubmissionTask ??= SubmitScore(CreateScore()); - scoreSubmissionTask.ContinueWith(t => Schedule(() => + scoreSubmissionTask ??= Task.Run(async () => { - using (BeginDelayedSequence(RESULTS_DISPLAY_DELAY)) + var score = CreateScore(); + + try { - completionProgressDelegate = Schedule(() => - { - // screen may be in the exiting transition phase. - if (this.IsCurrentScreen()) - this.Push(CreateResults(t.Result)); - }); + return await SubmitScore(score); } - })); + catch (Exception ex) + { + Logger.Error(ex, "Score submission failed!"); + return score.ScoreInfo; + } + }); + + using (BeginDelayedSequence(RESULTS_DISPLAY_DELAY)) + scheduleCompletion(); } + private void scheduleCompletion() => completionProgressDelegate = Schedule(() => + { + if (!scoreSubmissionTask.IsCompleted) + { + scheduleCompletion(); + return; + } + + // screen may be in the exiting transition phase. + if (this.IsCurrentScreen()) + this.Push(CreateResults(scoreSubmissionTask.Result)); + }); + protected override bool OnScroll(ScrollEvent e) => mouseWheelDisabled.Value && !GameplayClockContainer.IsPaused.Value; #region Fail Logic From eccfc8ccd2b420c99cc2b30f868ad74e1214881a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 18 Dec 2020 18:31:49 +0900 Subject: [PATCH 328/818] Fix potential cross-reference access --- osu.Game/Screens/Play/Player.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index b79b8eeae8..c8f1980ab1 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -23,8 +23,10 @@ using osu.Game.Graphics.Containers; using osu.Game.IO.Archives; using osu.Game.Online.API; using osu.Game.Overlays; +using osu.Game.Replays; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Scoring; @@ -781,8 +783,7 @@ namespace osu.Game.Screens.Play else { score.ScoreInfo.User = api.LocalUser.Value; - if (recordingScore?.Replay.Frames.Count > 0) - score.Replay = recordingScore.Replay; + score.Replay = new Replay { Frames = recordingScore?.Replay.Frames.ToList() ?? new List() }; } ScoreProcessor.PopulateScore(score.ScoreInfo); From c9e75e790830816cd4b90525a71a625319e43bae Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 18 Dec 2020 13:09:05 +0300 Subject: [PATCH 329/818] Add user avatar to leaderboard scores --- .../Play/HUD/GameplayLeaderboardScore.cs | 53 +++++++++++++++---- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index 0b58cf76a2..9c444a6f1f 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Users; +using osu.Game.Users.Drawables; using osu.Game.Utils; using osuTK; using osuTK.Graphics; @@ -129,7 +130,7 @@ namespace osu.Game.Screens.Play.HUD } [BackgroundDependencyLoader] - private void load() + private void load(OsuColour colours) { InternalChildren = new Drawable[] { @@ -195,19 +196,51 @@ namespace osu.Game.Screens.Play.HUD }, } }, - usernameText = new OsuSpriteText + new FillFlowContainer { Padding = new MarginPadding { Left = SHEAR_WIDTH }, - RelativeSizeAxes = Axes.X, - Width = 0.8f, Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Colour = Color4.White, - Font = OsuFont.Torus.With(size: 14, weight: FontWeight.SemiBold), - Text = User.Username, - Truncate = true, - Shadow = false, - } + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(4f, 0f), + Children = new Drawable[] + { + new CircularContainer + { + Masking = true, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Size = new Vector2(25f), + Children = new Drawable[] + { + new Box + { + Name = "Placeholder while avatar loads", + Alpha = 0.3f, + RelativeSizeAxes = Axes.Both, + Colour = colours.Gray4, + }, + new UpdateableAvatar(User) + { + RelativeSizeAxes = Axes.Both, + }, + } + }, + usernameText = new OsuSpriteText + { + RelativeSizeAxes = Axes.X, + Width = 0.8f, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Colour = Color4.White, + Font = OsuFont.Torus.With(size: 14, weight: FontWeight.SemiBold), + Text = User.Username, + Truncate = true, + Shadow = false, + } + } + }, } }, new Container From 030dce55599422cc4017b5b754eab9a66cee623a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 18 Dec 2020 13:09:50 +0300 Subject: [PATCH 330/818] Increase leaderboard score width a bit --- osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index 9c444a6f1f..af3cb640bb 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -20,9 +20,9 @@ namespace osu.Game.Screens.Play.HUD { public class GameplayLeaderboardScore : CompositeDrawable, ILeaderboardScore { - public const float EXTENDED_WIDTH = 235f; + public const float EXTENDED_WIDTH = 255f; - private const float regular_width = 215f; + private const float regular_width = 235f; public const float PANEL_HEIGHT = 35f; From 228acf25a7d1b483841f4c6b1bbc4ab46c15aad6 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 18 Dec 2020 13:13:31 +0300 Subject: [PATCH 331/818] Add test case creating leaderboard scores with existing users --- .../Gameplay/TestSceneGameplayLeaderboard.cs | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs index ff15e1d2dc..ca61672ef9 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs @@ -39,7 +39,7 @@ namespace osu.Game.Tests.Visual.Gameplay playerScore.Value = 1222333; }); - AddStep("add local player", () => createLeaderboardScore(playerScore, "You", true)); + AddStep("add local player", () => createLeaderboardScore(playerScore, new User { Username = "You", Id = 3 }, true)); AddSliderStep("set player score", 50, 5000000, 1222333, v => playerScore.Value = v); } @@ -49,8 +49,8 @@ namespace osu.Game.Tests.Visual.Gameplay var player2Score = new BindableDouble(1234567); var player3Score = new BindableDouble(1111111); - AddStep("add player 2", () => createLeaderboardScore(player2Score, "Player 2")); - AddStep("add player 3", () => createLeaderboardScore(player3Score, "Player 3")); + AddStep("add player 2", () => createLeaderboardScore(player2Score, new User { Username = "Player 2" })); + AddStep("add player 3", () => createLeaderboardScore(player3Score, new User { Username = "Player 3" })); AddAssert("is player 2 position #1", () => leaderboard.CheckPositionByUsername("Player 2", 1)); AddAssert("is player position #2", () => leaderboard.CheckPositionByUsername("You", 2)); @@ -71,12 +71,23 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestRandomScores() { int playerNumber = 1; - AddRepeatStep("add player with random score", () => createLeaderboardScore(new BindableDouble(RNG.Next(0, 5_000_000)), $"Player {playerNumber++}"), 10); + AddRepeatStep("add player with random score", () => createRandomScore(new User { Username = $"Player {playerNumber++}" }), 10); } - private void createLeaderboardScore(BindableDouble score, string username, bool isTracked = false) + [Test] + public void TestExistingUsers() { - var leaderboardScore = leaderboard.AddPlayer(new User { Username = username }, isTracked); + AddStep("add peppy", () => createRandomScore(new User { Username = "peppy", Id = 2 })); + AddStep("add smoogipoo", () => createRandomScore(new User { Username = "smoogipoo", Id = 1040328 })); + AddStep("add flyte", () => createRandomScore(new User { Username = "flyte", Id = 3103765 })); + AddStep("add frenzibyte", () => createRandomScore(new User { Username = "frenzibyte", Id = 14210502 })); + } + + private void createRandomScore(User user) => createLeaderboardScore(new BindableDouble(RNG.Next(0, 5_000_000)), user); + + private void createLeaderboardScore(BindableDouble score, User user, bool isTracked = false) + { + var leaderboardScore = leaderboard.AddPlayer(user, isTracked); leaderboardScore.TotalScore.BindTo(score); } From 122250f454059d9adc231e982bd976e3795e1beb Mon Sep 17 00:00:00 2001 From: Graham Johnson Date: Fri, 18 Dec 2020 10:45:23 -0500 Subject: [PATCH 332/818] replace drag cirle function with dictionary --- .../Edit/Compose/Components/SelectionBox.cs | 61 ++++++------------- 1 file changed, 17 insertions(+), 44 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs index 8ccd4f71e9..85e86499be 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -235,13 +236,27 @@ namespace osu.Game.Screens.Edit.Compose.Components private void addDragHandle(Anchor anchor) => AddInternal(new SelectionBoxDragHandle { Anchor = anchor, - X = getAdjustmentToCenterCircleOnBorder(anchor).X, - Y = getAdjustmentToCenterCircleOnBorder(anchor).Y, + X = dragCircleAdjustments[anchor].X, + Y = dragCircleAdjustments[anchor].Y, HandleDrag = e => OnScale?.Invoke(e.Delta, anchor), OperationStarted = operationStarted, OperationEnded = operationEnded }); + /// + /// Adjust Drag circle to be centered on the center of the border instead of on the edge. + /// + private Dictionary dragCircleAdjustments = new Dictionary(){ + {Anchor.TopLeft, new Vector2(BORDER_RADIUS / 2)}, + {Anchor.CentreLeft, new Vector2(BORDER_RADIUS / 2, 0)}, + {Anchor.BottomLeft, new Vector2(BORDER_RADIUS / 2, -BORDER_RADIUS / 2)}, + {Anchor.TopCentre, new Vector2(0, BORDER_RADIUS / 2)}, + {Anchor.BottomCentre, new Vector2(0, -BORDER_RADIUS / 2)}, + {Anchor.TopRight, new Vector2(-BORDER_RADIUS / 2, BORDER_RADIUS / 2)}, + {Anchor.CentreRight, new Vector2(-BORDER_RADIUS / 2, 0)}, + {Anchor.BottomRight, new Vector2(-BORDER_RADIUS / 2)} + }; + private int activeOperations; private float convertDragEventToAngleOfRotation(DragEvent e) @@ -253,48 +268,6 @@ namespace osu.Game.Screens.Edit.Compose.Components return (endAngle - startAngle) * 180 / MathF.PI; } - /// - /// Adjust Drag circle to be centered on the center of the border instead of on the edge. - /// - /// The part of the rectangle to be adjusted. - /// A 2d vector on how much to adjust the drag circle - private Vector2 getAdjustmentToCenterCircleOnBorder(Anchor anchor) - { - Vector2 adjustment = Vector2.Zero; - - switch (anchor) - { - case Anchor.TopLeft: - case Anchor.CentreLeft: - case Anchor.BottomLeft: - adjustment.X = BORDER_RADIUS / 2; - break; - - case Anchor.TopRight: - case Anchor.CentreRight: - case Anchor.BottomRight: - adjustment.X = -BORDER_RADIUS / 2; - break; - } - - switch (anchor) - { - case Anchor.TopLeft: - case Anchor.TopCentre: - case Anchor.TopRight: - adjustment.Y = BORDER_RADIUS / 2; - break; - - case Anchor.BottomLeft: - case Anchor.BottomCentre: - case Anchor.BottomRight: - adjustment.Y = -BORDER_RADIUS / 2; - break; - } - - return adjustment; - } - private void operationEnded() { if (--activeOperations == 0) From 4494bb1eb5a587157f180f3ae14d078a0604194d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 19 Dec 2020 00:15:41 +0900 Subject: [PATCH 333/818] Abstract RoomManager and Multiplayer --- .../Multiplayer/TestSceneMultiScreen.cs | 3 +- .../Navigation/TestSceneScreenNavigation.cs | 5 +- osu.Game/Screens/Menu/MainMenu.cs | 4 +- .../Components/ListingPollingComponent.cs | 68 ++++ .../Screens/Multi/Components/RoomManager.cs | 188 ++++++++++ .../Multi/Components/RoomPollingComponent.cs | 41 +++ .../Components/SelectionPollingComponent.cs | 69 ++++ osu.Game/Screens/Multi/IRoomManager.cs | 2 + osu.Game/Screens/Multi/Multiplayer.cs | 60 +--- osu.Game/Screens/Multi/RoomManager.cs | 337 ------------------ .../Multi/Timeshift/TimeshiftMultiplayer.cs | 48 +++ .../Multi/Timeshift/TimeshiftRoomManager.cs | 20 ++ 12 files changed, 460 insertions(+), 385 deletions(-) create mode 100644 osu.Game/Screens/Multi/Components/ListingPollingComponent.cs create mode 100644 osu.Game/Screens/Multi/Components/RoomManager.cs create mode 100644 osu.Game/Screens/Multi/Components/RoomPollingComponent.cs create mode 100644 osu.Game/Screens/Multi/Components/SelectionPollingComponent.cs delete mode 100644 osu.Game/Screens/Multi/RoomManager.cs create mode 100644 osu.Game/Screens/Multi/Timeshift/TimeshiftMultiplayer.cs create mode 100644 osu.Game/Screens/Multi/Timeshift/TimeshiftRoomManager.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiScreen.cs index 3924b0333f..0390b995e1 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiScreen.cs @@ -4,6 +4,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Game.Overlays; +using osu.Game.Screens.Multi.Timeshift; namespace osu.Game.Tests.Visual.Multiplayer { @@ -17,7 +18,7 @@ namespace osu.Game.Tests.Visual.Multiplayer public TestSceneMultiScreen() { - Screens.Multi.Multiplayer multi = new Screens.Multi.Multiplayer(); + var multi = new TimeshiftMultiplayer(); AddStep("show", () => LoadScreen(multi)); AddUntilStep("wait for loaded", () => multi.IsLoaded); diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index d87854a7ea..43f97d8ace 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -11,6 +11,7 @@ using osu.Game.Beatmaps; using osu.Game.Overlays; using osu.Game.Overlays.Mods; using osu.Game.Overlays.Toolbar; +using osu.Game.Screens.Multi.Timeshift; using osu.Game.Screens.Play; using osu.Game.Screens.Select; using osu.Game.Screens.Select.Options; @@ -107,14 +108,14 @@ namespace osu.Game.Tests.Visual.Navigation [Test] public void TestExitMultiWithEscape() { - PushAndConfirm(() => new Screens.Multi.Multiplayer()); + PushAndConfirm(() => new TimeshiftMultiplayer()); exitViaEscapeAndConfirm(); } [Test] public void TestExitMultiWithBackButton() { - PushAndConfirm(() => new Screens.Multi.Multiplayer()); + PushAndConfirm(() => new TimeshiftMultiplayer()); exitViaBackButtonAndConfirm(); } diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index c3ecd75963..b781c347f0 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -17,7 +17,7 @@ using osu.Game.Online.API; using osu.Game.Overlays; using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Edit; -using osu.Game.Screens.Multi; +using osu.Game.Screens.Multi.Timeshift; using osu.Game.Screens.Select; namespace osu.Game.Screens.Menu @@ -104,7 +104,7 @@ namespace osu.Game.Screens.Menu this.Push(new Editor()); }, OnSolo = onSolo, - OnMulti = delegate { this.Push(new Multiplayer()); }, + OnMulti = delegate { this.Push(new TimeshiftMultiplayer()); }, OnExit = confirmAndExit, } } diff --git a/osu.Game/Screens/Multi/Components/ListingPollingComponent.cs b/osu.Game/Screens/Multi/Components/ListingPollingComponent.cs new file mode 100644 index 0000000000..e22f09779e --- /dev/null +++ b/osu.Game/Screens/Multi/Components/ListingPollingComponent.cs @@ -0,0 +1,68 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Threading.Tasks; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Game.Online.Multiplayer; +using osu.Game.Screens.Multi.Lounge.Components; + +namespace osu.Game.Screens.Multi.Components +{ + /// + /// A that polls for the lounge listing. + /// + public class ListingPollingComponent : RoomPollingComponent + { + [Resolved] + private Bindable currentFilter { get; set; } + + [Resolved] + private Bindable selectedRoom { get; set; } + + [BackgroundDependencyLoader] + private void load() + { + currentFilter.BindValueChanged(_ => + { + if (IsLoaded) + PollImmediately(); + }); + } + + private GetRoomsRequest pollReq; + + protected override Task Poll() + { + if (!API.IsLoggedIn) + return base.Poll(); + + var tcs = new TaskCompletionSource(); + + pollReq?.Cancel(); + pollReq = new GetRoomsRequest(currentFilter.Value.Status, currentFilter.Value.Category); + + pollReq.Success += result => + { + for (int i = 0; i < result.Count; i++) + { + if (result[i].RoomID.Value == selectedRoom.Value?.RoomID.Value) + { + // The listing request always has less information than the opened room, so don't include it. + result[i] = selectedRoom.Value; + break; + } + } + + NotifyRoomsReceived(result); + tcs.SetResult(true); + }; + + pollReq.Failure += _ => tcs.SetResult(false); + + API.Queue(pollReq); + + return tcs.Task; + } + } +} diff --git a/osu.Game/Screens/Multi/Components/RoomManager.cs b/osu.Game/Screens/Multi/Components/RoomManager.cs new file mode 100644 index 0000000000..46941dc58e --- /dev/null +++ b/osu.Game/Screens/Multi/Components/RoomManager.cs @@ -0,0 +1,188 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Logging; +using osu.Game.Beatmaps; +using osu.Game.Online.API; +using osu.Game.Online.Multiplayer; +using osu.Game.Rulesets; + +namespace osu.Game.Screens.Multi.Components +{ + public abstract class RoomManager : CompositeDrawable, IRoomManager + { + public event Action RoomsUpdated; + + private readonly BindableList rooms = new BindableList(); + + public Bindable InitialRoomsReceived { get; } = new Bindable(); + + public IBindableList Rooms => rooms; + + [Resolved] + private RulesetStore rulesets { get; set; } + + [Resolved] + private BeatmapManager beatmaps { get; set; } + + [Resolved] + private IAPIProvider api { get; set; } + + private Room joinedRoom; + + protected RoomManager() + { + RelativeSizeAxes = Axes.Both; + + InternalChildren = CreatePollingComponents().Select(p => + { + p.InitialRoomsReceived.BindTo(InitialRoomsReceived); + p.RoomsReceived = onRoomsReceived; + return p; + }).ToList(); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + PartRoom(); + } + + public virtual void CreateRoom(Room room, Action onSuccess = null, Action onError = null) + { + room.Host.Value = api.LocalUser.Value; + + var req = new CreateRoomRequest(room); + + req.Success += result => + { + joinedRoom = room; + + update(room, result); + addRoom(room); + + RoomsUpdated?.Invoke(); + onSuccess?.Invoke(room); + }; + + req.Failure += exception => + { + if (req.Result != null) + onError?.Invoke(req.Result.Error); + else + Logger.Log($"Failed to create the room: {exception}", level: LogLevel.Important); + }; + + api.Queue(req); + } + + private JoinRoomRequest currentJoinRoomRequest; + + public virtual void JoinRoom(Room room, Action onSuccess = null, Action onError = null) + { + currentJoinRoomRequest?.Cancel(); + currentJoinRoomRequest = new JoinRoomRequest(room); + + currentJoinRoomRequest.Success += () => + { + joinedRoom = room; + onSuccess?.Invoke(room); + }; + + currentJoinRoomRequest.Failure += exception => + { + if (!(exception is OperationCanceledException)) + Logger.Log($"Failed to join room: {exception}", level: LogLevel.Important); + onError?.Invoke(exception.ToString()); + }; + + api.Queue(currentJoinRoomRequest); + } + + public void PartRoom() + { + currentJoinRoomRequest?.Cancel(); + + if (joinedRoom == null) + return; + + api.Queue(new PartRoomRequest(joinedRoom)); + joinedRoom = null; + } + + private readonly HashSet ignoredRooms = new HashSet(); + + private void onRoomsReceived(List received) + { + // Remove past matches + foreach (var r in rooms.ToList()) + { + if (received.All(e => e.RoomID.Value != r.RoomID.Value)) + rooms.Remove(r); + } + + for (int i = 0; i < received.Count; i++) + { + var room = received[i]; + + Debug.Assert(room.RoomID.Value != null); + + if (ignoredRooms.Contains(room.RoomID.Value.Value)) + continue; + + room.Position.Value = i; + + try + { + update(room, room); + addRoom(room); + } + catch (Exception ex) + { + Logger.Error(ex, $"Failed to update room: {room.Name.Value}."); + + ignoredRooms.Add(room.RoomID.Value.Value); + rooms.Remove(room); + } + } + + RoomsUpdated?.Invoke(); + } + + /// + /// Updates a local with a remote copy. + /// + /// The local to update. + /// The remote to update with. + private void update(Room local, Room remote) + { + foreach (var pi in remote.Playlist) + pi.MapObjects(beatmaps, rulesets); + + local.CopyFrom(remote); + } + + /// + /// Adds a to the list of available rooms. + /// + /// The to add. + private void addRoom(Room room) + { + var existing = rooms.FirstOrDefault(e => e.RoomID.Value == room.RoomID.Value); + if (existing == null) + rooms.Add(room); + else + existing.CopyFrom(room); + } + + protected abstract RoomPollingComponent[] CreatePollingComponents(); + } +} diff --git a/osu.Game/Screens/Multi/Components/RoomPollingComponent.cs b/osu.Game/Screens/Multi/Components/RoomPollingComponent.cs new file mode 100644 index 0000000000..5430d54644 --- /dev/null +++ b/osu.Game/Screens/Multi/Components/RoomPollingComponent.cs @@ -0,0 +1,41 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Game.Online; +using osu.Game.Online.API; +using osu.Game.Online.Multiplayer; + +namespace osu.Game.Screens.Multi.Components +{ + public abstract class RoomPollingComponent : PollingComponent + { + public Action> RoomsReceived; + + /// + /// The time in milliseconds to wait between polls. + /// Setting to zero stops all polling. + /// + public new readonly Bindable TimeBetweenPolls = new Bindable(); + + public IBindable InitialRoomsReceived => initialRoomsReceived; + private readonly Bindable initialRoomsReceived = new Bindable(); + + [Resolved] + protected IAPIProvider API { get; private set; } + + protected RoomPollingComponent() + { + TimeBetweenPolls.BindValueChanged(time => base.TimeBetweenPolls = time.NewValue); + } + + protected void NotifyRoomsReceived(List rooms) + { + initialRoomsReceived.Value = true; + RoomsReceived?.Invoke(rooms); + } + } +} diff --git a/osu.Game/Screens/Multi/Components/SelectionPollingComponent.cs b/osu.Game/Screens/Multi/Components/SelectionPollingComponent.cs new file mode 100644 index 0000000000..544d5b2388 --- /dev/null +++ b/osu.Game/Screens/Multi/Components/SelectionPollingComponent.cs @@ -0,0 +1,69 @@ +// 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.Threading.Tasks; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Game.Online.Multiplayer; + +namespace osu.Game.Screens.Multi.Components +{ + /// + /// A that polls for the currently-selected room. + /// + public class SelectionPollingComponent : RoomPollingComponent + { + [Resolved] + private Bindable selectedRoom { get; set; } + + [Resolved] + private IRoomManager roomManager { get; set; } + + [BackgroundDependencyLoader] + private void load() + { + selectedRoom.BindValueChanged(_ => + { + if (IsLoaded) + PollImmediately(); + }); + } + + private GetRoomRequest pollReq; + + protected override Task Poll() + { + if (!API.IsLoggedIn) + return base.Poll(); + + if (selectedRoom.Value?.RoomID.Value == null) + return base.Poll(); + + var tcs = new TaskCompletionSource(); + + pollReq?.Cancel(); + pollReq = new GetRoomRequest(selectedRoom.Value.RoomID.Value.Value); + + pollReq.Success += result => + { + var rooms = new List(roomManager.Rooms); + + int index = rooms.FindIndex(r => r.RoomID == result.RoomID); + if (index < 0) + return; + + rooms[index] = result; + + NotifyRoomsReceived(rooms); + tcs.SetResult(true); + }; + + pollReq.Failure += _ => tcs.SetResult(false); + + API.Queue(pollReq); + + return tcs.Task; + } + } +} diff --git a/osu.Game/Screens/Multi/IRoomManager.cs b/osu.Game/Screens/Multi/IRoomManager.cs index bf75843c3e..3d18edcd71 100644 --- a/osu.Game/Screens/Multi/IRoomManager.cs +++ b/osu.Game/Screens/Multi/IRoomManager.cs @@ -2,11 +2,13 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Online.Multiplayer; namespace osu.Game.Screens.Multi { + [Cached(typeof(IRoomManager))] public interface IRoomManager { /// diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index a323faeea1..5f61c5e635 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.cs @@ -8,7 +8,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Logging; using osu.Framework.Screens; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; @@ -30,7 +29,7 @@ using osuTK; namespace osu.Game.Screens.Multi { [Cached] - public class Multiplayer : OsuScreen + public abstract class Multiplayer : OsuScreen { public override bool CursorVisible => (screenStack.CurrentScreen as IMultiplayerSubScreen)?.CursorVisible ?? true; @@ -46,6 +45,9 @@ namespace osu.Game.Screens.Multi private readonly IBindable isIdle = new BindableBool(); + [Cached(Type = typeof(IRoomManager))] + protected IRoomManager RoomManager { get; private set; } + [Cached] private readonly Bindable selectedRoom = new Bindable(); @@ -55,9 +57,6 @@ namespace osu.Game.Screens.Multi [Resolved(CanBeNull = true)] private MusicController music { get; set; } - [Cached(Type = typeof(IRoomManager))] - private RoomManager roomManager; - [Resolved] private OsuGameBase game { get; set; } @@ -70,7 +69,7 @@ namespace osu.Game.Screens.Multi private readonly Drawable header; private readonly Drawable headerBackground; - public Multiplayer() + protected Multiplayer() { Anchor = Anchor.Centre; Origin = Anchor.Centre; @@ -82,7 +81,7 @@ namespace osu.Game.Screens.Multi InternalChild = waves = new MultiplayerWaveContainer { RelativeSizeAxes = Axes.Both, - Children = new Drawable[] + Children = new[] { new Box { @@ -137,7 +136,7 @@ namespace osu.Game.Screens.Multi Origin = Anchor.TopRight, Action = () => CreateRoom() }, - roomManager = new RoomManager() + (Drawable)(RoomManager = CreateRoomManager()) } }; @@ -168,7 +167,7 @@ namespace osu.Game.Screens.Multi protected override void LoadComplete() { base.LoadComplete(); - isIdle.BindValueChanged(idle => updatePollingRate(idle.NewValue), true); + isIdle.BindValueChanged(idle => UpdatePollingRate(idle.NewValue), true); } protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) @@ -178,36 +177,7 @@ namespace osu.Game.Screens.Multi return dependencies; } - private void updatePollingRate(bool idle) - { - if (!this.IsCurrentScreen()) - { - roomManager.TimeBetweenListingPolls = 0; - roomManager.TimeBetweenSelectionPolls = 0; - } - else - { - switch (screenStack.CurrentScreen) - { - case LoungeSubScreen _: - roomManager.TimeBetweenListingPolls = idle ? 120000 : 15000; - roomManager.TimeBetweenSelectionPolls = idle ? 120000 : 15000; - break; - - case MatchSubScreen _: - roomManager.TimeBetweenListingPolls = 0; - roomManager.TimeBetweenSelectionPolls = idle ? 30000 : 5000; - break; - - default: - roomManager.TimeBetweenListingPolls = 0; - roomManager.TimeBetweenSelectionPolls = 0; - break; - } - } - - Logger.Log($"Polling adjusted (listing: {roomManager.TimeBetweenListingPolls}, selection: {roomManager.TimeBetweenSelectionPolls})"); - } + protected abstract void UpdatePollingRate(bool isIdle); private void forcefullyExit() { @@ -241,7 +211,7 @@ namespace osu.Game.Screens.Multi beginHandlingTrack(); - updatePollingRate(isIdle.Value); + UpdatePollingRate(isIdle.Value); } public override void OnSuspending(IScreen next) @@ -251,12 +221,12 @@ namespace osu.Game.Screens.Multi endHandlingTrack(); - updatePollingRate(isIdle.Value); + UpdatePollingRate(isIdle.Value); } public override bool OnExiting(IScreen next) { - roomManager.PartRoom(); + RoomManager.PartRoom(); waves.Hide(); @@ -344,12 +314,14 @@ namespace osu.Game.Screens.Multi if (newScreen is IOsuScreen newOsuScreen) ((IBindable)Activity).BindTo(newOsuScreen.Activity); - updatePollingRate(isIdle.Value); + UpdatePollingRate(isIdle.Value); createButton.FadeTo(newScreen is LoungeSubScreen ? 1 : 0, 200); updateTrack(); } + protected IScreen CurrentSubScreen => screenStack.CurrentScreen; + private void updateTrack(ValueChangedEvent _ = null) { if (screenStack.CurrentScreen is MatchSubScreen) @@ -381,6 +353,8 @@ namespace osu.Game.Screens.Multi } } + protected abstract IRoomManager CreateRoomManager(); + private class MultiplayerWaveContainer : WaveContainer { protected override bool StartHidden => true; diff --git a/osu.Game/Screens/Multi/RoomManager.cs b/osu.Game/Screens/Multi/RoomManager.cs deleted file mode 100644 index fb0cf73bb9..0000000000 --- a/osu.Game/Screens/Multi/RoomManager.cs +++ /dev/null @@ -1,337 +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.Diagnostics; -using System.Linq; -using System.Threading.Tasks; -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Logging; -using osu.Game.Beatmaps; -using osu.Game.Online; -using osu.Game.Online.API; -using osu.Game.Online.Multiplayer; -using osu.Game.Rulesets; -using osu.Game.Screens.Multi.Lounge.Components; - -namespace osu.Game.Screens.Multi -{ - public class RoomManager : CompositeDrawable, IRoomManager - { - public event Action RoomsUpdated; - - private readonly BindableList rooms = new BindableList(); - - public Bindable InitialRoomsReceived { get; } = new Bindable(); - - public IBindableList Rooms => rooms; - - public double TimeBetweenListingPolls - { - get => listingPollingComponent.TimeBetweenPolls; - set => listingPollingComponent.TimeBetweenPolls = value; - } - - public double TimeBetweenSelectionPolls - { - get => selectionPollingComponent.TimeBetweenPolls; - set => selectionPollingComponent.TimeBetweenPolls = value; - } - - [Resolved] - private RulesetStore rulesets { get; set; } - - [Resolved] - private BeatmapManager beatmaps { get; set; } - - [Resolved] - private IAPIProvider api { get; set; } - - [Resolved] - private Bindable selectedRoom { get; set; } - - private readonly ListingPollingComponent listingPollingComponent; - private readonly SelectionPollingComponent selectionPollingComponent; - - private Room joinedRoom; - - public RoomManager() - { - RelativeSizeAxes = Axes.Both; - - InternalChildren = new Drawable[] - { - listingPollingComponent = new ListingPollingComponent - { - InitialRoomsReceived = { BindTarget = InitialRoomsReceived }, - RoomsReceived = onListingReceived - }, - selectionPollingComponent = new SelectionPollingComponent { RoomReceived = onSelectedRoomReceived } - }; - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - PartRoom(); - } - - public void CreateRoom(Room room, Action onSuccess = null, Action onError = null) - { - room.Host.Value = api.LocalUser.Value; - - var req = new CreateRoomRequest(room); - - req.Success += result => - { - joinedRoom = room; - - update(room, result); - addRoom(room); - - RoomsUpdated?.Invoke(); - onSuccess?.Invoke(room); - }; - - req.Failure += exception => - { - if (req.Result != null) - onError?.Invoke(req.Result.Error); - else - Logger.Log($"Failed to create the room: {exception}", level: LogLevel.Important); - }; - - api.Queue(req); - } - - private JoinRoomRequest currentJoinRoomRequest; - - public void JoinRoom(Room room, Action onSuccess = null, Action onError = null) - { - currentJoinRoomRequest?.Cancel(); - currentJoinRoomRequest = new JoinRoomRequest(room); - - currentJoinRoomRequest.Success += () => - { - joinedRoom = room; - onSuccess?.Invoke(room); - }; - - currentJoinRoomRequest.Failure += exception => - { - if (!(exception is OperationCanceledException)) - Logger.Log($"Failed to join room: {exception}", level: LogLevel.Important); - onError?.Invoke(exception.ToString()); - }; - - api.Queue(currentJoinRoomRequest); - } - - public void PartRoom() - { - currentJoinRoomRequest?.Cancel(); - - if (joinedRoom == null) - return; - - api.Queue(new PartRoomRequest(joinedRoom)); - joinedRoom = null; - } - - private readonly HashSet ignoredRooms = new HashSet(); - - /// - /// Invoked when the listing of all s is received from the server. - /// - /// The listing. - private void onListingReceived(List listing) - { - // Remove past matches - foreach (var r in rooms.ToList()) - { - if (listing.All(e => e.RoomID.Value != r.RoomID.Value)) - rooms.Remove(r); - } - - for (int i = 0; i < listing.Count; i++) - { - if (selectedRoom.Value?.RoomID?.Value == listing[i].RoomID.Value) - { - // The listing request contains less data than the selection request, so data from the selection request is always preferred while the room is selected. - continue; - } - - var room = listing[i]; - - Debug.Assert(room.RoomID.Value != null); - - if (ignoredRooms.Contains(room.RoomID.Value.Value)) - continue; - - room.Position.Value = i; - - try - { - update(room, room); - addRoom(room); - } - catch (Exception ex) - { - Logger.Error(ex, $"Failed to update room: {room.Name.Value}."); - - ignoredRooms.Add(room.RoomID.Value.Value); - rooms.Remove(room); - } - } - - RoomsUpdated?.Invoke(); - } - - /// - /// Invoked when a is received from the server. - /// - /// The received . - private void onSelectedRoomReceived(Room toUpdate) - { - foreach (var room in rooms) - { - if (room.RoomID.Value == toUpdate.RoomID.Value) - { - toUpdate.Position.Value = room.Position.Value; - update(room, toUpdate); - break; - } - } - } - - /// - /// Updates a local with a remote copy. - /// - /// The local to update. - /// The remote to update with. - private void update(Room local, Room remote) - { - foreach (var pi in remote.Playlist) - pi.MapObjects(beatmaps, rulesets); - - local.CopyFrom(remote); - } - - /// - /// Adds a to the list of available rooms. - /// - /// The to add. - private void addRoom(Room room) - { - var existing = rooms.FirstOrDefault(e => e.RoomID.Value == room.RoomID.Value); - if (existing == null) - rooms.Add(room); - else - existing.CopyFrom(room); - } - - private class SelectionPollingComponent : PollingComponent - { - public Action RoomReceived; - - [Resolved] - private IAPIProvider api { get; set; } - - [Resolved] - private Bindable selectedRoom { get; set; } - - [BackgroundDependencyLoader] - private void load() - { - selectedRoom.BindValueChanged(_ => - { - if (IsLoaded) - PollImmediately(); - }); - } - - private GetRoomRequest pollReq; - - protected override Task Poll() - { - if (!api.IsLoggedIn) - return base.Poll(); - - if (selectedRoom.Value?.RoomID.Value == null) - return base.Poll(); - - var tcs = new TaskCompletionSource(); - - pollReq?.Cancel(); - pollReq = new GetRoomRequest(selectedRoom.Value.RoomID.Value.Value); - - pollReq.Success += result => - { - RoomReceived?.Invoke(result); - tcs.SetResult(true); - }; - - pollReq.Failure += _ => tcs.SetResult(false); - - api.Queue(pollReq); - - return tcs.Task; - } - } - - private class ListingPollingComponent : PollingComponent - { - public Action> RoomsReceived; - - public readonly Bindable InitialRoomsReceived = new Bindable(); - - [Resolved] - private IAPIProvider api { get; set; } - - [Resolved] - private Bindable currentFilter { get; set; } - - [BackgroundDependencyLoader] - private void load() - { - currentFilter.BindValueChanged(_ => - { - InitialRoomsReceived.Value = false; - - if (IsLoaded) - PollImmediately(); - }); - } - - private GetRoomsRequest pollReq; - - protected override Task Poll() - { - if (!api.IsLoggedIn) - return base.Poll(); - - var tcs = new TaskCompletionSource(); - - pollReq?.Cancel(); - pollReq = new GetRoomsRequest(currentFilter.Value.Status, currentFilter.Value.Category); - - pollReq.Success += result => - { - InitialRoomsReceived.Value = true; - RoomsReceived?.Invoke(result); - tcs.SetResult(true); - }; - - pollReq.Failure += _ => tcs.SetResult(false); - - api.Queue(pollReq); - - return tcs.Task; - } - } - } -} diff --git a/osu.Game/Screens/Multi/Timeshift/TimeshiftMultiplayer.cs b/osu.Game/Screens/Multi/Timeshift/TimeshiftMultiplayer.cs new file mode 100644 index 0000000000..1ff9c670a8 --- /dev/null +++ b/osu.Game/Screens/Multi/Timeshift/TimeshiftMultiplayer.cs @@ -0,0 +1,48 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Logging; +using osu.Framework.Screens; +using osu.Game.Screens.Multi.Lounge; +using osu.Game.Screens.Multi.Match; + +namespace osu.Game.Screens.Multi.Timeshift +{ + public class TimeshiftMultiplayer : Multiplayer + { + protected override void UpdatePollingRate(bool isIdle) + { + var timeshiftManager = (TimeshiftRoomManager)RoomManager; + + if (!this.IsCurrentScreen()) + { + timeshiftManager.TimeBetweenListingPolls.Value = 0; + timeshiftManager.TimeBetweenSelectionPolls.Value = 0; + } + else + { + switch (CurrentSubScreen) + { + case LoungeSubScreen _: + timeshiftManager.TimeBetweenListingPolls.Value = isIdle ? 120000 : 15000; + timeshiftManager.TimeBetweenSelectionPolls.Value = isIdle ? 120000 : 15000; + break; + + case MatchSubScreen _: + timeshiftManager.TimeBetweenListingPolls.Value = 0; + timeshiftManager.TimeBetweenSelectionPolls.Value = isIdle ? 30000 : 5000; + break; + + default: + timeshiftManager.TimeBetweenListingPolls.Value = 0; + timeshiftManager.TimeBetweenSelectionPolls.Value = 0; + break; + } + } + + Logger.Log($"Polling adjusted (listing: {timeshiftManager.TimeBetweenListingPolls.Value}, selection: {timeshiftManager.TimeBetweenSelectionPolls.Value})"); + } + + protected override IRoomManager CreateRoomManager() => new TimeshiftRoomManager(); + } +} diff --git a/osu.Game/Screens/Multi/Timeshift/TimeshiftRoomManager.cs b/osu.Game/Screens/Multi/Timeshift/TimeshiftRoomManager.cs new file mode 100644 index 0000000000..ba96721afc --- /dev/null +++ b/osu.Game/Screens/Multi/Timeshift/TimeshiftRoomManager.cs @@ -0,0 +1,20 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Bindables; +using osu.Game.Screens.Multi.Components; + +namespace osu.Game.Screens.Multi.Timeshift +{ + public class TimeshiftRoomManager : RoomManager + { + public readonly Bindable TimeBetweenListingPolls = new Bindable(); + public readonly Bindable TimeBetweenSelectionPolls = new Bindable(); + + protected override RoomPollingComponent[] CreatePollingComponents() => new RoomPollingComponent[] + { + new ListingPollingComponent { TimeBetweenPolls = { BindTarget = TimeBetweenListingPolls } }, + new SelectionPollingComponent { TimeBetweenPolls = { BindTarget = TimeBetweenSelectionPolls } } + }; + } +} From f4e9703deb20a7abdd303781a17c63e736951136 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 19 Dec 2020 00:52:32 +0900 Subject: [PATCH 334/818] Fix incorrect comparison --- osu.Game/Screens/Multi/Components/SelectionPollingComponent.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Multi/Components/SelectionPollingComponent.cs b/osu.Game/Screens/Multi/Components/SelectionPollingComponent.cs index 544d5b2388..37a190b5e0 100644 --- a/osu.Game/Screens/Multi/Components/SelectionPollingComponent.cs +++ b/osu.Game/Screens/Multi/Components/SelectionPollingComponent.cs @@ -49,7 +49,7 @@ namespace osu.Game.Screens.Multi.Components { var rooms = new List(roomManager.Rooms); - int index = rooms.FindIndex(r => r.RoomID == result.RoomID); + int index = rooms.FindIndex(r => r.RoomID.Value == result.RoomID.Value); if (index < 0) return; From ab9158c306c5b19c6f314967216ab4fe4915fd23 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 18 Dec 2020 20:50:54 +0900 Subject: [PATCH 335/818] Add a stateful multiplayer client --- .../StatefulMultiplayerClient.cs | 389 ++++++++++++++++++ 1 file changed, 389 insertions(+) create mode 100644 osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs diff --git a/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs new file mode 100644 index 0000000000..3e2f435524 --- /dev/null +++ b/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs @@ -0,0 +1,389 @@ +// 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.Diagnostics; +using System.Linq; +using System.Threading.Tasks; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Game.Beatmaps; +using osu.Game.Database; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Online.Multiplayer; +using osu.Game.Online.Multiplayer.RoomStatuses; +using osu.Game.Rulesets; +using osu.Game.Users; +using osu.Game.Utils; + +namespace osu.Game.Online.RealtimeMultiplayer +{ + public abstract class StatefulMultiplayerClient : Component, IMultiplayerClient, IMultiplayerRoomServer + { + /// + /// Invoked when any change occurs to the multiplayer room. + /// + public event Action? RoomChanged; + + /// + /// Invoked when the multiplayer server requests the current beatmap to be loaded into play. + /// + public event Action? LoadRequested; + + /// + /// Invoked when the multiplayer server requests gameplay to be started. + /// + public event Action? MatchStarted; + + /// + /// Invoked when the multiplayer server has finished collating results. + /// + public event Action? ResultsReady; + + /// + /// Whether the is currently connected. + /// + public abstract IBindable IsConnected { get; } + + /// + /// The joined . + /// + public MultiplayerRoom? Room { get; private set; } + + /// + /// The users currently in gameplay. + /// + public readonly BindableList PlayingUsers = new BindableList(); + + [Resolved] + private UserLookupCache userLookupCache { get; set; } = null!; + + [Resolved] + private IAPIProvider api { get; set; } = null!; + + [Resolved] + private RulesetStore rulesets { get; set; } = null!; + + private Room? apiRoom; + private int playlistItemId; // Todo: THIS IS SUPER TEMPORARY!! + + /// + /// Joins the for a given API . + /// + /// The API . + public async Task JoinRoom(Room room) + { + Debug.Assert(Room == null); + Debug.Assert(room.RoomID.Value != null); + + apiRoom = room; + playlistItemId = room.Playlist.SingleOrDefault()?.ID ?? 0; + + Room = await JoinRoom(room.RoomID.Value.Value); + + Debug.Assert(Room != null); + + foreach (var user in Room.Users) + await PopulateUser(user); + + updateLocalRoomSettings(Room.Settings); + } + + /// + /// Joins the with a given ID. + /// + /// The room ID. + /// The joined . + protected abstract Task JoinRoom(long roomId); + + public virtual Task LeaveRoom() + { + if (Room == null) + return Task.CompletedTask; + + apiRoom = null; + Room = null; + + Schedule(() => RoomChanged?.Invoke()); + + return Task.CompletedTask; + } + + /// + /// Change the current settings. + /// + /// + /// A room must have been joined via for this to have any effect. + /// + /// The new room name, if any. + /// The new room playlist item, if any. + public void ChangeSettings(Optional name = default, Optional item = default) + { + if (Room == null) + return; + + // A dummy playlist item filled with the current room settings (except mods). + var existingPlaylistItem = new PlaylistItem + { + Beatmap = + { + Value = new BeatmapInfo + { + OnlineBeatmapID = Room.Settings.BeatmapID, + MD5Hash = Room.Settings.BeatmapChecksum + } + }, + RulesetID = Room.Settings.RulesetID + }; + + var newSettings = new MultiplayerRoomSettings + { + Name = name.GetOr(Room.Settings.Name), + BeatmapID = item.GetOr(existingPlaylistItem).BeatmapID, + BeatmapChecksum = item.GetOr(existingPlaylistItem).Beatmap.Value.MD5Hash, + RulesetID = item.GetOr(existingPlaylistItem).RulesetID, + Mods = item.HasValue ? item.Value!.RequiredMods.Select(m => new APIMod(m)).ToList() : Room.Settings.Mods + }; + + // Make sure there would be a meaningful change in settings. + if (newSettings.Equals(Room.Settings)) + return; + + ChangeSettings(newSettings); + } + + public abstract Task TransferHost(int userId); + + public abstract Task ChangeSettings(MultiplayerRoomSettings settings); + + public abstract Task ChangeState(MultiplayerUserState newState); + + public abstract Task StartMatch(); + + Task IMultiplayerClient.RoomStateChanged(MultiplayerRoomState state) + { + Schedule(() => + { + if (Room == null) + return; + + Debug.Assert(apiRoom != null); + + Room.State = state; + + switch (state) + { + case MultiplayerRoomState.Open: + apiRoom.Status.Value = new RoomStatusOpen(); + break; + + case MultiplayerRoomState.Playing: + apiRoom.Status.Value = new RoomStatusPlaying(); + break; + + case MultiplayerRoomState.Closed: + apiRoom.Status.Value = new RoomStatusEnded(); + break; + } + + RoomChanged?.Invoke(); + }); + + return Task.CompletedTask; + } + + async Task IMultiplayerClient.UserJoined(MultiplayerRoomUser user) + { + await PopulateUser(user); + + Schedule(() => + { + if (Room == null) + return; + + Room.Users.Add(user); + + RoomChanged?.Invoke(); + }); + } + + Task IMultiplayerClient.UserLeft(MultiplayerRoomUser user) + { + Schedule(() => + { + if (Room == null) + return; + + Room.Users.Remove(user); + PlayingUsers.Remove(user.UserID); + + RoomChanged?.Invoke(); + }); + + return Task.CompletedTask; + } + + Task IMultiplayerClient.HostChanged(int userId) + { + Schedule(() => + { + if (Room == null) + return; + + Debug.Assert(apiRoom != null); + + var user = Room.Users.FirstOrDefault(u => u.UserID == userId); + + Room.Host = user; + apiRoom.Host.Value = user?.User; + + RoomChanged?.Invoke(); + }); + + return Task.CompletedTask; + } + + Task IMultiplayerClient.SettingsChanged(MultiplayerRoomSettings newSettings) + { + updateLocalRoomSettings(newSettings); + return Task.CompletedTask; + } + + Task IMultiplayerClient.UserStateChanged(int userId, MultiplayerUserState state) + { + Schedule(() => + { + if (Room == null) + return; + + Room.Users.Single(u => u.UserID == userId).State = state; + + if (state != MultiplayerUserState.Playing) + PlayingUsers.Remove(userId); + + RoomChanged?.Invoke(); + }); + + return Task.CompletedTask; + } + + Task IMultiplayerClient.LoadRequested() + { + Schedule(() => + { + if (Room == null) + return; + + LoadRequested?.Invoke(); + }); + + return Task.CompletedTask; + } + + Task IMultiplayerClient.MatchStarted() + { + Debug.Assert(Room != null); + var players = Room.Users.Where(u => u.State == MultiplayerUserState.Playing).Select(u => u.UserID).ToList(); + + Schedule(() => + { + if (Room == null) + return; + + PlayingUsers.AddRange(players); + + MatchStarted?.Invoke(); + }); + + return Task.CompletedTask; + } + + Task IMultiplayerClient.ResultsReady() + { + Schedule(() => + { + if (Room == null) + return; + + ResultsReady?.Invoke(); + }); + + return Task.CompletedTask; + } + + /// + /// Populates the for a given . + /// + /// The to populate. + protected async Task PopulateUser(MultiplayerRoomUser multiplayerUser) => multiplayerUser.User ??= await userLookupCache.GetUserAsync(multiplayerUser.UserID); + + /// + /// Updates the local room settings with the given . + /// + /// + /// This updates both the joined and the respective API . + /// + /// The new to update from. + private void updateLocalRoomSettings(MultiplayerRoomSettings settings) + { + if (Room == null) + return; + + // Update a few instantaneously properties of the room. + Schedule(() => + { + if (Room == null) + return; + + Debug.Assert(apiRoom != null); + + Room.Settings = settings; + apiRoom.Name.Value = Room.Settings.Name; + + // The playlist update is delayed until an online beatmap lookup (below) succeeds. + // In-order for the client to not display an outdated beatmap, the playlist is forcefully cleared here. + apiRoom.Playlist.Clear(); + + RoomChanged?.Invoke(); + }); + + var req = new GetBeatmapSetRequest(settings.BeatmapID, BeatmapSetLookupType.BeatmapId); + req.Success += res => + { + var beatmapSet = res.ToBeatmapSet(rulesets); + + var beatmap = beatmapSet.Beatmaps.Single(b => b.OnlineBeatmapID == settings.BeatmapID); + beatmap.MD5Hash = settings.BeatmapChecksum; + + var ruleset = rulesets.GetRuleset(settings.RulesetID); + var mods = settings.Mods.Select(m => m.ToMod(ruleset.CreateInstance())); + + PlaylistItem playlistItem = new PlaylistItem + { + ID = playlistItemId, + Beatmap = { Value = beatmap }, + Ruleset = { Value = ruleset }, + }; + + playlistItem.RequiredMods.AddRange(mods); + + Schedule(() => + { + if (Room == null || !Room.Settings.Equals(settings)) + return; + + Debug.Assert(apiRoom != null); + + apiRoom.Playlist.Clear(); // Clearing should be unnecessary, but here for sanity. + apiRoom.Playlist.Add(playlistItem); + }); + }; + + api.Queue(req); + } + } +} From 9ceb090f04d682f583fdc2d06fe1462f12a9c8ec Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 19 Dec 2020 00:17:50 +0900 Subject: [PATCH 336/818] Fix ambiguous reference --- .../Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs index 3e2f435524..60960f4929 100644 --- a/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs @@ -117,7 +117,7 @@ namespace osu.Game.Online.RealtimeMultiplayer /// Change the current settings. /// /// - /// A room must have been joined via for this to have any effect. + /// A room must be joined for this to have any effect. /// /// The new room name, if any. /// The new room playlist item, if any. From cf2340cafb8b7ce964935a410f3bd3af49041458 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 19 Dec 2020 00:18:41 +0900 Subject: [PATCH 337/818] Add a realtime room manager --- .../RealtimeRoomManager.cs | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs new file mode 100644 index 0000000000..a86e924c85 --- /dev/null +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs @@ -0,0 +1,45 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Diagnostics; +using System.Threading.Tasks; +using osu.Framework.Allocation; +using osu.Framework.Logging; +using osu.Game.Online.Multiplayer; +using osu.Game.Online.RealtimeMultiplayer; +using osu.Game.Screens.Multi.Components; + +namespace osu.Game.Screens.Multi.RealtimeMultiplayer +{ + public class RealtimeRoomManager : RoomManager + { + [Resolved] + private StatefulMultiplayerClient multiplayerClient { get; set; } + + public override void CreateRoom(Room room, Action onSuccess = null, Action onError = null) + => base.CreateRoom(room, r => joinMultiplayerRoom(r, onSuccess), onError); + + public override void JoinRoom(Room room, Action onSuccess = null, Action onError = null) + => base.JoinRoom(room, r => joinMultiplayerRoom(r, onSuccess), onError); + + private void joinMultiplayerRoom(Room room, Action onSuccess = null) + { + Debug.Assert(room.RoomID.Value != null); + + var joinTask = multiplayerClient.JoinRoom(room); + joinTask.ContinueWith(_ => onSuccess?.Invoke(room)); + joinTask.ContinueWith(t => + { + PartRoom(); + if (t.Exception != null) + Logger.Error(t.Exception, "Failed to join multiplayer room."); + }, TaskContinuationOptions.NotOnRanToCompletion); + } + + protected override RoomPollingComponent[] CreatePollingComponents() => new RoomPollingComponent[] + { + new ListingPollingComponent() + }; + } +} From a6520d3d446230cb3c0ef49aa15cdf45ba8b2232 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 19 Dec 2020 01:01:09 +0900 Subject: [PATCH 338/818] Clear rooms and poll only when connected to multiplayer server --- .../Screens/Multi/Components/RoomManager.cs | 6 ++ .../RealtimeRoomManager.cs | 64 ++++++++++++++++++- 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Multi/Components/RoomManager.cs b/osu.Game/Screens/Multi/Components/RoomManager.cs index 46941dc58e..d92427680e 100644 --- a/osu.Game/Screens/Multi/Components/RoomManager.cs +++ b/osu.Game/Screens/Multi/Components/RoomManager.cs @@ -157,6 +157,12 @@ namespace osu.Game.Screens.Multi.Components RoomsUpdated?.Invoke(); } + protected void ClearRooms() + { + rooms.Clear(); + InitialRoomsReceived.Value = false; + } + /// /// Updates a local with a remote copy. /// diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs index a86e924c85..62ea5d5512 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs @@ -5,6 +5,7 @@ using System; using System.Diagnostics; using System.Threading.Tasks; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Logging; using osu.Game.Online.Multiplayer; using osu.Game.Online.RealtimeMultiplayer; @@ -17,6 +18,22 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer [Resolved] private StatefulMultiplayerClient multiplayerClient { get; set; } + public readonly Bindable TimeBetweenListingPolls = new Bindable(); + public readonly Bindable TimeBetweenSelectionPolls = new Bindable(); + private readonly IBindable isConnected = new Bindable(); + + protected override void LoadComplete() + { + base.LoadComplete(); + + isConnected.BindTo(multiplayerClient.IsConnected); + isConnected.BindValueChanged(connected => Schedule(() => + { + if (!connected.NewValue) + ClearRooms(); + })); + } + public override void CreateRoom(Room room, Action onSuccess = null, Action onError = null) => base.CreateRoom(room, r => joinMultiplayerRoom(r, onSuccess), onError); @@ -39,7 +56,52 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer protected override RoomPollingComponent[] CreatePollingComponents() => new RoomPollingComponent[] { - new ListingPollingComponent() + new RealtimeListingPollingComponent + { + TimeBetweenPolls = { BindTarget = TimeBetweenListingPolls }, + AllowPolling = { BindTarget = isConnected } + }, + new RealtimeSelectionPollingComponent + { + TimeBetweenPolls = { BindTarget = TimeBetweenSelectionPolls }, + AllowPolling = { BindTarget = isConnected } + } }; + + private class RealtimeListingPollingComponent : ListingPollingComponent + { + public readonly IBindable AllowPolling = new Bindable(); + + protected override void LoadComplete() + { + base.LoadComplete(); + + AllowPolling.BindValueChanged(_ => + { + if (IsLoaded) + PollImmediately(); + }); + } + + protected override Task Poll() => !AllowPolling.Value ? Task.CompletedTask : base.Poll(); + } + + private class RealtimeSelectionPollingComponent : SelectionPollingComponent + { + public readonly IBindable AllowPolling = new Bindable(); + + protected override void LoadComplete() + { + base.LoadComplete(); + + AllowPolling.BindValueChanged(_ => + { + if (IsLoaded) + PollImmediately(); + }); + } + + protected override Task Poll() => !AllowPolling.Value ? Task.CompletedTask : base.Poll(); + } } } From 1e2163f55e0378604ebe2f083a84b1271a8e7f4d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 19 Dec 2020 01:14:50 +0900 Subject: [PATCH 339/818] Add a testable realtime multiplayer client --- .../TestRealtimeMultiplayerClient.cs | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeMultiplayerClient.cs diff --git a/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeMultiplayerClient.cs b/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeMultiplayerClient.cs new file mode 100644 index 0000000000..2a90f1e744 --- /dev/null +++ b/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeMultiplayerClient.cs @@ -0,0 +1,114 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Game.Online.API; +using osu.Game.Online.RealtimeMultiplayer; +using osu.Game.Users; + +namespace osu.Game.Tests.Visual.RealtimeMultiplayer +{ + public class TestRealtimeMultiplayerClient : StatefulMultiplayerClient + { + public override IBindable IsConnected { get; } = new Bindable(true); + + [Resolved] + private IAPIProvider api { get; set; } = null!; + + public void AddUser(User user) => ((IMultiplayerClient)this).UserJoined(new MultiplayerRoomUser(user.Id) { User = user }); + + public void RemoveUser(User user) + { + Debug.Assert(Room != null); + ((IMultiplayerClient)this).UserLeft(Room.Users.Single(u => u.User == user)); + + Schedule(() => + { + if (Room.Users.Any()) + TransferHost(Room.Users.First().UserID); + }); + } + + public void ChangeUserState(int userId, MultiplayerUserState newState) + { + Debug.Assert(Room != null); + + ((IMultiplayerClient)this).UserStateChanged(userId, newState); + + Schedule(() => + { + switch (newState) + { + case MultiplayerUserState.Loaded: + if (Room.Users.All(u => u.State != MultiplayerUserState.WaitingForLoad)) + { + foreach (var u in Room.Users.Where(u => u.State == MultiplayerUserState.Loaded)) + ChangeUserState(u.UserID, MultiplayerUserState.Playing); + + ((IMultiplayerClient)this).MatchStarted(); + } + + break; + + case MultiplayerUserState.FinishedPlay: + if (Room.Users.All(u => u.State != MultiplayerUserState.Playing)) + { + foreach (var u in Room.Users.Where(u => u.State == MultiplayerUserState.FinishedPlay)) + ChangeUserState(u.UserID, MultiplayerUserState.Results); + + ((IMultiplayerClient)this).ResultsReady(); + } + + break; + } + }); + } + + protected override Task JoinRoom(long roomId) + { + var user = new MultiplayerRoomUser(api.LocalUser.Value.Id) { User = api.LocalUser.Value }; + + var room = new MultiplayerRoom(roomId); + room.Users.Add(user); + + if (room.Users.Count == 1) + room.Host = user; + + return Task.FromResult(room); + } + + public override Task TransferHost(int userId) => ((IMultiplayerClient)this).HostChanged(userId); + + public override async Task ChangeSettings(MultiplayerRoomSettings settings) + { + Debug.Assert(Room != null); + + await ((IMultiplayerClient)this).SettingsChanged(settings); + + foreach (var user in Room.Users.Where(u => u.State == MultiplayerUserState.Ready)) + ChangeUserState(user.UserID, MultiplayerUserState.Idle); + } + + public override Task ChangeState(MultiplayerUserState newState) + { + ChangeUserState(api.LocalUser.Value.Id, newState); + return Task.CompletedTask; + } + + public override async Task StartMatch() + { + Debug.Assert(Room != null); + + foreach (var user in Room.Users.Where(u => u.State == MultiplayerUserState.Ready)) + ChangeUserState(user.UserID, MultiplayerUserState.WaitingForLoad); + + await ((IMultiplayerClient)this).LoadRequested(); + } + } +} From 50a35c0f63222f767678d4b661b93f19e04a2b67 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 19 Dec 2020 01:16:00 +0900 Subject: [PATCH 340/818] Add connection/disconnection capability --- .../RealtimeMultiplayer/TestRealtimeMultiplayerClient.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeMultiplayerClient.cs b/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeMultiplayerClient.cs index 2a90f1e744..bfa8362c7e 100644 --- a/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeMultiplayerClient.cs @@ -16,11 +16,16 @@ namespace osu.Game.Tests.Visual.RealtimeMultiplayer { public class TestRealtimeMultiplayerClient : StatefulMultiplayerClient { - public override IBindable IsConnected { get; } = new Bindable(true); + public override IBindable IsConnected => isConnected; + private readonly Bindable isConnected = new Bindable(true); [Resolved] private IAPIProvider api { get; set; } = null!; + public void Connect() => isConnected.Value = true; + + public void Disconnect() => isConnected.Value = false; + public void AddUser(User user) => ((IMultiplayerClient)this).UserJoined(new MultiplayerRoomUser(user.Id) { User = user }); public void RemoveUser(User user) From c6555c53cc315b6df93cab9d7c56dc2c018ad2ed Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 19 Dec 2020 01:17:24 +0900 Subject: [PATCH 341/818] Add a testable realtime room manager --- .../API/Requests/GetBeatmapSetRequest.cs | 10 +- .../Online/Multiplayer/CreateRoomRequest.cs | 6 +- .../TestRealtimeRoomManager.cs | 91 +++++++++++++++++++ 3 files changed, 99 insertions(+), 8 deletions(-) create mode 100644 osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeRoomManager.cs diff --git a/osu.Game/Online/API/Requests/GetBeatmapSetRequest.cs b/osu.Game/Online/API/Requests/GetBeatmapSetRequest.cs index 8e6deeb3c6..158ae03b8d 100644 --- a/osu.Game/Online/API/Requests/GetBeatmapSetRequest.cs +++ b/osu.Game/Online/API/Requests/GetBeatmapSetRequest.cs @@ -7,16 +7,16 @@ namespace osu.Game.Online.API.Requests { public class GetBeatmapSetRequest : APIRequest { - private readonly int id; - private readonly BeatmapSetLookupType type; + public readonly int ID; + public readonly BeatmapSetLookupType Type; public GetBeatmapSetRequest(int id, BeatmapSetLookupType type = BeatmapSetLookupType.SetId) { - this.id = id; - this.type = type; + ID = id; + Type = type; } - protected override string Target => type == BeatmapSetLookupType.SetId ? $@"beatmapsets/{id}" : $@"beatmapsets/lookup?beatmap_id={id}"; + protected override string Target => Type == BeatmapSetLookupType.SetId ? $@"beatmapsets/{ID}" : $@"beatmapsets/lookup?beatmap_id={ID}"; } public enum BeatmapSetLookupType diff --git a/osu.Game/Online/Multiplayer/CreateRoomRequest.cs b/osu.Game/Online/Multiplayer/CreateRoomRequest.cs index dcb4ed51ea..5be99e9442 100644 --- a/osu.Game/Online/Multiplayer/CreateRoomRequest.cs +++ b/osu.Game/Online/Multiplayer/CreateRoomRequest.cs @@ -10,11 +10,11 @@ namespace osu.Game.Online.Multiplayer { public class CreateRoomRequest : APIRequest { - private readonly Room room; + public readonly Room Room; public CreateRoomRequest(Room room) { - this.room = room; + Room = room; } protected override WebRequest CreateWebRequest() @@ -24,7 +24,7 @@ namespace osu.Game.Online.Multiplayer req.ContentType = "application/json"; req.Method = HttpMethod.Post; - req.AddRaw(JsonConvert.SerializeObject(room)); + req.AddRaw(JsonConvert.SerializeObject(Room)); return req; } diff --git a/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeRoomManager.cs b/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeRoomManager.cs new file mode 100644 index 0000000000..773b72da88 --- /dev/null +++ b/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeRoomManager.cs @@ -0,0 +1,91 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Online.Multiplayer; +using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; +using osu.Game.Screens.Multi.RealtimeMultiplayer; + +namespace osu.Game.Tests.Visual.RealtimeMultiplayer +{ + public class TestRealtimeRoomManager : RealtimeRoomManager + { + [Resolved] + private IAPIProvider api { get; set; } + + [Resolved] + private OsuGameBase game { get; set; } + + private readonly List rooms = new List(); + + protected override void LoadComplete() + { + base.LoadComplete(); + + int currentScoreId = 0; + + ((DummyAPIAccess)api).HandleRequest = req => + { + switch (req) + { + case CreateRoomRequest createRoomRequest: + var createdRoom = new APICreatedRoom(); + + createdRoom.CopyFrom(createRoomRequest.Room); + createdRoom.RoomID.Value = 1; + + rooms.Add(createdRoom); + createRoomRequest.TriggerSuccess(createdRoom); + break; + + case JoinRoomRequest joinRoomRequest: + joinRoomRequest.TriggerSuccess(); + break; + + case PartRoomRequest partRoomRequest: + partRoomRequest.TriggerSuccess(); + break; + + case GetRoomsRequest getRoomsRequest: + getRoomsRequest.TriggerSuccess(rooms); + break; + + case GetBeatmapSetRequest getBeatmapSetRequest: + var onlineReq = new GetBeatmapSetRequest(getBeatmapSetRequest.ID, getBeatmapSetRequest.Type); + onlineReq.Success += res => getBeatmapSetRequest.TriggerSuccess(res); + onlineReq.Failure += e => getBeatmapSetRequest.TriggerFailure(e); + + // Get the online API from the game's dependencies. + game.Dependencies.Get().Queue(onlineReq); + break; + + case CreateRoomScoreRequest createRoomScoreRequest: + createRoomScoreRequest.TriggerSuccess(new APIScoreToken { ID = 1 }); + break; + + case SubmitRoomScoreRequest submitRoomScoreRequest: + submitRoomScoreRequest.TriggerSuccess(new MultiplayerScore + { + ID = currentScoreId++, + Accuracy = 1, + EndedAt = DateTimeOffset.Now, + Passed = true, + Rank = ScoreRank.S, + MaxCombo = 1000, + TotalScore = 1000000, + User = api.LocalUser.Value, + Statistics = new Dictionary() + }); + break; + } + }; + } + + public new void Schedule(Action action) => base.Schedule(action); + } +} From c6da680c803a0d9cf055fbedbd57e7992eed823e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 19 Dec 2020 01:19:08 +0900 Subject: [PATCH 342/818] Add a container for testing purposes --- .../TestRealtimeRoomContainer.cs | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeRoomContainer.cs diff --git a/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeRoomContainer.cs b/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeRoomContainer.cs new file mode 100644 index 0000000000..aa75968cca --- /dev/null +++ b/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeRoomContainer.cs @@ -0,0 +1,40 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Online.RealtimeMultiplayer; +using osu.Game.Screens.Multi.Lounge.Components; +using osu.Game.Screens.Multi.RealtimeMultiplayer; + +namespace osu.Game.Tests.Visual.RealtimeMultiplayer +{ + public class TestRealtimeRoomContainer : Container + { + protected override Container Content => content; + private readonly Container content; + + [Cached(typeof(StatefulMultiplayerClient))] + public readonly TestRealtimeMultiplayerClient Client; + + [Cached(typeof(RealtimeRoomManager))] + public readonly TestRealtimeRoomManager RoomManager; + + [Cached] + public readonly Bindable Filter = new Bindable(new FilterCriteria()); + + public TestRealtimeRoomContainer() + { + RelativeSizeAxes = Axes.Both; + + AddRangeInternal(new Drawable[] + { + Client = new TestRealtimeMultiplayerClient(), + RoomManager = new TestRealtimeRoomManager(), + content = new Container { RelativeSizeAxes = Axes.Both } + }); + } + } +} From 2fc5561b7ec7a4a5c53235203cd65b1c8d6869b8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 19 Dec 2020 01:22:52 +0900 Subject: [PATCH 343/818] Add handling for GetRoomRequest() --- osu.Game/Online/Multiplayer/GetRoomRequest.cs | 6 +++--- .../TestRealtimeRoomManager.cs | 19 ++++++++++++++++++- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/osu.Game/Online/Multiplayer/GetRoomRequest.cs b/osu.Game/Online/Multiplayer/GetRoomRequest.cs index 2907b49f1d..449c2c8e31 100644 --- a/osu.Game/Online/Multiplayer/GetRoomRequest.cs +++ b/osu.Game/Online/Multiplayer/GetRoomRequest.cs @@ -7,13 +7,13 @@ namespace osu.Game.Online.Multiplayer { public class GetRoomRequest : APIRequest { - private readonly int roomId; + public readonly int RoomId; public GetRoomRequest(int roomId) { - this.roomId = roomId; + RoomId = roomId; } - protected override string Target => $"rooms/{roomId}"; + protected override string Target => $"rooms/{RoomId}"; } } diff --git a/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeRoomManager.cs b/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeRoomManager.cs index 773b72da88..cee1c706ae 100644 --- a/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeRoomManager.cs +++ b/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeRoomManager.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; using osu.Game.Online.API; using osu.Game.Online.API.Requests; @@ -52,7 +53,23 @@ namespace osu.Game.Tests.Visual.RealtimeMultiplayer break; case GetRoomsRequest getRoomsRequest: - getRoomsRequest.TriggerSuccess(rooms); + var roomsWithoutParticipants = new List(); + + foreach (var r in rooms) + { + var newRoom = new Room(); + + newRoom.CopyFrom(r); + newRoom.RecentParticipants.Clear(); + + roomsWithoutParticipants.Add(newRoom); + } + + getRoomsRequest.TriggerSuccess(roomsWithoutParticipants); + break; + + case GetRoomRequest getRoomRequest: + getRoomRequest.TriggerSuccess(rooms.Single(r => r.RoomID.Value == getRoomRequest.RoomId)); break; case GetBeatmapSetRequest getBeatmapSetRequest: From 9b0ca8fc3b2b380578202e5e75145d1b88ed9743 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 19 Dec 2020 01:57:30 +0900 Subject: [PATCH 344/818] Make real time room manager not poll while inside a room --- .../Screens/Multi/Components/RoomManager.cs | 14 ++++++------ .../RealtimeRoomManager.cs | 22 +++++++++++++------ 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/osu.Game/Screens/Multi/Components/RoomManager.cs b/osu.Game/Screens/Multi/Components/RoomManager.cs index d92427680e..6e27515849 100644 --- a/osu.Game/Screens/Multi/Components/RoomManager.cs +++ b/osu.Game/Screens/Multi/Components/RoomManager.cs @@ -27,6 +27,8 @@ namespace osu.Game.Screens.Multi.Components public IBindableList Rooms => rooms; + protected Room JoinedRoom { get; private set; } + [Resolved] private RulesetStore rulesets { get; set; } @@ -36,8 +38,6 @@ namespace osu.Game.Screens.Multi.Components [Resolved] private IAPIProvider api { get; set; } - private Room joinedRoom; - protected RoomManager() { RelativeSizeAxes = Axes.Both; @@ -64,7 +64,7 @@ namespace osu.Game.Screens.Multi.Components req.Success += result => { - joinedRoom = room; + JoinedRoom = room; update(room, result); addRoom(room); @@ -93,7 +93,7 @@ namespace osu.Game.Screens.Multi.Components currentJoinRoomRequest.Success += () => { - joinedRoom = room; + JoinedRoom = room; onSuccess?.Invoke(room); }; @@ -111,11 +111,11 @@ namespace osu.Game.Screens.Multi.Components { currentJoinRoomRequest?.Cancel(); - if (joinedRoom == null) + if (JoinedRoom == null) return; - api.Queue(new PartRoomRequest(joinedRoom)); - joinedRoom = null; + api.Queue(new PartRoomRequest(JoinedRoom)); + JoinedRoom = null; } private readonly HashSet ignoredRooms = new HashSet(); diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs index 62ea5d5512..69addde2a6 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs @@ -21,17 +21,16 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer public readonly Bindable TimeBetweenListingPolls = new Bindable(); public readonly Bindable TimeBetweenSelectionPolls = new Bindable(); private readonly IBindable isConnected = new Bindable(); + private readonly Bindable allowPolling = new Bindable(); protected override void LoadComplete() { base.LoadComplete(); isConnected.BindTo(multiplayerClient.IsConnected); - isConnected.BindValueChanged(connected => Schedule(() => - { - if (!connected.NewValue) - ClearRooms(); - })); + isConnected.BindValueChanged(_ => Schedule(updatePolling), true); + + updatePolling(); } public override void CreateRoom(Room room, Action onSuccess = null, Action onError = null) @@ -54,17 +53,26 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer }, TaskContinuationOptions.NotOnRanToCompletion); } + private void updatePolling() + { + if (!isConnected.Value) + ClearRooms(); + + // Don't poll when not connected or when a room has been joined. + allowPolling.Value = isConnected.Value && JoinedRoom == null; + } + protected override RoomPollingComponent[] CreatePollingComponents() => new RoomPollingComponent[] { new RealtimeListingPollingComponent { TimeBetweenPolls = { BindTarget = TimeBetweenListingPolls }, - AllowPolling = { BindTarget = isConnected } + AllowPolling = { BindTarget = allowPolling } }, new RealtimeSelectionPollingComponent { TimeBetweenPolls = { BindTarget = TimeBetweenSelectionPolls }, - AllowPolling = { BindTarget = isConnected } + AllowPolling = { BindTarget = allowPolling } } }; From 7d1fe7955e633e332e2d43fafc1dd24f313e4ee5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 19 Dec 2020 01:57:40 +0900 Subject: [PATCH 345/818] Small improvements to testable room manager --- .../RealtimeMultiplayer/TestRealtimeRoomManager.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeRoomManager.cs b/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeRoomManager.cs index cee1c706ae..0d1314fb51 100644 --- a/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeRoomManager.cs +++ b/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeRoomManager.cs @@ -5,11 +5,13 @@ using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.Multiplayer; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; +using osu.Game.Screens.Multi.Lounge.Components; using osu.Game.Screens.Multi.RealtimeMultiplayer; namespace osu.Game.Tests.Visual.RealtimeMultiplayer @@ -22,6 +24,9 @@ namespace osu.Game.Tests.Visual.RealtimeMultiplayer [Resolved] private OsuGameBase game { get; set; } + [Cached] + public readonly Bindable Filter = new Bindable(new FilterCriteria()); + private readonly List rooms = new List(); protected override void LoadComplete() @@ -29,6 +34,7 @@ namespace osu.Game.Tests.Visual.RealtimeMultiplayer base.LoadComplete(); int currentScoreId = 0; + int currentRoomId = 0; ((DummyAPIAccess)api).HandleRequest = req => { @@ -38,7 +44,7 @@ namespace osu.Game.Tests.Visual.RealtimeMultiplayer var createdRoom = new APICreatedRoom(); createdRoom.CopyFrom(createRoomRequest.Room); - createdRoom.RoomID.Value = 1; + createdRoom.RoomID.Value ??= currentRoomId++; rooms.Add(createdRoom); createRoomRequest.TriggerSuccess(createdRoom); @@ -103,6 +109,8 @@ namespace osu.Game.Tests.Visual.RealtimeMultiplayer }; } + public new void ClearRooms() => base.ClearRooms(); + public new void Schedule(Action action) => base.Schedule(action); } } From 0fb8615f95b29dbff7ba1c839c3591a1b72aa5b1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 19 Dec 2020 02:02:04 +0900 Subject: [PATCH 346/818] Implement room parting --- .../Screens/Multi/Components/RoomManager.cs | 4 +++- .../RealtimeRoomManager.cs | 20 ++++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Multi/Components/RoomManager.cs b/osu.Game/Screens/Multi/Components/RoomManager.cs index 6e27515849..21bff70b8b 100644 --- a/osu.Game/Screens/Multi/Components/RoomManager.cs +++ b/osu.Game/Screens/Multi/Components/RoomManager.cs @@ -107,7 +107,7 @@ namespace osu.Game.Screens.Multi.Components api.Queue(currentJoinRoomRequest); } - public void PartRoom() + public virtual void PartRoom() { currentJoinRoomRequest?.Cancel(); @@ -157,6 +157,8 @@ namespace osu.Game.Screens.Multi.Components RoomsUpdated?.Invoke(); } + protected void RemoveRoom(Room room) => rooms.Remove(room); + protected void ClearRooms() { rooms.Clear(); diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs index 69addde2a6..4f73ee3865 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs @@ -23,6 +23,8 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer private readonly IBindable isConnected = new Bindable(); private readonly Bindable allowPolling = new Bindable(); + private ListingPollingComponent listingPollingComponent; + protected override void LoadComplete() { base.LoadComplete(); @@ -39,6 +41,22 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer public override void JoinRoom(Room room, Action onSuccess = null, Action onError = null) => base.JoinRoom(room, r => joinMultiplayerRoom(r, onSuccess), onError); + public override void PartRoom() + { + if (JoinedRoom == null) + return; + + var joinedRoom = JoinedRoom; + + base.PartRoom(); + multiplayerClient.LeaveRoom().Wait(); + + // 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. + RemoveRoom(joinedRoom); + // This is delayed one frame because upon exiting the match subscreen, multiplayer updates the polling rate and messes with polling. + Schedule(() => listingPollingComponent.PollImmediately()); + } + private void joinMultiplayerRoom(Room room, Action onSuccess = null) { Debug.Assert(room.RoomID.Value != null); @@ -64,7 +82,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer protected override RoomPollingComponent[] CreatePollingComponents() => new RoomPollingComponent[] { - new RealtimeListingPollingComponent + listingPollingComponent = new RealtimeListingPollingComponent { TimeBetweenPolls = { BindTarget = TimeBetweenListingPolls }, AllowPolling = { BindTarget = allowPolling } From a593f588db95a8ac6b8bb2594c1a94909ade9c5f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 19 Dec 2020 02:02:47 +0900 Subject: [PATCH 347/818] Add a test for the realtime room manager --- .../TestSceneRealtimeRoomManager.cs | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeRoomManager.cs diff --git a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeRoomManager.cs b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeRoomManager.cs new file mode 100644 index 0000000000..6bd8c410a4 --- /dev/null +++ b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeRoomManager.cs @@ -0,0 +1,105 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Game.Online.Multiplayer; + +namespace osu.Game.Tests.Visual.RealtimeMultiplayer +{ + public class TestSceneRealtimeRoomManager : MultiplayerTestScene + { + private TestRealtimeRoomContainer roomContainer; + private TestRealtimeRoomManager roomManager => roomContainer.RoomManager; + + [Test] + public void TestPollsInitially() + { + AddStep("create room manager with a few rooms", () => + { + createRoomManager().With(d => d.OnLoadComplete += _ => + { + roomManager.CreateRoom(new Room { Name = { Value = "1" } }); + roomManager.PartRoom(); + roomManager.CreateRoom(new Room { Name = { Value = "2" } }); + roomManager.PartRoom(); + roomManager.ClearRooms(); + }); + }); + + AddAssert("manager polled for rooms", () => roomManager.Rooms.Count == 2); + AddAssert("initial rooms received", () => roomManager.InitialRoomsReceived.Value); + } + + [Test] + public void TestRoomsClearedOnDisconnection() + { + AddStep("create room manager with a few rooms", () => + { + createRoomManager().With(d => d.OnLoadComplete += _ => + { + roomManager.CreateRoom(new Room()); + roomManager.PartRoom(); + roomManager.CreateRoom(new Room()); + roomManager.PartRoom(); + }); + }); + + AddStep("disconnect", () => roomContainer.Client.Disconnect()); + + AddAssert("rooms cleared", () => roomManager.Rooms.Count == 0); + AddAssert("initial rooms not received", () => !roomManager.InitialRoomsReceived.Value); + } + + [Test] + public void TestRoomsPolledOnReconnect() + { + AddStep("create room manager with a few rooms", () => + { + createRoomManager().With(d => d.OnLoadComplete += _ => + { + roomManager.CreateRoom(new Room()); + roomManager.PartRoom(); + roomManager.CreateRoom(new Room()); + roomManager.PartRoom(); + }); + }); + + AddStep("disconnect", () => roomContainer.Client.Disconnect()); + AddStep("connect", () => roomContainer.Client.Connect()); + + AddAssert("manager polled for rooms", () => roomManager.Rooms.Count == 2); + AddAssert("initial rooms received", () => roomManager.InitialRoomsReceived.Value); + } + + [Test] + public void TestRoomsNotPolledWhenJoined() + { + AddStep("create room manager with a room", () => + { + createRoomManager().With(d => d.OnLoadComplete += _ => + { + roomManager.CreateRoom(new Room()); + roomManager.ClearRooms(); + }); + }); + + AddAssert("manager not polled for rooms", () => roomManager.Rooms.Count == 0); + AddAssert("initial rooms not received", () => !roomManager.InitialRoomsReceived.Value); + } + + private TestRealtimeRoomManager createRoomManager() + { + Child = roomContainer = new TestRealtimeRoomContainer + { + RoomManager = + { + TimeBetweenListingPolls = { Value = 1 }, + TimeBetweenSelectionPolls = { Value = 1 } + } + }; + + return roomManager; + } + } +} From e84ce80d6cb0187b67527d5289ee3480e0746484 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 19 Dec 2020 02:17:07 +0900 Subject: [PATCH 348/818] Make test headless --- .../Visual/RealtimeMultiplayer/TestSceneRealtimeRoomManager.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeRoomManager.cs b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeRoomManager.cs index 6bd8c410a4..9a4b748de1 100644 --- a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeRoomManager.cs +++ b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeRoomManager.cs @@ -3,10 +3,12 @@ using NUnit.Framework; using osu.Framework.Graphics; +using osu.Framework.Testing; using osu.Game.Online.Multiplayer; namespace osu.Game.Tests.Visual.RealtimeMultiplayer { + [HeadlessTest] public class TestSceneRealtimeRoomManager : MultiplayerTestScene { private TestRealtimeRoomContainer roomContainer; From 109e6b4283e0a49ac13be9a59572ab1f6f599e0a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 19 Dec 2020 02:20:02 +0900 Subject: [PATCH 349/818] Add tests for creating/joining/parting multiplayer rooms --- .../TestSceneRealtimeRoomManager.cs | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeRoomManager.cs b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeRoomManager.cs index 9a4b748de1..598641682b 100644 --- a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeRoomManager.cs +++ b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeRoomManager.cs @@ -90,6 +90,52 @@ namespace osu.Game.Tests.Visual.RealtimeMultiplayer AddAssert("initial rooms not received", () => !roomManager.InitialRoomsReceived.Value); } + [Test] + public void TestMultiplayerRoomJoinedWhenCreated() + { + AddStep("create room manager with a room", () => + { + createRoomManager().With(d => d.OnLoadComplete += _ => + { + roomManager.CreateRoom(new Room()); + }); + }); + + AddAssert("multiplayer room joined", () => roomContainer.Client.Room != null); + } + + [Test] + public void TestMultiplayerRoomPartedWhenAPIRoomParted() + { + AddStep("create room manager with a room", () => + { + createRoomManager().With(d => d.OnLoadComplete += _ => + { + roomManager.CreateRoom(new Room()); + roomManager.PartRoom(); + }); + }); + + AddAssert("multiplayer room parted", () => roomContainer.Client.Room == null); + } + + [Test] + public void TestMultiplayerRoomPartedWhenAPIRoomJoined() + { + AddStep("create room manager with a room", () => + { + createRoomManager().With(d => d.OnLoadComplete += _ => + { + var r = new Room(); + roomManager.CreateRoom(r); + roomManager.PartRoom(); + roomManager.JoinRoom(r); + }); + }); + + AddAssert("multiplayer room parted", () => roomContainer.Client.Room != null); + } + private TestRealtimeRoomManager createRoomManager() { Child = roomContainer = new TestRealtimeRoomContainer From 3f4a66c4ae6036b98cb3512ba2440d75596c056e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 19 Dec 2020 02:23:42 +0900 Subject: [PATCH 350/818] Add realtime multiplayer test scene abstract class --- .../RealtimeMultiplayerTestScene.cs | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 osu.Game/Tests/Visual/RealtimeMultiplayer/RealtimeMultiplayerTestScene.cs diff --git a/osu.Game/Tests/Visual/RealtimeMultiplayer/RealtimeMultiplayerTestScene.cs b/osu.Game/Tests/Visual/RealtimeMultiplayer/RealtimeMultiplayerTestScene.cs new file mode 100644 index 0000000000..e41076a4fd --- /dev/null +++ b/osu.Game/Tests/Visual/RealtimeMultiplayer/RealtimeMultiplayerTestScene.cs @@ -0,0 +1,53 @@ +// 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.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Online.RealtimeMultiplayer; +using osu.Game.Screens.Multi.Lounge.Components; +using osu.Game.Screens.Multi.RealtimeMultiplayer; + +namespace osu.Game.Tests.Visual.RealtimeMultiplayer +{ + public class RealtimeMultiplayerTestScene : MultiplayerTestScene + { + [Cached(typeof(StatefulMultiplayerClient))] + public TestRealtimeMultiplayerClient Client { get; } + + [Cached(typeof(RealtimeRoomManager))] + public TestRealtimeRoomManager RoomManager { get; } + + [Cached] + public Bindable Filter { get; } + + protected override Container Content => content; + private readonly TestRealtimeRoomContainer content; + + private readonly bool joinRoom; + + public RealtimeMultiplayerTestScene(bool joinRoom = true) + { + this.joinRoom = joinRoom; + base.Content.Add(content = new TestRealtimeRoomContainer { RelativeSizeAxes = Axes.Both }); + + Client = content.Client; + RoomManager = content.RoomManager; + Filter = content.Filter; + } + + [SetUp] + public new void Setup() => Schedule(() => + { + RoomManager.Schedule(() => RoomManager.PartRoom()); + + if (joinRoom) + { + Room.RoomID.Value = 1; + RoomManager.Schedule(() => RoomManager.JoinRoom(Room, null, null)); + } + }); + } +} From 9b08f573baeb360e0f5135f98f5205b7145757e5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 19 Dec 2020 02:41:04 +0900 Subject: [PATCH 351/818] Fix room not created before being joined --- .../RealtimeMultiplayer/RealtimeMultiplayerTestScene.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Tests/Visual/RealtimeMultiplayer/RealtimeMultiplayerTestScene.cs b/osu.Game/Tests/Visual/RealtimeMultiplayer/RealtimeMultiplayerTestScene.cs index e41076a4fd..b52106551e 100644 --- a/osu.Game/Tests/Visual/RealtimeMultiplayer/RealtimeMultiplayerTestScene.cs +++ b/osu.Game/Tests/Visual/RealtimeMultiplayer/RealtimeMultiplayerTestScene.cs @@ -44,10 +44,7 @@ namespace osu.Game.Tests.Visual.RealtimeMultiplayer RoomManager.Schedule(() => RoomManager.PartRoom()); if (joinRoom) - { - Room.RoomID.Value = 1; - RoomManager.Schedule(() => RoomManager.JoinRoom(Room, null, null)); - } + RoomManager.Schedule(() => RoomManager.CreateRoom(Room)); }); } } From 4d051818a152573834a0e859994f25c83bda1b7a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 19 Dec 2020 02:30:53 +0900 Subject: [PATCH 352/818] Add base class for all realtime multiplayer classes --- .../RealtimeRoomComposite.cs | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomComposite.cs diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomComposite.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomComposite.cs new file mode 100644 index 0000000000..e6d1274316 --- /dev/null +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomComposite.cs @@ -0,0 +1,38 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using JetBrains.Annotations; +using osu.Framework.Allocation; +using osu.Game.Online.RealtimeMultiplayer; + +namespace osu.Game.Screens.Multi.RealtimeMultiplayer +{ + public abstract class RealtimeRoomComposite : MultiplayerComposite + { + [CanBeNull] + protected MultiplayerRoom Room => Client.Room; + + [Resolved] + protected StatefulMultiplayerClient Client { get; private set; } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Client.RoomChanged += OnRoomChanged; + OnRoomChanged(); + } + + protected virtual void OnRoomChanged() + { + } + + protected override void Dispose(bool isDisposing) + { + if (Client != null) + Client.RoomChanged -= OnRoomChanged; + + base.Dispose(isDisposing); + } + } +} From 1e5c32410ad572883295e0f8e47e90704ef4593e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 19 Dec 2020 02:37:47 +0900 Subject: [PATCH 353/818] Add the realtime multiplayer participants list --- .../TestSceneParticipantsList.cs | 96 +++++++++ .../Participants/ParticipantPanel.cs | 187 ++++++++++++++++++ .../Participants/ParticipantsList.cs | 55 ++++++ .../Participants/ReadyMark.cs | 51 +++++ 4 files changed, 389 insertions(+) create mode 100644 osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneParticipantsList.cs create mode 100644 osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantPanel.cs create mode 100644 osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantsList.cs create mode 100644 osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ReadyMark.cs diff --git a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneParticipantsList.cs b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneParticipantsList.cs new file mode 100644 index 0000000000..ee6bbc4ecd --- /dev/null +++ b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneParticipantsList.cs @@ -0,0 +1,96 @@ +// 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.Sprites; +using osu.Framework.Testing; +using osu.Game.Online.RealtimeMultiplayer; +using osu.Game.Screens.Multi.RealtimeMultiplayer.Participants; +using osu.Game.Users; +using osuTK; + +namespace osu.Game.Tests.Visual.RealtimeMultiplayer +{ + public class TestSceneParticipantsList : RealtimeMultiplayerTestScene + { + [SetUp] + public new void Setup() => Schedule(() => + { + Child = new ParticipantsList + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Y, + Size = new Vector2(380, 0.7f) + }; + }); + + [Test] + public void TestAddUser() + { + AddAssert("one unique panel", () => this.ChildrenOfType().Select(p => p.User).Distinct().Count() == 1); + + AddStep("add user", () => Client.AddUser(new User + { + Id = 3, + Username = "Second", + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", + })); + + AddAssert("two unique panels", () => this.ChildrenOfType().Select(p => p.User).Distinct().Count() == 2); + } + + [Test] + public void TestRemoveUser() + { + User secondUser = null; + + AddStep("add a user", () => + { + Client.AddUser(secondUser = new User + { + Id = 3, + Username = "Second", + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", + }); + }); + + AddStep("remove host", () => Client.RemoveUser(API.LocalUser.Value)); + + AddAssert("single panel is for second user", () => this.ChildrenOfType().Single().User.User == secondUser); + } + + [Test] + public void TestToggleReadyState() + { + AddAssert("ready mark invisible", () => !this.ChildrenOfType().Single().IsPresent); + + AddStep("make user ready", () => Client.ChangeState(MultiplayerUserState.Ready)); + AddUntilStep("ready mark visible", () => this.ChildrenOfType().Single().IsPresent); + + AddStep("make user idle", () => Client.ChangeState(MultiplayerUserState.Idle)); + AddUntilStep("ready mark invisible", () => !this.ChildrenOfType().Single().IsPresent); + } + + [Test] + public void TestCrownChangesStateWhenHostTransferred() + { + AddStep("add user", () => Client.AddUser(new User + { + Id = 3, + Username = "Second", + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", + })); + + AddUntilStep("first user crown visible", () => this.ChildrenOfType().ElementAt(0).ChildrenOfType().First().Alpha == 1); + AddUntilStep("second user crown hidden", () => this.ChildrenOfType().ElementAt(1).ChildrenOfType().First().Alpha == 0); + + AddStep("make second user host", () => Client.TransferHost(3)); + + AddUntilStep("first user crown hidden", () => this.ChildrenOfType().ElementAt(0).ChildrenOfType().First().Alpha == 0); + AddUntilStep("second user crown visible", () => this.ChildrenOfType().ElementAt(1).ChildrenOfType().First().Alpha == 1); + } + } +} diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantPanel.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantPanel.cs new file mode 100644 index 0000000000..306a54bfdc --- /dev/null +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantPanel.cs @@ -0,0 +1,187 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Diagnostics; +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.Cursor; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API; +using osu.Game.Online.RealtimeMultiplayer; +using osu.Game.Users; +using osu.Game.Users.Drawables; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Participants +{ + public class ParticipantPanel : RealtimeRoomComposite, IHasContextMenu + { + public readonly MultiplayerRoomUser User; + + [Resolved] + private IAPIProvider api { get; set; } + + private ReadyMark readyMark; + private SpriteIcon crown; + + public ParticipantPanel(MultiplayerRoomUser user) + { + User = user; + + RelativeSizeAxes = Axes.X; + Height = 40; + } + + [BackgroundDependencyLoader] + private void load() + { + Debug.Assert(User.User != null); + + var backgroundColour = Color4Extensions.FromHex("#33413C"); + + InternalChildren = new Drawable[] + { + crown = new SpriteIcon + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Icon = FontAwesome.Solid.Crown, + Size = new Vector2(14), + Colour = Color4Extensions.FromHex("#F7E65D"), + Alpha = 0 + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Left = 24 }, + Child = new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + CornerRadius = 5, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = backgroundColour + }, + new UserCoverBackground + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + RelativeSizeAxes = Axes.Both, + Width = 0.75f, + User = User.User, + Colour = ColourInfo.GradientHorizontal(Color4.White.Opacity(0), Color4.White.Opacity(0.25f)) + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Spacing = new Vector2(10), + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + new UpdateableAvatar + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit, + User = User.User + }, + new UpdateableFlag + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Size = new Vector2(30, 20), + Country = User.User.Country + }, + new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 18), + Text = User.User.Username + }, + new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Font = OsuFont.GetFont(size: 14), + Text = User.User.CurrentModeRank != null ? $"#{User.User.CurrentModeRank}" : string.Empty + } + } + }, + readyMark = new ReadyMark + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Margin = new MarginPadding { Right = 10 }, + Alpha = 0 + } + } + } + } + }; + } + + protected override void OnRoomChanged() + { + base.OnRoomChanged(); + + if (Room == null) + return; + + if (User.State == MultiplayerUserState.Ready) + readyMark.FadeIn(50); + else + readyMark.FadeOut(50); + + if (Room.Host?.Equals(User) == true) + crown.FadeIn(50); + else + crown.FadeOut(50); + } + + public MenuItem[] ContextMenuItems + { + get + { + if (Room == null) + return null; + + // If the local user is targetted. + if (User.UserID == api.LocalUser.Value.Id) + return null; + + // If the local user is not the host of the room. + if (Room.Host?.UserID != api.LocalUser.Value.Id) + return null; + + int targetUser = User.UserID; + + return new MenuItem[] + { + new OsuMenuItem("Give host", MenuItemType.Standard, () => + { + // Ensure the local user is still host. + if (Room.Host?.UserID != api.LocalUser.Value.Id) + return; + + Client.TransferHost(targetUser); + }) + }; + } + } + } +} diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantsList.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantsList.cs new file mode 100644 index 0000000000..d4c32d9189 --- /dev/null +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantsList.cs @@ -0,0 +1,55 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Cursor; +using osuTK; + +namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Participants +{ + public class ParticipantsList : RealtimeRoomComposite + { + private FillFlowContainer panels; + + [BackgroundDependencyLoader] + private void load() + { + InternalChild = new OsuContextMenuContainer + { + RelativeSizeAxes = Axes.Both, + Child = new OsuScrollContainer + { + RelativeSizeAxes = Axes.Both, + Child = panels = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 2) + } + } + }; + } + + protected override void OnRoomChanged() + { + base.OnRoomChanged(); + + if (Room == null) + panels.Clear(); + else + { + // Remove panels for users no longer in the room. + panels.RemoveAll(p => !Room.Users.Contains(p.User)); + + // Add panels for all users new to the room. + foreach (var user in Room.Users.Except(panels.Select(p => p.User))) + panels.Add(new ParticipantPanel(user)); + } + } + } +} diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ReadyMark.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ReadyMark.cs new file mode 100644 index 0000000000..df49d9342e --- /dev/null +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ReadyMark.cs @@ -0,0 +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 osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +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.Screens.Multi.RealtimeMultiplayer.Participants +{ + public class ReadyMark : CompositeDrawable + { + public ReadyMark() + { + AutoSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load() + { + InternalChild = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Spacing = new Vector2(5), + Children = new Drawable[] + { + new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Font = OsuFont.GetFont(weight: FontWeight.Regular, size: 12), + Text = "ready", + Colour = Color4Extensions.FromHex("#DDFFFF") + }, + new SpriteIcon + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Icon = FontAwesome.Solid.CheckCircle, + Size = new Vector2(12), + Colour = Color4Extensions.FromHex("#AADD00") + } + } + }; + } + } +} From 11a903a206a820191af25de9e4f953223869c44a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 19 Dec 2020 02:46:16 +0900 Subject: [PATCH 354/818] Add test for many users and disable scrollbar --- .../TestSceneParticipantsList.cs | 20 +++++++++++++++++++ .../Participants/ParticipantsList.cs | 1 + 2 files changed, 21 insertions(+) diff --git a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneParticipantsList.cs b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneParticipantsList.cs index ee6bbc4ecd..8c997e9e32 100644 --- a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneParticipantsList.cs +++ b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneParticipantsList.cs @@ -92,5 +92,25 @@ namespace osu.Game.Tests.Visual.RealtimeMultiplayer AddUntilStep("first user crown hidden", () => this.ChildrenOfType().ElementAt(0).ChildrenOfType().First().Alpha == 0); AddUntilStep("second user crown visible", () => this.ChildrenOfType().ElementAt(1).ChildrenOfType().First().Alpha == 1); } + + [Test] + public void TestManyUsers() + { + AddStep("add many users", () => + { + for (int i = 0; i < 20; i++) + { + Client.AddUser(new User + { + Id = i, + Username = $"User {i}", + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", + }); + + if (i % 2 == 0) + Client.ChangeUserState(i, MultiplayerUserState.Ready); + } + }); + } } } diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantsList.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantsList.cs index d4c32d9189..218c2cabb7 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantsList.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantsList.cs @@ -24,6 +24,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Participants Child = new OsuScrollContainer { RelativeSizeAxes = Axes.Both, + ScrollbarVisible = false, Child = panels = new FillFlowContainer { RelativeSizeAxes = Axes.X, From e4a54dc6cdccdf17b1e950fe1abf0424e9c28485 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 19 Dec 2020 02:52:51 +0900 Subject: [PATCH 355/818] Renamespace ready button --- osu.Game/Screens/Multi/{Match => }/Components/ReadyButton.cs | 2 +- osu.Game/Screens/Multi/Match/Components/Footer.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) rename osu.Game/Screens/Multi/{Match => }/Components/ReadyButton.cs (98%) diff --git a/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs b/osu.Game/Screens/Multi/Components/ReadyButton.cs similarity index 98% rename from osu.Game/Screens/Multi/Match/Components/ReadyButton.cs rename to osu.Game/Screens/Multi/Components/ReadyButton.cs index a64f24dd7e..6d12111d8f 100644 --- a/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs +++ b/osu.Game/Screens/Multi/Components/ReadyButton.cs @@ -11,7 +11,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Multiplayer; -namespace osu.Game.Screens.Multi.Match.Components +namespace osu.Game.Screens.Multi.Components { public class ReadyButton : TriangleButton { diff --git a/osu.Game/Screens/Multi/Match/Components/Footer.cs b/osu.Game/Screens/Multi/Match/Components/Footer.cs index be4ee873fa..4ec8628d2b 100644 --- a/osu.Game/Screens/Multi/Match/Components/Footer.cs +++ b/osu.Game/Screens/Multi/Match/Components/Footer.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Online.Multiplayer; +using osu.Game.Screens.Multi.Components; using osuTK; namespace osu.Game.Screens.Multi.Match.Components From 4e0113afbf51e5d9be43e73b1fee714a21c04ee1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 19 Dec 2020 02:55:48 +0900 Subject: [PATCH 356/818] Abstractify ready button and add a timeshift implementation --- .../Screens/Multi/Components/ReadyButton.cs | 24 +++--------- .../Screens/Multi/Match/Components/Footer.cs | 4 +- .../Multi/Timeshift/TimeshiftReadyButton.cs | 38 +++++++++++++++++++ 3 files changed, 46 insertions(+), 20 deletions(-) create mode 100644 osu.Game/Screens/Multi/Timeshift/TimeshiftReadyButton.cs diff --git a/osu.Game/Screens/Multi/Components/ReadyButton.cs b/osu.Game/Screens/Multi/Components/ReadyButton.cs index 6d12111d8f..0bb4ed8617 100644 --- a/osu.Game/Screens/Multi/Components/ReadyButton.cs +++ b/osu.Game/Screens/Multi/Components/ReadyButton.cs @@ -13,26 +13,20 @@ using osu.Game.Online.Multiplayer; namespace osu.Game.Screens.Multi.Components { - public class ReadyButton : TriangleButton + public abstract class ReadyButton : TriangleButton { public readonly Bindable SelectedItem = new Bindable(); - [Resolved(typeof(Room), nameof(Room.EndDate))] - private Bindable endDate { get; set; } + public new readonly BindableBool Enabled = new BindableBool(); [Resolved] - private IBindable gameBeatmap { get; set; } + protected IBindable GameBeatmap { get; private set; } [Resolved] private BeatmapManager beatmaps { get; set; } private bool hasBeatmap; - public ReadyButton() - { - Text = "Start"; - } - private IBindable> managerUpdated; private IBindable> managerRemoved; @@ -45,10 +39,6 @@ namespace osu.Game.Screens.Multi.Components managerRemoved.BindValueChanged(beatmapRemoved); SelectedItem.BindValueChanged(item => updateSelectedItem(item.NewValue), true); - - BackgroundColour = colours.Green; - Triangles.ColourDark = colours.Green; - Triangles.ColourLight = colours.GreenLight; } private void updateSelectedItem(PlaylistItem item) @@ -94,15 +84,13 @@ namespace osu.Game.Screens.Multi.Components private void updateEnabledState() { - if (gameBeatmap.Value == null || SelectedItem.Value == null) + if (GameBeatmap.Value == null || SelectedItem.Value == null) { - Enabled.Value = false; + base.Enabled.Value = false; return; } - bool hasEnoughTime = DateTimeOffset.UtcNow.AddSeconds(30).AddMilliseconds(gameBeatmap.Value.Track.Length) < endDate.Value; - - Enabled.Value = hasBeatmap && hasEnoughTime; + base.Enabled.Value = hasBeatmap && Enabled.Value; } } } diff --git a/osu.Game/Screens/Multi/Match/Components/Footer.cs b/osu.Game/Screens/Multi/Match/Components/Footer.cs index 4ec8628d2b..d6a7e380bf 100644 --- a/osu.Game/Screens/Multi/Match/Components/Footer.cs +++ b/osu.Game/Screens/Multi/Match/Components/Footer.cs @@ -10,7 +10,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Online.Multiplayer; -using osu.Game.Screens.Multi.Components; +using osu.Game.Screens.Multi.Timeshift; using osuTK; namespace osu.Game.Screens.Multi.Match.Components @@ -32,7 +32,7 @@ namespace osu.Game.Screens.Multi.Match.Components InternalChildren = new[] { background = new Box { RelativeSizeAxes = Axes.Both }, - new ReadyButton + new TimeshiftReadyButton { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Screens/Multi/Timeshift/TimeshiftReadyButton.cs b/osu.Game/Screens/Multi/Timeshift/TimeshiftReadyButton.cs new file mode 100644 index 0000000000..b6698b195c --- /dev/null +++ b/osu.Game/Screens/Multi/Timeshift/TimeshiftReadyButton.cs @@ -0,0 +1,38 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Game.Graphics; +using osu.Game.Online.Multiplayer; +using osu.Game.Screens.Multi.Components; + +namespace osu.Game.Screens.Multi.Timeshift +{ + public class TimeshiftReadyButton : ReadyButton + { + [Resolved(typeof(Room), nameof(Room.EndDate))] + private Bindable endDate { get; set; } + + public TimeshiftReadyButton() + { + Text = "Start"; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + BackgroundColour = colours.Green; + Triangles.ColourDark = colours.Green; + Triangles.ColourLight = colours.GreenLight; + } + + protected override void Update() + { + base.Update(); + + Enabled.Value = endDate.Value == null || DateTimeOffset.UtcNow.AddSeconds(30).AddMilliseconds(GameBeatmap.Value.Track.Length) < endDate.Value; + } + } +} From 6efe24695b2cf930f554fd72cd9c5c51214abf6f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 19 Dec 2020 02:59:11 +0900 Subject: [PATCH 357/818] Add the realtime multiplayer ready button --- .../TestSceneRealtimeReadyButton.cs | 129 ++++++++++++++++++ .../RealtimeReadyButton.cs | 122 +++++++++++++++++ 2 files changed, 251 insertions(+) create mode 100644 osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeReadyButton.cs create mode 100644 osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeReadyButton.cs diff --git a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeReadyButton.cs b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeReadyButton.cs new file mode 100644 index 0000000000..889c0c0be3 --- /dev/null +++ b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeReadyButton.cs @@ -0,0 +1,129 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Graphics; +using osu.Framework.Platform; +using osu.Game.Beatmaps; +using osu.Game.Online.Multiplayer; +using osu.Game.Online.RealtimeMultiplayer; +using osu.Game.Rulesets; +using osu.Game.Screens.Multi.RealtimeMultiplayer; +using osu.Game.Tests.Resources; +using osu.Game.Users; +using osuTK; +using osuTK.Input; + +namespace osu.Game.Tests.Visual.RealtimeMultiplayer +{ + public class TestSceneRealtimeReadyButton : RealtimeMultiplayerTestScene + { + private RealtimeReadyButton button; + + private BeatmapManager beatmaps; + private RulesetStore rulesets; + + [BackgroundDependencyLoader] + private void load(GameHost host, AudioManager audio) + { + Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); + Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default)); + beatmaps.Import(TestResources.GetTestBeatmapForImport(true)).Wait(); + } + + [SetUp] + public new void Setup() => Schedule(() => + { + var beatmap = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First().Beatmaps.First(); + + Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap); + + Child = button = new RealtimeReadyButton + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(200, 50), + SelectedItem = + { + Value = new PlaylistItem + { + Beatmap = { Value = beatmap }, + Ruleset = { Value = beatmap.Ruleset } + } + } + }; + + Client.AddUser(API.LocalUser.Value); + }); + + [Test] + public void TestToggleStateWhenNotHost() + { + AddStep("add second user as host", () => + { + Client.AddUser(new User { Id = 2, Username = "Another user" }); + Client.TransferHost(2); + }); + + addClickButtonStep(); + AddAssert("user is ready", () => Client.Room?.Users[0].State == MultiplayerUserState.Ready); + + addClickButtonStep(); + AddAssert("user is idle", () => Client.Room?.Users[0].State == MultiplayerUserState.Idle); + } + + [TestCase(true)] + [TestCase(false)] + public void TestToggleStateWhenHost(bool allReady) + { + AddStep("setup", () => + { + Client.TransferHost(Client.Room?.Users[0].UserID ?? 0); + + if (!allReady) + Client.AddUser(new User { Id = 2, Username = "Another user" }); + }); + + addClickButtonStep(); + AddAssert("user is ready", () => Client.Room?.Users[0].State == MultiplayerUserState.Ready); + + addClickButtonStep(); + AddAssert("match started", () => Client.Room?.Users[0].State == MultiplayerUserState.WaitingForLoad); + } + + [Test] + public void TestBecomeHostWhileReady() + { + addClickButtonStep(); + AddStep("make user host", () => Client.TransferHost(Client.Room?.Users[0].UserID ?? 0)); + + addClickButtonStep(); + AddAssert("match started", () => Client.Room?.Users[0].State == MultiplayerUserState.WaitingForLoad); + } + + [Test] + public void TestLoseHostWhileReady() + { + AddStep("setup", () => + { + Client.TransferHost(Client.Room?.Users[0].UserID ?? 0); + Client.AddUser(new User { Id = 2, Username = "Another user" }); + }); + + addClickButtonStep(); + AddStep("transfer host", () => Client.TransferHost(Client.Room?.Users[1].UserID ?? 0)); + + addClickButtonStep(); + AddAssert("match not started", () => Client.Room?.Users[0].State == MultiplayerUserState.Idle); + } + + private void addClickButtonStep() => AddStep("click button", () => + { + InputManager.MoveMouseTo(button); + InputManager.Click(MouseButton.Left); + }); + } +} diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeReadyButton.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeReadyButton.cs new file mode 100644 index 0000000000..d52df258ad --- /dev/null +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeReadyButton.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.Diagnostics; +using System.Linq; +using JetBrains.Annotations; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Game.Graphics; +using osu.Game.Graphics.Backgrounds; +using osu.Game.Online.API; +using osu.Game.Online.Multiplayer; +using osu.Game.Online.RealtimeMultiplayer; +using osu.Game.Screens.Multi.Components; +using osuTK; + +namespace osu.Game.Screens.Multi.RealtimeMultiplayer +{ + public class RealtimeReadyButton : RealtimeRoomComposite + { + public Bindable SelectedItem => button.SelectedItem; + + [Resolved] + private IAPIProvider api { get; set; } + + [CanBeNull] + private MultiplayerRoomUser localUser; + + [Resolved] + private OsuColour colours { get; set; } + + private readonly ButtonWithTrianglesExposed button; + + public RealtimeReadyButton() + { + InternalChild = button = new ButtonWithTrianglesExposed + { + RelativeSizeAxes = Axes.Both, + Size = Vector2.One, + Enabled = { Value = true }, + Action = onClick + }; + } + + protected override void OnRoomChanged() + { + base.OnRoomChanged(); + + localUser = Room?.Users.Single(u => u.User?.Id == api.LocalUser.Value.Id); + button.Enabled.Value = Client.Room?.State == MultiplayerRoomState.Open; + updateState(); + } + + private void updateState() + { + if (localUser == null) + return; + + Debug.Assert(Room != null); + + switch (localUser.State) + { + case MultiplayerUserState.Idle: + button.Text = "Ready"; + updateButtonColour(true); + break; + + case MultiplayerUserState.Ready: + if (Room?.Host?.Equals(localUser) == true) + { + button.Text = "Let's go!"; + updateButtonColour(Room.Users.All(u => u.State == MultiplayerUserState.Ready)); + } + else + { + button.Text = "Waiting for host..."; + updateButtonColour(false); + } + + break; + } + } + + private void updateButtonColour(bool green) + { + if (green) + { + button.BackgroundColour = colours.Green; + button.Triangles.ColourDark = colours.Green; + button.Triangles.ColourLight = colours.GreenLight; + } + else + { + button.BackgroundColour = colours.YellowDark; + button.Triangles.ColourDark = colours.YellowDark; + button.Triangles.ColourLight = colours.Yellow; + } + } + + private void onClick() + { + if (localUser == null) + return; + + if (localUser.State == MultiplayerUserState.Idle) + Client.ChangeState(MultiplayerUserState.Ready); + else + { + if (Room?.Host?.Equals(localUser) == true) + Client.StartMatch(); + else + Client.ChangeState(MultiplayerUserState.Idle); + } + } + + private class ButtonWithTrianglesExposed : ReadyButton + { + public new Triangles Triangles => base.Triangles; + } + } +} From cc22efaa6babc4592a06a34f6affb226caea56cb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 19 Dec 2020 03:17:04 +0900 Subject: [PATCH 358/818] Use tcs instead of delay-wait --- osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs index e106dc3a1c..2b7ca189ee 100644 --- a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs +++ b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs @@ -107,25 +107,23 @@ namespace osu.Game.Screens.Multi.Play { Debug.Assert(token != null); - bool completed = false; + var tcs = new TaskCompletionSource(); var request = new SubmitRoomScoreRequest(token.Value, roomId.Value ?? 0, playlistItem.ID, score.ScoreInfo); request.Success += s => { score.ScoreInfo.OnlineScoreID = s.ID; - completed = true; + tcs.SetResult(true); }; request.Failure += e => { Logger.Error(e, "Failed to submit score"); - completed = true; + tcs.SetResult(false); }; api.Queue(request); - - while (!completed) - await Task.Delay(100); + await tcs.Task; return await base.SubmitScore(score); } From 772dd0287e4932e232d4f6c033b7246f740d3541 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 19 Dec 2020 03:32:05 +0900 Subject: [PATCH 359/818] Split submission and import into two methods --- .../Screens/Multi/Play/TimeshiftPlayer.cs | 6 ++-- osu.Game/Screens/Play/Player.cs | 33 ++++++++++++++----- osu.Game/Screens/Play/ReplayPlayer.cs | 3 +- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs index 2b7ca189ee..41dcf61740 100644 --- a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs +++ b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs @@ -103,8 +103,10 @@ namespace osu.Game.Screens.Multi.Play return score; } - protected override async Task SubmitScore(Score score) + protected override async Task SubmitScore(Score score) { + await base.SubmitScore(score); + Debug.Assert(token != null); var tcs = new TaskCompletionSource(); @@ -124,8 +126,6 @@ namespace osu.Game.Screens.Multi.Play api.Queue(request); await tcs.Task; - - return await base.SubmitScore(score); } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index c8f1980ab1..a83f0e1b33 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -537,13 +537,23 @@ namespace osu.Game.Screens.Play try { - return await SubmitScore(score); + await SubmitScore(score); } catch (Exception ex) { Logger.Error(ex, "Score submission failed!"); - return score.ScoreInfo; } + + try + { + await ImportScore(score); + } + catch (Exception ex) + { + Logger.Error(ex, "Score import failed!"); + } + + return score.ScoreInfo; }); using (BeginDelayedSequence(RESULTS_DISPLAY_DELAY)) @@ -792,15 +802,15 @@ namespace osu.Game.Screens.Play } /// - /// Submits the player's . + /// Imports the player's to the local database. /// - /// The to submit. - /// The submitted score. - protected virtual async Task SubmitScore(Score score) + /// The to import. + /// The imported score. + protected virtual async Task ImportScore(Score score) { // Replays are already populated and present in the game's database, so should not be re-imported. if (DrawableRuleset.ReplayScore != null) - return score.ScoreInfo; + return; LegacyByteArrayReader replayReader; @@ -810,9 +820,16 @@ namespace osu.Game.Screens.Play replayReader = new LegacyByteArrayReader(stream.ToArray(), "replay.osr"); } - return await scoreManager.Import(score.ScoreInfo, replayReader); + await scoreManager.Import(score.ScoreInfo, replayReader); } + /// + /// Submits the player's . + /// + /// The to submit. + /// The submitted score. + protected virtual Task SubmitScore(Score score) => Task.CompletedTask; + /// /// Creates the for a . /// diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs index 390d1d1959..a07213cb33 100644 --- a/osu.Game/Screens/Play/ReplayPlayer.cs +++ b/osu.Game/Screens/Play/ReplayPlayer.cs @@ -37,7 +37,8 @@ namespace osu.Game.Screens.Play return Score; } - protected override Task SubmitScore(Score score) => Task.FromResult(score.ScoreInfo); + // Don't re-import replay scores as they're already present in the database. + protected override Task ImportScore(Score score) => Task.CompletedTask; protected override ResultsScreen CreateResults(ScoreInfo score) => new SoloResultsScreen(score, false); From f1878eff639bce6d336008cc85e4a4b2c8260bf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 18 Dec 2020 23:45:42 +0100 Subject: [PATCH 360/818] Use yet another solution leveraging padding --- .../Edit/Compose/Components/SelectionBox.cs | 26 ++++++------------- 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs index 85e86499be..2f4721f63e 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -93,6 +92,7 @@ namespace osu.Game.Screens.Edit.Compose.Components } } + private Container dragHandles; private FillFlowContainer buttons; public const float BORDER_RADIUS = 3; @@ -152,6 +152,12 @@ namespace osu.Game.Screens.Edit.Compose.Components }, } }, + dragHandles = new Container + { + RelativeSizeAxes = Axes.Both, + // ensures that the centres of all drag handles line up with the middle of the selection box border. + Padding = new MarginPadding(BORDER_RADIUS / 2) + }, buttons = new FillFlowContainer { Y = 20, @@ -233,30 +239,14 @@ namespace osu.Game.Screens.Edit.Compose.Components }); } - private void addDragHandle(Anchor anchor) => AddInternal(new SelectionBoxDragHandle + private void addDragHandle(Anchor anchor) => dragHandles.Add(new SelectionBoxDragHandle { Anchor = anchor, - X = dragCircleAdjustments[anchor].X, - Y = dragCircleAdjustments[anchor].Y, HandleDrag = e => OnScale?.Invoke(e.Delta, anchor), OperationStarted = operationStarted, OperationEnded = operationEnded }); - /// - /// Adjust Drag circle to be centered on the center of the border instead of on the edge. - /// - private Dictionary dragCircleAdjustments = new Dictionary(){ - {Anchor.TopLeft, new Vector2(BORDER_RADIUS / 2)}, - {Anchor.CentreLeft, new Vector2(BORDER_RADIUS / 2, 0)}, - {Anchor.BottomLeft, new Vector2(BORDER_RADIUS / 2, -BORDER_RADIUS / 2)}, - {Anchor.TopCentre, new Vector2(0, BORDER_RADIUS / 2)}, - {Anchor.BottomCentre, new Vector2(0, -BORDER_RADIUS / 2)}, - {Anchor.TopRight, new Vector2(-BORDER_RADIUS / 2, BORDER_RADIUS / 2)}, - {Anchor.CentreRight, new Vector2(-BORDER_RADIUS / 2, 0)}, - {Anchor.BottomRight, new Vector2(-BORDER_RADIUS / 2)} - }; - private int activeOperations; private float convertDragEventToAngleOfRotation(DragEvent e) From beaced321153867f0ae9c1d39af5a82848e2ced0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 19 Dec 2020 13:58:56 +0900 Subject: [PATCH 361/818] Remove unnecessary async state machine --- osu.Game/Screens/Play/Player.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index a83f0e1b33..c539dff5d9 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -806,11 +806,11 @@ namespace osu.Game.Screens.Play /// /// The to import. /// The imported score. - protected virtual async Task ImportScore(Score score) + protected virtual Task ImportScore(Score score) { // Replays are already populated and present in the game's database, so should not be re-imported. if (DrawableRuleset.ReplayScore != null) - return; + return Task.CompletedTask; LegacyByteArrayReader replayReader; @@ -820,7 +820,7 @@ namespace osu.Game.Screens.Play replayReader = new LegacyByteArrayReader(stream.ToArray(), "replay.osr"); } - await scoreManager.Import(score.ScoreInfo, replayReader); + return scoreManager.Import(score.ScoreInfo, replayReader); } /// From 28ca21b432627ffb9d8058475bf969143dd71163 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 19 Dec 2020 14:50:09 +0100 Subject: [PATCH 362/818] Seal banned method & throw better exception --- osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs index aa668cc4c8..d2d5594edd 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs @@ -40,9 +40,9 @@ namespace osu.Game.Screens.Play.HUD return drawable; } - public override void Add(GameplayLeaderboardScore drawable) + public sealed override void Add(GameplayLeaderboardScore drawable) { - throw new InvalidOperationException($"Use {nameof(AddPlayer)} instead."); + throw new NotSupportedException($"Use {nameof(AddPlayer)} instead."); } private void sort() From 22a2c3efdf2f15944e7714da36ea11962345055d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 19 Dec 2020 14:55:58 +0100 Subject: [PATCH 363/818] Add back xmldoc of AddPlayer --- osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs index d2d5594edd..cab1cbd3f1 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs @@ -24,6 +24,14 @@ namespace osu.Game.Screens.Play.HUD LayoutEasing = Easing.OutQuint; } + /// + /// Adds a player to the leaderboard. + /// + /// The player. + /// + /// Whether the player should be tracked on the leaderboard. + /// Set to true for the local player or a player whose replay is currently being played. + /// public ILeaderboardScore AddPlayer(User user, bool isTracked) { var drawable = new GameplayLeaderboardScore(user, isTracked) From d392e0f27e2444dfa8a21758a835ddbaef1842e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 19 Dec 2020 15:02:56 +0100 Subject: [PATCH 364/818] Extract shared rank-formatting helper --- osu.Game/Online/Leaderboards/LeaderboardScore.cs | 4 ++-- osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs | 3 +-- osu.Game/Utils/FormatUtils.cs | 8 ++++++++ 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index dcd0cb435a..d8207aa8f4 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -24,8 +24,8 @@ using osu.Game.Scoring; using osu.Game.Users.Drawables; using osuTK; using osuTK.Graphics; -using Humanizer; using osu.Game.Online.API; +using osu.Game.Utils; namespace osu.Game.Online.Leaderboards { @@ -358,7 +358,7 @@ namespace osu.Game.Online.Leaderboards Anchor = Anchor.Centre, Origin = Anchor.Centre, Font = OsuFont.GetFont(size: 20, italics: true), - Text = rank == null ? "-" : rank.Value.ToMetric(decimals: rank < 100000 ? 1 : 0), + Text = rank == null ? "-" : rank.Value.FormatRank() }; } diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index af3cb640bb..9684ae016c 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.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 Humanizer; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; @@ -49,7 +48,7 @@ namespace osu.Game.Screens.Play.HUD scorePosition = value; if (scorePosition.HasValue) - positionText.Text = $"#{scorePosition.Value.ToMetric(decimals: scorePosition < 100000 ? 1 : 0)}"; + positionText.Text = $"#{scorePosition.Value.FormatRank()}"; positionText.FadeTo(scorePosition.HasValue ? 1 : 0); updateColour(); diff --git a/osu.Game/Utils/FormatUtils.cs b/osu.Game/Utils/FormatUtils.cs index f2ab99f4b7..2578d8d835 100644 --- a/osu.Game/Utils/FormatUtils.cs +++ b/osu.Game/Utils/FormatUtils.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 Humanizer; + namespace osu.Game.Utils { public static class FormatUtils @@ -18,5 +20,11 @@ namespace osu.Game.Utils /// The accuracy to be formatted /// formatted accuracy in percentage public static string FormatAccuracy(this decimal accuracy) => $"{accuracy:0.00}%"; + + /// + /// Formats the supplied rank/leaderboard position in a consistent, simplified way. + /// + /// The rank/position to be formatted. + public static string FormatRank(this int rank) => rank.ToMetric(decimals: rank < 100_000 ? 1 : 0); } } From e2cc401c124b631c8ade6bde8120e71c44c3e2ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 19 Dec 2020 15:05:59 +0100 Subject: [PATCH 365/818] Move BDL above LoadComplete() --- .../Play/HUD/GameplayLeaderboardScore.cs | 106 +++++++++--------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index 9684ae016c..d510ea5f8b 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -75,59 +75,6 @@ namespace osu.Game.Screens.Play.HUD Size = new Vector2(EXTENDED_WIDTH, PANEL_HEIGHT); } - protected override void LoadComplete() - { - base.LoadComplete(); - - updateColour(); - FinishTransforms(true); - } - - private const double transition_duration = 500; - - private void updateColour() - { - if (scorePosition == 1) - { - mainFillContainer.ResizeWidthTo(EXTENDED_WIDTH, transition_duration, Easing.OutElastic); - panelColour = Color4Extensions.FromHex("7fcc33"); - textColour = Color4.White; - } - else if (trackedPlayer) - { - mainFillContainer.ResizeWidthTo(EXTENDED_WIDTH, transition_duration, Easing.OutElastic); - panelColour = Color4Extensions.FromHex("ffd966"); - textColour = Color4Extensions.FromHex("2e576b"); - } - else - { - mainFillContainer.ResizeWidthTo(regular_width, transition_duration, Easing.OutElastic); - panelColour = Color4Extensions.FromHex("3399cc"); - textColour = Color4.White; - } - } - - private Color4 panelColour - { - set - { - mainFillContainer.FadeColour(value, transition_duration, Easing.OutQuint); - centralFill.FadeColour(value, transition_duration, Easing.OutQuint); - } - } - - private Color4 textColour - { - set - { - scoreText.FadeColour(value, 200, Easing.OutQuint); - accuracyText.FadeColour(value, 200, Easing.OutQuint); - comboText.FadeColour(value, 200, Easing.OutQuint); - usernameText.FadeColour(value, 200, Easing.OutQuint); - positionText.FadeColour(value, 200, Easing.OutQuint); - } - } - [BackgroundDependencyLoader] private void load(OsuColour colours) { @@ -284,5 +231,58 @@ namespace osu.Game.Screens.Play.HUD Accuracy.BindValueChanged(v => accuracyText.Text = v.NewValue.FormatAccuracy(), true); Combo.BindValueChanged(v => comboText.Text = $"{v.NewValue}x", true); } + + protected override void LoadComplete() + { + base.LoadComplete(); + + updateColour(); + FinishTransforms(true); + } + + private const double transition_duration = 500; + + private void updateColour() + { + if (scorePosition == 1) + { + mainFillContainer.ResizeWidthTo(EXTENDED_WIDTH, transition_duration, Easing.OutElastic); + panelColour = Color4Extensions.FromHex("7fcc33"); + textColour = Color4.White; + } + else if (trackedPlayer) + { + mainFillContainer.ResizeWidthTo(EXTENDED_WIDTH, transition_duration, Easing.OutElastic); + panelColour = Color4Extensions.FromHex("ffd966"); + textColour = Color4Extensions.FromHex("2e576b"); + } + else + { + mainFillContainer.ResizeWidthTo(regular_width, transition_duration, Easing.OutElastic); + panelColour = Color4Extensions.FromHex("3399cc"); + textColour = Color4.White; + } + } + + private Color4 panelColour + { + set + { + mainFillContainer.FadeColour(value, transition_duration, Easing.OutQuint); + centralFill.FadeColour(value, transition_duration, Easing.OutQuint); + } + } + + private Color4 textColour + { + set + { + scoreText.FadeColour(value, 200, Easing.OutQuint); + accuracyText.FadeColour(value, 200, Easing.OutQuint); + comboText.FadeColour(value, 200, Easing.OutQuint); + usernameText.FadeColour(value, 200, Easing.OutQuint); + positionText.FadeColour(value, 200, Easing.OutQuint); + } + } } } From 315a957a0ca3ad242671e49ec73a27e05e1432b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 19 Dec 2020 15:17:31 +0100 Subject: [PATCH 366/818] Extract constant for text transition duration --- .../Screens/Play/HUD/GameplayLeaderboardScore.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index d510ea5f8b..cff3a1fc66 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -273,15 +273,17 @@ namespace osu.Game.Screens.Play.HUD } } + private const double text_transition_duration = 200; + private Color4 textColour { set { - scoreText.FadeColour(value, 200, Easing.OutQuint); - accuracyText.FadeColour(value, 200, Easing.OutQuint); - comboText.FadeColour(value, 200, Easing.OutQuint); - usernameText.FadeColour(value, 200, Easing.OutQuint); - positionText.FadeColour(value, 200, Easing.OutQuint); + scoreText.FadeColour(value, text_transition_duration, Easing.OutQuint); + accuracyText.FadeColour(value, text_transition_duration, Easing.OutQuint); + comboText.FadeColour(value, text_transition_duration, Easing.OutQuint); + usernameText.FadeColour(value, text_transition_duration, Easing.OutQuint); + positionText.FadeColour(value, text_transition_duration, Easing.OutQuint); } } } From 06a17a9d8cb9c94dd886e2b1e80992436521f42d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 19 Dec 2020 15:18:05 +0100 Subject: [PATCH 367/818] Rename other constant to be distinguishable --- .../Screens/Play/HUD/GameplayLeaderboardScore.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index cff3a1fc66..b93db09f71 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -240,25 +240,25 @@ namespace osu.Game.Screens.Play.HUD FinishTransforms(true); } - private const double transition_duration = 500; + private const double panel_transition_duration = 500; private void updateColour() { if (scorePosition == 1) { - mainFillContainer.ResizeWidthTo(EXTENDED_WIDTH, transition_duration, Easing.OutElastic); + mainFillContainer.ResizeWidthTo(EXTENDED_WIDTH, panel_transition_duration, Easing.OutElastic); panelColour = Color4Extensions.FromHex("7fcc33"); textColour = Color4.White; } else if (trackedPlayer) { - mainFillContainer.ResizeWidthTo(EXTENDED_WIDTH, transition_duration, Easing.OutElastic); + mainFillContainer.ResizeWidthTo(EXTENDED_WIDTH, panel_transition_duration, Easing.OutElastic); panelColour = Color4Extensions.FromHex("ffd966"); textColour = Color4Extensions.FromHex("2e576b"); } else { - mainFillContainer.ResizeWidthTo(regular_width, transition_duration, Easing.OutElastic); + mainFillContainer.ResizeWidthTo(regular_width, panel_transition_duration, Easing.OutElastic); panelColour = Color4Extensions.FromHex("3399cc"); textColour = Color4.White; } @@ -268,8 +268,8 @@ namespace osu.Game.Screens.Play.HUD { set { - mainFillContainer.FadeColour(value, transition_duration, Easing.OutQuint); - centralFill.FadeColour(value, transition_duration, Easing.OutQuint); + mainFillContainer.FadeColour(value, panel_transition_duration, Easing.OutQuint); + centralFill.FadeColour(value, panel_transition_duration, Easing.OutQuint); } } From c738a57b399b5f701fa314fb2dc01de43659c092 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 19 Dec 2020 18:48:17 +0100 Subject: [PATCH 368/818] Fix username overflow in new leaderboard design --- osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index b93db09f71..58281debf1 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -176,7 +176,7 @@ namespace osu.Game.Screens.Play.HUD usernameText = new OsuSpriteText { RelativeSizeAxes = Axes.X, - Width = 0.8f, + Width = 0.6f, Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, Colour = Color4.White, From afa6a869545692f4abed538f7d5e20e70a37a3c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 19 Dec 2020 19:00:05 +0100 Subject: [PATCH 369/818] Remove unnecessary lookup incrementing --- .../Visual/Online/TestSceneCurrentlyPlayingDisplay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs index eaa881b02c..1666c9cde4 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs @@ -92,7 +92,7 @@ namespace osu.Game.Tests.Visual.Online protected override Task ComputeValueAsync(int lookup, CancellationToken token = default) => Task.FromResult(new User { - Id = lookup++, + Id = lookup, Username = usernames[lookup % usernames.Length], }); } From ee33c0be93c16b61762edc6d85165fb5ec05fd93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 19 Dec 2020 19:08:29 +0100 Subject: [PATCH 370/818] Extract combo & accuracy ratio calculation helpers --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index f5fb918ba3..10d0cc2865 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -201,8 +201,8 @@ namespace osu.Game.Rulesets.Scoring private double getScore(ScoringMode mode) { return GetScore(mode, maxAchievableCombo, - maxBaseScore > 0 ? baseScore / maxBaseScore : 0, - maxAchievableCombo > 0 ? (double)HighestCombo.Value / maxAchievableCombo : 1, + calculateAccuracyRatio(baseScore), + calculateComboRatio(HighestCombo.Value), scoreResultCounts); } @@ -252,14 +252,17 @@ namespace osu.Game.Rulesets.Scoring computedBaseScore += Judgement.ToNumericResult(pair.Key) * pair.Value; } - double accuracy = maxBaseScore > 0 ? computedBaseScore / maxBaseScore : 0; - double comboRatio = maxAchievableCombo > 0 ? (double)maxCombo / maxAchievableCombo : 1; + double accuracy = calculateAccuracyRatio(computedBaseScore); + double comboRatio = calculateComboRatio(maxCombo); double score = GetScore(mode, maxAchievableCombo, accuracy, comboRatio, scoreResultCounts); return (score, accuracy); } + private double calculateAccuracyRatio(double baseScore) => maxBaseScore > 0 ? baseScore / maxBaseScore : 0; + private double calculateComboRatio(int maxCombo) => maxAchievableCombo > 0 ? (double)maxCombo / maxAchievableCombo : 1; + private double getBonusScore(Dictionary statistics) => statistics.GetOrDefault(HitResult.SmallBonus) * Judgement.SMALL_BONUS_SCORE + statistics.GetOrDefault(HitResult.LargeBonus) * Judgement.LARGE_BONUS_SCORE; From 631a0cea41e7e21b1ab592bc80fa748c733a22d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 19 Dec 2020 21:25:04 +0100 Subject: [PATCH 371/818] Fix intended random factor not being random in test --- .../Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs index 0c8d8ca165..0ca1d9008b 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs @@ -109,7 +109,7 @@ namespace osu.Game.Tests.Visual.Gameplay { foreach (var userId in PlayingUsers) { - if (RNG.Next(0, 1) == 1) + if (RNG.NextBool()) continue; if (!lastHeaders.TryGetValue(userId, out var header)) From f827b6c030b3cbde588effdd908b3138eff827fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 19 Dec 2020 21:26:40 +0100 Subject: [PATCH 372/818] Use terser dictionary initialiser syntax --- .../TestSceneMultiplayerGameplayLeaderboard.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs index 0ca1d9008b..e42ddeb35e 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs @@ -116,12 +116,12 @@ namespace osu.Game.Tests.Visual.Gameplay { lastHeaders[userId] = header = new FrameHeader(new ScoreInfo { - Statistics = new Dictionary(new[] + Statistics = new Dictionary { - new KeyValuePair(HitResult.Miss, 0), - new KeyValuePair(HitResult.Meh, 0), - new KeyValuePair(HitResult.Great, 0) - }) + [HitResult.Miss] = 0, + [HitResult.Meh] = 0, + [HitResult.Great] = 0 + } }); } From 4e5064c4f611de5704982e8dbbe670a247814f23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 19 Dec 2020 21:31:17 +0100 Subject: [PATCH 373/818] Start accuracy at 1 --- osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index c96f496cd0..12321de442 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -108,7 +108,7 @@ namespace osu.Game.Screens.Play.HUD public IBindableNumber Accuracy => accuracy; - private readonly BindableDouble accuracy = new BindableDouble(); + private readonly BindableDouble accuracy = new BindableDouble(1); public IBindableNumber CurrentCombo => currentCombo; From b87f89986a5b2bb5fd73a43ba04459df84f92417 Mon Sep 17 00:00:00 2001 From: Joehu Date: Sat, 19 Dec 2020 16:57:25 -0800 Subject: [PATCH 374/818] Fix selected item not being highlighted on some setting dropdowns --- .../Settings/Sections/Graphics/LayoutSettings.cs | 3 +-- .../Sections/UserInterface/MainMenuSettings.cs | 11 +++-------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 62dc1dc806..3df2a78552 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -59,11 +59,10 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics Children = new Drawable[] { - windowModeDropdown = new SettingsDropdown + windowModeDropdown = new SettingsEnumDropdown { LabelText = "Screen mode", Current = config.GetBindable(FrameworkSetting.WindowMode), - ItemSource = windowModes, }, resolutionSettingsContainer = new Container { diff --git a/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs b/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs index 598b666642..95e2e9da30 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Configuration; @@ -28,23 +26,20 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface LabelText = "osu! music theme", Current = config.GetBindable(OsuSetting.MenuMusic) }, - new SettingsDropdown + new SettingsEnumDropdown { LabelText = "Intro sequence", Current = config.GetBindable(OsuSetting.IntroSequence), - Items = Enum.GetValues(typeof(IntroSequence)).Cast() }, - new SettingsDropdown + new SettingsEnumDropdown { LabelText = "Background source", Current = config.GetBindable(OsuSetting.MenuBackgroundSource), - Items = Enum.GetValues(typeof(BackgroundSource)).Cast() }, - new SettingsDropdown + new SettingsEnumDropdown { LabelText = "Seasonal backgrounds", Current = config.GetBindable(OsuSetting.SeasonalBackgroundMode), - Items = Enum.GetValues(typeof(SeasonalBackgroundMode)).Cast() } }; } From 5b8e35c98cec02b359eb2cf598713638538d349a Mon Sep 17 00:00:00 2001 From: Joehu Date: Sat, 19 Dec 2020 16:57:42 -0800 Subject: [PATCH 375/818] Make settings dropdown abstract --- osu.Game/Overlays/Settings/SettingsDropdown.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/SettingsDropdown.cs b/osu.Game/Overlays/Settings/SettingsDropdown.cs index 1175ddaab8..e1c6333aa0 100644 --- a/osu.Game/Overlays/Settings/SettingsDropdown.cs +++ b/osu.Game/Overlays/Settings/SettingsDropdown.cs @@ -9,7 +9,7 @@ using osu.Game.Graphics.UserInterface; namespace osu.Game.Overlays.Settings { - public class SettingsDropdown : SettingsItem + public abstract class SettingsDropdown : SettingsItem { protected new OsuDropdown Control => (OsuDropdown)base.Control; From d20eb368f5f86a22bf91a9f8f9838ef862effbc5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 20 Dec 2020 17:36:23 +0900 Subject: [PATCH 376/818] Make return into IEnumerable --- osu.Game/Screens/Multi/Components/RoomManager.cs | 2 +- osu.Game/Screens/Multi/Timeshift/TimeshiftRoomManager.cs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Multi/Components/RoomManager.cs b/osu.Game/Screens/Multi/Components/RoomManager.cs index 46941dc58e..ffc5e94106 100644 --- a/osu.Game/Screens/Multi/Components/RoomManager.cs +++ b/osu.Game/Screens/Multi/Components/RoomManager.cs @@ -183,6 +183,6 @@ namespace osu.Game.Screens.Multi.Components existing.CopyFrom(room); } - protected abstract RoomPollingComponent[] CreatePollingComponents(); + protected abstract IEnumerable CreatePollingComponents(); } } diff --git a/osu.Game/Screens/Multi/Timeshift/TimeshiftRoomManager.cs b/osu.Game/Screens/Multi/Timeshift/TimeshiftRoomManager.cs index ba96721afc..d21f844e04 100644 --- a/osu.Game/Screens/Multi/Timeshift/TimeshiftRoomManager.cs +++ b/osu.Game/Screens/Multi/Timeshift/TimeshiftRoomManager.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using osu.Framework.Bindables; using osu.Game.Screens.Multi.Components; @@ -11,7 +12,7 @@ namespace osu.Game.Screens.Multi.Timeshift public readonly Bindable TimeBetweenListingPolls = new Bindable(); public readonly Bindable TimeBetweenSelectionPolls = new Bindable(); - protected override RoomPollingComponent[] CreatePollingComponents() => new RoomPollingComponent[] + protected override IEnumerable CreatePollingComponents() => new RoomPollingComponent[] { new ListingPollingComponent { TimeBetweenPolls = { BindTarget = TimeBetweenListingPolls } }, new SelectionPollingComponent { TimeBetweenPolls = { BindTarget = TimeBetweenSelectionPolls } } From 7c7f15089a6ee06b3e244fc03db8e7114e165cca Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 20 Dec 2020 17:42:23 +0900 Subject: [PATCH 377/818] Make CreateRoomManager return the drawable version --- osu.Game/Screens/Multi/Multiplayer.cs | 8 ++++---- osu.Game/Screens/Multi/Timeshift/TimeshiftMultiplayer.cs | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index 5f61c5e635..837ccdf2e9 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.cs @@ -46,7 +46,7 @@ namespace osu.Game.Screens.Multi private readonly IBindable isIdle = new BindableBool(); [Cached(Type = typeof(IRoomManager))] - protected IRoomManager RoomManager { get; private set; } + protected RoomManager RoomManager { get; private set; } [Cached] private readonly Bindable selectedRoom = new Bindable(); @@ -81,7 +81,7 @@ namespace osu.Game.Screens.Multi InternalChild = waves = new MultiplayerWaveContainer { RelativeSizeAxes = Axes.Both, - Children = new[] + Children = new Drawable[] { new Box { @@ -136,7 +136,7 @@ namespace osu.Game.Screens.Multi Origin = Anchor.TopRight, Action = () => CreateRoom() }, - (Drawable)(RoomManager = CreateRoomManager()) + RoomManager = CreateRoomManager() } }; @@ -353,7 +353,7 @@ namespace osu.Game.Screens.Multi } } - protected abstract IRoomManager CreateRoomManager(); + protected abstract RoomManager CreateRoomManager(); private class MultiplayerWaveContainer : WaveContainer { diff --git a/osu.Game/Screens/Multi/Timeshift/TimeshiftMultiplayer.cs b/osu.Game/Screens/Multi/Timeshift/TimeshiftMultiplayer.cs index 1ff9c670a8..d2d6a35a2e 100644 --- a/osu.Game/Screens/Multi/Timeshift/TimeshiftMultiplayer.cs +++ b/osu.Game/Screens/Multi/Timeshift/TimeshiftMultiplayer.cs @@ -3,6 +3,7 @@ using osu.Framework.Logging; using osu.Framework.Screens; +using osu.Game.Screens.Multi.Components; using osu.Game.Screens.Multi.Lounge; using osu.Game.Screens.Multi.Match; @@ -43,6 +44,6 @@ namespace osu.Game.Screens.Multi.Timeshift Logger.Log($"Polling adjusted (listing: {timeshiftManager.TimeBetweenListingPolls.Value}, selection: {timeshiftManager.TimeBetweenSelectionPolls.Value})"); } - protected override IRoomManager CreateRoomManager() => new TimeshiftRoomManager(); + protected override RoomManager CreateRoomManager() => new TimeshiftRoomManager(); } } From d74485704ae9c4a424bfb5fc5780b03e14f3f8be Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 20 Dec 2020 17:46:45 +0900 Subject: [PATCH 378/818] Reset intial rooms received on filter change --- osu.Game/Screens/Multi/Components/ListingPollingComponent.cs | 2 ++ osu.Game/Screens/Multi/Components/RoomPollingComponent.cs | 5 ++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Multi/Components/ListingPollingComponent.cs b/osu.Game/Screens/Multi/Components/ListingPollingComponent.cs index e22f09779e..ebb3403950 100644 --- a/osu.Game/Screens/Multi/Components/ListingPollingComponent.cs +++ b/osu.Game/Screens/Multi/Components/ListingPollingComponent.cs @@ -25,6 +25,8 @@ namespace osu.Game.Screens.Multi.Components { currentFilter.BindValueChanged(_ => { + InitialRoomsReceived.Value = false; + if (IsLoaded) PollImmediately(); }); diff --git a/osu.Game/Screens/Multi/Components/RoomPollingComponent.cs b/osu.Game/Screens/Multi/Components/RoomPollingComponent.cs index 5430d54644..a81a9540c3 100644 --- a/osu.Game/Screens/Multi/Components/RoomPollingComponent.cs +++ b/osu.Game/Screens/Multi/Components/RoomPollingComponent.cs @@ -21,8 +21,7 @@ namespace osu.Game.Screens.Multi.Components /// public new readonly Bindable TimeBetweenPolls = new Bindable(); - public IBindable InitialRoomsReceived => initialRoomsReceived; - private readonly Bindable initialRoomsReceived = new Bindable(); + public readonly Bindable InitialRoomsReceived = new Bindable(); [Resolved] protected IAPIProvider API { get; private set; } @@ -34,7 +33,7 @@ namespace osu.Game.Screens.Multi.Components protected void NotifyRoomsReceived(List rooms) { - initialRoomsReceived.Value = true; + InitialRoomsReceived.Value = true; RoomsReceived?.Invoke(rooms); } } From 812a1d2b4f43cfb1eade5f22d9290bbe26d0fb27 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 20 Dec 2020 18:03:30 +0900 Subject: [PATCH 379/818] Fix onSuccess callback potentially being called on failure --- .../Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs index 4f73ee3865..1a6e976d15 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs @@ -62,7 +62,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer Debug.Assert(room.RoomID.Value != null); var joinTask = multiplayerClient.JoinRoom(room); - joinTask.ContinueWith(_ => onSuccess?.Invoke(room)); + joinTask.ContinueWith(_ => onSuccess?.Invoke(room), TaskContinuationOptions.OnlyOnRanToCompletion); joinTask.ContinueWith(t => { PartRoom(); From fb61cdfd417701b4cacfe1aefd4bb374e344c2e5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 20 Dec 2020 18:05:43 +0900 Subject: [PATCH 380/818] Remove unnecessary first-frame polling + address concerns --- .../Screens/Multi/Components/RoomManager.cs | 11 ++++++----- .../RealtimeMultiplayer/RealtimeRoomManager.cs | 17 ++++++++++++----- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Multi/Components/RoomManager.cs b/osu.Game/Screens/Multi/Components/RoomManager.cs index 21bff70b8b..7b419c9efe 100644 --- a/osu.Game/Screens/Multi/Components/RoomManager.cs +++ b/osu.Game/Screens/Multi/Components/RoomManager.cs @@ -27,7 +27,8 @@ namespace osu.Game.Screens.Multi.Components public IBindableList Rooms => rooms; - protected Room JoinedRoom { get; private set; } + protected IBindable JoinedRoom => joinedRoom; + private readonly Bindable joinedRoom = new Bindable(); [Resolved] private RulesetStore rulesets { get; set; } @@ -64,7 +65,7 @@ namespace osu.Game.Screens.Multi.Components req.Success += result => { - JoinedRoom = room; + joinedRoom.Value = room; update(room, result); addRoom(room); @@ -93,7 +94,7 @@ namespace osu.Game.Screens.Multi.Components currentJoinRoomRequest.Success += () => { - JoinedRoom = room; + joinedRoom.Value = room; onSuccess?.Invoke(room); }; @@ -114,8 +115,8 @@ namespace osu.Game.Screens.Multi.Components if (JoinedRoom == null) return; - api.Queue(new PartRoomRequest(JoinedRoom)); - JoinedRoom = null; + api.Queue(new PartRoomRequest(joinedRoom.Value)); + joinedRoom.Value = null; } private readonly HashSet ignoredRooms = new HashSet(); diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs index 1a6e976d15..9e3c921d9e 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs @@ -30,7 +30,8 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer base.LoadComplete(); isConnected.BindTo(multiplayerClient.IsConnected); - isConnected.BindValueChanged(_ => Schedule(updatePolling), true); + isConnected.BindValueChanged(_ => Schedule(updatePolling)); + JoinedRoom.BindValueChanged(_ => updatePolling()); updatePolling(); } @@ -46,7 +47,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer if (JoinedRoom == null) return; - var joinedRoom = JoinedRoom; + var joinedRoom = JoinedRoom.Value; base.PartRoom(); multiplayerClient.LeaveRoom().Wait(); @@ -77,7 +78,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer ClearRooms(); // Don't poll when not connected or when a room has been joined. - allowPolling.Value = isConnected.Value && JoinedRoom == null; + allowPolling.Value = isConnected.Value && JoinedRoom.Value == null; } protected override RoomPollingComponent[] CreatePollingComponents() => new RoomPollingComponent[] @@ -102,8 +103,11 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer { base.LoadComplete(); - AllowPolling.BindValueChanged(_ => + AllowPolling.BindValueChanged(allowPolling => { + if (!allowPolling.NewValue) + return; + if (IsLoaded) PollImmediately(); }); @@ -120,8 +124,11 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer { base.LoadComplete(); - AllowPolling.BindValueChanged(_ => + AllowPolling.BindValueChanged(allowPolling => { + if (!allowPolling.NewValue) + return; + if (IsLoaded) PollImmediately(); }); From 724e4b83fe266896bef4c10f9a51840cc36a24ae Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 20 Dec 2020 18:19:41 +0900 Subject: [PATCH 381/818] Fix nullability and remove early check --- .../StatefulMultiplayerClient.cs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs index 60960f4929..fe2f4c88f0 100644 --- a/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs @@ -9,6 +9,7 @@ using System.Linq; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Database; @@ -140,20 +141,14 @@ namespace osu.Game.Online.RealtimeMultiplayer RulesetID = Room.Settings.RulesetID }; - var newSettings = new MultiplayerRoomSettings + ChangeSettings(new MultiplayerRoomSettings { Name = name.GetOr(Room.Settings.Name), BeatmapID = item.GetOr(existingPlaylistItem).BeatmapID, BeatmapChecksum = item.GetOr(existingPlaylistItem).Beatmap.Value.MD5Hash, RulesetID = item.GetOr(existingPlaylistItem).RulesetID, - Mods = item.HasValue ? item.Value!.RequiredMods.Select(m => new APIMod(m)).ToList() : Room.Settings.Mods - }; - - // Make sure there would be a meaningful change in settings. - if (newSettings.Equals(Room.Settings)) - return; - - ChangeSettings(newSettings); + Mods = item.HasValue ? item.Value.AsNonNull().RequiredMods.Select(m => new APIMod(m)).ToList() : Room.Settings.Mods + }); } public abstract Task TransferHost(int userId); From ba4307a74c9bf82503b2e5dad391e3b28442959a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 20 Dec 2020 18:24:13 +0900 Subject: [PATCH 382/818] Directly return task --- .../RealtimeMultiplayer/TestRealtimeMultiplayerClient.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeMultiplayerClient.cs b/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeMultiplayerClient.cs index bfa8362c7e..de52633c88 100644 --- a/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeMultiplayerClient.cs @@ -106,14 +106,14 @@ namespace osu.Game.Tests.Visual.RealtimeMultiplayer return Task.CompletedTask; } - public override async Task StartMatch() + public override Task StartMatch() { Debug.Assert(Room != null); foreach (var user in Room.Users.Where(u => u.State == MultiplayerUserState.Ready)) ChangeUserState(user.UserID, MultiplayerUserState.WaitingForLoad); - await ((IMultiplayerClient)this).LoadRequested(); + return ((IMultiplayerClient)this).LoadRequested(); } } } From 1e2b425f3f0b787a747896e485a80a066b1a7429 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 20 Dec 2020 18:24:15 +0900 Subject: [PATCH 383/818] Fix incorrect test name + assertion --- .../RealtimeMultiplayer/TestSceneRealtimeRoomManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeRoomManager.cs b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeRoomManager.cs index 598641682b..925a83a863 100644 --- a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeRoomManager.cs +++ b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeRoomManager.cs @@ -120,7 +120,7 @@ namespace osu.Game.Tests.Visual.RealtimeMultiplayer } [Test] - public void TestMultiplayerRoomPartedWhenAPIRoomJoined() + public void TestMultiplayerRoomJoinedWhenAPIRoomJoined() { AddStep("create room manager with a room", () => { @@ -133,7 +133,7 @@ namespace osu.Game.Tests.Visual.RealtimeMultiplayer }); }); - AddAssert("multiplayer room parted", () => roomContainer.Client.Room != null); + AddAssert("multiplayer room joined", () => roomContainer.Client.Room != null); } private TestRealtimeRoomManager createRoomManager() From 8b1f5ff4927fa83700a4b22e213fe8ff0914d8d8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 20 Dec 2020 18:25:23 +0900 Subject: [PATCH 384/818] Only instantiate ruleset once --- .../Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs index fe2f4c88f0..77dbc16786 100644 --- a/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs @@ -354,14 +354,14 @@ namespace osu.Game.Online.RealtimeMultiplayer var beatmap = beatmapSet.Beatmaps.Single(b => b.OnlineBeatmapID == settings.BeatmapID); beatmap.MD5Hash = settings.BeatmapChecksum; - var ruleset = rulesets.GetRuleset(settings.RulesetID); - var mods = settings.Mods.Select(m => m.ToMod(ruleset.CreateInstance())); + var ruleset = rulesets.GetRuleset(settings.RulesetID).CreateInstance(); + var mods = settings.Mods.Select(m => m.ToMod(ruleset)); PlaylistItem playlistItem = new PlaylistItem { ID = playlistItemId, Beatmap = { Value = beatmap }, - Ruleset = { Value = ruleset }, + Ruleset = { Value = ruleset.RulesetInfo }, }; playlistItem.RequiredMods.AddRange(mods); From 508f73d94961b8baae97956a3c328b41277ed434 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 20 Dec 2020 18:25:54 +0900 Subject: [PATCH 385/818] Fix up comment --- .../Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs index 77dbc16786..35c8a3397e 100644 --- a/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs @@ -328,7 +328,7 @@ namespace osu.Game.Online.RealtimeMultiplayer if (Room == null) return; - // Update a few instantaneously properties of the room. + // Update a few properties of the room instantaneously. Schedule(() => { if (Room == null) From 0cf078562dada06982d9af80136c02e145d42efa Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 20 Dec 2020 18:30:00 +0900 Subject: [PATCH 386/818] Split method up and remove nested scheduling --- .../StatefulMultiplayerClient.cs | 59 +++++++++---------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs index 35c8a3397e..8d3b161804 100644 --- a/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs @@ -15,6 +15,7 @@ using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.Online.API; using osu.Game.Online.API.Requests; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer.RoomStatuses; using osu.Game.Rulesets; @@ -347,38 +348,36 @@ namespace osu.Game.Online.RealtimeMultiplayer }); var req = new GetBeatmapSetRequest(settings.BeatmapID, BeatmapSetLookupType.BeatmapId); - req.Success += res => - { - var beatmapSet = res.ToBeatmapSet(rulesets); - - var beatmap = beatmapSet.Beatmaps.Single(b => b.OnlineBeatmapID == settings.BeatmapID); - beatmap.MD5Hash = settings.BeatmapChecksum; - - var ruleset = rulesets.GetRuleset(settings.RulesetID).CreateInstance(); - var mods = settings.Mods.Select(m => m.ToMod(ruleset)); - - PlaylistItem playlistItem = new PlaylistItem - { - ID = playlistItemId, - Beatmap = { Value = beatmap }, - Ruleset = { Value = ruleset.RulesetInfo }, - }; - - playlistItem.RequiredMods.AddRange(mods); - - Schedule(() => - { - if (Room == null || !Room.Settings.Equals(settings)) - return; - - Debug.Assert(apiRoom != null); - - apiRoom.Playlist.Clear(); // Clearing should be unnecessary, but here for sanity. - apiRoom.Playlist.Add(playlistItem); - }); - }; + req.Success += res => updatePlaylist(settings, res); api.Queue(req); } + + private void updatePlaylist(MultiplayerRoomSettings settings, APIBeatmapSet onlineSet) + { + if (Room == null || !Room.Settings.Equals(settings)) + return; + + Debug.Assert(apiRoom != null); + + var beatmapSet = onlineSet.ToBeatmapSet(rulesets); + var beatmap = beatmapSet.Beatmaps.Single(b => b.OnlineBeatmapID == settings.BeatmapID); + beatmap.MD5Hash = settings.BeatmapChecksum; + + var ruleset = rulesets.GetRuleset(settings.RulesetID).CreateInstance(); + var mods = settings.Mods.Select(m => m.ToMod(ruleset)); + + PlaylistItem playlistItem = new PlaylistItem + { + ID = playlistItemId, + Beatmap = { Value = beatmap }, + Ruleset = { Value = ruleset.RulesetInfo }, + }; + + playlistItem.RequiredMods.AddRange(mods); + + apiRoom.Playlist.Clear(); // Clearing should be unnecessary, but here for sanity. + apiRoom.Playlist.Add(playlistItem); + } } } From 45107280a00e310402a368a6f9b15050f1bcfd0a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 20 Dec 2020 18:34:54 +0900 Subject: [PATCH 387/818] Make TimeBetweenPolls into a bindable --- .../Components/TestScenePollingComponent.cs | 10 +++---- osu.Game/Online/Chat/ChannelManager.cs | 2 +- osu.Game/Online/PollingComponent.cs | 30 ++++++++----------- .../Multi/Components/RoomPollingComponent.cs | 11 ------- 4 files changed, 19 insertions(+), 34 deletions(-) diff --git a/osu.Game.Tests/Visual/Components/TestScenePollingComponent.cs b/osu.Game.Tests/Visual/Components/TestScenePollingComponent.cs index fb10015ef4..2236f85b92 100644 --- a/osu.Game.Tests/Visual/Components/TestScenePollingComponent.cs +++ b/osu.Game.Tests/Visual/Components/TestScenePollingComponent.cs @@ -61,12 +61,12 @@ namespace osu.Game.Tests.Visual.Components { createPoller(true); - AddStep("set poll interval to 1", () => poller.TimeBetweenPolls = TimePerAction * safety_adjust); + AddStep("set poll interval to 1", () => poller.TimeBetweenPolls.Value = TimePerAction * safety_adjust); checkCount(1); checkCount(2); checkCount(3); - AddStep("set poll interval to 5", () => poller.TimeBetweenPolls = TimePerAction * safety_adjust * 5); + AddStep("set poll interval to 5", () => poller.TimeBetweenPolls.Value = TimePerAction * safety_adjust * 5); checkCount(4); checkCount(4); checkCount(4); @@ -76,7 +76,7 @@ namespace osu.Game.Tests.Visual.Components checkCount(5); checkCount(5); - AddStep("set poll interval to 1", () => poller.TimeBetweenPolls = TimePerAction * safety_adjust); + AddStep("set poll interval to 1", () => poller.TimeBetweenPolls.Value = TimePerAction * safety_adjust); checkCount(6); checkCount(7); } @@ -87,7 +87,7 @@ namespace osu.Game.Tests.Visual.Components { createPoller(false); - AddStep("set poll interval to 1", () => poller.TimeBetweenPolls = TimePerAction * safety_adjust * 5); + AddStep("set poll interval to 1", () => poller.TimeBetweenPolls.Value = TimePerAction * safety_adjust * 5); checkCount(0); skip(); checkCount(0); @@ -141,7 +141,7 @@ namespace osu.Game.Tests.Visual.Components public class TestSlowPoller : TestPoller { - protected override Task Poll() => Task.Delay((int)(TimeBetweenPolls / 2f / Clock.Rate)).ContinueWith(_ => base.Poll()); + protected override Task Poll() => Task.Delay((int)(TimeBetweenPolls.Value / 2f / Clock.Rate)).ContinueWith(_ => base.Poll()); } } } diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 16f46581c5..62ae507419 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -57,7 +57,7 @@ namespace osu.Game.Online.Chat { CurrentChannel.ValueChanged += currentChannelChanged; - HighPollRate.BindValueChanged(enabled => TimeBetweenPolls = enabled.NewValue ? 1000 : 6000, true); + HighPollRate.BindValueChanged(enabled => TimeBetweenPolls.Value = enabled.NewValue ? 1000 : 6000, true); } /// diff --git a/osu.Game/Online/PollingComponent.cs b/osu.Game/Online/PollingComponent.cs index 228f147835..3d19f2ab09 100644 --- a/osu.Game/Online/PollingComponent.cs +++ b/osu.Game/Online/PollingComponent.cs @@ -3,6 +3,7 @@ using System; using System.Threading.Tasks; +using osu.Framework.Bindables; using osu.Framework.Graphics.Containers; using osu.Framework.Threading; @@ -19,22 +20,11 @@ namespace osu.Game.Online private bool pollingActive; - private double timeBetweenPolls; - /// /// The time in milliseconds to wait between polls. /// Setting to zero stops all polling. /// - public double TimeBetweenPolls - { - get => timeBetweenPolls; - set - { - timeBetweenPolls = value; - scheduledPoll?.Cancel(); - pollIfNecessary(); - } - } + public readonly Bindable TimeBetweenPolls = new Bindable(); /// /// @@ -42,7 +32,13 @@ namespace osu.Game.Online /// The initial time in milliseconds to wait between polls. Setting to zero stops all polling. protected PollingComponent(double timeBetweenPolls = 0) { - TimeBetweenPolls = timeBetweenPolls; + TimeBetweenPolls.BindValueChanged(_ => + { + scheduledPoll?.Cancel(); + pollIfNecessary(); + }); + + TimeBetweenPolls.Value = timeBetweenPolls; } protected override void LoadComplete() @@ -60,7 +56,7 @@ namespace osu.Game.Online if (pollingActive) return false; // don't try polling if the time between polls hasn't been set. - if (timeBetweenPolls == 0) return false; + if (TimeBetweenPolls.Value == 0) return false; if (!lastTimePolled.HasValue) { @@ -68,7 +64,7 @@ namespace osu.Game.Online return true; } - if (Time.Current - lastTimePolled.Value > timeBetweenPolls) + if (Time.Current - lastTimePolled.Value > TimeBetweenPolls.Value) { doPoll(); return true; @@ -99,7 +95,7 @@ namespace osu.Game.Online /// public void PollImmediately() { - lastTimePolled = Time.Current - timeBetweenPolls; + lastTimePolled = Time.Current - TimeBetweenPolls.Value; scheduleNextPoll(); } @@ -121,7 +117,7 @@ namespace osu.Game.Online double lastPollDuration = lastTimePolled.HasValue ? Time.Current - lastTimePolled.Value : 0; - scheduledPoll = Scheduler.AddDelayed(doPoll, Math.Max(0, timeBetweenPolls - lastPollDuration)); + scheduledPoll = Scheduler.AddDelayed(doPoll, Math.Max(0, TimeBetweenPolls.Value - lastPollDuration)); } } } diff --git a/osu.Game/Screens/Multi/Components/RoomPollingComponent.cs b/osu.Game/Screens/Multi/Components/RoomPollingComponent.cs index a81a9540c3..ad0720db7b 100644 --- a/osu.Game/Screens/Multi/Components/RoomPollingComponent.cs +++ b/osu.Game/Screens/Multi/Components/RoomPollingComponent.cs @@ -15,22 +15,11 @@ namespace osu.Game.Screens.Multi.Components { public Action> RoomsReceived; - /// - /// The time in milliseconds to wait between polls. - /// Setting to zero stops all polling. - /// - public new readonly Bindable TimeBetweenPolls = new Bindable(); - public readonly Bindable InitialRoomsReceived = new Bindable(); [Resolved] protected IAPIProvider API { get; private set; } - protected RoomPollingComponent() - { - TimeBetweenPolls.BindValueChanged(time => base.TimeBetweenPolls = time.NewValue); - } - protected void NotifyRoomsReceived(List rooms) { InitialRoomsReceived.Value = true; From ce2560b545deea1076c5f6cfd781e9089a360061 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 20 Dec 2020 18:36:31 +0900 Subject: [PATCH 388/818] Extract value into const --- .../Participants/ParticipantPanel.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantPanel.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantPanel.cs index 306a54bfdc..002849a275 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantPanel.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantPanel.cs @@ -142,15 +142,17 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Participants if (Room == null) return; + const double fade_time = 50; + if (User.State == MultiplayerUserState.Ready) - readyMark.FadeIn(50); + readyMark.FadeIn(fade_time); else - readyMark.FadeOut(50); + readyMark.FadeOut(fade_time); if (Room.Host?.Equals(User) == true) - crown.FadeIn(50); + crown.FadeIn(fade_time); else - crown.FadeOut(50); + crown.FadeOut(fade_time); } public MenuItem[] ContextMenuItems From 19db35501e156ceaaf45f65bb4360dc4d859a68f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 20 Dec 2020 18:44:36 +0900 Subject: [PATCH 389/818] Fix incorrect end date usage in timeshift ready button --- osu.Game/Screens/Multi/Timeshift/TimeshiftReadyButton.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Multi/Timeshift/TimeshiftReadyButton.cs b/osu.Game/Screens/Multi/Timeshift/TimeshiftReadyButton.cs index b6698b195c..ba639c29f4 100644 --- a/osu.Game/Screens/Multi/Timeshift/TimeshiftReadyButton.cs +++ b/osu.Game/Screens/Multi/Timeshift/TimeshiftReadyButton.cs @@ -13,7 +13,7 @@ namespace osu.Game.Screens.Multi.Timeshift public class TimeshiftReadyButton : ReadyButton { [Resolved(typeof(Room), nameof(Room.EndDate))] - private Bindable endDate { get; set; } + private Bindable endDate { get; set; } public TimeshiftReadyButton() { @@ -32,7 +32,7 @@ namespace osu.Game.Screens.Multi.Timeshift { base.Update(); - Enabled.Value = endDate.Value == null || DateTimeOffset.UtcNow.AddSeconds(30).AddMilliseconds(GameBeatmap.Value.Track.Length) < endDate.Value; + Enabled.Value = DateTimeOffset.UtcNow.AddSeconds(30).AddMilliseconds(GameBeatmap.Value.Track.Length) < endDate.Value; } } } From a07a36793a5d90781178b0f94743580359c17973 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 20 Dec 2020 18:44:41 +0900 Subject: [PATCH 390/818] Fix test not working --- .../RealtimeMultiplayer/TestSceneRealtimeReadyButton.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeReadyButton.cs b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeReadyButton.cs index 889c0c0be3..1f863028af 100644 --- a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeReadyButton.cs +++ b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeReadyButton.cs @@ -97,6 +97,12 @@ namespace osu.Game.Tests.Visual.RealtimeMultiplayer [Test] public void TestBecomeHostWhileReady() { + AddStep("add host", () => + { + Client.AddUser(new User { Id = 2, Username = "Another user" }); + Client.TransferHost(2); + }); + addClickButtonStep(); AddStep("make user host", () => Client.TransferHost(Client.Room?.Users[0].UserID ?? 0)); From b002c466660535e32e036d322e97e68335c6c497 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 20 Dec 2020 18:49:39 +0900 Subject: [PATCH 391/818] Add number of ready users to button --- .../Screens/Multi/RealtimeMultiplayer/RealtimeReadyButton.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeReadyButton.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeReadyButton.cs index d52df258ad..ea8fb04994 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeReadyButton.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeReadyButton.cs @@ -69,8 +69,9 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer case MultiplayerUserState.Ready: if (Room?.Host?.Equals(localUser) == true) { - button.Text = "Let's go!"; - updateButtonColour(Room.Users.All(u => u.State == MultiplayerUserState.Ready)); + int countReady = Room.Users.Count(u => u.State == MultiplayerUserState.Ready); + button.Text = $"Start match ({countReady} / {Room.Users.Count} ready)"; + updateButtonColour(true); } else { From f876a329b1b4e8b2cbef4effd6ef242e1be1c233 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 20 Dec 2020 22:51:33 +0900 Subject: [PATCH 392/818] Fire-and-forget leave-room request --- .../StatefulMultiplayerClient.cs | 29 +++++++++++++++++-- .../RealtimeRoomManager.cs | 2 +- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs index 60960f4929..b846d6732f 100644 --- a/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs @@ -77,7 +77,9 @@ namespace osu.Game.Online.RealtimeMultiplayer /// The API . public async Task JoinRoom(Room room) { - Debug.Assert(Room == null); + if (Room != null) + throw new InvalidOperationException("Cannot join a multiplayer room while already in one."); + Debug.Assert(room.RoomID.Value != null); apiRoom = room; @@ -166,6 +168,9 @@ namespace osu.Game.Online.RealtimeMultiplayer Task IMultiplayerClient.RoomStateChanged(MultiplayerRoomState state) { + if (Room == null) + return Task.CompletedTask; + Schedule(() => { if (Room == null) @@ -198,6 +203,9 @@ namespace osu.Game.Online.RealtimeMultiplayer async Task IMultiplayerClient.UserJoined(MultiplayerRoomUser user) { + if (Room == null) + return; + await PopulateUser(user); Schedule(() => @@ -213,6 +221,9 @@ namespace osu.Game.Online.RealtimeMultiplayer Task IMultiplayerClient.UserLeft(MultiplayerRoomUser user) { + if (Room == null) + return Task.CompletedTask; + Schedule(() => { if (Room == null) @@ -229,6 +240,9 @@ namespace osu.Game.Online.RealtimeMultiplayer Task IMultiplayerClient.HostChanged(int userId) { + if (Room == null) + return Task.CompletedTask; + Schedule(() => { if (Room == null) @@ -255,6 +269,9 @@ namespace osu.Game.Online.RealtimeMultiplayer Task IMultiplayerClient.UserStateChanged(int userId, MultiplayerUserState state) { + if (Room == null) + return Task.CompletedTask; + Schedule(() => { if (Room == null) @@ -273,6 +290,9 @@ namespace osu.Game.Online.RealtimeMultiplayer Task IMultiplayerClient.LoadRequested() { + if (Room == null) + return Task.CompletedTask; + Schedule(() => { if (Room == null) @@ -286,7 +306,9 @@ namespace osu.Game.Online.RealtimeMultiplayer Task IMultiplayerClient.MatchStarted() { - Debug.Assert(Room != null); + if (Room == null) + return Task.CompletedTask; + var players = Room.Users.Where(u => u.State == MultiplayerUserState.Playing).Select(u => u.UserID).ToList(); Schedule(() => @@ -304,6 +326,9 @@ namespace osu.Game.Online.RealtimeMultiplayer Task IMultiplayerClient.ResultsReady() { + if (Room == null) + return Task.CompletedTask; + Schedule(() => { if (Room == null) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs index 4f73ee3865..d2a03da714 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs @@ -49,7 +49,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer var joinedRoom = JoinedRoom; base.PartRoom(); - multiplayerClient.LeaveRoom().Wait(); + 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. RemoveRoom(joinedRoom); From 9d13a5b06a9f90e9cd9a8e489594f03b3d3ec4f0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 20 Dec 2020 22:53:07 +0900 Subject: [PATCH 393/818] Fix potential cross-thread list access --- .../Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs index b846d6732f..bf58849e35 100644 --- a/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs @@ -309,14 +309,12 @@ namespace osu.Game.Online.RealtimeMultiplayer if (Room == null) return Task.CompletedTask; - var players = Room.Users.Where(u => u.State == MultiplayerUserState.Playing).Select(u => u.UserID).ToList(); - Schedule(() => { if (Room == null) return; - PlayingUsers.AddRange(players); + PlayingUsers.AddRange(Room.Users.Where(u => u.State == MultiplayerUserState.Playing).Select(u => u.UserID)); MatchStarted?.Invoke(); }); From c33e693b8e3331af505931950940dc7684b04f41 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 20 Dec 2020 23:05:17 +0900 Subject: [PATCH 394/818] Refactor InitialRoomsReceived to avoid extra bindables --- .../Visual/Multiplayer/TestRoomManager.cs | 2 +- .../Multiplayer/TestSceneMatchSettingsOverlay.cs | 2 +- .../Multiplayer/TestSceneMatchSubScreen.cs | 2 +- .../Multi/Components/ListingPollingComponent.cs | 3 +-- osu.Game/Screens/Multi/Components/RoomManager.cs | 12 ++++++++++-- .../Multi/Components/RoomPollingComponent.cs | 16 ++++++++-------- osu.Game/Screens/Multi/IRoomManager.cs | 2 +- osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs | 2 +- 8 files changed, 24 insertions(+), 17 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestRoomManager.cs b/osu.Game.Tests/Visual/Multiplayer/TestRoomManager.cs index 67a53307fc..9dd4aea4bd 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestRoomManager.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestRoomManager.cs @@ -18,7 +18,7 @@ namespace osu.Game.Tests.Visual.Multiplayer public readonly BindableList Rooms = new BindableList(); - public Bindable InitialRoomsReceived { get; } = new Bindable(true); + public IBindable InitialRoomsReceived { get; } = new Bindable(true); IBindableList IRoomManager.Rooms => Rooms; diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs index cbe8cc6137..234374ee2b 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs @@ -131,7 +131,7 @@ namespace osu.Game.Tests.Visual.Multiplayer remove { } } - public Bindable InitialRoomsReceived { get; } = new Bindable(true); + public IBindable InitialRoomsReceived { get; } = new Bindable(true); public IBindableList Rooms => null; diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSubScreen.cs index 65e9893851..bceb6efac1 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSubScreen.cs @@ -151,7 +151,7 @@ namespace osu.Game.Tests.Visual.Multiplayer remove => throw new NotImplementedException(); } - public Bindable InitialRoomsReceived { get; } = new Bindable(true); + public IBindable InitialRoomsReceived { get; } = new Bindable(true); public IBindableList Rooms { get; } = new BindableList(); diff --git a/osu.Game/Screens/Multi/Components/ListingPollingComponent.cs b/osu.Game/Screens/Multi/Components/ListingPollingComponent.cs index ebb3403950..dff6c50bf2 100644 --- a/osu.Game/Screens/Multi/Components/ListingPollingComponent.cs +++ b/osu.Game/Screens/Multi/Components/ListingPollingComponent.cs @@ -25,8 +25,7 @@ namespace osu.Game.Screens.Multi.Components { currentFilter.BindValueChanged(_ => { - InitialRoomsReceived.Value = false; - + NotifyRoomsReceived(null); if (IsLoaded) PollImmediately(); }); diff --git a/osu.Game/Screens/Multi/Components/RoomManager.cs b/osu.Game/Screens/Multi/Components/RoomManager.cs index ffc5e94106..382ce52723 100644 --- a/osu.Game/Screens/Multi/Components/RoomManager.cs +++ b/osu.Game/Screens/Multi/Components/RoomManager.cs @@ -23,7 +23,8 @@ namespace osu.Game.Screens.Multi.Components private readonly BindableList rooms = new BindableList(); - public Bindable InitialRoomsReceived { get; } = new Bindable(); + public IBindable InitialRoomsReceived => initialRoomsReceived; + private readonly Bindable initialRoomsReceived = new Bindable(); public IBindableList Rooms => rooms; @@ -44,7 +45,6 @@ namespace osu.Game.Screens.Multi.Components InternalChildren = CreatePollingComponents().Select(p => { - p.InitialRoomsReceived.BindTo(InitialRoomsReceived); p.RoomsReceived = onRoomsReceived; return p; }).ToList(); @@ -122,6 +122,13 @@ namespace osu.Game.Screens.Multi.Components private void onRoomsReceived(List received) { + if (received == null) + { + rooms.Clear(); + initialRoomsReceived.Value = false; + return; + } + // Remove past matches foreach (var r in rooms.ToList()) { @@ -155,6 +162,7 @@ namespace osu.Game.Screens.Multi.Components } RoomsUpdated?.Invoke(); + initialRoomsReceived.Value = true; } /// diff --git a/osu.Game/Screens/Multi/Components/RoomPollingComponent.cs b/osu.Game/Screens/Multi/Components/RoomPollingComponent.cs index ad0720db7b..fbaf9dd930 100644 --- a/osu.Game/Screens/Multi/Components/RoomPollingComponent.cs +++ b/osu.Game/Screens/Multi/Components/RoomPollingComponent.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Game.Online; using osu.Game.Online.API; using osu.Game.Online.Multiplayer; @@ -13,17 +12,18 @@ namespace osu.Game.Screens.Multi.Components { public abstract class RoomPollingComponent : PollingComponent { + /// + /// Invoked when any s have been received from the API. + /// + /// Any s present locally but not returned by this event are to be removed from display. + /// If null, the display of local rooms is reset to an initial state. + /// + /// public Action> RoomsReceived; - public readonly Bindable InitialRoomsReceived = new Bindable(); - [Resolved] protected IAPIProvider API { get; private set; } - protected void NotifyRoomsReceived(List rooms) - { - InitialRoomsReceived.Value = true; - RoomsReceived?.Invoke(rooms); - } + protected void NotifyRoomsReceived(List rooms) => RoomsReceived?.Invoke(rooms); } } diff --git a/osu.Game/Screens/Multi/IRoomManager.cs b/osu.Game/Screens/Multi/IRoomManager.cs index 3d18edcd71..630e3af91c 100644 --- a/osu.Game/Screens/Multi/IRoomManager.cs +++ b/osu.Game/Screens/Multi/IRoomManager.cs @@ -19,7 +19,7 @@ namespace osu.Game.Screens.Multi /// /// Whether an initial listing of rooms has been received. /// - Bindable InitialRoomsReceived { get; } + IBindable InitialRoomsReceived { get; } /// /// All the active s. diff --git a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs index a26a64d86d..165a2b201c 100644 --- a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs @@ -27,7 +27,7 @@ namespace osu.Game.Screens.Multi.Lounge protected override UserActivity InitialActivity => new UserActivity.SearchingForLobby(); - private readonly Bindable initialRoomsReceived = new Bindable(); + private readonly IBindable initialRoomsReceived = new Bindable(); private Container content; private LoadingLayer loadingLayer; From 594db76cf3191ae55861633c1efa187794a018c5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 20 Dec 2020 23:10:45 +0900 Subject: [PATCH 395/818] Fix compilation errors --- osu.Game/Screens/Multi/Components/RoomManager.cs | 5 ++--- .../Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs | 3 ++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Multi/Components/RoomManager.cs b/osu.Game/Screens/Multi/Components/RoomManager.cs index 0bcf3a90c3..276a5a6148 100644 --- a/osu.Game/Screens/Multi/Components/RoomManager.cs +++ b/osu.Game/Screens/Multi/Components/RoomManager.cs @@ -125,8 +125,7 @@ namespace osu.Game.Screens.Multi.Components { if (received == null) { - rooms.Clear(); - initialRoomsReceived.Value = false; + ClearRooms(); return; } @@ -171,7 +170,7 @@ namespace osu.Game.Screens.Multi.Components protected void ClearRooms() { rooms.Clear(); - InitialRoomsReceived.Value = false; + initialRoomsReceived.Value = false; } /// diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs index 9e3c921d9e..a50628a5fa 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Diagnostics; using System.Threading.Tasks; using osu.Framework.Allocation; @@ -81,7 +82,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer allowPolling.Value = isConnected.Value && JoinedRoom.Value == null; } - protected override RoomPollingComponent[] CreatePollingComponents() => new RoomPollingComponent[] + protected override IEnumerable CreatePollingComponents() => new RoomPollingComponent[] { listingPollingComponent = new RealtimeListingPollingComponent { From 1d7d8bd6fccba84c32f004caddaf3fb0a3bdfe33 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 20 Dec 2020 23:26:31 +0900 Subject: [PATCH 396/818] Hook up a realtime multiplayer client --- .../RealtimeMultiplayerClient.cs | 171 ++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 osu.Game/Online/RealtimeMultiplayer/RealtimeMultiplayerClient.cs diff --git a/osu.Game/Online/RealtimeMultiplayer/RealtimeMultiplayerClient.cs b/osu.Game/Online/RealtimeMultiplayer/RealtimeMultiplayerClient.cs new file mode 100644 index 0000000000..6f18e1c922 --- /dev/null +++ b/osu.Game/Online/RealtimeMultiplayer/RealtimeMultiplayerClient.cs @@ -0,0 +1,171 @@ +// 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; +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; +using osu.Game.Online.API; + +namespace osu.Game.Online.RealtimeMultiplayer +{ + public class RealtimeMultiplayerClient : StatefulMultiplayerClient + { + private const string endpoint = "https://spectator.ppy.sh/multiplayer"; + + public override IBindable IsConnected => isConnected; + + private readonly Bindable isConnected = new Bindable(); + private readonly IBindable apiState = new Bindable(); + + [Resolved] + private IAPIProvider api { get; set; } = null!; + + private HubConnection? connection; + + [BackgroundDependencyLoader] + private void load() + { + apiState.BindTo(api.State); + apiState.BindValueChanged(apiStateChanged, true); + } + + private void apiStateChanged(ValueChangedEvent state) + { + switch (state.NewValue) + { + case APIState.Failing: + case APIState.Offline: + connection?.StopAsync(); + connection = null; + break; + + case APIState.Online: + Task.Run(Connect); + break; + } + } + + protected virtual async Task Connect() + { + if (connection != null) + return; + + connection = new HubConnectionBuilder() + .WithUrl(endpoint, options => + { + options.Headers.Add("Authorization", $"Bearer {api.AccessToken}"); + }) + .AddNewtonsoftJsonProtocol(options => { options.PayloadSerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; }) + .Build(); + + // this is kind of SILLY + // https://github.com/dotnet/aspnetcore/issues/15198 + connection.On(nameof(IMultiplayerClient.RoomStateChanged), ((IMultiplayerClient)this).RoomStateChanged); + connection.On(nameof(IMultiplayerClient.UserJoined), ((IMultiplayerClient)this).UserJoined); + connection.On(nameof(IMultiplayerClient.UserLeft), ((IMultiplayerClient)this).UserLeft); + connection.On(nameof(IMultiplayerClient.HostChanged), ((IMultiplayerClient)this).HostChanged); + connection.On(nameof(IMultiplayerClient.SettingsChanged), ((IMultiplayerClient)this).SettingsChanged); + connection.On(nameof(IMultiplayerClient.UserStateChanged), ((IMultiplayerClient)this).UserStateChanged); + connection.On(nameof(IMultiplayerClient.LoadRequested), ((IMultiplayerClient)this).LoadRequested); + connection.On(nameof(IMultiplayerClient.MatchStarted), ((IMultiplayerClient)this).MatchStarted); + connection.On(nameof(IMultiplayerClient.ResultsReady), ((IMultiplayerClient)this).ResultsReady); + + connection.Closed += async ex => + { + isConnected.Value = false; + + if (ex != null) + { + Logger.Log($"Multiplayer client lost connection: {ex}", LoggingTarget.Network); + await tryUntilConnected(); + } + }; + + await tryUntilConnected(); + + async Task tryUntilConnected() + { + Logger.Log("Multiplayer client connecting...", LoggingTarget.Network); + + while (api.State.Value == APIState.Online) + { + try + { + // reconnect on any failure + await connection.StartAsync(); + Logger.Log("Multiplayer client connected!", LoggingTarget.Network); + + // Success. + isConnected.Value = true; + break; + } + catch (Exception e) + { + Logger.Log($"Multiplayer client connection error: {e}", LoggingTarget.Network); + await Task.Delay(5000); + } + } + } + } + + protected override Task JoinRoom(long roomId) + { + if (!isConnected.Value) + return Task.FromCanceled(CancellationToken.None); + + return connection.InvokeAsync(nameof(IMultiplayerServer.JoinRoom), roomId); + } + + public override async Task LeaveRoom() + { + if (!isConnected.Value) + return; + + if (Room == null) + return; + + await base.LeaveRoom(); + await connection.InvokeAsync(nameof(IMultiplayerServer.LeaveRoom)); + } + + public override Task TransferHost(int userId) + { + if (!isConnected.Value) + return Task.CompletedTask; + + return connection.InvokeAsync(nameof(IMultiplayerServer.TransferHost), userId); + } + + public override Task ChangeSettings(MultiplayerRoomSettings settings) + { + if (!isConnected.Value) + return Task.CompletedTask; + + return connection.InvokeAsync(nameof(IMultiplayerServer.ChangeSettings), settings); + } + + public override Task ChangeState(MultiplayerUserState newState) + { + if (!isConnected.Value) + return Task.CompletedTask; + + return connection.InvokeAsync(nameof(IMultiplayerServer.ChangeState), newState); + } + + public override Task StartMatch() + { + if (!isConnected.Value) + return Task.CompletedTask; + + return connection.InvokeAsync(nameof(IMultiplayerServer.StartMatch)); + } + } +} From 455a84c73fb616e89ec76c8af8cb553ed61d3688 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 20 Dec 2020 23:32:57 +0900 Subject: [PATCH 397/818] Add realtime multiplayer screen --- .../Multi/Lounge/Components/DrawableRoom.cs | 2 +- osu.Game/Screens/Multi/Multiplayer.cs | 12 +++- .../RealtimeMultiplayer.cs | 65 +++++++++++++++++++ 3 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMultiplayer.cs diff --git a/osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs index 01a85382e4..56116b219a 100644 --- a/osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs @@ -242,7 +242,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components { new OsuMenuItem("Create copy", MenuItemType.Standard, () => { - multiplayer?.CreateRoom(Room.CreateCopy()); + multiplayer?.OpenNewRoom(Room.CreateCopy()); }) }; } diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index 837ccdf2e9..027fedaad6 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.cs @@ -134,7 +134,7 @@ namespace osu.Game.Screens.Multi { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, - Action = () => CreateRoom() + Action = () => OpenNewRoom() }, RoomManager = CreateRoomManager() } @@ -264,10 +264,16 @@ namespace osu.Game.Screens.Multi } /// - /// Create a new room. + /// Creates and opens the newly-created room. /// /// An optional template to use when creating the room. - public void CreateRoom(Room room = null) => loungeSubScreen.Open(room ?? new Room { Name = { Value = $"{api.LocalUser}'s awesome room" } }); + public void OpenNewRoom(Room room = null) => loungeSubScreen.Open(room ?? CreateNewRoom()); + + /// + /// Creates a new room. + /// + /// The created . + protected virtual Room CreateNewRoom() => new Room { Name = { Value = $"{api.LocalUser}'s awesome room" } }; private void beginHandlingTrack() { diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMultiplayer.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMultiplayer.cs new file mode 100644 index 0000000000..075c552437 --- /dev/null +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMultiplayer.cs @@ -0,0 +1,65 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Logging; +using osu.Framework.Screens; +using osu.Game.Online.Multiplayer; +using osu.Game.Online.RealtimeMultiplayer; +using osu.Game.Screens.Multi.Components; +using osu.Game.Screens.Multi.Lounge; + +namespace osu.Game.Screens.Multi.RealtimeMultiplayer +{ + public class RealtimeMultiplayer : Multiplayer + { + [Resolved] + private StatefulMultiplayerClient client { get; set; } + + public override void OnResuming(IScreen last) + { + base.OnResuming(last); + + if (client.Room != null) + client.ChangeState(MultiplayerUserState.Idle); + } + + protected override void UpdatePollingRate(bool isIdle) + { + var timeshiftManager = (RealtimeRoomManager)RoomManager; + + if (!this.IsCurrentScreen()) + { + timeshiftManager.TimeBetweenListingPolls.Value = 0; + timeshiftManager.TimeBetweenSelectionPolls.Value = 0; + } + else + { + switch (CurrentSubScreen) + { + case LoungeSubScreen _: + timeshiftManager.TimeBetweenListingPolls.Value = isIdle ? 120000 : 15000; + timeshiftManager.TimeBetweenSelectionPolls.Value = isIdle ? 120000 : 15000; + break; + + // Don't poll inside the match or anywhere else. + default: + timeshiftManager.TimeBetweenListingPolls.Value = 0; + timeshiftManager.TimeBetweenSelectionPolls.Value = 0; + break; + } + } + + Logger.Log($"Polling adjusted (listing: {timeshiftManager.TimeBetweenListingPolls.Value}, selection: {timeshiftManager.TimeBetweenSelectionPolls.Value})"); + } + + protected override Room CreateNewRoom() + { + var room = base.CreateNewRoom(); + room.Category.Value = RoomCategory.Realtime; + return room; + } + + protected override RoomManager CreateRoomManager() => new RealtimeRoomManager(); + } +} From b9e4a7196e454743a96736c597b6749975e197eb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 20 Dec 2020 23:36:56 +0900 Subject: [PATCH 398/818] Add realtime lounge subscreen --- ...s => TestSceneTimeshiftLoungeSubScreen.cs} | 5 ++-- .../Screens/Multi/Lounge/LoungeSubScreen.cs | 25 ++++++++++--------- osu.Game/Screens/Multi/Multiplayer.cs | 4 ++- .../RealtimeFilterControl.cs | 17 +++++++++++++ .../RealtimeLoungeSubScreen.cs | 13 ++++++++++ .../RealtimeMultiplayer.cs | 2 ++ .../Timeshift/TimeshiftLoungeSubScreen.cs | 13 ++++++++++ .../Multi/Timeshift/TimeshiftMultiplayer.cs | 2 ++ 8 files changed, 66 insertions(+), 15 deletions(-) rename osu.Game.Tests/Visual/Multiplayer/{TestSceneLoungeSubScreen.cs => TestSceneTimeshiftLoungeSubScreen.cs} (92%) create mode 100644 osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeFilterControl.cs create mode 100644 osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeLoungeSubScreen.cs create mode 100644 osu.Game/Screens/Multi/Timeshift/TimeshiftLoungeSubScreen.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftLoungeSubScreen.cs similarity index 92% rename from osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeSubScreen.cs rename to osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftLoungeSubScreen.cs index 68987127d2..73afd65d6d 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftLoungeSubScreen.cs @@ -10,10 +10,11 @@ using osu.Framework.Testing; using osu.Game.Graphics.Containers; using osu.Game.Screens.Multi.Lounge; using osu.Game.Screens.Multi.Lounge.Components; +using osu.Game.Screens.Multi.Timeshift; namespace osu.Game.Tests.Visual.Multiplayer { - public class TestSceneLoungeSubScreen : RoomManagerTestScene + public class TestSceneTimeshiftLoungeSubScreen : RoomManagerTestScene { private LoungeSubScreen loungeScreen; @@ -26,7 +27,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { base.SetUpSteps(); - AddStep("push screen", () => LoadScreen(loungeScreen = new LoungeSubScreen + AddStep("push screen", () => LoadScreen(loungeScreen = new TimeshiftLoungeSubScreen { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs index 165a2b201c..26e351fc2b 100644 --- a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs @@ -19,16 +19,15 @@ using osu.Game.Users; namespace osu.Game.Screens.Multi.Lounge { [Cached] - public class LoungeSubScreen : MultiplayerSubScreen + public abstract class LoungeSubScreen : MultiplayerSubScreen { public override string Title => "Lounge"; - protected FilterControl Filter; - protected override UserActivity InitialActivity => new UserActivity.SearchingForLobby(); private readonly IBindable initialRoomsReceived = new Bindable(); + private FilterControl filter; private Container content; private LoadingLayer loadingLayer; @@ -78,11 +77,11 @@ namespace osu.Game.Screens.Multi.Lounge }, }, }, - Filter = new TimeshiftFilterControl + filter = CreateFilterControl().With(d => { - RelativeSizeAxes = Axes.X, - Height = 80, - }, + d.RelativeSizeAxes = Axes.X; + d.Height = 80; + }) }; // scroll selected room into view on selection. @@ -108,7 +107,7 @@ namespace osu.Game.Screens.Multi.Lounge content.Padding = new MarginPadding { - Top = Filter.DrawHeight, + Top = filter.DrawHeight, Left = WaveOverlayContainer.WIDTH_PADDING - DrawableRoom.SELECTION_BORDER_WIDTH + HORIZONTAL_OVERFLOW_PADDING, Right = WaveOverlayContainer.WIDTH_PADDING + HORIZONTAL_OVERFLOW_PADDING, }; @@ -116,7 +115,7 @@ namespace osu.Game.Screens.Multi.Lounge protected override void OnFocus(FocusEvent e) { - Filter.TakeFocus(); + filter.TakeFocus(); } public override void OnEntering(IScreen last) @@ -140,19 +139,19 @@ namespace osu.Game.Screens.Multi.Lounge private void onReturning() { - Filter.HoldFocus = true; + filter.HoldFocus = true; } public override bool OnExiting(IScreen next) { - Filter.HoldFocus = false; + filter.HoldFocus = false; return base.OnExiting(next); } public override void OnSuspending(IScreen next) { base.OnSuspending(next); - Filter.HoldFocus = false; + filter.HoldFocus = false; } private void joinRequested(Room room) @@ -195,5 +194,7 @@ namespace osu.Game.Screens.Multi.Lounge this.Push(new MatchSubScreen(room)); } + + protected abstract FilterControl CreateFilterControl(); } } diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index 027fedaad6..b37ee45b84 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.cs @@ -143,7 +143,7 @@ namespace osu.Game.Screens.Multi screenStack.ScreenPushed += screenPushed; screenStack.ScreenExited += screenExited; - screenStack.Push(loungeSubScreen = new LoungeSubScreen()); + screenStack.Push(loungeSubScreen = CreateLounge()); } private readonly IBindable apiState = new Bindable(); @@ -361,6 +361,8 @@ namespace osu.Game.Screens.Multi protected abstract RoomManager CreateRoomManager(); + protected abstract LoungeSubScreen CreateLounge(); + private class MultiplayerWaveContainer : WaveContainer { protected override bool StartHidden => true; diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeFilterControl.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeFilterControl.cs new file mode 100644 index 0000000000..acd9a057e3 --- /dev/null +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeFilterControl.cs @@ -0,0 +1,17 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Screens.Multi.Lounge.Components; + +namespace osu.Game.Screens.Multi.RealtimeMultiplayer +{ + public class RealtimeFilterControl : FilterControl + { + protected override FilterCriteria CreateCriteria() + { + var criteria = base.CreateCriteria(); + criteria.Category = "realtime"; + return criteria; + } + } +} diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeLoungeSubScreen.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeLoungeSubScreen.cs new file mode 100644 index 0000000000..ed187e436f --- /dev/null +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeLoungeSubScreen.cs @@ -0,0 +1,13 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Screens.Multi.Lounge; +using osu.Game.Screens.Multi.Lounge.Components; + +namespace osu.Game.Screens.Multi.RealtimeMultiplayer +{ + public class RealtimeLoungeSubScreen : LoungeSubScreen + { + protected override FilterControl CreateFilterControl() => new RealtimeFilterControl(); + } +} diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMultiplayer.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMultiplayer.cs index 075c552437..6455701d31 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMultiplayer.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMultiplayer.cs @@ -61,5 +61,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer } protected override RoomManager CreateRoomManager() => new RealtimeRoomManager(); + + protected override LoungeSubScreen CreateLounge() => new RealtimeLoungeSubScreen(); } } diff --git a/osu.Game/Screens/Multi/Timeshift/TimeshiftLoungeSubScreen.cs b/osu.Game/Screens/Multi/Timeshift/TimeshiftLoungeSubScreen.cs new file mode 100644 index 0000000000..70fb1aef1d --- /dev/null +++ b/osu.Game/Screens/Multi/Timeshift/TimeshiftLoungeSubScreen.cs @@ -0,0 +1,13 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Screens.Multi.Lounge; +using osu.Game.Screens.Multi.Lounge.Components; + +namespace osu.Game.Screens.Multi.Timeshift +{ + public class TimeshiftLoungeSubScreen : LoungeSubScreen + { + protected override FilterControl CreateFilterControl() => new TimeshiftFilterControl(); + } +} diff --git a/osu.Game/Screens/Multi/Timeshift/TimeshiftMultiplayer.cs b/osu.Game/Screens/Multi/Timeshift/TimeshiftMultiplayer.cs index d2d6a35a2e..a38b2a931e 100644 --- a/osu.Game/Screens/Multi/Timeshift/TimeshiftMultiplayer.cs +++ b/osu.Game/Screens/Multi/Timeshift/TimeshiftMultiplayer.cs @@ -45,5 +45,7 @@ namespace osu.Game.Screens.Multi.Timeshift } protected override RoomManager CreateRoomManager() => new TimeshiftRoomManager(); + + protected override LoungeSubScreen CreateLounge() => new TimeshiftLoungeSubScreen(); } } From a1ba4b6979f0a0029855bfb30b3215499d6905a1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 20 Dec 2020 23:40:19 +0900 Subject: [PATCH 399/818] Split MatchSubScreen into abstract component + timeshift implementation --- ....cs => TestSceneTimeshiftRoomSubScreen.cs} | 12 +-- .../Screens/Multi/Lounge/LoungeSubScreen.cs | 4 +- osu.Game/Screens/Multi/Match/RoomSubScreen.cs | 74 ++++++++++++++++ osu.Game/Screens/Multi/Multiplayer.cs | 4 +- .../Multi/Timeshift/TimeshiftMultiplayer.cs | 2 +- .../TimeshiftRoomSubScreen.cs} | 88 +++---------------- 6 files changed, 97 insertions(+), 87 deletions(-) rename osu.Game.Tests/Visual/Multiplayer/{TestSceneMatchSubScreen.cs => TestSceneTimeshiftRoomSubScreen.cs} (94%) create mode 100644 osu.Game/Screens/Multi/Match/RoomSubScreen.cs rename osu.Game/Screens/Multi/{Match/MatchSubScreen.cs => Timeshift/TimeshiftRoomSubScreen.cs} (78%) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftRoomSubScreen.cs similarity index 94% rename from osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSubScreen.cs rename to osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftRoomSubScreen.cs index bceb6efac1..f56f78ce89 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftRoomSubScreen.cs @@ -16,15 +16,15 @@ using osu.Game.Online.Multiplayer; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; using osu.Game.Screens.Multi; -using osu.Game.Screens.Multi.Match; using osu.Game.Screens.Multi.Match.Components; +using osu.Game.Screens.Multi.Timeshift; using osu.Game.Tests.Beatmaps; using osu.Game.Users; using osuTK.Input; namespace osu.Game.Tests.Visual.Multiplayer { - public class TestSceneMatchSubScreen : MultiplayerTestScene + public class TestSceneTimeshiftRoomSubScreen : MultiplayerTestScene { protected override bool UseOnlineAPI => true; @@ -34,7 +34,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private BeatmapManager manager; private RulesetStore rulesets; - private TestMatchSubScreen match; + private TestTimeshiftRoomSubScreen match; [BackgroundDependencyLoader] private void load(GameHost host, AudioManager audio) @@ -48,7 +48,7 @@ namespace osu.Game.Tests.Visual.Multiplayer [SetUpSteps] public void SetupSteps() { - AddStep("load match", () => LoadScreen(match = new TestMatchSubScreen(Room))); + AddStep("load match", () => LoadScreen(match = new TestTimeshiftRoomSubScreen(Room))); AddUntilStep("wait for load", () => match.IsCurrentScreen()); } @@ -131,13 +131,13 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("match has original beatmap", () => match.Beatmap.Value.Beatmap.BeatmapInfo.BaseDifficulty.CircleSize != 1); } - private class TestMatchSubScreen : MatchSubScreen + private class TestTimeshiftRoomSubScreen : TimeshiftRoomSubScreen { public new Bindable SelectedItem => base.SelectedItem; public new Bindable Beatmap => base.Beatmap; - public TestMatchSubScreen(Room room) + public TestTimeshiftRoomSubScreen(Room room) : base(room) { } diff --git a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs index 26e351fc2b..f4591d089e 100644 --- a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs @@ -13,7 +13,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Online.Multiplayer; using osu.Game.Overlays; using osu.Game.Screens.Multi.Lounge.Components; -using osu.Game.Screens.Multi.Match; +using osu.Game.Screens.Multi.Timeshift; using osu.Game.Users; namespace osu.Game.Screens.Multi.Lounge @@ -192,7 +192,7 @@ namespace osu.Game.Screens.Multi.Lounge selectedRoom.Value = room; - this.Push(new MatchSubScreen(room)); + this.Push(new TimeshiftRoomSubScreen(room)); } protected abstract FilterControl CreateFilterControl(); diff --git a/osu.Game/Screens/Multi/Match/RoomSubScreen.cs b/osu.Game/Screens/Multi/Match/RoomSubScreen.cs new file mode 100644 index 0000000000..0cc9a4354e --- /dev/null +++ b/osu.Game/Screens/Multi/Match/RoomSubScreen.cs @@ -0,0 +1,74 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Screens; +using osu.Game.Audio; +using osu.Game.Beatmaps; +using osu.Game.Online.Multiplayer; +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Screens.Multi.Match +{ + [Cached(typeof(IPreviewTrackOwner))] + public abstract class RoomSubScreen : MultiplayerSubScreen, IPreviewTrackOwner + { + protected readonly Bindable SelectedItem = new Bindable(); + + public override bool DisallowExternalBeatmapRulesetChanges => true; + + [Resolved(typeof(Room), nameof(Room.Playlist))] + protected BindableList Playlist { get; private set; } + + [Resolved] + private BeatmapManager beatmapManager { get; set; } + + private IBindable> managerUpdated; + + protected override void LoadComplete() + { + base.LoadComplete(); + + SelectedItem.BindValueChanged(_ => Scheduler.AddOnce(selectedItemChanged)); + SelectedItem.Value = Playlist.FirstOrDefault(); + + managerUpdated = beatmapManager.ItemUpdated.GetBoundCopy(); + managerUpdated.BindValueChanged(beatmapUpdated); + } + + private void selectedItemChanged() + { + updateWorkingBeatmap(); + + var item = SelectedItem.Value; + + Mods.Value = item?.RequiredMods?.ToArray() ?? Array.Empty(); + + if (item?.Ruleset != null) + Ruleset.Value = item.Ruleset.Value; + } + + private void beatmapUpdated(ValueChangedEvent> weakSet) => Schedule(updateWorkingBeatmap); + + private void updateWorkingBeatmap() + { + var beatmap = SelectedItem.Value?.Beatmap.Value; + + // Retrieve the corresponding local beatmap, since we can't directly use the playlist's beatmap info + var localBeatmap = beatmap == null ? null : beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == beatmap.OnlineBeatmapID); + + Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap); + } + + public override bool OnExiting(IScreen next) + { + RoomManager?.PartRoom(); + Mods.Value = Array.Empty(); + + return base.OnExiting(next); + } + } +} diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index b37ee45b84..eae779421d 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.cs @@ -308,7 +308,7 @@ namespace osu.Game.Screens.Multi headerBackground.MoveToX(0, MultiplayerSubScreen.X_MOVE_DURATION, Easing.OutQuint); break; - case MatchSubScreen _: + case RoomSubScreen _: header.ResizeHeightTo(135, MultiplayerSubScreen.APPEAR_DURATION, Easing.OutQuint); headerBackground.MoveToX(-MultiplayerSubScreen.X_SHIFT, MultiplayerSubScreen.X_MOVE_DURATION, Easing.OutQuint); break; @@ -330,7 +330,7 @@ namespace osu.Game.Screens.Multi private void updateTrack(ValueChangedEvent _ = null) { - if (screenStack.CurrentScreen is MatchSubScreen) + if (screenStack.CurrentScreen is RoomSubScreen) { var track = Beatmap.Value?.Track; diff --git a/osu.Game/Screens/Multi/Timeshift/TimeshiftMultiplayer.cs b/osu.Game/Screens/Multi/Timeshift/TimeshiftMultiplayer.cs index a38b2a931e..2ea4857799 100644 --- a/osu.Game/Screens/Multi/Timeshift/TimeshiftMultiplayer.cs +++ b/osu.Game/Screens/Multi/Timeshift/TimeshiftMultiplayer.cs @@ -29,7 +29,7 @@ namespace osu.Game.Screens.Multi.Timeshift timeshiftManager.TimeBetweenSelectionPolls.Value = isIdle ? 120000 : 15000; break; - case MatchSubScreen _: + case RoomSubScreen _: timeshiftManager.TimeBetweenListingPolls.Value = 0; timeshiftManager.TimeBetweenSelectionPolls.Value = isIdle ? 30000 : 5000; break; diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Timeshift/TimeshiftRoomSubScreen.cs similarity index 78% rename from osu.Game/Screens/Multi/Match/MatchSubScreen.cs rename to osu.Game/Screens/Multi/Timeshift/TimeshiftRoomSubScreen.cs index 2f8aad4e65..433a980d60 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Timeshift/TimeshiftRoomSubScreen.cs @@ -1,7 +1,6 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.Diagnostics; using System.Linq; using osu.Framework.Allocation; @@ -9,28 +8,21 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Screens; -using osu.Game.Audio; -using osu.Game.Beatmaps; using osu.Game.Online.API; using osu.Game.Online.Multiplayer; -using osu.Game.Online.Multiplayer.GameTypes; -using osu.Game.Rulesets.Mods; using osu.Game.Screens.Multi.Components; +using osu.Game.Screens.Multi.Match; using osu.Game.Screens.Multi.Match.Components; using osu.Game.Screens.Multi.Play; using osu.Game.Screens.Multi.Ranking; using osu.Game.Screens.Play; using osu.Game.Screens.Select; using osu.Game.Users; -using Footer = osu.Game.Screens.Multi.Match.Components.Footer; -namespace osu.Game.Screens.Multi.Match +namespace osu.Game.Screens.Multi.Timeshift { - [Cached(typeof(IPreviewTrackOwner))] - public class MatchSubScreen : MultiplayerSubScreen, IPreviewTrackOwner + public class TimeshiftRoomSubScreen : RoomSubScreen { - public override bool DisallowExternalBeatmapRulesetChanges => true; - public override string Title { get; } public override string ShortTitle => "room"; @@ -38,27 +30,15 @@ namespace osu.Game.Screens.Multi.Match [Resolved(typeof(Room), nameof(Room.RoomID))] private Bindable roomId { get; set; } - [Resolved(typeof(Room), nameof(Room.Type))] - private Bindable type { get; set; } - - [Resolved(typeof(Room), nameof(Room.Playlist))] - private BindableList playlist { get; set; } - - [Resolved] - private BeatmapManager beatmapManager { get; set; } - [Resolved(canBeNull: true)] private Multiplayer multiplayer { get; set; } - protected readonly Bindable SelectedItem = new Bindable(); - private MatchSettingsOverlay settingsOverlay; private MatchLeaderboard leaderboard; - private IBindable> managerUpdated; private OverlinedHeader participantsHeader; - public MatchSubScreen(Room room) + public TimeshiftRoomSubScreen(Room room) { Title = room.RoomID.Value == null ? "New room" : room.Name.Value; Activity.Value = new UserActivity.InLobby(room); @@ -96,7 +76,7 @@ namespace osu.Game.Screens.Multi.Match }, Content = new[] { - new Drawable[] { new Components.Header() }, + new Drawable[] { new Match.Components.Header() }, new Drawable[] { participantsHeader = new OverlinedHeader("Participants") @@ -141,7 +121,7 @@ namespace osu.Game.Screens.Multi.Match new DrawableRoomPlaylistWithResults { RelativeSizeAxes = Axes.Both, - Items = { BindTarget = playlist }, + Items = { BindTarget = Playlist }, SelectedItem = { BindTarget = SelectedItem }, RequestShowResults = item => { @@ -195,7 +175,7 @@ namespace osu.Game.Screens.Multi.Match }, new Drawable[] { - new Footer + new Match.Components.Footer { OnStart = onStart, SelectedItem = { BindTarget = SelectedItem } @@ -234,61 +214,17 @@ namespace osu.Game.Screens.Multi.Match // Set the first playlist item. // This is scheduled since updating the room and playlist may happen in an arbitrary order (via Room.CopyFrom()). - Schedule(() => SelectedItem.Value = playlist.FirstOrDefault()); + Schedule(() => SelectedItem.Value = Playlist.FirstOrDefault()); } }, true); - - SelectedItem.BindValueChanged(_ => Scheduler.AddOnce(selectedItemChanged)); - SelectedItem.Value = playlist.FirstOrDefault(); - - managerUpdated = beatmapManager.ItemUpdated.GetBoundCopy(); - managerUpdated.BindValueChanged(beatmapUpdated); - } - - public override bool OnExiting(IScreen next) - { - RoomManager?.PartRoom(); - Mods.Value = Array.Empty(); - - return base.OnExiting(next); - } - - private void selectedItemChanged() - { - updateWorkingBeatmap(); - - var item = SelectedItem.Value; - - Mods.Value = item?.RequiredMods?.ToArray() ?? Array.Empty(); - - if (item?.Ruleset != null) - Ruleset.Value = item.Ruleset.Value; - } - - private void beatmapUpdated(ValueChangedEvent> weakSet) => Schedule(updateWorkingBeatmap); - - private void updateWorkingBeatmap() - { - var beatmap = SelectedItem.Value?.Beatmap.Value; - - // Retrieve the corresponding local beatmap, since we can't directly use the playlist's beatmap info - var localBeatmap = beatmap == null ? null : beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == beatmap.OnlineBeatmapID); - - Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap); } private void onStart() { - switch (type.Value) + multiplayer?.Push(new PlayerLoader(() => new TimeshiftPlayer(SelectedItem.Value) { - default: - case GameTypeTimeshift _: - multiplayer?.Push(new PlayerLoader(() => new TimeshiftPlayer(SelectedItem.Value) - { - Exited = () => leaderboard.RefreshScores() - })); - break; - } + Exited = () => leaderboard.RefreshScores() + })); } } } From a25cd910f83fe6ed012fdf63e1f2a20245ac625d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 14 Dec 2020 22:20:35 +0100 Subject: [PATCH 400/818] Prepare base DHO for HO application --- .../Objects/Drawables/DrawableTaikoHitObject.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs index cf3aa69b6f..6041eccb51 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; -using osu.Framework.Allocation; +using JetBrains.Annotations; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables private readonly Container nonProxiedContent; - protected DrawableTaikoHitObject(TaikoHitObject hitObject) + protected DrawableTaikoHitObject([CanBeNull] TaikoHitObject hitObject) : base(hitObject) { AddRangeInternal(new[] @@ -113,25 +113,23 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { public override Vector2 OriginPosition => new Vector2(DrawHeight / 2); - public new TObject HitObject; + public new TObject HitObject => (TObject)base.HitObject; protected Vector2 BaseSize; protected SkinnableDrawable MainPiece; - protected DrawableTaikoHitObject(TObject hitObject) + protected DrawableTaikoHitObject([CanBeNull] TObject hitObject) : base(hitObject) { - HitObject = hitObject; - Anchor = Anchor.CentreLeft; Origin = Anchor.Custom; RelativeSizeAxes = Axes.Both; } - [BackgroundDependencyLoader] - private void load() + protected override void OnApply() { + base.OnApply(); RecreatePieces(); } From 7b350fc8e5f61644d63700c236fe2c447eb39efd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 14 Dec 2020 22:27:45 +0100 Subject: [PATCH 401/818] Prepare strongable DHO for HO application --- .../DrawableTaikoStrongableHitObject.cs | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongableHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongableHitObject.cs index af3e94d9c6..62f338ca91 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongableHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongableHitObject.cs @@ -2,7 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Linq; -using osu.Framework.Allocation; +using JetBrains.Annotations; using osu.Framework.Bindables; using osu.Framework.Graphics.Containers; using osu.Game.Audio; @@ -16,28 +16,38 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables where TObject : TaikoStrongableHitObject where TStrongNestedObject : StrongNestedHitObject { - private readonly Bindable isStrong; + private readonly Bindable isStrong = new BindableBool(); private readonly Container strongHitContainer; - protected DrawableTaikoStrongableHitObject(TObject hitObject) + protected DrawableTaikoStrongableHitObject([CanBeNull] TObject hitObject) : base(hitObject) { - isStrong = HitObject.IsStrongBindable.GetBoundCopy(); - AddInternal(strongHitContainer = new Container()); } - [BackgroundDependencyLoader] - private void load() + protected override void OnApply() { + isStrong.BindTo(HitObject.IsStrongBindable); isStrong.BindValueChanged(_ => { - // will overwrite samples, should only be called on change. + // will overwrite samples, should only be called on subsequent changes + // after the initial application. updateSamplesFromStrong(); RecreatePieces(); }); + + base.OnApply(); + } + + protected override void OnFree() + { + base.OnFree(); + + isStrong.UnbindFrom(HitObject.IsStrongBindable); + // ensure the next application does not accidentally overwrite samples. + isStrong.UnbindEvents(); } private HitSampleInfo[] getStrongSamples() => HitObject.Samples.Where(s => s.Name == HitSampleInfo.HIT_FINISH).ToArray(); From a31e8d137f3c0bf2926e603a9ef294eb0f8f47c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 14 Dec 2020 22:53:00 +0100 Subject: [PATCH 402/818] Add guard when clearing samples --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 644c67ea59..da6da0ea97 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -303,7 +303,8 @@ namespace osu.Game.Rulesets.Objects.Drawables samplesBindable.CollectionChanged -= onSamplesChanged; // Release the samples for other hitobjects to use. - Samples.Samples = null; + if (Samples != null) + Samples.Samples = null; if (nestedHitObjects.IsValueCreated) { From 55cdff5be770414190ed13b7d0effea094c848bb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 20 Dec 2020 23:49:06 +0900 Subject: [PATCH 403/818] Renamespace ready button --- .../Visual/RealtimeMultiplayer/TestSceneRealtimeReadyButton.cs | 2 +- .../RealtimeMultiplayer/{ => Match}/RealtimeReadyButton.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename osu.Game/Screens/Multi/RealtimeMultiplayer/{ => Match}/RealtimeReadyButton.cs (98%) diff --git a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeReadyButton.cs b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeReadyButton.cs index 1f863028af..b7cd81fb32 100644 --- a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeReadyButton.cs +++ b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeReadyButton.cs @@ -11,7 +11,7 @@ using osu.Game.Beatmaps; using osu.Game.Online.Multiplayer; using osu.Game.Online.RealtimeMultiplayer; using osu.Game.Rulesets; -using osu.Game.Screens.Multi.RealtimeMultiplayer; +using osu.Game.Screens.Multi.RealtimeMultiplayer.Match; using osu.Game.Tests.Resources; using osu.Game.Users; using osuTK; diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeReadyButton.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeReadyButton.cs similarity index 98% rename from osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeReadyButton.cs rename to osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeReadyButton.cs index ea8fb04994..09487e9831 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeReadyButton.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeReadyButton.cs @@ -15,7 +15,7 @@ using osu.Game.Online.RealtimeMultiplayer; using osu.Game.Screens.Multi.Components; using osuTK; -namespace osu.Game.Screens.Multi.RealtimeMultiplayer +namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Match { public class RealtimeReadyButton : RealtimeRoomComposite { From 232c0205b4d7927c3b84dac00a719ec2fa4be7ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 14 Dec 2020 22:51:35 +0100 Subject: [PATCH 404/818] Refactor hit object application scene to work reliably --- .../HitObjectApplicationTestScene.cs | 14 ++++++++++---- .../TestSceneBarLineApplication.cs | 7 ++++--- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/HitObjectApplicationTestScene.cs b/osu.Game.Rulesets.Taiko.Tests/HitObjectApplicationTestScene.cs index 07c7b4d1db..a1d000386f 100644 --- a/osu.Game.Rulesets.Taiko.Tests/HitObjectApplicationTestScene.cs +++ b/osu.Game.Rulesets.Taiko.Tests/HitObjectApplicationTestScene.cs @@ -25,16 +25,22 @@ namespace osu.Game.Rulesets.Taiko.Tests private ScrollingHitObjectContainer hitObjectContainer; - [SetUpSteps] - public void SetUp() - => AddStep("create SHOC", () => Child = hitObjectContainer = new ScrollingHitObjectContainer + [BackgroundDependencyLoader] + private void load() + { + Child = hitObjectContainer = new ScrollingHitObjectContainer { RelativeSizeAxes = Axes.X, Height = 200, Anchor = Anchor.Centre, Origin = Anchor.Centre, Clock = new FramedClock(new StopwatchClock()) - }); + }; + } + + [SetUpSteps] + public void SetUp() + => AddStep("clear SHOC", () => hitObjectContainer.Clear(false)); protected void AddHitObject(DrawableHitObject hitObject) => AddStep("add to SHOC", () => hitObjectContainer.Add(hitObject)); diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneBarLineApplication.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneBarLineApplication.cs index 65230a07bc..a970965141 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneBarLineApplication.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneBarLineApplication.cs @@ -12,12 +12,13 @@ namespace osu.Game.Rulesets.Taiko.Tests [Test] public void TestApplyNewBarLine() { - DrawableBarLine barLine = new DrawableBarLine(PrepareObject(new BarLine + DrawableBarLine barLine = new DrawableBarLine(); + + AddStep("apply new bar line", () => barLine.Apply(PrepareObject(new BarLine { StartTime = 400, Major = true - })); - + }), null)); AddHitObject(barLine); RemoveHitObject(barLine); From 536df074a9a5505a9b14dddd29f616d42e243c93 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 21 Dec 2020 00:02:49 +0900 Subject: [PATCH 405/818] Don't attempt to re-map existing beatmap/ruleset (for testing) --- osu.Game/Online/Multiplayer/PlaylistItem.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/Multiplayer/PlaylistItem.cs b/osu.Game/Online/Multiplayer/PlaylistItem.cs index 416091a1aa..4c4c071fc9 100644 --- a/osu.Game/Online/Multiplayer/PlaylistItem.cs +++ b/osu.Game/Online/Multiplayer/PlaylistItem.cs @@ -64,8 +64,8 @@ namespace osu.Game.Online.Multiplayer public void MapObjects(BeatmapManager beatmaps, RulesetStore rulesets) { - Beatmap.Value = apiBeatmap.ToBeatmap(rulesets); - Ruleset.Value = rulesets.GetRuleset(RulesetID); + Beatmap.Value ??= apiBeatmap.ToBeatmap(rulesets); + Ruleset.Value ??= rulesets.GetRuleset(RulesetID); Ruleset rulesetInstance = Ruleset.Value.CreateInstance(); From 1fdc19ee0fe9855a44eb95e94a8555be2103b0b7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 21 Dec 2020 00:04:06 +0900 Subject: [PATCH 406/818] Add realtime match subscreen and related components --- .../TestSceneRealtimeMatchSubScreen.cs | 77 +++ .../Match/BeatmapSelectionControl.cs | 81 ++++ .../Match/RealtimeMatchFooter.cs | 48 ++ .../Match/RealtimeMatchHeader.cs | 106 +++++ .../Match/RealtimeMatchSettingsOverlay.cs | 441 ++++++++++++++++++ .../Participants/ParticipantsListHeader.cs | 31 ++ .../RealtimeMatchSongSelect.cs | 20 + .../RealtimeMatchSubScreen.cs | 201 ++++++++ .../RealtimeMultiplayer/RealtimePlayer.cs | 16 + 9 files changed, 1021 insertions(+) create mode 100644 osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeMatchSubScreen.cs create mode 100644 osu.Game/Screens/Multi/RealtimeMultiplayer/Match/BeatmapSelectionControl.cs create mode 100644 osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeMatchFooter.cs create mode 100644 osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeMatchHeader.cs create mode 100644 osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeMatchSettingsOverlay.cs create mode 100644 osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantsListHeader.cs create mode 100644 osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSongSelect.cs create mode 100644 osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSubScreen.cs create mode 100644 osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs diff --git a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeMatchSubScreen.cs b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeMatchSubScreen.cs new file mode 100644 index 0000000000..d31364d62e --- /dev/null +++ b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeMatchSubScreen.cs @@ -0,0 +1,77 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Framework.Screens; +using osu.Framework.Testing; +using osu.Game.Online.Multiplayer; +using osu.Game.Rulesets.Osu; +using osu.Game.Screens.Multi.RealtimeMultiplayer; +using osu.Game.Screens.Multi.RealtimeMultiplayer.Match; +using osu.Game.Tests.Beatmaps; +using osuTK.Input; + +namespace osu.Game.Tests.Visual.RealtimeMultiplayer +{ + public class TestSceneRealtimeMatchSubScreen : RealtimeMultiplayerTestScene + { + private RealtimeMatchSubScreen screen; + + public TestSceneRealtimeMatchSubScreen() + : base(false) + { + } + + [SetUp] + public new void Setup() => Schedule(() => + { + Room.Name.Value = "Test Room"; + }); + + [SetUpSteps] + public void SetupSteps() + { + AddStep("load match", () => LoadScreen(screen = new RealtimeMatchSubScreen(Room))); + AddUntilStep("wait for load", () => screen.IsCurrentScreen()); + } + + [Test] + public void TestSettingValidity() + { + AddAssert("create button not enabled", () => !this.ChildrenOfType().Single().Enabled.Value); + + AddStep("set playlist", () => + { + Room.Playlist.Add(new PlaylistItem + { + Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo }, + Ruleset = { Value = new OsuRuleset().RulesetInfo }, + }); + }); + + AddAssert("create button enabled", () => this.ChildrenOfType().Single().Enabled.Value); + } + + [Test] + public void TestCreatedRoom() + { + AddStep("set playlist", () => + { + Room.Playlist.Add(new PlaylistItem + { + Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo }, + Ruleset = { Value = new OsuRuleset().RulesetInfo }, + }); + }); + + AddStep("click create button", () => + { + InputManager.MoveMouseTo(this.ChildrenOfType().Single()); + InputManager.Click(MouseButton.Left); + }); + + AddWaitStep("wait", 500); + } + } +} diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/BeatmapSelectionControl.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/BeatmapSelectionControl.cs new file mode 100644 index 0000000000..1939744916 --- /dev/null +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/BeatmapSelectionControl.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.Specialized; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Screens; +using osu.Game.Online.API; +using osu.Game.Screens.Multi.Match.Components; + +namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Match +{ + public class BeatmapSelectionControl : MultiplayerComposite + { + [Resolved] + private RealtimeMatchSubScreen matchSubScreen { get; set; } + + [Resolved] + private IAPIProvider api { get; set; } + + private Container beatmapPanelContainer; + private Button selectButton; + + public BeatmapSelectionControl() + { + AutoSizeAxes = Axes.Y; + } + + [BackgroundDependencyLoader] + private void load() + { + InternalChild = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + beatmapPanelContainer = new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y + }, + selectButton = new PurpleTriangleButton + { + RelativeSizeAxes = Axes.X, + Height = 40, + Text = "Select beatmap", + Action = () => matchSubScreen.Push(new RealtimeMatchSongSelect()), + Alpha = 0 + } + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Playlist.BindCollectionChanged(onPlaylistChanged, true); + Host.BindValueChanged(host => + { + if (RoomID.Value == null || host.NewValue?.Equals(api.LocalUser.Value) == true) + selectButton.Show(); + else + selectButton.Hide(); + }, true); + } + + private void onPlaylistChanged(object sender, NotifyCollectionChangedEventArgs e) + { + if (Playlist.Any()) + beatmapPanelContainer.Child = new DrawableRoomPlaylistItem(Playlist.Single(), false, false); + else + beatmapPanelContainer.Clear(); + } + } +} diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeMatchFooter.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeMatchFooter.cs new file mode 100644 index 0000000000..31871729f6 --- /dev/null +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeMatchFooter.cs @@ -0,0 +1,48 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Online.Multiplayer; +using osuTK; + +namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Match +{ + public class RealtimeMatchFooter : CompositeDrawable + { + public const float HEIGHT = 50; + + public readonly Bindable SelectedItem = new Bindable(); + + private readonly Drawable background; + + public RealtimeMatchFooter() + { + RelativeSizeAxes = Axes.X; + Height = HEIGHT; + + InternalChildren = new[] + { + background = new Box { RelativeSizeAxes = Axes.Both }, + new RealtimeReadyButton + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(600, 50), + SelectedItem = { BindTarget = SelectedItem } + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + background.Colour = Color4Extensions.FromHex(@"28242d"); + } + } +} diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeMatchHeader.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeMatchHeader.cs new file mode 100644 index 0000000000..a9a10d1510 --- /dev/null +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeMatchHeader.cs @@ -0,0 +1,106 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Online.API; +using osu.Game.Screens.Multi.Match.Components; +using osu.Game.Users.Drawables; +using osuTK; +using FontWeight = osu.Game.Graphics.FontWeight; +using OsuColour = osu.Game.Graphics.OsuColour; +using OsuFont = osu.Game.Graphics.OsuFont; + +namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Match +{ + public class RealtimeMatchHeader : MultiplayerComposite + { + public const float HEIGHT = 50; + + public Action OpenSettings; + + private UpdateableAvatar avatar; + private LinkFlowContainer hostText; + private Button openSettingsButton; + + [Resolved] + private IAPIProvider api { get; set; } + + public RealtimeMatchHeader() + { + RelativeSizeAxes = Axes.X; + Height = HEIGHT; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + InternalChildren = new Drawable[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(10, 0), + Children = new Drawable[] + { + avatar = new UpdateableAvatar + { + Size = new Vector2(50), + Masking = true, + CornerRadius = 10, + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new OsuSpriteText + { + Font = OsuFont.GetFont(size: 30), + Current = { BindTarget = RoomName } + }, + hostText = new LinkFlowContainer(s => s.Font = OsuFont.GetFont(size: 20)) + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + } + } + } + } + }, + openSettingsButton = new PurpleTriangleButton + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Size = new Vector2(150, HEIGHT), + Text = "Open settings", + Action = () => OpenSettings?.Invoke(), + Alpha = 0 + } + }; + + Host.BindValueChanged(host => + { + avatar.User = host.NewValue; + + hostText.Clear(); + + if (host.NewValue != null) + { + hostText.AddText("hosted by "); + hostText.AddUserLink(host.NewValue, s => s.Font = s.Font.With(weight: FontWeight.SemiBold)); + } + + openSettingsButton.Alpha = host.NewValue?.Equals(api.LocalUser.Value) == true ? 1 : 0; + }, true); + } + } +} diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeMatchSettingsOverlay.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeMatchSettingsOverlay.cs new file mode 100644 index 0000000000..3b9603e1bd --- /dev/null +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeMatchSettingsOverlay.cs @@ -0,0 +1,441 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.Multiplayer; +using osu.Game.Online.RealtimeMultiplayer; +using osu.Game.Overlays; +using osu.Game.Rulesets; +using osu.Game.Screens.Multi.Match.Components; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Match +{ + public class RealtimeMatchSettingsOverlay : FocusedOverlayContainer + { + private const float transition_duration = 350; + private const float field_padding = 45; + + protected MatchSettings Settings { get; private set; } + + [BackgroundDependencyLoader] + private void load() + { + Masking = true; + + Child = Settings = new MatchSettings + { + RelativeSizeAxes = Axes.Both, + RelativePositionAxes = Axes.Y, + SettingsApplied = Hide + }; + } + + protected override void PopIn() + { + Settings.MoveToY(0, transition_duration, Easing.OutQuint); + } + + protected override void PopOut() + { + Settings.MoveToY(-1, transition_duration, Easing.InSine); + } + + protected class MatchSettings : MultiplayerComposite + { + private const float disabled_alpha = 0.2f; + + public Action SettingsApplied; + + public OsuTextBox NameField, MaxParticipantsField; + public RoomAvailabilityPicker AvailabilityPicker; + public GameTypePicker TypePicker; + public TriangleButton ApplyButton; + + public OsuSpriteText ErrorText; + + private OsuSpriteText typeLabel; + private LoadingLayer loadingLayer; + private BeatmapSelectionControl initialBeatmapControl; + + [Resolved] + private RealtimeRoomManager manager { get; set; } + + [Resolved] + private StatefulMultiplayerClient client { get; set; } + + [Resolved] + private Bindable currentRoom { get; set; } + + [Resolved] + private Bindable beatmap { get; set; } + + [Resolved] + private Bindable ruleset { get; set; } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Container dimContent; + + InternalChildren = new Drawable[] + { + dimContent = new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4Extensions.FromHex(@"28242d"), + }, + new GridContainer + { + RelativeSizeAxes = Axes.Both, + RowDimensions = new[] + { + new Dimension(GridSizeMode.Distributed), + new Dimension(GridSizeMode.AutoSize), + }, + Content = new[] + { + new Drawable[] + { + new OsuScrollContainer + { + Padding = new MarginPadding + { + Horizontal = OsuScreen.HORIZONTAL_OVERFLOW_PADDING, + Vertical = 10 + }, + RelativeSizeAxes = Axes.Both, + Children = new[] + { + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 10), + Children = new Drawable[] + { + new Container + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Padding = new MarginPadding { Horizontal = WaveOverlayContainer.WIDTH_PADDING }, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new SectionContainer + { + Padding = new MarginPadding { Right = field_padding / 2 }, + Children = new[] + { + new Section("Room name") + { + Child = NameField = new SettingsTextBox + { + RelativeSizeAxes = Axes.X, + TabbableContentContainer = this, + }, + }, + new Section("Room visibility") + { + Alpha = disabled_alpha, + Child = AvailabilityPicker = new RoomAvailabilityPicker + { + Enabled = { Value = false } + }, + }, + new Section("Game type") + { + Alpha = disabled_alpha, + Child = new FillFlowContainer + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Direction = FillDirection.Vertical, + Spacing = new Vector2(7), + Children = new Drawable[] + { + TypePicker = new GameTypePicker + { + RelativeSizeAxes = Axes.X, + Enabled = { Value = false } + }, + typeLabel = new OsuSpriteText + { + Font = OsuFont.GetFont(size: 14), + Colour = colours.Yellow + }, + }, + }, + }, + }, + }, + new SectionContainer + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Padding = new MarginPadding { Left = field_padding / 2 }, + Children = new[] + { + new Section("Max participants") + { + Alpha = disabled_alpha, + Child = MaxParticipantsField = new SettingsNumberTextBox + { + RelativeSizeAxes = Axes.X, + TabbableContentContainer = this, + ReadOnly = true, + }, + }, + new Section("Password (optional)") + { + Alpha = disabled_alpha, + Child = new SettingsPasswordTextBox + { + RelativeSizeAxes = Axes.X, + TabbableContentContainer = this, + ReadOnly = true, + }, + }, + } + } + }, + }, + initialBeatmapControl = new BeatmapSelectionControl + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.X, + Width = 0.5f + } + } + } + }, + }, + }, + new Drawable[] + { + new Container + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Y = 2, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4Extensions.FromHex(@"28242d").Darken(0.5f).Opacity(1f), + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 20), + Margin = new MarginPadding { Vertical = 20 }, + Padding = new MarginPadding { Horizontal = OsuScreen.HORIZONTAL_OVERFLOW_PADDING }, + Children = new Drawable[] + { + ApplyButton = new CreateOrUpdateButton + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Size = new Vector2(230, 55), + Enabled = { Value = false }, + Action = apply, + }, + ErrorText = new OsuSpriteText + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Alpha = 0, + Depth = 1, + Colour = colours.RedDark + } + } + } + } + } + } + } + }, + } + }, + loadingLayer = new LoadingLayer(dimContent) + }; + + TypePicker.Current.BindValueChanged(type => typeLabel.Text = type.NewValue?.Name ?? string.Empty, true); + RoomName.BindValueChanged(name => NameField.Text = name.NewValue, true); + Availability.BindValueChanged(availability => AvailabilityPicker.Current.Value = availability.NewValue, true); + Type.BindValueChanged(type => TypePicker.Current.Value = type.NewValue, true); + MaxParticipants.BindValueChanged(count => MaxParticipantsField.Text = count.NewValue?.ToString(), true); + RoomID.BindValueChanged(roomId => initialBeatmapControl.Alpha = roomId.NewValue == null ? 1 : 0, true); + } + + protected override void Update() + { + base.Update(); + + ApplyButton.Enabled.Value = Playlist.Count > 0 && NameField.Text.Length > 0; + } + + private void apply() + { + if (!ApplyButton.Enabled.Value) + return; + + hideError(); + loadingLayer.Show(); + + // If the client is already in a room, update via the client. + // Otherwise, update the room directly in preparation for it to be submitted to the API on match creation. + if (client.Room != null) + { + client.ChangeSettings(name: NameField.Text); + onSuccess(currentRoom.Value); + } + else + { + currentRoom.Value.Name.Value = NameField.Text; + currentRoom.Value.Availability.Value = AvailabilityPicker.Current.Value; + currentRoom.Value.Type.Value = TypePicker.Current.Value; + + if (int.TryParse(MaxParticipantsField.Text, out int max)) + currentRoom.Value.MaxParticipants.Value = max; + else + currentRoom.Value.MaxParticipants.Value = null; + + manager?.CreateRoom(currentRoom.Value, onSuccess, onError); + } + } + + private void hideError() => ErrorText.FadeOut(50); + + private void onSuccess(Room room) + { + loadingLayer.Hide(); + SettingsApplied?.Invoke(); + } + + private void onError(string text) + { + ErrorText.Text = text; + ErrorText.FadeIn(50); + + loadingLayer.Hide(); + } + } + + private class SettingsTextBox : OsuTextBox + { + [BackgroundDependencyLoader] + private void load() + { + BackgroundUnfocused = Color4.Black; + BackgroundFocused = Color4.Black; + } + } + + private class SettingsNumberTextBox : SettingsTextBox + { + protected override bool CanAddCharacter(char character) => char.IsNumber(character); + } + + private class SettingsPasswordTextBox : OsuPasswordTextBox + { + [BackgroundDependencyLoader] + private void load() + { + BackgroundUnfocused = Color4.Black; + BackgroundFocused = Color4.Black; + } + } + + private class SectionContainer : FillFlowContainer
+ { + public SectionContainer() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Width = 0.5f; + Direction = FillDirection.Vertical; + Spacing = new Vector2(field_padding); + } + } + + private class Section : Container + { + private readonly Container content; + + protected override Container Content => content; + + public Section(string title) + { + AutoSizeAxes = Axes.Y; + RelativeSizeAxes = Axes.X; + + InternalChild = new FillFlowContainer + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Direction = FillDirection.Vertical, + Spacing = new Vector2(5), + Children = new Drawable[] + { + new OsuSpriteText + { + Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 12), + Text = title.ToUpper(), + }, + content = new Container + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + }, + }, + }; + } + } + + public class CreateOrUpdateButton : TriangleButton + { + [Resolved(typeof(Room), nameof(Room.RoomID))] + private Bindable roomId { get; set; } + + protected override void LoadComplete() + { + base.LoadComplete(); + roomId.BindValueChanged(id => Text = id.NewValue == null ? "Create" : "Update", true); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + BackgroundColour = colours.Yellow; + Triangles.ColourLight = colours.YellowLight; + Triangles.ColourDark = colours.YellowDark; + } + } + } +} diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantsListHeader.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantsListHeader.cs new file mode 100644 index 0000000000..0ca7d34005 --- /dev/null +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantsListHeader.cs @@ -0,0 +1,31 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Game.Online.RealtimeMultiplayer; +using osu.Game.Screens.Multi.Components; + +namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Participants +{ + public class ParticipantsListHeader : OverlinedHeader + { + [Resolved] + private StatefulMultiplayerClient client { get; set; } + + public ParticipantsListHeader() + : base("Participants") + { + } + + protected override void Update() + { + base.Update(); + + var room = client.Room; + if (room == null) + return; + + Details.Value = room.Users.Count.ToString(); + } + } +} diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSongSelect.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSongSelect.cs new file mode 100644 index 0000000000..9bcc303065 --- /dev/null +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSongSelect.cs @@ -0,0 +1,20 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Screens.Select; + +namespace osu.Game.Screens.Multi.RealtimeMultiplayer +{ + public class RealtimeMatchSongSelect : SongSelect + { + protected override BeatmapDetailArea CreateBeatmapDetailArea() + { + throw new System.NotImplementedException(); + } + + protected override bool OnStart() + { + throw new System.NotImplementedException(); + } + } +} diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSubScreen.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSubScreen.cs new file mode 100644 index 0000000000..d8b42e065b --- /dev/null +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSubScreen.cs @@ -0,0 +1,201 @@ +// 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.Specialized; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Screens; +using osu.Game.Online.Multiplayer; +using osu.Game.Online.RealtimeMultiplayer; +using osu.Game.Screens.Multi.Components; +using osu.Game.Screens.Multi.Match; +using osu.Game.Screens.Multi.Match.Components; +using osu.Game.Screens.Multi.RealtimeMultiplayer.Match; +using osu.Game.Screens.Multi.RealtimeMultiplayer.Participants; +using osu.Game.Screens.Play; +using osu.Game.Users; + +namespace osu.Game.Screens.Multi.RealtimeMultiplayer +{ + [Cached] + public class RealtimeMatchSubScreen : RoomSubScreen + { + public override string Title { get; } + + public override string ShortTitle => "match"; + + [Resolved(canBeNull: true)] + private Multiplayer multiplayer { get; set; } + + [Resolved] + private StatefulMultiplayerClient client { get; set; } + + private RealtimeMatchSettingsOverlay settingsOverlay; + + public RealtimeMatchSubScreen(Room room) + { + Title = room.RoomID.Value == null ? "New match" : room.Name.Value; + Activity.Value = new UserActivity.InLobby(room); + } + + [BackgroundDependencyLoader] + private void load() + { + InternalChildren = new Drawable[] + { + new GridContainer + { + RelativeSizeAxes = Axes.Both, + Content = new[] + { + new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding + { + Horizontal = 105, + Vertical = 20 + }, + Child = new GridContainer + { + RelativeSizeAxes = Axes.Both, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + new Dimension(), + }, + Content = new[] + { + new Drawable[] + { + new RealtimeMatchHeader + { + OpenSettings = () => settingsOverlay.Show() + } + }, + new Drawable[] + { + new GridContainer + { + RelativeSizeAxes = Axes.Both, + Content = new[] + { + new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Horizontal = 5, Vertical = 10 }, + Child = new GridContainer + { + RelativeSizeAxes = Axes.Both, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize) + }, + Content = new[] + { + new Drawable[] { new ParticipantsListHeader() }, + new Drawable[] + { + new Participants.ParticipantsList + { + RelativeSizeAxes = Axes.Both + }, + } + } + } + }, + new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Horizontal = 5 }, + Children = new Drawable[] + { + new OverlinedHeader("Beatmap"), + new BeatmapSelectionControl { RelativeSizeAxes = Axes.X } + } + } + } + } + } + }, + new Drawable[] + { + new GridContainer + { + RelativeSizeAxes = Axes.Both, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize) + }, + Content = new[] + { + new Drawable[] { new OverlinedHeader("Chat") }, + new Drawable[] { new MatchChatDisplay { RelativeSizeAxes = Axes.Both } } + } + } + } + }, + } + } + }, + new Drawable[] + { + new Footer { SelectedItem = { BindTarget = SelectedItem } } + } + }, + RowDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.AutoSize), + } + }, + settingsOverlay = new RealtimeMatchSettingsOverlay + { + RelativeSizeAxes = Axes.Both, + State = { Value = client.Room == null ? Visibility.Visible : Visibility.Hidden } + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Playlist.BindCollectionChanged(onPlaylistChanged, true); + + client.LoadRequested += onLoadRequested; + } + + public override bool OnBackButton() + { + if (client.Room != null && settingsOverlay.State.Value == Visibility.Visible) + { + settingsOverlay.Hide(); + return true; + } + + return base.OnBackButton(); + } + + private void onPlaylistChanged(object sender, NotifyCollectionChangedEventArgs e) => SelectedItem.Value = Playlist.FirstOrDefault(); + + private void onLoadRequested() => multiplayer?.Push(new PlayerLoader(() => new RealtimePlayer(SelectedItem.Value))); + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (client != null) + client.LoadRequested -= onLoadRequested; + } + } +} diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs new file mode 100644 index 0000000000..d629027246 --- /dev/null +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs @@ -0,0 +1,16 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Online.Multiplayer; +using osu.Game.Screens.Multi.Play; + +namespace osu.Game.Screens.Multi.RealtimeMultiplayer +{ + public class RealtimePlayer : TimeshiftPlayer + { + public RealtimePlayer(PlaylistItem playlistItem) + : base(playlistItem) + { + } + } +} From 945ba59c8e3cb9c6df69fdba8ced4fc8322f9085 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 21 Dec 2020 00:06:44 +0900 Subject: [PATCH 407/818] Make timeshift player able to not allow pause --- osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs index 41dcf61740..f86d8ff267 100644 --- a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs +++ b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs @@ -35,7 +35,8 @@ namespace osu.Game.Screens.Multi.Play [Resolved] private IBindable ruleset { get; set; } - public TimeshiftPlayer(PlaylistItem playlistItem) + public TimeshiftPlayer(PlaylistItem playlistItem, bool allowPause = true) + : base(allowPause) { this.playlistItem = playlistItem; } From 07077b8f4e52e2aa3902d73a4f936fc2086e7e84 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 21 Dec 2020 00:13:05 +0900 Subject: [PATCH 408/818] Add realtime player --- .../Screens/Multi/Play/TimeshiftPlayer.cs | 32 ++++----- .../RealtimeMultiplayer/RealtimePlayer.cs | 67 ++++++++++++++++++- .../RealtimeResultsScreen.cs | 17 +++++ 3 files changed, 99 insertions(+), 17 deletions(-) create mode 100644 osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeResultsScreen.cs diff --git a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs index f86d8ff267..e8462088f1 100644 --- a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs +++ b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs @@ -25,9 +25,11 @@ namespace osu.Game.Screens.Multi.Play public Action Exited; [Resolved(typeof(Room), nameof(Room.RoomID))] - private Bindable roomId { get; set; } + protected Bindable RoomId { get; private set; } - private readonly PlaylistItem playlistItem; + protected readonly PlaylistItem PlaylistItem; + + protected int? Token { get; private set; } [Resolved] private IAPIProvider api { get; set; } @@ -38,30 +40,28 @@ namespace osu.Game.Screens.Multi.Play public TimeshiftPlayer(PlaylistItem playlistItem, bool allowPause = true) : base(allowPause) { - this.playlistItem = playlistItem; + PlaylistItem = playlistItem; } - private int? token; - [BackgroundDependencyLoader] private void load() { - token = null; + Token = null; bool failed = false; // Sanity checks to ensure that TimeshiftPlayer matches the settings for the current PlaylistItem - if (Beatmap.Value.BeatmapInfo.OnlineBeatmapID != playlistItem.Beatmap.Value.OnlineBeatmapID) + if (Beatmap.Value.BeatmapInfo.OnlineBeatmapID != PlaylistItem.Beatmap.Value.OnlineBeatmapID) throw new InvalidOperationException("Current Beatmap does not match PlaylistItem's Beatmap"); - if (ruleset.Value.ID != playlistItem.Ruleset.Value.ID) + if (ruleset.Value.ID != PlaylistItem.Ruleset.Value.ID) throw new InvalidOperationException("Current Ruleset does not match PlaylistItem's Ruleset"); - if (!playlistItem.RequiredMods.All(m => Mods.Value.Any(m.Equals))) + if (!PlaylistItem.RequiredMods.All(m => Mods.Value.Any(m.Equals))) throw new InvalidOperationException("Current Mods do not match PlaylistItem's RequiredMods"); - var req = new CreateRoomScoreRequest(roomId.Value ?? 0, playlistItem.ID, Game.VersionHash); - req.Success += r => token = r.ID; + var req = new CreateRoomScoreRequest(RoomId.Value ?? 0, PlaylistItem.ID, Game.VersionHash); + req.Success += r => Token = r.ID; req.Failure += e => { failed = true; @@ -77,7 +77,7 @@ namespace osu.Game.Screens.Multi.Play api.Queue(req); - while (!failed && !token.HasValue) + while (!failed && !Token.HasValue) Thread.Sleep(1000); } @@ -93,8 +93,8 @@ namespace osu.Game.Screens.Multi.Play protected override ResultsScreen CreateResults(ScoreInfo score) { - Debug.Assert(roomId.Value != null); - return new TimeshiftResultsScreen(score, roomId.Value.Value, playlistItem, true); + Debug.Assert(RoomId.Value != null); + return new TimeshiftResultsScreen(score, RoomId.Value.Value, PlaylistItem, true); } protected override Score CreateScore() @@ -108,10 +108,10 @@ namespace osu.Game.Screens.Multi.Play { await base.SubmitScore(score); - Debug.Assert(token != null); + Debug.Assert(Token != null); var tcs = new TaskCompletionSource(); - var request = new SubmitRoomScoreRequest(token.Value, roomId.Value ?? 0, playlistItem.ID, score.ScoreInfo); + var request = new SubmitRoomScoreRequest(Token.Value, RoomId.Value ?? 0, PlaylistItem.ID, score.ScoreInfo); request.Success += s => { diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs index d629027246..d654496f40 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs @@ -1,16 +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; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using osu.Framework.Allocation; using osu.Game.Online.Multiplayer; +using osu.Game.Online.RealtimeMultiplayer; +using osu.Game.Scoring; using osu.Game.Screens.Multi.Play; +using osu.Game.Screens.Ranking; namespace osu.Game.Screens.Multi.RealtimeMultiplayer { public class RealtimePlayer : TimeshiftPlayer { + protected override bool PauseOnFocusLost => false; + + // Disallow fails in multiplayer for now. + protected override bool CheckModsAllowFailure() => false; + + [Resolved] + private StatefulMultiplayerClient client { get; set; } + + private readonly TaskCompletionSource resultsReady = new TaskCompletionSource(); + private bool started; + public RealtimePlayer(PlaylistItem playlistItem) - : base(playlistItem) + : base(playlistItem, false) { } + + [BackgroundDependencyLoader] + private void load() + { + if (Token == null) + return; // Todo: Somehow handle token retrieval failure. + + client.MatchStarted += onMatchStarted; + client.ResultsReady += onResultsReady; + client.ChangeState(MultiplayerUserState.Loaded); + + while (!started) + Thread.Sleep(100); + } + + private void onMatchStarted() => started = true; + + private void onResultsReady() => resultsReady.SetResult(true); + + protected override async Task SubmitScore(Score score) + { + await base.SubmitScore(score); + + await client.ChangeState(MultiplayerUserState.FinishedPlay); + + // Await up to 30 seconds for results to become available (3 api request timeouts). + // This is arbitrary just to not leave the player in an essentially deadlocked state if any connection issues occur. + await Task.WhenAny(resultsReady.Task, Task.Delay(TimeSpan.FromSeconds(30))); + } + + protected override ResultsScreen CreateResults(ScoreInfo score) + { + Debug.Assert(RoomId.Value != null); + return new RealtimeResultsScreen(score, RoomId.Value.Value, PlaylistItem); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (client != null) + { + client.MatchStarted -= onMatchStarted; + client.ResultsReady -= onResultsReady; + } + } } } diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeResultsScreen.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeResultsScreen.cs new file mode 100644 index 0000000000..1598c8fbb5 --- /dev/null +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeResultsScreen.cs @@ -0,0 +1,17 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Online.Multiplayer; +using osu.Game.Scoring; +using osu.Game.Screens.Multi.Ranking; + +namespace osu.Game.Screens.Multi.RealtimeMultiplayer +{ + public class RealtimeResultsScreen : TimeshiftResultsScreen + { + public RealtimeResultsScreen(ScoreInfo score, int roomId, PlaylistItem playlistItem) + : base(score, roomId, playlistItem, false) + { + } + } +} From 5b4197a9efb7194e955d2d6eb891f8ec67638f91 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 21 Dec 2020 00:14:54 +0900 Subject: [PATCH 409/818] Disable watching replays from realtime results screen --- .../Multi/Ranking/TimeshiftResultsScreen.cs | 4 ++-- .../RealtimeResultsScreen.cs | 2 +- osu.Game/Screens/Ranking/ResultsScreen.cs | 23 +++++++++++-------- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs index 3623208fa7..d3f1c19c7c 100644 --- a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs +++ b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs @@ -32,8 +32,8 @@ namespace osu.Game.Screens.Multi.Ranking [Resolved] private IAPIProvider api { get; set; } - public TimeshiftResultsScreen(ScoreInfo score, int roomId, PlaylistItem playlistItem, bool allowRetry) - : base(score, allowRetry) + public TimeshiftResultsScreen(ScoreInfo score, int roomId, PlaylistItem playlistItem, bool allowRetry, bool allowWatchingReplay = true) + : base(score, allowRetry, allowWatchingReplay) { this.roomId = roomId; this.playlistItem = playlistItem; diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeResultsScreen.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeResultsScreen.cs index 1598c8fbb5..3964a87eb6 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeResultsScreen.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeResultsScreen.cs @@ -10,7 +10,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer public class RealtimeResultsScreen : TimeshiftResultsScreen { public RealtimeResultsScreen(ScoreInfo score, int roomId, PlaylistItem playlistItem) - : base(score, roomId, playlistItem, false) + : base(score, roomId, playlistItem, false, false) { } } diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index 887e7ec8a9..528a1842af 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -57,11 +57,13 @@ namespace osu.Game.Screens.Ranking private APIRequest nextPageRequest; private readonly bool allowRetry; + private readonly bool allowWatchingReplay; - protected ResultsScreen(ScoreInfo score, bool allowRetry) + protected ResultsScreen(ScoreInfo score, bool allowRetry, bool allowWatchingReplay = true) { Score = score; this.allowRetry = allowRetry; + this.allowWatchingReplay = allowWatchingReplay; SelectedScore.Value = score; } @@ -128,15 +130,7 @@ namespace osu.Game.Screens.Ranking Origin = Anchor.Centre, AutoSizeAxes = Axes.Both, Spacing = new Vector2(5), - Direction = FillDirection.Horizontal, - Children = new Drawable[] - { - new ReplayDownloadButton(null) - { - Score = { BindTarget = SelectedScore }, - Width = 300 - }, - } + Direction = FillDirection.Horizontal } } } @@ -157,6 +151,15 @@ namespace osu.Game.Screens.Ranking ScorePanelList.AddScore(Score, shouldFlair); } + if (allowWatchingReplay) + { + buttons.Add(new ReplayDownloadButton(null) + { + Score = { BindTarget = SelectedScore }, + Width = 300 + }); + } + if (player != null && allowRetry) { buttons.Add(new RetryButton { Width = 300 }); From 15480c006b6353620b64dce0affcb935d935e3a7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 21 Dec 2020 00:21:30 +0900 Subject: [PATCH 410/818] Create the correct room subscreen --- osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs | 6 ++++-- .../Multi/RealtimeMultiplayer/RealtimeLoungeSubScreen.cs | 4 ++++ .../Screens/Multi/Timeshift/TimeshiftLoungeSubScreen.cs | 4 ++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs index f4591d089e..44c893363b 100644 --- a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs @@ -13,7 +13,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Online.Multiplayer; using osu.Game.Overlays; using osu.Game.Screens.Multi.Lounge.Components; -using osu.Game.Screens.Multi.Timeshift; +using osu.Game.Screens.Multi.Match; using osu.Game.Users; namespace osu.Game.Screens.Multi.Lounge @@ -192,9 +192,11 @@ namespace osu.Game.Screens.Multi.Lounge selectedRoom.Value = room; - this.Push(new TimeshiftRoomSubScreen(room)); + this.Push(CreateRoomSubScreen(room)); } protected abstract FilterControl CreateFilterControl(); + + protected abstract RoomSubScreen CreateRoomSubScreen(Room room); } } diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeLoungeSubScreen.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeLoungeSubScreen.cs index ed187e436f..9fbf0c4654 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeLoungeSubScreen.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeLoungeSubScreen.cs @@ -1,13 +1,17 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Game.Online.Multiplayer; using osu.Game.Screens.Multi.Lounge; using osu.Game.Screens.Multi.Lounge.Components; +using osu.Game.Screens.Multi.Match; namespace osu.Game.Screens.Multi.RealtimeMultiplayer { public class RealtimeLoungeSubScreen : LoungeSubScreen { protected override FilterControl CreateFilterControl() => new RealtimeFilterControl(); + + protected override RoomSubScreen CreateRoomSubScreen(Room room) => new RealtimeMatchSubScreen(room); } } diff --git a/osu.Game/Screens/Multi/Timeshift/TimeshiftLoungeSubScreen.cs b/osu.Game/Screens/Multi/Timeshift/TimeshiftLoungeSubScreen.cs index 70fb1aef1d..8e426ffbcc 100644 --- a/osu.Game/Screens/Multi/Timeshift/TimeshiftLoungeSubScreen.cs +++ b/osu.Game/Screens/Multi/Timeshift/TimeshiftLoungeSubScreen.cs @@ -1,13 +1,17 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Game.Online.Multiplayer; using osu.Game.Screens.Multi.Lounge; using osu.Game.Screens.Multi.Lounge.Components; +using osu.Game.Screens.Multi.Match; namespace osu.Game.Screens.Multi.Timeshift { public class TimeshiftLoungeSubScreen : LoungeSubScreen { protected override FilterControl CreateFilterControl() => new TimeshiftFilterControl(); + + protected override RoomSubScreen CreateRoomSubScreen(Room room) => new TimeshiftRoomSubScreen(room); } } From 959959dbedf50b76d470d5f0457c260988e7c15b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 21 Dec 2020 00:21:41 +0900 Subject: [PATCH 411/818] Add multiplayer client to OsuGameBase --- osu.Game/OsuGameBase.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 150569f1dd..eb27821d82 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -30,6 +30,7 @@ using osu.Game.Database; using osu.Game.Input; using osu.Game.Input.Bindings; using osu.Game.IO; +using osu.Game.Online.RealtimeMultiplayer; using osu.Game.Online.Spectator; using osu.Game.Overlays; using osu.Game.Resources; @@ -78,6 +79,7 @@ namespace osu.Game protected IAPIProvider API; private SpectatorStreamingClient spectatorStreaming; + private StatefulMultiplayerClient multiplayerClient; protected MenuCursorContainer MenuCursorContainer; @@ -211,6 +213,7 @@ namespace osu.Game dependencies.CacheAs(API ??= new APIAccess(LocalConfig)); dependencies.CacheAs(spectatorStreaming = new SpectatorStreamingClient()); + dependencies.CacheAs(multiplayerClient = new RealtimeMultiplayerClient()); var defaultBeatmap = new DummyWorkingBeatmap(Audio, Textures); @@ -277,6 +280,7 @@ namespace osu.Game if (API is APIAccess apiAccess) AddInternal(apiAccess); AddInternal(spectatorStreaming); + AddInternal(multiplayerClient); AddInternal(RulesetConfigCache); From 275efd12b84bf8d775313bcf93d75840c48ad370 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 21 Dec 2020 00:21:48 +0900 Subject: [PATCH 412/818] Fix room manager reference --- .../RealtimeMultiplayer/Match/RealtimeMatchSettingsOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeMatchSettingsOverlay.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeMatchSettingsOverlay.cs index 3b9603e1bd..924c736472 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeMatchSettingsOverlay.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeMatchSettingsOverlay.cs @@ -71,7 +71,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Match private BeatmapSelectionControl initialBeatmapControl; [Resolved] - private RealtimeRoomManager manager { get; set; } + private IRoomManager manager { get; set; } [Resolved] private StatefulMultiplayerClient client { get; set; } From e32b1c34cac0436c39a5087896f386af30239d06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 14 Dec 2020 22:53:46 +0100 Subject: [PATCH 413/818] Implement hit application --- .../TestSceneHitApplication.cs | 37 ++++++++++++++++ .../Objects/Drawables/DrawableHit.cs | 44 ++++++++++++++----- 2 files changed, 70 insertions(+), 11 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko.Tests/TestSceneHitApplication.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneHitApplication.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneHitApplication.cs new file mode 100644 index 0000000000..52fd440857 --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneHitApplication.cs @@ -0,0 +1,37 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Taiko.Objects.Drawables; + +namespace osu.Game.Rulesets.Taiko.Tests +{ + public class TestSceneHitApplication : HitObjectApplicationTestScene + { + [Test] + public void TestApplyNewHit() + { + var hit = new DrawableHit(); + + AddStep("apply new hit", () => hit.Apply(PrepareObject(new Hit + { + Type = HitType.Rim, + IsStrong = false, + StartTime = 300 + }), null)); + + AddHitObject(hit); + RemoveHitObject(hit); + + AddStep("apply new hit", () => hit.Apply(PrepareObject(new Hit + { + Type = HitType.Centre, + IsStrong = true, + StartTime = 500 + }), null)); + + AddHitObject(hit); + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index 431f2980ec..5a479e1f53 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using osu.Framework.Allocation; +using JetBrains.Annotations; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Audio; @@ -36,29 +36,51 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables private bool pressHandledThisFrame; - private readonly Bindable type; + private readonly Bindable type = new Bindable(); - public DrawableHit(Hit hit) - : base(hit) + public DrawableHit() + : this(null) { - type = HitObject.TypeBindable.GetBoundCopy(); - FillMode = FillMode.Fit; - - updateActionsFromType(); } - [BackgroundDependencyLoader] - private void load() + public DrawableHit([CanBeNull] Hit hit) + : base(hit) { + FillMode = FillMode.Fit; + } + + protected override void OnApply() + { + type.BindTo(HitObject.TypeBindable); type.BindValueChanged(_ => { updateActionsFromType(); - // will overwrite samples, should only be called on change. + // will overwrite samples, should only be called on subsequent changes + // after the initial application. updateSamplesFromTypeChange(); RecreatePieces(); }); + + // action update also has to happen immediately on application. + updateActionsFromType(); + + base.OnApply(); + } + + protected override void OnFree() + { + base.OnFree(); + + type.UnbindFrom(HitObject.TypeBindable); + type.UnbindEvents(); + + UnproxyContent(); + + HitActions = null; + HitAction = null; + validActionPressed = pressHandledThisFrame = false; } private HitSampleInfo[] getRimSamples() => HitObject.Samples.Where(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE).ToArray(); From 8b6bc09b8f0c0dd48879fe05adb5f614aeb4dc29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 14 Dec 2020 23:02:33 +0100 Subject: [PATCH 414/818] Implement drum roll application --- .../TestSceneDrumRollApplication.cs | 39 +++++++++++++++++++ .../Objects/Drawables/DrawableDrumRoll.cs | 34 ++++++++++------ 2 files changed, 62 insertions(+), 11 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko.Tests/TestSceneDrumRollApplication.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneDrumRollApplication.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneDrumRollApplication.cs new file mode 100644 index 0000000000..54450e27db --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneDrumRollApplication.cs @@ -0,0 +1,39 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Taiko.Objects.Drawables; + +namespace osu.Game.Rulesets.Taiko.Tests +{ + public class TestSceneDrumRollApplication : HitObjectApplicationTestScene + { + [Test] + public void TestApplyNewDrumRoll() + { + var drumRoll = new DrawableDrumRoll(); + + AddStep("apply new drum roll", () => drumRoll.Apply(PrepareObject(new DrumRoll + { + StartTime = 300, + Duration = 500, + IsStrong = false, + TickRate = 2 + }), null)); + + AddHitObject(drumRoll); + RemoveHitObject(drumRoll); + + AddStep("apply new drum roll", () => drumRoll.Apply(PrepareObject(new DrumRoll + { + StartTime = 150, + Duration = 400, + IsStrong = true, + TickRate = 16 + }), null)); + + AddHitObject(drumRoll); + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs index 4925b6fdfc..ede7453804 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs @@ -3,6 +3,7 @@ using System; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Utils; using osu.Game.Graphics; @@ -31,15 +32,26 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables ///
private int rollingHits; - private Container tickContainer; + private readonly Container tickContainer; private Color4 colourIdle; private Color4 colourEngaged; - public DrawableDrumRoll(DrumRoll drumRoll) + public DrawableDrumRoll() + : this(null) + { + } + + public DrawableDrumRoll([CanBeNull] DrumRoll drumRoll) : base(drumRoll) { RelativeSizeAxes = Axes.Y; + + Content.Add(tickContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Depth = float.MinValue + }); } [BackgroundDependencyLoader] @@ -47,12 +59,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { colourIdle = colours.YellowDark; colourEngaged = colours.YellowDarker; - - Content.Add(tickContainer = new Container - { - RelativeSizeAxes = Axes.Both, - Depth = float.MinValue - }); } protected override void LoadComplete() @@ -68,6 +74,12 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables updateColour(); } + protected override void OnFree() + { + base.OnFree(); + rollingHits = 0; + } + protected override void AddNestedHitObject(DrawableHitObject hitObject) { base.AddNestedHitObject(hitObject); @@ -114,7 +126,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables rollingHits = Math.Clamp(rollingHits, 0, rolling_hits_for_engaged_colour); - updateColour(); + updateColour(100); } protected override void CheckForResult(bool userTriggered, double timeOffset) @@ -156,10 +168,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables protected override DrawableStrongNestedHit CreateStrongNestedHit(DrumRoll.StrongNestedHit hitObject) => new StrongNestedHit(hitObject, this); - private void updateColour() + private void updateColour(double fadeDuration = 0) { Color4 newColour = Interpolation.ValueAt((float)rollingHits / rolling_hits_for_engaged_colour, colourIdle, colourEngaged, 0, 1); - (MainPiece.Drawable as IHasAccentColour)?.FadeAccent(newColour, 100); + (MainPiece.Drawable as IHasAccentColour)?.FadeAccent(newColour, fadeDuration); } private class StrongNestedHit : DrawableStrongNestedHit From e3b6eaa39059ba8afc2e71a0d034bc3b227fe49f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 14 Dec 2020 23:06:22 +0100 Subject: [PATCH 415/818] Implement swell application Also removes a weird sizing application that seems to have no effect (introduced in 27e63eb; compare removals for other taiko DHO types in 9d00e5b and 58bf288). --- .../Objects/Drawables/DrawableSwell.cs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs index 229d581d0c..9798a79450 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs @@ -3,6 +3,7 @@ using System; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -35,7 +36,12 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables private readonly CircularContainer targetRing; private readonly CircularContainer expandingRing; - public DrawableSwell(Swell swell) + public DrawableSwell() + : this(null) + { + } + + public DrawableSwell([CanBeNull] Swell swell) : base(swell) { FillMode = FillMode.Fit; @@ -123,12 +129,13 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables Origin = Anchor.Centre, }); - protected override void LoadComplete() + protected override void OnFree() { - base.LoadComplete(); + base.OnFree(); - // We need to set this here because RelativeSizeAxes won't/can't set our size by default with a different RelativeChildSize - Width *= Parent.RelativeChildSize.X; + UnproxyContent(); + + lastWasCentre = null; } protected override void AddNestedHitObject(DrawableHitObject hitObject) From 3bd42795899eda793b9b0d346abdae7902585ca9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 14 Dec 2020 23:10:19 +0100 Subject: [PATCH 416/818] Implement drum roll tick application --- .../Objects/Drawables/DrawableDrumRollTick.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs index c6761de5e3..e7d8ef1e12 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using JetBrains.Annotations; using osu.Framework.Graphics; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Taiko.Skinning.Default; @@ -16,7 +17,12 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables ///
public HitType JudgementType; - public DrawableDrumRollTick(DrumRollTick tick) + public DrawableDrumRollTick() + : this(null) + { + } + + public DrawableDrumRollTick([CanBeNull] DrumRollTick tick) : base(tick) { FillMode = FillMode.Fit; From d823c77a63e315207bbe7f282ef07ef3eeb8bf1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 14 Dec 2020 23:11:51 +0100 Subject: [PATCH 417/818] Implement swell tick application --- .../Objects/Drawables/DrawableSwellTick.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs index 14c86d151f..47fc7e5ab3 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.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 JetBrains.Annotations; using osu.Framework.Graphics; using osu.Game.Rulesets.Taiko.Skinning.Default; using osu.Game.Skinning; @@ -11,7 +12,12 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { public override bool DisplayResult => false; - public DrawableSwellTick(SwellTick hitObject) + public DrawableSwellTick() + : this(null) + { + } + + public DrawableSwellTick([CanBeNull] SwellTick hitObject) : base(hitObject) { } From ae6dedacaf1edb784c9ca7bdfb4a871f6e0821f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 14 Dec 2020 23:21:19 +0100 Subject: [PATCH 418/818] Implement nested strong hit application --- .../Objects/Drawables/DrawableDrumRoll.cs | 17 ++++++++---- .../Objects/Drawables/DrawableDrumRollTick.cs | 17 ++++++++---- .../Objects/Drawables/DrawableHit.cs | 27 +++++++++++-------- .../Drawables/DrawableStrongNestedHit.cs | 7 +++-- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 2 +- 5 files changed, 44 insertions(+), 26 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs index ede7453804..01336ea2e4 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs @@ -166,7 +166,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables Content.X = DrawHeight / 2; } - protected override DrawableStrongNestedHit CreateStrongNestedHit(DrumRoll.StrongNestedHit hitObject) => new StrongNestedHit(hitObject, this); + protected override DrawableStrongNestedHit CreateStrongNestedHit(DrumRoll.StrongNestedHit hitObject) => new StrongNestedHit(hitObject); private void updateColour(double fadeDuration = 0) { @@ -176,17 +176,24 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables private class StrongNestedHit : DrawableStrongNestedHit { - public StrongNestedHit(DrumRoll.StrongNestedHit nestedHit, DrawableDrumRoll drumRoll) - : base(nestedHit, drumRoll) + public new DrawableDrumRoll ParentHitObject => (DrawableDrumRoll)base.ParentHitObject; + + public StrongNestedHit() + : this(null) + { + } + + public StrongNestedHit([CanBeNull] DrumRoll.StrongNestedHit nestedHit) + : base(nestedHit) { } protected override void CheckForResult(bool userTriggered, double timeOffset) { - if (!MainObject.Judged) + if (!ParentHitObject.Judged) return; - ApplyResult(r => r.Type = MainObject.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult); + ApplyResult(r => r.Type = ParentHitObject.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult); } public override bool OnPressed(TaikoAction action) => false; diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs index e7d8ef1e12..1e625d91d6 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs @@ -67,21 +67,28 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables return UpdateResult(true); } - protected override DrawableStrongNestedHit CreateStrongNestedHit(DrumRollTick.StrongNestedHit hitObject) => new StrongNestedHit(hitObject, this); + protected override DrawableStrongNestedHit CreateStrongNestedHit(DrumRollTick.StrongNestedHit hitObject) => new StrongNestedHit(hitObject); private class StrongNestedHit : DrawableStrongNestedHit { - public StrongNestedHit(DrumRollTick.StrongNestedHit nestedHit, DrawableDrumRollTick tick) - : base(nestedHit, tick) + public new DrawableDrumRollTick ParentHitObject => (DrawableDrumRollTick)base.ParentHitObject; + + public StrongNestedHit() + : this(null) + { + } + + public StrongNestedHit([CanBeNull] DrumRollTick.StrongNestedHit nestedHit) + : base(nestedHit) { } protected override void CheckForResult(bool userTriggered, double timeOffset) { - if (!MainObject.Judged) + if (!ParentHitObject.Judged) return; - ApplyResult(r => r.Type = MainObject.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult); + ApplyResult(r => r.Type = ParentHitObject.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult); } public override bool OnPressed(TaikoAction action) => false; diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index 5a479e1f53..73ebd7c117 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -250,32 +250,37 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables } } - protected override DrawableStrongNestedHit CreateStrongNestedHit(Hit.StrongNestedHit hitObject) => new StrongNestedHit(hitObject, this); + protected override DrawableStrongNestedHit CreateStrongNestedHit(Hit.StrongNestedHit hitObject) => new StrongNestedHit(hitObject); private class StrongNestedHit : DrawableStrongNestedHit { + public new DrawableHit ParentHitObject => (DrawableHit)base.ParentHitObject; + /// /// The lenience for the second key press. /// This does not adjust by map difficulty in ScoreV2 yet. /// private const double second_hit_window = 30; - public new DrawableHit MainObject => (DrawableHit)base.MainObject; + public StrongNestedHit() + : this(null) + { + } - public StrongNestedHit(Hit.StrongNestedHit nestedHit, DrawableHit hit) - : base(nestedHit, hit) + public StrongNestedHit([CanBeNull] Hit.StrongNestedHit nestedHit) + : base(nestedHit) { } protected override void CheckForResult(bool userTriggered, double timeOffset) { - if (!MainObject.Result.HasResult) + if (!ParentHitObject.Result.HasResult) { base.CheckForResult(userTriggered, timeOffset); return; } - if (!MainObject.Result.IsHit) + if (!ParentHitObject.Result.IsHit) { ApplyResult(r => r.Type = r.Judgement.MinResult); return; @@ -283,27 +288,27 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables if (!userTriggered) { - if (timeOffset - MainObject.Result.TimeOffset > second_hit_window) + if (timeOffset - ParentHitObject.Result.TimeOffset > second_hit_window) ApplyResult(r => r.Type = r.Judgement.MinResult); return; } - if (Math.Abs(timeOffset - MainObject.Result.TimeOffset) <= second_hit_window) + if (Math.Abs(timeOffset - ParentHitObject.Result.TimeOffset) <= second_hit_window) ApplyResult(r => r.Type = r.Judgement.MaxResult); } public override bool OnPressed(TaikoAction action) { // Don't process actions until the main hitobject is hit - if (!MainObject.IsHit) + if (!ParentHitObject.IsHit) return false; // Don't process actions if the pressed button was released - if (MainObject.HitAction == null) + if (ParentHitObject.HitAction == null) return false; // Don't handle invalid hit action presses - if (!MainObject.HitActions.Contains(action)) + if (!ParentHitObject.HitActions.Contains(action)) return false; return UpdateResult(true); diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableStrongNestedHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableStrongNestedHit.cs index d2e8888197..9c22e34387 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableStrongNestedHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableStrongNestedHit.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.Rulesets.Objects.Drawables; +using JetBrains.Annotations; using osu.Game.Rulesets.Taiko.Judgements; namespace osu.Game.Rulesets.Taiko.Objects.Drawables @@ -11,12 +11,11 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables ///
public abstract class DrawableStrongNestedHit : DrawableTaikoHitObject { - public readonly DrawableHitObject MainObject; + public new DrawableTaikoHitObject ParentHitObject => (DrawableTaikoHitObject)base.ParentHitObject; - protected DrawableStrongNestedHit(StrongNestedHitObject nestedHit, DrawableHitObject mainObject) + protected DrawableStrongNestedHit([CanBeNull] StrongNestedHitObject nestedHit) : base(nestedHit) { - MainObject = mainObject; } } } diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index d20b190c86..8682495b41 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -248,7 +248,7 @@ namespace osu.Game.Rulesets.Taiko.UI { case TaikoStrongJudgement _: if (result.IsHit) - hitExplosionContainer.Children.FirstOrDefault(e => e.JudgedObject == ((DrawableStrongNestedHit)judgedObject).MainObject)?.VisualiseSecondHit(); + hitExplosionContainer.Children.FirstOrDefault(e => e.JudgedObject == ((DrawableStrongNestedHit)judgedObject).ParentHitObject)?.VisualiseSecondHit(); break; case TaikoDrumRollTickJudgement _: From 3af702453f66663cef225445e11f5cfc5d2cf4b4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 21 Dec 2020 00:37:13 +0900 Subject: [PATCH 419/818] Implement realtime match song select --- .../RealtimeMatchSongSelect.cs | 45 ++++++++++++++++--- 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSongSelect.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSongSelect.cs index 9bcc303065..157216e9bb 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSongSelect.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSongSelect.cs @@ -1,20 +1,53 @@ // 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 Humanizer; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Screens; +using osu.Game.Online.Multiplayer; +using osu.Game.Online.RealtimeMultiplayer; using osu.Game.Screens.Select; namespace osu.Game.Screens.Multi.RealtimeMultiplayer { - public class RealtimeMatchSongSelect : SongSelect + public class RealtimeMatchSongSelect : SongSelect, IMultiplayerSubScreen { - protected override BeatmapDetailArea CreateBeatmapDetailArea() - { - throw new System.NotImplementedException(); - } + public string ShortTitle => "song selection"; + + public override string Title => ShortTitle.Humanize(); + + [Resolved(typeof(Room), nameof(Room.Playlist))] + private BindableList playlist { get; set; } + + [Resolved] + private StatefulMultiplayerClient client { get; set; } protected override bool OnStart() { - throw new System.NotImplementedException(); + var item = new PlaylistItem(); + + item.Beatmap.Value = Beatmap.Value.BeatmapInfo; + item.Ruleset.Value = Ruleset.Value; + + item.RequiredMods.Clear(); + item.RequiredMods.AddRange(Mods.Value.Select(m => m.CreateCopy())); + + // If the client is already in a room, update via the client. + // Otherwise, update the playlist directly in preparation for it to be submitted to the API on match creation. + if (client.Room != null) + client.ChangeSettings(item: item); + else + { + playlist.Clear(); + playlist.Add(item); + } + + this.Exit(); + return true; } + + protected override BeatmapDetailArea CreateBeatmapDetailArea() => new PlayBeatmapDetailArea(); } } From d127494c2dedfd133012e574f7fe68d15049073d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 21 Dec 2020 00:39:31 +0900 Subject: [PATCH 420/818] Fix thread-unsafe room removal --- .../Multi/RealtimeMultiplayer/RealtimeRoomManager.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs index a50628a5fa..734d00b9aa 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs @@ -54,9 +54,12 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer multiplayerClient.LeaveRoom().Wait(); // 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. - RemoveRoom(joinedRoom); // This is delayed one frame because upon exiting the match subscreen, multiplayer updates the polling rate and messes with polling. - Schedule(() => listingPollingComponent.PollImmediately()); + Schedule(() => + { + RemoveRoom(joinedRoom); + listingPollingComponent.PollImmediately(); + }); } private void joinMultiplayerRoom(Room room, Action onSuccess = null) From a893360c0e07d3c2b31206ace178ad33cf85a47f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 21 Dec 2020 00:41:14 +0900 Subject: [PATCH 421/818] Reword comment --- .../Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs index 8d3b161804..0065b425ec 100644 --- a/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs @@ -71,7 +71,9 @@ namespace osu.Game.Online.RealtimeMultiplayer private RulesetStore rulesets { get; set; } = null!; private Room? apiRoom; - private int playlistItemId; // Todo: THIS IS SUPER TEMPORARY!! + + // Todo: This is temporary, until the multiplayer server returns the item id on match start or otherwise. + private int playlistItemId; /// /// Joins the for a given API . From 0c5333bd586dfea1f40e3f6e6d4144bd887713a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 20 Dec 2020 17:57:19 +0100 Subject: [PATCH 422/818] Adjust top-level hitobjects to support nested pooling --- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs | 2 +- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs | 2 +- .../Objects/Drawables/DrawableTaikoStrongableHitObject.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs index 01336ea2e4..d085b95f35 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs @@ -95,7 +95,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables protected override void ClearNestedHitObjects() { base.ClearNestedHitObjects(); - tickContainer.Clear(); + tickContainer.Clear(false); } protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs index 9798a79450..60f9521996 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs @@ -153,7 +153,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables protected override void ClearNestedHitObjects() { base.ClearNestedHitObjects(); - ticks.Clear(); + ticks.Clear(false); } protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongableHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongableHitObject.cs index 62f338ca91..4f1523eb3f 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongableHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongableHitObject.cs @@ -96,7 +96,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables protected override void ClearNestedHitObjects() { base.ClearNestedHitObjects(); - strongHitContainer.Clear(); + strongHitContainer.Clear(false); } protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject) From 370f56eadbb97aa1ed05d7bc1952e3b35924430f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 20 Dec 2020 18:02:31 +0100 Subject: [PATCH 423/818] Make strong hit DHOs public for pool registration --- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs | 2 +- .../Objects/Drawables/DrawableDrumRollTick.cs | 2 +- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs index d085b95f35..d066abf767 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs @@ -174,7 +174,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables (MainPiece.Drawable as IHasAccentColour)?.FadeAccent(newColour, fadeDuration); } - private class StrongNestedHit : DrawableStrongNestedHit + public class StrongNestedHit : DrawableStrongNestedHit { public new DrawableDrumRoll ParentHitObject => (DrawableDrumRoll)base.ParentHitObject; diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs index 1e625d91d6..0df45c424d 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs @@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables protected override DrawableStrongNestedHit CreateStrongNestedHit(DrumRollTick.StrongNestedHit hitObject) => new StrongNestedHit(hitObject); - private class StrongNestedHit : DrawableStrongNestedHit + public class StrongNestedHit : DrawableStrongNestedHit { public new DrawableDrumRollTick ParentHitObject => (DrawableDrumRollTick)base.ParentHitObject; diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index 73ebd7c117..38cda69a46 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -252,7 +252,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables protected override DrawableStrongNestedHit CreateStrongNestedHit(Hit.StrongNestedHit hitObject) => new StrongNestedHit(hitObject); - private class StrongNestedHit : DrawableStrongNestedHit + public class StrongNestedHit : DrawableStrongNestedHit { public new DrawableHit ParentHitObject => (DrawableHit)base.ParentHitObject; From 62da4eff37db8dfc1cfeaeac7d35190eab54356a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 20 Dec 2020 18:07:59 +0100 Subject: [PATCH 424/818] Route new result callback via playfield Follows route taken by osu! and catch (and required for proper pooling support). --- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 8682495b41..6b001d6c70 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -149,6 +149,12 @@ namespace osu.Game.Rulesets.Taiko.UI }; } + protected override void LoadComplete() + { + base.LoadComplete(); + NewResult += OnNewResult; + } + protected override void Update() { base.Update(); @@ -208,7 +214,6 @@ namespace osu.Game.Rulesets.Taiko.UI break; case DrawableTaikoHitObject taikoObject: - h.OnNewResult += OnNewResult; topLevelHitContainer.Add(taikoObject.CreateProxiedContent()); base.Add(h); break; @@ -226,7 +231,6 @@ namespace osu.Game.Rulesets.Taiko.UI return barLinePlayfield.Remove(barLine); case DrawableTaikoHitObject _: - h.OnNewResult -= OnNewResult; // todo: consider tidying of proxied content if required. return base.Remove(h); From 5d575d2a9b8dd95d8c06e9ef9f1b91214b7794d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 20 Dec 2020 18:11:29 +0100 Subject: [PATCH 425/818] Accept proxied content via OnNewDrawableHitObject In the non-pooled case, `OnNewDrawableHitObject()` will be called automatically on each new DHO via `Playfield.Add(DrawableHitObject)`. In the pooled case, it will be called via `Playfield`'s implementation of `GetPooledDrawableRepresentation(HitObject, DrawableHitObject)`. --- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 6b001d6c70..b3bf0974c3 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -155,6 +155,14 @@ namespace osu.Game.Rulesets.Taiko.UI NewResult += OnNewResult; } + protected override void OnNewDrawableHitObject(DrawableHitObject drawableHitObject) + { + base.OnNewDrawableHitObject(drawableHitObject); + + var taikoObject = (DrawableTaikoHitObject)drawableHitObject; + topLevelHitContainer.Add(taikoObject.CreateProxiedContent()); + } + protected override void Update() { base.Update(); @@ -213,8 +221,7 @@ namespace osu.Game.Rulesets.Taiko.UI barLinePlayfield.Add(barLine); break; - case DrawableTaikoHitObject taikoObject: - topLevelHitContainer.Add(taikoObject.CreateProxiedContent()); + case DrawableTaikoHitObject _: base.Add(h); break; @@ -231,7 +238,6 @@ namespace osu.Game.Rulesets.Taiko.UI return barLinePlayfield.Remove(barLine); case DrawableTaikoHitObject _: - // todo: consider tidying of proxied content if required. return base.Remove(h); default: From b24fc1922e11b103243ef24e361609376c353b51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 20 Dec 2020 18:16:05 +0100 Subject: [PATCH 426/818] Enable pooling for taiko DHOs --- .../UI/DrawableTaikoRuleset.cs | 18 +----------------- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 12 ++++++++++++ 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs index 9cf931ee0a..ed8e6859a2 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs @@ -7,7 +7,6 @@ using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Taiko.Objects; -using osu.Game.Rulesets.Taiko.Objects.Drawables; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.Taiko.Replays; using osu.Framework.Input; @@ -64,22 +63,7 @@ namespace osu.Game.Rulesets.Taiko.UI protected override Playfield CreatePlayfield() => new TaikoPlayfield(Beatmap.ControlPointInfo); - public override DrawableHitObject CreateDrawableRepresentation(TaikoHitObject h) - { - switch (h) - { - case Hit hit: - return new DrawableHit(hit); - - case DrumRoll drumRoll: - return new DrawableDrumRoll(drumRoll); - - case Swell swell: - return new DrawableSwell(swell); - } - - return null; - } + public override DrawableHitObject CreateDrawableRepresentation(TaikoHitObject h) => null; protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new TaikoFramedReplayInputHandler(replay); diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index b3bf0974c3..148ec7755e 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -147,6 +147,18 @@ namespace osu.Game.Rulesets.Taiko.UI }, drumRollHitContainer.CreateProxy(), }; + + RegisterPool(50); + RegisterPool(50); + + RegisterPool(5); + RegisterPool(5); + + RegisterPool(100); + RegisterPool(100); + + RegisterPool(5); + RegisterPool(100); } protected override void LoadComplete() From 6e21806873976561322499602e516ee0a6c29d36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 20 Dec 2020 18:25:49 +0100 Subject: [PATCH 427/818] Adjust sample test to pass with pooling --- .../TestSceneSampleOutput.cs | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneSampleOutput.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneSampleOutput.cs index 4ba9c447fb..296468d98d 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneSampleOutput.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneSampleOutput.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using System.Linq; using osu.Framework.Testing; using osu.Game.Audio; @@ -18,24 +19,33 @@ namespace osu.Game.Rulesets.Taiko.Tests public override void SetUpSteps() { base.SetUpSteps(); - AddAssert("has correct samples", () => + + var expectedSampleNames = new[] { - var names = Player.DrawableRuleset.Playfield.AllHitObjects.OfType().Select(h => string.Join(',', h.GetSamples().Select(s => s.Name))); + string.Empty, + string.Empty, + string.Empty, + string.Empty, + HitSampleInfo.HIT_FINISH, + HitSampleInfo.HIT_WHISTLE, + HitSampleInfo.HIT_WHISTLE, + HitSampleInfo.HIT_WHISTLE, + }; + var actualSampleNames = new List(); - var expected = new[] - { - string.Empty, - string.Empty, - string.Empty, - string.Empty, - HitSampleInfo.HIT_FINISH, - HitSampleInfo.HIT_WHISTLE, - HitSampleInfo.HIT_WHISTLE, - HitSampleInfo.HIT_WHISTLE, - }; + // due to pooling we can't access all samples right away due to object re-use, + // so we need to collect as we go. + AddStep("collect sample names", () => Player.DrawableRuleset.Playfield.NewResult += (dho, _) => + { + if (!(dho is DrawableHit h)) + return; - return names.SequenceEqual(expected); + actualSampleNames.Add(string.Join(',', h.GetSamples().Select(s => s.Name))); }); + + AddUntilStep("all samples collected", () => actualSampleNames.Count == expectedSampleNames.Length); + + AddAssert("samples are correct", () => actualSampleNames.SequenceEqual(expectedSampleNames)); } protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TaikoBeatmapConversionTest().GetBeatmap("sample-to-type-conversions"); From a8569fe15cdf663685d960e69356205b11944f8a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 21 Dec 2020 13:35:46 +0900 Subject: [PATCH 428/818] Fix a couple of simple cases of incorrect TextureLoaderStore initialisation --- osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs | 3 ++- osu.Game/Storyboards/Drawables/DrawableStoryboard.cs | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs b/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs index a9b2a15b35..b13b20dae2 100644 --- a/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs +++ b/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs @@ -13,6 +13,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; +using osu.Framework.Platform; using osu.Game.Rulesets.Configuration; namespace osu.Game.Rulesets.UI @@ -46,7 +47,7 @@ namespace osu.Game.Rulesets.UI if (resources != null) { - TextureStore = new TextureStore(new TextureLoaderStore(new NamespacedResourceStore(resources, @"Textures"))); + TextureStore = new TextureStore(parent.Get().CreateTextureLoaderStore(new NamespacedResourceStore(resources, @"Textures"))); CacheAs(TextureStore = new FallbackTextureStore(TextureStore, parent.Get())); SampleStore = parent.Get().GetSampleStore(new NamespacedResourceStore(resources, @"Samples")); diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs index 4bc28e6cef..af4615c895 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs @@ -8,6 +8,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Textures; +using osu.Framework.Platform; using osu.Game.IO; using osu.Game.Screens.Play; @@ -59,12 +60,12 @@ namespace osu.Game.Storyboards.Drawables } [BackgroundDependencyLoader(true)] - private void load(FileStore fileStore, GameplayClock clock, CancellationToken? cancellationToken) + private void load(FileStore fileStore, GameplayClock clock, CancellationToken? cancellationToken, GameHost host) { if (clock != null) Clock = clock; - dependencies.Cache(new TextureStore(new TextureLoaderStore(fileStore.Store), false, scaleAdjust: 1)); + dependencies.Cache(new TextureStore(host.CreateTextureLoaderStore(fileStore.Store), false, scaleAdjust: 1)); foreach (var layer in Storyboard.Layers) { From 7c804be4d3c0dd28ba9cbe3c8eabb771bcc32670 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 21 Dec 2020 14:06:33 +0900 Subject: [PATCH 429/818] Rename textureStore to make its purpose more clear --- osu.Game/Beatmaps/BeatmapManager.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 33e024fa28..c9d507d068 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -68,7 +68,7 @@ namespace osu.Game.Beatmaps private readonly RulesetStore rulesets; private readonly BeatmapStore beatmaps; private readonly AudioManager audioManager; - private readonly TextureStore textureStore; + private readonly LargeTextureStore largeTextureStore; private readonly ITrackStore trackStore; [CanBeNull] @@ -92,7 +92,7 @@ namespace osu.Game.Beatmaps if (performOnlineLookups) onlineLookupQueue = new BeatmapOnlineLookupQueue(api, storage); - textureStore = new LargeTextureStore(host?.CreateTextureLoaderStore(Files.Store)); + largeTextureStore = new LargeTextureStore(host?.CreateTextureLoaderStore(Files.Store)); trackStore = audioManager.GetTrackStore(Files.Store); } @@ -302,7 +302,7 @@ namespace osu.Game.Beatmaps beatmapInfo.Metadata ??= beatmapInfo.BeatmapSet.Metadata; - workingCache.Add(working = new BeatmapManagerWorkingBeatmap(Files.Store, textureStore, trackStore, beatmapInfo, audioManager)); + workingCache.Add(working = new BeatmapManagerWorkingBeatmap(Files.Store, largeTextureStore, trackStore, beatmapInfo, audioManager)); return working; } From 0ffbe12fcc2c13af237d71aa06d1e0ccc170651b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 21 Dec 2020 14:06:50 +0900 Subject: [PATCH 430/818] Expose resources to beatmaps in a saner way --- osu.Game/Beatmaps/BeatmapManager.cs | 16 ++++++-- .../Beatmaps/BeatmapManager_WorkingBeatmap.cs | 28 +++++-------- osu.Game/Beatmaps/IBeatmapResourceProvider.cs | 40 +++++++++++++++++++ 3 files changed, 64 insertions(+), 20 deletions(-) create mode 100644 osu.Game/Beatmaps/IBeatmapResourceProvider.cs diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index c9d507d068..4b334952ef 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -16,6 +16,7 @@ using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Graphics.Textures; +using osu.Framework.IO.Stores; using osu.Framework.Lists; using osu.Framework.Logging; using osu.Framework.Platform; @@ -28,8 +29,8 @@ using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Rulesets; using osu.Game.Rulesets.Objects; -using osu.Game.Users; using osu.Game.Skinning; +using osu.Game.Users; using Decoder = osu.Game.Beatmaps.Formats.Decoder; namespace osu.Game.Beatmaps @@ -38,7 +39,7 @@ namespace osu.Game.Beatmaps /// Handles the storage and retrieval of Beatmaps/WorkingBeatmaps. /// [ExcludeFromDynamicCompile] - public partial class BeatmapManager : DownloadableArchiveModelManager, IDisposable + public partial class BeatmapManager : DownloadableArchiveModelManager, IDisposable, IBeatmapResourceProvider { /// /// Fired when a single difficulty has been hidden. @@ -71,6 +72,8 @@ namespace osu.Game.Beatmaps private readonly LargeTextureStore largeTextureStore; private readonly ITrackStore trackStore; + private readonly GameHost host; + [CanBeNull] private readonly BeatmapOnlineLookupQueue onlineLookupQueue; @@ -80,6 +83,7 @@ namespace osu.Game.Beatmaps { this.rulesets = rulesets; this.audioManager = audioManager; + this.host = host; DefaultBeatmap = defaultBeatmap; @@ -302,7 +306,7 @@ namespace osu.Game.Beatmaps beatmapInfo.Metadata ??= beatmapInfo.BeatmapSet.Metadata; - workingCache.Add(working = new BeatmapManagerWorkingBeatmap(Files.Store, largeTextureStore, trackStore, beatmapInfo, audioManager)); + workingCache.Add(working = new BeatmapManagerWorkingBeatmap(beatmapInfo, this)); return working; } @@ -492,6 +496,12 @@ namespace osu.Game.Beatmaps onlineLookupQueue?.Dispose(); } + TextureStore IBeatmapResourceProvider.LargeTextureStore => largeTextureStore; + ITrackStore IBeatmapResourceProvider.Tracks => trackStore; + AudioManager IBeatmapResourceProvider.AudioManager => audioManager; + IResourceStore IBeatmapResourceProvider.Files => Files.Store; + IResourceStore IBeatmapResourceProvider.CreateTextureLoaderStore(IResourceStore underlyingStore) => host.CreateTextureLoaderStore(underlyingStore); + /// /// A dummy WorkingBeatmap for the purpose of retrieving a beatmap for star difficulty calculation. /// diff --git a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs index f5c0d97c1f..1d9a496e36 100644 --- a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs @@ -3,10 +3,8 @@ using System; using System.Linq; -using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Graphics.Textures; -using osu.Framework.IO.Stores; using osu.Framework.Logging; using osu.Framework.Testing; using osu.Game.Beatmaps.Formats; @@ -21,16 +19,12 @@ namespace osu.Game.Beatmaps [ExcludeFromDynamicCompile] private class BeatmapManagerWorkingBeatmap : WorkingBeatmap { - private readonly IResourceStore store; - private readonly TextureStore textureStore; - private readonly ITrackStore trackStore; + private readonly IBeatmapResourceProvider resources; - public BeatmapManagerWorkingBeatmap(IResourceStore store, TextureStore textureStore, ITrackStore trackStore, BeatmapInfo beatmapInfo, AudioManager audioManager) - : base(beatmapInfo, audioManager) + public BeatmapManagerWorkingBeatmap(BeatmapInfo beatmapInfo, IBeatmapResourceProvider resources) + : base(beatmapInfo, resources?.AudioManager) { - this.store = store; - this.textureStore = textureStore; - this.trackStore = trackStore; + this.resources = resources; } protected override IBeatmap GetBeatmap() @@ -40,7 +34,7 @@ namespace osu.Game.Beatmaps try { - using (var stream = new LineBufferedReader(store.GetStream(getPathForFile(BeatmapInfo.Path)))) + using (var stream = new LineBufferedReader(resources.Files.GetStream(getPathForFile(BeatmapInfo.Path)))) return Decoder.GetDecoder(stream).Decode(stream); } catch (Exception e) @@ -61,7 +55,7 @@ namespace osu.Game.Beatmaps try { - return textureStore.Get(getPathForFile(Metadata.BackgroundFile)); + return resources.LargeTextureStore.Get(getPathForFile(Metadata.BackgroundFile)); } catch (Exception e) { @@ -77,7 +71,7 @@ namespace osu.Game.Beatmaps try { - return trackStore.Get(getPathForFile(Metadata.AudioFile)); + return resources.Tracks.Get(getPathForFile(Metadata.AudioFile)); } catch (Exception e) { @@ -93,7 +87,7 @@ namespace osu.Game.Beatmaps try { - var trackData = store.GetStream(getPathForFile(Metadata.AudioFile)); + var trackData = resources.Files.GetStream(getPathForFile(Metadata.AudioFile)); return trackData == null ? null : new Waveform(trackData); } catch (Exception e) @@ -109,7 +103,7 @@ namespace osu.Game.Beatmaps try { - using (var stream = new LineBufferedReader(store.GetStream(getPathForFile(BeatmapInfo.Path)))) + using (var stream = new LineBufferedReader(resources.Files.GetStream(getPathForFile(BeatmapInfo.Path)))) { var decoder = Decoder.GetDecoder(stream); @@ -118,7 +112,7 @@ namespace osu.Game.Beatmaps storyboard = decoder.Decode(stream); else { - using (var secondaryStream = new LineBufferedReader(store.GetStream(getPathForFile(BeatmapSetInfo.StoryboardFile)))) + using (var secondaryStream = new LineBufferedReader(resources.Files.GetStream(getPathForFile(BeatmapSetInfo.StoryboardFile)))) storyboard = decoder.Decode(stream, secondaryStream); } } @@ -138,7 +132,7 @@ namespace osu.Game.Beatmaps { try { - return new LegacyBeatmapSkin(BeatmapInfo, store, AudioManager); + return new LegacyBeatmapSkin(BeatmapInfo, resources.Files, AudioManager); } catch (Exception e) { diff --git a/osu.Game/Beatmaps/IBeatmapResourceProvider.cs b/osu.Game/Beatmaps/IBeatmapResourceProvider.cs new file mode 100644 index 0000000000..43d1a60784 --- /dev/null +++ b/osu.Game/Beatmaps/IBeatmapResourceProvider.cs @@ -0,0 +1,40 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Audio; +using osu.Framework.Audio.Track; +using osu.Framework.Graphics.Textures; +using osu.Framework.IO.Stores; + +namespace osu.Game.Beatmaps +{ + public interface IBeatmapResourceProvider + { + /// + /// Retrieve a global large texture store, used for loading beatmap backgrounds. + /// + TextureStore LargeTextureStore { get; } + + /// + /// Access a global track store for retrieving beatmap tracks from. + /// + ITrackStore Tracks { get; } + + /// + /// Retrieve the game-wide audio manager. + /// + AudioManager AudioManager { get; } + + /// + /// Access game-wide user files. + /// + IResourceStore Files { get; } + + /// + /// Create a texture loader store based on an underlying data store. + /// + /// The underlying provider of texture data (in arbitrary image formats). + /// A texture loader store. + IResourceStore CreateTextureLoaderStore(IResourceStore underlyingStore); + } +} From a5bcf1dc20b31f6fb318dd3bdc7f7be40ae078f8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 21 Dec 2020 15:14:32 +0900 Subject: [PATCH 431/818] Expose resources to skin via interface (and share common pieces with beatmap) --- .../Gameplay/TestSceneStoryboardSamples.cs | 28 +++++++++++------- osu.Game/Beatmaps/BeatmapManager.cs | 6 ++-- .../Beatmaps/BeatmapManager_WorkingBeatmap.cs | 2 +- osu.Game/Beatmaps/IBeatmapResourceProvider.cs | 22 ++------------ osu.Game/IO/IStorageResourceProvider.cs | 29 +++++++++++++++++++ osu.Game/Skinning/DefaultLegacySkin.cs | 6 ++-- osu.Game/Skinning/LegacyBeatmapSkin.cs | 6 ++-- osu.Game/Skinning/LegacySkin.cs | 11 ++++--- osu.Game/Skinning/SkinManager.cs | 19 ++++++++---- .../Tests/Beatmaps/HitObjectSampleTest.cs | 20 +++++++++---- .../Tests/Visual/LegacySkinPlayerTestScene.cs | 5 ++-- osu.Game/Tests/Visual/SkinnableTestScene.cs | 23 ++++++++++----- 12 files changed, 109 insertions(+), 68 deletions(-) create mode 100644 osu.Game/IO/IStorageResourceProvider.cs diff --git a/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs b/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs index d46769a7c0..36f99c5599 100644 --- a/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs +++ b/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs @@ -10,9 +10,11 @@ using NUnit.Framework; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Graphics.Audio; +using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; using osu.Framework.Testing; using osu.Game.Audio; +using osu.Game.IO; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; @@ -27,7 +29,7 @@ using osu.Game.Tests.Visual; namespace osu.Game.Tests.Gameplay { [HeadlessTest] - public class TestSceneStoryboardSamples : OsuTestScene + public class TestSceneStoryboardSamples : OsuTestScene, IStorageResourceProvider { [Test] public void TestRetrieveTopLevelSample() @@ -35,7 +37,7 @@ namespace osu.Game.Tests.Gameplay ISkin skin = null; SampleChannel channel = null; - AddStep("create skin", () => skin = new TestSkin("test-sample", Audio)); + AddStep("create skin", () => skin = new TestSkin("test-sample", this)); AddStep("retrieve sample", () => channel = skin.GetSample(new SampleInfo("test-sample"))); AddAssert("sample is non-null", () => channel != null); @@ -47,7 +49,7 @@ namespace osu.Game.Tests.Gameplay ISkin skin = null; SampleChannel channel = null; - AddStep("create skin", () => skin = new TestSkin("folder/test-sample", Audio)); + AddStep("create skin", () => skin = new TestSkin("folder/test-sample", this)); AddStep("retrieve sample", () => channel = skin.GetSample(new SampleInfo("folder/test-sample"))); AddAssert("sample is non-null", () => channel != null); @@ -105,7 +107,7 @@ namespace osu.Game.Tests.Gameplay AddStep("setup storyboard sample", () => { - Beatmap.Value = new TestCustomSkinWorkingBeatmap(new OsuRuleset().RulesetInfo, Audio); + Beatmap.Value = new TestCustomSkinWorkingBeatmap(new OsuRuleset().RulesetInfo, this); SelectedMods.Value = new[] { testedMod }; var beatmapSkinSourceContainer = new BeatmapSkinProvidingContainer(Beatmap.Value.Skin); @@ -128,8 +130,8 @@ namespace osu.Game.Tests.Gameplay private class TestSkin : LegacySkin { - public TestSkin(string resourceName, AudioManager audioManager) - : base(DefaultLegacySkin.Info, new TestResourceStore(resourceName), audioManager, "skin.ini") + public TestSkin(string resourceName, IStorageResourceProvider resources) + : base(DefaultLegacySkin.Info, new TestResourceStore(resourceName), resources, "skin.ini") { } } @@ -158,15 +160,15 @@ namespace osu.Game.Tests.Gameplay private class TestCustomSkinWorkingBeatmap : ClockBackedTestWorkingBeatmap { - private readonly AudioManager audio; + private readonly IStorageResourceProvider resources; - public TestCustomSkinWorkingBeatmap(RulesetInfo ruleset, AudioManager audio) - : base(ruleset, null, audio) + public TestCustomSkinWorkingBeatmap(RulesetInfo ruleset, IStorageResourceProvider resources) + : base(ruleset, null, resources.AudioManager) { - this.audio = audio; + this.resources = resources; } - protected override ISkin GetSkin() => new TestSkin("test-sample", audio); + protected override ISkin GetSkin() => new TestSkin("test-sample", resources); } private class TestDrawableStoryboardSample : DrawableStoryboardSample @@ -176,5 +178,9 @@ namespace osu.Game.Tests.Gameplay { } } + + public AudioManager AudioManager => Audio; + public IResourceStore Files => null; + public IResourceStore CreateTextureLoaderStore(IResourceStore underlyingStore) => null; } } diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 4b334952ef..3cf691da9a 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -498,9 +498,9 @@ namespace osu.Game.Beatmaps TextureStore IBeatmapResourceProvider.LargeTextureStore => largeTextureStore; ITrackStore IBeatmapResourceProvider.Tracks => trackStore; - AudioManager IBeatmapResourceProvider.AudioManager => audioManager; - IResourceStore IBeatmapResourceProvider.Files => Files.Store; - IResourceStore IBeatmapResourceProvider.CreateTextureLoaderStore(IResourceStore underlyingStore) => host.CreateTextureLoaderStore(underlyingStore); + AudioManager IStorageResourceProvider.AudioManager => audioManager; + IResourceStore IStorageResourceProvider.Files => Files.Store; + IResourceStore IStorageResourceProvider.CreateTextureLoaderStore(IResourceStore underlyingStore) => host.CreateTextureLoaderStore(underlyingStore); /// /// A dummy WorkingBeatmap for the purpose of retrieving a beatmap for star difficulty calculation. diff --git a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs index 1d9a496e36..5d298dbccc 100644 --- a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs @@ -132,7 +132,7 @@ namespace osu.Game.Beatmaps { try { - return new LegacyBeatmapSkin(BeatmapInfo, resources.Files, AudioManager); + return new LegacyBeatmapSkin(BeatmapInfo, resources.Files, resources); } catch (Exception e) { diff --git a/osu.Game/Beatmaps/IBeatmapResourceProvider.cs b/osu.Game/Beatmaps/IBeatmapResourceProvider.cs index 43d1a60784..dfea0c7a30 100644 --- a/osu.Game/Beatmaps/IBeatmapResourceProvider.cs +++ b/osu.Game/Beatmaps/IBeatmapResourceProvider.cs @@ -1,14 +1,13 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Graphics.Textures; -using osu.Framework.IO.Stores; +using osu.Game.IO; namespace osu.Game.Beatmaps { - public interface IBeatmapResourceProvider + public interface IBeatmapResourceProvider : IStorageResourceProvider { /// /// Retrieve a global large texture store, used for loading beatmap backgrounds. @@ -19,22 +18,5 @@ namespace osu.Game.Beatmaps /// Access a global track store for retrieving beatmap tracks from. /// ITrackStore Tracks { get; } - - /// - /// Retrieve the game-wide audio manager. - /// - AudioManager AudioManager { get; } - - /// - /// Access game-wide user files. - /// - IResourceStore Files { get; } - - /// - /// Create a texture loader store based on an underlying data store. - /// - /// The underlying provider of texture data (in arbitrary image formats). - /// A texture loader store. - IResourceStore CreateTextureLoaderStore(IResourceStore underlyingStore); } } diff --git a/osu.Game/IO/IStorageResourceProvider.cs b/osu.Game/IO/IStorageResourceProvider.cs new file mode 100644 index 0000000000..cbd1039807 --- /dev/null +++ b/osu.Game/IO/IStorageResourceProvider.cs @@ -0,0 +1,29 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Audio; +using osu.Framework.Graphics.Textures; +using osu.Framework.IO.Stores; + +namespace osu.Game.IO +{ + public interface IStorageResourceProvider + { + /// + /// Retrieve the game-wide audio manager. + /// + AudioManager AudioManager { get; } + + /// + /// Access game-wide user files. + /// + IResourceStore Files { get; } + + /// + /// Create a texture loader store based on an underlying data store. + /// + /// The underlying provider of texture data (in arbitrary image formats). + /// A texture loader store. + IResourceStore CreateTextureLoaderStore(IResourceStore underlyingStore); + } +} diff --git a/osu.Game/Skinning/DefaultLegacySkin.cs b/osu.Game/Skinning/DefaultLegacySkin.cs index 2758a4cbba..4027cc650d 100644 --- a/osu.Game/Skinning/DefaultLegacySkin.cs +++ b/osu.Game/Skinning/DefaultLegacySkin.cs @@ -1,16 +1,16 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Audio; using osu.Framework.IO.Stores; +using osu.Game.IO; using osuTK.Graphics; namespace osu.Game.Skinning { public class DefaultLegacySkin : LegacySkin { - public DefaultLegacySkin(IResourceStore storage, AudioManager audioManager) - : base(Info, storage, audioManager, string.Empty) + public DefaultLegacySkin(IResourceStore storage, IStorageResourceProvider resources) + : base(Info, storage, resources, string.Empty) { Configuration.CustomColours["SliderBall"] = new Color4(2, 170, 255, 255); Configuration.AddComboColours( diff --git a/osu.Game/Skinning/LegacyBeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs index d647bc4a2d..fdcb81b574 100644 --- a/osu.Game/Skinning/LegacyBeatmapSkin.cs +++ b/osu.Game/Skinning/LegacyBeatmapSkin.cs @@ -1,12 +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 osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.IO.Stores; using osu.Game.Audio; using osu.Game.Beatmaps; +using osu.Game.IO; using osu.Game.Rulesets.Objects.Legacy; namespace osu.Game.Skinning @@ -16,8 +16,8 @@ namespace osu.Game.Skinning protected override bool AllowManiaSkin => false; protected override bool UseCustomSampleBanks => true; - public LegacyBeatmapSkin(BeatmapInfo beatmap, IResourceStore storage, AudioManager audioManager) - : base(createSkinInfo(beatmap), new LegacySkinResourceStore(beatmap.BeatmapSet, storage), audioManager, beatmap.Path) + public LegacyBeatmapSkin(BeatmapInfo beatmap, IResourceStore storage, IStorageResourceProvider resources) + : base(createSkinInfo(beatmap), new LegacySkinResourceStore(beatmap.BeatmapSet, storage), resources, beatmap.Path) { // Disallow default colours fallback on beatmap skins to allow using parent skin combo colours. (via SkinProvidingContainer) Configuration.AllowDefaultComboColoursFallback = false; diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 80b2fef35c..5a7ad095f6 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -7,7 +7,6 @@ using System.Diagnostics; using System.IO; using System.Linq; using JetBrains.Annotations; -using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -54,12 +53,12 @@ namespace osu.Game.Skinning private readonly Dictionary maniaConfigurations = new Dictionary(); - public LegacySkin(SkinInfo skin, IResourceStore storage, AudioManager audioManager) - : this(skin, new LegacySkinResourceStore(skin, storage), audioManager, "skin.ini") + public LegacySkin(SkinInfo skin, IStorageResourceProvider resources) + : this(skin, new LegacySkinResourceStore(skin, resources.Files), resources, "skin.ini") { } - protected LegacySkin(SkinInfo skin, IResourceStore storage, AudioManager audioManager, string filename) + protected LegacySkin(SkinInfo skin, IResourceStore storage, IStorageResourceProvider resources, string filename) : base(skin) { using (var stream = storage?.GetStream(filename)) @@ -85,12 +84,12 @@ namespace osu.Game.Skinning if (storage != null) { - var samples = audioManager?.GetSampleStore(storage); + var samples = resources?.AudioManager?.GetSampleStore(storage); if (samples != null) samples.PlaybackConcurrency = OsuGameBase.SAMPLE_CONCURRENCY; Samples = samples; - Textures = new TextureStore(new TextureLoaderStore(storage)); + Textures = new TextureStore(resources?.CreateTextureLoaderStore(storage)); (storage as ResourceStore)?.AddExtension("ogg"); } diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 9b69a1eecd..2ee940c02d 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -22,15 +22,18 @@ using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Audio; using osu.Game.Database; +using osu.Game.IO; using osu.Game.IO.Archives; namespace osu.Game.Skinning { [ExcludeFromDynamicCompile] - public class SkinManager : ArchiveModelManager, ISkinSource + public class SkinManager : ArchiveModelManager, ISkinSource, IStorageResourceProvider { private readonly AudioManager audio; + private readonly GameHost host; + private readonly IResourceStore legacyDefaultResources; public readonly Bindable CurrentSkin = new Bindable(new DefaultSkin()); @@ -42,10 +45,12 @@ namespace osu.Game.Skinning protected override string ImportFromStablePath => "Skins"; - public SkinManager(Storage storage, DatabaseContextFactory contextFactory, IIpcHost importHost, AudioManager audio, IResourceStore legacyDefaultResources) - : base(storage, contextFactory, new SkinStore(contextFactory, storage), importHost) + public SkinManager(Storage storage, DatabaseContextFactory contextFactory, GameHost host, AudioManager audio, IResourceStore legacyDefaultResources) + : base(storage, contextFactory, new SkinStore(contextFactory, storage), host) { this.audio = audio; + this.host = host; + this.legacyDefaultResources = legacyDefaultResources; CurrentSkinInfo.ValueChanged += skin => CurrentSkin.Value = GetSkin(skin.NewValue); @@ -148,9 +153,9 @@ namespace osu.Game.Skinning return new DefaultSkin(); if (skinInfo == DefaultLegacySkin.Info) - return new DefaultLegacySkin(legacyDefaultResources, audio); + return new DefaultLegacySkin(legacyDefaultResources, this); - return new LegacySkin(skinInfo, Files.Store, audio); + return new LegacySkin(skinInfo, this); } /// @@ -169,5 +174,9 @@ namespace osu.Game.Skinning public SampleChannel GetSample(ISampleInfo sampleInfo) => CurrentSkin.Value.GetSample(sampleInfo); public IBindable GetConfig(TLookup lookup) => CurrentSkin.Value.GetConfig(lookup); + + AudioManager IStorageResourceProvider.AudioManager => audio; + IResourceStore IStorageResourceProvider.Files => Files.Store; + IResourceStore IStorageResourceProvider.CreateTextureLoaderStore(IResourceStore underlyingStore) => host.CreateTextureLoaderStore(underlyingStore); } } diff --git a/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs b/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs index e3557222d5..dd1979a7e3 100644 --- a/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs +++ b/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Audio; +using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; using osu.Framework.Testing; using osu.Framework.Timing; @@ -25,7 +26,7 @@ using osu.Game.Users; namespace osu.Game.Tests.Beatmaps { [HeadlessTest] - public abstract class HitObjectSampleTest : PlayerTestScene + public abstract class HitObjectSampleTest : PlayerTestScene, IStorageResourceProvider { protected abstract IResourceStore Resources { get; } protected LegacySkin Skin { get; private set; } @@ -58,7 +59,7 @@ namespace osu.Game.Tests.Beatmaps protected sealed override IBeatmap CreateBeatmap(RulesetInfo ruleset) => currentTestBeatmap; protected sealed override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) - => new TestWorkingBeatmap(beatmapInfo, beatmapSkinResourceStore, beatmap, storyboard, Clock, Audio); + => new TestWorkingBeatmap(beatmapInfo, beatmapSkinResourceStore, beatmap, storyboard, Clock, this); protected override TestPlayer CreatePlayer(Ruleset ruleset) => new TestPlayer(false); @@ -109,7 +110,7 @@ namespace osu.Game.Tests.Beatmaps }; // Need to refresh the cached skin source to refresh the skin resource store. - dependencies.SkinSource = new SkinProvidingContainer(Skin = new LegacySkin(userSkinInfo, userSkinResourceStore, Audio)); + dependencies.SkinSource = new SkinProvidingContainer(Skin = new LegacySkin(userSkinInfo, this)); }); } @@ -122,6 +123,10 @@ namespace osu.Game.Tests.Beatmaps protected void AssertNoLookup(string name) => AddAssert($"\"{name}\" not looked up", () => !beatmapSkinResourceStore.PerformedLookups.Contains(name) && !userSkinResourceStore.PerformedLookups.Contains(name)); + public AudioManager AudioManager => Audio; + public IResourceStore Files => userSkinResourceStore; + public IResourceStore CreateTextureLoaderStore(IResourceStore underlyingStore) => null; + private class SkinSourceDependencyContainer : IReadOnlyDependencyContainer { public ISkinSource SkinSource; @@ -191,14 +196,17 @@ namespace osu.Game.Tests.Beatmaps private readonly BeatmapInfo skinBeatmapInfo; private readonly IResourceStore resourceStore; - public TestWorkingBeatmap(BeatmapInfo skinBeatmapInfo, IResourceStore resourceStore, IBeatmap beatmap, Storyboard storyboard, IFrameBasedClock referenceClock, AudioManager audio) - : base(beatmap, storyboard, referenceClock, audio) + private readonly IStorageResourceProvider resources; + + public TestWorkingBeatmap(BeatmapInfo skinBeatmapInfo, IResourceStore resourceStore, IBeatmap beatmap, Storyboard storyboard, IFrameBasedClock referenceClock, IStorageResourceProvider resources) + : base(beatmap, storyboard, referenceClock, resources.AudioManager) { this.skinBeatmapInfo = skinBeatmapInfo; this.resourceStore = resourceStore; + this.resources = resources; } - protected override ISkin GetSkin() => new LegacyBeatmapSkin(skinBeatmapInfo, resourceStore, AudioManager); + protected override ISkin GetSkin() => new LegacyBeatmapSkin(skinBeatmapInfo, resourceStore, resources); } } } diff --git a/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs b/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs index 054f72400e..c186525757 100644 --- a/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs +++ b/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs @@ -3,7 +3,6 @@ using NUnit.Framework; using osu.Framework.Allocation; -using osu.Framework.Audio; using osu.Framework.IO.Stores; using osu.Game.Rulesets; using osu.Game.Skinning; @@ -18,9 +17,9 @@ namespace osu.Game.Tests.Visual protected override TestPlayer CreatePlayer(Ruleset ruleset) => new SkinProvidingPlayer(legacySkinSource); [BackgroundDependencyLoader] - private void load(AudioManager audio, OsuGameBase game) + private void load(OsuGameBase game, SkinManager skins) { - var legacySkin = new DefaultLegacySkin(new NamespacedResourceStore(game.Resources, "Skins/Legacy"), audio); + var legacySkin = new DefaultLegacySkin(new NamespacedResourceStore(game.Resources, "Skins/Legacy"), skins); legacySkinSource = new SkinProvidingContainer(legacySkin); } diff --git a/osu.Game/Tests/Visual/SkinnableTestScene.cs b/osu.Game/Tests/Visual/SkinnableTestScene.cs index 68098f9d3b..3a9c33788a 100644 --- a/osu.Game/Tests/Visual/SkinnableTestScene.cs +++ b/osu.Game/Tests/Visual/SkinnableTestScene.cs @@ -13,8 +13,10 @@ using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; +using osu.Framework.Platform; using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; +using osu.Game.IO; using osu.Game.Rulesets; using osu.Game.Skinning; using osuTK; @@ -22,13 +24,16 @@ using osuTK.Graphics; namespace osu.Game.Tests.Visual { - public abstract class SkinnableTestScene : OsuGridTestScene + public abstract class SkinnableTestScene : OsuGridTestScene, IStorageResourceProvider { private Skin metricsSkin; private Skin defaultSkin; private Skin specialSkin; private Skin oldSkin; + [Resolved] + private GameHost host { get; set; } + protected SkinnableTestScene() : base(2, 3) { @@ -39,10 +44,10 @@ namespace osu.Game.Tests.Visual { var dllStore = new DllResourceStore(DynamicCompilationOriginal.GetType().Assembly); - metricsSkin = new TestLegacySkin(new SkinInfo { Name = "metrics-skin" }, new NamespacedResourceStore(dllStore, "Resources/metrics_skin"), audio, true); - defaultSkin = new DefaultLegacySkin(new NamespacedResourceStore(game.Resources, "Skins/Legacy"), audio); - specialSkin = new TestLegacySkin(new SkinInfo { Name = "special-skin" }, new NamespacedResourceStore(dllStore, "Resources/special_skin"), audio, true); - oldSkin = new TestLegacySkin(new SkinInfo { Name = "old-skin" }, new NamespacedResourceStore(dllStore, "Resources/old_skin"), audio, true); + metricsSkin = new TestLegacySkin(new SkinInfo { Name = "metrics-skin" }, new NamespacedResourceStore(dllStore, "Resources/metrics_skin"), this, true); + defaultSkin = new DefaultLegacySkin(new NamespacedResourceStore(game.Resources, "Skins/Legacy"), this); + specialSkin = new TestLegacySkin(new SkinInfo { Name = "special-skin" }, new NamespacedResourceStore(dllStore, "Resources/special_skin"), this, true); + oldSkin = new TestLegacySkin(new SkinInfo { Name = "old-skin" }, new NamespacedResourceStore(dllStore, "Resources/old_skin"), this, true); } private readonly List createdDrawables = new List(); @@ -147,6 +152,10 @@ namespace osu.Game.Tests.Visual protected virtual IBeatmap CreateBeatmapForSkinProvider() => CreateWorkingBeatmap(Ruleset.Value).GetPlayableBeatmap(Ruleset.Value); + public AudioManager AudioManager => Audio; + public IResourceStore Files => null; + public IResourceStore CreateTextureLoaderStore(IResourceStore underlyingStore) => host.CreateTextureLoaderStore(underlyingStore); + private class OutlineBox : CompositeDrawable { public OutlineBox() @@ -170,8 +179,8 @@ namespace osu.Game.Tests.Visual { private readonly bool extrapolateAnimations; - public TestLegacySkin(SkinInfo skin, IResourceStore storage, AudioManager audioManager, bool extrapolateAnimations) - : base(skin, storage, audioManager, "skin.ini") + public TestLegacySkin(SkinInfo skin, IResourceStore storage, IStorageResourceProvider resources, bool extrapolateAnimations) + : base(skin, storage, resources, "skin.ini") { this.extrapolateAnimations = extrapolateAnimations; } From 82cf58353ce15e45770bea510181769abeaee846 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 21 Dec 2020 15:38:20 +0900 Subject: [PATCH 432/818] Fix incorrect joinedroom null checks --- osu.Game/Screens/Multi/Components/RoomManager.cs | 2 +- .../Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Multi/Components/RoomManager.cs b/osu.Game/Screens/Multi/Components/RoomManager.cs index 276a5a6148..482ee5492c 100644 --- a/osu.Game/Screens/Multi/Components/RoomManager.cs +++ b/osu.Game/Screens/Multi/Components/RoomManager.cs @@ -112,7 +112,7 @@ namespace osu.Game.Screens.Multi.Components { currentJoinRoomRequest?.Cancel(); - if (JoinedRoom == null) + if (JoinedRoom.Value == null) return; api.Queue(new PartRoomRequest(joinedRoom.Value)); diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs index b81ca7aade..e0fca3ce4c 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs @@ -45,7 +45,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer public override void PartRoom() { - if (JoinedRoom == null) + if (JoinedRoom.Value == null) return; var joinedRoom = JoinedRoom.Value; From a59124dd938a8c98a276c85299cbbb144f87b190 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 21 Dec 2020 16:18:39 +0900 Subject: [PATCH 433/818] Make room duration/endsat nullable --- .../Visual/Multiplayer/TestSceneRoomStatus.cs | 12 +++++++++--- osu.Game/Online/Multiplayer/Room.cs | 16 +++++++++++----- .../Screens/Multi/Components/RoomStatusInfo.cs | 11 +++++++++-- .../Match/Components/MatchSettingsOverlay.cs | 2 +- osu.Game/Screens/Multi/MultiplayerComposite.cs | 4 ++-- .../Multi/Timeshift/TimeshiftReadyButton.cs | 4 ++-- 6 files changed, 34 insertions(+), 15 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs index c1dfb94464..a6dd1437f7 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs @@ -22,22 +22,28 @@ namespace osu.Game.Tests.Visual.Multiplayer { new DrawableRoom(new Room { - Name = { Value = "Room 1" }, + Name = { Value = "Open - ending in 1 day" }, Status = { Value = new RoomStatusOpen() }, EndDate = { Value = DateTimeOffset.Now.AddDays(1) } }) { MatchingFilter = true }, new DrawableRoom(new Room { - Name = { Value = "Room 2" }, + Name = { Value = "Playing - ending in 1 day" }, Status = { Value = new RoomStatusPlaying() }, EndDate = { Value = DateTimeOffset.Now.AddDays(1) } }) { MatchingFilter = true }, new DrawableRoom(new Room { - Name = { Value = "Room 3" }, + Name = { Value = "Ended" }, Status = { Value = new RoomStatusEnded() }, EndDate = { Value = DateTimeOffset.Now } }) { MatchingFilter = true }, + new DrawableRoom(new Room + { + Name = { Value = "Open (realtime)" }, + Status = { Value = new RoomStatusOpen() }, + Category = { Value = RoomCategory.Realtime } + }) { MatchingFilter = true }, } }; } diff --git a/osu.Game/Online/Multiplayer/Room.cs b/osu.Game/Online/Multiplayer/Room.cs index 9a21543b2e..ee8992e399 100644 --- a/osu.Game/Online/Multiplayer/Room.cs +++ b/osu.Game/Online/Multiplayer/Room.cs @@ -40,7 +40,7 @@ namespace osu.Game.Online.Multiplayer [Cached] [JsonIgnore] - public readonly Bindable Duration = new Bindable(TimeSpan.FromMinutes(30)); + public readonly Bindable Duration = new Bindable(); [Cached] [JsonIgnore] @@ -78,16 +78,22 @@ namespace osu.Game.Online.Multiplayer } [JsonProperty("duration")] - private int duration + private int? duration { - get => (int)Duration.Value.TotalMinutes; - set => Duration.Value = TimeSpan.FromMinutes(value); + get => (int?)Duration.Value?.TotalMinutes; + set + { + if (value == null) + Duration.Value = null; + else + Duration.Value = TimeSpan.FromMinutes(value.Value); + } } // Only supports retrieval for now [Cached] [JsonProperty("ends_at")] - public readonly Bindable EndDate = new Bindable(); + public readonly Bindable EndDate = new Bindable(); // Todo: Find a better way to do this (https://github.com/ppy/osu-framework/issues/1930) [JsonProperty("max_attempts", DefaultValueHandling = DefaultValueHandling.Ignore)] diff --git a/osu.Game/Screens/Multi/Components/RoomStatusInfo.cs b/osu.Game/Screens/Multi/Components/RoomStatusInfo.cs index d799f846c2..b5676692a4 100644 --- a/osu.Game/Screens/Multi/Components/RoomStatusInfo.cs +++ b/osu.Game/Screens/Multi/Components/RoomStatusInfo.cs @@ -48,16 +48,23 @@ namespace osu.Game.Screens.Multi.Components private class EndDatePart : DrawableDate { - public readonly IBindable EndDate = new Bindable(); + public readonly IBindable EndDate = new Bindable(); public EndDatePart() : base(DateTimeOffset.UtcNow) { - EndDate.BindValueChanged(date => Date = date.NewValue); + EndDate.BindValueChanged(date => + { + // If null, set a very large future date to prevent unnecessary schedules. + Date = date.NewValue ?? DateTimeOffset.Now.AddYears(1); + }, true); } protected override string Format() { + if (EndDate.Value == null) + return string.Empty; + var diffToNow = Date.Subtract(DateTimeOffset.Now); if (diffToNow.TotalSeconds < -5) diff --git a/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs b/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs index b8003b9774..1859e8db8a 100644 --- a/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs +++ b/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs @@ -325,7 +325,7 @@ namespace osu.Game.Screens.Multi.Match.Components Availability.BindValueChanged(availability => AvailabilityPicker.Current.Value = availability.NewValue, true); Type.BindValueChanged(type => TypePicker.Current.Value = type.NewValue, true); MaxParticipants.BindValueChanged(count => MaxParticipantsField.Text = count.NewValue?.ToString(), true); - Duration.BindValueChanged(duration => DurationField.Current.Value = duration.NewValue, true); + Duration.BindValueChanged(duration => DurationField.Current.Value = duration.NewValue ?? TimeSpan.FromMinutes(30), true); playlist.Items.BindTo(Playlist); Playlist.BindCollectionChanged(onPlaylistChanged, true); diff --git a/osu.Game/Screens/Multi/MultiplayerComposite.cs b/osu.Game/Screens/Multi/MultiplayerComposite.cs index e612e77748..6e0c69d712 100644 --- a/osu.Game/Screens/Multi/MultiplayerComposite.cs +++ b/osu.Game/Screens/Multi/MultiplayerComposite.cs @@ -40,12 +40,12 @@ namespace osu.Game.Screens.Multi protected Bindable MaxParticipants { get; private set; } [Resolved(typeof(Room))] - protected Bindable EndDate { get; private set; } + protected Bindable EndDate { get; private set; } [Resolved(typeof(Room))] protected Bindable Availability { get; private set; } [Resolved(typeof(Room))] - protected Bindable Duration { get; private set; } + protected Bindable Duration { get; private set; } } } diff --git a/osu.Game/Screens/Multi/Timeshift/TimeshiftReadyButton.cs b/osu.Game/Screens/Multi/Timeshift/TimeshiftReadyButton.cs index ba639c29f4..c878451eee 100644 --- a/osu.Game/Screens/Multi/Timeshift/TimeshiftReadyButton.cs +++ b/osu.Game/Screens/Multi/Timeshift/TimeshiftReadyButton.cs @@ -13,7 +13,7 @@ namespace osu.Game.Screens.Multi.Timeshift public class TimeshiftReadyButton : ReadyButton { [Resolved(typeof(Room), nameof(Room.EndDate))] - private Bindable endDate { get; set; } + private Bindable endDate { get; set; } public TimeshiftReadyButton() { @@ -32,7 +32,7 @@ namespace osu.Game.Screens.Multi.Timeshift { base.Update(); - Enabled.Value = DateTimeOffset.UtcNow.AddSeconds(30).AddMilliseconds(GameBeatmap.Value.Track.Length) < endDate.Value; + Enabled.Value = endDate.Value != null && DateTimeOffset.UtcNow.AddSeconds(30).AddMilliseconds(GameBeatmap.Value.Track.Length) < endDate.Value; } } } From c3d1eaf36dec71ff703f623b1f274192e6f1275c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 21 Dec 2020 16:21:05 +0900 Subject: [PATCH 434/818] Make RealtimeMultiplayerTestScene abstract --- .../RealtimeMultiplayer/RealtimeMultiplayerTestScene.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Tests/Visual/RealtimeMultiplayer/RealtimeMultiplayerTestScene.cs b/osu.Game/Tests/Visual/RealtimeMultiplayer/RealtimeMultiplayerTestScene.cs index b52106551e..aec70d8be4 100644 --- a/osu.Game/Tests/Visual/RealtimeMultiplayer/RealtimeMultiplayerTestScene.cs +++ b/osu.Game/Tests/Visual/RealtimeMultiplayer/RealtimeMultiplayerTestScene.cs @@ -12,7 +12,7 @@ using osu.Game.Screens.Multi.RealtimeMultiplayer; namespace osu.Game.Tests.Visual.RealtimeMultiplayer { - public class RealtimeMultiplayerTestScene : MultiplayerTestScene + public abstract class RealtimeMultiplayerTestScene : MultiplayerTestScene { [Cached(typeof(StatefulMultiplayerClient))] public TestRealtimeMultiplayerClient Client { get; } @@ -28,7 +28,7 @@ namespace osu.Game.Tests.Visual.RealtimeMultiplayer private readonly bool joinRoom; - public RealtimeMultiplayerTestScene(bool joinRoom = true) + protected RealtimeMultiplayerTestScene(bool joinRoom = true) { this.joinRoom = joinRoom; base.Content.Add(content = new TestRealtimeRoomContainer { RelativeSizeAxes = Axes.Both }); From 64a32723f3b544fec8f02b120f00986ffcd3e65e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 21 Dec 2020 16:23:42 +0900 Subject: [PATCH 435/818] One more case --- osu.Game/Online/Multiplayer/Room.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Multiplayer/Room.cs b/osu.Game/Online/Multiplayer/Room.cs index ee8992e399..e3444d6b7e 100644 --- a/osu.Game/Online/Multiplayer/Room.cs +++ b/osu.Game/Online/Multiplayer/Room.cs @@ -139,7 +139,7 @@ namespace osu.Game.Online.Multiplayer ParticipantCount.Value = other.ParticipantCount.Value; EndDate.Value = other.EndDate.Value; - if (DateTimeOffset.Now >= EndDate.Value) + if (EndDate.Value != null && DateTimeOffset.Now >= EndDate.Value) Status.Value = new RoomStatusEnded(); if (!Playlist.SequenceEqual(other.Playlist)) From 5d73359bd7c0e95a9e18c202319d0ecf77071e18 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 21 Dec 2020 16:35:19 +0900 Subject: [PATCH 436/818] Make participant count non-nullable --- osu.Game/Online/Multiplayer/Room.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/osu.Game/Online/Multiplayer/Room.cs b/osu.Game/Online/Multiplayer/Room.cs index 9a21543b2e..5930a624bc 100644 --- a/osu.Game/Online/Multiplayer/Room.cs +++ b/osu.Game/Online/Multiplayer/Room.cs @@ -67,15 +67,8 @@ namespace osu.Game.Online.Multiplayer public readonly BindableList RecentParticipants = new BindableList(); [Cached] - public readonly Bindable ParticipantCount = new Bindable(); - - // todo: TEMPORARY [JsonProperty("participant_count")] - private int? participantCount - { - get => ParticipantCount.Value; - set => ParticipantCount.Value = value ?? 0; - } + public readonly Bindable ParticipantCount = new Bindable(); [JsonProperty("duration")] private int duration From d096f2f8f6445eb3524df9612e8d7a891528bebe Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 21 Dec 2020 16:39:46 +0900 Subject: [PATCH 437/818] Fix potential cross-thread operation during chat channel load The callbacks are scheduled to the API thread, but hooked up in BDL load. This causes a potential case of cross-thread collection enumeration. I've tested and it seems like the schedule logic should be fine for short term. Longer term, we probably want to re-think how this works so background operations aren't performed on the `DrawableChannel` in the first place (chat shouldn't have an overhead like this when not visible). Closes #11231. --- osu.Game/Overlays/Chat/DrawableChannel.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs index d63faebae4..5926d11c03 100644 --- a/osu.Game/Overlays/Chat/DrawableChannel.cs +++ b/osu.Game/Overlays/Chat/DrawableChannel.cs @@ -103,7 +103,7 @@ namespace osu.Game.Overlays.Chat Colour = colours.ChatBlue.Lighten(0.7f), }; - private void newMessagesArrived(IEnumerable newMessages) + private void newMessagesArrived(IEnumerable newMessages) => Schedule(() => { if (newMessages.Min(m => m.Id) < chatLines.Max(c => c.Message.Id)) { @@ -155,9 +155,9 @@ namespace osu.Game.Overlays.Chat if (shouldScrollToEnd) scrollToEnd(); - } + }); - private void pendingMessageResolved(Message existing, Message updated) + private void pendingMessageResolved(Message existing, Message updated) => Schedule(() => { var found = chatLines.LastOrDefault(c => c.Message == existing); @@ -169,12 +169,12 @@ namespace osu.Game.Overlays.Chat found.Message = updated; ChatLineFlow.Add(found); } - } + }); - private void messageRemoved(Message removed) + private void messageRemoved(Message removed) => Schedule(() => { chatLines.FirstOrDefault(c => c.Message == removed)?.FadeColour(Color4.Red, 400).FadeOut(600).Expire(); - } + }); private IEnumerable chatLines => ChatLineFlow.Children.OfType(); From a021aaf54673acf78c5272d077e757b1ded87bc8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 21 Dec 2020 16:42:21 +0900 Subject: [PATCH 438/818] Fix room category being serialised as ints --- osu.Game/Online/Multiplayer/Room.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Multiplayer/Room.cs b/osu.Game/Online/Multiplayer/Room.cs index 9a21543b2e..53ae142ad4 100644 --- a/osu.Game/Online/Multiplayer/Room.cs +++ b/osu.Game/Online/Multiplayer/Room.cs @@ -35,9 +35,22 @@ namespace osu.Game.Online.Multiplayer public readonly Bindable ChannelId = new Bindable(); [Cached] - [JsonProperty("category")] + [JsonIgnore] public readonly Bindable Category = new Bindable(); + // Todo: osu-framework bug (https://github.com/ppy/osu-framework/issues/4106) + [JsonProperty("category")] + private string categoryString + { + get => Category.Value.ToString().ToLower(); + set + { + if (!Enum.TryParse(value, true, out var enumValue)) + enumValue = RoomCategory.Normal; + Category.Value = enumValue; + } + } + [Cached] [JsonIgnore] public readonly Bindable Duration = new Bindable(TimeSpan.FromMinutes(30)); From e23d81bfc64594c80d21ea1043ec085a47161f25 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 21 Dec 2020 16:56:45 +0900 Subject: [PATCH 439/818] Use enum property --- .../Converters/SnakeCaseStringEnumConverter.cs | 16 ++++++++++++++++ osu.Game/Online/Multiplayer/Room.cs | 13 +++++-------- 2 files changed, 21 insertions(+), 8 deletions(-) create mode 100644 osu.Game/IO/Serialization/Converters/SnakeCaseStringEnumConverter.cs diff --git a/osu.Game/IO/Serialization/Converters/SnakeCaseStringEnumConverter.cs b/osu.Game/IO/Serialization/Converters/SnakeCaseStringEnumConverter.cs new file mode 100644 index 0000000000..1d82a5bc87 --- /dev/null +++ b/osu.Game/IO/Serialization/Converters/SnakeCaseStringEnumConverter.cs @@ -0,0 +1,16 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Serialization; + +namespace osu.Game.IO.Serialization.Converters +{ + public class SnakeCaseStringEnumConverter : StringEnumConverter + { + public SnakeCaseStringEnumConverter() + { + NamingStrategy = new SnakeCaseNamingStrategy(); + } + } +} diff --git a/osu.Game/Online/Multiplayer/Room.cs b/osu.Game/Online/Multiplayer/Room.cs index 53ae142ad4..66e3e8975a 100644 --- a/osu.Game/Online/Multiplayer/Room.cs +++ b/osu.Game/Online/Multiplayer/Room.cs @@ -6,6 +6,7 @@ using System.Linq; using Newtonsoft.Json; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Game.IO.Serialization.Converters; using osu.Game.Online.Multiplayer.GameTypes; using osu.Game.Online.Multiplayer.RoomStatuses; using osu.Game.Users; @@ -40,15 +41,11 @@ namespace osu.Game.Online.Multiplayer // Todo: osu-framework bug (https://github.com/ppy/osu-framework/issues/4106) [JsonProperty("category")] - private string categoryString + [JsonConverter(typeof(SnakeCaseStringEnumConverter))] + private RoomCategory category { - get => Category.Value.ToString().ToLower(); - set - { - if (!Enum.TryParse(value, true, out var enumValue)) - enumValue = RoomCategory.Normal; - Category.Value = enumValue; - } + get => Category.Value; + set => Category.Value = value; } [Cached] From eb46c9ce9be47341a18e08fedeeb65408ac5847c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 21 Dec 2020 17:11:30 +0900 Subject: [PATCH 440/818] Fix metadata lost in beatmapset deserialisation --- .../Online/API/Requests/Responses/APIBeatmapSet.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs index 6d0160fbc4..720d6bfff4 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs @@ -80,7 +80,7 @@ namespace osu.Game.Online.API.Requests.Responses public BeatmapSetInfo ToBeatmapSet(RulesetStore rulesets) { - return new BeatmapSetInfo + var beatmapSet = new BeatmapSetInfo { OnlineBeatmapSetID = OnlineBeatmapSetID, Metadata = this, @@ -104,8 +104,17 @@ namespace osu.Game.Online.API.Requests.Responses Genre = genre, Language = language }, - Beatmaps = beatmaps?.Select(b => b.ToBeatmap(rulesets)).ToList(), }; + + beatmapSet.Beatmaps = beatmaps?.Select(b => + { + var beatmap = b.ToBeatmap(rulesets); + beatmap.BeatmapSet = beatmapSet; + beatmap.Metadata = beatmapSet.Metadata; + return beatmap; + }).ToList(); + + return beatmapSet; } } } From e1b2de27a6395f051e4f9f71aa52dfe6398cc4fc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 21 Dec 2020 17:19:29 +0900 Subject: [PATCH 441/818] Update osu!mania legacy skin's judgement animation to match stable --- .../Legacy/LegacyManiaJudgementPiece.cs | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaJudgementPiece.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaJudgementPiece.cs index 9684cbb167..e275e1c0ca 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaJudgementPiece.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaJudgementPiece.cs @@ -5,11 +5,11 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Animations; using osu.Framework.Graphics.Containers; +using osu.Framework.Utils; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Scoring; using osu.Game.Skinning; -using osuTK; namespace osu.Game.Rulesets.Mania.Skinning.Legacy { @@ -56,31 +56,30 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy (animation as IFramedAnimation)?.GotoFrame(0); + this.FadeInFromZero(20, Easing.Out) + .Then().Delay(160) + .FadeOutFromOne(40, Easing.In); + switch (result) { case HitResult.None: break; case HitResult.Miss: - animation.ScaleTo(1.6f); - animation.ScaleTo(1, 100, Easing.In); - - animation.MoveTo(Vector2.Zero); - animation.MoveToOffset(new Vector2(0, 100), 800, Easing.InQuint); + animation.ScaleTo(1.2f).Then().ScaleTo(1, 100, Easing.Out); animation.RotateTo(0); - animation.RotateTo(40, 800, Easing.InQuint); - - this.FadeOutFromOne(800); + animation.RotateTo(RNG.NextSingle(-18, 18), 100, Easing.Out); break; default: - animation.ScaleTo(0.8f); - animation.ScaleTo(1, 250, Easing.OutElastic); - - animation.Delay(50).ScaleTo(0.75f, 250); - - this.Delay(50).FadeOut(200); + animation.ScaleTo(0.8f) + .Then().ScaleTo(1, 40) + // this is actually correct to match stable; there were overlapping transforms. + .Then().ScaleTo(0.85f) + .Then().ScaleTo(0.7f, 40) + .Then().Delay(100) + .Then().ScaleTo(0.4f, 40, Easing.In); break; } } From 9fa1f60b7d2f0fcae3537148374a5e9214ac907b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 21 Dec 2020 17:31:15 +0900 Subject: [PATCH 442/818] Fix incorrect footer being used --- .../Screens/Multi/RealtimeMultiplayer/RealtimeMatchSubScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSubScreen.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSubScreen.cs index d8b42e065b..cdab1435c0 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSubScreen.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSubScreen.cs @@ -149,7 +149,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer }, new Drawable[] { - new Footer { SelectedItem = { BindTarget = SelectedItem } } + new RealtimeMatchFooter { SelectedItem = { BindTarget = SelectedItem } } } }, RowDimensions = new[] From 83f1350d7d218ba69082bffe0fc7f495b428cf34 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 21 Dec 2020 17:49:10 +0900 Subject: [PATCH 443/818] Fix editor background not being correctly cleaned up on forced exit Closes #11214. Should be pretty obvious why. --- osu.Game/Screens/Edit/Editor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index ca7e5fbf20..223c678fba 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -461,7 +461,7 @@ namespace osu.Game.Screens.Edit if (dialogOverlay == null || dialogOverlay.CurrentDialog is PromptForSaveDialog) { confirmExit(); - return false; + return base.OnExiting(next); } if (isNewBeatmap || HasUnsavedChanges) From d11d754715220e5e84c991d497e91d561126444a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 21 Dec 2020 18:09:37 +0900 Subject: [PATCH 444/818] Increase size of circle display on timeline --- .../Compose/Components/Timeline/TimelineHitObjectBlueprint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index 657c5834b2..ae2a82fa10 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -29,7 +29,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { private const float thickness = 5; private const float shadow_radius = 5; - private const float circle_size = 24; + private const float circle_size = 34; public Action OnDragHandled; From d1be7c23d96ec9efe7d4edaaa87fd43c155af259 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 21 Dec 2020 18:09:56 +0900 Subject: [PATCH 445/818] Increase height of timeline drag area --- .../Compose/Components/Timeline/TimelineBlueprintContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index 2f14c607c2..ead1aa5c62 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -36,7 +36,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline Anchor = Anchor.Centre; Origin = Anchor.Centre; - Height = 0.4f; + Height = 0.6f; AddInternal(new Box { From 423c6158e16bda8c5382de4f99eeabcd776cfb97 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 21 Dec 2020 18:10:11 +0900 Subject: [PATCH 446/818] Highlight timeline drag area when hovered for better visibility --- .../Timeline/TimelineBlueprintContainer.cs | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index ead1aa5c62..1fc529910b 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Framework.Utils; +using osu.Game.Graphics; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts; @@ -25,10 +26,15 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline [Resolved] private EditorBeatmap beatmap { get; set; } + [Resolved] + private OsuColour colours { get; set; } + private DragEvent lastDragEvent; private Bindable placement; private SelectionBlueprint placementBlueprint; + private readonly Box backgroundBox; + public TimelineBlueprintContainer(HitObjectComposer composer) : base(composer) { @@ -38,7 +44,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline Height = 0.6f; - AddInternal(new Box + AddInternal(backgroundBox = new Box { Colour = Color4.Black, RelativeSizeAxes = Axes.Both, @@ -77,6 +83,18 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline protected override Container CreateSelectionBlueprintContainer() => new TimelineSelectionBlueprintContainer { RelativeSizeAxes = Axes.Both }; + protected override bool OnHover(HoverEvent e) + { + backgroundBox.FadeColour(colours.BlueLighter, 120, Easing.OutQuint); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + backgroundBox.FadeColour(Color4.Black, 600, Easing.OutQuint); + base.OnHoverLost(e); + } + protected override void OnDrag(DragEvent e) { handleScrollViaDrag(e); From 0566ed1a9b15a105530ab800865077bc68ab5a9e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 21 Dec 2020 18:38:44 +0900 Subject: [PATCH 447/818] Add button to main menu --- osu.Game/Screens/Menu/ButtonSystem.cs | 11 +++++------ osu.Game/Screens/Menu/MainMenu.cs | 5 +++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 4becdd58cd..0014f6768f 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -42,8 +42,7 @@ namespace osu.Game.Screens.Menu public Action OnBeatmapListing; public Action OnSolo; public Action OnSettings; - public Action OnMulti; - public Action OnChart; + public Action OnMulti; public const float BUTTON_WIDTH = 140f; public const float WEDGE_WIDTH = 20; @@ -124,8 +123,8 @@ namespace osu.Game.Screens.Menu private void load(AudioManager audio, IdleTracker idleTracker, GameHost host) { buttonsPlay.Add(new Button(@"solo", @"button-solo-select", FontAwesome.Solid.User, new Color4(102, 68, 204, 255), () => OnSolo?.Invoke(), WEDGE_WIDTH, Key.P)); - buttonsPlay.Add(new Button(@"multi", @"button-generic-select", FontAwesome.Solid.Users, new Color4(94, 63, 186, 255), onMulti, 0, Key.M)); - buttonsPlay.Add(new Button(@"chart", @"button-generic-select", OsuIcon.Charts, new Color4(80, 53, 160, 255), () => OnChart?.Invoke())); + buttonsPlay.Add(new Button(@"multi", @"button-generic-select", OsuIcon.Charts, new Color4(94, 63, 186, 255), () => onMulti(true), 0, Key.M)); + buttonsPlay.Add(new Button(@"timeshift", @"button-generic-select", FontAwesome.Solid.Users, new Color4(94, 63, 186, 255), () => onMulti(false), 0, Key.L)); buttonsPlay.ForEach(b => b.VisibleState = ButtonSystemState.Play); buttonsTopLevel.Add(new Button(@"play", @"button-play-select", OsuIcon.Logo, new Color4(102, 68, 204, 255), () => State = ButtonSystemState.Play, WEDGE_WIDTH, Key.P)); @@ -154,7 +153,7 @@ namespace osu.Game.Screens.Menu sampleBack = audio.Samples.Get(@"Menu/button-back-select"); } - private void onMulti() + private void onMulti(bool realtime) { if (!api.IsLoggedIn) { @@ -172,7 +171,7 @@ namespace osu.Game.Screens.Menu return; } - OnMulti?.Invoke(); + OnMulti?.Invoke(realtime); } private void updateIdleState(bool isIdle) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index b781c347f0..391bbf8429 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -17,6 +17,8 @@ using osu.Game.Online.API; using osu.Game.Overlays; using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Edit; +using osu.Game.Screens.Multi; +using osu.Game.Screens.Multi.RealtimeMultiplayer; using osu.Game.Screens.Multi.Timeshift; using osu.Game.Screens.Select; @@ -104,7 +106,7 @@ namespace osu.Game.Screens.Menu this.Push(new Editor()); }, OnSolo = onSolo, - OnMulti = delegate { this.Push(new TimeshiftMultiplayer()); }, + OnMulti = realtime => this.Push(realtime ? (Multiplayer)new RealtimeMultiplayer() : new TimeshiftMultiplayer()), OnExit = confirmAndExit, } } @@ -136,7 +138,6 @@ namespace osu.Game.Screens.Menu buttons.OnSettings = () => settings?.ToggleVisibility(); buttons.OnBeatmapListing = () => beatmapListing?.ToggleVisibility(); - buttons.OnChart = () => rankings?.ShowSpotlights(); LoadComponentAsync(background = new BackgroundScreenDefault()); preloadSongSelect(); From 8427ee1b8e6425b2d6e2bc837a737dba2f7e6e85 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 21 Dec 2020 18:42:23 +0900 Subject: [PATCH 448/818] Fix incorrect cached type --- .../RealtimeMultiplayer/RealtimeMultiplayerTestScene.cs | 4 ++-- .../Visual/RealtimeMultiplayer/TestRealtimeRoomContainer.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Tests/Visual/RealtimeMultiplayer/RealtimeMultiplayerTestScene.cs b/osu.Game/Tests/Visual/RealtimeMultiplayer/RealtimeMultiplayerTestScene.cs index aec70d8be4..30bd3ebc32 100644 --- a/osu.Game/Tests/Visual/RealtimeMultiplayer/RealtimeMultiplayerTestScene.cs +++ b/osu.Game/Tests/Visual/RealtimeMultiplayer/RealtimeMultiplayerTestScene.cs @@ -7,8 +7,8 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Online.RealtimeMultiplayer; +using osu.Game.Screens.Multi; using osu.Game.Screens.Multi.Lounge.Components; -using osu.Game.Screens.Multi.RealtimeMultiplayer; namespace osu.Game.Tests.Visual.RealtimeMultiplayer { @@ -17,7 +17,7 @@ namespace osu.Game.Tests.Visual.RealtimeMultiplayer [Cached(typeof(StatefulMultiplayerClient))] public TestRealtimeMultiplayerClient Client { get; } - [Cached(typeof(RealtimeRoomManager))] + [Cached(typeof(IRoomManager))] public TestRealtimeRoomManager RoomManager { get; } [Cached] diff --git a/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeRoomContainer.cs b/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeRoomContainer.cs index aa75968cca..3565d6ac5d 100644 --- a/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeRoomContainer.cs +++ b/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeRoomContainer.cs @@ -6,8 +6,8 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Online.RealtimeMultiplayer; +using osu.Game.Screens.Multi; using osu.Game.Screens.Multi.Lounge.Components; -using osu.Game.Screens.Multi.RealtimeMultiplayer; namespace osu.Game.Tests.Visual.RealtimeMultiplayer { @@ -19,7 +19,7 @@ namespace osu.Game.Tests.Visual.RealtimeMultiplayer [Cached(typeof(StatefulMultiplayerClient))] public readonly TestRealtimeMultiplayerClient Client; - [Cached(typeof(RealtimeRoomManager))] + [Cached(typeof(IRoomManager))] public readonly TestRealtimeRoomManager RoomManager; [Cached] From 56bd3d8a82230fd626fad39c23a40a3fd0729e1a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 21 Dec 2020 18:42:31 +0900 Subject: [PATCH 449/818] Add realtime multiplayer test scene --- .../TestSceneRealtimeMultiplayer.cs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeMultiplayer.cs diff --git a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeMultiplayer.cs b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeMultiplayer.cs new file mode 100644 index 0000000000..80955ca380 --- /dev/null +++ b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeMultiplayer.cs @@ -0,0 +1,23 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Screens.Multi.Components; + +namespace osu.Game.Tests.Visual.RealtimeMultiplayer +{ + public class TestSceneRealtimeMultiplayer : RealtimeMultiplayerTestScene + { + public TestSceneRealtimeMultiplayer() + { + var multi = new TestRealtimeMultiplayer(); + + AddStep("show", () => LoadScreen(multi)); + AddUntilStep("wait for loaded", () => multi.IsLoaded); + } + + private class TestRealtimeMultiplayer : Screens.Multi.RealtimeMultiplayer.RealtimeMultiplayer + { + protected override RoomManager CreateRoomManager() => new TestRealtimeRoomManager(); + } + } +} From f21b4a269fb7620ef6d612f7709c88f35093c3ed Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 21 Dec 2020 18:42:55 +0900 Subject: [PATCH 450/818] Reduce wait length --- .../RealtimeMultiplayer/TestSceneRealtimeMatchSubScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeMatchSubScreen.cs b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeMatchSubScreen.cs index d31364d62e..a059bb1cc0 100644 --- a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeMatchSubScreen.cs +++ b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeMatchSubScreen.cs @@ -71,7 +71,7 @@ namespace osu.Game.Tests.Visual.RealtimeMultiplayer InputManager.Click(MouseButton.Left); }); - AddWaitStep("wait", 500); + AddWaitStep("wait", 10); } } } From 10c2745682db8b3be8f35f81b8ef42345a4f3e44 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Dec 2020 12:01:09 +0900 Subject: [PATCH 451/818] Add region specifications around implicit interface implementations --- osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs | 4 ++++ osu.Game/Beatmaps/BeatmapManager.cs | 4 ++++ osu.Game/Skinning/SkinManager.cs | 4 ++++ osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs | 4 ++++ osu.Game/Tests/Visual/SkinnableTestScene.cs | 4 ++++ 5 files changed, 20 insertions(+) diff --git a/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs b/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs index 36f99c5599..38cb6729c3 100644 --- a/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs +++ b/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs @@ -179,8 +179,12 @@ namespace osu.Game.Tests.Gameplay } } + #region IResourceStorageProvider + public AudioManager AudioManager => Audio; public IResourceStore Files => null; public IResourceStore CreateTextureLoaderStore(IResourceStore underlyingStore) => null; + + #endregion } } diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 3cf691da9a..8a2991af90 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -496,12 +496,16 @@ namespace osu.Game.Beatmaps onlineLookupQueue?.Dispose(); } + #region IResourceStorageProvider + TextureStore IBeatmapResourceProvider.LargeTextureStore => largeTextureStore; ITrackStore IBeatmapResourceProvider.Tracks => trackStore; AudioManager IStorageResourceProvider.AudioManager => audioManager; IResourceStore IStorageResourceProvider.Files => Files.Store; IResourceStore IStorageResourceProvider.CreateTextureLoaderStore(IResourceStore underlyingStore) => host.CreateTextureLoaderStore(underlyingStore); + #endregion + /// /// A dummy WorkingBeatmap for the purpose of retrieving a beatmap for star difficulty calculation. /// diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 2ee940c02d..99c64b13a4 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -175,8 +175,12 @@ namespace osu.Game.Skinning public IBindable GetConfig(TLookup lookup) => CurrentSkin.Value.GetConfig(lookup); + #region IResourceStorageProvider + AudioManager IStorageResourceProvider.AudioManager => audio; IResourceStore IStorageResourceProvider.Files => Files.Store; IResourceStore IStorageResourceProvider.CreateTextureLoaderStore(IResourceStore underlyingStore) => host.CreateTextureLoaderStore(underlyingStore); + + #endregion } } diff --git a/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs b/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs index dd1979a7e3..62814d4ed4 100644 --- a/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs +++ b/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs @@ -123,10 +123,14 @@ namespace osu.Game.Tests.Beatmaps protected void AssertNoLookup(string name) => AddAssert($"\"{name}\" not looked up", () => !beatmapSkinResourceStore.PerformedLookups.Contains(name) && !userSkinResourceStore.PerformedLookups.Contains(name)); + #region IResourceStorageProvider + public AudioManager AudioManager => Audio; public IResourceStore Files => userSkinResourceStore; public IResourceStore CreateTextureLoaderStore(IResourceStore underlyingStore) => null; + #endregion + private class SkinSourceDependencyContainer : IReadOnlyDependencyContainer { public ISkinSource SkinSource; diff --git a/osu.Game/Tests/Visual/SkinnableTestScene.cs b/osu.Game/Tests/Visual/SkinnableTestScene.cs index 3a9c33788a..3d2c68c2ad 100644 --- a/osu.Game/Tests/Visual/SkinnableTestScene.cs +++ b/osu.Game/Tests/Visual/SkinnableTestScene.cs @@ -152,10 +152,14 @@ namespace osu.Game.Tests.Visual protected virtual IBeatmap CreateBeatmapForSkinProvider() => CreateWorkingBeatmap(Ruleset.Value).GetPlayableBeatmap(Ruleset.Value); + #region IResourceStorageProvider + public AudioManager AudioManager => Audio; public IResourceStore Files => null; public IResourceStore CreateTextureLoaderStore(IResourceStore underlyingStore) => host.CreateTextureLoaderStore(underlyingStore); + #endregion + private class OutlineBox : CompositeDrawable { public OutlineBox() From a97a2b2a6637b2ec49ff2430dd7dce1cbd935099 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Dec 2020 12:03:25 +0900 Subject: [PATCH 452/818] Add nullability to BeatmapManager's GameHost reference --- osu.Game/Beatmaps/BeatmapManager.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 8a2991af90..42418e532b 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -72,6 +72,7 @@ namespace osu.Game.Beatmaps private readonly LargeTextureStore largeTextureStore; private readonly ITrackStore trackStore; + [CanBeNull] private readonly GameHost host; [CanBeNull] @@ -502,7 +503,7 @@ namespace osu.Game.Beatmaps ITrackStore IBeatmapResourceProvider.Tracks => trackStore; AudioManager IStorageResourceProvider.AudioManager => audioManager; IResourceStore IStorageResourceProvider.Files => Files.Store; - IResourceStore IStorageResourceProvider.CreateTextureLoaderStore(IResourceStore underlyingStore) => host.CreateTextureLoaderStore(underlyingStore); + IResourceStore IStorageResourceProvider.CreateTextureLoaderStore(IResourceStore underlyingStore) => host?.CreateTextureLoaderStore(underlyingStore); #endregion From 85518b4d9919973a685d2467530064c0518fc605 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Dec 2020 12:06:10 +0900 Subject: [PATCH 453/818] Enforce non-null for BeatmapManager WorkingBeatmap resources --- osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs index 5d298dbccc..62cf29dc03 100644 --- a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Diagnostics.CodeAnalysis; using System.Linq; using osu.Framework.Audio.Track; using osu.Framework.Graphics.Textures; @@ -19,10 +20,11 @@ namespace osu.Game.Beatmaps [ExcludeFromDynamicCompile] private class BeatmapManagerWorkingBeatmap : WorkingBeatmap { + [NotNull] private readonly IBeatmapResourceProvider resources; - public BeatmapManagerWorkingBeatmap(BeatmapInfo beatmapInfo, IBeatmapResourceProvider resources) - : base(beatmapInfo, resources?.AudioManager) + public BeatmapManagerWorkingBeatmap(BeatmapInfo beatmapInfo, [NotNull] IBeatmapResourceProvider resources) + : base(beatmapInfo, resources.AudioManager) { this.resources = resources; } From 13ef097a53933e2e84608911aa6fd5976a73c945 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Dec 2020 12:08:40 +0900 Subject: [PATCH 454/818] Annotate potentially null parameters in protected ctor of LegacySkin --- osu.Game/Skinning/LegacySkin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 5a7ad095f6..e4e5bf2f75 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -58,7 +58,7 @@ namespace osu.Game.Skinning { } - protected LegacySkin(SkinInfo skin, IResourceStore storage, IStorageResourceProvider resources, string filename) + protected LegacySkin(SkinInfo skin, [CanBeNull] IResourceStore storage, [CanBeNull] IStorageResourceProvider resources, string filename) : base(skin) { using (var stream = storage?.GetStream(filename)) From bf39aa5980819bc4c0171251173111e47573fb4e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Dec 2020 12:18:42 +0900 Subject: [PATCH 455/818] Fix incorrectly converted rotation values --- .../Skinning/Legacy/LegacyManiaJudgementPiece.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaJudgementPiece.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaJudgementPiece.cs index e275e1c0ca..5d662c18d3 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaJudgementPiece.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaJudgementPiece.cs @@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy animation.ScaleTo(1.2f).Then().ScaleTo(1, 100, Easing.Out); animation.RotateTo(0); - animation.RotateTo(RNG.NextSingle(-18, 18), 100, Easing.Out); + animation.RotateTo(RNG.NextSingle(-5.73f, 5.73f), 100, Easing.Out); break; default: From 87176edca142a2fbbfe4aea1076902449fda1c79 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Dec 2020 12:52:30 +0900 Subject: [PATCH 456/818] Fix crash when attempting to scale two hitobjects on the same axis --- osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index ec8c68005f..660e1844aa 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -157,10 +157,16 @@ namespace osu.Game.Rulesets.Osu.Edit foreach (var h in hitObjects) { - h.Position = new Vector2( - quad.TopLeft.X + (h.X - quad.TopLeft.X) / quad.Width * (quad.Width + scale.X), - quad.TopLeft.Y + (h.Y - quad.TopLeft.Y) / quad.Height * (quad.Height + scale.Y) - ); + var newPosition = h.Position; + + // guard against no-ops and NaN. + if (scale.X != 0 && quad.Width > 0) + newPosition.X = quad.TopLeft.X + (h.X - quad.TopLeft.X) / quad.Width * (quad.Width + scale.X); + + if (scale.Y != 0 && quad.Height > 0) + newPosition.Y = quad.TopLeft.Y + (h.Y - quad.TopLeft.Y) / quad.Height * (quad.Height + scale.Y); + + h.Position = newPosition; } } From 7c5964fad8471635aa98a4fa1f81ef43ee9d5351 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Dec 2020 13:04:39 +0900 Subject: [PATCH 457/818] Revert window modes to previous code to correctly apply framework restrictions --- osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs | 3 ++- osu.Game/Overlays/Settings/SettingsDropdown.cs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 3df2a78552..3ef60c8fcd 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -59,9 +59,10 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics Children = new Drawable[] { - windowModeDropdown = new SettingsEnumDropdown + windowModeDropdown = new SettingsDropdown { LabelText = "Screen mode", + ItemSource = windowModes, Current = config.GetBindable(FrameworkSetting.WindowMode), }, resolutionSettingsContainer = new Container diff --git a/osu.Game/Overlays/Settings/SettingsDropdown.cs b/osu.Game/Overlays/Settings/SettingsDropdown.cs index e1c6333aa0..1175ddaab8 100644 --- a/osu.Game/Overlays/Settings/SettingsDropdown.cs +++ b/osu.Game/Overlays/Settings/SettingsDropdown.cs @@ -9,7 +9,7 @@ using osu.Game.Graphics.UserInterface; namespace osu.Game.Overlays.Settings { - public abstract class SettingsDropdown : SettingsItem + public class SettingsDropdown : SettingsItem { protected new OsuDropdown Control => (OsuDropdown)base.Control; From dab5924a63c869dc0ce74b3c22e8c797a6485169 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 22 Dec 2020 08:02:15 +0300 Subject: [PATCH 458/818] Fix resolution dropdown not respecting current display changes --- .../Sections/Graphics/LayoutSettings.cs | 103 ++++++++---------- 1 file changed, 45 insertions(+), 58 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 62dc1dc806..ff3b580c7e 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Collections.Generic; using System.Drawing; using System.Linq; using osu.Framework.Allocation; @@ -25,9 +24,13 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics private FillFlowContainer> scalingSettings; + private readonly IBindable currentDisplay = new Bindable(); + private readonly IBindableList windowModes = new BindableList(); + private Bindable scalingMode; private Bindable sizeFullscreen; - private readonly IBindableList windowModes = new BindableList(); + + private readonly BindableList resolutions = new BindableList(new[] { new Size(9999, 9999) }); [Resolved] private OsuGameBase game { get; set; } @@ -53,9 +56,10 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics scalingPositionY = osuConfig.GetBindable(OsuSetting.ScalingPositionY); if (host.Window != null) + { + currentDisplay.BindTo(host.Window.CurrentDisplayBindable); windowModes.BindTo(host.Window.SupportedWindowModes); - - Container resolutionSettingsContainer; + } Children = new Drawable[] { @@ -65,10 +69,12 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics Current = config.GetBindable(FrameworkSetting.WindowMode), ItemSource = windowModes, }, - resolutionSettingsContainer = new Container + resolutionDropdown = new ResolutionSettingsDropdown { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y + LabelText = "Resolution", + ShowsDefaultIndicator = false, + ItemSource = resolutions, + Current = sizeFullscreen }, new SettingsSlider { @@ -126,31 +132,34 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics }, }; - scalingSettings.ForEach(s => bindPreviewEvent(s.Current)); - - var resolutions = getResolutions(); - - if (resolutions.Count > 1) + windowModes.BindCollectionChanged((sender, args) => { - resolutionSettingsContainer.Child = resolutionDropdown = new ResolutionSettingsDropdown - { - LabelText = "Resolution", - ShowsDefaultIndicator = false, - Items = resolutions, - Current = sizeFullscreen - }; + if (windowModes.Count > 1) + windowModeDropdown.Show(); + else + windowModeDropdown.Hide(); + }, true); - windowModeDropdown.Current.BindValueChanged(mode => + windowModeDropdown.Current.ValueChanged += v => updateResolutionDropdown(); + + currentDisplay.BindValueChanged(v => Schedule(() => + { + resolutions.RemoveRange(1, resolutions.Count - 1); + + if (v.NewValue != null) { - if (mode.NewValue == WindowMode.Fullscreen) - { - resolutionDropdown.Show(); - sizeFullscreen.TriggerChange(); - } - else - resolutionDropdown.Hide(); - }, true); - } + resolutions.AddRange(v.NewValue.DisplayModes + .Where(m => m.Size.Width >= 800 && m.Size.Height >= 600) + .OrderByDescending(m => m.Size.Width) + .ThenByDescending(m => m.Size.Height) + .Select(m => m.Size) + .Distinct()); + } + + updateResolutionDropdown(); + }), true); + + scalingSettings.ForEach(s => bindPreviewEvent(s.Current)); scalingMode.BindValueChanged(mode => { @@ -163,17 +172,13 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics scalingSettings.ForEach(s => s.TransferValueOnCommit = mode.NewValue == ScalingMode.Everything); }, true); - windowModes.CollectionChanged += (sender, args) => windowModesChanged(); - - windowModesChanged(); - } - - private void windowModesChanged() - { - if (windowModes.Count > 1) - windowModeDropdown.Show(); - else - windowModeDropdown.Hide(); + void updateResolutionDropdown() + { + if (resolutions.Count > 1 && windowModeDropdown.Current.Value == WindowMode.Fullscreen) + resolutionDropdown.Show(); + else + resolutionDropdown.Hide(); + } } /// @@ -205,24 +210,6 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics preview.Expire(); } - private IReadOnlyList getResolutions() - { - var resolutions = new List { new Size(9999, 9999) }; - var currentDisplay = game.Window?.CurrentDisplayBindable.Value; - - if (currentDisplay != null) - { - resolutions.AddRange(currentDisplay.DisplayModes - .Where(m => m.Size.Width >= 800 && m.Size.Height >= 600) - .OrderByDescending(m => m.Size.Width) - .ThenByDescending(m => m.Size.Height) - .Select(m => m.Size) - .Distinct()); - } - - return resolutions; - } - private class ScalingPreview : ScalingContainer { public ScalingPreview() From dff865f335c8c4c70ed8b4ec7bf1975fc2f52440 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Dec 2020 14:12:02 +0900 Subject: [PATCH 459/818] Tidy up comments, code, and multiple linq enumeration --- osu.Game/OsuGame.cs | 18 ++++++------------ .../Screens/Select/DifficultyRecommender.cs | 2 ++ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index d2c27b766c..d76bd163e1 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -364,25 +364,19 @@ namespace osu.Game // we might even already be at the song if (Beatmap.Value.BeatmapSetInfo.Hash == databasedSet.Hash && (difficultyCriteria?.Invoke(Beatmap.Value.BeatmapInfo) ?? true)) - { return; - } // Find beatmaps that match our predicate. - var beatmaps = databasedSet.Beatmaps.Where(b => difficultyCriteria?.Invoke(b) ?? true); + var beatmaps = databasedSet.Beatmaps.Where(b => difficultyCriteria?.Invoke(b) ?? true).ToList(); // Use all beatmaps if predicate matched nothing - if (!beatmaps.Any()) + if (beatmaps.Count == 0) beatmaps = databasedSet.Beatmaps; - // Try to select recommended beatmap - // This should give us a beatmap from current ruleset if there are any in our matched beatmaps - var selection = DifficultyRecommender.GetRecommendedBeatmap(beatmaps); - // Fallback if a difficulty can't be recommended, maybe we are offline - // First try to find a beatmap in current ruleset - selection ??= beatmaps.FirstOrDefault(b => b.Ruleset.Equals(Ruleset.Value)); - // Otherwise use first beatmap - selection ??= beatmaps.First(); + // Prefer recommended beatmap if recommendations are available, else fallback to a sane selection. + var selection = DifficultyRecommender.GetRecommendedBeatmap(beatmaps) + ?? beatmaps.FirstOrDefault(b => b.Ruleset.Equals(Ruleset.Value)) + ?? beatmaps.First(); Ruleset.Value = selection.Ruleset; Beatmap.Value = BeatmapManager.GetWorkingBeatmap(selection); diff --git a/osu.Game/Screens/Select/DifficultyRecommender.cs b/osu.Game/Screens/Select/DifficultyRecommender.cs index fa48316e9d..3b3fc812e6 100644 --- a/osu.Game/Screens/Select/DifficultyRecommender.cs +++ b/osu.Game/Screens/Select/DifficultyRecommender.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.IEnumerableExtensions; @@ -47,6 +48,7 @@ namespace osu.Game.Screens.Select /// /// A collection of beatmaps to select a difficulty from. /// The recommended difficulty, or null if a recommendation could not be provided. + [CanBeNull] public BeatmapInfo GetRecommendedBeatmap(IEnumerable beatmaps) { if (!recommendedStarDifficulty.Any()) From 626b7615ad86ca6b93b68cf0dd32e5f23b30b03d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Dec 2020 14:23:33 +0900 Subject: [PATCH 460/818] Move and rename some fields for better readability --- osu.Game/OsuGameBase.cs | 6 +++--- osu.Game/Screens/Select/DifficultyRecommender.cs | 13 ++++++++----- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 5fcd6882a6..f6ec541bcb 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -99,6 +99,9 @@ namespace osu.Game [Cached(typeof(IBindable>))] protected readonly Bindable> SelectedMods = new Bindable>(Array.Empty()); + [Cached] + protected readonly DifficultyRecommender DifficultyRecommender = new DifficultyRecommender(); + /// /// Mods available for the current . /// @@ -117,9 +120,6 @@ namespace osu.Game public bool IsDeployedBuild => AssemblyVersion.Major > 0; - [Cached] - protected readonly DifficultyRecommender DifficultyRecommender = new DifficultyRecommender(); - public virtual string Version { get diff --git a/osu.Game/Screens/Select/DifficultyRecommender.cs b/osu.Game/Screens/Select/DifficultyRecommender.cs index 3b3fc812e6..5c031b5d6e 100644 --- a/osu.Game/Screens/Select/DifficultyRecommender.cs +++ b/osu.Game/Screens/Select/DifficultyRecommender.cs @@ -27,7 +27,10 @@ namespace osu.Game.Screens.Select [Resolved] private Bindable ruleset { get; set; } - private int storedUserId; + /// + /// The user for which the last requests were run. + /// + private int? requestedUserId; private readonly Dictionary recommendedStarDifficulty = new Dictionary(); @@ -73,12 +76,12 @@ namespace osu.Game.Screens.Select return beatmap; } - private void calculateRecommendedDifficulties() + private void fetchRecommendedValues() { - if (recommendedStarDifficulty.Any() && api.LocalUser.Value.Id == storedUserId) + if (recommendedStarDifficulty.Count > 0 && api.LocalUser.Value.Id == requestedUserId) return; - storedUserId = api.LocalUser.Value.Id; + requestedUserId = api.LocalUser.Value.Id; // only query API for built-in rulesets rulesets.AvailableRulesets.Where(ruleset => ruleset.ID <= ILegacyRuleset.MAX_LEGACY_RULESET_ID).ForEach(rulesetInfo => @@ -125,7 +128,7 @@ namespace osu.Game.Screens.Select switch (state.NewValue) { case APIState.Online: - calculateRecommendedDifficulties(); + fetchRecommendedValues(); break; } }); From 8cc2ed3faeb362e8c78c8a924b4f8c5889690660 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Dec 2020 14:28:26 +0900 Subject: [PATCH 461/818] Move from OsuGameBase to OsuGame Also moves to a more suitable namespace. --- .../{Screens/Select => Beatmaps}/DifficultyRecommender.cs | 7 +++++-- osu.Game/OsuGame.cs | 7 ++++++- osu.Game/OsuGameBase.cs | 6 ------ 3 files changed, 11 insertions(+), 9 deletions(-) rename osu.Game/{Screens/Select => Beatmaps}/DifficultyRecommender.cs (94%) diff --git a/osu.Game/Screens/Select/DifficultyRecommender.cs b/osu.Game/Beatmaps/DifficultyRecommender.cs similarity index 94% rename from osu.Game/Screens/Select/DifficultyRecommender.cs rename to osu.Game/Beatmaps/DifficultyRecommender.cs index 5c031b5d6e..f870e8f89f 100644 --- a/osu.Game/Screens/Select/DifficultyRecommender.cs +++ b/osu.Game/Beatmaps/DifficultyRecommender.cs @@ -9,13 +9,16 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; -using osu.Game.Beatmaps; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Rulesets; -namespace osu.Game.Screens.Select +namespace osu.Game.Beatmaps { + /// + /// A class which will recommend the most suitable difficulty for the local user from a beatmap set. + /// This requires the user to be logged in, as it sources from the user's online profile. + /// public class DifficultyRecommender : Component { [Resolved] diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index d76bd163e1..bb51c55551 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -80,6 +80,9 @@ namespace osu.Game private BeatmapSetOverlay beatmapSetOverlay; + [Cached] + private readonly DifficultyRecommender difficultyRecommender = new DifficultyRecommender(); + [Cached] private readonly ScreenshotManager screenshotManager = new ScreenshotManager(); @@ -374,7 +377,7 @@ namespace osu.Game beatmaps = databasedSet.Beatmaps; // Prefer recommended beatmap if recommendations are available, else fallback to a sane selection. - var selection = DifficultyRecommender.GetRecommendedBeatmap(beatmaps) + var selection = difficultyRecommender.GetRecommendedBeatmap(beatmaps) ?? beatmaps.FirstOrDefault(b => b.Ruleset.Equals(Ruleset.Value)) ?? beatmaps.First(); @@ -639,6 +642,8 @@ namespace osu.Game GetStableStorage = GetStorageForStableInstall }, Add, true); + loadComponentSingleFile(difficultyRecommender, Add); + loadComponentSingleFile(screenshotManager, Add); // dependency on notification overlay, dependent by settings overlay diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index f6ec541bcb..150569f1dd 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -36,7 +36,6 @@ using osu.Game.Resources; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Scoring; -using osu.Game.Screens.Select; using osu.Game.Skinning; using osuTK.Input; using RuntimeInfo = osu.Framework.RuntimeInfo; @@ -99,9 +98,6 @@ namespace osu.Game [Cached(typeof(IBindable>))] protected readonly Bindable> SelectedMods = new Bindable>(Array.Empty()); - [Cached] - protected readonly DifficultyRecommender DifficultyRecommender = new DifficultyRecommender(); - /// /// Mods available for the current . /// @@ -306,8 +302,6 @@ namespace osu.Game AddInternal(MusicController = new MusicController()); dependencies.CacheAs(MusicController); - Add(DifficultyRecommender); - Ruleset.BindValueChanged(onRulesetChanged); } From df5e1d83bd24fb0311868aa93034f790e7b47758 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Dec 2020 14:36:52 +0900 Subject: [PATCH 462/818] Allow recommender to potentially be null --- osu.Game/Screens/Select/SongSelect.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 2847b31e98..a5252fdc96 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -118,7 +118,7 @@ namespace osu.Game.Screens.Select BleedBottom = Footer.HEIGHT, SelectionChanged = updateSelectedBeatmap, BeatmapSetsChanged = carouselBeatmapsLoaded, - GetRecommendedBeatmap = recommender.GetRecommendedBeatmap, + GetRecommendedBeatmap = s => recommender?.GetRecommendedBeatmap(s), }, c => carouselContainer.Child = c); AddRangeInternal(new Drawable[] From c6be969e336e153d7e59fd5e23a5d3eeb8547f5a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Dec 2020 14:42:12 +0900 Subject: [PATCH 463/818] Remove unnecessary resolved recommender in test --- .../Visual/SongSelect/TestSceneBeatmapRecommendations.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs index 75a33af247..53a956c77c 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; -using osu.Framework.Allocation; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Online.API; @@ -15,7 +14,6 @@ using osu.Game.Rulesets.Catch; using osu.Game.Rulesets.Mania; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Taiko; -using osu.Game.Screens.Select; using osu.Game.Tests.Visual.Navigation; using osu.Game.Users; @@ -23,9 +21,6 @@ namespace osu.Game.Tests.Visual.SongSelect { public class TestSceneBeatmapRecommendations : OsuGameTestScene { - [Resolved] - private DifficultyRecommender recommender { get; set; } - [SetUpSteps] public override void SetUpSteps() { From 3bf670510ad61874b516255562c940da0f10c8b4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 22 Dec 2020 14:55:25 +0900 Subject: [PATCH 464/818] Split into two actions --- osu.Game/Screens/Menu/ButtonSystem.cs | 32 ++++++++++++++++++++++----- osu.Game/Screens/Menu/MainMenu.cs | 4 ++-- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 0014f6768f..b52ab2cb2f 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -42,7 +42,8 @@ namespace osu.Game.Screens.Menu public Action OnBeatmapListing; public Action OnSolo; public Action OnSettings; - public Action OnMulti; + public Action OnMultiplayer; + public Action OnTimeshift; public const float BUTTON_WIDTH = 140f; public const float WEDGE_WIDTH = 20; @@ -123,8 +124,8 @@ namespace osu.Game.Screens.Menu private void load(AudioManager audio, IdleTracker idleTracker, GameHost host) { buttonsPlay.Add(new Button(@"solo", @"button-solo-select", FontAwesome.Solid.User, new Color4(102, 68, 204, 255), () => OnSolo?.Invoke(), WEDGE_WIDTH, Key.P)); - buttonsPlay.Add(new Button(@"multi", @"button-generic-select", OsuIcon.Charts, new Color4(94, 63, 186, 255), () => onMulti(true), 0, Key.M)); - buttonsPlay.Add(new Button(@"timeshift", @"button-generic-select", FontAwesome.Solid.Users, new Color4(94, 63, 186, 255), () => onMulti(false), 0, Key.L)); + buttonsPlay.Add(new Button(@"multi", @"button-generic-select", OsuIcon.Charts, new Color4(94, 63, 186, 255), onMultiplayer, 0, Key.M)); + buttonsPlay.Add(new Button(@"timeshift", @"button-generic-select", FontAwesome.Solid.Users, new Color4(94, 63, 186, 255), onTimeshift, 0, Key.L)); buttonsPlay.ForEach(b => b.VisibleState = ButtonSystemState.Play); buttonsTopLevel.Add(new Button(@"play", @"button-play-select", OsuIcon.Logo, new Color4(102, 68, 204, 255), () => State = ButtonSystemState.Play, WEDGE_WIDTH, Key.P)); @@ -153,7 +154,7 @@ namespace osu.Game.Screens.Menu sampleBack = audio.Samples.Get(@"Menu/button-back-select"); } - private void onMulti(bool realtime) + private void onMultiplayer() { if (!api.IsLoggedIn) { @@ -171,7 +172,28 @@ namespace osu.Game.Screens.Menu return; } - OnMulti?.Invoke(realtime); + OnMultiplayer?.Invoke(); + } + + private void onTimeshift() + { + if (!api.IsLoggedIn) + { + notifications?.Post(new SimpleNotification + { + Text = "You gotta be logged in to multi 'yo!", + Icon = FontAwesome.Solid.Globe, + Activated = () => + { + loginOverlay?.Show(); + return true; + } + }); + + return; + } + + OnTimeshift?.Invoke(); } private void updateIdleState(bool isIdle) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 391bbf8429..fa96ac9c51 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -17,7 +17,6 @@ using osu.Game.Online.API; using osu.Game.Overlays; using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Edit; -using osu.Game.Screens.Multi; using osu.Game.Screens.Multi.RealtimeMultiplayer; using osu.Game.Screens.Multi.Timeshift; using osu.Game.Screens.Select; @@ -106,7 +105,8 @@ namespace osu.Game.Screens.Menu this.Push(new Editor()); }, OnSolo = onSolo, - OnMulti = realtime => this.Push(realtime ? (Multiplayer)new RealtimeMultiplayer() : new TimeshiftMultiplayer()), + OnMultiplayer = () => this.Push(new RealtimeMultiplayer()), + OnTimeshift = () => this.Push(new TimeshiftMultiplayer()), OnExit = confirmAndExit, } } From 807c1ecd1f47386554d4adc160d426e675413711 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Dec 2020 14:57:32 +0900 Subject: [PATCH 465/818] Refactor recommendation iteration code to read better --- osu.Game/Beatmaps/DifficultyRecommender.cs | 52 +++++++--------------- 1 file changed, 16 insertions(+), 36 deletions(-) diff --git a/osu.Game/Beatmaps/DifficultyRecommender.cs b/osu.Game/Beatmaps/DifficultyRecommender.cs index f870e8f89f..340c47d89b 100644 --- a/osu.Game/Beatmaps/DifficultyRecommender.cs +++ b/osu.Game/Beatmaps/DifficultyRecommender.cs @@ -35,7 +35,7 @@ namespace osu.Game.Beatmaps /// private int? requestedUserId; - private readonly Dictionary recommendedStarDifficulty = new Dictionary(); + private readonly Dictionary recommendedDifficultyMapping = new Dictionary(); private readonly IBindable apiState = new Bindable(); @@ -57,31 +57,27 @@ namespace osu.Game.Beatmaps [CanBeNull] public BeatmapInfo GetRecommendedBeatmap(IEnumerable beatmaps) { - if (!recommendedStarDifficulty.Any()) - return null; - - BeatmapInfo beatmap = null; - - foreach (var r in getBestRulesetOrder()) + foreach (var r in orderedRulesets) { - recommendedStarDifficulty.TryGetValue(r, out var stars); + if (!recommendedDifficultyMapping.TryGetValue(r, out var recommendation)) + continue; - beatmap = beatmaps.Where(b => b.Ruleset.Equals(r)).OrderBy(b => + BeatmapInfo beatmap = beatmaps.Where(b => b.Ruleset.Equals(r)).OrderBy(b => { - var difference = b.StarDifficulty - stars; + var difference = b.StarDifficulty - recommendation; return difference >= 0 ? difference * 2 : difference * -1; // prefer easier over harder }).FirstOrDefault(); if (beatmap != null) - break; + return beatmap; } - return beatmap; + return null; } private void fetchRecommendedValues() { - if (recommendedStarDifficulty.Count > 0 && api.LocalUser.Value.Id == requestedUserId) + if (recommendedDifficultyMapping.Count > 0 && api.LocalUser.Value.Id == requestedUserId) return; requestedUserId = api.LocalUser.Value.Id; @@ -94,7 +90,7 @@ namespace osu.Game.Beatmaps req.Success += result => { // algorithm taken from https://github.com/ppy/osu-web/blob/e6e2825516449e3d0f3f5e1852c6bdd3428c3437/app/Models/User.php#L1505 - recommendedStarDifficulty[rulesetInfo] = Math.Pow((double)(result.Statistics.PP ?? 0), 0.4) * 0.195; + recommendedDifficultyMapping[rulesetInfo] = Math.Pow((double)(result.Statistics.PP ?? 0), 0.4) * 0.195; }; api.Queue(req); @@ -102,29 +98,13 @@ namespace osu.Game.Beatmaps } /// - /// Rulesets ordered by highest recommended star difficulty, except currently selected ruleset first + /// Rulesets ordered descending by their respective recommended difficulties. + /// The currently selected ruleset will always be first. /// - private IEnumerable getBestRulesetOrder() - { - IEnumerable bestRulesetOrder = recommendedStarDifficulty.OrderByDescending(pair => pair.Value) - .Select(pair => pair.Key); - - List orderedRulesets; - - if (bestRulesetOrder.Contains(ruleset.Value)) - { - orderedRulesets = bestRulesetOrder.ToList(); - orderedRulesets.Remove(ruleset.Value); - orderedRulesets.Insert(0, ruleset.Value); - } - else - { - orderedRulesets = new List { ruleset.Value }; - orderedRulesets.AddRange(bestRulesetOrder); - } - - return orderedRulesets; - } + private IEnumerable orderedRulesets => + recommendedDifficultyMapping + .OrderByDescending(pair => pair.Value).Select(pair => pair.Key).Where(r => !r.Equals(ruleset.Value)) + .Prepend(ruleset.Value); private void onlineStateChanged(ValueChangedEvent state) => Schedule(() => { From dece41d050d2f063ed3002269475167484b2270c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 22 Dec 2020 14:58:47 +0900 Subject: [PATCH 466/818] Add todo --- osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs index d654496f40..f4e84510bf 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs @@ -14,6 +14,7 @@ using osu.Game.Screens.Ranking; namespace osu.Game.Screens.Multi.RealtimeMultiplayer { + // Todo: The "room" part of TimeshiftPlayer should be split out into an abstract player class to be inherited instead. public class RealtimePlayer : TimeshiftPlayer { protected override bool PauseOnFocusLost => false; From 81e2edc73ff95d47d272c711d1c7034829a68870 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 22 Dec 2020 14:59:11 +0900 Subject: [PATCH 467/818] Use MRE with timeout to wait on match start --- .../RealtimeMultiplayer/RealtimePlayer.cs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs index f4e84510bf..c6d44686b5 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs @@ -6,6 +6,8 @@ using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using osu.Framework.Allocation; +using osu.Framework.Logging; +using osu.Framework.Screens; using osu.Game.Online.Multiplayer; using osu.Game.Online.RealtimeMultiplayer; using osu.Game.Scoring; @@ -26,7 +28,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer private StatefulMultiplayerClient client { get; set; } private readonly TaskCompletionSource resultsReady = new TaskCompletionSource(); - private bool started; + private readonly ManualResetEventSlim startedEvent = new ManualResetEventSlim(); public RealtimePlayer(PlaylistItem playlistItem) : base(playlistItem, false) @@ -43,11 +45,19 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer client.ResultsReady += onResultsReady; client.ChangeState(MultiplayerUserState.Loaded); - while (!started) - Thread.Sleep(100); + if (!startedEvent.Wait(TimeSpan.FromSeconds(30))) + { + Logger.Log("Failed to start the multiplayer match in time.", LoggingTarget.Runtime, LogLevel.Important); + + Schedule(() => + { + ValidForResume = false; + this.Exit(); + }); + } } - private void onMatchStarted() => started = true; + private void onMatchStarted() => startedEvent.Set(); private void onResultsReady() => resultsReady.SetResult(true); From 3f966386ed563bd43c241068cf3bdd8ec10f8541 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Dec 2020 15:15:32 +0900 Subject: [PATCH 468/818] Fix compile time failure due to potentially null connection --- .../Online/RealtimeMultiplayer/RealtimeMultiplayerClient.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Online/RealtimeMultiplayer/RealtimeMultiplayerClient.cs b/osu.Game/Online/RealtimeMultiplayer/RealtimeMultiplayerClient.cs index 6f18e1c922..75bb578a29 100644 --- a/osu.Game/Online/RealtimeMultiplayer/RealtimeMultiplayerClient.cs +++ b/osu.Game/Online/RealtimeMultiplayer/RealtimeMultiplayerClient.cs @@ -4,6 +4,7 @@ #nullable enable using System; +using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.SignalR.Client; @@ -99,6 +100,8 @@ namespace osu.Game.Online.RealtimeMultiplayer { try { + Debug.Assert(connection != null); + // reconnect on any failure await connection.StartAsync(); Logger.Log("Multiplayer client connected!", LoggingTarget.Network); From 85e93c5ddec913aa48515814867e89688c92e518 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Dec 2020 15:22:27 +0900 Subject: [PATCH 469/818] Fix main menu multiplayer icons being back to front --- osu.Game/Screens/Menu/ButtonSystem.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index b52ab2cb2f..badfa3f693 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -124,8 +124,8 @@ namespace osu.Game.Screens.Menu private void load(AudioManager audio, IdleTracker idleTracker, GameHost host) { buttonsPlay.Add(new Button(@"solo", @"button-solo-select", FontAwesome.Solid.User, new Color4(102, 68, 204, 255), () => OnSolo?.Invoke(), WEDGE_WIDTH, Key.P)); - buttonsPlay.Add(new Button(@"multi", @"button-generic-select", OsuIcon.Charts, new Color4(94, 63, 186, 255), onMultiplayer, 0, Key.M)); - buttonsPlay.Add(new Button(@"timeshift", @"button-generic-select", FontAwesome.Solid.Users, new Color4(94, 63, 186, 255), onTimeshift, 0, Key.L)); + buttonsPlay.Add(new Button(@"multi", @"button-generic-select", FontAwesome.Solid.Users, new Color4(94, 63, 186, 255), onMultiplayer, 0, Key.M)); + buttonsPlay.Add(new Button(@"timeshift", @"button-generic-select", OsuIcon.Charts, new Color4(94, 63, 186, 255), onTimeshift, 0, Key.L)); buttonsPlay.ForEach(b => b.VisibleState = ButtonSystemState.Play); buttonsTopLevel.Add(new Button(@"play", @"button-play-select", OsuIcon.Logo, new Color4(102, 68, 204, 255), () => State = ButtonSystemState.Play, WEDGE_WIDTH, Key.P)); From ab90db7c8df2ab778d3b84d964eae62823e41443 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 22 Dec 2020 15:27:49 +0900 Subject: [PATCH 470/818] Fix stuck lounge on join failure --- .../Multi/RealtimeMultiplayer/RealtimeRoomManager.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs index e0fca3ce4c..0c9b58a0c4 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs @@ -38,10 +38,10 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer } public override void CreateRoom(Room room, Action onSuccess = null, Action onError = null) - => base.CreateRoom(room, r => joinMultiplayerRoom(r, onSuccess), onError); + => base.CreateRoom(room, r => joinMultiplayerRoom(r, onSuccess, onError), onError); public override void JoinRoom(Room room, Action onSuccess = null, Action onError = null) - => base.JoinRoom(room, r => joinMultiplayerRoom(r, onSuccess), onError); + => base.JoinRoom(room, r => joinMultiplayerRoom(r, onSuccess, onError), onError); public override void PartRoom() { @@ -62,7 +62,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer }); } - private void joinMultiplayerRoom(Room room, Action onSuccess = null) + private void joinMultiplayerRoom(Room room, Action onSuccess = null, Action onError = null) { Debug.Assert(room.RoomID.Value != null); @@ -73,6 +73,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer PartRoom(); if (t.Exception != null) Logger.Error(t.Exception, "Failed to join multiplayer room."); + onError?.Invoke(t.Exception?.ToString() ?? string.Empty); }, TaskContinuationOptions.NotOnRanToCompletion); } From 27e64bdb342aa33aa74da56fa155960662fc3a1e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 22 Dec 2020 15:31:07 +0900 Subject: [PATCH 471/818] Schedule callback continuations --- .../Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs index 0c9b58a0c4..7ce031e0e9 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs @@ -67,13 +67,13 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer Debug.Assert(room.RoomID.Value != null); var joinTask = multiplayerClient.JoinRoom(room); - joinTask.ContinueWith(_ => onSuccess?.Invoke(room), TaskContinuationOptions.OnlyOnRanToCompletion); + joinTask.ContinueWith(_ => Schedule(() => onSuccess?.Invoke(room)), TaskContinuationOptions.OnlyOnRanToCompletion); joinTask.ContinueWith(t => { PartRoom(); if (t.Exception != null) Logger.Error(t.Exception, "Failed to join multiplayer room."); - onError?.Invoke(t.Exception?.ToString() ?? string.Empty); + Schedule(() => onError?.Invoke(t.Exception?.ToString() ?? string.Empty)); }, TaskContinuationOptions.NotOnRanToCompletion); } From 8201fa8e3463024ab3155738df934bc1cf967822 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Dec 2020 15:51:24 +0900 Subject: [PATCH 472/818] Split out common implementation and private classes in MatchSettingsOverlay --- .../TestSceneMatchSettingsOverlay.cs | 10 +- .../TestSceneTimeshiftRoomSubScreen.cs | 2 +- .../Match/Components/MatchSettingsOverlay.cs | 185 +++++++++--------- .../Match/RealtimeMatchSettingsOverlay.cs | 95 +-------- .../Multi/Timeshift/TimeshiftRoomSubScreen.cs | 5 +- 5 files changed, 108 insertions(+), 189 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs index 234374ee2b..90eef26b20 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs @@ -109,14 +109,14 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("error not displayed", () => !settings.ErrorText.IsPresent); } - private class TestRoomSettings : MatchSettingsOverlay + private class TestRoomSettings : TimeshiftMatchSettingsOverlay { - public TriangleButton ApplyButton => Settings.ApplyButton; + public TriangleButton ApplyButton => ((MatchSettings)Settings).ApplyButton; - public OsuTextBox NameField => Settings.NameField; - public OsuDropdown DurationField => Settings.DurationField; + public OsuTextBox NameField => ((MatchSettings)Settings).NameField; + public OsuDropdown DurationField => ((MatchSettings)Settings).DurationField; - public OsuSpriteText ErrorText => Settings.ErrorText; + public OsuSpriteText ErrorText => ((MatchSettings)Settings).ErrorText; } private class TestRoomManager : IRoomManager diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftRoomSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftRoomSubScreen.cs index f56f78ce89..bbd7d84081 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftRoomSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftRoomSubScreen.cs @@ -120,7 +120,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("create room", () => { - InputManager.MoveMouseTo(match.ChildrenOfType().Single()); + InputManager.MoveMouseTo(match.ChildrenOfType().Single()); InputManager.Click(MouseButton.Left); }); diff --git a/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs b/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs index 1859e8db8a..d837ceaccf 100644 --- a/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs +++ b/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs @@ -21,20 +21,108 @@ using osuTK.Graphics; namespace osu.Game.Screens.Multi.Match.Components { - public class MatchSettingsOverlay : FocusedOverlayContainer + public abstract class MatchSettingsOverlay : FocusedOverlayContainer { - private const float transition_duration = 350; - private const float field_padding = 45; + protected const float TRANSITION_DURATION = 350; + protected const float FIELD_PADDING = 45; - public Action EditPlaylist; - - protected MatchSettings Settings { get; private set; } + protected MultiplayerComposite Settings { get; set; } [BackgroundDependencyLoader] private void load() { Masking = true; + } + protected override void PopIn() + { + Settings.MoveToY(0, TRANSITION_DURATION, Easing.OutQuint); + } + + protected override void PopOut() + { + Settings.MoveToY(-1, TRANSITION_DURATION, Easing.InSine); + } + + protected class SettingsTextBox : OsuTextBox + { + [BackgroundDependencyLoader] + private void load() + { + BackgroundUnfocused = Color4.Black; + BackgroundFocused = Color4.Black; + } + } + + protected class SettingsNumberTextBox : SettingsTextBox + { + protected override bool CanAddCharacter(char character) => char.IsNumber(character); + } + + protected class SettingsPasswordTextBox : OsuPasswordTextBox + { + [BackgroundDependencyLoader] + private void load() + { + BackgroundUnfocused = Color4.Black; + BackgroundFocused = Color4.Black; + } + } + + protected class SectionContainer : FillFlowContainer
+ { + public SectionContainer() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Width = 0.5f; + Direction = FillDirection.Vertical; + Spacing = new Vector2(FIELD_PADDING); + } + } + + protected class Section : Container + { + private readonly Container content; + + protected override Container Content => content; + + public Section(string title) + { + AutoSizeAxes = Axes.Y; + RelativeSizeAxes = Axes.X; + + InternalChild = new FillFlowContainer + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Direction = FillDirection.Vertical, + Spacing = new Vector2(5), + Children = new Drawable[] + { + new OsuSpriteText + { + Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 12), + Text = title.ToUpper(), + }, + content = new Container + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + }, + }, + }; + } + } + } + + public class TimeshiftMatchSettingsOverlay : MatchSettingsOverlay + { + public Action EditPlaylist; + + [BackgroundDependencyLoader] + private void load() + { Child = Settings = new MatchSettings { RelativeSizeAxes = Axes.Both, @@ -43,16 +131,6 @@ namespace osu.Game.Screens.Multi.Match.Components }; } - protected override void PopIn() - { - Settings.MoveToY(0, transition_duration, Easing.OutQuint); - } - - protected override void PopOut() - { - Settings.MoveToY(-1, transition_duration, Easing.InSine); - } - protected class MatchSettings : MultiplayerComposite { private const float disabled_alpha = 0.2f; @@ -126,7 +204,7 @@ namespace osu.Game.Screens.Multi.Match.Components { new SectionContainer { - Padding = new MarginPadding { Right = field_padding / 2 }, + Padding = new MarginPadding { Right = FIELD_PADDING / 2 }, Children = new[] { new Section("Room name") @@ -216,7 +294,7 @@ namespace osu.Game.Screens.Multi.Match.Components { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, - Padding = new MarginPadding { Left = field_padding / 2 }, + Padding = new MarginPadding { Left = FIELD_PADDING / 2 }, Children = new[] { new Section("Playlist") @@ -379,77 +457,6 @@ namespace osu.Game.Screens.Multi.Match.Components } } - private class SettingsTextBox : OsuTextBox - { - [BackgroundDependencyLoader] - private void load() - { - BackgroundUnfocused = Color4.Black; - BackgroundFocused = Color4.Black; - } - } - - private class SettingsNumberTextBox : SettingsTextBox - { - protected override bool CanAddCharacter(char character) => char.IsNumber(character); - } - - private class SettingsPasswordTextBox : OsuPasswordTextBox - { - [BackgroundDependencyLoader] - private void load() - { - BackgroundUnfocused = Color4.Black; - BackgroundFocused = Color4.Black; - } - } - - private class SectionContainer : FillFlowContainer
- { - public SectionContainer() - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - Width = 0.5f; - Direction = FillDirection.Vertical; - Spacing = new Vector2(field_padding); - } - } - - private class Section : Container - { - private readonly Container content; - - protected override Container Content => content; - - public Section(string title) - { - AutoSizeAxes = Axes.Y; - RelativeSizeAxes = Axes.X; - - InternalChild = new FillFlowContainer - { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Direction = FillDirection.Vertical, - Spacing = new Vector2(5), - Children = new Drawable[] - { - new OsuSpriteText - { - Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 12), - Text = title.ToUpper(), - }, - content = new Container - { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - }, - }, - }; - } - } - public class CreateRoomButton : TriangleButton { public CreateRoomButton() diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeMatchSettingsOverlay.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeMatchSettingsOverlay.cs index 924c736472..5bd388cceb 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeMatchSettingsOverlay.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeMatchSettingsOverlay.cs @@ -19,22 +19,14 @@ using osu.Game.Overlays; using osu.Game.Rulesets; using osu.Game.Screens.Multi.Match.Components; using osuTK; -using osuTK.Graphics; namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Match { - public class RealtimeMatchSettingsOverlay : FocusedOverlayContainer + public class RealtimeMatchSettingsOverlay : MatchSettingsOverlay { - private const float transition_duration = 350; - private const float field_padding = 45; - - protected MatchSettings Settings { get; private set; } - [BackgroundDependencyLoader] private void load() { - Masking = true; - Child = Settings = new MatchSettings { RelativeSizeAxes = Axes.Both, @@ -43,16 +35,6 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Match }; } - protected override void PopIn() - { - Settings.MoveToY(0, transition_duration, Easing.OutQuint); - } - - protected override void PopOut() - { - Settings.MoveToY(-1, transition_duration, Easing.InSine); - } - protected class MatchSettings : MultiplayerComposite { private const float disabled_alpha = 0.2f; @@ -143,7 +125,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Match { new SectionContainer { - Padding = new MarginPadding { Right = field_padding / 2 }, + Padding = new MarginPadding { Right = FIELD_PADDING / 2 }, Children = new[] { new Section("Room name") @@ -192,7 +174,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Match { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, - Padding = new MarginPadding { Left = field_padding / 2 }, + Padding = new MarginPadding { Left = FIELD_PADDING / 2 }, Children = new[] { new Section("Max participants") @@ -347,77 +329,6 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Match } } - private class SettingsTextBox : OsuTextBox - { - [BackgroundDependencyLoader] - private void load() - { - BackgroundUnfocused = Color4.Black; - BackgroundFocused = Color4.Black; - } - } - - private class SettingsNumberTextBox : SettingsTextBox - { - protected override bool CanAddCharacter(char character) => char.IsNumber(character); - } - - private class SettingsPasswordTextBox : OsuPasswordTextBox - { - [BackgroundDependencyLoader] - private void load() - { - BackgroundUnfocused = Color4.Black; - BackgroundFocused = Color4.Black; - } - } - - private class SectionContainer : FillFlowContainer
- { - public SectionContainer() - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - Width = 0.5f; - Direction = FillDirection.Vertical; - Spacing = new Vector2(field_padding); - } - } - - private class Section : Container - { - private readonly Container content; - - protected override Container Content => content; - - public Section(string title) - { - AutoSizeAxes = Axes.Y; - RelativeSizeAxes = Axes.X; - - InternalChild = new FillFlowContainer - { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Direction = FillDirection.Vertical, - Spacing = new Vector2(5), - Children = new Drawable[] - { - new OsuSpriteText - { - Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 12), - Text = title.ToUpper(), - }, - content = new Container - { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - }, - }, - }; - } - } - public class CreateOrUpdateButton : TriangleButton { [Resolved(typeof(Room), nameof(Room.RoomID))] diff --git a/osu.Game/Screens/Multi/Timeshift/TimeshiftRoomSubScreen.cs b/osu.Game/Screens/Multi/Timeshift/TimeshiftRoomSubScreen.cs index 433a980d60..fa901179e9 100644 --- a/osu.Game/Screens/Multi/Timeshift/TimeshiftRoomSubScreen.cs +++ b/osu.Game/Screens/Multi/Timeshift/TimeshiftRoomSubScreen.cs @@ -18,6 +18,7 @@ using osu.Game.Screens.Multi.Ranking; using osu.Game.Screens.Play; using osu.Game.Screens.Select; using osu.Game.Users; +using Footer = osu.Game.Screens.Multi.Match.Components.Footer; namespace osu.Game.Screens.Multi.Timeshift { @@ -175,7 +176,7 @@ namespace osu.Game.Screens.Multi.Timeshift }, new Drawable[] { - new Match.Components.Footer + new Footer { OnStart = onStart, SelectedItem = { BindTarget = SelectedItem } @@ -188,7 +189,7 @@ namespace osu.Game.Screens.Multi.Timeshift new Dimension(GridSizeMode.AutoSize), } }, - settingsOverlay = new MatchSettingsOverlay + settingsOverlay = new TimeshiftMatchSettingsOverlay { RelativeSizeAxes = Axes.Both, EditPlaylist = () => this.Push(new MatchSongSelect()), From 17d924c7554d561d028ed761681c6e4703286267 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Dec 2020 15:52:47 +0900 Subject: [PATCH 473/818] Move timeshift settings overlay to correct namespace --- .../TestSceneMatchSettingsOverlay.cs | 2 +- .../Match/Components/MatchSettingsOverlay.cs | 377 ----------------- .../TimeshiftMatchSettingsOverlay.cs | 391 ++++++++++++++++++ 3 files changed, 392 insertions(+), 378 deletions(-) create mode 100644 osu.Game/Screens/Multi/Timeshift/TimeshiftMatchSettingsOverlay.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs index 90eef26b20..1fcae9c709 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs @@ -11,7 +11,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Multiplayer; using osu.Game.Screens.Multi; -using osu.Game.Screens.Multi.Match.Components; +using osu.Game.Screens.Multi.Timeshift; namespace osu.Game.Tests.Visual.Multiplayer { diff --git a/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs b/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs index d837ceaccf..0bb56d0cdf 100644 --- a/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs +++ b/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs @@ -1,21 +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; -using System.Collections.Specialized; -using Humanizer; using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; -using osu.Game.Online.Multiplayer; -using osu.Game.Overlays; using osuTK; using osuTK.Graphics; @@ -115,372 +106,4 @@ namespace osu.Game.Screens.Multi.Match.Components } } } - - public class TimeshiftMatchSettingsOverlay : MatchSettingsOverlay - { - public Action EditPlaylist; - - [BackgroundDependencyLoader] - private void load() - { - Child = Settings = new MatchSettings - { - RelativeSizeAxes = Axes.Both, - RelativePositionAxes = Axes.Y, - EditPlaylist = () => EditPlaylist?.Invoke() - }; - } - - protected class MatchSettings : MultiplayerComposite - { - private const float disabled_alpha = 0.2f; - - public Action EditPlaylist; - - public OsuTextBox NameField, MaxParticipantsField; - public OsuDropdown DurationField; - public RoomAvailabilityPicker AvailabilityPicker; - public GameTypePicker TypePicker; - public TriangleButton ApplyButton; - - public OsuSpriteText ErrorText; - - private OsuSpriteText typeLabel; - private LoadingLayer loadingLayer; - private DrawableRoomPlaylist playlist; - private OsuSpriteText playlistLength; - - [Resolved(CanBeNull = true)] - private IRoomManager manager { get; set; } - - [Resolved] - private Bindable currentRoom { get; set; } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - Container dimContent; - - InternalChildren = new Drawable[] - { - dimContent = new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex(@"28242d"), - }, - new GridContainer - { - RelativeSizeAxes = Axes.Both, - RowDimensions = new[] - { - new Dimension(GridSizeMode.Distributed), - new Dimension(GridSizeMode.AutoSize), - }, - Content = new[] - { - new Drawable[] - { - new OsuScrollContainer - { - Padding = new MarginPadding - { - Horizontal = OsuScreen.HORIZONTAL_OVERFLOW_PADDING, - Vertical = 10 - }, - RelativeSizeAxes = Axes.Both, - Children = new[] - { - new Container - { - Padding = new MarginPadding { Horizontal = WaveOverlayContainer.WIDTH_PADDING }, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] - { - new SectionContainer - { - Padding = new MarginPadding { Right = FIELD_PADDING / 2 }, - Children = new[] - { - new Section("Room name") - { - Child = NameField = new SettingsTextBox - { - RelativeSizeAxes = Axes.X, - TabbableContentContainer = this, - LengthLimit = 100 - }, - }, - new Section("Duration") - { - Child = DurationField = new DurationDropdown - { - RelativeSizeAxes = Axes.X, - Items = new[] - { - TimeSpan.FromMinutes(30), - TimeSpan.FromHours(1), - TimeSpan.FromHours(2), - TimeSpan.FromHours(4), - TimeSpan.FromHours(8), - TimeSpan.FromHours(12), - //TimeSpan.FromHours(16), - TimeSpan.FromHours(24), - TimeSpan.FromDays(3), - TimeSpan.FromDays(7) - } - } - }, - new Section("Room visibility") - { - Alpha = disabled_alpha, - Child = AvailabilityPicker = new RoomAvailabilityPicker - { - Enabled = { Value = false } - }, - }, - new Section("Game type") - { - Alpha = disabled_alpha, - Child = new FillFlowContainer - { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Direction = FillDirection.Vertical, - Spacing = new Vector2(7), - Children = new Drawable[] - { - TypePicker = new GameTypePicker - { - RelativeSizeAxes = Axes.X, - Enabled = { Value = false } - }, - typeLabel = new OsuSpriteText - { - Font = OsuFont.GetFont(size: 14), - Colour = colours.Yellow - }, - }, - }, - }, - new Section("Max participants") - { - Alpha = disabled_alpha, - Child = MaxParticipantsField = new SettingsNumberTextBox - { - RelativeSizeAxes = Axes.X, - TabbableContentContainer = this, - ReadOnly = true, - }, - }, - new Section("Password (optional)") - { - Alpha = disabled_alpha, - Child = new SettingsPasswordTextBox - { - RelativeSizeAxes = Axes.X, - TabbableContentContainer = this, - ReadOnly = true, - }, - }, - }, - }, - new SectionContainer - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Padding = new MarginPadding { Left = FIELD_PADDING / 2 }, - Children = new[] - { - new Section("Playlist") - { - Child = new GridContainer - { - RelativeSizeAxes = Axes.X, - Height = 300, - Content = new[] - { - new Drawable[] - { - playlist = new DrawableRoomPlaylist(true, true) { RelativeSizeAxes = Axes.Both } - }, - new Drawable[] - { - playlistLength = new OsuSpriteText - { - Margin = new MarginPadding { Vertical = 5 }, - Colour = colours.Yellow, - Font = OsuFont.GetFont(size: 12), - } - }, - new Drawable[] - { - new PurpleTriangleButton - { - RelativeSizeAxes = Axes.X, - Height = 40, - Text = "Edit playlist", - Action = () => EditPlaylist?.Invoke() - } - } - }, - RowDimensions = new[] - { - new Dimension(), - new Dimension(GridSizeMode.AutoSize), - new Dimension(GridSizeMode.AutoSize), - } - } - }, - }, - }, - }, - } - }, - }, - }, - new Drawable[] - { - new Container - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Y = 2, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex(@"28242d").Darken(0.5f).Opacity(1f), - }, - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 20), - Margin = new MarginPadding { Vertical = 20 }, - Padding = new MarginPadding { Horizontal = OsuScreen.HORIZONTAL_OVERFLOW_PADDING }, - Children = new Drawable[] - { - ApplyButton = new CreateRoomButton - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Size = new Vector2(230, 55), - Enabled = { Value = false }, - Action = apply, - }, - ErrorText = new OsuSpriteText - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Alpha = 0, - Depth = 1, - Colour = colours.RedDark - } - } - } - } - } - } - } - }, - } - }, - loadingLayer = new LoadingLayer(dimContent) - }; - - TypePicker.Current.BindValueChanged(type => typeLabel.Text = type.NewValue?.Name ?? string.Empty, true); - RoomName.BindValueChanged(name => NameField.Text = name.NewValue, true); - Availability.BindValueChanged(availability => AvailabilityPicker.Current.Value = availability.NewValue, true); - Type.BindValueChanged(type => TypePicker.Current.Value = type.NewValue, true); - MaxParticipants.BindValueChanged(count => MaxParticipantsField.Text = count.NewValue?.ToString(), true); - Duration.BindValueChanged(duration => DurationField.Current.Value = duration.NewValue ?? TimeSpan.FromMinutes(30), true); - - playlist.Items.BindTo(Playlist); - Playlist.BindCollectionChanged(onPlaylistChanged, true); - } - - protected override void Update() - { - base.Update(); - - ApplyButton.Enabled.Value = hasValidSettings; - } - - private void onPlaylistChanged(object sender, NotifyCollectionChangedEventArgs e) => - playlistLength.Text = $"Length: {Playlist.GetTotalDuration()}"; - - private bool hasValidSettings => RoomID.Value == null && NameField.Text.Length > 0 && Playlist.Count > 0; - - private void apply() - { - if (!ApplyButton.Enabled.Value) - return; - - hideError(); - - RoomName.Value = NameField.Text; - Availability.Value = AvailabilityPicker.Current.Value; - Type.Value = TypePicker.Current.Value; - - if (int.TryParse(MaxParticipantsField.Text, out int max)) - MaxParticipants.Value = max; - else - MaxParticipants.Value = null; - - Duration.Value = DurationField.Current.Value; - - manager?.CreateRoom(currentRoom.Value, onSuccess, onError); - - loadingLayer.Show(); - } - - private void hideError() => ErrorText.FadeOut(50); - - private void onSuccess(Room room) => loadingLayer.Hide(); - - private void onError(string text) - { - ErrorText.Text = text; - ErrorText.FadeIn(50); - - loadingLayer.Hide(); - } - } - - public class CreateRoomButton : TriangleButton - { - public CreateRoomButton() - { - Text = "Create"; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - BackgroundColour = colours.Yellow; - Triangles.ColourLight = colours.YellowLight; - Triangles.ColourDark = colours.YellowDark; - } - } - - private class DurationDropdown : OsuDropdown - { - public DurationDropdown() - { - Menu.MaxHeight = 100; - } - - protected override string GenerateItemText(TimeSpan item) => item.Humanize(); - } - } } diff --git a/osu.Game/Screens/Multi/Timeshift/TimeshiftMatchSettingsOverlay.cs b/osu.Game/Screens/Multi/Timeshift/TimeshiftMatchSettingsOverlay.cs new file mode 100644 index 0000000000..7e1e9894d8 --- /dev/null +++ b/osu.Game/Screens/Multi/Timeshift/TimeshiftMatchSettingsOverlay.cs @@ -0,0 +1,391 @@ +// 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.Specialized; +using Humanizer; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.Multiplayer; +using osu.Game.Overlays; +using osu.Game.Screens.Multi.Match.Components; +using osuTK; + +namespace osu.Game.Screens.Multi.Timeshift +{ + public class TimeshiftMatchSettingsOverlay : MatchSettingsOverlay + { + public Action EditPlaylist; + + [BackgroundDependencyLoader] + private void load() + { + Child = Settings = new MatchSettings + { + RelativeSizeAxes = Axes.Both, + RelativePositionAxes = Axes.Y, + EditPlaylist = () => EditPlaylist?.Invoke() + }; + } + + protected class MatchSettings : MultiplayerComposite + { + private const float disabled_alpha = 0.2f; + + public Action EditPlaylist; + + public OsuTextBox NameField, MaxParticipantsField; + public OsuDropdown DurationField; + public RoomAvailabilityPicker AvailabilityPicker; + public GameTypePicker TypePicker; + public TriangleButton ApplyButton; + + public OsuSpriteText ErrorText; + + private OsuSpriteText typeLabel; + private LoadingLayer loadingLayer; + private DrawableRoomPlaylist playlist; + private OsuSpriteText playlistLength; + + [Resolved(CanBeNull = true)] + private IRoomManager manager { get; set; } + + [Resolved] + private Bindable currentRoom { get; set; } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Container dimContent; + + InternalChildren = new Drawable[] + { + dimContent = new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4Extensions.FromHex(@"28242d"), + }, + new GridContainer + { + RelativeSizeAxes = Axes.Both, + RowDimensions = new[] + { + new Dimension(GridSizeMode.Distributed), + new Dimension(GridSizeMode.AutoSize), + }, + Content = new[] + { + new Drawable[] + { + new OsuScrollContainer + { + Padding = new MarginPadding + { + Horizontal = OsuScreen.HORIZONTAL_OVERFLOW_PADDING, + Vertical = 10 + }, + RelativeSizeAxes = Axes.Both, + Children = new[] + { + new Container + { + Padding = new MarginPadding { Horizontal = WaveOverlayContainer.WIDTH_PADDING }, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new SectionContainer + { + Padding = new MarginPadding { Right = FIELD_PADDING / 2 }, + Children = new[] + { + new Section("Room name") + { + Child = NameField = new SettingsTextBox + { + RelativeSizeAxes = Axes.X, + TabbableContentContainer = this, + LengthLimit = 100 + }, + }, + new Section("Duration") + { + Child = DurationField = new DurationDropdown + { + RelativeSizeAxes = Axes.X, + Items = new[] + { + TimeSpan.FromMinutes(30), + TimeSpan.FromHours(1), + TimeSpan.FromHours(2), + TimeSpan.FromHours(4), + TimeSpan.FromHours(8), + TimeSpan.FromHours(12), + //TimeSpan.FromHours(16), + TimeSpan.FromHours(24), + TimeSpan.FromDays(3), + TimeSpan.FromDays(7) + } + } + }, + new Section("Room visibility") + { + Alpha = disabled_alpha, + Child = AvailabilityPicker = new RoomAvailabilityPicker + { + Enabled = { Value = false } + }, + }, + new Section("Game type") + { + Alpha = disabled_alpha, + Child = new FillFlowContainer + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Direction = FillDirection.Vertical, + Spacing = new Vector2(7), + Children = new Drawable[] + { + TypePicker = new GameTypePicker + { + RelativeSizeAxes = Axes.X, + Enabled = { Value = false } + }, + typeLabel = new OsuSpriteText + { + Font = OsuFont.GetFont(size: 14), + Colour = colours.Yellow + }, + }, + }, + }, + new Section("Max participants") + { + Alpha = disabled_alpha, + Child = MaxParticipantsField = new SettingsNumberTextBox + { + RelativeSizeAxes = Axes.X, + TabbableContentContainer = this, + ReadOnly = true, + }, + }, + new Section("Password (optional)") + { + Alpha = disabled_alpha, + Child = new SettingsPasswordTextBox + { + RelativeSizeAxes = Axes.X, + TabbableContentContainer = this, + ReadOnly = true, + }, + }, + }, + }, + new SectionContainer + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Padding = new MarginPadding { Left = FIELD_PADDING / 2 }, + Children = new[] + { + new Section("Playlist") + { + Child = new GridContainer + { + RelativeSizeAxes = Axes.X, + Height = 300, + Content = new[] + { + new Drawable[] + { + playlist = new DrawableRoomPlaylist(true, true) { RelativeSizeAxes = Axes.Both } + }, + new Drawable[] + { + playlistLength = new OsuSpriteText + { + Margin = new MarginPadding { Vertical = 5 }, + Colour = colours.Yellow, + Font = OsuFont.GetFont(size: 12), + } + }, + new Drawable[] + { + new PurpleTriangleButton + { + RelativeSizeAxes = Axes.X, + Height = 40, + Text = "Edit playlist", + Action = () => EditPlaylist?.Invoke() + } + } + }, + RowDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.AutoSize), + } + } + }, + }, + }, + }, + } + }, + }, + }, + new Drawable[] + { + new Container + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Y = 2, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4Extensions.FromHex(@"28242d").Darken(0.5f).Opacity(1f), + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 20), + Margin = new MarginPadding { Vertical = 20 }, + Padding = new MarginPadding { Horizontal = OsuScreen.HORIZONTAL_OVERFLOW_PADDING }, + Children = new Drawable[] + { + ApplyButton = new CreateRoomButton + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Size = new Vector2(230, 55), + Enabled = { Value = false }, + Action = apply, + }, + ErrorText = new OsuSpriteText + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Alpha = 0, + Depth = 1, + Colour = colours.RedDark + } + } + } + } + } + } + } + }, + } + }, + loadingLayer = new LoadingLayer(dimContent) + }; + + TypePicker.Current.BindValueChanged(type => typeLabel.Text = type.NewValue?.Name ?? string.Empty, true); + RoomName.BindValueChanged(name => NameField.Text = name.NewValue, true); + Availability.BindValueChanged(availability => AvailabilityPicker.Current.Value = availability.NewValue, true); + Type.BindValueChanged(type => TypePicker.Current.Value = type.NewValue, true); + MaxParticipants.BindValueChanged(count => MaxParticipantsField.Text = count.NewValue?.ToString(), true); + Duration.BindValueChanged(duration => DurationField.Current.Value = duration.NewValue ?? TimeSpan.FromMinutes(30), true); + + playlist.Items.BindTo(Playlist); + Playlist.BindCollectionChanged(onPlaylistChanged, true); + } + + protected override void Update() + { + base.Update(); + + ApplyButton.Enabled.Value = hasValidSettings; + } + + private void onPlaylistChanged(object sender, NotifyCollectionChangedEventArgs e) => + playlistLength.Text = $"Length: {Playlist.GetTotalDuration()}"; + + private bool hasValidSettings => RoomID.Value == null && NameField.Text.Length > 0 && Playlist.Count > 0; + + private void apply() + { + if (!ApplyButton.Enabled.Value) + return; + + hideError(); + + RoomName.Value = NameField.Text; + Availability.Value = AvailabilityPicker.Current.Value; + Type.Value = TypePicker.Current.Value; + + if (int.TryParse(MaxParticipantsField.Text, out int max)) + MaxParticipants.Value = max; + else + MaxParticipants.Value = null; + + Duration.Value = DurationField.Current.Value; + + manager?.CreateRoom(currentRoom.Value, onSuccess, onError); + + loadingLayer.Show(); + } + + private void hideError() => ErrorText.FadeOut(50); + + private void onSuccess(Room room) => loadingLayer.Hide(); + + private void onError(string text) + { + ErrorText.Text = text; + ErrorText.FadeIn(50); + + loadingLayer.Hide(); + } + } + + public class CreateRoomButton : TriangleButton + { + public CreateRoomButton() + { + Text = "Create"; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + BackgroundColour = colours.Yellow; + Triangles.ColourLight = colours.YellowLight; + Triangles.ColourDark = colours.YellowDark; + } + } + + private class DurationDropdown : OsuDropdown + { + public DurationDropdown() + { + Menu.MaxHeight = 100; + } + + protected override string GenerateItemText(TimeSpan item) => item.Humanize(); + } + } +} From 34421c92327c00fb3574141271f6d6094f5f94da Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Dec 2020 15:58:57 +0900 Subject: [PATCH 474/818] 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 2a08cb7867..fc01f9bf1d 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 960959f367..cbf9f6f1bd 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -26,7 +26,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index a5bcb91c74..adbcc0ef1c 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -88,7 +88,7 @@ - + From 3cf889b7c5c4d8f4e8382ab5245c30a551b1f7e1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Dec 2020 16:19:19 +0900 Subject: [PATCH 475/818] Fix some errors being completely ignored --- osu.Game/Screens/Multi/Components/RoomManager.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Screens/Multi/Components/RoomManager.cs b/osu.Game/Screens/Multi/Components/RoomManager.cs index 482ee5492c..f78d0d979e 100644 --- a/osu.Game/Screens/Multi/Components/RoomManager.cs +++ b/osu.Game/Screens/Multi/Components/RoomManager.cs @@ -76,10 +76,7 @@ namespace osu.Game.Screens.Multi.Components req.Failure += exception => { - if (req.Result != null) - onError?.Invoke(req.Result.Error); - else - Logger.Log($"Failed to create the room: {exception}", level: LogLevel.Important); + onError?.Invoke(req.Result?.Error ?? exception.Message); }; api.Queue(req); From 2d7174d99cc6e24b30f3266a32cd86fe9c735c34 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 22 Dec 2020 16:23:06 +0900 Subject: [PATCH 476/818] Add padding to song select --- .../Multi/RealtimeMultiplayer/RealtimeMatchSongSelect.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSongSelect.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSongSelect.cs index 157216e9bb..b2ae5402a1 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSongSelect.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSongSelect.cs @@ -5,6 +5,7 @@ using System.Linq; using Humanizer; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Graphics; using osu.Framework.Screens; using osu.Game.Online.Multiplayer; using osu.Game.Online.RealtimeMultiplayer; @@ -24,6 +25,11 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer [Resolved] private StatefulMultiplayerClient client { get; set; } + public RealtimeMatchSongSelect() + { + Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING }; + } + protected override bool OnStart() { var item = new PlaylistItem(); From 12876d7fb642500d7c5db31827a718a0af7b1798 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Dec 2020 16:50:30 +0900 Subject: [PATCH 477/818] Add very basic error handling on ChangeSettings calls --- .../StatefulMultiplayerClient.cs | 6 +++--- .../Match/RealtimeMatchSettingsOverlay.cs | 9 +++++++-- .../RealtimeMultiplayer/RealtimeMatchSongSelect.cs | 13 +++++++++++-- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs index f56499f040..4ebd648689 100644 --- a/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs @@ -127,10 +127,10 @@ namespace osu.Game.Online.RealtimeMultiplayer /// /// The new room name, if any. /// The new room playlist item, if any. - public void ChangeSettings(Optional name = default, Optional item = default) + public Task ChangeSettings(Optional name = default, Optional item = default) { if (Room == null) - return; + throw new InvalidOperationException("Must be joined to a match to change settings."); // A dummy playlist item filled with the current room settings (except mods). var existingPlaylistItem = new PlaylistItem @@ -146,7 +146,7 @@ namespace osu.Game.Online.RealtimeMultiplayer RulesetID = Room.Settings.RulesetID }; - ChangeSettings(new MultiplayerRoomSettings + return ChangeSettings(new MultiplayerRoomSettings { Name = name.GetOr(Room.Settings.Name), BeatmapID = item.GetOr(existingPlaylistItem).BeatmapID, diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeMatchSettingsOverlay.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeMatchSettingsOverlay.cs index 5bd388cceb..3e495b490f 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeMatchSettingsOverlay.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeMatchSettingsOverlay.cs @@ -294,8 +294,13 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Match // Otherwise, update the room directly in preparation for it to be submitted to the API on match creation. if (client.Room != null) { - client.ChangeSettings(name: NameField.Text); - onSuccess(currentRoom.Value); + client.ChangeSettings(name: NameField.Text).ContinueWith(t => Schedule(() => + { + if (t.IsCompletedSuccessfully) + onSuccess(currentRoom.Value); + else + onError(t.Exception?.Message ?? "Error changing settings."); + })); } else { diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSongSelect.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSongSelect.cs index b2ae5402a1..0feeed6fe5 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSongSelect.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSongSelect.cs @@ -6,6 +6,7 @@ using Humanizer; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Logging; using osu.Framework.Screens; using osu.Game.Online.Multiplayer; using osu.Game.Online.RealtimeMultiplayer; @@ -43,14 +44,22 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer // If the client is already in a room, update via the client. // Otherwise, update the playlist directly in preparation for it to be submitted to the API on match creation. if (client.Room != null) - client.ChangeSettings(item: item); + { + client.ChangeSettings(item: item).ContinueWith(t => Schedule(() => + { + if (t.IsCompletedSuccessfully) + this.Exit(); + else + Logger.Log($"Could not use current beatmap ({t.Exception?.Message})", level: LogLevel.Important); + })); + } else { playlist.Clear(); playlist.Add(item); + this.Exit(); } - this.Exit(); return true; } From 30357a9447a077e09b7e0a11006fd48a156890a5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Dec 2020 17:08:02 +0900 Subject: [PATCH 478/818] Add loading layer to multi song select to show during settings confirmation --- .../RealtimeMatchSongSelect.cs | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSongSelect.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSongSelect.cs index 0feeed6fe5..d4308d361c 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSongSelect.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSongSelect.cs @@ -2,12 +2,14 @@ // See the LICENCE file in the repository root for full licence text. using System.Linq; +using System.Threading; using Humanizer; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Logging; using osu.Framework.Screens; +using osu.Game.Graphics.UserInterface; using osu.Game.Online.Multiplayer; using osu.Game.Online.RealtimeMultiplayer; using osu.Game.Screens.Select; @@ -26,11 +28,19 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer [Resolved] private StatefulMultiplayerClient client { get; set; } + private LoadingLayer loadingLayer; + public RealtimeMatchSongSelect() { Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING }; } + [BackgroundDependencyLoader] + private void load() + { + AddInternal(loadingLayer = new LoadingLayer(Carousel)); + } + protected override bool OnStart() { var item = new PlaylistItem(); @@ -45,13 +55,20 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer // Otherwise, update the playlist directly in preparation for it to be submitted to the API on match creation. if (client.Room != null) { - client.ChangeSettings(item: item).ContinueWith(t => Schedule(() => + loadingLayer.Show(); + + client.ChangeSettings(item: item).ContinueWith(t => { - if (t.IsCompletedSuccessfully) - this.Exit(); - else - Logger.Log($"Could not use current beatmap ({t.Exception?.Message})", level: LogLevel.Important); - })); + return Schedule(() => + { + loadingLayer.Hide(); + + if (t.IsCompletedSuccessfully) + this.Exit(); + else + Logger.Log($"Could not use current beatmap ({t.Exception?.Message})", level: LogLevel.Important); + }); + }); } else { From 59734229ff94bd907fd640374ce081c62581cb61 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Dec 2020 17:21:53 +0900 Subject: [PATCH 479/818] Remove unused using --- .../Screens/Multi/RealtimeMultiplayer/RealtimeMatchSongSelect.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSongSelect.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSongSelect.cs index d4308d361c..f3dab93089 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSongSelect.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSongSelect.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System.Linq; -using System.Threading; using Humanizer; using osu.Framework.Allocation; using osu.Framework.Bindables; From 3d5783a0eab3e1360dad85f9b6b5183c56467be9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Dec 2020 17:34:51 +0900 Subject: [PATCH 480/818] Improve variable names --- .../Sections/Graphics/LayoutSettings.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 6ec5d1f03a..b722fd4137 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -140,20 +140,20 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics windowModeDropdown.Hide(); }, true); - windowModeDropdown.Current.ValueChanged += v => updateResolutionDropdown(); + windowModeDropdown.Current.ValueChanged += _ => updateResolutionDropdown(); - currentDisplay.BindValueChanged(v => Schedule(() => + currentDisplay.BindValueChanged(display => Schedule(() => { resolutions.RemoveRange(1, resolutions.Count - 1); - if (v.NewValue != null) + if (display.NewValue != null) { - resolutions.AddRange(v.NewValue.DisplayModes - .Where(m => m.Size.Width >= 800 && m.Size.Height >= 600) - .OrderByDescending(m => m.Size.Width) - .ThenByDescending(m => m.Size.Height) - .Select(m => m.Size) - .Distinct()); + resolutions.AddRange(display.NewValue.DisplayModes + .Where(m => m.Size.Width >= 800 && m.Size.Height >= 600) + .OrderByDescending(m => m.Size.Width) + .ThenByDescending(m => m.Size.Height) + .Select(m => m.Size) + .Distinct()); } updateResolutionDropdown(); From 4f02928601b75ed162b6527b728cce8d971182cb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Dec 2020 17:36:56 +0900 Subject: [PATCH 481/818] Change sorting to better handle portrait screens --- .../Overlays/Settings/Sections/Graphics/LayoutSettings.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index b722fd4137..3d3b543d70 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Drawing; using System.Linq; using osu.Framework.Allocation; @@ -150,8 +151,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics { resolutions.AddRange(display.NewValue.DisplayModes .Where(m => m.Size.Width >= 800 && m.Size.Height >= 600) - .OrderByDescending(m => m.Size.Width) - .ThenByDescending(m => m.Size.Height) + .OrderByDescending(m => Math.Max(m.Size.Height, m.Size.Width)) .Select(m => m.Size) .Distinct()); } From ce806dd880f58b03e18fdf80d76ce9f5c67c86e1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Dec 2020 18:25:45 +0900 Subject: [PATCH 482/818] Replace the ready mark display with a state display, showing all participant states --- .../Multiplayer/TestSceneParticipantsList.cs | 1 + .../TestSceneParticipantsList.cs | 11 +- .../Participants/ParticipantPanel.cs | 10 +- .../Participants/ReadyMark.cs | 51 ------- .../Participants/StateDisplay.cs | 128 ++++++++++++++++++ 5 files changed, 138 insertions(+), 63 deletions(-) delete mode 100644 osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ReadyMark.cs create mode 100644 osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/StateDisplay.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneParticipantsList.cs index 7bbec7d30e..360aa22af9 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneParticipantsList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneParticipantsList.cs @@ -20,6 +20,7 @@ namespace osu.Game.Tests.Visual.Multiplayer Room.RecentParticipants.Add(new User { Username = "peppy", + CurrentModeRank = 1234, Id = 2 }); } diff --git a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneParticipantsList.cs b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneParticipantsList.cs index 8c997e9e32..35ea0d3813 100644 --- a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneParticipantsList.cs +++ b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneParticipantsList.cs @@ -6,6 +6,7 @@ using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Testing; +using osu.Framework.Utils; using osu.Game.Online.RealtimeMultiplayer; using osu.Game.Screens.Multi.RealtimeMultiplayer.Participants; using osu.Game.Users; @@ -65,13 +66,13 @@ namespace osu.Game.Tests.Visual.RealtimeMultiplayer [Test] public void TestToggleReadyState() { - AddAssert("ready mark invisible", () => !this.ChildrenOfType().Single().IsPresent); + AddAssert("ready mark invisible", () => !this.ChildrenOfType().Single().IsPresent); AddStep("make user ready", () => Client.ChangeState(MultiplayerUserState.Ready)); - AddUntilStep("ready mark visible", () => this.ChildrenOfType().Single().IsPresent); + AddUntilStep("ready mark visible", () => this.ChildrenOfType().Single().IsPresent); AddStep("make user idle", () => Client.ChangeState(MultiplayerUserState.Idle)); - AddUntilStep("ready mark invisible", () => !this.ChildrenOfType().Single().IsPresent); + AddUntilStep("ready mark invisible", () => !this.ChildrenOfType().Single().IsPresent); } [Test] @@ -104,11 +105,11 @@ namespace osu.Game.Tests.Visual.RealtimeMultiplayer { Id = i, Username = $"User {i}", + CurrentModeRank = RNG.Next(1, 100000), CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", }); - if (i % 2 == 0) - Client.ChangeUserState(i, MultiplayerUserState.Ready); + Client.ChangeUserState(i, (MultiplayerUserState)RNG.Next(0, (int)MultiplayerUserState.Results)); } }); } diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantPanel.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantPanel.cs index 002849a275..a4ff2ce346 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantPanel.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantPanel.cs @@ -30,7 +30,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Participants [Resolved] private IAPIProvider api { get; set; } - private ReadyMark readyMark; + private StateDisplay userStateDisplay; private SpriteIcon crown; public ParticipantPanel(MultiplayerRoomUser user) @@ -122,12 +122,11 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Participants } } }, - readyMark = new ReadyMark + userStateDisplay = new StateDisplay { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, Margin = new MarginPadding { Right = 10 }, - Alpha = 0 } } } @@ -144,10 +143,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Participants const double fade_time = 50; - if (User.State == MultiplayerUserState.Ready) - readyMark.FadeIn(fade_time); - else - readyMark.FadeOut(fade_time); + userStateDisplay.Status = User.State; if (Room.Host?.Equals(User) == true) crown.FadeIn(fade_time); diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ReadyMark.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ReadyMark.cs deleted file mode 100644 index df49d9342e..0000000000 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ReadyMark.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; -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.Screens.Multi.RealtimeMultiplayer.Participants -{ - public class ReadyMark : CompositeDrawable - { - public ReadyMark() - { - AutoSizeAxes = Axes.Both; - } - - [BackgroundDependencyLoader] - private void load() - { - InternalChild = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Spacing = new Vector2(5), - Children = new Drawable[] - { - new OsuSpriteText - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Font = OsuFont.GetFont(weight: FontWeight.Regular, size: 12), - Text = "ready", - Colour = Color4Extensions.FromHex("#DDFFFF") - }, - new SpriteIcon - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Icon = FontAwesome.Solid.CheckCircle, - Size = new Vector2(12), - Colour = Color4Extensions.FromHex("#AADD00") - } - } - }; - } - } -} diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/StateDisplay.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/StateDisplay.cs new file mode 100644 index 0000000000..db93525217 --- /dev/null +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/StateDisplay.cs @@ -0,0 +1,128 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Online.RealtimeMultiplayer; +using osuTK; + +namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Participants +{ + public class StateDisplay : CompositeDrawable + { + public StateDisplay() + { + AutoSizeAxes = Axes.Both; + } + + private MultiplayerUserState status; + + private OsuSpriteText text; + private SpriteIcon icon; + + private const double fade_time = 50; + + public MultiplayerUserState Status + { + set + { + if (value == status) + return; + + status = value; + + if (IsLoaded) + updateStatus(); + } + } + + [BackgroundDependencyLoader] + private void load() + { + InternalChild = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Spacing = new Vector2(5), + Children = new Drawable[] + { + text = new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Font = OsuFont.GetFont(weight: FontWeight.Regular, size: 12), + Colour = Color4Extensions.FromHex("#DDFFFF") + }, + icon = new SpriteIcon + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Icon = FontAwesome.Solid.CheckCircle, + Size = new Vector2(12), + } + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + updateStatus(); + } + + [Resolved] + private OsuColour colours { get; set; } + + private void updateStatus() + { + switch (status) + { + default: + this.FadeOut(fade_time); + return; + + case MultiplayerUserState.Ready: + text.Text = "ready"; + icon.Icon = FontAwesome.Solid.CheckCircle; + icon.Colour = Color4Extensions.FromHex("#AADD00"); + break; + + case MultiplayerUserState.WaitingForLoad: + text.Text = "loading"; + icon.Icon = FontAwesome.Solid.PauseCircle; + icon.Colour = colours.Yellow; + break; + + case MultiplayerUserState.Loaded: + text.Text = "loaded"; + icon.Icon = FontAwesome.Solid.DotCircle; + icon.Colour = colours.YellowLight; + break; + + case MultiplayerUserState.Playing: + text.Text = "playing"; + icon.Icon = FontAwesome.Solid.PlayCircle; + icon.Colour = colours.BlueLight; + break; + + case MultiplayerUserState.FinishedPlay: + text.Text = "results pending"; + icon.Icon = FontAwesome.Solid.ArrowAltCircleUp; + icon.Colour = colours.BlueLighter; + break; + + case MultiplayerUserState.Results: + text.Text = "results"; + icon.Icon = FontAwesome.Solid.ArrowAltCircleUp; + icon.Colour = colours.BlueLighter; + break; + } + + this.FadeIn(fade_time); + } + } +} From 23bf9c372c71d15049595955c22bda7df3939dfd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Dec 2020 18:26:39 +0900 Subject: [PATCH 483/818] Fix naming conflict with test scenes --- ...articipantsList.cs => TestSceneTimeshiftParticipantsList.cs} | 2 +- ...sList.cs => TestSceneRealtimeMultiplayerParticipantsList.cs} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename osu.Game.Tests/Visual/Multiplayer/{TestSceneParticipantsList.cs => TestSceneTimeshiftParticipantsList.cs} (95%) rename osu.Game.Tests/Visual/RealtimeMultiplayer/{TestSceneParticipantsList.cs => TestSceneRealtimeMultiplayerParticipantsList.cs} (97%) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftParticipantsList.cs similarity index 95% rename from osu.Game.Tests/Visual/Multiplayer/TestSceneParticipantsList.cs rename to osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftParticipantsList.cs index 360aa22af9..efc3be032c 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneParticipantsList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftParticipantsList.cs @@ -8,7 +8,7 @@ using osu.Game.Users; namespace osu.Game.Tests.Visual.Multiplayer { - public class TestSceneParticipantsList : MultiplayerTestScene + public class TestSceneTimeshiftParticipantsList : MultiplayerTestScene { [SetUp] public new void Setup() => Schedule(() => diff --git a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneParticipantsList.cs b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeMultiplayerParticipantsList.cs similarity index 97% rename from osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneParticipantsList.cs rename to osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeMultiplayerParticipantsList.cs index 35ea0d3813..7fd31906f7 100644 --- a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneParticipantsList.cs +++ b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeMultiplayerParticipantsList.cs @@ -14,7 +14,7 @@ using osuTK; namespace osu.Game.Tests.Visual.RealtimeMultiplayer { - public class TestSceneParticipantsList : RealtimeMultiplayerTestScene + public class TestSceneRealtimeMultiplayerParticipantsList : RealtimeMultiplayerTestScene { [SetUp] public new void Setup() => Schedule(() => From 6517acc510885301d7bc0c3cf82d7638f7fc63cf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Dec 2020 19:09:59 +0900 Subject: [PATCH 484/818] Add leaderboard display to realtime player --- .../RealtimeMultiplayer/RealtimePlayer.cs | 23 +++++++++++++++++++ osu.Game/Screens/Play/HUDOverlay.cs | 7 +++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs index c6d44686b5..453e7e6140 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; +using System.Linq; using System.Threading; using System.Threading.Tasks; using osu.Framework.Allocation; @@ -12,7 +13,9 @@ using osu.Game.Online.Multiplayer; using osu.Game.Online.RealtimeMultiplayer; using osu.Game.Scoring; using osu.Game.Screens.Multi.Play; +using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Ranking; +using osuTK; namespace osu.Game.Screens.Multi.RealtimeMultiplayer { @@ -30,6 +33,8 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer private readonly TaskCompletionSource resultsReady = new TaskCompletionSource(); private readonly ManualResetEventSlim startedEvent = new ManualResetEventSlim(); + private MultiplayerGameplayLeaderboard leaderboard; + public RealtimePlayer(PlaylistItem playlistItem) : base(playlistItem, false) { @@ -55,6 +60,24 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer this.Exit(); }); } + + Debug.Assert(client.Room != null); + + int[] userIds = client.Room.Users.Where(u => u.State >= MultiplayerUserState.WaitingForLoad).Select(u => u.UserID).ToArray(); + + // todo: this should be implemented via a custom HUD implementation, and correctly masked to the main content area. + LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(ScoreProcessor, userIds), HUDOverlay.Add); + } + + protected override void Update() + { + base.Update(); + + const float padding = 44; // enough margin to avoid the hit error display. + + leaderboard.Position = new Vector2( + padding, + padding + HUDOverlay.TopScoringElementsHeight); } private void onMatchStarted() => startedEvent.Set(); diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 50195d571c..3dffab8102 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -28,6 +28,11 @@ namespace osu.Game.Screens.Play public const Easing FADE_EASING = Easing.Out; + /// + /// The total height of all the top of screen scoring elements. + /// + public float TopScoringElementsHeight { get; private set; } + public readonly KeyCounterDisplay KeyCounter; public readonly SkinnableComboCounter ComboCounter; public readonly SkinnableScoreCounter ScoreCounter; @@ -209,7 +214,7 @@ namespace osu.Game.Screens.Play // HACK: for now align with the accuracy counter. // this is done for the sake of hacky legacy skins which extend the health bar to take up the full screen area. // it only works with the default skin due to padding offsetting it *just enough* to coexist. - topRightElements.Y = ToLocalSpace(AccuracyCounter.Drawable.ScreenSpaceDrawQuad.BottomRight).Y; + topRightElements.Y = TopScoringElementsHeight = ToLocalSpace(AccuracyCounter.Drawable.ScreenSpaceDrawQuad.BottomRight).Y; bottomRightElements.Y = -Progress.Height; } From e3483147e2f682000121bd1534f494efbd42e6c8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 22 Dec 2020 13:53:01 +0300 Subject: [PATCH 485/818] Move track looping logic into subscreens --- osu.Game/Screens/Multi/Match/RoomSubScreen.cs | 52 ++++++++++++++++++ osu.Game/Screens/Multi/Multiplayer.cs | 53 ------------------- 2 files changed, 52 insertions(+), 53 deletions(-) diff --git a/osu.Game/Screens/Multi/Match/RoomSubScreen.cs b/osu.Game/Screens/Multi/Match/RoomSubScreen.cs index 0cc9a4354e..21316a98ce 100644 --- a/osu.Game/Screens/Multi/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/RoomSubScreen.cs @@ -9,6 +9,7 @@ using osu.Framework.Screens; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Online.Multiplayer; +using osu.Game.Overlays; using osu.Game.Rulesets.Mods; namespace osu.Game.Screens.Multi.Match @@ -23,6 +24,9 @@ namespace osu.Game.Screens.Multi.Match [Resolved(typeof(Room), nameof(Room.Playlist))] protected BindableList Playlist { get; private set; } + [Resolved] + private MusicController music { get; set; } + [Resolved] private BeatmapManager beatmapManager { get; set; } @@ -61,6 +65,30 @@ namespace osu.Game.Screens.Multi.Match var localBeatmap = beatmap == null ? null : beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == beatmap.OnlineBeatmapID); Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap); + if (this.IsCurrentScreen()) + applyTrackLooping(); + } + + public override void OnEntering(IScreen last) + { + base.OnEntering(last); + + music?.EnsurePlayingSomething(); + applyTrackLooping(); + } + + public override void OnSuspending(IScreen next) + { + cancelTrackLooping(); + base.OnSuspending(next); + } + + public override void OnResuming(IScreen last) + { + base.OnResuming(last); + + music?.EnsurePlayingSomething(); + applyTrackLooping(); } public override bool OnExiting(IScreen next) @@ -68,7 +96,31 @@ namespace osu.Game.Screens.Multi.Match RoomManager?.PartRoom(); Mods.Value = Array.Empty(); + cancelTrackLooping(); + return base.OnExiting(next); } + + private void applyTrackLooping() + { + var track = Beatmap.Value?.Track; + + if (track != null) + { + track.RestartPoint = Beatmap.Value.Metadata.PreviewTime; + track.Looping = true; + } + } + + private void cancelTrackLooping() + { + var track = Beatmap?.Value?.Track; + + if (track != null) + { + track.Looping = false; + track.RestartPoint = 0; + } + } } } diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index eae779421d..de2e0d58c9 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.cs @@ -9,7 +9,6 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Screens; -using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; @@ -198,8 +197,6 @@ namespace osu.Game.Screens.Multi { this.FadeIn(); waves.Show(); - - beginHandlingTrack(); } public override void OnResuming(IScreen last) @@ -209,8 +206,6 @@ namespace osu.Game.Screens.Multi base.OnResuming(last); - beginHandlingTrack(); - UpdatePollingRate(isIdle.Value); } @@ -219,8 +214,6 @@ namespace osu.Game.Screens.Multi this.ScaleTo(1.1f, 250, Easing.InSine); this.FadeOut(250); - endHandlingTrack(); - UpdatePollingRate(isIdle.Value); } @@ -235,8 +228,6 @@ namespace osu.Game.Screens.Multi if (screenStack.CurrentScreen != null) loungeSubScreen.MakeCurrent(); - endHandlingTrack(); - base.OnExiting(next); return false; } @@ -275,17 +266,6 @@ namespace osu.Game.Screens.Multi /// The created . protected virtual Room CreateNewRoom() => new Room { Name = { Value = $"{api.LocalUser}'s awesome room" } }; - private void beginHandlingTrack() - { - Beatmap.BindValueChanged(updateTrack, true); - } - - private void endHandlingTrack() - { - cancelLooping(); - Beatmap.ValueChanged -= updateTrack; - } - private void screenPushed(IScreen lastScreen, IScreen newScreen) { subScreenChanged(lastScreen, newScreen); @@ -322,43 +302,10 @@ namespace osu.Game.Screens.Multi UpdatePollingRate(isIdle.Value); createButton.FadeTo(newScreen is LoungeSubScreen ? 1 : 0, 200); - - updateTrack(); } protected IScreen CurrentSubScreen => screenStack.CurrentScreen; - private void updateTrack(ValueChangedEvent _ = null) - { - if (screenStack.CurrentScreen is RoomSubScreen) - { - var track = Beatmap.Value?.Track; - - if (track != null) - { - track.RestartPoint = Beatmap.Value.Metadata.PreviewTime; - track.Looping = true; - - music?.EnsurePlayingSomething(); - } - } - else - { - cancelLooping(); - } - } - - private void cancelLooping() - { - var track = Beatmap?.Value?.Track; - - if (track != null) - { - track.Looping = false; - track.RestartPoint = 0; - } - } - protected abstract RoomManager CreateRoomManager(); protected abstract LoungeSubScreen CreateLounge(); From 91d5c53643a7489066a23b36e6865d67c621ddf2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 22 Dec 2020 16:36:17 +0300 Subject: [PATCH 486/818] Add method for checking room joinability --- osu.Game.Tests/Visual/Multiplayer/TestRoomManager.cs | 2 ++ .../Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs | 2 ++ .../Visual/Multiplayer/TestSceneTimeshiftRoomSubScreen.cs | 2 ++ osu.Game/Screens/Multi/Components/RoomManager.cs | 2 ++ osu.Game/Screens/Multi/IRoomManager.cs | 6 ++++++ .../Multi/RealtimeMultiplayer/RealtimeRoomManager.cs | 3 +++ 6 files changed, 17 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestRoomManager.cs b/osu.Game.Tests/Visual/Multiplayer/TestRoomManager.cs index 9dd4aea4bd..ea8fb4ef49 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestRoomManager.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestRoomManager.cs @@ -24,6 +24,8 @@ namespace osu.Game.Tests.Visual.Multiplayer public void CreateRoom(Room room, Action onSuccess = null, Action onError = null) => Rooms.Add(room); + public bool CanJoinRoom(Room room) => true; + public void JoinRoom(Room room, Action onSuccess = null, Action onError = null) { } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs index 1fcae9c709..55dbd1a7c6 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs @@ -146,6 +146,8 @@ namespace osu.Game.Tests.Visual.Multiplayer onSuccess?.Invoke(room); } + public bool CanJoinRoom(Room room) => true; + public void JoinRoom(Room room, Action onSuccess = null, Action onError = null) => throw new NotImplementedException(); public void PartRoom() => throw new NotImplementedException(); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftRoomSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftRoomSubScreen.cs index bbd7d84081..8fdb5d4093 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftRoomSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftRoomSubScreen.cs @@ -161,6 +161,8 @@ namespace osu.Game.Tests.Visual.Multiplayer onSuccess?.Invoke(room); } + public bool CanJoinRoom(Room room) => true; + public void JoinRoom(Room room, Action onSuccess = null, Action onError = null) => onSuccess?.Invoke(room); public void PartRoom() diff --git a/osu.Game/Screens/Multi/Components/RoomManager.cs b/osu.Game/Screens/Multi/Components/RoomManager.cs index f78d0d979e..3fcfe1df25 100644 --- a/osu.Game/Screens/Multi/Components/RoomManager.cs +++ b/osu.Game/Screens/Multi/Components/RoomManager.cs @@ -82,6 +82,8 @@ namespace osu.Game.Screens.Multi.Components api.Queue(req); } + public virtual bool CanJoinRoom(Room room) => true; + private JoinRoomRequest currentJoinRoomRequest; public virtual void JoinRoom(Room room, Action onSuccess = null, Action onError = null) diff --git a/osu.Game/Screens/Multi/IRoomManager.cs b/osu.Game/Screens/Multi/IRoomManager.cs index 630e3af91c..a1cff129a9 100644 --- a/osu.Game/Screens/Multi/IRoomManager.cs +++ b/osu.Game/Screens/Multi/IRoomManager.cs @@ -34,6 +34,12 @@ namespace osu.Game.Screens.Multi /// An action to be invoked if an error occurred. void CreateRoom(Room room, Action onSuccess = null, Action onError = null); + /// + /// Whether the provided can be joined. + /// + /// The to check for. + bool CanJoinRoom(Room room); + /// /// Joins a . /// diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs index 7ce031e0e9..d8dc5f127a 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs @@ -9,6 +9,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Logging; using osu.Game.Online.Multiplayer; +using osu.Game.Online.Multiplayer.RoomStatuses; using osu.Game.Online.RealtimeMultiplayer; using osu.Game.Screens.Multi.Components; @@ -40,6 +41,8 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer public override void CreateRoom(Room room, Action onSuccess = null, Action onError = null) => base.CreateRoom(room, r => joinMultiplayerRoom(r, onSuccess, onError), onError); + public override bool CanJoinRoom(Room room) => !(room.Status.Value is RoomStatusEnded); + public override void JoinRoom(Room room, Action onSuccess = null, Action onError = null) => base.JoinRoom(room, r => joinMultiplayerRoom(r, onSuccess, onError), onError); From a64ffcd2949ca3f47c0bc3019de9596dc28e79d5 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 22 Dec 2020 16:38:10 +0300 Subject: [PATCH 487/818] Refrain from joining room if not allowed --- osu.Game/Screens/Multi/Lounge/Components/RoomsContainer.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Multi/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/Multi/Lounge/Components/RoomsContainer.cs index c7c37cbc0d..c1f69b779c 100644 --- a/osu.Game/Screens/Multi/Lounge/Components/RoomsContainer.cs +++ b/osu.Game/Screens/Multi/Lounge/Components/RoomsContainer.cs @@ -115,7 +115,9 @@ namespace osu.Game.Screens.Multi.Lounge.Components { if (room == selectedRoom.Value) { - joinSelected(); + if (roomManager.CanJoinRoom(room)) + joinSelected(); + return; } From 5efc3b94961cf918fa96b54818382e89b2cd9faf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 22 Dec 2020 21:12:28 +0100 Subject: [PATCH 488/818] Start state display as hidden Would otherwise flicker for a few frames when a new user was added to the list of participants. --- .../Multi/RealtimeMultiplayer/Participants/StateDisplay.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/StateDisplay.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/StateDisplay.cs index db93525217..844f239363 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/StateDisplay.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/StateDisplay.cs @@ -18,6 +18,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Participants public StateDisplay() { AutoSizeAxes = Axes.Both; + Alpha = 0; } private MultiplayerUserState status; From 4a677ecc190f0ec7b354db5f2436349353cd9f2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 22 Dec 2020 21:16:45 +0100 Subject: [PATCH 489/818] Make random state choice in test more robust `RNG.Next(int, int)` is max-exclusive, so the random state choice would actually never pick `MultiplayerUserState.Results` on its own. The only reason why that state ever did show up was by a freak accident of sorts (the logic in `TestRealtimeMultiplayerClient` would automatically convert every `FinishedPlay` state to `Results`, up until seeing the first player that was in the `Playing` state). --- .../TestSceneRealtimeMultiplayerParticipantsList.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeMultiplayerParticipantsList.cs b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeMultiplayerParticipantsList.cs index 7fd31906f7..4221821496 100644 --- a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeMultiplayerParticipantsList.cs +++ b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeMultiplayerParticipantsList.cs @@ -109,7 +109,7 @@ namespace osu.Game.Tests.Visual.RealtimeMultiplayer CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", }); - Client.ChangeUserState(i, (MultiplayerUserState)RNG.Next(0, (int)MultiplayerUserState.Results)); + Client.ChangeUserState(i, (MultiplayerUserState)RNG.Next(0, (int)MultiplayerUserState.Results + 1)); } }); } From 32728047044a426d2c076cc143d836412d53b7a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 22 Dec 2020 22:31:40 +0100 Subject: [PATCH 490/818] Fix potential crash when no submission token Can happen because `TimeshiftPlayer` will schedule a screen exit on token retrieval failure, and `RealtimePlayer`'s BDL won't even attempt to create a leaderboard in that case. --- .../Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs index 453e7e6140..7824b414f2 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Logging; using osu.Framework.Screens; @@ -33,6 +34,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer private readonly TaskCompletionSource resultsReady = new TaskCompletionSource(); private readonly ManualResetEventSlim startedEvent = new ManualResetEventSlim(); + [CanBeNull] private MultiplayerGameplayLeaderboard leaderboard; public RealtimePlayer(PlaylistItem playlistItem) @@ -72,6 +74,13 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer protected override void Update() { base.Update(); + adjustLeaderboardPosition(); + } + + private void adjustLeaderboardPosition() + { + if (leaderboard == null) + return; const float padding = 44; // enough margin to avoid the hit error display. From 7751ef4f3eadf4fa4583e3815bc6f3a95c5c354b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 23 Dec 2020 05:49:18 +0300 Subject: [PATCH 491/818] Revert previous logic of join guarding --- osu.Game.Tests/Visual/Multiplayer/TestRoomManager.cs | 2 -- .../Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs | 2 -- .../Visual/Multiplayer/TestSceneTimeshiftRoomSubScreen.cs | 2 -- osu.Game/Screens/Multi/Components/RoomManager.cs | 2 -- osu.Game/Screens/Multi/IRoomManager.cs | 6 ------ osu.Game/Screens/Multi/Lounge/Components/RoomsContainer.cs | 4 +--- .../Multi/RealtimeMultiplayer/RealtimeRoomManager.cs | 3 --- 7 files changed, 1 insertion(+), 20 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestRoomManager.cs b/osu.Game.Tests/Visual/Multiplayer/TestRoomManager.cs index ea8fb4ef49..9dd4aea4bd 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestRoomManager.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestRoomManager.cs @@ -24,8 +24,6 @@ namespace osu.Game.Tests.Visual.Multiplayer public void CreateRoom(Room room, Action onSuccess = null, Action onError = null) => Rooms.Add(room); - public bool CanJoinRoom(Room room) => true; - public void JoinRoom(Room room, Action onSuccess = null, Action onError = null) { } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs index 55dbd1a7c6..1fcae9c709 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs @@ -146,8 +146,6 @@ namespace osu.Game.Tests.Visual.Multiplayer onSuccess?.Invoke(room); } - public bool CanJoinRoom(Room room) => true; - public void JoinRoom(Room room, Action onSuccess = null, Action onError = null) => throw new NotImplementedException(); public void PartRoom() => throw new NotImplementedException(); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftRoomSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftRoomSubScreen.cs index 8fdb5d4093..bbd7d84081 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftRoomSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftRoomSubScreen.cs @@ -161,8 +161,6 @@ namespace osu.Game.Tests.Visual.Multiplayer onSuccess?.Invoke(room); } - public bool CanJoinRoom(Room room) => true; - public void JoinRoom(Room room, Action onSuccess = null, Action onError = null) => onSuccess?.Invoke(room); public void PartRoom() diff --git a/osu.Game/Screens/Multi/Components/RoomManager.cs b/osu.Game/Screens/Multi/Components/RoomManager.cs index 3fcfe1df25..f78d0d979e 100644 --- a/osu.Game/Screens/Multi/Components/RoomManager.cs +++ b/osu.Game/Screens/Multi/Components/RoomManager.cs @@ -82,8 +82,6 @@ namespace osu.Game.Screens.Multi.Components api.Queue(req); } - public virtual bool CanJoinRoom(Room room) => true; - private JoinRoomRequest currentJoinRoomRequest; public virtual void JoinRoom(Room room, Action onSuccess = null, Action onError = null) diff --git a/osu.Game/Screens/Multi/IRoomManager.cs b/osu.Game/Screens/Multi/IRoomManager.cs index a1cff129a9..630e3af91c 100644 --- a/osu.Game/Screens/Multi/IRoomManager.cs +++ b/osu.Game/Screens/Multi/IRoomManager.cs @@ -34,12 +34,6 @@ namespace osu.Game.Screens.Multi /// An action to be invoked if an error occurred. void CreateRoom(Room room, Action onSuccess = null, Action onError = null); - /// - /// Whether the provided can be joined. - /// - /// The to check for. - bool CanJoinRoom(Room room); - /// /// Joins a . /// diff --git a/osu.Game/Screens/Multi/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/Multi/Lounge/Components/RoomsContainer.cs index c1f69b779c..c7c37cbc0d 100644 --- a/osu.Game/Screens/Multi/Lounge/Components/RoomsContainer.cs +++ b/osu.Game/Screens/Multi/Lounge/Components/RoomsContainer.cs @@ -115,9 +115,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components { if (room == selectedRoom.Value) { - if (roomManager.CanJoinRoom(room)) - joinSelected(); - + joinSelected(); return; } diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs index d8dc5f127a..7ce031e0e9 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs @@ -9,7 +9,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Logging; using osu.Game.Online.Multiplayer; -using osu.Game.Online.Multiplayer.RoomStatuses; using osu.Game.Online.RealtimeMultiplayer; using osu.Game.Screens.Multi.Components; @@ -41,8 +40,6 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer public override void CreateRoom(Room room, Action onSuccess = null, Action onError = null) => base.CreateRoom(room, r => joinMultiplayerRoom(r, onSuccess, onError), onError); - public override bool CanJoinRoom(Room room) => !(room.Status.Value is RoomStatusEnded); - public override void JoinRoom(Room room, Action onSuccess = null, Action onError = null) => base.JoinRoom(room, r => joinMultiplayerRoom(r, onSuccess, onError), onError); From 3aa2b228380ccc8a2ce3e9043c5dcd43a80a6b97 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 23 Dec 2020 05:52:10 +0300 Subject: [PATCH 492/818] Add early check for room status before joining --- .../Multi/RealtimeMultiplayer/RealtimeRoomManager.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs index 7ce031e0e9..484d5cce0b 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs @@ -9,6 +9,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Logging; using osu.Game.Online.Multiplayer; +using osu.Game.Online.Multiplayer.RoomStatuses; using osu.Game.Online.RealtimeMultiplayer; using osu.Game.Screens.Multi.Components; @@ -41,7 +42,15 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer => base.CreateRoom(room, r => joinMultiplayerRoom(r, onSuccess, onError), onError); public override void JoinRoom(Room room, Action onSuccess = null, Action onError = null) - => base.JoinRoom(room, r => joinMultiplayerRoom(r, onSuccess, onError), onError); + { + if (room.Status.Value is RoomStatusEnded) + { + onError?.Invoke("Cannot join an ended room."); + return; + } + + base.JoinRoom(room, r => joinMultiplayerRoom(r, onSuccess, onError), onError); + } public override void PartRoom() { From 45dcd3242db7cc84753577d4de75ba571e75deb0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 23 Dec 2020 13:57:48 +0900 Subject: [PATCH 493/818] Add comment explaining why things are done where they are --- .../Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs index 484d5cce0b..f982574eb3 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs @@ -43,6 +43,8 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer public override void JoinRoom(Room room, Action onSuccess = null, Action onError = null) { + // this is done here as a pre-check to avoid clicking on already closed rooms in the lounge from triggering a server join. + // should probably be done at a higher level, but due to the current structure of things this is the easiest place for now. if (room.Status.Value is RoomStatusEnded) { onError?.Invoke("Cannot join an ended room."); From be427a4ec0fd7f30189f3b37941844483ad95d62 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 23 Dec 2020 14:20:35 +0900 Subject: [PATCH 494/818] Fix realtime leaderboard showing accuracy based on final base score, not rolling --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 23 ++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 10d0cc2865..4b2e2bf715 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -233,7 +233,7 @@ namespace osu.Game.Rulesets.Scoring } /// - /// Given a minimal set of inputs, return the computed score and accuracy for the tracked beatmap / mods combination. + /// Given a minimal set of inputs, return the computed score and accuracy for the tracked beatmap / mods combination, at the current point in time. /// /// The to compute the total score in. /// The maximum combo achievable in the beatmap. @@ -252,15 +252,28 @@ namespace osu.Game.Rulesets.Scoring computedBaseScore += Judgement.ToNumericResult(pair.Key) * pair.Value; } - double accuracy = calculateAccuracyRatio(computedBaseScore); + double pointInTimeAccuracy = calculateAccuracyRatio(computedBaseScore, true); double comboRatio = calculateComboRatio(maxCombo); - double score = GetScore(mode, maxAchievableCombo, accuracy, comboRatio, scoreResultCounts); + double score = GetScore(mode, maxAchievableCombo, calculateAccuracyRatio(computedBaseScore), comboRatio, scoreResultCounts); - return (score, accuracy); + return (score, pointInTimeAccuracy); + } + + /// + /// Get the accuracy fraction for the provided base score. + /// + /// The score to be used for accuracy calculation. + /// Whether the rolling base score should be used (ie. for the current point in time based on Apply/Reverted results). + /// The computed accuracy. + private double calculateAccuracyRatio(double baseScore, bool preferRolling = false) + { + if (preferRolling && rollingMaxBaseScore != 0) + return baseScore / rollingMaxBaseScore; + + return maxBaseScore > 0 ? baseScore / maxBaseScore : 0; } - private double calculateAccuracyRatio(double baseScore) => maxBaseScore > 0 ? baseScore / maxBaseScore : 0; private double calculateComboRatio(int maxCombo) => maxAchievableCombo > 0 ? (double)maxCombo / maxAchievableCombo : 1; private double getBonusScore(Dictionary statistics) From dec997c0f49687800b9c90641299d38b4c9e060d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 23 Dec 2020 14:44:20 +0900 Subject: [PATCH 495/818] Fix flashlight not updating its position during replay rewinding Closes #11260 --- osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs index ac20407ed2..3f770cfb5e 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs @@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Osu.Mods var destination = e.MousePosition; FlashlightPosition = Interpolation.ValueAt( - Math.Clamp(Clock.ElapsedFrameTime, 0, follow_delay), position, destination, 0, follow_delay, Easing.Out); + Math.Clamp(Math.Abs(Clock.ElapsedFrameTime), 0, follow_delay), position, destination, 0, follow_delay, Easing.Out); return base.OnMouseMove(e); } From 286884421d74d8cd565533bdd723448b88416dc3 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 23 Dec 2020 08:47:34 +0300 Subject: [PATCH 496/818] Apply track looping and play on track change --- osu.Game/Screens/Multi/Match/RoomSubScreen.cs | 72 ++++++++++--------- 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/osu.Game/Screens/Multi/Match/RoomSubScreen.cs b/osu.Game/Screens/Multi/Match/RoomSubScreen.cs index 21316a98ce..4f5d2a5b3e 100644 --- a/osu.Game/Screens/Multi/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/RoomSubScreen.cs @@ -41,6 +41,37 @@ namespace osu.Game.Screens.Multi.Match managerUpdated = beatmapManager.ItemUpdated.GetBoundCopy(); managerUpdated.BindValueChanged(beatmapUpdated); + + if (music != null) + music.TrackChanged += applyToTrack; + } + + public override void OnEntering(IScreen last) + { + base.OnEntering(last); + applyToTrack(); + } + + public override void OnSuspending(IScreen next) + { + resetTrack(); + base.OnSuspending(next); + } + + public override void OnResuming(IScreen last) + { + base.OnResuming(last); + applyToTrack(); + } + + public override bool OnExiting(IScreen next) + { + RoomManager?.PartRoom(); + Mods.Value = Array.Empty(); + + resetTrack(); + + return base.OnExiting(next); } private void selectedItemChanged() @@ -65,54 +96,25 @@ namespace osu.Game.Screens.Multi.Match var localBeatmap = beatmap == null ? null : beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == beatmap.OnlineBeatmapID); Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap); - if (this.IsCurrentScreen()) - applyTrackLooping(); } - public override void OnEntering(IScreen last) + private void applyToTrack(WorkingBeatmap _ = default, TrackChangeDirection __ = default) { - base.OnEntering(last); + if (!this.IsCurrentScreen()) + return; - music?.EnsurePlayingSomething(); - applyTrackLooping(); - } - - public override void OnSuspending(IScreen next) - { - cancelTrackLooping(); - base.OnSuspending(next); - } - - public override void OnResuming(IScreen last) - { - base.OnResuming(last); - - music?.EnsurePlayingSomething(); - applyTrackLooping(); - } - - public override bool OnExiting(IScreen next) - { - RoomManager?.PartRoom(); - Mods.Value = Array.Empty(); - - cancelTrackLooping(); - - return base.OnExiting(next); - } - - private void applyTrackLooping() - { var track = Beatmap.Value?.Track; if (track != null) { track.RestartPoint = Beatmap.Value.Metadata.PreviewTime; track.Looping = true; + + music?.EnsurePlayingSomething(); } } - private void cancelTrackLooping() + private void resetTrack() { var track = Beatmap?.Value?.Track; From 00d50150de6e39cbdff44e7f5a98c41a3c57b6a6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 23 Dec 2020 15:49:22 +0900 Subject: [PATCH 497/818] Ensure the current room is left at a mutliplayer client level on client disconnection --- .../RealtimeMultiplayer/RealtimeMultiplayerClient.cs | 4 ++++ .../RealtimeMultiplayer/StatefulMultiplayerClient.cs | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/osu.Game/Online/RealtimeMultiplayer/RealtimeMultiplayerClient.cs b/osu.Game/Online/RealtimeMultiplayer/RealtimeMultiplayerClient.cs index 75bb578a29..5cbf3be8ca 100644 --- a/osu.Game/Online/RealtimeMultiplayer/RealtimeMultiplayerClient.cs +++ b/osu.Game/Online/RealtimeMultiplayer/RealtimeMultiplayerClient.cs @@ -130,7 +130,11 @@ namespace osu.Game.Online.RealtimeMultiplayer public override async Task LeaveRoom() { if (!isConnected.Value) + { + // even if not connected, make sure the local room state can be cleaned up. + await base.LeaveRoom(); return; + } if (Room == null) return; diff --git a/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs index 4ebd648689..9680387fcc 100644 --- a/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs @@ -75,6 +75,16 @@ namespace osu.Game.Online.RealtimeMultiplayer // Todo: This is temporary, until the multiplayer server returns the item id on match start or otherwise. private int playlistItemId; + protected StatefulMultiplayerClient() + { + IsConnected.BindValueChanged(connected => + { + // clean up local room state on server disconnect. + if (!connected.NewValue) + LeaveRoom(); + }); + } + /// /// Joins the for a given API . /// From 12df3056e6d27d2da5d29a172708e849162eaa3c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 23 Dec 2020 15:58:50 +0900 Subject: [PATCH 498/818] Ensure appropriate screens handle exiting when the server gets disconnected I would have liked for this to be handled via the `OnRoomChanged` event flow, but this isn't present in RealtimeMatchSubScreen due to inheritence woes. --- .../RealtimeMultiplayer/RealtimeMatchSubScreen.cs | 14 ++++++++++++++ .../Multi/RealtimeMultiplayer/RealtimePlayer.cs | 12 ++++++++++++ 2 files changed, 26 insertions(+) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSubScreen.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSubScreen.cs index cdab1435c0..8405fc196b 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSubScreen.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSubScreen.cs @@ -4,8 +4,10 @@ using System.Collections.Specialized; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Logging; using osu.Framework.Screens; using osu.Game.Online.Multiplayer; using osu.Game.Online.RealtimeMultiplayer; @@ -34,6 +36,8 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer private RealtimeMatchSettingsOverlay settingsOverlay; + private IBindable isConnected; + public RealtimeMatchSubScreen(Room room) { Title = room.RoomID.Value == null ? "New match" : room.Name.Value; @@ -173,6 +177,16 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer Playlist.BindCollectionChanged(onPlaylistChanged, true); client.LoadRequested += onLoadRequested; + + isConnected = client.IsConnected.GetBoundCopy(); + isConnected.BindValueChanged(connected => + { + if (!connected.NewValue) + { + Logger.Log("Connection to multiplayer server was lost.", LoggingTarget.Runtime, LogLevel.Important); + Schedule(this.Exit); + } + }, true); } public override bool OnBackButton() diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs index c6d44686b5..d74ccdd32f 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Logging; using osu.Framework.Screens; using osu.Game.Online.Multiplayer; @@ -30,6 +31,8 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer private readonly TaskCompletionSource resultsReady = new TaskCompletionSource(); private readonly ManualResetEventSlim startedEvent = new ManualResetEventSlim(); + private IBindable isConnected; + public RealtimePlayer(PlaylistItem playlistItem) : base(playlistItem, false) { @@ -43,6 +46,15 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer client.MatchStarted += onMatchStarted; client.ResultsReady += onResultsReady; + + isConnected = client.IsConnected.GetBoundCopy(); + isConnected.BindValueChanged(connected => + { + if (!connected.NewValue) + // messaging to the user about this disconnect will be provided by the RealtimeMatchSubScreen. + Schedule(this.Exit); + }, true); + client.ChangeState(MultiplayerUserState.Loaded); if (!startedEvent.Wait(TimeSpan.FromSeconds(30))) From a1d42dc4a061a8fb2f50064c485cbd3cf07186be Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 23 Dec 2020 16:17:55 +0900 Subject: [PATCH 499/818] Don't allow creating or joining a room when not connected to server --- .../Screens/Multi/Lounge/LoungeSubScreen.cs | 2 +- .../RealtimeLoungeSubScreen.cs | 17 +++++++++++++++++ .../RealtimeMultiplayer/RealtimeRoomManager.cs | 6 ++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs index 44c893363b..6b08745dd7 100644 --- a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs @@ -184,7 +184,7 @@ namespace osu.Game.Screens.Multi.Lounge /// /// Push a room as a new subscreen. /// - public void Open(Room room) + public virtual void Open(Room room) { // Handles the case where a room is clicked 3 times in quick succession if (!this.IsCurrentScreen()) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeLoungeSubScreen.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeLoungeSubScreen.cs index 9fbf0c4654..b53ec94519 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeLoungeSubScreen.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeLoungeSubScreen.cs @@ -1,7 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; +using osu.Framework.Logging; using osu.Game.Online.Multiplayer; +using osu.Game.Online.RealtimeMultiplayer; using osu.Game.Screens.Multi.Lounge; using osu.Game.Screens.Multi.Lounge.Components; using osu.Game.Screens.Multi.Match; @@ -13,5 +16,19 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer protected override FilterControl CreateFilterControl() => new RealtimeFilterControl(); protected override RoomSubScreen CreateRoomSubScreen(Room room) => new RealtimeMatchSubScreen(room); + + [Resolved] + private StatefulMultiplayerClient client { get; set; } + + public override void Open(Room room) + { + if (!client.IsConnected.Value) + { + Logger.Log("Not currently connected to the multiplayer server.", LoggingTarget.Runtime, LogLevel.Important); + return; + } + + base.Open(room); + } } } diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs index f982574eb3..2f60f504de 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs @@ -43,6 +43,12 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer public override void JoinRoom(Room room, Action onSuccess = null, Action onError = null) { + if (!multiplayerClient.IsConnected.Value) + { + onError?.Invoke("Not currently connected to the multiplayer server."); + return; + } + // this is done here as a pre-check to avoid clicking on already closed rooms in the lounge from triggering a server join. // should probably be done at a higher level, but due to the current structure of things this is the easiest place for now. if (room.Status.Value is RoomStatusEnded) From 569c4092efe42a55caabc5a08c7f7bc227f3b1e3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 23 Dec 2020 16:19:03 +0900 Subject: [PATCH 500/818] Move notification to stateful client so it is only shown to the user from one location --- .../RealtimeMultiplayer/StatefulMultiplayerClient.cs | 4 ++++ .../Multi/RealtimeMultiplayer/RealtimeMatchSubScreen.cs | 7 ++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs index 9680387fcc..79d82a8d02 100644 --- a/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs @@ -11,6 +11,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; +using osu.Framework.Logging; using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.Online.API; @@ -81,7 +82,10 @@ namespace osu.Game.Online.RealtimeMultiplayer { // clean up local room state on server disconnect. if (!connected.NewValue) + { + Logger.Log("Connection to multiplayer server was lost.", LoggingTarget.Runtime, LogLevel.Important); LeaveRoom(); + } }); } diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSubScreen.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSubScreen.cs index 8405fc196b..807ea74404 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSubScreen.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSubScreen.cs @@ -7,7 +7,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Logging; using osu.Framework.Screens; using osu.Game.Online.Multiplayer; using osu.Game.Online.RealtimeMultiplayer; @@ -18,6 +17,7 @@ using osu.Game.Screens.Multi.RealtimeMultiplayer.Match; using osu.Game.Screens.Multi.RealtimeMultiplayer.Participants; using osu.Game.Screens.Play; using osu.Game.Users; +using ParticipantsList = osu.Game.Screens.Multi.RealtimeMultiplayer.Participants.ParticipantsList; namespace osu.Game.Screens.Multi.RealtimeMultiplayer { @@ -106,7 +106,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer new Drawable[] { new ParticipantsListHeader() }, new Drawable[] { - new Participants.ParticipantsList + new ParticipantsList { RelativeSizeAxes = Axes.Both }, @@ -182,10 +182,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer isConnected.BindValueChanged(connected => { if (!connected.NewValue) - { - Logger.Log("Connection to multiplayer server was lost.", LoggingTarget.Runtime, LogLevel.Important); Schedule(this.Exit); - } }, true); } From f5d27b40a8f5c9dd8b3e849360425ed540b79c39 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 23 Dec 2020 16:32:58 +0900 Subject: [PATCH 501/818] Standardise flow for aborting realtime player exit to avoid double-exit call --- .../Multi/RealtimeMultiplayer/RealtimePlayer.cs | 12 ++++++------ osu.Game/Screens/Play/Player.cs | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs index d74ccdd32f..edec40890e 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs @@ -51,8 +51,12 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer isConnected.BindValueChanged(connected => { if (!connected.NewValue) + { + startedEvent.Set(); + // messaging to the user about this disconnect will be provided by the RealtimeMatchSubScreen. - Schedule(this.Exit); + Schedule(PerformImmediateExit); + } }, true); client.ChangeState(MultiplayerUserState.Loaded); @@ -61,11 +65,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer { Logger.Log("Failed to start the multiplayer match in time.", LoggingTarget.Runtime, LogLevel.Important); - Schedule(() => - { - ValidForResume = false; - this.Exit(); - }); + Schedule(PerformImmediateExit); } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index c539dff5d9..c6265c48d2 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -386,7 +386,7 @@ namespace osu.Game.Screens.Play if (!this.IsCurrentScreen()) return; fadeOut(true); - performImmediateExit(); + PerformImmediateExit(); }, }, failAnimation = new FailAnimation(DrawableRuleset) { OnComplete = onFailComplete, }, @@ -458,7 +458,7 @@ namespace osu.Game.Screens.Play return playable; } - private void performImmediateExit() + protected void PerformImmediateExit() { // if a restart has been requested, cancel any pending completion (user has shown intent to restart). completionProgressDelegate?.Cancel(); @@ -498,7 +498,7 @@ namespace osu.Game.Screens.Play RestartRequested?.Invoke(); if (this.IsCurrentScreen()) - performImmediateExit(); + PerformImmediateExit(); else this.MakeCurrent(); } From 91021eb8c45b0826bf0503cc9d5d4fac822fe005 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 23 Dec 2020 16:49:17 +0900 Subject: [PATCH 502/818] Remove unused using --- osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs index edec40890e..9e2ba9b04a 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs @@ -8,7 +8,6 @@ using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Logging; -using osu.Framework.Screens; using osu.Game.Online.Multiplayer; using osu.Game.Online.RealtimeMultiplayer; using osu.Game.Scoring; From d27b83d678bb4c9f0525b685880a5e9dd136ee90 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 23 Dec 2020 16:51:11 +0900 Subject: [PATCH 503/818] More correctly handle fire-and-forget async call --- .../RealtimeMultiplayer/RealtimePlayer.cs | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs index 9e2ba9b04a..0f8c0f247c 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs @@ -51,21 +51,25 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer { if (!connected.NewValue) { - startedEvent.Set(); - // messaging to the user about this disconnect will be provided by the RealtimeMatchSubScreen. - Schedule(PerformImmediateExit); + failAndBail(); } }, true); - client.ChangeState(MultiplayerUserState.Loaded); + client.ChangeState(MultiplayerUserState.Loaded).ContinueWith(task => + failAndBail(task.Exception?.Message ?? "Server error"), TaskContinuationOptions.NotOnRanToCompletion); if (!startedEvent.Wait(TimeSpan.FromSeconds(30))) - { - Logger.Log("Failed to start the multiplayer match in time.", LoggingTarget.Runtime, LogLevel.Important); + failAndBail("Failed to start the multiplayer match in time."); + } - Schedule(PerformImmediateExit); - } + private void failAndBail(string message = null) + { + if (!string.IsNullOrEmpty(message)) + Logger.Log(message, LoggingTarget.Runtime, LogLevel.Important); + + startedEvent.Set(); + Schedule(PerformImmediateExit); } private void onMatchStarted() => startedEvent.Set(); From c3c3364d399b915f9d0d3044dc3cd045830bd3dd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 23 Dec 2020 16:56:51 +0900 Subject: [PATCH 504/818] Simplify error handling of JoinRoom call --- .../RealtimeRoomManager.cs | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs index 2f60f504de..8bdf2bdc1a 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs @@ -83,15 +83,19 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer { Debug.Assert(room.RoomID.Value != null); - var joinTask = multiplayerClient.JoinRoom(room); - joinTask.ContinueWith(_ => Schedule(() => onSuccess?.Invoke(room)), TaskContinuationOptions.OnlyOnRanToCompletion); - joinTask.ContinueWith(t => + multiplayerClient.JoinRoom(room).ContinueWith(t => { - PartRoom(); - if (t.Exception != null) - Logger.Error(t.Exception, "Failed to join multiplayer room."); - Schedule(() => onError?.Invoke(t.Exception?.ToString() ?? string.Empty)); - }, TaskContinuationOptions.NotOnRanToCompletion); + if (t.IsCompletedSuccessfully) + Schedule(() => onSuccess?.Invoke(room)); + else + { + if (t.Exception != null) + Logger.Error(t.Exception, "Failed to join multiplayer room."); + + PartRoom(); + Schedule(() => onError?.Invoke(t.Exception?.ToString() ?? string.Empty)); + } + }); } private void updatePolling() From 1864da00e69806eefb389fd0a5742c0477eb419d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 23 Dec 2020 17:10:02 +0900 Subject: [PATCH 505/818] Add extension method to handle cases of fire-and-forget async usage --- osu.Game/Extensions/TaskExtensions.cs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 osu.Game/Extensions/TaskExtensions.cs diff --git a/osu.Game/Extensions/TaskExtensions.cs b/osu.Game/Extensions/TaskExtensions.cs new file mode 100644 index 0000000000..913a622d9b --- /dev/null +++ b/osu.Game/Extensions/TaskExtensions.cs @@ -0,0 +1,26 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Threading.Tasks; +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 important, or silently ignored. + public static void FireAndForget(this Task task, bool logOnError = false) + { + task.ContinueWith(t => + { + if (logOnError) + Logger.Log($"Error running task: {t.Exception?.Message ?? "unknown"}", LoggingTarget.Runtime, LogLevel.Important); + }, TaskContinuationOptions.NotOnRanToCompletion); + } + } +} From 7cc38f03d10a44c30c5edb59e28f2bdc46e377dd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 23 Dec 2020 17:10:34 +0900 Subject: [PATCH 506/818] Use extension method in all call sites of fire-and-forget async usage --- .../RealtimeMultiplayer/StatefulMultiplayerClient.cs | 5 +++-- .../Multi/RealtimeMultiplayer/Match/RealtimeReadyButton.cs | 7 ++++--- .../RealtimeMultiplayer/Participants/ParticipantPanel.cs | 3 ++- .../Multi/RealtimeMultiplayer/RealtimeMatchSongSelect.cs | 2 +- .../Multi/RealtimeMultiplayer/RealtimeMultiplayer.cs | 3 ++- .../Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs | 4 ++-- .../Multi/RealtimeMultiplayer/RealtimeRoomManager.cs | 4 +++- 7 files changed, 17 insertions(+), 11 deletions(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs index 79d82a8d02..3196f10f6f 100644 --- a/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. + // See the LICENCE file in the repository root for full licence text. #nullable enable @@ -14,6 +14,7 @@ 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; @@ -84,7 +85,7 @@ namespace osu.Game.Online.RealtimeMultiplayer if (!connected.NewValue) { Logger.Log("Connection to multiplayer server was lost.", LoggingTarget.Runtime, LogLevel.Important); - LeaveRoom(); + LeaveRoom().FireAndForget(); } }); } diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeReadyButton.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeReadyButton.cs index 09487e9831..59f9d5e1ec 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeReadyButton.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeReadyButton.cs @@ -7,6 +7,7 @@ using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Game.Extensions; using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; using osu.Game.Online.API; @@ -105,13 +106,13 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Match return; if (localUser.State == MultiplayerUserState.Idle) - Client.ChangeState(MultiplayerUserState.Ready); + Client.ChangeState(MultiplayerUserState.Ready).FireAndForget(true); else { if (Room?.Host?.Equals(localUser) == true) - Client.StartMatch(); + Client.StartMatch().FireAndForget(true); else - Client.ChangeState(MultiplayerUserState.Idle); + Client.ChangeState(MultiplayerUserState.Idle).FireAndForget(true); } } diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantPanel.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantPanel.cs index a4ff2ce346..fd16754045 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantPanel.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantPanel.cs @@ -11,6 +11,7 @@ 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 +177,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Participants if (Room.Host?.UserID != api.LocalUser.Value.Id) return; - Client.TransferHost(targetUser); + Client.TransferHost(targetUser).FireAndForget(true); }) }; } diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSongSelect.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSongSelect.cs index f3dab93089..4a8e398008 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSongSelect.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSongSelect.cs @@ -58,7 +58,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer client.ChangeSettings(item: item).ContinueWith(t => { - return Schedule(() => + Schedule(() => { loadingLayer.Hide(); diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMultiplayer.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMultiplayer.cs index 6455701d31..d5b891f2cc 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMultiplayer.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMultiplayer.cs @@ -4,6 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Logging; using osu.Framework.Screens; +using osu.Game.Extensions; using osu.Game.Online.Multiplayer; using osu.Game.Online.RealtimeMultiplayer; using osu.Game.Screens.Multi.Components; @@ -21,7 +22,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer base.OnResuming(last); if (client.Room != null) - client.ChangeState(MultiplayerUserState.Idle); + client.ChangeState(MultiplayerUserState.Idle).FireAndForget(true); } protected override void UpdatePollingRate(bool isIdle) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs index 0f8c0f247c..7c6b33cddd 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs @@ -56,8 +56,8 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer } }, true); - client.ChangeState(MultiplayerUserState.Loaded).ContinueWith(task => - failAndBail(task.Exception?.Message ?? "Server error"), TaskContinuationOptions.NotOnRanToCompletion); + client.ChangeState(MultiplayerUserState.Loaded) + .ContinueWith(task => failAndBail(task.Exception?.Message ?? "Server error"), TaskContinuationOptions.NotOnRanToCompletion); if (!startedEvent.Wait(TimeSpan.FromSeconds(30))) failAndBail("Failed to start the multiplayer match in time."); diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs index 8bdf2bdc1a..98a0e5b694 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Logging; +using osu.Game.Extensions; using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer.RoomStatuses; using osu.Game.Online.RealtimeMultiplayer; @@ -68,7 +69,8 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer var joinedRoom = JoinedRoom.Value; base.PartRoom(); - multiplayerClient.LeaveRoom(); + + multiplayerClient.LeaveRoom().FireAndForget(); // 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. From 0ddcab574f05416953361e920a1af22856a16828 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 23 Dec 2020 17:14:58 +0900 Subject: [PATCH 507/818] Rename method to avoid weird code analysis rule --- osu.Game/Extensions/TaskExtensions.cs | 2 +- .../Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs | 2 +- .../Multi/RealtimeMultiplayer/Match/RealtimeReadyButton.cs | 6 +++--- .../RealtimeMultiplayer/Participants/ParticipantPanel.cs | 2 +- .../Multi/RealtimeMultiplayer/RealtimeMultiplayer.cs | 2 +- .../Multi/RealtimeMultiplayer/RealtimeRoomManager.cs | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game/Extensions/TaskExtensions.cs b/osu.Game/Extensions/TaskExtensions.cs index 913a622d9b..a1215d786b 100644 --- a/osu.Game/Extensions/TaskExtensions.cs +++ b/osu.Game/Extensions/TaskExtensions.cs @@ -14,7 +14,7 @@ namespace osu.Game.Extensions ///
/// The task. /// Whether errors should be logged as important, or silently ignored. - public static void FireAndForget(this Task task, bool logOnError = false) + public static void CatchUnobservedExceptions(this Task task, bool logOnError = false) { task.ContinueWith(t => { diff --git a/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs index 3196f10f6f..7b375ca475 100644 --- a/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs @@ -85,7 +85,7 @@ namespace osu.Game.Online.RealtimeMultiplayer if (!connected.NewValue) { Logger.Log("Connection to multiplayer server was lost.", LoggingTarget.Runtime, LogLevel.Important); - LeaveRoom().FireAndForget(); + LeaveRoom().CatchUnobservedExceptions(); } }); } diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeReadyButton.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeReadyButton.cs index 59f9d5e1ec..5bead2b271 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeReadyButton.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeReadyButton.cs @@ -106,13 +106,13 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Match return; if (localUser.State == MultiplayerUserState.Idle) - Client.ChangeState(MultiplayerUserState.Ready).FireAndForget(true); + Client.ChangeState(MultiplayerUserState.Ready).CatchUnobservedExceptions(true); else { if (Room?.Host?.Equals(localUser) == true) - Client.StartMatch().FireAndForget(true); + Client.StartMatch().CatchUnobservedExceptions(true); else - Client.ChangeState(MultiplayerUserState.Idle).FireAndForget(true); + Client.ChangeState(MultiplayerUserState.Idle).CatchUnobservedExceptions(true); } } diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantPanel.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantPanel.cs index fd16754045..85393d1bae 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantPanel.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantPanel.cs @@ -177,7 +177,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Participants if (Room.Host?.UserID != api.LocalUser.Value.Id) return; - Client.TransferHost(targetUser).FireAndForget(true); + Client.TransferHost(targetUser).CatchUnobservedExceptions(true); }) }; } diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMultiplayer.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMultiplayer.cs index d5b891f2cc..6685cf52d6 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMultiplayer.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMultiplayer.cs @@ -22,7 +22,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer base.OnResuming(last); if (client.Room != null) - client.ChangeState(MultiplayerUserState.Idle).FireAndForget(true); + client.ChangeState(MultiplayerUserState.Idle).CatchUnobservedExceptions(true); } protected override void UpdatePollingRate(bool isIdle) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs index 98a0e5b694..cd337bbb55 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs @@ -70,7 +70,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer base.PartRoom(); - multiplayerClient.LeaveRoom().FireAndForget(); + multiplayerClient.LeaveRoom().CatchUnobservedExceptions(); // 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. From 3c8f871b2815ca8f4618dbf2e51870e31324cae8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 23 Dec 2020 17:39:08 +0900 Subject: [PATCH 508/818] Move player constructor configuration to dedicated class; add AllowRestart parameter --- .../TestSceneHoldNoteInput.cs | 6 +- .../TestSceneOutOfOrderHits.cs | 6 +- .../TestSceneLegacyBeatmapSkin.cs | 6 +- .../TestSceneOutOfOrderHits.cs | 6 +- .../TestSceneSliderInput.cs | 6 +- .../Screens/Multi/Play/TimeshiftPlayer.cs | 4 +- .../RealtimeMultiplayer/RealtimePlayer.cs | 7 +- osu.Game/Screens/Play/Player.cs | 140 +++++++++--------- osu.Game/Screens/Play/PlayerConfiguration.cs | 23 +++ osu.Game/Screens/Play/ReplayPlayer.cs | 4 +- osu.Game/Screens/Ranking/ResultsScreen.cs | 15 +- osu.Game/Tests/Visual/TestPlayer.cs | 6 +- 12 files changed, 146 insertions(+), 83 deletions(-) create mode 100644 osu.Game/Screens/Play/PlayerConfiguration.cs diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs index 5cb1519196..596430f9e5 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs @@ -355,7 +355,11 @@ namespace osu.Game.Rulesets.Mania.Tests protected override bool PauseOnFocusLost => false; public ScoreAccessibleReplayPlayer(Score score) - : base(score, false, false) + : base(score, new PlayerConfiguration + { + AllowPause = false, + ShowResults = false, + }) { } } diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneOutOfOrderHits.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneOutOfOrderHits.cs index cecac38f70..18891f8c58 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneOutOfOrderHits.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneOutOfOrderHits.cs @@ -176,7 +176,11 @@ namespace osu.Game.Rulesets.Mania.Tests protected override bool PauseOnFocusLost => false; public ScoreAccessibleReplayPlayer(Score score) - : base(score, false, false) + : base(score, new PlayerConfiguration + { + AllowPause = false, + ShowResults = false, + }) { } } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs index 3ff37c4147..a768626005 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs @@ -74,7 +74,11 @@ namespace osu.Game.Rulesets.Osu.Tests private readonly bool userHasCustomColours; public ExposedPlayer(bool userHasCustomColours) - : base(false, false) + : base(new PlayerConfiguration + { + AllowPause = false, + ShowResults = false, + }) { this.userHasCustomColours = userHasCustomColours; } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOutOfOrderHits.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOutOfOrderHits.cs index 32a36ab317..296b421a11 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOutOfOrderHits.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOutOfOrderHits.cs @@ -439,7 +439,11 @@ namespace osu.Game.Rulesets.Osu.Tests protected override bool PauseOnFocusLost => false; public ScoreAccessibleReplayPlayer(Score score) - : base(score, false, false) + : base(score, new PlayerConfiguration + { + AllowPause = false, + ShowResults = false, + }) { } } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs index 0164fb8bf4..2cc031405e 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs @@ -378,7 +378,11 @@ namespace osu.Game.Rulesets.Osu.Tests protected override bool PauseOnFocusLost => false; public ScoreAccessibleReplayPlayer(Score score) - : base(score, false, false) + : base(score, new PlayerConfiguration + { + AllowPause = false, + ShowResults = false, + }) { } } diff --git a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs index e8462088f1..f07f1c2fb0 100644 --- a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs +++ b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs @@ -37,8 +37,8 @@ namespace osu.Game.Screens.Multi.Play [Resolved] private IBindable ruleset { get; set; } - public TimeshiftPlayer(PlaylistItem playlistItem, bool allowPause = true) - : base(allowPause) + public TimeshiftPlayer(PlaylistItem playlistItem, PlayerConfiguration configuration = null) + : base(configuration) { PlaylistItem = playlistItem; } diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs index c6d44686b5..4b878f43d0 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs @@ -12,6 +12,7 @@ using osu.Game.Online.Multiplayer; using osu.Game.Online.RealtimeMultiplayer; using osu.Game.Scoring; using osu.Game.Screens.Multi.Play; +using osu.Game.Screens.Play; using osu.Game.Screens.Ranking; namespace osu.Game.Screens.Multi.RealtimeMultiplayer @@ -31,7 +32,11 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer private readonly ManualResetEventSlim startedEvent = new ManualResetEventSlim(); public RealtimePlayer(PlaylistItem playlistItem) - : base(playlistItem, false) + : base(playlistItem, new PlayerConfiguration + { + AllowPause = false, + AllowRestart = false, + }) { } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index c539dff5d9..a1c91ab26b 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -128,18 +128,14 @@ namespace osu.Game.Screens.Play ///
protected virtual bool CheckModsAllowFailure() => Mods.Value.OfType().All(m => m.PerformFail()); - private readonly bool allowPause; - private readonly bool showResults; + public readonly PlayerConfiguration Configuration; /// /// Create a new player instance. /// - /// Whether pausing should be allowed. If not allowed, attempting to pause will quit. - /// Whether results screen should be pushed on completion. - public Player(bool allowPause = true, bool showResults = true) + public Player(PlayerConfiguration configuration = null) { - this.allowPause = allowPause; - this.showResults = showResults; + this.Configuration = configuration ??= new PlayerConfiguration(); } private GameplayBeatmap gameplayBeatmap; @@ -317,59 +313,77 @@ namespace osu.Game.Screens.Play } }; - private Drawable createOverlayComponents(WorkingBeatmap working) => new Container + private Drawable createOverlayComponents(WorkingBeatmap working) { - RelativeSizeAxes = Axes.Both, - Children = new[] + var container = new Container { - DimmableStoryboard.OverlayLayerContainer.CreateProxy(), - BreakOverlay = new BreakOverlay(working.Beatmap.BeatmapInfo.LetterboxInBreaks, ScoreProcessor) + RelativeSizeAxes = Axes.Both, + Children = new[] { - Clock = DrawableRuleset.FrameStableClock, - ProcessCustomClock = false, - Breaks = working.Beatmap.Breaks - }, - // display the cursor above some HUD elements. - DrawableRuleset.Cursor?.CreateProxy() ?? new Container(), - DrawableRuleset.ResumeOverlay?.CreateProxy() ?? new Container(), - HUDOverlay = new HUDOverlay(ScoreProcessor, HealthProcessor, DrawableRuleset, Mods.Value) - { - HoldToQuit = + DimmableStoryboard.OverlayLayerContainer.CreateProxy(), + BreakOverlay = new BreakOverlay(working.Beatmap.BeatmapInfo.LetterboxInBreaks, ScoreProcessor) { - Action = performUserRequestedExit, - IsPaused = { BindTarget = GameplayClockContainer.IsPaused } + Clock = DrawableRuleset.FrameStableClock, + ProcessCustomClock = false, + Breaks = working.Beatmap.Breaks }, - PlayerSettingsOverlay = { PlaybackSettings = { UserPlaybackRate = { BindTarget = GameplayClockContainer.UserPlaybackRate } } }, - KeyCounter = + // display the cursor above some HUD elements. + DrawableRuleset.Cursor?.CreateProxy() ?? new Container(), + DrawableRuleset.ResumeOverlay?.CreateProxy() ?? new Container(), + HUDOverlay = new HUDOverlay(ScoreProcessor, HealthProcessor, DrawableRuleset, Mods.Value) { - AlwaysVisible = { BindTarget = DrawableRuleset.HasReplayLoaded }, - IsCounting = false + HoldToQuit = + { + Action = performUserRequestedExit, + IsPaused = { BindTarget = GameplayClockContainer.IsPaused } + }, + PlayerSettingsOverlay = { PlaybackSettings = { UserPlaybackRate = { BindTarget = GameplayClockContainer.UserPlaybackRate } } }, + KeyCounter = + { + AlwaysVisible = { BindTarget = DrawableRuleset.HasReplayLoaded }, + IsCounting = false + }, + RequestSeek = time => + { + GameplayClockContainer.Seek(time); + GameplayClockContainer.Start(); + }, + Anchor = Anchor.Centre, + Origin = Anchor.Centre }, - RequestSeek = time => + skipOverlay = new SkipOverlay(DrawableRuleset.GameplayStartTime) { - GameplayClockContainer.Seek(time); - GameplayClockContainer.Start(); + RequestSkip = GameplayClockContainer.Skip }, - Anchor = Anchor.Centre, - Origin = Anchor.Centre - }, - skipOverlay = new SkipOverlay(DrawableRuleset.GameplayStartTime) - { - RequestSkip = GameplayClockContainer.Skip - }, - FailOverlay = new FailOverlay - { - OnRetry = Restart, - OnQuit = performUserRequestedExit, - }, - PauseOverlay = new PauseOverlay - { - OnResume = Resume, - Retries = RestartCount, - OnRetry = Restart, - OnQuit = performUserRequestedExit, - }, - new HotkeyRetryOverlay + FailOverlay = new FailOverlay + { + OnRetry = Restart, + OnQuit = performUserRequestedExit, + }, + PauseOverlay = new PauseOverlay + { + OnResume = Resume, + Retries = RestartCount, + OnRetry = Restart, + OnQuit = performUserRequestedExit, + }, + new HotkeyExitOverlay + { + Action = () => + { + if (!this.IsCurrentScreen()) return; + + fadeOut(true); + performImmediateExit(); + }, + }, + failAnimation = new FailAnimation(DrawableRuleset) { OnComplete = onFailComplete, }, + } + }; + + if (Configuration.AllowRestart) + { + container.Add(new HotkeyRetryOverlay { Action = () => { @@ -378,20 +392,11 @@ namespace osu.Game.Screens.Play fadeOut(true); Restart(); }, - }, - new HotkeyExitOverlay - { - Action = () => - { - if (!this.IsCurrentScreen()) return; - - fadeOut(true); - performImmediateExit(); - }, - }, - failAnimation = new FailAnimation(DrawableRuleset) { OnComplete = onFailComplete, }, + }); } - }; + + return container; + } private void onBreakTimeChanged(ValueChangedEvent isBreakTime) { @@ -490,6 +495,9 @@ namespace osu.Game.Screens.Play ///
public void Restart() { + if (!Configuration.AllowRestart) + return; + // at the point of restarting the track should either already be paused or the volume should be zero. // stopping here is to ensure music doesn't become audible after exiting back to PlayerLoader. musicController.Stop(); @@ -529,7 +537,7 @@ namespace osu.Game.Screens.Play ValidForResume = false; - if (!showResults) return; + if (!Configuration.ShowResults) return; scoreSubmissionTask ??= Task.Run(async () => { @@ -628,7 +636,7 @@ namespace osu.Game.Screens.Play private bool canPause => // must pass basic screen conditions (beatmap loaded, instance allows pause) - LoadedBeatmapSuccessfully && allowPause && ValidForResume + LoadedBeatmapSuccessfully && Configuration.AllowPause && ValidForResume // replays cannot be paused and exit immediately && !DrawableRuleset.HasReplayLoaded.Value // cannot pause if we are already in a fail state diff --git a/osu.Game/Screens/Play/PlayerConfiguration.cs b/osu.Game/Screens/Play/PlayerConfiguration.cs new file mode 100644 index 0000000000..475a234679 --- /dev/null +++ b/osu.Game/Screens/Play/PlayerConfiguration.cs @@ -0,0 +1,23 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Screens.Play +{ + public class PlayerConfiguration + { + /// + /// Whether pausing should be allowed. If not allowed, attempting to pause will quit. + /// + public bool AllowPause { get; set; } = true; + + /// + /// Whether results screen should be pushed on completion. + /// + public bool ShowResults { get; set; } = true; + + /// + /// Whether the player should be allowed to trigger a restart. + /// + public bool AllowRestart { get; set; } = true; + } +} diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs index a07213cb33..e23cc22929 100644 --- a/osu.Game/Screens/Play/ReplayPlayer.cs +++ b/osu.Game/Screens/Play/ReplayPlayer.cs @@ -16,8 +16,8 @@ namespace osu.Game.Screens.Play // Disallow replays from failing. (see https://github.com/ppy/osu/issues/6108) protected override bool CheckModsAllowFailure() => false; - public ReplayPlayer(Score score, bool allowPause = true, bool showResults = true) - : base(allowPause, showResults) + public ReplayPlayer(Score score, PlayerConfiguration configuration = null) + : base(configuration) { Score = score; } diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index 528a1842af..8a12427798 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -164,15 +164,18 @@ namespace osu.Game.Screens.Ranking { buttons.Add(new RetryButton { Width = 300 }); - AddInternal(new HotkeyRetryOverlay + if (player?.Configuration.AllowRestart == true) { - Action = () => + AddInternal(new HotkeyRetryOverlay { - if (!this.IsCurrentScreen()) return; + Action = () => + { + if (!this.IsCurrentScreen()) return; - player?.Restart(); - }, - }); + player?.Restart(); + }, + }); + } } } diff --git a/osu.Game/Tests/Visual/TestPlayer.cs b/osu.Game/Tests/Visual/TestPlayer.cs index f016d29f38..f47391ce6a 100644 --- a/osu.Game/Tests/Visual/TestPlayer.cs +++ b/osu.Game/Tests/Visual/TestPlayer.cs @@ -37,7 +37,11 @@ namespace osu.Game.Tests.Visual public readonly List Results = new List(); public TestPlayer(bool allowPause = true, bool showResults = true, bool pauseOnFocusLost = false) - : base(allowPause, showResults) + : base(new PlayerConfiguration + { + AllowPause = allowPause, + ShowResults = showResults + }) { PauseOnFocusLost = pauseOnFocusLost; } From f9fd909187c3839101e3c52aa476e2460b3e0778 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 23 Dec 2020 18:07:38 +0900 Subject: [PATCH 509/818] Fix missed inspections --- osu.Game/Screens/Play/Player.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index a1c91ab26b..f1fb27e154 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -135,7 +135,7 @@ namespace osu.Game.Screens.Play ///
public Player(PlayerConfiguration configuration = null) { - this.Configuration = configuration ??= new PlayerConfiguration(); + Configuration = configuration ?? new PlayerConfiguration(); } private GameplayBeatmap gameplayBeatmap; From 94e4928c4b5feb2bb404173e27fadedbb5d5468d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 23 Dec 2020 11:27:15 +0100 Subject: [PATCH 510/818] Bring back accidentally-removed license header --- .../Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs index 7b375ca475..6331d324a6 100644 --- a/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs @@ -1,4 +1,4 @@ - +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. #nullable enable From ea5da53597e2d1a57b76b6f4d80429645ccc6657 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Wed, 23 Dec 2020 13:31:27 +0100 Subject: [PATCH 511/818] Handle URL links with the osu scheme. --- osu.Android/OsuGameActivity.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Android/OsuGameActivity.cs b/osu.Android/OsuGameActivity.cs index e801c2ca6e..55484c439a 100644 --- a/osu.Android/OsuGameActivity.cs +++ b/osu.Android/OsuGameActivity.cs @@ -16,8 +16,11 @@ namespace osu.Android { [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullUser, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = false)] [IntentFilter(new[] { Intent.ActionDefault, Intent.ActionSend }, Categories = new[] { Intent.CategoryDefault }, DataPathPatterns = new[] { ".*\\.osz", ".*\\.osk" }, DataMimeType = "application/*")] + [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryBrowsable, Intent.CategoryDefault }, DataScheme = osu_url_scheme)] public class OsuGameActivity : AndroidGameActivity { + private const string osu_url_scheme = "osu"; + private OsuGameAndroid game; protected override Framework.Game CreateGame() => game = new OsuGameAndroid(this); @@ -49,6 +52,8 @@ namespace osu.Android case Intent.ActionDefault: if (intent.Scheme == ContentResolver.SchemeContent) handleImportFromUri(intent.Data); + else if (intent.Scheme == osu_url_scheme) + Task.Run(() => game.HandleLink(intent.DataString)); break; case Intent.ActionSend: From 78b8c60f1949aef5c5a63febcf46968038dd9287 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Wed, 23 Dec 2020 13:38:33 +0100 Subject: [PATCH 512/818] Opt for SingleInstance launch mode --- osu.Android/OsuGameActivity.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Android/OsuGameActivity.cs b/osu.Android/OsuGameActivity.cs index 55484c439a..dfdecdca2e 100644 --- a/osu.Android/OsuGameActivity.cs +++ b/osu.Android/OsuGameActivity.cs @@ -14,7 +14,7 @@ using osu.Framework.Android; namespace osu.Android { - [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullUser, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = false)] + [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullUser, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = false, LaunchMode = LaunchMode.SingleInstance)] [IntentFilter(new[] { Intent.ActionDefault, Intent.ActionSend }, Categories = new[] { Intent.CategoryDefault }, DataPathPatterns = new[] { ".*\\.osz", ".*\\.osk" }, DataMimeType = "application/*")] [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryBrowsable, Intent.CategoryDefault }, DataScheme = osu_url_scheme)] public class OsuGameActivity : AndroidGameActivity From 582b0d2a7467f54f4ae125f79fe6c5611fb88f66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 23 Dec 2020 13:47:28 +0100 Subject: [PATCH 513/818] Revert logic to be closer to original Note the reversal of the order of operations in `endHandlingTrack()` (done for extra safety, to ensure no more value changed events can be fired at the point of cancelling looping). --- osu.Game/Screens/Multi/Match/RoomSubScreen.cs | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Multi/Match/RoomSubScreen.cs b/osu.Game/Screens/Multi/Match/RoomSubScreen.cs index 4f5d2a5b3e..b9d7408946 100644 --- a/osu.Game/Screens/Multi/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/RoomSubScreen.cs @@ -41,27 +41,24 @@ namespace osu.Game.Screens.Multi.Match managerUpdated = beatmapManager.ItemUpdated.GetBoundCopy(); managerUpdated.BindValueChanged(beatmapUpdated); - - if (music != null) - music.TrackChanged += applyToTrack; } public override void OnEntering(IScreen last) { base.OnEntering(last); - applyToTrack(); + beginHandlingTrack(); } public override void OnSuspending(IScreen next) { - resetTrack(); + endHandlingTrack(); base.OnSuspending(next); } public override void OnResuming(IScreen last) { base.OnResuming(last); - applyToTrack(); + beginHandlingTrack(); } public override bool OnExiting(IScreen next) @@ -69,7 +66,7 @@ namespace osu.Game.Screens.Multi.Match RoomManager?.PartRoom(); Mods.Value = Array.Empty(); - resetTrack(); + endHandlingTrack(); return base.OnExiting(next); } @@ -98,7 +95,18 @@ namespace osu.Game.Screens.Multi.Match Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap); } - private void applyToTrack(WorkingBeatmap _ = default, TrackChangeDirection __ = default) + private void beginHandlingTrack() + { + Beatmap.BindValueChanged(applyLoopingToTrack, true); + } + + private void endHandlingTrack() + { + Beatmap.ValueChanged -= applyLoopingToTrack; + cancelTrackLooping(); + } + + private void applyLoopingToTrack(ValueChangedEvent _ = null) { if (!this.IsCurrentScreen()) return; @@ -114,7 +122,7 @@ namespace osu.Game.Screens.Multi.Match } } - private void resetTrack() + private void cancelTrackLooping() { var track = Beatmap?.Value?.Track; From c5692a5d6aa51c51513e8a34c5ac023cb9326847 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 23 Dec 2020 14:18:24 +0100 Subject: [PATCH 514/818] Re-enable carousel selection after error --- .../Multi/RealtimeMultiplayer/RealtimeMatchSongSelect.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSongSelect.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSongSelect.cs index 4a8e398008..8f317800e3 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSongSelect.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSongSelect.cs @@ -65,7 +65,10 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer if (t.IsCompletedSuccessfully) this.Exit(); else + { Logger.Log($"Could not use current beatmap ({t.Exception?.Message})", level: LogLevel.Important); + Carousel.AllowSelection = true; + } }); }); } From 4296f61d6cbe0a8d25a94a9ae22274139c8b1b1a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 23 Dec 2020 22:39:14 +0900 Subject: [PATCH 515/818] Tidy up event flow of change settings call --- .../RealtimeMultiplayer/StatefulMultiplayerClient.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs index 6331d324a6..dc999ee2be 100644 --- a/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs @@ -369,7 +369,6 @@ namespace osu.Game.Online.RealtimeMultiplayer if (Room == null) return; - // Update a few properties of the room instantaneously. Schedule(() => { if (Room == null) @@ -377,6 +376,7 @@ namespace osu.Game.Online.RealtimeMultiplayer Debug.Assert(apiRoom != null); + // Update a few properties of the room instantaneously. Room.Settings = settings; apiRoom.Name.Value = Room.Settings.Name; @@ -385,12 +385,12 @@ namespace osu.Game.Online.RealtimeMultiplayer apiRoom.Playlist.Clear(); RoomChanged?.Invoke(); + + var req = new GetBeatmapSetRequest(settings.BeatmapID, BeatmapSetLookupType.BeatmapId); + req.Success += res => updatePlaylist(settings, res); + + api.Queue(req); }); - - var req = new GetBeatmapSetRequest(settings.BeatmapID, BeatmapSetLookupType.BeatmapId); - req.Success += res => updatePlaylist(settings, res); - - api.Queue(req); } private void updatePlaylist(MultiplayerRoomSettings settings, APIBeatmapSet onlineSet) From 980e85ce25f087f28306c530e15b3d7916b2b5bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 23 Dec 2020 15:51:26 +0100 Subject: [PATCH 516/818] Refactor player exit logic to convey intention better --- .../RealtimeMultiplayer/RealtimePlayer.cs | 4 ++-- osu.Game/Screens/Play/Player.cs | 22 ++++++++++++++----- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs index 0d8e636450..e467e5fcf8 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs @@ -61,7 +61,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer startedEvent.Set(); // messaging to the user about this disconnect will be provided by the RealtimeMatchSubScreen. - Schedule(PerformImmediateExit); + Schedule(() => PerformExit(false)); } }, true); @@ -71,7 +71,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer { Logger.Log("Failed to start the multiplayer match in time.", LoggingTarget.Runtime, LogLevel.Important); - Schedule(PerformImmediateExit); + Schedule(() => PerformExit(false)); } Debug.Assert(client.Room != null); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index c6265c48d2..2bc84ce5d5 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -386,7 +386,7 @@ namespace osu.Game.Screens.Play if (!this.IsCurrentScreen()) return; fadeOut(true); - PerformImmediateExit(); + PerformExit(true); }, }, failAnimation = new FailAnimation(DrawableRuleset) { OnComplete = onFailComplete, }, @@ -458,20 +458,30 @@ namespace osu.Game.Screens.Play return playable; } - protected void PerformImmediateExit() + /// + /// Exits the . + /// + /// + /// Whether the exit is requested by the user, or a higher-level game component. + /// Pausing is allowed only in the former case. + /// + protected void PerformExit(bool userRequested) { // if a restart has been requested, cancel any pending completion (user has shown intent to restart). completionProgressDelegate?.Cancel(); ValidForResume = false; - performUserRequestedExit(); + if (!this.IsCurrentScreen()) return; + + if (userRequested) + performUserRequestedExit(); + else + this.Exit(); } private void performUserRequestedExit() { - if (!this.IsCurrentScreen()) return; - if (ValidForResume && HasFailed && !FailOverlay.IsPresent) { failAnimation.FinishTransforms(true); @@ -498,7 +508,7 @@ namespace osu.Game.Screens.Play RestartRequested?.Invoke(); if (this.IsCurrentScreen()) - PerformImmediateExit(); + PerformExit(true); else this.MakeCurrent(); } From 3b0bf1136642c40fd189cbbd1940d3121f286e0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 23 Dec 2020 17:01:01 +0100 Subject: [PATCH 517/818] Fix JoinRoom failing to return canceled token As it turns out, `Task.FromCanceled` expects to receive an already cancelled `CancellationToken`, which `CancellationToken.None` is not. --- .../Online/RealtimeMultiplayer/RealtimeMultiplayerClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/RealtimeMultiplayer/RealtimeMultiplayerClient.cs b/osu.Game/Online/RealtimeMultiplayer/RealtimeMultiplayerClient.cs index 5cbf3be8ca..026b7176d1 100644 --- a/osu.Game/Online/RealtimeMultiplayer/RealtimeMultiplayerClient.cs +++ b/osu.Game/Online/RealtimeMultiplayer/RealtimeMultiplayerClient.cs @@ -122,7 +122,7 @@ namespace osu.Game.Online.RealtimeMultiplayer protected override Task JoinRoom(long roomId) { if (!isConnected.Value) - return Task.FromCanceled(CancellationToken.None); + return Task.FromCanceled(new CancellationToken(true)); return connection.InvokeAsync(nameof(IMultiplayerServer.JoinRoom), roomId); } From e4959489b70d61f76c6f25e0c7125fd24d96becc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 23 Dec 2020 17:08:28 +0100 Subject: [PATCH 518/818] Improve user-facing error messages in room settings --- .../Match/RealtimeMatchSettingsOverlay.cs | 3 ++- .../Multi/RealtimeMultiplayer/RealtimeRoomManager.cs | 7 +++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeMatchSettingsOverlay.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeMatchSettingsOverlay.cs index 3e495b490f..a93b1b09d1 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeMatchSettingsOverlay.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeMatchSettingsOverlay.cs @@ -5,6 +5,7 @@ using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Extensions.ExceptionExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -299,7 +300,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Match if (t.IsCompletedSuccessfully) onSuccess(currentRoom.Value); else - onError(t.Exception?.Message ?? "Error changing settings."); + onError(t.Exception?.AsSingular().Message ?? "Error changing settings."); })); } else diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs index cd337bbb55..eb6e5fad07 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.Threading.Tasks; 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; @@ -91,11 +92,13 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer Schedule(() => onSuccess?.Invoke(room)); else { + const string message = "Failed to join multiplayer room."; + if (t.Exception != null) - Logger.Error(t.Exception, "Failed to join multiplayer room."); + Logger.Error(t.Exception, message); PartRoom(); - Schedule(() => onError?.Invoke(t.Exception?.ToString() ?? string.Empty)); + Schedule(() => onError?.Invoke(t.Exception?.AsSingular().Message ?? message)); } }); } From e89583d732e5c9996534bdb89a8ee42470cbe530 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Dec 2020 01:33:19 +0900 Subject: [PATCH 519/818] Prefer connecting to dev server when running in DEBUG --- osu.Game/Online/API/APIAccess.cs | 8 +++++++- .../RealtimeMultiplayer/RealtimeMultiplayerClient.cs | 4 ++++ osu.Game/Online/Spectator/SpectatorStreamingClient.cs | 4 ++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index fe500b9548..ca457ccf71 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -26,9 +26,15 @@ namespace osu.Game.Online.API private readonly OAuth authentication; +#if DEBUG + public string Endpoint => @"https://dev.ppy.sh"; + private const string client_secret = @"3LP2mhUrV89xxzD1YKNndXHEhWWCRLPNKioZ9ymT"; +#else public string Endpoint => @"https://osu.ppy.sh"; - private const string client_id = @"5"; private const string client_secret = @"FGc9GAtyHzeQDshWP5Ah7dega8hJACAJpQtw6OXk"; +#endif + + private const string client_id = @"5"; private readonly Queue queue = new Queue(); diff --git a/osu.Game/Online/RealtimeMultiplayer/RealtimeMultiplayerClient.cs b/osu.Game/Online/RealtimeMultiplayer/RealtimeMultiplayerClient.cs index 75bb578a29..4ec5b9af40 100644 --- a/osu.Game/Online/RealtimeMultiplayer/RealtimeMultiplayerClient.cs +++ b/osu.Game/Online/RealtimeMultiplayer/RealtimeMultiplayerClient.cs @@ -19,7 +19,11 @@ namespace osu.Game.Online.RealtimeMultiplayer { public class RealtimeMultiplayerClient : StatefulMultiplayerClient { +#if DEBUG + private const string endpoint = "https://dev.ppy.sh/multiplayer"; +#else private const string endpoint = "https://spectator.ppy.sh/multiplayer"; +#endif public override IBindable IsConnected => isConnected; diff --git a/osu.Game/Online/Spectator/SpectatorStreamingClient.cs b/osu.Game/Online/Spectator/SpectatorStreamingClient.cs index 0167a5d025..c9203b595e 100644 --- a/osu.Game/Online/Spectator/SpectatorStreamingClient.cs +++ b/osu.Game/Online/Spectator/SpectatorStreamingClient.cs @@ -104,7 +104,11 @@ namespace osu.Game.Online.Spectator } } +#if DEBUG + private const string endpoint = "https://dev.ppy.sh/spectator"; +#else private const string endpoint = "https://spectator.ppy.sh/spectator"; +#endif protected virtual async Task Connect() { From 9843da59f4866918391902de30125577b60c1464 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 23 Dec 2020 20:29:17 +0100 Subject: [PATCH 520/818] Fix intermittent test fail due to duplicate user `TestSceneRealtimeReadyButton` was manually adding `API.LocalUser`, which wasn't actually needed. The base `RealtimeMultiplayerTestScene` by default creates a new room as `API.LocalUser`, therefore automatically adding that user to the room - and as such there is no need to add them manually unless the `joinRoom` ctor param is specified as `false`. --- .../Visual/RealtimeMultiplayer/TestSceneRealtimeReadyButton.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeReadyButton.cs b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeReadyButton.cs index b7cd81fb32..e9d3ddb32d 100644 --- a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeReadyButton.cs +++ b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeReadyButton.cs @@ -55,8 +55,6 @@ namespace osu.Game.Tests.Visual.RealtimeMultiplayer } } }; - - Client.AddUser(API.LocalUser.Value); }); [Test] From 47020c888731c2370d6f81fbfe9f11d6e7fcfdc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 23 Dec 2020 21:00:47 +0100 Subject: [PATCH 521/818] Add failing test cases --- .../TestSceneRealtimeMultiplayer.cs | 24 +++++++++++++++++++ .../TestRealtimeMultiplayerClient.cs | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeMultiplayer.cs b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeMultiplayer.cs index 80955ca380..5cf80df6aa 100644 --- a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeMultiplayer.cs +++ b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeMultiplayer.cs @@ -1,7 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using NUnit.Framework; using osu.Game.Screens.Multi.Components; +using osu.Game.Users; namespace osu.Game.Tests.Visual.RealtimeMultiplayer { @@ -15,6 +17,28 @@ namespace osu.Game.Tests.Visual.RealtimeMultiplayer AddUntilStep("wait for loaded", () => multi.IsLoaded); } + [Test] + public void TestOneUserJoinedMultipleTimes() + { + var user = new User { Id = 33 }; + + AddRepeatStep("add user multiple times", () => Client.AddUser(user), 3); + + AddAssert("room has 2 users", () => Client.Room?.Users.Count == 2); + } + + [Test] + public void TestOneUserLeftMultipleTimes() + { + var user = new User { Id = 44 }; + + AddStep("add user", () => Client.AddUser(user)); + AddAssert("room has 2 users", () => Client.Room?.Users.Count == 2); + + AddRepeatStep("remove user multiple times", () => Client.RemoveUser(user), 3); + AddAssert("room has 1 user", () => Client.Room?.Users.Count == 1); + } + private class TestRealtimeMultiplayer : Screens.Multi.RealtimeMultiplayer.RealtimeMultiplayer { protected override RoomManager CreateRoomManager() => new TestRealtimeRoomManager(); diff --git a/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeMultiplayerClient.cs b/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeMultiplayerClient.cs index de52633c88..52047016e2 100644 --- a/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeMultiplayerClient.cs @@ -31,7 +31,7 @@ namespace osu.Game.Tests.Visual.RealtimeMultiplayer public void RemoveUser(User user) { Debug.Assert(Room != null); - ((IMultiplayerClient)this).UserLeft(Room.Users.Single(u => u.User == user)); + ((IMultiplayerClient)this).UserLeft(new MultiplayerRoomUser(user.Id)); Schedule(() => { From a71496bc4e9f0386d13b295eb92496f33144e5b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 23 Dec 2020 20:34:19 +0100 Subject: [PATCH 522/818] Sanity check received user joined messages While test failures fixed in 9843da5 were a shortcoming of the test, they exposed a potential vulnerable point of the multiplayer client logic. In case of unreliable message delivery it is not unreasonable that duplicate messages might arrive, in which case the same scenario that failed in the tests could crash the game. To ensure that is not the case, explicitly screen each new joined user against the room user list, to ensure that duplicates do not show up. `UserLeft` is already tolerant in that respect (if a user is requested to be removed twice by the server, the second removal just won't do anything). --- .../Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs index 6331d324a6..e8dbeda9cb 100644 --- a/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs @@ -226,6 +226,10 @@ namespace osu.Game.Online.RealtimeMultiplayer if (Room == null) return; + // for sanity, ensure that there can be no duplicate users in the room user list. + if (Room.Users.Any(existing => existing.UserID == user.UserID)) + return; + Room.Users.Add(user); RoomChanged?.Invoke(); From 05d9f2376224b42d9ee56f8ee094374a15dc2801 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 23 Dec 2020 16:38:18 +0100 Subject: [PATCH 523/818] Move out create room button to separate class --- osu.Game/Screens/Multi/CreateRoomButton.cs | 31 ++++++++++++++++++++++ osu.Game/Screens/Multi/Multiplayer.cs | 23 ---------------- 2 files changed, 31 insertions(+), 23 deletions(-) create mode 100644 osu.Game/Screens/Multi/CreateRoomButton.cs diff --git a/osu.Game/Screens/Multi/CreateRoomButton.cs b/osu.Game/Screens/Multi/CreateRoomButton.cs new file mode 100644 index 0000000000..b501de46ef --- /dev/null +++ b/osu.Game/Screens/Multi/CreateRoomButton.cs @@ -0,0 +1,31 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Screens.Multi.Match.Components; +using osuTK; + +namespace osu.Game.Screens.Multi +{ + public class CreateRoomButton : PurpleTriangleButton + { + public CreateRoomButton() + { + Size = new Vector2(150, Header.HEIGHT - 20); + Margin = new MarginPadding + { + Top = 10, + Right = 10 + OsuScreen.HORIZONTAL_OVERFLOW_PADDING, + }; + } + + [BackgroundDependencyLoader] + private void load() + { + Triangles.TriangleScale = 1.5f; + + Text = "Create room"; + } + } +} diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index de2e0d58c9..a957eb6824 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.cs @@ -21,9 +21,7 @@ using osu.Game.Screens.Multi.Components; using osu.Game.Screens.Multi.Lounge; using osu.Game.Screens.Multi.Lounge.Components; using osu.Game.Screens.Multi.Match; -using osu.Game.Screens.Multi.Match.Components; using osu.Game.Users; -using osuTK; namespace osu.Game.Screens.Multi { @@ -332,26 +330,5 @@ namespace osu.Game.Screens.Multi protected override double TransformDuration => 200; } } - - public class CreateRoomButton : PurpleTriangleButton - { - public CreateRoomButton() - { - Size = new Vector2(150, Header.HEIGHT - 20); - Margin = new MarginPadding - { - Top = 10, - Right = 10 + HORIZONTAL_OVERFLOW_PADDING, - }; - } - - [BackgroundDependencyLoader] - private void load() - { - Triangles.TriangleScale = 1.5f; - - Text = "Create room"; - } - } } } From c13acb609aed79df56e883586697be71b72e74c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 23 Dec 2020 21:50:29 +0100 Subject: [PATCH 524/818] Move out sizing logic to multiplayer screen --- osu.Game/Screens/Multi/CreateRoomButton.cs | 12 ------------ osu.Game/Screens/Multi/Multiplayer.cs | 7 +++++++ 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/Multi/CreateRoomButton.cs b/osu.Game/Screens/Multi/CreateRoomButton.cs index b501de46ef..9e53904510 100644 --- a/osu.Game/Screens/Multi/CreateRoomButton.cs +++ b/osu.Game/Screens/Multi/CreateRoomButton.cs @@ -2,24 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Framework.Graphics; using osu.Game.Screens.Multi.Match.Components; -using osuTK; namespace osu.Game.Screens.Multi { public class CreateRoomButton : PurpleTriangleButton { - public CreateRoomButton() - { - Size = new Vector2(150, Header.HEIGHT - 20); - Margin = new MarginPadding - { - Top = 10, - Right = 10 + OsuScreen.HORIZONTAL_OVERFLOW_PADDING, - }; - } - [BackgroundDependencyLoader] private void load() { diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index a957eb6824..c820eae51f 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.cs @@ -22,6 +22,7 @@ using osu.Game.Screens.Multi.Lounge; using osu.Game.Screens.Multi.Lounge.Components; using osu.Game.Screens.Multi.Match; using osu.Game.Users; +using osuTK; namespace osu.Game.Screens.Multi { @@ -131,6 +132,12 @@ namespace osu.Game.Screens.Multi { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, + Size = new Vector2(150, Header.HEIGHT - 20), + Margin = new MarginPadding + { + Top = 10, + Right = 10 + HORIZONTAL_OVERFLOW_PADDING, + }, Action = () => OpenNewRoom() }, RoomManager = CreateRoomManager() From 414f886b02d21c24ddaddc273ac747dc1d347b63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 23 Dec 2020 22:02:37 +0100 Subject: [PATCH 525/818] Split timeshift & multiplayer "create" buttons Multiplayer button gets new, different "Create match" text, and disable logic in case of a dropped connection to the multiplayer server. --- osu.Game/Screens/Multi/Multiplayer.cs | 18 ++++++++------- .../CreateRealtimeMatchButton.cs | 23 +++++++++++++++++++ .../RealtimeMultiplayer.cs | 3 +++ .../CreateTimeshiftRoomButton.cs} | 4 ++-- .../Multi/Timeshift/TimeshiftMultiplayer.cs | 3 +++ 5 files changed, 41 insertions(+), 10 deletions(-) create mode 100644 osu.Game/Screens/Multi/RealtimeMultiplayer/CreateRealtimeMatchButton.cs rename osu.Game/Screens/Multi/{CreateRoomButton.cs => Timeshift/CreateTimeshiftRoomButton.cs} (78%) diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index c820eae51f..a7d40a89d3 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.cs @@ -128,18 +128,18 @@ namespace osu.Game.Screens.Multi } }, new Header(screenStack), - createButton = new CreateRoomButton + createButton = CreateNewMultiplayerGameButton().With(button => { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Size = new Vector2(150, Header.HEIGHT - 20), - Margin = new MarginPadding + button.Anchor = Anchor.TopRight; + button.Origin = Anchor.TopRight; + button.Size = new Vector2(150, Header.HEIGHT - 20); + button.Margin = new MarginPadding { Top = 10, Right = 10 + HORIZONTAL_OVERFLOW_PADDING, - }, - Action = () => OpenNewRoom() - }, + }; + button.Action = () => OpenNewRoom(); + }), RoomManager = CreateRoomManager() } }; @@ -315,6 +315,8 @@ namespace osu.Game.Screens.Multi protected abstract LoungeSubScreen CreateLounge(); + protected abstract OsuButton CreateNewMultiplayerGameButton(); + private class MultiplayerWaveContainer : WaveContainer { protected override bool StartHidden => true; diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/CreateRealtimeMatchButton.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/CreateRealtimeMatchButton.cs new file mode 100644 index 0000000000..eda907f8cb --- /dev/null +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/CreateRealtimeMatchButton.cs @@ -0,0 +1,23 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Game.Online.RealtimeMultiplayer; +using osu.Game.Screens.Multi.Match.Components; + +namespace osu.Game.Screens.Multi.RealtimeMultiplayer +{ + public class CreateRealtimeMatchButton : PurpleTriangleButton + { + [BackgroundDependencyLoader] + private void load(StatefulMultiplayerClient multiplayerClient) + { + Triangles.TriangleScale = 1.5f; + + Text = "Create match"; + + ((IBindable)Enabled).BindTo(multiplayerClient.IsConnected); + } + } +} diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMultiplayer.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMultiplayer.cs index 6685cf52d6..6739a51fe8 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMultiplayer.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMultiplayer.cs @@ -5,6 +5,7 @@ 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.RealtimeMultiplayer; using osu.Game.Screens.Multi.Components; @@ -64,5 +65,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer protected override RoomManager CreateRoomManager() => new RealtimeRoomManager(); protected override LoungeSubScreen CreateLounge() => new RealtimeLoungeSubScreen(); + + protected override OsuButton CreateNewMultiplayerGameButton() => new CreateRealtimeMatchButton(); } } diff --git a/osu.Game/Screens/Multi/CreateRoomButton.cs b/osu.Game/Screens/Multi/Timeshift/CreateTimeshiftRoomButton.cs similarity index 78% rename from osu.Game/Screens/Multi/CreateRoomButton.cs rename to osu.Game/Screens/Multi/Timeshift/CreateTimeshiftRoomButton.cs index 9e53904510..bd9d667630 100644 --- a/osu.Game/Screens/Multi/CreateRoomButton.cs +++ b/osu.Game/Screens/Multi/Timeshift/CreateTimeshiftRoomButton.cs @@ -4,9 +4,9 @@ using osu.Framework.Allocation; using osu.Game.Screens.Multi.Match.Components; -namespace osu.Game.Screens.Multi +namespace osu.Game.Screens.Multi.Timeshift { - public class CreateRoomButton : PurpleTriangleButton + public class CreateTimeshiftRoomButton : PurpleTriangleButton { [BackgroundDependencyLoader] private void load() diff --git a/osu.Game/Screens/Multi/Timeshift/TimeshiftMultiplayer.cs b/osu.Game/Screens/Multi/Timeshift/TimeshiftMultiplayer.cs index 2ea4857799..d525a3800d 100644 --- a/osu.Game/Screens/Multi/Timeshift/TimeshiftMultiplayer.cs +++ b/osu.Game/Screens/Multi/Timeshift/TimeshiftMultiplayer.cs @@ -3,6 +3,7 @@ using osu.Framework.Logging; using osu.Framework.Screens; +using osu.Game.Graphics.UserInterface; using osu.Game.Screens.Multi.Components; using osu.Game.Screens.Multi.Lounge; using osu.Game.Screens.Multi.Match; @@ -47,5 +48,7 @@ namespace osu.Game.Screens.Multi.Timeshift protected override RoomManager CreateRoomManager() => new TimeshiftRoomManager(); protected override LoungeSubScreen CreateLounge() => new TimeshiftLoungeSubScreen(); + + protected override OsuButton CreateNewMultiplayerGameButton() => new CreateTimeshiftRoomButton(); } } From 06bc3cd54d448d5597a193d7024c036f25b9e081 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Thu, 24 Dec 2020 00:17:26 +0100 Subject: [PATCH 526/818] Apply review suggestions. --- osu.Android/OsuGameActivity.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Android/OsuGameActivity.cs b/osu.Android/OsuGameActivity.cs index dfdecdca2e..953c06f4e2 100644 --- a/osu.Android/OsuGameActivity.cs +++ b/osu.Android/OsuGameActivity.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.IO; +using System.Linq; using System.Threading.Tasks; using Android.App; using Android.Content; @@ -16,10 +17,10 @@ namespace osu.Android { [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullUser, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = false, LaunchMode = LaunchMode.SingleInstance)] [IntentFilter(new[] { Intent.ActionDefault, Intent.ActionSend }, Categories = new[] { Intent.CategoryDefault }, DataPathPatterns = new[] { ".*\\.osz", ".*\\.osk" }, DataMimeType = "application/*")] - [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryBrowsable, Intent.CategoryDefault }, DataScheme = osu_url_scheme)] + [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryBrowsable, Intent.CategoryDefault }, DataSchemes = new[] { "osu", "osump" })] public class OsuGameActivity : AndroidGameActivity { - private const string osu_url_scheme = "osu"; + private static readonly string[] osu_url_schemes = { "osu", "osump" }; private OsuGameAndroid game; @@ -52,8 +53,8 @@ namespace osu.Android case Intent.ActionDefault: if (intent.Scheme == ContentResolver.SchemeContent) handleImportFromUri(intent.Data); - else if (intent.Scheme == osu_url_scheme) - Task.Run(() => game.HandleLink(intent.DataString)); + else if (osu_url_schemes.Contains(intent.Scheme)) + game.HandleLink(intent.DataString); break; case Intent.ActionSend: From d6dadd12faa7c277befcdcefd0eecdc25196cf0c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Dec 2020 10:38:53 +0900 Subject: [PATCH 527/818] Send multiplayer user IDs via ctor for better thread safety --- .../RealtimeMultiplayer/RealtimeMatchSubScreen.cs | 10 +++++++++- .../Multi/RealtimeMultiplayer/RealtimePlayer.cs | 13 +++++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSubScreen.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSubScreen.cs index cdab1435c0..468908f83d 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSubScreen.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSubScreen.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Specialized; +using System.Diagnostics; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -188,7 +189,14 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer private void onPlaylistChanged(object sender, NotifyCollectionChangedEventArgs e) => SelectedItem.Value = Playlist.FirstOrDefault(); - private void onLoadRequested() => multiplayer?.Push(new PlayerLoader(() => new RealtimePlayer(SelectedItem.Value))); + private void onLoadRequested() + { + Debug.Assert(client.Room != null); + + int[] userIds = client.Room.Users.Where(u => u.State >= MultiplayerUserState.WaitingForLoad).Select(u => u.UserID).ToArray(); + + multiplayer?.Push(new PlayerLoader(() => new RealtimePlayer(SelectedItem.Value, userIds))); + } protected override void Dispose(bool isDisposing) { diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs index 7824b414f2..25543d3d6d 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs @@ -3,7 +3,6 @@ using System; using System.Diagnostics; -using System.Linq; using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; @@ -37,9 +36,17 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer [CanBeNull] private MultiplayerGameplayLeaderboard leaderboard; - public RealtimePlayer(PlaylistItem playlistItem) + private readonly int[] userIds; + + /// + /// Construct a multiplayer player. + /// + /// The playlist item to be played. + /// The users which are participating in this game. + public RealtimePlayer(PlaylistItem playlistItem, int[] userIds) : base(playlistItem, false) { + this.userIds = userIds; } [BackgroundDependencyLoader] @@ -65,8 +72,6 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer Debug.Assert(client.Room != null); - int[] userIds = client.Room.Users.Where(u => u.State >= MultiplayerUserState.WaitingForLoad).Select(u => u.UserID).ToArray(); - // todo: this should be implemented via a custom HUD implementation, and correctly masked to the main content area. LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(ScoreProcessor, userIds), HUDOverlay.Add); } From a411b26a09530bed75c404afe49d5b447bd5a863 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Dec 2020 10:51:24 +0900 Subject: [PATCH 528/818] Remove unnecessary clamp Co-authored-by: Joseph Madamba --- osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs index 3f770cfb5e..20c0818d03 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs @@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Osu.Mods var destination = e.MousePosition; FlashlightPosition = Interpolation.ValueAt( - Math.Clamp(Math.Abs(Clock.ElapsedFrameTime), 0, follow_delay), position, destination, 0, follow_delay, Easing.Out); + Math.Min(Math.Abs(Clock.ElapsedFrameTime), follow_delay), position, destination, 0, follow_delay, Easing.Out); return base.OnMouseMove(e); } From 61a5d3ef4ae8d7724eb02686a01a33e85cbc4e0c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Dec 2020 13:32:35 +0900 Subject: [PATCH 529/818] Remove double handling of restart allowance on results screen (already handled locally) --- osu.Game/Screens/Ranking/ResultsScreen.cs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index 8a12427798..528a1842af 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -164,18 +164,15 @@ namespace osu.Game.Screens.Ranking { buttons.Add(new RetryButton { Width = 300 }); - if (player?.Configuration.AllowRestart == true) + AddInternal(new HotkeyRetryOverlay { - AddInternal(new HotkeyRetryOverlay + Action = () => { - Action = () => - { - if (!this.IsCurrentScreen()) return; + if (!this.IsCurrentScreen()) return; - player?.Restart(); - }, - }); - } + player?.Restart(); + }, + }); } } From 1f80f01b53f4861a26ee8564bb7b81684cfad488 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Dec 2020 14:46:52 +0900 Subject: [PATCH 530/818] Add accuracy to frame bundle header --- osu.Game/Online/Spectator/FrameHeader.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Spectator/FrameHeader.cs b/osu.Game/Online/Spectator/FrameHeader.cs index b4988fecf9..135b356eda 100644 --- a/osu.Game/Online/Spectator/FrameHeader.cs +++ b/osu.Game/Online/Spectator/FrameHeader.cs @@ -14,6 +14,11 @@ namespace osu.Game.Online.Spectator [Serializable] public class FrameHeader { + /// + /// The current accuracy of the score. + /// + public double Accuracy { get; set; } + /// /// The current combo of the score. /// @@ -42,16 +47,18 @@ namespace osu.Game.Online.Spectator { Combo = score.Combo; MaxCombo = score.MaxCombo; + Accuracy = score.Accuracy; // copy for safety Statistics = new Dictionary(score.Statistics); } [JsonConstructor] - public FrameHeader(int combo, int maxCombo, Dictionary statistics, DateTimeOffset receivedTime) + public FrameHeader(int combo, int maxCombo, double accuracy, Dictionary statistics, DateTimeOffset receivedTime) { Combo = combo; MaxCombo = maxCombo; + Accuracy = accuracy; Statistics = statistics; ReceivedTime = receivedTime; } From d66e2183185bbedc26b2d7350499e71d3aebc0fb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Dec 2020 14:57:23 +0900 Subject: [PATCH 531/818] Source display accuracy from header and remove from ScoreProcessor function --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 15 +++++---------- .../Play/HUD/MultiplayerGameplayLeaderboard.cs | 4 ++-- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 4b2e2bf715..2024290460 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -193,7 +193,7 @@ namespace osu.Game.Rulesets.Scoring private void updateScore() { if (rollingMaxBaseScore != 0) - Accuracy.Value = baseScore / rollingMaxBaseScore; + Accuracy.Value = calculateAccuracyRatio(baseScore, true); TotalScore.Value = getScore(Mode.Value); } @@ -233,13 +233,13 @@ namespace osu.Game.Rulesets.Scoring } /// - /// Given a minimal set of inputs, return the computed score and accuracy for the tracked beatmap / mods combination, at the current point in time. + /// Given a minimal set of inputs, return the computed score for the tracked beatmap / mods combination, at the current point in time. /// /// The to compute the total score in. /// The maximum combo achievable in the beatmap. /// Statistics to be used for calculating accuracy, bonus score, etc. - /// The computed score and accuracy for provided inputs. - public (double score, double accuracy) GetScoreAndAccuracy(ScoringMode mode, int maxCombo, Dictionary statistics) + /// The computed score for provided inputs. + public double GetImmediateScore(ScoringMode mode, int maxCombo, Dictionary statistics) { // calculate base score from statistics pairs int computedBaseScore = 0; @@ -252,12 +252,7 @@ namespace osu.Game.Rulesets.Scoring computedBaseScore += Judgement.ToNumericResult(pair.Key) * pair.Value; } - double pointInTimeAccuracy = calculateAccuracyRatio(computedBaseScore, true); - double comboRatio = calculateComboRatio(maxCombo); - - double score = GetScore(mode, maxAchievableCombo, calculateAccuracyRatio(computedBaseScore), comboRatio, scoreResultCounts); - - return (score, pointInTimeAccuracy); + return GetScore(mode, maxAchievableCombo, calculateAccuracyRatio(computedBaseScore), calculateComboRatio(maxCombo), scoreResultCounts); } /// diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index 12321de442..c10ec9e004 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -122,8 +122,8 @@ namespace osu.Game.Screens.Play.HUD if (LastHeader == null) return; - (score.Value, accuracy.Value) = processor.GetScoreAndAccuracy(mode, LastHeader.MaxCombo, LastHeader.Statistics); - + score.Value = processor.GetImmediateScore(mode, LastHeader.MaxCombo, LastHeader.Statistics); + accuracy.Value = LastHeader.Accuracy; currentCombo.Value = LastHeader.Combo; } } From e86e9bfae625351e5323c9393e011190dd58c7b8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Dec 2020 15:32:55 +0900 Subject: [PATCH 532/818] Don't begin gameplay until all users are in a completely prepared state --- .../RealtimeMultiplayer/RealtimePlayer.cs | 39 ++++++++++++------- osu.Game/Screens/Play/Player.cs | 18 +++++++-- 2 files changed, 40 insertions(+), 17 deletions(-) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs index 085c52cb85..20b184bed3 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs @@ -3,12 +3,12 @@ using System; using System.Diagnostics; -using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Logging; +using osu.Game.Graphics.UserInterface; using osu.Game.Online.Multiplayer; using osu.Game.Online.RealtimeMultiplayer; using osu.Game.Scoring; @@ -33,13 +33,14 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer private IBindable isConnected; private readonly TaskCompletionSource resultsReady = new TaskCompletionSource(); - private readonly ManualResetEventSlim startedEvent = new ManualResetEventSlim(); [CanBeNull] private MultiplayerGameplayLeaderboard leaderboard; private readonly int[] userIds; + private LoadingLayer loadingDisplay; + /// /// Construct a multiplayer player. /// @@ -60,6 +61,12 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer client.MatchStarted += onMatchStarted; client.ResultsReady += onResultsReady; + ScoreProcessor.HasCompleted.BindValueChanged(completed => + { + // wait for server to tell us that results are ready (see SubmitScore implementation) + loadingDisplay.Show(); + }); + isConnected = client.IsConnected.GetBoundCopy(); isConnected.BindValueChanged(connected => { @@ -70,19 +77,20 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer } }, true); - client.ChangeState(MultiplayerUserState.Loaded) - .ContinueWith(task => failAndBail(task.Exception?.Message ?? "Server error"), TaskContinuationOptions.NotOnRanToCompletion); - - if (!startedEvent.Wait(TimeSpan.FromSeconds(30))) - { - failAndBail("Failed to start the multiplayer match in time."); - return; - } - Debug.Assert(client.Room != null); // todo: this should be implemented via a custom HUD implementation, and correctly masked to the main content area. LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(ScoreProcessor, userIds), HUDOverlay.Add); + + HUDOverlay.Add(loadingDisplay = new LoadingLayer(DrawableRuleset) { Depth = float.MaxValue }); + } + + protected override void StartGameplay() + { + // block base call, but let the server know we are ready to start. + loadingDisplay.Show(); + + client.ChangeState(MultiplayerUserState.Loaded).ContinueWith(task => failAndBail(task.Exception?.Message ?? "Server error"), TaskContinuationOptions.NotOnRanToCompletion); } private void failAndBail(string message = null) @@ -90,7 +98,6 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer if (!string.IsNullOrEmpty(message)) Logger.Log(message, LoggingTarget.Runtime, LogLevel.Important); - startedEvent.Set(); Schedule(() => PerformExit(false)); } @@ -112,7 +119,11 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer padding + HUDOverlay.TopScoringElementsHeight); } - private void onMatchStarted() => startedEvent.Set(); + private void onMatchStarted() => Scheduler.Add(() => + { + loadingDisplay.Hide(); + base.StartGameplay(); + }); private void onResultsReady() => resultsReady.SetResult(true); @@ -124,7 +135,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer // Await up to 30 seconds for results to become available (3 api request timeouts). // This is arbitrary just to not leave the player in an essentially deadlocked state if any connection issues occur. - await Task.WhenAny(resultsReady.Task, Task.Delay(TimeSpan.FromSeconds(30))); + await Task.WhenAny(resultsReady.Task, Task.Delay(TimeSpan.FromSeconds(60))); } protected override ResultsScreen CreateResults(ScoreInfo score) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 2bc84ce5d5..3f761d9e11 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -723,9 +723,6 @@ namespace osu.Game.Screens.Play storyboardReplacesBackground.Value = Beatmap.Value.Storyboard.ReplacesBackground && Beatmap.Value.Storyboard.HasDrawable; - GameplayClockContainer.Restart(); - GameplayClockContainer.FadeInFromZero(750, Easing.OutQuint); - foreach (var mod in Mods.Value.OfType()) mod.ApplyToPlayer(this); @@ -740,6 +737,21 @@ namespace osu.Game.Screens.Play mod.ApplyToTrack(musicController.CurrentTrack); updateGameplayState(); + + GameplayClockContainer.FadeInFromZero(750, Easing.OutQuint); + StartGameplay(); + } + + /// + /// Called to trigger the starting of the gameplay clock and underlying gameplay. + /// This will be called on entering the player screen once. A derived class may block the first call to this to delay the start of gameplay. + /// + protected virtual void StartGameplay() + { + if (GameplayClockContainer.GameplayClock.IsRunning) + throw new InvalidOperationException($"{nameof(StartGameplay)} should not be called when the gameplay clock is already running"); + + GameplayClockContainer.Restart(); } public override void OnSuspending(IScreen next) From 5457e4598b1a21405a183790cdd299218f789849 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Dec 2020 16:20:38 +0900 Subject: [PATCH 533/818] Schedule UpdateFilter calls to avoid operations occuring while at a sub screen --- osu.Game/Screens/Multi/Lounge/Components/FilterControl.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Multi/Lounge/Components/FilterControl.cs b/osu.Game/Screens/Multi/Lounge/Components/FilterControl.cs index 896c215c42..3712cbe33e 100644 --- a/osu.Game/Screens/Multi/Lounge/Components/FilterControl.cs +++ b/osu.Game/Screens/Multi/Lounge/Components/FilterControl.cs @@ -98,7 +98,9 @@ namespace osu.Game.Screens.Multi.Lounge.Components scheduledFilterUpdate = Scheduler.AddDelayed(UpdateFilter, 200); } - protected void UpdateFilter() + protected void UpdateFilter() => Scheduler.AddOnce(updateFilter); + + private void updateFilter() { scheduledFilterUpdate?.Cancel(); From 6bd6888a938667f2050ef55c24ff4f9872a418ec Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Dec 2020 16:29:51 +0900 Subject: [PATCH 534/818] Disallow skipping in multiplayer for now --- osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs | 1 + osu.Game/Screens/Play/Player.cs | 3 +++ osu.Game/Screens/Play/PlayerConfiguration.cs | 5 +++++ 3 files changed, 9 insertions(+) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs index b1615cc7f6..5da712bfcc 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs @@ -51,6 +51,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer { AllowPause = false, AllowRestart = false, + AllowSkippingIntro = false, }) { this.userIds = userIds; diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 4bd6340751..90600b0cf1 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -381,6 +381,9 @@ namespace osu.Game.Screens.Play } }; + if (!Configuration.AllowSkippingIntro) + skipOverlay.Expire(); + if (Configuration.AllowRestart) { container.Add(new HotkeyRetryOverlay diff --git a/osu.Game/Screens/Play/PlayerConfiguration.cs b/osu.Game/Screens/Play/PlayerConfiguration.cs index 475a234679..cd30ead638 100644 --- a/osu.Game/Screens/Play/PlayerConfiguration.cs +++ b/osu.Game/Screens/Play/PlayerConfiguration.cs @@ -19,5 +19,10 @@ namespace osu.Game.Screens.Play /// Whether the player should be allowed to trigger a restart. /// public bool AllowRestart { get; set; } = true; + + /// + /// Whether the player should be allowed to skip the intro, advancing to the start of gameplay. + /// + public bool AllowSkippingIntro { get; set; } = true; } } From 3148c04fb89493c65159d177e208fc2f03b6187e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Dec 2020 16:53:25 +0900 Subject: [PATCH 535/818] Play a sound when starting a timeshift or multiplayer room --- osu.Game/Screens/Multi/Match/RoomSubScreen.cs | 20 +++++++++++++++++++ .../RealtimeMatchSubScreen.cs | 6 +----- .../Multi/Timeshift/TimeshiftRoomSubScreen.cs | 10 +++------- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/Multi/Match/RoomSubScreen.cs b/osu.Game/Screens/Multi/Match/RoomSubScreen.cs index b9d7408946..0598524e81 100644 --- a/osu.Game/Screens/Multi/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/RoomSubScreen.cs @@ -4,6 +4,8 @@ using System; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Screens; using osu.Game.Audio; @@ -11,6 +13,7 @@ using osu.Game.Beatmaps; using osu.Game.Online.Multiplayer; using osu.Game.Overlays; using osu.Game.Rulesets.Mods; +using osu.Game.Screens.Play; namespace osu.Game.Screens.Multi.Match { @@ -21,6 +24,8 @@ namespace osu.Game.Screens.Multi.Match public override bool DisallowExternalBeatmapRulesetChanges => true; + private SampleChannel sampleStart; + [Resolved(typeof(Room), nameof(Room.Playlist))] protected BindableList Playlist { get; private set; } @@ -30,8 +35,17 @@ namespace osu.Game.Screens.Multi.Match [Resolved] private BeatmapManager beatmapManager { get; set; } + [Resolved(canBeNull: true)] + protected Multiplayer Multiplayer { get; private set; } + private IBindable> managerUpdated; + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + sampleStart = audio.Samples.Get(@"SongSelect/confirm-selection"); + } + protected override void LoadComplete() { base.LoadComplete(); @@ -71,6 +85,12 @@ namespace osu.Game.Screens.Multi.Match return base.OnExiting(next); } + protected void StartPlay(Func player) + { + sampleStart?.Play(); + Multiplayer?.Push(new PlayerLoader(player)); + } + private void selectedItemChanged() { updateWorkingBeatmap(); diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSubScreen.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSubScreen.cs index 45ac7e25d3..15d997605c 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSubScreen.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSubScreen.cs @@ -16,7 +16,6 @@ using osu.Game.Screens.Multi.Match; using osu.Game.Screens.Multi.Match.Components; using osu.Game.Screens.Multi.RealtimeMultiplayer.Match; using osu.Game.Screens.Multi.RealtimeMultiplayer.Participants; -using osu.Game.Screens.Play; using osu.Game.Users; using ParticipantsList = osu.Game.Screens.Multi.RealtimeMultiplayer.Participants.ParticipantsList; @@ -29,9 +28,6 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer public override string ShortTitle => "match"; - [Resolved(canBeNull: true)] - private Multiplayer multiplayer { get; set; } - [Resolved] private StatefulMultiplayerClient client { get; set; } @@ -206,7 +202,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer int[] userIds = client.Room.Users.Where(u => u.State >= MultiplayerUserState.WaitingForLoad).Select(u => u.UserID).ToArray(); - multiplayer?.Push(new PlayerLoader(() => new RealtimePlayer(SelectedItem.Value, userIds))); + StartPlay(() => new RealtimePlayer(SelectedItem.Value, userIds)); } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Screens/Multi/Timeshift/TimeshiftRoomSubScreen.cs b/osu.Game/Screens/Multi/Timeshift/TimeshiftRoomSubScreen.cs index fa901179e9..730ad795b2 100644 --- a/osu.Game/Screens/Multi/Timeshift/TimeshiftRoomSubScreen.cs +++ b/osu.Game/Screens/Multi/Timeshift/TimeshiftRoomSubScreen.cs @@ -15,7 +15,6 @@ using osu.Game.Screens.Multi.Match; using osu.Game.Screens.Multi.Match.Components; using osu.Game.Screens.Multi.Play; using osu.Game.Screens.Multi.Ranking; -using osu.Game.Screens.Play; using osu.Game.Screens.Select; using osu.Game.Users; using Footer = osu.Game.Screens.Multi.Match.Components.Footer; @@ -220,12 +219,9 @@ namespace osu.Game.Screens.Multi.Timeshift }, true); } - private void onStart() + private void onStart() => StartPlay(() => new TimeshiftPlayer(SelectedItem.Value) { - multiplayer?.Push(new PlayerLoader(() => new TimeshiftPlayer(SelectedItem.Value) - { - Exited = () => leaderboard.RefreshScores() - })); - } + Exited = () => leaderboard.RefreshScores() + }); } } From c35454081c931b2d1e836698d4f11186d1c7f56e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Dec 2020 17:17:45 +0900 Subject: [PATCH 536/818] Add sound when players change ready state --- .../TestSceneRealtimeReadyButton.cs | 31 ++++++++++++++++ .../Match/RealtimeReadyButton.cs | 36 +++++++++++++++++-- 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeReadyButton.cs b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeReadyButton.cs index e9d3ddb32d..825470846c 100644 --- a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeReadyButton.cs +++ b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeReadyButton.cs @@ -7,6 +7,7 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Graphics; using osu.Framework.Platform; +using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Online.Multiplayer; using osu.Game.Online.RealtimeMultiplayer; @@ -124,6 +125,36 @@ namespace osu.Game.Tests.Visual.RealtimeMultiplayer AddAssert("match not started", () => Client.Room?.Users[0].State == MultiplayerUserState.Idle); } + [TestCase(true)] + [TestCase(false)] + public void TestManyUsersChangingState(bool isHost) + { + const int users = 10; + AddStep("setup", () => + { + Client.TransferHost(Client.Room?.Users[0].UserID ?? 0); + for (int i = 0; i < users; i++) + Client.AddUser(new User { Id = i, Username = "Another user" }); + }); + + if (!isHost) + AddStep("transfer host", () => Client.TransferHost(2)); + + addClickButtonStep(); + + AddRepeatStep("change user ready state", () => + { + Client.ChangeUserState(RNG.Next(0, users), RNG.NextBool() ? MultiplayerUserState.Ready : MultiplayerUserState.Idle); + }, 20); + + AddRepeatStep("ready all users", () => + { + var nextUnready = Client.Room?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Idle); + if (nextUnready != null) + Client.ChangeUserState(nextUnready.UserID, MultiplayerUserState.Ready); + }, users); + } + private void addClickButtonStep() => AddStep("click button", () => { InputManager.MoveMouseTo(button); diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeReadyButton.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeReadyButton.cs index 5bead2b271..962c6bbead 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeReadyButton.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeReadyButton.cs @@ -5,6 +5,8 @@ using System.Diagnostics; using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Extensions; @@ -31,8 +33,12 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Match [Resolved] private OsuColour colours { get; set; } + private SampleChannel sampleReadyCount; + private readonly ButtonWithTrianglesExposed button; + private int countReady; + public RealtimeReadyButton() { InternalChild = button = new ButtonWithTrianglesExposed @@ -44,6 +50,12 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Match }; } + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + sampleReadyCount = audio.Samples.Get(@"SongSelect/select-difficulty"); + } + protected override void OnRoomChanged() { base.OnRoomChanged(); @@ -60,6 +72,10 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Match Debug.Assert(Room != null); + int newCountReady = Room.Users.Count(u => u.State == MultiplayerUserState.Ready); + + string countText = $"({newCountReady} / {Room.Users.Count} ready)"; + switch (localUser.State) { case MultiplayerUserState.Idle: @@ -70,18 +86,32 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Match case MultiplayerUserState.Ready: if (Room?.Host?.Equals(localUser) == true) { - int countReady = Room.Users.Count(u => u.State == MultiplayerUserState.Ready); - button.Text = $"Start match ({countReady} / {Room.Users.Count} ready)"; + button.Text = $"Start match {countText}"; updateButtonColour(true); } else { - button.Text = "Waiting for host..."; + button.Text = $"Waiting for host... {countText}"; updateButtonColour(false); } break; } + + if (newCountReady != countReady) + { + countReady = newCountReady; + Scheduler.AddOnce(playSound); + } + } + + private void playSound() + { + if (sampleReadyCount != null) + { + sampleReadyCount.Frequency.Value = 0.77f + countReady * 0.06f; + sampleReadyCount.Play(); + } } private void updateButtonColour(bool green) From eb795a212730b87f947f35eb25586a802563b543 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Dec 2020 17:58:38 +0900 Subject: [PATCH 537/818] Move all endpoint information to a configuration class --- ...TestSceneMultiplayerGameplayLeaderboard.cs | 2 ++ .../Visual/Gameplay/TestSceneSpectator.cs | 6 ++++ .../DevelopmentOsuConfigManager.cs | 19 ++++++++++++ osu.Game/Online/API/APIAccess.cs | 18 ++++------- .../DevelopmentEndpointConfiguration.cs | 17 +++++++++++ osu.Game/Online/EndpointConfiguration.cs | 30 +++++++++++++++++++ .../Online/ProductionEndpointConfiguration.cs | 17 +++++++++++ .../RealtimeMultiplayerClient.cs | 13 ++++---- .../Spectator/SpectatorStreamingClient.cs | 13 ++++---- osu.Game/OsuGameBase.cs | 18 +++++++---- 10 files changed, 124 insertions(+), 29 deletions(-) create mode 100644 osu.Game/Configuration/DevelopmentOsuConfigManager.cs create mode 100644 osu.Game/Online/DevelopmentEndpointConfiguration.cs create mode 100644 osu.Game/Online/EndpointConfiguration.cs create mode 100644 osu.Game/Online/ProductionEndpointConfiguration.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs index e42ddeb35e..8078c7b994 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs @@ -13,6 +13,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Database; +using osu.Game.Online; using osu.Game.Online.Spectator; using osu.Game.Replays.Legacy; using osu.Game.Rulesets.Osu.Scoring; @@ -87,6 +88,7 @@ namespace osu.Game.Tests.Visual.Gameplay private readonly int totalUsers; public TestMultiplayerStreaming(int totalUsers) + : base(new DevelopmentEndpointConfiguration()) { this.totalUsers = totalUsers; } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs index 1fdff99da6..26524f07da 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs @@ -12,6 +12,7 @@ using osu.Framework.Screens; using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Beatmaps; +using osu.Game.Online; using osu.Game.Online.Spectator; using osu.Game.Replays.Legacy; using osu.Game.Rulesets.Osu; @@ -238,6 +239,11 @@ namespace osu.Game.Tests.Visual.Gameplay private int beatmapId; + public TestSpectatorStreamingClient() + : base(new DevelopmentEndpointConfiguration()) + { + } + protected override Task Connect() { return Task.CompletedTask; diff --git a/osu.Game/Configuration/DevelopmentOsuConfigManager.cs b/osu.Game/Configuration/DevelopmentOsuConfigManager.cs new file mode 100644 index 0000000000..ff19dd874c --- /dev/null +++ b/osu.Game/Configuration/DevelopmentOsuConfigManager.cs @@ -0,0 +1,19 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Platform; +using osu.Framework.Testing; + +namespace osu.Game.Configuration +{ + [ExcludeFromDynamicCompile] + public class DevelopmentOsuConfigManager : OsuConfigManager + { + protected override string Filename => base.Filename.Replace(".ini", ".dev.ini"); + + public DevelopmentOsuConfigManager(Storage storage) + : base(storage) + { + } + } +} diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index ca457ccf71..49c815b2d3 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -26,18 +26,10 @@ namespace osu.Game.Online.API private readonly OAuth authentication; -#if DEBUG - public string Endpoint => @"https://dev.ppy.sh"; - private const string client_secret = @"3LP2mhUrV89xxzD1YKNndXHEhWWCRLPNKioZ9ymT"; -#else - public string Endpoint => @"https://osu.ppy.sh"; - private const string client_secret = @"FGc9GAtyHzeQDshWP5Ah7dega8hJACAJpQtw6OXk"; -#endif - - private const string client_id = @"5"; - private readonly Queue queue = new Queue(); + public string Endpoint { get; } + /// /// The username/email provided by the user when initiating a login. /// @@ -61,11 +53,13 @@ namespace osu.Game.Online.API private readonly Logger log; - public APIAccess(OsuConfigManager config) + public APIAccess(OsuConfigManager config, EndpointConfiguration endpointConfiguration) { this.config = config; - authentication = new OAuth(client_id, client_secret, Endpoint); + Endpoint = endpointConfiguration.APIEndpoint; + + authentication = new OAuth(endpointConfiguration.APIClientID, endpointConfiguration.APIClientSecret, Endpoint); log = Logger.GetLogger(LoggingTarget.Network); ProvidedUsername = config.Get(OsuSetting.Username); diff --git a/osu.Game/Online/DevelopmentEndpointConfiguration.cs b/osu.Game/Online/DevelopmentEndpointConfiguration.cs new file mode 100644 index 0000000000..5e4105f5fd --- /dev/null +++ b/osu.Game/Online/DevelopmentEndpointConfiguration.cs @@ -0,0 +1,17 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Online +{ + public class DevelopmentEndpointConfiguration : EndpointConfiguration + { + public DevelopmentEndpointConfiguration() + { + APIEndpoint = @"https://dev.ppy.sh"; + APIClientSecret = @"3LP2mhUrV89xxzD1YKNndXHEhWWCRLPNKioZ9ymT"; + APIClientID = "5"; + SpectatorEndpoint = $"{APIEndpoint}/spectator"; + MultiplayerEndpoint = $"{APIEndpoint}/multiplayer"; + } + } +} diff --git a/osu.Game/Online/EndpointConfiguration.cs b/osu.Game/Online/EndpointConfiguration.cs new file mode 100644 index 0000000000..a8b1a84e62 --- /dev/null +++ b/osu.Game/Online/EndpointConfiguration.cs @@ -0,0 +1,30 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Online +{ + /// + /// Holds configuration for API endpoints. + /// + public class EndpointConfiguration + { + /// + /// The endpoint for the main (osu-web) API. + /// + public string APIEndpoint { get; set; } + + /// + /// The OAuth client secret. + /// + public string APIClientSecret { get; set; } + + /// + /// The OAuth client ID. + /// + public string APIClientID { get; set; } + + public string SpectatorEndpoint { get; set; } + + public string MultiplayerEndpoint { get; set; } + } +} diff --git a/osu.Game/Online/ProductionEndpointConfiguration.cs b/osu.Game/Online/ProductionEndpointConfiguration.cs new file mode 100644 index 0000000000..f5c71ef737 --- /dev/null +++ b/osu.Game/Online/ProductionEndpointConfiguration.cs @@ -0,0 +1,17 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Online +{ + public class ProductionEndpointConfiguration : EndpointConfiguration + { + public ProductionEndpointConfiguration() + { + APIEndpoint = @"https://osu.ppy.sh"; + APIClientSecret = @"FGc9GAtyHzeQDshWP5Ah7dega8hJACAJpQtw6OXk"; + APIClientID = "5"; + SpectatorEndpoint = "https://spectator.ppy.sh/spectator"; + MultiplayerEndpoint = "https://spectator.ppy.sh/multiplayer"; + } + } +} diff --git a/osu.Game/Online/RealtimeMultiplayer/RealtimeMultiplayerClient.cs b/osu.Game/Online/RealtimeMultiplayer/RealtimeMultiplayerClient.cs index 4ec5b9af40..0e2b4855da 100644 --- a/osu.Game/Online/RealtimeMultiplayer/RealtimeMultiplayerClient.cs +++ b/osu.Game/Online/RealtimeMultiplayer/RealtimeMultiplayerClient.cs @@ -19,12 +19,6 @@ namespace osu.Game.Online.RealtimeMultiplayer { public class RealtimeMultiplayerClient : StatefulMultiplayerClient { -#if DEBUG - private const string endpoint = "https://dev.ppy.sh/multiplayer"; -#else - private const string endpoint = "https://spectator.ppy.sh/multiplayer"; -#endif - public override IBindable IsConnected => isConnected; private readonly Bindable isConnected = new Bindable(); @@ -35,6 +29,13 @@ namespace osu.Game.Online.RealtimeMultiplayer private HubConnection? connection; + private readonly string endpoint; + + public RealtimeMultiplayerClient(EndpointConfiguration endpoints) + { + endpoint = endpoints.MultiplayerEndpoint; + } + [BackgroundDependencyLoader] private void load() { diff --git a/osu.Game/Online/Spectator/SpectatorStreamingClient.cs b/osu.Game/Online/Spectator/SpectatorStreamingClient.cs index c9203b595e..1432fd1c98 100644 --- a/osu.Game/Online/Spectator/SpectatorStreamingClient.cs +++ b/osu.Game/Online/Spectator/SpectatorStreamingClient.cs @@ -81,6 +81,13 @@ namespace osu.Game.Online.Spectator ///
public event Action OnUserFinishedPlaying; + private readonly string endpoint; + + public SpectatorStreamingClient(EndpointConfiguration endpoints) + { + endpoint = endpoints.SpectatorEndpoint; + } + [BackgroundDependencyLoader] private void load() { @@ -104,12 +111,6 @@ namespace osu.Game.Online.Spectator } } -#if DEBUG - private const string endpoint = "https://dev.ppy.sh/spectator"; -#else - private const string endpoint = "https://spectator.ppy.sh/spectator"; -#endif - protected virtual async Task Connect() { if (connection != null) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index eb27821d82..bdc9e5eb7b 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -30,6 +30,7 @@ using osu.Game.Database; using osu.Game.Input; using osu.Game.Input.Bindings; using osu.Game.IO; +using osu.Game.Online; using osu.Game.Online.RealtimeMultiplayer; using osu.Game.Online.Spectator; using osu.Game.Overlays; @@ -54,6 +55,8 @@ namespace osu.Game public const int SAMPLE_CONCURRENCY = 6; + public bool UseDevelopmentServer { get; } + protected OsuConfigManager LocalConfig; protected BeatmapManager BeatmapManager; @@ -132,6 +135,7 @@ namespace osu.Game public OsuGameBase() { + UseDevelopmentServer = DebugUtils.IsDebugBuild; Name = @"osu!lazer"; } @@ -170,7 +174,7 @@ namespace osu.Game dependencies.Cache(largeStore); dependencies.CacheAs(this); - dependencies.Cache(LocalConfig); + dependencies.CacheAs(LocalConfig); AddFont(Resources, @"Fonts/osuFont"); @@ -210,10 +214,12 @@ namespace osu.Game } }); - dependencies.CacheAs(API ??= new APIAccess(LocalConfig)); + EndpointConfiguration endpoints = UseDevelopmentServer ? (EndpointConfiguration)new DevelopmentEndpointConfiguration() : new ProductionEndpointConfiguration(); - dependencies.CacheAs(spectatorStreaming = new SpectatorStreamingClient()); - dependencies.CacheAs(multiplayerClient = new RealtimeMultiplayerClient()); + dependencies.CacheAs(API ??= new APIAccess(LocalConfig, endpoints)); + + dependencies.CacheAs(spectatorStreaming = new SpectatorStreamingClient(endpoints)); + dependencies.CacheAs(multiplayerClient = new RealtimeMultiplayerClient(endpoints)); var defaultBeatmap = new DummyWorkingBeatmap(Audio, Textures); @@ -369,7 +375,9 @@ namespace osu.Game // may be non-null for certain tests Storage ??= host.Storage; - LocalConfig ??= new OsuConfigManager(Storage); + LocalConfig ??= UseDevelopmentServer + ? new DevelopmentOsuConfigManager(Storage) + : new OsuConfigManager(Storage); } protected override Storage CreateStorage(GameHost host, Storage defaultStorage) => new OsuStorage(host, defaultStorage); From 323da82477304456af0c9a4bd04e089061c6ea3e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Dec 2020 18:11:40 +0900 Subject: [PATCH 538/818] Add website root URL and update most links to use it For what it's worth, I intentionally didn't include news / changelog / supporter, because these should never change. --- osu.Game/Online/API/APIAccess.cs | 11 +++++++---- osu.Game/Online/API/APIRequest.cs | 2 +- osu.Game/Online/API/DummyAPIAccess.cs | 4 +++- osu.Game/Online/API/IAPIProvider.cs | 7 ++++++- osu.Game/Online/Chat/NowPlayingCommand.cs | 2 +- .../Online/DevelopmentEndpointConfiguration.cs | 6 +++--- osu.Game/Online/EndpointConfiguration.cs | 17 ++++++++++++++--- .../Online/ProductionEndpointConfiguration.cs | 6 +++--- .../RealtimeMultiplayerClient.cs | 2 +- .../Spectator/SpectatorStreamingClient.cs | 2 +- osu.Game/OsuGame.cs | 2 +- osu.Game/Overlays/BeatmapSet/Header.cs | 6 +++++- .../Profile/Header/BottomHeaderContainer.cs | 6 +++++- .../Profile/Header/TopHeaderContainer.cs | 6 +++++- .../Sections/Recent/DrawableRecentActivity.cs | 2 +- 15 files changed, 57 insertions(+), 24 deletions(-) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 49c815b2d3..133ba22406 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -28,7 +28,9 @@ namespace osu.Game.Online.API private readonly Queue queue = new Queue(); - public string Endpoint { get; } + public string APIEndpointUrl { get; } + + public string WebsiteRootUrl { get; } /// /// The username/email provided by the user when initiating a login. @@ -57,9 +59,10 @@ namespace osu.Game.Online.API { this.config = config; - Endpoint = endpointConfiguration.APIEndpoint; + APIEndpointUrl = endpointConfiguration.APIEndpointUrl; + WebsiteRootUrl = endpointConfiguration.WebsiteRootUrl; - authentication = new OAuth(endpointConfiguration.APIClientID, endpointConfiguration.APIClientSecret, Endpoint); + authentication = new OAuth(endpointConfiguration.APIClientID, endpointConfiguration.APIClientSecret, APIEndpointUrl); log = Logger.GetLogger(LoggingTarget.Network); ProvidedUsername = config.Get(OsuSetting.Username); @@ -245,7 +248,7 @@ namespace osu.Game.Online.API var req = new RegistrationRequest { - Url = $@"{Endpoint}/users", + Url = $@"{APIEndpointUrl}/users", Method = HttpMethod.Post, Username = username, Email = email, diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs index 6912d9b629..a7174324d8 100644 --- a/osu.Game/Online/API/APIRequest.cs +++ b/osu.Game/Online/API/APIRequest.cs @@ -57,7 +57,7 @@ namespace osu.Game.Online.API protected virtual WebRequest CreateWebRequest() => new OsuWebRequest(Uri); - protected virtual string Uri => $@"{API.Endpoint}/api/v2/{Target}"; + protected virtual string Uri => $@"{API.APIEndpointUrl}/api/v2/{Target}"; protected APIAccess API; protected WebRequest WebRequest; diff --git a/osu.Game/Online/API/DummyAPIAccess.cs b/osu.Game/Online/API/DummyAPIAccess.cs index 265298270c..3e996ac97f 100644 --- a/osu.Game/Online/API/DummyAPIAccess.cs +++ b/osu.Game/Online/API/DummyAPIAccess.cs @@ -28,7 +28,9 @@ namespace osu.Game.Online.API public string ProvidedUsername => LocalUser.Value.Username; - public string Endpoint => "http://localhost"; + public string APIEndpointUrl => "http://localhost"; + + public string WebsiteRootUrl => "http://localhost"; /// /// Provide handling logic for an arbitrary API request. diff --git a/osu.Game/Online/API/IAPIProvider.cs b/osu.Game/Online/API/IAPIProvider.cs index 3a444460f2..4407f1f55e 100644 --- a/osu.Game/Online/API/IAPIProvider.cs +++ b/osu.Game/Online/API/IAPIProvider.cs @@ -46,7 +46,12 @@ namespace osu.Game.Online.API /// /// The URL endpoint for this API. Does not include a trailing slash. /// - string Endpoint { get; } + string APIEndpointUrl { get; } + + /// + /// The root URL of of the website, excluding the trailing slash. + /// + public string WebsiteRootUrl { get; } /// /// The current connection state of the API. diff --git a/osu.Game/Online/Chat/NowPlayingCommand.cs b/osu.Game/Online/Chat/NowPlayingCommand.cs index c0b54812b6..926709694b 100644 --- a/osu.Game/Online/Chat/NowPlayingCommand.cs +++ b/osu.Game/Online/Chat/NowPlayingCommand.cs @@ -46,7 +46,7 @@ namespace osu.Game.Online.Chat break; } - var beatmapString = beatmap.OnlineBeatmapID.HasValue ? $"[https://osu.ppy.sh/b/{beatmap.OnlineBeatmapID} {beatmap}]" : beatmap.ToString(); + var beatmapString = beatmap.OnlineBeatmapID.HasValue ? $"[{api.WebsiteRootUrl}/b/{beatmap.OnlineBeatmapID} {beatmap}]" : beatmap.ToString(); channelManager.PostMessage($"is {verb} {beatmapString}", true); Expire(); diff --git a/osu.Game/Online/DevelopmentEndpointConfiguration.cs b/osu.Game/Online/DevelopmentEndpointConfiguration.cs index 5e4105f5fd..69531dbe1b 100644 --- a/osu.Game/Online/DevelopmentEndpointConfiguration.cs +++ b/osu.Game/Online/DevelopmentEndpointConfiguration.cs @@ -7,11 +7,11 @@ namespace osu.Game.Online { public DevelopmentEndpointConfiguration() { - APIEndpoint = @"https://dev.ppy.sh"; + WebsiteRootUrl = APIEndpointUrl = @"https://dev.ppy.sh"; APIClientSecret = @"3LP2mhUrV89xxzD1YKNndXHEhWWCRLPNKioZ9ymT"; APIClientID = "5"; - SpectatorEndpoint = $"{APIEndpoint}/spectator"; - MultiplayerEndpoint = $"{APIEndpoint}/multiplayer"; + SpectatorEndpointUrl = $"{APIEndpointUrl}/spectator"; + MultiplayerEndpointUrl = $"{APIEndpointUrl}/multiplayer"; } } } diff --git a/osu.Game/Online/EndpointConfiguration.cs b/osu.Game/Online/EndpointConfiguration.cs index a8b1a84e62..e347d3c653 100644 --- a/osu.Game/Online/EndpointConfiguration.cs +++ b/osu.Game/Online/EndpointConfiguration.cs @@ -8,10 +8,15 @@ namespace osu.Game.Online /// public class EndpointConfiguration { + /// + /// The base URL for the website. + /// + public string WebsiteRootUrl { get; set; } + /// /// The endpoint for the main (osu-web) API. /// - public string APIEndpoint { get; set; } + public string APIEndpointUrl { get; set; } /// /// The OAuth client secret. @@ -23,8 +28,14 @@ namespace osu.Game.Online /// public string APIClientID { get; set; } - public string SpectatorEndpoint { get; set; } + /// + /// The endpoint for the SignalR spectator server. + /// + public string SpectatorEndpointUrl { get; set; } - public string MultiplayerEndpoint { get; set; } + /// + /// The endpoint for the SignalR multiplayer server. + /// + public string MultiplayerEndpointUrl { get; set; } } } diff --git a/osu.Game/Online/ProductionEndpointConfiguration.cs b/osu.Game/Online/ProductionEndpointConfiguration.cs index f5c71ef737..c6ddc03564 100644 --- a/osu.Game/Online/ProductionEndpointConfiguration.cs +++ b/osu.Game/Online/ProductionEndpointConfiguration.cs @@ -7,11 +7,11 @@ namespace osu.Game.Online { public ProductionEndpointConfiguration() { - APIEndpoint = @"https://osu.ppy.sh"; + WebsiteRootUrl = APIEndpointUrl = @"https://osu.ppy.sh"; APIClientSecret = @"FGc9GAtyHzeQDshWP5Ah7dega8hJACAJpQtw6OXk"; APIClientID = "5"; - SpectatorEndpoint = "https://spectator.ppy.sh/spectator"; - MultiplayerEndpoint = "https://spectator.ppy.sh/multiplayer"; + SpectatorEndpointUrl = "https://spectator.ppy.sh/spectator"; + MultiplayerEndpointUrl = "https://spectator.ppy.sh/multiplayer"; } } } diff --git a/osu.Game/Online/RealtimeMultiplayer/RealtimeMultiplayerClient.cs b/osu.Game/Online/RealtimeMultiplayer/RealtimeMultiplayerClient.cs index 0e2b4855da..bfc89df483 100644 --- a/osu.Game/Online/RealtimeMultiplayer/RealtimeMultiplayerClient.cs +++ b/osu.Game/Online/RealtimeMultiplayer/RealtimeMultiplayerClient.cs @@ -33,7 +33,7 @@ namespace osu.Game.Online.RealtimeMultiplayer public RealtimeMultiplayerClient(EndpointConfiguration endpoints) { - endpoint = endpoints.MultiplayerEndpoint; + endpoint = endpoints.MultiplayerEndpointUrl; } [BackgroundDependencyLoader] diff --git a/osu.Game/Online/Spectator/SpectatorStreamingClient.cs b/osu.Game/Online/Spectator/SpectatorStreamingClient.cs index 1432fd1c98..344b73f3d9 100644 --- a/osu.Game/Online/Spectator/SpectatorStreamingClient.cs +++ b/osu.Game/Online/Spectator/SpectatorStreamingClient.cs @@ -85,7 +85,7 @@ namespace osu.Game.Online.Spectator public SpectatorStreamingClient(EndpointConfiguration endpoints) { - endpoint = endpoints.SpectatorEndpoint; + endpoint = endpoints.SpectatorEndpointUrl; } [BackgroundDependencyLoader] diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index bb51c55551..17831ed26b 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -294,7 +294,7 @@ namespace osu.Game public void OpenUrlExternally(string url) => waitForReady(() => externalLinkOpener, _ => { if (url.StartsWith('/')) - url = $"{API.Endpoint}{url}"; + url = $"{API.APIEndpointUrl}{url}"; externalLinkOpener.OpenUrlExternally(url); }); diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index 06e31277dd..321e496511 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -15,6 +15,7 @@ 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; @@ -40,6 +41,9 @@ namespace osu.Game.Overlays.BeatmapSet public bool DownloadButtonsVisible => downloadButtonsContainer.Any(); + [Resolved] + private IAPIProvider api { get; set; } + public BeatmapRulesetSelector RulesetSelector => beatmapSetHeader.RulesetSelector; public readonly BeatmapPicker Picker; @@ -213,7 +217,7 @@ namespace osu.Game.Overlays.BeatmapSet Picker.Beatmap.ValueChanged += b => { Details.Beatmap = b.NewValue; - externalLink.Link = $@"https://osu.ppy.sh/beatmapsets/{BeatmapSet.Value?.OnlineBeatmapSetID}#{b.NewValue?.Ruleset.ShortName}/{b.NewValue?.OnlineBeatmapID}"; + externalLink.Link = $@"{api.WebsiteRootUrl}/beatmapsets/{BeatmapSet.Value?.OnlineBeatmapSetID}#{b.NewValue?.Ruleset.ShortName}/{b.NewValue?.OnlineBeatmapID}"; }; } diff --git a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs index ebee377a51..2925107766 100644 --- a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Online.API; using osu.Game.Users; using osuTK; using osuTK.Graphics; @@ -27,6 +28,9 @@ namespace osu.Game.Overlays.Profile.Header private Color4 iconColour; + [Resolved] + private IAPIProvider api { get; set; } + public BottomHeaderContainer() { AutoSizeAxes = Axes.Y; @@ -109,7 +113,7 @@ namespace osu.Game.Overlays.Profile.Header } topLinkContainer.AddText("Contributed "); - topLinkContainer.AddLink($@"{user.PostCount:#,##0} forum posts", $"https://osu.ppy.sh/users/{user.Id}/posts", creationParameters: embolden); + topLinkContainer.AddLink($@"{user.PostCount:#,##0} forum posts", $"{api.WebsiteRootUrl}/users/{user.Id}/posts", creationParameters: embolden); string websiteWithoutProtocol = user.Website; diff --git a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs index 2cc1f6533f..e0642d650c 100644 --- a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API; using osu.Game.Overlays.Profile.Header.Components; using osu.Game.Users; using osu.Game.Users.Drawables; @@ -23,6 +24,9 @@ namespace osu.Game.Overlays.Profile.Header public readonly Bindable User = new Bindable(); + [Resolved] + private IAPIProvider api { get; set; } + private SupporterIcon supporterTag; private UpdateableAvatar avatar; private OsuSpriteText usernameText; @@ -166,7 +170,7 @@ namespace osu.Game.Overlays.Profile.Header { avatar.User = user; usernameText.Text = user?.Username ?? string.Empty; - openUserExternally.Link = $@"https://osu.ppy.sh/users/{user?.Id ?? 0}"; + openUserExternally.Link = $@"{api.WebsiteRootUrl}/users/{user?.Id ?? 0}"; userFlag.Country = user?.Country; userCountryText.Text = user?.Country?.FullName ?? "Alien"; supporterTag.SupportLevel = user?.SupportLevel ?? 0; diff --git a/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs b/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs index 8782e82642..49b46f7e7a 100644 --- a/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs +++ b/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs @@ -216,7 +216,7 @@ namespace osu.Game.Overlays.Profile.Sections.Recent private void addBeatmapsetLink() => content.AddLink(activity.Beatmapset?.Title, LinkAction.OpenBeatmapSet, getLinkArgument(activity.Beatmapset?.Url), creationParameters: t => t.Font = getLinkFont()); - private string getLinkArgument(string url) => MessageFormatter.GetLinkDetails($"{api.Endpoint}{url}").Argument; + private string getLinkArgument(string url) => MessageFormatter.GetLinkDetails($"{api.APIEndpointUrl}{url}").Argument; private FontUsage getLinkFont(FontWeight fontWeight = FontWeight.Regular) => OsuFont.GetFont(size: font_size, weight: fontWeight, italics: true); From 261c250b46dae0db37151601a2d789c0e789b5f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 24 Dec 2020 11:33:49 +0100 Subject: [PATCH 539/818] Update outdated comment --- osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs index 20b184bed3..b1179ea7cd 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs @@ -133,7 +133,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer await client.ChangeState(MultiplayerUserState.FinishedPlay); - // Await up to 30 seconds for results to become available (3 api request timeouts). + // Await up to 60 seconds for results to become available (6 api request timeouts). // This is arbitrary just to not leave the player in an essentially deadlocked state if any connection issues occur. await Task.WhenAny(resultsReady.Task, Task.Delay(TimeSpan.FromSeconds(60))); } From 40b9d1bc5ef7b3f0c63766a7478f278aaa565cc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 24 Dec 2020 12:45:01 +0100 Subject: [PATCH 540/818] Invert if & early-return to reduce nesting --- .../RealtimeMultiplayer/Match/RealtimeReadyButton.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeReadyButton.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeReadyButton.cs index 962c6bbead..be405feef1 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeReadyButton.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeReadyButton.cs @@ -107,11 +107,11 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Match private void playSound() { - if (sampleReadyCount != null) - { - sampleReadyCount.Frequency.Value = 0.77f + countReady * 0.06f; - sampleReadyCount.Play(); - } + if (sampleReadyCount == null) + return; + + sampleReadyCount.Frequency.Value = 0.77f + countReady * 0.06f; + sampleReadyCount.Play(); } private void updateButtonColour(bool green) From 66a23c22e5580f6866db87aebfe503d195a2a8dd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Dec 2020 21:28:21 +0900 Subject: [PATCH 541/818] Fix various tests failing due to dependence on specific online data --- .../Online/TestSceneChangelogOverlay.cs | 5 +- .../Online/TestSceneNowPlayingCommand.cs | 2 +- .../Requests/Responses/APIChangelogEntry.cs | 3 +- osu.Game/Overlays/Changelog/ChangelogBuild.cs | 49 ++++++++++--------- osu.Game/Overlays/ChangelogOverlay.cs | 1 + 5 files changed, 33 insertions(+), 27 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs index 02f6de2269..998e42b478 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs @@ -41,6 +41,7 @@ namespace osu.Game.Tests.Visual.Online } [Test] + [Ignore("needs to be updated to not be so server dependent")] public void ShowWithBuild() { AddStep(@"Show with Lazer 2018.712.0", () => @@ -49,7 +50,7 @@ namespace osu.Game.Tests.Visual.Online { Version = "2018.712.0", DisplayVersion = "2018.712.0", - UpdateStream = new APIUpdateStream { Id = 7, Name = OsuGameBase.CLIENT_STREAM_NAME }, + UpdateStream = new APIUpdateStream { Id = 5, Name = OsuGameBase.CLIENT_STREAM_NAME }, ChangelogEntries = new List { new APIChangelogEntry @@ -64,7 +65,7 @@ namespace osu.Game.Tests.Visual.Online AddUntilStep(@"wait for streams", () => changelog.Streams?.Count > 0); AddAssert(@"correct build displayed", () => changelog.Current.Value.Version == "2018.712.0"); - AddAssert(@"correct stream selected", () => changelog.Header.Streams.Current.Value.Id == 7); + AddAssert(@"correct stream selected", () => changelog.Header.Streams.Current.Value.Id == 5); } [Test] diff --git a/osu.Game.Tests/Visual/Online/TestSceneNowPlayingCommand.cs b/osu.Game.Tests/Visual/Online/TestSceneNowPlayingCommand.cs index 0324da6cf5..64e80e9f02 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneNowPlayingCommand.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneNowPlayingCommand.cs @@ -70,7 +70,7 @@ namespace osu.Game.Tests.Visual.Online AddStep("Run command", () => Add(new NowPlayingCommand())); if (hasOnlineId) - AddAssert("Check link presence", () => postTarget.LastMessage.Contains("https://osu.ppy.sh/b/1234")); + AddAssert("Check link presence", () => postTarget.LastMessage.Contains("/b/1234")); else AddAssert("Check link not present", () => !postTarget.LastMessage.Contains("https://")); } diff --git a/osu.Game/Online/API/Requests/Responses/APIChangelogEntry.cs b/osu.Game/Online/API/Requests/Responses/APIChangelogEntry.cs index f949ab5da5..1ff7523ba6 100644 --- a/osu.Game/Online/API/Requests/Responses/APIChangelogEntry.cs +++ b/osu.Game/Online/API/Requests/Responses/APIChangelogEntry.cs @@ -48,6 +48,7 @@ namespace osu.Game.Online.API.Requests.Responses public enum ChangelogEntryType { Add, - Fix + Fix, + Misc } } diff --git a/osu.Game/Overlays/Changelog/ChangelogBuild.cs b/osu.Game/Overlays/Changelog/ChangelogBuild.cs index 48bf6c2ddd..65ff0fef92 100644 --- a/osu.Game/Overlays/Changelog/ChangelogBuild.cs +++ b/osu.Game/Overlays/Changelog/ChangelogBuild.cs @@ -131,33 +131,36 @@ namespace osu.Game.Overlays.Changelog t.Padding = new MarginPadding { Left = 10 }; }); - if (entry.GithubUser.UserId != null) + if (entry.GithubUser != null) { - title.AddUserLink(new User + if (entry.GithubUser.UserId != null) { - Username = entry.GithubUser.OsuUsername, - Id = entry.GithubUser.UserId.Value - }, t => + title.AddUserLink(new User + { + Username = entry.GithubUser.OsuUsername, + Id = entry.GithubUser.UserId.Value + }, t => + { + t.Font = fontMedium; + t.Colour = entryColour; + }); + } + else if (entry.GithubUser.GithubUrl != null) { - t.Font = fontMedium; - t.Colour = entryColour; - }); - } - else if (entry.GithubUser.GithubUrl != null) - { - title.AddLink(entry.GithubUser.DisplayName, entry.GithubUser.GithubUrl, t => + title.AddLink(entry.GithubUser.DisplayName, entry.GithubUser.GithubUrl, t => + { + t.Font = fontMedium; + t.Colour = entryColour; + }); + } + else { - t.Font = fontMedium; - t.Colour = entryColour; - }); - } - else - { - title.AddText(entry.GithubUser.DisplayName, t => - { - t.Font = fontMedium; - t.Colour = entryColour; - }); + title.AddText(entry.GithubUser.DisplayName, t => + { + t.Font = fontMedium; + t.Colour = entryColour; + }); + } } ChangelogEntries.Add(titleContainer); diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index c7e9a86fa4..f591b1d427 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -86,6 +86,7 @@ namespace osu.Game.Overlays } public void ShowListing() + { Current.Value = null; Show(); From 4270c29f6083cea4abc28f9d82aef3bc6c5c222a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 24 Dec 2020 13:42:08 +0100 Subject: [PATCH 542/818] Trim stray newline --- osu.Game/Overlays/ChangelogOverlay.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index f591b1d427..c7e9a86fa4 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -86,7 +86,6 @@ namespace osu.Game.Overlays } public void ShowListing() - { Current.Value = null; Show(); From d5c348b568096b1a10fc2d2a04a0b2dd922c20e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 24 Dec 2020 13:44:46 +0100 Subject: [PATCH 543/818] Remove explicit public access modifier from interface --- osu.Game/Online/API/IAPIProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/API/IAPIProvider.cs b/osu.Game/Online/API/IAPIProvider.cs index 4407f1f55e..1951dfaf40 100644 --- a/osu.Game/Online/API/IAPIProvider.cs +++ b/osu.Game/Online/API/IAPIProvider.cs @@ -51,7 +51,7 @@ namespace osu.Game.Online.API /// /// The root URL of of the website, excluding the trailing slash. /// - public string WebsiteRootUrl { get; } + string WebsiteRootUrl { get; } /// /// The current connection state of the API. From f991448a3e6f359efa277f4491c472421c311ca9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Dec 2020 21:49:38 +0900 Subject: [PATCH 544/818] Re-sort the leaderboard order a maximum of once a second --- .../Screens/Play/HUD/GameplayLeaderboard.cs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs index cab1cbd3f1..e33cc05e64 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs @@ -3,6 +3,7 @@ using System; using System.Linq; +using osu.Framework.Caching; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Users; @@ -12,6 +13,8 @@ namespace osu.Game.Screens.Play.HUD { public class GameplayLeaderboard : FillFlowContainer { + private readonly Cached sorting = new Cached(); + public GameplayLeaderboard() { Width = GameplayLeaderboardScore.EXTENDED_WIDTH + GameplayLeaderboardScore.SHEAR_WIDTH; @@ -24,6 +27,13 @@ namespace osu.Game.Screens.Play.HUD LayoutEasing = Easing.OutQuint; } + protected override void LoadComplete() + { + base.LoadComplete(); + + Scheduler.AddDelayed(sort, 1000, true); + } + /// /// Adds a player to the leaderboard. /// @@ -41,7 +51,7 @@ namespace osu.Game.Screens.Play.HUD }; base.Add(drawable); - drawable.TotalScore.BindValueChanged(_ => Scheduler.AddOnce(sort), true); + drawable.TotalScore.BindValueChanged(_ => sorting.Invalidate(), true); Height = Count * (GameplayLeaderboardScore.PANEL_HEIGHT + Spacing.Y); @@ -55,6 +65,9 @@ namespace osu.Game.Screens.Play.HUD private void sort() { + if (sorting.IsValid) + return; + var orderedByScore = this.OrderByDescending(i => i.TotalScore.Value).ToList(); for (int i = 0; i < Count; i++) @@ -62,6 +75,8 @@ namespace osu.Game.Screens.Play.HUD SetLayoutPosition(orderedByScore[i], i); orderedByScore[i].ScorePosition = i + 1; } + + sorting.Validate(); } } } From aec25e2d73079043d810fbc97da19441c1b1c581 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Dec 2020 21:53:18 +0900 Subject: [PATCH 545/818] Rename "timeshift" to "playlists" This only covers the user-facing instances. Code and class name changes will happen once things have calmed down. --- osu.Game/Screens/Menu/ButtonSystem.cs | 2 +- osu.Game/Screens/Menu/Disclaimer.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index badfa3f693..5af6517f49 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -125,7 +125,7 @@ namespace osu.Game.Screens.Menu { buttonsPlay.Add(new Button(@"solo", @"button-solo-select", FontAwesome.Solid.User, new Color4(102, 68, 204, 255), () => OnSolo?.Invoke(), WEDGE_WIDTH, Key.P)); buttonsPlay.Add(new Button(@"multi", @"button-generic-select", FontAwesome.Solid.Users, new Color4(94, 63, 186, 255), onMultiplayer, 0, Key.M)); - buttonsPlay.Add(new Button(@"timeshift", @"button-generic-select", OsuIcon.Charts, new Color4(94, 63, 186, 255), onTimeshift, 0, Key.L)); + buttonsPlay.Add(new Button(@"playlists", @"button-generic-select", OsuIcon.Charts, new Color4(94, 63, 186, 255), onTimeshift, 0, Key.L)); buttonsPlay.ForEach(b => b.VisibleState = ButtonSystemState.Play); buttonsTopLevel.Add(new Button(@"play", @"button-play-select", OsuIcon.Logo, new Color4(102, 68, 204, 255), () => State = ButtonSystemState.Play, WEDGE_WIDTH, Key.P)); diff --git a/osu.Game/Screens/Menu/Disclaimer.cs b/osu.Game/Screens/Menu/Disclaimer.cs index ceec12c967..9c7206d259 100644 --- a/osu.Game/Screens/Menu/Disclaimer.cs +++ b/osu.Game/Screens/Menu/Disclaimer.cs @@ -208,7 +208,7 @@ namespace osu.Game.Screens.Menu "Most of the web content (profiles, rankings, etc.) are available natively in-game from the icons on the toolbar!", "Get more details, hide or delete a beatmap by right-clicking on its panel at song select!", "All delete operations are temporary until exiting. Restore accidentally deleted content from the maintenance settings!", - "Check out the \"timeshift\" multiplayer system, which has local permanent leaderboards and playlist support!", + "Check out the \"playlist\" system, which lets users create their own custom and permanent leaderboards!", "Toggle advanced frame / thread statistics with Ctrl-F11!", "Take a look under the hood at performance counters and enable verbose performance logging with Ctrl-F2!", }; From 3a46e210d48d903bc631021979518f1fcab59293 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Dec 2020 21:59:10 +0900 Subject: [PATCH 546/818] Change low-hanging references of "room" to "playlist" --- osu.Game/Screens/Multi/Multiplayer.cs | 6 +++--- .../Multi/RealtimeMultiplayer/RealtimeMultiplayer.cs | 2 +- .../Screens/Multi/Timeshift/CreateTimeshiftRoomButton.cs | 2 +- osu.Game/Screens/Multi/Timeshift/TimeshiftMultiplayer.cs | 6 ++++++ 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index a7d40a89d3..585ac71189 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.cs @@ -59,7 +59,7 @@ namespace osu.Game.Screens.Multi private OsuGameBase game { get; set; } [Resolved] - private IAPIProvider api { get; set; } + protected IAPIProvider API { get; private set; } [Resolved(CanBeNull = true)] private OsuLogo logo { get; set; } @@ -155,7 +155,7 @@ namespace osu.Game.Screens.Multi [BackgroundDependencyLoader(true)] private void load(IdleTracker idleTracker) { - apiState.BindTo(api.State); + apiState.BindTo(API.State); apiState.BindValueChanged(onlineStateChanged, true); if (idleTracker != null) @@ -269,7 +269,7 @@ namespace osu.Game.Screens.Multi /// Creates a new room. /// /// The created . - protected virtual Room CreateNewRoom() => new Room { Name = { Value = $"{api.LocalUser}'s awesome room" } }; + protected abstract Room CreateNewRoom(); private void screenPushed(IScreen lastScreen, IScreen newScreen) { diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMultiplayer.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMultiplayer.cs index 6739a51fe8..e15a6bd408 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMultiplayer.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMultiplayer.cs @@ -57,7 +57,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer protected override Room CreateNewRoom() { - var room = base.CreateNewRoom(); + var room = new Room { Name = { Value = $"{API.LocalUser}'s awesome room" } }; room.Category.Value = RoomCategory.Realtime; return room; } diff --git a/osu.Game/Screens/Multi/Timeshift/CreateTimeshiftRoomButton.cs b/osu.Game/Screens/Multi/Timeshift/CreateTimeshiftRoomButton.cs index bd9d667630..0424493472 100644 --- a/osu.Game/Screens/Multi/Timeshift/CreateTimeshiftRoomButton.cs +++ b/osu.Game/Screens/Multi/Timeshift/CreateTimeshiftRoomButton.cs @@ -13,7 +13,7 @@ namespace osu.Game.Screens.Multi.Timeshift { Triangles.TriangleScale = 1.5f; - Text = "Create room"; + Text = "Create playlist"; } } } diff --git a/osu.Game/Screens/Multi/Timeshift/TimeshiftMultiplayer.cs b/osu.Game/Screens/Multi/Timeshift/TimeshiftMultiplayer.cs index d525a3800d..e1b94f8455 100644 --- a/osu.Game/Screens/Multi/Timeshift/TimeshiftMultiplayer.cs +++ b/osu.Game/Screens/Multi/Timeshift/TimeshiftMultiplayer.cs @@ -4,6 +4,7 @@ using osu.Framework.Logging; using osu.Framework.Screens; using osu.Game.Graphics.UserInterface; +using osu.Game.Online.Multiplayer; using osu.Game.Screens.Multi.Components; using osu.Game.Screens.Multi.Lounge; using osu.Game.Screens.Multi.Match; @@ -45,6 +46,11 @@ namespace osu.Game.Screens.Multi.Timeshift Logger.Log($"Polling adjusted (listing: {timeshiftManager.TimeBetweenListingPolls.Value}, selection: {timeshiftManager.TimeBetweenSelectionPolls.Value})"); } + protected override Room CreateNewRoom() + { + return new Room { Name = { Value = $"{API.LocalUser}'s awesome playlist" } }; + } + protected override RoomManager CreateRoomManager() => new TimeshiftRoomManager(); protected override LoungeSubScreen CreateLounge() => new TimeshiftLoungeSubScreen(); From d0e834796818beedbfbb9ecb3ab70dfd8bb18b5a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Dec 2020 22:28:25 +0900 Subject: [PATCH 547/818] Change asserts into until steps --- .../Gameplay/TestSceneGameplayLeaderboard.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs index ca61672ef9..c0a021436e 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs @@ -52,19 +52,19 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("add player 2", () => createLeaderboardScore(player2Score, new User { Username = "Player 2" })); AddStep("add player 3", () => createLeaderboardScore(player3Score, new User { Username = "Player 3" })); - AddAssert("is player 2 position #1", () => leaderboard.CheckPositionByUsername("Player 2", 1)); - AddAssert("is player position #2", () => leaderboard.CheckPositionByUsername("You", 2)); - AddAssert("is player 3 position #3", () => leaderboard.CheckPositionByUsername("Player 3", 3)); + AddUntilStep("is player 2 position #1", () => leaderboard.CheckPositionByUsername("Player 2", 1)); + AddUntilStep("is player position #2", () => leaderboard.CheckPositionByUsername("You", 2)); + AddUntilStep("is player 3 position #3", () => leaderboard.CheckPositionByUsername("Player 3", 3)); AddStep("set score above player 3", () => player2Score.Value = playerScore.Value - 500); - AddAssert("is player position #1", () => leaderboard.CheckPositionByUsername("You", 1)); - AddAssert("is player 2 position #2", () => leaderboard.CheckPositionByUsername("Player 2", 2)); - AddAssert("is player 3 position #3", () => leaderboard.CheckPositionByUsername("Player 3", 3)); + AddUntilStep("is player position #1", () => leaderboard.CheckPositionByUsername("You", 1)); + AddUntilStep("is player 2 position #2", () => leaderboard.CheckPositionByUsername("Player 2", 2)); + AddUntilStep("is player 3 position #3", () => leaderboard.CheckPositionByUsername("Player 3", 3)); AddStep("set score below players", () => player2Score.Value = playerScore.Value - 123456); - AddAssert("is player position #1", () => leaderboard.CheckPositionByUsername("You", 1)); - AddAssert("is player 3 position #2", () => leaderboard.CheckPositionByUsername("Player 3", 2)); - AddAssert("is player 2 position #3", () => leaderboard.CheckPositionByUsername("Player 2", 3)); + AddUntilStep("is player position #1", () => leaderboard.CheckPositionByUsername("You", 1)); + AddUntilStep("is player 3 position #2", () => leaderboard.CheckPositionByUsername("Player 3", 2)); + AddUntilStep("is player 2 position #3", () => leaderboard.CheckPositionByUsername("Player 2", 3)); } [Test] From 76a7aabfe86b73d5619ff4b3e0d6d23e74907f00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 24 Dec 2020 14:32:30 +0100 Subject: [PATCH 548/818] Always create realtime-specific player elements regardless of token --- .../Multi/RealtimeMultiplayer/RealtimePlayer.cs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs index 07d21995ce..8eb6a11228 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs @@ -4,7 +4,6 @@ using System; using System.Diagnostics; using System.Threading.Tasks; -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Logging; @@ -35,7 +34,6 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer private readonly TaskCompletionSource resultsReady = new TaskCompletionSource(); - [CanBeNull] private MultiplayerGameplayLeaderboard leaderboard; private readonly int[] userIds; @@ -61,6 +59,11 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer [BackgroundDependencyLoader] private void load() { + // todo: this should be implemented via a custom HUD implementation, and correctly masked to the main content area. + LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(ScoreProcessor, userIds), HUDOverlay.Add); + + HUDOverlay.Add(loadingDisplay = new LoadingLayer(DrawableRuleset) { Depth = float.MaxValue }); + if (Token == null) return; // Todo: Somehow handle token retrieval failure. @@ -84,11 +87,6 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer }, true); Debug.Assert(client.Room != null); - - // todo: this should be implemented via a custom HUD implementation, and correctly masked to the main content area. - LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(ScoreProcessor, userIds), HUDOverlay.Add); - - HUDOverlay.Add(loadingDisplay = new LoadingLayer(DrawableRuleset) { Depth = float.MaxValue }); } protected override void StartGameplay() @@ -115,9 +113,6 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer private void adjustLeaderboardPosition() { - if (leaderboard == null) - return; - const float padding = 44; // enough margin to avoid the hit error display. leaderboard.Position = new Vector2( From a97681a5daa18b6e4374a901b65e836a1bef82eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 24 Dec 2020 15:07:03 +0100 Subject: [PATCH 549/818] Proxy screen transition events to subscreens in multiplayer --- osu.Game/Screens/Multi/Multiplayer.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index a7d40a89d3..c4259400fa 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.cs @@ -202,6 +202,11 @@ namespace osu.Game.Screens.Multi { this.FadeIn(); waves.Show(); + + if (loungeSubScreen.IsCurrentScreen()) + loungeSubScreen.OnEntering(last); + else + loungeSubScreen.MakeCurrent(); } public override void OnResuming(IScreen last) @@ -209,6 +214,7 @@ namespace osu.Game.Screens.Multi this.FadeIn(250); this.ScaleTo(1, 250, Easing.OutSine); + screenStack.CurrentScreen?.OnResuming(last); base.OnResuming(last); UpdatePollingRate(isIdle.Value); @@ -219,6 +225,8 @@ namespace osu.Game.Screens.Multi this.ScaleTo(1.1f, 250, Easing.InSine); this.FadeOut(250); + screenStack.CurrentScreen?.OnSuspending(next); + UpdatePollingRate(isIdle.Value); } @@ -230,9 +238,7 @@ namespace osu.Game.Screens.Multi this.Delay(WaveContainer.DISAPPEAR_DURATION).FadeOut(); - if (screenStack.CurrentScreen != null) - loungeSubScreen.MakeCurrent(); - + screenStack.CurrentScreen?.OnExiting(next); base.OnExiting(next); return false; } From 7f0f6d86b0f931401e4fb8b347485ff6656ee354 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 24 Dec 2020 16:08:45 +0100 Subject: [PATCH 550/818] Rename {room -> playlist} on playlist room screen --- osu.Game/Screens/Multi/Timeshift/TimeshiftRoomSubScreen.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Multi/Timeshift/TimeshiftRoomSubScreen.cs b/osu.Game/Screens/Multi/Timeshift/TimeshiftRoomSubScreen.cs index 730ad795b2..4a66f09a22 100644 --- a/osu.Game/Screens/Multi/Timeshift/TimeshiftRoomSubScreen.cs +++ b/osu.Game/Screens/Multi/Timeshift/TimeshiftRoomSubScreen.cs @@ -25,7 +25,7 @@ namespace osu.Game.Screens.Multi.Timeshift { public override string Title { get; } - public override string ShortTitle => "room"; + public override string ShortTitle => "playlist"; [Resolved(typeof(Room), nameof(Room.RoomID))] private Bindable roomId { get; set; } @@ -40,7 +40,7 @@ namespace osu.Game.Screens.Multi.Timeshift public TimeshiftRoomSubScreen(Room room) { - Title = room.RoomID.Value == null ? "New room" : room.Name.Value; + Title = room.RoomID.Value == null ? "New playlist" : room.Name.Value; Activity.Value = new UserActivity.InLobby(room); } From db1c11073f430edd13aa8a480b0e30233de4697e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 24 Dec 2020 16:10:29 +0100 Subject: [PATCH 551/818] Rename back to "room" for "realtime" multiplayer --- .../Multi/RealtimeMultiplayer/CreateRealtimeMatchButton.cs | 2 +- .../Multi/RealtimeMultiplayer/RealtimeMatchSubScreen.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/CreateRealtimeMatchButton.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/CreateRealtimeMatchButton.cs index eda907f8cb..cdaeb6faec 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/CreateRealtimeMatchButton.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/CreateRealtimeMatchButton.cs @@ -15,7 +15,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer { Triangles.TriangleScale = 1.5f; - Text = "Create match"; + Text = "Create room"; ((IBindable)Enabled).BindTo(multiplayerClient.IsConnected); } diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSubScreen.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSubScreen.cs index 15d997605c..1778bc272c 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSubScreen.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSubScreen.cs @@ -26,7 +26,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer { public override string Title { get; } - public override string ShortTitle => "match"; + public override string ShortTitle => "room"; [Resolved] private StatefulMultiplayerClient client { get; set; } @@ -37,7 +37,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer public RealtimeMatchSubScreen(Room room) { - Title = room.RoomID.Value == null ? "New match" : room.Name.Value; + Title = room.RoomID.Value == null ? "New room" : room.Name.Value; Activity.Value = new UserActivity.InLobby(room); } From 6ec045f2353e80534efc1961990ac3d04adf58f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 24 Dec 2020 16:18:35 +0100 Subject: [PATCH 552/818] Distinguish primary multi screen titles in header --- osu.Game.Tests/Visual/Multiplayer/TestSceneMultiHeader.cs | 2 +- osu.Game/Screens/Multi/Header.cs | 8 ++++---- osu.Game/Screens/Multi/Multiplayer.cs | 4 +++- .../Multi/RealtimeMultiplayer/RealtimeMultiplayer.cs | 2 ++ osu.Game/Screens/Multi/Timeshift/TimeshiftMultiplayer.cs | 2 ++ 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiHeader.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiHeader.cs index 76ab402b72..0ccd882d95 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiHeader.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiHeader.cs @@ -23,7 +23,7 @@ namespace osu.Game.Tests.Visual.Multiplayer Children = new Drawable[] { screenStack, - new Header(screenStack) + new Header("Multiplayer", screenStack) }; AddStep("push multi screen", () => screenStack.CurrentScreen.Push(new TestMultiplayerSubScreen(++index))); diff --git a/osu.Game/Screens/Multi/Header.cs b/osu.Game/Screens/Multi/Header.cs index cd8695286b..637d8bb52b 100644 --- a/osu.Game/Screens/Multi/Header.cs +++ b/osu.Game/Screens/Multi/Header.cs @@ -22,7 +22,7 @@ namespace osu.Game.Screens.Multi { public const float HEIGHT = 80; - public Header(ScreenStack stack) + public Header(string mainTitle, ScreenStack stack) { RelativeSizeAxes = Axes.X; Height = HEIGHT; @@ -45,7 +45,7 @@ namespace osu.Game.Screens.Multi Padding = new MarginPadding { Left = WaveOverlayContainer.WIDTH_PADDING + OsuScreen.HORIZONTAL_OVERFLOW_PADDING }, Children = new Drawable[] { - title = new MultiHeaderTitle + title = new MultiHeaderTitle(mainTitle) { Anchor = Anchor.CentreLeft, Origin = Anchor.BottomLeft, @@ -80,7 +80,7 @@ namespace osu.Game.Screens.Multi set => pageTitle.Text = value.ShortTitle.Titleize(); } - public MultiHeaderTitle() + public MultiHeaderTitle(string mainTitle) { AutoSizeAxes = Axes.Both; @@ -98,7 +98,7 @@ namespace osu.Game.Screens.Multi Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, Font = OsuFont.GetFont(size: 24), - Text = "Multiplayer" + Text = mainTitle }, dot = new OsuSpriteText { diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index 585ac71189..56c1c6cb37 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.cs @@ -127,7 +127,7 @@ namespace osu.Game.Screens.Multi screenStack = new MultiplayerSubScreenStack { RelativeSizeAxes = Axes.Both } } }, - new Header(screenStack), + new Header(ScreenTitle, screenStack), createButton = CreateNewMultiplayerGameButton().With(button => { button.Anchor = Anchor.TopRight; @@ -311,6 +311,8 @@ namespace osu.Game.Screens.Multi protected IScreen CurrentSubScreen => screenStack.CurrentScreen; + protected abstract string ScreenTitle { get; } + protected abstract RoomManager CreateRoomManager(); protected abstract LoungeSubScreen CreateLounge(); diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMultiplayer.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMultiplayer.cs index e15a6bd408..70308844b0 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMultiplayer.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMultiplayer.cs @@ -62,6 +62,8 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer return room; } + protected override string ScreenTitle => "Multiplayer"; + protected override RoomManager CreateRoomManager() => new RealtimeRoomManager(); protected override LoungeSubScreen CreateLounge() => new RealtimeLoungeSubScreen(); diff --git a/osu.Game/Screens/Multi/Timeshift/TimeshiftMultiplayer.cs b/osu.Game/Screens/Multi/Timeshift/TimeshiftMultiplayer.cs index e1b94f8455..60b01ee431 100644 --- a/osu.Game/Screens/Multi/Timeshift/TimeshiftMultiplayer.cs +++ b/osu.Game/Screens/Multi/Timeshift/TimeshiftMultiplayer.cs @@ -51,6 +51,8 @@ namespace osu.Game.Screens.Multi.Timeshift return new Room { Name = { Value = $"{API.LocalUser}'s awesome playlist" } }; } + protected override string ScreenTitle => "Playlists"; + protected override RoomManager CreateRoomManager() => new TimeshiftRoomManager(); protected override LoungeSubScreen CreateLounge() => new TimeshiftLoungeSubScreen(); From 60c7c8b63badedf7c0bef7ec7b5c365d1877e486 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 25 Dec 2020 00:44:42 +0900 Subject: [PATCH 553/818] Pluralise playlists in tip --- osu.Game/Screens/Menu/Disclaimer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/Disclaimer.cs b/osu.Game/Screens/Menu/Disclaimer.cs index 9c7206d259..46fddabb26 100644 --- a/osu.Game/Screens/Menu/Disclaimer.cs +++ b/osu.Game/Screens/Menu/Disclaimer.cs @@ -208,7 +208,7 @@ namespace osu.Game.Screens.Menu "Most of the web content (profiles, rankings, etc.) are available natively in-game from the icons on the toolbar!", "Get more details, hide or delete a beatmap by right-clicking on its panel at song select!", "All delete operations are temporary until exiting. Restore accidentally deleted content from the maintenance settings!", - "Check out the \"playlist\" system, which lets users create their own custom and permanent leaderboards!", + "Check out the \"playlists\" system, which lets users create their own custom and permanent leaderboards!", "Toggle advanced frame / thread statistics with Ctrl-F11!", "Take a look under the hood at performance counters and enable verbose performance logging with Ctrl-F2!", }; From a1384942b1e6b2720988cb4618f9eab7bf8058cb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 25 Dec 2020 13:11:21 +0900 Subject: [PATCH 554/818] Timeshift -> Playlists at a code level --- .../TestSceneMatchSettingsOverlay.cs | 4 +-- .../Multiplayer/TestSceneMultiScreen.cs | 4 +-- ....cs => TestScenePlaylistsFilterControl.cs} | 6 ++-- ...s => TestScenePlaylistsLoungeSubScreen.cs} | 6 ++-- ... => TestScenePlaylistsParticipantsList.cs} | 2 +- ....cs => TestScenePlaylistsResultsScreen.cs} | 4 +-- ....cs => TestScenePlaylistsRoomSubScreen.cs} | 14 ++++----- .../Navigation/TestSceneScreenNavigation.cs | 6 ++-- .../GameTypes/GameTypeTimeshift.cs | 4 +-- osu.Game/Online/Multiplayer/Room.cs | 2 +- osu.Game/Screens/Menu/ButtonSystem.cs | 8 ++--- osu.Game/Screens/Menu/MainMenu.cs | 4 +-- .../Components/TimeshiftFilterControl.cs | 14 ++++----- .../Screens/Multi/Match/Components/Footer.cs | 4 +-- .../Multi/Match/Components/GameTypePicker.cs | 2 +- .../Screens/Multi/Play/TimeshiftPlayer.cs | 8 ++--- .../CreatePlaylistsRoomButton.cs} | 4 +-- .../PlaylistsLoungeSubScreen.cs} | 8 ++--- .../PlaylistsMatchSettingsOverlay.cs} | 4 +-- .../PlaylistsMultiplayer.cs} | 30 +++++++++---------- .../PlaylistsReadyButton.cs} | 6 ++-- .../PlaylistsRoomManager.cs} | 4 +-- .../PlaylistsRoomSubScreen.cs} | 12 ++++---- .../Multi/Ranking/TimeshiftResultsScreen.cs | 4 +-- .../RealtimeMultiplayer.cs | 16 +++++----- .../RealtimeMultiplayer/RealtimePlayer.cs | 4 +-- .../RealtimeResultsScreen.cs | 2 +- 27 files changed, 93 insertions(+), 93 deletions(-) rename osu.Game.Tests/Visual/Multiplayer/{TestSceneTimeshiftFilterControl.cs => TestScenePlaylistsFilterControl.cs} (76%) rename osu.Game.Tests/Visual/Multiplayer/{TestSceneTimeshiftLoungeSubScreen.cs => TestScenePlaylistsLoungeSubScreen.cs} (93%) rename osu.Game.Tests/Visual/Multiplayer/{TestSceneTimeshiftParticipantsList.cs => TestScenePlaylistsParticipantsList.cs} (95%) rename osu.Game.Tests/Visual/Multiplayer/{TestSceneTimeshiftResultsScreen.cs => TestScenePlaylistsResultsScreen.cs} (99%) rename osu.Game.Tests/Visual/Multiplayer/{TestSceneTimeshiftRoomSubScreen.cs => TestScenePlaylistsRoomSubScreen.cs} (93%) rename osu.Game/Screens/Multi/{Timeshift/CreateTimeshiftRoomButton.cs => Playlists/CreatePlaylistsRoomButton.cs} (79%) rename osu.Game/Screens/Multi/{Timeshift/TimeshiftLoungeSubScreen.cs => Playlists/PlaylistsLoungeSubScreen.cs} (71%) rename osu.Game/Screens/Multi/{Timeshift/TimeshiftMatchSettingsOverlay.cs => Playlists/PlaylistsMatchSettingsOverlay.cs} (99%) rename osu.Game/Screens/Multi/{Timeshift/TimeshiftMultiplayer.cs => Playlists/PlaylistsMultiplayer.cs} (62%) rename osu.Game/Screens/Multi/{Timeshift/TimeshiftReadyButton.cs => Playlists/PlaylistsReadyButton.cs} (88%) rename osu.Game/Screens/Multi/{Timeshift/TimeshiftRoomManager.cs => Playlists/PlaylistsRoomManager.cs} (89%) rename osu.Game/Screens/Multi/{Timeshift/TimeshiftRoomSubScreen.cs => Playlists/PlaylistsRoomSubScreen.cs} (97%) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs index 1fcae9c709..90abecd26d 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs @@ -11,7 +11,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Multiplayer; using osu.Game.Screens.Multi; -using osu.Game.Screens.Multi.Timeshift; +using osu.Game.Screens.Multi.Playlists; namespace osu.Game.Tests.Visual.Multiplayer { @@ -109,7 +109,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("error not displayed", () => !settings.ErrorText.IsPresent); } - private class TestRoomSettings : TimeshiftMatchSettingsOverlay + private class TestRoomSettings : PlaylistsMatchSettingsOverlay { public TriangleButton ApplyButton => ((MatchSettings)Settings).ApplyButton; diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiScreen.cs index 0390b995e1..9ac1eb8013 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiScreen.cs @@ -4,7 +4,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Game.Overlays; -using osu.Game.Screens.Multi.Timeshift; +using osu.Game.Screens.Multi.Playlists; namespace osu.Game.Tests.Visual.Multiplayer { @@ -18,7 +18,7 @@ namespace osu.Game.Tests.Visual.Multiplayer public TestSceneMultiScreen() { - var multi = new TimeshiftMultiplayer(); + var multi = new PlaylistsMultiplayer(); AddStep("show", () => LoadScreen(multi)); AddUntilStep("wait for loaded", () => multi.IsLoaded); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftFilterControl.cs b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsFilterControl.cs similarity index 76% rename from osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftFilterControl.cs rename to osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsFilterControl.cs index f635a28b5c..427a69552d 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftFilterControl.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsFilterControl.cs @@ -6,11 +6,11 @@ using osu.Game.Screens.Multi.Lounge.Components; namespace osu.Game.Tests.Visual.Multiplayer { - public class TestSceneTimeshiftFilterControl : OsuTestScene + public class TestScenePlaylistsFilterControl : OsuTestScene { - public TestSceneTimeshiftFilterControl() + public TestScenePlaylistsFilterControl() { - Child = new TimeshiftFilterControl + Child = new PlaylistsFilterControl { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftLoungeSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsLoungeSubScreen.cs similarity index 93% rename from osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftLoungeSubScreen.cs rename to osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsLoungeSubScreen.cs index 73afd65d6d..f8788f0c36 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftLoungeSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsLoungeSubScreen.cs @@ -10,11 +10,11 @@ using osu.Framework.Testing; using osu.Game.Graphics.Containers; using osu.Game.Screens.Multi.Lounge; using osu.Game.Screens.Multi.Lounge.Components; -using osu.Game.Screens.Multi.Timeshift; +using osu.Game.Screens.Multi.Playlists; namespace osu.Game.Tests.Visual.Multiplayer { - public class TestSceneTimeshiftLoungeSubScreen : RoomManagerTestScene + public class TestScenePlaylistsLoungeSubScreen : RoomManagerTestScene { private LoungeSubScreen loungeScreen; @@ -27,7 +27,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { base.SetUpSteps(); - AddStep("push screen", () => LoadScreen(loungeScreen = new TimeshiftLoungeSubScreen + AddStep("push screen", () => LoadScreen(loungeScreen = new PlaylistsLoungeSubScreen { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsParticipantsList.cs similarity index 95% rename from osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftParticipantsList.cs rename to osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsParticipantsList.cs index efc3be032c..d71fdc42e3 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftParticipantsList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsParticipantsList.cs @@ -8,7 +8,7 @@ using osu.Game.Users; namespace osu.Game.Tests.Visual.Multiplayer { - public class TestSceneTimeshiftParticipantsList : MultiplayerTestScene + public class TestScenePlaylistsParticipantsList : MultiplayerTestScene { [SetUp] public new void Setup() => Schedule(() => diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsResultsScreen.cs similarity index 99% rename from osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs rename to osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsResultsScreen.cs index 03fd2b968c..99fc6597ee 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsResultsScreen.cs @@ -26,7 +26,7 @@ using osu.Game.Users; namespace osu.Game.Tests.Visual.Multiplayer { - public class TestSceneTimeshiftResultsScreen : ScreenTestScene + public class TestScenePlaylistsResultsScreen : ScreenTestScene { private const int scores_per_result = 10; @@ -360,7 +360,7 @@ namespace osu.Game.Tests.Visual.Multiplayer }; } - private class TestResultsScreen : TimeshiftResultsScreen + private class TestResultsScreen : PlaylistsResultsScreen { public new LoadingSpinner LeftSpinner => base.LeftSpinner; public new LoadingSpinner CentreSpinner => base.CentreSpinner; diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftRoomSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsRoomSubScreen.cs similarity index 93% rename from osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftRoomSubScreen.cs rename to osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsRoomSubScreen.cs index bbd7d84081..02cc03eca4 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftRoomSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsRoomSubScreen.cs @@ -17,14 +17,14 @@ using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; using osu.Game.Screens.Multi; using osu.Game.Screens.Multi.Match.Components; -using osu.Game.Screens.Multi.Timeshift; +using osu.Game.Screens.Multi.Playlists; using osu.Game.Tests.Beatmaps; using osu.Game.Users; using osuTK.Input; namespace osu.Game.Tests.Visual.Multiplayer { - public class TestSceneTimeshiftRoomSubScreen : MultiplayerTestScene + public class TestScenePlaylistsRoomSubScreen : MultiplayerTestScene { protected override bool UseOnlineAPI => true; @@ -34,7 +34,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private BeatmapManager manager; private RulesetStore rulesets; - private TestTimeshiftRoomSubScreen match; + private TestPlaylistsRoomSubScreen match; [BackgroundDependencyLoader] private void load(GameHost host, AudioManager audio) @@ -48,7 +48,7 @@ namespace osu.Game.Tests.Visual.Multiplayer [SetUpSteps] public void SetupSteps() { - AddStep("load match", () => LoadScreen(match = new TestTimeshiftRoomSubScreen(Room))); + AddStep("load match", () => LoadScreen(match = new TestPlaylistsRoomSubScreen(Room))); AddUntilStep("wait for load", () => match.IsCurrentScreen()); } @@ -120,7 +120,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("create room", () => { - InputManager.MoveMouseTo(match.ChildrenOfType().Single()); + InputManager.MoveMouseTo(match.ChildrenOfType().Single()); InputManager.Click(MouseButton.Left); }); @@ -131,13 +131,13 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("match has original beatmap", () => match.Beatmap.Value.Beatmap.BeatmapInfo.BaseDifficulty.CircleSize != 1); } - private class TestTimeshiftRoomSubScreen : TimeshiftRoomSubScreen + private class TestPlaylistsRoomSubScreen : PlaylistsRoomSubScreen { public new Bindable SelectedItem => base.SelectedItem; public new Bindable Beatmap => base.Beatmap; - public TestTimeshiftRoomSubScreen(Room room) + public TestPlaylistsRoomSubScreen(Room room) : base(room) { } diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index 43f97d8ace..381bbca74f 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -11,7 +11,7 @@ using osu.Game.Beatmaps; using osu.Game.Overlays; using osu.Game.Overlays.Mods; using osu.Game.Overlays.Toolbar; -using osu.Game.Screens.Multi.Timeshift; +using osu.Game.Screens.Multi.Playlists; using osu.Game.Screens.Play; using osu.Game.Screens.Select; using osu.Game.Screens.Select.Options; @@ -108,14 +108,14 @@ namespace osu.Game.Tests.Visual.Navigation [Test] public void TestExitMultiWithEscape() { - PushAndConfirm(() => new TimeshiftMultiplayer()); + PushAndConfirm(() => new PlaylistsMultiplayer()); exitViaEscapeAndConfirm(); } [Test] public void TestExitMultiWithBackButton() { - PushAndConfirm(() => new TimeshiftMultiplayer()); + PushAndConfirm(() => new PlaylistsMultiplayer()); exitViaBackButtonAndConfirm(); } diff --git a/osu.Game/Online/Multiplayer/GameTypes/GameTypeTimeshift.cs b/osu.Game/Online/Multiplayer/GameTypes/GameTypeTimeshift.cs index 1a3d2837ce..5840ccb803 100644 --- a/osu.Game/Online/Multiplayer/GameTypes/GameTypeTimeshift.cs +++ b/osu.Game/Online/Multiplayer/GameTypes/GameTypeTimeshift.cs @@ -8,9 +8,9 @@ using osuTK; namespace osu.Game.Online.Multiplayer.GameTypes { - public class GameTypeTimeshift : GameType + public class GameTypePlaylists : GameType { - public override string Name => "Timeshift"; + public override string Name => "Playlists"; public override Drawable GetIcon(OsuColour colours, float size) => new SpriteIcon { diff --git a/osu.Game/Online/Multiplayer/Room.cs b/osu.Game/Online/Multiplayer/Room.cs index 11efe281d1..d13d7dc774 100644 --- a/osu.Game/Online/Multiplayer/Room.cs +++ b/osu.Game/Online/Multiplayer/Room.cs @@ -66,7 +66,7 @@ namespace osu.Game.Online.Multiplayer [Cached] [JsonIgnore] - public readonly Bindable Type = new Bindable(new GameTypeTimeshift()); + public readonly Bindable Type = new Bindable(new GameTypePlaylists()); [Cached] [JsonIgnore] diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 5af6517f49..474cbde192 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -43,7 +43,7 @@ namespace osu.Game.Screens.Menu public Action OnSolo; public Action OnSettings; public Action OnMultiplayer; - public Action OnTimeshift; + public Action OnPlaylists; public const float BUTTON_WIDTH = 140f; public const float WEDGE_WIDTH = 20; @@ -125,7 +125,7 @@ namespace osu.Game.Screens.Menu { buttonsPlay.Add(new Button(@"solo", @"button-solo-select", FontAwesome.Solid.User, new Color4(102, 68, 204, 255), () => OnSolo?.Invoke(), WEDGE_WIDTH, Key.P)); buttonsPlay.Add(new Button(@"multi", @"button-generic-select", FontAwesome.Solid.Users, new Color4(94, 63, 186, 255), onMultiplayer, 0, Key.M)); - buttonsPlay.Add(new Button(@"playlists", @"button-generic-select", OsuIcon.Charts, new Color4(94, 63, 186, 255), onTimeshift, 0, Key.L)); + buttonsPlay.Add(new Button(@"playlists", @"button-generic-select", OsuIcon.Charts, new Color4(94, 63, 186, 255), onPlaylists, 0, Key.L)); buttonsPlay.ForEach(b => b.VisibleState = ButtonSystemState.Play); buttonsTopLevel.Add(new Button(@"play", @"button-play-select", OsuIcon.Logo, new Color4(102, 68, 204, 255), () => State = ButtonSystemState.Play, WEDGE_WIDTH, Key.P)); @@ -175,7 +175,7 @@ namespace osu.Game.Screens.Menu OnMultiplayer?.Invoke(); } - private void onTimeshift() + private void onPlaylists() { if (!api.IsLoggedIn) { @@ -193,7 +193,7 @@ namespace osu.Game.Screens.Menu return; } - OnTimeshift?.Invoke(); + OnPlaylists?.Invoke(); } private void updateIdleState(bool isIdle) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index fa96ac9c51..5650b22f25 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -18,7 +18,7 @@ using osu.Game.Overlays; using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Edit; using osu.Game.Screens.Multi.RealtimeMultiplayer; -using osu.Game.Screens.Multi.Timeshift; +using osu.Game.Screens.Multi.Playlists; using osu.Game.Screens.Select; namespace osu.Game.Screens.Menu @@ -106,7 +106,7 @@ namespace osu.Game.Screens.Menu }, OnSolo = onSolo, OnMultiplayer = () => this.Push(new RealtimeMultiplayer()), - OnTimeshift = () => this.Push(new TimeshiftMultiplayer()), + OnPlaylists = () => this.Push(new PlaylistsMultiplayer()), OnExit = confirmAndExit, } } diff --git a/osu.Game/Screens/Multi/Lounge/Components/TimeshiftFilterControl.cs b/osu.Game/Screens/Multi/Lounge/Components/TimeshiftFilterControl.cs index 68cab283a0..3c55c3c43f 100644 --- a/osu.Game/Screens/Multi/Lounge/Components/TimeshiftFilterControl.cs +++ b/osu.Game/Screens/Multi/Lounge/Components/TimeshiftFilterControl.cs @@ -7,13 +7,13 @@ using osu.Game.Graphics.UserInterface; namespace osu.Game.Screens.Multi.Lounge.Components { - public class TimeshiftFilterControl : FilterControl + public class PlaylistsFilterControl : FilterControl { - private readonly Dropdown dropdown; + private readonly Dropdown dropdown; - public TimeshiftFilterControl() + public PlaylistsFilterControl() { - AddInternal(dropdown = new SlimEnumDropdown + AddInternal(dropdown = new SlimEnumDropdown { Anchor = Anchor.BottomRight, Origin = Anchor.TopRight, @@ -37,11 +37,11 @@ namespace osu.Game.Screens.Multi.Lounge.Components switch (dropdown.Current.Value) { - case TimeshiftCategory.Normal: + case PlaylistsCategory.Normal: criteria.Category = "normal"; break; - case TimeshiftCategory.Spotlight: + case PlaylistsCategory.Spotlight: criteria.Category = "spotlight"; break; } @@ -49,7 +49,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components return criteria; } - private enum TimeshiftCategory + private enum PlaylistsCategory { Any, Normal, diff --git a/osu.Game/Screens/Multi/Match/Components/Footer.cs b/osu.Game/Screens/Multi/Match/Components/Footer.cs index d6a7e380bf..fdf1d0dbeb 100644 --- a/osu.Game/Screens/Multi/Match/Components/Footer.cs +++ b/osu.Game/Screens/Multi/Match/Components/Footer.cs @@ -10,7 +10,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Online.Multiplayer; -using osu.Game.Screens.Multi.Timeshift; +using osu.Game.Screens.Multi.Playlists; using osuTK; namespace osu.Game.Screens.Multi.Match.Components @@ -32,7 +32,7 @@ namespace osu.Game.Screens.Multi.Match.Components InternalChildren = new[] { background = new Box { RelativeSizeAxes = Axes.Both }, - new TimeshiftReadyButton + new PlaylistsReadyButton { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Screens/Multi/Match/Components/GameTypePicker.cs b/osu.Game/Screens/Multi/Match/Components/GameTypePicker.cs index b69cb9705d..c7fc329a1a 100644 --- a/osu.Game/Screens/Multi/Match/Components/GameTypePicker.cs +++ b/osu.Game/Screens/Multi/Match/Components/GameTypePicker.cs @@ -33,7 +33,7 @@ namespace osu.Game.Screens.Multi.Match.Components AddItem(new GameTypeVersus()); AddItem(new GameTypeTagTeam()); AddItem(new GameTypeTeamVersus()); - AddItem(new GameTypeTimeshift()); + AddItem(new GameTypePlaylists()); } private class GameTypePickerItem : DisableableTabItem diff --git a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs index f07f1c2fb0..65b0091505 100644 --- a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs +++ b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs @@ -20,7 +20,7 @@ using osu.Game.Screens.Ranking; namespace osu.Game.Screens.Multi.Play { - public class TimeshiftPlayer : Player + public class PlaylistsPlayer : Player { public Action Exited; @@ -37,7 +37,7 @@ namespace osu.Game.Screens.Multi.Play [Resolved] private IBindable ruleset { get; set; } - public TimeshiftPlayer(PlaylistItem playlistItem, PlayerConfiguration configuration = null) + public PlaylistsPlayer(PlaylistItem playlistItem, PlayerConfiguration configuration = null) : base(configuration) { PlaylistItem = playlistItem; @@ -50,7 +50,7 @@ namespace osu.Game.Screens.Multi.Play bool failed = false; - // Sanity checks to ensure that TimeshiftPlayer matches the settings for the current PlaylistItem + // Sanity checks to ensure that PlaylistsPlayer matches the settings for the current PlaylistItem if (Beatmap.Value.BeatmapInfo.OnlineBeatmapID != PlaylistItem.Beatmap.Value.OnlineBeatmapID) throw new InvalidOperationException("Current Beatmap does not match PlaylistItem's Beatmap"); @@ -94,7 +94,7 @@ namespace osu.Game.Screens.Multi.Play protected override ResultsScreen CreateResults(ScoreInfo score) { Debug.Assert(RoomId.Value != null); - return new TimeshiftResultsScreen(score, RoomId.Value.Value, PlaylistItem, true); + return new PlaylistsResultsScreen(score, RoomId.Value.Value, PlaylistItem, true); } protected override Score CreateScore() diff --git a/osu.Game/Screens/Multi/Timeshift/CreateTimeshiftRoomButton.cs b/osu.Game/Screens/Multi/Playlists/CreatePlaylistsRoomButton.cs similarity index 79% rename from osu.Game/Screens/Multi/Timeshift/CreateTimeshiftRoomButton.cs rename to osu.Game/Screens/Multi/Playlists/CreatePlaylistsRoomButton.cs index 0424493472..acee063115 100644 --- a/osu.Game/Screens/Multi/Timeshift/CreateTimeshiftRoomButton.cs +++ b/osu.Game/Screens/Multi/Playlists/CreatePlaylistsRoomButton.cs @@ -4,9 +4,9 @@ using osu.Framework.Allocation; using osu.Game.Screens.Multi.Match.Components; -namespace osu.Game.Screens.Multi.Timeshift +namespace osu.Game.Screens.Multi.Playlists { - public class CreateTimeshiftRoomButton : PurpleTriangleButton + public class CreatePlaylistsRoomButton : PurpleTriangleButton { [BackgroundDependencyLoader] private void load() diff --git a/osu.Game/Screens/Multi/Timeshift/TimeshiftLoungeSubScreen.cs b/osu.Game/Screens/Multi/Playlists/PlaylistsLoungeSubScreen.cs similarity index 71% rename from osu.Game/Screens/Multi/Timeshift/TimeshiftLoungeSubScreen.cs rename to osu.Game/Screens/Multi/Playlists/PlaylistsLoungeSubScreen.cs index 8e426ffbcc..b40c543b68 100644 --- a/osu.Game/Screens/Multi/Timeshift/TimeshiftLoungeSubScreen.cs +++ b/osu.Game/Screens/Multi/Playlists/PlaylistsLoungeSubScreen.cs @@ -6,12 +6,12 @@ using osu.Game.Screens.Multi.Lounge; using osu.Game.Screens.Multi.Lounge.Components; using osu.Game.Screens.Multi.Match; -namespace osu.Game.Screens.Multi.Timeshift +namespace osu.Game.Screens.Multi.Playlists { - public class TimeshiftLoungeSubScreen : LoungeSubScreen + public class PlaylistsLoungeSubScreen : LoungeSubScreen { - protected override FilterControl CreateFilterControl() => new TimeshiftFilterControl(); + protected override FilterControl CreateFilterControl() => new PlaylistsFilterControl(); - protected override RoomSubScreen CreateRoomSubScreen(Room room) => new TimeshiftRoomSubScreen(room); + protected override RoomSubScreen CreateRoomSubScreen(Room room) => new PlaylistsRoomSubScreen(room); } } diff --git a/osu.Game/Screens/Multi/Timeshift/TimeshiftMatchSettingsOverlay.cs b/osu.Game/Screens/Multi/Playlists/PlaylistsMatchSettingsOverlay.cs similarity index 99% rename from osu.Game/Screens/Multi/Timeshift/TimeshiftMatchSettingsOverlay.cs rename to osu.Game/Screens/Multi/Playlists/PlaylistsMatchSettingsOverlay.cs index 7e1e9894d8..af29e8d34d 100644 --- a/osu.Game/Screens/Multi/Timeshift/TimeshiftMatchSettingsOverlay.cs +++ b/osu.Game/Screens/Multi/Playlists/PlaylistsMatchSettingsOverlay.cs @@ -19,9 +19,9 @@ using osu.Game.Overlays; using osu.Game.Screens.Multi.Match.Components; using osuTK; -namespace osu.Game.Screens.Multi.Timeshift +namespace osu.Game.Screens.Multi.Playlists { - public class TimeshiftMatchSettingsOverlay : MatchSettingsOverlay + public class PlaylistsMatchSettingsOverlay : MatchSettingsOverlay { public Action EditPlaylist; diff --git a/osu.Game/Screens/Multi/Timeshift/TimeshiftMultiplayer.cs b/osu.Game/Screens/Multi/Playlists/PlaylistsMultiplayer.cs similarity index 62% rename from osu.Game/Screens/Multi/Timeshift/TimeshiftMultiplayer.cs rename to osu.Game/Screens/Multi/Playlists/PlaylistsMultiplayer.cs index 60b01ee431..fce24da966 100644 --- a/osu.Game/Screens/Multi/Timeshift/TimeshiftMultiplayer.cs +++ b/osu.Game/Screens/Multi/Playlists/PlaylistsMultiplayer.cs @@ -9,41 +9,41 @@ using osu.Game.Screens.Multi.Components; using osu.Game.Screens.Multi.Lounge; using osu.Game.Screens.Multi.Match; -namespace osu.Game.Screens.Multi.Timeshift +namespace osu.Game.Screens.Multi.Playlists { - public class TimeshiftMultiplayer : Multiplayer + public class PlaylistsMultiplayer : Multiplayer { protected override void UpdatePollingRate(bool isIdle) { - var timeshiftManager = (TimeshiftRoomManager)RoomManager; + var playlistsManager = (PlaylistsRoomManager)RoomManager; if (!this.IsCurrentScreen()) { - timeshiftManager.TimeBetweenListingPolls.Value = 0; - timeshiftManager.TimeBetweenSelectionPolls.Value = 0; + playlistsManager.TimeBetweenListingPolls.Value = 0; + playlistsManager.TimeBetweenSelectionPolls.Value = 0; } else { switch (CurrentSubScreen) { case LoungeSubScreen _: - timeshiftManager.TimeBetweenListingPolls.Value = isIdle ? 120000 : 15000; - timeshiftManager.TimeBetweenSelectionPolls.Value = isIdle ? 120000 : 15000; + playlistsManager.TimeBetweenListingPolls.Value = isIdle ? 120000 : 15000; + playlistsManager.TimeBetweenSelectionPolls.Value = isIdle ? 120000 : 15000; break; case RoomSubScreen _: - timeshiftManager.TimeBetweenListingPolls.Value = 0; - timeshiftManager.TimeBetweenSelectionPolls.Value = isIdle ? 30000 : 5000; + playlistsManager.TimeBetweenListingPolls.Value = 0; + playlistsManager.TimeBetweenSelectionPolls.Value = isIdle ? 30000 : 5000; break; default: - timeshiftManager.TimeBetweenListingPolls.Value = 0; - timeshiftManager.TimeBetweenSelectionPolls.Value = 0; + playlistsManager.TimeBetweenListingPolls.Value = 0; + playlistsManager.TimeBetweenSelectionPolls.Value = 0; break; } } - Logger.Log($"Polling adjusted (listing: {timeshiftManager.TimeBetweenListingPolls.Value}, selection: {timeshiftManager.TimeBetweenSelectionPolls.Value})"); + Logger.Log($"Polling adjusted (listing: {playlistsManager.TimeBetweenListingPolls.Value}, selection: {playlistsManager.TimeBetweenSelectionPolls.Value})"); } protected override Room CreateNewRoom() @@ -53,10 +53,10 @@ namespace osu.Game.Screens.Multi.Timeshift protected override string ScreenTitle => "Playlists"; - protected override RoomManager CreateRoomManager() => new TimeshiftRoomManager(); + protected override RoomManager CreateRoomManager() => new PlaylistsRoomManager(); - protected override LoungeSubScreen CreateLounge() => new TimeshiftLoungeSubScreen(); + protected override LoungeSubScreen CreateLounge() => new PlaylistsLoungeSubScreen(); - protected override OsuButton CreateNewMultiplayerGameButton() => new CreateTimeshiftRoomButton(); + protected override OsuButton CreateNewMultiplayerGameButton() => new CreatePlaylistsRoomButton(); } } diff --git a/osu.Game/Screens/Multi/Timeshift/TimeshiftReadyButton.cs b/osu.Game/Screens/Multi/Playlists/PlaylistsReadyButton.cs similarity index 88% rename from osu.Game/Screens/Multi/Timeshift/TimeshiftReadyButton.cs rename to osu.Game/Screens/Multi/Playlists/PlaylistsReadyButton.cs index c878451eee..f5adf899e3 100644 --- a/osu.Game/Screens/Multi/Timeshift/TimeshiftReadyButton.cs +++ b/osu.Game/Screens/Multi/Playlists/PlaylistsReadyButton.cs @@ -8,14 +8,14 @@ using osu.Game.Graphics; using osu.Game.Online.Multiplayer; using osu.Game.Screens.Multi.Components; -namespace osu.Game.Screens.Multi.Timeshift +namespace osu.Game.Screens.Multi.Playlists { - public class TimeshiftReadyButton : ReadyButton + public class PlaylistsReadyButton : ReadyButton { [Resolved(typeof(Room), nameof(Room.EndDate))] private Bindable endDate { get; set; } - public TimeshiftReadyButton() + public PlaylistsReadyButton() { Text = "Start"; } diff --git a/osu.Game/Screens/Multi/Timeshift/TimeshiftRoomManager.cs b/osu.Game/Screens/Multi/Playlists/PlaylistsRoomManager.cs similarity index 89% rename from osu.Game/Screens/Multi/Timeshift/TimeshiftRoomManager.cs rename to osu.Game/Screens/Multi/Playlists/PlaylistsRoomManager.cs index d21f844e04..ae57eeddcc 100644 --- a/osu.Game/Screens/Multi/Timeshift/TimeshiftRoomManager.cs +++ b/osu.Game/Screens/Multi/Playlists/PlaylistsRoomManager.cs @@ -5,9 +5,9 @@ using System.Collections.Generic; using osu.Framework.Bindables; using osu.Game.Screens.Multi.Components; -namespace osu.Game.Screens.Multi.Timeshift +namespace osu.Game.Screens.Multi.Playlists { - public class TimeshiftRoomManager : RoomManager + public class PlaylistsRoomManager : RoomManager { public readonly Bindable TimeBetweenListingPolls = new Bindable(); public readonly Bindable TimeBetweenSelectionPolls = new Bindable(); diff --git a/osu.Game/Screens/Multi/Timeshift/TimeshiftRoomSubScreen.cs b/osu.Game/Screens/Multi/Playlists/PlaylistsRoomSubScreen.cs similarity index 97% rename from osu.Game/Screens/Multi/Timeshift/TimeshiftRoomSubScreen.cs rename to osu.Game/Screens/Multi/Playlists/PlaylistsRoomSubScreen.cs index 4a66f09a22..b2bcba1724 100644 --- a/osu.Game/Screens/Multi/Timeshift/TimeshiftRoomSubScreen.cs +++ b/osu.Game/Screens/Multi/Playlists/PlaylistsRoomSubScreen.cs @@ -19,9 +19,9 @@ using osu.Game.Screens.Select; using osu.Game.Users; using Footer = osu.Game.Screens.Multi.Match.Components.Footer; -namespace osu.Game.Screens.Multi.Timeshift +namespace osu.Game.Screens.Multi.Playlists { - public class TimeshiftRoomSubScreen : RoomSubScreen + public class PlaylistsRoomSubScreen : RoomSubScreen { public override string Title { get; } @@ -38,7 +38,7 @@ namespace osu.Game.Screens.Multi.Timeshift private OverlinedHeader participantsHeader; - public TimeshiftRoomSubScreen(Room room) + public PlaylistsRoomSubScreen(Room room) { Title = room.RoomID.Value == null ? "New playlist" : room.Name.Value; Activity.Value = new UserActivity.InLobby(room); @@ -126,7 +126,7 @@ namespace osu.Game.Screens.Multi.Timeshift RequestShowResults = item => { Debug.Assert(roomId.Value != null); - multiplayer?.Push(new TimeshiftResultsScreen(null, roomId.Value.Value, item, false)); + multiplayer?.Push(new PlaylistsResultsScreen(null, roomId.Value.Value, item, false)); } } }, @@ -188,7 +188,7 @@ namespace osu.Game.Screens.Multi.Timeshift new Dimension(GridSizeMode.AutoSize), } }, - settingsOverlay = new TimeshiftMatchSettingsOverlay + settingsOverlay = new PlaylistsMatchSettingsOverlay { RelativeSizeAxes = Axes.Both, EditPlaylist = () => this.Push(new MatchSongSelect()), @@ -219,7 +219,7 @@ namespace osu.Game.Screens.Multi.Timeshift }, true); } - private void onStart() => StartPlay(() => new TimeshiftPlayer(SelectedItem.Value) + private void onStart() => StartPlay(() => new PlaylistsPlayer(SelectedItem.Value) { Exited = () => leaderboard.RefreshScores() }); diff --git a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs index d3f1c19c7c..c757433e2f 100644 --- a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs +++ b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs @@ -17,7 +17,7 @@ using osu.Game.Screens.Ranking; namespace osu.Game.Screens.Multi.Ranking { - public class TimeshiftResultsScreen : ResultsScreen + public class PlaylistsResultsScreen : ResultsScreen { private readonly int roomId; private readonly PlaylistItem playlistItem; @@ -32,7 +32,7 @@ namespace osu.Game.Screens.Multi.Ranking [Resolved] private IAPIProvider api { get; set; } - public TimeshiftResultsScreen(ScoreInfo score, int roomId, PlaylistItem playlistItem, bool allowRetry, bool allowWatchingReplay = true) + public PlaylistsResultsScreen(ScoreInfo score, int roomId, PlaylistItem playlistItem, bool allowRetry, bool allowWatchingReplay = true) : base(score, allowRetry, allowWatchingReplay) { this.roomId = roomId; diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMultiplayer.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMultiplayer.cs index 70308844b0..87c838f29f 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMultiplayer.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMultiplayer.cs @@ -28,31 +28,31 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer protected override void UpdatePollingRate(bool isIdle) { - var timeshiftManager = (RealtimeRoomManager)RoomManager; + var playlistsManager = (RealtimeRoomManager)RoomManager; if (!this.IsCurrentScreen()) { - timeshiftManager.TimeBetweenListingPolls.Value = 0; - timeshiftManager.TimeBetweenSelectionPolls.Value = 0; + playlistsManager.TimeBetweenListingPolls.Value = 0; + playlistsManager.TimeBetweenSelectionPolls.Value = 0; } else { switch (CurrentSubScreen) { case LoungeSubScreen _: - timeshiftManager.TimeBetweenListingPolls.Value = isIdle ? 120000 : 15000; - timeshiftManager.TimeBetweenSelectionPolls.Value = isIdle ? 120000 : 15000; + playlistsManager.TimeBetweenListingPolls.Value = isIdle ? 120000 : 15000; + playlistsManager.TimeBetweenSelectionPolls.Value = isIdle ? 120000 : 15000; break; // Don't poll inside the match or anywhere else. default: - timeshiftManager.TimeBetweenListingPolls.Value = 0; - timeshiftManager.TimeBetweenSelectionPolls.Value = 0; + playlistsManager.TimeBetweenListingPolls.Value = 0; + playlistsManager.TimeBetweenSelectionPolls.Value = 0; break; } } - Logger.Log($"Polling adjusted (listing: {timeshiftManager.TimeBetweenListingPolls.Value}, selection: {timeshiftManager.TimeBetweenSelectionPolls.Value})"); + Logger.Log($"Polling adjusted (listing: {playlistsManager.TimeBetweenListingPolls.Value}, selection: {playlistsManager.TimeBetweenSelectionPolls.Value})"); } protected override Room CreateNewRoom() diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs index 8eb6a11228..033e4756eb 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs @@ -19,8 +19,8 @@ using osuTK; namespace osu.Game.Screens.Multi.RealtimeMultiplayer { - // Todo: The "room" part of TimeshiftPlayer should be split out into an abstract player class to be inherited instead. - public class RealtimePlayer : TimeshiftPlayer + // Todo: The "room" part of PlaylistsPlayer should be split out into an abstract player class to be inherited instead. + public class RealtimePlayer : PlaylistsPlayer { protected override bool PauseOnFocusLost => false; diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeResultsScreen.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeResultsScreen.cs index 3964a87eb6..6bec06cbba 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeResultsScreen.cs +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeResultsScreen.cs @@ -7,7 +7,7 @@ using osu.Game.Screens.Multi.Ranking; namespace osu.Game.Screens.Multi.RealtimeMultiplayer { - public class RealtimeResultsScreen : TimeshiftResultsScreen + public class RealtimeResultsScreen : PlaylistsResultsScreen { public RealtimeResultsScreen(ScoreInfo score, int roomId, PlaylistItem playlistItem) : base(score, roomId, playlistItem, false, false) From 12e4bbdc5b00de3a1c81da809fad4c69787269a1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 25 Dec 2020 13:20:37 +0900 Subject: [PATCH 555/818] Reorganise test scenes into playlists specific namespace --- .../TestSceneRealtimeMatchSubScreen.cs | 3 ++- .../TestSceneRealtimeMultiplayer.cs | 3 ++- .../TestSceneRealtimeMultiplayerParticipantsList.cs | 3 ++- .../TestSceneRealtimeReadyButton.cs | 3 ++- .../TestSceneRealtimeRoomManager.cs | 3 ++- .../TestScenePlaylistsFilterControl.cs | 2 +- .../TestScenePlaylistsLoungeSubScreen.cs | 3 ++- .../TestScenePlaylistsMatchSettingsOverlay.cs} | 4 ++-- .../TestScenePlaylistsParticipantsList.cs | 2 +- .../TestScenePlaylistsResultsScreen.cs | 2 +- .../TestScenePlaylistsRoomSubScreen.cs | 2 +- .../TestScenePlaylistsScreen.cs} | 6 +++--- 12 files changed, 21 insertions(+), 15 deletions(-) rename osu.Game.Tests/Visual/{RealtimeMultiplayer => Multiplayer}/TestSceneRealtimeMatchSubScreen.cs (96%) rename osu.Game.Tests/Visual/{RealtimeMultiplayer => Multiplayer}/TestSceneRealtimeMultiplayer.cs (94%) rename osu.Game.Tests/Visual/{RealtimeMultiplayer => Multiplayer}/TestSceneRealtimeMultiplayerParticipantsList.cs (97%) rename osu.Game.Tests/Visual/{RealtimeMultiplayer => Multiplayer}/TestSceneRealtimeReadyButton.cs (98%) rename osu.Game.Tests/Visual/{RealtimeMultiplayer => Multiplayer}/TestSceneRealtimeRoomManager.cs (98%) rename osu.Game.Tests/Visual/{Multiplayer => Playlists}/TestScenePlaylistsFilterControl.cs (93%) rename osu.Game.Tests/Visual/{Multiplayer => Playlists}/TestScenePlaylistsLoungeSubScreen.cs (95%) rename osu.Game.Tests/Visual/{Multiplayer/TestSceneMatchSettingsOverlay.cs => Playlists/TestScenePlaylistsMatchSettingsOverlay.cs} (97%) rename osu.Game.Tests/Visual/{Multiplayer => Playlists}/TestScenePlaylistsParticipantsList.cs (97%) rename osu.Game.Tests/Visual/{Multiplayer => Playlists}/TestScenePlaylistsResultsScreen.cs (99%) rename osu.Game.Tests/Visual/{Multiplayer => Playlists}/TestScenePlaylistsRoomSubScreen.cs (99%) rename osu.Game.Tests/Visual/{Multiplayer/TestSceneMultiScreen.cs => Playlists/TestScenePlaylistsScreen.cs} (82%) diff --git a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneRealtimeMatchSubScreen.cs similarity index 96% rename from osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeMatchSubScreen.cs rename to osu.Game.Tests/Visual/Multiplayer/TestSceneRealtimeMatchSubScreen.cs index a059bb1cc0..dff375d1bb 100644 --- a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeMatchSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneRealtimeMatchSubScreen.cs @@ -10,9 +10,10 @@ using osu.Game.Rulesets.Osu; using osu.Game.Screens.Multi.RealtimeMultiplayer; using osu.Game.Screens.Multi.RealtimeMultiplayer.Match; using osu.Game.Tests.Beatmaps; +using osu.Game.Tests.Visual.RealtimeMultiplayer; using osuTK.Input; -namespace osu.Game.Tests.Visual.RealtimeMultiplayer +namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneRealtimeMatchSubScreen : RealtimeMultiplayerTestScene { diff --git a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneRealtimeMultiplayer.cs similarity index 94% rename from osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeMultiplayer.cs rename to osu.Game.Tests/Visual/Multiplayer/TestSceneRealtimeMultiplayer.cs index 5cf80df6aa..cc07bb8e79 100644 --- a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneRealtimeMultiplayer.cs @@ -3,9 +3,10 @@ using NUnit.Framework; using osu.Game.Screens.Multi.Components; +using osu.Game.Tests.Visual.RealtimeMultiplayer; using osu.Game.Users; -namespace osu.Game.Tests.Visual.RealtimeMultiplayer +namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneRealtimeMultiplayer : RealtimeMultiplayerTestScene { diff --git a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeMultiplayerParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneRealtimeMultiplayerParticipantsList.cs similarity index 97% rename from osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeMultiplayerParticipantsList.cs rename to osu.Game.Tests/Visual/Multiplayer/TestSceneRealtimeMultiplayerParticipantsList.cs index 4221821496..7c26918927 100644 --- a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeMultiplayerParticipantsList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneRealtimeMultiplayerParticipantsList.cs @@ -9,10 +9,11 @@ using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Online.RealtimeMultiplayer; using osu.Game.Screens.Multi.RealtimeMultiplayer.Participants; +using osu.Game.Tests.Visual.RealtimeMultiplayer; using osu.Game.Users; using osuTK; -namespace osu.Game.Tests.Visual.RealtimeMultiplayer +namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneRealtimeMultiplayerParticipantsList : RealtimeMultiplayerTestScene { diff --git a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeReadyButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneRealtimeReadyButton.cs similarity index 98% rename from osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeReadyButton.cs rename to osu.Game.Tests/Visual/Multiplayer/TestSceneRealtimeReadyButton.cs index 825470846c..3c92276629 100644 --- a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeReadyButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneRealtimeReadyButton.cs @@ -14,11 +14,12 @@ using osu.Game.Online.RealtimeMultiplayer; using osu.Game.Rulesets; using osu.Game.Screens.Multi.RealtimeMultiplayer.Match; using osu.Game.Tests.Resources; +using osu.Game.Tests.Visual.RealtimeMultiplayer; using osu.Game.Users; using osuTK; using osuTK.Input; -namespace osu.Game.Tests.Visual.RealtimeMultiplayer +namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneRealtimeReadyButton : RealtimeMultiplayerTestScene { diff --git a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeRoomManager.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneRealtimeRoomManager.cs similarity index 98% rename from osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeRoomManager.cs rename to osu.Game.Tests/Visual/Multiplayer/TestSceneRealtimeRoomManager.cs index 925a83a863..ba90e0840b 100644 --- a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeRoomManager.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneRealtimeRoomManager.cs @@ -5,8 +5,9 @@ using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Testing; using osu.Game.Online.Multiplayer; +using osu.Game.Tests.Visual.RealtimeMultiplayer; -namespace osu.Game.Tests.Visual.RealtimeMultiplayer +namespace osu.Game.Tests.Visual.Multiplayer { [HeadlessTest] public class TestSceneRealtimeRoomManager : MultiplayerTestScene diff --git a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsFilterControl.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsFilterControl.cs similarity index 93% rename from osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsFilterControl.cs rename to osu.Game.Tests/Visual/Playlists/TestScenePlaylistsFilterControl.cs index 427a69552d..66992b27a2 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsFilterControl.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsFilterControl.cs @@ -4,7 +4,7 @@ using osu.Framework.Graphics; using osu.Game.Screens.Multi.Lounge.Components; -namespace osu.Game.Tests.Visual.Multiplayer +namespace osu.Game.Tests.Visual.Playlists { public class TestScenePlaylistsFilterControl : OsuTestScene { diff --git a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsLoungeSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs similarity index 95% rename from osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsLoungeSubScreen.cs rename to osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs index f8788f0c36..04555857f5 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsLoungeSubScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs @@ -11,8 +11,9 @@ using osu.Game.Graphics.Containers; using osu.Game.Screens.Multi.Lounge; using osu.Game.Screens.Multi.Lounge.Components; using osu.Game.Screens.Multi.Playlists; +using osu.Game.Tests.Visual.Multiplayer; -namespace osu.Game.Tests.Visual.Multiplayer +namespace osu.Game.Tests.Visual.Playlists { public class TestScenePlaylistsLoungeSubScreen : RoomManagerTestScene { diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs similarity index 97% rename from osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs rename to osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs index 90abecd26d..2a110c0386 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs @@ -13,9 +13,9 @@ using osu.Game.Online.Multiplayer; using osu.Game.Screens.Multi; using osu.Game.Screens.Multi.Playlists; -namespace osu.Game.Tests.Visual.Multiplayer +namespace osu.Game.Tests.Visual.Playlists { - public class TestSceneMatchSettingsOverlay : MultiplayerTestScene + public class TestScenePlaylistsMatchSettingsOverlay : MultiplayerTestScene { [Cached(Type = typeof(IRoomManager))] private TestRoomManager roomManager = new TestRoomManager(); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsParticipantsList.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsParticipantsList.cs similarity index 97% rename from osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsParticipantsList.cs rename to osu.Game.Tests/Visual/Playlists/TestScenePlaylistsParticipantsList.cs index d71fdc42e3..d5553e4527 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsParticipantsList.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsParticipantsList.cs @@ -6,7 +6,7 @@ using osu.Framework.Graphics; using osu.Game.Screens.Multi.Components; using osu.Game.Users; -namespace osu.Game.Tests.Visual.Multiplayer +namespace osu.Game.Tests.Visual.Playlists { public class TestScenePlaylistsParticipantsList : MultiplayerTestScene { diff --git a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsResultsScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs similarity index 99% rename from osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsResultsScreen.cs rename to osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs index 99fc6597ee..10ae44351b 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsResultsScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs @@ -24,7 +24,7 @@ using osu.Game.Screens.Ranking; using osu.Game.Tests.Beatmaps; using osu.Game.Users; -namespace osu.Game.Tests.Visual.Multiplayer +namespace osu.Game.Tests.Visual.Playlists { public class TestScenePlaylistsResultsScreen : ScreenTestScene { diff --git a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsRoomSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs similarity index 99% rename from osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsRoomSubScreen.cs rename to osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs index 02cc03eca4..aebff14c80 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsRoomSubScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs @@ -22,7 +22,7 @@ using osu.Game.Tests.Beatmaps; using osu.Game.Users; using osuTK.Input; -namespace osu.Game.Tests.Visual.Multiplayer +namespace osu.Game.Tests.Visual.Playlists { public class TestScenePlaylistsRoomSubScreen : MultiplayerTestScene { diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsScreen.cs similarity index 82% rename from osu.Game.Tests/Visual/Multiplayer/TestSceneMultiScreen.cs rename to osu.Game.Tests/Visual/Playlists/TestScenePlaylistsScreen.cs index 9ac1eb8013..8203ca4845 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsScreen.cs @@ -6,17 +6,17 @@ using osu.Framework.Allocation; using osu.Game.Overlays; using osu.Game.Screens.Multi.Playlists; -namespace osu.Game.Tests.Visual.Multiplayer +namespace osu.Game.Tests.Visual.Playlists { [TestFixture] - public class TestSceneMultiScreen : ScreenTestScene + public class TestScenePlaylistsScreen : ScreenTestScene { protected override bool UseOnlineAPI => true; [Cached] private MusicController musicController { get; set; } = new MusicController(); - public TestSceneMultiScreen() + public TestScenePlaylistsScreen() { var multi = new PlaylistsMultiplayer(); From 5d4b73baa5f655c1c7f77a69228c06e895c40b6d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 25 Dec 2020 13:38:11 +0900 Subject: [PATCH 556/818] RealtimeMultiplayer -> Multiplayer --- .../Multiplayer/RoomManagerTestScene.cs | 4 ++-- .../Visual/Multiplayer/TestRoomManager.cs | 2 +- .../TestSceneDrawableRoomPlaylist.cs | 2 +- .../Multiplayer/TestSceneLoungeRoomInfo.cs | 2 +- .../TestSceneLoungeRoomsContainer.cs | 2 +- .../TestSceneMatchBeatmapDetailArea.cs | 2 +- .../Multiplayer/TestSceneMatchHeader.cs | 2 +- ...Multiplayer.cs => TestSceneMultiplayer.cs} | 11 ++++----- ... => TestSceneMultiplayerMatchSubScreen.cs} | 21 ++++++++-------- ...> TestSceneMultiplayerParticipantsList.cs} | 7 +++--- ....cs => TestSceneMultiplayerReadyButton.cs} | 11 ++++----- ....cs => TestSceneMultiplayerRoomManager.cs} | 13 +++++----- .../Visual/Multiplayer/TestSceneRoomStatus.cs | 8 +++---- .../TestScenePlaylistsMatchSettingsOverlay.cs | 4 ++-- .../TestScenePlaylistsParticipantsList.cs | 2 +- .../TestScenePlaylistsResultsScreen.cs | 2 +- .../TestScenePlaylistsRoomSubScreen.cs | 4 ++-- .../IMultiplayerClient.cs | 2 +- .../IMultiplayerLoungeServer.cs | 2 +- .../IMultiplayerRoomServer.cs | 2 +- .../IMultiplayerServer.cs | 2 +- .../InvalidStateChangeException.cs | 2 +- .../InvalidStateException.cs | 2 +- .../MultiplayerClient.cs} | 6 ++--- .../MultiplayerRoom.cs | 2 +- .../MultiplayerRoomSettings.cs | 2 +- .../MultiplayerRoomState.cs | 4 ++-- .../MultiplayerRoomUser.cs | 2 +- .../MultiplayerUserState.cs | 2 +- .../NotHostException.cs | 2 +- .../NotJoinedRoomException.cs | 2 +- .../StatefulMultiplayerClient.cs | 6 ++--- .../{Multiplayer => Rooms}/APICreatedRoom.cs | 2 +- .../{Multiplayer => Rooms}/APILeaderboard.cs | 2 +- .../APIPlaylistBeatmap.cs | 2 +- .../{Multiplayer => Rooms}/APIScoreToken.cs | 2 +- .../CreateRoomRequest.cs | 2 +- .../CreateRoomScoreRequest.cs | 2 +- .../Online/{Multiplayer => Rooms}/GameType.cs | 2 +- .../GameTypes/GameTypeTag.cs | 2 +- .../GameTypes/GameTypeTagTeam.cs | 2 +- .../GameTypes/GameTypeTeamVersus.cs | 2 +- .../GameTypes/GameTypeTimeshift.cs | 2 +- .../GameTypes/GameTypeVersus.cs | 2 +- .../GameTypes/VersusRow.cs | 2 +- .../GetRoomLeaderboardRequest.cs | 2 +- .../{Multiplayer => Rooms}/GetRoomRequest.cs | 2 +- .../{Multiplayer => Rooms}/GetRoomsRequest.cs | 2 +- .../IndexPlaylistScoresRequest.cs | 2 +- .../IndexScoresParams.cs | 2 +- .../IndexedMultiplayerScores.cs | 2 +- .../{Multiplayer => Rooms}/JoinRoomRequest.cs | 2 +- .../MultiplayerScore.cs | 2 +- .../MultiplayerScores.cs | 2 +- .../MultiplayerScoresAround.cs | 2 +- .../{Multiplayer => Rooms}/PartRoomRequest.cs | 2 +- .../PlaylistExtensions.cs | 2 +- .../{Multiplayer => Rooms}/PlaylistItem.cs | 2 +- .../Online/{Multiplayer => Rooms}/Room.cs | 6 ++--- .../RoomAvailability.cs | 2 +- .../{Multiplayer => Rooms}/RoomCategory.cs | 4 ++-- .../{Multiplayer => Rooms}/RoomStatus.cs | 4 ++-- .../RoomStatuses/RoomStatusEnded.cs | 2 +- .../RoomStatuses/RoomStatusOpen.cs | 2 +- .../RoomStatuses/RoomStatusPlaying.cs | 2 +- .../ShowPlaylistUserScoreRequest.cs | 2 +- .../SubmitRoomScoreRequest.cs | 2 +- osu.Game/OsuGameBase.cs | 4 ++-- osu.Game/Screens/Menu/MainMenu.cs | 4 ++-- .../Multi/Components/DrawableGameType.cs | 2 +- .../Components/ListingPollingComponent.cs | 2 +- .../Components/MatchBeatmapDetailArea.cs | 2 +- .../Components/OverlinedPlaylistHeader.cs | 2 +- .../Screens/Multi/Components/ReadyButton.cs | 2 +- .../Screens/Multi/Components/RoomManager.cs | 2 +- .../Multi/Components/RoomPollingComponent.cs | 2 +- .../Multi/Components/RoomStatusInfo.cs | 4 ++-- .../Components/SelectionPollingComponent.cs | 2 +- .../Components/StatusColouredContainer.cs | 2 +- .../Screens/Multi/DrawableRoomPlaylist.cs | 2 +- .../Screens/Multi/DrawableRoomPlaylistItem.cs | 2 +- .../Multi/DrawableRoomPlaylistWithResults.cs | 2 +- osu.Game/Screens/Multi/IRoomManager.cs | 2 +- .../Multi/Lounge/Components/DrawableRoom.cs | 6 ++--- .../Multi/Lounge/Components/RoomsContainer.cs | 2 +- .../Screens/Multi/Lounge/LoungeSubScreen.cs | 2 +- .../Screens/Multi/Match/Components/Footer.cs | 2 +- .../Multi/Match/Components/GameTypePicker.cs | 4 ++-- .../Match/Components/MatchChatDisplay.cs | 2 +- .../Match/Components/MatchLeaderboard.cs | 2 +- .../Components/RoomAvailabilityPicker.cs | 2 +- osu.Game/Screens/Multi/Match/RoomSubScreen.cs | 4 ++-- .../CreateMultiplayerMatchButton.cs} | 6 ++--- .../Match/BeatmapSelectionControl.cs | 6 ++--- .../Match/MultiplayerMatchFooter.cs} | 10 ++++---- .../Match/MultiplayerMatchHeader.cs} | 6 ++--- .../Match/MultiplayerMatchSettingsOverlay.cs} | 6 ++--- .../Match/MultiplayerReadyButton.cs} | 8 +++---- .../Multiplayer.cs} | 16 ++++++------- .../MultiplayerFilterControl.cs} | 4 ++-- .../MultiplayerLoungeSubScreen.cs} | 10 ++++---- .../MultiplayerMatchSongSelect.cs} | 8 +++---- .../MultiplayerMatchSubScreen.cs} | 24 +++++++++---------- .../MultiplayerPlayer.cs} | 12 +++++----- .../MultiplayerResultsScreen.cs} | 8 +++---- .../MultiplayerRoomComposite.cs} | 6 ++--- .../MultiplayerRoomManager.cs} | 16 ++++++------- .../Participants/ParticipantPanel.cs | 6 ++--- .../Participants/ParticipantsList.cs | 4 ++-- .../Participants/ParticipantsListHeader.cs | 4 ++-- .../Participants/StateDisplay.cs | 4 ++-- .../Screens/Multi/MultiplayerComposite.cs | 2 +- .../{Multiplayer.cs => MultiplayerScreen.cs} | 6 ++--- .../Screens/Multi/Play/TimeshiftPlayer.cs | 2 +- .../Playlists/PlaylistsLoungeSubScreen.cs | 2 +- .../PlaylistsMatchSettingsOverlay.cs | 2 +- .../Multi/Playlists/PlaylistsMultiplayer.cs | 4 ++-- .../Multi/Playlists/PlaylistsReadyButton.cs | 2 +- .../Multi/Playlists/PlaylistsRoomSubScreen.cs | 4 ++-- .../Multi/Ranking/TimeshiftResultsScreen.cs | 2 +- osu.Game/Screens/Select/MatchSongSelect.cs | 2 +- .../MultiplayerTestScene.cs} | 16 ++++++------- .../TestMultiplayerClient.cs} | 6 ++--- .../TestMultiplayerRoomContainer.cs} | 16 ++++++------- .../TestMultiplayerRoomManager.cs} | 8 +++---- ...ltiplayerTestScene.cs => RoomTestScene.cs} | 4 ++-- osu.Game/Users/UserActivity.cs | 2 +- 127 files changed, 260 insertions(+), 265 deletions(-) rename osu.Game.Tests/Visual/Multiplayer/{TestSceneRealtimeMultiplayer.cs => TestSceneMultiplayer.cs} (77%) rename osu.Game.Tests/Visual/Multiplayer/{TestSceneRealtimeMatchSubScreen.cs => TestSceneMultiplayerMatchSubScreen.cs} (73%) rename osu.Game.Tests/Visual/Multiplayer/{TestSceneRealtimeMultiplayerParticipantsList.cs => TestSceneMultiplayerParticipantsList.cs} (94%) rename osu.Game.Tests/Visual/Multiplayer/{TestSceneRealtimeReadyButton.cs => TestSceneMultiplayerReadyButton.cs} (94%) rename osu.Game.Tests/Visual/Multiplayer/{TestSceneRealtimeRoomManager.cs => TestSceneMultiplayerRoomManager.cs} (92%) rename osu.Game/Online/{RealtimeMultiplayer => Multiplayer}/IMultiplayerClient.cs (98%) rename osu.Game/Online/{RealtimeMultiplayer => Multiplayer}/IMultiplayerLoungeServer.cs (93%) rename osu.Game/Online/{RealtimeMultiplayer => Multiplayer}/IMultiplayerRoomServer.cs (98%) rename osu.Game/Online/{RealtimeMultiplayer => Multiplayer}/IMultiplayerServer.cs (88%) rename osu.Game/Online/{RealtimeMultiplayer => Multiplayer}/InvalidStateChangeException.cs (93%) rename osu.Game/Online/{RealtimeMultiplayer => Multiplayer}/InvalidStateException.cs (92%) rename osu.Game/Online/{RealtimeMultiplayer/RealtimeMultiplayerClient.cs => Multiplayer/MultiplayerClient.cs} (97%) rename osu.Game/Online/{RealtimeMultiplayer => Multiplayer}/MultiplayerRoom.cs (98%) rename osu.Game/Online/{RealtimeMultiplayer => Multiplayer}/MultiplayerRoomSettings.cs (96%) rename osu.Game/Online/{RealtimeMultiplayer => Multiplayer}/MultiplayerRoomState.cs (86%) rename osu.Game/Online/{RealtimeMultiplayer => Multiplayer}/MultiplayerRoomUser.cs (96%) rename osu.Game/Online/{RealtimeMultiplayer => Multiplayer}/MultiplayerUserState.cs (98%) rename osu.Game/Online/{RealtimeMultiplayer => Multiplayer}/NotHostException.cs (92%) rename osu.Game/Online/{RealtimeMultiplayer => Multiplayer}/NotJoinedRoomException.cs (92%) rename osu.Game/Online/{RealtimeMultiplayer => Multiplayer}/StatefulMultiplayerClient.cs (99%) rename osu.Game/Online/{Multiplayer => Rooms}/APICreatedRoom.cs (88%) rename osu.Game/Online/{Multiplayer => Rooms}/APILeaderboard.cs (92%) rename osu.Game/Online/{Multiplayer => Rooms}/APIPlaylistBeatmap.cs (94%) rename osu.Game/Online/{Multiplayer => Rooms}/APIScoreToken.cs (88%) rename osu.Game/Online/{Multiplayer => Rooms}/CreateRoomRequest.cs (95%) rename osu.Game/Online/{Multiplayer => Rooms}/CreateRoomScoreRequest.cs (96%) rename osu.Game/Online/{Multiplayer => Rooms}/GameType.cs (93%) rename osu.Game/Online/{Multiplayer => Rooms}/GameTypes/GameTypeTag.cs (94%) rename osu.Game/Online/{Multiplayer => Rooms}/GameTypes/GameTypeTagTeam.cs (96%) rename osu.Game/Online/{Multiplayer => Rooms}/GameTypes/GameTypeTeamVersus.cs (95%) rename osu.Game/Online/{Multiplayer => Rooms}/GameTypes/GameTypeTimeshift.cs (93%) rename osu.Game/Online/{Multiplayer => Rooms}/GameTypes/GameTypeVersus.cs (92%) rename osu.Game/Online/{Multiplayer => Rooms}/GameTypes/VersusRow.cs (97%) rename osu.Game/Online/{Multiplayer => Rooms}/GetRoomLeaderboardRequest.cs (92%) rename osu.Game/Online/{Multiplayer => Rooms}/GetRoomRequest.cs (92%) rename osu.Game/Online/{Multiplayer => Rooms}/GetRoomsRequest.cs (96%) rename osu.Game/Online/{Multiplayer => Rooms}/IndexPlaylistScoresRequest.cs (97%) rename osu.Game/Online/{Multiplayer => Rooms}/IndexScoresParams.cs (94%) rename osu.Game/Online/{Multiplayer => Rooms}/IndexedMultiplayerScores.cs (95%) rename osu.Game/Online/{Multiplayer => Rooms}/JoinRoomRequest.cs (94%) rename osu.Game/Online/{Multiplayer => Rooms}/MultiplayerScore.cs (98%) rename osu.Game/Online/{Multiplayer => Rooms}/MultiplayerScores.cs (95%) rename osu.Game/Online/{Multiplayer => Rooms}/MultiplayerScoresAround.cs (95%) rename osu.Game/Online/{Multiplayer => Rooms}/PartRoomRequest.cs (94%) rename osu.Game/Online/{Multiplayer => Rooms}/PlaylistExtensions.cs (93%) rename osu.Game/Online/{Multiplayer => Rooms}/PlaylistItem.cs (98%) rename osu.Game/Online/{Multiplayer => Rooms}/Room.cs (97%) rename osu.Game/Online/{Multiplayer => Rooms}/RoomAvailability.cs (90%) rename osu.Game/Online/{Multiplayer => Rooms}/RoomCategory.cs (80%) rename osu.Game/Online/{Multiplayer => Rooms}/RoomStatus.cs (93%) rename osu.Game/Online/{Multiplayer => Rooms}/RoomStatuses/RoomStatusEnded.cs (88%) rename osu.Game/Online/{Multiplayer => Rooms}/RoomStatuses/RoomStatusOpen.cs (89%) rename osu.Game/Online/{Multiplayer => Rooms}/RoomStatuses/RoomStatusPlaying.cs (88%) rename osu.Game/Online/{Multiplayer => Rooms}/ShowPlaylistUserScoreRequest.cs (95%) rename osu.Game/Online/{Multiplayer => Rooms}/SubmitRoomScoreRequest.cs (97%) rename osu.Game/Screens/Multi/{RealtimeMultiplayer/CreateRealtimeMatchButton.cs => Multiplayer/CreateMultiplayerMatchButton.cs} (77%) rename osu.Game/Screens/Multi/{RealtimeMultiplayer => Multiplayer}/Match/BeatmapSelectionControl.cs (91%) rename osu.Game/Screens/Multi/{RealtimeMultiplayer/Match/RealtimeMatchFooter.cs => Multiplayer/Match/MultiplayerMatchFooter.cs} (84%) rename osu.Game/Screens/Multi/{RealtimeMultiplayer/Match/RealtimeMatchHeader.cs => Multiplayer/Match/MultiplayerMatchHeader.cs} (95%) rename osu.Game/Screens/Multi/{RealtimeMultiplayer/Match/RealtimeMatchSettingsOverlay.cs => Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs} (99%) rename osu.Game/Screens/Multi/{RealtimeMultiplayer/Match/RealtimeReadyButton.cs => Multiplayer/Match/MultiplayerReadyButton.cs} (95%) rename osu.Game/Screens/Multi/{RealtimeMultiplayer/RealtimeMultiplayer.cs => Multiplayer/Multiplayer.cs} (86%) rename osu.Game/Screens/Multi/{RealtimeMultiplayer/RealtimeFilterControl.cs => Multiplayer/MultiplayerFilterControl.cs} (79%) rename osu.Game/Screens/Multi/{RealtimeMultiplayer/RealtimeLoungeSubScreen.cs => Multiplayer/MultiplayerLoungeSubScreen.cs} (79%) rename osu.Game/Screens/Multi/{RealtimeMultiplayer/RealtimeMatchSongSelect.cs => Multiplayer/MultiplayerMatchSongSelect.cs} (92%) rename osu.Game/Screens/Multi/{RealtimeMultiplayer/RealtimeMatchSubScreen.cs => Multiplayer/MultiplayerMatchSubScreen.cs} (92%) rename osu.Game/Screens/Multi/{RealtimeMultiplayer/RealtimePlayer.cs => Multiplayer/MultiplayerPlayer.cs} (93%) rename osu.Game/Screens/Multi/{RealtimeMultiplayer/RealtimeResultsScreen.cs => Multiplayer/MultiplayerResultsScreen.cs} (55%) rename osu.Game/Screens/Multi/{RealtimeMultiplayer/RealtimeRoomComposite.cs => Multiplayer/MultiplayerRoomComposite.cs} (83%) rename osu.Game/Screens/Multi/{RealtimeMultiplayer/RealtimeRoomManager.cs => Multiplayer/MultiplayerRoomManager.cs} (92%) rename osu.Game/Screens/Multi/{RealtimeMultiplayer => Multiplayer}/Participants/ParticipantPanel.cs (97%) rename osu.Game/Screens/Multi/{RealtimeMultiplayer => Multiplayer}/Participants/ParticipantsList.cs (93%) rename osu.Game/Screens/Multi/{RealtimeMultiplayer => Multiplayer}/Participants/ParticipantsListHeader.cs (86%) rename osu.Game/Screens/Multi/{RealtimeMultiplayer => Multiplayer}/Participants/StateDisplay.cs (97%) rename osu.Game/Screens/Multi/{Multiplayer.cs => MultiplayerScreen.cs} (99%) rename osu.Game/Tests/Visual/{RealtimeMultiplayer/RealtimeMultiplayerTestScene.cs => Multiplayer/MultiplayerTestScene.cs} (67%) rename osu.Game/Tests/Visual/{RealtimeMultiplayer/TestRealtimeMultiplayerClient.cs => Multiplayer/TestMultiplayerClient.cs} (95%) rename osu.Game/Tests/Visual/{RealtimeMultiplayer/TestRealtimeRoomContainer.cs => Multiplayer/TestMultiplayerRoomContainer.cs} (67%) rename osu.Game/Tests/Visual/{RealtimeMultiplayer/TestRealtimeRoomManager.cs => Multiplayer/TestMultiplayerRoomManager.cs} (95%) rename osu.Game/Tests/Visual/{MultiplayerTestScene.cs => RoomTestScene.cs} (90%) diff --git a/osu.Game.Tests/Visual/Multiplayer/RoomManagerTestScene.cs b/osu.Game.Tests/Visual/Multiplayer/RoomManagerTestScene.cs index 8b7e0fd9da..a2c496a504 100644 --- a/osu.Game.Tests/Visual/Multiplayer/RoomManagerTestScene.cs +++ b/osu.Game.Tests/Visual/Multiplayer/RoomManagerTestScene.cs @@ -4,14 +4,14 @@ using System; using osu.Framework.Allocation; using osu.Game.Beatmaps; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; using osu.Game.Rulesets; using osu.Game.Screens.Multi; using osu.Game.Users; namespace osu.Game.Tests.Visual.Multiplayer { - public abstract class RoomManagerTestScene : MultiplayerTestScene + public abstract class RoomManagerTestScene : RoomTestScene { [Cached(Type = typeof(IRoomManager))] protected TestRoomManager RoomManager { get; } = new TestRoomManager(); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestRoomManager.cs b/osu.Game.Tests/Visual/Multiplayer/TestRoomManager.cs index 9dd4aea4bd..7d9d4a6542 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestRoomManager.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestRoomManager.cs @@ -3,7 +3,7 @@ using System; using osu.Framework.Bindables; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; using osu.Game.Screens.Multi; namespace osu.Game.Tests.Visual.Multiplayer diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs index 55b026eff6..722e1a50ef 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs @@ -13,7 +13,7 @@ using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; using osu.Game.Overlays; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomInfo.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomInfo.cs index 9baaa42c83..1359274512 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomInfo.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomInfo.cs @@ -4,7 +4,7 @@ using System; using NUnit.Framework; using osu.Framework.Graphics; -using osu.Game.Online.Multiplayer.RoomStatuses; +using osu.Game.Online.Rooms.RoomStatuses; using osu.Game.Screens.Multi.Lounge.Components; using osu.Game.Users; diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs index e33d15cfff..9b6a6de7c2 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs @@ -6,7 +6,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Graphics; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; using osu.Game.Rulesets.Catch; using osu.Game.Rulesets.Osu; using osu.Game.Screens.Multi.Lounge.Components; diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.cs index 6b1d90e06e..f4e0cc415c 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.cs @@ -5,7 +5,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Beatmaps; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchHeader.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchHeader.cs index ec5292e51e..e162009771 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchHeader.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchHeader.cs @@ -3,7 +3,7 @@ using NUnit.Framework; using osu.Game.Beatmaps; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Screens.Multi.Match.Components; diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneRealtimeMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs similarity index 77% rename from osu.Game.Tests/Visual/Multiplayer/TestSceneRealtimeMultiplayer.cs rename to osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index cc07bb8e79..5c07b9d0ea 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneRealtimeMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -3,16 +3,15 @@ using NUnit.Framework; using osu.Game.Screens.Multi.Components; -using osu.Game.Tests.Visual.RealtimeMultiplayer; using osu.Game.Users; namespace osu.Game.Tests.Visual.Multiplayer { - public class TestSceneRealtimeMultiplayer : RealtimeMultiplayerTestScene + public class TestSceneMultiplayer : MultiplayerTestScene { - public TestSceneRealtimeMultiplayer() + public TestSceneMultiplayer() { - var multi = new TestRealtimeMultiplayer(); + var multi = new TestMultiplayer(); AddStep("show", () => LoadScreen(multi)); AddUntilStep("wait for loaded", () => multi.IsLoaded); @@ -40,9 +39,9 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("room has 1 user", () => Client.Room?.Users.Count == 1); } - private class TestRealtimeMultiplayer : Screens.Multi.RealtimeMultiplayer.RealtimeMultiplayer + private class TestMultiplayer : Screens.Multi.Multiplayer.Multiplayer { - protected override RoomManager CreateRoomManager() => new TestRealtimeRoomManager(); + protected override RoomManager CreateRoomManager() => new TestMultiplayerRoomManager(); } } } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneRealtimeMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs similarity index 73% rename from osu.Game.Tests/Visual/Multiplayer/TestSceneRealtimeMatchSubScreen.cs rename to osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs index dff375d1bb..6dc26dae57 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneRealtimeMatchSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs @@ -5,21 +5,20 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Screens; using osu.Framework.Testing; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; using osu.Game.Rulesets.Osu; -using osu.Game.Screens.Multi.RealtimeMultiplayer; -using osu.Game.Screens.Multi.RealtimeMultiplayer.Match; +using osu.Game.Screens.Multi.Multiplayer; +using osu.Game.Screens.Multi.Multiplayer.Match; using osu.Game.Tests.Beatmaps; -using osu.Game.Tests.Visual.RealtimeMultiplayer; using osuTK.Input; namespace osu.Game.Tests.Visual.Multiplayer { - public class TestSceneRealtimeMatchSubScreen : RealtimeMultiplayerTestScene + public class TestSceneMultiplayerMatchSubScreen : MultiplayerTestScene { - private RealtimeMatchSubScreen screen; + private MultiplayerMatchSubScreen screen; - public TestSceneRealtimeMatchSubScreen() + public TestSceneMultiplayerMatchSubScreen() : base(false) { } @@ -33,14 +32,14 @@ namespace osu.Game.Tests.Visual.Multiplayer [SetUpSteps] public void SetupSteps() { - AddStep("load match", () => LoadScreen(screen = new RealtimeMatchSubScreen(Room))); + AddStep("load match", () => LoadScreen(screen = new MultiplayerMatchSubScreen(Room))); AddUntilStep("wait for load", () => screen.IsCurrentScreen()); } [Test] public void TestSettingValidity() { - AddAssert("create button not enabled", () => !this.ChildrenOfType().Single().Enabled.Value); + AddAssert("create button not enabled", () => !this.ChildrenOfType().Single().Enabled.Value); AddStep("set playlist", () => { @@ -51,7 +50,7 @@ namespace osu.Game.Tests.Visual.Multiplayer }); }); - AddAssert("create button enabled", () => this.ChildrenOfType().Single().Enabled.Value); + AddAssert("create button enabled", () => this.ChildrenOfType().Single().Enabled.Value); } [Test] @@ -68,7 +67,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("click create button", () => { - InputManager.MoveMouseTo(this.ChildrenOfType().Single()); + InputManager.MoveMouseTo(this.ChildrenOfType().Single()); InputManager.Click(MouseButton.Left); }); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneRealtimeMultiplayerParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs similarity index 94% rename from osu.Game.Tests/Visual/Multiplayer/TestSceneRealtimeMultiplayerParticipantsList.cs rename to osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs index 7c26918927..ee2fa4ef5a 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneRealtimeMultiplayerParticipantsList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs @@ -7,15 +7,14 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Testing; using osu.Framework.Utils; -using osu.Game.Online.RealtimeMultiplayer; -using osu.Game.Screens.Multi.RealtimeMultiplayer.Participants; -using osu.Game.Tests.Visual.RealtimeMultiplayer; +using osu.Game.Online.Multiplayer; +using osu.Game.Screens.Multi.Multiplayer.Participants; using osu.Game.Users; using osuTK; namespace osu.Game.Tests.Visual.Multiplayer { - public class TestSceneRealtimeMultiplayerParticipantsList : RealtimeMultiplayerTestScene + public class TestSceneMultiplayerParticipantsList : MultiplayerTestScene { [SetUp] public new void Setup() => Schedule(() => diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneRealtimeReadyButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs similarity index 94% rename from osu.Game.Tests/Visual/Multiplayer/TestSceneRealtimeReadyButton.cs rename to osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs index 3c92276629..c8ebdb1b76 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneRealtimeReadyButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs @@ -10,20 +10,19 @@ using osu.Framework.Platform; using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Online.Multiplayer; -using osu.Game.Online.RealtimeMultiplayer; +using osu.Game.Online.Rooms; using osu.Game.Rulesets; -using osu.Game.Screens.Multi.RealtimeMultiplayer.Match; +using osu.Game.Screens.Multi.Multiplayer.Match; using osu.Game.Tests.Resources; -using osu.Game.Tests.Visual.RealtimeMultiplayer; using osu.Game.Users; using osuTK; using osuTK.Input; namespace osu.Game.Tests.Visual.Multiplayer { - public class TestSceneRealtimeReadyButton : RealtimeMultiplayerTestScene + public class TestSceneMultiplayerReadyButton : MultiplayerTestScene { - private RealtimeReadyButton button; + private MultiplayerReadyButton button; private BeatmapManager beatmaps; private RulesetStore rulesets; @@ -43,7 +42,7 @@ namespace osu.Game.Tests.Visual.Multiplayer Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap); - Child = button = new RealtimeReadyButton + Child = button = new MultiplayerReadyButton { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneRealtimeRoomManager.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs similarity index 92% rename from osu.Game.Tests/Visual/Multiplayer/TestSceneRealtimeRoomManager.cs rename to osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs index ba90e0840b..292c4846ab 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneRealtimeRoomManager.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs @@ -4,16 +4,15 @@ using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Testing; -using osu.Game.Online.Multiplayer; -using osu.Game.Tests.Visual.RealtimeMultiplayer; +using osu.Game.Online.Rooms; namespace osu.Game.Tests.Visual.Multiplayer { [HeadlessTest] - public class TestSceneRealtimeRoomManager : MultiplayerTestScene + public class TestSceneMultiplayerRoomManager : MultiplayerTestScene { - private TestRealtimeRoomContainer roomContainer; - private TestRealtimeRoomManager roomManager => roomContainer.RoomManager; + private TestMultiplayerRoomContainer roomContainer; + private TestMultiplayerRoomManager roomManager => roomContainer.RoomManager; [Test] public void TestPollsInitially() @@ -137,9 +136,9 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("multiplayer room joined", () => roomContainer.Client.Room != null); } - private TestRealtimeRoomManager createRoomManager() + private TestMultiplayerRoomManager createRoomManager() { - Child = roomContainer = new TestRealtimeRoomContainer + Child = roomContainer = new TestMultiplayerRoomContainer { RoomManager = { diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs index a6dd1437f7..788255501b 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs @@ -4,8 +4,8 @@ using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Online.Multiplayer; -using osu.Game.Online.Multiplayer.RoomStatuses; +using osu.Game.Online.Rooms; +using osu.Game.Online.Rooms.RoomStatuses; using osu.Game.Screens.Multi.Lounge.Components; namespace osu.Game.Tests.Visual.Multiplayer @@ -40,9 +40,9 @@ namespace osu.Game.Tests.Visual.Multiplayer }) { MatchingFilter = true }, new DrawableRoom(new Room { - Name = { Value = "Open (realtime)" }, + Name = { Value = "Open" }, Status = { Value = new RoomStatusOpen() }, - Category = { Value = RoomCategory.Realtime } + Category = { Value = RoomCategory.Multiplayer } }) { MatchingFilter = true }, } }; diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs index 2a110c0386..4e75619f13 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs @@ -9,13 +9,13 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; using osu.Game.Screens.Multi; using osu.Game.Screens.Multi.Playlists; namespace osu.Game.Tests.Visual.Playlists { - public class TestScenePlaylistsMatchSettingsOverlay : MultiplayerTestScene + public class TestScenePlaylistsMatchSettingsOverlay : RoomTestScene { [Cached(Type = typeof(IRoomManager))] private TestRoomManager roomManager = new TestRoomManager(); diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsParticipantsList.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsParticipantsList.cs index d5553e4527..5a0f196df9 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsParticipantsList.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsParticipantsList.cs @@ -8,7 +8,7 @@ using osu.Game.Users; namespace osu.Game.Tests.Visual.Playlists { - public class TestScenePlaylistsParticipantsList : MultiplayerTestScene + public class TestScenePlaylistsParticipantsList : RoomTestScene { [SetUp] public new void Setup() => Schedule(() => diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs index 10ae44351b..b42f2e1d2d 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs @@ -15,7 +15,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Online.API.Requests; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs index aebff14c80..96ff93a145 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs @@ -12,7 +12,7 @@ using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; using osu.Game.Screens.Multi; @@ -24,7 +24,7 @@ using osuTK.Input; namespace osu.Game.Tests.Visual.Playlists { - public class TestScenePlaylistsRoomSubScreen : MultiplayerTestScene + public class TestScenePlaylistsRoomSubScreen : RoomTestScene { protected override bool UseOnlineAPI => true; diff --git a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerClient.cs b/osu.Game/Online/Multiplayer/IMultiplayerClient.cs similarity index 98% rename from osu.Game/Online/RealtimeMultiplayer/IMultiplayerClient.cs rename to osu.Game/Online/Multiplayer/IMultiplayerClient.cs index 9af0047137..b97fcc9ae7 100644 --- a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerClient.cs @@ -3,7 +3,7 @@ using System.Threading.Tasks; -namespace osu.Game.Online.RealtimeMultiplayer +namespace osu.Game.Online.Multiplayer { /// /// An interface defining a multiplayer client instance. diff --git a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerLoungeServer.cs b/osu.Game/Online/Multiplayer/IMultiplayerLoungeServer.cs similarity index 93% rename from osu.Game/Online/RealtimeMultiplayer/IMultiplayerLoungeServer.cs rename to osu.Game/Online/Multiplayer/IMultiplayerLoungeServer.cs index eecb61bcb0..4640640c5f 100644 --- a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerLoungeServer.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerLoungeServer.cs @@ -3,7 +3,7 @@ using System.Threading.Tasks; -namespace osu.Game.Online.RealtimeMultiplayer +namespace osu.Game.Online.Multiplayer { /// /// Interface for an out-of-room multiplayer server. diff --git a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerRoomServer.cs b/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs similarity index 98% rename from osu.Game/Online/RealtimeMultiplayer/IMultiplayerRoomServer.cs rename to osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs index 12dfe481c4..481e3fb1de 100644 --- a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerRoomServer.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs @@ -3,7 +3,7 @@ using System.Threading.Tasks; -namespace osu.Game.Online.RealtimeMultiplayer +namespace osu.Game.Online.Multiplayer { /// /// Interface for an in-room multiplayer server. diff --git a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs b/osu.Game/Online/Multiplayer/IMultiplayerServer.cs similarity index 88% rename from osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs rename to osu.Game/Online/Multiplayer/IMultiplayerServer.cs index 1d093af743..d3a070af6d 100644 --- a/osu.Game/Online/RealtimeMultiplayer/IMultiplayerServer.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerServer.cs @@ -1,7 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -namespace osu.Game.Online.RealtimeMultiplayer +namespace osu.Game.Online.Multiplayer { /// /// An interface defining the multiplayer server instance. diff --git a/osu.Game/Online/RealtimeMultiplayer/InvalidStateChangeException.cs b/osu.Game/Online/Multiplayer/InvalidStateChangeException.cs similarity index 93% rename from osu.Game/Online/RealtimeMultiplayer/InvalidStateChangeException.cs rename to osu.Game/Online/Multiplayer/InvalidStateChangeException.cs index d9a276fc19..69b6d4bc13 100644 --- a/osu.Game/Online/RealtimeMultiplayer/InvalidStateChangeException.cs +++ b/osu.Game/Online/Multiplayer/InvalidStateChangeException.cs @@ -5,7 +5,7 @@ using System; using System.Runtime.Serialization; using Microsoft.AspNetCore.SignalR; -namespace osu.Game.Online.RealtimeMultiplayer +namespace osu.Game.Online.Multiplayer { [Serializable] public class InvalidStateChangeException : HubException diff --git a/osu.Game/Online/RealtimeMultiplayer/InvalidStateException.cs b/osu.Game/Online/Multiplayer/InvalidStateException.cs similarity index 92% rename from osu.Game/Online/RealtimeMultiplayer/InvalidStateException.cs rename to osu.Game/Online/Multiplayer/InvalidStateException.cs index 7791bfc69f..77a3533dd3 100644 --- a/osu.Game/Online/RealtimeMultiplayer/InvalidStateException.cs +++ b/osu.Game/Online/Multiplayer/InvalidStateException.cs @@ -5,7 +5,7 @@ using System; using System.Runtime.Serialization; using Microsoft.AspNetCore.SignalR; -namespace osu.Game.Online.RealtimeMultiplayer +namespace osu.Game.Online.Multiplayer { [Serializable] public class InvalidStateException : HubException diff --git a/osu.Game/Online/RealtimeMultiplayer/RealtimeMultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs similarity index 97% rename from osu.Game/Online/RealtimeMultiplayer/RealtimeMultiplayerClient.cs rename to osu.Game/Online/Multiplayer/MultiplayerClient.cs index cb5c21a8c9..24ea6abc4a 100644 --- a/osu.Game/Online/RealtimeMultiplayer/RealtimeMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -15,9 +15,9 @@ using osu.Framework.Bindables; using osu.Framework.Logging; using osu.Game.Online.API; -namespace osu.Game.Online.RealtimeMultiplayer +namespace osu.Game.Online.Multiplayer { - public class RealtimeMultiplayerClient : StatefulMultiplayerClient + public class MultiplayerClient : StatefulMultiplayerClient { public override IBindable IsConnected => isConnected; @@ -31,7 +31,7 @@ namespace osu.Game.Online.RealtimeMultiplayer private readonly string endpoint; - public RealtimeMultiplayerClient(EndpointConfiguration endpoints) + public MultiplayerClient(EndpointConfiguration endpoints) { endpoint = endpoints.MultiplayerEndpointUrl; } diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs b/osu.Game/Online/Multiplayer/MultiplayerRoom.cs similarity index 98% rename from osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs rename to osu.Game/Online/Multiplayer/MultiplayerRoom.cs index e009a34707..2134e50d72 100644 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoom.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerRoom.cs @@ -9,7 +9,7 @@ using System.Threading; using Newtonsoft.Json; using osu.Framework.Allocation; -namespace osu.Game.Online.RealtimeMultiplayer +namespace osu.Game.Online.Multiplayer { /// /// A multiplayer room. diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomSettings.cs b/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs similarity index 96% rename from osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomSettings.cs rename to osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs index 60e0d1292e..857b38ea60 100644 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomSettings.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs @@ -9,7 +9,7 @@ using System.Linq; using JetBrains.Annotations; using osu.Game.Online.API; -namespace osu.Game.Online.RealtimeMultiplayer +namespace osu.Game.Online.Multiplayer { [Serializable] public class MultiplayerRoomSettings : IEquatable diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomState.cs b/osu.Game/Online/Multiplayer/MultiplayerRoomState.cs similarity index 86% rename from osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomState.cs rename to osu.Game/Online/Multiplayer/MultiplayerRoomState.cs index 69c04b09a8..48f25d7ca2 100644 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomState.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerRoomState.cs @@ -3,10 +3,10 @@ #nullable enable -namespace osu.Game.Online.RealtimeMultiplayer +namespace osu.Game.Online.Multiplayer { /// - /// The current overall state of a realtime multiplayer room. + /// The current overall state of a multiplayer room. /// public enum MultiplayerRoomState { diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomUser.cs b/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs similarity index 96% rename from osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomUser.cs rename to osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs index caf1a70197..99624dc3e7 100644 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerRoomUser.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs @@ -7,7 +7,7 @@ using System; using Newtonsoft.Json; using osu.Game.Users; -namespace osu.Game.Online.RealtimeMultiplayer +namespace osu.Game.Online.Multiplayer { [Serializable] public class MultiplayerRoomUser : IEquatable diff --git a/osu.Game/Online/RealtimeMultiplayer/MultiplayerUserState.cs b/osu.Game/Online/Multiplayer/MultiplayerUserState.cs similarity index 98% rename from osu.Game/Online/RealtimeMultiplayer/MultiplayerUserState.cs rename to osu.Game/Online/Multiplayer/MultiplayerUserState.cs index ed9acd146e..e54c71cd85 100644 --- a/osu.Game/Online/RealtimeMultiplayer/MultiplayerUserState.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerUserState.cs @@ -1,7 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -namespace osu.Game.Online.RealtimeMultiplayer +namespace osu.Game.Online.Multiplayer { public enum MultiplayerUserState { diff --git a/osu.Game/Online/RealtimeMultiplayer/NotHostException.cs b/osu.Game/Online/Multiplayer/NotHostException.cs similarity index 92% rename from osu.Game/Online/RealtimeMultiplayer/NotHostException.cs rename to osu.Game/Online/Multiplayer/NotHostException.cs index 56095043f0..051cde45a0 100644 --- a/osu.Game/Online/RealtimeMultiplayer/NotHostException.cs +++ b/osu.Game/Online/Multiplayer/NotHostException.cs @@ -5,7 +5,7 @@ using System; using System.Runtime.Serialization; using Microsoft.AspNetCore.SignalR; -namespace osu.Game.Online.RealtimeMultiplayer +namespace osu.Game.Online.Multiplayer { [Serializable] public class NotHostException : HubException diff --git a/osu.Game/Online/RealtimeMultiplayer/NotJoinedRoomException.cs b/osu.Game/Online/Multiplayer/NotJoinedRoomException.cs similarity index 92% rename from osu.Game/Online/RealtimeMultiplayer/NotJoinedRoomException.cs rename to osu.Game/Online/Multiplayer/NotJoinedRoomException.cs index 7a6e089d0b..0e9902f002 100644 --- a/osu.Game/Online/RealtimeMultiplayer/NotJoinedRoomException.cs +++ b/osu.Game/Online/Multiplayer/NotJoinedRoomException.cs @@ -5,7 +5,7 @@ using System; using System.Runtime.Serialization; using Microsoft.AspNetCore.SignalR; -namespace osu.Game.Online.RealtimeMultiplayer +namespace osu.Game.Online.Multiplayer { [Serializable] public class NotJoinedRoomException : HubException diff --git a/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs similarity index 99% rename from osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs rename to osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs index 9149bdcba3..e422e982ae 100644 --- a/osu.Game/Online/RealtimeMultiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs @@ -18,13 +18,13 @@ using osu.Game.Extensions; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; -using osu.Game.Online.Multiplayer; -using osu.Game.Online.Multiplayer.RoomStatuses; +using osu.Game.Online.Rooms; +using osu.Game.Online.Rooms.RoomStatuses; using osu.Game.Rulesets; using osu.Game.Users; using osu.Game.Utils; -namespace osu.Game.Online.RealtimeMultiplayer +namespace osu.Game.Online.Multiplayer { public abstract class StatefulMultiplayerClient : Component, IMultiplayerClient, IMultiplayerRoomServer { diff --git a/osu.Game/Online/Multiplayer/APICreatedRoom.cs b/osu.Game/Online/Rooms/APICreatedRoom.cs similarity index 88% rename from osu.Game/Online/Multiplayer/APICreatedRoom.cs rename to osu.Game/Online/Rooms/APICreatedRoom.cs index 2a3bb39647..d1062b2306 100644 --- a/osu.Game/Online/Multiplayer/APICreatedRoom.cs +++ b/osu.Game/Online/Rooms/APICreatedRoom.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; -namespace osu.Game.Online.Multiplayer +namespace osu.Game.Online.Rooms { public class APICreatedRoom : Room { diff --git a/osu.Game/Online/Multiplayer/APILeaderboard.cs b/osu.Game/Online/Rooms/APILeaderboard.cs similarity index 92% rename from osu.Game/Online/Multiplayer/APILeaderboard.cs rename to osu.Game/Online/Rooms/APILeaderboard.cs index 65863d6e0e..c487123906 100644 --- a/osu.Game/Online/Multiplayer/APILeaderboard.cs +++ b/osu.Game/Online/Rooms/APILeaderboard.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Newtonsoft.Json; using osu.Game.Online.API.Requests.Responses; -namespace osu.Game.Online.Multiplayer +namespace osu.Game.Online.Rooms { public class APILeaderboard { diff --git a/osu.Game/Online/Multiplayer/APIPlaylistBeatmap.cs b/osu.Game/Online/Rooms/APIPlaylistBeatmap.cs similarity index 94% rename from osu.Game/Online/Multiplayer/APIPlaylistBeatmap.cs rename to osu.Game/Online/Rooms/APIPlaylistBeatmap.cs index 98972ef36d..973dccd528 100644 --- a/osu.Game/Online/Multiplayer/APIPlaylistBeatmap.cs +++ b/osu.Game/Online/Rooms/APIPlaylistBeatmap.cs @@ -6,7 +6,7 @@ using osu.Game.Beatmaps; using osu.Game.Online.API.Requests.Responses; using osu.Game.Rulesets; -namespace osu.Game.Online.Multiplayer +namespace osu.Game.Online.Rooms { public class APIPlaylistBeatmap : APIBeatmap { diff --git a/osu.Game/Online/Multiplayer/APIScoreToken.cs b/osu.Game/Online/Rooms/APIScoreToken.cs similarity index 88% rename from osu.Game/Online/Multiplayer/APIScoreToken.cs rename to osu.Game/Online/Rooms/APIScoreToken.cs index 1f0063d94e..f652c1720d 100644 --- a/osu.Game/Online/Multiplayer/APIScoreToken.cs +++ b/osu.Game/Online/Rooms/APIScoreToken.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; -namespace osu.Game.Online.Multiplayer +namespace osu.Game.Online.Rooms { public class APIScoreToken { diff --git a/osu.Game/Online/Multiplayer/CreateRoomRequest.cs b/osu.Game/Online/Rooms/CreateRoomRequest.cs similarity index 95% rename from osu.Game/Online/Multiplayer/CreateRoomRequest.cs rename to osu.Game/Online/Rooms/CreateRoomRequest.cs index 5be99e9442..f058eb9ba8 100644 --- a/osu.Game/Online/Multiplayer/CreateRoomRequest.cs +++ b/osu.Game/Online/Rooms/CreateRoomRequest.cs @@ -6,7 +6,7 @@ using Newtonsoft.Json; using osu.Framework.IO.Network; using osu.Game.Online.API; -namespace osu.Game.Online.Multiplayer +namespace osu.Game.Online.Rooms { public class CreateRoomRequest : APIRequest { diff --git a/osu.Game/Online/Multiplayer/CreateRoomScoreRequest.cs b/osu.Game/Online/Rooms/CreateRoomScoreRequest.cs similarity index 96% rename from osu.Game/Online/Multiplayer/CreateRoomScoreRequest.cs rename to osu.Game/Online/Rooms/CreateRoomScoreRequest.cs index 2d99b12519..afd0dadc7e 100644 --- a/osu.Game/Online/Multiplayer/CreateRoomScoreRequest.cs +++ b/osu.Game/Online/Rooms/CreateRoomScoreRequest.cs @@ -5,7 +5,7 @@ using System.Net.Http; using osu.Framework.IO.Network; using osu.Game.Online.API; -namespace osu.Game.Online.Multiplayer +namespace osu.Game.Online.Rooms { public class CreateRoomScoreRequest : APIRequest { diff --git a/osu.Game/Online/Multiplayer/GameType.cs b/osu.Game/Online/Rooms/GameType.cs similarity index 93% rename from osu.Game/Online/Multiplayer/GameType.cs rename to osu.Game/Online/Rooms/GameType.cs index 10381d93bb..caa352d812 100644 --- a/osu.Game/Online/Multiplayer/GameType.cs +++ b/osu.Game/Online/Rooms/GameType.cs @@ -4,7 +4,7 @@ using osu.Framework.Graphics; using osu.Game.Graphics; -namespace osu.Game.Online.Multiplayer +namespace osu.Game.Online.Rooms { public abstract class GameType { diff --git a/osu.Game/Online/Multiplayer/GameTypes/GameTypeTag.cs b/osu.Game/Online/Rooms/GameTypes/GameTypeTag.cs similarity index 94% rename from osu.Game/Online/Multiplayer/GameTypes/GameTypeTag.cs rename to osu.Game/Online/Rooms/GameTypes/GameTypeTag.cs index 5ba5f1a415..e468612738 100644 --- a/osu.Game/Online/Multiplayer/GameTypes/GameTypeTag.cs +++ b/osu.Game/Online/Rooms/GameTypes/GameTypeTag.cs @@ -6,7 +6,7 @@ using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osuTK; -namespace osu.Game.Online.Multiplayer.GameTypes +namespace osu.Game.Online.Rooms.GameTypes { public class GameTypeTag : GameType { diff --git a/osu.Game/Online/Multiplayer/GameTypes/GameTypeTagTeam.cs b/osu.Game/Online/Rooms/GameTypes/GameTypeTagTeam.cs similarity index 96% rename from osu.Game/Online/Multiplayer/GameTypes/GameTypeTagTeam.cs rename to osu.Game/Online/Rooms/GameTypes/GameTypeTagTeam.cs index ef0a00a9f0..b82f203fac 100644 --- a/osu.Game/Online/Multiplayer/GameTypes/GameTypeTagTeam.cs +++ b/osu.Game/Online/Rooms/GameTypes/GameTypeTagTeam.cs @@ -7,7 +7,7 @@ using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osuTK; -namespace osu.Game.Online.Multiplayer.GameTypes +namespace osu.Game.Online.Rooms.GameTypes { public class GameTypeTagTeam : GameType { diff --git a/osu.Game/Online/Multiplayer/GameTypes/GameTypeTeamVersus.cs b/osu.Game/Online/Rooms/GameTypes/GameTypeTeamVersus.cs similarity index 95% rename from osu.Game/Online/Multiplayer/GameTypes/GameTypeTeamVersus.cs rename to osu.Game/Online/Rooms/GameTypes/GameTypeTeamVersus.cs index c25bce1c71..5ad4033dc9 100644 --- a/osu.Game/Online/Multiplayer/GameTypes/GameTypeTeamVersus.cs +++ b/osu.Game/Online/Rooms/GameTypes/GameTypeTeamVersus.cs @@ -6,7 +6,7 @@ using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osuTK; -namespace osu.Game.Online.Multiplayer.GameTypes +namespace osu.Game.Online.Rooms.GameTypes { public class GameTypeTeamVersus : GameType { diff --git a/osu.Game/Online/Multiplayer/GameTypes/GameTypeTimeshift.cs b/osu.Game/Online/Rooms/GameTypes/GameTypeTimeshift.cs similarity index 93% rename from osu.Game/Online/Multiplayer/GameTypes/GameTypeTimeshift.cs rename to osu.Game/Online/Rooms/GameTypes/GameTypeTimeshift.cs index 5840ccb803..3425c6c5cd 100644 --- a/osu.Game/Online/Multiplayer/GameTypes/GameTypeTimeshift.cs +++ b/osu.Game/Online/Rooms/GameTypes/GameTypeTimeshift.cs @@ -6,7 +6,7 @@ using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osuTK; -namespace osu.Game.Online.Multiplayer.GameTypes +namespace osu.Game.Online.Rooms.GameTypes { public class GameTypePlaylists : GameType { diff --git a/osu.Game/Online/Multiplayer/GameTypes/GameTypeVersus.cs b/osu.Game/Online/Rooms/GameTypes/GameTypeVersus.cs similarity index 92% rename from osu.Game/Online/Multiplayer/GameTypes/GameTypeVersus.cs rename to osu.Game/Online/Rooms/GameTypes/GameTypeVersus.cs index 4640c7b361..3783cc67b0 100644 --- a/osu.Game/Online/Multiplayer/GameTypes/GameTypeVersus.cs +++ b/osu.Game/Online/Rooms/GameTypes/GameTypeVersus.cs @@ -4,7 +4,7 @@ using osu.Framework.Graphics; using osu.Game.Graphics; -namespace osu.Game.Online.Multiplayer.GameTypes +namespace osu.Game.Online.Rooms.GameTypes { public class GameTypeVersus : GameType { diff --git a/osu.Game/Online/Multiplayer/GameTypes/VersusRow.cs b/osu.Game/Online/Rooms/GameTypes/VersusRow.cs similarity index 97% rename from osu.Game/Online/Multiplayer/GameTypes/VersusRow.cs rename to osu.Game/Online/Rooms/GameTypes/VersusRow.cs index b6e8e4458f..0bd09a23ac 100644 --- a/osu.Game/Online/Multiplayer/GameTypes/VersusRow.cs +++ b/osu.Game/Online/Rooms/GameTypes/VersusRow.cs @@ -7,7 +7,7 @@ using osu.Framework.Graphics.Shapes; using osuTK; using osuTK.Graphics; -namespace osu.Game.Online.Multiplayer.GameTypes +namespace osu.Game.Online.Rooms.GameTypes { public class VersusRow : FillFlowContainer { diff --git a/osu.Game/Online/Multiplayer/GetRoomLeaderboardRequest.cs b/osu.Game/Online/Rooms/GetRoomLeaderboardRequest.cs similarity index 92% rename from osu.Game/Online/Multiplayer/GetRoomLeaderboardRequest.cs rename to osu.Game/Online/Rooms/GetRoomLeaderboardRequest.cs index 37c21457bc..15f1221a00 100644 --- a/osu.Game/Online/Multiplayer/GetRoomLeaderboardRequest.cs +++ b/osu.Game/Online/Rooms/GetRoomLeaderboardRequest.cs @@ -3,7 +3,7 @@ using osu.Game.Online.API; -namespace osu.Game.Online.Multiplayer +namespace osu.Game.Online.Rooms { public class GetRoomLeaderboardRequest : APIRequest { diff --git a/osu.Game/Online/Multiplayer/GetRoomRequest.cs b/osu.Game/Online/Rooms/GetRoomRequest.cs similarity index 92% rename from osu.Game/Online/Multiplayer/GetRoomRequest.cs rename to osu.Game/Online/Rooms/GetRoomRequest.cs index 449c2c8e31..ce117075c7 100644 --- a/osu.Game/Online/Multiplayer/GetRoomRequest.cs +++ b/osu.Game/Online/Rooms/GetRoomRequest.cs @@ -3,7 +3,7 @@ using osu.Game.Online.API; -namespace osu.Game.Online.Multiplayer +namespace osu.Game.Online.Rooms { public class GetRoomRequest : APIRequest { diff --git a/osu.Game/Online/Multiplayer/GetRoomsRequest.cs b/osu.Game/Online/Rooms/GetRoomsRequest.cs similarity index 96% rename from osu.Game/Online/Multiplayer/GetRoomsRequest.cs rename to osu.Game/Online/Rooms/GetRoomsRequest.cs index a0609f77dd..5084b8627f 100644 --- a/osu.Game/Online/Multiplayer/GetRoomsRequest.cs +++ b/osu.Game/Online/Rooms/GetRoomsRequest.cs @@ -7,7 +7,7 @@ using osu.Framework.IO.Network; using osu.Game.Online.API; using osu.Game.Screens.Multi.Lounge.Components; -namespace osu.Game.Online.Multiplayer +namespace osu.Game.Online.Rooms { public class GetRoomsRequest : APIRequest> { diff --git a/osu.Game/Online/Multiplayer/IndexPlaylistScoresRequest.cs b/osu.Game/Online/Rooms/IndexPlaylistScoresRequest.cs similarity index 97% rename from osu.Game/Online/Multiplayer/IndexPlaylistScoresRequest.cs rename to osu.Game/Online/Rooms/IndexPlaylistScoresRequest.cs index 684d0aecd8..43f80a2dc4 100644 --- a/osu.Game/Online/Multiplayer/IndexPlaylistScoresRequest.cs +++ b/osu.Game/Online/Rooms/IndexPlaylistScoresRequest.cs @@ -8,7 +8,7 @@ using osu.Game.Extensions; using osu.Game.Online.API; using osu.Game.Online.API.Requests; -namespace osu.Game.Online.Multiplayer +namespace osu.Game.Online.Rooms { /// /// Returns a list of scores for the specified playlist item. diff --git a/osu.Game/Online/Multiplayer/IndexScoresParams.cs b/osu.Game/Online/Rooms/IndexScoresParams.cs similarity index 94% rename from osu.Game/Online/Multiplayer/IndexScoresParams.cs rename to osu.Game/Online/Rooms/IndexScoresParams.cs index a511e9a780..3df8c8e753 100644 --- a/osu.Game/Online/Multiplayer/IndexScoresParams.cs +++ b/osu.Game/Online/Rooms/IndexScoresParams.cs @@ -6,7 +6,7 @@ using JetBrains.Annotations; using Newtonsoft.Json; using Newtonsoft.Json.Linq; -namespace osu.Game.Online.Multiplayer +namespace osu.Game.Online.Rooms { /// /// A collection of parameters which should be passed to the index endpoint to fetch the next page. diff --git a/osu.Game/Online/Multiplayer/IndexedMultiplayerScores.cs b/osu.Game/Online/Rooms/IndexedMultiplayerScores.cs similarity index 95% rename from osu.Game/Online/Multiplayer/IndexedMultiplayerScores.cs rename to osu.Game/Online/Rooms/IndexedMultiplayerScores.cs index e237b7e3fb..2008d1aa52 100644 --- a/osu.Game/Online/Multiplayer/IndexedMultiplayerScores.cs +++ b/osu.Game/Online/Rooms/IndexedMultiplayerScores.cs @@ -4,7 +4,7 @@ using JetBrains.Annotations; using Newtonsoft.Json; -namespace osu.Game.Online.Multiplayer +namespace osu.Game.Online.Rooms { /// /// A object returned via a . diff --git a/osu.Game/Online/Multiplayer/JoinRoomRequest.cs b/osu.Game/Online/Rooms/JoinRoomRequest.cs similarity index 94% rename from osu.Game/Online/Multiplayer/JoinRoomRequest.cs rename to osu.Game/Online/Rooms/JoinRoomRequest.cs index 74375af856..faa20a3e6c 100644 --- a/osu.Game/Online/Multiplayer/JoinRoomRequest.cs +++ b/osu.Game/Online/Rooms/JoinRoomRequest.cs @@ -5,7 +5,7 @@ using System.Net.Http; using osu.Framework.IO.Network; using osu.Game.Online.API; -namespace osu.Game.Online.Multiplayer +namespace osu.Game.Online.Rooms { public class JoinRoomRequest : APIRequest { diff --git a/osu.Game/Online/Multiplayer/MultiplayerScore.cs b/osu.Game/Online/Rooms/MultiplayerScore.cs similarity index 98% rename from osu.Game/Online/Multiplayer/MultiplayerScore.cs rename to osu.Game/Online/Rooms/MultiplayerScore.cs index 8191003aad..677a3d3026 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerScore.cs +++ b/osu.Game/Online/Rooms/MultiplayerScore.cs @@ -13,7 +13,7 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Users; -namespace osu.Game.Online.Multiplayer +namespace osu.Game.Online.Rooms { public class MultiplayerScore { diff --git a/osu.Game/Online/Multiplayer/MultiplayerScores.cs b/osu.Game/Online/Rooms/MultiplayerScores.cs similarity index 95% rename from osu.Game/Online/Multiplayer/MultiplayerScores.cs rename to osu.Game/Online/Rooms/MultiplayerScores.cs index 7b9dcff828..3f970b2f8e 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerScores.cs +++ b/osu.Game/Online/Rooms/MultiplayerScores.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Newtonsoft.Json; using osu.Game.Online.API.Requests; -namespace osu.Game.Online.Multiplayer +namespace osu.Game.Online.Rooms { /// /// An object which contains scores and related data for fetching next pages. diff --git a/osu.Game/Online/Multiplayer/MultiplayerScoresAround.cs b/osu.Game/Online/Rooms/MultiplayerScoresAround.cs similarity index 95% rename from osu.Game/Online/Multiplayer/MultiplayerScoresAround.cs rename to osu.Game/Online/Rooms/MultiplayerScoresAround.cs index 2ac62d0300..a99439312a 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerScoresAround.cs +++ b/osu.Game/Online/Rooms/MultiplayerScoresAround.cs @@ -4,7 +4,7 @@ using JetBrains.Annotations; using Newtonsoft.Json; -namespace osu.Game.Online.Multiplayer +namespace osu.Game.Online.Rooms { /// /// An object which stores scores higher and lower than the user's score. diff --git a/osu.Game/Online/Multiplayer/PartRoomRequest.cs b/osu.Game/Online/Rooms/PartRoomRequest.cs similarity index 94% rename from osu.Game/Online/Multiplayer/PartRoomRequest.cs rename to osu.Game/Online/Rooms/PartRoomRequest.cs index 54bb005d96..2f036abc8c 100644 --- a/osu.Game/Online/Multiplayer/PartRoomRequest.cs +++ b/osu.Game/Online/Rooms/PartRoomRequest.cs @@ -5,7 +5,7 @@ using System.Net.Http; using osu.Framework.IO.Network; using osu.Game.Online.API; -namespace osu.Game.Online.Multiplayer +namespace osu.Game.Online.Rooms { public class PartRoomRequest : APIRequest { diff --git a/osu.Game/Online/Multiplayer/PlaylistExtensions.cs b/osu.Game/Online/Rooms/PlaylistExtensions.cs similarity index 93% rename from osu.Game/Online/Multiplayer/PlaylistExtensions.cs rename to osu.Game/Online/Rooms/PlaylistExtensions.cs index fe3d96e295..992011da3c 100644 --- a/osu.Game/Online/Multiplayer/PlaylistExtensions.cs +++ b/osu.Game/Online/Rooms/PlaylistExtensions.cs @@ -6,7 +6,7 @@ using Humanizer; using Humanizer.Localisation; using osu.Framework.Bindables; -namespace osu.Game.Online.Multiplayer +namespace osu.Game.Online.Rooms { public static class PlaylistExtensions { diff --git a/osu.Game/Online/Multiplayer/PlaylistItem.cs b/osu.Game/Online/Rooms/PlaylistItem.cs similarity index 98% rename from osu.Game/Online/Multiplayer/PlaylistItem.cs rename to osu.Game/Online/Rooms/PlaylistItem.cs index 4c4c071fc9..ada2140ca6 100644 --- a/osu.Game/Online/Multiplayer/PlaylistItem.cs +++ b/osu.Game/Online/Rooms/PlaylistItem.cs @@ -10,7 +10,7 @@ using osu.Game.Online.API; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; -namespace osu.Game.Online.Multiplayer +namespace osu.Game.Online.Rooms { public class PlaylistItem : IEquatable { diff --git a/osu.Game/Online/Multiplayer/Room.cs b/osu.Game/Online/Rooms/Room.cs similarity index 97% rename from osu.Game/Online/Multiplayer/Room.cs rename to osu.Game/Online/Rooms/Room.cs index d13d7dc774..67f874cdc4 100644 --- a/osu.Game/Online/Multiplayer/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -7,11 +7,11 @@ using Newtonsoft.Json; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.IO.Serialization.Converters; -using osu.Game.Online.Multiplayer.GameTypes; -using osu.Game.Online.Multiplayer.RoomStatuses; +using osu.Game.Online.Rooms.GameTypes; +using osu.Game.Online.Rooms.RoomStatuses; using osu.Game.Users; -namespace osu.Game.Online.Multiplayer +namespace osu.Game.Online.Rooms { public class Room { diff --git a/osu.Game/Online/Multiplayer/RoomAvailability.cs b/osu.Game/Online/Rooms/RoomAvailability.cs similarity index 90% rename from osu.Game/Online/Multiplayer/RoomAvailability.cs rename to osu.Game/Online/Rooms/RoomAvailability.cs index 08fa853562..3aea0e5948 100644 --- a/osu.Game/Online/Multiplayer/RoomAvailability.cs +++ b/osu.Game/Online/Rooms/RoomAvailability.cs @@ -3,7 +3,7 @@ using System.ComponentModel; -namespace osu.Game.Online.Multiplayer +namespace osu.Game.Online.Rooms { public enum RoomAvailability { diff --git a/osu.Game/Online/Multiplayer/RoomCategory.cs b/osu.Game/Online/Rooms/RoomCategory.cs similarity index 80% rename from osu.Game/Online/Multiplayer/RoomCategory.cs rename to osu.Game/Online/Rooms/RoomCategory.cs index d6786a72fe..f485e65ba9 100644 --- a/osu.Game/Online/Multiplayer/RoomCategory.cs +++ b/osu.Game/Online/Rooms/RoomCategory.cs @@ -1,12 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -namespace osu.Game.Online.Multiplayer +namespace osu.Game.Online.Rooms { public enum RoomCategory { Normal, Spotlight, - Realtime, + Multiplayer, } } diff --git a/osu.Game/Online/Multiplayer/RoomStatus.cs b/osu.Game/Online/Rooms/RoomStatus.cs similarity index 93% rename from osu.Game/Online/Multiplayer/RoomStatus.cs rename to osu.Game/Online/Rooms/RoomStatus.cs index 3ff2770ab4..87c5aa3fda 100644 --- a/osu.Game/Online/Multiplayer/RoomStatus.cs +++ b/osu.Game/Online/Rooms/RoomStatus.cs @@ -1,10 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osuTK.Graphics; using osu.Game.Graphics; +using osuTK.Graphics; -namespace osu.Game.Online.Multiplayer +namespace osu.Game.Online.Rooms { public abstract class RoomStatus { diff --git a/osu.Game/Online/Multiplayer/RoomStatuses/RoomStatusEnded.cs b/osu.Game/Online/Rooms/RoomStatuses/RoomStatusEnded.cs similarity index 88% rename from osu.Game/Online/Multiplayer/RoomStatuses/RoomStatusEnded.cs rename to osu.Game/Online/Rooms/RoomStatuses/RoomStatusEnded.cs index 4177d28a99..c852f86f6b 100644 --- a/osu.Game/Online/Multiplayer/RoomStatuses/RoomStatusEnded.cs +++ b/osu.Game/Online/Rooms/RoomStatuses/RoomStatusEnded.cs @@ -4,7 +4,7 @@ using osu.Game.Graphics; using osuTK.Graphics; -namespace osu.Game.Online.Multiplayer.RoomStatuses +namespace osu.Game.Online.Rooms.RoomStatuses { public class RoomStatusEnded : RoomStatus { diff --git a/osu.Game/Online/Multiplayer/RoomStatuses/RoomStatusOpen.cs b/osu.Game/Online/Rooms/RoomStatuses/RoomStatusOpen.cs similarity index 89% rename from osu.Game/Online/Multiplayer/RoomStatuses/RoomStatusOpen.cs rename to osu.Game/Online/Rooms/RoomStatuses/RoomStatusOpen.cs index 45a1cb1909..4f7f0d6f5d 100644 --- a/osu.Game/Online/Multiplayer/RoomStatuses/RoomStatusOpen.cs +++ b/osu.Game/Online/Rooms/RoomStatuses/RoomStatusOpen.cs @@ -4,7 +4,7 @@ using osu.Game.Graphics; using osuTK.Graphics; -namespace osu.Game.Online.Multiplayer.RoomStatuses +namespace osu.Game.Online.Rooms.RoomStatuses { public class RoomStatusOpen : RoomStatus { diff --git a/osu.Game/Online/Multiplayer/RoomStatuses/RoomStatusPlaying.cs b/osu.Game/Online/Rooms/RoomStatuses/RoomStatusPlaying.cs similarity index 88% rename from osu.Game/Online/Multiplayer/RoomStatuses/RoomStatusPlaying.cs rename to osu.Game/Online/Rooms/RoomStatuses/RoomStatusPlaying.cs index b2cb5c4510..f04f1b23af 100644 --- a/osu.Game/Online/Multiplayer/RoomStatuses/RoomStatusPlaying.cs +++ b/osu.Game/Online/Rooms/RoomStatuses/RoomStatusPlaying.cs @@ -4,7 +4,7 @@ using osu.Game.Graphics; using osuTK.Graphics; -namespace osu.Game.Online.Multiplayer.RoomStatuses +namespace osu.Game.Online.Rooms.RoomStatuses { public class RoomStatusPlaying : RoomStatus { diff --git a/osu.Game/Online/Multiplayer/ShowPlaylistUserScoreRequest.cs b/osu.Game/Online/Rooms/ShowPlaylistUserScoreRequest.cs similarity index 95% rename from osu.Game/Online/Multiplayer/ShowPlaylistUserScoreRequest.cs rename to osu.Game/Online/Rooms/ShowPlaylistUserScoreRequest.cs index 936b8bbe89..3f728a5417 100644 --- a/osu.Game/Online/Multiplayer/ShowPlaylistUserScoreRequest.cs +++ b/osu.Game/Online/Rooms/ShowPlaylistUserScoreRequest.cs @@ -3,7 +3,7 @@ using osu.Game.Online.API; -namespace osu.Game.Online.Multiplayer +namespace osu.Game.Online.Rooms { public class ShowPlaylistUserScoreRequest : APIRequest { diff --git a/osu.Game/Online/Multiplayer/SubmitRoomScoreRequest.cs b/osu.Game/Online/Rooms/SubmitRoomScoreRequest.cs similarity index 97% rename from osu.Game/Online/Multiplayer/SubmitRoomScoreRequest.cs rename to osu.Game/Online/Rooms/SubmitRoomScoreRequest.cs index d31aef2ea5..5a78b9fabd 100644 --- a/osu.Game/Online/Multiplayer/SubmitRoomScoreRequest.cs +++ b/osu.Game/Online/Rooms/SubmitRoomScoreRequest.cs @@ -7,7 +7,7 @@ using osu.Framework.IO.Network; using osu.Game.Online.API; using osu.Game.Scoring; -namespace osu.Game.Online.Multiplayer +namespace osu.Game.Online.Rooms { public class SubmitRoomScoreRequest : APIRequest { diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index bdc9e5eb7b..0b5abc4e31 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -31,7 +31,7 @@ using osu.Game.Input; using osu.Game.Input.Bindings; using osu.Game.IO; using osu.Game.Online; -using osu.Game.Online.RealtimeMultiplayer; +using osu.Game.Online.Multiplayer; using osu.Game.Online.Spectator; using osu.Game.Overlays; using osu.Game.Resources; @@ -219,7 +219,7 @@ namespace osu.Game dependencies.CacheAs(API ??= new APIAccess(LocalConfig, endpoints)); dependencies.CacheAs(spectatorStreaming = new SpectatorStreamingClient(endpoints)); - dependencies.CacheAs(multiplayerClient = new RealtimeMultiplayerClient(endpoints)); + dependencies.CacheAs(multiplayerClient = new MultiplayerClient(endpoints)); var defaultBeatmap = new DummyWorkingBeatmap(Audio, Textures); diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 5650b22f25..15ac2237d3 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -17,7 +17,7 @@ using osu.Game.Online.API; using osu.Game.Overlays; using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Edit; -using osu.Game.Screens.Multi.RealtimeMultiplayer; +using osu.Game.Screens.Multi.Multiplayer; using osu.Game.Screens.Multi.Playlists; using osu.Game.Screens.Select; @@ -105,7 +105,7 @@ namespace osu.Game.Screens.Menu this.Push(new Editor()); }, OnSolo = onSolo, - OnMultiplayer = () => this.Push(new RealtimeMultiplayer()), + OnMultiplayer = () => this.Push(new Multiplayer()), OnPlaylists = () => this.Push(new PlaylistsMultiplayer()), OnExit = confirmAndExit, } diff --git a/osu.Game/Screens/Multi/Components/DrawableGameType.cs b/osu.Game/Screens/Multi/Components/DrawableGameType.cs index 28240f0796..38af6d065e 100644 --- a/osu.Game/Screens/Multi/Components/DrawableGameType.cs +++ b/osu.Game/Screens/Multi/Components/DrawableGameType.cs @@ -8,7 +8,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; namespace osu.Game.Screens.Multi.Components { diff --git a/osu.Game/Screens/Multi/Components/ListingPollingComponent.cs b/osu.Game/Screens/Multi/Components/ListingPollingComponent.cs index dff6c50bf2..edce5400d1 100644 --- a/osu.Game/Screens/Multi/Components/ListingPollingComponent.cs +++ b/osu.Game/Screens/Multi/Components/ListingPollingComponent.cs @@ -4,7 +4,7 @@ using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; using osu.Game.Screens.Multi.Lounge.Components; namespace osu.Game.Screens.Multi.Components diff --git a/osu.Game/Screens/Multi/Components/MatchBeatmapDetailArea.cs b/osu.Game/Screens/Multi/Components/MatchBeatmapDetailArea.cs index 2c5fd2d397..6997840c92 100644 --- a/osu.Game/Screens/Multi/Components/MatchBeatmapDetailArea.cs +++ b/osu.Game/Screens/Multi/Components/MatchBeatmapDetailArea.cs @@ -8,7 +8,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.UserInterface; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; using osu.Game.Screens.Select; using osuTK; diff --git a/osu.Game/Screens/Multi/Components/OverlinedPlaylistHeader.cs b/osu.Game/Screens/Multi/Components/OverlinedPlaylistHeader.cs index 5552c1cb72..ebebe8b660 100644 --- a/osu.Game/Screens/Multi/Components/OverlinedPlaylistHeader.cs +++ b/osu.Game/Screens/Multi/Components/OverlinedPlaylistHeader.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.Online.Multiplayer; +using osu.Game.Online.Rooms; namespace osu.Game.Screens.Multi.Components { diff --git a/osu.Game/Screens/Multi/Components/ReadyButton.cs b/osu.Game/Screens/Multi/Components/ReadyButton.cs index 0bb4ed8617..68df30965d 100644 --- a/osu.Game/Screens/Multi/Components/ReadyButton.cs +++ b/osu.Game/Screens/Multi/Components/ReadyButton.cs @@ -9,7 +9,7 @@ using osu.Framework.Bindables; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; namespace osu.Game.Screens.Multi.Components { diff --git a/osu.Game/Screens/Multi/Components/RoomManager.cs b/osu.Game/Screens/Multi/Components/RoomManager.cs index f78d0d979e..7e0c8c4ec5 100644 --- a/osu.Game/Screens/Multi/Components/RoomManager.cs +++ b/osu.Game/Screens/Multi/Components/RoomManager.cs @@ -12,7 +12,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Logging; using osu.Game.Beatmaps; using osu.Game.Online.API; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; using osu.Game.Rulesets; namespace osu.Game.Screens.Multi.Components diff --git a/osu.Game/Screens/Multi/Components/RoomPollingComponent.cs b/osu.Game/Screens/Multi/Components/RoomPollingComponent.cs index fbaf9dd930..1d10277d1c 100644 --- a/osu.Game/Screens/Multi/Components/RoomPollingComponent.cs +++ b/osu.Game/Screens/Multi/Components/RoomPollingComponent.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Game.Online; using osu.Game.Online.API; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; namespace osu.Game.Screens.Multi.Components { diff --git a/osu.Game/Screens/Multi/Components/RoomStatusInfo.cs b/osu.Game/Screens/Multi/Components/RoomStatusInfo.cs index b5676692a4..89021691f3 100644 --- a/osu.Game/Screens/Multi/Components/RoomStatusInfo.cs +++ b/osu.Game/Screens/Multi/Components/RoomStatusInfo.cs @@ -8,8 +8,8 @@ using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; -using osu.Game.Online.Multiplayer; -using osu.Game.Online.Multiplayer.RoomStatuses; +using osu.Game.Online.Rooms; +using osu.Game.Online.Rooms.RoomStatuses; namespace osu.Game.Screens.Multi.Components { diff --git a/osu.Game/Screens/Multi/Components/SelectionPollingComponent.cs b/osu.Game/Screens/Multi/Components/SelectionPollingComponent.cs index 37a190b5e0..3050765931 100644 --- a/osu.Game/Screens/Multi/Components/SelectionPollingComponent.cs +++ b/osu.Game/Screens/Multi/Components/SelectionPollingComponent.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; namespace osu.Game.Screens.Multi.Components { diff --git a/osu.Game/Screens/Multi/Components/StatusColouredContainer.cs b/osu.Game/Screens/Multi/Components/StatusColouredContainer.cs index a115f06e7b..68c14eeb15 100644 --- a/osu.Game/Screens/Multi/Components/StatusColouredContainer.cs +++ b/osu.Game/Screens/Multi/Components/StatusColouredContainer.cs @@ -6,7 +6,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; namespace osu.Game.Screens.Multi.Components { diff --git a/osu.Game/Screens/Multi/DrawableRoomPlaylist.cs b/osu.Game/Screens/Multi/DrawableRoomPlaylist.cs index 89c335183b..956d38a90e 100644 --- a/osu.Game/Screens/Multi/DrawableRoomPlaylist.cs +++ b/osu.Game/Screens/Multi/DrawableRoomPlaylist.cs @@ -7,7 +7,7 @@ using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.Containers; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; using osuTK; namespace osu.Game.Screens.Multi diff --git a/osu.Game/Screens/Multi/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/Multi/DrawableRoomPlaylistItem.cs index bda00b65b5..a2aeae154a 100644 --- a/osu.Game/Screens/Multi/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/Multi/DrawableRoomPlaylistItem.cs @@ -21,7 +21,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online; using osu.Game.Online.Chat; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; using osu.Game.Overlays.BeatmapListing.Panels; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; diff --git a/osu.Game/Screens/Multi/DrawableRoomPlaylistWithResults.cs b/osu.Game/Screens/Multi/DrawableRoomPlaylistWithResults.cs index 439aaaa275..fa241a3c42 100644 --- a/osu.Game/Screens/Multi/DrawableRoomPlaylistWithResults.cs +++ b/osu.Game/Screens/Multi/DrawableRoomPlaylistWithResults.cs @@ -11,7 +11,7 @@ using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; namespace osu.Game.Screens.Multi { diff --git a/osu.Game/Screens/Multi/IRoomManager.cs b/osu.Game/Screens/Multi/IRoomManager.cs index 630e3af91c..eee2a223a1 100644 --- a/osu.Game/Screens/Multi/IRoomManager.cs +++ b/osu.Game/Screens/Multi/IRoomManager.cs @@ -4,7 +4,7 @@ using System; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; namespace osu.Game.Screens.Multi { diff --git a/osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs index 56116b219a..6e4d8b46ed 100644 --- a/osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs @@ -17,12 +17,12 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; -using osu.Game.Online.Multiplayer; using osu.Game.Screens.Multi.Components; using osuTK; using osuTK.Graphics; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.UserInterface; +using osu.Game.Online.Rooms; namespace osu.Game.Screens.Multi.Lounge.Components { @@ -42,7 +42,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components private CachedModelDependencyContainer dependencies; [Resolved(canBeNull: true)] - private Multiplayer multiplayer { get; set; } + private MultiplayerScreen multiplayer { get; set; } [Resolved] private BeatmapManager beatmaps { get; set; } @@ -228,7 +228,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components private class RoomName : OsuSpriteText { - [Resolved(typeof(Room), nameof(Online.Multiplayer.Room.Name))] + [Resolved(typeof(Room), nameof(Online.Rooms.Room.Name))] private Bindable name { get; set; } [BackgroundDependencyLoader] diff --git a/osu.Game/Screens/Multi/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/Multi/Lounge/Components/RoomsContainer.cs index c7c37cbc0d..fbd5f44a30 100644 --- a/osu.Game/Screens/Multi/Lounge/Components/RoomsContainer.cs +++ b/osu.Game/Screens/Multi/Lounge/Components/RoomsContainer.cs @@ -15,9 +15,9 @@ using osu.Framework.Threading; using osu.Game.Extensions; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; -using osu.Game.Online.Multiplayer; using osuTK; using osu.Game.Graphics.Cursor; +using osu.Game.Online.Rooms; namespace osu.Game.Screens.Multi.Lounge.Components { diff --git a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs index 6b08745dd7..cbab79e2ab 100644 --- a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs @@ -10,7 +10,7 @@ using osu.Framework.Input.Events; using osu.Framework.Screens; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; using osu.Game.Overlays; using osu.Game.Screens.Multi.Lounge.Components; using osu.Game.Screens.Multi.Match; diff --git a/osu.Game/Screens/Multi/Match/Components/Footer.cs b/osu.Game/Screens/Multi/Match/Components/Footer.cs index fdf1d0dbeb..7074ceca38 100644 --- a/osu.Game/Screens/Multi/Match/Components/Footer.cs +++ b/osu.Game/Screens/Multi/Match/Components/Footer.cs @@ -9,7 +9,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; using osu.Game.Screens.Multi.Playlists; using osuTK; diff --git a/osu.Game/Screens/Multi/Match/Components/GameTypePicker.cs b/osu.Game/Screens/Multi/Match/Components/GameTypePicker.cs index c7fc329a1a..23a3da6e38 100644 --- a/osu.Game/Screens/Multi/Match/Components/GameTypePicker.cs +++ b/osu.Game/Screens/Multi/Match/Components/GameTypePicker.cs @@ -8,8 +8,8 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; using osu.Game.Graphics; -using osu.Game.Online.Multiplayer; -using osu.Game.Online.Multiplayer.GameTypes; +using osu.Game.Online.Rooms; +using osu.Game.Online.Rooms.GameTypes; using osu.Game.Screens.Multi.Components; using osuTK; diff --git a/osu.Game/Screens/Multi/Match/Components/MatchChatDisplay.cs b/osu.Game/Screens/Multi/Match/Components/MatchChatDisplay.cs index f8b64a54ef..b790ad9be5 100644 --- a/osu.Game/Screens/Multi/Match/Components/MatchChatDisplay.cs +++ b/osu.Game/Screens/Multi/Match/Components/MatchChatDisplay.cs @@ -4,7 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Online.Chat; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; namespace osu.Game.Screens.Multi.Match.Components { diff --git a/osu.Game/Screens/Multi/Match/Components/MatchLeaderboard.cs b/osu.Game/Screens/Multi/Match/Components/MatchLeaderboard.cs index f2409d64e7..8cc7b62f98 100644 --- a/osu.Game/Screens/Multi/Match/Components/MatchLeaderboard.cs +++ b/osu.Game/Screens/Multi/Match/Components/MatchLeaderboard.cs @@ -8,7 +8,7 @@ using osu.Framework.Bindables; using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Leaderboards; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; namespace osu.Game.Screens.Multi.Match.Components { diff --git a/osu.Game/Screens/Multi/Match/Components/RoomAvailabilityPicker.cs b/osu.Game/Screens/Multi/Match/Components/RoomAvailabilityPicker.cs index 7ef39c2a74..2292826d55 100644 --- a/osu.Game/Screens/Multi/Match/Components/RoomAvailabilityPicker.cs +++ b/osu.Game/Screens/Multi/Match/Components/RoomAvailabilityPicker.cs @@ -10,7 +10,7 @@ using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; using osu.Game.Screens.Multi.Components; using osuTK; using osuTK.Graphics; diff --git a/osu.Game/Screens/Multi/Match/RoomSubScreen.cs b/osu.Game/Screens/Multi/Match/RoomSubScreen.cs index 0598524e81..b626156852 100644 --- a/osu.Game/Screens/Multi/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/RoomSubScreen.cs @@ -10,7 +10,7 @@ using osu.Framework.Bindables; using osu.Framework.Screens; using osu.Game.Audio; using osu.Game.Beatmaps; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; using osu.Game.Overlays; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Play; @@ -36,7 +36,7 @@ namespace osu.Game.Screens.Multi.Match private BeatmapManager beatmapManager { get; set; } [Resolved(canBeNull: true)] - protected Multiplayer Multiplayer { get; private set; } + protected MultiplayerScreen Multiplayer { get; private set; } private IBindable> managerUpdated; diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/CreateRealtimeMatchButton.cs b/osu.Game/Screens/Multi/Multiplayer/CreateMultiplayerMatchButton.cs similarity index 77% rename from osu.Game/Screens/Multi/RealtimeMultiplayer/CreateRealtimeMatchButton.cs rename to osu.Game/Screens/Multi/Multiplayer/CreateMultiplayerMatchButton.cs index cdaeb6faec..b8b3f15fca 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/CreateRealtimeMatchButton.cs +++ b/osu.Game/Screens/Multi/Multiplayer/CreateMultiplayerMatchButton.cs @@ -3,12 +3,12 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Game.Online.RealtimeMultiplayer; +using osu.Game.Online.Multiplayer; using osu.Game.Screens.Multi.Match.Components; -namespace osu.Game.Screens.Multi.RealtimeMultiplayer +namespace osu.Game.Screens.Multi.Multiplayer { - public class CreateRealtimeMatchButton : PurpleTriangleButton + public class CreateMultiplayerMatchButton : PurpleTriangleButton { [BackgroundDependencyLoader] private void load(StatefulMultiplayerClient multiplayerClient) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/BeatmapSelectionControl.cs b/osu.Game/Screens/Multi/Multiplayer/Match/BeatmapSelectionControl.cs similarity index 91% rename from osu.Game/Screens/Multi/RealtimeMultiplayer/Match/BeatmapSelectionControl.cs rename to osu.Game/Screens/Multi/Multiplayer/Match/BeatmapSelectionControl.cs index 1939744916..dfb6feeaf9 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/BeatmapSelectionControl.cs +++ b/osu.Game/Screens/Multi/Multiplayer/Match/BeatmapSelectionControl.cs @@ -11,12 +11,12 @@ using osu.Framework.Screens; using osu.Game.Online.API; using osu.Game.Screens.Multi.Match.Components; -namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Match +namespace osu.Game.Screens.Multi.Multiplayer.Match { public class BeatmapSelectionControl : MultiplayerComposite { [Resolved] - private RealtimeMatchSubScreen matchSubScreen { get; set; } + private MultiplayerMatchSubScreen matchSubScreen { get; set; } [Resolved] private IAPIProvider api { get; set; } @@ -49,7 +49,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Match RelativeSizeAxes = Axes.X, Height = 40, Text = "Select beatmap", - Action = () => matchSubScreen.Push(new RealtimeMatchSongSelect()), + Action = () => matchSubScreen.Push(new MultiplayerMatchSongSelect()), Alpha = 0 } } diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeMatchFooter.cs b/osu.Game/Screens/Multi/Multiplayer/Match/MultiplayerMatchFooter.cs similarity index 84% rename from osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeMatchFooter.cs rename to osu.Game/Screens/Multi/Multiplayer/Match/MultiplayerMatchFooter.cs index 31871729f6..145ae18817 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeMatchFooter.cs +++ b/osu.Game/Screens/Multi/Multiplayer/Match/MultiplayerMatchFooter.cs @@ -8,12 +8,12 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; using osuTK; -namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Match +namespace osu.Game.Screens.Multi.Multiplayer.Match { - public class RealtimeMatchFooter : CompositeDrawable + public class MultiplayerMatchFooter : CompositeDrawable { public const float HEIGHT = 50; @@ -21,7 +21,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Match private readonly Drawable background; - public RealtimeMatchFooter() + public MultiplayerMatchFooter() { RelativeSizeAxes = Axes.X; Height = HEIGHT; @@ -29,7 +29,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Match InternalChildren = new[] { background = new Box { RelativeSizeAxes = Axes.Both }, - new RealtimeReadyButton + new MultiplayerReadyButton { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeMatchHeader.cs b/osu.Game/Screens/Multi/Multiplayer/Match/MultiplayerMatchHeader.cs similarity index 95% rename from osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeMatchHeader.cs rename to osu.Game/Screens/Multi/Multiplayer/Match/MultiplayerMatchHeader.cs index a9a10d1510..0c0e580a3e 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeMatchHeader.cs +++ b/osu.Game/Screens/Multi/Multiplayer/Match/MultiplayerMatchHeader.cs @@ -17,9 +17,9 @@ using FontWeight = osu.Game.Graphics.FontWeight; using OsuColour = osu.Game.Graphics.OsuColour; using OsuFont = osu.Game.Graphics.OsuFont; -namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Match +namespace osu.Game.Screens.Multi.Multiplayer.Match { - public class RealtimeMatchHeader : MultiplayerComposite + public class MultiplayerMatchHeader : MultiplayerComposite { public const float HEIGHT = 50; @@ -32,7 +32,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Match [Resolved] private IAPIProvider api { get; set; } - public RealtimeMatchHeader() + public MultiplayerMatchHeader() { RelativeSizeAxes = Axes.X; Height = HEIGHT; diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeMatchSettingsOverlay.cs b/osu.Game/Screens/Multi/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs similarity index 99% rename from osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeMatchSettingsOverlay.cs rename to osu.Game/Screens/Multi/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index a93b1b09d1..4a5b5fd181 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeMatchSettingsOverlay.cs +++ b/osu.Game/Screens/Multi/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -15,15 +15,15 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Multiplayer; -using osu.Game.Online.RealtimeMultiplayer; +using osu.Game.Online.Rooms; using osu.Game.Overlays; using osu.Game.Rulesets; using osu.Game.Screens.Multi.Match.Components; using osuTK; -namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Match +namespace osu.Game.Screens.Multi.Multiplayer.Match { - public class RealtimeMatchSettingsOverlay : MatchSettingsOverlay + public class MultiplayerMatchSettingsOverlay : MatchSettingsOverlay { [BackgroundDependencyLoader] private void load() diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeReadyButton.cs b/osu.Game/Screens/Multi/Multiplayer/Match/MultiplayerReadyButton.cs similarity index 95% rename from osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeReadyButton.cs rename to osu.Game/Screens/Multi/Multiplayer/Match/MultiplayerReadyButton.cs index be405feef1..cea1eeecbb 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/Match/RealtimeReadyButton.cs +++ b/osu.Game/Screens/Multi/Multiplayer/Match/MultiplayerReadyButton.cs @@ -14,13 +14,13 @@ using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; using osu.Game.Online.API; using osu.Game.Online.Multiplayer; -using osu.Game.Online.RealtimeMultiplayer; +using osu.Game.Online.Rooms; using osu.Game.Screens.Multi.Components; using osuTK; -namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Match +namespace osu.Game.Screens.Multi.Multiplayer.Match { - public class RealtimeReadyButton : RealtimeRoomComposite + public class MultiplayerReadyButton : MultiplayerRoomComposite { public Bindable SelectedItem => button.SelectedItem; @@ -39,7 +39,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Match private int countReady; - public RealtimeReadyButton() + public MultiplayerReadyButton() { InternalChild = button = new ButtonWithTrianglesExposed { diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMultiplayer.cs b/osu.Game/Screens/Multi/Multiplayer/Multiplayer.cs similarity index 86% rename from osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMultiplayer.cs rename to osu.Game/Screens/Multi/Multiplayer/Multiplayer.cs index 87c838f29f..97fa4cfa6e 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMultiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer/Multiplayer.cs @@ -7,13 +7,13 @@ using osu.Framework.Screens; using osu.Game.Extensions; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Multiplayer; -using osu.Game.Online.RealtimeMultiplayer; +using osu.Game.Online.Rooms; using osu.Game.Screens.Multi.Components; using osu.Game.Screens.Multi.Lounge; -namespace osu.Game.Screens.Multi.RealtimeMultiplayer +namespace osu.Game.Screens.Multi.Multiplayer { - public class RealtimeMultiplayer : Multiplayer + public class Multiplayer : MultiplayerScreen { [Resolved] private StatefulMultiplayerClient client { get; set; } @@ -28,7 +28,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer protected override void UpdatePollingRate(bool isIdle) { - var playlistsManager = (RealtimeRoomManager)RoomManager; + var playlistsManager = (MultiplayerRoomManager)RoomManager; if (!this.IsCurrentScreen()) { @@ -58,16 +58,16 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer protected override Room CreateNewRoom() { var room = new Room { Name = { Value = $"{API.LocalUser}'s awesome room" } }; - room.Category.Value = RoomCategory.Realtime; + room.Category.Value = RoomCategory.Multiplayer; return room; } protected override string ScreenTitle => "Multiplayer"; - protected override RoomManager CreateRoomManager() => new RealtimeRoomManager(); + protected override RoomManager CreateRoomManager() => new MultiplayerRoomManager(); - protected override LoungeSubScreen CreateLounge() => new RealtimeLoungeSubScreen(); + protected override LoungeSubScreen CreateLounge() => new MultiplayerLoungeSubScreen(); - protected override OsuButton CreateNewMultiplayerGameButton() => new CreateRealtimeMatchButton(); + protected override OsuButton CreateNewMultiplayerGameButton() => new CreateMultiplayerMatchButton(); } } diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeFilterControl.cs b/osu.Game/Screens/Multi/Multiplayer/MultiplayerFilterControl.cs similarity index 79% rename from osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeFilterControl.cs rename to osu.Game/Screens/Multi/Multiplayer/MultiplayerFilterControl.cs index acd9a057e3..bebad1944e 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeFilterControl.cs +++ b/osu.Game/Screens/Multi/Multiplayer/MultiplayerFilterControl.cs @@ -3,9 +3,9 @@ using osu.Game.Screens.Multi.Lounge.Components; -namespace osu.Game.Screens.Multi.RealtimeMultiplayer +namespace osu.Game.Screens.Multi.Multiplayer { - public class RealtimeFilterControl : FilterControl + public class MultiplayerFilterControl : FilterControl { protected override FilterCriteria CreateCriteria() { diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeLoungeSubScreen.cs b/osu.Game/Screens/Multi/Multiplayer/MultiplayerLoungeSubScreen.cs similarity index 79% rename from osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeLoungeSubScreen.cs rename to osu.Game/Screens/Multi/Multiplayer/MultiplayerLoungeSubScreen.cs index b53ec94519..ffc81efe3c 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeLoungeSubScreen.cs +++ b/osu.Game/Screens/Multi/Multiplayer/MultiplayerLoungeSubScreen.cs @@ -4,18 +4,18 @@ using osu.Framework.Allocation; using osu.Framework.Logging; using osu.Game.Online.Multiplayer; -using osu.Game.Online.RealtimeMultiplayer; +using osu.Game.Online.Rooms; using osu.Game.Screens.Multi.Lounge; using osu.Game.Screens.Multi.Lounge.Components; using osu.Game.Screens.Multi.Match; -namespace osu.Game.Screens.Multi.RealtimeMultiplayer +namespace osu.Game.Screens.Multi.Multiplayer { - public class RealtimeLoungeSubScreen : LoungeSubScreen + public class MultiplayerLoungeSubScreen : LoungeSubScreen { - protected override FilterControl CreateFilterControl() => new RealtimeFilterControl(); + protected override FilterControl CreateFilterControl() => new MultiplayerFilterControl(); - protected override RoomSubScreen CreateRoomSubScreen(Room room) => new RealtimeMatchSubScreen(room); + protected override RoomSubScreen CreateRoomSubScreen(Room room) => new MultiplayerMatchSubScreen(room); [Resolved] private StatefulMultiplayerClient client { get; set; } diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSongSelect.cs b/osu.Game/Screens/Multi/Multiplayer/MultiplayerMatchSongSelect.cs similarity index 92% rename from osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSongSelect.cs rename to osu.Game/Screens/Multi/Multiplayer/MultiplayerMatchSongSelect.cs index 8f317800e3..ed1321d4e2 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSongSelect.cs +++ b/osu.Game/Screens/Multi/Multiplayer/MultiplayerMatchSongSelect.cs @@ -10,12 +10,12 @@ using osu.Framework.Logging; using osu.Framework.Screens; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Multiplayer; -using osu.Game.Online.RealtimeMultiplayer; +using osu.Game.Online.Rooms; using osu.Game.Screens.Select; -namespace osu.Game.Screens.Multi.RealtimeMultiplayer +namespace osu.Game.Screens.Multi.Multiplayer { - public class RealtimeMatchSongSelect : SongSelect, IMultiplayerSubScreen + public class MultiplayerMatchSongSelect : SongSelect, IMultiplayerSubScreen { public string ShortTitle => "song selection"; @@ -29,7 +29,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer private LoadingLayer loadingLayer; - public RealtimeMatchSongSelect() + public MultiplayerMatchSongSelect() { Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING }; } diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSubScreen.cs b/osu.Game/Screens/Multi/Multiplayer/MultiplayerMatchSubScreen.cs similarity index 92% rename from osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSubScreen.cs rename to osu.Game/Screens/Multi/Multiplayer/MultiplayerMatchSubScreen.cs index 1778bc272c..4e371d4ed4 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeMatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Multiplayer/MultiplayerMatchSubScreen.cs @@ -10,19 +10,19 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Screens; using osu.Game.Online.Multiplayer; -using osu.Game.Online.RealtimeMultiplayer; +using osu.Game.Online.Rooms; using osu.Game.Screens.Multi.Components; using osu.Game.Screens.Multi.Match; using osu.Game.Screens.Multi.Match.Components; -using osu.Game.Screens.Multi.RealtimeMultiplayer.Match; -using osu.Game.Screens.Multi.RealtimeMultiplayer.Participants; +using osu.Game.Screens.Multi.Multiplayer.Match; +using osu.Game.Screens.Multi.Multiplayer.Participants; using osu.Game.Users; -using ParticipantsList = osu.Game.Screens.Multi.RealtimeMultiplayer.Participants.ParticipantsList; +using ParticipantsList = osu.Game.Screens.Multi.Multiplayer.Participants.ParticipantsList; -namespace osu.Game.Screens.Multi.RealtimeMultiplayer +namespace osu.Game.Screens.Multi.Multiplayer { [Cached] - public class RealtimeMatchSubScreen : RoomSubScreen + public class MultiplayerMatchSubScreen : RoomSubScreen { public override string Title { get; } @@ -31,11 +31,11 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer [Resolved] private StatefulMultiplayerClient client { get; set; } - private RealtimeMatchSettingsOverlay settingsOverlay; + private MultiplayerMatchSettingsOverlay settingsOverlay; private IBindable isConnected; - public RealtimeMatchSubScreen(Room room) + public MultiplayerMatchSubScreen(Room room) { Title = room.RoomID.Value == null ? "New room" : room.Name.Value; Activity.Value = new UserActivity.InLobby(room); @@ -73,7 +73,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer { new Drawable[] { - new RealtimeMatchHeader + new MultiplayerMatchHeader { OpenSettings = () => settingsOverlay.Show() } @@ -150,7 +150,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer }, new Drawable[] { - new RealtimeMatchFooter { SelectedItem = { BindTarget = SelectedItem } } + new MultiplayerMatchFooter { SelectedItem = { BindTarget = SelectedItem } } } }, RowDimensions = new[] @@ -159,7 +159,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer new Dimension(GridSizeMode.AutoSize), } }, - settingsOverlay = new RealtimeMatchSettingsOverlay + settingsOverlay = new MultiplayerMatchSettingsOverlay { RelativeSizeAxes = Axes.Both, State = { Value = client.Room == null ? Visibility.Visible : Visibility.Hidden } @@ -202,7 +202,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer int[] userIds = client.Room.Users.Where(u => u.State >= MultiplayerUserState.WaitingForLoad).Select(u => u.UserID).ToArray(); - StartPlay(() => new RealtimePlayer(SelectedItem.Value, userIds)); + StartPlay(() => new MultiplayerPlayer(SelectedItem.Value, userIds)); } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs b/osu.Game/Screens/Multi/Multiplayer/MultiplayerPlayer.cs similarity index 93% rename from osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs rename to osu.Game/Screens/Multi/Multiplayer/MultiplayerPlayer.cs index 033e4756eb..3255bcc642 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimePlayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer/MultiplayerPlayer.cs @@ -9,7 +9,7 @@ using osu.Framework.Bindables; using osu.Framework.Logging; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Multiplayer; -using osu.Game.Online.RealtimeMultiplayer; +using osu.Game.Online.Rooms; using osu.Game.Scoring; using osu.Game.Screens.Multi.Play; using osu.Game.Screens.Play; @@ -17,10 +17,10 @@ using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Ranking; using osuTK; -namespace osu.Game.Screens.Multi.RealtimeMultiplayer +namespace osu.Game.Screens.Multi.Multiplayer { // Todo: The "room" part of PlaylistsPlayer should be split out into an abstract player class to be inherited instead. - public class RealtimePlayer : PlaylistsPlayer + public class MultiplayerPlayer : PlaylistsPlayer { protected override bool PauseOnFocusLost => false; @@ -45,7 +45,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer /// /// The playlist item to be played. /// The users which are participating in this game. - public RealtimePlayer(PlaylistItem playlistItem, int[] userIds) + public MultiplayerPlayer(PlaylistItem playlistItem, int[] userIds) : base(playlistItem, new PlayerConfiguration { AllowPause = false, @@ -81,7 +81,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer { if (!connected.NewValue) { - // messaging to the user about this disconnect will be provided by the RealtimeMatchSubScreen. + // messaging to the user about this disconnect will be provided by the MultiplayerMatchSubScreen. failAndBail(); } }, true); @@ -142,7 +142,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer protected override ResultsScreen CreateResults(ScoreInfo score) { Debug.Assert(RoomId.Value != null); - return new RealtimeResultsScreen(score, RoomId.Value.Value, PlaylistItem); + return new MultiplayerResultsScreen(score, RoomId.Value.Value, PlaylistItem); } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeResultsScreen.cs b/osu.Game/Screens/Multi/Multiplayer/MultiplayerResultsScreen.cs similarity index 55% rename from osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeResultsScreen.cs rename to osu.Game/Screens/Multi/Multiplayer/MultiplayerResultsScreen.cs index 6bec06cbba..a01930c5ef 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeResultsScreen.cs +++ b/osu.Game/Screens/Multi/Multiplayer/MultiplayerResultsScreen.cs @@ -1,15 +1,15 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; using osu.Game.Scoring; using osu.Game.Screens.Multi.Ranking; -namespace osu.Game.Screens.Multi.RealtimeMultiplayer +namespace osu.Game.Screens.Multi.Multiplayer { - public class RealtimeResultsScreen : PlaylistsResultsScreen + public class MultiplayerResultsScreen : PlaylistsResultsScreen { - public RealtimeResultsScreen(ScoreInfo score, int roomId, PlaylistItem playlistItem) + public MultiplayerResultsScreen(ScoreInfo score, int roomId, PlaylistItem playlistItem) : base(score, roomId, playlistItem, false, false) { } diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomComposite.cs b/osu.Game/Screens/Multi/Multiplayer/MultiplayerRoomComposite.cs similarity index 83% rename from osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomComposite.cs rename to osu.Game/Screens/Multi/Multiplayer/MultiplayerRoomComposite.cs index e6d1274316..6fe7c10bfb 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomComposite.cs +++ b/osu.Game/Screens/Multi/Multiplayer/MultiplayerRoomComposite.cs @@ -3,11 +3,11 @@ using JetBrains.Annotations; using osu.Framework.Allocation; -using osu.Game.Online.RealtimeMultiplayer; +using osu.Game.Online.Multiplayer; -namespace osu.Game.Screens.Multi.RealtimeMultiplayer +namespace osu.Game.Screens.Multi.Multiplayer { - public abstract class RealtimeRoomComposite : MultiplayerComposite + public abstract class MultiplayerRoomComposite : MultiplayerComposite { [CanBeNull] protected MultiplayerRoom Room => Client.Room; diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs b/osu.Game/Screens/Multi/Multiplayer/MultiplayerRoomManager.cs similarity index 92% rename from osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs rename to osu.Game/Screens/Multi/Multiplayer/MultiplayerRoomManager.cs index eb6e5fad07..fdd46cdc2a 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeRoomManager.cs +++ b/osu.Game/Screens/Multi/Multiplayer/MultiplayerRoomManager.cs @@ -11,13 +11,13 @@ using osu.Framework.Extensions.ExceptionExtensions; using osu.Framework.Logging; using osu.Game.Extensions; using osu.Game.Online.Multiplayer; -using osu.Game.Online.Multiplayer.RoomStatuses; -using osu.Game.Online.RealtimeMultiplayer; +using osu.Game.Online.Rooms; +using osu.Game.Online.Rooms.RoomStatuses; using osu.Game.Screens.Multi.Components; -namespace osu.Game.Screens.Multi.RealtimeMultiplayer +namespace osu.Game.Screens.Multi.Multiplayer { - public class RealtimeRoomManager : RoomManager + public class MultiplayerRoomManager : RoomManager { [Resolved] private StatefulMultiplayerClient multiplayerClient { get; set; } @@ -114,19 +114,19 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer protected override IEnumerable CreatePollingComponents() => new RoomPollingComponent[] { - listingPollingComponent = new RealtimeListingPollingComponent + listingPollingComponent = new MultiplayerListingPollingComponent { TimeBetweenPolls = { BindTarget = TimeBetweenListingPolls }, AllowPolling = { BindTarget = allowPolling } }, - new RealtimeSelectionPollingComponent + new MultiplayerSelectionPollingComponent { TimeBetweenPolls = { BindTarget = TimeBetweenSelectionPolls }, AllowPolling = { BindTarget = allowPolling } } }; - private class RealtimeListingPollingComponent : ListingPollingComponent + private class MultiplayerListingPollingComponent : ListingPollingComponent { public readonly IBindable AllowPolling = new Bindable(); @@ -147,7 +147,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer protected override Task Poll() => !AllowPolling.Value ? Task.CompletedTask : base.Poll(); } - private class RealtimeSelectionPollingComponent : SelectionPollingComponent + private class MultiplayerSelectionPollingComponent : SelectionPollingComponent { public readonly IBindable AllowPolling = new Bindable(); diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantPanel.cs b/osu.Game/Screens/Multi/Multiplayer/Participants/ParticipantPanel.cs similarity index 97% rename from osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantPanel.cs rename to osu.Game/Screens/Multi/Multiplayer/Participants/ParticipantPanel.cs index 85393d1bae..93f8a9de8b 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantPanel.cs +++ b/osu.Game/Screens/Multi/Multiplayer/Participants/ParticipantPanel.cs @@ -16,15 +16,15 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; -using osu.Game.Online.RealtimeMultiplayer; +using osu.Game.Online.Multiplayer; using osu.Game.Users; using osu.Game.Users.Drawables; using osuTK; using osuTK.Graphics; -namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Participants +namespace osu.Game.Screens.Multi.Multiplayer.Participants { - public class ParticipantPanel : RealtimeRoomComposite, IHasContextMenu + public class ParticipantPanel : MultiplayerRoomComposite, IHasContextMenu { public readonly MultiplayerRoomUser User; diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantsList.cs b/osu.Game/Screens/Multi/Multiplayer/Participants/ParticipantsList.cs similarity index 93% rename from osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantsList.cs rename to osu.Game/Screens/Multi/Multiplayer/Participants/ParticipantsList.cs index 218c2cabb7..3f37274d4c 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantsList.cs +++ b/osu.Game/Screens/Multi/Multiplayer/Participants/ParticipantsList.cs @@ -9,9 +9,9 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Cursor; using osuTK; -namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Participants +namespace osu.Game.Screens.Multi.Multiplayer.Participants { - public class ParticipantsList : RealtimeRoomComposite + public class ParticipantsList : MultiplayerRoomComposite { private FillFlowContainer panels; diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantsListHeader.cs b/osu.Game/Screens/Multi/Multiplayer/Participants/ParticipantsListHeader.cs similarity index 86% rename from osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantsListHeader.cs rename to osu.Game/Screens/Multi/Multiplayer/Participants/ParticipantsListHeader.cs index 0ca7d34005..dc3ceedfd7 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/ParticipantsListHeader.cs +++ b/osu.Game/Screens/Multi/Multiplayer/Participants/ParticipantsListHeader.cs @@ -2,10 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Game.Online.RealtimeMultiplayer; +using osu.Game.Online.Multiplayer; using osu.Game.Screens.Multi.Components; -namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Participants +namespace osu.Game.Screens.Multi.Multiplayer.Participants { public class ParticipantsListHeader : OverlinedHeader { diff --git a/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/StateDisplay.cs b/osu.Game/Screens/Multi/Multiplayer/Participants/StateDisplay.cs similarity index 97% rename from osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/StateDisplay.cs rename to osu.Game/Screens/Multi/Multiplayer/Participants/StateDisplay.cs index 844f239363..61faa0d85d 100644 --- a/osu.Game/Screens/Multi/RealtimeMultiplayer/Participants/StateDisplay.cs +++ b/osu.Game/Screens/Multi/Multiplayer/Participants/StateDisplay.cs @@ -8,10 +8,10 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osu.Game.Online.RealtimeMultiplayer; +using osu.Game.Online.Multiplayer; using osuTK; -namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Participants +namespace osu.Game.Screens.Multi.Multiplayer.Participants { public class StateDisplay : CompositeDrawable { diff --git a/osu.Game/Screens/Multi/MultiplayerComposite.cs b/osu.Game/Screens/Multi/MultiplayerComposite.cs index 6e0c69d712..fe4ca759b0 100644 --- a/osu.Game/Screens/Multi/MultiplayerComposite.cs +++ b/osu.Game/Screens/Multi/MultiplayerComposite.cs @@ -5,7 +5,7 @@ using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics.Containers; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; using osu.Game.Users; namespace osu.Game.Screens.Multi diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/MultiplayerScreen.cs similarity index 99% rename from osu.Game/Screens/Multi/Multiplayer.cs rename to osu.Game/Screens/Multi/MultiplayerScreen.cs index 34b7139f2d..983c7aeac9 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/MultiplayerScreen.cs @@ -14,7 +14,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Input; using osu.Game.Online.API; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; using osu.Game.Overlays; using osu.Game.Screens.Menu; using osu.Game.Screens.Multi.Components; @@ -27,7 +27,7 @@ using osuTK; namespace osu.Game.Screens.Multi { [Cached] - public abstract class Multiplayer : OsuScreen + public abstract class MultiplayerScreen : OsuScreen { public override bool CursorVisible => (screenStack.CurrentScreen as IMultiplayerSubScreen)?.CursorVisible ?? true; @@ -67,7 +67,7 @@ namespace osu.Game.Screens.Multi private readonly Drawable header; private readonly Drawable headerBackground; - protected Multiplayer() + protected MultiplayerScreen() { Anchor = Anchor.Centre; Origin = Anchor.Centre; diff --git a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs index 65b0091505..f05f732494 100644 --- a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs +++ b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs @@ -11,7 +11,7 @@ using osu.Framework.Bindables; using osu.Framework.Logging; using osu.Framework.Screens; using osu.Game.Online.API; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; using osu.Game.Rulesets; using osu.Game.Scoring; using osu.Game.Screens.Multi.Ranking; diff --git a/osu.Game/Screens/Multi/Playlists/PlaylistsLoungeSubScreen.cs b/osu.Game/Screens/Multi/Playlists/PlaylistsLoungeSubScreen.cs index b40c543b68..513854515b 100644 --- a/osu.Game/Screens/Multi/Playlists/PlaylistsLoungeSubScreen.cs +++ b/osu.Game/Screens/Multi/Playlists/PlaylistsLoungeSubScreen.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.Online.Multiplayer; +using osu.Game.Online.Rooms; using osu.Game.Screens.Multi.Lounge; using osu.Game.Screens.Multi.Lounge.Components; using osu.Game.Screens.Multi.Match; diff --git a/osu.Game/Screens/Multi/Playlists/PlaylistsMatchSettingsOverlay.cs b/osu.Game/Screens/Multi/Playlists/PlaylistsMatchSettingsOverlay.cs index af29e8d34d..f3109a33e4 100644 --- a/osu.Game/Screens/Multi/Playlists/PlaylistsMatchSettingsOverlay.cs +++ b/osu.Game/Screens/Multi/Playlists/PlaylistsMatchSettingsOverlay.cs @@ -14,7 +14,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; using osu.Game.Overlays; using osu.Game.Screens.Multi.Match.Components; using osuTK; diff --git a/osu.Game/Screens/Multi/Playlists/PlaylistsMultiplayer.cs b/osu.Game/Screens/Multi/Playlists/PlaylistsMultiplayer.cs index fce24da966..92b0160247 100644 --- a/osu.Game/Screens/Multi/Playlists/PlaylistsMultiplayer.cs +++ b/osu.Game/Screens/Multi/Playlists/PlaylistsMultiplayer.cs @@ -4,14 +4,14 @@ using osu.Framework.Logging; using osu.Framework.Screens; using osu.Game.Graphics.UserInterface; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; using osu.Game.Screens.Multi.Components; using osu.Game.Screens.Multi.Lounge; using osu.Game.Screens.Multi.Match; namespace osu.Game.Screens.Multi.Playlists { - public class PlaylistsMultiplayer : Multiplayer + public class PlaylistsMultiplayer : MultiplayerScreen { protected override void UpdatePollingRate(bool isIdle) { diff --git a/osu.Game/Screens/Multi/Playlists/PlaylistsReadyButton.cs b/osu.Game/Screens/Multi/Playlists/PlaylistsReadyButton.cs index f5adf899e3..3c35e2a6f3 100644 --- a/osu.Game/Screens/Multi/Playlists/PlaylistsReadyButton.cs +++ b/osu.Game/Screens/Multi/Playlists/PlaylistsReadyButton.cs @@ -5,7 +5,7 @@ using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Graphics; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; using osu.Game.Screens.Multi.Components; namespace osu.Game.Screens.Multi.Playlists diff --git a/osu.Game/Screens/Multi/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/Multi/Playlists/PlaylistsRoomSubScreen.cs index b2bcba1724..67242003e6 100644 --- a/osu.Game/Screens/Multi/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/Multi/Playlists/PlaylistsRoomSubScreen.cs @@ -9,7 +9,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Screens; using osu.Game.Online.API; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; using osu.Game.Screens.Multi.Components; using osu.Game.Screens.Multi.Match; using osu.Game.Screens.Multi.Match.Components; @@ -31,7 +31,7 @@ namespace osu.Game.Screens.Multi.Playlists private Bindable roomId { get; set; } [Resolved(canBeNull: true)] - private Multiplayer multiplayer { get; set; } + private MultiplayerScreen multiplayer { get; set; } private MatchSettingsOverlay settingsOverlay; private MatchLeaderboard leaderboard; diff --git a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs index c757433e2f..7b1ab2c5b7 100644 --- a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs +++ b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs @@ -11,7 +11,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; using osu.Game.Scoring; using osu.Game.Screens.Ranking; diff --git a/osu.Game/Screens/Select/MatchSongSelect.cs b/osu.Game/Screens/Select/MatchSongSelect.cs index 8692833a21..80fd5c2067 100644 --- a/osu.Game/Screens/Select/MatchSongSelect.cs +++ b/osu.Game/Screens/Select/MatchSongSelect.cs @@ -9,7 +9,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Screens; using osu.Game.Beatmaps; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; using osu.Game.Screens.Multi; using osu.Game.Screens.Multi.Components; diff --git a/osu.Game/Tests/Visual/RealtimeMultiplayer/RealtimeMultiplayerTestScene.cs b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs similarity index 67% rename from osu.Game/Tests/Visual/RealtimeMultiplayer/RealtimeMultiplayerTestScene.cs rename to osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs index 30bd3ebc32..0e23f4d8c6 100644 --- a/osu.Game/Tests/Visual/RealtimeMultiplayer/RealtimeMultiplayerTestScene.cs +++ b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs @@ -6,32 +6,32 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Online.RealtimeMultiplayer; +using osu.Game.Online.Multiplayer; using osu.Game.Screens.Multi; using osu.Game.Screens.Multi.Lounge.Components; -namespace osu.Game.Tests.Visual.RealtimeMultiplayer +namespace osu.Game.Tests.Visual.Multiplayer { - public abstract class RealtimeMultiplayerTestScene : MultiplayerTestScene + public abstract class MultiplayerTestScene : RoomTestScene { [Cached(typeof(StatefulMultiplayerClient))] - public TestRealtimeMultiplayerClient Client { get; } + public TestMultiplayerClient Client { get; } [Cached(typeof(IRoomManager))] - public TestRealtimeRoomManager RoomManager { get; } + public TestMultiplayerRoomManager RoomManager { get; } [Cached] public Bindable Filter { get; } protected override Container Content => content; - private readonly TestRealtimeRoomContainer content; + private readonly TestMultiplayerRoomContainer content; private readonly bool joinRoom; - protected RealtimeMultiplayerTestScene(bool joinRoom = true) + protected MultiplayerTestScene(bool joinRoom = true) { this.joinRoom = joinRoom; - base.Content.Add(content = new TestRealtimeRoomContainer { RelativeSizeAxes = Axes.Both }); + base.Content.Add(content = new TestMultiplayerRoomContainer { RelativeSizeAxes = Axes.Both }); Client = content.Client; RoomManager = content.RoomManager; diff --git a/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs similarity index 95% rename from osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeMultiplayerClient.cs rename to osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 52047016e2..9a839c8d22 100644 --- a/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -9,12 +9,12 @@ using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Online.API; -using osu.Game.Online.RealtimeMultiplayer; +using osu.Game.Online.Multiplayer; using osu.Game.Users; -namespace osu.Game.Tests.Visual.RealtimeMultiplayer +namespace osu.Game.Tests.Visual.Multiplayer { - public class TestRealtimeMultiplayerClient : StatefulMultiplayerClient + public class TestMultiplayerClient : StatefulMultiplayerClient { public override IBindable IsConnected => isConnected; private readonly Bindable isConnected = new Bindable(true); diff --git a/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeRoomContainer.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomContainer.cs similarity index 67% rename from osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeRoomContainer.cs rename to osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomContainer.cs index 3565d6ac5d..0df397d98e 100644 --- a/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeRoomContainer.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomContainer.cs @@ -5,34 +5,34 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Online.RealtimeMultiplayer; +using osu.Game.Online.Multiplayer; using osu.Game.Screens.Multi; using osu.Game.Screens.Multi.Lounge.Components; -namespace osu.Game.Tests.Visual.RealtimeMultiplayer +namespace osu.Game.Tests.Visual.Multiplayer { - public class TestRealtimeRoomContainer : Container + public class TestMultiplayerRoomContainer : Container { protected override Container Content => content; private readonly Container content; [Cached(typeof(StatefulMultiplayerClient))] - public readonly TestRealtimeMultiplayerClient Client; + public readonly TestMultiplayerClient Client; [Cached(typeof(IRoomManager))] - public readonly TestRealtimeRoomManager RoomManager; + public readonly TestMultiplayerRoomManager RoomManager; [Cached] public readonly Bindable Filter = new Bindable(new FilterCriteria()); - public TestRealtimeRoomContainer() + public TestMultiplayerRoomContainer() { RelativeSizeAxes = Axes.Both; AddRangeInternal(new Drawable[] { - Client = new TestRealtimeMultiplayerClient(), - RoomManager = new TestRealtimeRoomManager(), + Client = new TestMultiplayerClient(), + RoomManager = new TestMultiplayerRoomManager(), content = new Container { RelativeSizeAxes = Axes.Both } }); } diff --git a/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeRoomManager.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs similarity index 95% rename from osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeRoomManager.cs rename to osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs index 0d1314fb51..abfefd363a 100644 --- a/osu.Game/Tests/Visual/RealtimeMultiplayer/TestRealtimeRoomManager.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs @@ -8,15 +8,15 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Online.API; using osu.Game.Online.API.Requests; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Multi.Lounge.Components; -using osu.Game.Screens.Multi.RealtimeMultiplayer; +using osu.Game.Screens.Multi.Multiplayer; -namespace osu.Game.Tests.Visual.RealtimeMultiplayer +namespace osu.Game.Tests.Visual.Multiplayer { - public class TestRealtimeRoomManager : RealtimeRoomManager + public class TestMultiplayerRoomManager : MultiplayerRoomManager { [Resolved] private IAPIProvider api { get; set; } diff --git a/osu.Game/Tests/Visual/MultiplayerTestScene.cs b/osu.Game/Tests/Visual/RoomTestScene.cs similarity index 90% rename from osu.Game/Tests/Visual/MultiplayerTestScene.cs rename to osu.Game/Tests/Visual/RoomTestScene.cs index 6f24e00a92..aaf5c7624f 100644 --- a/osu.Game/Tests/Visual/MultiplayerTestScene.cs +++ b/osu.Game/Tests/Visual/RoomTestScene.cs @@ -4,11 +4,11 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; namespace osu.Game.Tests.Visual { - public abstract class MultiplayerTestScene : ScreenTestScene + public abstract class RoomTestScene : ScreenTestScene { [Cached] private readonly Bindable currentRoom = new Bindable(); diff --git a/osu.Game/Users/UserActivity.cs b/osu.Game/Users/UserActivity.cs index 0b4fa94942..f633773d11 100644 --- a/osu.Game/Users/UserActivity.cs +++ b/osu.Game/Users/UserActivity.cs @@ -3,7 +3,7 @@ using osu.Game.Beatmaps; using osu.Game.Graphics; -using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; using osu.Game.Rulesets; using osuTK.Graphics; From e49dce2c866f804b2e1a93fa49b025c112fc8f06 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 25 Dec 2020 15:34:13 +0900 Subject: [PATCH 557/818] Fix some missed renames --- .../GameTypes/{GameTypeTimeshift.cs => GameTypePlaylists.cs} | 0 .../{TimeshiftFilterControl.cs => PlaylistsFilterControl.cs} | 0 .../Screens/Multi/Play/{TimeshiftPlayer.cs => PlaylistsPlayer.cs} | 0 .../{TimeshiftResultsScreen.cs => PlaylistsResultsScreen.cs} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename osu.Game/Online/Rooms/GameTypes/{GameTypeTimeshift.cs => GameTypePlaylists.cs} (100%) rename osu.Game/Screens/Multi/Lounge/Components/{TimeshiftFilterControl.cs => PlaylistsFilterControl.cs} (100%) rename osu.Game/Screens/Multi/Play/{TimeshiftPlayer.cs => PlaylistsPlayer.cs} (100%) rename osu.Game/Screens/Multi/Ranking/{TimeshiftResultsScreen.cs => PlaylistsResultsScreen.cs} (100%) diff --git a/osu.Game/Online/Rooms/GameTypes/GameTypeTimeshift.cs b/osu.Game/Online/Rooms/GameTypes/GameTypePlaylists.cs similarity index 100% rename from osu.Game/Online/Rooms/GameTypes/GameTypeTimeshift.cs rename to osu.Game/Online/Rooms/GameTypes/GameTypePlaylists.cs diff --git a/osu.Game/Screens/Multi/Lounge/Components/TimeshiftFilterControl.cs b/osu.Game/Screens/Multi/Lounge/Components/PlaylistsFilterControl.cs similarity index 100% rename from osu.Game/Screens/Multi/Lounge/Components/TimeshiftFilterControl.cs rename to osu.Game/Screens/Multi/Lounge/Components/PlaylistsFilterControl.cs diff --git a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs b/osu.Game/Screens/Multi/Play/PlaylistsPlayer.cs similarity index 100% rename from osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs rename to osu.Game/Screens/Multi/Play/PlaylistsPlayer.cs diff --git a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs b/osu.Game/Screens/Multi/Ranking/PlaylistsResultsScreen.cs similarity index 100% rename from osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs rename to osu.Game/Screens/Multi/Ranking/PlaylistsResultsScreen.cs From 13c38c9b5556d988ab343beb70fbe2659a5237db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 25 Dec 2020 14:18:21 +0100 Subject: [PATCH 558/818] Fix tests failing due to wrong inheritance --- osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs | 2 +- .../Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs index a72f71d79c..71b8ad05ee 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs @@ -13,7 +13,7 @@ using osuTK; namespace osu.Game.Tests.Visual.Multiplayer { - public class TestSceneMatchLeaderboard : MultiplayerTestScene + public class TestSceneMatchLeaderboard : RoomTestScene { protected override bool UseOnlineAPI => true; diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs index 292c4846ab..7a3845cbf3 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs @@ -9,7 +9,7 @@ using osu.Game.Online.Rooms; namespace osu.Game.Tests.Visual.Multiplayer { [HeadlessTest] - public class TestSceneMultiplayerRoomManager : MultiplayerTestScene + public class TestSceneMultiplayerRoomManager : RoomTestScene { private TestMultiplayerRoomContainer roomContainer; private TestMultiplayerRoomManager roomManager => roomContainer.RoomManager; From da8365f9d0047301b63a10103f0e7630fc8b3a07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 25 Dec 2020 14:34:47 +0100 Subject: [PATCH 559/818] Fix other missed cases of changing inheritance --- osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomInfo.cs | 2 +- .../Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.cs | 2 +- osu.Game.Tests/Visual/Multiplayer/TestSceneMatchHeader.cs | 2 +- osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSongSelect.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomInfo.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomInfo.cs index 1359274512..f58e1114b8 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomInfo.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomInfo.cs @@ -10,7 +10,7 @@ using osu.Game.Users; namespace osu.Game.Tests.Visual.Multiplayer { - public class TestSceneLoungeRoomInfo : MultiplayerTestScene + public class TestSceneLoungeRoomInfo : RoomTestScene { [SetUp] public new void Setup() => Schedule(() => diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.cs index f4e0cc415c..571330f50e 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.cs @@ -15,7 +15,7 @@ using osuTK; namespace osu.Game.Tests.Visual.Multiplayer { - public class TestSceneMatchBeatmapDetailArea : MultiplayerTestScene + public class TestSceneMatchBeatmapDetailArea : RoomTestScene { [Resolved] private BeatmapManager beatmapManager { get; set; } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchHeader.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchHeader.cs index e162009771..61968dce46 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchHeader.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchHeader.cs @@ -11,7 +11,7 @@ using osu.Game.Users; namespace osu.Game.Tests.Visual.Multiplayer { - public class TestSceneMatchHeader : MultiplayerTestScene + public class TestSceneMatchHeader : RoomTestScene { public TestSceneMatchHeader() { diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSongSelect.cs index 4742fd0d84..157597e800 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSongSelect.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSongSelect.cs @@ -23,7 +23,7 @@ using osu.Game.Screens.Select; namespace osu.Game.Tests.Visual.Multiplayer { - public class TestSceneMatchSongSelect : MultiplayerTestScene + public class TestSceneMatchSongSelect : RoomTestScene { [Resolved] private BeatmapManager beatmapManager { get; set; } From 3a6a3a067b4816b81bf1851b75640a8e73349221 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 25 Dec 2020 14:54:21 +0100 Subject: [PATCH 560/818] Rewrite test to cover failure case --- .../Online/TestSceneAccountCreationOverlay.cs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs index dcfe0432a8..3d65e7e4ba 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -13,13 +14,12 @@ namespace osu.Game.Tests.Visual.Online public class TestSceneAccountCreationOverlay : OsuTestScene { private readonly Container userPanelArea; + private readonly AccountCreationOverlay accountCreation; private IBindable localUser; public TestSceneAccountCreationOverlay() { - AccountCreationOverlay accountCreation; - Children = new Drawable[] { accountCreation = new AccountCreationOverlay(), @@ -31,8 +31,6 @@ namespace osu.Game.Tests.Visual.Online Origin = Anchor.TopRight, }, }; - - AddStep("show", () => accountCreation.Show()); } [BackgroundDependencyLoader] @@ -42,8 +40,19 @@ namespace osu.Game.Tests.Visual.Online localUser = API.LocalUser.GetBoundCopy(); localUser.BindValueChanged(user => { userPanelArea.Child = new UserGridPanel(user.NewValue) { Width = 200 }; }, true); + } - AddStep("logout", API.Logout); + [Test] + public void TestOverlayVisibility() + { + AddStep("start hidden", () => accountCreation.Hide()); + AddStep("log out", API.Logout); + + AddStep("show manually", () => accountCreation.Show()); + AddUntilStep("overlay is visible", () => accountCreation.State.Value == Visibility.Visible); + + AddStep("log back in", () => API.Login("dummy", "password")); + AddUntilStep("overlay is hidden", () => accountCreation.State.Value == Visibility.Hidden); } } } From 2d7f9bf29045e9096ad8cb9dc672a05ad6ab7c97 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 25 Dec 2020 23:34:29 +0900 Subject: [PATCH 561/818] Revert RoomCategory naming change to avoid json deserialization failures --- osu.Game/Online/Rooms/RoomCategory.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Rooms/RoomCategory.cs b/osu.Game/Online/Rooms/RoomCategory.cs index f485e65ba9..bb9f1298d3 100644 --- a/osu.Game/Online/Rooms/RoomCategory.cs +++ b/osu.Game/Online/Rooms/RoomCategory.cs @@ -5,8 +5,9 @@ namespace osu.Game.Online.Rooms { public enum RoomCategory { + // used for osu-web deserialization so names shouldn't be changed. Normal, Spotlight, - Multiplayer, + Realtime, } } From e421b6d34e50642cae45794fdb28e64805c6efd5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 25 Dec 2020 23:36:09 +0900 Subject: [PATCH 562/818] Update some missed variables --- .../Visual/Multiplayer/TestSceneRoomStatus.cs | 2 +- .../Screens/Multi/Multiplayer/Multiplayer.cs | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs index 788255501b..7140050bd5 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs @@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { Name = { Value = "Open" }, Status = { Value = new RoomStatusOpen() }, - Category = { Value = RoomCategory.Multiplayer } + Category = { Value = RoomCategory.Realtime } }) { MatchingFilter = true }, } }; diff --git a/osu.Game/Screens/Multi/Multiplayer/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer/Multiplayer.cs index 97fa4cfa6e..76aa6b9f8f 100644 --- a/osu.Game/Screens/Multi/Multiplayer/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer/Multiplayer.cs @@ -28,37 +28,37 @@ namespace osu.Game.Screens.Multi.Multiplayer protected override void UpdatePollingRate(bool isIdle) { - var playlistsManager = (MultiplayerRoomManager)RoomManager; + var multiplayerRoomManager = (MultiplayerRoomManager)RoomManager; if (!this.IsCurrentScreen()) { - playlistsManager.TimeBetweenListingPolls.Value = 0; - playlistsManager.TimeBetweenSelectionPolls.Value = 0; + multiplayerRoomManager.TimeBetweenListingPolls.Value = 0; + multiplayerRoomManager.TimeBetweenSelectionPolls.Value = 0; } else { switch (CurrentSubScreen) { case LoungeSubScreen _: - playlistsManager.TimeBetweenListingPolls.Value = isIdle ? 120000 : 15000; - playlistsManager.TimeBetweenSelectionPolls.Value = isIdle ? 120000 : 15000; + multiplayerRoomManager.TimeBetweenListingPolls.Value = isIdle ? 120000 : 15000; + multiplayerRoomManager.TimeBetweenSelectionPolls.Value = isIdle ? 120000 : 15000; break; // Don't poll inside the match or anywhere else. default: - playlistsManager.TimeBetweenListingPolls.Value = 0; - playlistsManager.TimeBetweenSelectionPolls.Value = 0; + multiplayerRoomManager.TimeBetweenListingPolls.Value = 0; + multiplayerRoomManager.TimeBetweenSelectionPolls.Value = 0; break; } } - Logger.Log($"Polling adjusted (listing: {playlistsManager.TimeBetweenListingPolls.Value}, selection: {playlistsManager.TimeBetweenSelectionPolls.Value})"); + Logger.Log($"Polling adjusted (listing: {multiplayerRoomManager.TimeBetweenListingPolls.Value}, selection: {multiplayerRoomManager.TimeBetweenSelectionPolls.Value})"); } protected override Room CreateNewRoom() { var room = new Room { Name = { Value = $"{API.LocalUser}'s awesome room" } }; - room.Category.Value = RoomCategory.Multiplayer; + room.Category.Value = RoomCategory.Realtime; return room; } From 0d8fb83d0a0a56359947ac8e52ff9e10a953db66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 25 Dec 2020 15:37:14 +0100 Subject: [PATCH 563/818] Ensure account creation overlay is shown after logout Scheduling the entire API state change callback caused the scheduled hide to fire the first time the user attempted to display the account creation overlay after a logout, because the drawable wasn't present before that (so its scheduler wasn't running). It is not theoretically safe to run `Hide()` unscheduled at its present call site (as the value change callbacks are fired on the background API thread). This could also be fixed by setting `AlwaysPresent = true`, but that's a pretty ugly and unperformant change to make in general. --- osu.Game/Overlays/AccountCreationOverlay.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/AccountCreationOverlay.cs b/osu.Game/Overlays/AccountCreationOverlay.cs index 58ede5502a..3084c7475a 100644 --- a/osu.Game/Overlays/AccountCreationOverlay.cs +++ b/osu.Game/Overlays/AccountCreationOverlay.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Screens; +using osu.Framework.Threading; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Online.API; @@ -93,6 +94,11 @@ namespace osu.Game.Overlays if (welcomeScreen.GetChildScreen() != null) welcomeScreen.MakeCurrent(); + + // there might be a stale scheduled hide from a previous API state change. + // cancel it here so that the overlay is not hidden again after one frame. + scheduledHide?.Cancel(); + scheduledHide = null; } protected override void PopOut() @@ -101,7 +107,9 @@ namespace osu.Game.Overlays this.FadeOut(100); } - private void apiStateChanged(ValueChangedEvent state) => Schedule(() => + private ScheduledDelegate scheduledHide; + + private void apiStateChanged(ValueChangedEvent state) { switch (state.NewValue) { @@ -113,9 +121,10 @@ namespace osu.Game.Overlays break; case APIState.Online: - Hide(); + scheduledHide?.Cancel(); + scheduledHide = Schedule(Hide); break; } - }); + } } } From 8a36eab060264b8b903142000657caf5c7d3056c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 25 Dec 2020 23:42:02 +0900 Subject: [PATCH 564/818] Move missed file from Play namespace --- osu.Game/Screens/Multi/Multiplayer/MultiplayerPlayer.cs | 2 +- osu.Game/Screens/Multi/{Play => Playlists}/PlaylistsPlayer.cs | 2 +- osu.Game/Screens/Multi/Playlists/PlaylistsRoomSubScreen.cs | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) rename osu.Game/Screens/Multi/{Play => Playlists}/PlaylistsPlayer.cs (99%) diff --git a/osu.Game/Screens/Multi/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/Multi/Multiplayer/MultiplayerPlayer.cs index 3255bcc642..32d95aa11f 100644 --- a/osu.Game/Screens/Multi/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer/MultiplayerPlayer.cs @@ -11,7 +11,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; using osu.Game.Scoring; -using osu.Game.Screens.Multi.Play; +using osu.Game.Screens.Multi.Playlists; using osu.Game.Screens.Play; using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Ranking; diff --git a/osu.Game/Screens/Multi/Play/PlaylistsPlayer.cs b/osu.Game/Screens/Multi/Playlists/PlaylistsPlayer.cs similarity index 99% rename from osu.Game/Screens/Multi/Play/PlaylistsPlayer.cs rename to osu.Game/Screens/Multi/Playlists/PlaylistsPlayer.cs index f05f732494..40198d82cd 100644 --- a/osu.Game/Screens/Multi/Play/PlaylistsPlayer.cs +++ b/osu.Game/Screens/Multi/Playlists/PlaylistsPlayer.cs @@ -18,7 +18,7 @@ using osu.Game.Screens.Multi.Ranking; using osu.Game.Screens.Play; using osu.Game.Screens.Ranking; -namespace osu.Game.Screens.Multi.Play +namespace osu.Game.Screens.Multi.Playlists { public class PlaylistsPlayer : Player { diff --git a/osu.Game/Screens/Multi/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/Multi/Playlists/PlaylistsRoomSubScreen.cs index 67242003e6..f6d084b112 100644 --- a/osu.Game/Screens/Multi/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/Multi/Playlists/PlaylistsRoomSubScreen.cs @@ -13,7 +13,6 @@ using osu.Game.Online.Rooms; using osu.Game.Screens.Multi.Components; using osu.Game.Screens.Multi.Match; using osu.Game.Screens.Multi.Match.Components; -using osu.Game.Screens.Multi.Play; using osu.Game.Screens.Multi.Ranking; using osu.Game.Screens.Select; using osu.Game.Users; From 836d1491d0d1576ee444179953eb863bb8c1d740 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 25 Dec 2020 23:45:44 +0900 Subject: [PATCH 565/818] PlaylistsMultiplayer -> Playlists --- .../Visual/Navigation/TestSceneScreenNavigation.cs | 5 ++--- osu.Game.Tests/Visual/Playlists/TestScenePlaylistsScreen.cs | 3 +-- osu.Game/Screens/Menu/MainMenu.cs | 2 +- .../Playlists/{PlaylistsMultiplayer.cs => Playlists.cs} | 2 +- 4 files changed, 5 insertions(+), 7 deletions(-) rename osu.Game/Screens/Multi/Playlists/{PlaylistsMultiplayer.cs => Playlists.cs} (97%) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index 381bbca74f..ac63c89183 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -11,7 +11,6 @@ using osu.Game.Beatmaps; using osu.Game.Overlays; using osu.Game.Overlays.Mods; using osu.Game.Overlays.Toolbar; -using osu.Game.Screens.Multi.Playlists; using osu.Game.Screens.Play; using osu.Game.Screens.Select; using osu.Game.Screens.Select.Options; @@ -108,14 +107,14 @@ namespace osu.Game.Tests.Visual.Navigation [Test] public void TestExitMultiWithEscape() { - PushAndConfirm(() => new PlaylistsMultiplayer()); + PushAndConfirm(() => new Screens.Multi.Playlists.Playlists()); exitViaEscapeAndConfirm(); } [Test] public void TestExitMultiWithBackButton() { - PushAndConfirm(() => new PlaylistsMultiplayer()); + PushAndConfirm(() => new Screens.Multi.Playlists.Playlists()); exitViaBackButtonAndConfirm(); } diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsScreen.cs index 8203ca4845..b780eb5347 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsScreen.cs @@ -4,7 +4,6 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Game.Overlays; -using osu.Game.Screens.Multi.Playlists; namespace osu.Game.Tests.Visual.Playlists { @@ -18,7 +17,7 @@ namespace osu.Game.Tests.Visual.Playlists public TestScenePlaylistsScreen() { - var multi = new PlaylistsMultiplayer(); + var multi = new Screens.Multi.Playlists.Playlists(); AddStep("show", () => LoadScreen(multi)); AddUntilStep("wait for loaded", () => multi.IsLoaded); diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 15ac2237d3..aeb58a75fc 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -106,7 +106,7 @@ namespace osu.Game.Screens.Menu }, OnSolo = onSolo, OnMultiplayer = () => this.Push(new Multiplayer()), - OnPlaylists = () => this.Push(new PlaylistsMultiplayer()), + OnPlaylists = () => this.Push(new Playlists()), OnExit = confirmAndExit, } } diff --git a/osu.Game/Screens/Multi/Playlists/PlaylistsMultiplayer.cs b/osu.Game/Screens/Multi/Playlists/Playlists.cs similarity index 97% rename from osu.Game/Screens/Multi/Playlists/PlaylistsMultiplayer.cs rename to osu.Game/Screens/Multi/Playlists/Playlists.cs index 92b0160247..da6aade942 100644 --- a/osu.Game/Screens/Multi/Playlists/PlaylistsMultiplayer.cs +++ b/osu.Game/Screens/Multi/Playlists/Playlists.cs @@ -11,7 +11,7 @@ using osu.Game.Screens.Multi.Match; namespace osu.Game.Screens.Multi.Playlists { - public class PlaylistsMultiplayer : MultiplayerScreen + public class Playlists : MultiplayerScreen { protected override void UpdatePollingRate(bool isIdle) { From 9de1a67e0389017d5a233fa7b38478f26287b305 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 25 Dec 2020 23:47:32 +0900 Subject: [PATCH 566/818] Move PlaylistsResultsScreen to correct namespace --- .../Visual/Playlists/TestScenePlaylistsResultsScreen.cs | 2 +- osu.Game/Screens/Multi/Multiplayer/MultiplayerResultsScreen.cs | 2 +- osu.Game/Screens/Multi/Playlists/PlaylistsPlayer.cs | 1 - .../Multi/{Ranking => Playlists}/PlaylistsResultsScreen.cs | 2 +- osu.Game/Screens/Multi/Playlists/PlaylistsRoomSubScreen.cs | 1 - 5 files changed, 3 insertions(+), 5 deletions(-) rename osu.Game/Screens/Multi/{Ranking => Playlists}/PlaylistsResultsScreen.cs (99%) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs index b42f2e1d2d..97bb48c7b5 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs @@ -19,7 +19,7 @@ using osu.Game.Online.Rooms; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; -using osu.Game.Screens.Multi.Ranking; +using osu.Game.Screens.Multi.Playlists; using osu.Game.Screens.Ranking; using osu.Game.Tests.Beatmaps; using osu.Game.Users; diff --git a/osu.Game/Screens/Multi/Multiplayer/MultiplayerResultsScreen.cs b/osu.Game/Screens/Multi/Multiplayer/MultiplayerResultsScreen.cs index a01930c5ef..3e39473954 100644 --- a/osu.Game/Screens/Multi/Multiplayer/MultiplayerResultsScreen.cs +++ b/osu.Game/Screens/Multi/Multiplayer/MultiplayerResultsScreen.cs @@ -3,7 +3,7 @@ using osu.Game.Online.Rooms; using osu.Game.Scoring; -using osu.Game.Screens.Multi.Ranking; +using osu.Game.Screens.Multi.Playlists; namespace osu.Game.Screens.Multi.Multiplayer { diff --git a/osu.Game/Screens/Multi/Playlists/PlaylistsPlayer.cs b/osu.Game/Screens/Multi/Playlists/PlaylistsPlayer.cs index 40198d82cd..25a3340ead 100644 --- a/osu.Game/Screens/Multi/Playlists/PlaylistsPlayer.cs +++ b/osu.Game/Screens/Multi/Playlists/PlaylistsPlayer.cs @@ -14,7 +14,6 @@ using osu.Game.Online.API; using osu.Game.Online.Rooms; using osu.Game.Rulesets; using osu.Game.Scoring; -using osu.Game.Screens.Multi.Ranking; using osu.Game.Screens.Play; using osu.Game.Screens.Ranking; diff --git a/osu.Game/Screens/Multi/Ranking/PlaylistsResultsScreen.cs b/osu.Game/Screens/Multi/Playlists/PlaylistsResultsScreen.cs similarity index 99% rename from osu.Game/Screens/Multi/Ranking/PlaylistsResultsScreen.cs rename to osu.Game/Screens/Multi/Playlists/PlaylistsResultsScreen.cs index 7b1ab2c5b7..5d1d3a2724 100644 --- a/osu.Game/Screens/Multi/Ranking/PlaylistsResultsScreen.cs +++ b/osu.Game/Screens/Multi/Playlists/PlaylistsResultsScreen.cs @@ -15,7 +15,7 @@ using osu.Game.Online.Rooms; using osu.Game.Scoring; using osu.Game.Screens.Ranking; -namespace osu.Game.Screens.Multi.Ranking +namespace osu.Game.Screens.Multi.Playlists { public class PlaylistsResultsScreen : ResultsScreen { diff --git a/osu.Game/Screens/Multi/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/Multi/Playlists/PlaylistsRoomSubScreen.cs index f6d084b112..4867268fe1 100644 --- a/osu.Game/Screens/Multi/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/Multi/Playlists/PlaylistsRoomSubScreen.cs @@ -13,7 +13,6 @@ using osu.Game.Online.Rooms; using osu.Game.Screens.Multi.Components; using osu.Game.Screens.Multi.Match; using osu.Game.Screens.Multi.Match.Components; -using osu.Game.Screens.Multi.Ranking; using osu.Game.Screens.Select; using osu.Game.Users; using Footer = osu.Game.Screens.Multi.Match.Components.Footer; From e797e5ce7a73441cc2c65e4fd5909c789b3fc3fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 25 Dec 2020 16:48:03 +0100 Subject: [PATCH 567/818] Rename Multi directory to OnlinePlay --- .../Components/BeatmapDetailAreaPlaylistTabItem.cs | 0 osu.Game/Screens/{Multi => OnlinePlay}/Components/BeatmapTitle.cs | 0 .../Screens/{Multi => OnlinePlay}/Components/BeatmapTypeInfo.cs | 0 .../{Multi => OnlinePlay}/Components/DisableableTabControl.cs | 0 .../Screens/{Multi => OnlinePlay}/Components/DrawableGameType.cs | 0 .../{Multi => OnlinePlay}/Components/ListingPollingComponent.cs | 0 .../{Multi => OnlinePlay}/Components/MatchBeatmapDetailArea.cs | 0 osu.Game/Screens/{Multi => OnlinePlay}/Components/ModeTypeInfo.cs | 0 .../Components/MultiplayerBackgroundSprite.cs | 0 .../Screens/{Multi => OnlinePlay}/Components/OverlinedHeader.cs | 0 .../{Multi => OnlinePlay}/Components/OverlinedPlaylistHeader.cs | 0 .../{Multi => OnlinePlay}/Components/ParticipantCountDisplay.cs | 0 .../{Multi => OnlinePlay}/Components/ParticipantsDisplay.cs | 0 .../Screens/{Multi => OnlinePlay}/Components/ParticipantsList.cs | 0 osu.Game/Screens/{Multi => OnlinePlay}/Components/ReadyButton.cs | 0 osu.Game/Screens/{Multi => OnlinePlay}/Components/RoomManager.cs | 0 .../{Multi => OnlinePlay}/Components/RoomPollingComponent.cs | 0 .../Screens/{Multi => OnlinePlay}/Components/RoomStatusInfo.cs | 0 .../{Multi => OnlinePlay}/Components/SelectionPollingComponent.cs | 0 .../{Multi => OnlinePlay}/Components/StatusColouredContainer.cs | 0 osu.Game/Screens/{Multi => OnlinePlay}/DrawableRoomPlaylist.cs | 0 .../Screens/{Multi => OnlinePlay}/DrawableRoomPlaylistItem.cs | 0 .../{Multi => OnlinePlay}/DrawableRoomPlaylistWithResults.cs | 0 osu.Game/Screens/{Multi => OnlinePlay}/Header.cs | 0 osu.Game/Screens/{Multi => OnlinePlay}/IMultiplayerSubScreen.cs | 0 osu.Game/Screens/{Multi => OnlinePlay}/IRoomManager.cs | 0 .../{Multi => OnlinePlay}/Lounge/Components/DrawableRoom.cs | 0 .../{Multi => OnlinePlay}/Lounge/Components/FilterControl.cs | 0 .../{Multi => OnlinePlay}/Lounge/Components/FilterCriteria.cs | 0 .../{Multi => OnlinePlay}/Lounge/Components/ParticipantInfo.cs | 0 .../Lounge/Components/PlaylistsFilterControl.cs | 0 .../Screens/{Multi => OnlinePlay}/Lounge/Components/RoomInfo.cs | 0 .../{Multi => OnlinePlay}/Lounge/Components/RoomInspector.cs | 0 .../{Multi => OnlinePlay}/Lounge/Components/RoomStatusFilter.cs | 0 .../{Multi => OnlinePlay}/Lounge/Components/RoomsContainer.cs | 0 osu.Game/Screens/{Multi => OnlinePlay}/Lounge/LoungeSubScreen.cs | 0 osu.Game/Screens/{Multi => OnlinePlay}/Match/Components/Footer.cs | 0 .../{Multi => OnlinePlay}/Match/Components/GameTypePicker.cs | 0 osu.Game/Screens/{Multi => OnlinePlay}/Match/Components/Header.cs | 0 .../{Multi => OnlinePlay}/Match/Components/MatchChatDisplay.cs | 0 .../{Multi => OnlinePlay}/Match/Components/MatchLeaderboard.cs | 0 .../Match/Components/MatchLeaderboardScore.cs | 0 .../Match/Components/MatchSettingsOverlay.cs | 0 .../Match/Components/PurpleTriangleButton.cs | 0 .../Match/Components/RoomAvailabilityPicker.cs | 0 osu.Game/Screens/{Multi => OnlinePlay}/Match/RoomSubScreen.cs | 0 .../Multiplayer/CreateMultiplayerMatchButton.cs | 0 .../Multiplayer/Match/BeatmapSelectionControl.cs | 0 .../Multiplayer/Match/MultiplayerMatchFooter.cs | 0 .../Multiplayer/Match/MultiplayerMatchHeader.cs | 0 .../Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs | 0 .../Multiplayer/Match/MultiplayerReadyButton.cs | 0 osu.Game/Screens/{Multi => OnlinePlay}/Multiplayer/Multiplayer.cs | 0 .../{Multi => OnlinePlay}/Multiplayer/MultiplayerFilterControl.cs | 0 .../Multiplayer/MultiplayerLoungeSubScreen.cs | 0 .../Multiplayer/MultiplayerMatchSongSelect.cs | 0 .../Multiplayer/MultiplayerMatchSubScreen.cs | 0 .../{Multi => OnlinePlay}/Multiplayer/MultiplayerPlayer.cs | 0 .../{Multi => OnlinePlay}/Multiplayer/MultiplayerResultsScreen.cs | 0 .../{Multi => OnlinePlay}/Multiplayer/MultiplayerRoomComposite.cs | 0 .../{Multi => OnlinePlay}/Multiplayer/MultiplayerRoomManager.cs | 0 .../Multiplayer/Participants/ParticipantPanel.cs | 0 .../Multiplayer/Participants/ParticipantsList.cs | 0 .../Multiplayer/Participants/ParticipantsListHeader.cs | 0 .../Multiplayer/Participants/StateDisplay.cs | 0 osu.Game/Screens/{Multi => OnlinePlay}/MultiplayerComposite.cs | 0 osu.Game/Screens/{Multi => OnlinePlay}/MultiplayerScreen.cs | 0 osu.Game/Screens/{Multi => OnlinePlay}/MultiplayerSubScreen.cs | 0 .../Screens/{Multi => OnlinePlay}/MultiplayerSubScreenStack.cs | 0 .../{Multi => OnlinePlay}/Playlists/CreatePlaylistsRoomButton.cs | 0 osu.Game/Screens/{Multi => OnlinePlay}/Playlists/Playlists.cs | 0 .../{Multi => OnlinePlay}/Playlists/PlaylistsLoungeSubScreen.cs | 0 .../Playlists/PlaylistsMatchSettingsOverlay.cs | 0 .../Screens/{Multi => OnlinePlay}/Playlists/PlaylistsPlayer.cs | 0 .../{Multi => OnlinePlay}/Playlists/PlaylistsReadyButton.cs | 0 .../{Multi => OnlinePlay}/Playlists/PlaylistsResultsScreen.cs | 0 .../{Multi => OnlinePlay}/Playlists/PlaylistsRoomManager.cs | 0 .../{Multi => OnlinePlay}/Playlists/PlaylistsRoomSubScreen.cs | 0 78 files changed, 0 insertions(+), 0 deletions(-) rename osu.Game/Screens/{Multi => OnlinePlay}/Components/BeatmapDetailAreaPlaylistTabItem.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Components/BeatmapTitle.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Components/BeatmapTypeInfo.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Components/DisableableTabControl.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Components/DrawableGameType.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Components/ListingPollingComponent.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Components/MatchBeatmapDetailArea.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Components/ModeTypeInfo.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Components/MultiplayerBackgroundSprite.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Components/OverlinedHeader.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Components/OverlinedPlaylistHeader.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Components/ParticipantCountDisplay.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Components/ParticipantsDisplay.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Components/ParticipantsList.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Components/ReadyButton.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Components/RoomManager.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Components/RoomPollingComponent.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Components/RoomStatusInfo.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Components/SelectionPollingComponent.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Components/StatusColouredContainer.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/DrawableRoomPlaylist.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/DrawableRoomPlaylistItem.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/DrawableRoomPlaylistWithResults.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Header.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/IMultiplayerSubScreen.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/IRoomManager.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Lounge/Components/DrawableRoom.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Lounge/Components/FilterControl.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Lounge/Components/FilterCriteria.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Lounge/Components/ParticipantInfo.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Lounge/Components/PlaylistsFilterControl.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Lounge/Components/RoomInfo.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Lounge/Components/RoomInspector.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Lounge/Components/RoomStatusFilter.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Lounge/Components/RoomsContainer.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Lounge/LoungeSubScreen.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Match/Components/Footer.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Match/Components/GameTypePicker.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Match/Components/Header.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Match/Components/MatchChatDisplay.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Match/Components/MatchLeaderboard.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Match/Components/MatchLeaderboardScore.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Match/Components/MatchSettingsOverlay.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Match/Components/PurpleTriangleButton.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Match/Components/RoomAvailabilityPicker.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Match/RoomSubScreen.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Multiplayer/CreateMultiplayerMatchButton.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Multiplayer/Match/BeatmapSelectionControl.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Multiplayer/Match/MultiplayerMatchFooter.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Multiplayer/Match/MultiplayerMatchHeader.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Multiplayer/Match/MultiplayerReadyButton.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Multiplayer/Multiplayer.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Multiplayer/MultiplayerFilterControl.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Multiplayer/MultiplayerLoungeSubScreen.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Multiplayer/MultiplayerMatchSongSelect.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Multiplayer/MultiplayerMatchSubScreen.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Multiplayer/MultiplayerPlayer.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Multiplayer/MultiplayerResultsScreen.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Multiplayer/MultiplayerRoomComposite.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Multiplayer/MultiplayerRoomManager.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Multiplayer/Participants/ParticipantPanel.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Multiplayer/Participants/ParticipantsList.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Multiplayer/Participants/ParticipantsListHeader.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Multiplayer/Participants/StateDisplay.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/MultiplayerComposite.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/MultiplayerScreen.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/MultiplayerSubScreen.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/MultiplayerSubScreenStack.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Playlists/CreatePlaylistsRoomButton.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Playlists/Playlists.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Playlists/PlaylistsLoungeSubScreen.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Playlists/PlaylistsMatchSettingsOverlay.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Playlists/PlaylistsPlayer.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Playlists/PlaylistsReadyButton.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Playlists/PlaylistsResultsScreen.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Playlists/PlaylistsRoomManager.cs (100%) rename osu.Game/Screens/{Multi => OnlinePlay}/Playlists/PlaylistsRoomSubScreen.cs (100%) diff --git a/osu.Game/Screens/Multi/Components/BeatmapDetailAreaPlaylistTabItem.cs b/osu.Game/Screens/OnlinePlay/Components/BeatmapDetailAreaPlaylistTabItem.cs similarity index 100% rename from osu.Game/Screens/Multi/Components/BeatmapDetailAreaPlaylistTabItem.cs rename to osu.Game/Screens/OnlinePlay/Components/BeatmapDetailAreaPlaylistTabItem.cs diff --git a/osu.Game/Screens/Multi/Components/BeatmapTitle.cs b/osu.Game/Screens/OnlinePlay/Components/BeatmapTitle.cs similarity index 100% rename from osu.Game/Screens/Multi/Components/BeatmapTitle.cs rename to osu.Game/Screens/OnlinePlay/Components/BeatmapTitle.cs diff --git a/osu.Game/Screens/Multi/Components/BeatmapTypeInfo.cs b/osu.Game/Screens/OnlinePlay/Components/BeatmapTypeInfo.cs similarity index 100% rename from osu.Game/Screens/Multi/Components/BeatmapTypeInfo.cs rename to osu.Game/Screens/OnlinePlay/Components/BeatmapTypeInfo.cs diff --git a/osu.Game/Screens/Multi/Components/DisableableTabControl.cs b/osu.Game/Screens/OnlinePlay/Components/DisableableTabControl.cs similarity index 100% rename from osu.Game/Screens/Multi/Components/DisableableTabControl.cs rename to osu.Game/Screens/OnlinePlay/Components/DisableableTabControl.cs diff --git a/osu.Game/Screens/Multi/Components/DrawableGameType.cs b/osu.Game/Screens/OnlinePlay/Components/DrawableGameType.cs similarity index 100% rename from osu.Game/Screens/Multi/Components/DrawableGameType.cs rename to osu.Game/Screens/OnlinePlay/Components/DrawableGameType.cs diff --git a/osu.Game/Screens/Multi/Components/ListingPollingComponent.cs b/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs similarity index 100% rename from osu.Game/Screens/Multi/Components/ListingPollingComponent.cs rename to osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs diff --git a/osu.Game/Screens/Multi/Components/MatchBeatmapDetailArea.cs b/osu.Game/Screens/OnlinePlay/Components/MatchBeatmapDetailArea.cs similarity index 100% rename from osu.Game/Screens/Multi/Components/MatchBeatmapDetailArea.cs rename to osu.Game/Screens/OnlinePlay/Components/MatchBeatmapDetailArea.cs diff --git a/osu.Game/Screens/Multi/Components/ModeTypeInfo.cs b/osu.Game/Screens/OnlinePlay/Components/ModeTypeInfo.cs similarity index 100% rename from osu.Game/Screens/Multi/Components/ModeTypeInfo.cs rename to osu.Game/Screens/OnlinePlay/Components/ModeTypeInfo.cs diff --git a/osu.Game/Screens/Multi/Components/MultiplayerBackgroundSprite.cs b/osu.Game/Screens/OnlinePlay/Components/MultiplayerBackgroundSprite.cs similarity index 100% rename from osu.Game/Screens/Multi/Components/MultiplayerBackgroundSprite.cs rename to osu.Game/Screens/OnlinePlay/Components/MultiplayerBackgroundSprite.cs diff --git a/osu.Game/Screens/Multi/Components/OverlinedHeader.cs b/osu.Game/Screens/OnlinePlay/Components/OverlinedHeader.cs similarity index 100% rename from osu.Game/Screens/Multi/Components/OverlinedHeader.cs rename to osu.Game/Screens/OnlinePlay/Components/OverlinedHeader.cs diff --git a/osu.Game/Screens/Multi/Components/OverlinedPlaylistHeader.cs b/osu.Game/Screens/OnlinePlay/Components/OverlinedPlaylistHeader.cs similarity index 100% rename from osu.Game/Screens/Multi/Components/OverlinedPlaylistHeader.cs rename to osu.Game/Screens/OnlinePlay/Components/OverlinedPlaylistHeader.cs diff --git a/osu.Game/Screens/Multi/Components/ParticipantCountDisplay.cs b/osu.Game/Screens/OnlinePlay/Components/ParticipantCountDisplay.cs similarity index 100% rename from osu.Game/Screens/Multi/Components/ParticipantCountDisplay.cs rename to osu.Game/Screens/OnlinePlay/Components/ParticipantCountDisplay.cs diff --git a/osu.Game/Screens/Multi/Components/ParticipantsDisplay.cs b/osu.Game/Screens/OnlinePlay/Components/ParticipantsDisplay.cs similarity index 100% rename from osu.Game/Screens/Multi/Components/ParticipantsDisplay.cs rename to osu.Game/Screens/OnlinePlay/Components/ParticipantsDisplay.cs diff --git a/osu.Game/Screens/Multi/Components/ParticipantsList.cs b/osu.Game/Screens/OnlinePlay/Components/ParticipantsList.cs similarity index 100% rename from osu.Game/Screens/Multi/Components/ParticipantsList.cs rename to osu.Game/Screens/OnlinePlay/Components/ParticipantsList.cs diff --git a/osu.Game/Screens/Multi/Components/ReadyButton.cs b/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs similarity index 100% rename from osu.Game/Screens/Multi/Components/ReadyButton.cs rename to osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs diff --git a/osu.Game/Screens/Multi/Components/RoomManager.cs b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs similarity index 100% rename from osu.Game/Screens/Multi/Components/RoomManager.cs rename to osu.Game/Screens/OnlinePlay/Components/RoomManager.cs diff --git a/osu.Game/Screens/Multi/Components/RoomPollingComponent.cs b/osu.Game/Screens/OnlinePlay/Components/RoomPollingComponent.cs similarity index 100% rename from osu.Game/Screens/Multi/Components/RoomPollingComponent.cs rename to osu.Game/Screens/OnlinePlay/Components/RoomPollingComponent.cs diff --git a/osu.Game/Screens/Multi/Components/RoomStatusInfo.cs b/osu.Game/Screens/OnlinePlay/Components/RoomStatusInfo.cs similarity index 100% rename from osu.Game/Screens/Multi/Components/RoomStatusInfo.cs rename to osu.Game/Screens/OnlinePlay/Components/RoomStatusInfo.cs diff --git a/osu.Game/Screens/Multi/Components/SelectionPollingComponent.cs b/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs similarity index 100% rename from osu.Game/Screens/Multi/Components/SelectionPollingComponent.cs rename to osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs diff --git a/osu.Game/Screens/Multi/Components/StatusColouredContainer.cs b/osu.Game/Screens/OnlinePlay/Components/StatusColouredContainer.cs similarity index 100% rename from osu.Game/Screens/Multi/Components/StatusColouredContainer.cs rename to osu.Game/Screens/OnlinePlay/Components/StatusColouredContainer.cs diff --git a/osu.Game/Screens/Multi/DrawableRoomPlaylist.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs similarity index 100% rename from osu.Game/Screens/Multi/DrawableRoomPlaylist.cs rename to osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs diff --git a/osu.Game/Screens/Multi/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs similarity index 100% rename from osu.Game/Screens/Multi/DrawableRoomPlaylistItem.cs rename to osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs diff --git a/osu.Game/Screens/Multi/DrawableRoomPlaylistWithResults.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistWithResults.cs similarity index 100% rename from osu.Game/Screens/Multi/DrawableRoomPlaylistWithResults.cs rename to osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistWithResults.cs diff --git a/osu.Game/Screens/Multi/Header.cs b/osu.Game/Screens/OnlinePlay/Header.cs similarity index 100% rename from osu.Game/Screens/Multi/Header.cs rename to osu.Game/Screens/OnlinePlay/Header.cs diff --git a/osu.Game/Screens/Multi/IMultiplayerSubScreen.cs b/osu.Game/Screens/OnlinePlay/IMultiplayerSubScreen.cs similarity index 100% rename from osu.Game/Screens/Multi/IMultiplayerSubScreen.cs rename to osu.Game/Screens/OnlinePlay/IMultiplayerSubScreen.cs diff --git a/osu.Game/Screens/Multi/IRoomManager.cs b/osu.Game/Screens/OnlinePlay/IRoomManager.cs similarity index 100% rename from osu.Game/Screens/Multi/IRoomManager.cs rename to osu.Game/Screens/OnlinePlay/IRoomManager.cs diff --git a/osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs similarity index 100% rename from osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs rename to osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs diff --git a/osu.Game/Screens/Multi/Lounge/Components/FilterControl.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/FilterControl.cs similarity index 100% rename from osu.Game/Screens/Multi/Lounge/Components/FilterControl.cs rename to osu.Game/Screens/OnlinePlay/Lounge/Components/FilterControl.cs diff --git a/osu.Game/Screens/Multi/Lounge/Components/FilterCriteria.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/FilterCriteria.cs similarity index 100% rename from osu.Game/Screens/Multi/Lounge/Components/FilterCriteria.cs rename to osu.Game/Screens/OnlinePlay/Lounge/Components/FilterCriteria.cs diff --git a/osu.Game/Screens/Multi/Lounge/Components/ParticipantInfo.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/ParticipantInfo.cs similarity index 100% rename from osu.Game/Screens/Multi/Lounge/Components/ParticipantInfo.cs rename to osu.Game/Screens/OnlinePlay/Lounge/Components/ParticipantInfo.cs diff --git a/osu.Game/Screens/Multi/Lounge/Components/PlaylistsFilterControl.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistsFilterControl.cs similarity index 100% rename from osu.Game/Screens/Multi/Lounge/Components/PlaylistsFilterControl.cs rename to osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistsFilterControl.cs diff --git a/osu.Game/Screens/Multi/Lounge/Components/RoomInfo.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomInfo.cs similarity index 100% rename from osu.Game/Screens/Multi/Lounge/Components/RoomInfo.cs rename to osu.Game/Screens/OnlinePlay/Lounge/Components/RoomInfo.cs diff --git a/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomInspector.cs similarity index 100% rename from osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs rename to osu.Game/Screens/OnlinePlay/Lounge/Components/RoomInspector.cs diff --git a/osu.Game/Screens/Multi/Lounge/Components/RoomStatusFilter.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusFilter.cs similarity index 100% rename from osu.Game/Screens/Multi/Lounge/Components/RoomStatusFilter.cs rename to osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusFilter.cs diff --git a/osu.Game/Screens/Multi/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs similarity index 100% rename from osu.Game/Screens/Multi/Lounge/Components/RoomsContainer.cs rename to osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs diff --git a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs similarity index 100% rename from osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs rename to osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs diff --git a/osu.Game/Screens/Multi/Match/Components/Footer.cs b/osu.Game/Screens/OnlinePlay/Match/Components/Footer.cs similarity index 100% rename from osu.Game/Screens/Multi/Match/Components/Footer.cs rename to osu.Game/Screens/OnlinePlay/Match/Components/Footer.cs diff --git a/osu.Game/Screens/Multi/Match/Components/GameTypePicker.cs b/osu.Game/Screens/OnlinePlay/Match/Components/GameTypePicker.cs similarity index 100% rename from osu.Game/Screens/Multi/Match/Components/GameTypePicker.cs rename to osu.Game/Screens/OnlinePlay/Match/Components/GameTypePicker.cs diff --git a/osu.Game/Screens/Multi/Match/Components/Header.cs b/osu.Game/Screens/OnlinePlay/Match/Components/Header.cs similarity index 100% rename from osu.Game/Screens/Multi/Match/Components/Header.cs rename to osu.Game/Screens/OnlinePlay/Match/Components/Header.cs diff --git a/osu.Game/Screens/Multi/Match/Components/MatchChatDisplay.cs b/osu.Game/Screens/OnlinePlay/Match/Components/MatchChatDisplay.cs similarity index 100% rename from osu.Game/Screens/Multi/Match/Components/MatchChatDisplay.cs rename to osu.Game/Screens/OnlinePlay/Match/Components/MatchChatDisplay.cs diff --git a/osu.Game/Screens/Multi/Match/Components/MatchLeaderboard.cs b/osu.Game/Screens/OnlinePlay/Match/Components/MatchLeaderboard.cs similarity index 100% rename from osu.Game/Screens/Multi/Match/Components/MatchLeaderboard.cs rename to osu.Game/Screens/OnlinePlay/Match/Components/MatchLeaderboard.cs diff --git a/osu.Game/Screens/Multi/Match/Components/MatchLeaderboardScore.cs b/osu.Game/Screens/OnlinePlay/Match/Components/MatchLeaderboardScore.cs similarity index 100% rename from osu.Game/Screens/Multi/Match/Components/MatchLeaderboardScore.cs rename to osu.Game/Screens/OnlinePlay/Match/Components/MatchLeaderboardScore.cs diff --git a/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs similarity index 100% rename from osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs rename to osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs diff --git a/osu.Game/Screens/Multi/Match/Components/PurpleTriangleButton.cs b/osu.Game/Screens/OnlinePlay/Match/Components/PurpleTriangleButton.cs similarity index 100% rename from osu.Game/Screens/Multi/Match/Components/PurpleTriangleButton.cs rename to osu.Game/Screens/OnlinePlay/Match/Components/PurpleTriangleButton.cs diff --git a/osu.Game/Screens/Multi/Match/Components/RoomAvailabilityPicker.cs b/osu.Game/Screens/OnlinePlay/Match/Components/RoomAvailabilityPicker.cs similarity index 100% rename from osu.Game/Screens/Multi/Match/Components/RoomAvailabilityPicker.cs rename to osu.Game/Screens/OnlinePlay/Match/Components/RoomAvailabilityPicker.cs diff --git a/osu.Game/Screens/Multi/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs similarity index 100% rename from osu.Game/Screens/Multi/Match/RoomSubScreen.cs rename to osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs diff --git a/osu.Game/Screens/Multi/Multiplayer/CreateMultiplayerMatchButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs similarity index 100% rename from osu.Game/Screens/Multi/Multiplayer/CreateMultiplayerMatchButton.cs rename to osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs diff --git a/osu.Game/Screens/Multi/Multiplayer/Match/BeatmapSelectionControl.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs similarity index 100% rename from osu.Game/Screens/Multi/Multiplayer/Match/BeatmapSelectionControl.cs rename to osu.Game/Screens/OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs diff --git a/osu.Game/Screens/Multi/Multiplayer/Match/MultiplayerMatchFooter.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs similarity index 100% rename from osu.Game/Screens/Multi/Multiplayer/Match/MultiplayerMatchFooter.cs rename to osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs diff --git a/osu.Game/Screens/Multi/Multiplayer/Match/MultiplayerMatchHeader.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchHeader.cs similarity index 100% rename from osu.Game/Screens/Multi/Multiplayer/Match/MultiplayerMatchHeader.cs rename to osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchHeader.cs diff --git a/osu.Game/Screens/Multi/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs similarity index 100% rename from osu.Game/Screens/Multi/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs rename to osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs diff --git a/osu.Game/Screens/Multi/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs similarity index 100% rename from osu.Game/Screens/Multi/Multiplayer/Match/MultiplayerReadyButton.cs rename to osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs diff --git a/osu.Game/Screens/Multi/Multiplayer/Multiplayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs similarity index 100% rename from osu.Game/Screens/Multi/Multiplayer/Multiplayer.cs rename to osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs diff --git a/osu.Game/Screens/Multi/Multiplayer/MultiplayerFilterControl.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerFilterControl.cs similarity index 100% rename from osu.Game/Screens/Multi/Multiplayer/MultiplayerFilterControl.cs rename to osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerFilterControl.cs diff --git a/osu.Game/Screens/Multi/Multiplayer/MultiplayerLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs similarity index 100% rename from osu.Game/Screens/Multi/Multiplayer/MultiplayerLoungeSubScreen.cs rename to osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs diff --git a/osu.Game/Screens/Multi/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs similarity index 100% rename from osu.Game/Screens/Multi/Multiplayer/MultiplayerMatchSongSelect.cs rename to osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs diff --git a/osu.Game/Screens/Multi/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs similarity index 100% rename from osu.Game/Screens/Multi/Multiplayer/MultiplayerMatchSubScreen.cs rename to osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs diff --git a/osu.Game/Screens/Multi/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs similarity index 100% rename from osu.Game/Screens/Multi/Multiplayer/MultiplayerPlayer.cs rename to osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs diff --git a/osu.Game/Screens/Multi/Multiplayer/MultiplayerResultsScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerResultsScreen.cs similarity index 100% rename from osu.Game/Screens/Multi/Multiplayer/MultiplayerResultsScreen.cs rename to osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerResultsScreen.cs diff --git a/osu.Game/Screens/Multi/Multiplayer/MultiplayerRoomComposite.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomComposite.cs similarity index 100% rename from osu.Game/Screens/Multi/Multiplayer/MultiplayerRoomComposite.cs rename to osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomComposite.cs diff --git a/osu.Game/Screens/Multi/Multiplayer/MultiplayerRoomManager.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs similarity index 100% rename from osu.Game/Screens/Multi/Multiplayer/MultiplayerRoomManager.cs rename to osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs diff --git a/osu.Game/Screens/Multi/Multiplayer/Participants/ParticipantPanel.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs similarity index 100% rename from osu.Game/Screens/Multi/Multiplayer/Participants/ParticipantPanel.cs rename to osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs diff --git a/osu.Game/Screens/Multi/Multiplayer/Participants/ParticipantsList.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantsList.cs similarity index 100% rename from osu.Game/Screens/Multi/Multiplayer/Participants/ParticipantsList.cs rename to osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantsList.cs diff --git a/osu.Game/Screens/Multi/Multiplayer/Participants/ParticipantsListHeader.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantsListHeader.cs similarity index 100% rename from osu.Game/Screens/Multi/Multiplayer/Participants/ParticipantsListHeader.cs rename to osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantsListHeader.cs diff --git a/osu.Game/Screens/Multi/Multiplayer/Participants/StateDisplay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/StateDisplay.cs similarity index 100% rename from osu.Game/Screens/Multi/Multiplayer/Participants/StateDisplay.cs rename to osu.Game/Screens/OnlinePlay/Multiplayer/Participants/StateDisplay.cs diff --git a/osu.Game/Screens/Multi/MultiplayerComposite.cs b/osu.Game/Screens/OnlinePlay/MultiplayerComposite.cs similarity index 100% rename from osu.Game/Screens/Multi/MultiplayerComposite.cs rename to osu.Game/Screens/OnlinePlay/MultiplayerComposite.cs diff --git a/osu.Game/Screens/Multi/MultiplayerScreen.cs b/osu.Game/Screens/OnlinePlay/MultiplayerScreen.cs similarity index 100% rename from osu.Game/Screens/Multi/MultiplayerScreen.cs rename to osu.Game/Screens/OnlinePlay/MultiplayerScreen.cs diff --git a/osu.Game/Screens/Multi/MultiplayerSubScreen.cs b/osu.Game/Screens/OnlinePlay/MultiplayerSubScreen.cs similarity index 100% rename from osu.Game/Screens/Multi/MultiplayerSubScreen.cs rename to osu.Game/Screens/OnlinePlay/MultiplayerSubScreen.cs diff --git a/osu.Game/Screens/Multi/MultiplayerSubScreenStack.cs b/osu.Game/Screens/OnlinePlay/MultiplayerSubScreenStack.cs similarity index 100% rename from osu.Game/Screens/Multi/MultiplayerSubScreenStack.cs rename to osu.Game/Screens/OnlinePlay/MultiplayerSubScreenStack.cs diff --git a/osu.Game/Screens/Multi/Playlists/CreatePlaylistsRoomButton.cs b/osu.Game/Screens/OnlinePlay/Playlists/CreatePlaylistsRoomButton.cs similarity index 100% rename from osu.Game/Screens/Multi/Playlists/CreatePlaylistsRoomButton.cs rename to osu.Game/Screens/OnlinePlay/Playlists/CreatePlaylistsRoomButton.cs diff --git a/osu.Game/Screens/Multi/Playlists/Playlists.cs b/osu.Game/Screens/OnlinePlay/Playlists/Playlists.cs similarity index 100% rename from osu.Game/Screens/Multi/Playlists/Playlists.cs rename to osu.Game/Screens/OnlinePlay/Playlists/Playlists.cs diff --git a/osu.Game/Screens/Multi/Playlists/PlaylistsLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs similarity index 100% rename from osu.Game/Screens/Multi/Playlists/PlaylistsLoungeSubScreen.cs rename to osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs diff --git a/osu.Game/Screens/Multi/Playlists/PlaylistsMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs similarity index 100% rename from osu.Game/Screens/Multi/Playlists/PlaylistsMatchSettingsOverlay.cs rename to osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs diff --git a/osu.Game/Screens/Multi/Playlists/PlaylistsPlayer.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs similarity index 100% rename from osu.Game/Screens/Multi/Playlists/PlaylistsPlayer.cs rename to osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs diff --git a/osu.Game/Screens/Multi/Playlists/PlaylistsReadyButton.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsReadyButton.cs similarity index 100% rename from osu.Game/Screens/Multi/Playlists/PlaylistsReadyButton.cs rename to osu.Game/Screens/OnlinePlay/Playlists/PlaylistsReadyButton.cs diff --git a/osu.Game/Screens/Multi/Playlists/PlaylistsResultsScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsResultsScreen.cs similarity index 100% rename from osu.Game/Screens/Multi/Playlists/PlaylistsResultsScreen.cs rename to osu.Game/Screens/OnlinePlay/Playlists/PlaylistsResultsScreen.cs diff --git a/osu.Game/Screens/Multi/Playlists/PlaylistsRoomManager.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomManager.cs similarity index 100% rename from osu.Game/Screens/Multi/Playlists/PlaylistsRoomManager.cs rename to osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomManager.cs diff --git a/osu.Game/Screens/Multi/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs similarity index 100% rename from osu.Game/Screens/Multi/Playlists/PlaylistsRoomSubScreen.cs rename to osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs From 83fb7c7a1ae3a0aac1e42ec8160c90d1aef5009e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 25 Dec 2020 16:50:00 +0100 Subject: [PATCH 568/818] Re-namespace all files in OnlinePlay directory --- .../Visual/Multiplayer/RoomManagerTestScene.cs | 2 +- .../Visual/Multiplayer/TestRoomManager.cs | 2 +- .../Multiplayer/TestSceneDrawableRoomPlaylist.cs | 2 +- .../Visual/Multiplayer/TestSceneLoungeRoomInfo.cs | 2 +- .../Multiplayer/TestSceneLoungeRoomsContainer.cs | 2 +- .../Multiplayer/TestSceneMatchBeatmapDetailArea.cs | 2 +- .../Visual/Multiplayer/TestSceneMatchHeader.cs | 2 +- .../Multiplayer/TestSceneMatchLeaderboard.cs | 2 +- .../Visual/Multiplayer/TestSceneMatchSongSelect.cs | 2 +- .../Visual/Multiplayer/TestSceneMultiHeader.cs | 2 +- .../Visual/Multiplayer/TestSceneMultiplayer.cs | 4 ++-- .../TestSceneMultiplayerMatchSubScreen.cs | 4 ++-- .../TestSceneMultiplayerParticipantsList.cs | 2 +- .../Multiplayer/TestSceneMultiplayerReadyButton.cs | 2 +- .../Visual/Multiplayer/TestSceneRoomStatus.cs | 2 +- .../Visual/Navigation/TestSceneScreenNavigation.cs | 4 ++-- .../Playlists/TestScenePlaylistsFilterControl.cs | 2 +- .../Playlists/TestScenePlaylistsLoungeSubScreen.cs | 6 +++--- .../TestScenePlaylistsMatchSettingsOverlay.cs | 4 ++-- .../TestScenePlaylistsParticipantsList.cs | 2 +- .../Playlists/TestScenePlaylistsResultsScreen.cs | 2 +- .../Playlists/TestScenePlaylistsRoomSubScreen.cs | 6 +++--- .../Visual/Playlists/TestScenePlaylistsScreen.cs | 2 +- osu.Game/Online/Rooms/GetRoomsRequest.cs | 2 +- .../Overlays/Dashboard/CurrentlyPlayingDisplay.cs | 2 +- osu.Game/Screens/Menu/MainMenu.cs | 4 ++-- .../Components/BeatmapDetailAreaPlaylistTabItem.cs | 2 +- .../Screens/OnlinePlay/Components/BeatmapTitle.cs | 2 +- .../OnlinePlay/Components/BeatmapTypeInfo.cs | 2 +- .../OnlinePlay/Components/DisableableTabControl.cs | 2 +- .../OnlinePlay/Components/DrawableGameType.cs | 2 +- .../Components/ListingPollingComponent.cs | 4 ++-- .../Components/MatchBeatmapDetailArea.cs | 2 +- .../Screens/OnlinePlay/Components/ModeTypeInfo.cs | 2 +- .../Components/MultiplayerBackgroundSprite.cs | 2 +- .../OnlinePlay/Components/OverlinedHeader.cs | 2 +- .../Components/OverlinedPlaylistHeader.cs | 2 +- .../Components/ParticipantCountDisplay.cs | 2 +- .../OnlinePlay/Components/ParticipantsDisplay.cs | 2 +- .../OnlinePlay/Components/ParticipantsList.cs | 2 +- .../Screens/OnlinePlay/Components/ReadyButton.cs | 2 +- .../Screens/OnlinePlay/Components/RoomManager.cs | 2 +- .../OnlinePlay/Components/RoomPollingComponent.cs | 2 +- .../OnlinePlay/Components/RoomStatusInfo.cs | 2 +- .../Components/SelectionPollingComponent.cs | 2 +- .../Components/StatusColouredContainer.cs | 2 +- .../Screens/OnlinePlay/DrawableRoomPlaylist.cs | 2 +- .../Screens/OnlinePlay/DrawableRoomPlaylistItem.cs | 2 +- .../OnlinePlay/DrawableRoomPlaylistWithResults.cs | 2 +- osu.Game/Screens/OnlinePlay/Header.cs | 4 ++-- .../Screens/OnlinePlay/IMultiplayerSubScreen.cs | 2 +- osu.Game/Screens/OnlinePlay/IRoomManager.cs | 2 +- .../OnlinePlay/Lounge/Components/DrawableRoom.cs | 10 +++++----- .../OnlinePlay/Lounge/Components/FilterControl.cs | 2 +- .../OnlinePlay/Lounge/Components/FilterCriteria.cs | 2 +- .../Lounge/Components/ParticipantInfo.cs | 2 +- .../Lounge/Components/PlaylistsFilterControl.cs | 2 +- .../OnlinePlay/Lounge/Components/RoomInfo.cs | 4 ++-- .../OnlinePlay/Lounge/Components/RoomInspector.cs | 4 ++-- .../Lounge/Components/RoomStatusFilter.cs | 2 +- .../OnlinePlay/Lounge/Components/RoomsContainer.cs | 6 +++--- .../Screens/OnlinePlay/Lounge/LoungeSubScreen.cs | 6 +++--- .../Screens/OnlinePlay/Match/Components/Footer.cs | 4 ++-- .../OnlinePlay/Match/Components/GameTypePicker.cs | 4 ++-- .../Screens/OnlinePlay/Match/Components/Header.cs | 2 +- .../Match/Components/MatchChatDisplay.cs | 2 +- .../Match/Components/MatchLeaderboard.cs | 2 +- .../Match/Components/MatchLeaderboardScore.cs | 2 +- .../Match/Components/MatchSettingsOverlay.cs | 2 +- .../Match/Components/PurpleTriangleButton.cs | 2 +- .../Match/Components/RoomAvailabilityPicker.cs | 4 ++-- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 2 +- .../Multiplayer/CreateMultiplayerMatchButton.cs | 4 ++-- .../Multiplayer/Match/BeatmapSelectionControl.cs | 4 ++-- .../Multiplayer/Match/MultiplayerMatchFooter.cs | 2 +- .../Multiplayer/Match/MultiplayerMatchHeader.cs | 4 ++-- .../Match/MultiplayerMatchSettingsOverlay.cs | 4 ++-- .../Multiplayer/Match/MultiplayerReadyButton.cs | 4 ++-- .../Screens/OnlinePlay/Multiplayer/Multiplayer.cs | 6 +++--- .../Multiplayer/MultiplayerFilterControl.cs | 4 ++-- .../Multiplayer/MultiplayerLoungeSubScreen.cs | 8 ++++---- .../Multiplayer/MultiplayerMatchSongSelect.cs | 2 +- .../Multiplayer/MultiplayerMatchSubScreen.cs | 14 +++++++------- .../OnlinePlay/Multiplayer/MultiplayerPlayer.cs | 4 ++-- .../Multiplayer/MultiplayerResultsScreen.cs | 4 ++-- .../Multiplayer/MultiplayerRoomComposite.cs | 2 +- .../Multiplayer/MultiplayerRoomManager.cs | 4 ++-- .../Multiplayer/Participants/ParticipantPanel.cs | 2 +- .../Multiplayer/Participants/ParticipantsList.cs | 2 +- .../Participants/ParticipantsListHeader.cs | 4 ++-- .../Multiplayer/Participants/StateDisplay.cs | 2 +- .../Screens/OnlinePlay/MultiplayerComposite.cs | 2 +- osu.Game/Screens/OnlinePlay/MultiplayerScreen.cs | 10 +++++----- .../Screens/OnlinePlay/MultiplayerSubScreen.cs | 2 +- .../OnlinePlay/MultiplayerSubScreenStack.cs | 2 +- .../Playlists/CreatePlaylistsRoomButton.cs | 4 ++-- osu.Game/Screens/OnlinePlay/Playlists/Playlists.cs | 8 ++++---- .../Playlists/PlaylistsLoungeSubScreen.cs | 8 ++++---- .../Playlists/PlaylistsMatchSettingsOverlay.cs | 4 ++-- .../OnlinePlay/Playlists/PlaylistsPlayer.cs | 2 +- .../OnlinePlay/Playlists/PlaylistsReadyButton.cs | 4 ++-- .../OnlinePlay/Playlists/PlaylistsResultsScreen.cs | 2 +- .../OnlinePlay/Playlists/PlaylistsRoomManager.cs | 4 ++-- .../OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs | 10 +++++----- osu.Game/Screens/Play/Spectator.cs | 2 +- osu.Game/Screens/Select/MatchSongSelect.cs | 4 ++-- .../Visual/Multiplayer/MultiplayerTestScene.cs | 4 ++-- .../Multiplayer/TestMultiplayerRoomContainer.cs | 4 ++-- .../Multiplayer/TestMultiplayerRoomManager.cs | 4 ++-- 109 files changed, 176 insertions(+), 176 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/RoomManagerTestScene.cs b/osu.Game.Tests/Visual/Multiplayer/RoomManagerTestScene.cs index a2c496a504..c665a57452 100644 --- a/osu.Game.Tests/Visual/Multiplayer/RoomManagerTestScene.cs +++ b/osu.Game.Tests/Visual/Multiplayer/RoomManagerTestScene.cs @@ -6,7 +6,7 @@ using osu.Framework.Allocation; using osu.Game.Beatmaps; using osu.Game.Online.Rooms; using osu.Game.Rulesets; -using osu.Game.Screens.Multi; +using osu.Game.Screens.OnlinePlay; using osu.Game.Users; namespace osu.Game.Tests.Visual.Multiplayer diff --git a/osu.Game.Tests/Visual/Multiplayer/TestRoomManager.cs b/osu.Game.Tests/Visual/Multiplayer/TestRoomManager.cs index 7d9d4a6542..1785c99784 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestRoomManager.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestRoomManager.cs @@ -4,7 +4,7 @@ using System; using osu.Framework.Bindables; using osu.Game.Online.Rooms; -using osu.Game.Screens.Multi; +using osu.Game.Screens.OnlinePlay; namespace osu.Game.Tests.Visual.Multiplayer { diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs index 722e1a50ef..65c0cfd328 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs @@ -18,7 +18,7 @@ using osu.Game.Overlays; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; -using osu.Game.Screens.Multi; +using osu.Game.Screens.OnlinePlay; using osu.Game.Tests.Beatmaps; using osuTK; using osuTK.Input; diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomInfo.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomInfo.cs index f58e1114b8..9f24347ae9 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomInfo.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomInfo.cs @@ -5,7 +5,7 @@ using System; using NUnit.Framework; using osu.Framework.Graphics; using osu.Game.Online.Rooms.RoomStatuses; -using osu.Game.Screens.Multi.Lounge.Components; +using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Users; namespace osu.Game.Tests.Visual.Multiplayer diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs index 9b6a6de7c2..279dcfa584 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs @@ -9,7 +9,7 @@ using osu.Game.Graphics; using osu.Game.Online.Rooms; using osu.Game.Rulesets.Catch; using osu.Game.Rulesets.Osu; -using osu.Game.Screens.Multi.Lounge.Components; +using osu.Game.Screens.OnlinePlay.Lounge.Components; using osuTK.Graphics; using osuTK.Input; diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.cs index 571330f50e..9ad9f2c883 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.cs @@ -9,7 +9,7 @@ using osu.Game.Online.Rooms; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; -using osu.Game.Screens.Multi.Components; +using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Tests.Beatmaps; using osuTK; diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchHeader.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchHeader.cs index 61968dce46..7cdc6b1a7d 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchHeader.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchHeader.cs @@ -6,7 +6,7 @@ using osu.Game.Beatmaps; using osu.Game.Online.Rooms; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; -using osu.Game.Screens.Multi.Match.Components; +using osu.Game.Screens.OnlinePlay.Match.Components; using osu.Game.Users; namespace osu.Game.Tests.Visual.Multiplayer diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs index 71b8ad05ee..64eaf0556b 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs @@ -7,7 +7,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Online.API; -using osu.Game.Screens.Multi.Match.Components; +using osu.Game.Screens.OnlinePlay.Match.Components; using osu.Game.Users; using osuTK; diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSongSelect.cs index 157597e800..e0fd7d9874 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSongSelect.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSongSelect.cs @@ -18,7 +18,7 @@ using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; -using osu.Game.Screens.Multi.Components; +using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.Select; namespace osu.Game.Tests.Visual.Multiplayer diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiHeader.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiHeader.cs index 0ccd882d95..089de223fc 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiHeader.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiHeader.cs @@ -5,7 +5,7 @@ using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Screens; using osu.Game.Screens; -using osu.Game.Screens.Multi; +using osu.Game.Screens.OnlinePlay; namespace osu.Game.Tests.Visual.Multiplayer { diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 5c07b9d0ea..2e39471dc0 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -2,7 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; -using osu.Game.Screens.Multi.Components; +using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Users; namespace osu.Game.Tests.Visual.Multiplayer @@ -39,7 +39,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("room has 1 user", () => Client.Room?.Users.Count == 1); } - private class TestMultiplayer : Screens.Multi.Multiplayer.Multiplayer + private class TestMultiplayer : Screens.OnlinePlay.Multiplayer.Multiplayer { protected override RoomManager CreateRoomManager() => new TestMultiplayerRoomManager(); } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs index 6dc26dae57..8869718fd1 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs @@ -7,8 +7,8 @@ using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Online.Rooms; using osu.Game.Rulesets.Osu; -using osu.Game.Screens.Multi.Multiplayer; -using osu.Game.Screens.Multi.Multiplayer.Match; +using osu.Game.Screens.OnlinePlay.Multiplayer; +using osu.Game.Screens.OnlinePlay.Multiplayer.Match; using osu.Game.Tests.Beatmaps; using osuTK.Input; diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs index ee2fa4ef5a..9181170bee 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs @@ -8,7 +8,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Online.Multiplayer; -using osu.Game.Screens.Multi.Multiplayer.Participants; +using osu.Game.Screens.OnlinePlay.Multiplayer.Participants; using osu.Game.Users; using osuTK; diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs index c8ebdb1b76..6b11613f1c 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs @@ -12,7 +12,7 @@ using osu.Game.Beatmaps; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; using osu.Game.Rulesets; -using osu.Game.Screens.Multi.Multiplayer.Match; +using osu.Game.Screens.OnlinePlay.Multiplayer.Match; using osu.Game.Tests.Resources; using osu.Game.Users; using osuTK; diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs index 7140050bd5..cec40635f3 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs @@ -6,7 +6,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Online.Rooms; using osu.Game.Online.Rooms.RoomStatuses; -using osu.Game.Screens.Multi.Lounge.Components; +using osu.Game.Screens.OnlinePlay.Lounge.Components; namespace osu.Game.Tests.Visual.Multiplayer { diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index ac63c89183..8480e6eaaa 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -107,14 +107,14 @@ namespace osu.Game.Tests.Visual.Navigation [Test] public void TestExitMultiWithEscape() { - PushAndConfirm(() => new Screens.Multi.Playlists.Playlists()); + PushAndConfirm(() => new Screens.OnlinePlay.Playlists.Playlists()); exitViaEscapeAndConfirm(); } [Test] public void TestExitMultiWithBackButton() { - PushAndConfirm(() => new Screens.Multi.Playlists.Playlists()); + PushAndConfirm(() => new Screens.OnlinePlay.Playlists.Playlists()); exitViaBackButtonAndConfirm(); } diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsFilterControl.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsFilterControl.cs index 66992b27a2..40e191dd7e 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsFilterControl.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsFilterControl.cs @@ -2,7 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; -using osu.Game.Screens.Multi.Lounge.Components; +using osu.Game.Screens.OnlinePlay.Lounge.Components; namespace osu.Game.Tests.Visual.Playlists { diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs index 04555857f5..008c862cc3 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs @@ -8,9 +8,9 @@ using osu.Framework.Graphics; using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Graphics.Containers; -using osu.Game.Screens.Multi.Lounge; -using osu.Game.Screens.Multi.Lounge.Components; -using osu.Game.Screens.Multi.Playlists; +using osu.Game.Screens.OnlinePlay.Lounge; +using osu.Game.Screens.OnlinePlay.Lounge.Components; +using osu.Game.Screens.OnlinePlay.Playlists; using osu.Game.Tests.Visual.Multiplayer; namespace osu.Game.Tests.Visual.Playlists diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs index 4e75619f13..44a79b6598 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs @@ -10,8 +10,8 @@ using osu.Framework.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Rooms; -using osu.Game.Screens.Multi; -using osu.Game.Screens.Multi.Playlists; +using osu.Game.Screens.OnlinePlay; +using osu.Game.Screens.OnlinePlay.Playlists; namespace osu.Game.Tests.Visual.Playlists { diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsParticipantsList.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsParticipantsList.cs index 5a0f196df9..8dd81e02e2 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsParticipantsList.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsParticipantsList.cs @@ -3,7 +3,7 @@ using NUnit.Framework; using osu.Framework.Graphics; -using osu.Game.Screens.Multi.Components; +using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Users; namespace osu.Game.Tests.Visual.Playlists diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs index 97bb48c7b5..cdcded8f61 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs @@ -19,7 +19,7 @@ using osu.Game.Online.Rooms; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; -using osu.Game.Screens.Multi.Playlists; +using osu.Game.Screens.OnlinePlay.Playlists; using osu.Game.Screens.Ranking; using osu.Game.Tests.Beatmaps; using osu.Game.Users; diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs index 96ff93a145..a4c87d3ace 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs @@ -15,9 +15,9 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Online.Rooms; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; -using osu.Game.Screens.Multi; -using osu.Game.Screens.Multi.Match.Components; -using osu.Game.Screens.Multi.Playlists; +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; using osuTK.Input; diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsScreen.cs index b780eb5347..e52f823f0b 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsScreen.cs @@ -17,7 +17,7 @@ namespace osu.Game.Tests.Visual.Playlists public TestScenePlaylistsScreen() { - var multi = new Screens.Multi.Playlists.Playlists(); + var multi = new Screens.OnlinePlay.Playlists.Playlists(); AddStep("show", () => LoadScreen(multi)); AddUntilStep("wait for loaded", () => multi.IsLoaded); diff --git a/osu.Game/Online/Rooms/GetRoomsRequest.cs b/osu.Game/Online/Rooms/GetRoomsRequest.cs index 5084b8627f..e45365797a 100644 --- a/osu.Game/Online/Rooms/GetRoomsRequest.cs +++ b/osu.Game/Online/Rooms/GetRoomsRequest.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Humanizer; using osu.Framework.IO.Network; using osu.Game.Online.API; -using osu.Game.Screens.Multi.Lounge.Components; +using osu.Game.Screens.OnlinePlay.Lounge.Components; namespace osu.Game.Online.Rooms { diff --git a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs index d39a81f5e8..c89699f2ee 100644 --- a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs +++ b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs @@ -11,7 +11,7 @@ using osu.Framework.Screens; using osu.Game.Database; using osu.Game.Online.API; using osu.Game.Online.Spectator; -using osu.Game.Screens.Multi.Match.Components; +using osu.Game.Screens.OnlinePlay.Match.Components; using osu.Game.Screens.Play; using osu.Game.Users; using osuTK; diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index aeb58a75fc..9d5720ff34 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -17,8 +17,8 @@ using osu.Game.Online.API; using osu.Game.Overlays; using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Edit; -using osu.Game.Screens.Multi.Multiplayer; -using osu.Game.Screens.Multi.Playlists; +using osu.Game.Screens.OnlinePlay.Multiplayer; +using osu.Game.Screens.OnlinePlay.Playlists; using osu.Game.Screens.Select; namespace osu.Game.Screens.Menu diff --git a/osu.Game/Screens/OnlinePlay/Components/BeatmapDetailAreaPlaylistTabItem.cs b/osu.Game/Screens/OnlinePlay/Components/BeatmapDetailAreaPlaylistTabItem.cs index 3f2ab28f1a..fb927411e6 100644 --- a/osu.Game/Screens/OnlinePlay/Components/BeatmapDetailAreaPlaylistTabItem.cs +++ b/osu.Game/Screens/OnlinePlay/Components/BeatmapDetailAreaPlaylistTabItem.cs @@ -3,7 +3,7 @@ using osu.Game.Screens.Select; -namespace osu.Game.Screens.Multi.Components +namespace osu.Game.Screens.OnlinePlay.Components { public class BeatmapDetailAreaPlaylistTabItem : BeatmapDetailAreaTabItem { diff --git a/osu.Game/Screens/OnlinePlay/Components/BeatmapTitle.cs b/osu.Game/Screens/OnlinePlay/Components/BeatmapTitle.cs index 9e7a59d7d2..bc355d18a9 100644 --- a/osu.Game/Screens/OnlinePlay/Components/BeatmapTitle.cs +++ b/osu.Game/Screens/OnlinePlay/Components/BeatmapTitle.cs @@ -10,7 +10,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Online.Chat; -namespace osu.Game.Screens.Multi.Components +namespace osu.Game.Screens.OnlinePlay.Components { public class BeatmapTitle : MultiplayerComposite { diff --git a/osu.Game/Screens/OnlinePlay/Components/BeatmapTypeInfo.cs b/osu.Game/Screens/OnlinePlay/Components/BeatmapTypeInfo.cs index ce3b612262..434d7b75ed 100644 --- a/osu.Game/Screens/OnlinePlay/Components/BeatmapTypeInfo.cs +++ b/osu.Game/Screens/OnlinePlay/Components/BeatmapTypeInfo.cs @@ -9,7 +9,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osuTK; -namespace osu.Game.Screens.Multi.Components +namespace osu.Game.Screens.OnlinePlay.Components { public class BeatmapTypeInfo : MultiplayerComposite { diff --git a/osu.Game/Screens/OnlinePlay/Components/DisableableTabControl.cs b/osu.Game/Screens/OnlinePlay/Components/DisableableTabControl.cs index 27b5aec4d3..bbc407e926 100644 --- a/osu.Game/Screens/OnlinePlay/Components/DisableableTabControl.cs +++ b/osu.Game/Screens/OnlinePlay/Components/DisableableTabControl.cs @@ -5,7 +5,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; -namespace osu.Game.Screens.Multi.Components +namespace osu.Game.Screens.OnlinePlay.Components { public abstract class DisableableTabControl : TabControl { diff --git a/osu.Game/Screens/OnlinePlay/Components/DrawableGameType.cs b/osu.Game/Screens/OnlinePlay/Components/DrawableGameType.cs index 38af6d065e..c4dc2a2b8f 100644 --- a/osu.Game/Screens/OnlinePlay/Components/DrawableGameType.cs +++ b/osu.Game/Screens/OnlinePlay/Components/DrawableGameType.cs @@ -10,7 +10,7 @@ using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Online.Rooms; -namespace osu.Game.Screens.Multi.Components +namespace osu.Game.Screens.OnlinePlay.Components { public class DrawableGameType : CircularContainer, IHasTooltip { diff --git a/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs b/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs index edce5400d1..e50784fcbe 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs @@ -5,9 +5,9 @@ using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Online.Rooms; -using osu.Game.Screens.Multi.Lounge.Components; +using osu.Game.Screens.OnlinePlay.Lounge.Components; -namespace osu.Game.Screens.Multi.Components +namespace osu.Game.Screens.OnlinePlay.Components { /// /// A that polls for the lounge listing. diff --git a/osu.Game/Screens/OnlinePlay/Components/MatchBeatmapDetailArea.cs b/osu.Game/Screens/OnlinePlay/Components/MatchBeatmapDetailArea.cs index 6997840c92..b013cbafd8 100644 --- a/osu.Game/Screens/OnlinePlay/Components/MatchBeatmapDetailArea.cs +++ b/osu.Game/Screens/OnlinePlay/Components/MatchBeatmapDetailArea.cs @@ -12,7 +12,7 @@ using osu.Game.Online.Rooms; using osu.Game.Screens.Select; using osuTK; -namespace osu.Game.Screens.Multi.Components +namespace osu.Game.Screens.OnlinePlay.Components { public class MatchBeatmapDetailArea : BeatmapDetailArea { diff --git a/osu.Game/Screens/OnlinePlay/Components/ModeTypeInfo.cs b/osu.Game/Screens/OnlinePlay/Components/ModeTypeInfo.cs index f07bd8c3b2..719afcdd33 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ModeTypeInfo.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ModeTypeInfo.cs @@ -8,7 +8,7 @@ using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps.Drawables; using osuTK; -namespace osu.Game.Screens.Multi.Components +namespace osu.Game.Screens.OnlinePlay.Components { public class ModeTypeInfo : MultiplayerComposite { diff --git a/osu.Game/Screens/OnlinePlay/Components/MultiplayerBackgroundSprite.cs b/osu.Game/Screens/OnlinePlay/Components/MultiplayerBackgroundSprite.cs index 2240e55e2f..45e2c553e7 100644 --- a/osu.Game/Screens/OnlinePlay/Components/MultiplayerBackgroundSprite.cs +++ b/osu.Game/Screens/OnlinePlay/Components/MultiplayerBackgroundSprite.cs @@ -6,7 +6,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Beatmaps.Drawables; -namespace osu.Game.Screens.Multi.Components +namespace osu.Game.Screens.OnlinePlay.Components { public class MultiplayerBackgroundSprite : MultiplayerComposite { diff --git a/osu.Game/Screens/OnlinePlay/Components/OverlinedHeader.cs b/osu.Game/Screens/OnlinePlay/Components/OverlinedHeader.cs index 7ec20c8cae..c78dfef592 100644 --- a/osu.Game/Screens/OnlinePlay/Components/OverlinedHeader.cs +++ b/osu.Game/Screens/OnlinePlay/Components/OverlinedHeader.cs @@ -10,7 +10,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osuTK; -namespace osu.Game.Screens.Multi.Components +namespace osu.Game.Screens.OnlinePlay.Components { /// /// A header used in the multiplayer interface which shows text / details beneath a line. diff --git a/osu.Game/Screens/OnlinePlay/Components/OverlinedPlaylistHeader.cs b/osu.Game/Screens/OnlinePlay/Components/OverlinedPlaylistHeader.cs index ebebe8b660..45b822d20a 100644 --- a/osu.Game/Screens/OnlinePlay/Components/OverlinedPlaylistHeader.cs +++ b/osu.Game/Screens/OnlinePlay/Components/OverlinedPlaylistHeader.cs @@ -3,7 +3,7 @@ using osu.Game.Online.Rooms; -namespace osu.Game.Screens.Multi.Components +namespace osu.Game.Screens.OnlinePlay.Components { public class OverlinedPlaylistHeader : OverlinedHeader { diff --git a/osu.Game/Screens/OnlinePlay/Components/ParticipantCountDisplay.cs b/osu.Game/Screens/OnlinePlay/Components/ParticipantCountDisplay.cs index 498eeb09b3..357974adfc 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ParticipantCountDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ParticipantCountDisplay.cs @@ -7,7 +7,7 @@ using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -namespace osu.Game.Screens.Multi.Components +namespace osu.Game.Screens.OnlinePlay.Components { public class ParticipantCountDisplay : MultiplayerComposite { diff --git a/osu.Game/Screens/OnlinePlay/Components/ParticipantsDisplay.cs b/osu.Game/Screens/OnlinePlay/Components/ParticipantsDisplay.cs index 6ea4283379..5184f873f3 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ParticipantsDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ParticipantsDisplay.cs @@ -6,7 +6,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Graphics.Containers; -namespace osu.Game.Screens.Multi.Components +namespace osu.Game.Screens.OnlinePlay.Components { public class ParticipantsDisplay : MultiplayerComposite { diff --git a/osu.Game/Screens/OnlinePlay/Components/ParticipantsList.cs b/osu.Game/Screens/OnlinePlay/Components/ParticipantsList.cs index 7978b4eaab..b5019b4cdc 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ParticipantsList.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ParticipantsList.cs @@ -12,7 +12,7 @@ using osu.Game.Users; using osu.Game.Users.Drawables; using osuTK; -namespace osu.Game.Screens.Multi.Components +namespace osu.Game.Screens.OnlinePlay.Components { public class ParticipantsList : MultiplayerComposite { diff --git a/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs b/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs index 68df30965d..08f89d8ed8 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs @@ -11,7 +11,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Rooms; -namespace osu.Game.Screens.Multi.Components +namespace osu.Game.Screens.OnlinePlay.Components { public abstract class ReadyButton : TriangleButton { diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs index 7e0c8c4ec5..2ed259e2b8 100644 --- a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs @@ -15,7 +15,7 @@ using osu.Game.Online.API; using osu.Game.Online.Rooms; using osu.Game.Rulesets; -namespace osu.Game.Screens.Multi.Components +namespace osu.Game.Screens.OnlinePlay.Components { public abstract class RoomManager : CompositeDrawable, IRoomManager { diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomPollingComponent.cs b/osu.Game/Screens/OnlinePlay/Components/RoomPollingComponent.cs index 1d10277d1c..b2ea3a05d6 100644 --- a/osu.Game/Screens/OnlinePlay/Components/RoomPollingComponent.cs +++ b/osu.Game/Screens/OnlinePlay/Components/RoomPollingComponent.cs @@ -8,7 +8,7 @@ using osu.Game.Online; using osu.Game.Online.API; using osu.Game.Online.Rooms; -namespace osu.Game.Screens.Multi.Components +namespace osu.Game.Screens.OnlinePlay.Components { public abstract class RoomPollingComponent : PollingComponent { diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomStatusInfo.cs b/osu.Game/Screens/OnlinePlay/Components/RoomStatusInfo.cs index 89021691f3..58cb25f30e 100644 --- a/osu.Game/Screens/OnlinePlay/Components/RoomStatusInfo.cs +++ b/osu.Game/Screens/OnlinePlay/Components/RoomStatusInfo.cs @@ -11,7 +11,7 @@ using osu.Game.Graphics; using osu.Game.Online.Rooms; using osu.Game.Online.Rooms.RoomStatuses; -namespace osu.Game.Screens.Multi.Components +namespace osu.Game.Screens.OnlinePlay.Components { public class RoomStatusInfo : MultiplayerComposite { diff --git a/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs b/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs index 3050765931..0eec155060 100644 --- a/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs +++ b/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs @@ -7,7 +7,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Online.Rooms; -namespace osu.Game.Screens.Multi.Components +namespace osu.Game.Screens.OnlinePlay.Components { /// /// A that polls for the currently-selected room. diff --git a/osu.Game/Screens/OnlinePlay/Components/StatusColouredContainer.cs b/osu.Game/Screens/OnlinePlay/Components/StatusColouredContainer.cs index 68c14eeb15..760de354dc 100644 --- a/osu.Game/Screens/OnlinePlay/Components/StatusColouredContainer.cs +++ b/osu.Game/Screens/OnlinePlay/Components/StatusColouredContainer.cs @@ -8,7 +8,7 @@ using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Online.Rooms; -namespace osu.Game.Screens.Multi.Components +namespace osu.Game.Screens.OnlinePlay.Components { public class StatusColouredContainer : Container { diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs index 956d38a90e..a08d9edb34 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs @@ -10,7 +10,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Online.Rooms; using osuTK; -namespace osu.Game.Screens.Multi +namespace osu.Game.Screens.OnlinePlay { public class DrawableRoomPlaylist : OsuRearrangeableListContainer { diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index a2aeae154a..e3bce4029f 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -29,7 +29,7 @@ using osu.Game.Screens.Play.HUD; using osuTK; using osuTK.Graphics; -namespace osu.Game.Screens.Multi +namespace osu.Game.Screens.OnlinePlay { public class DrawableRoomPlaylistItem : OsuRearrangeableListItem { diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistWithResults.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistWithResults.cs index fa241a3c42..575f336e58 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistWithResults.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistWithResults.cs @@ -13,7 +13,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Rooms; -namespace osu.Game.Screens.Multi +namespace osu.Game.Screens.OnlinePlay { public class DrawableRoomPlaylistWithResults : DrawableRoomPlaylist { diff --git a/osu.Game/Screens/OnlinePlay/Header.cs b/osu.Game/Screens/OnlinePlay/Header.cs index 637d8bb52b..bffd744fdc 100644 --- a/osu.Game/Screens/OnlinePlay/Header.cs +++ b/osu.Game/Screens/OnlinePlay/Header.cs @@ -10,13 +10,13 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Framework.Screens; using osu.Game.Graphics; -using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; using osuTK; using osuTK.Graphics; -namespace osu.Game.Screens.Multi +namespace osu.Game.Screens.OnlinePlay { public class Header : Container { diff --git a/osu.Game/Screens/OnlinePlay/IMultiplayerSubScreen.cs b/osu.Game/Screens/OnlinePlay/IMultiplayerSubScreen.cs index 31ee123f83..fc149cd2b2 100644 --- a/osu.Game/Screens/OnlinePlay/IMultiplayerSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/IMultiplayerSubScreen.cs @@ -1,7 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -namespace osu.Game.Screens.Multi +namespace osu.Game.Screens.OnlinePlay { public interface IMultiplayerSubScreen : IOsuScreen { diff --git a/osu.Game/Screens/OnlinePlay/IRoomManager.cs b/osu.Game/Screens/OnlinePlay/IRoomManager.cs index eee2a223a1..8ff02536f3 100644 --- a/osu.Game/Screens/OnlinePlay/IRoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/IRoomManager.cs @@ -6,7 +6,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Online.Rooms; -namespace osu.Game.Screens.Multi +namespace osu.Game.Screens.OnlinePlay { [Cached(typeof(IRoomManager))] public interface IRoomManager diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 6e4d8b46ed..6d37a483a9 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -9,22 +9,22 @@ using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.UserInterface; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; -using osu.Game.Screens.Multi.Components; +using osu.Game.Online.Rooms; +using osu.Game.Screens.OnlinePlay.Components; using osuTK; using osuTK.Graphics; -using osu.Framework.Graphics.Cursor; -using osu.Framework.Graphics.UserInterface; -using osu.Game.Online.Rooms; -namespace osu.Game.Screens.Multi.Lounge.Components +namespace osu.Game.Screens.OnlinePlay.Lounge.Components { public class DrawableRoom : OsuClickableContainer, IStateful, IFilterable, IHasContextMenu { diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/FilterControl.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/FilterControl.cs index 3712cbe33e..7fc1c670ca 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/FilterControl.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/FilterControl.cs @@ -12,7 +12,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets; using osuTK.Graphics; -namespace osu.Game.Screens.Multi.Lounge.Components +namespace osu.Game.Screens.OnlinePlay.Lounge.Components { public abstract class FilterControl : CompositeDrawable { diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/FilterCriteria.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/FilterCriteria.cs index 7b04be86b1..488af5d4de 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/FilterCriteria.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/FilterCriteria.cs @@ -3,7 +3,7 @@ using osu.Game.Rulesets; -namespace osu.Game.Screens.Multi.Lounge.Components +namespace osu.Game.Screens.OnlinePlay.Lounge.Components { public class FilterCriteria { diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/ParticipantInfo.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/ParticipantInfo.cs index 4152a9a3b2..895c0e3eda 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/ParticipantInfo.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/ParticipantInfo.cs @@ -11,7 +11,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Users.Drawables; using osuTK; -namespace osu.Game.Screens.Multi.Lounge.Components +namespace osu.Game.Screens.OnlinePlay.Lounge.Components { public class ParticipantInfo : MultiplayerComposite { diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistsFilterControl.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistsFilterControl.cs index 3c55c3c43f..a463742097 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistsFilterControl.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistsFilterControl.cs @@ -5,7 +5,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics.UserInterface; -namespace osu.Game.Screens.Multi.Lounge.Components +namespace osu.Game.Screens.OnlinePlay.Lounge.Components { public class PlaylistsFilterControl : FilterControl { diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomInfo.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomInfo.cs index e6f6ce5ed2..8552d425aa 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomInfo.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomInfo.cs @@ -6,10 +6,10 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Graphics.Containers; -using osu.Game.Screens.Multi.Components; +using osu.Game.Screens.OnlinePlay.Components; using osuTK; -namespace osu.Game.Screens.Multi.Lounge.Components +namespace osu.Game.Screens.OnlinePlay.Lounge.Components { public class RoomInfo : MultiplayerComposite { diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomInspector.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomInspector.cs index dfee278e87..4b1ec9ae89 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomInspector.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomInspector.cs @@ -7,10 +7,10 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; using osu.Game.Graphics; -using osu.Game.Screens.Multi.Components; +using osu.Game.Screens.OnlinePlay.Components; using osuTK.Graphics; -namespace osu.Game.Screens.Multi.Lounge.Components +namespace osu.Game.Screens.OnlinePlay.Lounge.Components { public class RoomInspector : MultiplayerComposite { diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusFilter.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusFilter.cs index 9da938ac8b..0c8dc8832b 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusFilter.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusFilter.cs @@ -3,7 +3,7 @@ using System.ComponentModel; -namespace osu.Game.Screens.Multi.Lounge.Components +namespace osu.Game.Screens.OnlinePlay.Lounge.Components { public enum RoomStatusFilter { diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs index fbd5f44a30..f70c33babe 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs @@ -13,13 +13,13 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Input.Bindings; using osu.Framework.Threading; using osu.Game.Extensions; +using osu.Game.Graphics.Cursor; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; -using osuTK; -using osu.Game.Graphics.Cursor; using osu.Game.Online.Rooms; +using osuTK; -namespace osu.Game.Screens.Multi.Lounge.Components +namespace osu.Game.Screens.OnlinePlay.Lounge.Components { public class RoomsContainer : CompositeDrawable, IKeyBindingHandler { diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index cbab79e2ab..cc56c11d32 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -12,11 +12,11 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Rooms; using osu.Game.Overlays; -using osu.Game.Screens.Multi.Lounge.Components; -using osu.Game.Screens.Multi.Match; +using osu.Game.Screens.OnlinePlay.Lounge.Components; +using osu.Game.Screens.OnlinePlay.Match; using osu.Game.Users; -namespace osu.Game.Screens.Multi.Lounge +namespace osu.Game.Screens.OnlinePlay.Lounge { [Cached] public abstract class LoungeSubScreen : MultiplayerSubScreen diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/Footer.cs b/osu.Game/Screens/OnlinePlay/Match/Components/Footer.cs index 7074ceca38..5c27d78d50 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/Footer.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/Footer.cs @@ -10,10 +10,10 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Online.Rooms; -using osu.Game.Screens.Multi.Playlists; +using osu.Game.Screens.OnlinePlay.Playlists; using osuTK; -namespace osu.Game.Screens.Multi.Match.Components +namespace osu.Game.Screens.OnlinePlay.Match.Components { public class Footer : CompositeDrawable { diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/GameTypePicker.cs b/osu.Game/Screens/OnlinePlay/Match/Components/GameTypePicker.cs index 23a3da6e38..cca1f84bbb 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/GameTypePicker.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/GameTypePicker.cs @@ -10,10 +10,10 @@ using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Online.Rooms; using osu.Game.Online.Rooms.GameTypes; -using osu.Game.Screens.Multi.Components; +using osu.Game.Screens.OnlinePlay.Components; using osuTK; -namespace osu.Game.Screens.Multi.Match.Components +namespace osu.Game.Screens.OnlinePlay.Match.Components { public class GameTypePicker : DisableableTabControl { diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/Header.cs b/osu.Game/Screens/OnlinePlay/Match/Components/Header.cs index 134a0b3f2e..df0dfc6ec1 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/Header.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/Header.cs @@ -10,7 +10,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Users.Drawables; using osuTK; -namespace osu.Game.Screens.Multi.Match.Components +namespace osu.Game.Screens.OnlinePlay.Match.Components { public class Header : MultiplayerComposite { diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/MatchChatDisplay.cs b/osu.Game/Screens/OnlinePlay/Match/Components/MatchChatDisplay.cs index b790ad9be5..8800215c2e 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/MatchChatDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/MatchChatDisplay.cs @@ -6,7 +6,7 @@ using osu.Framework.Bindables; using osu.Game.Online.Chat; using osu.Game.Online.Rooms; -namespace osu.Game.Screens.Multi.Match.Components +namespace osu.Game.Screens.OnlinePlay.Match.Components { public class MatchChatDisplay : StandAloneChatDisplay { diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/MatchLeaderboard.cs b/osu.Game/Screens/OnlinePlay/Match/Components/MatchLeaderboard.cs index 8cc7b62f98..50869f42ff 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/MatchLeaderboard.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/MatchLeaderboard.cs @@ -10,7 +10,7 @@ using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Leaderboards; using osu.Game.Online.Rooms; -namespace osu.Game.Screens.Multi.Match.Components +namespace osu.Game.Screens.OnlinePlay.Match.Components { public class MatchLeaderboard : Leaderboard { diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/MatchLeaderboardScore.cs b/osu.Game/Screens/OnlinePlay/Match/Components/MatchLeaderboardScore.cs index 1fabdbb86a..e8f5b1e826 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/MatchLeaderboardScore.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/MatchLeaderboardScore.cs @@ -8,7 +8,7 @@ using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Leaderboards; using osu.Game.Scoring; -namespace osu.Game.Screens.Multi.Match.Components +namespace osu.Game.Screens.OnlinePlay.Match.Components { public class MatchLeaderboardScore : LeaderboardScore { diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs index 0bb56d0cdf..998ab889d6 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs @@ -10,7 +10,7 @@ using osu.Game.Graphics.UserInterface; using osuTK; using osuTK.Graphics; -namespace osu.Game.Screens.Multi.Match.Components +namespace osu.Game.Screens.OnlinePlay.Match.Components { public abstract class MatchSettingsOverlay : FocusedOverlayContainer { diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/PurpleTriangleButton.cs b/osu.Game/Screens/OnlinePlay/Match/Components/PurpleTriangleButton.cs index 1d93116d07..28e8961a9a 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/PurpleTriangleButton.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/PurpleTriangleButton.cs @@ -5,7 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Game.Graphics.UserInterface; -namespace osu.Game.Screens.Multi.Match.Components +namespace osu.Game.Screens.OnlinePlay.Match.Components { public class PurpleTriangleButton : TriangleButton { diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/RoomAvailabilityPicker.cs b/osu.Game/Screens/OnlinePlay/Match/Components/RoomAvailabilityPicker.cs index 2292826d55..677a5be0d9 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/RoomAvailabilityPicker.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/RoomAvailabilityPicker.cs @@ -11,11 +11,11 @@ using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Online.Rooms; -using osu.Game.Screens.Multi.Components; +using osu.Game.Screens.OnlinePlay.Components; using osuTK; using osuTK.Graphics; -namespace osu.Game.Screens.Multi.Match.Components +namespace osu.Game.Screens.OnlinePlay.Match.Components { public class RoomAvailabilityPicker : DisableableTabControl { diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index b626156852..2b0035c8bc 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -15,7 +15,7 @@ using osu.Game.Overlays; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Play; -namespace osu.Game.Screens.Multi.Match +namespace osu.Game.Screens.OnlinePlay.Match { [Cached(typeof(IPreviewTrackOwner))] public abstract class RoomSubScreen : MultiplayerSubScreen, IPreviewTrackOwner diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs index b8b3f15fca..163efd9c20 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs @@ -4,9 +4,9 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Online.Multiplayer; -using osu.Game.Screens.Multi.Match.Components; +using osu.Game.Screens.OnlinePlay.Match.Components; -namespace osu.Game.Screens.Multi.Multiplayer +namespace osu.Game.Screens.OnlinePlay.Multiplayer { public class CreateMultiplayerMatchButton : PurpleTriangleButton { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs index dfb6feeaf9..1718ebd83a 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs @@ -9,9 +9,9 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; using osu.Framework.Screens; using osu.Game.Online.API; -using osu.Game.Screens.Multi.Match.Components; +using osu.Game.Screens.OnlinePlay.Match.Components; -namespace osu.Game.Screens.Multi.Multiplayer.Match +namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { public class BeatmapSelectionControl : MultiplayerComposite { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs index 145ae18817..a52f62fe00 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs @@ -11,7 +11,7 @@ using osu.Game.Graphics; using osu.Game.Online.Rooms; using osuTK; -namespace osu.Game.Screens.Multi.Multiplayer.Match +namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { public class MultiplayerMatchFooter : CompositeDrawable { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchHeader.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchHeader.cs index 0c0e580a3e..42e34c4be3 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchHeader.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchHeader.cs @@ -10,14 +10,14 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Online.API; -using osu.Game.Screens.Multi.Match.Components; +using osu.Game.Screens.OnlinePlay.Match.Components; using osu.Game.Users.Drawables; using osuTK; using FontWeight = osu.Game.Graphics.FontWeight; using OsuColour = osu.Game.Graphics.OsuColour; using OsuFont = osu.Game.Graphics.OsuFont; -namespace osu.Game.Screens.Multi.Multiplayer.Match +namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { public class MultiplayerMatchHeader : MultiplayerComposite { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index 4a5b5fd181..8741b0323d 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -18,10 +18,10 @@ using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; using osu.Game.Overlays; using osu.Game.Rulesets; -using osu.Game.Screens.Multi.Match.Components; +using osu.Game.Screens.OnlinePlay.Match.Components; using osuTK; -namespace osu.Game.Screens.Multi.Multiplayer.Match +namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { public class MultiplayerMatchSettingsOverlay : MatchSettingsOverlay { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index cea1eeecbb..15d6ef8aff 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -15,10 +15,10 @@ using osu.Game.Graphics.Backgrounds; using osu.Game.Online.API; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; -using osu.Game.Screens.Multi.Components; +using osu.Game.Screens.OnlinePlay.Components; using osuTK; -namespace osu.Game.Screens.Multi.Multiplayer.Match +namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { public class MultiplayerReadyButton : MultiplayerRoomComposite { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs index 76aa6b9f8f..ce4918dae1 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs @@ -8,10 +8,10 @@ using osu.Game.Extensions; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; -using osu.Game.Screens.Multi.Components; -using osu.Game.Screens.Multi.Lounge; +using osu.Game.Screens.OnlinePlay.Components; +using osu.Game.Screens.OnlinePlay.Lounge; -namespace osu.Game.Screens.Multi.Multiplayer +namespace osu.Game.Screens.OnlinePlay.Multiplayer { public class Multiplayer : MultiplayerScreen { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerFilterControl.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerFilterControl.cs index bebad1944e..37e0fd109a 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerFilterControl.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerFilterControl.cs @@ -1,9 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Game.Screens.Multi.Lounge.Components; +using osu.Game.Screens.OnlinePlay.Lounge.Components; -namespace osu.Game.Screens.Multi.Multiplayer +namespace osu.Game.Screens.OnlinePlay.Multiplayer { public class MultiplayerFilterControl : FilterControl { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs index ffc81efe3c..0a9a3f680f 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs @@ -5,11 +5,11 @@ using osu.Framework.Allocation; using osu.Framework.Logging; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; -using osu.Game.Screens.Multi.Lounge; -using osu.Game.Screens.Multi.Lounge.Components; -using osu.Game.Screens.Multi.Match; +using osu.Game.Screens.OnlinePlay.Lounge; +using osu.Game.Screens.OnlinePlay.Lounge.Components; +using osu.Game.Screens.OnlinePlay.Match; -namespace osu.Game.Screens.Multi.Multiplayer +namespace osu.Game.Screens.OnlinePlay.Multiplayer { public class MultiplayerLoungeSubScreen : LoungeSubScreen { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs index ed1321d4e2..76869300e8 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs @@ -13,7 +13,7 @@ using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; using osu.Game.Screens.Select; -namespace osu.Game.Screens.Multi.Multiplayer +namespace osu.Game.Screens.OnlinePlay.Multiplayer { public class MultiplayerMatchSongSelect : SongSelect, IMultiplayerSubScreen { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 4e371d4ed4..58314c3774 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -11,15 +11,15 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Screens; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; -using osu.Game.Screens.Multi.Components; -using osu.Game.Screens.Multi.Match; -using osu.Game.Screens.Multi.Match.Components; -using osu.Game.Screens.Multi.Multiplayer.Match; -using osu.Game.Screens.Multi.Multiplayer.Participants; +using osu.Game.Screens.OnlinePlay.Components; +using osu.Game.Screens.OnlinePlay.Match; +using osu.Game.Screens.OnlinePlay.Match.Components; +using osu.Game.Screens.OnlinePlay.Multiplayer.Match; +using osu.Game.Screens.OnlinePlay.Multiplayer.Participants; using osu.Game.Users; -using ParticipantsList = osu.Game.Screens.Multi.Multiplayer.Participants.ParticipantsList; +using ParticipantsList = osu.Game.Screens.OnlinePlay.Multiplayer.Participants.ParticipantsList; -namespace osu.Game.Screens.Multi.Multiplayer +namespace osu.Game.Screens.OnlinePlay.Multiplayer { [Cached] public class MultiplayerMatchSubScreen : RoomSubScreen diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index 32d95aa11f..4247e954bd 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -11,13 +11,13 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; using osu.Game.Scoring; -using osu.Game.Screens.Multi.Playlists; +using osu.Game.Screens.OnlinePlay.Playlists; using osu.Game.Screens.Play; using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Ranking; using osuTK; -namespace osu.Game.Screens.Multi.Multiplayer +namespace osu.Game.Screens.OnlinePlay.Multiplayer { // Todo: The "room" part of PlaylistsPlayer should be split out into an abstract player class to be inherited instead. public class MultiplayerPlayer : PlaylistsPlayer diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerResultsScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerResultsScreen.cs index 3e39473954..e3b47b3254 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerResultsScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerResultsScreen.cs @@ -3,9 +3,9 @@ using osu.Game.Online.Rooms; using osu.Game.Scoring; -using osu.Game.Screens.Multi.Playlists; +using osu.Game.Screens.OnlinePlay.Playlists; -namespace osu.Game.Screens.Multi.Multiplayer +namespace osu.Game.Screens.OnlinePlay.Multiplayer { public class MultiplayerResultsScreen : PlaylistsResultsScreen { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomComposite.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomComposite.cs index 6fe7c10bfb..654dafe9aa 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomComposite.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomComposite.cs @@ -5,7 +5,7 @@ using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Game.Online.Multiplayer; -namespace osu.Game.Screens.Multi.Multiplayer +namespace osu.Game.Screens.OnlinePlay.Multiplayer { public abstract class MultiplayerRoomComposite : MultiplayerComposite { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs index fdd46cdc2a..3cb263298f 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs @@ -13,9 +13,9 @@ using osu.Game.Extensions; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; using osu.Game.Online.Rooms.RoomStatuses; -using osu.Game.Screens.Multi.Components; +using osu.Game.Screens.OnlinePlay.Components; -namespace osu.Game.Screens.Multi.Multiplayer +namespace osu.Game.Screens.OnlinePlay.Multiplayer { public class MultiplayerRoomManager : RoomManager { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs index 93f8a9de8b..044afa7445 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs @@ -22,7 +22,7 @@ using osu.Game.Users.Drawables; using osuTK; using osuTK.Graphics; -namespace osu.Game.Screens.Multi.Multiplayer.Participants +namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants { public class ParticipantPanel : MultiplayerRoomComposite, IHasContextMenu { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantsList.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantsList.cs index 3f37274d4c..b9ac096c4a 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantsList.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantsList.cs @@ -9,7 +9,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Cursor; using osuTK; -namespace osu.Game.Screens.Multi.Multiplayer.Participants +namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants { public class ParticipantsList : MultiplayerRoomComposite { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantsListHeader.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantsListHeader.cs index dc3ceedfd7..6c1a55a0eb 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantsListHeader.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantsListHeader.cs @@ -3,9 +3,9 @@ using osu.Framework.Allocation; using osu.Game.Online.Multiplayer; -using osu.Game.Screens.Multi.Components; +using osu.Game.Screens.OnlinePlay.Components; -namespace osu.Game.Screens.Multi.Multiplayer.Participants +namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants { public class ParticipantsListHeader : OverlinedHeader { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/StateDisplay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/StateDisplay.cs index 61faa0d85d..8d2879fc93 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/StateDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/StateDisplay.cs @@ -11,7 +11,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Online.Multiplayer; using osuTK; -namespace osu.Game.Screens.Multi.Multiplayer.Participants +namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants { public class StateDisplay : CompositeDrawable { diff --git a/osu.Game/Screens/OnlinePlay/MultiplayerComposite.cs b/osu.Game/Screens/OnlinePlay/MultiplayerComposite.cs index fe4ca759b0..ab54178ab4 100644 --- a/osu.Game/Screens/OnlinePlay/MultiplayerComposite.cs +++ b/osu.Game/Screens/OnlinePlay/MultiplayerComposite.cs @@ -8,7 +8,7 @@ using osu.Framework.Graphics.Containers; using osu.Game.Online.Rooms; using osu.Game.Users; -namespace osu.Game.Screens.Multi +namespace osu.Game.Screens.OnlinePlay { public class MultiplayerComposite : CompositeDrawable { diff --git a/osu.Game/Screens/OnlinePlay/MultiplayerScreen.cs b/osu.Game/Screens/OnlinePlay/MultiplayerScreen.cs index 983c7aeac9..3693e897ae 100644 --- a/osu.Game/Screens/OnlinePlay/MultiplayerScreen.cs +++ b/osu.Game/Screens/OnlinePlay/MultiplayerScreen.cs @@ -17,14 +17,14 @@ using osu.Game.Online.API; using osu.Game.Online.Rooms; using osu.Game.Overlays; using osu.Game.Screens.Menu; -using osu.Game.Screens.Multi.Components; -using osu.Game.Screens.Multi.Lounge; -using osu.Game.Screens.Multi.Lounge.Components; -using osu.Game.Screens.Multi.Match; +using osu.Game.Screens.OnlinePlay.Components; +using osu.Game.Screens.OnlinePlay.Lounge; +using osu.Game.Screens.OnlinePlay.Lounge.Components; +using osu.Game.Screens.OnlinePlay.Match; using osu.Game.Users; using osuTK; -namespace osu.Game.Screens.Multi +namespace osu.Game.Screens.OnlinePlay { [Cached] public abstract class MultiplayerScreen : OsuScreen diff --git a/osu.Game/Screens/OnlinePlay/MultiplayerSubScreen.cs b/osu.Game/Screens/OnlinePlay/MultiplayerSubScreen.cs index 8e46de1a95..d7fca98ebe 100644 --- a/osu.Game/Screens/OnlinePlay/MultiplayerSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/MultiplayerSubScreen.cs @@ -5,7 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Screens; -namespace osu.Game.Screens.Multi +namespace osu.Game.Screens.OnlinePlay { public abstract class MultiplayerSubScreen : OsuScreen, IMultiplayerSubScreen { diff --git a/osu.Game/Screens/OnlinePlay/MultiplayerSubScreenStack.cs b/osu.Game/Screens/OnlinePlay/MultiplayerSubScreenStack.cs index 3b0ed0dba1..335da86a65 100644 --- a/osu.Game/Screens/OnlinePlay/MultiplayerSubScreenStack.cs +++ b/osu.Game/Screens/OnlinePlay/MultiplayerSubScreenStack.cs @@ -3,7 +3,7 @@ using osu.Framework.Screens; -namespace osu.Game.Screens.Multi +namespace osu.Game.Screens.OnlinePlay { public class MultiplayerSubScreenStack : OsuScreenStack { diff --git a/osu.Game/Screens/OnlinePlay/Playlists/CreatePlaylistsRoomButton.cs b/osu.Game/Screens/OnlinePlay/Playlists/CreatePlaylistsRoomButton.cs index acee063115..fcb773f8be 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/CreatePlaylistsRoomButton.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/CreatePlaylistsRoomButton.cs @@ -2,9 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Game.Screens.Multi.Match.Components; +using osu.Game.Screens.OnlinePlay.Match.Components; -namespace osu.Game.Screens.Multi.Playlists +namespace osu.Game.Screens.OnlinePlay.Playlists { public class CreatePlaylistsRoomButton : PurpleTriangleButton { diff --git a/osu.Game/Screens/OnlinePlay/Playlists/Playlists.cs b/osu.Game/Screens/OnlinePlay/Playlists/Playlists.cs index da6aade942..a7fb391fbc 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/Playlists.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/Playlists.cs @@ -5,11 +5,11 @@ using osu.Framework.Logging; using osu.Framework.Screens; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Rooms; -using osu.Game.Screens.Multi.Components; -using osu.Game.Screens.Multi.Lounge; -using osu.Game.Screens.Multi.Match; +using osu.Game.Screens.OnlinePlay.Components; +using osu.Game.Screens.OnlinePlay.Lounge; +using osu.Game.Screens.OnlinePlay.Match; -namespace osu.Game.Screens.Multi.Playlists +namespace osu.Game.Screens.OnlinePlay.Playlists { public class Playlists : MultiplayerScreen { diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs index 513854515b..bfbff4240c 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs @@ -2,11 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Online.Rooms; -using osu.Game.Screens.Multi.Lounge; -using osu.Game.Screens.Multi.Lounge.Components; -using osu.Game.Screens.Multi.Match; +using osu.Game.Screens.OnlinePlay.Lounge; +using osu.Game.Screens.OnlinePlay.Lounge.Components; +using osu.Game.Screens.OnlinePlay.Match; -namespace osu.Game.Screens.Multi.Playlists +namespace osu.Game.Screens.OnlinePlay.Playlists { public class PlaylistsLoungeSubScreen : LoungeSubScreen { diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs index f3109a33e4..557f1df657 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs @@ -16,10 +16,10 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Rooms; using osu.Game.Overlays; -using osu.Game.Screens.Multi.Match.Components; +using osu.Game.Screens.OnlinePlay.Match.Components; using osuTK; -namespace osu.Game.Screens.Multi.Playlists +namespace osu.Game.Screens.OnlinePlay.Playlists { public class PlaylistsMatchSettingsOverlay : MatchSettingsOverlay { diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs index 25a3340ead..2c3e7a12e2 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs @@ -17,7 +17,7 @@ using osu.Game.Scoring; using osu.Game.Screens.Play; using osu.Game.Screens.Ranking; -namespace osu.Game.Screens.Multi.Playlists +namespace osu.Game.Screens.OnlinePlay.Playlists { public class PlaylistsPlayer : Player { diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsReadyButton.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsReadyButton.cs index 3c35e2a6f3..edee8e571a 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsReadyButton.cs @@ -6,9 +6,9 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Graphics; using osu.Game.Online.Rooms; -using osu.Game.Screens.Multi.Components; +using osu.Game.Screens.OnlinePlay.Components; -namespace osu.Game.Screens.Multi.Playlists +namespace osu.Game.Screens.OnlinePlay.Playlists { public class PlaylistsReadyButton : ReadyButton { diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsResultsScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsResultsScreen.cs index 5d1d3a2724..e13c8a9f82 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsResultsScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsResultsScreen.cs @@ -15,7 +15,7 @@ using osu.Game.Online.Rooms; using osu.Game.Scoring; using osu.Game.Screens.Ranking; -namespace osu.Game.Screens.Multi.Playlists +namespace osu.Game.Screens.OnlinePlay.Playlists { public class PlaylistsResultsScreen : ResultsScreen { diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomManager.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomManager.cs index ae57eeddcc..c55d1c3e94 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomManager.cs @@ -3,9 +3,9 @@ using System.Collections.Generic; using osu.Framework.Bindables; -using osu.Game.Screens.Multi.Components; +using osu.Game.Screens.OnlinePlay.Components; -namespace osu.Game.Screens.Multi.Playlists +namespace osu.Game.Screens.OnlinePlay.Playlists { public class PlaylistsRoomManager : RoomManager { diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs index 4867268fe1..51a9ae569e 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs @@ -10,14 +10,14 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Screens; using osu.Game.Online.API; using osu.Game.Online.Rooms; -using osu.Game.Screens.Multi.Components; -using osu.Game.Screens.Multi.Match; -using osu.Game.Screens.Multi.Match.Components; +using osu.Game.Screens.OnlinePlay.Components; +using osu.Game.Screens.OnlinePlay.Match; +using osu.Game.Screens.OnlinePlay.Match.Components; using osu.Game.Screens.Select; using osu.Game.Users; -using Footer = osu.Game.Screens.Multi.Match.Components.Footer; +using Footer = osu.Game.Screens.OnlinePlay.Match.Components.Footer; -namespace osu.Game.Screens.Multi.Playlists +namespace osu.Game.Screens.OnlinePlay.Playlists { public class PlaylistsRoomSubScreen : RoomSubScreen { diff --git a/osu.Game/Screens/Play/Spectator.cs b/osu.Game/Screens/Play/Spectator.cs index 71ce157296..28311f5113 100644 --- a/osu.Game/Screens/Play/Spectator.cs +++ b/osu.Game/Screens/Play/Spectator.cs @@ -30,7 +30,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Replays.Types; using osu.Game.Scoring; -using osu.Game.Screens.Multi.Match.Components; +using osu.Game.Screens.OnlinePlay.Match.Components; using osu.Game.Users; using osuTK; diff --git a/osu.Game/Screens/Select/MatchSongSelect.cs b/osu.Game/Screens/Select/MatchSongSelect.cs index 80fd5c2067..1b89a58b40 100644 --- a/osu.Game/Screens/Select/MatchSongSelect.cs +++ b/osu.Game/Screens/Select/MatchSongSelect.cs @@ -10,8 +10,8 @@ using osu.Framework.Graphics; using osu.Framework.Screens; using osu.Game.Beatmaps; using osu.Game.Online.Rooms; -using osu.Game.Screens.Multi; -using osu.Game.Screens.Multi.Components; +using osu.Game.Screens.OnlinePlay; +using osu.Game.Screens.OnlinePlay.Components; namespace osu.Game.Screens.Select { diff --git a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs index 0e23f4d8c6..da0e39d965 100644 --- a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs +++ b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs @@ -7,8 +7,8 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Online.Multiplayer; -using osu.Game.Screens.Multi; -using osu.Game.Screens.Multi.Lounge.Components; +using osu.Game.Screens.OnlinePlay; +using osu.Game.Screens.OnlinePlay.Lounge.Components; namespace osu.Game.Tests.Visual.Multiplayer { diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomContainer.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomContainer.cs index 0df397d98e..ad3e2f7105 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomContainer.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomContainer.cs @@ -6,8 +6,8 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Online.Multiplayer; -using osu.Game.Screens.Multi; -using osu.Game.Screens.Multi.Lounge.Components; +using osu.Game.Screens.OnlinePlay; +using osu.Game.Screens.OnlinePlay.Lounge.Components; namespace osu.Game.Tests.Visual.Multiplayer { diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs index abfefd363a..5e12156f3c 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs @@ -11,8 +11,8 @@ using osu.Game.Online.API.Requests; using osu.Game.Online.Rooms; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; -using osu.Game.Screens.Multi.Lounge.Components; -using osu.Game.Screens.Multi.Multiplayer; +using osu.Game.Screens.OnlinePlay.Lounge.Components; +using osu.Game.Screens.OnlinePlay.Multiplayer; namespace osu.Game.Tests.Visual.Multiplayer { From 4caf75850b4faf18d62827fcb4e9b1b50f4f1b31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 25 Dec 2020 17:00:00 +0100 Subject: [PATCH 569/818] Rename {Multiplayer -> OnlinePlay}Screen --- .../Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs | 4 ++-- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 4 ++-- osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs | 2 +- .../OnlinePlay/{MultiplayerScreen.cs => OnlinePlayScreen.cs} | 4 ++-- osu.Game/Screens/OnlinePlay/Playlists/Playlists.cs | 2 +- .../Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs | 5 +---- 6 files changed, 9 insertions(+), 12 deletions(-) rename osu.Game/Screens/OnlinePlay/{MultiplayerScreen.cs => OnlinePlayScreen.cs} (99%) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 6d37a483a9..f4d167a193 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -42,7 +42,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private CachedModelDependencyContainer dependencies; [Resolved(canBeNull: true)] - private MultiplayerScreen multiplayer { get; set; } + private OnlinePlayScreen parentScreen { get; set; } [Resolved] private BeatmapManager beatmaps { get; set; } @@ -242,7 +242,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { new OsuMenuItem("Create copy", MenuItemType.Standard, () => { - multiplayer?.OpenNewRoom(Room.CreateCopy()); + parentScreen?.OpenNewRoom(Room.CreateCopy()); }) }; } diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 2b0035c8bc..3a5af90824 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -36,7 +36,7 @@ namespace osu.Game.Screens.OnlinePlay.Match private BeatmapManager beatmapManager { get; set; } [Resolved(canBeNull: true)] - protected MultiplayerScreen Multiplayer { get; private set; } + protected OnlinePlayScreen ParentScreen { get; private set; } private IBindable> managerUpdated; @@ -88,7 +88,7 @@ namespace osu.Game.Screens.OnlinePlay.Match protected void StartPlay(Func player) { sampleStart?.Play(); - Multiplayer?.Push(new PlayerLoader(player)); + ParentScreen?.Push(new PlayerLoader(player)); } private void selectedItemChanged() diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs index ce4918dae1..76f5c74433 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs @@ -13,7 +13,7 @@ using osu.Game.Screens.OnlinePlay.Lounge; namespace osu.Game.Screens.OnlinePlay.Multiplayer { - public class Multiplayer : MultiplayerScreen + public class Multiplayer : OnlinePlayScreen { [Resolved] private StatefulMultiplayerClient client { get; set; } diff --git a/osu.Game/Screens/OnlinePlay/MultiplayerScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs similarity index 99% rename from osu.Game/Screens/OnlinePlay/MultiplayerScreen.cs rename to osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index 3693e897ae..08741ef4fe 100644 --- a/osu.Game/Screens/OnlinePlay/MultiplayerScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -27,7 +27,7 @@ using osuTK; namespace osu.Game.Screens.OnlinePlay { [Cached] - public abstract class MultiplayerScreen : OsuScreen + public abstract class OnlinePlayScreen : OsuScreen { public override bool CursorVisible => (screenStack.CurrentScreen as IMultiplayerSubScreen)?.CursorVisible ?? true; @@ -67,7 +67,7 @@ namespace osu.Game.Screens.OnlinePlay private readonly Drawable header; private readonly Drawable headerBackground; - protected MultiplayerScreen() + protected OnlinePlayScreen() { Anchor = Anchor.Centre; Origin = Anchor.Centre; diff --git a/osu.Game/Screens/OnlinePlay/Playlists/Playlists.cs b/osu.Game/Screens/OnlinePlay/Playlists/Playlists.cs index a7fb391fbc..5b132c97fd 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/Playlists.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/Playlists.cs @@ -11,7 +11,7 @@ using osu.Game.Screens.OnlinePlay.Match; namespace osu.Game.Screens.OnlinePlay.Playlists { - public class Playlists : MultiplayerScreen + public class Playlists : OnlinePlayScreen { protected override void UpdatePollingRate(bool isIdle) { diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs index 51a9ae569e..e76ca995bf 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs @@ -28,9 +28,6 @@ namespace osu.Game.Screens.OnlinePlay.Playlists [Resolved(typeof(Room), nameof(Room.RoomID))] private Bindable roomId { get; set; } - [Resolved(canBeNull: true)] - private MultiplayerScreen multiplayer { get; set; } - private MatchSettingsOverlay settingsOverlay; private MatchLeaderboard leaderboard; @@ -124,7 +121,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists RequestShowResults = item => { Debug.Assert(roomId.Value != null); - multiplayer?.Push(new PlaylistsResultsScreen(null, roomId.Value.Value, item, false)); + ParentScreen?.Push(new PlaylistsResultsScreen(null, roomId.Value.Value, item, false)); } } }, From eb0f125fefb5691e82c31a1730db1da323a0a095 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 25 Dec 2020 17:00:31 +0100 Subject: [PATCH 570/818] Rename {Multiplayer -> OnlinePlay}SubScreenStack --- osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs | 2 +- ...MultiplayerSubScreenStack.cs => OnlinePlaySubScreenStack.cs} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename osu.Game/Screens/OnlinePlay/{MultiplayerSubScreenStack.cs => OnlinePlaySubScreenStack.cs} (93%) diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index 08741ef4fe..66309bd47e 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -124,7 +124,7 @@ namespace osu.Game.Screens.OnlinePlay } } }, - screenStack = new MultiplayerSubScreenStack { RelativeSizeAxes = Axes.Both } + screenStack = new OnlinePlaySubScreenStack { RelativeSizeAxes = Axes.Both } } }, new Header(ScreenTitle, screenStack), diff --git a/osu.Game/Screens/OnlinePlay/MultiplayerSubScreenStack.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreenStack.cs similarity index 93% rename from osu.Game/Screens/OnlinePlay/MultiplayerSubScreenStack.cs rename to osu.Game/Screens/OnlinePlay/OnlinePlaySubScreenStack.cs index 335da86a65..7f2a0980c1 100644 --- a/osu.Game/Screens/OnlinePlay/MultiplayerSubScreenStack.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreenStack.cs @@ -5,7 +5,7 @@ using osu.Framework.Screens; namespace osu.Game.Screens.OnlinePlay { - public class MultiplayerSubScreenStack : OsuScreenStack + public class OnlinePlaySubScreenStack : OsuScreenStack { protected override void ScreenChanged(IScreen prev, IScreen next) { From e5064ee930c58a01fcf79807c14aaf83a7e420d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 25 Dec 2020 17:02:35 +0100 Subject: [PATCH 571/818] Rename {Multiplayer -> OnlinePlay}SubScreen --- osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs | 2 +- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 2 +- osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs | 8 ++++---- .../{MultiplayerSubScreen.cs => OnlinePlaySubScreen.cs} | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) rename osu.Game/Screens/OnlinePlay/{MultiplayerSubScreen.cs => OnlinePlaySubScreen.cs} (94%) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index cc56c11d32..79f5dfdee1 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -19,7 +19,7 @@ using osu.Game.Users; namespace osu.Game.Screens.OnlinePlay.Lounge { [Cached] - public abstract class LoungeSubScreen : MultiplayerSubScreen + public abstract class LoungeSubScreen : OnlinePlaySubScreen { public override string Title => "Lounge"; diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 3a5af90824..2449563c73 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -18,7 +18,7 @@ using osu.Game.Screens.Play; namespace osu.Game.Screens.OnlinePlay.Match { [Cached(typeof(IPreviewTrackOwner))] - public abstract class RoomSubScreen : MultiplayerSubScreen, IPreviewTrackOwner + public abstract class RoomSubScreen : OnlinePlaySubScreen, IPreviewTrackOwner { protected readonly Bindable SelectedItem = new Bindable(); diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index 66309bd47e..60897e8b4c 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -295,13 +295,13 @@ namespace osu.Game.Screens.OnlinePlay switch (newScreen) { case LoungeSubScreen _: - header.Delay(MultiplayerSubScreen.RESUME_TRANSITION_DELAY).ResizeHeightTo(400, MultiplayerSubScreen.APPEAR_DURATION, Easing.OutQuint); - headerBackground.MoveToX(0, MultiplayerSubScreen.X_MOVE_DURATION, Easing.OutQuint); + header.Delay(OnlinePlaySubScreen.RESUME_TRANSITION_DELAY).ResizeHeightTo(400, OnlinePlaySubScreen.APPEAR_DURATION, Easing.OutQuint); + headerBackground.MoveToX(0, OnlinePlaySubScreen.X_MOVE_DURATION, Easing.OutQuint); break; case RoomSubScreen _: - header.ResizeHeightTo(135, MultiplayerSubScreen.APPEAR_DURATION, Easing.OutQuint); - headerBackground.MoveToX(-MultiplayerSubScreen.X_SHIFT, MultiplayerSubScreen.X_MOVE_DURATION, Easing.OutQuint); + header.ResizeHeightTo(135, OnlinePlaySubScreen.APPEAR_DURATION, Easing.OutQuint); + headerBackground.MoveToX(-OnlinePlaySubScreen.X_SHIFT, OnlinePlaySubScreen.X_MOVE_DURATION, Easing.OutQuint); break; } diff --git a/osu.Game/Screens/OnlinePlay/MultiplayerSubScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs similarity index 94% rename from osu.Game/Screens/OnlinePlay/MultiplayerSubScreen.cs rename to osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs index d7fca98ebe..b6dcfe6dab 100644 --- a/osu.Game/Screens/OnlinePlay/MultiplayerSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs @@ -7,7 +7,7 @@ using osu.Framework.Screens; namespace osu.Game.Screens.OnlinePlay { - public abstract class MultiplayerSubScreen : OsuScreen, IMultiplayerSubScreen + public abstract class OnlinePlaySubScreen : OsuScreen, IMultiplayerSubScreen { public override bool DisallowExternalBeatmapRulesetChanges => false; @@ -16,7 +16,7 @@ namespace osu.Game.Screens.OnlinePlay [Resolved(CanBeNull = true)] protected IRoomManager RoomManager { get; private set; } - protected MultiplayerSubScreen() + protected OnlinePlaySubScreen() { Anchor = Anchor.Centre; Origin = Anchor.Centre; From 4c43a67b68a9b0d1c574f33a4aa4e739e80397e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 25 Dec 2020 17:05:29 +0100 Subject: [PATCH 572/818] Rename I{Multiplayer -> OnlinePlay}SubScreen --- osu.Game.Tests/Visual/Multiplayer/TestSceneMultiHeader.cs | 8 ++++---- osu.Game/Screens/OnlinePlay/Header.cs | 6 +++--- .../{IMultiplayerSubScreen.cs => IOnlinePlaySubScreen.cs} | 2 +- .../OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs | 2 +- osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs | 4 ++-- osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs | 2 +- osu.Game/Screens/Select/MatchSongSelect.cs | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) rename osu.Game/Screens/OnlinePlay/{IMultiplayerSubScreen.cs => IOnlinePlaySubScreen.cs} (82%) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiHeader.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiHeader.cs index 089de223fc..2244dcfc56 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiHeader.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiHeader.cs @@ -18,7 +18,7 @@ namespace osu.Game.Tests.Visual.Multiplayer OsuScreenStack screenStack = new OsuScreenStack { RelativeSizeAxes = Axes.Both }; - screenStack.Push(new TestMultiplayerSubScreen(index)); + screenStack.Push(new TestOnlinePlaySubScreen(index)); Children = new Drawable[] { @@ -26,16 +26,16 @@ namespace osu.Game.Tests.Visual.Multiplayer new Header("Multiplayer", screenStack) }; - AddStep("push multi screen", () => screenStack.CurrentScreen.Push(new TestMultiplayerSubScreen(++index))); + AddStep("push multi screen", () => screenStack.CurrentScreen.Push(new TestOnlinePlaySubScreen(++index))); } - private class TestMultiplayerSubScreen : OsuScreen, IMultiplayerSubScreen + private class TestOnlinePlaySubScreen : OsuScreen, IOnlinePlaySubScreen { private readonly int index; public string ShortTitle => $"Screen {index}"; - public TestMultiplayerSubScreen(int index) + public TestOnlinePlaySubScreen(int index) { this.index = index; } diff --git a/osu.Game/Screens/OnlinePlay/Header.cs b/osu.Game/Screens/OnlinePlay/Header.cs index bffd744fdc..bf0a53cbb6 100644 --- a/osu.Game/Screens/OnlinePlay/Header.cs +++ b/osu.Game/Screens/OnlinePlay/Header.cs @@ -61,8 +61,8 @@ namespace osu.Game.Screens.OnlinePlay breadcrumbs.Current.ValueChanged += screen => { - if (screen.NewValue is IMultiplayerSubScreen multiScreen) - title.Screen = multiScreen; + if (screen.NewValue is IOnlinePlaySubScreen onlineSubScreen) + title.Screen = onlineSubScreen; }; breadcrumbs.Current.TriggerChange(); @@ -75,7 +75,7 @@ namespace osu.Game.Screens.OnlinePlay private readonly OsuSpriteText dot; private readonly OsuSpriteText pageTitle; - public IMultiplayerSubScreen Screen + public IOnlinePlaySubScreen Screen { set => pageTitle.Text = value.ShortTitle.Titleize(); } diff --git a/osu.Game/Screens/OnlinePlay/IMultiplayerSubScreen.cs b/osu.Game/Screens/OnlinePlay/IOnlinePlaySubScreen.cs similarity index 82% rename from osu.Game/Screens/OnlinePlay/IMultiplayerSubScreen.cs rename to osu.Game/Screens/OnlinePlay/IOnlinePlaySubScreen.cs index fc149cd2b2..a4762292a9 100644 --- a/osu.Game/Screens/OnlinePlay/IMultiplayerSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/IOnlinePlaySubScreen.cs @@ -3,7 +3,7 @@ namespace osu.Game.Screens.OnlinePlay { - public interface IMultiplayerSubScreen : IOsuScreen + public interface IOnlinePlaySubScreen : IOsuScreen { string Title { get; } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs index 76869300e8..0842574f54 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs @@ -15,7 +15,7 @@ using osu.Game.Screens.Select; namespace osu.Game.Screens.OnlinePlay.Multiplayer { - public class MultiplayerMatchSongSelect : SongSelect, IMultiplayerSubScreen + public class MultiplayerMatchSongSelect : SongSelect, IOnlinePlaySubScreen { public string ShortTitle => "song selection"; diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index 60897e8b4c..9ba2e41d12 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -29,7 +29,7 @@ namespace osu.Game.Screens.OnlinePlay [Cached] public abstract class OnlinePlayScreen : OsuScreen { - public override bool CursorVisible => (screenStack.CurrentScreen as IMultiplayerSubScreen)?.CursorVisible ?? true; + public override bool CursorVisible => (screenStack.CurrentScreen as IOnlinePlaySubScreen)?.CursorVisible ?? true; // this is required due to PlayerLoader eventually being pushed to the main stack // while leases may be taken out by a subscreen. @@ -245,7 +245,7 @@ namespace osu.Game.Screens.OnlinePlay public override bool OnBackButton() { - if ((screenStack.CurrentScreen as IMultiplayerSubScreen)?.OnBackButton() == true) + if ((screenStack.CurrentScreen as IOnlinePlaySubScreen)?.OnBackButton() == true) return true; if (screenStack.CurrentScreen != null && !(screenStack.CurrentScreen is LoungeSubScreen)) diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs index b6dcfe6dab..e1bd889088 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs @@ -7,7 +7,7 @@ using osu.Framework.Screens; namespace osu.Game.Screens.OnlinePlay { - public abstract class OnlinePlaySubScreen : OsuScreen, IMultiplayerSubScreen + public abstract class OnlinePlaySubScreen : OsuScreen, IOnlinePlaySubScreen { public override bool DisallowExternalBeatmapRulesetChanges => false; diff --git a/osu.Game/Screens/Select/MatchSongSelect.cs b/osu.Game/Screens/Select/MatchSongSelect.cs index 1b89a58b40..0948a4d19a 100644 --- a/osu.Game/Screens/Select/MatchSongSelect.cs +++ b/osu.Game/Screens/Select/MatchSongSelect.cs @@ -15,7 +15,7 @@ using osu.Game.Screens.OnlinePlay.Components; namespace osu.Game.Screens.Select { - public class MatchSongSelect : SongSelect, IMultiplayerSubScreen + public class MatchSongSelect : SongSelect, IOnlinePlaySubScreen { public Action Selected; From ed4b8482b60d1133753296aebf78afd4fec95c58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 25 Dec 2020 17:12:33 +0100 Subject: [PATCH 573/818] Rename {Multiplayer -> OnlinePlay}Composite --- osu.Game/Screens/OnlinePlay/Components/BeatmapTitle.cs | 2 +- osu.Game/Screens/OnlinePlay/Components/BeatmapTypeInfo.cs | 2 +- osu.Game/Screens/OnlinePlay/Components/ModeTypeInfo.cs | 2 +- osu.Game/Screens/OnlinePlay/Components/OverlinedHeader.cs | 2 +- .../Screens/OnlinePlay/Components/ParticipantCountDisplay.cs | 2 +- osu.Game/Screens/OnlinePlay/Components/ParticipantsDisplay.cs | 2 +- osu.Game/Screens/OnlinePlay/Components/ParticipantsList.cs | 2 +- osu.Game/Screens/OnlinePlay/Components/RoomStatusInfo.cs | 2 +- osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs | 2 +- .../Screens/OnlinePlay/Lounge/Components/ParticipantInfo.cs | 2 +- osu.Game/Screens/OnlinePlay/Lounge/Components/RoomInfo.cs | 2 +- osu.Game/Screens/OnlinePlay/Lounge/Components/RoomInspector.cs | 2 +- osu.Game/Screens/OnlinePlay/Match/Components/Header.cs | 2 +- .../Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs | 2 +- .../OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs | 2 +- .../OnlinePlay/Multiplayer/Match/MultiplayerMatchHeader.cs | 2 +- .../Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs | 2 +- .../Screens/OnlinePlay/Multiplayer/MultiplayerRoomComposite.cs | 2 +- .../{MultiplayerComposite.cs => OnlinePlayComposite.cs} | 2 +- osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs | 2 +- .../OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs | 2 +- 21 files changed, 21 insertions(+), 21 deletions(-) rename osu.Game/Screens/OnlinePlay/{MultiplayerComposite.cs => OnlinePlayComposite.cs} (96%) diff --git a/osu.Game/Screens/OnlinePlay/Components/BeatmapTitle.cs b/osu.Game/Screens/OnlinePlay/Components/BeatmapTitle.cs index bc355d18a9..acb82360b3 100644 --- a/osu.Game/Screens/OnlinePlay/Components/BeatmapTitle.cs +++ b/osu.Game/Screens/OnlinePlay/Components/BeatmapTitle.cs @@ -12,7 +12,7 @@ using osu.Game.Online.Chat; namespace osu.Game.Screens.OnlinePlay.Components { - public class BeatmapTitle : MultiplayerComposite + public class BeatmapTitle : OnlinePlayComposite { private readonly LinkFlowContainer textFlow; diff --git a/osu.Game/Screens/OnlinePlay/Components/BeatmapTypeInfo.cs b/osu.Game/Screens/OnlinePlay/Components/BeatmapTypeInfo.cs index 434d7b75ed..3aa13458a4 100644 --- a/osu.Game/Screens/OnlinePlay/Components/BeatmapTypeInfo.cs +++ b/osu.Game/Screens/OnlinePlay/Components/BeatmapTypeInfo.cs @@ -11,7 +11,7 @@ using osuTK; namespace osu.Game.Screens.OnlinePlay.Components { - public class BeatmapTypeInfo : MultiplayerComposite + public class BeatmapTypeInfo : OnlinePlayComposite { private LinkFlowContainer beatmapAuthor; diff --git a/osu.Game/Screens/OnlinePlay/Components/ModeTypeInfo.cs b/osu.Game/Screens/OnlinePlay/Components/ModeTypeInfo.cs index 719afcdd33..03b27b605c 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ModeTypeInfo.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ModeTypeInfo.cs @@ -10,7 +10,7 @@ using osuTK; namespace osu.Game.Screens.OnlinePlay.Components { - public class ModeTypeInfo : MultiplayerComposite + public class ModeTypeInfo : OnlinePlayComposite { private const float height = 30; private const float transition_duration = 100; diff --git a/osu.Game/Screens/OnlinePlay/Components/OverlinedHeader.cs b/osu.Game/Screens/OnlinePlay/Components/OverlinedHeader.cs index c78dfef592..08a0a3405e 100644 --- a/osu.Game/Screens/OnlinePlay/Components/OverlinedHeader.cs +++ b/osu.Game/Screens/OnlinePlay/Components/OverlinedHeader.cs @@ -15,7 +15,7 @@ namespace osu.Game.Screens.OnlinePlay.Components /// /// A header used in the multiplayer interface which shows text / details beneath a line. /// - public class OverlinedHeader : MultiplayerComposite + public class OverlinedHeader : OnlinePlayComposite { private bool showLine = true; diff --git a/osu.Game/Screens/OnlinePlay/Components/ParticipantCountDisplay.cs b/osu.Game/Screens/OnlinePlay/Components/ParticipantCountDisplay.cs index 357974adfc..53821da8fd 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ParticipantCountDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ParticipantCountDisplay.cs @@ -9,7 +9,7 @@ using osu.Game.Graphics.Sprites; namespace osu.Game.Screens.OnlinePlay.Components { - public class ParticipantCountDisplay : MultiplayerComposite + public class ParticipantCountDisplay : OnlinePlayComposite { private const float text_size = 30; private const float transition_duration = 100; diff --git a/osu.Game/Screens/OnlinePlay/Components/ParticipantsDisplay.cs b/osu.Game/Screens/OnlinePlay/Components/ParticipantsDisplay.cs index 5184f873f3..c36d1a2e76 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ParticipantsDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ParticipantsDisplay.cs @@ -8,7 +8,7 @@ using osu.Game.Graphics.Containers; namespace osu.Game.Screens.OnlinePlay.Components { - public class ParticipantsDisplay : MultiplayerComposite + public class ParticipantsDisplay : OnlinePlayComposite { public Bindable Details = new Bindable(); diff --git a/osu.Game/Screens/OnlinePlay/Components/ParticipantsList.cs b/osu.Game/Screens/OnlinePlay/Components/ParticipantsList.cs index b5019b4cdc..9aceb39a27 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ParticipantsList.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ParticipantsList.cs @@ -14,7 +14,7 @@ using osuTK; namespace osu.Game.Screens.OnlinePlay.Components { - public class ParticipantsList : MultiplayerComposite + public class ParticipantsList : OnlinePlayComposite { public const float TILE_SIZE = 35; diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomStatusInfo.cs b/osu.Game/Screens/OnlinePlay/Components/RoomStatusInfo.cs index 58cb25f30e..bcc256bcff 100644 --- a/osu.Game/Screens/OnlinePlay/Components/RoomStatusInfo.cs +++ b/osu.Game/Screens/OnlinePlay/Components/RoomStatusInfo.cs @@ -13,7 +13,7 @@ using osu.Game.Online.Rooms.RoomStatuses; namespace osu.Game.Screens.OnlinePlay.Components { - public class RoomStatusInfo : MultiplayerComposite + public class RoomStatusInfo : OnlinePlayComposite { public RoomStatusInfo() { diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index f4d167a193..0a7198a7fa 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -155,7 +155,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components Width = cover_width, Masking = true, Margin = new MarginPadding { Left = stripWidth }, - Child = new MultiplayerBackgroundSprite(BeatmapSetCoverType.List) { RelativeSizeAxes = Axes.Both } + Child = new OnlinePlayBackgroundSprite(BeatmapSetCoverType.List) { RelativeSizeAxes = Axes.Both } }, new Container { diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/ParticipantInfo.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/ParticipantInfo.cs index 895c0e3eda..0d5ce65d5a 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/ParticipantInfo.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/ParticipantInfo.cs @@ -13,7 +13,7 @@ using osuTK; namespace osu.Game.Screens.OnlinePlay.Lounge.Components { - public class ParticipantInfo : MultiplayerComposite + public class ParticipantInfo : OnlinePlayComposite { public ParticipantInfo() { diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomInfo.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomInfo.cs index 8552d425aa..0a17702f2a 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomInfo.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomInfo.cs @@ -11,7 +11,7 @@ using osuTK; namespace osu.Game.Screens.OnlinePlay.Lounge.Components { - public class RoomInfo : MultiplayerComposite + public class RoomInfo : OnlinePlayComposite { private readonly List statusElements = new List(); private readonly OsuTextFlowContainer roomName; diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomInspector.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomInspector.cs index 4b1ec9ae89..c28354c753 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomInspector.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomInspector.cs @@ -12,7 +12,7 @@ using osuTK.Graphics; namespace osu.Game.Screens.OnlinePlay.Lounge.Components { - public class RoomInspector : MultiplayerComposite + public class RoomInspector : OnlinePlayComposite { private const float transition_duration = 100; diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/Header.cs b/osu.Game/Screens/OnlinePlay/Match/Components/Header.cs index df0dfc6ec1..a2d11c54c1 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/Header.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/Header.cs @@ -12,7 +12,7 @@ using osuTK; namespace osu.Game.Screens.OnlinePlay.Match.Components { - public class Header : MultiplayerComposite + public class Header : OnlinePlayComposite { public const float HEIGHT = 50; diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs index 998ab889d6..ea3951fc3b 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs @@ -17,7 +17,7 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components protected const float TRANSITION_DURATION = 350; protected const float FIELD_PADDING = 45; - protected MultiplayerComposite Settings { get; set; } + protected OnlinePlayComposite Settings { get; set; } [BackgroundDependencyLoader] private void load() diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs index 1718ebd83a..f17e04d4d4 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs @@ -13,7 +13,7 @@ using osu.Game.Screens.OnlinePlay.Match.Components; namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { - public class BeatmapSelectionControl : MultiplayerComposite + public class BeatmapSelectionControl : OnlinePlayComposite { [Resolved] private MultiplayerMatchSubScreen matchSubScreen { get; set; } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchHeader.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchHeader.cs index 42e34c4be3..bb351d06d3 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchHeader.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchHeader.cs @@ -19,7 +19,7 @@ using OsuFont = osu.Game.Graphics.OsuFont; namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { - public class MultiplayerMatchHeader : MultiplayerComposite + public class MultiplayerMatchHeader : OnlinePlayComposite { public const float HEIGHT = 50; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index 8741b0323d..ae03d384f6 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -36,7 +36,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match }; } - protected class MatchSettings : MultiplayerComposite + protected class MatchSettings : OnlinePlayComposite { private const float disabled_alpha = 0.2f; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomComposite.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomComposite.cs index 654dafe9aa..ac608a13d4 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomComposite.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomComposite.cs @@ -7,7 +7,7 @@ using osu.Game.Online.Multiplayer; namespace osu.Game.Screens.OnlinePlay.Multiplayer { - public abstract class MultiplayerRoomComposite : MultiplayerComposite + public abstract class MultiplayerRoomComposite : OnlinePlayComposite { [CanBeNull] protected MultiplayerRoom Room => Client.Room; diff --git a/osu.Game/Screens/OnlinePlay/MultiplayerComposite.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs similarity index 96% rename from osu.Game/Screens/OnlinePlay/MultiplayerComposite.cs rename to osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs index ab54178ab4..64792a32f3 100644 --- a/osu.Game/Screens/OnlinePlay/MultiplayerComposite.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs @@ -10,7 +10,7 @@ using osu.Game.Users; namespace osu.Game.Screens.OnlinePlay { - public class MultiplayerComposite : CompositeDrawable + public class OnlinePlayComposite : CompositeDrawable { [Resolved(typeof(Room))] protected Bindable RoomID { get; private set; } diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index 9ba2e41d12..4074dd1573 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -338,7 +338,7 @@ namespace osu.Game.Screens.OnlinePlay } } - private class HeaderBackgroundSprite : MultiplayerBackgroundSprite + private class HeaderBackgroundSprite : OnlinePlayBackgroundSprite { protected override UpdateableBeatmapBackgroundSprite CreateBackgroundSprite() => new BackgroundSprite { RelativeSizeAxes = Axes.Both }; diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs index 557f1df657..6b92526f35 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs @@ -36,7 +36,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists }; } - protected class MatchSettings : MultiplayerComposite + protected class MatchSettings : OnlinePlayComposite { private const float disabled_alpha = 0.2f; From 2e4b1b95c283418818bd72f40a0885778112728f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 25 Dec 2020 17:12:46 +0100 Subject: [PATCH 574/818] Rename {Multiplayer -> OnlinePlay}BackgroundSprite --- ...layerBackgroundSprite.cs => OnlinePlayBackgroundSprite.cs} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename osu.Game/Screens/OnlinePlay/Components/{MultiplayerBackgroundSprite.cs => OnlinePlayBackgroundSprite.cs} (85%) diff --git a/osu.Game/Screens/OnlinePlay/Components/MultiplayerBackgroundSprite.cs b/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundSprite.cs similarity index 85% rename from osu.Game/Screens/OnlinePlay/Components/MultiplayerBackgroundSprite.cs rename to osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundSprite.cs index 45e2c553e7..d8dfac496d 100644 --- a/osu.Game/Screens/OnlinePlay/Components/MultiplayerBackgroundSprite.cs +++ b/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundSprite.cs @@ -8,12 +8,12 @@ using osu.Game.Beatmaps.Drawables; namespace osu.Game.Screens.OnlinePlay.Components { - public class MultiplayerBackgroundSprite : MultiplayerComposite + public class OnlinePlayBackgroundSprite : OnlinePlayComposite { private readonly BeatmapSetCoverType beatmapSetCoverType; private UpdateableBeatmapBackgroundSprite sprite; - public MultiplayerBackgroundSprite(BeatmapSetCoverType beatmapSetCoverType = BeatmapSetCoverType.Cover) + public OnlinePlayBackgroundSprite(BeatmapSetCoverType beatmapSetCoverType = BeatmapSetCoverType.Cover) { this.beatmapSetCoverType = beatmapSetCoverType; } From 0bd9f68cbd6758faa79fbb8a3feac064edb2bdda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 25 Dec 2020 18:39:55 +0100 Subject: [PATCH 575/818] Refactor update stream colour mapping code --- .../API/Requests/Responses/APIUpdateStream.cs | 37 +++++-------------- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIUpdateStream.cs b/osu.Game/Online/API/Requests/Responses/APIUpdateStream.cs index d9e48373bb..5af7d6a01c 100644 --- a/osu.Game/Online/API/Requests/Responses/APIUpdateStream.cs +++ b/osu.Game/Online/API/Requests/Responses/APIUpdateStream.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using Newtonsoft.Json; using osu.Framework.Graphics.Colour; using osuTK.Graphics; @@ -27,34 +28,16 @@ namespace osu.Game.Online.API.Requests.Responses public bool Equals(APIUpdateStream other) => Id == other?.Id; - public ColourInfo Colour + internal static readonly Dictionary KNOWN_STREAMS = new Dictionary { - get - { - switch (Name) - { - case "stable40": - return new Color4(102, 204, 255, 255); + ["stable40"] = new Color4(102, 204, 255, 255), + ["stable"] = new Color4(34, 153, 187, 255), + ["beta40"] = new Color4(255, 221, 85, 255), + ["cuttingedge"] = new Color4(238, 170, 0, 255), + [OsuGameBase.CLIENT_STREAM_NAME] = new Color4(237, 18, 33, 255), + ["web"] = new Color4(136, 102, 238, 255) + }; - case "stable": - return new Color4(34, 153, 187, 255); - - case "beta40": - return new Color4(255, 221, 85, 255); - - case "cuttingedge": - return new Color4(238, 170, 0, 255); - - case OsuGameBase.CLIENT_STREAM_NAME: - return new Color4(237, 18, 33, 255); - - case "web": - return new Color4(136, 102, 238, 255); - - default: - return new Color4(0, 0, 0, 255); - } - } - } + public ColourInfo Colour => KNOWN_STREAMS.TryGetValue(Name, out var colour) ? colour : new Color4(0, 0, 0, 255); } } From dacf6d5a34134b2d10070a0fc74232492e37e237 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 25 Dec 2020 19:28:33 +0100 Subject: [PATCH 576/818] Decouple changelog test scene from web --- .../Online/TestSceneChangelogOverlay.cs | 132 ++++++++++++------ 1 file changed, 92 insertions(+), 40 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs index 998e42b478..9f617d49da 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs @@ -1,8 +1,13 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; +using System.Linq; +using Humanizer; using NUnit.Framework; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; using osu.Game.Overlays.Changelog; @@ -12,15 +17,63 @@ namespace osu.Game.Tests.Visual.Online [TestFixture] public class TestSceneChangelogOverlay : OsuTestScene { + private DummyAPIAccess dummyAPI => (DummyAPIAccess)API; + + private readonly Dictionary streams; + private readonly Dictionary builds; + + private APIChangelogBuild requestedBuild; private TestChangelogOverlay changelog; - protected override bool UseOnlineAPI => true; + public TestSceneChangelogOverlay() + { + streams = APIUpdateStream.KNOWN_STREAMS.Keys.Select((stream, id) => new APIUpdateStream + { + Id = id + 1, + Name = stream, + DisplayName = stream.Humanize(), // not quite there, but good enough. + }).ToDictionary(stream => stream.Name); + + string version = DateTimeOffset.Now.ToString("yyyy.Mdd.0"); + builds = APIUpdateStream.KNOWN_STREAMS.Keys.Select(stream => new APIChangelogBuild + { + Version = version, + DisplayVersion = version, + UpdateStream = streams[stream], + ChangelogEntries = new List() + }).ToDictionary(build => build.UpdateStream.Name); + + foreach (var stream in streams.Values) + stream.LatestBuild = builds[stream.Name]; + } [SetUp] - public void SetUp() => Schedule(() => + public void SetUp() { - Child = changelog = new TestChangelogOverlay(); - }); + requestedBuild = null; + + dummyAPI.HandleRequest = request => + { + switch (request) + { + case GetChangelogRequest changelogRequest: + var changelogResponse = new APIChangelogIndex + { + Streams = streams.Values.ToList(), + Builds = builds.Values.ToList() + }; + changelogRequest.TriggerSuccess(changelogResponse); + break; + + case GetChangelogBuildRequest buildRequest: + if (requestedBuild != null) + buildRequest.TriggerSuccess(requestedBuild); + break; + } + }; + + Schedule(() => Child = changelog = new TestChangelogOverlay()); + } [Test] public void ShowWithNoFetch() @@ -41,26 +94,22 @@ namespace osu.Game.Tests.Visual.Online } [Test] - [Ignore("needs to be updated to not be so server dependent")] public void ShowWithBuild() { - AddStep(@"Show with Lazer 2018.712.0", () => + showBuild(() => new APIChangelogBuild { - changelog.ShowBuild(new APIChangelogBuild + Version = "2018.712.0", + DisplayVersion = "2018.712.0", + UpdateStream = streams[OsuGameBase.CLIENT_STREAM_NAME], + ChangelogEntries = new List { - Version = "2018.712.0", - DisplayVersion = "2018.712.0", - UpdateStream = new APIUpdateStream { Id = 5, Name = OsuGameBase.CLIENT_STREAM_NAME }, - ChangelogEntries = new List + new APIChangelogEntry { - new APIChangelogEntry - { - Category = "Test", - Title = "Title", - MessageHtml = "Message", - } + Category = "Test", + Title = "Title", + MessageHtml = "Message", } - }); + } }); AddUntilStep(@"wait for streams", () => changelog.Streams?.Count > 0); @@ -71,35 +120,38 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestHTMLUnescaping() { - AddStep(@"Ensure HTML string unescaping", () => + showBuild(() => new APIChangelogBuild { - changelog.ShowBuild(new APIChangelogBuild + Version = "2019.920.0", + DisplayVersion = "2019.920.0", + UpdateStream = new APIUpdateStream { - Version = "2019.920.0", - DisplayVersion = "2019.920.0", - UpdateStream = new APIUpdateStream + Name = "Test", + DisplayName = "Test" + }, + ChangelogEntries = new List + { + new APIChangelogEntry { - Name = "Test", - DisplayName = "Test" - }, - ChangelogEntries = new List - { - new APIChangelogEntry + Category = "Testing HTML strings unescaping", + Title = "Ensuring HTML strings are being unescaped", + MessageHtml = """"This text should appear triple-quoted""" >_<", + GithubUser = new APIChangelogUser { - Category = "Testing HTML strings unescaping", - Title = "Ensuring HTML strings are being unescaped", - MessageHtml = """"This text should appear triple-quoted""" >_<", - GithubUser = new APIChangelogUser - { - DisplayName = "Dummy", - OsuUsername = "Dummy", - } - }, - } - }); + DisplayName = "Dummy", + OsuUsername = "Dummy", + } + }, + } }); } + private void showBuild(Func build) + { + AddStep("set up build", () => requestedBuild = build.Invoke()); + AddStep("show build", () => changelog.ShowBuild(requestedBuild)); + } + private class TestChangelogOverlay : ChangelogOverlay { public new List Streams => base.Streams; From 5f43299d3779ffbde32d3bd2f735592f30fad820 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 25 Dec 2020 19:49:41 +0100 Subject: [PATCH 577/818] Fix tests failing due to base logic firing It turns out that the changelog code was semi-intentionally relying on the request to get release streams to be slow to initially show the listing of all streams. Locally suppress the base tab control logic to fix this. --- osu.Game/Overlays/Changelog/ChangelogUpdateStreamControl.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Overlays/Changelog/ChangelogUpdateStreamControl.cs b/osu.Game/Overlays/Changelog/ChangelogUpdateStreamControl.cs index 509a6dabae..6bbff045b5 100644 --- a/osu.Game/Overlays/Changelog/ChangelogUpdateStreamControl.cs +++ b/osu.Game/Overlays/Changelog/ChangelogUpdateStreamControl.cs @@ -8,5 +8,11 @@ namespace osu.Game.Overlays.Changelog public class ChangelogUpdateStreamControl : OverlayStreamControl { protected override OverlayStreamItem CreateStreamItem(APIUpdateStream value) => new ChangelogUpdateStreamItem(value); + + protected override void LoadComplete() + { + // suppress base logic of immediately selecting first item if one exists + // (we always want to start with no stream selected). + } } } From 3ac618778f11010de62bedec719141319b37cc40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 25 Dec 2020 20:08:57 +0100 Subject: [PATCH 578/818] Handle all changelog entry types correctly --- .../Online/TestSceneChangelogOverlay.cs | 20 +++++++++++++++--- osu.Game/Overlays/Changelog/ChangelogBuild.cs | 21 ++++++++++++++++++- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs index 9f617d49da..42f822664f 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs @@ -105,9 +105,23 @@ namespace osu.Game.Tests.Visual.Online { new APIChangelogEntry { - Category = "Test", - Title = "Title", - MessageHtml = "Message", + Type = ChangelogEntryType.Fix, + Category = "osu!", + Title = "Fix thing", + MessageHtml = "Additional info goes here.", + }, + new APIChangelogEntry + { + Type = ChangelogEntryType.Add, + Category = "osu!", + Title = "Add thing", + Major = true + }, + new APIChangelogEntry + { + Type = ChangelogEntryType.Misc, + Category = "Code quality", + Title = "Clean up thing" } } }); diff --git a/osu.Game/Overlays/Changelog/ChangelogBuild.cs b/osu.Game/Overlays/Changelog/ChangelogBuild.cs index 65ff0fef92..6ec3f08a2b 100644 --- a/osu.Game/Overlays/Changelog/ChangelogBuild.cs +++ b/osu.Game/Overlays/Changelog/ChangelogBuild.cs @@ -84,7 +84,7 @@ namespace osu.Game.Overlays.Changelog Anchor = Anchor.CentreLeft, Origin = Anchor.CentreRight, Size = new Vector2(10), - Icon = entry.Type == ChangelogEntryType.Fix ? FontAwesome.Solid.Check : FontAwesome.Solid.Plus, + Icon = getIconForChangelogEntry(entry.Type), Colour = entryColour.Opacity(0.5f), Margin = new MarginPadding { Right = 5 }, }, @@ -186,6 +186,25 @@ namespace osu.Game.Overlays.Changelog } } + private static IconUsage getIconForChangelogEntry(ChangelogEntryType entryType) + { + // compare: https://github.com/ppy/osu-web/blob/master/resources/assets/coffee/react/_components/changelog-entry.coffee#L8-L11 + switch (entryType) + { + case ChangelogEntryType.Add: + return FontAwesome.Solid.Plus; + + case ChangelogEntryType.Fix: + return FontAwesome.Solid.Check; + + case ChangelogEntryType.Misc: + return FontAwesome.Regular.Circle; + + default: + throw new ArgumentOutOfRangeException(nameof(entryType), $"Unrecognised entry type {entryType}"); + } + } + protected virtual FillFlowContainer CreateHeader() => new FillFlowContainer { Anchor = Anchor.TopCentre, From 0aedc720f2c61a9f0d8d4223d3896259181ad071 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 25 Dec 2020 20:31:12 +0100 Subject: [PATCH 579/818] Extract changelog entry component --- osu.Game/Overlays/Changelog/ChangelogBuild.cs | 146 +------------ osu.Game/Overlays/Changelog/ChangelogEntry.cs | 202 ++++++++++++++++++ 2 files changed, 203 insertions(+), 145 deletions(-) create mode 100644 osu.Game/Overlays/Changelog/ChangelogEntry.cs diff --git a/osu.Game/Overlays/Changelog/ChangelogBuild.cs b/osu.Game/Overlays/Changelog/ChangelogBuild.cs index 6ec3f08a2b..2d071b7345 100644 --- a/osu.Game/Overlays/Changelog/ChangelogBuild.cs +++ b/osu.Game/Overlays/Changelog/ChangelogBuild.cs @@ -9,14 +9,8 @@ using osu.Game.Graphics.Containers; using osu.Game.Online.API.Requests.Responses; using System; using System.Linq; -using System.Text.RegularExpressions; using osu.Game.Graphics.Sprites; -using osu.Game.Users; -using osuTK.Graphics; using osu.Framework.Allocation; -using System.Net; -using osuTK; -using osu.Framework.Extensions.Color4Extensions; namespace osu.Game.Overlays.Changelog { @@ -63,145 +57,7 @@ namespace osu.Game.Overlays.Changelog Margin = new MarginPadding { Top = 35, Bottom = 15 }, }); - var fontLarge = OsuFont.GetFont(size: 16); - var fontMedium = OsuFont.GetFont(size: 12); - - foreach (var entry in categoryEntries) - { - var entryColour = entry.Major ? colours.YellowLight : Color4.White; - - LinkFlowContainer title; - - var titleContainer = new Container - { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Margin = new MarginPadding { Vertical = 5 }, - Children = new Drawable[] - { - new SpriteIcon - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreRight, - Size = new Vector2(10), - Icon = getIconForChangelogEntry(entry.Type), - Colour = entryColour.Opacity(0.5f), - Margin = new MarginPadding { Right = 5 }, - }, - title = new LinkFlowContainer - { - Direction = FillDirection.Full, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - TextAnchor = Anchor.BottomLeft, - } - } - }; - - title.AddText(entry.Title, t => - { - t.Font = fontLarge; - t.Colour = entryColour; - }); - - if (!string.IsNullOrEmpty(entry.Repository)) - { - title.AddText(" (", t => - { - t.Font = fontLarge; - t.Colour = entryColour; - }); - title.AddLink($"{entry.Repository.Replace("ppy/", "")}#{entry.GithubPullRequestId}", entry.GithubUrl, - creationParameters: t => - { - t.Font = fontLarge; - t.Colour = entryColour; - }); - title.AddText(")", t => - { - t.Font = fontLarge; - t.Colour = entryColour; - }); - } - - title.AddText("by ", t => - { - t.Font = fontMedium; - t.Colour = entryColour; - t.Padding = new MarginPadding { Left = 10 }; - }); - - if (entry.GithubUser != null) - { - if (entry.GithubUser.UserId != null) - { - title.AddUserLink(new User - { - Username = entry.GithubUser.OsuUsername, - Id = entry.GithubUser.UserId.Value - }, t => - { - t.Font = fontMedium; - t.Colour = entryColour; - }); - } - else if (entry.GithubUser.GithubUrl != null) - { - title.AddLink(entry.GithubUser.DisplayName, entry.GithubUser.GithubUrl, t => - { - t.Font = fontMedium; - t.Colour = entryColour; - }); - } - else - { - title.AddText(entry.GithubUser.DisplayName, t => - { - t.Font = fontMedium; - t.Colour = entryColour; - }); - } - } - - ChangelogEntries.Add(titleContainer); - - if (!string.IsNullOrEmpty(entry.MessageHtml)) - { - var message = new TextFlowContainer - { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - }; - - // todo: use markdown parsing once API returns markdown - message.AddText(WebUtility.HtmlDecode(Regex.Replace(entry.MessageHtml, @"<(.|\n)*?>", string.Empty)), t => - { - t.Font = fontMedium; - t.Colour = colourProvider.Foreground1; - }); - - ChangelogEntries.Add(message); - } - } - } - } - - private static IconUsage getIconForChangelogEntry(ChangelogEntryType entryType) - { - // compare: https://github.com/ppy/osu-web/blob/master/resources/assets/coffee/react/_components/changelog-entry.coffee#L8-L11 - switch (entryType) - { - case ChangelogEntryType.Add: - return FontAwesome.Solid.Plus; - - case ChangelogEntryType.Fix: - return FontAwesome.Solid.Check; - - case ChangelogEntryType.Misc: - return FontAwesome.Regular.Circle; - - default: - throw new ArgumentOutOfRangeException(nameof(entryType), $"Unrecognised entry type {entryType}"); + ChangelogEntries.AddRange(categoryEntries.Select(entry => new ChangelogEntry(entry))); } } diff --git a/osu.Game/Overlays/Changelog/ChangelogEntry.cs b/osu.Game/Overlays/Changelog/ChangelogEntry.cs new file mode 100644 index 0000000000..55edb40283 --- /dev/null +++ b/osu.Game/Overlays/Changelog/ChangelogEntry.cs @@ -0,0 +1,202 @@ +// 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.Net; +using System.Text.RegularExpressions; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Users; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Overlays.Changelog +{ + public class ChangelogEntry : FillFlowContainer + { + private readonly APIChangelogEntry entry; + + [Resolved] + private OsuColour colours { get; set; } + + [Resolved] + private OverlayColourProvider colourProvider { get; set; } + + private FontUsage fontLarge; + private FontUsage fontMedium; + + public ChangelogEntry(APIChangelogEntry entry) + { + this.entry = entry; + + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Direction = FillDirection.Vertical; + } + + [BackgroundDependencyLoader] + private void load() + { + fontLarge = OsuFont.GetFont(size: 16); + fontMedium = OsuFont.GetFont(size: 12); + + Children = new[] + { + createTitle(), + createMessage() + }; + } + + private Drawable createTitle() + { + var entryColour = entry.Major ? colours.YellowLight : Color4.White; + + LinkFlowContainer title; + + var titleContainer = new Container + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Margin = new MarginPadding { Vertical = 5 }, + Children = new Drawable[] + { + new SpriteIcon + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreRight, + Size = new Vector2(10), + Icon = getIconForChangelogEntry(entry.Type), + Colour = entryColour.Opacity(0.5f), + Margin = new MarginPadding { Right = 5 }, + }, + title = new LinkFlowContainer + { + Direction = FillDirection.Full, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + TextAnchor = Anchor.BottomLeft, + } + } + }; + + title.AddText(entry.Title, t => + { + t.Font = fontLarge; + t.Colour = entryColour; + }); + + if (!string.IsNullOrEmpty(entry.Repository)) + addRepositoryReference(title, entryColour); + + if (entry.GithubUser != null) + addGithubAuthorReference(title, entryColour); + + return titleContainer; + } + + private void addRepositoryReference(LinkFlowContainer title, Color4 entryColour) + { + title.AddText(" (", t => + { + t.Font = fontLarge; + t.Colour = entryColour; + }); + title.AddLink($"{entry.Repository.Replace("ppy/", "")}#{entry.GithubPullRequestId}", entry.GithubUrl, + t => + { + t.Font = fontLarge; + t.Colour = entryColour; + }); + title.AddText(")", t => + { + t.Font = fontLarge; + t.Colour = entryColour; + }); + } + + private void addGithubAuthorReference(LinkFlowContainer title, Color4 entryColour) + { + title.AddText("by ", t => + { + t.Font = fontMedium; + t.Colour = entryColour; + t.Padding = new MarginPadding { Left = 10 }; + }); + + if (entry.GithubUser.UserId != null) + { + title.AddUserLink(new User + { + Username = entry.GithubUser.OsuUsername, + Id = entry.GithubUser.UserId.Value + }, t => + { + t.Font = fontMedium; + t.Colour = entryColour; + }); + } + else if (entry.GithubUser.GithubUrl != null) + { + title.AddLink(entry.GithubUser.DisplayName, entry.GithubUser.GithubUrl, t => + { + t.Font = fontMedium; + t.Colour = entryColour; + }); + } + else + { + title.AddText(entry.GithubUser.DisplayName, t => + { + t.Font = fontMedium; + t.Colour = entryColour; + }); + } + } + + private Drawable createMessage() + { + if (string.IsNullOrEmpty(entry.MessageHtml)) + return Empty(); + + var message = new TextFlowContainer + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + }; + + // todo: use markdown parsing once API returns markdown + message.AddText(WebUtility.HtmlDecode(Regex.Replace(entry.MessageHtml, @"<(.|\n)*?>", string.Empty)), t => + { + t.Font = fontMedium; + t.Colour = colourProvider.Foreground1; + }); + + return message; + } + + private static IconUsage getIconForChangelogEntry(ChangelogEntryType entryType) + { + // compare: https://github.com/ppy/osu-web/blob/master/resources/assets/coffee/react/_components/changelog-entry.coffee#L8-L11 + switch (entryType) + { + case ChangelogEntryType.Add: + return FontAwesome.Solid.Plus; + + case ChangelogEntryType.Fix: + return FontAwesome.Solid.Check; + + case ChangelogEntryType.Misc: + return FontAwesome.Regular.Circle; + + default: + throw new ArgumentOutOfRangeException(nameof(entryType), $"Unrecognised entry type {entryType}"); + } + } + } +} From c32fc05f69435a8b5adc6b7c715ce57b75cb404e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 25 Dec 2020 20:41:35 +0100 Subject: [PATCH 580/818] Improve test scene coverage of corner cases --- .../Online/TestSceneChangelogOverlay.cs | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs index 42f822664f..eef2892290 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs @@ -109,19 +109,43 @@ namespace osu.Game.Tests.Visual.Online Category = "osu!", Title = "Fix thing", MessageHtml = "Additional info goes here.", + Repository = "osu", + GithubPullRequestId = 11100, + GithubUser = new APIChangelogUser + { + OsuUsername = "smoogipoo", + UserId = 1040328 + } }, new APIChangelogEntry { Type = ChangelogEntryType.Add, Category = "osu!", Title = "Add thing", - Major = true + Major = true, + Repository = "ppy/osu-framework", + GithubPullRequestId = 4444, + GithubUser = new APIChangelogUser + { + DisplayName = "frenzibyte", + GithubUrl = "https://github.com/frenzibyte" + } }, new APIChangelogEntry { Type = ChangelogEntryType.Misc, Category = "Code quality", - Title = "Clean up thing" + Title = "Clean up thing", + GithubUser = new APIChangelogUser + { + DisplayName = "some dude" + } + }, + new APIChangelogEntry + { + Type = ChangelogEntryType.Misc, + Category = "Code quality", + Title = "Clean up another thing" } } }); From 09b0a57290fe224f9a45bd759ae4c85856c3f518 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 25 Dec 2020 20:44:19 +0100 Subject: [PATCH 581/818] Schedule all of setup to avoid headless test fail --- osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs index eef2892290..cd2c4e9346 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs @@ -48,7 +48,7 @@ namespace osu.Game.Tests.Visual.Online } [SetUp] - public void SetUp() + public void SetUp() => Schedule(() => { requestedBuild = null; @@ -72,8 +72,8 @@ namespace osu.Game.Tests.Visual.Online } }; - Schedule(() => Child = changelog = new TestChangelogOverlay()); - } + Child = changelog = new TestChangelogOverlay(); + }); [Test] public void ShowWithNoFetch() From e0198c36aeebfddea645a9386a66a7439568d3d4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 26 Dec 2020 09:48:13 +0900 Subject: [PATCH 582/818] Fix user population happening in single file --- osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs index e422e982ae..6d253d5992 100644 --- a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs @@ -108,8 +108,7 @@ namespace osu.Game.Online.Multiplayer Debug.Assert(Room != null); - foreach (var user in Room.Users) - await PopulateUser(user); + await Task.WhenAll(Room.Users.Select(PopulateUser)); updateLocalRoomSettings(Room.Settings); } From 5ce5b6cec06403b2bbbeea6864f97cf0ebca0bc0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 26 Dec 2020 10:25:16 +0900 Subject: [PATCH 583/818] Fix non-safe thread access to room users on room join --- .../Multiplayer/StatefulMultiplayerClient.cs | 44 ++++++++++++++++--- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs index 6d253d5992..ec0967df75 100644 --- a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs @@ -4,8 +4,10 @@ #nullable enable using System; +using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Threading; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -108,7 +110,9 @@ namespace osu.Game.Online.Multiplayer Debug.Assert(Room != null); - await Task.WhenAll(Room.Users.Select(PopulateUser)); + var users = getRoomUsers(); + + await Task.WhenAll(users.Select(PopulateUser)); updateLocalRoomSettings(Room.Settings); } @@ -122,13 +126,16 @@ namespace osu.Game.Online.Multiplayer public virtual Task LeaveRoom() { - if (Room == null) - return Task.CompletedTask; + Schedule(() => + { + if (Room == null) + return; - apiRoom = null; - Room = null; + apiRoom = null; + Room = null; - Schedule(() => RoomChanged?.Invoke()); + RoomChanged?.Invoke(); + }); return Task.CompletedTask; } @@ -360,6 +367,31 @@ namespace osu.Game.Online.Multiplayer /// The to populate. protected async Task PopulateUser(MultiplayerRoomUser multiplayerUser) => multiplayerUser.User ??= await userLookupCache.GetUserAsync(multiplayerUser.UserID); + /// + /// Retrieve a copy of users currently in the joined in a thread-safe manner. + /// This should be used whenever accessing users from outside of an Update thread context (ie. when not calling ). + /// + /// A copy of users in the current room, or null if unavailable. + private List? getRoomUsers() + { + List? users = null; + + ManualResetEventSlim resetEvent = new ManualResetEventSlim(); + + // at some point we probably want to replace all these schedule calls with Room.LockForUpdate. + // for now, as this would require quite some consideration due to the number of accesses to the room instance, + // let's just to a schedule for the non-scheduled usages instead. + Schedule(() => + { + users = Room?.Users.ToList(); + resetEvent.Set(); + }); + + resetEvent.Wait(100); + + return users; + } + /// /// Updates the local room settings with the given . /// From f9900720d58e9b5e61f48f3a83643b8f2c11b88c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 26 Dec 2020 10:48:55 +0900 Subject: [PATCH 584/818] Rename OnRoomChanged to OnRoomUpdated to avoid confusion --- .../Multiplayer/StatefulMultiplayerClient.cs | 16 ++++++++-------- .../Multiplayer/Match/MultiplayerReadyButton.cs | 4 ++-- .../Multiplayer/MultiplayerRoomComposite.cs | 11 +++++++---- .../Multiplayer/Participants/ParticipantPanel.cs | 4 ++-- .../Multiplayer/Participants/ParticipantsList.cs | 4 ++-- 5 files changed, 21 insertions(+), 18 deletions(-) diff --git a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs index ec0967df75..8839b79c13 100644 --- a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs @@ -33,7 +33,7 @@ namespace osu.Game.Online.Multiplayer /// /// Invoked when any change occurs to the multiplayer room. /// - public event Action? RoomChanged; + public event Action? RoomUpdated; /// /// Invoked when the multiplayer server requests the current beatmap to be loaded into play. @@ -134,7 +134,7 @@ namespace osu.Game.Online.Multiplayer apiRoom = null; Room = null; - RoomChanged?.Invoke(); + RoomUpdated?.Invoke(); }); return Task.CompletedTask; @@ -214,7 +214,7 @@ namespace osu.Game.Online.Multiplayer break; } - RoomChanged?.Invoke(); + RoomUpdated?.Invoke(); }); return Task.CompletedTask; @@ -238,7 +238,7 @@ namespace osu.Game.Online.Multiplayer Room.Users.Add(user); - RoomChanged?.Invoke(); + RoomUpdated?.Invoke(); }); } @@ -255,7 +255,7 @@ namespace osu.Game.Online.Multiplayer Room.Users.Remove(user); PlayingUsers.Remove(user.UserID); - RoomChanged?.Invoke(); + RoomUpdated?.Invoke(); }); return Task.CompletedTask; @@ -278,7 +278,7 @@ namespace osu.Game.Online.Multiplayer Room.Host = user; apiRoom.Host.Value = user?.User; - RoomChanged?.Invoke(); + RoomUpdated?.Invoke(); }); return Task.CompletedTask; @@ -305,7 +305,7 @@ namespace osu.Game.Online.Multiplayer if (state != MultiplayerUserState.Playing) PlayingUsers.Remove(userId); - RoomChanged?.Invoke(); + RoomUpdated?.Invoke(); }); return Task.CompletedTask; @@ -419,7 +419,7 @@ namespace osu.Game.Online.Multiplayer // In-order for the client to not display an outdated beatmap, the playlist is forcefully cleared here. apiRoom.Playlist.Clear(); - RoomChanged?.Invoke(); + RoomUpdated?.Invoke(); var req = new GetBeatmapSetRequest(settings.BeatmapID, BeatmapSetLookupType.BeatmapId); req.Success += res => updatePlaylist(settings, res); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index 15d6ef8aff..975a2cf023 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -56,9 +56,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match sampleReadyCount = audio.Samples.Get(@"SongSelect/select-difficulty"); } - protected override void OnRoomChanged() + protected override void OnRoomUpdated() { - base.OnRoomChanged(); + base.OnRoomUpdated(); localUser = Room?.Users.Single(u => u.User?.Id == api.LocalUser.Value.Id); button.Enabled.Value = Client.Room?.State == MultiplayerRoomState.Open; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomComposite.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomComposite.cs index ac608a13d4..8030107ad8 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomComposite.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomComposite.cs @@ -19,18 +19,21 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { base.LoadComplete(); - Client.RoomChanged += OnRoomChanged; - OnRoomChanged(); + Client.RoomUpdated += OnRoomUpdated; + OnRoomUpdated(); } - protected virtual void OnRoomChanged() + /// + /// Invoked when any change occurs to the multiplayer room. + /// + protected virtual void OnRoomUpdated() { } protected override void Dispose(bool isDisposing) { if (Client != null) - Client.RoomChanged -= OnRoomChanged; + Client.RoomUpdated -= OnRoomUpdated; base.Dispose(isDisposing); } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs index 044afa7445..de3069b2f6 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs @@ -135,9 +135,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants }; } - protected override void OnRoomChanged() + protected override void OnRoomUpdated() { - base.OnRoomChanged(); + base.OnRoomUpdated(); if (Room == null) return; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantsList.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantsList.cs index b9ac096c4a..3759e45f18 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantsList.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantsList.cs @@ -36,9 +36,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants }; } - protected override void OnRoomChanged() + protected override void OnRoomUpdated() { - base.OnRoomChanged(); + base.OnRoomUpdated(); if (Room == null) panels.Clear(); From fe1bbb1cac671b65dca154784955314db434adbc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 26 Dec 2020 10:49:22 +0900 Subject: [PATCH 585/818] Don't fail if the local user is not present in room users when updating ready button state --- .../OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index 975a2cf023..281e92404c 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -60,7 +60,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { base.OnRoomUpdated(); - localUser = Room?.Users.Single(u => u.User?.Id == api.LocalUser.Value.Id); + // this method is called on leaving the room, so the local user may not exist in the room any more. + localUser = Room?.Users.SingleOrDefault(u => u.User?.Id == api.LocalUser.Value.Id); + button.Enabled.Value = Client.Room?.State == MultiplayerRoomState.Open; updateState(); } From e7339d6959ba8c94fd76ec2e5f2d0e9da0f5074d Mon Sep 17 00:00:00 2001 From: Neuheit <38368299+Neuheit@users.noreply.github.com> Date: Fri, 25 Dec 2020 21:07:33 -0500 Subject: [PATCH 586/818] fix(osu.Game): Ensure Category property is copied in Room. --- osu.Game/Online/Rooms/Room.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index 67f874cdc4..bb21971afa 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -130,6 +130,7 @@ namespace osu.Game.Online.Rooms { RoomID.Value = other.RoomID.Value; Name.Value = other.Name.Value; + Category.Value = other.Category.Value; if (other.Host.Value != null && Host.Value?.Id != other.Host.Value.Id) Host.Value = other.Host.Value; From ff57562956515a4895c6f50c0ec49746a2ad4963 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 26 Dec 2020 11:34:05 +0900 Subject: [PATCH 587/818] Fix multiplayer leaderboard not unsubscribing from quit users --- ...TestSceneMultiplayerGameplayLeaderboard.cs | 5 +- .../HUD/MultiplayerGameplayLeaderboard.cs | 61 ++++++++++++++----- 2 files changed, 48 insertions(+), 18 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs index 8078c7b994..98a3ce9b47 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs @@ -20,11 +20,12 @@ using osu.Game.Rulesets.Osu.Scoring; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Play.HUD; +using osu.Game.Tests.Visual.Multiplayer; using osu.Game.Tests.Visual.Online; namespace osu.Game.Tests.Visual.Gameplay { - public class TestSceneMultiplayerGameplayLeaderboard : OsuTestScene + public class TestSceneMultiplayerGameplayLeaderboard : MultiplayerTestScene { [Cached(typeof(SpectatorStreamingClient))] private TestMultiplayerStreaming streamingClient = new TestMultiplayerStreaming(16); @@ -47,7 +48,7 @@ namespace osu.Game.Tests.Visual.Gameplay } [SetUpSteps] - public void SetUpSteps() + public override void SetUpSteps() { AddStep("create leaderboard", () => { diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index c10ec9e004..ce6e19aea0 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -2,12 +2,16 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Collections.Specialized; +using System.Diagnostics; +using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Configuration; using osu.Game.Database; using osu.Game.Online.API; +using osu.Game.Online.Multiplayer; using osu.Game.Online.Spectator; using osu.Game.Rulesets.Scoring; @@ -18,10 +22,21 @@ namespace osu.Game.Screens.Play.HUD { private readonly ScoreProcessor scoreProcessor; - private readonly int[] userIds; - private readonly Dictionary userScores = new Dictionary(); + [Resolved] + private SpectatorStreamingClient streamingClient { get; set; } + + [Resolved] + private StatefulMultiplayerClient multiplayerClient { get; set; } + + [Resolved] + private UserLookupCache userLookupCache { get; set; } + + private Bindable scoringMode; + + private readonly BindableList playingUsers; + /// /// Construct a new leaderboard. /// @@ -33,32 +48,24 @@ namespace osu.Game.Screens.Play.HUD this.scoreProcessor = scoreProcessor; // todo: this will likely be passed in as User instances. - this.userIds = userIds; + playingUsers = new BindableList(userIds); } - [Resolved] - private SpectatorStreamingClient streamingClient { get; set; } - - [Resolved] - private UserLookupCache userLookupCache { get; set; } - - private Bindable scoringMode; - [BackgroundDependencyLoader] private void load(OsuConfigManager config, IAPIProvider api) { streamingClient.OnNewFrames += handleIncomingFrames; - foreach (var user in userIds) + foreach (var userId in playingUsers) { - streamingClient.WatchUser(user); + streamingClient.WatchUser(userId); // probably won't be required in the final implementation. - var resolvedUser = userLookupCache.GetUserAsync(user).Result; + var resolvedUser = userLookupCache.GetUserAsync(userId).Result; var trackedUser = new TrackedUserData(); - userScores[user] = trackedUser; + userScores[userId] = trackedUser; var leaderboardScore = AddPlayer(resolvedUser, resolvedUser.Id == api.LocalUser.Value.Id); ((IBindable)leaderboardScore.Accuracy).BindTo(trackedUser.Accuracy); @@ -70,6 +77,28 @@ namespace osu.Game.Screens.Play.HUD scoringMode.BindValueChanged(updateAllScores, true); } + protected override void LoadComplete() + { + base.LoadComplete(); + + playingUsers.BindCollectionChanged(usersChanged); + playingUsers.BindTo(multiplayerClient.PlayingUsers); + } + + private void usersChanged(object sender, NotifyCollectionChangedEventArgs e) + { + switch (e.Action) + { + case NotifyCollectionChangedAction.Remove: + foreach (var userId in e.OldItems.OfType()) + { + streamingClient.StopWatchingUser(userId); + } + + break; + } + } + private void updateAllScores(ValueChangedEvent mode) { foreach (var trackedData in userScores.Values) @@ -91,7 +120,7 @@ namespace osu.Game.Screens.Play.HUD if (streamingClient != null) { - foreach (var user in userIds) + foreach (var user in playingUsers) { streamingClient.StopWatchingUser(user); } From 116acc2b5e073dc6578ad1169ce109a09bc76d24 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 26 Dec 2020 11:35:51 +0900 Subject: [PATCH 588/818] Add flow for marking user as quit for further handling --- .../Play/HUD/MultiplayerGameplayLeaderboard.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index ce6e19aea0..a71c4685d9 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -93,6 +93,9 @@ namespace osu.Game.Screens.Play.HUD foreach (var userId in e.OldItems.OfType()) { streamingClient.StopWatchingUser(userId); + + if (userScores.TryGetValue(userId, out var trackedData)) + trackedData.MarkUserQuit(); } break; @@ -143,11 +146,19 @@ namespace osu.Game.Screens.Play.HUD private readonly BindableInt currentCombo = new BindableInt(); + public IBindable UserQuit => userQuit; + + private readonly BindableBool userQuit = new BindableBool(); + [CanBeNull] public FrameHeader LastHeader; + public void MarkUserQuit() => userQuit.Value = true; + public void UpdateScore(ScoreProcessor processor, ScoringMode mode) { + Debug.Assert(UserQuit.Value); + if (LastHeader == null) return; From 71dcbeaf7ce57eb0aae0cd72be4f3a74a2e505bd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 26 Dec 2020 11:43:10 +0900 Subject: [PATCH 589/818] Mark user as quit visually on the leaderboard --- .../Screens/Play/HUD/GameplayLeaderboardScore.cs | 13 +++++++++++++ osu.Game/Screens/Play/HUD/ILeaderboardScore.cs | 2 ++ .../Play/HUD/MultiplayerGameplayLeaderboard.cs | 12 ++++++++++-- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index 58281debf1..ed86f3241d 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -34,6 +34,7 @@ namespace osu.Game.Screens.Play.HUD public BindableDouble TotalScore { get; } = new BindableDouble(); public BindableDouble Accuracy { get; } = new BindableDouble(1); public BindableInt Combo { get; } = new BindableInt(); + public BindableBool HasQuit { get; } = new BindableBool(); private int? scorePosition; @@ -230,6 +231,15 @@ namespace osu.Game.Screens.Play.HUD TotalScore.BindValueChanged(v => scoreText.Text = v.NewValue.ToString("N0"), true); Accuracy.BindValueChanged(v => accuracyText.Text = v.NewValue.FormatAccuracy(), true); Combo.BindValueChanged(v => comboText.Text = $"{v.NewValue}x", true); + HasQuit.BindValueChanged(v => + { + if (v.NewValue) + { + // we will probably want to display this in a better way once we have a design. + // and also show states other than quit. + panelColour = Color4.Gray; + } + }, true); } protected override void LoadComplete() @@ -244,6 +254,9 @@ namespace osu.Game.Screens.Play.HUD private void updateColour() { + if (HasQuit.Value) + return; + if (scorePosition == 1) { mainFillContainer.ResizeWidthTo(EXTENDED_WIDTH, panel_transition_duration, Easing.OutElastic); diff --git a/osu.Game/Screens/Play/HUD/ILeaderboardScore.cs b/osu.Game/Screens/Play/HUD/ILeaderboardScore.cs index bc1a03c5aa..83b6f6621b 100644 --- a/osu.Game/Screens/Play/HUD/ILeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/ILeaderboardScore.cs @@ -10,5 +10,7 @@ namespace osu.Game.Screens.Play.HUD BindableDouble TotalScore { get; } BindableDouble Accuracy { get; } BindableInt Combo { get; } + + BindableBool HasQuit { get; } } } diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index a71c4685d9..6b0ca4d74c 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -71,6 +71,7 @@ namespace osu.Game.Screens.Play.HUD ((IBindable)leaderboardScore.Accuracy).BindTo(trackedUser.Accuracy); ((IBindable)leaderboardScore.TotalScore).BindTo(trackedUser.Score); ((IBindable)leaderboardScore.Combo).BindTo(trackedUser.CurrentCombo); + ((IBindable)leaderboardScore.HasQuit).BindTo(trackedUser.UserQuit); } scoringMode = config.GetBindable(OsuSetting.ScoreDisplayMode); @@ -81,8 +82,15 @@ namespace osu.Game.Screens.Play.HUD { base.LoadComplete(); - playingUsers.BindCollectionChanged(usersChanged); + // BindableList handles binding in a really bad way (Clear then AddRange) so we need to do this manually.. + foreach (int userId in playingUsers) + { + if (!multiplayerClient.PlayingUsers.Contains(userId)) + usersChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, new[] { userId })); + } + playingUsers.BindTo(multiplayerClient.PlayingUsers); + playingUsers.BindCollectionChanged(usersChanged); } private void usersChanged(object sender, NotifyCollectionChangedEventArgs e) @@ -157,7 +165,7 @@ namespace osu.Game.Screens.Play.HUD public void UpdateScore(ScoreProcessor processor, ScoringMode mode) { - Debug.Assert(UserQuit.Value); + Debug.Assert(!UserQuit.Value); if (LastHeader == null) return; From 2599e95335a95ac8ae6b4a9685ac074432089a60 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 26 Dec 2020 12:11:19 +0900 Subject: [PATCH 590/818] Add test coverage --- .../TestSceneMultiplayerGameplayLeaderboard.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs index 98a3ce9b47..c214a34fe3 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs @@ -27,8 +27,10 @@ namespace osu.Game.Tests.Visual.Gameplay { public class TestSceneMultiplayerGameplayLeaderboard : MultiplayerTestScene { + private const int users = 16; + [Cached(typeof(SpectatorStreamingClient))] - private TestMultiplayerStreaming streamingClient = new TestMultiplayerStreaming(16); + private TestMultiplayerStreaming streamingClient = new TestMultiplayerStreaming(users); [Cached(typeof(UserLookupCache))] private UserLookupCache lookupCache = new TestSceneCurrentlyPlayingDisplay.TestUserLookupCache(); @@ -59,6 +61,9 @@ namespace osu.Game.Tests.Visual.Gameplay streamingClient.Start(Beatmap.Value.BeatmapInfo.OnlineBeatmapID ?? 0); + Client.PlayingUsers.Clear(); + Client.PlayingUsers.AddRange(streamingClient.PlayingUsers); + Children = new Drawable[] { scoreProcessor = new OsuScoreProcessor(), @@ -82,6 +87,12 @@ namespace osu.Game.Tests.Visual.Gameplay AddRepeatStep("update state", () => streamingClient.RandomlyUpdateState(), 100); } + [Test] + public void TestUserQuit() + { + AddRepeatStep("mark user quit", () => Client.PlayingUsers.RemoveAt(0), users); + } + public class TestMultiplayerStreaming : SpectatorStreamingClient { public new BindableList PlayingUsers => (BindableList)base.PlayingUsers; From 966a2151e3c2af58e9cdb506ffe8637dc5ce9fe3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 26 Dec 2020 17:55:24 +0900 Subject: [PATCH 591/818] Ensure the previous leaderboard is removed --- .../Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs index c214a34fe3..975c54c3f6 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs @@ -54,6 +54,8 @@ namespace osu.Game.Tests.Visual.Gameplay { AddStep("create leaderboard", () => { + leaderboard?.Expire(); + OsuScoreProcessor scoreProcessor; Beatmap.Value = CreateWorkingBeatmap(Ruleset.Value); From dae27fefe433cefe70b1eb69be65e62b6c1f689b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 26 Dec 2020 10:59:14 +0100 Subject: [PATCH 592/818] Run user list copy inline if possible `getRoomUsers()` was not safe to call from the update thread, as evidenced by the test failures. This was due to the fact that the added reset event could never actually be set from within the method, as the wait was blocking the scheduled set from ever proceeding. Resolve by allowing the scheduled copy & set to run inline if on the update thread already. --- osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs index ec0967df75..06ed4b4a6c 100644 --- a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs @@ -380,12 +380,12 @@ namespace osu.Game.Online.Multiplayer // at some point we probably want to replace all these schedule calls with Room.LockForUpdate. // for now, as this would require quite some consideration due to the number of accesses to the room instance, - // let's just to a schedule for the non-scheduled usages instead. - Schedule(() => + // let's just add a manual schedule for the non-scheduled usages instead. + Scheduler.Add(() => { users = Room?.Users.ToList(); resetEvent.Set(); - }); + }, false); resetEvent.Wait(100); From 04d54c40dbdc65f08975f1ca065ddd4ca1b67d9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 26 Dec 2020 11:58:25 +0100 Subject: [PATCH 593/818] Allow all StatefulMultiplayerClient schedules to run inline Fixes test failures due to not allowing to do so, therefore inverting execution order in some cases - for example, calling JoinRoom(room); LeaveRoom(); on the update thread would invert execution order due to the first being unscheduled but the second being scheduled. --- .../Multiplayer/StatefulMultiplayerClient.cs | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs index 06ed4b4a6c..98b53e723c 100644 --- a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs @@ -126,7 +126,7 @@ namespace osu.Game.Online.Multiplayer public virtual Task LeaveRoom() { - Schedule(() => + Scheduler.Add(() => { if (Room == null) return; @@ -135,7 +135,7 @@ namespace osu.Game.Online.Multiplayer Room = null; RoomChanged?.Invoke(); - }); + }, false); return Task.CompletedTask; } @@ -190,7 +190,7 @@ namespace osu.Game.Online.Multiplayer if (Room == null) return Task.CompletedTask; - Schedule(() => + Scheduler.Add(() => { if (Room == null) return; @@ -215,7 +215,7 @@ namespace osu.Game.Online.Multiplayer } RoomChanged?.Invoke(); - }); + }, false); return Task.CompletedTask; } @@ -227,7 +227,7 @@ namespace osu.Game.Online.Multiplayer await PopulateUser(user); - Schedule(() => + Scheduler.Add(() => { if (Room == null) return; @@ -239,7 +239,7 @@ namespace osu.Game.Online.Multiplayer Room.Users.Add(user); RoomChanged?.Invoke(); - }); + }, false); } Task IMultiplayerClient.UserLeft(MultiplayerRoomUser user) @@ -247,7 +247,7 @@ namespace osu.Game.Online.Multiplayer if (Room == null) return Task.CompletedTask; - Schedule(() => + Scheduler.Add(() => { if (Room == null) return; @@ -256,7 +256,7 @@ namespace osu.Game.Online.Multiplayer PlayingUsers.Remove(user.UserID); RoomChanged?.Invoke(); - }); + }, false); return Task.CompletedTask; } @@ -266,7 +266,7 @@ namespace osu.Game.Online.Multiplayer if (Room == null) return Task.CompletedTask; - Schedule(() => + Scheduler.Add(() => { if (Room == null) return; @@ -279,7 +279,7 @@ namespace osu.Game.Online.Multiplayer apiRoom.Host.Value = user?.User; RoomChanged?.Invoke(); - }); + }, false); return Task.CompletedTask; } @@ -295,7 +295,7 @@ namespace osu.Game.Online.Multiplayer if (Room == null) return Task.CompletedTask; - Schedule(() => + Scheduler.Add(() => { if (Room == null) return; @@ -306,7 +306,7 @@ namespace osu.Game.Online.Multiplayer PlayingUsers.Remove(userId); RoomChanged?.Invoke(); - }); + }, false); return Task.CompletedTask; } @@ -316,13 +316,13 @@ namespace osu.Game.Online.Multiplayer if (Room == null) return Task.CompletedTask; - Schedule(() => + Scheduler.Add(() => { if (Room == null) return; LoadRequested?.Invoke(); - }); + }, false); return Task.CompletedTask; } @@ -332,7 +332,7 @@ namespace osu.Game.Online.Multiplayer if (Room == null) return Task.CompletedTask; - Schedule(() => + Scheduler.Add(() => { if (Room == null) return; @@ -340,7 +340,7 @@ namespace osu.Game.Online.Multiplayer PlayingUsers.AddRange(Room.Users.Where(u => u.State == MultiplayerUserState.Playing).Select(u => u.UserID)); MatchStarted?.Invoke(); - }); + }, false); return Task.CompletedTask; } @@ -350,13 +350,13 @@ namespace osu.Game.Online.Multiplayer if (Room == null) return Task.CompletedTask; - Schedule(() => + Scheduler.Add(() => { if (Room == null) return; ResultsReady?.Invoke(); - }); + }, false); return Task.CompletedTask; } @@ -404,7 +404,7 @@ namespace osu.Game.Online.Multiplayer if (Room == null) return; - Schedule(() => + Scheduler.Add(() => { if (Room == null) return; @@ -425,7 +425,7 @@ namespace osu.Game.Online.Multiplayer req.Success += res => updatePlaylist(settings, res); api.Queue(req); - }); + }, false); } private void updatePlaylist(MultiplayerRoomSettings settings, APIBeatmapSet onlineSet) From b9d725ab4928c0385b63da2d5ed5496b0faee21a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 26 Dec 2020 20:13:28 +0900 Subject: [PATCH 594/818] Don't copy spotlight category --- osu.Game/Online/Rooms/Room.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index bb21971afa..763ba25d52 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -130,7 +130,9 @@ namespace osu.Game.Online.Rooms { RoomID.Value = other.RoomID.Value; Name.Value = other.Name.Value; - Category.Value = other.Category.Value; + + if (other.Category.Value != RoomCategory.Spotlight) + Category.Value = other.Category.Value; if (other.Host.Value != null && Host.Value?.Id != other.Host.Value.Id) Host.Value = other.Host.Value; From 0b42b4b95598dcb067a4d06b4b734a0e468204c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 26 Dec 2020 13:54:10 +0100 Subject: [PATCH 595/818] Rename {Drawable -> Clickable}Avatar --- osu.Game/Online/Leaderboards/LeaderboardScore.cs | 4 ++-- osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs | 4 ++-- .../Drawables/{DrawableAvatar.cs => ClickableAvatar.cs} | 7 ++++--- osu.Game/Users/Drawables/UpdateableAvatar.cs | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) rename osu.Game/Users/Drawables/{DrawableAvatar.cs => ClickableAvatar.cs} (90%) diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index d8207aa8f4..5608002513 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -78,7 +78,7 @@ namespace osu.Game.Online.Leaderboards statisticsLabels = GetStatistics(score).Select(s => new ScoreComponentLabel(s)).ToList(); - DrawableAvatar innerAvatar; + ClickableAvatar innerAvatar; Children = new Drawable[] { @@ -115,7 +115,7 @@ namespace osu.Game.Online.Leaderboards Children = new[] { avatar = new DelayedLoadWrapper( - innerAvatar = new DrawableAvatar(user) + innerAvatar = new ClickableAvatar(user) { RelativeSizeAxes = Axes.Both, CornerRadius = corner_radius, diff --git a/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs b/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs index 5b428a3825..00f46b0035 100644 --- a/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs @@ -25,7 +25,7 @@ namespace osu.Game.Overlays.Chat.Tabs if (value.Type != ChannelType.PM) throw new ArgumentException("Argument value needs to have the targettype user!"); - DrawableAvatar avatar; + ClickableAvatar avatar; AddRange(new Drawable[] { @@ -48,7 +48,7 @@ namespace osu.Game.Overlays.Chat.Tabs Anchor = Anchor.Centre, Origin = Anchor.Centre, Masking = true, - Child = new DelayedLoadWrapper(avatar = new DrawableAvatar(value.Users.First()) + Child = new DelayedLoadWrapper(avatar = new ClickableAvatar(value.Users.First()) { RelativeSizeAxes = Axes.Both, OpenOnClick = { Value = false }, diff --git a/osu.Game/Users/Drawables/DrawableAvatar.cs b/osu.Game/Users/Drawables/ClickableAvatar.cs similarity index 90% rename from osu.Game/Users/Drawables/DrawableAvatar.cs rename to osu.Game/Users/Drawables/ClickableAvatar.cs index 42d2dbb1c6..61af2d8e27 100644 --- a/osu.Game/Users/Drawables/DrawableAvatar.cs +++ b/osu.Game/Users/Drawables/ClickableAvatar.cs @@ -14,7 +14,7 @@ using osu.Game.Graphics.Containers; namespace osu.Game.Users.Drawables { [LongRunningLoad] - public class DrawableAvatar : Container + public class ClickableAvatar : Container { /// /// Whether to open the user's profile when clicked. @@ -27,10 +27,11 @@ namespace osu.Game.Users.Drawables private OsuGame game { get; set; } /// - /// An avatar for specified user. + /// A clickable avatar for specified user, with UI sounds included. + /// If is true, clicking will open the user's profile. /// /// The user. A null value will get a placeholder avatar. - public DrawableAvatar(User user = null) + public ClickableAvatar(User user = null) { this.user = user; } diff --git a/osu.Game/Users/Drawables/UpdateableAvatar.cs b/osu.Game/Users/Drawables/UpdateableAvatar.cs index 171462f3fc..4772207edf 100644 --- a/osu.Game/Users/Drawables/UpdateableAvatar.cs +++ b/osu.Game/Users/Drawables/UpdateableAvatar.cs @@ -65,7 +65,7 @@ namespace osu.Game.Users.Drawables if (user == null && !ShowGuestOnNull) return null; - var avatar = new DrawableAvatar(user) + var avatar = new ClickableAvatar(user) { RelativeSizeAxes = Axes.Both, }; From e8f96b24013827b5c173159b960a0443d18ee87c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 26 Dec 2020 14:02:53 +0100 Subject: [PATCH 596/818] Bring back DrawableAvatar as a simple sprite --- osu.Game/Users/Drawables/ClickableAvatar.cs | 22 ++---------- osu.Game/Users/Drawables/DrawableAvatar.cs | 39 +++++++++++++++++++++ 2 files changed, 42 insertions(+), 19 deletions(-) create mode 100644 osu.Game/Users/Drawables/DrawableAvatar.cs diff --git a/osu.Game/Users/Drawables/ClickableAvatar.cs b/osu.Game/Users/Drawables/ClickableAvatar.cs index 61af2d8e27..0fca9c7c9b 100644 --- a/osu.Game/Users/Drawables/ClickableAvatar.cs +++ b/osu.Game/Users/Drawables/ClickableAvatar.cs @@ -1,19 +1,16 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Input.Events; using osu.Game.Graphics.Containers; namespace osu.Game.Users.Drawables { - [LongRunningLoad] public class ClickableAvatar : Container { /// @@ -27,7 +24,7 @@ namespace osu.Game.Users.Drawables private OsuGame game { get; set; } /// - /// A clickable avatar for specified user, with UI sounds included. + /// A clickable avatar for the specified user, with UI sounds included. /// If is true, clicking will open the user's profile. /// /// The user. A null value will get a placeholder avatar. @@ -39,28 +36,15 @@ namespace osu.Game.Users.Drawables [BackgroundDependencyLoader] private void load(LargeTextureStore textures) { - if (textures == null) - throw new ArgumentNullException(nameof(textures)); - - Texture texture = null; - if (user != null && user.Id > 1) texture = textures.Get($@"https://a.ppy.sh/{user.Id}"); - texture ??= textures.Get(@"Online/avatar-guest"); - ClickableArea clickableArea; Add(clickableArea = new ClickableArea { RelativeSizeAxes = Axes.Both, - Child = new Sprite - { - RelativeSizeAxes = Axes.Both, - Texture = texture, - FillMode = FillMode.Fit, - Anchor = Anchor.Centre, - Origin = Anchor.Centre - }, Action = openProfile }); + LoadComponentAsync(new DrawableAvatar(user), clickableArea.Add); + clickableArea.Enabled.BindTo(OpenOnClick); } diff --git a/osu.Game/Users/Drawables/DrawableAvatar.cs b/osu.Game/Users/Drawables/DrawableAvatar.cs new file mode 100644 index 0000000000..81f6ad52d9 --- /dev/null +++ b/osu.Game/Users/Drawables/DrawableAvatar.cs @@ -0,0 +1,39 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; + +namespace osu.Game.Users.Drawables +{ + [LongRunningLoad] + public class DrawableAvatar : Sprite + { + private readonly User user; + + /// + /// A simple, non-interactable avatar sprite for the specified user. + /// + /// The user. A null value will get a placeholder avatar. + public DrawableAvatar(User user = null) + { + this.user = user; + + RelativeSizeAxes = Axes.Both; + FillMode = FillMode.Fit; + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + } + + [BackgroundDependencyLoader] + private void load(LargeTextureStore textures) + { + if (user != null && user.Id > 1) + Texture = textures.Get($@"https://a.ppy.sh/{user.Id}"); + + Texture ??= textures.Get(@"Online/avatar-guest"); + } + } +} From 8ec7970b6ab10a028d4066571255587e95baf29f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 26 Dec 2020 14:06:09 +0100 Subject: [PATCH 597/818] Move load-complete fade specification inside --- osu.Game/Users/Drawables/DrawableAvatar.cs | 6 ++++++ osu.Game/Users/Drawables/UpdateableAvatar.cs | 1 - 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Users/Drawables/DrawableAvatar.cs b/osu.Game/Users/Drawables/DrawableAvatar.cs index 81f6ad52d9..3dae3afe3f 100644 --- a/osu.Game/Users/Drawables/DrawableAvatar.cs +++ b/osu.Game/Users/Drawables/DrawableAvatar.cs @@ -35,5 +35,11 @@ namespace osu.Game.Users.Drawables Texture ??= textures.Get(@"Online/avatar-guest"); } + + protected override void LoadComplete() + { + base.LoadComplete(); + this.FadeInFromZero(300, Easing.OutQuint); + } } } diff --git a/osu.Game/Users/Drawables/UpdateableAvatar.cs b/osu.Game/Users/Drawables/UpdateableAvatar.cs index 4772207edf..927e48cb56 100644 --- a/osu.Game/Users/Drawables/UpdateableAvatar.cs +++ b/osu.Game/Users/Drawables/UpdateableAvatar.cs @@ -70,7 +70,6 @@ namespace osu.Game.Users.Drawables RelativeSizeAxes = Axes.Both, }; - avatar.OnLoadComplete += d => d.FadeInFromZero(300, Easing.OutQuint); avatar.OpenOnClick.BindTo(OpenOnClick); return avatar; From 15948de2f0cbe1c64abf0b6a4a9fba813c047a2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 26 Dec 2020 14:06:23 +0100 Subject: [PATCH 598/818] Fix gameplay leaderboard avatars being clickable --- .../Screens/Play/HUD/GameplayLeaderboardScore.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index 58281debf1..51b19a8d45 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -78,6 +78,8 @@ namespace osu.Game.Screens.Play.HUD [BackgroundDependencyLoader] private void load(OsuColour colours) { + Container avatarContainer; + InternalChildren = new Drawable[] { mainFillContainer = new Container @@ -152,7 +154,7 @@ namespace osu.Game.Screens.Play.HUD Spacing = new Vector2(4f, 0f), Children = new Drawable[] { - new CircularContainer + avatarContainer = new CircularContainer { Masking = true, Anchor = Anchor.CentreLeft, @@ -166,11 +168,7 @@ namespace osu.Game.Screens.Play.HUD Alpha = 0.3f, RelativeSizeAxes = Axes.Both, Colour = colours.Gray4, - }, - new UpdateableAvatar(User) - { - RelativeSizeAxes = Axes.Both, - }, + } } }, usernameText = new OsuSpriteText @@ -227,6 +225,8 @@ namespace osu.Game.Screens.Play.HUD } }; + LoadComponentAsync(new DrawableAvatar(User), avatarContainer.Add); + TotalScore.BindValueChanged(v => scoreText.Text = v.NewValue.ToString("N0"), true); Accuracy.BindValueChanged(v => accuracyText.Text = v.NewValue.FormatAccuracy(), true); Combo.BindValueChanged(v => comboText.Text = $"{v.NewValue}x", true); From 9e15dccc56bfcdde0f36a6c15db413faff07b06b Mon Sep 17 00:00:00 2001 From: Shivam Date: Sat, 26 Dec 2020 15:36:21 +0100 Subject: [PATCH 599/818] Move graceful exit to OsuGameBase --- osu.Game/OsuGame.cs | 12 ------------ osu.Game/OsuGameBase.cs | 12 ++++++++++++ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index bb638bcf3a..710dfd7031 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -918,18 +918,6 @@ namespace osu.Game return base.OnExiting(); } - /// - /// Use to programatically exit the game as if the user was triggering via alt-f4. - /// Will keep persisting until an exit occurs (exit may be blocked multiple times). - /// - public void GracefullyExit() - { - if (!OnExiting()) - Exit(); - else - Scheduler.AddDelayed(GracefullyExit, 2000); - } - protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index e7b5d3304d..91ab2bdc1e 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -368,6 +368,18 @@ namespace osu.Game LocalConfig ??= new OsuConfigManager(Storage); } + /// + /// Use to programatically exit the game as if the user was triggering via alt-f4. + /// Will keep persisting until an exit occurs (exit may be blocked multiple times). + /// + public void GracefullyExit() + { + if (!OnExiting()) + Exit(); + else + Scheduler.AddDelayed(GracefullyExit, 2000); + } + protected override Storage CreateStorage(GameHost host, Storage defaultStorage) => new OsuStorage(host, defaultStorage); private readonly List fileImporters = new List(); From 8e428353ee0ae94e7efd7995ddbd01317842b7f7 Mon Sep 17 00:00:00 2001 From: Shivam Date: Sat, 26 Dec 2020 15:44:59 +0100 Subject: [PATCH 600/818] Revise TournamentSwitcher to include a close button --- osu.Game.Tournament/Screens/SetupScreen.cs | 44 ++++++++++++++++++---- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tournament/Screens/SetupScreen.cs b/osu.Game.Tournament/Screens/SetupScreen.cs index 3c9d3c949b..de3321397e 100644 --- a/osu.Game.Tournament/Screens/SetupScreen.cs +++ b/osu.Game.Tournament/Screens/SetupScreen.cs @@ -42,6 +42,9 @@ namespace osu.Game.Tournament.Screens [Resolved] private RulesetStore rulesets { get; set; } + [Resolved] + private TournamentGameBase game { get; set; } + [Resolved(canBeNull: true)] private TournamentSceneManager sceneManager { get; set; } @@ -68,9 +71,6 @@ namespace osu.Game.Tournament.Screens reload(); } - [Resolved] - private Framework.Game game { get; set; } - private void reload() { var fileBasedIpc = ipc as FileBasedIPC; @@ -115,12 +115,14 @@ namespace osu.Game.Tournament.Screens Items = rulesets.AvailableRulesets, Current = LadderInfo.Ruleset, }, - new LabelledDropdown + new TournamentSwitcher { Label = "Current tournament", Description = "Changes the background videos and bracket to match the selected tournament. This requires a restart to apply changes.", Items = storage.ListTournaments(), Current = storage.CurrentTournament, + ButtonText = "Close osu!", + Action = () => game.GracefullyExit() }, resolution = new ResolutionSelector { @@ -165,7 +167,7 @@ namespace osu.Game.Tournament.Screens private class ActionableInfo : LabelledDrawable { - private OsuButton button; + protected OsuButton Button; public ActionableInfo() : base(true) @@ -174,7 +176,7 @@ namespace osu.Game.Tournament.Screens public string ButtonText { - set => button.Text = value; + set => Button.Text = value; } public string Value @@ -211,7 +213,7 @@ namespace osu.Game.Tournament.Screens Spacing = new Vector2(10, 0), Children = new Drawable[] { - button = new TriangleButton + Button = new TriangleButton { Size = new Vector2(100, 40), Action = () => Action?.Invoke() @@ -222,6 +224,34 @@ namespace osu.Game.Tournament.Screens }; } + private class TournamentSwitcher : ActionableInfo + { + private OsuDropdown dropdown; + + public IEnumerable Items + { + get => dropdown.Items; + set => dropdown.Items = value; + } + + public Bindable Current + { + get => dropdown.Current; + set => dropdown.Current = value; + } + + protected override Drawable CreateComponent() + { + var drawable = base.CreateComponent(); + FlowContainer.Insert(-1, dropdown = new OsuDropdown + { + Width = 510 + }); + + return drawable; + } + } + private class ResolutionSelector : ActionableInfo { private const int minimum_window_height = 480; From fa0576f47f05faa4631e7a00a5a50b4c50d38eb1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 27 Dec 2020 13:40:02 +0900 Subject: [PATCH 601/818] Move quit colour change implementation to updateColour for better coverage --- .../Play/HUD/GameplayLeaderboardScore.cs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index ed86f3241d..4aeb65bb01 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -231,15 +231,7 @@ namespace osu.Game.Screens.Play.HUD TotalScore.BindValueChanged(v => scoreText.Text = v.NewValue.ToString("N0"), true); Accuracy.BindValueChanged(v => accuracyText.Text = v.NewValue.FormatAccuracy(), true); Combo.BindValueChanged(v => comboText.Text = $"{v.NewValue}x", true); - HasQuit.BindValueChanged(v => - { - if (v.NewValue) - { - // we will probably want to display this in a better way once we have a design. - // and also show states other than quit. - panelColour = Color4.Gray; - } - }, true); + HasQuit.BindValueChanged(v => updateColour()); } protected override void LoadComplete() @@ -255,7 +247,14 @@ namespace osu.Game.Screens.Play.HUD private void updateColour() { if (HasQuit.Value) + { + // we will probably want to display this in a better way once we have a design. + // and also show states other than quit. + mainFillContainer.ResizeWidthTo(regular_width, panel_transition_duration, Easing.OutElastic); + panelColour = Color4.Gray; + textColour = Color4.White; return; + } if (scorePosition == 1) { From d14a8d24b5c3cb7669a3b7537ba69820aa1e0fa4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 27 Dec 2020 16:42:20 +0900 Subject: [PATCH 602/818] Remove assert for now --- osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index 6b0ca4d74c..42df0cfe8c 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -165,8 +165,6 @@ namespace osu.Game.Screens.Play.HUD public void UpdateScore(ScoreProcessor processor, ScoringMode mode) { - Debug.Assert(!UserQuit.Value); - if (LastHeader == null) return; From 1b34f2115f6ef323aa66bf07d3e0b7ac05e76606 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 27 Dec 2020 16:57:23 +0900 Subject: [PATCH 603/818] Remove dignostics using --- osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index 42df0cfe8c..00e2b8bfa7 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Collections.Specialized; -using System.Diagnostics; using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; From 6b6b1514e2b7b2249db579297cfe94aa44a8b52d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 27 Dec 2020 12:58:37 +0100 Subject: [PATCH 604/818] Rename method to be less misleading As it doesn't only change colour, but also width. --- osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index 4aeb65bb01..1bd279922c 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -52,7 +52,7 @@ namespace osu.Game.Screens.Play.HUD positionText.Text = $"#{scorePosition.Value.FormatRank()}"; positionText.FadeTo(scorePosition.HasValue ? 1 : 0); - updateColour(); + updateState(); } } @@ -231,20 +231,20 @@ namespace osu.Game.Screens.Play.HUD TotalScore.BindValueChanged(v => scoreText.Text = v.NewValue.ToString("N0"), true); Accuracy.BindValueChanged(v => accuracyText.Text = v.NewValue.FormatAccuracy(), true); Combo.BindValueChanged(v => comboText.Text = $"{v.NewValue}x", true); - HasQuit.BindValueChanged(v => updateColour()); + HasQuit.BindValueChanged(v => updateState()); } protected override void LoadComplete() { base.LoadComplete(); - updateColour(); + updateState(); FinishTransforms(true); } private const double panel_transition_duration = 500; - private void updateColour() + private void updateState() { if (HasQuit.Value) { From f75dccc9e4872477c66a484ddc13ef943511e43f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 27 Dec 2020 13:00:27 +0100 Subject: [PATCH 605/818] Explicitly use discard in value changed callback --- osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index 1bd279922c..43e259695e 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -231,7 +231,7 @@ namespace osu.Game.Screens.Play.HUD TotalScore.BindValueChanged(v => scoreText.Text = v.NewValue.ToString("N0"), true); Accuracy.BindValueChanged(v => accuracyText.Text = v.NewValue.FormatAccuracy(), true); Combo.BindValueChanged(v => comboText.Text = $"{v.NewValue}x", true); - HasQuit.BindValueChanged(v => updateState()); + HasQuit.BindValueChanged(_ => updateState()); } protected override void LoadComplete() From 4d61c143db058bcc47bac98490dbaa246e873872 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 28 Dec 2020 14:56:29 +0900 Subject: [PATCH 606/818] Fix lookup cache throwing a null reference if no matches were successful --- osu.Game/Database/UserLookupCache.cs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/osu.Game/Database/UserLookupCache.cs b/osu.Game/Database/UserLookupCache.cs index 05d6930992..e7ddf4c567 100644 --- a/osu.Game/Database/UserLookupCache.cs +++ b/osu.Game/Database/UserLookupCache.cs @@ -72,6 +72,7 @@ namespace osu.Game.Database var request = new GetUsersRequest(userTasks.Keys.ToArray()); // rather than queueing, we maintain our own single-threaded request stream. + // todo: we probably want retry logic here. api.Perform(request); // Create a new request task if there's still more users to query. @@ -82,14 +83,19 @@ namespace osu.Game.Database createNewTask(); } - foreach (var user in request.Result.Users) - { - if (userTasks.TryGetValue(user.Id, out var tasks)) - { - foreach (var task in tasks) - task.SetResult(user); + List foundUsers = request.Result?.Users; - userTasks.Remove(user.Id); + if (foundUsers != null) + { + foreach (var user in foundUsers) + { + if (userTasks.TryGetValue(user.Id, out var tasks)) + { + foreach (var task in tasks) + task.SetResult(user); + + userTasks.Remove(user.Id); + } } } From 046a76cb1d1597414ae08e9e47a2dfa1dcac8e09 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 28 Dec 2020 14:56:53 +0900 Subject: [PATCH 607/818] Allow null users to still be displayed in the participant list The fix here is correcting the access of `user.Country`. The deicision to have null users display is because this is the best we can do (if osu-web could not resolve the user). We still want the users in the lobby to be aware of this user's presence, rather than hiding them from view. osu-stable does a similar thing, showing these users as `[Loading]`. I decided to go with blank names instead because having *any* text there causes confusion. We can iterate on this in future design updates. --- .../TestSceneMultiplayerParticipantsList.cs | 10 ++++++++++ .../Multiplayer/Participants/ParticipantPanel.cs | 13 ++++++------- .../Visual/Multiplayer/TestMultiplayerClient.cs | 2 ++ 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs index 9181170bee..968a869532 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs @@ -43,6 +43,16 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("two unique panels", () => this.ChildrenOfType().Select(p => p.User).Distinct().Count() == 2); } + [Test] + public void TestAddNullUser() + { + AddAssert("one unique panel", () => this.ChildrenOfType().Select(p => p.User).Distinct().Count() == 1); + + AddStep("add non-resolvable user", () => Client.AddNullUser(-3)); + + AddUntilStep("two unique panels", () => this.ChildrenOfType().Select(p => p.User).Distinct().Count() == 2); + } + [Test] public void TestRemoveUser() { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs index de3069b2f6..f99655e305 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -45,7 +44,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants [BackgroundDependencyLoader] private void load() { - Debug.Assert(User.User != null); + var user = User.User; var backgroundColour = Color4Extensions.FromHex("#33413C"); @@ -82,7 +81,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants Origin = Anchor.CentreRight, RelativeSizeAxes = Axes.Both, Width = 0.75f, - User = User.User, + User = user, Colour = ColourInfo.GradientHorizontal(Color4.White.Opacity(0), Color4.White.Opacity(0.25f)) }, new FillFlowContainer @@ -98,28 +97,28 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants Origin = Anchor.CentreLeft, RelativeSizeAxes = Axes.Both, FillMode = FillMode.Fit, - User = User.User + User = user }, new UpdateableFlag { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, Size = new Vector2(30, 20), - Country = User.User.Country + Country = user?.Country }, new OsuSpriteText { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 18), - Text = User.User.Username + Text = user?.Username }, new OsuSpriteText { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, Font = OsuFont.GetFont(size: 14), - Text = User.User.CurrentModeRank != null ? $"#{User.User.CurrentModeRank}" : string.Empty + Text = user?.CurrentModeRank != null ? $"#{user.CurrentModeRank}" : string.Empty } } }, diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 9a839c8d22..2ce5211757 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -28,6 +28,8 @@ namespace osu.Game.Tests.Visual.Multiplayer public void AddUser(User user) => ((IMultiplayerClient)this).UserJoined(new MultiplayerRoomUser(user.Id) { User = user }); + public void AddNullUser(int userId) => ((IMultiplayerClient)this).UserJoined(new MultiplayerRoomUser(userId)); + public void RemoveUser(User user) { Debug.Assert(Room != null); From bdbc210f6d6cbea01a5d8106b31c34be5c43955c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 28 Dec 2020 16:51:45 +0900 Subject: [PATCH 608/818] Update fastlane and dependencies --- Gemfile.lock | 56 +++++++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index a4b49af7e4..8ac863c9a8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,27 +1,27 @@ GEM remote: https://rubygems.org/ specs: - CFPropertyList (3.0.2) + CFPropertyList (3.0.3) addressable (2.7.0) public_suffix (>= 2.0.2, < 5.0) atomos (0.1.3) aws-eventstream (1.1.0) - aws-partitions (1.354.0) - aws-sdk-core (3.104.3) + aws-partitions (1.413.0) + aws-sdk-core (3.110.0) aws-eventstream (~> 1, >= 1.0.2) aws-partitions (~> 1, >= 1.239.0) aws-sigv4 (~> 1.1) jmespath (~> 1.0) - aws-sdk-kms (1.36.0) - aws-sdk-core (~> 3, >= 3.99.0) + aws-sdk-kms (1.40.0) + aws-sdk-core (~> 3, >= 3.109.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.78.0) - aws-sdk-core (~> 3, >= 3.104.3) + aws-sdk-s3 (1.87.0) + aws-sdk-core (~> 3, >= 3.109.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.1) - aws-sigv4 (1.2.1) + aws-sigv4 (1.2.2) aws-eventstream (~> 1, >= 1.0.2) - babosa (1.0.3) + babosa (1.0.4) claide (1.0.3) colored (1.2) colored2 (3.1.2) @@ -29,22 +29,23 @@ GEM highline (~> 1.7.2) declarative (0.0.20) declarative-option (0.1.0) - digest-crc (0.6.1) - rake (~> 13.0) + digest-crc (0.6.3) + rake (>= 12.0.0, < 14.0.0) domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) dotenv (2.7.6) - emoji_regex (3.0.0) - excon (0.76.0) - faraday (1.0.1) + emoji_regex (3.2.1) + excon (0.78.1) + faraday (1.2.0) multipart-post (>= 1.2, < 3) - faraday-cookie_jar (0.0.6) - faraday (>= 0.7.4) + ruby2_keywords + faraday-cookie_jar (0.0.7) + faraday (>= 0.8.0) http-cookie (~> 1.0.0) faraday_middleware (1.0.0) faraday (~> 1.0) - fastimage (2.2.0) - fastlane (2.156.0) + fastimage (2.2.1) + fastlane (2.170.0) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.3, < 3.0.0) aws-sdk-s3 (~> 1.0) @@ -96,17 +97,17 @@ GEM google-cloud-core (1.5.0) google-cloud-env (~> 1.0) google-cloud-errors (~> 1.0) - google-cloud-env (1.3.3) + google-cloud-env (1.4.0) faraday (>= 0.17.3, < 2.0) google-cloud-errors (1.0.1) - google-cloud-storage (1.27.0) + google-cloud-storage (1.29.2) addressable (~> 2.5) digest-crc (~> 0.4) google-api-client (~> 0.33) google-cloud-core (~> 1.2) googleauth (~> 0.9) mini_mime (~> 1.0) - googleauth (0.13.1) + googleauth (0.14.0) faraday (>= 0.17.3, < 2.0) jwt (>= 1.4, < 3.0) memoist (~> 0.16) @@ -118,10 +119,10 @@ GEM domain_name (~> 0.5) httpclient (2.8.3) jmespath (1.4.0) - json (2.3.1) - jwt (2.2.1) + json (2.5.1) + jwt (2.2.2) memoist (0.16.2) - mini_magick (4.10.1) + mini_magick (4.11.0) mini_mime (1.0.2) mini_portile2 (2.4.0) multi_json (1.15.0) @@ -132,14 +133,15 @@ GEM mini_portile2 (~> 2.4.0) os (1.1.1) plist (3.5.0) - public_suffix (4.0.5) - rake (13.0.1) + public_suffix (4.0.6) + rake (13.0.3) representable (3.0.4) declarative (< 0.1.0) declarative-option (< 0.2.0) uber (< 0.2.0) retriable (3.1.2) rouge (2.0.7) + ruby2_keywords (0.0.2) rubyzip (2.3.0) security (0.1.3) signet (0.14.0) @@ -168,7 +170,7 @@ GEM unf_ext (0.0.7.7) unicode-display_width (1.7.0) word_wrap (1.0.0) - xcodeproj (1.18.0) + xcodeproj (1.19.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) From 2e3537e9664d24dbb6f5fec80e0c9a3e42cb1664 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 28 Dec 2020 16:52:54 +0900 Subject: [PATCH 609/818] 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 fc01f9bf1d..cd2ce58c55 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 cbf9f6f1bd..3e1b56c29c 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -26,7 +26,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index adbcc0ef1c..85ba0590ea 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -88,7 +88,7 @@ - + From 5ca9a6a98018544d67f61ef1912596084a70999a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 28 Dec 2020 20:05:48 +0900 Subject: [PATCH 610/818] Add xmldoc on UserLookupCache's lookup method --- osu.Game/Database/UserLookupCache.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Database/UserLookupCache.cs b/osu.Game/Database/UserLookupCache.cs index 05d6930992..8a99e27708 100644 --- a/osu.Game/Database/UserLookupCache.cs +++ b/osu.Game/Database/UserLookupCache.cs @@ -17,6 +17,12 @@ namespace osu.Game.Database [Resolved] private IAPIProvider api { get; set; } + /// + /// Perform an API lookup on the specified user, populating a model. + /// + /// The user to lookup. + /// An optional cancellation token. + /// The populated user, or null if the user does not exist or the request could not be satisfied. public Task GetUserAsync(int userId, CancellationToken token = default) => GetAsync(userId, token); protected override async Task ComputeValueAsync(int lookup, CancellationToken token = default) From 545dcac4ec0950f2ade486c55a88f9f39b1802ff Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 28 Dec 2020 20:13:24 +0900 Subject: [PATCH 611/818] Add null hinting on UserLookupCache query method --- osu.Game/Database/UserLookupCache.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Database/UserLookupCache.cs b/osu.Game/Database/UserLookupCache.cs index 8a99e27708..49ea82a4fe 100644 --- a/osu.Game/Database/UserLookupCache.cs +++ b/osu.Game/Database/UserLookupCache.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Game.Online.API; using osu.Game.Online.API.Requests; @@ -23,6 +24,7 @@ namespace osu.Game.Database /// The user to lookup. /// An optional cancellation token. /// The populated user, or null if the user does not exist or the request could not be satisfied. + [ItemCanBeNull] public Task GetUserAsync(int userId, CancellationToken token = default) => GetAsync(userId, token); protected override async Task ComputeValueAsync(int lookup, CancellationToken token = default) From 447a55ce11cf144eddc85dea8e37d99c2dbb46e2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 28 Dec 2020 20:16:53 +0900 Subject: [PATCH 612/818] Fix incorrect null handling in GameplayLeaderboard --- osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs | 6 ++++-- osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index 83b70911c6..cb20deb272 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.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 JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; @@ -56,6 +57,7 @@ namespace osu.Game.Screens.Play.HUD } } + [CanBeNull] public User User { get; } private readonly bool trackedPlayer; @@ -68,7 +70,7 @@ namespace osu.Game.Screens.Play.HUD /// /// The score's player. /// Whether the player is the local user or a replay player. - public GameplayLeaderboardScore(User user, bool trackedPlayer) + public GameplayLeaderboardScore([CanBeNull] User user, bool trackedPlayer) { User = user; this.trackedPlayer = trackedPlayer; @@ -180,7 +182,7 @@ namespace osu.Game.Screens.Play.HUD Origin = Anchor.CentreLeft, Colour = Color4.White, Font = OsuFont.Torus.With(size: 14, weight: FontWeight.SemiBold), - Text = User.Username, + Text = User?.Username, Truncate = true, Shadow = false, } diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index 00e2b8bfa7..e7e5459f76 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -65,7 +65,7 @@ namespace osu.Game.Screens.Play.HUD var trackedUser = new TrackedUserData(); userScores[userId] = trackedUser; - var leaderboardScore = AddPlayer(resolvedUser, resolvedUser.Id == api.LocalUser.Value.Id); + var leaderboardScore = AddPlayer(resolvedUser, resolvedUser?.Id == api.LocalUser.Value.Id); ((IBindable)leaderboardScore.Accuracy).BindTo(trackedUser.Accuracy); ((IBindable)leaderboardScore.TotalScore).BindTo(trackedUser.Score); From 8f0413472cfd57b480d0feedd365a5ce22aae3ea Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 28 Dec 2020 20:30:08 +0900 Subject: [PATCH 613/818] Add test coverage of null users in scoreboard --- .../Visual/Online/TestSceneCurrentlyPlayingDisplay.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs index 1666c9cde4..1baa07f208 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs @@ -90,11 +90,17 @@ namespace osu.Game.Tests.Visual.Online }; protected override Task ComputeValueAsync(int lookup, CancellationToken token = default) - => Task.FromResult(new User + { + // tests against failed lookups + if (lookup == 13) + return Task.FromResult(null); + + return Task.FromResult(new User { Id = lookup, Username = usernames[lookup % usernames.Length], }); + } } } } From 6254907ef984032f158bb0a3164862cc13a5d435 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 28 Dec 2020 20:31:08 +0900 Subject: [PATCH 614/818] Move multiplayer leaderboard test to correct namespace --- .../TestSceneMultiplayerGameplayLeaderboard.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) rename osu.Game.Tests/Visual/{Gameplay => Multiplayer}/TestSceneMultiplayerGameplayLeaderboard.cs (98%) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs similarity index 98% rename from osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs rename to osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs index 975c54c3f6..d0b1e77549 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneMultiplayerGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs @@ -20,10 +20,9 @@ using osu.Game.Rulesets.Osu.Scoring; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Play.HUD; -using osu.Game.Tests.Visual.Multiplayer; using osu.Game.Tests.Visual.Online; -namespace osu.Game.Tests.Visual.Gameplay +namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneMultiplayerGameplayLeaderboard : MultiplayerTestScene { From fb21b7c0167bed184df6c71dcfcd7a1f29c3ecfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 28 Dec 2020 12:09:32 +0100 Subject: [PATCH 615/818] Add failing test cases --- .../TestSceneMultiplayerMatchSongSelect.cs | 145 ++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs new file mode 100644 index 0000000000..95c333e9f4 --- /dev/null +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs @@ -0,0 +1,145 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Extensions; +using osu.Framework.Platform; +using osu.Framework.Screens; +using osu.Framework.Utils; +using osu.Game.Beatmaps; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Catch; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Rulesets.Taiko; +using osu.Game.Rulesets.Taiko.Mods; +using osu.Game.Screens.OnlinePlay.Multiplayer; +using osu.Game.Screens.Select; + +namespace osu.Game.Tests.Visual.Multiplayer +{ + public class TestSceneMultiplayerMatchSongSelect : RoomTestScene + { + private BeatmapManager manager; + private RulesetStore rulesets; + + private List beatmaps; + + private TestMultiplayerMatchSongSelect songSelect; + + [BackgroundDependencyLoader] + private void load(GameHost host, AudioManager audio) + { + Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); + Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default)); + + beatmaps = new List(); + + for (int i = 0; i < 8; ++i) + { + int beatmapId = 10 * 10 + i; + + int length = RNG.Next(30000, 200000); + double bpm = RNG.NextSingle(80, 200); + + beatmaps.Add(new BeatmapInfo + { + Ruleset = rulesets.GetRuleset(i % 4), + OnlineBeatmapID = beatmapId, + Length = length, + BPM = bpm, + BaseDifficulty = new BeatmapDifficulty() + }); + } + + manager.Import(new BeatmapSetInfo + { + OnlineBeatmapSetID = 10, + Hash = Guid.NewGuid().ToString().ComputeMD5Hash(), + Metadata = new BeatmapMetadata + { + Artist = "Some Artist", + Title = "Some Beatmap", + AuthorString = "Some Author" + }, + Beatmaps = beatmaps, + DateAdded = DateTimeOffset.UtcNow + }).Wait(); + } + + public override void SetUpSteps() + { + base.SetUpSteps(); + + AddStep("reset", () => + { + Ruleset.Value = new OsuRuleset().RulesetInfo; + Beatmap.SetDefault(); + SelectedMods.SetDefault(); + }); + + AddStep("create song select", () => LoadScreen(songSelect = new TestMultiplayerMatchSongSelect())); + AddUntilStep("wait for present", () => songSelect.IsCurrentScreen()); + } + + [Test] + public void TestBeatmapRevertedOnExitIfNoSelection() + { + BeatmapInfo selectedBeatmap = null; + + AddStep("select beatmap", + () => songSelect.Carousel.SelectBeatmap(selectedBeatmap = beatmaps.Where(beatmap => beatmap.RulesetID == new OsuRuleset().LegacyID).ElementAt(1))); + AddUntilStep("wait for selection", () => Beatmap.Value.BeatmapInfo.Equals(selectedBeatmap)); + + AddStep("exit song select", () => songSelect.Exit()); + AddAssert("beatmap reverted", () => Beatmap.IsDefault); + } + + [Test] + public void TestModsRevertedOnExitIfNoSelection() + { + AddStep("change mods", () => SelectedMods.Value = new[] { new OsuModDoubleTime() }); + + AddStep("exit song select", () => songSelect.Exit()); + AddAssert("mods reverted", () => SelectedMods.Value.Count == 0); + } + + [Test] + public void TestRulesetRevertedOnExitIfNoSelection() + { + AddStep("change ruleset", () => Ruleset.Value = new CatchRuleset().RulesetInfo); + + AddStep("exit song select", () => songSelect.Exit()); + AddAssert("ruleset reverted", () => Ruleset.Value.Equals(new OsuRuleset().RulesetInfo)); + } + + [Test] + public void TestBeatmapConfirmed() + { + BeatmapInfo selectedBeatmap = null; + + AddStep("change ruleset", () => Ruleset.Value = new TaikoRuleset().RulesetInfo); + AddStep("select beatmap", + () => songSelect.Carousel.SelectBeatmap(selectedBeatmap = beatmaps.First(beatmap => beatmap.RulesetID == new TaikoRuleset().LegacyID))); + AddUntilStep("wait for selection", () => Beatmap.Value.BeatmapInfo.Equals(selectedBeatmap)); + AddStep("set mods", () => SelectedMods.Value = new[] { new TaikoModDoubleTime() }); + + AddStep("confirm selection", () => songSelect.FinaliseSelection()); + AddStep("exit song select", () => songSelect.Exit()); + + AddAssert("beatmap not changed", () => Beatmap.Value.BeatmapInfo.Equals(selectedBeatmap)); + AddAssert("ruleset not changed", () => Ruleset.Value.Equals(new TaikoRuleset().RulesetInfo)); + AddAssert("mods not changed", () => SelectedMods.Value.Single() is TaikoModDoubleTime); + } + + private class TestMultiplayerMatchSongSelect : MultiplayerMatchSongSelect + { + public new BeatmapCarousel Carousel => base.Carousel; + } + } +} From f16b516e5880d8d27a439ab0a6615535879eee34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 28 Dec 2020 12:32:06 +0100 Subject: [PATCH 616/818] Revert user changes if no selection was made --- .../Multiplayer/MultiplayerMatchSongSelect.cs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs index 0842574f54..72539a2e3a 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using System.Linq; using Humanizer; using osu.Framework.Allocation; @@ -8,9 +9,12 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Logging; using osu.Framework.Screens; +using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; using osu.Game.Screens.Select; namespace osu.Game.Screens.OnlinePlay.Multiplayer @@ -29,6 +33,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private LoadingLayer loadingLayer; + private WorkingBeatmap initialBeatmap; + private RulesetInfo initialRuleset; + private IReadOnlyList initialMods; + + private bool itemSelected; + public MultiplayerMatchSongSelect() { Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING }; @@ -38,10 +48,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private void load() { AddInternal(loadingLayer = new LoadingLayer(Carousel)); + initialBeatmap = Beatmap.Value; + initialRuleset = Ruleset.Value; + initialMods = Mods.Value.ToList(); } protected override bool OnStart() { + itemSelected = true; var item = new PlaylistItem(); item.Beatmap.Value = Beatmap.Value.BeatmapInfo; @@ -82,6 +96,18 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer return true; } + public override bool OnExiting(IScreen next) + { + if (!itemSelected) + { + Beatmap.Value = initialBeatmap; + Ruleset.Value = initialRuleset; + Mods.Value = initialMods; + } + + return base.OnExiting(next); + } + protected override BeatmapDetailArea CreateBeatmapDetailArea() => new PlayBeatmapDetailArea(); } } From 92d74a9343df3fd7378205fe26ff1753ebf52134 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 28 Dec 2020 20:48:14 +0900 Subject: [PATCH 617/818] Fix potential nullref in test scene --- osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs index c0a021436e..17fe09f2c6 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs @@ -95,7 +95,7 @@ namespace osu.Game.Tests.Visual.Gameplay { public bool CheckPositionByUsername(string username, int? expectedPosition) { - var scoreItem = this.FirstOrDefault(i => i.User.Username == username); + var scoreItem = this.FirstOrDefault(i => i.User?.Username == username); return scoreItem != null && scoreItem.ScorePosition == expectedPosition; } From a9822800fc368986a152723b883d9842b920f54c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 28 Dec 2020 13:00:05 +0100 Subject: [PATCH 618/818] Add more null hinting in GameplayLeaderboard --- osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs index e33cc05e64..7b94bf19ec 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs @@ -3,6 +3,7 @@ using System; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Caching; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -42,7 +43,7 @@ namespace osu.Game.Screens.Play.HUD /// Whether the player should be tracked on the leaderboard. /// Set to true for the local player or a player whose replay is currently being played. /// - public ILeaderboardScore AddPlayer(User user, bool isTracked) + public ILeaderboardScore AddPlayer([CanBeNull] User user, bool isTracked) { var drawable = new GameplayLeaderboardScore(user, isTracked) { From b352c1503ff2eff470dd573d08ad8e2015c07906 Mon Sep 17 00:00:00 2001 From: Susko3 <16479013+Susko3@users.noreply.github.com> Date: Mon, 28 Dec 2020 15:13:33 +0100 Subject: [PATCH 619/818] Fix IntentFilter capturing all file types Removed string arrays and split the IntentFilter into multiple. Also added DataHost and DataMimeType --- osu.Android/OsuGameActivity.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Android/OsuGameActivity.cs b/osu.Android/OsuGameActivity.cs index 953c06f4e2..7abaff3cdb 100644 --- a/osu.Android/OsuGameActivity.cs +++ b/osu.Android/OsuGameActivity.cs @@ -16,7 +16,10 @@ using osu.Framework.Android; namespace osu.Android { [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullUser, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = false, LaunchMode = LaunchMode.SingleInstance)] - [IntentFilter(new[] { Intent.ActionDefault, Intent.ActionSend }, Categories = new[] { Intent.CategoryDefault }, DataPathPatterns = new[] { ".*\\.osz", ".*\\.osk" }, DataMimeType = "application/*")] + [IntentFilter(new[] { Intent.ActionDefault, Intent.ActionSend }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osz", DataHost = "*", DataMimeType = "*/*")] + [IntentFilter(new[] { Intent.ActionDefault, Intent.ActionSend }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osk", DataHost = "*", DataMimeType = "*/*")] + [IntentFilter(new[] { Intent.ActionDefault, Intent.ActionSend }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "file", DataPathPattern = ".*\\\\.osz", DataHost = "*", DataMimeType = "*/*")] + [IntentFilter(new[] { Intent.ActionDefault, Intent.ActionSend }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "file", DataPathPattern = ".*\\\\.osk", DataHost = "*", DataMimeType = "*/*")] [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryBrowsable, Intent.CategoryDefault }, DataSchemes = new[] { "osu", "osump" })] public class OsuGameActivity : AndroidGameActivity { From d971aa5295b7d20a471d8be62fdc4c6e263dd139 Mon Sep 17 00:00:00 2001 From: Susko3 <16479013+Susko3@users.noreply.github.com> Date: Mon, 28 Dec 2020 15:54:21 +0100 Subject: [PATCH 620/818] Remove file intents and add Send intent Removed IntentFilters with DataScheme = "file" Added Intent.ActionSend with application/octet-stream and application/zip --- osu.Android/OsuGameActivity.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android/OsuGameActivity.cs b/osu.Android/OsuGameActivity.cs index 7abaff3cdb..cdf033e685 100644 --- a/osu.Android/OsuGameActivity.cs +++ b/osu.Android/OsuGameActivity.cs @@ -16,10 +16,10 @@ using osu.Framework.Android; namespace osu.Android { [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullUser, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = false, LaunchMode = LaunchMode.SingleInstance)] - [IntentFilter(new[] { Intent.ActionDefault, Intent.ActionSend }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osz", DataHost = "*", DataMimeType = "*/*")] - [IntentFilter(new[] { Intent.ActionDefault, Intent.ActionSend }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osk", DataHost = "*", DataMimeType = "*/*")] - [IntentFilter(new[] { Intent.ActionDefault, Intent.ActionSend }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "file", DataPathPattern = ".*\\\\.osz", DataHost = "*", DataMimeType = "*/*")] - [IntentFilter(new[] { Intent.ActionDefault, Intent.ActionSend }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "file", DataPathPattern = ".*\\\\.osk", DataHost = "*", DataMimeType = "*/*")] + [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osz", DataHost = "*", DataMimeType = "*/*")] + [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osk", DataHost = "*", DataMimeType = "*/*")] + [IntentFilter(new[] { Intent.ActionSend }, Categories = new[] { Intent.CategoryDefault }, DataMimeType = "application/octet-stream")] + [IntentFilter(new[] { Intent.ActionSend }, Categories = new[] { Intent.CategoryDefault }, DataMimeType = "application/zip")] [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryBrowsable, Intent.CategoryDefault }, DataSchemes = new[] { "osu", "osump" })] public class OsuGameActivity : AndroidGameActivity { From d2301068b6c2a04961986570075ef5075c2cf168 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 28 Dec 2020 16:35:33 +0100 Subject: [PATCH 621/818] Fix changelog header staying dimmed after build show --- .../Overlays/Changelog/ChangelogUpdateStreamControl.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogUpdateStreamControl.cs b/osu.Game/Overlays/Changelog/ChangelogUpdateStreamControl.cs index 6bbff045b5..aa36a5c8fd 100644 --- a/osu.Game/Overlays/Changelog/ChangelogUpdateStreamControl.cs +++ b/osu.Game/Overlays/Changelog/ChangelogUpdateStreamControl.cs @@ -7,12 +7,11 @@ namespace osu.Game.Overlays.Changelog { public class ChangelogUpdateStreamControl : OverlayStreamControl { - protected override OverlayStreamItem CreateStreamItem(APIUpdateStream value) => new ChangelogUpdateStreamItem(value); - - protected override void LoadComplete() + public ChangelogUpdateStreamControl() { - // suppress base logic of immediately selecting first item if one exists - // (we always want to start with no stream selected). + SelectFirstTabByDefault = false; } + + protected override OverlayStreamItem CreateStreamItem(APIUpdateStream value) => new ChangelogUpdateStreamItem(value); } } From 1d311a66805aad4c784ff0a51bc523e69019ccde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 28 Dec 2020 19:11:44 +0100 Subject: [PATCH 622/818] Change PlayingUsers population logic to match expectations --- .../Multiplayer/StatefulMultiplayerClient.cs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs index fcb0977f53..c15401e99d 100644 --- a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs @@ -133,6 +133,7 @@ namespace osu.Game.Online.Multiplayer apiRoom = null; Room = null; + PlayingUsers.Clear(); RoomUpdated?.Invoke(); }, false); @@ -302,8 +303,7 @@ namespace osu.Game.Online.Multiplayer Room.Users.Single(u => u.UserID == userId).State = state; - if (state != MultiplayerUserState.Playing) - PlayingUsers.Remove(userId); + updatePlayingUsers(userId, state); RoomUpdated?.Invoke(); }, false); @@ -337,8 +337,6 @@ namespace osu.Game.Online.Multiplayer if (Room == null) return; - PlayingUsers.AddRange(Room.Users.Where(u => u.State == MultiplayerUserState.Playing).Select(u => u.UserID)); - MatchStarted?.Invoke(); }, false); @@ -454,5 +452,17 @@ namespace osu.Game.Online.Multiplayer apiRoom.Playlist.Clear(); // Clearing should be unnecessary, but here for sanity. apiRoom.Playlist.Add(playlistItem); } + + private void updatePlayingUsers(int userId, MultiplayerUserState state) + { + bool isPlaying = state >= MultiplayerUserState.WaitingForLoad && state <= MultiplayerUserState.FinishedPlay; + bool wasPlaying = PlayingUsers.Contains(userId); + + if (!wasPlaying && isPlaying) + PlayingUsers.Add(userId); + + if (wasPlaying && !isPlaying) + PlayingUsers.Remove(userId); + } } } From a014d0ec18953854650c33263489273171d828f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 28 Dec 2020 19:12:58 +0100 Subject: [PATCH 623/818] Use PlayingUsers when constructing player directly --- .../Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 58314c3774..ba0ed16cf4 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -200,7 +200,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { Debug.Assert(client.Room != null); - int[] userIds = client.Room.Users.Where(u => u.State >= MultiplayerUserState.WaitingForLoad).Select(u => u.UserID).ToArray(); + int[] userIds = client.PlayingUsers.ToArray(); StartPlay(() => new MultiplayerPlayer(SelectedItem.Value, userIds)); } From f7407347f78445a141c196fbcda5282f1ba14250 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 28 Dec 2020 20:01:17 +0100 Subject: [PATCH 624/818] Add test coverage of PlayingUsers tracking --- .../StatefulMultiplayerClientTest.cs | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs diff --git a/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs b/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs new file mode 100644 index 0000000000..8d543e7485 --- /dev/null +++ b/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs @@ -0,0 +1,57 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using Humanizer; +using NUnit.Framework; +using osu.Framework.Testing; +using osu.Game.Online.Multiplayer; +using osu.Game.Tests.Visual.Multiplayer; +using osu.Game.Users; + +namespace osu.Game.Tests.NonVisual.Multiplayer +{ + [HeadlessTest] + public class StatefulMultiplayerClientTest : MultiplayerTestScene + { + [Test] + public void TestPlayingUserTracking() + { + int id = 2000; + + AddRepeatStep("add some users", () => Client.AddUser(new User { Id = id++ }), 5); + checkPlayingUserCount(0); + + changeState(3, MultiplayerUserState.WaitingForLoad); + checkPlayingUserCount(3); + + changeState(3, MultiplayerUserState.Playing); + checkPlayingUserCount(3); + + changeState(3, MultiplayerUserState.Results); + checkPlayingUserCount(0); + + changeState(6, MultiplayerUserState.WaitingForLoad); + checkPlayingUserCount(6); + + AddStep("another user left", () => Client.RemoveUser(Client.Room?.Users.Last().User)); + checkPlayingUserCount(5); + + AddStep("leave room", () => Client.LeaveRoom()); + checkPlayingUserCount(0); + } + + private void checkPlayingUserCount(int expectedCount) + => AddAssert($"{"user".ToQuantity(expectedCount)} playing", () => Client.PlayingUsers.Count == expectedCount); + + private void changeState(int userCount, MultiplayerUserState state) + => AddStep($"{"user".ToQuantity(userCount)} in {state}", () => + { + for (int i = 0; i < userCount; ++i) + { + var userId = Client.Room?.Users[i].UserID ?? throw new AssertionException("Room cannot be null!"); + Client.ChangeUserState(userId, state); + } + }); + } +} From 770a5a85dff5cff621287732639eb33534ea95ad Mon Sep 17 00:00:00 2001 From: Susko3 <16479013+Susko3@users.noreply.github.com> Date: Mon, 28 Dec 2020 20:57:08 +0100 Subject: [PATCH 625/818] Merge Intent.ActionSend into one IntentFilter Co-authored-by: Lucas A. --- osu.Android/OsuGameActivity.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Android/OsuGameActivity.cs b/osu.Android/OsuGameActivity.cs index cdf033e685..da69b516dd 100644 --- a/osu.Android/OsuGameActivity.cs +++ b/osu.Android/OsuGameActivity.cs @@ -19,7 +19,7 @@ namespace osu.Android [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osz", DataHost = "*", DataMimeType = "*/*")] [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osk", DataHost = "*", DataMimeType = "*/*")] [IntentFilter(new[] { Intent.ActionSend }, Categories = new[] { Intent.CategoryDefault }, DataMimeType = "application/octet-stream")] - [IntentFilter(new[] { Intent.ActionSend }, Categories = new[] { Intent.CategoryDefault }, DataMimeType = "application/zip")] + [IntentFilter(new[] { Intent.ActionSend }, Categories = new[] { Intent.CategoryDefault }, DataMimeTypes = new[] { "application/zip", "application/octet-stream" })] [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryBrowsable, Intent.CategoryDefault }, DataSchemes = new[] { "osu", "osump" })] public class OsuGameActivity : AndroidGameActivity { From 9e6994166c0eef43cb63b6d1d34e70e0cb324634 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 28 Dec 2020 20:59:12 +0100 Subject: [PATCH 626/818] Add helper to track ongoing operations in UI --- .../OnlinePlay/OngoingOperationTracker.cs | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs diff --git a/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs b/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs new file mode 100644 index 0000000000..f2d943e14f --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs @@ -0,0 +1,47 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Bindables; + +namespace osu.Game.Screens.OnlinePlay +{ + /// + /// Utility class to track ongoing online operations' progress. + /// Can be used to disable interactivity while waiting for a response from online sources. + /// + public class OngoingOperationTracker + { + /// + /// Whether there is an online operation in progress. + /// + public IBindable InProgress => inProgress; + + private readonly Bindable inProgress = new BindableBool(); + + private LeasedBindable leasedInProgress; + + /// + /// Begins tracking a new online operation. + /// + /// An operation has already been started. + public void BeginOperation() + { + if (leasedInProgress != null) + throw new InvalidOperationException("Cannot begin operation while another is in progress."); + + leasedInProgress = inProgress.BeginLease(true); + leasedInProgress.Value = true; + } + + /// + /// Ends tracking an online operation. + /// Does nothing if an operation has not been begun yet. + /// + public void EndOperation() + { + leasedInProgress?.Return(); + leasedInProgress = null; + } + } +} From 47ab7c9fd67270079d39e8ad9f45fb3e986c76df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 28 Dec 2020 20:59:38 +0100 Subject: [PATCH 627/818] Disable ready button after host click --- .../TestSceneMultiplayerReadyButton.cs | 22 +++++++++++++++---- .../Match/MultiplayerReadyButton.cs | 13 ++++++++++- .../Multiplayer/MultiplayerMatchSubScreen.cs | 4 ++++ 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs index 6b11613f1c..958c6d218b 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs @@ -7,11 +7,14 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Graphics; using osu.Framework.Platform; +using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Beatmaps; +using osu.Game.Graphics.UserInterface; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; using osu.Game.Rulesets; +using osu.Game.Screens.OnlinePlay; using osu.Game.Screens.OnlinePlay.Multiplayer.Match; using osu.Game.Tests.Resources; using osu.Game.Users; @@ -27,6 +30,9 @@ namespace osu.Game.Tests.Visual.Multiplayer private BeatmapManager beatmaps; private RulesetStore rulesets; + [Cached] + private OngoingOperationTracker gameplayStartTracker = new OngoingOperationTracker(); + [BackgroundDependencyLoader] private void load(GameHost host, AudioManager audio) { @@ -89,8 +95,7 @@ namespace osu.Game.Tests.Visual.Multiplayer addClickButtonStep(); AddAssert("user is ready", () => Client.Room?.Users[0].State == MultiplayerUserState.Ready); - addClickButtonStep(); - AddAssert("match started", () => Client.Room?.Users[0].State == MultiplayerUserState.WaitingForLoad); + verifyGameplayStartFlow(); } [Test] @@ -105,8 +110,7 @@ namespace osu.Game.Tests.Visual.Multiplayer addClickButtonStep(); AddStep("make user host", () => Client.TransferHost(Client.Room?.Users[0].UserID ?? 0)); - addClickButtonStep(); - AddAssert("match started", () => Client.Room?.Users[0].State == MultiplayerUserState.WaitingForLoad); + verifyGameplayStartFlow(); } [Test] @@ -160,5 +164,15 @@ namespace osu.Game.Tests.Visual.Multiplayer InputManager.MoveMouseTo(button); InputManager.Click(MouseButton.Left); }); + + private void verifyGameplayStartFlow() + { + addClickButtonStep(); + AddAssert("user waiting for load", () => Client.Room?.Users[0].State == MultiplayerUserState.WaitingForLoad); + AddAssert("ready button disabled", () => !button.ChildrenOfType().Single().Enabled.Value); + + AddStep("transitioned to gameplay", () => gameplayStartTracker.EndOperation()); + AddAssert("ready button enabled", () => button.ChildrenOfType().Single().Enabled.Value); + } } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index 281e92404c..5009b435f4 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -33,11 +33,15 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match [Resolved] private OsuColour colours { get; set; } + [Resolved] + private OngoingOperationTracker gameplayStartTracker { get; set; } + private SampleChannel sampleReadyCount; private readonly ButtonWithTrianglesExposed button; private int countReady; + private IBindable gameplayStartInProgress; public MultiplayerReadyButton() { @@ -54,6 +58,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match private void load(AudioManager audio) { sampleReadyCount = audio.Samples.Get(@"SongSelect/select-difficulty"); + + gameplayStartInProgress = gameplayStartTracker.InProgress.GetBoundCopy(); + gameplayStartInProgress.BindValueChanged(_ => updateState()); } protected override void OnRoomUpdated() @@ -63,7 +70,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match // this method is called on leaving the room, so the local user may not exist in the room any more. localUser = Room?.Users.SingleOrDefault(u => u.User?.Id == api.LocalUser.Value.Id); - button.Enabled.Value = Client.Room?.State == MultiplayerRoomState.Open; updateState(); } @@ -100,6 +106,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match break; } + button.Enabled.Value = Client.Room?.State == MultiplayerRoomState.Open && !gameplayStartInProgress.Value; + if (newCountReady != countReady) { countReady = newCountReady; @@ -142,7 +150,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match else { if (Room?.Host?.Equals(localUser) == true) + { + gameplayStartTracker.BeginOperation(); Client.StartMatch().CatchUnobservedExceptions(true); + } else Client.ChangeState(MultiplayerUserState.Idle).CatchUnobservedExceptions(true); } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 58314c3774..93db913ce0 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -31,6 +31,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [Resolved] private StatefulMultiplayerClient client { get; set; } + [Cached] + private OngoingOperationTracker gameplayStartTracker = new OngoingOperationTracker(); + private MultiplayerMatchSettingsOverlay settingsOverlay; private IBindable isConnected; @@ -203,6 +206,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer int[] userIds = client.Room.Users.Where(u => u.State >= MultiplayerUserState.WaitingForLoad).Select(u => u.UserID).ToArray(); StartPlay(() => new MultiplayerPlayer(SelectedItem.Value, userIds)); + gameplayStartTracker.EndOperation(); } protected override void Dispose(bool isDisposing) From af66e4531196853215e8625cf858d4a0d221aaec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 28 Dec 2020 21:39:11 +0100 Subject: [PATCH 628/818] Disable create room button after triggering join --- .../TestSceneCreateMultiplayerMatchButton.cs | 52 +++++++++++++++++++ .../OnlinePlay/Lounge/LoungeSubScreen.cs | 22 ++++---- .../CreateMultiplayerMatchButton.cs | 19 ++++++- .../Screens/OnlinePlay/OnlinePlayScreen.cs | 3 ++ 4 files changed, 83 insertions(+), 13 deletions(-) create mode 100644 osu.Game.Tests/Visual/Multiplayer/TestSceneCreateMultiplayerMatchButton.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneCreateMultiplayerMatchButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneCreateMultiplayerMatchButton.cs new file mode 100644 index 0000000000..2a549e5262 --- /dev/null +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneCreateMultiplayerMatchButton.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 NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Screens.OnlinePlay; +using osu.Game.Screens.OnlinePlay.Multiplayer; + +namespace osu.Game.Tests.Visual.Multiplayer +{ + public class TestSceneCreateMultiplayerMatchButton : MultiplayerTestScene + { + [Cached] + private OngoingOperationTracker joiningRoomTracker = new OngoingOperationTracker(); + + private CreateMultiplayerMatchButton button; + + public override void SetUpSteps() + { + base.SetUpSteps(); + AddStep("create button", () => Child = button = new CreateMultiplayerMatchButton + { + Width = 200, + Height = 100, + Anchor = Anchor.Centre, + Origin = Anchor.Centre + }); + } + + [Test] + public void TestButtonEnableStateChanges() + { + assertButtonEnableState(true); + + AddStep("begin joining room", () => joiningRoomTracker.BeginOperation()); + assertButtonEnableState(false); + + AddStep("end joining room", () => joiningRoomTracker.EndOperation()); + assertButtonEnableState(true); + + AddStep("disconnect client", () => Client.Disconnect()); + assertButtonEnableState(false); + + AddStep("re-connect client", () => Client.Connect()); + assertButtonEnableState(true); + } + + private void assertButtonEnableState(bool enabled) + => AddAssert($"button {(enabled ? "enabled" : "disabled")}", () => button.Enabled.Value == enabled); + } +} diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 79f5dfdee1..2730693f73 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -26,6 +26,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge protected override UserActivity InitialActivity => new UserActivity.SearchingForLobby(); private readonly IBindable initialRoomsReceived = new Bindable(); + private readonly IBindable joiningRoom = new Bindable(); private FilterControl filter; private Container content; @@ -37,7 +38,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge [Resolved] private MusicController music { get; set; } - private bool joiningRoom; + [Resolved] + private OngoingOperationTracker joiningRoomTracker { get; set; } [BackgroundDependencyLoader] private void load() @@ -98,7 +100,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge base.LoadComplete(); initialRoomsReceived.BindTo(RoomManager.InitialRoomsReceived); - initialRoomsReceived.BindValueChanged(onInitialRoomsReceivedChanged, true); + initialRoomsReceived.BindValueChanged(_ => updateLoadingLayer()); + + joiningRoom.BindTo(joiningRoomTracker.InProgress); + joiningRoom.BindValueChanged(_ => updateLoadingLayer(), true); } protected override void UpdateAfterChildren() @@ -156,26 +161,21 @@ namespace osu.Game.Screens.OnlinePlay.Lounge private void joinRequested(Room room) { - joiningRoom = true; - updateLoadingLayer(); + joiningRoomTracker.BeginOperation(); RoomManager?.JoinRoom(room, r => { Open(room); - joiningRoom = false; - updateLoadingLayer(); + joiningRoomTracker.EndOperation(); }, _ => { - joiningRoom = false; - updateLoadingLayer(); + joiningRoomTracker.EndOperation(); }); } - private void onInitialRoomsReceivedChanged(ValueChangedEvent received) => updateLoadingLayer(); - private void updateLoadingLayer() { - if (joiningRoom || !initialRoomsReceived.Value) + if (joiningRoom.Value || !initialRoomsReceived.Value) loadingLayer.Show(); else loadingLayer.Hide(); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs index 163efd9c20..7518a4e71b 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs @@ -10,14 +10,29 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { public class CreateMultiplayerMatchButton : PurpleTriangleButton { + private IBindable isConnected; + private IBindable joiningRoom; + + [Resolved] + private StatefulMultiplayerClient multiplayerClient { get; set; } + + [Resolved] + private OngoingOperationTracker joiningRoomTracker { get; set; } + [BackgroundDependencyLoader] - private void load(StatefulMultiplayerClient multiplayerClient) + private void load() { Triangles.TriangleScale = 1.5f; Text = "Create room"; - ((IBindable)Enabled).BindTo(multiplayerClient.IsConnected); + isConnected = multiplayerClient.IsConnected.GetBoundCopy(); + isConnected.BindValueChanged(_ => updateState()); + + joiningRoom = joiningRoomTracker.InProgress.GetBoundCopy(); + joiningRoom.BindValueChanged(_ => updateState(), true); } + + private void updateState() => Enabled.Value = isConnected.Value && !joiningRoom.Value; } } diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index 4074dd1573..92665c498b 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -52,6 +52,9 @@ namespace osu.Game.Screens.OnlinePlay [Cached] private readonly Bindable currentFilter = new Bindable(new FilterCriteria()); + [Cached] + private readonly OngoingOperationTracker joiningRoomTracker = new OngoingOperationTracker(); + [Resolved(CanBeNull = true)] private MusicController music { get; set; } From 6dc0f6af50a3befd707a7f9d4a5fe10cd0862413 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 28 Dec 2020 21:44:58 +0100 Subject: [PATCH 629/818] Disable setting apply button for duration of operation --- .../Match/MultiplayerMatchSettingsOverlay.cs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index ae03d384f6..de25cff546 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -68,6 +68,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match [Resolved] private Bindable ruleset { get; set; } + private readonly OngoingOperationTracker applyingSettingsTracker = new OngoingOperationTracker(); + [BackgroundDependencyLoader] private void load(OsuColour colours) { @@ -274,13 +276,21 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match Type.BindValueChanged(type => TypePicker.Current.Value = type.NewValue, true); MaxParticipants.BindValueChanged(count => MaxParticipantsField.Text = count.NewValue?.ToString(), true); RoomID.BindValueChanged(roomId => initialBeatmapControl.Alpha = roomId.NewValue == null ? 1 : 0, true); + + applyingSettingsTracker.InProgress.BindValueChanged(v => + { + if (v.NewValue) + loadingLayer.Show(); + else + loadingLayer.Hide(); + }); } protected override void Update() { base.Update(); - ApplyButton.Enabled.Value = Playlist.Count > 0 && NameField.Text.Length > 0; + ApplyButton.Enabled.Value = Playlist.Count > 0 && NameField.Text.Length > 0 && !applyingSettingsTracker.InProgress.Value; } private void apply() @@ -289,7 +299,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match return; hideError(); - loadingLayer.Show(); + applyingSettingsTracker.BeginOperation(); // If the client is already in a room, update via the client. // Otherwise, update the room directly in preparation for it to be submitted to the API on match creation. @@ -322,7 +332,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match private void onSuccess(Room room) { - loadingLayer.Hide(); + applyingSettingsTracker.EndOperation(); SettingsApplied?.Invoke(); } @@ -330,8 +340,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { ErrorText.Text = text; ErrorText.FadeIn(50); - - loadingLayer.Hide(); + applyingSettingsTracker.EndOperation(); } } From 540dec2e7ccbb96342683545d744ffa74b4011d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 28 Dec 2020 22:54:52 +0100 Subject: [PATCH 630/818] Allow null tracker in lounge screen for tests --- .../Screens/OnlinePlay/Lounge/LoungeSubScreen.cs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 2730693f73..f25835f56a 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -38,7 +38,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge [Resolved] private MusicController music { get; set; } - [Resolved] + [Resolved(CanBeNull = true)] private OngoingOperationTracker joiningRoomTracker { get; set; } [BackgroundDependencyLoader] @@ -102,8 +102,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge initialRoomsReceived.BindTo(RoomManager.InitialRoomsReceived); initialRoomsReceived.BindValueChanged(_ => updateLoadingLayer()); - joiningRoom.BindTo(joiningRoomTracker.InProgress); - joiningRoom.BindValueChanged(_ => updateLoadingLayer(), true); + if (joiningRoomTracker != null) + { + joiningRoom.BindTo(joiningRoomTracker.InProgress); + joiningRoom.BindValueChanged(_ => updateLoadingLayer(), true); + } } protected override void UpdateAfterChildren() @@ -161,15 +164,15 @@ namespace osu.Game.Screens.OnlinePlay.Lounge private void joinRequested(Room room) { - joiningRoomTracker.BeginOperation(); + joiningRoomTracker?.BeginOperation(); RoomManager?.JoinRoom(room, r => { Open(room); - joiningRoomTracker.EndOperation(); + joiningRoomTracker?.EndOperation(); }, _ => { - joiningRoomTracker.EndOperation(); + joiningRoomTracker?.EndOperation(); }); } From 355ecc4499bf1357612c6c6607b65a46e84ea432 Mon Sep 17 00:00:00 2001 From: TheOmyNomy Date: Tue, 29 Dec 2020 12:37:57 +1100 Subject: [PATCH 631/818] Change cursor trail blending mode to match stable --- osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs index f18d3191ca..af9ea99232 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs @@ -20,17 +20,14 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy private double lastTrailTime; private IBindable cursorSize; - public LegacyCursorTrail() - { - Blending = BlendingParameters.Additive; - } - [BackgroundDependencyLoader] private void load(ISkinSource skin, OsuConfigManager config) { Texture = skin.GetTexture("cursortrail"); disjointTrail = skin.GetTexture("cursormiddle") == null; + Blending = !disjointTrail ? BlendingParameters.Additive : BlendingParameters.Inherit; + if (Texture != null) { // stable "magic ratio". see OsuPlayfieldAdjustmentContainer for full explanation. From 6aeb7ece660ef5600f8b8da00ac38fd05a5def41 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 29 Dec 2020 14:25:08 +0900 Subject: [PATCH 632/818] Tidy up update state code, naming, xmldoc --- .../Multiplayer/StatefulMultiplayerClient.cs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs index c15401e99d..bf62c450c9 100644 --- a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs @@ -303,7 +303,7 @@ namespace osu.Game.Online.Multiplayer Room.Users.Single(u => u.UserID == userId).State = state; - updatePlayingUsers(userId, state); + updateUserPlayingState(userId, state); RoomUpdated?.Invoke(); }, false); @@ -453,15 +453,22 @@ namespace osu.Game.Online.Multiplayer apiRoom.Playlist.Add(playlistItem); } - private void updatePlayingUsers(int userId, MultiplayerUserState state) + /// + /// For the provided user ID, update whether the user is included in . + /// + /// The user's ID. + /// The new state of the user. + private void updateUserPlayingState(int userId, MultiplayerUserState state) { - bool isPlaying = state >= MultiplayerUserState.WaitingForLoad && state <= MultiplayerUserState.FinishedPlay; bool wasPlaying = PlayingUsers.Contains(userId); + bool isPlaying = state >= MultiplayerUserState.WaitingForLoad && state <= MultiplayerUserState.FinishedPlay; - if (!wasPlaying && isPlaying) + if (isPlaying == wasPlaying) + return; + + if (isPlaying) PlayingUsers.Add(userId); - - if (wasPlaying && !isPlaying) + else PlayingUsers.Remove(userId); } } From e3a41f61186525d18d01bd8306e1d2a6f7a2baa3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 29 Dec 2020 14:27:33 +0900 Subject: [PATCH 633/818] Rename variable to make more sense It needs to be explicitly stated that the users in this list are related to the *joined* room. Especially since it's sharing its variable name with `SpectatorStreamingClient` where it has the opposite meaning (is a list of *globally* playing players). --- .../Multiplayer/StatefulMultiplayerClientTest.cs | 2 +- .../TestSceneMultiplayerGameplayLeaderboard.cs | 6 +++--- .../Multiplayer/StatefulMultiplayerClient.cs | 16 ++++++++-------- .../Multiplayer/MultiplayerMatchSubScreen.cs | 2 +- .../Play/HUD/MultiplayerGameplayLeaderboard.cs | 4 ++-- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs b/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs index 8d543e7485..a2ad37cf4a 100644 --- a/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs +++ b/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs @@ -42,7 +42,7 @@ namespace osu.Game.Tests.NonVisual.Multiplayer } private void checkPlayingUserCount(int expectedCount) - => AddAssert($"{"user".ToQuantity(expectedCount)} playing", () => Client.PlayingUsers.Count == expectedCount); + => AddAssert($"{"user".ToQuantity(expectedCount)} playing", () => Client.CurrentMatchPlayingUserIds.Count == expectedCount); private void changeState(int userCount, MultiplayerUserState state) => AddStep($"{"user".ToQuantity(userCount)} in {state}", () => diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs index d0b1e77549..d016accc25 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs @@ -62,8 +62,8 @@ namespace osu.Game.Tests.Visual.Multiplayer streamingClient.Start(Beatmap.Value.BeatmapInfo.OnlineBeatmapID ?? 0); - Client.PlayingUsers.Clear(); - Client.PlayingUsers.AddRange(streamingClient.PlayingUsers); + Client.CurrentMatchPlayingUserIds.Clear(); + Client.CurrentMatchPlayingUserIds.AddRange(streamingClient.PlayingUsers); Children = new Drawable[] { @@ -91,7 +91,7 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestUserQuit() { - AddRepeatStep("mark user quit", () => Client.PlayingUsers.RemoveAt(0), users); + AddRepeatStep("mark user quit", () => Client.CurrentMatchPlayingUserIds.RemoveAt(0), users); } public class TestMultiplayerStreaming : SpectatorStreamingClient diff --git a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs index bf62c450c9..bb690d786a 100644 --- a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs @@ -61,9 +61,9 @@ namespace osu.Game.Online.Multiplayer public MultiplayerRoom? Room { get; private set; } /// - /// The users currently in gameplay. + /// The users in the joined which are currently in gameplay. /// - public readonly BindableList PlayingUsers = new BindableList(); + public readonly BindableList CurrentMatchPlayingUserIds = new BindableList(); [Resolved] private UserLookupCache userLookupCache { get; set; } = null!; @@ -133,7 +133,7 @@ namespace osu.Game.Online.Multiplayer apiRoom = null; Room = null; - PlayingUsers.Clear(); + CurrentMatchPlayingUserIds.Clear(); RoomUpdated?.Invoke(); }, false); @@ -254,7 +254,7 @@ namespace osu.Game.Online.Multiplayer return; Room.Users.Remove(user); - PlayingUsers.Remove(user.UserID); + CurrentMatchPlayingUserIds.Remove(user.UserID); RoomUpdated?.Invoke(); }, false); @@ -454,22 +454,22 @@ namespace osu.Game.Online.Multiplayer } /// - /// For the provided user ID, update whether the user is included in . + /// For the provided user ID, update whether the user is included in . /// /// The user's ID. /// The new state of the user. private void updateUserPlayingState(int userId, MultiplayerUserState state) { - bool wasPlaying = PlayingUsers.Contains(userId); + bool wasPlaying = CurrentMatchPlayingUserIds.Contains(userId); bool isPlaying = state >= MultiplayerUserState.WaitingForLoad && state <= MultiplayerUserState.FinishedPlay; if (isPlaying == wasPlaying) return; if (isPlaying) - PlayingUsers.Add(userId); + CurrentMatchPlayingUserIds.Add(userId); else - PlayingUsers.Remove(userId); + CurrentMatchPlayingUserIds.Remove(userId); } } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index ba0ed16cf4..ffa36ecfdb 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -200,7 +200,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { Debug.Assert(client.Room != null); - int[] userIds = client.PlayingUsers.ToArray(); + int[] userIds = client.CurrentMatchPlayingUserIds.ToArray(); StartPlay(() => new MultiplayerPlayer(SelectedItem.Value, userIds)); } diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index e7e5459f76..d4ce542a67 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -84,11 +84,11 @@ namespace osu.Game.Screens.Play.HUD // BindableList handles binding in a really bad way (Clear then AddRange) so we need to do this manually.. foreach (int userId in playingUsers) { - if (!multiplayerClient.PlayingUsers.Contains(userId)) + if (!multiplayerClient.CurrentMatchPlayingUserIds.Contains(userId)) usersChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, new[] { userId })); } - playingUsers.BindTo(multiplayerClient.PlayingUsers); + playingUsers.BindTo(multiplayerClient.CurrentMatchPlayingUserIds); playingUsers.BindCollectionChanged(usersChanged); } From f31a0e455a7834e839643a711df9dcd96409c315 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 29 Dec 2020 14:29:40 +0900 Subject: [PATCH 634/818] Minor xmldoc rewording --- osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs index bb690d786a..39d119b2a4 100644 --- a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs @@ -61,7 +61,7 @@ namespace osu.Game.Online.Multiplayer public MultiplayerRoom? Room { get; private set; } /// - /// The users in the joined which are currently in gameplay. + /// The users in the joined which are participating in the current gameplay loop. /// public readonly BindableList CurrentMatchPlayingUserIds = new BindableList(); From 45c578b85780f81051035dee3094efeef995fff9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 29 Dec 2020 15:10:08 +0900 Subject: [PATCH 635/818] Remove selection polling from multiplayer Looks like this was just copy-paste without any thought into whether it should exist. It really shouldn't exist. This is a thing for the playlists system because the *whole system* there relies on polling the web API to get updated information. In the case of mutliplayer, we hand off all communications to the realtime server at the point of joining the rooms. The argument that this was there to do faster polling on the selection isn't valid since the polling times were the same for both cases. Closes #11348. --- .../OnlinePlay/Multiplayer/Multiplayer.cs | 5 +--- .../Multiplayer/MultiplayerRoomManager.cs | 28 +------------------ 2 files changed, 2 insertions(+), 31 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs index 76f5c74433..310617a0bc 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs @@ -33,7 +33,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer if (!this.IsCurrentScreen()) { multiplayerRoomManager.TimeBetweenListingPolls.Value = 0; - multiplayerRoomManager.TimeBetweenSelectionPolls.Value = 0; } else { @@ -41,18 +40,16 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { case LoungeSubScreen _: multiplayerRoomManager.TimeBetweenListingPolls.Value = isIdle ? 120000 : 15000; - multiplayerRoomManager.TimeBetweenSelectionPolls.Value = isIdle ? 120000 : 15000; break; // Don't poll inside the match or anywhere else. default: multiplayerRoomManager.TimeBetweenListingPolls.Value = 0; - multiplayerRoomManager.TimeBetweenSelectionPolls.Value = 0; break; } } - Logger.Log($"Polling adjusted (listing: {multiplayerRoomManager.TimeBetweenListingPolls.Value}, selection: {multiplayerRoomManager.TimeBetweenSelectionPolls.Value})"); + Logger.Log($"Polling adjusted (listing: {multiplayerRoomManager.TimeBetweenListingPolls.Value})"); } protected override Room CreateNewRoom() diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs index 3cb263298f..5c327266a3 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs @@ -23,7 +23,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private StatefulMultiplayerClient multiplayerClient { get; set; } public readonly Bindable TimeBetweenListingPolls = new Bindable(); - public readonly Bindable TimeBetweenSelectionPolls = new Bindable(); + private readonly IBindable isConnected = new Bindable(); private readonly Bindable allowPolling = new Bindable(); @@ -119,11 +119,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer TimeBetweenPolls = { BindTarget = TimeBetweenListingPolls }, AllowPolling = { BindTarget = allowPolling } }, - new MultiplayerSelectionPollingComponent - { - TimeBetweenPolls = { BindTarget = TimeBetweenSelectionPolls }, - AllowPolling = { BindTarget = allowPolling } - } }; private class MultiplayerListingPollingComponent : ListingPollingComponent @@ -146,26 +141,5 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer protected override Task Poll() => !AllowPolling.Value ? Task.CompletedTask : base.Poll(); } - - private class MultiplayerSelectionPollingComponent : SelectionPollingComponent - { - public readonly IBindable AllowPolling = new Bindable(); - - protected override void LoadComplete() - { - base.LoadComplete(); - - AllowPolling.BindValueChanged(allowPolling => - { - if (!allowPolling.NewValue) - return; - - if (IsLoaded) - PollImmediately(); - }); - } - - protected override Task Poll() => !AllowPolling.Value ? Task.CompletedTask : base.Poll(); - } } } From 2cb84c5111b9e244d98492e4b7fe43e3ef553492 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 29 Dec 2020 15:19:52 +0900 Subject: [PATCH 636/818] Fix error message being shown to user on multiplayer disconnection when not in room --- osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs index 39d119b2a4..dc80488d39 100644 --- a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs @@ -84,7 +84,7 @@ namespace osu.Game.Online.Multiplayer IsConnected.BindValueChanged(connected => { // clean up local room state on server disconnect. - if (!connected.NewValue) + if (!connected.NewValue && Room != null) { Logger.Log("Connection to multiplayer server was lost.", LoggingTarget.Runtime, LogLevel.Important); LeaveRoom().CatchUnobservedExceptions(); From 903dca875e7aa364277dc336e52c9c350a6f363a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 29 Dec 2020 07:46:22 +0100 Subject: [PATCH 637/818] Make localUser a client property --- .../Online/Multiplayer/StatefulMultiplayerClient.cs | 5 +++++ .../Multiplayer/Match/MultiplayerReadyButton.cs | 10 ++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs index 39d119b2a4..0f44085134 100644 --- a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs @@ -65,6 +65,11 @@ namespace osu.Game.Online.Multiplayer /// public readonly BindableList CurrentMatchPlayingUserIds = new BindableList(); + /// + /// The corresponding to the local player, if available. + /// + public MultiplayerRoomUser? LocalUser => Room?.Users.SingleOrDefault(u => u.User?.Id == api.LocalUser.Value.Id); + [Resolved] private UserLookupCache userLookupCache { get; set; } = null!; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index 5009b435f4..9b406ff52c 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -27,9 +27,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match [Resolved] private IAPIProvider api { get; set; } - [CanBeNull] - private MultiplayerRoomUser localUser; - [Resolved] private OsuColour colours { get; set; } @@ -67,14 +64,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { base.OnRoomUpdated(); - // this method is called on leaving the room, so the local user may not exist in the room any more. - localUser = Room?.Users.SingleOrDefault(u => u.User?.Id == api.LocalUser.Value.Id); - updateState(); } private void updateState() { + var localUser = Client.LocalUser; + if (localUser == null) return; @@ -142,6 +138,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match private void onClick() { + var localUser = Client.LocalUser; + if (localUser == null) return; From 9ff214023297f4300a439e5c2427281a0cb37bcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 29 Dec 2020 07:51:46 +0100 Subject: [PATCH 638/818] Move ready-up logic to match sub-screen --- .../Match/MultiplayerMatchFooter.cs | 9 +++++- .../Match/MultiplayerReadyButton.cs | 30 ++++--------------- .../Multiplayer/MultiplayerMatchSubScreen.cs | 28 ++++++++++++++++- 3 files changed, 41 insertions(+), 26 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs index a52f62fe00..bd1ca1377e 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; @@ -19,7 +20,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match public readonly Bindable SelectedItem = new Bindable(); + public Action OnReady + { + set => readyButton.OnReady = value; + } + private readonly Drawable background; + private readonly MultiplayerReadyButton readyButton; public MultiplayerMatchFooter() { @@ -29,7 +36,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match InternalChildren = new[] { background = new Box { RelativeSizeAxes = Axes.Both }, - new MultiplayerReadyButton + readyButton = new MultiplayerReadyButton { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index 9b406ff52c..96470cf070 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -1,15 +1,14 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Diagnostics; using System.Linq; -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Game.Extensions; using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; using osu.Game.Online.API; @@ -24,6 +23,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { public Bindable SelectedItem => button.SelectedItem; + public Action OnReady + { + set => button.Action = value; + } + [Resolved] private IAPIProvider api { get; set; } @@ -47,7 +51,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match RelativeSizeAxes = Axes.Both, Size = Vector2.One, Enabled = { Value = true }, - Action = onClick }; } @@ -136,27 +139,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match } } - private void onClick() - { - var localUser = Client.LocalUser; - - if (localUser == null) - return; - - if (localUser.State == MultiplayerUserState.Idle) - Client.ChangeState(MultiplayerUserState.Ready).CatchUnobservedExceptions(true); - else - { - if (Room?.Host?.Equals(localUser) == true) - { - gameplayStartTracker.BeginOperation(); - Client.StartMatch().CatchUnobservedExceptions(true); - } - else - Client.ChangeState(MultiplayerUserState.Idle).CatchUnobservedExceptions(true); - } - } - private class ButtonWithTrianglesExposed : ReadyButton { public new Triangles Triangles => base.Triangles; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 8eafd7c7df..a87471c8af 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -9,6 +9,7 @@ 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; @@ -153,7 +154,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer }, new Drawable[] { - new MultiplayerMatchFooter { SelectedItem = { BindTarget = SelectedItem } } + new MultiplayerMatchFooter + { + SelectedItem = { BindTarget = SelectedItem }, + OnReady = onReady + } } }, RowDimensions = new[] @@ -199,6 +204,27 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private void onPlaylistChanged(object sender, NotifyCollectionChangedEventArgs e) => SelectedItem.Value = Playlist.FirstOrDefault(); + private void onReady() + { + var localUser = client.LocalUser; + + if (localUser == null) + return; + + if (localUser.State == MultiplayerUserState.Idle) + client.ChangeState(MultiplayerUserState.Ready).CatchUnobservedExceptions(true); + else + { + if (client.Room?.Host?.Equals(localUser) == true) + { + gameplayStartTracker.BeginOperation(); + client.StartMatch().CatchUnobservedExceptions(true); + } + else + client.ChangeState(MultiplayerUserState.Idle).CatchUnobservedExceptions(true); + } + } + private void onLoadRequested() { Debug.Assert(client.Room != null); From f59ba799d30390f030fcac0e12ac99a1893f78f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 29 Dec 2020 07:54:27 +0100 Subject: [PATCH 639/818] Adjust operation tracker implementation --- osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs b/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs index f2d943e14f..6a340d7954 100644 --- a/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs +++ b/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Allocation; using osu.Framework.Bindables; namespace osu.Game.Screens.OnlinePlay @@ -24,21 +25,26 @@ namespace osu.Game.Screens.OnlinePlay /// /// Begins tracking a new online operation. /// + /// + /// An that will automatically mark the operation as ended on disposal. + /// /// An operation has already been started. - public void BeginOperation() + public IDisposable BeginOperation() { if (leasedInProgress != null) throw new InvalidOperationException("Cannot begin operation while another is in progress."); leasedInProgress = inProgress.BeginLease(true); leasedInProgress.Value = true; + + return new InvokeOnDisposal(endOperation); } /// /// Ends tracking an online operation. /// Does nothing if an operation has not been begun yet. /// - public void EndOperation() + private void endOperation() { leasedInProgress?.Return(); leasedInProgress = null; From db52255bbec7a284b07baac967280e2cf5b3d9a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 29 Dec 2020 08:20:43 +0100 Subject: [PATCH 640/818] Adjust tracker usages to match new API --- .../TestSceneCreateMultiplayerMatchButton.cs | 9 ++++-- .../TestSceneMultiplayerReadyButton.cs | 9 ++++-- .../OnlinePlay/Lounge/LoungeSubScreen.cs | 21 +++++++++---- .../CreateMultiplayerMatchButton.cs | 10 +++--- .../Match/MultiplayerMatchSettingsOverlay.cs | 31 +++++++++++++++---- .../Match/MultiplayerReadyButton.cs | 10 +++--- .../Multiplayer/MultiplayerMatchSubScreen.cs | 19 +++++++++--- .../Screens/OnlinePlay/OnlinePlayScreen.cs | 2 +- 8 files changed, 79 insertions(+), 32 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneCreateMultiplayerMatchButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneCreateMultiplayerMatchButton.cs index 2a549e5262..381270c5aa 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneCreateMultiplayerMatchButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneCreateMultiplayerMatchButton.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -12,7 +13,7 @@ namespace osu.Game.Tests.Visual.Multiplayer public class TestSceneCreateMultiplayerMatchButton : MultiplayerTestScene { [Cached] - private OngoingOperationTracker joiningRoomTracker = new OngoingOperationTracker(); + private OngoingOperationTracker ongoingOperationTracker = new OngoingOperationTracker(); private CreateMultiplayerMatchButton button; @@ -31,12 +32,14 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestButtonEnableStateChanges() { + IDisposable joiningRoomOperation = null; + assertButtonEnableState(true); - AddStep("begin joining room", () => joiningRoomTracker.BeginOperation()); + AddStep("begin joining room", () => joiningRoomOperation = ongoingOperationTracker.BeginOperation()); assertButtonEnableState(false); - AddStep("end joining room", () => joiningRoomTracker.EndOperation()); + AddStep("end joining room", () => joiningRoomOperation.Dispose()); assertButtonEnableState(true); AddStep("disconnect client", () => Client.Disconnect()); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs index 958c6d218b..de02c6c844 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; @@ -31,7 +32,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private RulesetStore rulesets; [Cached] - private OngoingOperationTracker gameplayStartTracker = new OngoingOperationTracker(); + private OngoingOperationTracker ongoingOperationTracker = new OngoingOperationTracker(); [BackgroundDependencyLoader] private void load(GameHost host, AudioManager audio) @@ -167,11 +168,15 @@ namespace osu.Game.Tests.Visual.Multiplayer private void verifyGameplayStartFlow() { + IDisposable gameplayStartOperation = null; + + AddStep("hook up tracker", () => button.OnReady = () => gameplayStartOperation = ongoingOperationTracker.BeginOperation()); + addClickButtonStep(); AddAssert("user waiting for load", () => Client.Room?.Users[0].State == MultiplayerUserState.WaitingForLoad); AddAssert("ready button disabled", () => !button.ChildrenOfType().Single().Enabled.Value); - AddStep("transitioned to gameplay", () => gameplayStartTracker.EndOperation()); + AddStep("transitioned to gameplay", () => gameplayStartOperation.Dispose()); AddAssert("ready button enabled", () => button.ChildrenOfType().Single().Enabled.Value); } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index f25835f56a..2a16a62714 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -1,7 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.Diagnostics; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -39,7 +42,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge private MusicController music { get; set; } [Resolved(CanBeNull = true)] - private OngoingOperationTracker joiningRoomTracker { get; set; } + private OngoingOperationTracker ongoingOperationTracker { get; set; } + + [CanBeNull] + private IDisposable joiningRoomOperation { get; set; } [BackgroundDependencyLoader] private void load() @@ -102,9 +108,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge initialRoomsReceived.BindTo(RoomManager.InitialRoomsReceived); initialRoomsReceived.BindValueChanged(_ => updateLoadingLayer()); - if (joiningRoomTracker != null) + if (ongoingOperationTracker != null) { - joiningRoom.BindTo(joiningRoomTracker.InProgress); + joiningRoom.BindTo(ongoingOperationTracker.InProgress); joiningRoom.BindValueChanged(_ => updateLoadingLayer(), true); } } @@ -164,15 +170,18 @@ namespace osu.Game.Screens.OnlinePlay.Lounge private void joinRequested(Room room) { - joiningRoomTracker?.BeginOperation(); + Debug.Assert(joiningRoomOperation == null); + joiningRoomOperation = ongoingOperationTracker?.BeginOperation(); RoomManager?.JoinRoom(room, r => { Open(room); - joiningRoomTracker?.EndOperation(); + joiningRoomOperation?.Dispose(); + joiningRoomOperation = null; }, _ => { - joiningRoomTracker?.EndOperation(); + joiningRoomOperation?.Dispose(); + joiningRoomOperation = null; }); } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs index 7518a4e71b..3785dfc29f 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs @@ -11,13 +11,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer public class CreateMultiplayerMatchButton : PurpleTriangleButton { private IBindable isConnected; - private IBindable joiningRoom; + private IBindable operationInProgress; [Resolved] private StatefulMultiplayerClient multiplayerClient { get; set; } [Resolved] - private OngoingOperationTracker joiningRoomTracker { get; set; } + private OngoingOperationTracker ongoingOperationTracker { get; set; } [BackgroundDependencyLoader] private void load() @@ -29,10 +29,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer isConnected = multiplayerClient.IsConnected.GetBoundCopy(); isConnected.BindValueChanged(_ => updateState()); - joiningRoom = joiningRoomTracker.InProgress.GetBoundCopy(); - joiningRoom.BindValueChanged(_ => updateState(), true); + operationInProgress = ongoingOperationTracker.InProgress.GetBoundCopy(); + operationInProgress.BindValueChanged(_ => updateState(), true); } - private void updateState() => Enabled.Value = isConnected.Value && !joiningRoom.Value; + private void updateState() => Enabled.Value = isConnected.Value && !operationInProgress.Value; } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index de25cff546..59b138123d 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -2,6 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Diagnostics; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; @@ -68,7 +70,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match [Resolved] private Bindable ruleset { get; set; } - private readonly OngoingOperationTracker applyingSettingsTracker = new OngoingOperationTracker(); + [Resolved] + private OngoingOperationTracker ongoingOperationTracker { get; set; } + + private readonly IBindable operationInProgress = new BindableBool(); + + [CanBeNull] + private IDisposable applyingSettingsOperation; [BackgroundDependencyLoader] private void load(OsuColour colours) @@ -277,7 +285,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match MaxParticipants.BindValueChanged(count => MaxParticipantsField.Text = count.NewValue?.ToString(), true); RoomID.BindValueChanged(roomId => initialBeatmapControl.Alpha = roomId.NewValue == null ? 1 : 0, true); - applyingSettingsTracker.InProgress.BindValueChanged(v => + operationInProgress.BindTo(ongoingOperationTracker.InProgress); + operationInProgress.BindValueChanged(v => { if (v.NewValue) loadingLayer.Show(); @@ -290,7 +299,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { base.Update(); - ApplyButton.Enabled.Value = Playlist.Count > 0 && NameField.Text.Length > 0 && !applyingSettingsTracker.InProgress.Value; + ApplyButton.Enabled.Value = Playlist.Count > 0 && NameField.Text.Length > 0 && !operationInProgress.Value; } private void apply() @@ -299,7 +308,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match return; hideError(); - applyingSettingsTracker.BeginOperation(); + + Debug.Assert(applyingSettingsOperation == null); + applyingSettingsOperation = ongoingOperationTracker.BeginOperation(); // If the client is already in a room, update via the client. // Otherwise, update the room directly in preparation for it to be submitted to the API on match creation. @@ -332,15 +343,23 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match private void onSuccess(Room room) { - applyingSettingsTracker.EndOperation(); + Debug.Assert(applyingSettingsOperation != null); + SettingsApplied?.Invoke(); + + applyingSettingsOperation.Dispose(); + applyingSettingsOperation = null; } private void onError(string text) { + Debug.Assert(applyingSettingsOperation != null); + ErrorText.Text = text; ErrorText.FadeIn(50); - applyingSettingsTracker.EndOperation(); + + applyingSettingsOperation.Dispose(); + applyingSettingsOperation = null; } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index 96470cf070..b145cd46c1 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -35,14 +35,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match private OsuColour colours { get; set; } [Resolved] - private OngoingOperationTracker gameplayStartTracker { get; set; } + private OngoingOperationTracker ongoingOperationTracker { get; set; } private SampleChannel sampleReadyCount; private readonly ButtonWithTrianglesExposed button; private int countReady; - private IBindable gameplayStartInProgress; + private IBindable operationInProgress; public MultiplayerReadyButton() { @@ -59,8 +59,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { sampleReadyCount = audio.Samples.Get(@"SongSelect/select-difficulty"); - gameplayStartInProgress = gameplayStartTracker.InProgress.GetBoundCopy(); - gameplayStartInProgress.BindValueChanged(_ => updateState()); + operationInProgress = ongoingOperationTracker.InProgress.GetBoundCopy(); + operationInProgress.BindValueChanged(_ => updateState()); } protected override void OnRoomUpdated() @@ -105,7 +105,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match break; } - button.Enabled.Value = Client.Room?.State == MultiplayerRoomState.Open && !gameplayStartInProgress.Value; + button.Enabled.Value = Client.Room?.State == MultiplayerRoomState.Open && !operationInProgress.Value; if (newCountReady != countReady) { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index a87471c8af..b82bf508ce 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -1,9 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Specialized; using System.Diagnostics; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -32,13 +34,16 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [Resolved] private StatefulMultiplayerClient client { get; set; } - [Cached] - private OngoingOperationTracker gameplayStartTracker = new OngoingOperationTracker(); + [Resolved] + private OngoingOperationTracker ongoingOperationTracker { get; set; } private MultiplayerMatchSettingsOverlay settingsOverlay; private IBindable isConnected; + [CanBeNull] + private IDisposable gameplayStartOperation; + public MultiplayerMatchSubScreen(Room room) { Title = room.RoomID.Value == null ? "New room" : room.Name.Value; @@ -217,7 +222,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { if (client.Room?.Host?.Equals(localUser) == true) { - gameplayStartTracker.BeginOperation(); + Debug.Assert(gameplayStartOperation == null); + gameplayStartOperation = ongoingOperationTracker.BeginOperation(); + client.StartMatch().CatchUnobservedExceptions(true); } else @@ -232,7 +239,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer int[] userIds = client.CurrentMatchPlayingUserIds.ToArray(); StartPlay(() => new MultiplayerPlayer(SelectedItem.Value, userIds)); - gameplayStartTracker.EndOperation(); + + Debug.Assert(gameplayStartOperation != null); + + gameplayStartOperation.Dispose(); + gameplayStartOperation = null; } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index 92665c498b..13ebc1912e 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -53,7 +53,7 @@ namespace osu.Game.Screens.OnlinePlay private readonly Bindable currentFilter = new Bindable(new FilterCriteria()); [Cached] - private readonly OngoingOperationTracker joiningRoomTracker = new OngoingOperationTracker(); + private readonly OngoingOperationTracker ongoingOperationTracker = new OngoingOperationTracker(); [Resolved(CanBeNull = true)] private MusicController music { get; set; } From 03b78d1c4b2de871626068a454b163448f216e09 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 29 Dec 2020 15:27:22 +0900 Subject: [PATCH 641/818] Handle SocketExceptions and HttpRequestExceptions more silently These can occur when a network connection is completely unavailable (ie. host resolution failures are occurring). Currently these would appear as important errors which spammed the notification overlay every retry forever, while no network connection is available. I also took this opportunity to remove a lot of `bool` passing which was no longer in use (previously the fail count / retry process was different to what we have today). --- osu.Game/Online/API/APIAccess.cs | 46 ++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 133ba22406..2aaea22155 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Net; using System.Net.Http; +using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; using Newtonsoft.Json.Linq; @@ -293,8 +294,21 @@ namespace osu.Game.Online.API failureCount = 0; return true; } + catch (HttpRequestException re) + { + log.Add($"{nameof(HttpRequestException)} while performing request {req}: {re.Message}"); + handleFailure(); + return false; + } + catch (SocketException se) + { + log.Add($"{nameof(SocketException)} while performing request {req}: {se.Message}"); + handleFailure(); + return false; + } catch (WebException we) { + log.Add($"{nameof(WebException)} while performing request {req}: {we.Message}"); handleWebException(we); return false; } @@ -312,7 +326,7 @@ namespace osu.Game.Online.API /// public IBindable State => state; - private bool handleWebException(WebException we) + private void handleWebException(WebException we) { HttpStatusCode statusCode = (we.Response as HttpWebResponse)?.StatusCode ?? (we.Status == WebExceptionStatus.UnknownError ? HttpStatusCode.NotAcceptable : HttpStatusCode.RequestTimeout); @@ -330,26 +344,24 @@ namespace osu.Game.Online.API { case HttpStatusCode.Unauthorized: Logout(); - return true; + break; case HttpStatusCode.RequestTimeout: - failureCount++; - log.Add($@"API failure count is now {failureCount}"); - - if (failureCount < 3) - // we might try again at an api level. - return false; - - if (State.Value == APIState.Online) - { - state.Value = APIState.Failing; - flushQueue(); - } - - return true; + handleFailure(); + break; } + } - return true; + private void handleFailure() + { + failureCount++; + log.Add($@"API failure count is now {failureCount}"); + + if (failureCount >= 3 && State.Value == APIState.Online) + { + state.Value = APIState.Failing; + flushQueue(); + } } public bool IsLoggedIn => localUser.Value.Id > 1; From 4d04e0dee7c73eaab08ecb4a47cee7822221972a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 29 Dec 2020 16:25:51 +0900 Subject: [PATCH 642/818] Disallow entering the playlists/multiplayer screens if API is failing --- osu.Game/Screens/Menu/ButtonSystem.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 474cbde192..65a16f2fcb 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -156,7 +156,7 @@ namespace osu.Game.Screens.Menu private void onMultiplayer() { - if (!api.IsLoggedIn) + if (!api.IsLoggedIn || api.State.Value != APIState.Online) { notifications?.Post(new SimpleNotification { @@ -177,11 +177,11 @@ namespace osu.Game.Screens.Menu private void onPlaylists() { - if (!api.IsLoggedIn) + if (!api.IsLoggedIn || api.State.Value != APIState.Online) { notifications?.Post(new SimpleNotification { - Text = "You gotta be logged in to multi 'yo!", + Text = "You gotta be logged in to view playlists 'yo!", Icon = FontAwesome.Solid.Globe, Activated = () => { From 906a9b79b5b62a6d25c304549a6308451127079b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 29 Dec 2020 16:47:36 +0900 Subject: [PATCH 643/818] Show an error when forcefully exiting online play due to API failure --- osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index 4074dd1573..75612516a9 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Logging; using osu.Framework.Screens; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics.Containers; @@ -165,7 +166,10 @@ namespace osu.Game.Screens.OnlinePlay private void onlineStateChanged(ValueChangedEvent state) => Schedule(() => { if (state.NewValue != APIState.Online) + { + Logger.Log("API connection was lost, can't continue with online play", LoggingTarget.Network, LogLevel.Important); Schedule(forcefullyExit); + } }); protected override void LoadComplete() From e9b0652359e9bcffed8c4e84f95a35f3397eef45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 29 Dec 2020 09:09:47 +0100 Subject: [PATCH 644/818] Move ready-up operation logic again to client To salvage ready up button tests. --- .../TestSceneMultiplayerReadyButton.cs | 16 ++++++--- .../Multiplayer/StatefulMultiplayerClient.cs | 33 +++++++++++++++++++ .../Match/MultiplayerMatchFooter.cs | 4 +-- .../Match/MultiplayerReadyButton.cs | 2 +- .../Multiplayer/MultiplayerMatchSubScreen.cs | 33 ++++++++----------- 5 files changed, 61 insertions(+), 27 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs index de02c6c844..86398b205d 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs @@ -34,6 +34,8 @@ namespace osu.Game.Tests.Visual.Multiplayer [Cached] private OngoingOperationTracker ongoingOperationTracker = new OngoingOperationTracker(); + private IDisposable toggleReadyOperation; + [BackgroundDependencyLoader] private void load(GameHost host, AudioManager audio) { @@ -61,6 +63,14 @@ namespace osu.Game.Tests.Visual.Multiplayer Beatmap = { Value = beatmap }, Ruleset = { Value = beatmap.Ruleset } } + }, + OnToggleReady = async () => + { + toggleReadyOperation = ongoingOperationTracker.BeginOperation(); + + bool gameplayStarted = await Client.ToggleReady(); + if (!gameplayStarted) + toggleReadyOperation.Dispose(); } }; }); @@ -168,15 +178,11 @@ namespace osu.Game.Tests.Visual.Multiplayer private void verifyGameplayStartFlow() { - IDisposable gameplayStartOperation = null; - - AddStep("hook up tracker", () => button.OnReady = () => gameplayStartOperation = ongoingOperationTracker.BeginOperation()); - addClickButtonStep(); AddAssert("user waiting for load", () => Client.Room?.Users[0].State == MultiplayerUserState.WaitingForLoad); AddAssert("ready button disabled", () => !button.ChildrenOfType().Single().Enabled.Value); - AddStep("transitioned to gameplay", () => gameplayStartOperation.Dispose()); + AddStep("transitioned to gameplay", () => toggleReadyOperation.Dispose()); AddAssert("ready button enabled", () => button.ChildrenOfType().Single().Enabled.Value); } } diff --git a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs index 0f44085134..ee42e56d75 100644 --- a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs @@ -183,6 +183,39 @@ namespace osu.Game.Online.Multiplayer }); } + /// + /// Toggles the 's ready state. + /// + /// true if this toggle triggered a gameplay start; false otherwise. + /// If a toggle of ready state is not valid at this time. + public async Task ToggleReady() + { + var localUser = LocalUser; + + if (localUser == null) + return false; + + switch (localUser.State) + { + case MultiplayerUserState.Idle: + await ChangeState(MultiplayerUserState.Ready); + return false; + + case MultiplayerUserState.Ready: + if (Room?.Host?.Equals(localUser) == true) + { + await StartMatch(); + return true; + } + + await ChangeState(MultiplayerUserState.Idle); + return false; + + default: + throw new InvalidOperationException($"Cannot toggle ready when in {localUser.State}"); + } + } + public abstract Task TransferHost(int userId); public abstract Task ChangeSettings(MultiplayerRoomSettings settings); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs index bd1ca1377e..a5e7650e31 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs @@ -20,9 +20,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match public readonly Bindable SelectedItem = new Bindable(); - public Action OnReady + public Action OnToggleReady { - set => readyButton.OnReady = value; + set => readyButton.OnToggleReady = value; } private readonly Drawable background; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index b145cd46c1..251b5b30ae 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -23,7 +23,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { public Bindable SelectedItem => button.SelectedItem; - public Action OnReady + public Action OnToggleReady { set => button.Action = value; } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index b82bf508ce..88b4c8a1bf 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -162,7 +162,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer new MultiplayerMatchFooter { SelectedItem = { BindTarget = SelectedItem }, - OnReady = onReady + OnToggleReady = onToggleReady } } }, @@ -209,27 +209,22 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private void onPlaylistChanged(object sender, NotifyCollectionChangedEventArgs e) => SelectedItem.Value = Playlist.FirstOrDefault(); - private void onReady() + private void onToggleReady() { - var localUser = client.LocalUser; + Debug.Assert(gameplayStartOperation == null); + gameplayStartOperation = ongoingOperationTracker.BeginOperation(); - if (localUser == null) - return; + client.ToggleReady() + .ContinueWith(t => + { + // if gameplay was started, the button will be unblocked on load requested. + if (t.Result) return; - if (localUser.State == MultiplayerUserState.Idle) - client.ChangeState(MultiplayerUserState.Ready).CatchUnobservedExceptions(true); - else - { - if (client.Room?.Host?.Equals(localUser) == true) - { - Debug.Assert(gameplayStartOperation == null); - gameplayStartOperation = ongoingOperationTracker.BeginOperation(); - - client.StartMatch().CatchUnobservedExceptions(true); - } - else - client.ChangeState(MultiplayerUserState.Idle).CatchUnobservedExceptions(true); - } + // gameplay was not started; unblock button. + gameplayStartOperation?.Dispose(); + gameplayStartOperation = null; + }) + .CatchUnobservedExceptions(); } private void onLoadRequested() From 274730de34336b646ca57027c6820922cd9fc0ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 29 Dec 2020 09:15:33 +0100 Subject: [PATCH 645/818] Cache tracker in test scene to resolve test fails --- .../Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs index 8869718fd1..2344ebea0e 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs @@ -3,10 +3,12 @@ using System.Linq; using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Online.Rooms; using osu.Game.Rulesets.Osu; +using osu.Game.Screens.OnlinePlay; using osu.Game.Screens.OnlinePlay.Multiplayer; using osu.Game.Screens.OnlinePlay.Multiplayer.Match; using osu.Game.Tests.Beatmaps; @@ -18,6 +20,9 @@ namespace osu.Game.Tests.Visual.Multiplayer { private MultiplayerMatchSubScreen screen; + [Cached] + private OngoingOperationTracker ongoingOperationTracker = new OngoingOperationTracker(); + public TestSceneMultiplayerMatchSubScreen() : base(false) { From 6bbd0ecfac9fc77ae680064ec61b29f2409380e8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 29 Dec 2020 17:39:00 +0900 Subject: [PATCH 646/818] Remove unused lock object --- osu.Game/Online/Multiplayer/MultiplayerRoom.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerRoom.cs b/osu.Game/Online/Multiplayer/MultiplayerRoom.cs index 2134e50d72..99f0eae2b3 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerRoom.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerRoom.cs @@ -42,8 +42,6 @@ namespace osu.Game.Online.Multiplayer /// public MultiplayerRoomUser? Host { get; set; } - private object writeLock = new object(); - [JsonConstructor] public MultiplayerRoom(in long roomId) { From cafa241ef3e7d73952956fda2ec83b04ef519da4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 29 Dec 2020 09:41:32 +0100 Subject: [PATCH 647/818] Fix ready-up button getting stuck if server operation fails --- .../OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 88b4c8a1bf..5676bb22b4 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; @@ -218,13 +217,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer .ContinueWith(t => { // if gameplay was started, the button will be unblocked on load requested. - if (t.Result) return; + // accessing Exception here also silences any potential errors from the antecedent task + // (we still want to unblock the button if the ready-up fails). + if (t.Exception == null && t.Result) return; // gameplay was not started; unblock button. gameplayStartOperation?.Dispose(); gameplayStartOperation = null; - }) - .CatchUnobservedExceptions(); + }); } private void onLoadRequested() From 5d2319923324b48fa749474c11686db3f82b41d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 29 Dec 2020 10:56:29 +0100 Subject: [PATCH 648/818] Trim redundant IsLoggedIn checks --- osu.Game/Screens/Menu/ButtonSystem.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 65a16f2fcb..3ac9bb6c24 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -156,7 +156,7 @@ namespace osu.Game.Screens.Menu private void onMultiplayer() { - if (!api.IsLoggedIn || api.State.Value != APIState.Online) + if (api.State.Value != APIState.Online) { notifications?.Post(new SimpleNotification { @@ -177,7 +177,7 @@ namespace osu.Game.Screens.Menu private void onPlaylists() { - if (!api.IsLoggedIn || api.State.Value != APIState.Online) + if (api.State.Value != APIState.Online) { notifications?.Post(new SimpleNotification { From 361d215ab4def9965856c4e998a6373c504bfc0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 29 Dec 2020 10:56:59 +0100 Subject: [PATCH 649/818] Reword notification messages to match new logic --- osu.Game/Screens/Menu/ButtonSystem.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 3ac9bb6c24..fd4d74dc6b 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -160,7 +160,7 @@ namespace osu.Game.Screens.Menu { notifications?.Post(new SimpleNotification { - Text = "You gotta be logged in to multi 'yo!", + Text = "You gotta be online to multi 'yo!", Icon = FontAwesome.Solid.Globe, Activated = () => { @@ -181,7 +181,7 @@ namespace osu.Game.Screens.Menu { notifications?.Post(new SimpleNotification { - Text = "You gotta be logged in to view playlists 'yo!", + Text = "You gotta be online to view playlists 'yo!", Icon = FontAwesome.Solid.Globe, Activated = () => { From f2163a471a83eee0c0d41486f256a9b607113e27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 29 Dec 2020 11:53:42 +0100 Subject: [PATCH 650/818] Trim missed reference to deleted member --- .../Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs index 7a3845cbf3..80d1acd145 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs @@ -143,7 +143,6 @@ namespace osu.Game.Tests.Visual.Multiplayer RoomManager = { TimeBetweenListingPolls = { Value = 1 }, - TimeBetweenSelectionPolls = { Value = 1 } } }; From 3552034ffe0044dc5edff9195e6e609504336c84 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 30 Dec 2020 00:55:27 +0900 Subject: [PATCH 651/818] 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 cd2ce58c55..611f0d05f4 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 3e1b56c29c..93aa2bc701 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -26,7 +26,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 85ba0590ea..5445adb3fb 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -88,7 +88,7 @@ - + From 497d644a19f04ad19d990bbe7a042f1a556265f1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 30 Dec 2020 20:24:50 +0900 Subject: [PATCH 652/818] Move thread safety / locking logic from MultiplayerRoom --- .../Online/Multiplayer/MultiplayerRoom.cs | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerRoom.cs b/osu.Game/Online/Multiplayer/MultiplayerRoom.cs index 99f0eae2b3..12fcf25ace 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerRoom.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerRoom.cs @@ -5,9 +5,7 @@ using System; using System.Collections.Generic; -using System.Threading; using Newtonsoft.Json; -using osu.Framework.Allocation; namespace osu.Game.Online.Multiplayer { @@ -48,27 +46,6 @@ namespace osu.Game.Online.Multiplayer RoomID = roomId; } - private object updateLock = new object(); - - private ManualResetEventSlim freeForWrite = new ManualResetEventSlim(true); - - /// - /// Request a lock on this room to perform a thread-safe update. - /// - public IDisposable LockForUpdate() - { - // ReSharper disable once InconsistentlySynchronizedField - freeForWrite.Wait(); - - lock (updateLock) - { - freeForWrite.Wait(); - freeForWrite.Reset(); - - return new ValueInvokeOnDisposal(this, r => freeForWrite.Set()); - } - } - public override string ToString() => $"RoomID:{RoomID} Host:{Host?.UserID} Users:{Users.Count} State:{State} Settings: [{Settings}]"; } } From 59f2017a13672be09b527873470d528717d4ae70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 30 Dec 2020 16:22:11 +0100 Subject: [PATCH 653/818] Move BindValueChanged subscriptions to LoadComplete --- .../Multiplayer/CreateMultiplayerMatchButton.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs index 3785dfc29f..87b0e49b5b 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs @@ -27,9 +27,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer Text = "Create room"; isConnected = multiplayerClient.IsConnected.GetBoundCopy(); - isConnected.BindValueChanged(_ => updateState()); - operationInProgress = ongoingOperationTracker.InProgress.GetBoundCopy(); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + isConnected.BindValueChanged(_ => updateState()); operationInProgress.BindValueChanged(_ => updateState(), true); } From dd87478690096232e4833dc69116006b1bf1e57e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 30 Dec 2020 16:29:19 +0100 Subject: [PATCH 654/818] Add helper IsHost property to Client --- .../Online/Multiplayer/StatefulMultiplayerClient.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs index ee42e56d75..290765dc35 100644 --- a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs @@ -70,6 +70,18 @@ namespace osu.Game.Online.Multiplayer /// public MultiplayerRoomUser? LocalUser => Room?.Users.SingleOrDefault(u => u.User?.Id == api.LocalUser.Value.Id); + /// + /// Whether the is the host in . + /// + public bool IsHost + { + get + { + var localUser = LocalUser; + return localUser != null && Room?.Host != null && localUser.Equals(Room.Host); + } + } + [Resolved] private UserLookupCache userLookupCache { get; set; } = null!; From d34609b98ed925ccb181f2fc6d47101e3c1623a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 30 Dec 2020 16:29:36 +0100 Subject: [PATCH 655/818] Rename On{ToggleReady -> ReadyClick} --- .../Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs | 2 +- .../OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs | 4 ++-- .../OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs | 2 +- .../OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs index 86398b205d..1d82b29a91 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs @@ -64,7 +64,7 @@ namespace osu.Game.Tests.Visual.Multiplayer Ruleset = { Value = beatmap.Ruleset } } }, - OnToggleReady = async () => + OnReadyClick = async () => { toggleReadyOperation = ongoingOperationTracker.BeginOperation(); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs index a5e7650e31..bbf861fac3 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs @@ -20,9 +20,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match public readonly Bindable SelectedItem = new Bindable(); - public Action OnToggleReady + public Action OnReadyClick { - set => readyButton.OnToggleReady = value; + set => readyButton.OnReadyClick = value; } private readonly Drawable background; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index 251b5b30ae..c6018a79e9 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -23,7 +23,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { public Bindable SelectedItem => button.SelectedItem; - public Action OnToggleReady + public Action OnReadyClick { set => button.Action = value; } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 5676bb22b4..b00ab065fb 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -161,7 +161,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer new MultiplayerMatchFooter { SelectedItem = { BindTarget = SelectedItem }, - OnToggleReady = onToggleReady + OnReadyClick = onToggleReady } } }, From f800448c87f163346a2ce4284ace782646816823 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 30 Dec 2020 18:00:57 +0100 Subject: [PATCH 656/818] Move game start logic to a higher level --- .../TestSceneMultiplayerReadyButton.cs | 17 ++++--- .../Multiplayer/StatefulMultiplayerClient.cs | 15 ++---- .../Multiplayer/MultiplayerMatchSubScreen.cs | 51 ++++++++++++------- 3 files changed, 48 insertions(+), 35 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs index 1d82b29a91..a6037dcbf2 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs @@ -34,7 +34,7 @@ namespace osu.Game.Tests.Visual.Multiplayer [Cached] private OngoingOperationTracker ongoingOperationTracker = new OngoingOperationTracker(); - private IDisposable toggleReadyOperation; + private IDisposable readyClickOperation; [BackgroundDependencyLoader] private void load(GameHost host, AudioManager audio) @@ -66,11 +66,16 @@ namespace osu.Game.Tests.Visual.Multiplayer }, OnReadyClick = async () => { - toggleReadyOperation = ongoingOperationTracker.BeginOperation(); + readyClickOperation = ongoingOperationTracker.BeginOperation(); - bool gameplayStarted = await Client.ToggleReady(); - if (!gameplayStarted) - toggleReadyOperation.Dispose(); + if (Client.IsHost && Client.LocalUser?.State == MultiplayerUserState.Ready) + { + await Client.StartMatch(); + return; + } + + await Client.ToggleReady(); + readyClickOperation.Dispose(); } }; }); @@ -182,7 +187,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("user waiting for load", () => Client.Room?.Users[0].State == MultiplayerUserState.WaitingForLoad); AddAssert("ready button disabled", () => !button.ChildrenOfType().Single().Enabled.Value); - AddStep("transitioned to gameplay", () => toggleReadyOperation.Dispose()); + AddStep("transitioned to gameplay", () => readyClickOperation.Dispose()); AddAssert("ready button enabled", () => button.ChildrenOfType().Single().Enabled.Value); } } diff --git a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs index 290765dc35..5c6baa4fea 100644 --- a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs @@ -198,30 +198,23 @@ namespace osu.Game.Online.Multiplayer /// /// Toggles the 's ready state. /// - /// true if this toggle triggered a gameplay start; false otherwise. /// If a toggle of ready state is not valid at this time. - public async Task ToggleReady() + public async Task ToggleReady() { var localUser = LocalUser; if (localUser == null) - return false; + return; switch (localUser.State) { case MultiplayerUserState.Idle: await ChangeState(MultiplayerUserState.Ready); - return false; + return; case MultiplayerUserState.Ready: - if (Room?.Host?.Equals(localUser) == true) - { - await StartMatch(); - return true; - } - await ChangeState(MultiplayerUserState.Idle); - return false; + return; default: throw new InvalidOperationException($"Cannot toggle ready when in {localUser.State}"); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index b00ab065fb..1bef8841f5 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -11,6 +11,7 @@ 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; @@ -41,7 +42,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private IBindable isConnected; [CanBeNull] - private IDisposable gameplayStartOperation; + private IDisposable readyClickOperation; public MultiplayerMatchSubScreen(Room room) { @@ -161,7 +162,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer new MultiplayerMatchFooter { SelectedItem = { BindTarget = SelectedItem }, - OnReadyClick = onToggleReady + OnReadyClick = onReadyClick } } }, @@ -208,23 +209,37 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private void onPlaylistChanged(object sender, NotifyCollectionChangedEventArgs e) => SelectedItem.Value = Playlist.FirstOrDefault(); - private void onToggleReady() + private void onReadyClick() { - Debug.Assert(gameplayStartOperation == null); - gameplayStartOperation = ongoingOperationTracker.BeginOperation(); + Debug.Assert(readyClickOperation == null); + readyClickOperation = ongoingOperationTracker.BeginOperation(); + + if (client.IsHost && client.LocalUser?.State == MultiplayerUserState.Ready) + { + client.StartMatch() + .ContinueWith(t => + { + // accessing Exception here silences any potential errors from the antecedent task + if (t.Exception != null) + { + // gameplay was not started due to an exception; unblock button. + endOperation(); + } + + // gameplay is starting, the button will be unblocked on load requested. + }); + return; + } client.ToggleReady() - .ContinueWith(t => - { - // if gameplay was started, the button will be unblocked on load requested. - // accessing Exception here also silences any potential errors from the antecedent task - // (we still want to unblock the button if the ready-up fails). - if (t.Exception == null && t.Result) return; + .ContinueWith(_ => endOperation()) + .CatchUnobservedExceptions(); - // gameplay was not started; unblock button. - gameplayStartOperation?.Dispose(); - gameplayStartOperation = null; - }); + void endOperation() + { + readyClickOperation?.Dispose(); + readyClickOperation = null; + } } private void onLoadRequested() @@ -235,10 +250,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer StartPlay(() => new MultiplayerPlayer(SelectedItem.Value, userIds)); - Debug.Assert(gameplayStartOperation != null); + Debug.Assert(readyClickOperation != null); - gameplayStartOperation.Dispose(); - gameplayStartOperation = null; + readyClickOperation.Dispose(); + readyClickOperation = null; } protected override void Dispose(bool isDisposing) From eb64e6bf4da427df3b3b0f1bfab8dbab42f4819f Mon Sep 17 00:00:00 2001 From: Susko3 <16479013+Susko3@users.noreply.github.com> Date: Wed, 30 Dec 2020 23:35:07 +0100 Subject: [PATCH 657/818] Remove duplicate application/octet-stream --- osu.Android/OsuGameActivity.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Android/OsuGameActivity.cs b/osu.Android/OsuGameActivity.cs index da69b516dd..9d28ad7c5b 100644 --- a/osu.Android/OsuGameActivity.cs +++ b/osu.Android/OsuGameActivity.cs @@ -18,7 +18,6 @@ namespace osu.Android [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullUser, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = false, LaunchMode = LaunchMode.SingleInstance)] [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osz", DataHost = "*", DataMimeType = "*/*")] [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osk", DataHost = "*", DataMimeType = "*/*")] - [IntentFilter(new[] { Intent.ActionSend }, Categories = new[] { Intent.CategoryDefault }, DataMimeType = "application/octet-stream")] [IntentFilter(new[] { Intent.ActionSend }, Categories = new[] { Intent.CategoryDefault }, DataMimeTypes = new[] { "application/zip", "application/octet-stream" })] [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryBrowsable, Intent.CategoryDefault }, DataSchemes = new[] { "osu", "osump" })] public class OsuGameActivity : AndroidGameActivity From f9196ae9767653c75dc2d9ff6a0a273671a4466c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 31 Dec 2020 16:36:20 +0900 Subject: [PATCH 658/818] Fix PerformFromMenuRunner failing if CurrentScreen is null --- osu.Game/PerformFromMenuRunner.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/PerformFromMenuRunner.cs b/osu.Game/PerformFromMenuRunner.cs index e2d4fc6051..9222a64023 100644 --- a/osu.Game/PerformFromMenuRunner.cs +++ b/osu.Game/PerformFromMenuRunner.cs @@ -73,13 +73,16 @@ namespace osu.Game // find closest valid target IScreen current = getCurrentScreen(); + if (current == null) + return; + // a dialog may be blocking the execution for now. if (checkForDialog(current)) return; game?.CloseAllOverlays(false); // we may already be at the target screen type. - if (validScreens.Contains(getCurrentScreen().GetType()) && !beatmap.Disabled) + if (validScreens.Contains(current.GetType()) && !beatmap.Disabled) { complete(); return; From 00c6703c51df3cd05788d1c41d84b64d88de1ff6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 31 Dec 2020 10:27:42 +0100 Subject: [PATCH 659/818] Inline complete method as well For better guarantees that `finalAction` is actually called on the same screen that `checkCanComplete()` was (uses result of one `getCurrentScreen()` call throughout instead of calling multiple times). --- osu.Game/PerformFromMenuRunner.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/osu.Game/PerformFromMenuRunner.cs b/osu.Game/PerformFromMenuRunner.cs index 9222a64023..7999023998 100644 --- a/osu.Game/PerformFromMenuRunner.cs +++ b/osu.Game/PerformFromMenuRunner.cs @@ -84,7 +84,8 @@ namespace osu.Game // we may already be at the target screen type. if (validScreens.Contains(current.GetType()) && !beatmap.Disabled) { - complete(); + finalAction(current); + Cancel(); return; } @@ -138,11 +139,5 @@ namespace osu.Game lastEncounteredDialogScreen = current; return true; } - - private void complete() - { - finalAction(getCurrentScreen()); - Cancel(); - } } } From 2d279350ad5f6a4e544601c7be7bca768bef1cbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 31 Dec 2020 11:29:02 +0100 Subject: [PATCH 660/818] Catch multiplayer client-related unobserved exceptions better Silencing an exception from a task continuation requires accessing `task.Exception` in any way, which was not done previously if `logOnError` was false. To resolve without having to worry whether the compiler will optimise away a useless access or now, just always log, but switch the logging level. The unimportant errors will be logged as debug and therefore essentially silenced on release builds (but could still be potentially useful in debugging). --- osu.Game/Extensions/TaskExtensions.cs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/osu.Game/Extensions/TaskExtensions.cs b/osu.Game/Extensions/TaskExtensions.cs index a1215d786b..4138c2757a 100644 --- a/osu.Game/Extensions/TaskExtensions.cs +++ b/osu.Game/Extensions/TaskExtensions.cs @@ -1,7 +1,11 @@ // 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 @@ -13,13 +17,19 @@ namespace osu.Game.Extensions /// Avoids unobserved exceptions from being fired. /// /// The task. - /// Whether errors should be logged as important, or silently ignored. - public static void CatchUnobservedExceptions(this Task task, bool logOnError = false) + /// + /// 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 => { - if (logOnError) - Logger.Log($"Error running task: {t.Exception?.Message ?? "unknown"}", LoggingTarget.Runtime, LogLevel.Important); + 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); } } From 7d9a61fbc1b7afad59df89d3cf13cea4cce8be8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 31 Dec 2020 11:50:59 +0100 Subject: [PATCH 661/818] Handle unobserved exceptions from ready button properly --- .../OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 1bef8841f5..39323d9db9 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -222,6 +222,7 @@ 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(); } @@ -232,8 +233,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer } client.ToggleReady() - .ContinueWith(_ => endOperation()) - .CatchUnobservedExceptions(); + .ContinueWith(t => + { + t.CatchUnobservedExceptions(true); // will run immediately. + endOperation(); + }); void endOperation() { From 17abe90c27ff837d39e97ec3678dad33bad3e6c9 Mon Sep 17 00:00:00 2001 From: mcendu Date: Thu, 31 Dec 2020 20:23:13 +0800 Subject: [PATCH 662/818] move SkinnableHealthDisplay Similar components are in osu.Game.Screens.Play.HUD while this is not --- .../Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs | 2 +- osu.Game/Screens/Play/{ => HUD}/SkinnableHealthDisplay.cs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) rename osu.Game/Screens/Play/{ => HUD}/SkinnableHealthDisplay.cs (95%) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs index e1b0820662..5bac8582d7 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs @@ -9,7 +9,7 @@ using osu.Game.Rulesets; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Judgements; -using osu.Game.Screens.Play; +using osu.Game.Screens.Play.HUD; namespace osu.Game.Tests.Visual.Gameplay { diff --git a/osu.Game/Screens/Play/SkinnableHealthDisplay.cs b/osu.Game/Screens/Play/HUD/SkinnableHealthDisplay.cs similarity index 95% rename from osu.Game/Screens/Play/SkinnableHealthDisplay.cs rename to osu.Game/Screens/Play/HUD/SkinnableHealthDisplay.cs index d35d15d665..1f91f5e50f 100644 --- a/osu.Game/Screens/Play/SkinnableHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableHealthDisplay.cs @@ -5,10 +5,9 @@ using System; using osu.Framework.Bindables; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; -using osu.Game.Screens.Play.HUD; using osu.Game.Skinning; -namespace osu.Game.Screens.Play +namespace osu.Game.Screens.Play.HUD { public class SkinnableHealthDisplay : SkinnableDrawable, IHealthDisplay { From b4df2d6d43f96659c6687badc4c739770957ebfa Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 1 Jan 2021 03:46:09 +0300 Subject: [PATCH 663/818] Add method for copying properties from another mod --- osu.Game/Rulesets/Mods/Mod.cs | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index b8dc7a2661..236bf9ff00 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -131,22 +131,31 @@ namespace osu.Game.Rulesets.Mods /// public virtual Mod CreateCopy() { - var copy = (Mod)Activator.CreateInstance(GetType()); + var result = (Mod)Activator.CreateInstance(GetType(), true); + result.CopyFrom(this); + return result; + } + + /// + /// Copies properties of given mod into here, through changing value. + /// + /// The mod to copy properties from. + public void CopyFrom(Mod them) + { + if (them.GetType() != GetType()) + throw new ArgumentException($"Expected mod of type {GetType()}, got {them.GetType()}.", nameof(them)); - // Copy bindable values across foreach (var (_, prop) in this.GetSettingsSourceProperties()) { - var origBindable = prop.GetValue(this); - var copyBindable = prop.GetValue(copy); + var ourBindable = prop.GetValue(this); + var theirBindable = prop.GetValue(them); // The bindables themselves are readonly, so the value must be transferred through the Bindable.Value property. - var valueProperty = origBindable.GetType().GetProperty(nameof(Bindable.Value), BindingFlags.Public | BindingFlags.Instance); + var valueProperty = theirBindable.GetType().GetProperty(nameof(Bindable.Value), BindingFlags.Public | BindingFlags.Instance); Debug.Assert(valueProperty != null); - valueProperty.SetValue(copyBindable, valueProperty.GetValue(origBindable)); + valueProperty.SetValue(ourBindable, valueProperty.GetValue(theirBindable)); } - - return copy; } public bool Equals(IMod other) => GetType() == other?.GetType(); From 2ce9599957d50f5c9b8220cf7d3105372191a6d4 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 1 Jan 2021 03:47:13 +0300 Subject: [PATCH 664/818] Copy selected mods properties into overlay's buttons --- osu.Game/Overlays/Mods/ModSection.cs | 24 +++++++++++++++------- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 2 +- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index 0107f94dcf..86da179064 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -127,18 +127,28 @@ namespace osu.Game.Overlays.Mods } /// - /// Select one or more mods in this section and deselects all other ones. + /// Updates all buttons with the given list of selected mods. /// - /// The types of s which should be selected. - public void SelectTypes(IEnumerable modTypes) + /// The types of s to select. + public void UpdateSelectedMods(IReadOnlyList newSelectedMods) { foreach (var button in buttons) { - int i = Array.FindIndex(button.Mods, m => modTypes.Any(t => t == m.GetType())); + int index = -1; - if (i >= 0) - button.SelectAt(i); - else + foreach (var mod in newSelectedMods) + { + index = Array.FindIndex(button.Mods, m1 => mod.GetType() == m1.GetType()); + if (index < 0) + continue; + + var buttonMod = button.Mods[index]; + buttonMod.CopyFrom(mod); + button.SelectAt(index); + break; + } + + if (index < 0) button.Deselect(); } } diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 34f5c70adb..c1622548f5 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -409,7 +409,7 @@ namespace osu.Game.Overlays.Mods private void selectedModsChanged(ValueChangedEvent> mods) { foreach (var section in ModSectionsContainer.Children) - section.SelectTypes(mods.NewValue.Select(m => m.GetType()).ToList()); + section.UpdateSelectedMods(mods.NewValue); updateMods(); } From ee664ad57117fbdd01dd38a3025a621e69bf7eca Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 1 Jan 2021 03:47:18 +0300 Subject: [PATCH 665/818] Add test coverage --- .../UserInterface/TestSceneModSelectOverlay.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 6f083f4ab6..0d0acbb8f4 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -131,6 +131,18 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("ensure mods not selected", () => modDisplay.Current.Value.Count == 0); } + [Test] + public void TestExternallySetCustomizedMod() + { + AddStep("set customized mod externally", () => SelectedMods.Value = new[] { new OsuModDoubleTime { SpeedChange = { Value = 1.01 } } }); + + AddAssert("ensure button is selected and customized accordingly", () => + { + var button = modSelect.GetModButton(SelectedMods.Value.Single()); + return ((OsuModDoubleTime)button.SelectedMod).SpeedChange.Value == 1.01; + }); + } + private void testSingleMod(Mod mod) { selectNext(mod); From a031c8e0b62f3933e23e049dc75633f6c4603aef Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 1 Jan 2021 15:34:09 +0300 Subject: [PATCH 666/818] Apply documentation suggestions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Overlays/Mods/ModSection.cs | 2 +- osu.Game/Rulesets/Mods/Mod.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index 86da179064..e29ed4f5c4 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -129,7 +129,7 @@ namespace osu.Game.Overlays.Mods /// /// Updates all buttons with the given list of selected mods. /// - /// The types of s to select. + /// The new list of selected mods to select. public void UpdateSelectedMods(IReadOnlyList newSelectedMods) { foreach (var button in buttons) diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index 236bf9ff00..6e83299d63 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -137,7 +137,7 @@ namespace osu.Game.Rulesets.Mods } /// - /// Copies properties of given mod into here, through changing value. + /// Copies mod setting values from into this instance. /// /// The mod to copy properties from. public void CopyFrom(Mod them) From c1a1e3acc5aa199442ce7a96058e1eaae2bc7dd9 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 1 Jan 2021 15:40:40 +0300 Subject: [PATCH 667/818] Revert drive-by changes --- osu.Game/Rulesets/Mods/Mod.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index 6e83299d63..3f1f16e561 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -131,7 +131,7 @@ namespace osu.Game.Rulesets.Mods /// public virtual Mod CreateCopy() { - var result = (Mod)Activator.CreateInstance(GetType(), true); + var result = (Mod)Activator.CreateInstance(GetType()); result.CopyFrom(this); return result; } @@ -151,7 +151,7 @@ namespace osu.Game.Rulesets.Mods var theirBindable = prop.GetValue(them); // The bindables themselves are readonly, so the value must be transferred through the Bindable.Value property. - var valueProperty = theirBindable.GetType().GetProperty(nameof(Bindable.Value), BindingFlags.Public | BindingFlags.Instance); + var valueProperty = ourBindable.GetType().GetProperty(nameof(Bindable.Value), BindingFlags.Public | BindingFlags.Instance); Debug.Assert(valueProperty != null); valueProperty.SetValue(ourBindable, valueProperty.GetValue(theirBindable)); From 988f9b98a153d95b5f6de42dc3782d08912c125f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 1 Jan 2021 16:16:00 +0300 Subject: [PATCH 668/818] Split button mods updating to private method --- osu.Game/Overlays/Mods/ModSection.cs | 30 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index e29ed4f5c4..573d1e5355 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -133,24 +133,24 @@ namespace osu.Game.Overlays.Mods public void UpdateSelectedMods(IReadOnlyList newSelectedMods) { foreach (var button in buttons) + updateButtonMods(button, newSelectedMods); + } + + private void updateButtonMods(ModButton button, IReadOnlyList newSelectedMods) + { + foreach (var mod in newSelectedMods) { - int index = -1; - - foreach (var mod in newSelectedMods) - { - index = Array.FindIndex(button.Mods, m1 => mod.GetType() == m1.GetType()); - if (index < 0) - continue; - - var buttonMod = button.Mods[index]; - buttonMod.CopyFrom(mod); - button.SelectAt(index); - break; - } - + var index = Array.FindIndex(button.Mods, m1 => mod.GetType() == m1.GetType()); if (index < 0) - button.Deselect(); + continue; + + var buttonMod = button.Mods[index]; + buttonMod.CopyFrom(mod); + button.SelectAt(index); + return; } + + button.Deselect(); } protected ModSection() From 7441cfd94ee93e764102eead7839bb2c862ce541 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 1 Jan 2021 17:53:29 +0000 Subject: [PATCH 669/818] Bump Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson Bumps [Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson](https://github.com/aspnet/AspNetCore) from 3.1.9 to 3.1.10. - [Release notes](https://github.com/aspnet/AspNetCore/releases) - [Commits](https://github.com/aspnet/AspNetCore/compare/v3.1.9...v3.1.10) Signed-off-by: dependabot-preview[bot] --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 93aa2bc701..4c57eb6d00 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -22,7 +22,7 @@ - + From 0fd2e368c1c73e0d2e4932a620f2f4c6021633c7 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 1 Jan 2021 17:53:41 +0000 Subject: [PATCH 670/818] Bump Microsoft.NET.Test.Sdk from 16.8.0 to 16.8.3 Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 16.8.0 to 16.8.3. - [Release notes](https://github.com/microsoft/vstest/releases) - [Commits](https://github.com/microsoft/vstest/compare/v16.8.0...v16.8.3) Signed-off-by: dependabot-preview[bot] --- .../osu.Game.Rulesets.Catch.Tests.csproj | 2 +- .../osu.Game.Rulesets.Mania.Tests.csproj | 2 +- osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj | 2 +- .../osu.Game.Rulesets.Taiko.Tests.csproj | 2 +- osu.Game.Tests/osu.Game.Tests.csproj | 2 +- osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj index 61ecd79e3d..51d2032795 100644 --- a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj +++ b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj @@ -2,7 +2,7 @@ - + diff --git a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj index fa7bfd7169..3261f632f2 100644 --- a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj +++ b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj @@ -2,7 +2,7 @@ - + diff --git a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj index d6a03da807..32243e0bc3 100644 --- a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj +++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj @@ -2,7 +2,7 @@ - + diff --git a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj index a89645d881..210f81d111 100644 --- a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj +++ b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj @@ -2,7 +2,7 @@ - + diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 83d7b4135a..9049b67f90 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -3,7 +3,7 @@ - + diff --git a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj index bc6b994988..dc4f22788d 100644 --- a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj +++ b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj @@ -5,7 +5,7 @@ - + From 652b0ccd8fe5c47dbde90e41f15d8d11baea76f7 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 1 Jan 2021 17:54:11 +0000 Subject: [PATCH 671/818] Bump Microsoft.AspNetCore.SignalR.Client from 3.1.9 to 3.1.10 Bumps [Microsoft.AspNetCore.SignalR.Client](https://github.com/aspnet/AspNetCore) from 3.1.9 to 3.1.10. - [Release notes](https://github.com/aspnet/AspNetCore/releases) - [Commits](https://github.com/aspnet/AspNetCore/compare/v3.1.9...v3.1.10) Signed-off-by: dependabot-preview[bot] --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 93aa2bc701..3c8df17ee5 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -21,7 +21,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 5445adb3fb..f5c133263b 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -78,7 +78,7 @@ $(NoWarn);NU1605 - + From 6cd838fd4b8e510739891e64019c192122e08f2f Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 1 Jan 2021 17:55:04 +0000 Subject: [PATCH 672/818] Bump Microsoft.CodeAnalysis.BannedApiAnalyzers from 3.3.1 to 3.3.2 Bumps [Microsoft.CodeAnalysis.BannedApiAnalyzers](https://github.com/dotnet/roslyn-analyzers) from 3.3.1 to 3.3.2. - [Release notes](https://github.com/dotnet/roslyn-analyzers/releases) - [Changelog](https://github.com/dotnet/roslyn-analyzers/blob/master/PostReleaseActivities.md) - [Commits](https://github.com/dotnet/roslyn-analyzers/compare/v3.3.1...v3.3.2) Signed-off-by: dependabot-preview[bot] --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 551cb75077..9ec442aafa 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -16,7 +16,7 @@ - + From fa73d0172e01492f6f22c8c457751c0ea37fb659 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 2 Jan 2021 00:11:21 +0100 Subject: [PATCH 673/818] Keep SignalR at last working version on iOS --- osu.iOS.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.iOS.props b/osu.iOS.props index f5c133263b..5445adb3fb 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -78,7 +78,7 @@ $(NoWarn);NU1605 - + From 72a6ca77559c409bc0a5671663510d1fbcbaffb6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 2 Jan 2021 16:47:00 +0900 Subject: [PATCH 674/818] Allow signalr to retry connecting when connection is closed without an exception --- osu.Game/Online/Multiplayer/MultiplayerClient.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 24ea6abc4a..7cd1ef78f7 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -88,11 +88,12 @@ namespace osu.Game.Online.Multiplayer { isConnected.Value = false; - if (ex != null) - { - Logger.Log($"Multiplayer client lost connection: {ex}", LoggingTarget.Network); + Logger.Log(ex != null + ? $"Multiplayer client lost connection: {ex}" + : "Multiplayer client disconnected", LoggingTarget.Network); + + if (connection != null) await tryUntilConnected(); - } }; await tryUntilConnected(); From 66bd847b4507cb575549c01496dbabf819c6164b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 2 Jan 2021 11:53:06 +0100 Subject: [PATCH 675/818] Bump InspectCode tool to 2020.3.2 --- .config/dotnet-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index dd53eefd23..58c24181d3 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "jetbrains.resharper.globaltools": { - "version": "2020.2.4", + "version": "2020.3.2", "commands": [ "jb" ] From 18ac97ca563e9a02d56c469e5c084ccfa4b1e978 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 2 Jan 2021 12:21:53 +0100 Subject: [PATCH 676/818] Disable "merge sequential patterns" suggestions As they were considered to be detrimental to code readability. --- osu.sln.DotSettings | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index 22ea73858e..aa8f8739c1 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -106,6 +106,7 @@ HINT WARNING WARNING + DO_NOT_SHOW WARNING WARNING WARNING From 924af58f5b1ba2b5fcdaa00a79db21ce6c19bceb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 2 Jan 2021 12:25:16 +0100 Subject: [PATCH 677/818] Replace using static with explicit nested reference This seems to be an inspectcode bug, as the code is correct and compiles, but let's just work around it for now. --- .../Visual/Components/TestScenePreviewTrackManager.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Components/TestScenePreviewTrackManager.cs b/osu.Game.Tests/Visual/Components/TestScenePreviewTrackManager.cs index a3db20ce83..9a999a4931 100644 --- a/osu.Game.Tests/Visual/Components/TestScenePreviewTrackManager.cs +++ b/osu.Game.Tests/Visual/Components/TestScenePreviewTrackManager.cs @@ -8,7 +8,6 @@ using osu.Framework.Audio.Track; using osu.Framework.Graphics.Containers; using osu.Game.Audio; using osu.Game.Beatmaps; -using static osu.Game.Tests.Visual.Components.TestScenePreviewTrackManager.TestPreviewTrackManager; namespace osu.Game.Tests.Visual.Components { @@ -100,7 +99,7 @@ namespace osu.Game.Tests.Visual.Components [Test] public void TestNonPresentTrack() { - TestPreviewTrack track = null; + TestPreviewTrackManager.TestPreviewTrack track = null; AddStep("get non-present track", () => { @@ -182,9 +181,9 @@ namespace osu.Game.Tests.Visual.Components AddAssert("track stopped", () => !track.IsRunning); } - private TestPreviewTrack getTrack() => (TestPreviewTrack)trackManager.Get(null); + private TestPreviewTrackManager.TestPreviewTrack getTrack() => (TestPreviewTrackManager.TestPreviewTrack)trackManager.Get(null); - private TestPreviewTrack getOwnedTrack() + private TestPreviewTrackManager.TestPreviewTrack getOwnedTrack() { var track = getTrack(); From 6c3ccaddbfec4a148ad6080e2ab6e958d54cd2e0 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 2 Jan 2021 12:51:41 +0000 Subject: [PATCH 678/818] Bump Microsoft.CodeAnalysis.FxCopAnalyzers from 3.3.1 to 3.3.2 Bumps [Microsoft.CodeAnalysis.FxCopAnalyzers](https://github.com/dotnet/roslyn-analyzers) from 3.3.1 to 3.3.2. - [Release notes](https://github.com/dotnet/roslyn-analyzers/releases) - [Changelog](https://github.com/dotnet/roslyn-analyzers/blob/master/PostReleaseActivities.md) - [Commits](https://github.com/dotnet/roslyn-analyzers/compare/v3.3.1...v3.3.2) Signed-off-by: dependabot-preview[bot] --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 9ec442aafa..a74b204436 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -18,7 +18,7 @@ - + $(MSBuildThisFileDirectory)CodeAnalysis\osu.ruleset From e2de5bb8f940e71fcf9cfefa8fe504966c5e00bf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 2 Jan 2021 22:05:41 +0900 Subject: [PATCH 679/818] Fix the beatmap carousel not returning to centre correctly after resizing window --- osu.Game/Screens/Select/BeatmapCarousel.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index d76f0abb9e..c83c89bb7f 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -13,6 +13,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Pooling; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Framework.Layout; using osu.Framework.Threading; using osu.Framework.Utils; using osu.Game.Beatmaps; @@ -567,6 +568,15 @@ namespace osu.Game.Screens.Select #endregion + protected override bool OnInvalidate(Invalidation invalidation, InvalidationSource source) + { + // handles the vertical size of the carousel changing (ie. on window resize when aspect ratio has changed). + if ((invalidation & Invalidation.Layout) > 0) + itemsCache.Invalidate(); + + return base.OnInvalidate(invalidation, source); + } + protected override void Update() { base.Update(); From 26b2a065bc365fc75e04315dfe9cbc71a3b0bb57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 2 Jan 2021 14:25:15 +0100 Subject: [PATCH 680/818] Add deprecation warning ignore with explanation --- Directory.Build.props | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index a74b204436..b55eff9df9 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -28,9 +28,17 @@ $(NoWarn);CS1591 - - $(NoWarn);NU1701 + + $(NoWarn);NU1701;CA9998 false @@ -43,4 +51,4 @@ Copyright (c) 2020 ppy Pty Ltd osu game - \ No newline at end of file + From 8bb84570df904c9c55cc781e18cc13bfb13e834e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 3 Jan 2021 04:21:50 +0300 Subject: [PATCH 681/818] Introduce beatmap availability structure --- osu.Game/Online/Rooms/BeatmapAvailability.cs | 56 ++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 osu.Game/Online/Rooms/BeatmapAvailability.cs diff --git a/osu.Game/Online/Rooms/BeatmapAvailability.cs b/osu.Game/Online/Rooms/BeatmapAvailability.cs new file mode 100644 index 0000000000..ca53cb2295 --- /dev/null +++ b/osu.Game/Online/Rooms/BeatmapAvailability.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; + +namespace osu.Game.Online.Rooms +{ + /// + /// The local availability information about a certain beatmap for the client. + /// + public readonly struct BeatmapAvailability : IEquatable + { + /// + /// The beatmap's availability state. + /// + public readonly DownloadState State; + + /// + /// The beatmap's downloading progress, null when not in state. + /// + public readonly double? DownloadProgress; + + /// + /// Constructs a new non- beatmap availability state. + /// + /// The beatmap availability state. + /// Throws if was specified in this constructor, as it has its own constructor (see . + public BeatmapAvailability(DownloadState state) + { + if (state == DownloadState.Downloading) + throw new ArgumentException($"{nameof(DownloadState.Downloading)} state has its own constructor, use it instead."); + + State = state; + DownloadProgress = null; + } + + /// + /// Constructs a new -specific beatmap availability state. + /// + /// The beatmap availability state (always ). + /// The beatmap's downloading current progress. + /// Throws if non- was specified in this constructor, as they have their own constructor (see . + public BeatmapAvailability(DownloadState state, double downloadProgress) + { + if (state != DownloadState.Downloading) + throw new ArgumentException($"This is a constructor specific for {DownloadState.Downloading} state, use the regular one instead."); + + State = DownloadState.Downloading; + DownloadProgress = downloadProgress; + } + + public bool Equals(BeatmapAvailability other) => State == other.State && DownloadProgress == other.DownloadProgress; + + public override string ToString() => $"{string.Join(", ", State, $"{DownloadProgress:0.00%}")}"; + } +} From 09e5e2629ab2ffa0ad052683bcb91000e18a87fe Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 3 Jan 2021 04:25:06 +0300 Subject: [PATCH 682/818] Add user beatmap availability property --- osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs b/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs index 99624dc3e7..92c92f438d 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs @@ -5,6 +5,7 @@ using System; using Newtonsoft.Json; +using osu.Game.Online.Rooms; using osu.Game.Users; namespace osu.Game.Online.Multiplayer @@ -16,6 +17,11 @@ namespace osu.Game.Online.Multiplayer public MultiplayerUserState State { get; set; } = MultiplayerUserState.Idle; + /// + /// The availability state of the beatmap, set to by default. + /// + public BeatmapAvailability BeatmapAvailability { get; set; } = new BeatmapAvailability(DownloadState.LocallyAvailable); + public User? User { get; set; } [JsonConstructor] From dfa8be9173839c3f5dd7aaabb14f0a60692580f1 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 3 Jan 2021 04:32:50 +0300 Subject: [PATCH 683/818] Add beatmap availability change state & event methods --- .../Online/Multiplayer/IMultiplayerClient.cs | 8 ++++++++ .../Multiplayer/IMultiplayerRoomServer.cs | 8 ++++++++ .../Online/Multiplayer/MultiplayerClient.cs | 9 +++++++++ .../Multiplayer/StatefulMultiplayerClient.cs | 20 +++++++++++++++++++ .../Multiplayer/TestMultiplayerClient.cs | 15 ++++++++++++++ 5 files changed, 60 insertions(+) diff --git a/osu.Game/Online/Multiplayer/IMultiplayerClient.cs b/osu.Game/Online/Multiplayer/IMultiplayerClient.cs index b97fcc9ae7..5410fbc030 100644 --- a/osu.Game/Online/Multiplayer/IMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerClient.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Threading.Tasks; +using osu.Game.Online.Rooms; namespace osu.Game.Online.Multiplayer { @@ -47,6 +48,13 @@ namespace osu.Game.Online.Multiplayer /// The new state of the user. Task UserStateChanged(int userId, MultiplayerUserState state); + /// + /// Signals that a user in this room has their beatmap availability state changed. + /// + /// The ID of the user whose beatmap availability state has changed. + /// The new beatmap availability state of the user. + Task UserBeatmapAvailabilityChanged(int userId, BeatmapAvailability beatmapAvailability); + /// /// Signals that a match is to be started. This will *only* be sent to clients which are to begin loading at this point. /// diff --git a/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs b/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs index 481e3fb1de..7fda526faf 100644 --- a/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Threading.Tasks; +using osu.Game.Online.Rooms; namespace osu.Game.Online.Multiplayer { @@ -40,6 +41,13 @@ namespace osu.Game.Online.Multiplayer /// If the user is not in a room. Task ChangeState(MultiplayerUserState newState); + /// + /// Change the user's local availability state of the beatmap set in joined room. + /// This will also force user state back to . + /// + /// The proposed new beatmap availability state. + Task ChangeBeatmapAvailability(BeatmapAvailability newBeatmapAvailability); + /// /// As the host of a room, start the match. /// diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 7cd1ef78f7..50dc8f661c 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -14,6 +14,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Logging; using osu.Game.Online.API; +using osu.Game.Online.Rooms; namespace osu.Game.Online.Multiplayer { @@ -173,6 +174,14 @@ namespace osu.Game.Online.Multiplayer return connection.InvokeAsync(nameof(IMultiplayerServer.ChangeState), newState); } + public override Task ChangeBeatmapAvailability(BeatmapAvailability newBeatmapAvailability) + { + if (!isConnected.Value) + return Task.CompletedTask; + + return connection.InvokeAsync(nameof(IMultiplayerServer.ChangeBeatmapAvailability), newBeatmapAvailability); + } + public override Task StartMatch() { if (!isConnected.Value) diff --git a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs index dc80488d39..c1818de87a 100644 --- a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs @@ -184,6 +184,8 @@ namespace osu.Game.Online.Multiplayer public abstract Task ChangeState(MultiplayerUserState newState); + public abstract Task ChangeBeatmapAvailability(BeatmapAvailability newBeatmapAvailability); + public abstract Task StartMatch(); Task IMultiplayerClient.RoomStateChanged(MultiplayerRoomState state) @@ -311,6 +313,24 @@ namespace osu.Game.Online.Multiplayer return Task.CompletedTask; } + Task IMultiplayerClient.UserBeatmapAvailabilityChanged(int userId, BeatmapAvailability beatmapAvailability) + { + if (Room == null) + return Task.CompletedTask; + + Scheduler.Add(() => + { + if (Room == null) + return; + + Room.Users.Single(u => u.UserID == userId).BeatmapAvailability = beatmapAvailability; + + RoomUpdated?.Invoke(); + }, false); + + return Task.CompletedTask; + } + Task IMultiplayerClient.LoadRequested() { if (Room == null) diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 2ce5211757..c155447f8c 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -10,6 +10,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Online.API; using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; using osu.Game.Users; namespace osu.Game.Tests.Visual.Multiplayer @@ -77,6 +78,14 @@ namespace osu.Game.Tests.Visual.Multiplayer }); } + public void ChangeUserBeatmapAvailability(int userId, BeatmapAvailability newBeatmapAvailability) + { + Debug.Assert(Room != null); + + ((IMultiplayerClient)this).UserBeatmapAvailabilityChanged(userId, newBeatmapAvailability); + ChangeUserState(userId, MultiplayerUserState.Idle); + } + protected override Task JoinRoom(long roomId) { var user = new MultiplayerRoomUser(api.LocalUser.Value.Id) { User = api.LocalUser.Value }; @@ -108,6 +117,12 @@ namespace osu.Game.Tests.Visual.Multiplayer return Task.CompletedTask; } + public override Task ChangeBeatmapAvailability(BeatmapAvailability newBeatmapAvailability) + { + ChangeUserBeatmapAvailability(api.LocalUser.Value.Id, newBeatmapAvailability); + return Task.CompletedTask; + } + public override Task StartMatch() { Debug.Assert(Room != null); From caa5109e3a0e1e526c789024d2ecbc9d57767e76 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 3 Jan 2021 12:18:35 +0900 Subject: [PATCH 684/818] Add precautionary null checks to update methods in SongSelect --- osu.Game/Screens/Select/SongSelect.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index a5252fdc96..8ad1ace36a 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -428,7 +428,7 @@ namespace osu.Game.Screens.Select private void updateSelectedBeatmap(BeatmapInfo beatmap) { - if (beatmap?.Equals(beatmapNoDebounce) == true) + if (beatmap == null || beatmap.Equals(beatmapNoDebounce)) return; beatmapNoDebounce = beatmap; @@ -438,7 +438,7 @@ namespace osu.Game.Screens.Select private void updateSelectedRuleset(RulesetInfo ruleset) { - if (ruleset?.Equals(rulesetNoDebounce) == true) + if (ruleset == null || ruleset.Equals(rulesetNoDebounce)) return; rulesetNoDebounce = ruleset; From 2e5c67be3f415bc0841c4ae2cd122237ff49c380 Mon Sep 17 00:00:00 2001 From: LavaDesu Date: Wed, 30 Dec 2020 12:29:51 +0700 Subject: [PATCH 685/818] Add ability to toggle discord rich presence There are 3 modes: enabled, limited, and disabled. The limited mode hides identifiable information such as username, rank, and (if participating in one) multiplayer lobby name. --- osu.Desktop/DiscordRichPresence.cs | 15 ++++++++--- .../Configuration/DiscordRichPresenceMode.cs | 17 ++++++++++++ osu.Game/Configuration/OsuConfigManager.cs | 5 +++- .../Sections/Online/IntegrationSettings.cs | 27 +++++++++++++++++++ .../Settings/Sections/OnlineSection.cs | 3 ++- 5 files changed, 61 insertions(+), 6 deletions(-) create mode 100644 osu.Game/Configuration/DiscordRichPresenceMode.cs create mode 100644 osu.Game/Overlays/Settings/Sections/Online/IntegrationSettings.cs diff --git a/osu.Desktop/DiscordRichPresence.cs b/osu.Desktop/DiscordRichPresence.cs index f1878d967d..6b331d4952 100644 --- a/osu.Desktop/DiscordRichPresence.cs +++ b/osu.Desktop/DiscordRichPresence.cs @@ -9,6 +9,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Logging; +using osu.Game.Configuration; using osu.Game.Online.API; using osu.Game.Rulesets; using osu.Game.Users; @@ -30,6 +31,7 @@ namespace osu.Desktop private readonly IBindable status = new Bindable(); private readonly IBindable activity = new Bindable(); + private readonly Bindable mode = new Bindable(); private readonly RichPresence presence = new RichPresence { @@ -37,7 +39,7 @@ namespace osu.Desktop }; [BackgroundDependencyLoader] - private void load(IAPIProvider provider) + private void load(IAPIProvider provider, OsuConfigManager config) { client = new DiscordRpcClient(client_id) { @@ -51,6 +53,7 @@ namespace osu.Desktop client.OnError += (_, e) => Logger.Log($"An error occurred with Discord RPC Client: {e.Code} {e.Message}", LoggingTarget.Network); + config.BindWith(OsuSetting.DiscordRichPresence, mode); (user = provider.LocalUser.GetBoundCopy()).BindValueChanged(u => { status.UnbindBindings(); @@ -63,6 +66,7 @@ namespace osu.Desktop ruleset.BindValueChanged(_ => updateStatus()); status.BindValueChanged(_ => updateStatus()); activity.BindValueChanged(_ => updateStatus()); + mode.BindValueChanged(_ => updateStatus()); client.Initialize(); } @@ -78,7 +82,7 @@ namespace osu.Desktop if (!client.IsInitialized) return; - if (status.Value is UserStatusOffline) + if (status.Value is UserStatusOffline || mode.Value == DiscordRichPresenceMode.Disabled) { client.ClearPresence(); return; @@ -96,7 +100,10 @@ namespace osu.Desktop } // update user information - presence.Assets.LargeImageText = $"{user.Value.Username}" + (user.Value.Statistics?.Ranks.Global > 0 ? $" (rank #{user.Value.Statistics.Ranks.Global:N0})" : string.Empty); + if (mode.Value == DiscordRichPresenceMode.Limited) + presence.Assets.LargeImageText = string.Empty; + else + presence.Assets.LargeImageText = $"{user.Value.Username}" + (user.Value.Statistics?.Ranks.Global > 0 ? $" (rank #{user.Value.Statistics.Ranks.Global:N0})" : string.Empty); // update ruleset presence.Assets.SmallImageKey = ruleset.Value.ID <= 3 ? $"mode_{ruleset.Value.ID}" : "mode_custom"; @@ -137,7 +144,7 @@ namespace osu.Desktop return edit.Beatmap.ToString(); case UserActivity.InLobby lobby: - return lobby.Room.Name.Value; + return mode.Value == DiscordRichPresenceMode.Limited ? string.Empty : lobby.Room.Name.Value; } return string.Empty; diff --git a/osu.Game/Configuration/DiscordRichPresenceMode.cs b/osu.Game/Configuration/DiscordRichPresenceMode.cs new file mode 100644 index 0000000000..bd39faff4d --- /dev/null +++ b/osu.Game/Configuration/DiscordRichPresenceMode.cs @@ -0,0 +1,17 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.ComponentModel; + +namespace osu.Game.Configuration +{ + public enum DiscordRichPresenceMode + { + Disabled, + + [Description("Hide identifiable information")] + Limited, + + Enabled + } +} diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index a07e446d2e..c733fe2fb4 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -138,6 +138,8 @@ namespace osu.Game.Configuration Set(OsuSetting.MenuBackgroundSource, BackgroundSource.Skin); Set(OsuSetting.SeasonalBackgroundMode, SeasonalBackgroundMode.Sometimes); + Set(OsuSetting.DiscordRichPresence, DiscordRichPresenceMode.Enabled); + Set(OsuSetting.EditorWaveformOpacity, 1f); } @@ -266,6 +268,7 @@ namespace osu.Game.Configuration GameplayDisableWinKey, SeasonalBackgroundMode, EditorWaveformOpacity, + DiscordRichPresence, AutomaticallyDownloadWhenSpectating, } } diff --git a/osu.Game/Overlays/Settings/Sections/Online/IntegrationSettings.cs b/osu.Game/Overlays/Settings/Sections/Online/IntegrationSettings.cs new file mode 100644 index 0000000000..d2867962c0 --- /dev/null +++ b/osu.Game/Overlays/Settings/Sections/Online/IntegrationSettings.cs @@ -0,0 +1,27 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Configuration; + +namespace osu.Game.Overlays.Settings.Sections.Online +{ + public class IntegrationSettings : SettingsSubsection + { + protected override string Header => "Integrations"; + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + Children = new Drawable[] + { + new SettingsEnumDropdown + { + LabelText = "Discord Rich Presence", + Current = config.GetBindable(OsuSetting.DiscordRichPresence) + } + }; + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/OnlineSection.cs b/osu.Game/Overlays/Settings/Sections/OnlineSection.cs index 150cddb388..7aa4eff29a 100644 --- a/osu.Game/Overlays/Settings/Sections/OnlineSection.cs +++ b/osu.Game/Overlays/Settings/Sections/OnlineSection.cs @@ -20,7 +20,8 @@ namespace osu.Game.Overlays.Settings.Sections { Children = new Drawable[] { - new WebSettings() + new WebSettings(), + new IntegrationSettings() }; } } From a6d49929978a2617ef2f180d3e3e868513e8737e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 3 Jan 2021 12:53:25 +0900 Subject: [PATCH 686/818] Ensure SelectionChanged events are only sent once when selection is null --- osu.Game/Screens/Select/BeatmapCarousel.cs | 31 ++++++++++++++++------ 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index c83c89bb7f..37213c6003 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -125,6 +125,8 @@ namespace osu.Game.Screens.Select { BeatmapSetsChanged?.Invoke(); BeatmapSetsLoaded = true; + + itemsCache.Invalidate(); }); } @@ -731,6 +733,12 @@ namespace osu.Game.Screens.Select private const float panel_padding = 5; + /// + /// After loading, we want to invoke a selection changed event at least once. + /// This handles the case where this event is potentially sending a null selection. + /// + private bool sentInitialSelectionEvent; + /// /// Computes the target Y positions for every item in the carousel. /// @@ -787,13 +795,21 @@ namespace osu.Game.Screens.Select Scroll.ScrollContent.Height = currentY; - if (BeatmapSetsLoaded && (selectedBeatmapSet == null || selectedBeatmap == null || selectedBeatmapSet.State.Value != CarouselItemState.Selected)) - { - selectedBeatmapSet = null; - SelectionChanged?.Invoke(null); - } - itemsCache.Validate(); + + // update and let external consumers know about selection loss. + if (BeatmapSetsLoaded) + { + bool selectionLost = selectedBeatmapSet != null && selectedBeatmapSet.State.Value != CarouselItemState.Selected; + + if (selectionLost || !sentInitialSelectionEvent) + { + selectedBeatmapSet = null; + SelectionChanged?.Invoke(null); + + sentInitialSelectionEvent = true; + } + } } private bool firstScroll = true; @@ -816,14 +832,13 @@ namespace osu.Game.Screens.Select break; case PendingScrollOperation.Immediate: + // in order to simplify animation logic, rather than using the animated version of ScrollTo, // we take the difference in scroll height and apply to all visible panels. // this avoids edge cases like when the visible panels is reduced suddenly, causing ScrollContainer // to enter clamp-special-case mode where it animates completely differently to normal. float scrollChange = scrollTarget.Value - Scroll.Current; - Scroll.ScrollTo(scrollTarget.Value, false); - foreach (var i in Scroll.Children) i.Y += scrollChange; break; From 1a443381247492b9233ae9c01ebf1a44510359ef Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 3 Jan 2021 15:38:28 +0900 Subject: [PATCH 687/818] Use SingleOrDefault for added safety when looking up mod acronyms --- osu.Game/Online/API/APIMod.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/API/APIMod.cs b/osu.Game/Online/API/APIMod.cs index 780e5daa16..6c988e2e00 100644 --- a/osu.Game/Online/API/APIMod.cs +++ b/osu.Game/Online/API/APIMod.cs @@ -36,7 +36,7 @@ namespace osu.Game.Online.API public Mod ToMod(Ruleset ruleset) { - Mod resultMod = ruleset.GetAllMods().FirstOrDefault(m => m.Acronym == Acronym); + Mod resultMod = ruleset.GetAllMods().SingleOrDefault(m => m.Acronym == Acronym); if (resultMod == null) throw new InvalidOperationException($"There is no mod in the ruleset ({ruleset.ShortName}) matching the acronym {Acronym}."); From 23e216fa0b374e23751c97124dc069877004a60d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 3 Jan 2021 15:47:15 +0900 Subject: [PATCH 688/818] Simplify some default value checks (we are sure the return is an IBindable) --- osu.Game/Online/API/APIMod.cs | 7 ++++++- osu.Game/Rulesets/Mods/Mod.cs | 8 +++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/osu.Game/Online/API/APIMod.cs b/osu.Game/Online/API/APIMod.cs index 6c988e2e00..a7e5f0d6f9 100644 --- a/osu.Game/Online/API/APIMod.cs +++ b/osu.Game/Online/API/APIMod.cs @@ -31,7 +31,12 @@ namespace osu.Game.Online.API Acronym = mod.Acronym; foreach (var (_, property) in mod.GetSettingsSourceProperties()) - Settings.Add(property.Name.Underscore(), property.GetValue(mod)); + { + var bindable = (IBindable)property.GetValue(mod); + + if (!bindable.IsDefault) + Settings.Add(property.Name.Underscore(), property.GetValue(mod)); + } } public Mod ToMod(Ruleset ruleset) diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index b8dc7a2661..92b548a9cc 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -84,12 +84,10 @@ namespace osu.Game.Rulesets.Mods foreach ((SettingSourceAttribute attr, PropertyInfo property) in this.GetOrderedSettingsSourceProperties()) { - object bindableObj = property.GetValue(this); + var bindable = (IBindable)property.GetValue(this); - if ((bindableObj as IHasDefaultValue)?.IsDefault == true) - continue; - - tooltipTexts.Add($"{attr.Label} {bindableObj}"); + if (!bindable.IsDefault) + tooltipTexts.Add($"{attr.Label} {bindable}"); } return string.Join(", ", tooltipTexts.Where(s => !string.IsNullOrEmpty(s))); From 29dbb1cc0d94d26b3fb077e924bd2eb6d6b16dfd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 3 Jan 2021 15:48:28 +0900 Subject: [PATCH 689/818] Add internal pathway for ensuring correct application of bindable mods --- osu.Game/Online/API/APIMod.cs | 2 +- osu.Game/Rulesets/Mods/Mod.cs | 21 ++++++++++++------- osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs | 6 ++++++ 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/osu.Game/Online/API/APIMod.cs b/osu.Game/Online/API/APIMod.cs index a7e5f0d6f9..6e3bdac61a 100644 --- a/osu.Game/Online/API/APIMod.cs +++ b/osu.Game/Online/API/APIMod.cs @@ -51,7 +51,7 @@ namespace osu.Game.Online.API if (!Settings.TryGetValue(property.Name.Underscore(), out object settingValue)) continue; - ((IBindable)property.GetValue(resultMod)).Parse(settingValue); + resultMod.CopyAdjustedSetting((IBindable)property.GetValue(resultMod), settingValue); } return resultMod; diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index 92b548a9cc..487cdedd13 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Reflection; using Newtonsoft.Json; @@ -134,19 +133,25 @@ namespace osu.Game.Rulesets.Mods // Copy bindable values across foreach (var (_, prop) in this.GetSettingsSourceProperties()) { - var origBindable = prop.GetValue(this); - var copyBindable = prop.GetValue(copy); + var origBindable = (IBindable)prop.GetValue(this); + var copyBindable = (IBindable)prop.GetValue(copy); - // The bindables themselves are readonly, so the value must be transferred through the Bindable.Value property. - var valueProperty = origBindable.GetType().GetProperty(nameof(Bindable.Value), BindingFlags.Public | BindingFlags.Instance); - Debug.Assert(valueProperty != null); - - valueProperty.SetValue(copyBindable, valueProperty.GetValue(origBindable)); + // we only care about changes that have been made away from defaults. + if (!origBindable.IsDefault) + copy.CopyAdjustedSetting(copyBindable, origBindable); } return copy; } + /// + /// When creating copies or clones of a Mod, this method will be called to copy explicitly adjusted user settings. + /// The base implementation will transfer the value via and should be called unless replaced with custom logic. + /// + /// The target bindable to apply the adjustment. + /// The adjustment to apply. + internal virtual void CopyAdjustedSetting(IBindable bindable, object value) => bindable.Parse(value); + public bool Equals(IMod other) => GetType() == other?.GetType(); } } diff --git a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs index 165644edbe..58af96a8df 100644 --- a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs +++ b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs @@ -114,6 +114,12 @@ namespace osu.Game.Rulesets.Mods bindable.ValueChanged += _ => userChangedSettings[bindable] = !bindable.IsDefault; } + internal override void CopyAdjustedSetting(IBindable bindable, object value) + { + userChangedSettings[bindable] = true; + base.CopyAdjustedSetting(bindable, value); + } + /// /// Apply all custom settings to the provided beatmap. /// From 99fa0e25dcb542e98e6bdf3c71de45a891634806 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 3 Jan 2021 16:46:24 +0900 Subject: [PATCH 690/818] Switch back to FirstOrDefault to allow for weird testing logic to pass --- osu.Game/Online/API/APIMod.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/API/APIMod.cs b/osu.Game/Online/API/APIMod.cs index 6e3bdac61a..3ceafc5160 100644 --- a/osu.Game/Online/API/APIMod.cs +++ b/osu.Game/Online/API/APIMod.cs @@ -41,7 +41,7 @@ namespace osu.Game.Online.API public Mod ToMod(Ruleset ruleset) { - Mod resultMod = ruleset.GetAllMods().SingleOrDefault(m => m.Acronym == Acronym); + Mod resultMod = ruleset.GetAllMods().FirstOrDefault(m => m.Acronym == Acronym); if (resultMod == null) throw new InvalidOperationException($"There is no mod in the ruleset ({ruleset.ShortName}) matching the acronym {Acronym}."); From 6ad1b7767e6ded5cd992838a7395c7205378b90c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 3 Jan 2021 17:04:16 +0900 Subject: [PATCH 691/818] Update osu.Game/Online/API/APIMod.cs Co-authored-by: Salman Ahmed --- osu.Game/Online/API/APIMod.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/API/APIMod.cs b/osu.Game/Online/API/APIMod.cs index 3ceafc5160..c8b76b9685 100644 --- a/osu.Game/Online/API/APIMod.cs +++ b/osu.Game/Online/API/APIMod.cs @@ -35,7 +35,7 @@ namespace osu.Game.Online.API var bindable = (IBindable)property.GetValue(mod); if (!bindable.IsDefault) - Settings.Add(property.Name.Underscore(), property.GetValue(mod)); + Settings.Add(property.Name.Underscore(), bindable); } } From 7c9f345cd27ff36ea5e638a53fb1c12f74712361 Mon Sep 17 00:00:00 2001 From: LavaDesu Date: Sun, 3 Jan 2021 16:46:25 +0700 Subject: [PATCH 692/818] Use better naming for DiscordRichPresenceMode --- osu.Desktop/DiscordRichPresence.cs | 2 +- osu.Game/Configuration/DiscordRichPresenceMode.cs | 4 ++-- osu.Game/Configuration/OsuConfigManager.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Desktop/DiscordRichPresence.cs b/osu.Desktop/DiscordRichPresence.cs index 6b331d4952..172db324cb 100644 --- a/osu.Desktop/DiscordRichPresence.cs +++ b/osu.Desktop/DiscordRichPresence.cs @@ -82,7 +82,7 @@ namespace osu.Desktop if (!client.IsInitialized) return; - if (status.Value is UserStatusOffline || mode.Value == DiscordRichPresenceMode.Disabled) + if (status.Value is UserStatusOffline || mode.Value == DiscordRichPresenceMode.Off) { client.ClearPresence(); return; diff --git a/osu.Game/Configuration/DiscordRichPresenceMode.cs b/osu.Game/Configuration/DiscordRichPresenceMode.cs index bd39faff4d..2e58e3554b 100644 --- a/osu.Game/Configuration/DiscordRichPresenceMode.cs +++ b/osu.Game/Configuration/DiscordRichPresenceMode.cs @@ -7,11 +7,11 @@ namespace osu.Game.Configuration { public enum DiscordRichPresenceMode { - Disabled, + Off, [Description("Hide identifiable information")] Limited, - Enabled + Full } } diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index c733fe2fb4..eb34a0885d 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -138,7 +138,7 @@ namespace osu.Game.Configuration Set(OsuSetting.MenuBackgroundSource, BackgroundSource.Skin); Set(OsuSetting.SeasonalBackgroundMode, SeasonalBackgroundMode.Sometimes); - Set(OsuSetting.DiscordRichPresence, DiscordRichPresenceMode.Enabled); + Set(OsuSetting.DiscordRichPresence, DiscordRichPresenceMode.Full); Set(OsuSetting.EditorWaveformOpacity, 1f); } From 2501707d7d86c9927fda5f3ece0d75c14e769bb0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 3 Jan 2021 20:44:57 +0900 Subject: [PATCH 693/818] Copy values using Bind to also copy defaults --- osu.Game/Rulesets/Mods/Mod.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index 487cdedd13..d2747d98c7 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -150,7 +150,17 @@ namespace osu.Game.Rulesets.Mods /// /// The target bindable to apply the adjustment. /// The adjustment to apply. - internal virtual void CopyAdjustedSetting(IBindable bindable, object value) => bindable.Parse(value); + internal virtual void CopyAdjustedSetting(IBindable bindable, object value) + { + if (value is IBindable incoming) + { + //copy including transfer of default values. + bindable.BindTo(incoming); + bindable.UnbindFrom(incoming); + } + else + bindable.Parse(value); + } public bool Equals(IMod other) => GetType() == other?.GetType(); } From a3e29b9154c996efbe051da95e9c8bbaac8c2096 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 3 Jan 2021 13:25:44 +0100 Subject: [PATCH 694/818] Rename parameters for readability --- osu.Game/Rulesets/Mods/Mod.cs | 14 +++++++------- osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index d2747d98c7..b7432ec966 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -148,18 +148,18 @@ namespace osu.Game.Rulesets.Mods /// When creating copies or clones of a Mod, this method will be called to copy explicitly adjusted user settings. /// The base implementation will transfer the value via and should be called unless replaced with custom logic. /// - /// The target bindable to apply the adjustment. - /// The adjustment to apply. - internal virtual void CopyAdjustedSetting(IBindable bindable, object value) + /// The target bindable to apply the adjustment. + /// The adjustment to apply. + internal virtual void CopyAdjustedSetting(IBindable target, object source) { - if (value is IBindable incoming) + if (source is IBindable sourceBindable) { //copy including transfer of default values. - bindable.BindTo(incoming); - bindable.UnbindFrom(incoming); + target.BindTo(sourceBindable); + target.UnbindFrom(sourceBindable); } else - bindable.Parse(value); + target.Parse(source); } public bool Equals(IMod other) => GetType() == other?.GetType(); diff --git a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs index 58af96a8df..72a4bb297f 100644 --- a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs +++ b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs @@ -114,10 +114,10 @@ namespace osu.Game.Rulesets.Mods bindable.ValueChanged += _ => userChangedSettings[bindable] = !bindable.IsDefault; } - internal override void CopyAdjustedSetting(IBindable bindable, object value) + internal override void CopyAdjustedSetting(IBindable target, object source) { - userChangedSettings[bindable] = true; - base.CopyAdjustedSetting(bindable, value); + userChangedSettings[target] = true; + base.CopyAdjustedSetting(target, source); } /// From 9e4a925ab1f071fced203cc081f2c6f0b7c2ca96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 3 Jan 2021 13:30:21 +0100 Subject: [PATCH 695/818] Clarify & cleanup comments some --- osu.Game/Rulesets/Mods/Mod.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index b7432ec966..24d184e531 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -145,16 +145,19 @@ namespace osu.Game.Rulesets.Mods } /// - /// When creating copies or clones of a Mod, this method will be called to copy explicitly adjusted user settings. - /// The base implementation will transfer the value via and should be called unless replaced with custom logic. + /// When creating copies or clones of a Mod, this method will be called + /// to copy explicitly adjusted user settings from . + /// The base implementation will transfer the value via + /// or by binding and unbinding (if is an ) + /// and should be called unless replaced with custom logic. /// - /// The target bindable to apply the adjustment. + /// The target bindable to apply the adjustment to. /// The adjustment to apply. internal virtual void CopyAdjustedSetting(IBindable target, object source) { if (source is IBindable sourceBindable) { - //copy including transfer of default values. + // copy including transfer of default values. target.BindTo(sourceBindable); target.UnbindFrom(sourceBindable); } From efb71713efdde039ab6a3d0fce58476e6ef6d575 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 3 Jan 2021 22:43:02 +0900 Subject: [PATCH 696/818] Fix null condition inhibiting deselection events --- osu.Game/Screens/Select/SongSelect.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 8ad1ace36a..e3036c662b 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -428,7 +428,10 @@ namespace osu.Game.Screens.Select private void updateSelectedBeatmap(BeatmapInfo beatmap) { - if (beatmap == null || beatmap.Equals(beatmapNoDebounce)) + if (beatmap == null && beatmapNoDebounce == null) + return; + + if (beatmap?.Equals(beatmapNoDebounce) == true) return; beatmapNoDebounce = beatmap; From 53e6a349bbed8492c62b66ff8b47333da8101230 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 3 Jan 2021 22:44:30 +0900 Subject: [PATCH 697/818] Fix incorrect initial conditional Turns out this wasn't actually required. --- osu.Game/Screens/Select/BeatmapCarousel.cs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 37213c6003..36f8fbedb3 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -733,12 +733,6 @@ namespace osu.Game.Screens.Select private const float panel_padding = 5; - /// - /// After loading, we want to invoke a selection changed event at least once. - /// This handles the case where this event is potentially sending a null selection. - /// - private bool sentInitialSelectionEvent; - /// /// Computes the target Y positions for every item in the carousel. /// @@ -802,12 +796,10 @@ namespace osu.Game.Screens.Select { bool selectionLost = selectedBeatmapSet != null && selectedBeatmapSet.State.Value != CarouselItemState.Selected; - if (selectionLost || !sentInitialSelectionEvent) + if (selectionLost) { selectedBeatmapSet = null; SelectionChanged?.Invoke(null); - - sentInitialSelectionEvent = true; } } } From 152e9ecccf0f6150236d70a0d90f6d3a721e87aa Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 3 Jan 2021 18:34:11 +0300 Subject: [PATCH 698/818] Make `BeatmapAvailability` class in-line with other online data structures --- osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs | 2 +- osu.Game/Online/Rooms/BeatmapAvailability.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs b/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs index 92c92f438d..f515b574df 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs @@ -20,7 +20,7 @@ namespace osu.Game.Online.Multiplayer /// /// The availability state of the beatmap, set to by default. /// - public BeatmapAvailability BeatmapAvailability { get; set; } = new BeatmapAvailability(DownloadState.LocallyAvailable); + public BeatmapAvailability BeatmapAvailability { get; set; } = BeatmapAvailability.LocallyAvailable(); public User? User { get; set; } diff --git a/osu.Game/Online/Rooms/BeatmapAvailability.cs b/osu.Game/Online/Rooms/BeatmapAvailability.cs index ca53cb2295..04dcd2a84a 100644 --- a/osu.Game/Online/Rooms/BeatmapAvailability.cs +++ b/osu.Game/Online/Rooms/BeatmapAvailability.cs @@ -8,7 +8,7 @@ namespace osu.Game.Online.Rooms /// /// The local availability information about a certain beatmap for the client. /// - public readonly struct BeatmapAvailability : IEquatable + public class BeatmapAvailability : IEquatable { /// /// The beatmap's availability state. @@ -49,7 +49,7 @@ namespace osu.Game.Online.Rooms DownloadProgress = downloadProgress; } - public bool Equals(BeatmapAvailability other) => State == other.State && DownloadProgress == other.DownloadProgress; + public bool Equals(BeatmapAvailability other) => other != null && State == other.State && DownloadProgress == other.DownloadProgress; public override string ToString() => $"{string.Join(", ", State, $"{DownloadProgress:0.00%}")}"; } From c8423d1c4600d89bf4979715e7da540ca118633c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 3 Jan 2021 18:34:58 +0300 Subject: [PATCH 699/818] Make constructors design more pleasent to eyes --- osu.Game/Online/Rooms/BeatmapAvailability.cs | 30 ++++---------------- 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/osu.Game/Online/Rooms/BeatmapAvailability.cs b/osu.Game/Online/Rooms/BeatmapAvailability.cs index 04dcd2a84a..1fd099fcc7 100644 --- a/osu.Game/Online/Rooms/BeatmapAvailability.cs +++ b/osu.Game/Online/Rooms/BeatmapAvailability.cs @@ -20,35 +20,17 @@ namespace osu.Game.Online.Rooms /// public readonly double? DownloadProgress; - /// - /// Constructs a new non- beatmap availability state. - /// - /// The beatmap availability state. - /// Throws if was specified in this constructor, as it has its own constructor (see . - public BeatmapAvailability(DownloadState state) + private BeatmapAvailability(DownloadState state, double? downloadProgress = null) { - if (state == DownloadState.Downloading) - throw new ArgumentException($"{nameof(DownloadState.Downloading)} state has its own constructor, use it instead."); - State = state; - DownloadProgress = null; - } - - /// - /// Constructs a new -specific beatmap availability state. - /// - /// The beatmap availability state (always ). - /// The beatmap's downloading current progress. - /// Throws if non- was specified in this constructor, as they have their own constructor (see . - public BeatmapAvailability(DownloadState state, double downloadProgress) - { - if (state != DownloadState.Downloading) - throw new ArgumentException($"This is a constructor specific for {DownloadState.Downloading} state, use the regular one instead."); - - State = DownloadState.Downloading; DownloadProgress = downloadProgress; } + public static BeatmapAvailability NotDownload() => new BeatmapAvailability(DownloadState.NotDownloaded); + public static BeatmapAvailability Downloading(double progress) => new BeatmapAvailability(DownloadState.Downloading, progress); + public static BeatmapAvailability Downloaded() => new BeatmapAvailability(DownloadState.Downloaded); + public static BeatmapAvailability LocallyAvailable() => new BeatmapAvailability(DownloadState.LocallyAvailable); + public bool Equals(BeatmapAvailability other) => other != null && State == other.State && DownloadProgress == other.DownloadProgress; public override string ToString() => $"{string.Join(", ", State, $"{DownloadProgress:0.00%}")}"; From 839f5a75705f92cb77dfb75392ecf748a7f955d9 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 3 Jan 2021 18:36:37 +0300 Subject: [PATCH 700/818] Ensure clients don't blow up when given user isn't in room --- osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs index c1818de87a..799b66020c 100644 --- a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs @@ -320,10 +320,13 @@ namespace osu.Game.Online.Multiplayer Scheduler.Add(() => { - if (Room == null) + var user = Room?.Users.SingleOrDefault(u => u.UserID == userId); + + // we don't care whether the room doesn't exist or user isn't in joined room, just return in that point. + if (user == null) return; - Room.Users.Single(u => u.UserID == userId).BeatmapAvailability = beatmapAvailability; + user.BeatmapAvailability = beatmapAvailability; RoomUpdated?.Invoke(); }, false); From df04dd21de6a16da70031ee4fac020ae0f923211 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 4 Jan 2021 07:45:29 +0300 Subject: [PATCH 701/818] Add failing test case --- .../Beatmaps/IO/ImportBeatmapTest.cs | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index c32e359de6..041df3e6ff 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -852,6 +852,39 @@ namespace osu.Game.Tests.Beatmaps.IO } } + [Test] + public async Task TestItemRemovedShouldPassConsumableBeatmapSet() + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest))) + { + try + { + var osu = LoadOsuIntoHost(host); + var manager = osu.Dependencies.Get(); + + var removedQueue = new Queue(); + manager.ItemRemoved.BindValueChanged(evt => + { + if (evt.NewValue.TryGetTarget(out var target)) + removedQueue.Enqueue(target); + }); + + var imported = await LoadOszIntoOsu(osu); + deleteBeatmapSet(imported, osu); + + Assert.That(removedQueue.Count, Is.EqualTo(1)); + + var removedItem = removedQueue.Single(); + Assert.That(removedItem.Metadata, Is.EqualTo(imported.Metadata)); + Assert.That(removedItem.Beatmaps, Is.EquivalentTo(imported.Beatmaps)); + } + finally + { + host.Exit(); + } + } + } + public static async Task LoadOszIntoOsu(OsuGameBase osu, string path = null, bool virtualTrack = false) { var temp = path ?? TestResources.GetTestBeatmapForImport(virtualTrack); From 738c94d1938f99ff5fb638528cff02bf80f297df Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 4 Jan 2021 07:46:51 +0300 Subject: [PATCH 702/818] Update soft-deletion logic to use model store's consumable items instead --- osu.Game/Database/ArchiveModelManager.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 36cc4cce39..61b2f7668e 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -76,7 +76,7 @@ namespace osu.Game.Database protected readonly IDatabaseContextFactory ContextFactory; - protected readonly MutableDatabaseBackedStore ModelStore; + protected readonly MutableDatabaseBackedStoreWithFileIncludes ModelStore; // ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised) private ArchiveImportIPCChannel ipc; @@ -492,7 +492,7 @@ namespace osu.Game.Database using (ContextFactory.GetForWrite()) { // re-fetch the model on the import context. - var foundModel = queryModel().Include(s => s.Files).ThenInclude(f => f.FileInfo).FirstOrDefault(s => s.ID == item.ID); + var foundModel = ModelStore.ConsumableItems.SingleOrDefault(i => i.ID == item.ID); if (foundModel == null || foundModel.DeletePending) return false; @@ -731,8 +731,6 @@ namespace osu.Game.Database yield return f.Filename; } - private DbSet queryModel() => ContextFactory.Get().Set(); - protected virtual string HumanisedModelName => $"{typeof(TModel).Name.Replace("Info", "").ToLower()}"; #region Event handling / delaying From 1463ff288629232333880a0f18888c4f1a65e5d6 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 4 Jan 2021 08:12:31 +0300 Subject: [PATCH 703/818] Remove unnecessary using directive --- osu.Game/Database/ArchiveModelManager.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 61b2f7668e..b9f805ae31 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -9,7 +9,6 @@ using System.Threading; using System.Threading.Tasks; using Humanizer; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore; using osu.Framework; using osu.Framework.Bindables; using osu.Framework.Extensions; From ca5f2bcd4ca8395698a5267d6a8fb2a6cb214e45 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 4 Jan 2021 08:49:03 +0300 Subject: [PATCH 704/818] Revert database-side changes --- .../Beatmaps/IO/ImportBeatmapTest.cs | 33 ------------------- osu.Game/Database/ArchiveModelManager.cs | 6 ++-- 2 files changed, 4 insertions(+), 35 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 041df3e6ff..c32e359de6 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -852,39 +852,6 @@ namespace osu.Game.Tests.Beatmaps.IO } } - [Test] - public async Task TestItemRemovedShouldPassConsumableBeatmapSet() - { - using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest))) - { - try - { - var osu = LoadOsuIntoHost(host); - var manager = osu.Dependencies.Get(); - - var removedQueue = new Queue(); - manager.ItemRemoved.BindValueChanged(evt => - { - if (evt.NewValue.TryGetTarget(out var target)) - removedQueue.Enqueue(target); - }); - - var imported = await LoadOszIntoOsu(osu); - deleteBeatmapSet(imported, osu); - - Assert.That(removedQueue.Count, Is.EqualTo(1)); - - var removedItem = removedQueue.Single(); - Assert.That(removedItem.Metadata, Is.EqualTo(imported.Metadata)); - Assert.That(removedItem.Beatmaps, Is.EquivalentTo(imported.Beatmaps)); - } - finally - { - host.Exit(); - } - } - } - public static async Task LoadOszIntoOsu(OsuGameBase osu, string path = null, bool virtualTrack = false) { var temp = path ?? TestResources.GetTestBeatmapForImport(virtualTrack); diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index b9f805ae31..81d8668196 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -75,7 +75,7 @@ namespace osu.Game.Database protected readonly IDatabaseContextFactory ContextFactory; - protected readonly MutableDatabaseBackedStoreWithFileIncludes ModelStore; + protected readonly MutableDatabaseBackedStore ModelStore; // ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised) private ArchiveImportIPCChannel ipc; @@ -491,7 +491,7 @@ namespace osu.Game.Database using (ContextFactory.GetForWrite()) { // re-fetch the model on the import context. - var foundModel = ModelStore.ConsumableItems.SingleOrDefault(i => i.ID == item.ID); + var foundModel = queryModel().Include(s => s.Files).ThenInclude(f => f.FileInfo).FirstOrDefault(s => s.ID == item.ID); if (foundModel == null || foundModel.DeletePending) return false; @@ -730,6 +730,8 @@ namespace osu.Game.Database yield return f.Filename; } + private DbSet queryModel() => ContextFactory.Get().Set(); + protected virtual string HumanisedModelName => $"{typeof(TModel).Name.Replace("Info", "").ToLower()}"; #region Event handling / delaying From 445a4bd01c70428f33bc36b603a52e80e92d0aeb Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 4 Jan 2021 08:51:37 +0300 Subject: [PATCH 705/818] Re-query beatmap info on database changes --- osu.Game/Database/ArchiveModelManager.cs | 1 + .../OnlinePlay/Components/ReadyButton.cs | 34 ++++--------------- 2 files changed, 8 insertions(+), 27 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 81d8668196..36cc4cce39 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -9,6 +9,7 @@ using System.Threading; using System.Threading.Tasks; using Humanizer; using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; using osu.Framework; using osu.Framework.Bindables; using osu.Framework.Extensions; diff --git a/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs b/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs index 08f89d8ed8..144535ed4c 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs @@ -2,8 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Linq; -using System.Linq.Expressions; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Beatmaps; @@ -41,38 +39,20 @@ namespace osu.Game.Screens.OnlinePlay.Components SelectedItem.BindValueChanged(item => updateSelectedItem(item.NewValue), true); } - private void updateSelectedItem(PlaylistItem item) - { - hasBeatmap = findBeatmap(expr => beatmaps.QueryBeatmap(expr)); - } + private void updateSelectedItem(PlaylistItem _) => updateBeatmapState(); + private void beatmapUpdated(ValueChangedEvent> _) => updateBeatmapState(); + private void beatmapRemoved(ValueChangedEvent> _) => updateBeatmapState(); - private void beatmapUpdated(ValueChangedEvent> weakSet) - { - if (weakSet.NewValue.TryGetTarget(out var set)) - { - if (findBeatmap(expr => set.Beatmaps.AsQueryable().FirstOrDefault(expr))) - Schedule(() => hasBeatmap = true); - } - } - - private void beatmapRemoved(ValueChangedEvent> weakSet) - { - if (weakSet.NewValue.TryGetTarget(out var set)) - { - if (findBeatmap(expr => set.Beatmaps.AsQueryable().FirstOrDefault(expr))) - Schedule(() => hasBeatmap = false); - } - } - - private bool findBeatmap(Func>, BeatmapInfo> expression) + private void updateBeatmapState() { int? beatmapId = SelectedItem.Value?.Beatmap.Value?.OnlineBeatmapID; string checksum = SelectedItem.Value?.Beatmap.Value?.MD5Hash; if (beatmapId == null || checksum == null) - return false; + return; - return expression(b => b.OnlineBeatmapID == beatmapId && b.MD5Hash == checksum) != null; + var databasedBeatmap = beatmaps.QueryBeatmap(b => b.OnlineBeatmapID == beatmapId && b.MD5Hash == checksum); + hasBeatmap = databasedBeatmap != null && !databasedBeatmap.BeatmapSet.DeletePending; } protected override void Update() From cb7df0fe1172935581909b938cab6fe987fcc5ff Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Jan 2021 15:14:39 +0900 Subject: [PATCH 706/818] Add failing test for storyboard start time ordering --- .../Formats/LegacyStoryboardDecoderTest.cs | 20 +++++++++++++++++++ .../Resources/out-of-order-starttimes.osb | 6 ++++++ 2 files changed, 26 insertions(+) create mode 100644 osu.Game.Tests/Resources/out-of-order-starttimes.osb diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs index 9ebedb3c80..b36597a949 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs @@ -95,6 +95,26 @@ namespace osu.Game.Tests.Beatmaps.Formats } } + [Test] + public void TestOutOfOrderStartTimes() + { + var decoder = new LegacyStoryboardDecoder(); + + using (var resStream = TestResources.OpenResource("out-of-order-starttimes.osb")) + using (var stream = new LineBufferedReader(resStream)) + { + var storyboard = decoder.Decode(stream); + + StoryboardLayer background = storyboard.Layers.Single(l => l.Depth == 3); + Assert.AreEqual(2, background.Elements.Count); + + Assert.AreEqual(1500, background.Elements[0].StartTime); + Assert.AreEqual(1000, background.Elements[1].StartTime); + + Assert.AreEqual(1000, storyboard.FirstEventTime); + } + } + [Test] public void TestDecodeVariableWithSuffix() { diff --git a/osu.Game.Tests/Resources/out-of-order-starttimes.osb b/osu.Game.Tests/Resources/out-of-order-starttimes.osb new file mode 100644 index 0000000000..09988ff64e --- /dev/null +++ b/osu.Game.Tests/Resources/out-of-order-starttimes.osb @@ -0,0 +1,6 @@ +[Events] +//Storyboard Layer 0 (Background) +Sprite,Background,TopCentre,"img.jpg",320,240 + F,0,1500,1600,0,1 +Sprite,Background,TopCentre,"img.jpg",320,240 + F,0,1000,1100,0,1 From 20d04d69332f18b7f14eedc44fed5bdf0ae9e9a9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Jan 2021 15:16:01 +0900 Subject: [PATCH 707/818] Fix Storyboard's FirstEventTime not finding the true earliest event --- .../Beatmaps/Formats/LegacyStoryboardDecoderTest.cs | 2 +- osu.Game/Screens/Play/GameplayClockContainer.cs | 4 +++- osu.Game/Storyboards/Storyboard.cs | 10 +++++++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs index b36597a949..7bee580863 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs @@ -111,7 +111,7 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.AreEqual(1500, background.Elements[0].StartTime); Assert.AreEqual(1000, background.Elements[1].StartTime); - Assert.AreEqual(1000, storyboard.FirstEventTime); + Assert.AreEqual(1000, storyboard.EarliestEventTime); } } diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 0248432917..ddbb087962 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -131,7 +131,9 @@ namespace osu.Game.Screens.Play // if a storyboard is present, it may dictate the appropriate start time by having events in negative time space. // this is commonly used to display an intro before the audio track start. - startTime = Math.Min(startTime, beatmap.Storyboard.FirstEventTime); + double? firstStoryboardEvent = beatmap.Storyboard.EarliestEventTime; + if (firstStoryboardEvent != null) + startTime = Math.Min(startTime, firstStoryboardEvent.Value); // some beatmaps specify a current lead-in time which should be used instead of the ruleset-provided value when available. // this is not available as an option in the live editor but can still be applied via .osu editing. diff --git a/osu.Game/Storyboards/Storyboard.cs b/osu.Game/Storyboards/Storyboard.cs index e0d18eab00..d4ba18d394 100644 --- a/osu.Game/Storyboards/Storyboard.cs +++ b/osu.Game/Storyboards/Storyboard.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.EntityFrameworkCore.Internal; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; @@ -27,7 +28,14 @@ namespace osu.Game.Storyboards public bool HasDrawable => Layers.Any(l => l.Elements.Any(e => e.IsDrawable)); - public double FirstEventTime => Layers.Min(l => l.Elements.FirstOrDefault()?.StartTime ?? 0); + /// + /// Across all layers, find the earliest point in time that a storyboard element exists at. + /// Will return null if there are no elements. + /// + /// + /// This iterates all elements and as such should be used sparingly or stored locally. + /// + public double? EarliestEventTime => Layers.SelectMany(l => l.Elements).OrderBy(e => e.StartTime).FirstOrDefault()?.StartTime; /// /// Depth of the currently front-most storyboard layer, excluding the overlay layer. From 9e0c490141e4e85ecde7c96d7a6b3f37f0b3b4fa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Jan 2021 15:40:22 +0900 Subject: [PATCH 708/818] Remove unused using --- osu.Game/Storyboards/Storyboard.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Storyboards/Storyboard.cs b/osu.Game/Storyboards/Storyboard.cs index d4ba18d394..1ba25cc11e 100644 --- a/osu.Game/Storyboards/Storyboard.cs +++ b/osu.Game/Storyboards/Storyboard.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using Microsoft.EntityFrameworkCore.Internal; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; From ea38b00b29adabf0ec91b0b085f2af0b11829b23 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 4 Jan 2021 10:27:08 +0300 Subject: [PATCH 709/818] Schedule all calls to `updateBeatmapState()` --- osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs b/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs index 144535ed4c..6f86f2b879 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs @@ -39,9 +39,9 @@ namespace osu.Game.Screens.OnlinePlay.Components SelectedItem.BindValueChanged(item => updateSelectedItem(item.NewValue), true); } - private void updateSelectedItem(PlaylistItem _) => updateBeatmapState(); - private void beatmapUpdated(ValueChangedEvent> _) => updateBeatmapState(); - private void beatmapRemoved(ValueChangedEvent> _) => updateBeatmapState(); + private void updateSelectedItem(PlaylistItem _) => Scheduler.AddOnce(updateBeatmapState); + private void beatmapUpdated(ValueChangedEvent> _) => Scheduler.AddOnce(updateBeatmapState); + private void beatmapRemoved(ValueChangedEvent> _) => Scheduler.AddOnce(updateBeatmapState); private void updateBeatmapState() { From 485a57776b205581528925011a1d2ec77d8fc26d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 4 Jan 2021 10:28:41 +0300 Subject: [PATCH 710/818] Fix `hasBeatmap` potentially checking on outdated `DeletePending` value --- osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs b/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs index 6f86f2b879..b782df75df 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs @@ -52,7 +52,15 @@ namespace osu.Game.Screens.OnlinePlay.Components return; var databasedBeatmap = beatmaps.QueryBeatmap(b => b.OnlineBeatmapID == beatmapId && b.MD5Hash == checksum); - hasBeatmap = databasedBeatmap != null && !databasedBeatmap.BeatmapSet.DeletePending; + + if (databasedBeatmap == null) + hasBeatmap = false; + else + { + // DeletePending isn't updated in the beatmap info query above, need to directly query the beatmap set from database as well. + var databasedSet = beatmaps.QueryBeatmapSet(s => s.Equals(databasedBeatmap.BeatmapSet)); + hasBeatmap = databasedSet?.DeletePending == false; + } } protected override void Update() From ba4e41142262d683e25185af98e13a07157214ce Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Jan 2021 16:37:07 +0900 Subject: [PATCH 711/818] Clone and copy ControlPointInfo when retrieving a playable beatmap --- osu.Game/Beatmaps/ControlPoints/ControlPoint.cs | 17 +++++++++++++++++ .../Beatmaps/ControlPoints/ControlPointInfo.cs | 12 +++++++++++- .../ControlPoints/DifficultyControlPoint.cs | 7 +++++++ .../ControlPoints/EffectControlPoint.cs | 8 ++++++++ .../ControlPoints/SampleControlPoint.cs | 8 ++++++++ .../ControlPoints/TimingControlPoint.cs | 8 ++++++++ osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 15 +++++++++++++-- osu.Game/Beatmaps/IBeatmap.cs | 2 +- osu.Game/Beatmaps/WorkingBeatmap.cs | 3 +++ osu.Game/Screens/Edit/EditorBeatmap.cs | 6 +++++- osu.Game/Screens/Play/GameplayBeatmap.cs | 6 +++++- 11 files changed, 86 insertions(+), 6 deletions(-) diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs index c6649f6af1..090675473d 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs @@ -28,5 +28,22 @@ namespace osu.Game.Beatmaps.ControlPoints /// An existing control point to compare with. /// Whether this is redundant when placed alongside . public abstract bool IsRedundant(ControlPoint existing); + + /// + /// Create a copy of this room without online information. + /// Should be used to create a local copy of a room for submitting in the future. + /// + public ControlPoint CreateCopy() + { + var copy = (ControlPoint)Activator.CreateInstance(GetType()); + + copy.CopyFrom(this); + + return copy; + } + + public virtual void CopyFrom(ControlPoint other) + { + } } } diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs index b843aad950..b56f9a106b 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs @@ -11,7 +11,7 @@ using osu.Framework.Lists; namespace osu.Game.Beatmaps.ControlPoints { [Serializable] - public class ControlPointInfo + public class ControlPointInfo : ICloneable { /// /// All control points grouped by time. @@ -297,5 +297,15 @@ namespace osu.Game.Beatmaps.ControlPoints break; } } + + public object Clone() + { + var controlPointInfo = new ControlPointInfo(); + + foreach (var point in AllControlPoints) + controlPointInfo.Add(point.Time, point.CreateCopy()); + + return controlPointInfo; + } } } diff --git a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs index 283bf76572..0bc5605051 100644 --- a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs @@ -39,5 +39,12 @@ namespace osu.Game.Beatmaps.ControlPoints public override bool IsRedundant(ControlPoint existing) => existing is DifficultyControlPoint existingDifficulty && SpeedMultiplier == existingDifficulty.SpeedMultiplier; + + public override void CopyFrom(ControlPoint other) + { + SpeedMultiplier = ((DifficultyControlPoint)other).SpeedMultiplier; + + base.CopyFrom(other); + } } } diff --git a/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs index ea28fca170..79bc88e773 100644 --- a/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs @@ -50,5 +50,13 @@ namespace osu.Game.Beatmaps.ControlPoints && existing is EffectControlPoint existingEffect && KiaiMode == existingEffect.KiaiMode && OmitFirstBarLine == existingEffect.OmitFirstBarLine; + + public override void CopyFrom(ControlPoint other) + { + KiaiMode = ((EffectControlPoint)other).KiaiMode; + OmitFirstBarLine = ((EffectControlPoint)other).OmitFirstBarLine; + + base.CopyFrom(other); + } } } diff --git a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs index fd0b496335..4aa6a3d6e9 100644 --- a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs @@ -72,5 +72,13 @@ namespace osu.Game.Beatmaps.ControlPoints => existing is SampleControlPoint existingSample && SampleBank == existingSample.SampleBank && SampleVolume == existingSample.SampleVolume; + + public override void CopyFrom(ControlPoint other) + { + SampleVolume = ((SampleControlPoint)other).SampleVolume; + SampleBank = ((SampleControlPoint)other).SampleBank; + + base.CopyFrom(other); + } } } diff --git a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs index d9378bca4a..580642f593 100644 --- a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs @@ -69,5 +69,13 @@ namespace osu.Game.Beatmaps.ControlPoints // Timing points are never redundant as they can change the time signature. public override bool IsRedundant(ControlPoint existing) => false; + + public override void CopyFrom(ControlPoint other) + { + TimeSignature = ((TimingControlPoint)other).TimeSignature; + BeatLength = ((TimingControlPoint)other).BeatLength; + + base.CopyFrom(other); + } } } diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index c9d139bdd0..06ff677aed 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -164,13 +164,24 @@ namespace osu.Game.Beatmaps.Formats /// Legacy BPM multiplier that introduces floating-point errors for rulesets that depend on it. /// DO NOT USE THIS UNLESS 100% SURE. /// - public readonly float BpmMultiplier; + public float BpmMultiplier { get; set; } public LegacyDifficultyControlPoint(double beatLength) + : this() + { + BpmMultiplier = beatLength < 0 ? Math.Clamp((float)-beatLength, 10, 10000) / 100f : 1; + } + + public LegacyDifficultyControlPoint() { SpeedMultiplierBindable.Precision = double.Epsilon; + } - BpmMultiplier = beatLength < 0 ? Math.Clamp((float)-beatLength, 10, 10000) / 100f : 1; + public override void CopyFrom(ControlPoint other) + { + base.CopyFrom(other); + + BpmMultiplier = ((LegacyDifficultyControlPoint)other).BpmMultiplier; } } 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..06b5913b18 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -14,6 +14,7 @@ using osu.Framework.Graphics.Textures; using osu.Framework.Logging; using osu.Framework.Statistics; using osu.Framework.Testing; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Types; @@ -111,6 +112,8 @@ namespace osu.Game.Beatmaps // Convert IBeatmap converted = converter.Convert(cancellationSource.Token); + converted.ControlPointInfo = (ControlPointInfo)converted.ControlPointInfo.Clone(); + // 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 b4a779108e7c87994198690cba57ca05e1a0e503 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Jan 2021 16:37:49 +0900 Subject: [PATCH 712/818] Ensure working beatmap is reloaded on exiting the editor --- osu.Game/Screens/Edit/Editor.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 223c678fba..a9fe7ba38d 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -483,6 +483,8 @@ namespace osu.Game.Screens.Edit Background.FadeColour(Color4.White, 500); resetTrack(); + Beatmap.Value = beatmapManager.GetWorkingBeatmap(Beatmap.Value.BeatmapInfo); + return base.OnExiting(next); } From 7fdf876b4c8fb19020033d080fca3ee08dd44adc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Jan 2021 16:38:15 +0900 Subject: [PATCH 713/818] Fix editor timing screen mutating the WorkingBeatmap instead of EditorBeatmap --- osu.Game/Screens/Edit/Timing/DifficultySection.cs | 2 +- osu.Game/Screens/Edit/Timing/EffectSection.cs | 2 +- osu.Game/Screens/Edit/Timing/GroupSection.cs | 9 ++++----- osu.Game/Screens/Edit/Timing/SampleSection.cs | 2 +- osu.Game/Screens/Edit/Timing/Section.cs | 3 +-- osu.Game/Screens/Edit/Timing/TimingScreen.cs | 11 +++++------ osu.Game/Screens/Edit/Timing/TimingSection.cs | 2 +- 7 files changed, 14 insertions(+), 17 deletions(-) diff --git a/osu.Game/Screens/Edit/Timing/DifficultySection.cs b/osu.Game/Screens/Edit/Timing/DifficultySection.cs index b55d74e3b4..b87b8961f8 100644 --- a/osu.Game/Screens/Edit/Timing/DifficultySection.cs +++ b/osu.Game/Screens/Edit/Timing/DifficultySection.cs @@ -34,7 +34,7 @@ namespace osu.Game.Screens.Edit.Timing protected override DifficultyControlPoint CreatePoint() { - var reference = Beatmap.Value.Beatmap.ControlPointInfo.DifficultyPointAt(SelectedGroup.Value.Time); + var reference = Beatmap.ControlPointInfo.DifficultyPointAt(SelectedGroup.Value.Time); return new DifficultyControlPoint { diff --git a/osu.Game/Screens/Edit/Timing/EffectSection.cs b/osu.Game/Screens/Edit/Timing/EffectSection.cs index 2f143108a9..6d23b52c05 100644 --- a/osu.Game/Screens/Edit/Timing/EffectSection.cs +++ b/osu.Game/Screens/Edit/Timing/EffectSection.cs @@ -37,7 +37,7 @@ namespace osu.Game.Screens.Edit.Timing protected override EffectControlPoint CreatePoint() { - var reference = Beatmap.Value.Beatmap.ControlPointInfo.EffectPointAt(SelectedGroup.Value.Time); + var reference = Beatmap.ControlPointInfo.EffectPointAt(SelectedGroup.Value.Time); return new EffectControlPoint { diff --git a/osu.Game/Screens/Edit/Timing/GroupSection.cs b/osu.Game/Screens/Edit/Timing/GroupSection.cs index 2605ea8b75..2e2c380d4a 100644 --- a/osu.Game/Screens/Edit/Timing/GroupSection.cs +++ b/osu.Game/Screens/Edit/Timing/GroupSection.cs @@ -6,7 +6,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; @@ -24,7 +23,7 @@ namespace osu.Game.Screens.Edit.Timing protected Bindable SelectedGroup { get; private set; } [Resolved] - protected IBindable Beatmap { get; private set; } + protected EditorBeatmap Beatmap { get; private set; } [Resolved] private EditorClock clock { get; set; } @@ -107,13 +106,13 @@ namespace osu.Game.Screens.Edit.Timing var currentGroupItems = SelectedGroup.Value.ControlPoints.ToArray(); - Beatmap.Value.Beatmap.ControlPointInfo.RemoveGroup(SelectedGroup.Value); + Beatmap.ControlPointInfo.RemoveGroup(SelectedGroup.Value); foreach (var cp in currentGroupItems) - Beatmap.Value.Beatmap.ControlPointInfo.Add(time, cp); + Beatmap.ControlPointInfo.Add(time, cp); // the control point might not necessarily exist yet, if currentGroupItems was empty. - SelectedGroup.Value = Beatmap.Value.Beatmap.ControlPointInfo.GroupAt(time, true); + SelectedGroup.Value = Beatmap.ControlPointInfo.GroupAt(time, true); changeHandler?.EndChange(); } diff --git a/osu.Game/Screens/Edit/Timing/SampleSection.cs b/osu.Game/Screens/Edit/Timing/SampleSection.cs index 280e19c99a..cc73af6349 100644 --- a/osu.Game/Screens/Edit/Timing/SampleSection.cs +++ b/osu.Game/Screens/Edit/Timing/SampleSection.cs @@ -44,7 +44,7 @@ namespace osu.Game.Screens.Edit.Timing protected override SampleControlPoint CreatePoint() { - var reference = Beatmap.Value.Beatmap.ControlPointInfo.SamplePointAt(SelectedGroup.Value.Time); + var reference = Beatmap.ControlPointInfo.SamplePointAt(SelectedGroup.Value.Time); return new SampleControlPoint { diff --git a/osu.Game/Screens/Edit/Timing/Section.cs b/osu.Game/Screens/Edit/Timing/Section.cs index 7a81eeb1a4..5269fa9774 100644 --- a/osu.Game/Screens/Edit/Timing/Section.cs +++ b/osu.Game/Screens/Edit/Timing/Section.cs @@ -7,7 +7,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; @@ -27,7 +26,7 @@ namespace osu.Game.Screens.Edit.Timing private const float header_height = 20; [Resolved] - protected IBindable Beatmap { get; private set; } + protected EditorBeatmap Beatmap { get; private set; } [Resolved] protected Bindable SelectedGroup { get; private set; } diff --git a/osu.Game/Screens/Edit/Timing/TimingScreen.cs b/osu.Game/Screens/Edit/Timing/TimingScreen.cs index eab909b798..c5d2dd756a 100644 --- a/osu.Game/Screens/Edit/Timing/TimingScreen.cs +++ b/osu.Game/Screens/Edit/Timing/TimingScreen.cs @@ -7,7 +7,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; using osu.Game.Graphics.Containers; @@ -62,7 +61,7 @@ namespace osu.Game.Screens.Edit.Timing private EditorClock clock { get; set; } [Resolved] - protected IBindable Beatmap { get; private set; } + protected EditorBeatmap Beatmap { get; private set; } [Resolved] private Bindable selectedGroup { get; set; } @@ -124,7 +123,7 @@ namespace osu.Game.Screens.Edit.Timing selectedGroup.BindValueChanged(selected => { deleteButton.Enabled.Value = selected.NewValue != null; }, true); - controlPointGroups.BindTo(Beatmap.Value.Beatmap.ControlPointInfo.Groups); + controlPointGroups.BindTo(Beatmap.ControlPointInfo.Groups); controlPointGroups.BindCollectionChanged((sender, args) => { table.ControlGroups = controlPointGroups; @@ -137,14 +136,14 @@ namespace osu.Game.Screens.Edit.Timing if (selectedGroup.Value == null) return; - Beatmap.Value.Beatmap.ControlPointInfo.RemoveGroup(selectedGroup.Value); + Beatmap.ControlPointInfo.RemoveGroup(selectedGroup.Value); - selectedGroup.Value = Beatmap.Value.Beatmap.ControlPointInfo.Groups.FirstOrDefault(g => g.Time >= clock.CurrentTime); + selectedGroup.Value = Beatmap.ControlPointInfo.Groups.FirstOrDefault(g => g.Time >= clock.CurrentTime); } private void addNew() { - selectedGroup.Value = Beatmap.Value.Beatmap.ControlPointInfo.GroupAt(clock.CurrentTime, true); + selectedGroup.Value = Beatmap.ControlPointInfo.GroupAt(clock.CurrentTime, true); } } } diff --git a/osu.Game/Screens/Edit/Timing/TimingSection.cs b/osu.Game/Screens/Edit/Timing/TimingSection.cs index 1ae2a86885..a0bb9ac506 100644 --- a/osu.Game/Screens/Edit/Timing/TimingSection.cs +++ b/osu.Game/Screens/Edit/Timing/TimingSection.cs @@ -49,7 +49,7 @@ namespace osu.Game.Screens.Edit.Timing protected override TimingControlPoint CreatePoint() { - var reference = Beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(SelectedGroup.Value.Time); + var reference = Beatmap.ControlPointInfo.TimingPointAt(SelectedGroup.Value.Time); return new TimingControlPoint { From b7dd54847fc97e98ac5d0eef7d06b5b4d12b509b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Jan 2021 16:47:08 +0900 Subject: [PATCH 714/818] Move resolved usage of WorkingBeatmap in editor components as local as possible to avoid misuse --- .../Screens/Edit/Compose/ComposeScreen.cs | 5 ++++- osu.Game/Screens/Edit/EditorScreen.cs | 5 ----- .../Screens/Edit/Setup/DifficultySection.cs | 16 ++++++++-------- .../Screens/Edit/Setup/MetadataSection.cs | 16 ++++++++-------- .../Screens/Edit/Setup/ResourcesSection.cs | 19 +++++++++++-------- osu.Game/Screens/Edit/Setup/SetupSection.cs | 4 +--- 6 files changed, 32 insertions(+), 33 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs index c297a03dbf..81b1195a40 100644 --- a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs +++ b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs @@ -16,6 +16,9 @@ namespace osu.Game.Screens.Edit.Compose { public class ComposeScreen : EditorScreenWithTimeline { + [Resolved] + private IBindable beatmap { get; set; } + private HitObjectComposer composer; public ComposeScreen() @@ -59,7 +62,7 @@ namespace osu.Game.Screens.Edit.Compose { Debug.Assert(ruleset != null); - var beatmapSkinProvider = new BeatmapSkinProvidingContainer(Beatmap.Value.Skin); + var beatmapSkinProvider = new BeatmapSkinProvidingContainer(beatmap.Value.Skin); // the beatmapSkinProvider is used as the fallback source here to allow the ruleset-specific skin implementation // full access to all skin sources. diff --git a/osu.Game/Screens/Edit/EditorScreen.cs b/osu.Game/Screens/Edit/EditorScreen.cs index 4d62a7d3cd..7fbb6a8ca0 100644 --- a/osu.Game/Screens/Edit/EditorScreen.cs +++ b/osu.Game/Screens/Edit/EditorScreen.cs @@ -2,10 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; namespace osu.Game.Screens.Edit { @@ -14,9 +12,6 @@ namespace osu.Game.Screens.Edit /// public abstract class EditorScreen : Container { - [Resolved] - protected IBindable Beatmap { get; private set; } - [Resolved] protected EditorBeatmap EditorBeatmap { get; private set; } diff --git a/osu.Game/Screens/Edit/Setup/DifficultySection.cs b/osu.Game/Screens/Edit/Setup/DifficultySection.cs index 897ddc6955..f180d7e63e 100644 --- a/osu.Game/Screens/Edit/Setup/DifficultySection.cs +++ b/osu.Game/Screens/Edit/Setup/DifficultySection.cs @@ -34,7 +34,7 @@ namespace osu.Game.Screens.Edit.Setup { Label = "Object Size", Description = "The size of all hit objects", - Current = new BindableFloat(Beatmap.Value.BeatmapInfo.BaseDifficulty.CircleSize) + Current = new BindableFloat(Beatmap.BeatmapInfo.BaseDifficulty.CircleSize) { Default = BeatmapDifficulty.DEFAULT_DIFFICULTY, MinValue = 0, @@ -46,7 +46,7 @@ namespace osu.Game.Screens.Edit.Setup { Label = "Health Drain", Description = "The rate of passive health drain throughout playable time", - Current = new BindableFloat(Beatmap.Value.BeatmapInfo.BaseDifficulty.DrainRate) + Current = new BindableFloat(Beatmap.BeatmapInfo.BaseDifficulty.DrainRate) { Default = BeatmapDifficulty.DEFAULT_DIFFICULTY, MinValue = 0, @@ -58,7 +58,7 @@ namespace osu.Game.Screens.Edit.Setup { Label = "Approach Rate", Description = "The speed at which objects are presented to the player", - Current = new BindableFloat(Beatmap.Value.BeatmapInfo.BaseDifficulty.ApproachRate) + Current = new BindableFloat(Beatmap.BeatmapInfo.BaseDifficulty.ApproachRate) { Default = BeatmapDifficulty.DEFAULT_DIFFICULTY, MinValue = 0, @@ -70,7 +70,7 @@ namespace osu.Game.Screens.Edit.Setup { Label = "Overall Difficulty", Description = "The harshness of hit windows and difficulty of special objects (ie. spinners)", - Current = new BindableFloat(Beatmap.Value.BeatmapInfo.BaseDifficulty.OverallDifficulty) + Current = new BindableFloat(Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty) { Default = BeatmapDifficulty.DEFAULT_DIFFICULTY, MinValue = 0, @@ -88,10 +88,10 @@ namespace osu.Game.Screens.Edit.Setup { // for now, update these on commit rather than making BeatmapMetadata bindables. // after switching database engines we can reconsider if switching to bindables is a good direction. - Beatmap.Value.BeatmapInfo.BaseDifficulty.CircleSize = circleSizeSlider.Current.Value; - Beatmap.Value.BeatmapInfo.BaseDifficulty.DrainRate = healthDrainSlider.Current.Value; - Beatmap.Value.BeatmapInfo.BaseDifficulty.ApproachRate = approachRateSlider.Current.Value; - Beatmap.Value.BeatmapInfo.BaseDifficulty.OverallDifficulty = overallDifficultySlider.Current.Value; + Beatmap.BeatmapInfo.BaseDifficulty.CircleSize = circleSizeSlider.Current.Value; + Beatmap.BeatmapInfo.BaseDifficulty.DrainRate = healthDrainSlider.Current.Value; + Beatmap.BeatmapInfo.BaseDifficulty.ApproachRate = approachRateSlider.Current.Value; + Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty = overallDifficultySlider.Current.Value; editorBeatmap.UpdateAllHitObjects(); } diff --git a/osu.Game/Screens/Edit/Setup/MetadataSection.cs b/osu.Game/Screens/Edit/Setup/MetadataSection.cs index 4ddee2acc6..e812c042fb 100644 --- a/osu.Game/Screens/Edit/Setup/MetadataSection.cs +++ b/osu.Game/Screens/Edit/Setup/MetadataSection.cs @@ -29,25 +29,25 @@ namespace osu.Game.Screens.Edit.Setup artistTextBox = new LabelledTextBox { Label = "Artist", - Current = { Value = Beatmap.Value.Metadata.Artist }, + Current = { Value = Beatmap.Metadata.Artist }, TabbableContentContainer = this }, titleTextBox = new LabelledTextBox { Label = "Title", - Current = { Value = Beatmap.Value.Metadata.Title }, + Current = { Value = Beatmap.Metadata.Title }, TabbableContentContainer = this }, creatorTextBox = new LabelledTextBox { Label = "Creator", - Current = { Value = Beatmap.Value.Metadata.AuthorString }, + Current = { Value = Beatmap.Metadata.AuthorString }, TabbableContentContainer = this }, difficultyTextBox = new LabelledTextBox { Label = "Difficulty Name", - Current = { Value = Beatmap.Value.BeatmapInfo.Version }, + Current = { Value = Beatmap.BeatmapInfo.Version }, TabbableContentContainer = this }, }; @@ -62,10 +62,10 @@ namespace osu.Game.Screens.Edit.Setup // for now, update these on commit rather than making BeatmapMetadata bindables. // after switching database engines we can reconsider if switching to bindables is a good direction. - Beatmap.Value.Metadata.Artist = artistTextBox.Current.Value; - Beatmap.Value.Metadata.Title = titleTextBox.Current.Value; - Beatmap.Value.Metadata.AuthorString = creatorTextBox.Current.Value; - Beatmap.Value.BeatmapInfo.Version = difficultyTextBox.Current.Value; + Beatmap.Metadata.Artist = artistTextBox.Current.Value; + Beatmap.Metadata.Title = titleTextBox.Current.Value; + Beatmap.Metadata.AuthorString = creatorTextBox.Current.Value; + Beatmap.BeatmapInfo.Version = difficultyTextBox.Current.Value; } } } diff --git a/osu.Game/Screens/Edit/Setup/ResourcesSection.cs b/osu.Game/Screens/Edit/Setup/ResourcesSection.cs index 0c957b80af..b3bceceea3 100644 --- a/osu.Game/Screens/Edit/Setup/ResourcesSection.cs +++ b/osu.Game/Screens/Edit/Setup/ResourcesSection.cs @@ -42,6 +42,9 @@ namespace osu.Game.Screens.Edit.Setup [Resolved] private BeatmapManager beatmaps { get; set; } + [Resolved] + private IBindable working { get; set; } + [Resolved(canBeNull: true)] private Editor editor { get; set; } @@ -70,7 +73,7 @@ namespace osu.Game.Screens.Edit.Setup audioTrackTextBox = new FileChooserLabelledTextBox { Label = "Audio Track", - Current = { Value = Beatmap.Value.Metadata.AudioFile ?? "Click to select a track" }, + Current = { Value = Beatmap.Metadata.AudioFile ?? "Click to select a track" }, Target = audioTrackFileChooserContainer, TabbableContentContainer = this }, @@ -115,11 +118,11 @@ namespace osu.Game.Screens.Edit.Setup if (!info.Exists) return false; - var set = Beatmap.Value.BeatmapSetInfo; + var set = working.Value.BeatmapSetInfo; // remove the previous background for now. // in the future we probably want to check if this is being used elsewhere (other difficulties?) - var oldFile = set.Files.FirstOrDefault(f => f.Filename == Beatmap.Value.Metadata.BackgroundFile); + var oldFile = set.Files.FirstOrDefault(f => f.Filename == working.Value.Metadata.BackgroundFile); using (var stream = info.OpenRead()) { @@ -129,7 +132,7 @@ namespace osu.Game.Screens.Edit.Setup beatmaps.AddFile(set, stream, info.Name); } - Beatmap.Value.Metadata.BackgroundFile = info.Name; + working.Value.Metadata.BackgroundFile = info.Name; updateBackgroundSprite(); return true; @@ -148,11 +151,11 @@ namespace osu.Game.Screens.Edit.Setup if (!info.Exists) return false; - var set = Beatmap.Value.BeatmapSetInfo; + var set = working.Value.BeatmapSetInfo; // remove the previous audio track for now. // in the future we probably want to check if this is being used elsewhere (other difficulties?) - var oldFile = set.Files.FirstOrDefault(f => f.Filename == Beatmap.Value.Metadata.AudioFile); + var oldFile = set.Files.FirstOrDefault(f => f.Filename == working.Value.Metadata.AudioFile); using (var stream = info.OpenRead()) { @@ -162,7 +165,7 @@ namespace osu.Game.Screens.Edit.Setup beatmaps.AddFile(set, stream, info.Name); } - Beatmap.Value.Metadata.AudioFile = info.Name; + working.Value.Metadata.AudioFile = info.Name; music.ReloadCurrentTrack(); @@ -178,7 +181,7 @@ namespace osu.Game.Screens.Edit.Setup private void updateBackgroundSprite() { - LoadComponentAsync(new BeatmapBackgroundSprite(Beatmap.Value) + LoadComponentAsync(new BeatmapBackgroundSprite(working.Value) { RelativeSizeAxes = Axes.Both, Anchor = Anchor.Centre, diff --git a/osu.Game/Screens/Edit/Setup/SetupSection.cs b/osu.Game/Screens/Edit/Setup/SetupSection.cs index cdf17d355e..88521a8fb0 100644 --- a/osu.Game/Screens/Edit/Setup/SetupSection.cs +++ b/osu.Game/Screens/Edit/Setup/SetupSection.cs @@ -2,10 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; using osu.Game.Graphics; using osuTK; @@ -19,7 +17,7 @@ namespace osu.Game.Screens.Edit.Setup protected OsuColour Colours { get; private set; } [Resolved] - protected IBindable Beatmap { get; private set; } + protected EditorBeatmap Beatmap { get; private set; } protected override Container Content => flow; From 3b08faa0ea134d99f6cd9dcd02096df1ffac115a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Jan 2021 17:49:11 +0900 Subject: [PATCH 715/818] Fix RemoveBlockingOverlay causing transform mutation from disposal threads --- osu.Game/OsuGame.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 17831ed26b..36e3078653 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -151,11 +151,11 @@ namespace osu.Game updateBlockingOverlayFade(); } - public void RemoveBlockingOverlay(OverlayContainer overlay) + public void RemoveBlockingOverlay(OverlayContainer overlay) => Schedule(() => { visibleBlockingOverlays.Remove(overlay); updateBlockingOverlayFade(); - } + }); /// /// Close all game-wide overlays. From 2d1b52be0d8e02ffc4c4fbbd6a9f8f621d54f211 Mon Sep 17 00:00:00 2001 From: KyeKiller Date: Mon, 4 Jan 2021 17:21:31 +0000 Subject: [PATCH 716/818] Moved "ToolbarSocialButton" This will remove it from coming off the screen. --- osu.Game/Overlays/Toolbar/Toolbar.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index 393e349bd0..88a2518f84 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -69,12 +69,13 @@ namespace osu.Game.Overlays.Toolbar AutoSizeAxes = Axes.X, Children = new Drawable[] { + + new ToolbarSocialButton(), new ToolbarNewsButton(), new ToolbarChangelogButton(), new ToolbarRankingsButton(), new ToolbarBeatmapListingButton(), new ToolbarChatButton(), - new ToolbarSocialButton(), new ToolbarMusicButton(), //new ToolbarButton //{ From 2e2b3ab5d4f66cd0295cd939a209c51485d59b41 Mon Sep 17 00:00:00 2001 From: KyeKiller Date: Mon, 4 Jan 2021 17:26:42 +0000 Subject: [PATCH 717/818] Should remove codeFactor error --- osu.Game/Overlays/Toolbar/Toolbar.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index 88a2518f84..c95d02cea4 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -69,7 +69,6 @@ namespace osu.Game.Overlays.Toolbar AutoSizeAxes = Axes.X, Children = new Drawable[] { - new ToolbarSocialButton(), new ToolbarNewsButton(), new ToolbarChangelogButton(), From 73f5e5aaf9fb94726b6906f98b6493202fedb680 Mon Sep 17 00:00:00 2001 From: KyeKiller Date: Mon, 4 Jan 2021 21:03:51 +0000 Subject: [PATCH 718/818] Moved "ToolbarSocialButton" back --- osu.Game/Overlays/Toolbar/Toolbar.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index c95d02cea4..393e349bd0 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -69,12 +69,12 @@ namespace osu.Game.Overlays.Toolbar AutoSizeAxes = Axes.X, Children = new Drawable[] { - new ToolbarSocialButton(), new ToolbarNewsButton(), new ToolbarChangelogButton(), new ToolbarRankingsButton(), new ToolbarBeatmapListingButton(), new ToolbarChatButton(), + new ToolbarSocialButton(), new ToolbarMusicButton(), //new ToolbarButton //{ From 3468df840b37291dfdae94af3fb0d29a8e7493fa Mon Sep 17 00:00:00 2001 From: KyeKiller Date: Mon, 4 Jan 2021 21:04:30 +0000 Subject: [PATCH 719/818] Moved tooltip to the left to stop the overflow --- osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs b/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs index e62c7bc807..ca334702ce 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs @@ -2,12 +2,16 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Game.Input.Bindings; namespace osu.Game.Overlays.Toolbar { public class ToolbarSocialButton : ToolbarOverlayToggleButton { + + protected override Anchor TooltipAnchor => Anchor.TopRight; + public ToolbarSocialButton() { Hotkey = GlobalAction.ToggleSocial; From 0e42d415c18888192358e4f7e2a41074ebd0530f Mon Sep 17 00:00:00 2001 From: KyeKiller Date: Mon, 4 Jan 2021 21:05:28 +0000 Subject: [PATCH 720/818] Hit another oopie --- osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs b/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs index ca334702ce..1e00afc5fd 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs @@ -9,7 +9,6 @@ namespace osu.Game.Overlays.Toolbar { public class ToolbarSocialButton : ToolbarOverlayToggleButton { - protected override Anchor TooltipAnchor => Anchor.TopRight; public ToolbarSocialButton() From 1234d0fa0409c786c4e4ec4e2a63b5a5da406292 Mon Sep 17 00:00:00 2001 From: KyeKiller Date: Mon, 4 Jan 2021 22:01:12 +0000 Subject: [PATCH 721/818] Applied all tooltips to the right --- osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs | 3 +++ osu.Game/Overlays/Toolbar/ToolbarChangelogButton.cs | 5 ++++- osu.Game/Overlays/Toolbar/ToolbarChatButton.cs | 3 +++ osu.Game/Overlays/Toolbar/ToolbarNewsButton.cs | 3 +++ osu.Game/Overlays/Toolbar/ToolbarRankingsButton.cs | 3 +++ 5 files changed, 16 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs b/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs index 0363873326..c495d673ce 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs @@ -2,12 +2,15 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Game.Input.Bindings; namespace osu.Game.Overlays.Toolbar { public class ToolbarBeatmapListingButton : ToolbarOverlayToggleButton { + protected override Anchor TooltipAnchor => Anchor.TopRight; + public ToolbarBeatmapListingButton() { Hotkey = GlobalAction.ToggleDirect; diff --git a/osu.Game/Overlays/Toolbar/ToolbarChangelogButton.cs b/osu.Game/Overlays/Toolbar/ToolbarChangelogButton.cs index 23f8b141b2..28112d178f 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarChangelogButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarChangelogButton.cs @@ -2,11 +2,14 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Graphics; namespace osu.Game.Overlays.Toolbar { public class ToolbarChangelogButton : ToolbarOverlayToggleButton - { + { + protected override Anchor TooltipAnchor => Anchor.TopRight; + [BackgroundDependencyLoader(true)] private void load(ChangelogOverlay changelog) { diff --git a/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs b/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs index f9a66ae7bb..2d3b33e9bc 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs @@ -2,12 +2,15 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Game.Input.Bindings; namespace osu.Game.Overlays.Toolbar { public class ToolbarChatButton : ToolbarOverlayToggleButton { + protected override Anchor TooltipAnchor => Anchor.TopRight; + public ToolbarChatButton() { Hotkey = GlobalAction.ToggleChat; diff --git a/osu.Game/Overlays/Toolbar/ToolbarNewsButton.cs b/osu.Game/Overlays/Toolbar/ToolbarNewsButton.cs index 0ba2935c80..9b2573ad07 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarNewsButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarNewsButton.cs @@ -2,11 +2,14 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Graphics; namespace osu.Game.Overlays.Toolbar { public class ToolbarNewsButton : ToolbarOverlayToggleButton { + protected override Anchor TooltipAnchor => Anchor.TopRight; + [BackgroundDependencyLoader(true)] private void load(NewsOverlay news) { diff --git a/osu.Game/Overlays/Toolbar/ToolbarRankingsButton.cs b/osu.Game/Overlays/Toolbar/ToolbarRankingsButton.cs index 22a01bcdb5..312fc41aab 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarRankingsButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarRankingsButton.cs @@ -2,11 +2,14 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Graphics; namespace osu.Game.Overlays.Toolbar { public class ToolbarRankingsButton : ToolbarOverlayToggleButton { + protected override Anchor TooltipAnchor => Anchor.TopRight; + [BackgroundDependencyLoader(true)] private void load(RankingsOverlay rankings) { From 77e660e42677eee4d2407b3b59a649f836b35f34 Mon Sep 17 00:00:00 2001 From: KyeKiller Date: Mon, 4 Jan 2021 22:11:52 +0000 Subject: [PATCH 722/818] Should pass all checks again now. --- osu.Game/Overlays/Toolbar/ToolbarChangelogButton.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarChangelogButton.cs b/osu.Game/Overlays/Toolbar/ToolbarChangelogButton.cs index 28112d178f..86bc73361a 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarChangelogButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarChangelogButton.cs @@ -7,7 +7,7 @@ using osu.Framework.Graphics; namespace osu.Game.Overlays.Toolbar { public class ToolbarChangelogButton : ToolbarOverlayToggleButton - { + { protected override Anchor TooltipAnchor => Anchor.TopRight; [BackgroundDependencyLoader(true)] From 81355652fa8601d4537cb6998afe6208da1b925d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 5 Jan 2021 06:00:15 +0300 Subject: [PATCH 723/818] Add simple test coverage --- .../TestSceneMultiplayerReadyButton.cs | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs index 6b11613f1c..03ba73d35b 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs @@ -7,8 +7,10 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Graphics; using osu.Framework.Platform; +using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Beatmaps; +using osu.Game.Graphics.UserInterface; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; using osu.Game.Rulesets; @@ -23,6 +25,7 @@ namespace osu.Game.Tests.Visual.Multiplayer public class TestSceneMultiplayerReadyButton : MultiplayerTestScene { private MultiplayerReadyButton button; + private BeatmapSetInfo importedSet; private BeatmapManager beatmaps; private RulesetStore rulesets; @@ -38,9 +41,8 @@ namespace osu.Game.Tests.Visual.Multiplayer [SetUp] public new void Setup() => Schedule(() => { - var beatmap = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First().Beatmaps.First(); - - Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap); + importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First(); + Beatmap.Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First()); Child = button = new MultiplayerReadyButton { @@ -51,13 +53,30 @@ namespace osu.Game.Tests.Visual.Multiplayer { Value = new PlaylistItem { - Beatmap = { Value = beatmap }, - Ruleset = { Value = beatmap.Ruleset } + Beatmap = { Value = Beatmap.Value.BeatmapInfo }, + Ruleset = { Value = Beatmap.Value.BeatmapInfo.Ruleset } } } }; }); + [Test] + public void TestDeletedBeatmapDisableReady() + { + OsuButton readyButton = null; + + AddAssert("ensure ready button enabled", () => + { + readyButton = button.ChildrenOfType().Single(); + return readyButton.Enabled.Value; + }); + + AddStep("delete beatmap", () => beatmaps.Delete(importedSet)); + AddAssert("ready button disabled", () => !readyButton.Enabled.Value); + AddStep("undelete beatmap", () => beatmaps.Undelete(importedSet)); + AddAssert("ready button enabled back", () => readyButton.Enabled.Value); + } + [Test] public void TestToggleStateWhenNotHost() { From caa88c6100cb17d3307336ab43e00f16fbe98b01 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Jan 2021 13:13:51 +0900 Subject: [PATCH 724/818] Use CreateCopy instead of Clone interface I was going for conformity by using the IClonable interface, but it doesn't look like we use it anywhere else in the project. --- osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs | 4 ++-- osu.Game/Beatmaps/WorkingBeatmap.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs index b56f9a106b..e8a91e4001 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs @@ -11,7 +11,7 @@ using osu.Framework.Lists; namespace osu.Game.Beatmaps.ControlPoints { [Serializable] - public class ControlPointInfo : ICloneable + public class ControlPointInfo { /// /// All control points grouped by time. @@ -298,7 +298,7 @@ namespace osu.Game.Beatmaps.ControlPoints } } - public object Clone() + public ControlPointInfo CreateCopy() { var controlPointInfo = new ControlPointInfo(); diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 06b5913b18..8688017887 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -112,7 +112,7 @@ namespace osu.Game.Beatmaps // Convert IBeatmap converted = converter.Convert(cancellationSource.Token); - converted.ControlPointInfo = (ControlPointInfo)converted.ControlPointInfo.Clone(); + converted.ControlPointInfo = converted.ControlPointInfo.CreateCopy(); // Apply conversion mods to the result foreach (var mod in mods.OfType()) From 385c9cd2e23e9f66b3b316b11b594c5377d27b77 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Jan 2021 13:14:16 +0900 Subject: [PATCH 725/818] Add test coverage --- .../NonVisual/ControlPointInfoTest.cs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs b/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs index 90a487c0ac..b27c257795 100644 --- a/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs +++ b/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs @@ -246,5 +246,32 @@ namespace osu.Game.Tests.NonVisual Assert.That(cpi.DifficultyPoints.Count, Is.EqualTo(0)); Assert.That(cpi.AllControlPoints.Count, Is.EqualTo(0)); } + + [Test] + public void TestCreateCopyIsDeepClone() + { + var cpi = new ControlPointInfo(); + + cpi.Add(1000, new TimingControlPoint { BeatLength = 500 }); + + var cpiCopy = cpi.CreateCopy(); + + cpiCopy.Add(2000, new TimingControlPoint { BeatLength = 500 }); + + Assert.That(cpi.Groups.Count, Is.EqualTo(1)); + Assert.That(cpiCopy.Groups.Count, Is.EqualTo(2)); + + Assert.That(cpi.TimingPoints.Count, Is.EqualTo(1)); + Assert.That(cpiCopy.TimingPoints.Count, Is.EqualTo(2)); + + Assert.That(cpi.TimingPoints[0], Is.Not.SameAs(cpiCopy.TimingPoints[0])); + Assert.That(cpi.TimingPoints[0].BeatLengthBindable, Is.Not.SameAs(cpiCopy.TimingPoints[0].BeatLengthBindable)); + + Assert.That(cpi.TimingPoints[0].BeatLength, Is.EqualTo(cpiCopy.TimingPoints[0].BeatLength)); + + cpi.TimingPoints[0].BeatLength = 800; + + Assert.That(cpi.TimingPoints[0].BeatLength, Is.Not.EqualTo(cpiCopy.TimingPoints[0].BeatLength)); + } } } From 6b8e1913eee030038ed4af72be5168d0c2ba7fab Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Jan 2021 13:27:45 +0900 Subject: [PATCH 726/818] Fix dependency not always available due to nested LoadComponentAsync call --- osu.Game/Screens/Edit/EditorScreenWithTimeline.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs index b9457f422a..2d623a200c 100644 --- a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs +++ b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs @@ -30,16 +30,16 @@ namespace osu.Game.Screens.Edit { } + private Container mainContent; + + private LoadingSpinner spinner; + [BackgroundDependencyLoader(true)] private void load([CanBeNull] BindableBeatDivisor beatDivisor) { if (beatDivisor != null) this.beatDivisor.BindTo(beatDivisor); - Container mainContent; - - LoadingSpinner spinner; - Children = new Drawable[] { mainContent = new Container @@ -99,6 +99,11 @@ namespace osu.Game.Screens.Edit } }, }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); LoadComponentAsync(CreateMainContent(), content => { From afab35a31ad71e8f25e0d9084dc4edbb8e0183b4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Jan 2021 13:41:31 +0900 Subject: [PATCH 727/818] Fix missing copy implementation in LegacySampleControlPiont --- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 06ff677aed..a06ad35b89 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -203,6 +203,13 @@ namespace osu.Game.Beatmaps.Formats => base.IsRedundant(existing) && existing is LegacySampleControlPoint existingSample && CustomSampleBank == existingSample.CustomSampleBank; + + public override void CopyFrom(ControlPoint other) + { + base.CopyFrom(other); + + CustomSampleBank = ((LegacySampleControlPoint)other).CustomSampleBank; + } } } } From 31a6e9b860ceb9ee1ece9441beb41b8aca042f5d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Jan 2021 14:24:49 +0900 Subject: [PATCH 728/818] Remove unused using --- osu.Game/Beatmaps/WorkingBeatmap.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 8688017887..d25adca92b 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -14,7 +14,6 @@ using osu.Framework.Graphics.Textures; using osu.Framework.Logging; using osu.Framework.Statistics; using osu.Framework.Testing; -using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Types; From ed6ffe2ef1b55d957851391d4635d0e93585dcf2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Jan 2021 14:54:59 +0900 Subject: [PATCH 729/818] Remove hacky code --- osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs b/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs index b782df75df..64ddba669d 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs @@ -53,14 +53,7 @@ namespace osu.Game.Screens.OnlinePlay.Components var databasedBeatmap = beatmaps.QueryBeatmap(b => b.OnlineBeatmapID == beatmapId && b.MD5Hash == checksum); - if (databasedBeatmap == null) - hasBeatmap = false; - else - { - // DeletePending isn't updated in the beatmap info query above, need to directly query the beatmap set from database as well. - var databasedSet = beatmaps.QueryBeatmapSet(s => s.Equals(databasedBeatmap.BeatmapSet)); - hasBeatmap = databasedSet?.DeletePending == false; - } + hasBeatmap = databasedBeatmap?.BeatmapSet?.DeletePending == false; } protected override void Update() From 962c95dc0158bfddf43ad8128dc4e1db2a131cb6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Jan 2021 19:06:00 +0900 Subject: [PATCH 730/818] Fix ModSelection making unsafe advances of ModSection --- osu.Game/Overlays/Mods/ModSection.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index 0107f94dcf..d00a365dde 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -130,7 +130,7 @@ namespace osu.Game.Overlays.Mods /// Select one or more mods in this section and deselects all other ones. /// /// The types of s which should be selected. - public void SelectTypes(IEnumerable modTypes) + public void SelectTypes(IEnumerable modTypes) => Schedule(() => { foreach (var button in buttons) { @@ -141,7 +141,7 @@ namespace osu.Game.Overlays.Mods else button.Deselect(); } - } + }); protected ModSection() { From a3e4e2f6c394544efae9df90228690fbdd3660c4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Jan 2021 18:24:21 +0900 Subject: [PATCH 731/818] Switch ResultsScreen and SongSelect inheritance and remove local implementation --- osu.Game/Screens/Ranking/ResultsScreen.cs | 4 +--- osu.Game/Screens/Select/SongSelect.cs | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index 528a1842af..98794627fe 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -25,7 +25,7 @@ using osuTK; namespace osu.Game.Screens.Ranking { - public abstract class ResultsScreen : OsuScreen, IKeyBindingHandler + public abstract class ResultsScreen : ScreenWithBeatmapBackground, IKeyBindingHandler { protected const float BACKGROUND_BLUR = 20; private static readonly float screen_height = 768 - TwoLayerButton.SIZE_EXTENDED.Y; @@ -35,8 +35,6 @@ namespace osu.Game.Screens.Ranking // Temporary for now to stop dual transitions. Should respect the current toolbar mode, but there's no way to do so currently. public override bool HideOverlaysOnEnter => true; - protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap.Value); - public readonly Bindable SelectedScore = new Bindable(); public readonly ScoreInfo Score; diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index a5252fdc96..50a1dd6edf 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -41,7 +41,7 @@ using System.Diagnostics; namespace osu.Game.Screens.Select { - public abstract class SongSelect : OsuScreen, IKeyBindingHandler + public abstract class SongSelect : ScreenWithBeatmapBackground, IKeyBindingHandler { public static readonly float WEDGE_HEIGHT = 245; @@ -76,8 +76,6 @@ namespace osu.Game.Screens.Select [Resolved] private Bindable> selectedMods { get; set; } - protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap.Value); - protected BeatmapCarousel Carousel { get; private set; } private BeatmapInfoWedge beatmapInfoWedge; From b3f08b29ca60b99f6c3aa2f3a5d3dec679452a06 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Jan 2021 18:32:23 +0900 Subject: [PATCH 732/818] Ensure that all changes to screen backgrounds are on the correct thread --- .../Background/TestSceneUserDimBackgrounds.cs | 55 ++++++++++++------- osu.Game/Rulesets/Mods/ModCinema.cs | 2 +- osu.Game/Screens/BackgroundScreen.cs | 2 + osu.Game/Screens/Edit/Editor.cs | 13 +++-- osu.Game/Screens/Menu/MainMenu.cs | 6 +- osu.Game/Screens/OsuScreen.cs | 21 +++++-- osu.Game/Screens/Play/EpilepsyWarning.cs | 21 ++++++- osu.Game/Screens/Play/Player.cs | 17 ++++-- osu.Game/Screens/Play/PlayerLoader.cs | 32 +++++++---- .../Play/ScreenWithBeatmapBackground.cs | 3 +- osu.Game/Screens/Ranking/ResultsScreen.cs | 14 +++-- osu.Game/Screens/Select/SongSelect.cs | 6 +- 12 files changed, 132 insertions(+), 60 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs index 5323f58a66..b5df3285f0 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs @@ -82,7 +82,7 @@ namespace osu.Game.Tests.Visual.Background }); AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); AddStep("Stop background preview", () => InputManager.MoveMouseTo(playerLoader.ScreenPos)); - AddUntilStep("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && playerLoader.IsBlurCorrect()); + AddUntilStep("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && songSelect.CheckBackgroundBlur(playerLoader.ExpectedBackgroundBlur)); } /// @@ -106,6 +106,7 @@ namespace osu.Game.Tests.Visual.Background public void TestStoryboardBackgroundVisibility() { performFullSetup(); + AddAssert("Background retained from song select", () => songSelect.IsBackgroundCurrent()); createFakeStoryboard(); AddStep("Enable Storyboard", () => { @@ -198,8 +199,9 @@ namespace osu.Game.Tests.Visual.Background }))); AddUntilStep("Wait for results is current", () => results.IsCurrentScreen()); + AddUntilStep("Screen is undimmed, original background retained", () => - songSelect.IsBackgroundUndimmed() && songSelect.IsBackgroundCurrent() && results.IsBlurCorrect()); + songSelect.IsBackgroundUndimmed() && songSelect.IsBackgroundCurrent() && songSelect.CheckBackgroundBlur(results.ExpectedBackgroundBlur)); } /// @@ -224,7 +226,7 @@ namespace osu.Game.Tests.Visual.Background AddStep("Resume PlayerLoader", () => player.Restart()); AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); AddStep("Move mouse to center of screen", () => InputManager.MoveMouseTo(playerLoader.ScreenPos)); - AddUntilStep("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && playerLoader.IsBlurCorrect()); + AddUntilStep("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && songSelect.CheckBackgroundBlur(playerLoader.ExpectedBackgroundBlur)); } private void createFakeStoryboard() => AddStep("Create storyboard", () => @@ -274,9 +276,11 @@ namespace osu.Game.Tests.Visual.Background private class DummySongSelect : PlaySongSelect { + private FadeAccessibleBackground background; + protected override BackgroundScreen CreateBackground() { - FadeAccessibleBackground background = new FadeAccessibleBackground(Beatmap.Value); + background = new FadeAccessibleBackground(Beatmap.Value); DimEnabled.BindTo(background.EnableUserDim); return background; } @@ -294,42 +298,54 @@ namespace osu.Game.Tests.Visual.Background config.BindWith(OsuSetting.BlurLevel, BlurLevel); } - public bool IsBackgroundDimmed() => ((FadeAccessibleBackground)Background).CurrentColour == OsuColour.Gray(1f - ((FadeAccessibleBackground)Background).CurrentDim); + public bool IsBackgroundDimmed() => background.CurrentColour == OsuColour.Gray(1f - background.CurrentDim); - public bool IsBackgroundUndimmed() => ((FadeAccessibleBackground)Background).CurrentColour == Color4.White; + public bool IsBackgroundUndimmed() => background.CurrentColour == Color4.White; - public bool IsUserBlurApplied() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2((float)BlurLevel.Value * BackgroundScreenBeatmap.USER_BLUR_FACTOR); + public bool IsUserBlurApplied() => background.CurrentBlur == new Vector2((float)BlurLevel.Value * BackgroundScreenBeatmap.USER_BLUR_FACTOR); - public bool IsUserBlurDisabled() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(0); + public bool IsUserBlurDisabled() => background.CurrentBlur == new Vector2(0); - public bool IsBackgroundInvisible() => ((FadeAccessibleBackground)Background).CurrentAlpha == 0; + public bool IsBackgroundInvisible() => background.CurrentAlpha == 0; - public bool IsBackgroundVisible() => ((FadeAccessibleBackground)Background).CurrentAlpha == 1; + public bool IsBackgroundVisible() => background.CurrentAlpha == 1; - public bool IsBlurCorrect() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(BACKGROUND_BLUR); + public bool IsBlurCorrect() => background.CurrentBlur == new Vector2(BACKGROUND_BLUR); + + public bool CheckBackgroundBlur(Vector2 expected) => background.CurrentBlur == expected; /// /// Make sure every time a screen gets pushed, the background doesn't get replaced /// /// Whether or not the original background (The one created in DummySongSelect) is still the current background - public bool IsBackgroundCurrent() => ((FadeAccessibleBackground)Background).IsCurrentScreen(); + public bool IsBackgroundCurrent() => background?.IsCurrentScreen() == true; } private class FadeAccessibleResults : ResultsScreen { + private FadeAccessibleBackground background; + public FadeAccessibleResults(ScoreInfo score) : base(score, true) { } - protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value); + protected override BackgroundScreen CreateBackground() => background = new FadeAccessibleBackground(Beatmap.Value); - public bool IsBlurCorrect() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(BACKGROUND_BLUR); + public Vector2 ExpectedBackgroundBlur => new Vector2(BACKGROUND_BLUR); } private class LoadBlockingTestPlayer : TestPlayer { - protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value); + protected override BackgroundScreen CreateBackground() => + new FadeAccessibleBackground(Beatmap.Value); + + public override void OnEntering(IScreen last) + { + base.OnEntering(last); + + ApplyToBackground(b => ReplacesBackground.BindTo(b.StoryboardReplacesBackground)); + } public new DimmableStoryboard DimmableStoryboard => base.DimmableStoryboard; @@ -354,15 +370,16 @@ namespace osu.Game.Tests.Visual.Background Thread.Sleep(1); StoryboardEnabled = config.GetBindable(OsuSetting.ShowStoryboard); - ReplacesBackground.BindTo(Background.StoryboardReplacesBackground); DrawableRuleset.IsPaused.BindTo(IsPaused); } } private class TestPlayerLoader : PlayerLoader { + private FadeAccessibleBackground background; + public VisualSettings VisualSettingsPos => VisualSettings; - public BackgroundScreen ScreenPos => Background; + public BackgroundScreen ScreenPos => background; public TestPlayerLoader(Player player) : base(() => player) @@ -371,9 +388,9 @@ namespace osu.Game.Tests.Visual.Background public void TriggerOnHover() => OnHover(new HoverEvent(new InputState())); - public bool IsBlurCorrect() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(BACKGROUND_BLUR); + public Vector2 ExpectedBackgroundBlur => new Vector2(BACKGROUND_BLUR); - protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value); + protected override BackgroundScreen CreateBackground() => background = new FadeAccessibleBackground(Beatmap.Value); } private class FadeAccessibleBackground : BackgroundScreenBeatmap diff --git a/osu.Game/Rulesets/Mods/ModCinema.cs b/osu.Game/Rulesets/Mods/ModCinema.cs index cf8128301c..bee9e56edd 100644 --- a/osu.Game/Rulesets/Mods/ModCinema.cs +++ b/osu.Game/Rulesets/Mods/ModCinema.cs @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Mods public void ApplyToPlayer(Player player) { - player.Background.EnableUserDim.Value = false; + player.ApplyToBackground(b => b.EnableUserDim.Value = false); player.DimmableStoryboard.IgnoreUserSettings.Value = true; diff --git a/osu.Game/Screens/BackgroundScreen.cs b/osu.Game/Screens/BackgroundScreen.cs index 0f3615b7a9..ea220c2f82 100644 --- a/osu.Game/Screens/BackgroundScreen.cs +++ b/osu.Game/Screens/BackgroundScreen.cs @@ -34,6 +34,8 @@ namespace osu.Game.Screens return false; } + public void ApplyToBackground(Action action) => Schedule(() => action.Invoke(this)); + protected override void Update() { base.Update(); diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 223c678fba..8c34cb2e08 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -444,11 +444,14 @@ namespace osu.Game.Screens.Edit { base.OnEntering(last); - // todo: temporary. we want to be applying dim using the UserDimContainer eventually. - Background.FadeColour(Color4.DarkGray, 500); + ApplyToBackground(b => + { + // todo: temporary. we want to be applying dim using the UserDimContainer eventually. + b.FadeColour(Color4.DarkGray, 500); - Background.EnableUserDim.Value = false; - Background.BlurAmount.Value = 0; + b.EnableUserDim.Value = false; + b.BlurAmount.Value = 0; + }); resetTrack(true); } @@ -480,7 +483,7 @@ namespace osu.Game.Screens.Edit } } - Background.FadeColour(Color4.White, 500); + ApplyToBackground(b => b.FadeColour(Color4.White, 500)); resetTrack(); return base.OnExiting(next); diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 9d5720ff34..97fd58318b 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -127,11 +127,11 @@ namespace osu.Game.Screens.Menu { case ButtonSystemState.Initial: case ButtonSystemState.Exit: - Background.FadeColour(Color4.White, 500, Easing.OutSine); + ApplyToBackground(b => b.FadeColour(Color4.White, 500, Easing.OutSine)); break; default: - Background.FadeColour(OsuColour.Gray(0.8f), 500, Easing.OutSine); + ApplyToBackground(b => b.FadeColour(OsuColour.Gray(0.8f), 500, Easing.OutSine)); break; } }; @@ -256,7 +256,7 @@ namespace osu.Game.Screens.Menu { base.OnResuming(last); - (Background as BackgroundScreenDefault)?.Next(); + ApplyToBackground(b => (b as BackgroundScreenDefault)?.Next()); // we may have consumed our preloaded instance, so let's make another. preloadSongSelect(); diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 851aedd84f..c97c0aef2b 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -114,9 +114,14 @@ namespace osu.Game.Screens Mods = screenDependencies.Mods; } - protected BackgroundScreen Background => backgroundStack?.CurrentScreen as BackgroundScreen; + public void ApplyToBackground(Action action) => background.ApplyToBackground(action); - private BackgroundScreen localBackground; + /// + /// The background created and owned by this screen. May be null if the background didn't change. + /// + private BackgroundScreen ownedBackground; + + private BackgroundScreen background; [Resolved(canBeNull: true)] private BackgroundScreenStack backgroundStack { get; set; } @@ -160,7 +165,15 @@ namespace osu.Game.Screens { applyArrivingDefaults(false); - backgroundStack?.Push(localBackground = CreateBackground()); + backgroundStack?.Push(ownedBackground = CreateBackground()); + + background = backgroundStack?.CurrentScreen as BackgroundScreen; + + if (background != ownedBackground) + { + // background may have not been replaced, at which point we don't want to track the background lifetime. + ownedBackground = null; + } base.OnEntering(last); } @@ -173,7 +186,7 @@ namespace osu.Game.Screens if (base.OnExiting(next)) return true; - if (localBackground != null && backgroundStack?.CurrentScreen == localBackground) + if (ownedBackground != null && backgroundStack?.CurrentScreen == ownedBackground) backgroundStack?.Exit(); return false; diff --git a/osu.Game/Screens/Play/EpilepsyWarning.cs b/osu.Game/Screens/Play/EpilepsyWarning.cs index dc42427fbf..89e25d849f 100644 --- a/osu.Game/Screens/Play/EpilepsyWarning.cs +++ b/osu.Game/Screens/Play/EpilepsyWarning.cs @@ -24,7 +24,19 @@ namespace osu.Game.Screens.Play Alpha = 0f; } - public BackgroundScreenBeatmap DimmableBackground { get; set; } + private BackgroundScreenBeatmap dimmableBackground; + + public BackgroundScreenBeatmap DimmableBackground + { + get => dimmableBackground; + set + { + dimmableBackground = value; + + if (IsLoaded) + updateBackgroundFade(); + } + } [BackgroundDependencyLoader] private void load(OsuColour colours, IBindable beatmap) @@ -75,11 +87,16 @@ namespace osu.Game.Screens.Play protected override void PopIn() { - DimmableBackground?.FadeColour(OsuColour.Gray(0.5f), FADE_DURATION, Easing.OutQuint); + updateBackgroundFade(); this.FadeIn(FADE_DURATION, Easing.OutQuint); } + private void updateBackgroundFade() + { + DimmableBackground?.FadeColour(OsuColour.Gray(0.5f), FADE_DURATION, Easing.OutQuint); + } + protected override void PopOut() => this.FadeOut(FADE_DURATION); } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index bf2e6f5379..1fcbed7ef7 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -721,15 +721,20 @@ namespace osu.Game.Screens.Play .Delay(250) .FadeIn(250); - Background.EnableUserDim.Value = true; - Background.BlurAmount.Value = 0; + ApplyToBackground(b => + { + b.EnableUserDim.Value = true; + b.BlurAmount.Value = 0; + + // bind component bindables. + b.IsBreakTime.BindTo(breakTracker.IsBreakTime); + + b.StoryboardReplacesBackground.BindTo(storyboardReplacesBackground); + }); - // bind component bindables. - Background.IsBreakTime.BindTo(breakTracker.IsBreakTime); HUDOverlay.IsBreakTime.BindTo(breakTracker.IsBreakTime); DimmableStoryboard.IsBreakTime.BindTo(breakTracker.IsBreakTime); - Background.StoryboardReplacesBackground.BindTo(storyboardReplacesBackground); DimmableStoryboard.StoryboardReplacesBackground.BindTo(storyboardReplacesBackground); storyboardReplacesBackground.Value = Beatmap.Value.Storyboard.ReplacesBackground && Beatmap.Value.Storyboard.HasDrawable; @@ -875,7 +880,7 @@ namespace osu.Game.Screens.Play float fadeOutDuration = instant ? 0 : 250; this.FadeOut(fadeOutDuration); - Background.EnableUserDim.Value = false; + ApplyToBackground(b => b.EnableUserDim.Value = false); storyboardReplacesBackground.Value = false; } diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index f59b36bc42..5b4bd11216 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -67,7 +67,7 @@ namespace osu.Game.Screens.Play backgroundBrightnessReduction = value; - Background.FadeColour(OsuColour.Gray(backgroundBrightnessReduction ? 0.8f : 1), 200); + ApplyToBackground(b => b.FadeColour(OsuColour.Gray(backgroundBrightnessReduction ? 0.8f : 1), 200)); } } @@ -176,12 +176,17 @@ namespace osu.Game.Screens.Play { base.OnEntering(last); - if (epilepsyWarning != null) - epilepsyWarning.DimmableBackground = Background; + ApplyToBackground(b => + { + if (epilepsyWarning != null) + epilepsyWarning.DimmableBackground = b; + + b?.FadeColour(Color4.White, 800, Easing.OutQuint); + }); + Beatmap.Value.Track.AddAdjustment(AdjustableProperty.Volume, volumeAdjustment); content.ScaleTo(0.7f); - Background?.FadeColour(Color4.White, 800, Easing.OutQuint); contentIn(); @@ -225,7 +230,8 @@ namespace osu.Game.Screens.Play content.ScaleTo(0.7f, 150, Easing.InQuint); this.FadeOut(150); - Background.EnableUserDim.Value = false; + ApplyToBackground(b => b.EnableUserDim.Value = false); + BackgroundBrightnessReduction = false; Beatmap.Value.Track.RemoveAdjustment(AdjustableProperty.Volume, volumeAdjustment); @@ -270,16 +276,22 @@ namespace osu.Game.Screens.Play if (inputManager.HoveredDrawables.Contains(VisualSettings)) { // Preview user-defined background dim and blur when hovered on the visual settings panel. - Background.EnableUserDim.Value = true; - Background.BlurAmount.Value = 0; + ApplyToBackground(b => + { + b.EnableUserDim.Value = true; + b.BlurAmount.Value = 0; + }); BackgroundBrightnessReduction = false; } else { - // Returns background dim and blur to the values specified by PlayerLoader. - Background.EnableUserDim.Value = false; - Background.BlurAmount.Value = BACKGROUND_BLUR; + ApplyToBackground(b => + { + // Returns background dim and blur to the values specified by PlayerLoader. + b.EnableUserDim.Value = false; + b.BlurAmount.Value = BACKGROUND_BLUR; + }); BackgroundBrightnessReduction = true; } diff --git a/osu.Game/Screens/Play/ScreenWithBeatmapBackground.cs b/osu.Game/Screens/Play/ScreenWithBeatmapBackground.cs index 8eb253608b..88dab88d42 100644 --- a/osu.Game/Screens/Play/ScreenWithBeatmapBackground.cs +++ b/osu.Game/Screens/Play/ScreenWithBeatmapBackground.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Game.Screens.Backgrounds; namespace osu.Game.Screens.Play @@ -9,6 +10,6 @@ namespace osu.Game.Screens.Play { protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap.Value); - public new BackgroundScreenBeatmap Background => (BackgroundScreenBeatmap)base.Background; + public void ApplyToBackground(Action action) => base.ApplyToBackground(b => action.Invoke((BackgroundScreenBeatmap)b)); } } diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index 98794627fe..c1f5d92d17 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -18,7 +18,6 @@ using osu.Game.Input.Bindings; using osu.Game.Online.API; using osu.Game.Rulesets.Mods; using osu.Game.Scoring; -using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Play; using osu.Game.Screens.Ranking.Statistics; using osuTK; @@ -235,15 +234,18 @@ namespace osu.Game.Screens.Ranking { base.OnEntering(last); - ((BackgroundScreenBeatmap)Background).BlurAmount.Value = BACKGROUND_BLUR; + ApplyToBackground(b => + { + b.BlurAmount.Value = BACKGROUND_BLUR; + b.FadeTo(0.5f, 250); + }); - Background.FadeTo(0.5f, 250); bottomPanel.FadeTo(1, 250); } public override bool OnExiting(IScreen next) { - Background.FadeTo(1, 250); + ApplyToBackground(b => b.FadeTo(1, 250)); return base.OnExiting(next); } @@ -293,7 +295,7 @@ namespace osu.Game.Screens.Ranking ScorePanelList.HandleInput = false; // Dim background. - Background.FadeTo(0.1f, 150); + ApplyToBackground(b => b.FadeTo(0.1f, 150)); detachedPanel = expandedPanel; } @@ -317,7 +319,7 @@ namespace osu.Game.Screens.Ranking ScorePanelList.HandleInput = true; // Un-dim background. - Background.FadeTo(0.5f, 150); + ApplyToBackground(b => b.FadeTo(0.5f, 150)); detachedPanel = null; } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 50a1dd6edf..3c255011c9 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -19,7 +19,6 @@ using osu.Game.Overlays; using osu.Game.Overlays.Mods; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; -using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Edit; using osu.Game.Screens.Menu; using osu.Game.Screens.Select.Options; @@ -38,6 +37,7 @@ using osu.Game.Collections; using osu.Game.Graphics.UserInterface; using osu.Game.Scoring; using System.Diagnostics; +using osu.Game.Screens.Play; namespace osu.Game.Screens.Select { @@ -682,12 +682,12 @@ namespace osu.Game.Screens.Select /// The working beatmap. private void updateComponentFromBeatmap(WorkingBeatmap beatmap) { - if (Background is BackgroundScreenBeatmap backgroundModeBeatmap) + ApplyToBackground(backgroundModeBeatmap => { backgroundModeBeatmap.Beatmap = beatmap; backgroundModeBeatmap.BlurAmount.Value = BACKGROUND_BLUR; backgroundModeBeatmap.FadeColour(Color4.White, 250); - } + }); beatmapInfoWedge.Beatmap = beatmap; From 5904e426ebf666ce8191ec5902389adc9f25b1b7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Jan 2021 16:00:25 +0900 Subject: [PATCH 733/818] Remove unused variable --- .../Visual/Background/TestSceneUserDimBackgrounds.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs index b5df3285f0..7ade7725d9 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs @@ -323,14 +323,12 @@ namespace osu.Game.Tests.Visual.Background private class FadeAccessibleResults : ResultsScreen { - private FadeAccessibleBackground background; - public FadeAccessibleResults(ScoreInfo score) : base(score, true) { } - protected override BackgroundScreen CreateBackground() => background = new FadeAccessibleBackground(Beatmap.Value); + protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value); public Vector2 ExpectedBackgroundBlur => new Vector2(BACKGROUND_BLUR); } From 57a8cd74615765e54d60a425f904a4034045b1a0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Jan 2021 16:17:55 +0900 Subject: [PATCH 734/818] Schedule deselection operations for safety --- osu.Game/Overlays/Mods/ModSection.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index d00a365dde..47b101c2b0 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -104,7 +104,7 @@ namespace osu.Game.Overlays.Mods /// /// The types of s which should be deselected. /// Set to true to bypass animations and update selections immediately. - public void DeselectTypes(IEnumerable modTypes, bool immediate = false) + public void DeselectTypes(IEnumerable modTypes, bool immediate = false) => Schedule(() => { int delay = 0; @@ -124,7 +124,7 @@ namespace osu.Game.Overlays.Mods } } } - } + }); /// /// Select one or more mods in this section and deselects all other ones. From 9bac791a576a0b16860f3addb3ef3a451d166d1c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Jan 2021 16:17:42 +0900 Subject: [PATCH 735/818] Fix deselection of autoplay mod failing --- osu.Game/Screens/Select/PlaySongSelect.cs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 50a61ed4c2..e61d5cce85 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -9,6 +9,7 @@ using osu.Framework.Screens; using osu.Game.Graphics; using osu.Game.Overlays; using osu.Game.Overlays.Notifications; +using osu.Game.Rulesets.Mods; using osu.Game.Scoring; using osu.Game.Screens.Play; using osu.Game.Screens.Ranking; @@ -42,6 +43,8 @@ namespace osu.Game.Screens.Select protected override BeatmapDetailArea CreateBeatmapDetailArea() => new PlayBeatmapDetailArea(); + private ModAutoplay getAutoplayMod() => Ruleset.Value.CreateInstance().GetAutoplayMod(); + public override void OnResuming(IScreen last) { base.OnResuming(last); @@ -50,10 +53,10 @@ namespace osu.Game.Screens.Select if (removeAutoModOnResume) { - var autoType = Ruleset.Value.CreateInstance().GetAutoplayMod()?.GetType(); + var autoType = getAutoplayMod()?.GetType(); if (autoType != null) - ModSelect.DeselectTypes(new[] { autoType }, true); + Mods.Value = Mods.Value.Where(m => m.GetType() != autoType).ToArray(); removeAutoModOnResume = false; } @@ -81,12 +84,9 @@ namespace osu.Game.Screens.Select // Ctrl+Enter should start map with autoplay enabled. if (GetContainingInputManager().CurrentState?.Keyboard.ControlPressed == true) { - var auto = Ruleset.Value.CreateInstance().GetAutoplayMod(); - var autoType = auto?.GetType(); + var autoplayMod = getAutoplayMod(); - var mods = Mods.Value; - - if (autoType == null) + if (autoplayMod == null) { notifications?.Post(new SimpleNotification { @@ -95,9 +95,11 @@ namespace osu.Game.Screens.Select return false; } - if (mods.All(m => m.GetType() != autoType)) + var mods = Mods.Value; + + if (mods.All(m => m.GetType() != autoplayMod.GetType())) { - Mods.Value = mods.Append(auto).ToArray(); + Mods.Value = mods.Append(autoplayMod).ToArray(); removeAutoModOnResume = true; } } From 4d6c13f169cc7c09c4db61bbfdfd780c5b44fef5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Jan 2021 16:18:13 +0900 Subject: [PATCH 736/818] Privatise ModSelectOverlay methods that may be unsafe to be called externally --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 34f5c70adb..491052fa2c 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -249,7 +249,7 @@ namespace osu.Game.Overlays.Mods { Width = 180, Text = "Deselect All", - Action = DeselectAll, + Action = deselectAll, Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, }, @@ -318,7 +318,7 @@ namespace osu.Game.Overlays.Mods sampleOff = audio.Samples.Get(@"UI/check-off"); } - public void DeselectAll() + private void deselectAll() { foreach (var section in ModSectionsContainer.Children) section.DeselectAll(); @@ -331,7 +331,7 @@ namespace osu.Game.Overlays.Mods /// /// The types of s which should be deselected. /// Set to true to bypass animations and update selections immediately. - public void DeselectTypes(Type[] modTypes, bool immediate = false) + private void deselectTypes(Type[] modTypes, bool immediate = false) { if (modTypes.Length == 0) return; @@ -438,7 +438,7 @@ namespace osu.Game.Overlays.Mods { if (State.Value == Visibility.Visible) sampleOn?.Play(); - DeselectTypes(selectedMod.IncompatibleMods, true); + deselectTypes(selectedMod.IncompatibleMods, true); if (selectedMod.RequiresConfiguration) ModSettingsContainer.Show(); } From 5d8c153c1e21949156a0dbf54b9cf8d71bca4c4d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Jan 2021 16:41:03 +0900 Subject: [PATCH 737/818] Move schedule logic to buttons rather than section It turns out there's some quite convoluted scheduling / order of execution requirements of ModSelectOverlay and ModSection. Applying scheduling causes a runaway condition ending in zero frames after many mod button changes. I wanted to avoid rewriting the whole component, so have just moved the schedule to guard against the part where drawables are actually changed. --- osu.Game/Overlays/Mods/ModButton.cs | 62 +++++++++++++++------------- osu.Game/Overlays/Mods/ModSection.cs | 8 ++-- 2 files changed, 37 insertions(+), 33 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index e574828cd2..51c8bcf9a9 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -64,41 +64,45 @@ namespace osu.Game.Overlays.Mods if (newIndex >= 0 && !Mods[newIndex].HasImplementation) return false; - selectedIndex = newIndex; - Mod modAfter = SelectedMod ?? Mods[0]; - - if (beforeSelected != Selected) + Schedule(() => { - iconsContainer.RotateTo(Selected ? 5f : 0f, 300, Easing.OutElastic); - iconsContainer.ScaleTo(Selected ? 1.1f : 1f, 300, Easing.OutElastic); - } + selectedIndex = newIndex; + Mod modAfter = SelectedMod ?? Mods[0]; - if (modBefore != modAfter) - { - const float rotate_angle = 16; - - foregroundIcon.RotateTo(rotate_angle * direction, mod_switch_duration, mod_switch_easing); - backgroundIcon.RotateTo(-rotate_angle * direction, mod_switch_duration, mod_switch_easing); - - backgroundIcon.Mod = modAfter; - - using (BeginDelayedSequence(mod_switch_duration, true)) + if (beforeSelected != Selected) { - foregroundIcon - .RotateTo(-rotate_angle * direction) - .RotateTo(0f, mod_switch_duration, mod_switch_easing); - - backgroundIcon - .RotateTo(rotate_angle * direction) - .RotateTo(0f, mod_switch_duration, mod_switch_easing); - - Schedule(() => displayMod(modAfter)); + iconsContainer.RotateTo(Selected ? 5f : 0f, 300, Easing.OutElastic); + iconsContainer.ScaleTo(Selected ? 1.1f : 1f, 300, Easing.OutElastic); } - } - foregroundIcon.Selected.Value = Selected; + if (modBefore != modAfter) + { + const float rotate_angle = 16; + + foregroundIcon.RotateTo(rotate_angle * direction, mod_switch_duration, mod_switch_easing); + backgroundIcon.RotateTo(-rotate_angle * direction, mod_switch_duration, mod_switch_easing); + + backgroundIcon.Mod = modAfter; + + using (BeginDelayedSequence(mod_switch_duration, true)) + { + foregroundIcon + .RotateTo(-rotate_angle * direction) + .RotateTo(0f, mod_switch_duration, mod_switch_easing); + + backgroundIcon + .RotateTo(rotate_angle * direction) + .RotateTo(0f, mod_switch_duration, mod_switch_easing); + + Schedule(() => displayMod(modAfter)); + } + } + + foregroundIcon.Selected.Value = Selected; + + SelectionChanged?.Invoke(SelectedMod); + }); - SelectionChanged?.Invoke(SelectedMod); return true; } diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index 47b101c2b0..0107f94dcf 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -104,7 +104,7 @@ namespace osu.Game.Overlays.Mods /// /// The types of s which should be deselected. /// Set to true to bypass animations and update selections immediately. - public void DeselectTypes(IEnumerable modTypes, bool immediate = false) => Schedule(() => + public void DeselectTypes(IEnumerable modTypes, bool immediate = false) { int delay = 0; @@ -124,13 +124,13 @@ namespace osu.Game.Overlays.Mods } } } - }); + } /// /// Select one or more mods in this section and deselects all other ones. /// /// The types of s which should be selected. - public void SelectTypes(IEnumerable modTypes) => Schedule(() => + public void SelectTypes(IEnumerable modTypes) { foreach (var button in buttons) { @@ -141,7 +141,7 @@ namespace osu.Game.Overlays.Mods else button.Deselect(); } - }); + } protected ModSection() { From 54982dcdd7e46ee8081f176ebc07b9037e0bfd5c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Jan 2021 22:42:39 +0900 Subject: [PATCH 738/818] Refactor LoadingLayer to avoid applying effects to external drawables In theory this seemed like a good idea (and an optimisation in some cases, due to lower fill rate), but in practice this leads to weird edge cases. This aims to do away with the operations on external drawables by applying a dim to the area behind the `LoadingLayer` when required. I went over each usage and ensured they look as good or better than previously. The specific bad usage here was the restoration of the colour on dispose (if the `LoadingLayer` was disposed in a still-visible state). I'm aware that the `BeatmapListingOverlay` will now dim completely during load. I think this is fine for the time being. --- .../UserInterface/TestSceneLoadingLayer.cs | 10 +- .../Graphics/UserInterface/LoadingLayer.cs | 29 +- .../Overlays/AccountCreation/ScreenEntry.cs | 2 +- osu.Game/Overlays/BeatmapListingOverlay.cs | 8 +- .../BeatmapSet/Buttons/FavouriteButton.cs | 2 +- .../BeatmapSet/Scores/ScoresContainer.cs | 10 +- .../Dashboard/Friends/FriendDisplay.cs | 2 +- osu.Game/Overlays/DashboardOverlay.cs | 2 +- osu.Game/Overlays/NewsOverlay.cs | 2 +- .../Overlays/Rankings/SpotlightsLayout.cs | 3 +- osu.Game/Overlays/RankingsOverlay.cs | 6 +- .../OnlinePlay/Lounge/LoungeSubScreen.cs | 2 +- .../Match/MultiplayerMatchSettingsOverlay.cs | 279 ++++++------ .../Multiplayer/MultiplayerMatchSongSelect.cs | 2 +- .../Multiplayer/MultiplayerPlayer.cs | 2 +- .../PlaylistsMatchSettingsOverlay.cs | 409 +++++++++--------- .../Screens/Play/BeatmapMetadataDisplay.cs | 2 +- osu.Game/Screens/Select/BeatmapDetails.cs | 6 +- 18 files changed, 389 insertions(+), 389 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneLoadingLayer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneLoadingLayer.cs index 1be191fc29..a694595115 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneLoadingLayer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneLoadingLayer.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -14,11 +15,12 @@ namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneLoadingLayer : OsuTestScene { - private Drawable dimContent; private LoadingLayer overlay; private Container content; + private Drawable dimContent => overlay.Children.OfType().First(); + [SetUp] public void SetUp() => Schedule(() => { @@ -29,14 +31,14 @@ namespace osu.Game.Tests.Visual.UserInterface Size = new Vector2(300), Anchor = Anchor.Centre, Origin = Anchor.Centre, - Children = new[] + Children = new Drawable[] { new Box { Colour = Color4.SlateGray, RelativeSizeAxes = Axes.Both, }, - dimContent = new FillFlowContainer + new FillFlowContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -51,7 +53,7 @@ namespace osu.Game.Tests.Visual.UserInterface new TriangleButton { Text = "puush me", Width = 200, Action = () => { } }, } }, - overlay = new LoadingLayer(dimContent), + overlay = new LoadingLayer(true), } }, }; diff --git a/osu.Game/Graphics/UserInterface/LoadingLayer.cs b/osu.Game/Graphics/UserInterface/LoadingLayer.cs index c8c4424bee..6aca30328a 100644 --- a/osu.Game/Graphics/UserInterface/LoadingLayer.cs +++ b/osu.Game/Graphics/UserInterface/LoadingLayer.cs @@ -4,6 +4,7 @@ using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osuTK; using osuTK.Graphics; @@ -17,22 +18,31 @@ namespace osu.Game.Graphics.UserInterface /// public class LoadingLayer : LoadingSpinner { - private readonly Drawable dimTarget; + private readonly Box backgroundDimLayer; /// - /// Constuct a new loading spinner. + /// Construct a new loading spinner. /// - /// An optional target to dim when displayed. + /// Whether the full background area should be dimmed while loading. /// Whether the spinner should have a surrounding black box for visibility. - public LoadingLayer(Drawable dimTarget = null, bool withBox = true) + public LoadingLayer(bool dimBackground = false, bool withBox = true) : base(withBox) { RelativeSizeAxes = Axes.Both; Size = new Vector2(1); - this.dimTarget = dimTarget; - MainContents.RelativeSizeAxes = Axes.None; + + if (dimBackground) + { + AddInternal(backgroundDimLayer = new Box + { + Depth = float.MaxValue, + Colour = Color4.Black, + Alpha = 0, + RelativeSizeAxes = Axes.Both, + }); + } } public override bool HandleNonPositionalInput => false; @@ -56,19 +66,20 @@ namespace osu.Game.Graphics.UserInterface protected override void PopIn() { - dimTarget?.FadeColour(OsuColour.Gray(0.5f), TRANSITION_DURATION, Easing.OutQuint); + backgroundDimLayer?.FadeTo(0.5f, TRANSITION_DURATION * 2, Easing.OutQuint); base.PopIn(); } protected override void PopOut() { - dimTarget?.FadeColour(Color4.White, TRANSITION_DURATION, Easing.OutQuint); + backgroundDimLayer?.FadeOut(TRANSITION_DURATION, Easing.OutQuint); base.PopOut(); } protected override void Update() { base.Update(); + MainContents.Size = new Vector2(Math.Clamp(Math.Min(DrawWidth, DrawHeight) * 0.25f, 30, 100)); } @@ -79,7 +90,7 @@ namespace osu.Game.Graphics.UserInterface if (State.Value == Visibility.Visible) { // ensure we don't leave the target in a bad state. - dimTarget?.FadeColour(Color4.White, TRANSITION_DURATION, Easing.OutQuint); + // dimTarget?.FadeColour(Color4.White, TRANSITION_DURATION, Easing.OutQuint); } } } diff --git a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs index a0b1b27ebf..0d5538155a 100644 --- a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs +++ b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs @@ -124,7 +124,7 @@ namespace osu.Game.Overlays.AccountCreation }, }, }, - loadingLayer = new LoadingLayer(mainContent) + loadingLayer = new LoadingLayer(true) }; textboxes = new[] { usernameTextBox, emailTextBox, passwordTextBox }; diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index 1e29e713af..0c9c995dd6 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -92,14 +92,14 @@ namespace osu.Game.Overlays { foundContent = new FillFlowContainer(), notFoundContent = new NotFoundDrawable(), - loadingLayer = new LoadingLayer(panelTarget) } } - } + }, }, } - } - } + }, + }, + loadingLayer = new LoadingLayer(true) }; } diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs index c983b337b5..7ad6906cea 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs @@ -53,7 +53,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons Size = new Vector2(18), Shadow = false, }, - loading = new LoadingLayer(icon, false), + loading = new LoadingLayer(true, false), }); Action = () => diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index 9a2dcd014a..b598b7d97f 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -157,11 +157,11 @@ namespace osu.Game.Overlays.BeatmapSet.Scores } } }, - loading = new LoadingLayer() } } - } - } + }, + }, + loading = new LoadingLayer() }); } @@ -228,7 +228,9 @@ namespace osu.Game.Overlays.BeatmapSet.Scores { Scores = null; notSupporterPlaceholder.Show(); + loading.Hide(); + loading.FinishTransforms(); return; } @@ -241,6 +243,8 @@ namespace osu.Game.Overlays.BeatmapSet.Scores getScoresRequest.Success += scores => { loading.Hide(); + loading.FinishTransforms(); + Scores = scores; if (!scores.Scores.Any()) diff --git a/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs b/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs index cc26a11da1..e6fe6ac749 100644 --- a/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs +++ b/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs @@ -128,7 +128,7 @@ namespace osu.Game.Overlays.Dashboard.Friends AutoSizeAxes = Axes.Y, Padding = new MarginPadding { Horizontal = 50 } }, - loading = new LoadingLayer(itemsPlaceholder) + loading = new LoadingLayer(true) } } } diff --git a/osu.Game/Overlays/DashboardOverlay.cs b/osu.Game/Overlays/DashboardOverlay.cs index 04defce636..03c320debe 100644 --- a/osu.Game/Overlays/DashboardOverlay.cs +++ b/osu.Game/Overlays/DashboardOverlay.cs @@ -68,7 +68,7 @@ namespace osu.Game.Overlays } } }, - loading = new LoadingLayer(content), + loading = new LoadingLayer(true), }; } diff --git a/osu.Game/Overlays/NewsOverlay.cs b/osu.Game/Overlays/NewsOverlay.cs index c8c1db012f..5820d405d4 100644 --- a/osu.Game/Overlays/NewsOverlay.cs +++ b/osu.Game/Overlays/NewsOverlay.cs @@ -59,7 +59,7 @@ namespace osu.Game.Overlays }, }, }, - loading = new LoadingLayer(content), + loading = new LoadingLayer(true), }; } diff --git a/osu.Game/Overlays/Rankings/SpotlightsLayout.cs b/osu.Game/Overlays/Rankings/SpotlightsLayout.cs index 61339df76f..b16e0a4908 100644 --- a/osu.Game/Overlays/Rankings/SpotlightsLayout.cs +++ b/osu.Game/Overlays/Rankings/SpotlightsLayout.cs @@ -45,6 +45,7 @@ namespace osu.Game.Overlays.Rankings { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; + InternalChild = new ReverseChildIDFillFlowContainer { RelativeSizeAxes = Axes.X, @@ -68,7 +69,7 @@ namespace osu.Game.Overlays.Rankings AutoSizeAxes = Axes.Y, Margin = new MarginPadding { Vertical = 10 } }, - loading = new LoadingLayer(content) + loading = new LoadingLayer(true) } } } diff --git a/osu.Game/Overlays/RankingsOverlay.cs b/osu.Game/Overlays/RankingsOverlay.cs index ae6d49960a..25350e310a 100644 --- a/osu.Game/Overlays/RankingsOverlay.cs +++ b/osu.Game/Overlays/RankingsOverlay.cs @@ -42,6 +42,8 @@ namespace osu.Game.Overlays Depth = -float.MaxValue }) { + loading = new LoadingLayer(true); + Children = new Drawable[] { background = new Box @@ -74,12 +76,12 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.X, Margin = new MarginPadding { Bottom = 10 } }, - loading = new LoadingLayer(contentContainer), } } } } - } + }, + loading }; } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 79f5dfdee1..0f06188dc2 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -65,7 +65,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge Padding = new MarginPadding(10), Child = roomsContainer = new RoomsContainer { JoinRequested = joinRequested } }, - loadingLayer = new LoadingLayer(roomsContainer), + loadingLayer = new LoadingLayer(true), } }, new RoomInspector diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index ae03d384f6..67c6aa7add 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -71,201 +71,192 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match [BackgroundDependencyLoader] private void load(OsuColour colours) { - Container dimContent; - InternalChildren = new Drawable[] { - dimContent = new Container + new Box { RelativeSizeAxes = Axes.Both, - Children = new Drawable[] + Colour = Color4Extensions.FromHex(@"28242d"), + }, + new GridContainer + { + RelativeSizeAxes = Axes.Both, + RowDimensions = new[] { - new Box + new Dimension(GridSizeMode.Distributed), + new Dimension(GridSizeMode.AutoSize), + }, + Content = new[] + { + new Drawable[] { - RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex(@"28242d"), - }, - new GridContainer - { - RelativeSizeAxes = Axes.Both, - RowDimensions = new[] + new OsuScrollContainer { - new Dimension(GridSizeMode.Distributed), - new Dimension(GridSizeMode.AutoSize), - }, - Content = new[] - { - new Drawable[] + Padding = new MarginPadding { - new OsuScrollContainer + Horizontal = OsuScreen.HORIZONTAL_OVERFLOW_PADDING, + Vertical = 10 + }, + RelativeSizeAxes = Axes.Both, + Children = new[] + { + new FillFlowContainer { - Padding = new MarginPadding + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 10), + Children = new Drawable[] { - Horizontal = OsuScreen.HORIZONTAL_OVERFLOW_PADDING, - Vertical = 10 - }, - RelativeSizeAxes = Axes.Both, - Children = new[] - { - new FillFlowContainer + new Container { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Padding = new MarginPadding { Horizontal = WaveOverlayContainer.WIDTH_PADDING }, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 10), Children = new Drawable[] { - new Container + new SectionContainer { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Padding = new MarginPadding { Horizontal = WaveOverlayContainer.WIDTH_PADDING }, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] + Padding = new MarginPadding { Right = FIELD_PADDING / 2 }, + Children = new[] { - new SectionContainer + new Section("Room name") { - Padding = new MarginPadding { Right = FIELD_PADDING / 2 }, - Children = new[] + Child = NameField = new SettingsTextBox { - new Section("Room name") + RelativeSizeAxes = Axes.X, + TabbableContentContainer = this, + }, + }, + new Section("Room visibility") + { + Alpha = disabled_alpha, + Child = AvailabilityPicker = new RoomAvailabilityPicker + { + Enabled = { Value = false } + }, + }, + new Section("Game type") + { + Alpha = disabled_alpha, + Child = new FillFlowContainer + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Direction = FillDirection.Vertical, + Spacing = new Vector2(7), + Children = new Drawable[] { - Child = NameField = new SettingsTextBox + TypePicker = new GameTypePicker { RelativeSizeAxes = Axes.X, - TabbableContentContainer = this, - }, - }, - new Section("Room visibility") - { - Alpha = disabled_alpha, - Child = AvailabilityPicker = new RoomAvailabilityPicker - { Enabled = { Value = false } }, - }, - new Section("Game type") - { - Alpha = disabled_alpha, - Child = new FillFlowContainer + typeLabel = new OsuSpriteText { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Direction = FillDirection.Vertical, - Spacing = new Vector2(7), - Children = new Drawable[] - { - TypePicker = new GameTypePicker - { - RelativeSizeAxes = Axes.X, - Enabled = { Value = false } - }, - typeLabel = new OsuSpriteText - { - Font = OsuFont.GetFont(size: 14), - Colour = colours.Yellow - }, - }, + Font = OsuFont.GetFont(size: 14), + Colour = colours.Yellow }, }, }, }, - new SectionContainer - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Padding = new MarginPadding { Left = FIELD_PADDING / 2 }, - Children = new[] - { - new Section("Max participants") - { - Alpha = disabled_alpha, - Child = MaxParticipantsField = new SettingsNumberTextBox - { - RelativeSizeAxes = Axes.X, - TabbableContentContainer = this, - ReadOnly = true, - }, - }, - new Section("Password (optional)") - { - Alpha = disabled_alpha, - Child = new SettingsPasswordTextBox - { - RelativeSizeAxes = Axes.X, - TabbableContentContainer = this, - ReadOnly = true, - }, - }, - } - } }, }, - initialBeatmapControl = new BeatmapSelectionControl + new SectionContainer { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - RelativeSizeAxes = Axes.X, - Width = 0.5f + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Padding = new MarginPadding { Left = FIELD_PADDING / 2 }, + Children = new[] + { + new Section("Max participants") + { + Alpha = disabled_alpha, + Child = MaxParticipantsField = new SettingsNumberTextBox + { + RelativeSizeAxes = Axes.X, + TabbableContentContainer = this, + ReadOnly = true, + }, + }, + new Section("Password (optional)") + { + Alpha = disabled_alpha, + Child = new SettingsPasswordTextBox + { + RelativeSizeAxes = Axes.X, + TabbableContentContainer = this, + ReadOnly = true, + }, + }, + } } - } + }, + }, + initialBeatmapControl = new BeatmapSelectionControl + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.X, + Width = 0.5f } - }, - }, + } + } }, - new Drawable[] + }, + }, + new Drawable[] + { + new Container + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Y = 2, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] { - new Container + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4Extensions.FromHex(@"28242d").Darken(0.5f).Opacity(1f), + }, + new FillFlowContainer { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Y = 2, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 20), + Margin = new MarginPadding { Vertical = 20 }, + Padding = new MarginPadding { Horizontal = OsuScreen.HORIZONTAL_OVERFLOW_PADDING }, Children = new Drawable[] { - new Box + ApplyButton = new CreateOrUpdateButton { - RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex(@"28242d").Darken(0.5f).Opacity(1f), + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Size = new Vector2(230, 55), + Enabled = { Value = false }, + Action = apply, }, - new FillFlowContainer + ErrorText = new OsuSpriteText { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 20), - Margin = new MarginPadding { Vertical = 20 }, - Padding = new MarginPadding { Horizontal = OsuScreen.HORIZONTAL_OVERFLOW_PADDING }, - Children = new Drawable[] - { - ApplyButton = new CreateOrUpdateButton - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Size = new Vector2(230, 55), - Enabled = { Value = false }, - Action = apply, - }, - ErrorText = new OsuSpriteText - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Alpha = 0, - Depth = 1, - Colour = colours.RedDark - } - } + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Alpha = 0, + Depth = 1, + Colour = colours.RedDark } } } } } - }, + } } }, - loadingLayer = new LoadingLayer(dimContent) + loadingLayer = new LoadingLayer(true) }; TypePicker.Current.BindValueChanged(type => typeLabel.Text = type.NewValue?.Name ?? string.Empty, true); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs index 72539a2e3a..36dbb9e792 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs @@ -47,7 +47,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [BackgroundDependencyLoader] private void load() { - AddInternal(loadingLayer = new LoadingLayer(Carousel)); + AddInternal(loadingLayer = new LoadingLayer(true)); initialBeatmap = Beatmap.Value; initialRuleset = Ruleset.Value; initialMods = Mods.Value.ToList(); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index 4247e954bd..4bee502e2e 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -62,7 +62,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer // todo: this should be implemented via a custom HUD implementation, and correctly masked to the main content area. LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(ScoreProcessor, userIds), HUDOverlay.Add); - HUDOverlay.Add(loadingDisplay = new LoadingLayer(DrawableRuleset) { Depth = float.MaxValue }); + HUDOverlay.Add(loadingDisplay = new LoadingLayer(true) { Depth = float.MaxValue }); if (Token == null) return; // Todo: Somehow handle token retrieval failure. diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs index 6b92526f35..01f9920609 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs @@ -64,243 +64,234 @@ namespace osu.Game.Screens.OnlinePlay.Playlists [BackgroundDependencyLoader] private void load(OsuColour colours) { - Container dimContent; - InternalChildren = new Drawable[] { - dimContent = new Container + new Box { RelativeSizeAxes = Axes.Both, - Children = new Drawable[] + Colour = Color4Extensions.FromHex(@"28242d"), + }, + new GridContainer + { + RelativeSizeAxes = Axes.Both, + RowDimensions = new[] { - new Box + new Dimension(GridSizeMode.Distributed), + new Dimension(GridSizeMode.AutoSize), + }, + Content = new[] + { + new Drawable[] { - RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex(@"28242d"), - }, - new GridContainer - { - RelativeSizeAxes = Axes.Both, - RowDimensions = new[] + new OsuScrollContainer { - new Dimension(GridSizeMode.Distributed), - new Dimension(GridSizeMode.AutoSize), - }, - Content = new[] - { - new Drawable[] + Padding = new MarginPadding { - new OsuScrollContainer - { - Padding = new MarginPadding - { - Horizontal = OsuScreen.HORIZONTAL_OVERFLOW_PADDING, - Vertical = 10 - }, - RelativeSizeAxes = Axes.Both, - Children = new[] - { - new Container - { - Padding = new MarginPadding { Horizontal = WaveOverlayContainer.WIDTH_PADDING }, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] - { - new SectionContainer - { - Padding = new MarginPadding { Right = FIELD_PADDING / 2 }, - Children = new[] - { - new Section("Room name") - { - Child = NameField = new SettingsTextBox - { - RelativeSizeAxes = Axes.X, - TabbableContentContainer = this, - LengthLimit = 100 - }, - }, - new Section("Duration") - { - Child = DurationField = new DurationDropdown - { - RelativeSizeAxes = Axes.X, - Items = new[] - { - TimeSpan.FromMinutes(30), - TimeSpan.FromHours(1), - TimeSpan.FromHours(2), - TimeSpan.FromHours(4), - TimeSpan.FromHours(8), - TimeSpan.FromHours(12), - //TimeSpan.FromHours(16), - TimeSpan.FromHours(24), - TimeSpan.FromDays(3), - TimeSpan.FromDays(7) - } - } - }, - new Section("Room visibility") - { - Alpha = disabled_alpha, - Child = AvailabilityPicker = new RoomAvailabilityPicker - { - Enabled = { Value = false } - }, - }, - new Section("Game type") - { - Alpha = disabled_alpha, - Child = new FillFlowContainer - { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Direction = FillDirection.Vertical, - Spacing = new Vector2(7), - Children = new Drawable[] - { - TypePicker = new GameTypePicker - { - RelativeSizeAxes = Axes.X, - Enabled = { Value = false } - }, - typeLabel = new OsuSpriteText - { - Font = OsuFont.GetFont(size: 14), - Colour = colours.Yellow - }, - }, - }, - }, - new Section("Max participants") - { - Alpha = disabled_alpha, - Child = MaxParticipantsField = new SettingsNumberTextBox - { - RelativeSizeAxes = Axes.X, - TabbableContentContainer = this, - ReadOnly = true, - }, - }, - new Section("Password (optional)") - { - Alpha = disabled_alpha, - Child = new SettingsPasswordTextBox - { - RelativeSizeAxes = Axes.X, - TabbableContentContainer = this, - ReadOnly = true, - }, - }, - }, - }, - new SectionContainer - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Padding = new MarginPadding { Left = FIELD_PADDING / 2 }, - Children = new[] - { - new Section("Playlist") - { - Child = new GridContainer - { - RelativeSizeAxes = Axes.X, - Height = 300, - Content = new[] - { - new Drawable[] - { - playlist = new DrawableRoomPlaylist(true, true) { RelativeSizeAxes = Axes.Both } - }, - new Drawable[] - { - playlistLength = new OsuSpriteText - { - Margin = new MarginPadding { Vertical = 5 }, - Colour = colours.Yellow, - Font = OsuFont.GetFont(size: 12), - } - }, - new Drawable[] - { - new PurpleTriangleButton - { - RelativeSizeAxes = Axes.X, - Height = 40, - Text = "Edit playlist", - Action = () => EditPlaylist?.Invoke() - } - } - }, - RowDimensions = new[] - { - new Dimension(), - new Dimension(GridSizeMode.AutoSize), - new Dimension(GridSizeMode.AutoSize), - } - } - }, - }, - }, - }, - } - }, - }, + Horizontal = OsuScreen.HORIZONTAL_OVERFLOW_PADDING, + Vertical = 10 }, - new Drawable[] + RelativeSizeAxes = Axes.Both, + Children = new[] { new Container { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Y = 2, + Padding = new MarginPadding { Horizontal = WaveOverlayContainer.WIDTH_PADDING }, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Children = new Drawable[] { - new Box + new SectionContainer { - RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex(@"28242d").Darken(0.5f).Opacity(1f), - }, - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 20), - Margin = new MarginPadding { Vertical = 20 }, - Padding = new MarginPadding { Horizontal = OsuScreen.HORIZONTAL_OVERFLOW_PADDING }, - Children = new Drawable[] + Padding = new MarginPadding { Right = FIELD_PADDING / 2 }, + Children = new[] { - ApplyButton = new CreateRoomButton + new Section("Room name") { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Size = new Vector2(230, 55), - Enabled = { Value = false }, - Action = apply, + Child = NameField = new SettingsTextBox + { + RelativeSizeAxes = Axes.X, + TabbableContentContainer = this, + LengthLimit = 100 + }, }, - ErrorText = new OsuSpriteText + new Section("Duration") { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Alpha = 0, - Depth = 1, - Colour = colours.RedDark - } - } + Child = DurationField = new DurationDropdown + { + RelativeSizeAxes = Axes.X, + Items = new[] + { + TimeSpan.FromMinutes(30), + TimeSpan.FromHours(1), + TimeSpan.FromHours(2), + TimeSpan.FromHours(4), + TimeSpan.FromHours(8), + TimeSpan.FromHours(12), + //TimeSpan.FromHours(16), + TimeSpan.FromHours(24), + TimeSpan.FromDays(3), + TimeSpan.FromDays(7) + } + } + }, + new Section("Room visibility") + { + Alpha = disabled_alpha, + Child = AvailabilityPicker = new RoomAvailabilityPicker + { + Enabled = { Value = false } + }, + }, + new Section("Game type") + { + Alpha = disabled_alpha, + Child = new FillFlowContainer + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Direction = FillDirection.Vertical, + Spacing = new Vector2(7), + Children = new Drawable[] + { + TypePicker = new GameTypePicker + { + RelativeSizeAxes = Axes.X, + Enabled = { Value = false } + }, + typeLabel = new OsuSpriteText + { + Font = OsuFont.GetFont(size: 14), + Colour = colours.Yellow + }, + }, + }, + }, + new Section("Max participants") + { + Alpha = disabled_alpha, + Child = MaxParticipantsField = new SettingsNumberTextBox + { + RelativeSizeAxes = Axes.X, + TabbableContentContainer = this, + ReadOnly = true, + }, + }, + new Section("Password (optional)") + { + Alpha = disabled_alpha, + Child = new SettingsPasswordTextBox + { + RelativeSizeAxes = Axes.X, + TabbableContentContainer = this, + ReadOnly = true, + }, + }, + }, + }, + new SectionContainer + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Padding = new MarginPadding { Left = FIELD_PADDING / 2 }, + Children = new[] + { + new Section("Playlist") + { + Child = new GridContainer + { + RelativeSizeAxes = Axes.X, + Height = 300, + Content = new[] + { + new Drawable[] + { + playlist = new DrawableRoomPlaylist(true, true) { RelativeSizeAxes = Axes.Both } + }, + new Drawable[] + { + playlistLength = new OsuSpriteText + { + Margin = new MarginPadding { Vertical = 5 }, + Colour = colours.Yellow, + Font = OsuFont.GetFont(size: 12), + } + }, + new Drawable[] + { + new PurpleTriangleButton + { + RelativeSizeAxes = Axes.X, + Height = 40, + Text = "Edit playlist", + Action = () => EditPlaylist?.Invoke() + } + } + }, + RowDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.AutoSize), + } + } + }, + }, + }, + }, + } + }, + }, + }, + new Drawable[] + { + new Container + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Y = 2, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4Extensions.FromHex(@"28242d").Darken(0.5f).Opacity(1f), + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 20), + Margin = new MarginPadding { Vertical = 20 }, + Padding = new MarginPadding { Horizontal = OsuScreen.HORIZONTAL_OVERFLOW_PADDING }, + Children = new Drawable[] + { + ApplyButton = new CreateRoomButton + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Size = new Vector2(230, 55), + Enabled = { Value = false }, + Action = apply, + }, + ErrorText = new OsuSpriteText + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Alpha = 0, + Depth = 1, + Colour = colours.RedDark } } } } } - }, + } } }, - loadingLayer = new LoadingLayer(dimContent) + loadingLayer = new LoadingLayer(true) }; TypePicker.Current.BindValueChanged(type => typeLabel.Text = type.NewValue?.Name ?? string.Empty, true); diff --git a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs index 5530b4beac..00176e9127 100644 --- a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs +++ b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs @@ -131,7 +131,7 @@ namespace osu.Game.Screens.Play Anchor = Anchor.Centre, FillMode = FillMode.Fill, }, - loading = new LoadingLayer(backgroundSprite) + loading = new LoadingLayer(true) } }, new OsuSpriteText diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index 71f78c5c95..8a1c291fca 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -63,8 +63,6 @@ namespace osu.Game.Screens.Select public BeatmapDetails() { - Container content; - Children = new Drawable[] { new Box @@ -72,7 +70,7 @@ namespace osu.Game.Screens.Select RelativeSizeAxes = Axes.Both, Colour = Color4.Black.Opacity(0.5f), }, - content = new Container + new Container { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Horizontal = spacing }, @@ -159,7 +157,7 @@ namespace osu.Game.Screens.Select }, }, }, - loading = new LoadingLayer(content), + loading = new LoadingLayer(true), }; } From 0b1ee2e267f3daea22a437d445edf4336bd048f2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Jan 2021 17:42:19 +0900 Subject: [PATCH 739/818] Remove unused dispose logic --- osu.Game/Graphics/UserInterface/LoadingLayer.cs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/LoadingLayer.cs b/osu.Game/Graphics/UserInterface/LoadingLayer.cs index 6aca30328a..a4905156a9 100644 --- a/osu.Game/Graphics/UserInterface/LoadingLayer.cs +++ b/osu.Game/Graphics/UserInterface/LoadingLayer.cs @@ -3,7 +3,6 @@ using System; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osuTK; @@ -82,16 +81,5 @@ namespace osu.Game.Graphics.UserInterface MainContents.Size = new Vector2(Math.Clamp(Math.Min(DrawWidth, DrawHeight) * 0.25f, 30, 100)); } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - if (State.Value == Visibility.Visible) - { - // ensure we don't leave the target in a bad state. - // dimTarget?.FadeColour(Color4.White, TRANSITION_DURATION, Easing.OutQuint); - } - } } } From 0639429a23bb706d84b56dea98b7f23d3f4563b3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Jan 2021 18:10:39 +0900 Subject: [PATCH 740/818] Fix test (and remove no longer valid test) --- .../UserInterface/TestSceneLoadingLayer.cs | 36 ++++++++----------- .../Graphics/UserInterface/LoadingLayer.cs | 8 ++--- 2 files changed, 19 insertions(+), 25 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneLoadingLayer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneLoadingLayer.cs index a694595115..d426723f0b 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneLoadingLayer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneLoadingLayer.cs @@ -1,11 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Utils; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osuTK; @@ -15,12 +15,10 @@ namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneLoadingLayer : OsuTestScene { - private LoadingLayer overlay; + private TestLoadingLayer overlay; private Container content; - private Drawable dimContent => overlay.Children.OfType().First(); - [SetUp] public void SetUp() => Schedule(() => { @@ -53,7 +51,7 @@ namespace osu.Game.Tests.Visual.UserInterface new TriangleButton { Text = "puush me", Width = 200, Action = () => { } }, } }, - overlay = new LoadingLayer(true), + overlay = new TestLoadingLayer(true), } }, }; @@ -66,25 +64,11 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("show", () => overlay.Show()); - AddUntilStep("wait for content dim", () => dimContent.Colour != Color4.White); + AddUntilStep("wait for content dim", () => overlay.BackgroundDimLayer.Alpha > 0); AddStep("hide", () => overlay.Hide()); - AddUntilStep("wait for content restore", () => dimContent.Colour == Color4.White); - } - - [Test] - public void TestContentRestoreOnDispose() - { - AddAssert("not visible", () => !overlay.IsPresent); - - AddStep("show", () => overlay.Show()); - - AddUntilStep("wait for content dim", () => dimContent.Colour != Color4.White); - - AddStep("expire", () => overlay.Expire()); - - AddUntilStep("wait for content restore", () => dimContent.Colour == Color4.White); + AddUntilStep("wait for content restore", () => Precision.AlmostEquals(overlay.BackgroundDimLayer.Alpha, 0)); } [Test] @@ -100,5 +84,15 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("hide", () => overlay.Hide()); } + + private class TestLoadingLayer : LoadingLayer + { + public new Box BackgroundDimLayer => base.BackgroundDimLayer; + + public TestLoadingLayer(bool dimBackground = false, bool withBox = true) + : base(dimBackground, withBox) + { + } + } } } diff --git a/osu.Game/Graphics/UserInterface/LoadingLayer.cs b/osu.Game/Graphics/UserInterface/LoadingLayer.cs index a4905156a9..3e3fd32d65 100644 --- a/osu.Game/Graphics/UserInterface/LoadingLayer.cs +++ b/osu.Game/Graphics/UserInterface/LoadingLayer.cs @@ -17,7 +17,7 @@ namespace osu.Game.Graphics.UserInterface /// public class LoadingLayer : LoadingSpinner { - private readonly Box backgroundDimLayer; + protected Box BackgroundDimLayer { get; private set; } /// /// Construct a new loading spinner. @@ -34,7 +34,7 @@ namespace osu.Game.Graphics.UserInterface if (dimBackground) { - AddInternal(backgroundDimLayer = new Box + AddInternal(BackgroundDimLayer = new Box { Depth = float.MaxValue, Colour = Color4.Black, @@ -65,13 +65,13 @@ namespace osu.Game.Graphics.UserInterface protected override void PopIn() { - backgroundDimLayer?.FadeTo(0.5f, TRANSITION_DURATION * 2, Easing.OutQuint); + BackgroundDimLayer?.FadeTo(0.5f, TRANSITION_DURATION * 2, Easing.OutQuint); base.PopIn(); } protected override void PopOut() { - backgroundDimLayer?.FadeOut(TRANSITION_DURATION, Easing.OutQuint); + BackgroundDimLayer?.FadeOut(TRANSITION_DURATION, Easing.OutQuint); base.PopOut(); } From d0d2e41b28df281507e16d5de19f4ec0c5e8a8b7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Jan 2021 18:19:28 +0900 Subject: [PATCH 741/818] Fix display settings binding to configuration bindables in async load --- .../Settings/Sections/Graphics/LayoutSettings.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 3d3b543d70..e815e2f68b 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -133,6 +133,15 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics }, }; + scalingSettings.ForEach(s => bindPreviewEvent(s.Current)); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + windowModeDropdown.Current.ValueChanged += _ => updateResolutionDropdown(); + windowModes.BindCollectionChanged((sender, args) => { if (windowModes.Count > 1) @@ -141,8 +150,6 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics windowModeDropdown.Hide(); }, true); - windowModeDropdown.Current.ValueChanged += _ => updateResolutionDropdown(); - currentDisplay.BindValueChanged(display => Schedule(() => { resolutions.RemoveRange(1, resolutions.Count - 1); @@ -159,8 +166,6 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics updateResolutionDropdown(); }), true); - scalingSettings.ForEach(s => bindPreviewEvent(s.Current)); - scalingMode.BindValueChanged(mode => { scalingSettings.ClearTransforms(); From 83dbba3cbfb3a02297ae8bc801a11bf2028f3cb6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Jan 2021 18:41:45 +0900 Subject: [PATCH 742/818] Fix carousel beatmap set panels applying transforms to difficulties while they are loading --- .../Carousel/DrawableCarouselBeatmapSet.cs | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index b3c5d458d6..17fa66447d 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -40,6 +41,8 @@ namespace osu.Game.Screens.Select.Carousel private BeatmapSetInfo beatmapSet; + private Task beatmapsLoadTask; + [Resolved] private BeatmapManager manager { get; set; } @@ -85,7 +88,9 @@ namespace osu.Game.Screens.Select.Carousel base.UpdateItem(); Content.Clear(); + beatmapContainer = null; + beatmapsLoadTask = null; if (Item == null) return; @@ -122,11 +127,7 @@ namespace osu.Game.Screens.Select.Carousel MovementContainer.MoveToX(0, 500, Easing.OutExpo); - if (beatmapContainer != null) - { - foreach (var beatmap in beatmapContainer) - beatmap.MoveToY(0, 800, Easing.OutQuint); - } + updateBeatmapYPositions(); } protected override void Selected() @@ -163,7 +164,7 @@ namespace osu.Game.Screens.Select.Carousel ChildrenEnumerable = visibleBeatmaps.Select(c => c.CreateDrawableRepresentation()) }; - LoadComponentAsync(beatmapContainer, loaded => + beatmapsLoadTask = LoadComponentAsync(beatmapContainer, loaded => { // make sure the pooled target hasn't changed. if (beatmapContainer != loaded) @@ -173,16 +174,26 @@ namespace osu.Game.Screens.Select.Carousel updateBeatmapYPositions(); }); } + } - void updateBeatmapYPositions() + private void updateBeatmapYPositions() + { + if (beatmapsLoadTask == null || !beatmapsLoadTask.IsCompleted) + return; + + float yPos = DrawableCarouselBeatmap.CAROUSEL_BEATMAP_SPACING; + + bool isSelected = Item.State.Value == CarouselItemState.Selected; + + foreach (var panel in beatmapContainer.Children) { - float yPos = DrawableCarouselBeatmap.CAROUSEL_BEATMAP_SPACING; - - foreach (var panel in beatmapContainer.Children) + if (isSelected) { panel.MoveToY(yPos, 800, Easing.OutQuint); yPos += panel.Item.TotalHeight; } + else + panel.MoveToY(0, 800, Easing.OutQuint); } } From 4b539b01c1862bfb0a29ad69dc6b400f3719e99b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Jan 2021 20:38:58 +0900 Subject: [PATCH 743/818] Match code between updateSelectedBeatmap/Ruleset --- osu.Game/Screens/Select/SongSelect.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index e3036c662b..7e217ca7a4 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -435,13 +435,15 @@ namespace osu.Game.Screens.Select return; beatmapNoDebounce = beatmap; - performUpdateSelected(); } private void updateSelectedRuleset(RulesetInfo ruleset) { - if (ruleset == null || ruleset.Equals(rulesetNoDebounce)) + if (ruleset == null && rulesetNoDebounce == null) + return; + + if (ruleset?.Equals(rulesetNoDebounce) == true) return; rulesetNoDebounce = ruleset; From 2b253f6d01426901994e95b03ead4233f7cb67dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 5 Jan 2021 22:39:59 +0100 Subject: [PATCH 744/818] Remove now-unused fields & locals --- osu.Game/Overlays/AccountCreation/ScreenEntry.cs | 4 +--- osu.Game/Screens/Play/BeatmapMetadataDisplay.cs | 3 +-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs index 0d5538155a..bcb3d4b635 100644 --- a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs +++ b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs @@ -48,11 +48,9 @@ namespace osu.Game.Overlays.AccountCreation [BackgroundDependencyLoader] private void load(OsuColour colours) { - FillFlowContainer mainContent; - InternalChildren = new Drawable[] { - mainContent = new FillFlowContainer + new FillFlowContainer { RelativeSizeAxes = Axes.Both, Direction = FillDirection.Vertical, diff --git a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs index 00176e9127..b53141e8fb 100644 --- a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs +++ b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs @@ -53,7 +53,6 @@ namespace osu.Game.Screens.Play private readonly Bindable> mods; private readonly Drawable facade; private LoadingSpinner loading; - private Sprite backgroundSprite; public IBindable> Mods => mods; @@ -123,7 +122,7 @@ namespace osu.Game.Screens.Play Masking = true, Children = new Drawable[] { - backgroundSprite = new Sprite + new Sprite { RelativeSizeAxes = Axes.Both, Texture = beatmap?.Background, From ac1d6d444430296d2ab08ac3f5e89f5a965655ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 5 Jan 2021 22:40:16 +0100 Subject: [PATCH 745/818] Make auto-property get-only --- osu.Game/Graphics/UserInterface/LoadingLayer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/LoadingLayer.cs b/osu.Game/Graphics/UserInterface/LoadingLayer.cs index 3e3fd32d65..d86ea9e4ea 100644 --- a/osu.Game/Graphics/UserInterface/LoadingLayer.cs +++ b/osu.Game/Graphics/UserInterface/LoadingLayer.cs @@ -17,7 +17,7 @@ namespace osu.Game.Graphics.UserInterface /// public class LoadingLayer : LoadingSpinner { - protected Box BackgroundDimLayer { get; private set; } + protected Box BackgroundDimLayer { get; } /// /// Construct a new loading spinner. From 0880e76da8d58d02ffe4ad95c173d9373e3d50b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 5 Jan 2021 22:47:57 +0100 Subject: [PATCH 746/818] Mark background dim layer as possibly-null --- osu.Game/Graphics/UserInterface/LoadingLayer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Graphics/UserInterface/LoadingLayer.cs b/osu.Game/Graphics/UserInterface/LoadingLayer.cs index d86ea9e4ea..47ba5fce4d 100644 --- a/osu.Game/Graphics/UserInterface/LoadingLayer.cs +++ b/osu.Game/Graphics/UserInterface/LoadingLayer.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using JetBrains.Annotations; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; @@ -17,6 +18,7 @@ namespace osu.Game.Graphics.UserInterface /// public class LoadingLayer : LoadingSpinner { + [CanBeNull] protected Box BackgroundDimLayer { get; } /// From 15dd7a87a62d73d0ba6e85fea034f1d7cb9656c1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Jan 2021 15:19:12 +0900 Subject: [PATCH 747/818] Move gameplay preview event binding to LoadComplete --- .../Settings/Sections/Graphics/LayoutSettings.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index e815e2f68b..7acbf038d8 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -132,14 +132,14 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics } }, }; - - scalingSettings.ForEach(s => bindPreviewEvent(s.Current)); } protected override void LoadComplete() { base.LoadComplete(); + scalingSettings.ForEach(s => bindPreviewEvent(s.Current)); + windowModeDropdown.Current.ValueChanged += _ => updateResolutionDropdown(); windowModes.BindCollectionChanged((sender, args) => @@ -186,11 +186,6 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics } } - /// - /// Create a delayed bindable which only updates when a condition is met. - /// - /// The config bindable. - /// A bindable which will propagate updates with a delay. private void bindPreviewEvent(Bindable bindable) { bindable.ValueChanged += _ => From 11a0c637bc07812e01125df31251791cf9a24694 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Jan 2021 15:25:53 +0900 Subject: [PATCH 748/818] Mark background properties as nullable --- osu.Game/Screens/OsuScreen.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index c97c0aef2b..d1cdb9f1de 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; @@ -119,8 +120,10 @@ namespace osu.Game.Screens /// /// The background created and owned by this screen. May be null if the background didn't change. /// + [CanBeNull] private BackgroundScreen ownedBackground; + [CanBeNull] private BackgroundScreen background; [Resolved(canBeNull: true)] From e9d4e4d1d5bb5c6df13474c867c81b4d516cd61d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Jan 2021 15:26:13 +0900 Subject: [PATCH 749/818] Add xmldoc and throw a local exception on null background --- osu.Game/Screens/BackgroundScreen.cs | 4 ++++ osu.Game/Screens/OsuScreen.cs | 14 ++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/BackgroundScreen.cs b/osu.Game/Screens/BackgroundScreen.cs index ea220c2f82..c81362eebe 100644 --- a/osu.Game/Screens/BackgroundScreen.cs +++ b/osu.Game/Screens/BackgroundScreen.cs @@ -34,6 +34,10 @@ namespace osu.Game.Screens return false; } + /// + /// Apply arbitrary changes to this background in a thread safe manner. + /// + /// The operation to perform. public void ApplyToBackground(Action action) => Schedule(() => action.Invoke(this)); protected override void Update() diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index d1cdb9f1de..11467db6c8 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -115,8 +115,6 @@ namespace osu.Game.Screens Mods = screenDependencies.Mods; } - public void ApplyToBackground(Action action) => background.ApplyToBackground(action); - /// /// The background created and owned by this screen. May be null if the background didn't change. /// @@ -148,6 +146,18 @@ namespace osu.Game.Screens Activity.Value ??= InitialActivity; } + /// + /// Apply arbitrary changes to the current background screen in a thread safe manner. + /// + /// The operation to perform. + public void ApplyToBackground(Action action) + { + if (background == null) + throw new InvalidOperationException("Attempted to apply to background before screen is pushed"); + + background.ApplyToBackground(action); + } + public override void OnResuming(IScreen last) { if (PlayResumeSound) From 550ef3f13307f3d8592a0bc1ecd103f67af046e1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Jan 2021 15:28:01 +0900 Subject: [PATCH 750/818] Aggressively dispose ownedBackground if it was not used, because we can --- osu.Game/Screens/OsuScreen.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 11467db6c8..fb26739a44 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -185,6 +185,7 @@ namespace osu.Game.Screens if (background != ownedBackground) { // background may have not been replaced, at which point we don't want to track the background lifetime. + ownedBackground?.Dispose(); ownedBackground = null; } From 07cff7038728545a5ce399c3b727b1d03c68813c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Jan 2021 18:19:03 +0900 Subject: [PATCH 751/818] Add specific messaging for when there's no background stack available --- osu.Game/Screens/OsuScreen.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index fb26739a44..e1a29946f4 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -125,6 +125,7 @@ namespace osu.Game.Screens private BackgroundScreen background; [Resolved(canBeNull: true)] + [CanBeNull] private BackgroundScreenStack backgroundStack { get; set; } [Resolved(canBeNull: true)] @@ -152,8 +153,11 @@ namespace osu.Game.Screens /// The operation to perform. public void ApplyToBackground(Action action) { + if (backgroundStack == null) + throw new InvalidOperationException("Attempted to apply to background without a background stack being available."); + if (background == null) - throw new InvalidOperationException("Attempted to apply to background before screen is pushed"); + throw new InvalidOperationException("Attempted to apply to background before screen is pushed."); background.ApplyToBackground(action); } From 99701a6d9b22e2fbc4864556a14f9f44311b5236 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Jan 2021 21:06:33 +0900 Subject: [PATCH 752/818] Add null check on beatmapContainer for safety --- osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index 17fa66447d..c0415384c8 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -178,6 +178,9 @@ namespace osu.Game.Screens.Select.Carousel private void updateBeatmapYPositions() { + if (beatmapContainer == null) + return; + if (beatmapsLoadTask == null || !beatmapsLoadTask.IsCompleted) return; From 43b9fde45731fdbd5da77f119eaedc5e0afa0f24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 6 Jan 2021 13:15:15 +0100 Subject: [PATCH 753/818] Add some nullability annotations for good measure --- osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index c0415384c8..d7e901b71e 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -37,10 +38,12 @@ namespace osu.Game.Screens.Select.Carousel public IEnumerable DrawableBeatmaps => beatmapContainer?.Children ?? Enumerable.Empty(); + [CanBeNull] private Container beatmapContainer; private BeatmapSetInfo beatmapSet; + [CanBeNull] private Task beatmapsLoadTask; [Resolved] From 32accc8eab227aebcc40cc026897324119c0e589 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Jan 2021 22:56:10 +0900 Subject: [PATCH 754/818] Remove "osu!direct" button --- osu.Game/Input/Bindings/GlobalActionContainer.cs | 2 +- osu.Game/Screens/Menu/ButtonSystem.cs | 1 - osu.Game/Screens/Menu/Disclaimer.cs | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 1270df5374..fa3f70735a 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -112,7 +112,7 @@ namespace osu.Game.Input.Bindings [Description("Toggle settings")] ToggleSettings, - [Description("Toggle osu!direct")] + [Description("Toggle beatmap listing")] ToggleDirect, [Description("Increase volume")] diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index fd4d74dc6b..0c47ef3b1e 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -130,7 +130,6 @@ namespace osu.Game.Screens.Menu buttonsTopLevel.Add(new Button(@"play", @"button-play-select", OsuIcon.Logo, new Color4(102, 68, 204, 255), () => State = ButtonSystemState.Play, WEDGE_WIDTH, Key.P)); buttonsTopLevel.Add(new Button(@"osu!editor", @"button-generic-select", OsuIcon.EditCircle, new Color4(238, 170, 0, 255), () => OnEdit?.Invoke(), 0, Key.E)); - buttonsTopLevel.Add(new Button(@"osu!direct", @"button-direct-select", OsuIcon.ChevronDownCircle, new Color4(165, 204, 0, 255), () => OnBeatmapListing?.Invoke(), 0, Key.D)); if (host.CanExit) buttonsTopLevel.Add(new Button(@"exit", string.Empty, OsuIcon.CrossCircle, new Color4(238, 51, 153, 255), () => OnExit?.Invoke(), 0, Key.Q)); diff --git a/osu.Game/Screens/Menu/Disclaimer.cs b/osu.Game/Screens/Menu/Disclaimer.cs index 46fddabb26..72eb9c7c0c 100644 --- a/osu.Game/Screens/Menu/Disclaimer.cs +++ b/osu.Game/Screens/Menu/Disclaimer.cs @@ -201,7 +201,7 @@ namespace osu.Game.Screens.Menu "New features are coming online every update. Make sure to stay up-to-date!", "If you find the UI too large or small, try adjusting UI scale in settings!", "Try adjusting the \"Screen Scaling\" mode to change your gameplay or UI area, even in fullscreen!", - "For now, osu!direct is available to all users on lazer. You can access it anywhere using Ctrl-D!", + "For now, what used to be \"osu!direct\" is available to all users on lazer. You can access it anywhere using Ctrl-D!", "Seeking in replays is available by dragging on the difficulty bar at the bottom of the screen!", "Multithreading support means that even with low \"FPS\" your input and judgements will be accurate!", "Try scrolling down in the mod select panel to find a bunch of new fun mods!", From 59025e9d50a6866b71f2571919460c94a155222c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Jan 2021 23:09:58 +0900 Subject: [PATCH 755/818] Remove related events --- osu.Game/Screens/Menu/ButtonSystem.cs | 1 - osu.Game/Screens/Menu/MainMenu.cs | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 0c47ef3b1e..8417858878 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -39,7 +39,6 @@ namespace osu.Game.Screens.Menu public Action OnEdit; public Action OnExit; - public Action OnBeatmapListing; public Action OnSolo; public Action OnSettings; public Action OnMultiplayer; diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 97fd58318b..ef45a656fa 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -71,7 +71,7 @@ namespace osu.Game.Screens.Menu private SongTicker songTicker; [BackgroundDependencyLoader(true)] - private void load(BeatmapListingOverlay beatmapListing, SettingsOverlay settings, RankingsOverlay rankings, OsuConfigManager config, SessionStatics statics) + private void load(SettingsOverlay settings, OsuConfigManager config, SessionStatics statics) { holdDelay = config.GetBindable(OsuSetting.UIHoldActivationDelay); loginDisplayed = statics.GetBindable(Static.LoginOverlayDisplayed); @@ -137,7 +137,6 @@ namespace osu.Game.Screens.Menu }; buttons.OnSettings = () => settings?.ToggleVisibility(); - buttons.OnBeatmapListing = () => beatmapListing?.ToggleVisibility(); LoadComponentAsync(background = new BackgroundScreenDefault()); preloadSongSelect(); From 283c69a68f3f4a1a29ea6d676fdaabdad67000aa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Jan 2021 23:12:56 +0900 Subject: [PATCH 756/818] Update enum name in line with changes --- osu.Game/Input/Bindings/GlobalActionContainer.cs | 4 ++-- osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index fa3f70735a..b8c2fa201f 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -34,7 +34,7 @@ namespace osu.Game.Input.Bindings new KeyBinding(new[] { InputKey.Control, InputKey.Alt, InputKey.R }, GlobalAction.ResetInputSettings), new KeyBinding(new[] { InputKey.Control, InputKey.T }, GlobalAction.ToggleToolbar), new KeyBinding(new[] { InputKey.Control, InputKey.O }, GlobalAction.ToggleSettings), - new KeyBinding(new[] { InputKey.Control, InputKey.D }, GlobalAction.ToggleDirect), + new KeyBinding(new[] { InputKey.Control, InputKey.D }, GlobalAction.ToggleBeatmapListing), new KeyBinding(new[] { InputKey.Control, InputKey.N }, GlobalAction.ToggleNotifications), new KeyBinding(InputKey.Escape, GlobalAction.Back), @@ -113,7 +113,7 @@ namespace osu.Game.Input.Bindings ToggleSettings, [Description("Toggle beatmap listing")] - ToggleDirect, + ToggleBeatmapListing, [Description("Increase volume")] IncreaseVolume, diff --git a/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs b/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs index c495d673ce..bfe36a6a0f 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs @@ -13,7 +13,7 @@ namespace osu.Game.Overlays.Toolbar public ToolbarBeatmapListingButton() { - Hotkey = GlobalAction.ToggleDirect; + Hotkey = GlobalAction.ToggleBeatmapListing; } [BackgroundDependencyLoader(true)] From cf3043fc08fa723dde4f6c4a7ea4a060a4e72c50 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Jan 2021 23:20:26 +0900 Subject: [PATCH 757/818] Only show "development build" footer on debug releases --- osu.Desktop/Overlays/VersionManager.cs | 39 ++++++++++++++++---------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs index 8c759f8487..59d0f9635d 100644 --- a/osu.Desktop/Overlays/VersionManager.cs +++ b/osu.Desktop/Overlays/VersionManager.cs @@ -26,9 +26,11 @@ namespace osu.Desktop.Overlays Alpha = 0; + FillFlowContainer mainFill; + Children = new Drawable[] { - new FillFlowContainer + mainFill = new FillFlowContainer { AutoSizeAxes = Axes.Both, Direction = FillDirection.Vertical, @@ -55,23 +57,30 @@ namespace osu.Desktop.Overlays }, } }, - new OsuSpriteText - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Font = OsuFont.Numeric.With(size: 12), - Colour = colours.Yellow, - Text = @"Development Build" - }, - new Sprite - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Texture = textures.Get(@"Menu/dev-build-footer"), - }, } } }; + + if (DebugUtils.IsDebugBuild) + { + mainFill.AddRange(new Drawable[] + { + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Font = OsuFont.Numeric.With(size: 12), + Colour = colours.Yellow, + Text = @"Development Build" + }, + new Sprite + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Texture = textures.Get(@"Menu/dev-build-footer"), + }, + }); + } } protected override void PopIn() From cfbfb8d58bd2768bf83be25c2e2da1cd26a1d4ce Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Jan 2021 23:21:46 +0900 Subject: [PATCH 758/818] Revert "Remove related events" This reverts commit 59025e9d50a6866b71f2571919460c94a155222c. --- osu.Game/Screens/Menu/ButtonSystem.cs | 1 + osu.Game/Screens/Menu/MainMenu.cs | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 8417858878..0c47ef3b1e 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -39,6 +39,7 @@ namespace osu.Game.Screens.Menu public Action OnEdit; public Action OnExit; + public Action OnBeatmapListing; public Action OnSolo; public Action OnSettings; public Action OnMultiplayer; diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index ef45a656fa..97fd58318b 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -71,7 +71,7 @@ namespace osu.Game.Screens.Menu private SongTicker songTicker; [BackgroundDependencyLoader(true)] - private void load(SettingsOverlay settings, OsuConfigManager config, SessionStatics statics) + private void load(BeatmapListingOverlay beatmapListing, SettingsOverlay settings, RankingsOverlay rankings, OsuConfigManager config, SessionStatics statics) { holdDelay = config.GetBindable(OsuSetting.UIHoldActivationDelay); loginDisplayed = statics.GetBindable(Static.LoginOverlayDisplayed); @@ -137,6 +137,7 @@ namespace osu.Game.Screens.Menu }; buttons.OnSettings = () => settings?.ToggleVisibility(); + buttons.OnBeatmapListing = () => beatmapListing?.ToggleVisibility(); LoadComponentAsync(background = new BackgroundScreenDefault()); preloadSongSelect(); From 35be7ec0e1477c9d904538c4dd726e50b6f2e827 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Jan 2021 23:28:14 +0900 Subject: [PATCH 759/818] Add back button but rename to "browse" --- osu.Game/Screens/Menu/ButtonSystem.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 0c47ef3b1e..f400b2114b 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -129,7 +129,8 @@ namespace osu.Game.Screens.Menu buttonsPlay.ForEach(b => b.VisibleState = ButtonSystemState.Play); buttonsTopLevel.Add(new Button(@"play", @"button-play-select", OsuIcon.Logo, new Color4(102, 68, 204, 255), () => State = ButtonSystemState.Play, WEDGE_WIDTH, Key.P)); - buttonsTopLevel.Add(new Button(@"osu!editor", @"button-generic-select", OsuIcon.EditCircle, new Color4(238, 170, 0, 255), () => OnEdit?.Invoke(), 0, Key.E)); + buttonsTopLevel.Add(new Button(@"edit", @"button-generic-select", OsuIcon.EditCircle, new Color4(238, 170, 0, 255), () => OnEdit?.Invoke(), 0, Key.E)); + buttonsTopLevel.Add(new Button(@"browse", @"button-direct-select", OsuIcon.ChevronDownCircle, new Color4(165, 204, 0, 255), () => OnBeatmapListing?.Invoke(), 0, Key.D)); if (host.CanExit) buttonsTopLevel.Add(new Button(@"exit", string.Empty, OsuIcon.CrossCircle, new Color4(238, 51, 153, 255), () => OnExit?.Invoke(), 0, Key.Q)); From d056e6575e1cd711890ffee0592b173b6eccb960 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Jan 2021 23:30:54 +0900 Subject: [PATCH 760/818] Use IsDeployedBuild instead of IsDebugBuild for footer display conditional --- osu.Desktop/Overlays/VersionManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs index 59d0f9635d..e4a3451651 100644 --- a/osu.Desktop/Overlays/VersionManager.cs +++ b/osu.Desktop/Overlays/VersionManager.cs @@ -61,7 +61,7 @@ namespace osu.Desktop.Overlays } }; - if (DebugUtils.IsDebugBuild) + if (!game.IsDeployedBuild) { mainFill.AddRange(new Drawable[] { From a8530fde9d3dea6f4a0064e8371c2debbcdd2471 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Jan 2021 00:05:12 +0900 Subject: [PATCH 761/818] Tidy up variables and spacing --- osu.Desktop/DiscordRichPresence.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/osu.Desktop/DiscordRichPresence.cs b/osu.Desktop/DiscordRichPresence.cs index 172db324cb..63b12fb84b 100644 --- a/osu.Desktop/DiscordRichPresence.cs +++ b/osu.Desktop/DiscordRichPresence.cs @@ -31,7 +31,8 @@ namespace osu.Desktop private readonly IBindable status = new Bindable(); private readonly IBindable activity = new Bindable(); - private readonly Bindable mode = new Bindable(); + + private readonly Bindable privacyMode = new Bindable(); private readonly RichPresence presence = new RichPresence { @@ -53,7 +54,8 @@ namespace osu.Desktop client.OnError += (_, e) => Logger.Log($"An error occurred with Discord RPC Client: {e.Code} {e.Message}", LoggingTarget.Network); - config.BindWith(OsuSetting.DiscordRichPresence, mode); + config.BindWith(OsuSetting.DiscordRichPresence, privacyMode); + (user = provider.LocalUser.GetBoundCopy()).BindValueChanged(u => { status.UnbindBindings(); @@ -66,7 +68,7 @@ namespace osu.Desktop ruleset.BindValueChanged(_ => updateStatus()); status.BindValueChanged(_ => updateStatus()); activity.BindValueChanged(_ => updateStatus()); - mode.BindValueChanged(_ => updateStatus()); + privacyMode.BindValueChanged(_ => updateStatus()); client.Initialize(); } @@ -82,7 +84,7 @@ namespace osu.Desktop if (!client.IsInitialized) return; - if (status.Value is UserStatusOffline || mode.Value == DiscordRichPresenceMode.Off) + if (status.Value is UserStatusOffline || privacyMode.Value == DiscordRichPresenceMode.Off) { client.ClearPresence(); return; @@ -100,7 +102,7 @@ namespace osu.Desktop } // update user information - if (mode.Value == DiscordRichPresenceMode.Limited) + if (privacyMode.Value == DiscordRichPresenceMode.Limited) presence.Assets.LargeImageText = string.Empty; else presence.Assets.LargeImageText = $"{user.Value.Username}" + (user.Value.Statistics?.Ranks.Global > 0 ? $" (rank #{user.Value.Statistics.Ranks.Global:N0})" : string.Empty); @@ -144,7 +146,7 @@ namespace osu.Desktop return edit.Beatmap.ToString(); case UserActivity.InLobby lobby: - return mode.Value == DiscordRichPresenceMode.Limited ? string.Empty : lobby.Room.Name.Value; + return privacyMode.Value == DiscordRichPresenceMode.Limited ? string.Empty : lobby.Room.Name.Value; } return string.Empty; From fb057857e707349ead9bf7aa539d5e61c3a42cbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 6 Jan 2021 17:40:29 +0100 Subject: [PATCH 762/818] Update references to current year --- Directory.Build.props | 4 ++-- LICENCE | 2 +- osu.Desktop/osu.nuspec | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 9ec442aafa..049db816a8 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -40,7 +40,7 @@ https://github.com/ppy/osu Automated release. ppy Pty Ltd - Copyright (c) 2020 ppy Pty Ltd + Copyright (c) 2021 ppy Pty Ltd osu game - \ No newline at end of file + diff --git a/LICENCE b/LICENCE index 2435c23545..b5962ad3b2 100644 --- a/LICENCE +++ b/LICENCE @@ -1,4 +1,4 @@ -Copyright (c) 2020 ppy Pty Ltd . +Copyright (c) 2021 ppy Pty Ltd . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/osu.Desktop/osu.nuspec b/osu.Desktop/osu.nuspec index 2fc6009183..fa182f8e70 100644 --- a/osu.Desktop/osu.nuspec +++ b/osu.Desktop/osu.nuspec @@ -11,7 +11,7 @@ false A free-to-win rhythm game. Rhythm is just a *click* away! testing - Copyright (c) 2020 ppy Pty Ltd + Copyright (c) 2021 ppy Pty Ltd en-AU From 09742998cdeaeaae7fda4c77309e45cd14b69b18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 6 Jan 2021 17:40:46 +0100 Subject: [PATCH 763/818] Fix mistaken obsoletion notice It was added in c9f38f7bb6af116fa7ec813ceb3f100dcad30adb, which specified 2021 in another place (and was committed in October of 2020 anyway). Update the year so that it doesn't get culled prematurely. --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index da6da0ea97..e5eaf5db88 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -750,7 +750,7 @@ namespace osu.Game.Rulesets.Objects.Drawables if (Result.Type != originalType) { Logger.Log($"{GetType().ReadableName()} applied an invalid hit result ({originalType}) when {nameof(HitResult.IgnoreMiss)} or {nameof(HitResult.IgnoreHit)} is expected.\n" - + $"This has been automatically adjusted to {Result.Type}, and support will be removed from 2020-03-28 onwards.", level: LogLevel.Important); + + $"This has been automatically adjusted to {Result.Type}, and support will be removed from 2021-03-28 onwards.", level: LogLevel.Important); } } From 539785e422accca0ccd360b4b02969ccf7a70a24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 6 Jan 2021 17:46:44 +0100 Subject: [PATCH 764/818] Remove obsoleted IHasCurve --- .../Rulesets/Objects/Legacy/ConvertSlider.cs | 5 +- osu.Game/Rulesets/Objects/Types/IHasCurve.cs | 55 ------------------- 2 files changed, 1 insertion(+), 59 deletions(-) delete mode 100644 osu.Game/Rulesets/Objects/Types/IHasCurve.cs diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs index 36b421586e..df569b91c1 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs @@ -10,10 +10,7 @@ using osu.Game.Beatmaps.ControlPoints; namespace osu.Game.Rulesets.Objects.Legacy { - internal abstract class ConvertSlider : ConvertHitObject, IHasPathWithRepeats, IHasLegacyLastTickOffset, -#pragma warning disable 618 - IHasCurve -#pragma warning restore 618 + internal abstract class ConvertSlider : ConvertHitObject, IHasPathWithRepeats, IHasLegacyLastTickOffset { /// /// Scoring distance with a speed-adjusted beat length of 1 second. diff --git a/osu.Game/Rulesets/Objects/Types/IHasCurve.cs b/osu.Game/Rulesets/Objects/Types/IHasCurve.cs deleted file mode 100644 index 26f50ffa31..0000000000 --- a/osu.Game/Rulesets/Objects/Types/IHasCurve.cs +++ /dev/null @@ -1,55 +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 osuTK; - -namespace osu.Game.Rulesets.Objects.Types -{ - [Obsolete("Use IHasPathWithRepeats instead.")] // can be removed 20201126 - public interface IHasCurve : IHasDistance, IHasRepeats - { - /// - /// The curve. - /// - SliderPath Path { get; } - } - -#pragma warning disable 618 - [Obsolete("Use IHasPathWithRepeats instead.")] // can be removed 20201126 - public static class HasCurveExtensions - { - /// - /// Computes the position on the curve relative to how much of the has been completed. - /// - /// The curve. - /// [0, 1] where 0 is the start time of the and 1 is the end time of the . - /// The position on the curve. - public static Vector2 CurvePositionAt(this IHasCurve obj, double progress) - => obj.Path.PositionAt(obj.ProgressAt(progress)); - - /// - /// Computes the progress along the curve relative to how much of the has been completed. - /// - /// The curve. - /// [0, 1] where 0 is the start time of the and 1 is the end time of the . - /// [0, 1] where 0 is the beginning of the curve and 1 is the end of the curve. - public static double ProgressAt(this IHasCurve obj, double progress) - { - double p = progress * obj.SpanCount() % 1; - if (obj.SpanAt(progress) % 2 == 1) - p = 1 - p; - return p; - } - - /// - /// Determines which span of the curve the progress point is on. - /// - /// The curve. - /// [0, 1] where 0 is the beginning of the curve and 1 is the end of the curve. - /// [0, SpanCount) where 0 is the first run. - public static int SpanAt(this IHasCurve obj, double progress) - => (int)(progress * obj.SpanCount()); - } -#pragma warning restore 618 -} From 9cc63e8dce8e65feba008ffb02195928f8ea8a46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 6 Jan 2021 17:48:46 +0100 Subject: [PATCH 765/818] Remove obsoleted IHasEndTime --- .../Rulesets/Objects/Types/IHasDuration.cs | 16 +++--------- .../Rulesets/Objects/Types/IHasEndTime.cs | 26 ------------------- 2 files changed, 3 insertions(+), 39 deletions(-) delete mode 100644 osu.Game/Rulesets/Objects/Types/IHasEndTime.cs diff --git a/osu.Game/Rulesets/Objects/Types/IHasDuration.cs b/osu.Game/Rulesets/Objects/Types/IHasDuration.cs index b558273650..ca734da5ad 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasDuration.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasDuration.cs @@ -6,26 +6,16 @@ namespace osu.Game.Rulesets.Objects.Types /// /// A HitObject that ends at a different time than its start time. /// -#pragma warning disable 618 - public interface IHasDuration : IHasEndTime -#pragma warning restore 618 + public interface IHasDuration { - double IHasEndTime.EndTime - { - get => EndTime; - set => Duration = (Duration - EndTime) + value; - } - - double IHasEndTime.Duration => Duration; - /// /// The time at which the HitObject ends. /// - new double EndTime { get; } + double EndTime { get; } /// /// The duration of the HitObject. /// - new double Duration { get; set; } + double Duration { get; set; } } } diff --git a/osu.Game/Rulesets/Objects/Types/IHasEndTime.cs b/osu.Game/Rulesets/Objects/Types/IHasEndTime.cs deleted file mode 100644 index c3769c5909..0000000000 --- a/osu.Game/Rulesets/Objects/Types/IHasEndTime.cs +++ /dev/null @@ -1,26 +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 Newtonsoft.Json; - -namespace osu.Game.Rulesets.Objects.Types -{ - /// - /// A HitObject that ends at a different time than its start time. - /// - [Obsolete("Use IHasDuration instead.")] // can be removed 20201126 - public interface IHasEndTime - { - /// - /// The time at which the HitObject ends. - /// - [JsonIgnore] - double EndTime { get; set; } - - /// - /// The duration of the HitObject. - /// - double Duration { get; } - } -} From 68352782db59f2db881ba5f443bf352cf8a9c03b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 6 Jan 2021 18:11:47 +0100 Subject: [PATCH 766/818] Change .StartsWith() to .Equals() In line with planned-but-delayed breaking change. --- osu.Game/Rulesets/RulesetStore.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index d422bca087..deabea57ef 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -100,9 +100,7 @@ namespace osu.Game.Rulesets foreach (var r in instances.Where(r => !(r is ILegacyRuleset))) { - // todo: StartsWith can be changed to Equals on 2020-11-08 - // This is to give users enough time to have their database use new abbreviated info). - if (existingRulesets.FirstOrDefault(ri => ri.InstantiationInfo.StartsWith(r.RulesetInfo.InstantiationInfo, StringComparison.Ordinal)) == null) + if (existingRulesets.FirstOrDefault(ri => ri.InstantiationInfo.Equals(r.RulesetInfo.InstantiationInfo, StringComparison.Ordinal)) == null) context.RulesetInfo.Add(r.RulesetInfo); } From 4998aaaa987b75de4d017f09067f44dd05caeec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 6 Jan 2021 18:13:30 +0100 Subject: [PATCH 767/818] Remove outdated warning disable Does not trigger any more on Rider 2020.3.2. --- osu.Game/Screens/Select/BeatmapCarousel.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 36f8fbedb3..7ba6e400bf 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -936,7 +936,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; From 9984c80c87c428316d622523f805a4d7a4a4f196 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 6 Jan 2021 20:40:47 +0100 Subject: [PATCH 768/818] Make useless existing test actually fail --- .../Mods/TestSceneOsuModDifficultyAdjust.cs | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDifficultyAdjust.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDifficultyAdjust.cs index 49c1fe8540..db8546c71b 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDifficultyAdjust.cs @@ -1,13 +1,17 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Framework.Utils; +using osu.Game.Beatmaps; using osu.Game.Graphics.Containers; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; namespace osu.Game.Rulesets.Osu.Tests.Mods @@ -18,8 +22,23 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods public void TestNoAdjustment() => CreateModTest(new ModTestData { Mod = new OsuModDifficultyAdjust(), + Beatmap = new Beatmap + { + BeatmapInfo = new BeatmapInfo + { + BaseDifficulty = new BeatmapDifficulty + { + CircleSize = 8 + } + }, + HitObjects = new List + { + new HitCircle { StartTime = 1000 }, + new HitCircle { StartTime = 2000 } + } + }, Autoplay = true, - PassCondition = checkSomeHit + PassCondition = () => checkSomeHit() && checkObjectsScale(0.29f) }); [Test] From 303cc62ee7c8eec32cb9cd4522202bb3cbdf512d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 6 Jan 2021 21:57:54 +0100 Subject: [PATCH 769/818] Transfer flags indicating if settings were changed --- .../Mods/CatchModDifficultyAdjust.cs | 4 ++-- .../Mods/OsuModDifficultyAdjust.cs | 4 ++-- osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs | 18 +++++++++++++++--- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs b/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs index acdd0a420c..438d17dbc5 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs @@ -59,8 +59,8 @@ namespace osu.Game.Rulesets.Catch.Mods { base.ApplySettings(difficulty); - difficulty.CircleSize = CircleSize.Value; - difficulty.ApproachRate = ApproachRate.Value; + ApplySetting(CircleSize, cs => difficulty.CircleSize = cs); + ApplySetting(ApproachRate, ar => difficulty.ApproachRate = ar); } } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs index ff995e38ce..a638234dbd 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs @@ -59,8 +59,8 @@ namespace osu.Game.Rulesets.Osu.Mods { base.ApplySettings(difficulty); - difficulty.CircleSize = CircleSize.Value; - difficulty.ApproachRate = ApproachRate.Value; + ApplySetting(CircleSize, cs => difficulty.CircleSize = cs); + ApplySetting(ApproachRate, ar => difficulty.ApproachRate = ar); } } } diff --git a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs index 72a4bb297f..a531e885db 100644 --- a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs +++ b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs @@ -116,18 +116,30 @@ namespace osu.Game.Rulesets.Mods internal override void CopyAdjustedSetting(IBindable target, object source) { - userChangedSettings[target] = true; + // if the value is non-bindable, it's presumably coming from an external source (like the API) - therefore presume it is not default. + // if the value is bindable, defer to the source's IsDefault to be able to tell. + userChangedSettings[target] = !(source is IBindable bindableSource) || !bindableSource.IsDefault; base.CopyAdjustedSetting(target, source); } + /// + /// Applies a setting from a configuration bindable using , if it has been changed by the user. + /// + protected void ApplySetting(BindableNumber setting, Action applyFunc) + where T : struct, IComparable, IConvertible, IEquatable + { + if (userChangedSettings.TryGetValue(setting, out bool userChangedSetting) && userChangedSetting) + applyFunc.Invoke(setting.Value); + } + /// /// Apply all custom settings to the provided beatmap. /// /// The beatmap to have settings applied. protected virtual void ApplySettings(BeatmapDifficulty difficulty) { - difficulty.DrainRate = DrainRate.Value; - difficulty.OverallDifficulty = OverallDifficulty.Value; + ApplySetting(DrainRate, dr => difficulty.DrainRate = dr); + ApplySetting(OverallDifficulty, od => difficulty.OverallDifficulty = od); } } } From 6620eadec3f1c7fe5fac7b98d52683d1a05aba4a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Jan 2021 18:47:03 +0900 Subject: [PATCH 770/818] Reduce default hover sound debounce interval --- osu.Game/Graphics/UserInterface/HoverSounds.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/HoverSounds.cs b/osu.Game/Graphics/UserInterface/HoverSounds.cs index 40899e7e95..22d59e70f7 100644 --- a/osu.Game/Graphics/UserInterface/HoverSounds.cs +++ b/osu.Game/Graphics/UserInterface/HoverSounds.cs @@ -24,7 +24,7 @@ namespace osu.Game.Graphics.UserInterface /// /// Length of debounce for hover sound playback, in milliseconds. Default is 50ms. /// - public double HoverDebounceTime { get; } = 50; + public double HoverDebounceTime { get; } = 20; protected readonly HoverSampleSet SampleSet; From 8f52a83b297d277bf07bbda9e9e2efd2d0684092 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Jan 2021 18:47:20 +0900 Subject: [PATCH 771/818] Share hover sound debounce across all instances via SessionStatics --- osu.Game/Configuration/SessionStatics.cs | 9 ++++++ .../Graphics/UserInterface/HoverSounds.cs | 29 +++++++++++-------- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/osu.Game/Configuration/SessionStatics.cs b/osu.Game/Configuration/SessionStatics.cs index 03bc434aac..f28ee707b9 100644 --- a/osu.Game/Configuration/SessionStatics.cs +++ b/osu.Game/Configuration/SessionStatics.cs @@ -1,7 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Game.Graphics.UserInterface; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Overlays; namespace osu.Game.Configuration { @@ -14,6 +16,7 @@ namespace osu.Game.Configuration { Set(Static.LoginOverlayDisplayed, false); Set(Static.MutedAudioNotificationShownOnce, false); + Set(Static.LastHoverSoundPlaybackTime, 0.0); Set(Static.SeasonalBackgrounds, null); } } @@ -28,5 +31,11 @@ namespace osu.Game.Configuration /// Value under this lookup can be null if there are no backgrounds available (or API is not reachable). /// SeasonalBackgrounds, + + /// + /// The last playback time in milliseconds of a hover sample (from ). + /// Used to debounce hover sounds game-wide to avoid volume saturation, especially in scrolling views with many UI controls like . + /// + LastHoverSoundPlaybackTime } } diff --git a/osu.Game/Graphics/UserInterface/HoverSounds.cs b/osu.Game/Graphics/UserInterface/HoverSounds.cs index 22d59e70f7..efd55c892c 100644 --- a/osu.Game/Graphics/UserInterface/HoverSounds.cs +++ b/osu.Game/Graphics/UserInterface/HoverSounds.cs @@ -5,11 +5,12 @@ using System.ComponentModel; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; -using osu.Framework.Threading; +using osu.Game.Configuration; namespace osu.Game.Graphics.UserInterface { @@ -28,31 +29,35 @@ namespace osu.Game.Graphics.UserInterface protected readonly HoverSampleSet SampleSet; + private Bindable lastPlaybackTime; + public HoverSounds(HoverSampleSet sampleSet = HoverSampleSet.Normal) { SampleSet = sampleSet; RelativeSizeAxes = Axes.Both; } - private ScheduledDelegate playDelegate; + [BackgroundDependencyLoader] + private void load(AudioManager audio, SessionStatics statics) + { + lastPlaybackTime = statics.GetBindable(Static.LastHoverSoundPlaybackTime); + + sampleHover = audio.Samples.Get($@"UI/generic-hover{SampleSet.GetDescription()}"); + } protected override bool OnHover(HoverEvent e) { - playDelegate?.Cancel(); + bool requiresDebounce = HoverDebounceTime <= 0; + bool enoughTimePassedSinceLastPlayback = lastPlaybackTime.Value == 0 || Time.Current - lastPlaybackTime.Value > HoverDebounceTime; - if (HoverDebounceTime <= 0) + if (!requiresDebounce || enoughTimePassedSinceLastPlayback) + { sampleHover?.Play(); - else - playDelegate = Scheduler.AddDelayed(() => sampleHover?.Play(), HoverDebounceTime); + lastPlaybackTime.Value = Time.Current; + } return base.OnHover(e); } - - [BackgroundDependencyLoader] - private void load(AudioManager audio) - { - sampleHover = audio.Samples.Get($@"UI/generic-hover{SampleSet.GetDescription()}"); - } } public enum HoverSampleSet From 69ac22dd7fdd477943ce041cb42072de318c15e3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Jan 2021 19:06:10 +0900 Subject: [PATCH 772/818] Fix incorrectly copy pasted xmldoc --- osu.Game/Beatmaps/ControlPoints/ControlPoint.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs index 090675473d..e8dc623ddb 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs @@ -30,8 +30,7 @@ namespace osu.Game.Beatmaps.ControlPoints public abstract bool IsRedundant(ControlPoint existing); /// - /// Create a copy of this room without online information. - /// Should be used to create a local copy of a room for submitting in the future. + /// Create an unbound copy of this control point. /// public ControlPoint CreateCopy() { From 00dc98e3ab0fef1b568969a5086d9ede48ec8003 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Jan 2021 19:06:52 +0900 Subject: [PATCH 773/818] Make legacy control point's BpmMultiplier setter private again --- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index a06ad35b89..069a25b83d 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -164,7 +164,7 @@ namespace osu.Game.Beatmaps.Formats /// Legacy BPM multiplier that introduces floating-point errors for rulesets that depend on it. /// DO NOT USE THIS UNLESS 100% SURE. /// - public float BpmMultiplier { get; set; } + public float BpmMultiplier { get; private set; } public LegacyDifficultyControlPoint(double beatLength) : this() From 42643fbaf69e321f8ae1d52e70ddbc17ac985a42 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Jan 2021 19:10:19 +0900 Subject: [PATCH 774/818] Use already resolved EditorBeatmap rather than resolving a second time locally --- osu.Game/Screens/Edit/Setup/DifficultySection.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Setup/DifficultySection.cs b/osu.Game/Screens/Edit/Setup/DifficultySection.cs index f180d7e63e..36fb0191b0 100644 --- a/osu.Game/Screens/Edit/Setup/DifficultySection.cs +++ b/osu.Game/Screens/Edit/Setup/DifficultySection.cs @@ -13,9 +13,6 @@ namespace osu.Game.Screens.Edit.Setup { internal class DifficultySection : SetupSection { - [Resolved] - private EditorBeatmap editorBeatmap { get; set; } - private LabelledSliderBar circleSizeSlider; private LabelledSliderBar healthDrainSlider; private LabelledSliderBar approachRateSlider; @@ -93,7 +90,7 @@ namespace osu.Game.Screens.Edit.Setup Beatmap.BeatmapInfo.BaseDifficulty.ApproachRate = approachRateSlider.Current.Value; Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty = overallDifficultySlider.Current.Value; - editorBeatmap.UpdateAllHitObjects(); + Beatmap.UpdateAllHitObjects(); } } } From 77b55212a3c9870a0e42cd90e77e3ae4a552e273 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Jan 2021 19:11:51 +0900 Subject: [PATCH 775/818] Change access of beatmap to use working for consistency in file --- osu.Game/Screens/Edit/Setup/ResourcesSection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Setup/ResourcesSection.cs b/osu.Game/Screens/Edit/Setup/ResourcesSection.cs index b3bceceea3..010d7c2797 100644 --- a/osu.Game/Screens/Edit/Setup/ResourcesSection.cs +++ b/osu.Game/Screens/Edit/Setup/ResourcesSection.cs @@ -73,7 +73,7 @@ namespace osu.Game.Screens.Edit.Setup audioTrackTextBox = new FileChooserLabelledTextBox { Label = "Audio Track", - Current = { Value = Beatmap.Metadata.AudioFile ?? "Click to select a track" }, + Current = { Value = working.Value.Metadata.AudioFile ?? "Click to select a track" }, Target = audioTrackFileChooserContainer, TabbableContentContainer = this }, From 3c3e860dbc34d37855b79786a1abb754af1667e8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Jan 2021 23:52:04 +0900 Subject: [PATCH 776/818] Move ControlPointInfo copying to base Beatmap.Clone method (and remove setter) --- 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, 12 insertions(+), 14 deletions(-) diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index 5435e86dfd..be2006e67a 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -50,7 +50,15 @@ namespace osu.Game.Beatmaps IBeatmap IBeatmap.Clone() => Clone(); - public Beatmap Clone() => (Beatmap)MemberwiseClone(); + public Beatmap Clone() + { + var clone = (Beatmap)MemberwiseClone(); + + clone.ControlPointInfo = ControlPointInfo.CreateCopy(); + // todo: deep clone other elements as required. + + return clone; + } } public class Beatmap : Beatmap diff --git a/osu.Game/Beatmaps/IBeatmap.cs b/osu.Game/Beatmaps/IBeatmap.cs index 7dd85e1232..8f27e0b0e9 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; set; } + ControlPointInfo ControlPointInfo { get; } /// /// The breaks in this beatmap. 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/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs index a54a95f59d..165d2ba278 100644 --- a/osu.Game/Screens/Edit/EditorBeatmap.cs +++ b/osu.Game/Screens/Edit/EditorBeatmap.cs @@ -74,11 +74,7 @@ namespace osu.Game.Screens.Edit public BeatmapMetadata Metadata => PlayableBeatmap.Metadata; - public ControlPointInfo ControlPointInfo - { - get => PlayableBeatmap.ControlPointInfo; - set => PlayableBeatmap.ControlPointInfo = value; - } + public ControlPointInfo ControlPointInfo => PlayableBeatmap.ControlPointInfo; public List Breaks => PlayableBeatmap.Breaks; diff --git a/osu.Game/Screens/Play/GameplayBeatmap.cs b/osu.Game/Screens/Play/GameplayBeatmap.cs index 565595656f..64894544f4 100644 --- a/osu.Game/Screens/Play/GameplayBeatmap.cs +++ b/osu.Game/Screens/Play/GameplayBeatmap.cs @@ -29,11 +29,7 @@ namespace osu.Game.Screens.Play public BeatmapMetadata Metadata => PlayableBeatmap.Metadata; - public ControlPointInfo ControlPointInfo - { - get => PlayableBeatmap.ControlPointInfo; - set => PlayableBeatmap.ControlPointInfo = value; - } + public ControlPointInfo ControlPointInfo => PlayableBeatmap.ControlPointInfo; public List Breaks => PlayableBeatmap.Breaks; From 11801d61c1b3701356b253c9feb3a23eaa0d1dcf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Jan 2021 14:05:22 +0900 Subject: [PATCH 777/818] Use nullable doubule to better represent initial playback case --- osu.Game/Configuration/SessionStatics.cs | 2 +- osu.Game/Graphics/UserInterface/HoverSounds.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Configuration/SessionStatics.cs b/osu.Game/Configuration/SessionStatics.cs index f28ee707b9..382eab751b 100644 --- a/osu.Game/Configuration/SessionStatics.cs +++ b/osu.Game/Configuration/SessionStatics.cs @@ -16,7 +16,7 @@ namespace osu.Game.Configuration { Set(Static.LoginOverlayDisplayed, false); Set(Static.MutedAudioNotificationShownOnce, false); - Set(Static.LastHoverSoundPlaybackTime, 0.0); + Set(Static.LastHoverSoundPlaybackTime, (double?)0.0); Set(Static.SeasonalBackgrounds, null); } } diff --git a/osu.Game/Graphics/UserInterface/HoverSounds.cs b/osu.Game/Graphics/UserInterface/HoverSounds.cs index efd55c892c..29b4bf5704 100644 --- a/osu.Game/Graphics/UserInterface/HoverSounds.cs +++ b/osu.Game/Graphics/UserInterface/HoverSounds.cs @@ -29,7 +29,7 @@ namespace osu.Game.Graphics.UserInterface protected readonly HoverSampleSet SampleSet; - private Bindable lastPlaybackTime; + private Bindable lastPlaybackTime; public HoverSounds(HoverSampleSet sampleSet = HoverSampleSet.Normal) { @@ -40,7 +40,7 @@ namespace osu.Game.Graphics.UserInterface [BackgroundDependencyLoader] private void load(AudioManager audio, SessionStatics statics) { - lastPlaybackTime = statics.GetBindable(Static.LastHoverSoundPlaybackTime); + lastPlaybackTime = statics.GetBindable(Static.LastHoverSoundPlaybackTime); sampleHover = audio.Samples.Get($@"UI/generic-hover{SampleSet.GetDescription()}"); } @@ -48,7 +48,7 @@ namespace osu.Game.Graphics.UserInterface protected override bool OnHover(HoverEvent e) { bool requiresDebounce = HoverDebounceTime <= 0; - bool enoughTimePassedSinceLastPlayback = lastPlaybackTime.Value == 0 || Time.Current - lastPlaybackTime.Value > HoverDebounceTime; + bool enoughTimePassedSinceLastPlayback = !lastPlaybackTime.Value.HasValue || Time.Current - lastPlaybackTime.Value >= HoverDebounceTime; if (!requiresDebounce || enoughTimePassedSinceLastPlayback) { From e156bcdcae175f46a5c78fb9eb7c4ed5d0dbf5d0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Jan 2021 14:05:34 +0900 Subject: [PATCH 778/818] Remove unnecessary (and broken) requiresDebounce check --- osu.Game/Graphics/UserInterface/HoverSounds.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/HoverSounds.cs b/osu.Game/Graphics/UserInterface/HoverSounds.cs index 29b4bf5704..4d30d61ff0 100644 --- a/osu.Game/Graphics/UserInterface/HoverSounds.cs +++ b/osu.Game/Graphics/UserInterface/HoverSounds.cs @@ -47,10 +47,9 @@ namespace osu.Game.Graphics.UserInterface protected override bool OnHover(HoverEvent e) { - bool requiresDebounce = HoverDebounceTime <= 0; bool enoughTimePassedSinceLastPlayback = !lastPlaybackTime.Value.HasValue || Time.Current - lastPlaybackTime.Value >= HoverDebounceTime; - if (!requiresDebounce || enoughTimePassedSinceLastPlayback) + if (enoughTimePassedSinceLastPlayback) { sampleHover?.Play(); lastPlaybackTime.Value = Time.Current; From c208800150494eceb06472e45310eecc8859b29b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Jan 2021 14:17:14 +0900 Subject: [PATCH 779/818] Fix auto selection scenario regressing due to scheduling too much --- osu.Game/Overlays/Mods/ModButton.cs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index 51c8bcf9a9..b58e70cae6 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -52,9 +52,10 @@ namespace osu.Game.Overlays.Mods if (newIndex == selectedIndex) return false; int direction = newIndex < selectedIndex ? -1 : 1; + bool beforeSelected = Selected; - Mod modBefore = SelectedMod ?? Mods[0]; + Mod previousSelection = SelectedMod ?? Mods[0]; if (newIndex >= Mods.Length) newIndex = -1; @@ -64,25 +65,26 @@ namespace osu.Game.Overlays.Mods if (newIndex >= 0 && !Mods[newIndex].HasImplementation) return false; + selectedIndex = newIndex; + + Mod newSelection = SelectedMod ?? Mods[0]; + Schedule(() => { - selectedIndex = newIndex; - Mod modAfter = SelectedMod ?? Mods[0]; - if (beforeSelected != Selected) { iconsContainer.RotateTo(Selected ? 5f : 0f, 300, Easing.OutElastic); iconsContainer.ScaleTo(Selected ? 1.1f : 1f, 300, Easing.OutElastic); } - if (modBefore != modAfter) + if (previousSelection != newSelection) { const float rotate_angle = 16; foregroundIcon.RotateTo(rotate_angle * direction, mod_switch_duration, mod_switch_easing); backgroundIcon.RotateTo(-rotate_angle * direction, mod_switch_duration, mod_switch_easing); - backgroundIcon.Mod = modAfter; + backgroundIcon.Mod = newSelection; using (BeginDelayedSequence(mod_switch_duration, true)) { @@ -94,15 +96,15 @@ namespace osu.Game.Overlays.Mods .RotateTo(rotate_angle * direction) .RotateTo(0f, mod_switch_duration, mod_switch_easing); - Schedule(() => displayMod(modAfter)); + Schedule(() => displayMod(newSelection)); } } foregroundIcon.Selected.Value = Selected; - - SelectionChanged?.Invoke(SelectedMod); }); + SelectionChanged?.Invoke(newSelection); + return true; } From a6766e64de95cdd23dfa17e029275be3e7ea3c15 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Jan 2021 17:08:01 +0900 Subject: [PATCH 780/818] Add custom handling of Point serialization to fix startup crashes of tournament client SixLabors moved their data types around in a recent update (see https://github.com/ppy/osu-framework/pull/4025) and it was deemed that we should prefer `System.Drawing` primitives where possible. This was applied to the tournament client via https://github.com/ppy/osu/pull/11072 without correct consideration given to the fact that we serialize these types. `System.Drawing.Point` serializes into a comma separated string, which seems to be less correct than what we had, so I've switched back to the old format for the time being. We can reasses this in the future; the main goal here is to restore usability to the tournament client. Closes #11443. --- osu.Game.Tournament/JsonPointConverter.cs | 67 +++++++++++++++++++++++ osu.Game.Tournament/TournamentGameBase.cs | 7 ++- 2 files changed, 71 insertions(+), 3 deletions(-) create mode 100644 osu.Game.Tournament/JsonPointConverter.cs diff --git a/osu.Game.Tournament/JsonPointConverter.cs b/osu.Game.Tournament/JsonPointConverter.cs new file mode 100644 index 0000000000..57b91958d8 --- /dev/null +++ b/osu.Game.Tournament/JsonPointConverter.cs @@ -0,0 +1,67 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Diagnostics; +using System.Drawing; +using Newtonsoft.Json; + +namespace osu.Game.Tournament +{ + /// + /// We made a change from using SixLabors.ImageSharp.Point to System.Drawing.Point at some stage. + /// This handles converting to a standardised format on json serialize/deserialize operations. + /// + internal class JsonPointConverter : JsonConverter + { + public override void WriteJson(JsonWriter writer, Point value, JsonSerializer serializer) + { + // use the format of LaborSharp's Point since it is nicer. + serializer.Serialize(writer, new SixLabors.ImageSharp.Point(value.X, value.Y)); + } + + public override Point ReadJson(JsonReader reader, Type objectType, Point existingValue, bool hasExistingValue, JsonSerializer serializer) + { + if (reader.TokenType != JsonToken.StartObject) + { + // if there's no object present then this is using string representation (System.Drawing.Point serializes to "x,y") + string str = (string)reader.Value; + + Debug.Assert(str != null); + + var split = str.Split(','); + + return new Point(int.Parse(split[0]), int.Parse(split[1])); + } + + var point = new Point(); + + while (reader.Read()) + { + if (reader.TokenType == JsonToken.EndObject) break; + + if (reader.TokenType == JsonToken.PropertyName) + { + var name = reader.Value?.ToString(); + int? val = reader.ReadAsInt32(); + + if (val == null) + continue; + + switch (name) + { + case "X": + point.X = val.Value; + break; + + case "Y": + point.Y = val.Value; + break; + } + } + } + + return point; + } + } +} diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index dbda6aa023..bc36f27e5b 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -8,12 +8,12 @@ using Newtonsoft.Json; using osu.Framework.Allocation; using osu.Framework.Graphics.Textures; using osu.Framework.Input; -using osu.Framework.Platform; using osu.Framework.IO.Stores; +using osu.Framework.Platform; using osu.Game.Beatmaps; using osu.Game.Online.API.Requests; -using osu.Game.Tournament.IPC; using osu.Game.Tournament.IO; +using osu.Game.Tournament.IPC; using osu.Game.Tournament.Models; using osu.Game.Users; using osuTK.Input; @@ -60,7 +60,7 @@ namespace osu.Game.Tournament { using (Stream stream = storage.GetStream(bracket_filename, FileAccess.Read, FileMode.Open)) using (var sr = new StreamReader(stream)) - ladder = JsonConvert.DeserializeObject(sr.ReadToEnd()); + ladder = JsonConvert.DeserializeObject(sr.ReadToEnd(), new JsonPointConverter()); } ladder ??= new LadderInfo(); @@ -251,6 +251,7 @@ namespace osu.Game.Tournament Formatting = Formatting.Indented, NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore, + Converters = new JsonConverter[] { new JsonPointConverter() } })); } } From edd328c8fecdb2566c8f68f087f2c8089219bb77 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Jan 2021 17:24:55 +0900 Subject: [PATCH 781/818] Move bindable closer to source class --- .../OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index c6018a79e9..04030cdbfd 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -37,12 +37,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match [Resolved] private OngoingOperationTracker ongoingOperationTracker { get; set; } + private IBindable operationInProgress; + private SampleChannel sampleReadyCount; private readonly ButtonWithTrianglesExposed button; private int countReady; - private IBindable operationInProgress; public MultiplayerReadyButton() { From 924f91ed9b91445d4cba0bd819441a0959021faa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 8 Jan 2021 15:56:35 +0100 Subject: [PATCH 782/818] Fix song select test doing the completely wrong thing --- .../Visual/SongSelect/TestSceneAdvancedStats.cs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs index 3d3517ada4..40b2f66d74 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs @@ -11,6 +11,7 @@ using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Mods; using osu.Game.Screens.Select.Details; using osuTK.Graphics; @@ -141,16 +142,12 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("select changed Difficulty Adjust mod", () => { var ruleset = advancedStats.Beatmap.Ruleset.CreateInstance(); - var difficultyAdjustMod = ruleset.GetAllMods().OfType().Single(); + var difficultyAdjustMod = ruleset.GetAllMods().OfType().Single(); var originalDifficulty = advancedStats.Beatmap.BaseDifficulty; - var adjustedDifficulty = new BeatmapDifficulty - { - CircleSize = originalDifficulty.CircleSize, - DrainRate = originalDifficulty.DrainRate - 0.5f, - OverallDifficulty = originalDifficulty.OverallDifficulty, - ApproachRate = originalDifficulty.ApproachRate + 2.2f, - }; - difficultyAdjustMod.ReadFromDifficulty(adjustedDifficulty); + + difficultyAdjustMod.ReadFromDifficulty(originalDifficulty); + difficultyAdjustMod.DrainRate.Value = originalDifficulty.DrainRate - 0.5f; + difficultyAdjustMod.ApproachRate.Value = originalDifficulty.ApproachRate + 2.2f; SelectedMods.Value = new[] { difficultyAdjustMod }; }); From 9182f5dafb224baf6e6534edc7c727026231a1b2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 9 Jan 2021 00:38:38 +0900 Subject: [PATCH 783/818] Switch to using an anonymous type for serialisation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game.Tournament/JsonPointConverter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tournament/JsonPointConverter.cs b/osu.Game.Tournament/JsonPointConverter.cs index 57b91958d8..7ad972f3e7 100644 --- a/osu.Game.Tournament/JsonPointConverter.cs +++ b/osu.Game.Tournament/JsonPointConverter.cs @@ -17,7 +17,7 @@ namespace osu.Game.Tournament public override void WriteJson(JsonWriter writer, Point value, JsonSerializer serializer) { // use the format of LaborSharp's Point since it is nicer. - serializer.Serialize(writer, new SixLabors.ImageSharp.Point(value.X, value.Y)); + serializer.Serialize(writer, new { value.X, value.Y }); } public override Point ReadJson(JsonReader reader, Type objectType, Point existingValue, bool hasExistingValue, JsonSerializer serializer) From 82725b59c030d27abd3159ee3397a4b77e834b1a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 9 Jan 2021 00:56:54 +0900 Subject: [PATCH 784/818] Use PointConverter --- osu.Game.Tournament/JsonPointConverter.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game.Tournament/JsonPointConverter.cs b/osu.Game.Tournament/JsonPointConverter.cs index 7ad972f3e7..9c82f8ac06 100644 --- a/osu.Game.Tournament/JsonPointConverter.cs +++ b/osu.Game.Tournament/JsonPointConverter.cs @@ -29,9 +29,7 @@ namespace osu.Game.Tournament Debug.Assert(str != null); - var split = str.Split(','); - - return new Point(int.Parse(split[0]), int.Parse(split[1])); + return new PointConverter().ConvertFromString(str) as Point? ?? new Point(); } var point = new Point(); From 0cf5be3ef432a718d23bab4fc951f04182375ebb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 8 Jan 2021 17:02:57 +0100 Subject: [PATCH 785/818] Fix selection change event being invoked with wrong mod --- osu.Game/Overlays/Mods/ModButton.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index b58e70cae6..ab8efdabcc 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -103,7 +103,7 @@ namespace osu.Game.Overlays.Mods foregroundIcon.Selected.Value = Selected; }); - SelectionChanged?.Invoke(newSelection); + SelectionChanged?.Invoke(SelectedMod); return true; } From 8feaf3fb6ad084885f8b69b997caea7faf92bb50 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 9 Jan 2021 01:24:18 +0900 Subject: [PATCH 786/818] 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 611f0d05f4..492c88c7e4 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 6c220a5c21..f28a55e016 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -26,7 +26,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 5445adb3fb..93be3645ee 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -88,7 +88,7 @@ - + From d507a08951b33618a00fcce8e3c61ab289ee9172 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 8 Jan 2021 18:16:03 +0100 Subject: [PATCH 787/818] Start with null last hover playback time --- osu.Game/Configuration/SessionStatics.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Configuration/SessionStatics.cs b/osu.Game/Configuration/SessionStatics.cs index 382eab751b..fd401119ff 100644 --- a/osu.Game/Configuration/SessionStatics.cs +++ b/osu.Game/Configuration/SessionStatics.cs @@ -16,7 +16,7 @@ namespace osu.Game.Configuration { Set(Static.LoginOverlayDisplayed, false); Set(Static.MutedAudioNotificationShownOnce, false); - Set(Static.LastHoverSoundPlaybackTime, (double?)0.0); + Set(Static.LastHoverSoundPlaybackTime, (double?)null); Set(Static.SeasonalBackgrounds, null); } } From 49c6abcb5c1a8eaa3baa8d0c3b94b2799b3e82cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 8 Jan 2021 18:25:51 +0100 Subject: [PATCH 788/818] Remove mention of default value in xmldoc Just bound to get outdated with every change anyway. Look at the actual default value declaration to see what the default is. --- osu.Game/Graphics/UserInterface/HoverSounds.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/HoverSounds.cs b/osu.Game/Graphics/UserInterface/HoverSounds.cs index 4d30d61ff0..a1d06711db 100644 --- a/osu.Game/Graphics/UserInterface/HoverSounds.cs +++ b/osu.Game/Graphics/UserInterface/HoverSounds.cs @@ -23,7 +23,7 @@ namespace osu.Game.Graphics.UserInterface private SampleChannel sampleHover; /// - /// Length of debounce for hover sound playback, in milliseconds. Default is 50ms. + /// Length of debounce for hover sound playback, in milliseconds. /// public double HoverDebounceTime { get; } = 20; From 52789118a363bf0f648936d9d629d2afa554c76b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 8 Jan 2021 19:59:13 +0100 Subject: [PATCH 789/818] Schedule play button state update Revealed by the framework-side transform thread safety checks. `Stopped` is even annotated as not being thread-safe (but was annotated as such long after the class's nascence). --- osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs b/osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs index e95fdeecf4..04df27ce52 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs @@ -142,7 +142,9 @@ namespace osu.Game.Overlays.BeatmapListing.Panels AddInternal(preview); loading = false; - preview.Stopped += () => Playing.Value = false; + // make sure that the update of value of Playing (and the ensuing value change callbacks) + // are marshaled back to the update thread. + preview.Stopped += () => Schedule(() => Playing.Value = false); // user may have changed their mind. if (Playing.Value) From 274a045d8dbd03640280b422674bd5c7b0eebf44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 8 Jan 2021 20:01:47 +0100 Subject: [PATCH 790/818] Remove Dispose() override Culls another non-thread-safe mutation of the `Playing` bindable. It seems to be a weird vestige from an earlier revision of the old "direct" panel, which relied on `DisposeOnDeathRemoval` to finish track playback (and then was removed in 6c150c9ed793799fd6672cc2107c97c2e3844a09). The play button is no longer responsible for managing preview track lifetime anyway; `PreviewTrackManager`'s method are intended for that. --- osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs b/osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs index 04df27ce52..eb409785e0 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs @@ -163,11 +163,5 @@ namespace osu.Game.Overlays.BeatmapListing.Panels if (Preview?.Start() != true) Playing.Value = false; } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - Playing.Value = false; - } } } From 284d30d336d0d7b851481a084c3067c45ff3cfdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 8 Jan 2021 21:13:36 +0100 Subject: [PATCH 791/818] Move screen activity update to LoadComplete() Fixes a potential crash when moving from main menu to editor after having previously opened the login settings overlay. Setting the activity in BDL as done before is unsafe, as that set can trigger value change callbacks, which in turn can trigger adding transforms, which should always be done on the update thread. Semantically it also makes sense, as the user activity should change once the screen they're moving to has actually loaded and displayed to the user. --- osu.Game/Screens/OsuScreen.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index e1a29946f4..9b716b323d 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -143,7 +143,11 @@ namespace osu.Game.Screens private void load(OsuGame osu, AudioManager audio) { sampleExit = audio.Samples.Get(@"UI/screen-back"); + } + protected override void LoadComplete() + { + base.LoadComplete(); Activity.Value ??= InitialActivity; } From dad5dd36676de0c965c645b2564e496adb334908 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 8 Jan 2021 22:17:37 +0100 Subject: [PATCH 792/818] Remove unnecessary permissiveness wrt null --- .../OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs | 3 ++- osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs | 9 ++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 39323d9db9..e539b315e4 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -241,7 +241,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer void endOperation() { - readyClickOperation?.Dispose(); + Debug.Assert(readyClickOperation != null); + readyClickOperation.Dispose(); readyClickOperation = null; } } diff --git a/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs b/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs index 6a340d7954..c34d39136e 100644 --- a/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs +++ b/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs @@ -40,13 +40,12 @@ namespace osu.Game.Screens.OnlinePlay return new InvokeOnDisposal(endOperation); } - /// - /// Ends tracking an online operation. - /// Does nothing if an operation has not been begun yet. - /// private void endOperation() { - leasedInProgress?.Return(); + if (leasedInProgress == null) + throw new InvalidOperationException("Cannot end operation multiple times."); + + leasedInProgress.Return(); leasedInProgress = null; } } From c2eeb822b84d007beec6190cda094ca3ea1ba062 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 8 Jan 2021 22:23:38 +0100 Subject: [PATCH 793/818] Rename {joiningRoom -> operationInProgress} --- osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 2a16a62714..9b4e78543c 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -29,7 +29,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge protected override UserActivity InitialActivity => new UserActivity.SearchingForLobby(); private readonly IBindable initialRoomsReceived = new Bindable(); - private readonly IBindable joiningRoom = new Bindable(); + private readonly IBindable operationInProgress = new Bindable(); private FilterControl filter; private Container content; @@ -110,8 +110,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge if (ongoingOperationTracker != null) { - joiningRoom.BindTo(ongoingOperationTracker.InProgress); - joiningRoom.BindValueChanged(_ => updateLoadingLayer(), true); + operationInProgress.BindTo(ongoingOperationTracker.InProgress); + operationInProgress.BindValueChanged(_ => updateLoadingLayer(), true); } } @@ -187,7 +187,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge private void updateLoadingLayer() { - if (joiningRoom.Value || !initialRoomsReceived.Value) + if (operationInProgress.Value || !initialRoomsReceived.Value) loadingLayer.Show(); else loadingLayer.Hide(); From ff60d652ed6094054125990375dc9edc8ea05313 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 8 Jan 2021 22:28:21 +0100 Subject: [PATCH 794/818] Move out test ongoing operation tracker to higher level --- .../Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs | 6 +----- osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs | 3 +++ 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs index a6037dcbf2..8b9bffcee1 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs @@ -15,7 +15,6 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; using osu.Game.Rulesets; -using osu.Game.Screens.OnlinePlay; using osu.Game.Screens.OnlinePlay.Multiplayer.Match; using osu.Game.Tests.Resources; using osu.Game.Users; @@ -31,9 +30,6 @@ namespace osu.Game.Tests.Visual.Multiplayer private BeatmapManager beatmaps; private RulesetStore rulesets; - [Cached] - private OngoingOperationTracker ongoingOperationTracker = new OngoingOperationTracker(); - private IDisposable readyClickOperation; [BackgroundDependencyLoader] @@ -66,7 +62,7 @@ namespace osu.Game.Tests.Visual.Multiplayer }, OnReadyClick = async () => { - readyClickOperation = ongoingOperationTracker.BeginOperation(); + readyClickOperation = OngoingOperationTracker.BeginOperation(); if (Client.IsHost && Client.LocalUser?.State == MultiplayerUserState.Ready) { diff --git a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs index da0e39d965..75ddb34685 100644 --- a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs +++ b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs @@ -23,6 +23,9 @@ namespace osu.Game.Tests.Visual.Multiplayer [Cached] public Bindable Filter { get; } + [Cached] + public OngoingOperationTracker OngoingOperationTracker { get; } = new OngoingOperationTracker(); + protected override Container Content => content; private readonly TestMultiplayerRoomContainer content; From 0aad0c7c6c5dd363bd5bb75d2eb4cc5c35a3804f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 9 Jan 2021 00:30:12 +0300 Subject: [PATCH 795/818] Target logic at `this` and adjust variables --- osu.Game/Rulesets/Mods/Mod.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index 7fe9a06597..e72e9a004f 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -144,12 +144,12 @@ namespace osu.Game.Rulesets.Mods foreach (var (_, prop) in this.GetSettingsSourceProperties()) { - var origBindable = (IBindable)prop.GetValue(this); - var copyBindable = (IBindable)prop.GetValue(copy); + var targetBindable = (IBindable)prop.GetValue(this); + var sourceBindable = (IBindable)prop.GetValue(them); // we only care about changes that have been made away from defaults. - if (!origBindable.IsDefault) - copy.CopyAdjustedSetting(copyBindable, origBindable); + if (!sourceBindable.IsDefault) + CopyAdjustedSetting(targetBindable, sourceBindable); } } From 8c3955d34136026b2387864223b065c7f57b185e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 9 Jan 2021 21:38:20 +0100 Subject: [PATCH 796/818] Improve safety of ongoing operation tracker Finishing an operation started via `OngoingOperationTracker.BeginOperation()` was risky in cases where the operation ended at a callback on another thread (which, in the case of multiplayer, is *most* cases). In particular, if any consumer registered a callback that mutates transforms when the operation ends, it would result in crashes after the framework-side safety checks. Rework `OngoingOperationTracker` into an always-present component residing in the drawable hierarchy, and ensure that the `operationInProgress` bindable is always updated on the update thread. This way consumers don't have to add local schedules in multiple places. --- .../TestSceneCreateMultiplayerMatchButton.cs | 7 +------ .../Screens/OnlinePlay/OngoingOperationTracker.cs | 11 +++++++++-- osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs | 5 +++-- .../Tests/Visual/Multiplayer/MultiplayerTestScene.cs | 3 ++- .../Multiplayer/TestMultiplayerRoomContainer.cs | 4 ++++ 5 files changed, 19 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneCreateMultiplayerMatchButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneCreateMultiplayerMatchButton.cs index 381270c5aa..2f0398c6ef 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneCreateMultiplayerMatchButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneCreateMultiplayerMatchButton.cs @@ -3,18 +3,13 @@ using System; using NUnit.Framework; -using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Game.Screens.OnlinePlay; using osu.Game.Screens.OnlinePlay.Multiplayer; namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneCreateMultiplayerMatchButton : MultiplayerTestScene { - [Cached] - private OngoingOperationTracker ongoingOperationTracker = new OngoingOperationTracker(); - private CreateMultiplayerMatchButton button; public override void SetUpSteps() @@ -36,7 +31,7 @@ namespace osu.Game.Tests.Visual.Multiplayer assertButtonEnableState(true); - AddStep("begin joining room", () => joiningRoomOperation = ongoingOperationTracker.BeginOperation()); + AddStep("begin joining room", () => joiningRoomOperation = OngoingOperationTracker.BeginOperation()); assertButtonEnableState(false); AddStep("end joining room", () => joiningRoomOperation.Dispose()); diff --git a/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs b/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs index c34d39136e..5c9e9ce90b 100644 --- a/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs +++ b/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs @@ -4,6 +4,7 @@ using System; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Graphics; namespace osu.Game.Screens.OnlinePlay { @@ -11,7 +12,7 @@ namespace osu.Game.Screens.OnlinePlay /// Utility class to track ongoing online operations' progress. /// Can be used to disable interactivity while waiting for a response from online sources. /// - public class OngoingOperationTracker + public class OngoingOperationTracker : Component { /// /// Whether there is an online operation in progress. @@ -22,6 +23,11 @@ namespace osu.Game.Screens.OnlinePlay private LeasedBindable leasedInProgress; + public OngoingOperationTracker() + { + AlwaysPresent = true; + } + /// /// Begins tracking a new online operation. /// @@ -37,7 +43,8 @@ namespace osu.Game.Screens.OnlinePlay leasedInProgress = inProgress.BeginLease(true); leasedInProgress.Value = true; - return new InvokeOnDisposal(endOperation); + // for extra safety, marshal the end of operation back to the update thread if necessary. + return new InvokeOnDisposal(() => Scheduler.Add(endOperation, false)); } private void endOperation() diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index b73e0a7c52..71fd0d5c76 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -54,7 +54,7 @@ namespace osu.Game.Screens.OnlinePlay private readonly Bindable currentFilter = new Bindable(new FilterCriteria()); [Cached] - private readonly OngoingOperationTracker ongoingOperationTracker = new OngoingOperationTracker(); + private OngoingOperationTracker ongoingOperationTracker { get; set; } [Resolved(CanBeNull = true)] private MusicController music { get; set; } @@ -144,7 +144,8 @@ namespace osu.Game.Screens.OnlinePlay }; button.Action = () => OpenNewRoom(); }), - RoomManager = CreateRoomManager() + RoomManager = CreateRoomManager(), + ongoingOperationTracker = new OngoingOperationTracker() } }; diff --git a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs index 75ddb34685..a87b22affe 100644 --- a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs +++ b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs @@ -24,7 +24,7 @@ namespace osu.Game.Tests.Visual.Multiplayer public Bindable Filter { get; } [Cached] - public OngoingOperationTracker OngoingOperationTracker { get; } = new OngoingOperationTracker(); + public OngoingOperationTracker OngoingOperationTracker { get; } protected override Container Content => content; private readonly TestMultiplayerRoomContainer content; @@ -39,6 +39,7 @@ namespace osu.Game.Tests.Visual.Multiplayer Client = content.Client; RoomManager = content.RoomManager; Filter = content.Filter; + OngoingOperationTracker = content.OngoingOperationTracker; } [SetUp] diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomContainer.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomContainer.cs index ad3e2f7105..860caef071 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomContainer.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomContainer.cs @@ -25,6 +25,9 @@ namespace osu.Game.Tests.Visual.Multiplayer [Cached] public readonly Bindable Filter = new Bindable(new FilterCriteria()); + [Cached] + public readonly OngoingOperationTracker OngoingOperationTracker; + public TestMultiplayerRoomContainer() { RelativeSizeAxes = Axes.Both; @@ -33,6 +36,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { Client = new TestMultiplayerClient(), RoomManager = new TestMultiplayerRoomManager(), + OngoingOperationTracker = new OngoingOperationTracker(), content = new Container { RelativeSizeAxes = Axes.Both } }); } From 4b4adc927cb49a166f9f13c093c318afbbe18f2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 10 Jan 2021 15:35:53 +0100 Subject: [PATCH 797/818] Rename param to match method body --- osu.Game/Rulesets/Mods/Mod.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index e72e9a004f..3a8717e678 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -134,18 +134,18 @@ namespace osu.Game.Rulesets.Mods } /// - /// Copies mod setting values from into this instance. + /// Copies mod setting values from into this instance. /// - /// The mod to copy properties from. - public void CopyFrom(Mod them) + /// The mod to copy properties from. + public void CopyFrom(Mod source) { - if (them.GetType() != GetType()) - throw new ArgumentException($"Expected mod of type {GetType()}, got {them.GetType()}.", nameof(them)); + if (source.GetType() != GetType()) + throw new ArgumentException($"Expected mod of type {GetType()}, got {source.GetType()}.", nameof(source)); foreach (var (_, prop) in this.GetSettingsSourceProperties()) { var targetBindable = (IBindable)prop.GetValue(this); - var sourceBindable = (IBindable)prop.GetValue(them); + var sourceBindable = (IBindable)prop.GetValue(source); // we only care about changes that have been made away from defaults. if (!sourceBindable.IsDefault) From f466791b69d3341b05625157ae52301a4a035be6 Mon Sep 17 00:00:00 2001 From: Shivam Date: Sun, 10 Jan 2021 17:34:20 +0100 Subject: [PATCH 798/818] Move assignments to the TournamentSwitcher component This also adds conditional checks for displaying the "Close osu!" button --- osu.Game.Tournament/Screens/SetupScreen.cs | 41 ++++++++++++++++------ 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tournament/Screens/SetupScreen.cs b/osu.Game.Tournament/Screens/SetupScreen.cs index de3321397e..7ae0375b9a 100644 --- a/osu.Game.Tournament/Screens/SetupScreen.cs +++ b/osu.Game.Tournament/Screens/SetupScreen.cs @@ -42,17 +42,13 @@ namespace osu.Game.Tournament.Screens [Resolved] private RulesetStore rulesets { get; set; } - [Resolved] - private TournamentGameBase game { get; set; } - [Resolved(canBeNull: true)] private TournamentSceneManager sceneManager { get; set; } private Bindable windowSize; - private TournamentStorage storage; [BackgroundDependencyLoader] - private void load(FrameworkConfigManager frameworkConfig, Storage storage) + private void load(FrameworkConfigManager frameworkConfig) { windowSize = frameworkConfig.GetBindable(FrameworkSetting.WindowedSize); @@ -67,7 +63,6 @@ namespace osu.Game.Tournament.Screens api.LocalUser.BindValueChanged(_ => Schedule(reload)); stableInfo.OnStableInfoSaved += () => Schedule(reload); - this.storage = (TournamentStorage)storage; reload(); } @@ -119,10 +114,6 @@ namespace osu.Game.Tournament.Screens { Label = "Current tournament", Description = "Changes the background videos and bracket to match the selected tournament. This requires a restart to apply changes.", - Items = storage.ListTournaments(), - Current = storage.CurrentTournament, - ButtonText = "Close osu!", - Action = () => game.GracefullyExit() }, resolution = new ResolutionSelector { @@ -240,14 +231,44 @@ namespace osu.Game.Tournament.Screens set => dropdown.Current = value; } + private string originalTournament; + + private TournamentStorage storage; + + [Resolved] + private TournamentGameBase game { get; set; } + + [BackgroundDependencyLoader] + private void load(Storage storage) + { + this.storage = (TournamentStorage)storage; + Current = this.storage.CurrentTournament; + originalTournament = this.storage.CurrentTournament.Value; + Items = this.storage.ListTournaments(); + Action = () => game.GracefullyExit(); + ButtonText = "Close osu!"; + } + protected override Drawable CreateComponent() { var drawable = base.CreateComponent(); + FlowContainer.Insert(-1, dropdown = new OsuDropdown { Width = 510 }); + Current.BindValueChanged(v => + { + if (v.NewValue == originalTournament) + { + Button.Hide(); + return; + } + + Button.Show(); + }); + return drawable; } } From bd377237884c071617df95c797650fd1af2774bd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Jan 2021 01:47:04 +0900 Subject: [PATCH 799/818] Expose as IBindable for added safety --- .../BeatmapListing/Panels/BeatmapPanel.cs | 2 +- .../BeatmapListing/Panels/PlayButton.cs | 21 +++++++++++-------- .../BeatmapSet/Buttons/PreviewButton.cs | 3 ++- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs index 1567e18caa..afb5eeda36 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs @@ -38,7 +38,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels private Container content; public PreviewTrack Preview => PlayButton.Preview; - public Bindable PreviewPlaying => PlayButton?.Playing; + public IBindable PreviewPlaying => PlayButton?.Playing; protected abstract PlayButton PlayButton { get; } protected abstract Box PreviewBar { get; } diff --git a/osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs b/osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs index eb409785e0..4bbc3569fe 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs @@ -18,7 +18,10 @@ namespace osu.Game.Overlays.BeatmapListing.Panels { public class PlayButton : Container { - public readonly BindableBool Playing = new BindableBool(); + public IBindable Playing => playing; + + private readonly BindableBool playing = new BindableBool(); + public PreviewTrack Preview { get; private set; } private BeatmapSetInfo beatmapSet; @@ -36,7 +39,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels Preview?.Expire(); Preview = null; - Playing.Value = false; + playing.Value = false; } } @@ -82,7 +85,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels }, }); - Playing.ValueChanged += playingStateChanged; + playing.ValueChanged += playingStateChanged; } [Resolved] @@ -96,7 +99,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels protected override bool OnClick(ClickEvent e) { - Playing.Toggle(); + playing.Toggle(); return true; } @@ -108,7 +111,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels protected override void OnHoverLost(HoverLostEvent e) { - if (!Playing.Value) + if (!playing.Value) icon.FadeColour(Color4.White, 120, Easing.InOutQuint); base.OnHoverLost(e); } @@ -122,7 +125,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels { if (BeatmapSet == null) { - Playing.Value = false; + playing.Value = false; return; } @@ -144,10 +147,10 @@ namespace osu.Game.Overlays.BeatmapListing.Panels loading = false; // make sure that the update of value of Playing (and the ensuing value change callbacks) // are marshaled back to the update thread. - preview.Stopped += () => Schedule(() => Playing.Value = false); + preview.Stopped += () => Schedule(() => playing.Value = false); // user may have changed their mind. - if (Playing.Value) + if (playing.Value) attemptStart(); }); } @@ -161,7 +164,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels private void attemptStart() { if (Preview?.Start() != true) - Playing.Value = false; + playing.Value = false; } } } diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs index 6accce7d77..56d60a97b2 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs @@ -24,7 +24,8 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons private readonly PlayButton playButton; private PreviewTrack preview => playButton.Preview; - public Bindable Playing => playButton.Playing; + + public IBindable Playing => playButton.Playing; public BeatmapSetInfo BeatmapSet { From d2ca6da0fdae2f14b7430aa0d183e31087f5c3f0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Jan 2021 01:56:09 +0900 Subject: [PATCH 800/818] Remove unused constant --- osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs index 56d60a97b2..a5e5f664c9 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs @@ -18,8 +18,6 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons { public class PreviewButton : OsuClickableContainer { - private const float transition_duration = 500; - private readonly Box background, progress; private readonly PlayButton playButton; From e99310b59cfddbad40882e187a79668cdd865bb4 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 11 Jan 2021 08:02:57 +0300 Subject: [PATCH 801/818] Add JsonConstructor attribute --- osu.Game/Online/Rooms/BeatmapAvailability.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Online/Rooms/BeatmapAvailability.cs b/osu.Game/Online/Rooms/BeatmapAvailability.cs index 1fd099fcc7..4796e26d14 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 Newtonsoft.Json; namespace osu.Game.Online.Rooms { @@ -20,6 +21,7 @@ namespace osu.Game.Online.Rooms /// public readonly double? DownloadProgress; + [JsonConstructor] private BeatmapAvailability(DownloadState state, double? downloadProgress = null) { State = state; From a8dfa5e2a9b09e07893c83fb410b0c669e6868a5 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 11 Jan 2021 08:04:00 +0300 Subject: [PATCH 802/818] Rename typo'd method --- osu.Game/Online/Rooms/BeatmapAvailability.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Rooms/BeatmapAvailability.cs b/osu.Game/Online/Rooms/BeatmapAvailability.cs index 4796e26d14..794e79ce55 100644 --- a/osu.Game/Online/Rooms/BeatmapAvailability.cs +++ b/osu.Game/Online/Rooms/BeatmapAvailability.cs @@ -28,7 +28,7 @@ namespace osu.Game.Online.Rooms DownloadProgress = downloadProgress; } - public static BeatmapAvailability NotDownload() => new BeatmapAvailability(DownloadState.NotDownloaded); + public static BeatmapAvailability NotDownloaded() => new BeatmapAvailability(DownloadState.NotDownloaded); public static BeatmapAvailability Downloading(double progress) => new BeatmapAvailability(DownloadState.Downloading, progress); public static BeatmapAvailability Downloaded() => new BeatmapAvailability(DownloadState.Downloaded); public static BeatmapAvailability LocallyAvailable() => new BeatmapAvailability(DownloadState.LocallyAvailable); From 2286e3679f75adb10b9ba7f32316db4e116cea71 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 11 Jan 2021 08:21:07 +0300 Subject: [PATCH 803/818] Downloaded -> Importing --- osu.Game/Online/Rooms/BeatmapAvailability.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Rooms/BeatmapAvailability.cs b/osu.Game/Online/Rooms/BeatmapAvailability.cs index 794e79ce55..b6b9c632fe 100644 --- a/osu.Game/Online/Rooms/BeatmapAvailability.cs +++ b/osu.Game/Online/Rooms/BeatmapAvailability.cs @@ -30,7 +30,7 @@ namespace osu.Game.Online.Rooms public static BeatmapAvailability NotDownloaded() => new BeatmapAvailability(DownloadState.NotDownloaded); public static BeatmapAvailability Downloading(double progress) => new BeatmapAvailability(DownloadState.Downloading, progress); - public static BeatmapAvailability Downloaded() => new BeatmapAvailability(DownloadState.Downloaded); + public static BeatmapAvailability Importing() => new BeatmapAvailability(DownloadState.Downloaded); public static BeatmapAvailability LocallyAvailable() => new BeatmapAvailability(DownloadState.LocallyAvailable); public bool Equals(BeatmapAvailability other) => other != null && State == other.State && DownloadProgress == other.DownloadProgress; From 49057e8cbcca3921affcefca625365c5d9ccf23c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Jan 2021 14:35:42 +0900 Subject: [PATCH 804/818] Cache TournamentStorage explicitly for better safety --- osu.Game.Tournament/TournamentGameBase.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index bc36f27e5b..97c950261b 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -40,6 +40,8 @@ namespace osu.Game.Tournament Resources.AddStore(new DllResourceStore(typeof(TournamentGameBase).Assembly)); dependencies.CacheAs(storage = new TournamentStorage(baseStorage)); + dependencies.CacheAs(storage); + dependencies.Cache(new TournamentVideoResourceStore(storage)); Textures.AddStore(new TextureLoaderStore(new StorageBackedResourceStore(storage))); From ba3a7a0501aad64940217bd104d4d74f96ed5e1d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Jan 2021 14:35:47 +0900 Subject: [PATCH 805/818] Clean up code --- osu.Game.Tournament/Screens/SetupScreen.cs | 40 +++++----------------- 1 file changed, 9 insertions(+), 31 deletions(-) diff --git a/osu.Game.Tournament/Screens/SetupScreen.cs b/osu.Game.Tournament/Screens/SetupScreen.cs index 7ae0375b9a..cefad148a0 100644 --- a/osu.Game.Tournament/Screens/SetupScreen.cs +++ b/osu.Game.Tournament/Screens/SetupScreen.cs @@ -219,33 +219,22 @@ namespace osu.Game.Tournament.Screens { private OsuDropdown dropdown; - public IEnumerable Items - { - get => dropdown.Items; - set => dropdown.Items = value; - } - - public Bindable Current - { - get => dropdown.Current; - set => dropdown.Current = value; - } - - private string originalTournament; - - private TournamentStorage storage; + private string startupTournament; [Resolved] private TournamentGameBase game { get; set; } [BackgroundDependencyLoader] - private void load(Storage storage) + private void load(TournamentStorage storage) { - this.storage = (TournamentStorage)storage; - Current = this.storage.CurrentTournament; - originalTournament = this.storage.CurrentTournament.Value; - Items = this.storage.ListTournaments(); + dropdown.Current = storage.CurrentTournament; + dropdown.Items = storage.ListTournaments(); + dropdown.Current.BindValueChanged(v => Button.FadeTo(v.NewValue == startupTournament ? 0 : 1)); + + startupTournament = storage.CurrentTournament.Value; + Action = () => game.GracefullyExit(); + ButtonText = "Close osu!"; } @@ -258,17 +247,6 @@ namespace osu.Game.Tournament.Screens Width = 510 }); - Current.BindValueChanged(v => - { - if (v.NewValue == originalTournament) - { - Button.Hide(); - return; - } - - Button.Show(); - }); - return drawable; } } From bd627534b76edaf9bb71329eb50597ac3b17f9cf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Jan 2021 14:38:51 +0900 Subject: [PATCH 806/818] Use disabled state instead of hiding button --- osu.Game.Tournament/Screens/SetupScreen.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tournament/Screens/SetupScreen.cs b/osu.Game.Tournament/Screens/SetupScreen.cs index cefad148a0..c7b299e07f 100644 --- a/osu.Game.Tournament/Screens/SetupScreen.cs +++ b/osu.Game.Tournament/Screens/SetupScreen.cs @@ -227,11 +227,11 @@ namespace osu.Game.Tournament.Screens [BackgroundDependencyLoader] private void load(TournamentStorage storage) { + startupTournament = storage.CurrentTournament.Value; + dropdown.Current = storage.CurrentTournament; dropdown.Items = storage.ListTournaments(); - dropdown.Current.BindValueChanged(v => Button.FadeTo(v.NewValue == startupTournament ? 0 : 1)); - - startupTournament = storage.CurrentTournament.Value; + dropdown.Current.BindValueChanged(v => Button.Enabled.Value = v.NewValue != startupTournament, true); Action = () => game.GracefullyExit(); From 7a7c583ded7de7e6a354f49d83f131b7471c89f9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Jan 2021 14:44:07 +0900 Subject: [PATCH 807/818] Move setup screen classes out of single file and into their own namespace --- .../Screens/TestSceneSetupScreen.cs | 2 +- .../TestSceneStablePathSelectScreen.cs | 2 +- .../Screens/Setup/ActionableInfo.cs | 69 +++++++++ .../Screens/Setup/ResolutionSelector.cs | 50 ++++++ .../Screens/{ => Setup}/SetupScreen.cs | 145 +----------------- .../{ => Setup}/StablePathSelectScreen.cs | 4 +- .../Screens/Setup/TournamentSwitcher.cs | 43 ++++++ osu.Game.Tournament/TournamentSceneManager.cs | 1 + 8 files changed, 168 insertions(+), 148 deletions(-) create mode 100644 osu.Game.Tournament/Screens/Setup/ActionableInfo.cs create mode 100644 osu.Game.Tournament/Screens/Setup/ResolutionSelector.cs rename osu.Game.Tournament/Screens/{ => Setup}/SetupScreen.cs (54%) rename osu.Game.Tournament/Screens/{ => Setup}/StablePathSelectScreen.cs (99%) create mode 100644 osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneSetupScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneSetupScreen.cs index 650b4c5412..70b260c84c 100644 --- a/osu.Game.Tournament.Tests/Screens/TestSceneSetupScreen.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneSetupScreen.cs @@ -2,7 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Game.Tournament.Screens; +using osu.Game.Tournament.Screens.Setup; namespace osu.Game.Tournament.Tests.Screens { diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneStablePathSelectScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneStablePathSelectScreen.cs index 6e63b2d799..b422227788 100644 --- a/osu.Game.Tournament.Tests/Screens/TestSceneStablePathSelectScreen.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneStablePathSelectScreen.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.Tournament.Screens; +using osu.Game.Tournament.Screens.Setup; namespace osu.Game.Tournament.Tests.Screens { diff --git a/osu.Game.Tournament/Screens/Setup/ActionableInfo.cs b/osu.Game.Tournament/Screens/Setup/ActionableInfo.cs new file mode 100644 index 0000000000..f7d52a294e --- /dev/null +++ b/osu.Game.Tournament/Screens/Setup/ActionableInfo.cs @@ -0,0 +1,69 @@ +using System; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.UserInterface; +using osu.Game.Graphics.UserInterfaceV2; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Tournament.Screens.Setup +{ + internal class ActionableInfo : LabelledDrawable + { + protected OsuButton Button; + + public ActionableInfo() + : base(true) + { + } + + public string ButtonText + { + set => Button.Text = value; + } + + public string Value + { + set => valueText.Text = value; + } + + public bool Failing + { + set => valueText.Colour = value ? Color4.Red : Color4.White; + } + + public Action Action; + + private TournamentSpriteText valueText; + protected FillFlowContainer FlowContainer; + + protected override Drawable CreateComponent() => new Container + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Children = new Drawable[] + { + valueText = new TournamentSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, + FlowContainer = new FillFlowContainer + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + AutoSizeAxes = Axes.Both, + Spacing = new Vector2(10, 0), + Children = new Drawable[] + { + Button = new TriangleButton + { + Size = new Vector2(100, 40), + Action = () => Action?.Invoke() + } + } + } + } + }; + } +} diff --git a/osu.Game.Tournament/Screens/Setup/ResolutionSelector.cs b/osu.Game.Tournament/Screens/Setup/ResolutionSelector.cs new file mode 100644 index 0000000000..47472c386c --- /dev/null +++ b/osu.Game.Tournament/Screens/Setup/ResolutionSelector.cs @@ -0,0 +1,50 @@ +using System; +using osu.Framework.Graphics; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Tournament.Screens.Setup +{ + internal class ResolutionSelector : ActionableInfo + { + private const int minimum_window_height = 480; + private const int maximum_window_height = 2160; + + public new Action Action; + + private OsuNumberBox numberBox; + + protected override Drawable CreateComponent() + { + var drawable = base.CreateComponent(); + FlowContainer.Insert(-1, numberBox = new OsuNumberBox + { + Text = "1080", + Width = 100 + }); + + base.Action = () => + { + if (string.IsNullOrEmpty(numberBox.Text)) + return; + + // box contains text + if (!int.TryParse(numberBox.Text, out var number)) + { + // at this point, the only reason we can arrive here is if the input number was too big to parse into an int + // so clamp to max allowed value + number = maximum_window_height; + } + else + { + number = Math.Clamp(number, minimum_window_height, maximum_window_height); + } + + // in case number got clamped, reset number in numberBox + numberBox.Text = number.ToString(); + + Action?.Invoke(number); + }; + return drawable; + } + } +} diff --git a/osu.Game.Tournament/Screens/SetupScreen.cs b/osu.Game.Tournament/Screens/Setup/SetupScreen.cs similarity index 54% rename from osu.Game.Tournament/Screens/SetupScreen.cs rename to osu.Game.Tournament/Screens/Setup/SetupScreen.cs index c7b299e07f..5d8f0405ca 100644 --- a/osu.Game.Tournament/Screens/SetupScreen.cs +++ b/osu.Game.Tournament/Screens/Setup/SetupScreen.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.Collections.Generic; using System.Drawing; using osu.Framework.Allocation; @@ -9,19 +8,16 @@ using osu.Framework.Bindables; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Platform; using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; -using osu.Game.Tournament.IO; using osu.Game.Online.API; using osu.Game.Overlays; using osu.Game.Rulesets; using osu.Game.Tournament.IPC; using osu.Game.Tournament.Models; using osuTK; -using osuTK.Graphics; -namespace osu.Game.Tournament.Screens +namespace osu.Game.Tournament.Screens.Setup { public class SetupScreen : TournamentScreen, IProvideVideo { @@ -155,144 +151,5 @@ namespace osu.Game.Tournament.Screens Width = 0.5f, }; } - - private class ActionableInfo : LabelledDrawable - { - protected OsuButton Button; - - public ActionableInfo() - : base(true) - { - } - - public string ButtonText - { - set => Button.Text = value; - } - - public string Value - { - set => valueText.Text = value; - } - - public bool Failing - { - set => valueText.Colour = value ? Color4.Red : Color4.White; - } - - public Action Action; - - private TournamentSpriteText valueText; - protected FillFlowContainer FlowContainer; - - protected override Drawable CreateComponent() => new Container - { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Children = new Drawable[] - { - valueText = new TournamentSpriteText - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - }, - FlowContainer = new FillFlowContainer - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - AutoSizeAxes = Axes.Both, - Spacing = new Vector2(10, 0), - Children = new Drawable[] - { - Button = new TriangleButton - { - Size = new Vector2(100, 40), - Action = () => Action?.Invoke() - } - } - } - } - }; - } - - private class TournamentSwitcher : ActionableInfo - { - private OsuDropdown dropdown; - - private string startupTournament; - - [Resolved] - private TournamentGameBase game { get; set; } - - [BackgroundDependencyLoader] - private void load(TournamentStorage storage) - { - startupTournament = storage.CurrentTournament.Value; - - dropdown.Current = storage.CurrentTournament; - dropdown.Items = storage.ListTournaments(); - dropdown.Current.BindValueChanged(v => Button.Enabled.Value = v.NewValue != startupTournament, true); - - Action = () => game.GracefullyExit(); - - ButtonText = "Close osu!"; - } - - protected override Drawable CreateComponent() - { - var drawable = base.CreateComponent(); - - FlowContainer.Insert(-1, dropdown = new OsuDropdown - { - Width = 510 - }); - - return drawable; - } - } - - private class ResolutionSelector : ActionableInfo - { - private const int minimum_window_height = 480; - private const int maximum_window_height = 2160; - - public new Action Action; - - private OsuNumberBox numberBox; - - protected override Drawable CreateComponent() - { - var drawable = base.CreateComponent(); - FlowContainer.Insert(-1, numberBox = new OsuNumberBox - { - Text = "1080", - Width = 100 - }); - - base.Action = () => - { - if (string.IsNullOrEmpty(numberBox.Text)) - return; - - // box contains text - if (!int.TryParse(numberBox.Text, out var number)) - { - // at this point, the only reason we can arrive here is if the input number was too big to parse into an int - // so clamp to max allowed value - number = maximum_window_height; - } - else - { - number = Math.Clamp(number, minimum_window_height, maximum_window_height); - } - - // in case number got clamped, reset number in numberBox - numberBox.Text = number.ToString(); - - Action?.Invoke(number); - }; - return drawable; - } - } } } diff --git a/osu.Game.Tournament/Screens/StablePathSelectScreen.cs b/osu.Game.Tournament/Screens/Setup/StablePathSelectScreen.cs similarity index 99% rename from osu.Game.Tournament/Screens/StablePathSelectScreen.cs rename to osu.Game.Tournament/Screens/Setup/StablePathSelectScreen.cs index 717b43f704..03f79b644f 100644 --- a/osu.Game.Tournament/Screens/StablePathSelectScreen.cs +++ b/osu.Game.Tournament/Screens/Setup/StablePathSelectScreen.cs @@ -13,11 +13,11 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Overlays; -using osu.Game.Tournament.IPC; using osu.Game.Tournament.Components; +using osu.Game.Tournament.IPC; using osuTK; -namespace osu.Game.Tournament.Screens +namespace osu.Game.Tournament.Screens.Setup { public class StablePathSelectScreen : TournamentScreen { diff --git a/osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs b/osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs new file mode 100644 index 0000000000..a993a0594a --- /dev/null +++ b/osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs @@ -0,0 +1,43 @@ +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Graphics.UserInterface; +using osu.Game.Tournament.IO; + +namespace osu.Game.Tournament.Screens.Setup +{ + internal class TournamentSwitcher : ActionableInfo + { + private OsuDropdown dropdown; + + private string startupTournament; + + [Resolved] + private TournamentGameBase game { get; set; } + + [BackgroundDependencyLoader] + private void load(TournamentStorage storage) + { + startupTournament = storage.CurrentTournament.Value; + + dropdown.Current = storage.CurrentTournament; + dropdown.Items = storage.ListTournaments(); + dropdown.Current.BindValueChanged(v => Button.Enabled.Value = v.NewValue != startupTournament, true); + + Action = () => game.GracefullyExit(); + + ButtonText = "Close osu!"; + } + + protected override Drawable CreateComponent() + { + var drawable = base.CreateComponent(); + + FlowContainer.Insert(-1, dropdown = new OsuDropdown + { + Width = 510 + }); + + return drawable; + } + } +} \ No newline at end of file diff --git a/osu.Game.Tournament/TournamentSceneManager.cs b/osu.Game.Tournament/TournamentSceneManager.cs index 870ea466cc..ced1a8ec72 100644 --- a/osu.Game.Tournament/TournamentSceneManager.cs +++ b/osu.Game.Tournament/TournamentSceneManager.cs @@ -19,6 +19,7 @@ using osu.Game.Tournament.Screens.Gameplay; using osu.Game.Tournament.Screens.Ladder; using osu.Game.Tournament.Screens.MapPool; using osu.Game.Tournament.Screens.Schedule; +using osu.Game.Tournament.Screens.Setup; using osu.Game.Tournament.Screens.Showcase; using osu.Game.Tournament.Screens.TeamIntro; using osu.Game.Tournament.Screens.TeamWin; From c9466426b743471e36e4126f3637d44c00fc819c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Jan 2021 14:45:01 +0900 Subject: [PATCH 808/818] Change field to local variable --- osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs b/osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs index a993a0594a..e38a374e1c 100644 --- a/osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs +++ b/osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs @@ -9,15 +9,13 @@ namespace osu.Game.Tournament.Screens.Setup { private OsuDropdown dropdown; - private string startupTournament; - [Resolved] private TournamentGameBase game { get; set; } [BackgroundDependencyLoader] private void load(TournamentStorage storage) { - startupTournament = storage.CurrentTournament.Value; + string startupTournament = storage.CurrentTournament.Value; dropdown.Current = storage.CurrentTournament; dropdown.Items = storage.ListTournaments(); @@ -40,4 +38,4 @@ namespace osu.Game.Tournament.Screens.Setup return drawable; } } -} \ No newline at end of file +} From f65042cf4452997589ba678b8c4670a16eafdd65 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Jan 2021 15:47:27 +0900 Subject: [PATCH 809/818] Add missing licence headers --- osu.Game.Tournament/Screens/Setup/ActionableInfo.cs | 3 +++ osu.Game.Tournament/Screens/Setup/ResolutionSelector.cs | 3 +++ osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs | 3 +++ 3 files changed, 9 insertions(+) diff --git a/osu.Game.Tournament/Screens/Setup/ActionableInfo.cs b/osu.Game.Tournament/Screens/Setup/ActionableInfo.cs index f7d52a294e..cfdf9c99ae 100644 --- a/osu.Game.Tournament/Screens/Setup/ActionableInfo.cs +++ b/osu.Game.Tournament/Screens/Setup/ActionableInfo.cs @@ -1,3 +1,6 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; diff --git a/osu.Game.Tournament/Screens/Setup/ResolutionSelector.cs b/osu.Game.Tournament/Screens/Setup/ResolutionSelector.cs index 47472c386c..4b518ea7c7 100644 --- a/osu.Game.Tournament/Screens/Setup/ResolutionSelector.cs +++ b/osu.Game.Tournament/Screens/Setup/ResolutionSelector.cs @@ -1,3 +1,6 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + using System; using osu.Framework.Graphics; using osu.Game.Graphics.UserInterface; diff --git a/osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs b/osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs index e38a374e1c..74c872646c 100644 --- a/osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs +++ b/osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs @@ -1,3 +1,6 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Graphics.UserInterface; From 90fb67b377e2022f3bfa60dfca4f31a7d3670228 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 11 Jan 2021 20:52:02 +0300 Subject: [PATCH 810/818] Update code in-line with decided direction --- osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs | 1 - osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs b/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs index 7fda526faf..f324a1a216 100644 --- a/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs @@ -43,7 +43,6 @@ namespace osu.Game.Online.Multiplayer /// /// Change the user's local availability state of the beatmap set in joined room. - /// This will also force user state back to . /// /// The proposed new beatmap availability state. Task ChangeBeatmapAvailability(BeatmapAvailability newBeatmapAvailability); diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index c155447f8c..7fbc770351 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -83,7 +83,6 @@ namespace osu.Game.Tests.Visual.Multiplayer Debug.Assert(Room != null); ((IMultiplayerClient)this).UserBeatmapAvailabilityChanged(userId, newBeatmapAvailability); - ChangeUserState(userId, MultiplayerUserState.Idle); } protected override Task JoinRoom(long roomId) From 0d5fbb15ac06c321a0f43d9a2e6ff5d9a61e8cc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 11 Jan 2021 20:28:24 +0100 Subject: [PATCH 811/818] Fix up code comments Default value restated in xmldoc was snipped because it's made redundant by the initialiser and possibly bound to be outdated at some point. --- osu.Game/Online/Multiplayer/IMultiplayerClient.cs | 2 +- osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs | 2 +- osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs | 2 +- osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Online/Multiplayer/IMultiplayerClient.cs b/osu.Game/Online/Multiplayer/IMultiplayerClient.cs index 5410fbc030..19dd473230 100644 --- a/osu.Game/Online/Multiplayer/IMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerClient.cs @@ -49,7 +49,7 @@ namespace osu.Game.Online.Multiplayer Task UserStateChanged(int userId, MultiplayerUserState state); /// - /// Signals that a user in this room has their beatmap availability state changed. + /// Signals that a user in this room changed their beatmap availability state. /// /// The ID of the user whose beatmap availability state has changed. /// The new beatmap availability state of the user. diff --git a/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs b/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs index f324a1a216..09816974a7 100644 --- a/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs @@ -42,7 +42,7 @@ namespace osu.Game.Online.Multiplayer Task ChangeState(MultiplayerUserState newState); /// - /// Change the user's local availability state of the beatmap set in joined room. + /// Change the local user's availability state of the current beatmap set in joined room. /// /// The proposed new beatmap availability state. Task ChangeBeatmapAvailability(BeatmapAvailability newBeatmapAvailability); diff --git a/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs b/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs index f515b574df..2590acbc81 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs @@ -18,7 +18,7 @@ namespace osu.Game.Online.Multiplayer public MultiplayerUserState State { get; set; } = MultiplayerUserState.Idle; /// - /// The availability state of the beatmap, set to by default. + /// The availability state of the current beatmap. /// public BeatmapAvailability BeatmapAvailability { get; set; } = BeatmapAvailability.LocallyAvailable(); diff --git a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs index 799b66020c..99aa8fe015 100644 --- a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs @@ -322,7 +322,7 @@ namespace osu.Game.Online.Multiplayer { var user = Room?.Users.SingleOrDefault(u => u.UserID == userId); - // we don't care whether the room doesn't exist or user isn't in joined room, just return in that point. + // errors here are not critical - beatmap availability state is mostly for display. if (user == null) return; From 22a0f99f35496d22b5b610626b057c36d1bea82d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 12 Jan 2021 17:49:18 +0900 Subject: [PATCH 812/818] Add failing test --- .../TaikoBeatmapConversionTest.cs | 1 + ...rating-drumroll-2-expected-conversion.json | 18 ++++++++++++++++++ .../Beatmaps/slider-generating-drumroll-2.osu | 19 +++++++++++++++++++ 3 files changed, 38 insertions(+) create mode 100644 osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/slider-generating-drumroll-2-expected-conversion.json create mode 100644 osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/slider-generating-drumroll-2.osu diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs index 3d77fb05db..b6db333dc9 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs @@ -23,6 +23,7 @@ namespace osu.Game.Rulesets.Taiko.Tests [TestCase("sample-to-type-conversions")] [TestCase("slider-conversion-v6")] [TestCase("slider-conversion-v14")] + [TestCase("slider-generating-drumroll-2")] public void Test(string name) => base.Test(name); protected override IEnumerable CreateConvertValue(HitObject hitObject) diff --git a/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/slider-generating-drumroll-2-expected-conversion.json b/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/slider-generating-drumroll-2-expected-conversion.json new file mode 100644 index 0000000000..b4ee98c86a --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/slider-generating-drumroll-2-expected-conversion.json @@ -0,0 +1,18 @@ +{ + "Mappings": [ + { + "StartTime": 51532, + "Objects": [ + { + "StartTime": 51532, + "EndTime": 52301, + "IsRim": false, + "IsCentre": false, + "IsDrumRoll": true, + "IsSwell": false, + "IsStrong": false + } + ] + } + ] +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/slider-generating-drumroll-2.osu b/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/slider-generating-drumroll-2.osu new file mode 100644 index 0000000000..d81b09ee26 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/slider-generating-drumroll-2.osu @@ -0,0 +1,19 @@ +osu file format v14 + +[General] +Mode: 0 + +[Difficulty] +HPDrainRate:2 +CircleSize:3.2 +OverallDifficulty:2 +ApproachRate:3 +SliderMultiplier:0.999999999999999 +SliderTickRate:1 + +[TimingPoints] +763,384.615384615385,4,2,0,70,1,0 +49993,-90.9090909090909,4,2,0,75,0,1 + +[HitObjects] +51,245,51532,2,0,P|18:150|17:122,2,110.000003356934,0|8|0,0:0|0:0|0:0,0:0:0:0: From 9a22df2b88029de3f22b51938ad6b163322d95ec Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 12 Jan 2021 17:50:22 +0900 Subject: [PATCH 813/818] Fix BPM multiplier not working in all cases --- .../Beatmaps/TaikoBeatmapConverter.cs | 6 +- ...er-conversion-v14-expected-conversion.json | 307 +----------------- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 5 +- 3 files changed, 18 insertions(+), 300 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 1214c594aa..b51f096d7d 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -160,7 +160,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps } } - private bool shouldConvertSliderToHits(HitObject obj, IBeatmap beatmap, IHasDistance distanceData, out double taikoDuration, out double tickSpacing) + private bool shouldConvertSliderToHits(HitObject obj, IBeatmap beatmap, IHasDistance distanceData, out int taikoDuration, out double tickSpacing) { // DO NOT CHANGE OR REFACTOR ANYTHING IN HERE WITHOUT TESTING AGAINST _ALL_ BEATMAPS. // Some of these calculations look redundant, but they are not - extremely small floating point errors are introduced to maintain 1:1 compatibility with stable. @@ -185,7 +185,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps // The velocity and duration of the taiko hit object - calculated as the velocity of a drum roll. double taikoVelocity = sliderScoringPointDistance * beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate; - taikoDuration = distance / taikoVelocity * beatLength; + taikoDuration = (int)(distance / taikoVelocity * beatLength); if (isForCurrentRuleset) { @@ -200,7 +200,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps beatLength = timingPoint.BeatLength; // If the drum roll is to be split into hit circles, assume the ticks are 1/8 spaced within the duration of one beat - tickSpacing = Math.Min(beatLength / beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate, taikoDuration / spans); + tickSpacing = Math.Min(beatLength / beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate, (double)taikoDuration / spans); return tickSpacing > 0 && distance / osuVelocity * 1000 < 2 * beatLength; diff --git a/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/slider-conversion-v14-expected-conversion.json b/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/slider-conversion-v14-expected-conversion.json index 6a6063cb74..b7ad128cab 100644 --- a/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/slider-conversion-v14-expected-conversion.json +++ b/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/slider-conversion-v14-expected-conversion.json @@ -1,7 +1,9 @@ { - "Mappings": [{ + "Mappings": [ + { "StartTime": 2000, - "Objects": [{ + "Objects": [ + { "StartTime": 2000, "EndTime": 2000, "IsRim": false, @@ -23,7 +25,8 @@ }, { "StartTime": 4000, - "Objects": [{ + "Objects": [ + { "StartTime": 4000, "EndTime": 4000, "IsRim": false, @@ -45,7 +48,8 @@ }, { "StartTime": 6000, - "Objects": [{ + "Objects": [ + { "StartTime": 6000, "EndTime": 6000, "IsRim": true, @@ -76,300 +80,13 @@ }, { "StartTime": 8000, - "Objects": [{ + "Objects": [ + { "StartTime": 8000, - "EndTime": 8000, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8026, - "EndTime": 8026, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8053, - "EndTime": 8053, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8080, - "EndTime": 8080, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8107, - "EndTime": 8107, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8133, - "EndTime": 8133, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8160, - "EndTime": 8160, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8187, - "EndTime": 8187, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8214, - "EndTime": 8214, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8241, - "EndTime": 8241, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8267, - "EndTime": 8267, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8294, - "EndTime": 8294, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8321, - "EndTime": 8321, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8348, - "EndTime": 8348, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8374, - "EndTime": 8374, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8401, - "EndTime": 8401, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8428, - "EndTime": 8428, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8455, - "EndTime": 8455, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8482, - "EndTime": 8482, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8508, - "EndTime": 8508, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8535, - "EndTime": 8535, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8562, - "EndTime": 8562, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8589, - "EndTime": 8589, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8615, - "EndTime": 8615, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8642, - "EndTime": 8642, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8669, - "EndTime": 8669, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8696, - "EndTime": 8696, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8723, - "EndTime": 8723, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8749, - "EndTime": 8749, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8776, - "EndTime": 8776, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8803, - "EndTime": 8803, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8830, - "EndTime": 8830, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8857, "EndTime": 8857, "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, + "IsCentre": false, + "IsDrumRoll": true, "IsSwell": false, "IsStrong": false } diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 069a25b83d..2fb24c24e0 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -164,12 +164,13 @@ namespace osu.Game.Beatmaps.Formats /// Legacy BPM multiplier that introduces floating-point errors for rulesets that depend on it. /// DO NOT USE THIS UNLESS 100% SURE. /// - public float BpmMultiplier { get; private set; } + public double BpmMultiplier { get; private set; } public LegacyDifficultyControlPoint(double beatLength) : this() { - BpmMultiplier = beatLength < 0 ? Math.Clamp((float)-beatLength, 10, 10000) / 100f : 1; + // Note: In stable, the division occurs on floats, but with compiler optimisations turned on actually seems to occur on doubles via some .NET black magic (possibly inlining?). + BpmMultiplier = beatLength < 0 ? Math.Clamp((float)-beatLength, 10, 10000) / 100.0 : 1; } public LegacyDifficultyControlPoint() From 2d3cacca11bc1d8de6c78cf8b6f1154f1e058050 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 13 Jan 2021 00:58:53 +0100 Subject: [PATCH 814/818] Fix non-hosts crashing on load requested `onLoadRequested()` always released the `readyClickOperation` ongoing operation, without checking whether it actually needs to/should (it should only do so if the action initiating the operation was starting the game by the host). This would crash all other consumers, who already released the operation when their ready-up operation completed server side. To resolve, relax the constraint such that the operation can be ended multiple times in any order. At the end of the day the thing that matters is that the operation is done and the ready button is unblocked. --- .../OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index e539b315e4..80991569dc 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -241,8 +241,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer void endOperation() { - Debug.Assert(readyClickOperation != null); - readyClickOperation.Dispose(); + readyClickOperation?.Dispose(); readyClickOperation = null; } } @@ -255,9 +254,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer StartPlay(() => new MultiplayerPlayer(SelectedItem.Value, userIds)); - Debug.Assert(readyClickOperation != null); - - readyClickOperation.Dispose(); + readyClickOperation?.Dispose(); readyClickOperation = null; } From 1f12b2bd091e7968c60e6c100cc9369edd5c3973 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 13 Jan 2021 18:04:29 +0300 Subject: [PATCH 815/818] Rename download state `Downloaded` to `Importing` --- osu.Game/Graphics/UserInterface/DownloadButton.cs | 2 +- osu.Game/Online/DownloadState.cs | 2 +- osu.Game/Online/DownloadTrackingComposite.cs | 4 ++-- .../BeatmapListing/Panels/BeatmapPanelDownloadButton.cs | 2 +- .../Overlays/BeatmapListing/Panels/DownloadProgressBar.cs | 2 +- osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs | 2 +- osu.Game/Overlays/BeatmapSet/Header.cs | 2 +- osu.Game/Screens/Ranking/ReplayDownloadButton.cs | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/DownloadButton.cs b/osu.Game/Graphics/UserInterface/DownloadButton.cs index da6c95299e..5168ff646b 100644 --- a/osu.Game/Graphics/UserInterface/DownloadButton.cs +++ b/osu.Game/Graphics/UserInterface/DownloadButton.cs @@ -73,7 +73,7 @@ namespace osu.Game.Graphics.UserInterface TooltipText = "Downloading..."; break; - case DownloadState.Downloaded: + case DownloadState.Importing: background.FadeColour(colours.Yellow, 500, Easing.InOutExpo); TooltipText = "Importing"; break; diff --git a/osu.Game/Online/DownloadState.cs b/osu.Game/Online/DownloadState.cs index 72efbc286e..a58c40d16a 100644 --- a/osu.Game/Online/DownloadState.cs +++ b/osu.Game/Online/DownloadState.cs @@ -7,7 +7,7 @@ namespace osu.Game.Online { NotDownloaded, Downloading, - Downloaded, + Importing, LocallyAvailable } } diff --git a/osu.Game/Online/DownloadTrackingComposite.cs b/osu.Game/Online/DownloadTrackingComposite.cs index bed95344c6..7a64c9002d 100644 --- a/osu.Game/Online/DownloadTrackingComposite.cs +++ b/osu.Game/Online/DownloadTrackingComposite.cs @@ -106,7 +106,7 @@ namespace osu.Game.Online { if (attachedRequest.Progress == 1) { - State.Value = DownloadState.Downloaded; + State.Value = DownloadState.Importing; Progress.Value = 1; } else @@ -125,7 +125,7 @@ namespace osu.Game.Online } } - private void onRequestSuccess(string _) => Schedule(() => State.Value = DownloadState.Downloaded); + private void onRequestSuccess(string _) => Schedule(() => State.Value = DownloadState.Importing); private void onRequestProgress(float progress) => Schedule(() => Progress.Value = progress); diff --git a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs index 001ca801d9..cec1a5ac12 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs @@ -57,7 +57,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels switch (State.Value) { case DownloadState.Downloading: - case DownloadState.Downloaded: + case DownloadState.Importing: shakeContainer.Shake(); break; diff --git a/osu.Game/Overlays/BeatmapListing/Panels/DownloadProgressBar.cs b/osu.Game/Overlays/BeatmapListing/Panels/DownloadProgressBar.cs index 93cf8799b5..6a2f2e4569 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/DownloadProgressBar.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/DownloadProgressBar.cs @@ -50,7 +50,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels progressBar.ResizeHeightTo(4, 400, Easing.OutQuint); break; - case DownloadState.Downloaded: + case DownloadState.Importing: progressBar.FadeIn(400, Easing.OutQuint); progressBar.ResizeHeightTo(4, 400, Easing.OutQuint); diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs index 56c0052bfe..cffff86a64 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs @@ -126,7 +126,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons }; break; - case DownloadState.Downloaded: + case DownloadState.Importing: textSprites.Children = new Drawable[] { new OsuSpriteText diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index 321e496511..c17901bb3f 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -287,7 +287,7 @@ namespace osu.Game.Overlays.BeatmapSet break; case DownloadState.Downloading: - case DownloadState.Downloaded: + 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; diff --git a/osu.Game/Screens/Ranking/ReplayDownloadButton.cs b/osu.Game/Screens/Ranking/ReplayDownloadButton.cs index b76842f405..18b8649a59 100644 --- a/osu.Game/Screens/Ranking/ReplayDownloadButton.cs +++ b/osu.Game/Screens/Ranking/ReplayDownloadButton.cs @@ -63,7 +63,7 @@ namespace osu.Game.Screens.Ranking scores.Download(Model.Value); break; - case DownloadState.Downloaded: + case DownloadState.Importing: case DownloadState.Downloading: shakeContainer.Shake(); break; From 1ba586a683b3321cf7806d9dabf068983d0bd27b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 13 Jan 2021 17:53:01 +0100 Subject: [PATCH 816/818] Revert overlooked AR<8 speed buff Pull request #11107 introduced changes in osu! performance calculation, related to a scaling coefficient applied to the speed and aim skills. The coefficient in question was dependent on the approach rate of a map. During a post-merge review of that PR, it was spotted that the scaling coefficient for speed also had a 10x buff applied for AR<8, which could reach magnitudes as large as 80% on AR0, which seems quite exorbitant. This change was not discussed or mentioned anywhere in the review process. Revert back to the old multiplier of 0.01 rather than 0.1 for AR<8. The negative slope through AR0 to 8 is retained in its previous form. --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index a44c97c3cf..44a9dd2f1f 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -104,7 +104,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (Attributes.ApproachRate > 10.33) approachRateFactor += 0.4 * (Attributes.ApproachRate - 10.33); else if (Attributes.ApproachRate < 8.0) - approachRateFactor += 0.1 * (8.0 - Attributes.ApproachRate); + approachRateFactor += 0.01 * (8.0 - Attributes.ApproachRate); aimValue *= 1.0 + Math.Min(approachRateFactor, approachRateFactor * (totalHits / 1000.0)); From 95acc457aad5f08df2b7a7a4bc37af4a241ffc2d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 13 Jan 2021 22:34:48 +0300 Subject: [PATCH 817/818] Fix stupid mistake fuck. --- osu.Game/Online/Rooms/BeatmapAvailability.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Rooms/BeatmapAvailability.cs b/osu.Game/Online/Rooms/BeatmapAvailability.cs index b6b9c632fe..e7dbc5f436 100644 --- a/osu.Game/Online/Rooms/BeatmapAvailability.cs +++ b/osu.Game/Online/Rooms/BeatmapAvailability.cs @@ -30,7 +30,7 @@ namespace osu.Game.Online.Rooms public static BeatmapAvailability NotDownloaded() => new BeatmapAvailability(DownloadState.NotDownloaded); public static BeatmapAvailability Downloading(double progress) => new BeatmapAvailability(DownloadState.Downloading, progress); - public static BeatmapAvailability Importing() => new BeatmapAvailability(DownloadState.Downloaded); + public static BeatmapAvailability Importing() => new BeatmapAvailability(DownloadState.Importing); public static BeatmapAvailability LocallyAvailable() => new BeatmapAvailability(DownloadState.LocallyAvailable); public bool Equals(BeatmapAvailability other) => other != null && State == other.State && DownloadProgress == other.DownloadProgress; From d5878db615ef55e87b9e39e22b480610f12cdf6c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 14 Jan 2021 12:33:33 +0900 Subject: [PATCH 818/818] Fix default judgement text mispositioned for one frame --- osu.Game/Rulesets/Judgements/DefaultJudgementPiece.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Rulesets/Judgements/DefaultJudgementPiece.cs b/osu.Game/Rulesets/Judgements/DefaultJudgementPiece.cs index d94346cb72..21ac017685 100644 --- a/osu.Game/Rulesets/Judgements/DefaultJudgementPiece.cs +++ b/osu.Game/Rulesets/Judgements/DefaultJudgementPiece.cs @@ -37,6 +37,8 @@ namespace osu.Game.Rulesets.Judgements { JudgementText = new OsuSpriteText { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, Text = Result.GetDescription().ToUpperInvariant(), Colour = colours.ForHitResult(Result), Font = OsuFont.Numeric.With(size: 20),