From 6de91d7b6bb38caa25ea455a2868b548b083d5ab Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 6 Apr 2021 21:37:21 +0900 Subject: [PATCH] Add spectate button + test --- .../TestSceneMultiplayerSpectateButton.cs | 53 +++++++++++ .../Multiplayer/StatefulMultiplayerClient.cs | 27 ++++++ .../Match/MultiplayerSpectateButton.cs | 92 +++++++++++++++++++ 3 files changed, 172 insertions(+) create mode 100644 osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs create mode 100644 osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerSpectateButton.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs new file mode 100644 index 0000000000..730fee2fba --- /dev/null +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs @@ -0,0 +1,53 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Game.Online.Multiplayer; +using osu.Game.Screens.OnlinePlay.Multiplayer.Match; +using osuTK; +using osuTK.Input; + +namespace osu.Game.Tests.Visual.Multiplayer +{ + public class TestSceneMultiplayerSpectateButton : MultiplayerTestScene + { + private MultiplayerSpectateButton button; + private IDisposable readyClickOperation; + + [SetUp] + public new void Setup() => Schedule(() => + { + Child = button = new MultiplayerSpectateButton + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(200, 50), + OnSpectateClick = async () => + { + readyClickOperation = OngoingOperationTracker.BeginOperation(); + await Client.ToggleSpectate(); + readyClickOperation.Dispose(); + } + }; + }); + + [TestCase(MultiplayerUserState.Idle)] + [TestCase(MultiplayerUserState.Ready)] + public void TestToggleWhenIdle(MultiplayerUserState initialState) + { + addClickButtonStep(); + AddAssert("user is spectating", () => Client.Room?.Users[0].State == MultiplayerUserState.Spectating); + + addClickButtonStep(); + AddAssert("user is idle", () => Client.Room?.Users[0].State == MultiplayerUserState.Idle); + } + + private void addClickButtonStep() => AddStep("click button", () => + { + InputManager.MoveMouseTo(button); + InputManager.Click(MouseButton.Left); + }); + } +} diff --git a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs index 0f7050596f..2ddc10db0f 100644 --- a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs @@ -249,6 +249,33 @@ namespace osu.Game.Online.Multiplayer } } + /// + /// Toggles the 's spectating state. + /// + /// If a toggle of the spectating state is not valid at this time. + public async Task ToggleSpectate() + { + var localUser = LocalUser; + + if (localUser == null) + return; + + switch (localUser.State) + { + case MultiplayerUserState.Idle: + case MultiplayerUserState.Ready: + await ChangeState(MultiplayerUserState.Spectating).ConfigureAwait(false); + return; + + case MultiplayerUserState.Spectating: + await ChangeState(MultiplayerUserState.Idle).ConfigureAwait(false); + return; + + default: + throw new InvalidOperationException($"Cannot toggle spectate when in {localUser.State}"); + } + } + public abstract Task TransferHost(int userId); public abstract Task ChangeSettings(MultiplayerRoomSettings settings); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerSpectateButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerSpectateButton.cs new file mode 100644 index 0000000000..50be7719d9 --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerSpectateButton.cs @@ -0,0 +1,92 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Diagnostics; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Game.Graphics; +using osu.Game.Graphics.Backgrounds; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.Multiplayer; +using osuTK; + +namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match +{ + public class MultiplayerSpectateButton : MultiplayerRoomComposite + { + public Action OnSpectateClick + { + set => button.Action = value; + } + + [Resolved] + private OngoingOperationTracker ongoingOperationTracker { get; set; } + + [Resolved] + private OsuColour colours { get; set; } + + private IBindable operationInProgress; + + private readonly ButtonWithTrianglesExposed button; + + public MultiplayerSpectateButton() + { + InternalChild = button = new ButtonWithTrianglesExposed + { + RelativeSizeAxes = Axes.Both, + Size = Vector2.One, + Enabled = { Value = true }, + }; + } + + [BackgroundDependencyLoader] + private void load() + { + operationInProgress = ongoingOperationTracker.InProgress.GetBoundCopy(); + operationInProgress.BindValueChanged(_ => updateState()); + } + + protected override void OnRoomUpdated() + { + base.OnRoomUpdated(); + + updateState(); + } + + private void updateState() + { + var localUser = Client.LocalUser; + + if (localUser == null) + return; + + Debug.Assert(Room != null); + + switch (localUser.State) + { + default: + button.Text = "Spectate"; + button.BackgroundColour = colours.Blue; + button.Triangles.ColourDark = colours.Blue; + button.Triangles.ColourLight = colours.BlueLight; + break; + + case MultiplayerUserState.Spectating: + button.Text = "Stop spectating"; + button.BackgroundColour = colours.Red; + button.Triangles.ColourDark = colours.Red; + button.Triangles.ColourLight = colours.RedLight; + break; + } + + button.Enabled.Value = Client.Room?.State == MultiplayerRoomState.Open && !operationInProgress.Value; + } + + private class ButtonWithTrianglesExposed : TriangleButton + { + public new Triangles Triangles => base.Triangles; + } + } +}