From 3ad092d80879975b371698f9c4dd8e510f2d8227 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 25 Mar 2022 18:29:00 +0900 Subject: [PATCH 1/5] Always show the countdown button when host --- .../Multiplayer/Match/MatchStartControl.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs index 6297203684..fa1b682779 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs @@ -30,7 +30,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match private Sample sampleReadyAll; private Sample sampleUnready; - private readonly BindableBool enabled = new BindableBool(); + private readonly MultiplayerReadyButton readyButton; private readonly MultiplayerCountdownButton countdownButton; private int countReady; private ScheduledDelegate readySampleDelegate; @@ -50,12 +50,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { new Drawable[] { - new MultiplayerReadyButton + readyButton = new MultiplayerReadyButton { RelativeSizeAxes = Axes.Both, Size = Vector2.One, Action = onReadyClick, - Enabled = { BindTarget = enabled }, }, countdownButton = new MultiplayerCountdownButton { @@ -63,7 +62,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match Size = new Vector2(40, 1), Alpha = 0, Action = startCountdown, - Enabled = { BindTarget = enabled } } } } @@ -163,7 +161,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { if (Room == null) { - enabled.Value = false; + readyButton.Enabled.Value = false; + countdownButton.Enabled.Value = false; return; } @@ -172,7 +171,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match int newCountReady = Room.Users.Count(u => u.State == MultiplayerUserState.Ready); int newCountTotal = Room.Users.Count(u => u.State != MultiplayerUserState.Spectating); - if (!Client.IsHost || Room.Countdown != null || Room.Settings.AutoStartEnabled) + if (!Client.IsHost || Room.Settings.AutoStartEnabled) countdownButton.Hide(); else { @@ -182,6 +181,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match countdownButton.Hide(); break; + case MultiplayerUserState.Idle: case MultiplayerUserState.Spectating: case MultiplayerUserState.Ready: countdownButton.Show(); @@ -189,15 +189,15 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match } } - enabled.Value = + readyButton.Enabled.Value = countdownButton.Enabled.Value = Room.State == MultiplayerRoomState.Open && CurrentPlaylistItem.Value?.ID == Room.Settings.PlaylistItemId && !Room.Playlist.Single(i => i.ID == Room.Settings.PlaylistItemId).Expired && !operationInProgress.Value; - // When the local user is the host and spectating the match, the "start match" state should be enabled if any users are ready. + // When the local user is the host and spectating the match, the ready button should be enabled only if any users are ready. if (localUser?.State == MultiplayerUserState.Spectating) - enabled.Value &= Client.IsHost && newCountReady > 0; + readyButton.Enabled.Value &= Client.IsHost && newCountReady > 0; if (newCountReady == countReady) return; From 9963efce517068bbb7232374097ac88e8be1979d Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 25 Mar 2022 18:40:32 +0900 Subject: [PATCH 2/5] Improve ready/countdown button UX --- .../Multiplayer/Match/MatchStartControl.cs | 32 ++++++++----------- .../Match/MultiplayerCountdownButton.cs | 23 +++++++++++++ 2 files changed, 36 insertions(+), 19 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs index fa1b682779..1201279929 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs @@ -62,6 +62,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match Size = new Vector2(40, 1), Alpha = 0, Action = startCountdown, + CancelAction = cancelCountdown } } } @@ -106,30 +107,15 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match 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 || Room.Settings.AutoStartEnabled) - { + if (isReady() && Client.IsHost && Room.Countdown == null) + startMatch(); + else toggleReady(); - return; - } - - // Local user is the room host and is in a ready state. - // The only action they can take is to stop a countdown if one's currently running. - if (Room.Countdown != null) - { - stopCountdown(); - 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 stopCountdown() => Client.SendMatchRequest(new StopCountdownRequest()).ContinueWith(_ => endOperation()); - void startMatch() => Client.StartMatch().ContinueWith(t => { // accessing Exception here silences any potential errors from the antecedent task @@ -151,6 +137,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match Client.SendMatchRequest(new StartMatchCountdownRequest { Duration = duration }).ContinueWith(_ => endOperation()); } + private void cancelCountdown() + { + Debug.Assert(clickOperation == null); + clickOperation = ongoingOperationTracker.BeginOperation(); + + Client.SendMatchRequest(new StopCountdownRequest()).ContinueWith(_ => endOperation()); + } + private void endOperation() { clickOperation?.Dispose(); @@ -197,7 +191,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match // When the local user is the host and spectating the match, the ready button should be enabled only if any users are ready. if (localUser?.State == MultiplayerUserState.Spectating) - readyButton.Enabled.Value &= Client.IsHost && newCountReady > 0; + readyButton.Enabled.Value &= Client.IsHost && newCountReady > 0 && Room.Countdown == null; if (newCountReady == countReady) return; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerCountdownButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerCountdownButton.cs index 3bf7e91a55..d1e28fb5e0 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerCountdownButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerCountdownButton.cs @@ -14,6 +14,7 @@ using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Online.Multiplayer; using osuTK; namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match @@ -29,6 +30,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match }; public new Action Action; + public Action CancelAction; + + [Resolved] + private MultiplayerClient multiplayerClient { get; set; } + + [Resolved] + private OsuColour colours { get; set; } private readonly Drawable background; @@ -77,6 +85,21 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match }); } + if (multiplayerClient.Room?.Countdown != null && multiplayerClient.IsHost) + { + flow.Add(new OsuButton + { + RelativeSizeAxes = Axes.X, + Text = "Cancel", + BackgroundColour = colours.Red, + Action = () => + { + CancelAction(); + this.HidePopover(); + } + }); + } + return new OsuPopover { Child = flow }; } } From 0146717fcb9bda32c892657d8030edde92774c16 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 26 Mar 2022 14:29:31 +0900 Subject: [PATCH 3/5] Adjust test button sizing to better match actual usage --- osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs index 52854db235..b0a451f1d0 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs @@ -67,7 +67,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Size = new Vector2(200, 50), + Size = new Vector2(250, 50), } }; }); From 5725cc36ff27b315f4246460a72366701e271637 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 26 Mar 2022 14:29:51 +0900 Subject: [PATCH 4/5] Add animation to countdown button when countdown is active --- .../Match/MultiplayerCountdownButton.cs | 37 ++++++++++++++++++- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerCountdownButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerCountdownButton.cs index 7f819d9e75..c84fcff11e 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerCountdownButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerCountdownButton.cs @@ -30,6 +30,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match }; public new Action Action; + public Action CancelAction; [Resolved] @@ -61,6 +62,38 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match background.Colour = colours.Green; } + protected override void LoadComplete() + { + base.LoadComplete(); + + multiplayerClient.RoomUpdated += onRoomUpdated; + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + multiplayerClient.RoomUpdated -= onRoomUpdated; + } + + private void onRoomUpdated() => Scheduler.AddOnce(() => + { + bool countdownActive = multiplayerClient.Room?.Countdown != null; + + if (countdownActive) + { + background + .FadeColour(colours.YellowLight, 100, Easing.In) + .Then() + .FadeColour(colours.YellowDark, 900, Easing.OutQuint) + .Loop(); + } + else + { + background + .FadeColour(colours.Green, 200, Easing.OutQuint); + } + }); + public Popover GetPopover() { var flow = new FillFlowContainer @@ -77,7 +110,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { RelativeSizeAxes = Axes.X, Text = $"Start match in {duration.Humanize()}", - BackgroundColour = background.Colour, + BackgroundColour = colours.Green, Action = () => { Action(duration); @@ -91,7 +124,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match flow.Add(new OsuButton { RelativeSizeAxes = Axes.X, - Text = "Cancel", + Text = "Stop countdown", BackgroundColour = colours.Red, Action = () => { From c3a0f0d6b8bbb5a13387ac84c98abd9431d1651d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 26 Mar 2022 14:43:41 +0900 Subject: [PATCH 5/5] Update tests in line with new button behaviour --- .../Multiplayer/TestSceneMatchStartControl.cs | 37 +++++-------------- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs index b0a451f1d0..5c2fd26857 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs @@ -85,7 +85,6 @@ namespace osu.Game.Tests.Visual.Multiplayer InputManager.Click(MouseButton.Left); }); - AddAssert("countdown button not visible", () => !this.ChildrenOfType().Single().IsPresent); AddStep("finish countdown", () => MultiplayerClient.SkipToEndOfCountdown()); AddUntilStep("match started", () => MultiplayerClient.LocalUser?.State == MultiplayerUserState.WaitingForLoad); } @@ -103,7 +102,13 @@ namespace osu.Game.Tests.Visual.Multiplayer InputManager.Click(MouseButton.Left); }); - ClickButtonWhenEnabled(); + ClickButtonWhenEnabled(); + AddStep("click the cancel button", () => + { + var popoverButton = this.ChildrenOfType().Single().ChildrenOfType().Last(); + InputManager.MoveMouseTo(popoverButton); + InputManager.Click(MouseButton.Left); + }); AddStep("finish countdown", () => MultiplayerClient.SkipToEndOfCountdown()); AddUntilStep("match not started", () => MultiplayerClient.LocalUser?.State == MultiplayerUserState.Ready); @@ -128,43 +133,21 @@ namespace osu.Game.Tests.Visual.Multiplayer } [Test] - public void TestCountdownButtonEnablementAndVisibilityWhileSpectating() + public void TestCountdownWhileSpectating() { AddStep("set spectating", () => MultiplayerClient.ChangeUserState(API.LocalUser.Value.OnlineID, MultiplayerUserState.Spectating)); AddUntilStep("local user is spectating", () => MultiplayerClient.LocalUser?.State == MultiplayerUserState.Spectating); AddAssert("countdown button is visible", () => this.ChildrenOfType().Single().IsPresent); - AddAssert("countdown button disabled", () => !this.ChildrenOfType().Single().Enabled.Value); + AddAssert("countdown button enabled", () => this.ChildrenOfType().Single().Enabled.Value); AddStep("add second user", () => MultiplayerClient.AddUser(new APIUser { Id = 2, Username = "Another user" })); - AddAssert("countdown button disabled", () => !this.ChildrenOfType().Single().Enabled.Value); + AddAssert("countdown button enabled", () => this.ChildrenOfType().Single().Enabled.Value); AddStep("set second user ready", () => MultiplayerClient.ChangeUserState(2, MultiplayerUserState.Ready)); AddAssert("countdown button enabled", () => this.ChildrenOfType().Single().Enabled.Value); } - [Test] - public void TestReadyButtonEnabledWhileSpectatingDuringCountdown() - { - AddStep("add second user", () => MultiplayerClient.AddUser(new APIUser { Id = 2, Username = "Another user" })); - AddStep("set second user ready", () => MultiplayerClient.ChangeUserState(2, MultiplayerUserState.Ready)); - - ClickButtonWhenEnabled(); - AddUntilStep("countdown button shown", () => this.ChildrenOfType().SingleOrDefault()?.IsPresent == true); - ClickButtonWhenEnabled(); - AddStep("click the first countdown button", () => - { - var popoverButton = this.ChildrenOfType().Single().ChildrenOfType().First(); - InputManager.MoveMouseTo(popoverButton); - InputManager.Click(MouseButton.Left); - }); - - AddStep("set spectating", () => MultiplayerClient.ChangeUserState(API.LocalUser.Value.OnlineID, MultiplayerUserState.Spectating)); - AddUntilStep("local user is spectating", () => MultiplayerClient.LocalUser?.State == MultiplayerUserState.Spectating); - - AddAssert("ready button enabled", () => this.ChildrenOfType().Single().Enabled.Value); - } - [Test] public void TestBecomeHostDuringCountdownAndReady() {