From 8c3b541312ebcfe59cea912c755de5dcfb37ee1c Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 21 Apr 2022 22:35:07 +0900 Subject: [PATCH 01/38] Add state for when user is ready for gameplay --- .../Online/Multiplayer/MultiplayerRoomUser.cs | 16 ++++++++++++++++ .../Online/Multiplayer/MultiplayerUserState.cs | 8 +++++++- .../Multiplayer/Participants/StateDisplay.cs | 1 + 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs b/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs index f0b7dcbff8..50e539e8a6 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs @@ -65,5 +65,21 @@ namespace osu.Game.Online.Multiplayer } public override int GetHashCode() => UserID.GetHashCode(); + + /// + /// Whether this user has finished loading and can start gameplay. + /// + public bool CanStartGameplay() + { + switch (State) + { + case MultiplayerUserState.Loaded: + case MultiplayerUserState.ReadyForGameplay: + return true; + + default: + return false; + } + } } } diff --git a/osu.Game/Online/Multiplayer/MultiplayerUserState.cs b/osu.Game/Online/Multiplayer/MultiplayerUserState.cs index c467ff84bb..d1369a7970 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerUserState.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerUserState.cs @@ -29,10 +29,16 @@ namespace osu.Game.Online.Multiplayer WaitingForLoad, /// - /// The user's client has marked itself as loaded and ready to begin gameplay. + /// The user has marked itself as loaded, but may still be adjusting settings prior to being ready for gameplay. + /// Players remaining in this state for an extended period of time will be automatically transitioned to the state by the server. /// Loaded, + /// + /// The user has finished adjusting settings and is ready to start gameplay. + /// + ReadyForGameplay, + /// /// The user is currently playing in a game. This is a reserved state, and is set by the server. /// diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/StateDisplay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/StateDisplay.cs index 2616b07c1f..658fc43e8d 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/StateDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/StateDisplay.cs @@ -112,6 +112,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants break; case MultiplayerUserState.Loaded: + case MultiplayerUserState.ReadyForGameplay: text.Text = "loaded"; icon.Icon = FontAwesome.Solid.DotCircle; icon.Colour = colours.YellowLight; From 08d250fe587e854affd32332f6d10dfd51bc5e61 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 21 Apr 2022 22:37:26 +0900 Subject: [PATCH 02/38] Rename MatchStarted() -> GameplayStarted() --- .../Visual/Multiplayer/TestSceneMultiplayerPlayer.cs | 2 +- osu.Game/Online/Multiplayer/IMultiplayerClient.cs | 5 +++-- osu.Game/Online/Multiplayer/MultiplayerClient.cs | 6 +++--- osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs | 2 +- .../Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs | 6 +++--- osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs | 2 +- 6 files changed, 12 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs index 312281ac18..e05580fed6 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs @@ -35,7 +35,7 @@ namespace osu.Game.Tests.Visual.Multiplayer }); AddUntilStep("wait for player to be current", () => player.IsCurrentScreen() && player.IsLoaded); - AddStep("start gameplay", () => ((IMultiplayerClient)MultiplayerClient).MatchStarted()); + AddStep("start gameplay", () => ((IMultiplayerClient)MultiplayerClient).GameplayStarted()); } [Test] diff --git a/osu.Game/Online/Multiplayer/IMultiplayerClient.cs b/osu.Game/Online/Multiplayer/IMultiplayerClient.cs index 3e6821b1cd..43ccfd36ed 100644 --- a/osu.Game/Online/Multiplayer/IMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerClient.cs @@ -98,9 +98,10 @@ namespace osu.Game.Online.Multiplayer Task LoadRequested(); /// - /// Signals that a match has started. All users in the state should begin gameplay as soon as possible. + /// Signals that gameplay has started. + /// All users in the or states should begin gameplay as soon as possible. /// - Task MatchStarted(); + Task GameplayStarted(); /// /// Signals that the match has ended, all players have finished and results are ready to be displayed. diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 967220abbf..a9fdf1110f 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -72,7 +72,7 @@ namespace osu.Game.Online.Multiplayer /// /// Invoked when the multiplayer server requests gameplay to be started. /// - public event Action? MatchStarted; + public event Action? GameplayStarted; /// /// Invoked when the multiplayer server has finished collating results. @@ -604,14 +604,14 @@ namespace osu.Game.Online.Multiplayer return Task.CompletedTask; } - Task IMultiplayerClient.MatchStarted() + Task IMultiplayerClient.GameplayStarted() { Scheduler.Add(() => { if (Room == null) return; - MatchStarted?.Invoke(); + GameplayStarted?.Invoke(); }, false); return Task.CompletedTask; diff --git a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs index 7e62908ecd..de93233830 100644 --- a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs @@ -54,7 +54,7 @@ namespace osu.Game.Online.Multiplayer 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.GameplayStarted), ((IMultiplayerClient)this).GameplayStarted); connection.On(nameof(IMultiplayerClient.ResultsReady), ((IMultiplayerClient)this).ResultsReady); connection.On>(nameof(IMultiplayerClient.UserModsChanged), ((IMultiplayerClient)this).UserModsChanged); connection.On(nameof(IMultiplayerClient.UserBeatmapAvailabilityChanged), ((IMultiplayerClient)this).UserBeatmapAvailabilityChanged); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index 70f8f1b752..d3a78a43a5 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -115,7 +115,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer if (!ValidForResume) return; // token retrieval may have failed. - client.MatchStarted += onMatchStarted; + client.GameplayStarted += onGameplayStarted; client.ResultsReady += onResultsReady; ScoreProcessor.HasCompleted.BindValueChanged(completed => @@ -175,7 +175,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer leaderboardFlow.Position = new Vector2(padding, padding + HUDOverlay.TopScoringElementsHeight); } - private void onMatchStarted() => Scheduler.Add(() => + private void onGameplayStarted() => Scheduler.Add(() => { if (!this.IsCurrentScreen()) return; @@ -223,7 +223,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer if (client != null) { - client.MatchStarted -= onMatchStarted; + client.GameplayStarted -= onGameplayStarted; client.ResultsReady -= onResultsReady; } } diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 21774b73a0..725499d0e5 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -155,7 +155,7 @@ namespace osu.Game.Tests.Visual.Multiplayer foreach (var u in Room.Users.Where(u => u.State == MultiplayerUserState.Loaded)) ChangeUserState(u.UserID, MultiplayerUserState.Playing); - ((IMultiplayerClient)this).MatchStarted(); + ((IMultiplayerClient)this).GameplayStarted(); ChangeRoomState(MultiplayerRoomState.Playing); } From 59622deb1fcf0d06b60302698bad24238e66e49e Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 21 Apr 2022 22:38:58 +0900 Subject: [PATCH 03/38] Add LoadAborted() event --- .../Online/Multiplayer/IMultiplayerClient.cs | 7 ++++++- .../Online/Multiplayer/MultiplayerClient.cs | 18 ++++++++++++++++++ .../Multiplayer/OnlineMultiplayerClient.cs | 1 + 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Multiplayer/IMultiplayerClient.cs b/osu.Game/Online/Multiplayer/IMultiplayerClient.cs index 43ccfd36ed..2f454ea835 100644 --- a/osu.Game/Online/Multiplayer/IMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerClient.cs @@ -93,10 +93,15 @@ namespace osu.Game.Online.Multiplayer Task UserModsChanged(int userId, IEnumerable mods); /// - /// Signals that a match is to be started. This will *only* be sent to clients which are to begin loading at this point. + /// Signals that the match is starting and the loading of gameplay should be started. This will *only* be sent to clients which are to begin loading at this point. /// Task LoadRequested(); + /// + /// Signals that loading of gameplay is to be aborted. + /// + Task LoadAborted(); + /// /// Signals that gameplay has started. /// All users in the or states should begin gameplay as soon as possible. diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index a9fdf1110f..cae675b406 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -69,6 +69,11 @@ namespace osu.Game.Online.Multiplayer /// public virtual event Action? LoadRequested; + /// + /// Invoked when the multiplayer server requests loading of play to be aborted. + /// + public event Action? LoadAborted; + /// /// Invoked when the multiplayer server requests gameplay to be started. /// @@ -604,6 +609,19 @@ namespace osu.Game.Online.Multiplayer return Task.CompletedTask; } + Task IMultiplayerClient.LoadAborted() + { + Scheduler.Add(() => + { + if (Room == null) + return; + + LoadAborted?.Invoke(); + }, false); + + return Task.CompletedTask; + } + Task IMultiplayerClient.GameplayStarted() { Scheduler.Add(() => diff --git a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs index de93233830..4dc23d8b85 100644 --- a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs @@ -55,6 +55,7 @@ namespace osu.Game.Online.Multiplayer connection.On(nameof(IMultiplayerClient.UserStateChanged), ((IMultiplayerClient)this).UserStateChanged); connection.On(nameof(IMultiplayerClient.LoadRequested), ((IMultiplayerClient)this).LoadRequested); connection.On(nameof(IMultiplayerClient.GameplayStarted), ((IMultiplayerClient)this).GameplayStarted); + connection.On(nameof(IMultiplayerClient.LoadAborted), ((IMultiplayerClient)this).LoadAborted); connection.On(nameof(IMultiplayerClient.ResultsReady), ((IMultiplayerClient)this).ResultsReady); connection.On>(nameof(IMultiplayerClient.UserModsChanged), ((IMultiplayerClient)this).UserModsChanged); connection.On(nameof(IMultiplayerClient.UserBeatmapAvailabilityChanged), ((IMultiplayerClient)this).UserBeatmapAvailabilityChanged); From 41355384bdab55039c78b59f11500d2d1d497761 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 21 Apr 2022 22:55:13 +0900 Subject: [PATCH 04/38] Add support for gameplay abort/force start --- .../OnlinePlay/Multiplayer/Multiplayer.cs | 18 ++++++++++++++++++ .../Multiplayer/MultiplayerPlayer.cs | 13 +++++++++---- .../Multiplayer/MultiplayerPlayerLoader.cs | 10 ++++++++++ osu.Game/Screens/Play/PlayerLoader.cs | 12 ++++++++---- 4 files changed, 45 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs index 2482d52492..c78756771a 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using osu.Framework.Allocation; +using osu.Framework.Logging; using osu.Framework.Screens; using osu.Game.Online.Multiplayer; using osu.Game.Screens.OnlinePlay.Components; @@ -20,6 +21,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer base.LoadComplete(); client.RoomUpdated += onRoomUpdated; + client.LoadAborted += onLoadAborted; onRoomUpdated(); } @@ -35,6 +37,16 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer transitionFromResults(); } + private void onLoadAborted() + { + // If the server aborts gameplay for this user (due to loading too slow), exit gameplay screens. + if (!this.IsCurrentScreen()) + { + Logger.Log("Gameplay aborted because loading the beatmap took too long.", LoggingTarget.Runtime, LogLevel.Important); + this.MakeCurrent(); + } + } + public override void OnResuming(IScreen last) { base.OnResuming(last); @@ -42,9 +54,15 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer if (client.Room == null) return; + Debug.Assert(client.LocalUser != null); + if (!(last is MultiplayerPlayerLoader playerLoader)) return; + // Nothing needs to be done if already in the idle state (e.g. via load being aborted by the server). + if (client.LocalUser.State == MultiplayerUserState.Idle) + return; + // If gameplay wasn't finished, then we have a simple path back to the idle state by aborting gameplay. if (!playerLoader.GameplayPassed) { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index d3a78a43a5..7dd57ee50f 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -133,6 +133,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer failAndBail(); } }), true); + + client.ChangeState(MultiplayerUserState.Loaded) + .ContinueWith(task => failAndBail(task.Exception?.Message ?? "Server error"), TaskContinuationOptions.NotOnRanToCompletion); } protected override void LoadComplete() @@ -144,10 +147,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer 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); + if (client.LocalUser?.State == MultiplayerUserState.Loaded) + { + // block base call, but let the server know we are ready to start. + loadingDisplay.Show(); + client.ChangeState(MultiplayerUserState.ReadyForGameplay); + } } private void failAndBail(string message = null) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayerLoader.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayerLoader.cs index 772651727e..9964452f61 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayerLoader.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayerLoader.cs @@ -2,7 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Allocation; using osu.Framework.Screens; +using osu.Game.Online.Multiplayer; using osu.Game.Screens.Play; namespace osu.Game.Screens.OnlinePlay.Multiplayer @@ -11,6 +13,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { public bool GameplayPassed => player?.GameplayState.HasPassed == true; + [Resolved] + private MultiplayerClient multiplayerClient { get; set; } + private Player player; public MultiplayerPlayerLoader(Func createPlayer) @@ -18,6 +23,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { } + protected override bool ReadyForGameplay => + base.ReadyForGameplay + // The server is forcefully starting gameplay. + || multiplayerClient.LocalUser?.State == MultiplayerUserState.Playing; + public override void OnSuspending(IScreen next) { base.OnSuspending(next); diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index aea39bc8ff..f7dae6eaa9 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -92,11 +92,15 @@ namespace osu.Game.Screens.Play !playerConsumed // don't push unless the player is completely loaded && CurrentPlayer?.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. + // don't push unless the player is ready to start gameplay + && ReadyForGameplay; + + protected virtual bool ReadyForGameplay => + // not ready if the user is hovering one of the panes, unless they are idle. + (IsHovered || idleTracker.IsIdle.Value) + // not ready if the user is dragging a slider or otherwise. && inputManager.DraggedDrawable == null - // don't push if a focused overlay is visible, like settings. + // not ready if a focused overlay is visible, like settings. && inputManager.FocusedDrawable == null; private readonly Func createPlayer; From cbb07d4011a8db5de79c775f659eaa6ea7f0da34 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 21 Apr 2022 22:56:24 +0900 Subject: [PATCH 05/38] Add countdown classes --- .../Online/Multiplayer/GameplayStartCountdown.cs | 15 +++++++++++++++ .../Online/Multiplayer/MultiplayerCountdown.cs | 1 + osu.Game/Online/SignalRWorkaroundTypes.cs | 3 ++- 3 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Online/Multiplayer/GameplayStartCountdown.cs diff --git a/osu.Game/Online/Multiplayer/GameplayStartCountdown.cs b/osu.Game/Online/Multiplayer/GameplayStartCountdown.cs new file mode 100644 index 0000000000..d3d7f78507 --- /dev/null +++ b/osu.Game/Online/Multiplayer/GameplayStartCountdown.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 MessagePack; + +namespace osu.Game.Online.Multiplayer +{ + /// + /// A indicating that gameplay will start after a given period of time. + /// + [MessagePackObject] + public class GameplayStartCountdown : MultiplayerCountdown + { + } +} diff --git a/osu.Game/Online/Multiplayer/MultiplayerCountdown.cs b/osu.Game/Online/Multiplayer/MultiplayerCountdown.cs index 81190e64c9..308eea3aef 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerCountdown.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerCountdown.cs @@ -14,6 +14,7 @@ namespace osu.Game.Online.Multiplayer /// [MessagePackObject] [Union(0, typeof(MatchStartCountdown))] // IMPORTANT: Add rules to SignalRUnionWorkaroundResolver for new derived types. + [Union(1, typeof(GameplayStartCountdown))] public abstract class MultiplayerCountdown { /// diff --git a/osu.Game/Online/SignalRWorkaroundTypes.cs b/osu.Game/Online/SignalRWorkaroundTypes.cs index 156f916cef..00fdadb87e 100644 --- a/osu.Game/Online/SignalRWorkaroundTypes.cs +++ b/osu.Game/Online/SignalRWorkaroundTypes.cs @@ -24,7 +24,8 @@ namespace osu.Game.Online (typeof(CountdownChangedEvent), typeof(MatchServerEvent)), (typeof(TeamVersusRoomState), typeof(MatchRoomState)), (typeof(TeamVersusUserState), typeof(MatchUserState)), - (typeof(MatchStartCountdown), typeof(MultiplayerCountdown)) + (typeof(MatchStartCountdown), typeof(MultiplayerCountdown)), + (typeof(GameplayStartCountdown), typeof(MultiplayerCountdown)) }; } } From ef1955d8ab2651a9ceff1386acd152762577bf45 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 21 Apr 2022 23:01:16 +0900 Subject: [PATCH 06/38] Make buttons only respond to MatchStartCountdown --- .../Match/MultiplayerCountdownButton.cs | 2 +- .../Multiplayer/Match/MultiplayerReadyButton.cs | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerCountdownButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerCountdownButton.cs index c84fcff11e..1a51aebb76 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerCountdownButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerCountdownButton.cs @@ -77,7 +77,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match private void onRoomUpdated() => Scheduler.AddOnce(() => { - bool countdownActive = multiplayerClient.Room?.Countdown != null; + bool countdownActive = multiplayerClient.Room?.Countdown is MatchStartCountdown; if (countdownActive) { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index 22a0243f8f..a7e18622dc 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -55,7 +55,21 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match private void onRoomUpdated() => Scheduler.AddOnce(() => { - if (countdown != room?.Countdown) + MultiplayerCountdown newCountdown; + + switch (room?.Countdown) + { + case MatchStartCountdown _: + newCountdown = room.Countdown; + break; + + // Clear the countdown with any other (including non-null) countdown values. + default: + newCountdown = null; + break; + } + + if (newCountdown != countdown) { countdown = room?.Countdown; countdownChangeTime = Time.Current; From d6395b14beae1116a3b7271363136c89642a1b98 Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Thu, 28 Apr 2022 05:15:04 +0200 Subject: [PATCH 07/38] Add incompatibilities --- osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 2 ++ osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 10 +++++++++- 5 files changed, 14 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs b/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs index f46573c494..5d262cd95e 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModClassic : ModClassic, IApplicableToHitObject, IApplicableToDrawableHitObject, IApplicableToDrawableRuleset { - public override Type[] IncompatibleMods => new[] { typeof(OsuModStrictTracking) }; + public override Type[] IncompatibleMods => new[] { typeof(OsuModStrictTracking), typeof(OsuModTarget) }; [SettingSource("No slider head accuracy requirement", "Scores sliders proportionally to the number of ticks hit.")] public Bindable NoSliderHeadAccuracy { get; } = new BindableBool(true); diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index fea9246035..7a7e189c29 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -22,6 +22,8 @@ namespace osu.Game.Rulesets.Osu.Mods { public override string Description => "It never gets boring!"; + public override Type[] IncompatibleMods => new[] { typeof(OsuModTarget) }; + private static readonly float playfield_diagonal = OsuPlayfield.BASE_SIZE.LengthFast; private Random? rng; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs b/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs index 9be0dc748a..d9ab749ad3 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override ModType Type => ModType.Automation; public override string Description => @"Spinners will be automatically completed."; public override double ScoreMultiplier => 0.9; - public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(OsuModAutopilot) }; + public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(OsuModAutopilot), typeof(OsuModTarget) }; public void ApplyToDrawableHitObject(DrawableHitObject hitObject) { diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs b/osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs index ee325db66a..ab45e5192d 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override ModType Type => ModType.DifficultyIncrease; public override string Description => @"Follow circles just got serious..."; public override double ScoreMultiplier => 1.0; - public override Type[] IncompatibleMods => new[] { typeof(ModClassic) }; + public override Type[] IncompatibleMods => new[] { typeof(ModClassic), typeof(OsuModTarget) }; public void ApplyToDrawableHitObject(DrawableHitObject drawable) { diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 4fab9b6a5a..4d6c09f919 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -42,7 +42,15 @@ namespace osu.Game.Rulesets.Osu.Mods public override string Description => @"Practice keeping up with the beat of the song."; public override double ScoreMultiplier => 1; - public override Type[] IncompatibleMods => new[] { typeof(IRequiresApproachCircles), typeof(OsuModSuddenDeath) }; + public override Type[] IncompatibleMods => new[] + { + typeof(IRequiresApproachCircles), + typeof(OsuModClassic), + typeof(OsuModRandom), + typeof(OsuModSpunOut), + typeof(OsuModStrictTracking), + typeof(OsuModSuddenDeath) + }; [SettingSource("Seed", "Use a custom seed instead of a random one", SettingControlType = typeof(SettingsNumberBox))] public Bindable Seed { get; } = new Bindable From fe0fcc7e9ea522ebfa9bca7a71603de316a80371 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 28 Apr 2022 20:00:38 +0900 Subject: [PATCH 08/38] Rename countdown object --- .../Multiplayer/ForceGameplayStartCountdown.cs | 17 +++++++++++++++++ .../Multiplayer/GameplayStartCountdown.cs | 15 --------------- .../Online/Multiplayer/MultiplayerCountdown.cs | 2 +- osu.Game/Online/SignalRWorkaroundTypes.cs | 2 +- 4 files changed, 19 insertions(+), 17 deletions(-) create mode 100644 osu.Game/Online/Multiplayer/ForceGameplayStartCountdown.cs delete mode 100644 osu.Game/Online/Multiplayer/GameplayStartCountdown.cs diff --git a/osu.Game/Online/Multiplayer/ForceGameplayStartCountdown.cs b/osu.Game/Online/Multiplayer/ForceGameplayStartCountdown.cs new file mode 100644 index 0000000000..4ec5019a07 --- /dev/null +++ b/osu.Game/Online/Multiplayer/ForceGameplayStartCountdown.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 MessagePack; + +namespace osu.Game.Online.Multiplayer +{ + /// + /// A started by the server when clients being to load. + /// Indicates how long until gameplay will forcefully start, excluding any users which have not completed loading, + /// and forcing progression of any clients that are blocking load due to user interaction. + /// + [MessagePackObject] + public class ForceGameplayStartCountdown : MultiplayerCountdown + { + } +} diff --git a/osu.Game/Online/Multiplayer/GameplayStartCountdown.cs b/osu.Game/Online/Multiplayer/GameplayStartCountdown.cs deleted file mode 100644 index d3d7f78507..0000000000 --- a/osu.Game/Online/Multiplayer/GameplayStartCountdown.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using MessagePack; - -namespace osu.Game.Online.Multiplayer -{ - /// - /// A indicating that gameplay will start after a given period of time. - /// - [MessagePackObject] - public class GameplayStartCountdown : MultiplayerCountdown - { - } -} diff --git a/osu.Game/Online/Multiplayer/MultiplayerCountdown.cs b/osu.Game/Online/Multiplayer/MultiplayerCountdown.cs index 308eea3aef..dbf2ab667b 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerCountdown.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerCountdown.cs @@ -14,7 +14,7 @@ namespace osu.Game.Online.Multiplayer /// [MessagePackObject] [Union(0, typeof(MatchStartCountdown))] // IMPORTANT: Add rules to SignalRUnionWorkaroundResolver for new derived types. - [Union(1, typeof(GameplayStartCountdown))] + [Union(1, typeof(ForceGameplayStartCountdown))] public abstract class MultiplayerCountdown { /// diff --git a/osu.Game/Online/SignalRWorkaroundTypes.cs b/osu.Game/Online/SignalRWorkaroundTypes.cs index 00fdadb87e..d1f0ba725f 100644 --- a/osu.Game/Online/SignalRWorkaroundTypes.cs +++ b/osu.Game/Online/SignalRWorkaroundTypes.cs @@ -25,7 +25,7 @@ namespace osu.Game.Online (typeof(TeamVersusRoomState), typeof(MatchRoomState)), (typeof(TeamVersusUserState), typeof(MatchUserState)), (typeof(MatchStartCountdown), typeof(MultiplayerCountdown)), - (typeof(GameplayStartCountdown), typeof(MultiplayerCountdown)) + (typeof(ForceGameplayStartCountdown), typeof(MultiplayerCountdown)) }; } } From 1d8ac6917dc4a3fe58f67b254a32143b2c9bd632 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 28 Apr 2022 20:10:47 +0900 Subject: [PATCH 09/38] Send Loaded state from PlayerLoader on update thread --- .../Multiplayer/MultiplayerPlayer.cs | 3 --- .../Multiplayer/MultiplayerPlayerLoader.cs | 22 +++++++++++++++++++ osu.Game/Screens/Play/PlayerLoader.cs | 10 ++++++++- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index 7dd57ee50f..02ff040a94 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -133,9 +133,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer failAndBail(); } }), true); - - client.ChangeState(MultiplayerUserState.Loaded) - .ContinueWith(task => failAndBail(task.Exception?.Message ?? "Server error"), TaskContinuationOptions.NotOnRanToCompletion); } protected override void LoadComplete() diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayerLoader.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayerLoader.cs index 236df7917c..7f01bd64ab 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayerLoader.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayerLoader.cs @@ -2,7 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Threading.Tasks; using osu.Framework.Allocation; +using osu.Framework.Logging; using osu.Framework.Screens; using osu.Game.Online.Multiplayer; using osu.Game.Screens.Play; @@ -28,6 +30,26 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer // The server is forcefully starting gameplay. || multiplayerClient.LocalUser?.State == MultiplayerUserState.Playing; + protected override void OnPlayerLoaded() + { + base.OnPlayerLoaded(); + + multiplayerClient.ChangeState(MultiplayerUserState.Loaded) + .ContinueWith(task => failAndBail(task.Exception?.Message ?? "Server error"), TaskContinuationOptions.NotOnRanToCompletion); + } + + private void failAndBail(string message = null) + { + if (!string.IsNullOrEmpty(message)) + Logger.Log(message, LoggingTarget.Runtime, LogLevel.Important); + + Schedule(() => + { + if (this.IsCurrentScreen()) + this.Exit(); + }); + } + public override void OnSuspending(ScreenTransitionEvent e) { base.OnSuspending(e); diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index bdfa822cb0..d75466764d 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -368,7 +368,15 @@ namespace osu.Game.Screens.Play CurrentPlayer.RestartCount = restartCount++; CurrentPlayer.RestartRequested = restartRequested; - LoadTask = LoadComponentAsync(CurrentPlayer, _ => MetadataInfo.Loading = false); + LoadTask = LoadComponentAsync(CurrentPlayer, _ => + { + MetadataInfo.Loading = false; + OnPlayerLoaded(); + }); + } + + protected virtual void OnPlayerLoaded() + { } private void restartRequested() From d5702e29558f7845e27e4a60bbf2c88231535d3f Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Thu, 28 Apr 2022 18:29:37 +0200 Subject: [PATCH 10/38] Append incompatible mods to `base.IncompatibleMods` --- osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs b/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs index 5d262cd95e..b27b1a7267 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModClassic : ModClassic, IApplicableToHitObject, IApplicableToDrawableHitObject, IApplicableToDrawableRuleset { - public override Type[] IncompatibleMods => new[] { typeof(OsuModStrictTracking), typeof(OsuModTarget) }; + public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModStrictTracking), typeof(OsuModTarget) }).ToArray(); [SettingSource("No slider head accuracy requirement", "Scores sliders proportionally to the number of ticks hit.")] public Bindable NoSliderHeadAccuracy { get; } = new BindableBool(true); diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 7a7e189c29..23500f5da6 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Mods { public override string Description => "It never gets boring!"; - public override Type[] IncompatibleMods => new[] { typeof(OsuModTarget) }; + public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModTarget)).ToArray(); private static readonly float playfield_diagonal = OsuPlayfield.BASE_SIZE.LengthFast; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 4d6c09f919..bb21550c56 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override string Description => @"Practice keeping up with the beat of the song."; public override double ScoreMultiplier => 1; - public override Type[] IncompatibleMods => new[] + public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(IRequiresApproachCircles), typeof(OsuModClassic), @@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.Mods typeof(OsuModSpunOut), typeof(OsuModStrictTracking), typeof(OsuModSuddenDeath) - }; + }).ToArray(); [SettingSource("Seed", "Use a custom seed instead of a random one", SettingControlType = typeof(SettingsNumberBox))] public Bindable Seed { get; } = new Bindable From fa5c05120c913dd9c5931d7e92a84d8ba5b4b4c0 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 28 Apr 2022 20:04:52 +0300 Subject: [PATCH 11/38] Allow rewinding random in song select with Shift + Left Click --- osu.Game/Screens/Select/FooterButtonRandom.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/osu.Game/Screens/Select/FooterButtonRandom.cs b/osu.Game/Screens/Select/FooterButtonRandom.cs index 1d4722cf5d..5cdf381879 100644 --- a/osu.Game/Screens/Select/FooterButtonRandom.cs +++ b/osu.Game/Screens/Select/FooterButtonRandom.cs @@ -59,6 +59,18 @@ namespace osu.Game.Screens.Select }; } + protected override bool OnClick(ClickEvent e) + { + rewindSearch = e.ShiftPressed; + return base.OnClick(e); + } + + protected override void OnMouseUp(MouseUpEvent e) + { + rewindSearch = false; + base.OnMouseUp(e); + } + public override bool OnPressed(KeyBindingPressEvent e) { rewindSearch = e.Action == GlobalAction.SelectPreviousRandom; From e31fdc28ddbadae1e8bd2150d6ea695bd4b133e6 Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Thu, 28 Apr 2022 22:31:48 +0200 Subject: [PATCH 12/38] Make Classic and Target compatible again --- osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs b/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs index b27b1a7267..76ff361ce3 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModClassic : ModClassic, IApplicableToHitObject, IApplicableToDrawableHitObject, IApplicableToDrawableRuleset { - public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModStrictTracking), typeof(OsuModTarget) }).ToArray(); + public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModStrictTracking)).ToArray(); [SettingSource("No slider head accuracy requirement", "Scores sliders proportionally to the number of ticks hit.")] public Bindable NoSliderHeadAccuracy { get; } = new BindableBool(true); diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index bb21550c56..5b121f4673 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -45,7 +45,6 @@ namespace osu.Game.Rulesets.Osu.Mods public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(IRequiresApproachCircles), - typeof(OsuModClassic), typeof(OsuModRandom), typeof(OsuModSpunOut), typeof(OsuModStrictTracking), From 294340279eb5614a8a6d1c23d305b6ba9aa5a174 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 29 Apr 2022 04:14:09 +0300 Subject: [PATCH 13/38] Add inputting test coverage --- .../SongSelect/TestSceneSongSelectFooter.cs | 90 ++++++++++++++----- 1 file changed, 70 insertions(+), 20 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectFooter.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectFooter.cs index 0ac65b357c..6485123b29 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectFooter.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectFooter.cs @@ -1,35 +1,85 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using NUnit.Framework; using osu.Framework.Graphics; using osu.Game.Screens.Select; +using osuTK.Input; namespace osu.Game.Tests.Visual.SongSelect { public class TestSceneSongSelectFooter : OsuManualInputManagerTestScene { - public TestSceneSongSelectFooter() - { - AddStep("Create footer", () => - { - Footer footer; - AddRange(new Drawable[] - { - footer = new Footer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - } - }); + private FooterButtonRandom randomButton; - footer.AddButton(new FooterButtonMods(), null); - footer.AddButton(new FooterButtonRandom - { - NextRandom = () => { }, - PreviousRandom = () => { }, - }, null); - footer.AddButton(new FooterButtonOptions(), null); + private bool nextRandomCalled; + private bool previousRandomCalled; + + [SetUp] + public void SetUp() => Schedule(() => + { + nextRandomCalled = false; + previousRandomCalled = false; + + Footer footer; + + Child = footer = new Footer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }; + + footer.AddButton(new FooterButtonMods(), null); + footer.AddButton(randomButton = new FooterButtonRandom + { + NextRandom = () => nextRandomCalled = true, + PreviousRandom = () => previousRandomCalled = true, + }, null); + footer.AddButton(new FooterButtonOptions(), null); + }); + + [Test] + public void TestFooterRandom() + { + AddStep("press F2", () => InputManager.Key(Key.F2)); + AddAssert("next random invoked", () => nextRandomCalled && !previousRandomCalled); + } + + [Test] + public void TestFooterRandomViaMouse() + { + AddStep("click button", () => + { + InputManager.MoveMouseTo(randomButton); + InputManager.Click(MouseButton.Left); }); + AddAssert("next random invoked", () => nextRandomCalled && !previousRandomCalled); + } + + [Test] + public void TestFooterRewind() + { + AddStep("press Shift+F2", () => + { + InputManager.PressKey(Key.LShift); + InputManager.PressKey(Key.F2); + InputManager.ReleaseKey(Key.F2); + InputManager.ReleaseKey(Key.LShift); + }); + AddAssert("previous random invoked", () => previousRandomCalled && !nextRandomCalled); + } + + [Test] + public void TestFooterRewindViaMouse() + { + AddStep("shift + click button", () => + { + InputManager.PressKey(Key.LShift); + InputManager.MoveMouseTo(randomButton); + InputManager.Click(MouseButton.Left); + InputManager.ReleaseKey(Key.LShift); + }); + AddAssert("previous random invoked", () => previousRandomCalled && !nextRandomCalled); } } } From 7442ae283cee27f591c76c80de62c915d190829d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 29 Apr 2022 05:24:00 +0300 Subject: [PATCH 14/38] Tidy up test cases a touch --- .../Mods/TestSceneOsuModAlternate.cs | 60 +++++++++++-------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAlternate.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAlternate.cs index de1f61a0bd..67654c11f2 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAlternate.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAlternate.cs @@ -16,31 +16,6 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods { public class TestSceneOsuModAlternate : OsuModTestScene { - [Test] - public void TestInputAtIntro() => CreateModTest(new ModTestData - { - Mod = new OsuModAlternate(), - PassCondition = () => Player.ScoreProcessor.Combo.Value == 1, - Autoplay = false, - Beatmap = new Beatmap - { - HitObjects = new List - { - new HitCircle - { - StartTime = 1000, - Position = new Vector2(100), - }, - }, - }, - ReplayFrames = new List - { - new OsuReplayFrame(500, new Vector2(200), OsuAction.LeftButton), - new OsuReplayFrame(501, new Vector2(200)), - new OsuReplayFrame(1000, new Vector2(100), OsuAction.LeftButton), - } - }); - [Test] public void TestInputAlternating() => CreateModTest(new ModTestData { @@ -116,6 +91,39 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods } }); + /// + /// Ensures alternation is reset before the first hitobject after intro. + /// + [Test] + public void TestInputSingularAtIntro() => CreateModTest(new ModTestData + { + Mod = new OsuModAlternate(), + PassCondition = () => Player.ScoreProcessor.Combo.Value == 1, + Autoplay = false, + Beatmap = new Beatmap + { + HitObjects = new List + { + new HitCircle + { + StartTime = 1000, + Position = new Vector2(100), + }, + }, + }, + ReplayFrames = new List + { + // first press during intro. + new OsuReplayFrame(500, new Vector2(200), OsuAction.LeftButton), + new OsuReplayFrame(501, new Vector2(200)), + // press same key at hitobject and ensure it has been hit. + new OsuReplayFrame(1000, new Vector2(100), OsuAction.LeftButton), + } + }); + + /// + /// Ensures alternation is reset after a break. + /// [Test] public void TestInputSingularWithBreak() => CreateModTest(new ModTestData { @@ -144,8 +152,10 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods }, ReplayFrames = new List { + // first press to start alternate lock. new OsuReplayFrame(500, new Vector2(100), OsuAction.LeftButton), new OsuReplayFrame(501, new Vector2(100)), + // press same key at second hitobject and ensure it has been hit. new OsuReplayFrame(2500, new Vector2(100), OsuAction.LeftButton), new OsuReplayFrame(2501, new Vector2(100)), } From 723d70be7096b300e86568b937b2f2998f4ded56 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 29 Apr 2022 05:26:19 +0300 Subject: [PATCH 15/38] Change `TestInputSingularWithBreak` to reproduce case of pressing before second object --- .../Mods/TestSceneOsuModAlternate.cs | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAlternate.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAlternate.cs index 67654c11f2..5e46498aca 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAlternate.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAlternate.cs @@ -122,19 +122,19 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods }); /// - /// Ensures alternation is reset after a break. + /// Ensures alternation is reset before the first hitobject after a break. /// [Test] public void TestInputSingularWithBreak() => CreateModTest(new ModTestData { Mod = new OsuModAlternate(), - PassCondition = () => Player.ScoreProcessor.Combo.Value == 2, + PassCondition = () => Player.ScoreProcessor.Combo.Value == 0 && Player.ScoreProcessor.HighestCombo.Value == 2, Autoplay = false, Beatmap = new Beatmap { Breaks = new List { - new BreakPeriod(500, 2250), + new BreakPeriod(500, 2000), }, HitObjects = new List { @@ -146,8 +146,13 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods new HitCircle { StartTime = 2500, - Position = new Vector2(100), - } + Position = new Vector2(500, 100), + }, + new HitCircle + { + StartTime = 3000, + Position = new Vector2(500, 100), + }, } }, ReplayFrames = new List @@ -155,9 +160,15 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods // first press to start alternate lock. new OsuReplayFrame(500, new Vector2(100), OsuAction.LeftButton), new OsuReplayFrame(501, new Vector2(100)), + // press same key after break but before hit object. + new OsuReplayFrame(2250, new Vector2(300, 100), OsuAction.LeftButton), + new OsuReplayFrame(2251, new Vector2(300, 100)), // press same key at second hitobject and ensure it has been hit. - new OsuReplayFrame(2500, new Vector2(100), OsuAction.LeftButton), - new OsuReplayFrame(2501, new Vector2(100)), + new OsuReplayFrame(2500, new Vector2(500, 100), OsuAction.LeftButton), + new OsuReplayFrame(2501, new Vector2(500, 100)), + // press same key at third hitobject and ensure it has been missed. + new OsuReplayFrame(3000, new Vector2(500, 100), OsuAction.LeftButton), + new OsuReplayFrame(3001, new Vector2(500, 100)), } }); } From a4ca8bfe75ab0aa19c3aa20275492cdfe4a182fa Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 29 Apr 2022 05:26:56 +0300 Subject: [PATCH 16/38] Improve "Alternate" to reset before first hitobject after break --- osu.Game.Rulesets.Osu/Mods/OsuModAlternate.cs | 51 +++++++++++-------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModAlternate.cs b/osu.Game.Rulesets.Osu/Mods/OsuModAlternate.cs index 46b97dd23b..dfa28a537a 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModAlternate.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModAlternate.cs @@ -2,21 +2,24 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Linq; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Screens.Play; +using osu.Game.Utils; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModAlternate : Mod, IApplicableToDrawableRuleset, IApplicableToPlayer + public class OsuModAlternate : Mod, IApplicableToDrawableRuleset { public override string Name => @"Alternate"; public override string Acronym => @"AL"; @@ -26,9 +29,16 @@ namespace osu.Game.Rulesets.Osu.Mods public override ModType Type => ModType.Conversion; public override IconUsage? Icon => FontAwesome.Solid.Keyboard; - private double firstObjectValidJudgementTime; - private IBindable isBreakTime; private const double flash_duration = 1000; + + /// + /// A tracker for periods where alternate should not be forced (i.e. non-gameplay periods). + /// + /// + /// This is different from in that the periods here end strictly at the first object after the break, rather than the break's end time. + /// + private PeriodTracker nonGameplayPeriods; + private OsuAction? lastActionPressed; private DrawableRuleset ruleset; @@ -39,29 +49,30 @@ namespace osu.Game.Rulesets.Osu.Mods ruleset = drawableRuleset; drawableRuleset.KeyBindingInputManager.Add(new InputInterceptor(this)); - var firstHitObject = ruleset.Objects.FirstOrDefault(); - firstObjectValidJudgementTime = (firstHitObject?.StartTime ?? 0) - (firstHitObject?.HitWindows.WindowFor(HitResult.Meh) ?? 0); + var periods = new List(); + + if (drawableRuleset.Objects.Any()) + { + periods.Add(new Period(int.MinValue, getValidJudgementTime(ruleset.Objects.First()) - 1)); + + foreach (BreakPeriod b in drawableRuleset.Beatmap.Breaks) + periods.Add(new Period(b.StartTime, getValidJudgementTime(ruleset.Objects.First(h => h.StartTime >= b.EndTime)) - 1)); + + static double getValidJudgementTime(HitObject hitObject) => hitObject.StartTime - hitObject.HitWindows.WindowFor(HitResult.Meh); + } + + nonGameplayPeriods = new PeriodTracker(periods); gameplayClock = drawableRuleset.FrameStableClock; } - public void ApplyToPlayer(Player player) - { - isBreakTime = player.IsBreakTime.GetBoundCopy(); - isBreakTime.ValueChanged += e => - { - if (e.NewValue) - lastActionPressed = null; - }; - } - private bool checkCorrectAction(OsuAction action) { - if (isBreakTime.Value) - return true; - - if (gameplayClock.CurrentTime < firstObjectValidJudgementTime) + if (nonGameplayPeriods.IsInAny(gameplayClock.CurrentTime)) + { + lastActionPressed = null; return true; + } switch (action) { From 7e3d1511c67d620ba2dc63059c6cec86ed25d244 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 29 Apr 2022 06:55:44 +0300 Subject: [PATCH 17/38] Hide "Rank Achieved" sorting mode until it's supported --- osu.Game/Screens/Select/Filter/SortMode.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/Filter/SortMode.cs b/osu.Game/Screens/Select/Filter/SortMode.cs index 1ab54fa069..5dfa2a2664 100644 --- a/osu.Game/Screens/Select/Filter/SortMode.cs +++ b/osu.Game/Screens/Select/Filter/SortMode.cs @@ -27,8 +27,9 @@ namespace osu.Game.Screens.Select.Filter [LocalisableDescription(typeof(SortStrings), nameof(SortStrings.ArtistTracksLength))] Length, - [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchFiltersRank))] - RankAchieved, + // todo: pending support (https://github.com/ppy/osu/issues/4917) + // [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchFiltersRank))] + // RankAchieved, [LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowInfoSource))] Source, From ef56dc07b5eea8392407aec09de8f3adbd1e8681 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 29 Apr 2022 06:55:58 +0300 Subject: [PATCH 18/38] Hide "modding" tab in user profile until it's implemented --- osu.Game/Overlays/Profile/ProfileHeader.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index fab2487c0d..44e0d9c37f 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -31,7 +31,9 @@ namespace osu.Game.Overlays.Profile User.ValueChanged += e => updateDisplay(e.NewValue); TabControl.AddItem(LayoutStrings.HeaderUsersShow); - TabControl.AddItem(LayoutStrings.HeaderUsersModding); + + // todo: pending implementation. + // TabControl.AddItem(LayoutStrings.HeaderUsersModding); centreHeaderContainer.DetailsVisible.BindValueChanged(visible => detailHeaderContainer.Expanded = visible.NewValue, true); } From a9d67d3e92dd9f892d0843ac5961172294346373 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 29 Apr 2022 10:10:19 +0300 Subject: [PATCH 19/38] Change random button text when holding shift key --- osu.Game/Screens/Select/FooterButtonRandom.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/FooterButtonRandom.cs b/osu.Game/Screens/Select/FooterButtonRandom.cs index 5cdf381879..5f8fdce054 100644 --- a/osu.Game/Screens/Select/FooterButtonRandom.cs +++ b/osu.Game/Screens/Select/FooterButtonRandom.cs @@ -25,7 +25,7 @@ namespace osu.Game.Screens.Select { SelectedColour = colours.Green; DeselectedColour = SelectedColour.Opacity(0.5f); - Text = @"random"; + updateText(); Action = () => { @@ -59,6 +59,18 @@ namespace osu.Game.Screens.Select }; } + protected override bool OnKeyDown(KeyDownEvent e) + { + updateText(e.ShiftPressed); + return base.OnKeyDown(e); + } + + protected override void OnKeyUp(KeyUpEvent e) + { + updateText(e.ShiftPressed); + base.OnKeyUp(e); + } + protected override bool OnClick(ClickEvent e) { rewindSearch = e.ShiftPressed; @@ -91,5 +103,7 @@ namespace osu.Game.Screens.Select rewindSearch = false; } } + + private void updateText(bool rewind = false) => Text = rewind ? "rewind" : "random"; } } From 856ca96b66ec0073f42b290cbde260e04267c8eb Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 29 Apr 2022 10:12:20 +0300 Subject: [PATCH 20/38] Allow right-clicking to rewind on random button --- .../SongSelect/TestSceneSongSelectFooter.cs | 16 +++++++++++++- osu.Game/Screens/Select/FooterButtonRandom.cs | 21 ++++++++++++++++--- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectFooter.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectFooter.cs index 6485123b29..f27615eea5 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectFooter.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectFooter.cs @@ -4,6 +4,7 @@ using NUnit.Framework; using osu.Framework.Graphics; using osu.Game.Screens.Select; +using osuTK; using osuTK.Input; namespace osu.Game.Tests.Visual.SongSelect @@ -36,6 +37,8 @@ namespace osu.Game.Tests.Visual.SongSelect PreviousRandom = () => previousRandomCalled = true, }, null); footer.AddButton(new FooterButtonOptions(), null); + + InputManager.MoveMouseTo(Vector2.Zero); }); [Test] @@ -70,7 +73,7 @@ namespace osu.Game.Tests.Visual.SongSelect } [Test] - public void TestFooterRewindViaMouse() + public void TestFooterRewindViaShiftMouseLeft() { AddStep("shift + click button", () => { @@ -81,5 +84,16 @@ namespace osu.Game.Tests.Visual.SongSelect }); AddAssert("previous random invoked", () => previousRandomCalled && !nextRandomCalled); } + + [Test] + public void TestFooterRewindViaMouseRight() + { + AddStep("right click button", () => + { + InputManager.MoveMouseTo(randomButton); + InputManager.Click(MouseButton.Right); + }); + AddAssert("previous random invoked", () => previousRandomCalled && !nextRandomCalled); + } } } diff --git a/osu.Game/Screens/Select/FooterButtonRandom.cs b/osu.Game/Screens/Select/FooterButtonRandom.cs index 5f8fdce054..48d5836826 100644 --- a/osu.Game/Screens/Select/FooterButtonRandom.cs +++ b/osu.Game/Screens/Select/FooterButtonRandom.cs @@ -10,6 +10,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Input.Bindings; using osuTK; +using osuTK.Input; namespace osu.Game.Screens.Select { @@ -73,13 +74,27 @@ namespace osu.Game.Screens.Select protected override bool OnClick(ClickEvent e) { - rewindSearch = e.ShiftPressed; - return base.OnClick(e); + try + { + // this uses OR to handle rewinding when clicks are triggered by other sources (i.e. right button in OnMouseUp). + rewindSearch |= e.ShiftPressed; + return base.OnClick(e); + } + finally + { + rewindSearch = false; + } } protected override void OnMouseUp(MouseUpEvent e) { - rewindSearch = false; + if (e.Button == MouseButton.Right) + { + rewindSearch = true; + TriggerClick(); + return; + } + base.OnMouseUp(e); } From 3829d278451c71aa4223c70520331f970ad79cb7 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 29 Apr 2022 12:19:22 +0300 Subject: [PATCH 21/38] Update overlay ruleset selector design --- osu.Game/Overlays/OverlayRulesetTabItem.cs | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/osu.Game/Overlays/OverlayRulesetTabItem.cs b/osu.Game/Overlays/OverlayRulesetTabItem.cs index 9d4afc94d1..4f58d4c189 100644 --- a/osu.Game/Overlays/OverlayRulesetTabItem.cs +++ b/osu.Game/Overlays/OverlayRulesetTabItem.cs @@ -5,14 +5,12 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets; using osuTK.Graphics; using osuTK; using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; +using osu.Game.Graphics.Containers; namespace osu.Game.Overlays { @@ -26,7 +24,7 @@ namespace osu.Game.Overlays set { accentColour = value; - text.FadeColour(value, 120, Easing.OutQuint); + icon.FadeColour(value, 120, Easing.OutQuint); } } @@ -35,7 +33,7 @@ namespace osu.Game.Overlays [Resolved] private OverlayColourProvider colourProvider { get; set; } - private readonly OsuSpriteText text; + private readonly Drawable icon; public OverlayRulesetTabItem(RulesetInfo value) : base(value) @@ -48,15 +46,14 @@ namespace osu.Game.Overlays { AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, - Spacing = new Vector2(3, 0), - Child = text = new OsuSpriteText + Spacing = new Vector2(5f, 0), + Child = icon = new ConstrainedIconContainer { - Origin = Anchor.Centre, Anchor = Anchor.Centre, - Text = value.Name, - Font = OsuFont.GetFont(size: 14), - ShadowColour = Color4.Black.Opacity(0.75f) - } + Origin = Anchor.Centre, + Size = new Vector2(20f), + Icon = value.CreateInstance().CreateIcon(), + }, }, new HoverClickSounds() }); @@ -91,7 +88,6 @@ namespace osu.Game.Overlays private void updateState() { - text.Font = text.Font.With(weight: Active.Value ? FontWeight.Bold : FontWeight.Medium); AccentColour = Enabled.Value ? getActiveColour() : colourProvider.Foreground1; } From 4016fe1e19695637ea0a954b63ec633fcff2d5b4 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 29 Apr 2022 12:19:47 +0300 Subject: [PATCH 22/38] Adjust profile ruleset selector to new design Looks weird with `AlwaysPresent`. --- .../Profile/Header/Components/ProfileRulesetTabItem.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetTabItem.cs b/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetTabItem.cs index 3d20fba542..15640ddc5e 100644 --- a/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetTabItem.cs +++ b/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetTabItem.cs @@ -23,7 +23,7 @@ namespace osu.Game.Overlays.Profile.Header.Components isDefault = value; - icon.FadeTo(isDefault ? 1 : 0, 200, Easing.OutQuint); + icon.Alpha = isDefault ? 1 : 0; } } @@ -47,7 +47,6 @@ namespace osu.Game.Overlays.Profile.Header.Components Origin = Anchor.Centre, Anchor = Anchor.Centre, Alpha = 0, - AlwaysPresent = true, Icon = FontAwesome.Solid.Star, Size = new Vector2(12), }); From fae8d86e154df9e31674a2b94f66a79c259ba709 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 29 Apr 2022 12:20:33 +0300 Subject: [PATCH 23/38] Fix regressed profile ruleset selector test scene Due to the changes in `APIUser`, which change equality to be based on ID. --- .../Visual/Online/TestSceneProfileRulesetSelector.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs index cbbe8b8eac..4e572b665b 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs @@ -32,14 +32,14 @@ namespace osu.Game.Tests.Visual.Online }; AddStep("set osu! as default", () => selector.SetDefaultRuleset(new OsuRuleset().RulesetInfo)); - AddStep("set mania as default", () => selector.SetDefaultRuleset(new ManiaRuleset().RulesetInfo)); AddStep("set taiko as default", () => selector.SetDefaultRuleset(new TaikoRuleset().RulesetInfo)); AddStep("set catch as default", () => selector.SetDefaultRuleset(new CatchRuleset().RulesetInfo)); + AddStep("set mania as default", () => selector.SetDefaultRuleset(new ManiaRuleset().RulesetInfo)); - AddStep("User with osu as default", () => user.Value = new APIUser { PlayMode = "osu" }); - AddStep("User with mania as default", () => user.Value = new APIUser { PlayMode = "mania" }); - AddStep("User with taiko as default", () => user.Value = new APIUser { PlayMode = "taiko" }); - AddStep("User with catch as default", () => user.Value = new APIUser { PlayMode = "fruits" }); + AddStep("User with osu as default", () => user.Value = new APIUser { Id = 0, PlayMode = "osu" }); + AddStep("User with taiko as default", () => user.Value = new APIUser { Id = 2, PlayMode = "taiko" }); + AddStep("User with catch as default", () => user.Value = new APIUser { Id = 3, PlayMode = "fruits" }); + AddStep("User with mania as default", () => user.Value = new APIUser { Id = 1, PlayMode = "mania" }); AddStep("null user", () => user.Value = null); } } From 5e19bdbf430b76c8124f81afd4e6d04a69978950 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 29 Apr 2022 12:21:12 +0300 Subject: [PATCH 24/38] Refactor beatmap ruleset selector test scene --- .../Online/TestSceneBeatmapRulesetSelector.cs | 99 ++++++++++--------- 1 file changed, 53 insertions(+), 46 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapRulesetSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapRulesetSelector.cs index 63741451f3..c550c9afda 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapRulesetSelector.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapRulesetSelector.cs @@ -4,11 +4,11 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; -using osu.Framework.Graphics.UserInterface; +using osu.Framework.Graphics; +using osu.Framework.Testing; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; using osu.Game.Overlays.BeatmapSet; -using osu.Game.Rulesets; namespace osu.Game.Tests.Visual.Online { @@ -17,79 +17,86 @@ namespace osu.Game.Tests.Visual.Online [Cached] private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); - private readonly TestRulesetSelector selector; + private BeatmapRulesetSelector selector; - public TestSceneBeatmapRulesetSelector() + [SetUp] + public void SetUp() => Schedule(() => Child = selector = new BeatmapRulesetSelector { - Add(selector = new TestRulesetSelector()); - } + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + BeatmapSet = new APIBeatmapSet(), + }); - [Resolved] - private IRulesetStore rulesets { get; set; } + [Test] + public void TestDisplay() + { + AddSliderStep("osu", 0, 100, 0, v => updateBeatmaps(0, v)); + AddSliderStep("taiko", 0, 100, 0, v => updateBeatmaps(1, v)); + AddSliderStep("fruits", 0, 100, 0, v => updateBeatmaps(2, v)); + AddSliderStep("mania", 0, 100, 0, v => updateBeatmaps(3, v)); + + void updateBeatmaps(int ruleset, int count) + { + if (selector == null) + return; + + selector.BeatmapSet = new APIBeatmapSet + { + Beatmaps = selector.BeatmapSet.Beatmaps + .Where(b => b.Ruleset.OnlineID != ruleset) + .Concat(Enumerable.Range(0, count).Select(_ => new APIBeatmap { RulesetID = ruleset })) + .ToArray(), + }; + } + } [Test] public void TestMultipleRulesetsBeatmapSet() { - var enabledRulesets = rulesets.AvailableRulesets.Skip(1).Take(2); - AddStep("load multiple rulesets beatmapset", () => - { - selector.BeatmapSet = new APIBeatmapSet - { - Beatmaps = enabledRulesets.Select(r => new APIBeatmap { RulesetID = r.OnlineID }).ToArray() - }; - }); - - var tabItems = selector.TabContainer.TabItems; - AddAssert("other rulesets disabled", () => tabItems.Except(tabItems.Where(t => enabledRulesets.Any(r => r.Equals(t.Value)))).All(t => !t.Enabled.Value)); - AddAssert("left-most ruleset selected", () => tabItems.First(t => t.Enabled.Value).Active.Value); - } - - [Test] - public void TestSingleRulesetBeatmapSet() - { - var enabledRuleset = rulesets.AvailableRulesets.Last(); - - AddStep("load single ruleset beatmapset", () => { selector.BeatmapSet = new APIBeatmapSet { Beatmaps = new[] { - new APIBeatmap - { - RulesetID = enabledRuleset.OnlineID - } + new APIBeatmap { RulesetID = 1 }, + new APIBeatmap { RulesetID = 2 }, } }; }); - AddAssert("single ruleset selected", () => selector.SelectedTab.Value.Equals(enabledRuleset)); + AddAssert("osu disabled", () => !selector.ChildrenOfType().Single(t => t.Value.OnlineID == 0).Enabled.Value); + AddAssert("mania disabled", () => !selector.ChildrenOfType().Single(t => t.Value.OnlineID == 3).Enabled.Value); + + AddAssert("taiko selected", () => selector.ChildrenOfType().Single(t => t.Active.Value).Value.OnlineID == 1); + } + + [Test] + public void TestSingleRulesetBeatmapSet() + { + AddStep("load single ruleset beatmapset", () => + { + selector.BeatmapSet = new APIBeatmapSet + { + Beatmaps = new[] { new APIBeatmap { RulesetID = 3 } } + }; + }); + + AddAssert("single ruleset selected", () => selector.ChildrenOfType().Single(t => t.Active.Value).Value.OnlineID == 3); } [Test] public void TestEmptyBeatmapSet() { AddStep("load empty beatmapset", () => selector.BeatmapSet = new APIBeatmapSet()); - - AddAssert("no ruleset selected", () => selector.SelectedTab == null); - AddAssert("all rulesets disabled", () => selector.TabContainer.TabItems.All(t => !t.Enabled.Value)); + AddAssert("all rulesets disabled", () => selector.ChildrenOfType().All(t => !t.Active.Value && !t.Enabled.Value)); } [Test] public void TestNullBeatmapSet() { AddStep("load null beatmapset", () => selector.BeatmapSet = null); - - AddAssert("no ruleset selected", () => selector.SelectedTab == null); - AddAssert("all rulesets disabled", () => selector.TabContainer.TabItems.All(t => !t.Enabled.Value)); - } - - private class TestRulesetSelector : BeatmapRulesetSelector - { - public new TabItem SelectedTab => base.SelectedTab; - - public new TabFillFlowContainer TabContainer => base.TabContainer; + AddAssert("all rulesets disabled", () => selector.ChildrenOfType().All(t => !t.Active.Value && !t.Enabled.Value)); } } } From 55c03dc04d0505f462c41876a50797acc3698f0b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 29 Apr 2022 12:40:40 +0300 Subject: [PATCH 25/38] Fix silly mistake in ordering and update test colour scheme --- .../Visual/Online/TestSceneProfileRulesetSelector.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs index 4e572b665b..ae90872439 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs @@ -17,7 +17,7 @@ namespace osu.Game.Tests.Visual.Online public class TestSceneProfileRulesetSelector : OsuTestScene { [Cached] - private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green); + private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Pink); public TestSceneProfileRulesetSelector() { @@ -37,9 +37,9 @@ namespace osu.Game.Tests.Visual.Online AddStep("set mania as default", () => selector.SetDefaultRuleset(new ManiaRuleset().RulesetInfo)); AddStep("User with osu as default", () => user.Value = new APIUser { Id = 0, PlayMode = "osu" }); - AddStep("User with taiko as default", () => user.Value = new APIUser { Id = 2, PlayMode = "taiko" }); - AddStep("User with catch as default", () => user.Value = new APIUser { Id = 3, PlayMode = "fruits" }); - AddStep("User with mania as default", () => user.Value = new APIUser { Id = 1, PlayMode = "mania" }); + AddStep("User with taiko as default", () => user.Value = new APIUser { Id = 1, PlayMode = "taiko" }); + AddStep("User with catch as default", () => user.Value = new APIUser { Id = 2, PlayMode = "fruits" }); + AddStep("User with mania as default", () => user.Value = new APIUser { Id = 3, PlayMode = "mania" }); AddStep("null user", () => user.Value = null); } } From 9ea3e244e7d141b1a0d56862a03a2f15aa56a372 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 29 Apr 2022 12:48:05 +0300 Subject: [PATCH 26/38] Adjust ruleset tab item content spacing to match web Not too noticeable, but better match web in any case. --- osu.Game/Overlays/OverlayRulesetTabItem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/OverlayRulesetTabItem.cs b/osu.Game/Overlays/OverlayRulesetTabItem.cs index 4f58d4c189..32177b2b96 100644 --- a/osu.Game/Overlays/OverlayRulesetTabItem.cs +++ b/osu.Game/Overlays/OverlayRulesetTabItem.cs @@ -46,7 +46,7 @@ namespace osu.Game.Overlays { AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, - Spacing = new Vector2(5f, 0), + Spacing = new Vector2(4f, 0), Child = icon = new ConstrainedIconContainer { Anchor = Anchor.Centre, From 670b51324ea0d6e52c036391039329ca27f3e1b2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Apr 2022 23:45:02 +0900 Subject: [PATCH 27/38] Add basic test at top of first run scene to make adjusting UI easier --- .../Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs index 836cf6caad..39298f56ba 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs @@ -65,6 +65,12 @@ namespace osu.Game.Tests.Visual.UserInterface }); } + [Test] + public void TestBasic() + { + AddAssert("overlay visible", () => overlay.State.Value == Visibility.Visible); + } + [Test] [Ignore("Enable when first run setup is being displayed on first run.")] public void TestDoesntOpenOnSecondRun() From 27ee990359ab4f412e589b53ae70f740c9de7e1c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Apr 2022 23:45:17 +0900 Subject: [PATCH 28/38] Update first-run overlay footer buttons to use new sheared design --- osu.Game/Overlays/FirstRunSetupOverlay.cs | 36 ++++++++++++++--------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index 4277f0f2ba..75778e6c4d 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -25,7 +25,6 @@ using osu.Game.Overlays.Mods; using osu.Game.Overlays.Notifications; using osu.Game.Screens; using osu.Game.Screens.Menu; -using osu.Game.Screens.OnlinePlay.Match.Components; namespace osu.Game.Overlays { @@ -45,8 +44,8 @@ namespace osu.Game.Overlays private ScreenStack? stack; - public PurpleTriangleButton NextButton = null!; - public DangerousTriangleButton BackButton = null!; + public ShearedButton NextButton = null!; + public ShearedButton BackButton = null!; private readonly Bindable showFirstRunSetup = new Bindable(); @@ -72,7 +71,7 @@ namespace osu.Game.Overlays private Container content = null!; [BackgroundDependencyLoader] - private void load() + private void load(OsuColour colours) { Header.Title = FirstRunSetupOverlayStrings.FirstRunSetupTitle; Header.Description = FirstRunSetupOverlayStrings.FirstRunSetupDescription; @@ -84,7 +83,11 @@ namespace osu.Game.Overlays Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Horizontal = 70 * 1.2f }, + Padding = new MarginPadding + { + Horizontal = 70 * 1.2f, + Bottom = 20, + }, Child = new InputBlockingContainer { Masking = true, @@ -117,14 +120,15 @@ namespace osu.Game.Overlays { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Width = 0.98f, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, + Margin = new MarginPadding { Vertical = PADDING }, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, ColumnDimensions = new[] { - new Dimension(GridSizeMode.AutoSize), new Dimension(GridSizeMode.Absolute, 10), + new Dimension(GridSizeMode.AutoSize), new Dimension(), + new Dimension(GridSizeMode.Absolute, 10), }, RowDimensions = new[] { @@ -134,21 +138,25 @@ namespace osu.Game.Overlays { new[] { - BackButton = new DangerousTriangleButton + Empty(), + BackButton = new ShearedButton(300) { - Width = 300, Text = CommonStrings.Back, Action = showPreviousStep, Enabled = { Value = false }, + DarkerColour = colours.Pink2, + LighterColour = colours.Pink1, }, - Empty(), - NextButton = new PurpleTriangleButton + NextButton = new ShearedButton(0) { RelativeSizeAxes = Axes.X, Width = 1, Text = FirstRunSetupOverlayStrings.GetStarted, + DarkerColour = ColourProvider.Colour2, + LighterColour = ColourProvider.Colour1, Action = showNextStep - } + }, + Empty(), }, } }); From 3996972867507a1b46c551d7c921a5a973dad3c1 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 30 Apr 2022 12:23:08 +0300 Subject: [PATCH 29/38] Remove unnecessary `f` suffix --- osu.Game/Overlays/OverlayRulesetTabItem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/OverlayRulesetTabItem.cs b/osu.Game/Overlays/OverlayRulesetTabItem.cs index 32177b2b96..84815b0cc1 100644 --- a/osu.Game/Overlays/OverlayRulesetTabItem.cs +++ b/osu.Game/Overlays/OverlayRulesetTabItem.cs @@ -46,7 +46,7 @@ namespace osu.Game.Overlays { AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, - Spacing = new Vector2(4f, 0), + Spacing = new Vector2(4, 0), Child = icon = new ConstrainedIconContainer { Anchor = Anchor.Centre, From c7ab9a89285f457737a6e5b1f2261da4a46c4e0c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 30 Apr 2022 15:35:04 +0300 Subject: [PATCH 30/38] Add ruleset tab item tooltips --- osu.Game/Overlays/OverlayRulesetTabItem.cs | 6 ++++- .../Components/ProfileRulesetTabItem.cs | 23 +++++++++++++------ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/OverlayRulesetTabItem.cs b/osu.Game/Overlays/OverlayRulesetTabItem.cs index 84815b0cc1..8ed75a528b 100644 --- a/osu.Game/Overlays/OverlayRulesetTabItem.cs +++ b/osu.Game/Overlays/OverlayRulesetTabItem.cs @@ -10,11 +10,13 @@ using osu.Game.Rulesets; using osuTK.Graphics; using osuTK; using osu.Framework.Allocation; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Localisation; using osu.Game.Graphics.Containers; namespace osu.Game.Overlays { - public class OverlayRulesetTabItem : TabItem + public class OverlayRulesetTabItem : TabItem, IHasTooltip { private Color4 accentColour; @@ -35,6 +37,8 @@ namespace osu.Game.Overlays private readonly Drawable icon; + public LocalisableString TooltipText => Value.Name; + public OverlayRulesetTabItem(RulesetInfo value) : base(value) { diff --git a/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetTabItem.cs b/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetTabItem.cs index 15640ddc5e..4a44e285bf 100644 --- a/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetTabItem.cs +++ b/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetTabItem.cs @@ -2,7 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; +using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets; using osuTK; using osuTK.Graphics; @@ -42,14 +45,20 @@ namespace osu.Game.Overlays.Profile.Header.Components public ProfileRulesetTabItem(RulesetInfo value) : base(value) { - Add(icon = new SpriteIcon + Add(icon = new DefaultRulesetIcon { Alpha = 0 }); + } + + public class DefaultRulesetIcon : SpriteIcon, IHasTooltip + { + public LocalisableString TooltipText => UsersStrings.ShowEditDefaultPlaymodeIsDefaultTooltip; + + public DefaultRulesetIcon() { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Alpha = 0, - Icon = FontAwesome.Solid.Star, - Size = new Vector2(12), - }); + Origin = Anchor.Centre; + Anchor = Anchor.Centre; + Icon = FontAwesome.Solid.Star; + Size = new Vector2(12); + } } } } From ba5da8a52aa40cc4c1092854f33b9054b4fbe5e1 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 30 Apr 2022 15:36:15 +0300 Subject: [PATCH 31/38] Fix tooltips not shown on selected tab --- osu.Game/Overlays/OverlayRulesetTabItem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/OverlayRulesetTabItem.cs b/osu.Game/Overlays/OverlayRulesetTabItem.cs index 8ed75a528b..1f11b98881 100644 --- a/osu.Game/Overlays/OverlayRulesetTabItem.cs +++ b/osu.Game/Overlays/OverlayRulesetTabItem.cs @@ -71,7 +71,7 @@ namespace osu.Game.Overlays Enabled.BindValueChanged(_ => updateState(), true); } - public override bool PropagatePositionalInputSubTree => Enabled.Value && !Active.Value && base.PropagatePositionalInputSubTree; + public override bool PropagatePositionalInputSubTree => Enabled.Value && base.PropagatePositionalInputSubTree; protected override bool OnHover(HoverEvent e) { From b6fb0197ab1ef3f03a9270af7e035d0f97839c86 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 30 Apr 2022 23:52:36 +0900 Subject: [PATCH 32/38] 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 ff6499631d..b6260fd1d4 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 26891ad978..fa7563da55 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -35,7 +35,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index d261e13ade..e472b5f1a8 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -61,7 +61,7 @@ - + @@ -84,7 +84,7 @@ - + From 5c13200c75d581b796306b04e5d21ad3082beb3b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 1 May 2022 13:01:38 +0900 Subject: [PATCH 33/38] Update production endpoint to new version --- osu.Game/Online/ProductionEndpointConfiguration.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/ProductionEndpointConfiguration.cs b/osu.Game/Online/ProductionEndpointConfiguration.cs index c6ddc03564..e44dad1db5 100644 --- a/osu.Game/Online/ProductionEndpointConfiguration.cs +++ b/osu.Game/Online/ProductionEndpointConfiguration.cs @@ -10,8 +10,8 @@ namespace osu.Game.Online WebsiteRootUrl = APIEndpointUrl = @"https://osu.ppy.sh"; APIClientSecret = @"FGc9GAtyHzeQDshWP5Ah7dega8hJACAJpQtw6OXk"; APIClientID = "5"; - SpectatorEndpointUrl = "https://spectator.ppy.sh/spectator"; - MultiplayerEndpointUrl = "https://spectator.ppy.sh/multiplayer"; + SpectatorEndpointUrl = "https://spectator2.ppy.sh/spectator"; + MultiplayerEndpointUrl = "https://spectator2.ppy.sh/multiplayer"; } } } From 4b30d0e59b84cc4a8a496cd5d2ab8fcf16f125f8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 May 2022 17:42:56 +0900 Subject: [PATCH 34/38] Fix first-run overlay's song select applying track adjustments Closes https://github.com/ppy/osu/issues/18041. --- osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs b/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs index 1bd82f6d99..862506add2 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs @@ -99,6 +99,8 @@ namespace osu.Game.Overlays.FirstRunSetup private class NestedSongSelect : PlaySongSelect { protected override bool ControlGlobalMusic => false; + + public override bool? AllowTrackAdjustments => false; } private class PinnedMainMenu : MainMenu From c3e0ba5c8db4bd3d2042445ad1d8db136e5b93e6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 May 2022 20:27:00 +0900 Subject: [PATCH 35/38] Fix clicking anywhere in the beatmap overlay dismissing it --- osu.Game/Overlays/BeatmapSetOverlay.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index b9d3854066..bd63c997df 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -5,7 +5,6 @@ using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Input.Events; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.BeatmapSet; @@ -24,9 +23,6 @@ namespace osu.Game.Overlays private readonly Bindable beatmapSet = new Bindable(); - // receive input outside our bounds so we can trigger a close event on ourselves. - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; - public BeatmapSetOverlay() : base(OverlayColourScheme.Blue) { @@ -71,12 +67,6 @@ namespace osu.Game.Overlays beatmapSet.Value = null; } - protected override bool OnClick(ClickEvent e) - { - Hide(); - return true; - } - public void FetchAndShowBeatmap(int beatmapId) { beatmapSet.Value = null; From 55949e8407bd91ad1bc32e09084d3db27cb986e3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 May 2022 20:30:03 +0900 Subject: [PATCH 36/38] Make disabled sheared buttons darker --- osu.Game/Graphics/UserInterface/ShearedButton.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/ShearedButton.cs b/osu.Game/Graphics/UserInterface/ShearedButton.cs index c3c566782f..d46fa24409 100644 --- a/osu.Game/Graphics/UserInterface/ShearedButton.cs +++ b/osu.Game/Graphics/UserInterface/ShearedButton.cs @@ -176,8 +176,8 @@ namespace osu.Game.Graphics.UserInterface if (!Enabled.Value) { - colourDark = colourDark.Darken(0.3f); - colourLight = colourLight.Darken(0.3f); + colourDark = colourDark.Darken(1f); + colourLight = colourLight.Darken(1f); } else if (IsHovered) { From f9a1d9df56968b8ebe8ff094ae990c4615214690 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 May 2022 20:30:36 +0900 Subject: [PATCH 37/38] Reduce scale effect slightly (was feeling too bouncy) --- osu.Game/Graphics/UserInterface/ShearedButton.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/ShearedButton.cs b/osu.Game/Graphics/UserInterface/ShearedButton.cs index d46fa24409..dea44e6d99 100644 --- a/osu.Game/Graphics/UserInterface/ShearedButton.cs +++ b/osu.Game/Graphics/UserInterface/ShearedButton.cs @@ -158,7 +158,7 @@ namespace osu.Game.Graphics.UserInterface protected override bool OnMouseDown(MouseDownEvent e) { - Content.ScaleTo(0.8f, 2000, Easing.OutQuint); + Content.ScaleTo(0.9f, 2000, Easing.OutQuint); return base.OnMouseDown(e); } From 18852b250965df3f8c51be41682c2fca9719f298 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 2 May 2022 15:31:47 +0300 Subject: [PATCH 38/38] Fix footer random button autosizing to text length --- osu.Game/Screens/Select/FooterButtonRandom.cs | 51 +++++++++++++++---- 1 file changed, 42 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Select/FooterButtonRandom.cs b/osu.Game/Screens/Select/FooterButtonRandom.cs index 48d5836826..f855b80f75 100644 --- a/osu.Game/Screens/Select/FooterButtonRandom.cs +++ b/osu.Game/Screens/Select/FooterButtonRandom.cs @@ -5,6 +5,7 @@ using System; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; @@ -19,6 +20,9 @@ namespace osu.Game.Screens.Select public Action NextRandom { get; set; } public Action PreviousRandom { get; set; } + private Container persistentText; + private OsuSpriteText randomSpriteText; + private OsuSpriteText rewindSpriteText; private bool rewindSearch; [BackgroundDependencyLoader] @@ -26,7 +30,32 @@ namespace osu.Game.Screens.Select { SelectedColour = colours.Green; DeselectedColour = SelectedColour.Opacity(0.5f); - updateText(); + + TextContainer.Add(persistentText = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AlwaysPresent = true, + AutoSizeAxes = Axes.Both, + Children = new[] + { + randomSpriteText = new OsuSpriteText + { + AlwaysPresent = true, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = "random", + }, + rewindSpriteText = new OsuSpriteText + { + AlwaysPresent = true, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = "rewind", + Alpha = 0f, + } + } + }); Action = () => { @@ -34,22 +63,22 @@ namespace osu.Game.Screens.Select { const double fade_time = 500; - OsuSpriteText rewindSpriteText; + OsuSpriteText fallingRewind; - TextContainer.Add(rewindSpriteText = new OsuSpriteText + TextContainer.Add(fallingRewind = new OsuSpriteText { Alpha = 0, - Text = @"rewind", + Text = rewindSpriteText.Text, AlwaysPresent = true, // make sure the button is sized large enough to always show this Anchor = Anchor.Centre, Origin = Anchor.Centre, }); - rewindSpriteText.FadeOutFromOne(fade_time, Easing.In); - rewindSpriteText.MoveTo(Vector2.Zero).MoveTo(new Vector2(0, 10), fade_time, Easing.In); - rewindSpriteText.Expire(); + fallingRewind.FadeOutFromOne(fade_time, Easing.In); + fallingRewind.MoveTo(Vector2.Zero).MoveTo(new Vector2(0, 10), fade_time, Easing.In); + fallingRewind.Expire(); - SpriteText.FadeInFromZero(fade_time, Easing.In); + persistentText.FadeInFromZero(fade_time, Easing.In); PreviousRandom.Invoke(); } @@ -119,6 +148,10 @@ namespace osu.Game.Screens.Select } } - private void updateText(bool rewind = false) => Text = rewind ? "rewind" : "random"; + private void updateText(bool rewind = false) + { + randomSpriteText.Alpha = rewind ? 0 : 1; + rewindSpriteText.Alpha = rewind ? 1 : 0; + } } }