From 8c3b541312ebcfe59cea912c755de5dcfb37ee1c Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 21 Apr 2022 22:35:07 +0900 Subject: [PATCH 1/8] 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 2/8] 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 3/8] 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 4/8] 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 5/8] 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 6/8] 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 fe0fcc7e9ea522ebfa9bca7a71603de316a80371 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 28 Apr 2022 20:00:38 +0900 Subject: [PATCH 7/8] 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 8/8] 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()