From 6efe24695b2cf930f554fd72cd9c5c51214abf6f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 19 Dec 2020 02:59:11 +0900 Subject: [PATCH] Add the realtime multiplayer ready button --- .../TestSceneRealtimeReadyButton.cs | 129 ++++++++++++++++++ .../RealtimeReadyButton.cs | 122 +++++++++++++++++ 2 files changed, 251 insertions(+) create mode 100644 osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeReadyButton.cs create mode 100644 osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeReadyButton.cs diff --git a/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeReadyButton.cs b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeReadyButton.cs new file mode 100644 index 0000000000..889c0c0be3 --- /dev/null +++ b/osu.Game.Tests/Visual/RealtimeMultiplayer/TestSceneRealtimeReadyButton.cs @@ -0,0 +1,129 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Graphics; +using osu.Framework.Platform; +using osu.Game.Beatmaps; +using osu.Game.Online.Multiplayer; +using osu.Game.Online.RealtimeMultiplayer; +using osu.Game.Rulesets; +using osu.Game.Screens.Multi.RealtimeMultiplayer; +using osu.Game.Tests.Resources; +using osu.Game.Users; +using osuTK; +using osuTK.Input; + +namespace osu.Game.Tests.Visual.RealtimeMultiplayer +{ + public class TestSceneRealtimeReadyButton : RealtimeMultiplayerTestScene + { + private RealtimeReadyButton button; + + private BeatmapManager beatmaps; + private RulesetStore rulesets; + + [BackgroundDependencyLoader] + private void load(GameHost host, AudioManager audio) + { + Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); + Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default)); + beatmaps.Import(TestResources.GetTestBeatmapForImport(true)).Wait(); + } + + [SetUp] + public new void Setup() => Schedule(() => + { + var beatmap = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First().Beatmaps.First(); + + Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap); + + Child = button = new RealtimeReadyButton + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(200, 50), + SelectedItem = + { + Value = new PlaylistItem + { + Beatmap = { Value = beatmap }, + Ruleset = { Value = beatmap.Ruleset } + } + } + }; + + Client.AddUser(API.LocalUser.Value); + }); + + [Test] + public void TestToggleStateWhenNotHost() + { + AddStep("add second user as host", () => + { + Client.AddUser(new User { Id = 2, Username = "Another user" }); + Client.TransferHost(2); + }); + + addClickButtonStep(); + AddAssert("user is ready", () => Client.Room?.Users[0].State == MultiplayerUserState.Ready); + + addClickButtonStep(); + AddAssert("user is idle", () => Client.Room?.Users[0].State == MultiplayerUserState.Idle); + } + + [TestCase(true)] + [TestCase(false)] + public void TestToggleStateWhenHost(bool allReady) + { + AddStep("setup", () => + { + Client.TransferHost(Client.Room?.Users[0].UserID ?? 0); + + if (!allReady) + Client.AddUser(new User { Id = 2, Username = "Another user" }); + }); + + addClickButtonStep(); + AddAssert("user is ready", () => Client.Room?.Users[0].State == MultiplayerUserState.Ready); + + addClickButtonStep(); + AddAssert("match started", () => Client.Room?.Users[0].State == MultiplayerUserState.WaitingForLoad); + } + + [Test] + public void TestBecomeHostWhileReady() + { + addClickButtonStep(); + AddStep("make user host", () => Client.TransferHost(Client.Room?.Users[0].UserID ?? 0)); + + addClickButtonStep(); + AddAssert("match started", () => Client.Room?.Users[0].State == MultiplayerUserState.WaitingForLoad); + } + + [Test] + public void TestLoseHostWhileReady() + { + AddStep("setup", () => + { + Client.TransferHost(Client.Room?.Users[0].UserID ?? 0); + Client.AddUser(new User { Id = 2, Username = "Another user" }); + }); + + addClickButtonStep(); + AddStep("transfer host", () => Client.TransferHost(Client.Room?.Users[1].UserID ?? 0)); + + addClickButtonStep(); + AddAssert("match not started", () => 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/Screens/Multi/RealtimeMultiplayer/RealtimeReadyButton.cs b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeReadyButton.cs new file mode 100644 index 0000000000..d52df258ad --- /dev/null +++ b/osu.Game/Screens/Multi/RealtimeMultiplayer/RealtimeReadyButton.cs @@ -0,0 +1,122 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Diagnostics; +using System.Linq; +using JetBrains.Annotations; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Game.Graphics; +using osu.Game.Graphics.Backgrounds; +using osu.Game.Online.API; +using osu.Game.Online.Multiplayer; +using osu.Game.Online.RealtimeMultiplayer; +using osu.Game.Screens.Multi.Components; +using osuTK; + +namespace osu.Game.Screens.Multi.RealtimeMultiplayer +{ + public class RealtimeReadyButton : RealtimeRoomComposite + { + public Bindable SelectedItem => button.SelectedItem; + + [Resolved] + private IAPIProvider api { get; set; } + + [CanBeNull] + private MultiplayerRoomUser localUser; + + [Resolved] + private OsuColour colours { get; set; } + + private readonly ButtonWithTrianglesExposed button; + + public RealtimeReadyButton() + { + InternalChild = button = new ButtonWithTrianglesExposed + { + RelativeSizeAxes = Axes.Both, + Size = Vector2.One, + Enabled = { Value = true }, + Action = onClick + }; + } + + protected override void OnRoomChanged() + { + base.OnRoomChanged(); + + localUser = Room?.Users.Single(u => u.User?.Id == api.LocalUser.Value.Id); + button.Enabled.Value = Client.Room?.State == MultiplayerRoomState.Open; + updateState(); + } + + private void updateState() + { + if (localUser == null) + return; + + Debug.Assert(Room != null); + + switch (localUser.State) + { + case MultiplayerUserState.Idle: + button.Text = "Ready"; + updateButtonColour(true); + break; + + case MultiplayerUserState.Ready: + if (Room?.Host?.Equals(localUser) == true) + { + button.Text = "Let's go!"; + updateButtonColour(Room.Users.All(u => u.State == MultiplayerUserState.Ready)); + } + else + { + button.Text = "Waiting for host..."; + updateButtonColour(false); + } + + break; + } + } + + private void updateButtonColour(bool green) + { + if (green) + { + button.BackgroundColour = colours.Green; + button.Triangles.ColourDark = colours.Green; + button.Triangles.ColourLight = colours.GreenLight; + } + else + { + button.BackgroundColour = colours.YellowDark; + button.Triangles.ColourDark = colours.YellowDark; + button.Triangles.ColourLight = colours.Yellow; + } + } + + private void onClick() + { + if (localUser == null) + return; + + if (localUser.State == MultiplayerUserState.Idle) + Client.ChangeState(MultiplayerUserState.Ready); + else + { + if (Room?.Host?.Equals(localUser) == true) + Client.StartMatch(); + else + Client.ChangeState(MultiplayerUserState.Idle); + } + } + + private class ButtonWithTrianglesExposed : ReadyButton + { + public new Triangles Triangles => base.Triangles; + } + } +}