diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs index c86d5e482a..f34f7c6c91 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs @@ -1,9 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.Linq; -using System.Threading.Tasks; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -35,8 +33,6 @@ namespace osu.Game.Tests.Visual.Multiplayer private BeatmapManager beatmaps; private RulesetStore rulesets; - private IDisposable readyClickOperation; - [BackgroundDependencyLoader] private void load(GameHost host, AudioManager audio) { @@ -67,23 +63,6 @@ namespace osu.Game.Tests.Visual.Multiplayer Anchor = Anchor.Centre, Origin = Anchor.Centre, Size = new Vector2(200, 50), - OnReadyClick = () => - { - readyClickOperation = OngoingOperationTracker.BeginOperation(); - - Task.Run(async () => - { - if (MultiplayerClient.IsHost && MultiplayerClient.LocalUser?.State == MultiplayerUserState.Ready) - { - await MultiplayerClient.StartMatch(); - return; - } - - await MultiplayerClient.ToggleReady(); - - readyClickOperation.Dispose(); - }); - } }); }); @@ -208,9 +187,6 @@ namespace osu.Game.Tests.Visual.Multiplayer ClickButtonWhenEnabled(); AddUntilStep("user waiting for load", () => MultiplayerClient.Room?.Users[0].State == MultiplayerUserState.WaitingForLoad); - AddAssert("ready button disabled", () => !button.ChildrenOfType().Single().Enabled.Value); - AddStep("transitioned to gameplay", () => readyClickOperation.Dispose()); - AddStep("finish gameplay", () => { MultiplayerClient.ChangeUserState(MultiplayerClient.Room?.Users[0].UserID ?? 0, MultiplayerUserState.Loaded); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs index 31f8e00772..33ad0fd1de 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs @@ -1,9 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.Linq; -using System.Threading.Tasks; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -36,8 +34,6 @@ namespace osu.Game.Tests.Visual.Multiplayer private BeatmapManager beatmaps; private RulesetStore rulesets; - private IDisposable readyClickOperation; - [BackgroundDependencyLoader] private void load(GameHost host, AudioManager audio) { @@ -77,23 +73,6 @@ namespace osu.Game.Tests.Visual.Multiplayer Anchor = Anchor.Centre, Origin = Anchor.Centre, Size = new Vector2(200, 50), - OnReadyClick = () => - { - readyClickOperation = OngoingOperationTracker.BeginOperation(); - - Task.Run(async () => - { - if (MultiplayerClient.IsHost && MultiplayerClient.LocalUser?.State == MultiplayerUserState.Ready) - { - await MultiplayerClient.StartMatch(); - return; - } - - await MultiplayerClient.ToggleReady(); - - readyClickOperation.Dispose(); - }); - } } } }; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs index 4d4cff021d..b4fce5903b 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -12,13 +11,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match private const float ready_button_width = 600; private const float spectate_button_width = 200; - public Action OnReadyClick - { - set => readyButton.OnReadyClick = value; - } - - private readonly MultiplayerReadyButton readyButton; - public MultiplayerMatchFooter() { RelativeSizeAxes = Axes.Both; @@ -36,7 +28,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match RelativeSizeAxes = Axes.Both, }, null, - readyButton = new MultiplayerReadyButton + new MultiplayerReadyButton { RelativeSizeAxes = Axes.Both, }, diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index 023af85f3b..0c80f6ef5b 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -2,7 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Diagnostics; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; @@ -19,28 +21,23 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { public class MultiplayerReadyButton : MultiplayerRoomComposite { - public Action OnReadyClick - { - set => button.Action = value; - } - [Resolved] private OsuColour colours { get; set; } [Resolved] private OngoingOperationTracker ongoingOperationTracker { get; set; } - private IBindable operationInProgress; + [CanBeNull] + private IDisposable clickOperation; private Sample sampleReady; private Sample sampleReadyAll; private Sample sampleUnready; private readonly ButtonWithTrianglesExposed button; - private int countReady; - private ScheduledDelegate readySampleDelegate; + private IBindable operationInProgress; public MultiplayerReadyButton() { @@ -48,6 +45,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { RelativeSizeAxes = Axes.Both, Size = Vector2.One, + Action = onReadyClick, Enabled = { Value = true }, }; } @@ -73,10 +71,56 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match protected override void OnRoomUpdated() { base.OnRoomUpdated(); - updateState(); } + protected override void OnRoomLoadRequested() + { + base.OnRoomLoadRequested(); + endOperation(); + } + + private void onReadyClick() + { + if (Room == null) + return; + + Debug.Assert(clickOperation == null); + clickOperation = ongoingOperationTracker.BeginOperation(); + + // Ensure the current user becomes ready before being able to do anything else (start match, stop countdown, unready). + if (!isReady() || !Client.IsHost) + { + toggleReady(); + return; + } + + // And if a countdown isn't running, start the match. + startMatch(); + + bool isReady() => Client.LocalUser?.State == MultiplayerUserState.Ready || Client.LocalUser?.State == MultiplayerUserState.Spectating; + + void toggleReady() => Client.ToggleReady().ContinueWith(_ => endOperation()); + + void startMatch() => Client.StartMatch().ContinueWith(t => + { + // accessing Exception here silences any potential errors from the antecedent task + if (t.Exception != null) + { + // gameplay was not started due to an exception; unblock button. + endOperation(); + } + + // gameplay is starting, the button will be unblocked on load requested. + }); + } + + private void endOperation() + { + clickOperation?.Dispose(); + clickOperation = null; + } + private void updateState() { var localUser = Client.LocalUser; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 3a8816de99..e53153e017 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -1,11 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -46,14 +44,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [Resolved] private MultiplayerClient client { get; set; } - [Resolved] - private OngoingOperationTracker ongoingOperationTracker { get; set; } - private readonly IBindable isConnected = new Bindable(); - [CanBeNull] - private IDisposable readyClickOperation; - private AddItemButton addItemButton; public MultiplayerMatchSubScreen(Room room) @@ -230,10 +222,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer this.Push(new MultiplayerMatchSongSelect(Room, itemToEdit)); } - protected override Drawable CreateFooter() => new MultiplayerMatchFooter - { - OnReadyClick = onReadyClick, - }; + protected override Drawable CreateFooter() => new MultiplayerMatchFooter(); protected override RoomSettingsOverlay CreateRoomSettingsOverlay(Room room) => new MultiplayerMatchSettingsOverlay(room); @@ -331,38 +320,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer } } - private void onReadyClick() - { - Debug.Assert(readyClickOperation == null); - readyClickOperation = ongoingOperationTracker.BeginOperation(); - - if (client.IsHost && (client.LocalUser?.State == MultiplayerUserState.Ready || client.LocalUser?.State == MultiplayerUserState.Spectating)) - { - client.StartMatch() - .ContinueWith(t => - { - // accessing Exception here silences any potential errors from the antecedent task - if (t.Exception != null) - { - // gameplay was not started due to an exception; unblock button. - endOperation(); - } - - // gameplay is starting, the button will be unblocked on load requested. - }); - return; - } - - client.ToggleReady() - .ContinueWith(t => endOperation()); - - void endOperation() - { - readyClickOperation?.Dispose(); - readyClickOperation = null; - } - } - private void onRoomUpdated() { // may happen if the client is kicked or otherwise removed from the room. @@ -418,9 +375,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer return; StartPlay(); - - readyClickOperation?.Dispose(); - readyClickOperation = null; } protected override Screen CreateGameplayScreen() diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomComposite.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomComposite.cs index 7d2fe44c4e..f6f815a3cb 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomComposite.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomComposite.cs @@ -21,6 +21,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer base.LoadComplete(); Client.RoomUpdated += invokeOnRoomUpdated; + Client.LoadRequested += invokeOnRoomLoadRequested; Client.UserLeft += invokeUserLeft; Client.UserKicked += invokeUserKicked; Client.UserJoined += invokeUserJoined; @@ -38,6 +39,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private void invokeItemAdded(MultiplayerPlaylistItem item) => Schedule(() => PlaylistItemAdded(item)); private void invokeItemRemoved(long item) => Schedule(() => PlaylistItemRemoved(item)); private void invokeItemChanged(MultiplayerPlaylistItem item) => Schedule(() => PlaylistItemChanged(item)); + private void invokeOnRoomLoadRequested() => Scheduler.AddOnce(OnRoomLoadRequested); /// /// Invoked when a user has joined the room. @@ -94,6 +96,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { } + /// + /// Invoked when the room requests the local user to load into gameplay. + /// + protected virtual void OnRoomLoadRequested() + { + } + protected override void Dispose(bool isDisposing) { if (Client != null)