diff --git a/osu.Game.Tests/Visual/TestCaseLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/TestCaseLoungeRoomsContainer.cs index 1dfc3cdc60..2b362743e8 100644 --- a/osu.Game.Tests/Visual/TestCaseLoungeRoomsContainer.cs +++ b/osu.Game.Tests/Visual/TestCaseLoungeRoomsContainer.cs @@ -61,7 +61,7 @@ namespace osu.Game.Tests.Visual AddAssert("first room removed", () => container.Rooms.All(r => r.Room.RoomID.Value != 0)); AddStep("select first room", () => container.Rooms.First().Action?.Invoke()); - AddAssert("first room selected", () => container.SelectedRoom.Value == roomManager.Rooms.First()); + AddAssert("first room selected", () => roomManager.CurrentRoom.Value == roomManager.Rooms.First()); AddStep("join first room", () => container.Rooms.First().Action?.Invoke()); AddAssert("first room joined", () => roomManager.Rooms.First().Status.Value is JoinedRoomStatus); @@ -76,6 +76,8 @@ namespace osu.Game.Tests.Visual public readonly BindableList Rooms = new BindableList(); IBindableList IRoomManager.Rooms => Rooms; + public Bindable CurrentRoom { get; } = new Bindable(); + public void CreateRoom(Room room, Action onSuccess = null, Action onError = null) => Rooms.Add(room); public void JoinRoom(Room room, Action onSuccess = null, Action onError = null) diff --git a/osu.Game.Tests/Visual/TestCaseMatchHeader.cs b/osu.Game.Tests/Visual/TestCaseMatchHeader.cs index c1664c99a3..296e5f24ac 100644 --- a/osu.Game.Tests/Visual/TestCaseMatchHeader.cs +++ b/osu.Game.Tests/Visual/TestCaseMatchHeader.cs @@ -12,7 +12,7 @@ using osu.Game.Screens.Multi.Match.Components; namespace osu.Game.Tests.Visual { - public class TestCaseMatchHeader : OsuTestCase + public class TestCaseMatchHeader : MultiplayerTestCase { public override IReadOnlyList RequiredTypes => new[] { @@ -21,11 +21,7 @@ namespace osu.Game.Tests.Visual public TestCaseMatchHeader() { - var room = new Room(); - - var header = new Header(room); - - room.Playlist.Add(new PlaylistItem + Room.Playlist.Add(new PlaylistItem { Beatmap = new BeatmapInfo { @@ -46,9 +42,9 @@ namespace osu.Game.Tests.Visual } }); - room.Type.Value = new GameTypeTimeshift(); + Room.Type.Value = new GameTypeTimeshift(); - Child = header; + Child = new Header(); } } } diff --git a/osu.Game.Tests/Visual/TestCaseMatchInfo.cs b/osu.Game.Tests/Visual/TestCaseMatchInfo.cs index 57b21f2d79..901c4f1644 100644 --- a/osu.Game.Tests/Visual/TestCaseMatchInfo.cs +++ b/osu.Game.Tests/Visual/TestCaseMatchInfo.cs @@ -14,7 +14,7 @@ using osu.Game.Screens.Multi.Match.Components; namespace osu.Game.Tests.Visual { [TestFixture] - public class TestCaseMatchInfo : OsuTestCase + public class TestCaseMatchInfo : MultiplayerTestCase { public override IReadOnlyList RequiredTypes => new[] { @@ -27,18 +27,15 @@ namespace osu.Game.Tests.Visual [BackgroundDependencyLoader] private void load(RulesetStore rulesets) { - var room = new Room(); + Add(new Info()); - Info info = new Info(room); - Add(info); - - AddStep(@"set name", () => room.Name.Value = @"Room Name?"); - AddStep(@"set availability", () => room.Availability.Value = RoomAvailability.FriendsOnly); - AddStep(@"set status", () => room.Status.Value = new RoomStatusPlaying()); + AddStep(@"set name", () => Room.Name.Value = @"Room Name?"); + AddStep(@"set availability", () => Room.Availability.Value = RoomAvailability.FriendsOnly); + AddStep(@"set status", () => Room.Status.Value = new RoomStatusPlaying()); AddStep(@"set beatmap", () => { - room.Playlist.Clear(); - room.Playlist.Add(new PlaylistItem + Room.Playlist.Clear(); + Room.Playlist.Add(new PlaylistItem { Beatmap = new BeatmapInfo { @@ -54,14 +51,14 @@ namespace osu.Game.Tests.Visual }); }); - AddStep(@"change name", () => room.Name.Value = @"Room Name!"); - AddStep(@"change availability", () => room.Availability.Value = RoomAvailability.InviteOnly); - AddStep(@"change status", () => room.Status.Value = new RoomStatusOpen()); - AddStep(@"null beatmap", () => room.Playlist.Clear()); + AddStep(@"change name", () => Room.Name.Value = @"Room Name!"); + AddStep(@"change availability", () => Room.Availability.Value = RoomAvailability.InviteOnly); + AddStep(@"change status", () => Room.Status.Value = new RoomStatusOpen()); + AddStep(@"null beatmap", () => Room.Playlist.Clear()); AddStep(@"change beatmap", () => { - room.Playlist.Clear(); - room.Playlist.Add(new PlaylistItem + Room.Playlist.Clear(); + Room.Playlist.Add(new PlaylistItem { Beatmap = new BeatmapInfo { diff --git a/osu.Game.Tests/Visual/TestCaseMatchLeaderboard.cs b/osu.Game.Tests/Visual/TestCaseMatchLeaderboard.cs index 110c7699cb..42a886a5a3 100644 --- a/osu.Game.Tests/Visual/TestCaseMatchLeaderboard.cs +++ b/osu.Game.Tests/Visual/TestCaseMatchLeaderboard.cs @@ -6,24 +6,24 @@ using Newtonsoft.Json; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Online.API; -using osu.Game.Online.Multiplayer; using osu.Game.Screens.Multi.Match.Components; using osu.Game.Users; using osuTK; namespace osu.Game.Tests.Visual { - public class TestCaseMatchLeaderboard : OsuTestCase + public class TestCaseMatchLeaderboard : MultiplayerTestCase { public TestCaseMatchLeaderboard() { + Room.RoomID.Value = 3; + Add(new MatchLeaderboard { Origin = Anchor.Centre, Anchor = Anchor.Centre, Size = new Vector2(550f, 450f), Scope = MatchLeaderboardScope.Overall, - Room = new Room { RoomID = { Value = 3 } } }); } diff --git a/osu.Game.Tests/Visual/TestCaseMatchParticipants.cs b/osu.Game.Tests/Visual/TestCaseMatchParticipants.cs index 174f39a702..716523c23c 100644 --- a/osu.Game.Tests/Visual/TestCaseMatchParticipants.cs +++ b/osu.Game.Tests/Visual/TestCaseMatchParticipants.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.Collections.Generic; using NUnit.Framework; -using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Game.Screens.Multi.Match.Components; using osu.Game.Users; @@ -11,22 +9,14 @@ using osu.Game.Users; namespace osu.Game.Tests.Visual { [TestFixture] - public class TestCaseMatchParticipants : OsuTestCase + public class TestCaseMatchParticipants : MultiplayerTestCase { - private readonly Bindable maxParticipants = new Bindable(); - private readonly Bindable> users = new Bindable>(); - public TestCaseMatchParticipants() { - Participants participants; + Add(new Participants { RelativeSizeAxes = Axes.Both }); - Add(participants = new Participants { RelativeSizeAxes = Axes.Both }); - - participants.MaxParticipants.BindTo(maxParticipants); - participants.Users.BindTo(users); - - AddStep(@"set max to null", () => maxParticipants.Value = null); - AddStep(@"set users", () => users.Value = new[] + AddStep(@"set max to null", () => Room.MaxParticipants.Value = null); + AddStep(@"set users", () => Room.Participants.Value = new[] { new User { @@ -54,9 +44,9 @@ namespace osu.Game.Tests.Visual }, }); - AddStep(@"set max", () => maxParticipants.Value = 10); - AddStep(@"clear users", () => users.Value = new User[] { }); - AddStep(@"set max to null", () => maxParticipants.Value = null); + AddStep(@"set max", () => Room.MaxParticipants.Value = 10); + AddStep(@"clear users", () => Room.Participants.Value = new User[] { }); + AddStep(@"set max to null", () => Room.MaxParticipants.Value = null); } } } diff --git a/osu.Game.Tests/Visual/TestCaseMatchResults.cs b/osu.Game.Tests/Visual/TestCaseMatchResults.cs index 3ce03cf723..9ef520af4b 100644 --- a/osu.Game.Tests/Visual/TestCaseMatchResults.cs +++ b/osu.Game.Tests/Visual/TestCaseMatchResults.cs @@ -8,7 +8,6 @@ using osu.Framework.Allocation; using osu.Game.Beatmaps; using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; -using osu.Game.Online.Multiplayer; using osu.Game.Scoring; using osu.Game.Screens.Multi.Match.Components; using osu.Game.Screens.Multi.Ranking; @@ -19,7 +18,7 @@ using osu.Game.Users; namespace osu.Game.Tests.Visual { - public class TestCaseMatchResults : OsuTestCase + public class TestCaseMatchResults : MultiplayerTestCase { public override IReadOnlyList RequiredTypes => new[] { @@ -38,6 +37,9 @@ namespace osu.Game.Tests.Visual if (beatmapInfo != null) Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmapInfo); + Room.RoomID.Value = 1; + Room.Name.Value = "an awesome room"; + Child = new TestMatchResults(new ScoreInfo { User = new User { Id = 10 }, @@ -46,60 +48,41 @@ namespace osu.Game.Tests.Visual private class TestMatchResults : MatchResults { - private readonly Room room; - public TestMatchResults(ScoreInfo score) - : this(score, new Room - { - RoomID = { Value = 1 }, - Name = { Value = "an awesome room" } - }) + : base(score) { } - public TestMatchResults(ScoreInfo score, Room room) - : base(score, room) - { - this.room = room; - } - - protected override IEnumerable CreateResultPages() => new[] { new TestRoomLeaderboardPageInfo(Score, Beatmap, room) }; + protected override IEnumerable CreateResultPages() => new[] { new TestRoomLeaderboardPageInfo(Score, Beatmap) }; } private class TestRoomLeaderboardPageInfo : RoomLeaderboardPageInfo { private readonly ScoreInfo score; private readonly WorkingBeatmap beatmap; - private readonly Room room; - public TestRoomLeaderboardPageInfo(ScoreInfo score, WorkingBeatmap beatmap, Room room) - : base(score, beatmap, room) + public TestRoomLeaderboardPageInfo(ScoreInfo score, WorkingBeatmap beatmap) + : base(score, beatmap) { this.score = score; this.beatmap = beatmap; - this.room = room; } - public override ResultsPage CreatePage() => new TestRoomLeaderboardPage(score, beatmap, room); + public override ResultsPage CreatePage() => new TestRoomLeaderboardPage(score, beatmap); } private class TestRoomLeaderboardPage : RoomLeaderboardPage { - public TestRoomLeaderboardPage(ScoreInfo score, WorkingBeatmap beatmap, Room room) - : base(score, beatmap, room) + public TestRoomLeaderboardPage(ScoreInfo score, WorkingBeatmap beatmap) + : base(score, beatmap) { } - protected override MatchLeaderboard CreateLeaderboard(Room room) => new TestMatchLeaderboard(room); + protected override MatchLeaderboard CreateLeaderboard() => new TestMatchLeaderboard(); } private class TestMatchLeaderboard : RoomLeaderboardPage.ResultsMatchLeaderboard { - public TestMatchLeaderboard(Room room) - : base(room) - { - } - protected override APIRequest FetchScores(Action> scoresCallback) { var scores = Enumerable.Range(0, 50).Select(createRoomScore).ToArray(); diff --git a/osu.Game.Tests/Visual/TestCaseMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/TestCaseMatchSettingsOverlay.cs index 16240f0c45..54b2e8a8fc 100644 --- a/osu.Game.Tests/Visual/TestCaseMatchSettingsOverlay.cs +++ b/osu.Game.Tests/Visual/TestCaseMatchSettingsOverlay.cs @@ -18,7 +18,7 @@ using osu.Game.Screens.Multi.Match.Components; namespace osu.Game.Tests.Visual { - public class TestCaseMatchSettingsOverlay : OsuTestCase + public class TestCaseMatchSettingsOverlay : MultiplayerTestCase { public override IReadOnlyList RequiredTypes => new[] { @@ -28,14 +28,14 @@ namespace osu.Game.Tests.Visual [Cached(Type = typeof(IRoomManager))] private TestRoomManager roomManager = new TestRoomManager(); - private Room room; private TestRoomSettings settings; [SetUp] public void Setup() => Schedule(() => { - room = new Room(); - settings = new TestRoomSettings(room) + Room = new Room(); + + settings = new TestRoomSettings { RelativeSizeAxes = Axes.Both, State = Visibility.Visible @@ -49,19 +49,19 @@ namespace osu.Game.Tests.Visual { AddStep("clear name and beatmap", () => { - room.Name.Value = ""; - room.Playlist.Clear(); + Room.Name.Value = ""; + Room.Playlist.Clear(); }); AddAssert("button disabled", () => !settings.ApplyButton.Enabled); - AddStep("set name", () => room.Name.Value = "Room name"); + AddStep("set name", () => Room.Name.Value = "Room name"); AddAssert("button disabled", () => !settings.ApplyButton.Enabled); - AddStep("set beatmap", () => room.Playlist.Add(new PlaylistItem { Beatmap = new DummyWorkingBeatmap().BeatmapInfo })); + AddStep("set beatmap", () => Room.Playlist.Add(new PlaylistItem { Beatmap = new DummyWorkingBeatmap().BeatmapInfo })); AddAssert("button enabled", () => settings.ApplyButton.Enabled); - AddStep("clear name", () => room.Name.Value = ""); + AddStep("clear name", () => Room.Name.Value = ""); AddAssert("button disabled", () => !settings.ApplyButton.Enabled); } @@ -117,17 +117,12 @@ namespace osu.Game.Tests.Visual private class TestRoomSettings : MatchSettingsOverlay { - public new TriangleButton ApplyButton => base.ApplyButton; + public TriangleButton ApplyButton => Settings.ApplyButton; - public new OsuTextBox NameField => base.NameField; - public new OsuDropdown DurationField => base.DurationField; + public OsuTextBox NameField => Settings.NameField; + public OsuDropdown DurationField => Settings.DurationField; - public new OsuSpriteText ErrorText => base.ErrorText; - - public TestRoomSettings(Room room) - : base(room) - { - } + public OsuSpriteText ErrorText => Settings.ErrorText; } private class TestRoomManager : IRoomManager @@ -140,6 +135,8 @@ namespace osu.Game.Tests.Visual public IBindableList Rooms { get; } = null; + public Bindable CurrentRoom { get; } = new Bindable(); + public void CreateRoom(Room room, Action onSuccess = null, Action onError = null) { if (CreateRequested == null) diff --git a/osu.Game/Online/Multiplayer/Room.cs b/osu.Game/Online/Multiplayer/Room.cs index 46c22ef0cd..0d5b168dcb 100644 --- a/osu.Game/Online/Multiplayer/Room.cs +++ b/osu.Game/Online/Multiplayer/Room.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using Newtonsoft.Json; +using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Game.Online.Multiplayer.GameTypes; using osu.Game.Online.Multiplayer.RoomStatuses; @@ -14,42 +15,55 @@ namespace osu.Game.Online.Multiplayer { public class Room { + [Cached] [JsonProperty("id")] public Bindable RoomID { get; private set; } = new Bindable(); + [Cached] [JsonProperty("name")] public Bindable Name { get; private set; } = new Bindable(); + [Cached] [JsonProperty("host")] public Bindable Host { get; private set; } = new Bindable(); + [Cached] [JsonProperty("playlist")] - public BindableList Playlist { get; set; } = new BindableList(); + public BindableList Playlist { get; private set; } = new BindableList(); + [Cached] [JsonProperty("channel_id")] public Bindable ChannelId { get; private set; } = new Bindable(); + [Cached] [JsonIgnore] public Bindable Duration { get; private set; } = new Bindable(TimeSpan.FromMinutes(30)); + [Cached] [JsonIgnore] public Bindable MaxAttempts { get; private set; } = new Bindable(); + [Cached] [JsonIgnore] public Bindable Status { get; private set; } = new Bindable(new RoomStatusOpen()); + [Cached] [JsonIgnore] public Bindable Availability { get; private set; } = new Bindable(); + [Cached] [JsonIgnore] public Bindable Type { get; private set; } = new Bindable(new GameTypeTimeshift()); + [Cached] [JsonIgnore] public Bindable MaxParticipants { get; private set; } = new Bindable(); + [Cached] [JsonIgnore] public Bindable> Participants { get; private set; } = new Bindable>(Enumerable.Empty()); + [Cached] public Bindable ParticipantCount { get; private set; } = new Bindable(); // todo: TEMPORARY @@ -68,6 +82,7 @@ namespace osu.Game.Online.Multiplayer } // Only supports retrieval for now + [Cached] [JsonProperty("ends_at")] public Bindable EndDate { get; private set; } = new Bindable(); diff --git a/osu.Game/Screens/Multi/Components/BeatmapTitle.cs b/osu.Game/Screens/Multi/Components/BeatmapTitle.cs index 145375422a..dca0545035 100644 --- a/osu.Game/Screens/Multi/Components/BeatmapTitle.cs +++ b/osu.Game/Screens/Multi/Components/BeatmapTitle.cs @@ -2,11 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Framework.Configuration; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Localisation; -using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -14,10 +11,8 @@ using osu.Game.Online.Chat; namespace osu.Game.Screens.Multi.Components { - public class BeatmapTitle : CompositeDrawable + public class BeatmapTitle : MultiplayerComposite { - public readonly IBindable Beatmap = new Bindable(); - private readonly LinkFlowContainer textFlow; public BeatmapTitle() @@ -27,10 +22,10 @@ namespace osu.Game.Screens.Multi.Components InternalChild = textFlow = new LinkFlowContainer { AutoSizeAxes = Axes.Both }; } - protected override void LoadComplete() + [BackgroundDependencyLoader] + private void load() { - base.LoadComplete(); - Beatmap.BindValueChanged(v => updateText(), true); + CurrentBeatmap.BindValueChanged(v => updateText(), true); } private float textSize = OsuSpriteText.FONT_SIZE; @@ -58,7 +53,7 @@ namespace osu.Game.Screens.Multi.Components textFlow.Clear(); - if (Beatmap.Value == null) + if (CurrentBeatmap.Value == null) textFlow.AddText("No beatmap selected", s => { s.TextSize = TextSize; @@ -70,7 +65,7 @@ namespace osu.Game.Screens.Multi.Components { new OsuSpriteText { - Text = new LocalisedString((Beatmap.Value.Metadata.ArtistUnicode, Beatmap.Value.Metadata.Artist)), + Text = new LocalisedString((CurrentBeatmap.Value.Metadata.ArtistUnicode, CurrentBeatmap.Value.Metadata.Artist)), TextSize = TextSize, }, new OsuSpriteText @@ -80,10 +75,10 @@ namespace osu.Game.Screens.Multi.Components }, new OsuSpriteText { - Text = new LocalisedString((Beatmap.Value.Metadata.TitleUnicode, Beatmap.Value.Metadata.Title)), + Text = new LocalisedString((CurrentBeatmap.Value.Metadata.TitleUnicode, CurrentBeatmap.Value.Metadata.Title)), TextSize = TextSize, } - }, null, LinkAction.OpenBeatmap, Beatmap.Value.OnlineBeatmapID.ToString(), "Open beatmap"); + }, null, LinkAction.OpenBeatmap, CurrentBeatmap.Value.OnlineBeatmapID.ToString(), "Open beatmap"); } } } diff --git a/osu.Game/Screens/Multi/Components/BeatmapTypeInfo.cs b/osu.Game/Screens/Multi/Components/BeatmapTypeInfo.cs index 24c8e3c148..35d1ffad1c 100644 --- a/osu.Game/Screens/Multi/Components/BeatmapTypeInfo.cs +++ b/osu.Game/Screens/Multi/Components/BeatmapTypeInfo.cs @@ -1,31 +1,26 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Configuration; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Online.Chat; -using osu.Game.Online.Multiplayer; -using osu.Game.Rulesets; using osuTK; namespace osu.Game.Screens.Multi.Components { - public class BeatmapTypeInfo : CompositeDrawable + public class BeatmapTypeInfo : MultiplayerComposite { - public readonly IBindable Beatmap = new Bindable(); - public readonly IBindable Ruleset = new Bindable(); - public readonly IBindable Type = new Bindable(); - public BeatmapTypeInfo() { AutoSizeAxes = Axes.Both; + } - BeatmapTitle beatmapTitle; - ModeTypeInfo modeTypeInfo; + [BackgroundDependencyLoader] + private void load() + { LinkFlowContainer beatmapAuthor; InternalChild = new FillFlowContainer @@ -36,7 +31,7 @@ namespace osu.Game.Screens.Multi.Components Spacing = new Vector2(5, 0), Children = new Drawable[] { - modeTypeInfo = new ModeTypeInfo(), + new ModeTypeInfo(), new Container { AutoSizeAxes = Axes.X, @@ -44,7 +39,7 @@ namespace osu.Game.Screens.Multi.Components Margin = new MarginPadding { Left = 5 }, Children = new Drawable[] { - beatmapTitle = new BeatmapTitle(), + new BeatmapTitle(), beatmapAuthor = new LinkFlowContainer(s => s.TextSize = 14) { Anchor = Anchor.BottomLeft, @@ -56,13 +51,7 @@ namespace osu.Game.Screens.Multi.Components } }; - modeTypeInfo.Beatmap.BindTo(Beatmap); - modeTypeInfo.Ruleset.BindTo(Ruleset); - modeTypeInfo.Type.BindTo(Type); - - beatmapTitle.Beatmap.BindTo(Beatmap); - - Beatmap.BindValueChanged(v => + CurrentBeatmap.BindValueChanged(v => { beatmapAuthor.Clear(); diff --git a/osu.Game/Screens/Multi/Components/ModeTypeInfo.cs b/osu.Game/Screens/Multi/Components/ModeTypeInfo.cs index cdb5974f8f..6ae3b7be7c 100644 --- a/osu.Game/Screens/Multi/Components/ModeTypeInfo.cs +++ b/osu.Game/Screens/Multi/Components/ModeTypeInfo.cs @@ -1,32 +1,29 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Configuration; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; -using osu.Game.Online.Multiplayer; -using osu.Game.Rulesets; using osuTK; namespace osu.Game.Screens.Multi.Components { - public class ModeTypeInfo : CompositeDrawable + public class ModeTypeInfo : MultiplayerComposite { private const float height = 30; private const float transition_duration = 100; - private readonly Container rulesetContainer; - - public readonly IBindable Beatmap = new Bindable(); - public readonly IBindable Ruleset = new Bindable(); - public readonly IBindable Type = new Bindable(); + private Container rulesetContainer; public ModeTypeInfo() { AutoSizeAxes = Axes.Both; + } + [BackgroundDependencyLoader] + private void load() + { Container gameTypeContainer; InternalChild = new FillFlowContainer @@ -48,17 +45,17 @@ namespace osu.Game.Screens.Multi.Components }, }; - Beatmap.BindValueChanged(updateBeatmap); - Ruleset.BindValueChanged(_ => updateBeatmap(Beatmap.Value)); + CurrentBeatmap.BindValueChanged(_ => updateBeatmap()); + CurrentRuleset.BindValueChanged(_ => updateBeatmap()); Type.BindValueChanged(v => gameTypeContainer.Child = new DrawableGameType(v) { Size = new Vector2(height) }); } - private void updateBeatmap(BeatmapInfo beatmap) + private void updateBeatmap() { - if (beatmap != null) + if (CurrentBeatmap.Value != null) { rulesetContainer.FadeIn(transition_duration); - rulesetContainer.Child = new DifficultyIcon(beatmap, Ruleset.Value) { Size = new Vector2(height) }; + rulesetContainer.Child = new DifficultyIcon(CurrentBeatmap.Value, CurrentRuleset.Value) { Size = new Vector2(height) }; } else rulesetContainer.FadeOut(transition_duration); diff --git a/osu.Game/Screens/Multi/Components/MultiplayerBackgroundSprite.cs b/osu.Game/Screens/Multi/Components/MultiplayerBackgroundSprite.cs new file mode 100644 index 0000000000..8eff7b14af --- /dev/null +++ b/osu.Game/Screens/Multi/Components/MultiplayerBackgroundSprite.cs @@ -0,0 +1,24 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Beatmaps.Drawables; + +namespace osu.Game.Screens.Multi.Components +{ + public class MultiplayerBackgroundSprite : MultiplayerComposite + { + [BackgroundDependencyLoader] + private void load() + { + UpdateableBeatmapBackgroundSprite sprite; + + InternalChild = sprite = CreateBackgroundSprite(); + + sprite.Beatmap.BindTo(CurrentBeatmap); + } + + protected virtual UpdateableBeatmapBackgroundSprite CreateBackgroundSprite() => new UpdateableBeatmapBackgroundSprite { RelativeSizeAxes = Axes.Both }; + } +} diff --git a/osu.Game/Screens/Multi/Components/ParticipantCount.cs b/osu.Game/Screens/Multi/Components/ParticipantCount.cs index 95cd6a7a47..9711767924 100644 --- a/osu.Game/Screens/Multi/Components/ParticipantCount.cs +++ b/osu.Game/Screens/Multi/Components/ParticipantCount.cs @@ -1,31 +1,29 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Collections.Generic; using System.Linq; -using osu.Framework.Configuration; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.Sprites; -using osu.Game.Users; namespace osu.Game.Screens.Multi.Components { - public class ParticipantCountDisplay : CompositeDrawable + public class ParticipantCountDisplay : MultiplayerComposite { private const float text_size = 30; private const float transition_duration = 100; - private readonly OsuSpriteText slash, maxText; - - public readonly IBindable> Participants = new Bindable>(); - public readonly IBindable ParticipantCount = new Bindable(); - public readonly IBindable MaxParticipants = new Bindable(); + private OsuSpriteText slash, maxText; public ParticipantCountDisplay() { AutoSizeAxes = Axes.Both; + } + [BackgroundDependencyLoader] + private void load() + { OsuSpriteText count; InternalChild = new FillFlowContainer diff --git a/osu.Game/Screens/Multi/Components/RoomStatusInfo.cs b/osu.Game/Screens/Multi/Components/RoomStatusInfo.cs index 743cfe0114..24a2d70b60 100644 --- a/osu.Game/Screens/Multi/Components/RoomStatusInfo.cs +++ b/osu.Game/Screens/Multi/Components/RoomStatusInfo.cs @@ -13,16 +13,16 @@ using osu.Game.Online.Multiplayer.RoomStatuses; namespace osu.Game.Screens.Multi.Components { - public class RoomStatusInfo : CompositeDrawable + public class RoomStatusInfo : MultiplayerComposite { - private readonly RoomBindings bindings = new RoomBindings(); - - public RoomStatusInfo(Room room) + public RoomStatusInfo() { - bindings.Room = room; - AutoSizeAxes = Axes.Both; + } + [BackgroundDependencyLoader] + private void load() + { StatusPart statusPart; EndDatePart endDatePart; @@ -41,10 +41,10 @@ namespace osu.Game.Screens.Multi.Components } }; - statusPart.EndDate.BindTo(bindings.EndDate); - statusPart.Status.BindTo(bindings.Status); - statusPart.Availability.BindTo(bindings.Availability); - endDatePart.EndDate.BindTo(bindings.EndDate); + statusPart.EndDate.BindTo(EndDate); + statusPart.Status.BindTo(Status); + statusPart.Availability.BindTo(Availability); + endDatePart.EndDate.BindTo(EndDate); } private class EndDatePart : DrawableDate diff --git a/osu.Game/Screens/Multi/IRoomManager.cs b/osu.Game/Screens/Multi/IRoomManager.cs index 980879d79a..a00e08a80a 100644 --- a/osu.Game/Screens/Multi/IRoomManager.cs +++ b/osu.Game/Screens/Multi/IRoomManager.cs @@ -19,6 +19,11 @@ namespace osu.Game.Screens.Multi /// IBindableList Rooms { get; } + /// + /// The currently-active . + /// + Bindable CurrentRoom { get; } + /// /// Creates a new . /// diff --git a/osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs index 896a5eafdc..3918148b07 100644 --- a/osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs @@ -10,7 +10,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -34,12 +33,8 @@ namespace osu.Game.Screens.Multi.Lounge.Components public event Action StateChanged; - private readonly RoomBindings bindings = new RoomBindings(); - private readonly Box selectionBox; - private UpdateableBeatmapBackgroundSprite background; - private BeatmapTitle beatmapTitle; - private ModeTypeInfo modeTypeInfo; + private CachedModelDependencyContainer dependencies; [Resolved] private BeatmapManager beatmaps { get; set; } @@ -80,7 +75,6 @@ namespace osu.Game.Screens.Multi.Lounge.Components public DrawableRoom(Room room) { Room = room; - bindings.Room = room; RelativeSizeAxes = Axes.X; Height = height + SELECTION_BORDER_WIDTH * 2; @@ -99,7 +93,6 @@ namespace osu.Game.Screens.Multi.Lounge.Components private void load(OsuColour colours) { Box sideStrip; - ParticipantInfo participantInfo; OsuSpriteText name; Children = new Drawable[] @@ -138,7 +131,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components Width = cover_width, Masking = true, Margin = new MarginPadding { Left = side_strip_width }, - Child = background = new UpdateableBeatmapBackgroundSprite { RelativeSizeAxes = Axes.Both } + Child = new MultiplayerBackgroundSprite { RelativeSizeAxes = Axes.Both } }, new Container { @@ -159,8 +152,11 @@ namespace osu.Game.Screens.Multi.Lounge.Components Spacing = new Vector2(5f), Children = new Drawable[] { - name = new OsuSpriteText { TextSize = 18 }, - participantInfo = new ParticipantInfo(), + name = new OsuSpriteText + { + TextSize = 18 + }, + new ParticipantInfo(), }, }, new FillFlowContainer @@ -173,11 +169,11 @@ namespace osu.Game.Screens.Multi.Lounge.Components Spacing = new Vector2(0, 5), Children = new Drawable[] { - new RoomStatusInfo(Room), - beatmapTitle = new BeatmapTitle { TextSize = 14 }, + new RoomStatusInfo(), + new BeatmapTitle { TextSize = 14 }, }, }, - modeTypeInfo = new ModeTypeInfo + new ModeTypeInfo { Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, @@ -189,23 +185,21 @@ namespace osu.Game.Screens.Multi.Lounge.Components }, }; - background.Beatmap.BindTo(bindings.CurrentBeatmap); - modeTypeInfo.Beatmap.BindTo(bindings.CurrentBeatmap); - modeTypeInfo.Ruleset.BindTo(bindings.CurrentRuleset); - modeTypeInfo.Type.BindTo(bindings.Type); - beatmapTitle.Beatmap.BindTo(bindings.CurrentBeatmap); - participantInfo.Host.BindTo(bindings.Host); - participantInfo.Participants.BindTo(bindings.Participants); - participantInfo.ParticipantCount.BindTo(bindings.ParticipantCount); - - bindings.Name.BindValueChanged(n => name.Text = n, true); - bindings.Status.BindValueChanged(s => + dependencies.ShadowModel.Name.BindValueChanged(n => name.Text = n, true); + dependencies.ShadowModel.Status.BindValueChanged(s => { foreach (Drawable d in new Drawable[] { selectionBox, sideStrip }) d.FadeColour(s.GetAppropriateColour(colours), transition_duration); }, true); } + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) + { + dependencies = new CachedModelDependencyContainer(base.CreateChildDependencies(parent)); + dependencies.Model.Value = Room; + return dependencies; + } + protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Game/Screens/Multi/Lounge/Components/ParticipantInfo.cs b/osu.Game/Screens/Multi/Lounge/Components/ParticipantInfo.cs index a053032404..806bc92882 100644 --- a/osu.Game/Screens/Multi/Lounge/Components/ParticipantInfo.cs +++ b/osu.Game/Screens/Multi/Lounge/Components/ParticipantInfo.cs @@ -1,10 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Collections.Generic; using Humanizer; using osu.Framework.Allocation; -using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; @@ -16,26 +14,24 @@ using osuTK; namespace osu.Game.Screens.Multi.Lounge.Components { - public class ParticipantInfo : Container + public class ParticipantInfo : MultiplayerComposite { - private readonly FillFlowContainer summaryContainer; - - public readonly IBindable Host = new Bindable(); - public readonly IBindable> Participants = new Bindable>(); - public readonly IBindable ParticipantCount = new Bindable(); - public ParticipantInfo() { - OsuSpriteText summary; RelativeSizeAxes = Axes.X; Height = 15f; + } + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + OsuSpriteText summary; OsuSpriteText levelRangeHigher; OsuSpriteText levelRangeLower; Container flagContainer; LinkFlowContainer hostText; - Children = new Drawable[] + InternalChildren = new Drawable[] { new FillFlowContainer { @@ -73,12 +69,13 @@ namespace osu.Game.Screens.Multi.Lounge.Components } }, }, - summaryContainer = new FillFlowContainer + new FillFlowContainer { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, + Colour = colours.Gray9, Children = new[] { summary = new OsuSpriteText @@ -101,9 +98,9 @@ namespace osu.Game.Screens.Multi.Lounge.Components hostText.AddLink(v.Username, null, LinkAction.OpenUserProfile, v.Id.ToString(), "Open profile", s => s.Font = "Exo2.0-BoldItalic"); flagContainer.Child = new DrawableFlag(v.Country) { RelativeSizeAxes = Axes.Both }; } - }); + }, true); - ParticipantCount.BindValueChanged(v => summary.Text = $"{v:#,0}{" participant".Pluralize(v == 1)}"); + ParticipantCount.BindValueChanged(v => summary.Text = $"{v:#,0}{" participant".Pluralize(v == 1)}", true); /*Participants.BindValueChanged(v => { @@ -112,11 +109,5 @@ namespace osu.Game.Screens.Multi.Lounge.Components levelRangeHigher.Text = ranks.Max().ToString(); });*/ } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - summaryContainer.Colour = colours.Gray9; - } } } diff --git a/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs b/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs index 16af06fdd7..45855c2812 100644 --- a/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs +++ b/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs @@ -3,7 +3,6 @@ using System; using osu.Framework.Allocation; -using osu.Framework.Configuration; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; @@ -11,7 +10,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Online.API; @@ -24,24 +22,18 @@ using osuTK.Graphics; namespace osu.Game.Screens.Multi.Lounge.Components { - public class RoomInspector : Container + public class RoomInspector : MultiplayerComposite { private const float transition_duration = 100; - public readonly IBindable Room = new Bindable(); - private readonly MarginPadding contentPadding = new MarginPadding { Horizontal = 20, Vertical = 10 }; - private readonly RoomBindings bindings = new RoomBindings(); - private OsuColour colours; private Box statusStrip; - private UpdateableBeatmapBackgroundSprite background; private ParticipantCountDisplay participantCount; private OsuSpriteText name, status; private BeatmapTypeInfo beatmapTypeInfo; private ParticipantInfo participantInfo; - private MatchParticipants participants; [Resolved] private BeatmapManager beatmaps { get; set; } @@ -51,7 +43,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components { this.colours = colours; - Children = new Drawable[] + InternalChildren = new Drawable[] { new Box { @@ -84,7 +76,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components Masking = true, Children = new Drawable[] { - background = new UpdateableBeatmapBackgroundSprite { RelativeSizeAxes = Axes.Both }, + new MultiplayerBackgroundSprite { RelativeSizeAxes = Axes.Both }, new Box { RelativeSizeAxes = Axes.Both, @@ -162,7 +154,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components }, new Drawable[] { - participants = new MatchParticipants + new MatchParticipants { RelativeSizeAxes = Axes.Both, } @@ -171,27 +163,15 @@ namespace osu.Game.Screens.Multi.Lounge.Components } }; - participantInfo.Host.BindTo(bindings.Host); - participantInfo.ParticipantCount.BindTo(bindings.ParticipantCount); - participantInfo.Participants.BindTo(bindings.Participants); - participantCount.Participants.BindTo(bindings.Participants); - participantCount.ParticipantCount.BindTo(bindings.ParticipantCount); - participantCount.MaxParticipants.BindTo(bindings.MaxParticipants); - beatmapTypeInfo.Beatmap.BindTo(bindings.CurrentBeatmap); - beatmapTypeInfo.Ruleset.BindTo(bindings.CurrentRuleset); - beatmapTypeInfo.Type.BindTo(bindings.Type); - background.Beatmap.BindTo(bindings.CurrentBeatmap); - bindings.Status.BindValueChanged(displayStatus); - bindings.Name.BindValueChanged(n => name.Text = n); - Room.BindValueChanged(updateRoom, true); + Status.BindValueChanged(displayStatus); + Name.BindValueChanged(n => name.Text = n); + + RoomID.BindValueChanged(updateRoom); } - private void updateRoom(Room room) + private void updateRoom(int? roomId) { - bindings.Room = room; - participants.Room = room; - - if (room != null) + if (roomId != null) { participantCount.FadeIn(transition_duration); beatmapTypeInfo.FadeIn(transition_duration); @@ -224,24 +204,10 @@ namespace osu.Game.Screens.Multi.Lounge.Components public override Color4 GetAppropriateColour(OsuColour colours) => colours.Gray8; } - private class MatchParticipants : CompositeDrawable + private class MatchParticipants : MultiplayerComposite { - private Room room; private readonly FillFlowContainer fill; - public Room Room - { - get { return room; } - set - { - if (room == value) - return; - - room = value; - updateParticipants(); - } - } - public MatchParticipants() { Padding = new MarginPadding { Horizontal = 10 }; @@ -259,6 +225,12 @@ namespace osu.Game.Screens.Multi.Lounge.Components }; } + [BackgroundDependencyLoader] + private void load() + { + RoomID.BindValueChanged(_ => updateParticipants(), true); + } + [Resolved] private APIAccess api { get; set; } @@ -266,7 +238,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components private void updateParticipants() { - var roomId = room?.RoomID.Value ?? 0; + var roomId = RoomID.Value ?? 0; request?.Cancel(); @@ -284,7 +256,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components request = new GetRoomScoresRequest(roomId); request.Success += scores => { - if (roomId != room.RoomID.Value) + if (roomId != RoomID.Value) return; fill.Clear(); diff --git a/osu.Game/Screens/Multi/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/Multi/Lounge/Components/RoomsContainer.cs index ac13a16388..12d9b2957c 100644 --- a/osu.Game/Screens/Multi/Lounge/Components/RoomsContainer.cs +++ b/osu.Game/Screens/Multi/Lounge/Components/RoomsContainer.cs @@ -19,8 +19,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components { public Action JoinRequested; - private readonly Bindable selectedRoom = new Bindable(); - public IBindable SelectedRoom => selectedRoom; + private readonly Bindable currentRoom = new Bindable(); private readonly IBindableList rooms = new BindableList(); @@ -47,6 +46,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components [BackgroundDependencyLoader] private void load() { + currentRoom.BindTo(roomManager.CurrentRoom); rooms.BindTo(roomManager.Rooms); rooms.ItemsAdded += addRooms; @@ -121,7 +121,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components else roomFlow.Children.ForEach(r => r.State = r.Room == room ? SelectionState.Selected : SelectionState.NotSelected); - selectedRoom.Value = room; + currentRoom.Value = room; } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs index 5bc36090d5..50788a2fc2 100644 --- a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs @@ -2,6 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; @@ -24,13 +26,12 @@ namespace osu.Game.Screens.Multi.Lounge private readonly Action pushGameplayScreen; private readonly ProcessingOverlay processingOverlay; + private readonly Bindable currentRoom = new Bindable(); + public LoungeSubScreen(Action pushGameplayScreen) { this.pushGameplayScreen = pushGameplayScreen; - RoomInspector inspector; - RoomsContainer rooms; - InternalChildren = new Drawable[] { Filter = new FilterControl { Depth = -1 }, @@ -54,13 +55,13 @@ namespace osu.Game.Screens.Multi.Lounge { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Child = rooms = new RoomsContainer { JoinRequested = joinRequested } + Child = new RoomsContainer { JoinRequested = joinRequested } }, }, processingOverlay = new ProcessingOverlay { Alpha = 0 } } }, - inspector = new RoomInspector + new RoomInspector { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, @@ -71,11 +72,15 @@ namespace osu.Game.Screens.Multi.Lounge }, }; - inspector.Room.BindTo(rooms.SelectedRoom); - Filter.Search.Exit += this.Exit; } + [BackgroundDependencyLoader] + private void load(IRoomManager roomManager) + { + currentRoom.BindTo(roomManager.CurrentRoom); + } + protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); @@ -116,7 +121,7 @@ namespace osu.Game.Screens.Multi.Lounge processingOverlay.Show(); Manager?.JoinRoom(room, r => { - Push(room); + Open(room); processingOverlay.Hide(); }, _ => processingOverlay.Hide()); } @@ -124,13 +129,15 @@ namespace osu.Game.Screens.Multi.Lounge /// /// Push a room as a new subscreen. /// - public void Push(Room room) + public void Open(Room room) { // Handles the case where a room is clicked 3 times in quick succession if (!this.IsCurrentScreen()) return; - this.Push(new MatchSubScreen(room, s => pushGameplayScreen?.Invoke(s))); + currentRoom.Value = room; + + this.Push(new MatchSubScreen(s => pushGameplayScreen?.Invoke(s))); } } } diff --git a/osu.Game/Screens/Multi/Match/Components/Header.cs b/osu.Game/Screens/Multi/Match/Components/Header.cs index 29546f9b06..55c7c02be4 100644 --- a/osu.Game/Screens/Multi/Match/Components/Header.cs +++ b/osu.Game/Screens/Multi/Match/Components/Header.cs @@ -20,31 +20,27 @@ using osuTK.Graphics; namespace osu.Game.Screens.Multi.Match.Components { - public class Header : Container + public class Header : MultiplayerComposite { public const float HEIGHT = 200; - private readonly RoomBindings bindings = new RoomBindings(); + public MatchTabControl Tabs; - private readonly Box tabStrip; + public Action RequestBeatmapSelection; - public readonly MatchTabControl Tabs; - - public Action OnRequestSelectBeatmap; - - public Header(Room room) + public Header() { RelativeSizeAxes = Axes.X; Height = HEIGHT; + } - bindings.Room = room; - - BeatmapTypeInfo beatmapTypeInfo; + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { BeatmapSelectButton beatmapButton; - UpdateableBeatmapBackgroundSprite background; ModDisplay modDisplay; - Children = new Drawable[] + InternalChildren = new Drawable[] { new Container { @@ -52,7 +48,7 @@ namespace osu.Game.Screens.Multi.Match.Components Masking = true, Children = new Drawable[] { - background = new HeaderBeatmapBackgroundSprite { RelativeSizeAxes = Axes.Both }, + new HeaderBackgroundSprite { RelativeSizeAxes = Axes.Both }, new Box { RelativeSizeAxes = Axes.Both, @@ -60,12 +56,13 @@ namespace osu.Game.Screens.Multi.Match.Components }, } }, - tabStrip = new Box + new Box { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, RelativeSizeAxes = Axes.X, Height = 1, + Colour = colours.Yellow }, new Container { @@ -80,7 +77,7 @@ namespace osu.Game.Screens.Multi.Match.Components Direction = FillDirection.Vertical, Children = new Drawable[] { - beatmapTypeInfo = new BeatmapTypeInfo(), + new BeatmapTypeInfo(), modDisplay = new ModDisplay { Scale = new Vector2(0.75f), @@ -95,13 +92,13 @@ namespace osu.Game.Screens.Multi.Match.Components RelativeSizeAxes = Axes.Y, Width = 200, Padding = new MarginPadding { Vertical = 10 }, - Child = beatmapButton = new BeatmapSelectButton(room) + Child = beatmapButton = new BeatmapSelectButton { RelativeSizeAxes = Axes.Both, Height = 1, }, }, - Tabs = new MatchTabControl(room) + Tabs = new MatchTabControl { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, @@ -111,37 +108,36 @@ namespace osu.Game.Screens.Multi.Match.Components }, }; - beatmapTypeInfo.Beatmap.BindTo(bindings.CurrentBeatmap); - beatmapTypeInfo.Ruleset.BindTo(bindings.CurrentRuleset); - beatmapTypeInfo.Type.BindTo(bindings.Type); - background.Beatmap.BindTo(bindings.CurrentBeatmap); - bindings.CurrentMods.BindValueChanged(m => modDisplay.Current.Value = m, true); + CurrentMods.BindValueChanged(m => modDisplay.Current.Value = m, true); - beatmapButton.Action = () => OnRequestSelectBeatmap?.Invoke(); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - tabStrip.Colour = colours.Yellow; + beatmapButton.Action = () => RequestBeatmapSelection?.Invoke(); } private class BeatmapSelectButton : HeaderButton { - private readonly IBindable roomIDBind = new Bindable(); + [Resolved(typeof(Room), nameof(Online.Multiplayer.Room.RoomID))] + private Bindable roomId { get; set; } - public BeatmapSelectButton(Room room) + public BeatmapSelectButton() { Text = "Select beatmap"; + } - roomIDBind.BindTo(room.RoomID); - roomIDBind.BindValueChanged(v => this.FadeTo(v.HasValue ? 0 : 1), true); + [BackgroundDependencyLoader] + private void load() + { + roomId.BindValueChanged(v => this.FadeTo(v.HasValue ? 0 : 1), true); } } - private class HeaderBeatmapBackgroundSprite : UpdateableBeatmapBackgroundSprite + private class HeaderBackgroundSprite : MultiplayerBackgroundSprite { - protected override double FadeDuration => 200; + protected override UpdateableBeatmapBackgroundSprite CreateBackgroundSprite() => new BackgroundSprite { RelativeSizeAxes = Axes.Both }; + + private class BackgroundSprite : UpdateableBeatmapBackgroundSprite + { + protected override double FadeDuration => 200; + } } } } diff --git a/osu.Game/Screens/Multi/Match/Components/Info.cs b/osu.Game/Screens/Multi/Match/Components/Info.cs index 7894385afb..ec6dbb6d12 100644 --- a/osu.Game/Screens/Multi/Match/Components/Info.cs +++ b/osu.Game/Screens/Multi/Match/Components/Info.cs @@ -2,35 +2,37 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osu.Game.Online.Multiplayer; using osu.Game.Overlays.SearchableList; using osu.Game.Screens.Multi.Components; using osuTK; namespace osu.Game.Screens.Multi.Match.Components { - public class Info : Container + public class Info : MultiplayerComposite { public Action OnStart; - private readonly RoomBindings bindings = new RoomBindings(); - - public Info(Room room) + public Info() { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; + } + [BackgroundDependencyLoader] + private void load() + { ReadyButton readyButton; ViewBeatmapButton viewBeatmapButton; HostInfo hostInfo; RoomStatusInfo statusInfo; - Children = new Drawable[] + InternalChildren = new Drawable[] { new Box { @@ -61,9 +63,9 @@ namespace osu.Game.Screens.Multi.Match.Components new OsuSpriteText { TextSize = 30, - Current = bindings.Name + Current = Name }, - new RoomStatusInfo(room), + new RoomStatusInfo(), } }, hostInfo = new HostInfo(), @@ -80,7 +82,7 @@ namespace osu.Game.Screens.Multi.Match.Components Children = new Drawable[] { viewBeatmapButton = new ViewBeatmapButton(), - readyButton = new ReadyButton(room) + readyButton = new ReadyButton { Action = () => OnStart?.Invoke() } @@ -90,11 +92,9 @@ namespace osu.Game.Screens.Multi.Match.Components }, }; - viewBeatmapButton.Beatmap.BindTo(bindings.CurrentBeatmap); - readyButton.Beatmap.BindTo(bindings.CurrentBeatmap); - hostInfo.Host.BindTo(bindings.Host); - - bindings.Room = room; + viewBeatmapButton.Beatmap.BindTo(CurrentBeatmap); + readyButton.Beatmap.BindTo(CurrentBeatmap); + hostInfo.Host.BindTo(Host); } } } diff --git a/osu.Game/Screens/Multi/Match/Components/MatchChatDisplay.cs b/osu.Game/Screens/Multi/Match/Components/MatchChatDisplay.cs index e0438e6729..0b61637cd5 100644 --- a/osu.Game/Screens/Multi/Match/Components/MatchChatDisplay.cs +++ b/osu.Game/Screens/Multi/Match/Components/MatchChatDisplay.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Game.Online.Chat; using osu.Game.Online.Multiplayer; @@ -9,28 +10,31 @@ namespace osu.Game.Screens.Multi.Match.Components { public class MatchChatDisplay : StandAloneChatDisplay { - private readonly Room room; + [Resolved(typeof(Room), nameof(Room.RoomID))] + private Bindable roomId { get; set; } + + [Resolved(typeof(Room), nameof(Room.ChannelId))] + private Bindable channelId { get; set; } [Resolved(CanBeNull = true)] private ChannelManager channelManager { get; set; } - public MatchChatDisplay(Room room) + public MatchChatDisplay() : base(true) { - this.room = room; } protected override void LoadComplete() { base.LoadComplete(); - room.RoomID.BindValueChanged(v => updateChannel(), true); + roomId.BindValueChanged(v => updateChannel(), true); } private void updateChannel() { - if (room.RoomID.Value != null) - Channel.Value = channelManager?.JoinChannel(new Channel { Id = room.ChannelId, Type = ChannelType.Multiplayer, Name = $"#mp_{room.RoomID}" }); + if (roomId.Value != null) + Channel.Value = channelManager?.JoinChannel(new Channel { Id = channelId, Type = ChannelType.Multiplayer, Name = $"#mp_{roomId.Value}" }); } } } diff --git a/osu.Game/Screens/Multi/Match/Components/MatchLeaderboard.cs b/osu.Game/Screens/Multi/Match/Components/MatchLeaderboard.cs index 33953bd5a6..60604eeb5c 100644 --- a/osu.Game/Screens/Multi/Match/Components/MatchLeaderboard.cs +++ b/osu.Game/Screens/Multi/Match/Components/MatchLeaderboard.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; @@ -16,18 +17,13 @@ namespace osu.Game.Screens.Multi.Match.Components { public Action> ScoresLoaded; - public Room Room - { - get => bindings.Room; - set => bindings.Room = value; - } - - private readonly RoomBindings bindings = new RoomBindings(); + [Resolved(typeof(Room), nameof(Room.RoomID))] + private Bindable roomId { get; set; } [BackgroundDependencyLoader] private void load() { - bindings.RoomID.BindValueChanged(id => + roomId.BindValueChanged(id => { if (id == null) return; @@ -39,10 +35,10 @@ namespace osu.Game.Screens.Multi.Match.Components protected override APIRequest FetchScores(Action> scoresCallback) { - if (bindings.RoomID.Value == null) + if (roomId.Value == null) return null; - var req = new GetRoomScoresRequest(bindings.RoomID.Value ?? 0); + var req = new GetRoomScoresRequest(roomId.Value ?? 0); req.Success += r => { diff --git a/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs b/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs index 5b9402683e..dcbeb07904 100644 --- a/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs +++ b/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs @@ -22,43 +22,53 @@ namespace osu.Game.Screens.Multi.Match.Components { private const float transition_duration = 350; private const float field_padding = 45; - private const float disabled_alpha = 0.2f; - private readonly RoomBindings bindings = new RoomBindings(); + protected MatchSettings Settings { get; private set; } - private readonly Container content; - - private readonly OsuSpriteText typeLabel; - - protected readonly OsuTextBox NameField, MaxParticipantsField; - protected readonly OsuDropdown DurationField; - protected readonly RoomAvailabilityPicker AvailabilityPicker; - protected readonly GameTypePicker TypePicker; - protected readonly TriangleButton ApplyButton; - protected readonly OsuPasswordTextBox PasswordField; - - protected readonly OsuSpriteText ErrorText; - - private readonly ProcessingOverlay processingOverlay; - - private readonly Room room; - - [Resolved(CanBeNull = true)] - private IRoomManager manager { get; set; } - - public MatchSettingsOverlay(Room room) + [BackgroundDependencyLoader] + private void load() { - this.room = room; - - bindings.Room = room; - Masking = true; - Child = content = new Container + Child = Settings = new MatchSettings { RelativeSizeAxes = Axes.Both, - RelativePositionAxes = Axes.Y, - Children = new Drawable[] + RelativePositionAxes = Axes.Y + }; + } + + protected override void PopIn() + { + Settings.MoveToY(0, transition_duration, Easing.OutQuint); + } + + protected override void PopOut() + { + Settings.MoveToY(-1, transition_duration, Easing.InSine); + } + + protected class MatchSettings : MultiplayerComposite + { + private const float disabled_alpha = 0.2f; + + public OsuTextBox NameField, MaxParticipantsField; + public OsuDropdown DurationField; + public RoomAvailabilityPicker AvailabilityPicker; + public GameTypePicker TypePicker; + public TriangleButton ApplyButton; + + public OsuSpriteText ErrorText; + + private OsuSpriteText typeLabel; + private ProcessingOverlay processingOverlay; + + [Resolved(CanBeNull = true)] + private IRoomManager manager { get; set; } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + InternalChildren = new Drawable[] { new Box { @@ -111,7 +121,10 @@ namespace osu.Game.Screens.Multi.Match.Components new Section("Room visibility") { Alpha = disabled_alpha, - Child = AvailabilityPicker = new RoomAvailabilityPicker(), + Child = AvailabilityPicker = new RoomAvailabilityPicker + { + Enabled = { Value = false } + }, }, new Section("Game type") { @@ -127,10 +140,12 @@ namespace osu.Game.Screens.Multi.Match.Components TypePicker = new GameTypePicker { RelativeSizeAxes = Axes.X, + Enabled = { Value = false } }, typeLabel = new OsuSpriteText { TextSize = 14, + Colour = colours.Yellow }, }, }, @@ -151,7 +166,8 @@ namespace osu.Game.Screens.Multi.Match.Components { RelativeSizeAxes = Axes.X, TabbableContentContainer = this, - OnCommit = (sender, text) => apply(), + ReadOnly = true, + OnCommit = (sender, text) => apply() }, }, new Section("Duration") @@ -177,10 +193,11 @@ namespace osu.Game.Screens.Multi.Match.Components new Section("Password (optional)") { Alpha = disabled_alpha, - Child = PasswordField = new SettingsPasswordTextBox + Child = new SettingsPasswordTextBox { RelativeSizeAxes = Axes.X, TabbableContentContainer = this, + ReadOnly = true, OnCommit = (sender, text) => apply() }, }, @@ -222,6 +239,7 @@ namespace osu.Game.Screens.Multi.Match.Components Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, Size = new Vector2(230, 55), + Enabled = { Value = false }, Action = apply, }, ErrorText = new OsuSpriteText @@ -229,7 +247,8 @@ namespace osu.Game.Screens.Multi.Match.Components Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, Alpha = 0, - Depth = 1 + Depth = 1, + Colour = colours.RedDark } } } @@ -239,80 +258,58 @@ namespace osu.Game.Screens.Multi.Match.Components } }, processingOverlay = new ProcessingOverlay { Alpha = 0 } - }, - }; + }; - TypePicker.Current.ValueChanged += t => typeLabel.Text = t.Name; + TypePicker.Current.ValueChanged += t => typeLabel.Text = t.Name; - bindings.Name.BindValueChanged(n => NameField.Text = n, true); - bindings.Availability.BindValueChanged(a => AvailabilityPicker.Current.Value = a, true); - bindings.Type.BindValueChanged(t => TypePicker.Current.Value = t, true); - bindings.MaxParticipants.BindValueChanged(m => MaxParticipantsField.Text = m?.ToString(), true); - bindings.Duration.BindValueChanged(d => DurationField.Current.Value = d, true); - } + Name.BindValueChanged(n => NameField.Text = n, true); + Availability.BindValueChanged(a => AvailabilityPicker.Current.Value = a, true); + Type.BindValueChanged(t => TypePicker.Current.Value = t, true); + MaxParticipants.BindValueChanged(m => MaxParticipantsField.Text = m?.ToString(), true); + Duration.BindValueChanged(d => DurationField.Current.Value = d, true); + } - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - typeLabel.Colour = colours.Yellow; - ErrorText.Colour = colours.RedDark; + protected override void Update() + { + base.Update(); - MaxParticipantsField.ReadOnly = true; - PasswordField.ReadOnly = true; - AvailabilityPicker.Enabled.Value = false; - TypePicker.Enabled.Value = false; - ApplyButton.Enabled.Value = false; - } + ApplyButton.Enabled.Value = hasValidSettings; + } - protected override void Update() - { - base.Update(); + private bool hasValidSettings => RoomID.Value == null && NameField.Text.Length > 0 && Playlist.Count > 0; - ApplyButton.Enabled.Value = hasValidSettings; - } - private bool hasValidSettings => bindings.Room.RoomID.Value == null && NameField.Text.Length > 0 && bindings.Playlist.Count > 0; + private void apply() + { + hideError(); - protected override void PopIn() - { - content.MoveToY(0, transition_duration, Easing.OutQuint); - } + Name.Value = NameField.Text; + Availability.Value = AvailabilityPicker.Current.Value; + Type.Value = TypePicker.Current.Value; - protected override void PopOut() - { - content.MoveToY(-1, transition_duration, Easing.InSine); - } + if (int.TryParse(MaxParticipantsField.Text, out int max)) + MaxParticipants.Value = max; + else + MaxParticipants.Value = null; - private void apply() - { - hideError(); + Duration.Value = DurationField.Current.Value; - bindings.Name.Value = NameField.Text; - bindings.Availability.Value = AvailabilityPicker.Current.Value; - bindings.Type.Value = TypePicker.Current.Value; + manager?.CreateRoom(Room, onSuccess, onError); - if (int.TryParse(MaxParticipantsField.Text, out int max)) - bindings.MaxParticipants.Value = max; - else - bindings.MaxParticipants.Value = null; + processingOverlay.Show(); + } - bindings.Duration.Value = DurationField.Current.Value; + private void hideError() => ErrorText.FadeOut(50); - manager?.CreateRoom(room, onSuccess, onError); + private void onSuccess(Room room) => processingOverlay.Hide(); - processingOverlay.Show(); - } + private void onError(string text) + { + ErrorText.Text = text; + ErrorText.FadeIn(50); - private void hideError() => ErrorText.FadeOut(50); - - private void onSuccess(Room room) => processingOverlay.Hide(); - - private void onError(string text) - { - ErrorText.Text = text; - ErrorText.FadeIn(50); - - processingOverlay.Hide(); + processingOverlay.Hide(); + } } private class SettingsTextBox : OsuTextBox diff --git a/osu.Game/Screens/Multi/Match/Components/MatchTabControl.cs b/osu.Game/Screens/Multi/Match/Components/MatchTabControl.cs index 84998417e9..7ac1016e72 100644 --- a/osu.Game/Screens/Multi/Match/Components/MatchTabControl.cs +++ b/osu.Game/Screens/Multi/Match/Components/MatchTabControl.cs @@ -14,12 +14,11 @@ namespace osu.Game.Screens.Multi.Match.Components { public class MatchTabControl : PageTabControl { - private readonly IBindable roomIdBind = new Bindable(); + [Resolved(typeof(Room), nameof(Room.RoomID))] + private Bindable roomId { get; set; } - public MatchTabControl(Room room) + public MatchTabControl() { - roomIdBind.BindTo(room.RoomID); - AddItem(new RoomMatchPage()); AddItem(new SettingsMatchPage()); } @@ -27,7 +26,7 @@ namespace osu.Game.Screens.Multi.Match.Components [BackgroundDependencyLoader] private void load() { - roomIdBind.BindValueChanged(v => + roomId.BindValueChanged(v => { if (v.HasValue) { diff --git a/osu.Game/Screens/Multi/Match/Components/Participants.cs b/osu.Game/Screens/Multi/Match/Components/Participants.cs index 5b9498ce7c..82a79475ff 100644 --- a/osu.Game/Screens/Multi/Match/Components/Participants.cs +++ b/osu.Game/Screens/Multi/Match/Components/Participants.cs @@ -1,9 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Collections.Generic; using System.Linq; -using osu.Framework.Configuration; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Overlays.SearchableList; @@ -13,16 +12,12 @@ using osuTK; namespace osu.Game.Screens.Multi.Match.Components { - public class Participants : CompositeDrawable + public class Participants : MultiplayerComposite { - public readonly IBindable> Users = new Bindable>(); - public readonly IBindable ParticipantCount = new Bindable(); - public readonly IBindable MaxParticipants = new Bindable(); - - public Participants() + [BackgroundDependencyLoader] + private void load() { FillFlowContainer usersFlow; - ParticipantCountDisplay count; InternalChild = new Container { @@ -36,7 +31,7 @@ namespace osu.Game.Screens.Multi.Match.Components Padding = new MarginPadding { Top = 10 }, Children = new Drawable[] { - count = new ParticipantCountDisplay + new ParticipantCountDisplay { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, @@ -55,11 +50,7 @@ namespace osu.Game.Screens.Multi.Match.Components }, }; - count.Participants.BindTo(Users); - count.ParticipantCount.BindTo(ParticipantCount); - count.MaxParticipants.BindTo(MaxParticipants); - - Users.BindValueChanged(v => + Participants.BindValueChanged(v => { usersFlow.Children = v.Select(u => new UserPanel(u) { diff --git a/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs b/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs index 1bde6270f6..276e8f3530 100644 --- a/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs +++ b/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs @@ -16,7 +16,8 @@ namespace osu.Game.Screens.Multi.Match.Components { public readonly IBindable Beatmap = new Bindable(); - private readonly Room room; + [Resolved(typeof(Room), nameof(Room.EndDate))] + private Bindable endDate { get; set; } [Resolved] private IBindableBeatmap gameBeatmap { get; set; } @@ -26,9 +27,8 @@ namespace osu.Game.Screens.Multi.Match.Components private bool hasBeatmap; - public ReadyButton(Room room) + public ReadyButton() { - this.room = room; RelativeSizeAxes = Axes.Y; Size = new Vector2(200, 1); @@ -74,7 +74,7 @@ namespace osu.Game.Screens.Multi.Match.Components return; } - bool hasEnoughTime = DateTimeOffset.UtcNow.AddSeconds(30).AddMilliseconds(gameBeatmap.Value.Track.Length) < room.EndDate; + bool hasEnoughTime = DateTimeOffset.UtcNow.AddSeconds(30).AddMilliseconds(gameBeatmap.Value.Track.Length) < endDate; Enabled.Value = hasBeatmap && hasEnoughTime; } diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index 980f321c92..12930d814b 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -4,6 +4,7 @@ using System; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Screens; @@ -22,130 +23,38 @@ namespace osu.Game.Screens.Multi.Match { public override bool AllowBeatmapRulesetChange => false; - public override string Title => room.RoomID.Value == null ? "New room" : room.Name.Value; + public override string Title => roomId?.Value == null ? "New room" : name.Value; + public override string ShortTitle => "room"; - private readonly RoomBindings bindings = new RoomBindings(); + [Resolved(typeof(Room), nameof(Room.RoomID))] + private Bindable roomId { get; set; } - private readonly MatchLeaderboard leaderboard; + [Resolved(typeof(Room), nameof(Room.Name))] + private Bindable name { get; set; } - private readonly Action pushGameplayScreen; + [Resolved(typeof(Room), nameof(Room.Playlist))] + private BindableList playlist { get; set; } - [Cached] - private readonly Room room; - - [Resolved] - private BeatmapManager beatmapManager { get; set; } - - public MatchSubScreen(Room room, Action pushGameplayScreen) + public MatchSubScreen(Action pushGameplayScreen) { - this.room = room; - this.pushGameplayScreen = pushGameplayScreen; - - bindings.Room = room; - - MatchChatDisplay chat; - Components.Header header; - Info info; - GridContainer bottomRow; - MatchSettingsOverlay settings; - - InternalChildren = new Drawable[] + InternalChild = new Match(pushGameplayScreen) { - new GridContainer + RelativeSizeAxes = Axes.Both, + RequestBeatmapSelection = () => this.Push(new MatchSongSelect { - RelativeSizeAxes = Axes.Both, - Content = new[] + Selected = item => { - new Drawable[] { header = new Components.Header(room) { Depth = -1 } }, - new Drawable[] { info = new Info(room) { OnStart = onStart } }, - new Drawable[] - { - bottomRow = new GridContainer - { - RelativeSizeAxes = Axes.Both, - Content = new[] - { - new Drawable[] - { - leaderboard = new MatchLeaderboard - { - Padding = new MarginPadding - { - Left = 10 + OsuScreen.HORIZONTAL_OVERFLOW_PADDING, - Right = 10, - Vertical = 10, - }, - RelativeSizeAxes = Axes.Both, - Room = room - }, - new Container - { - Padding = new MarginPadding - { - Left = 10, - Right = 10 + OsuScreen.HORIZONTAL_OVERFLOW_PADDING, - Vertical = 10, - }, - RelativeSizeAxes = Axes.Both, - Child = chat = new MatchChatDisplay(room) - { - RelativeSizeAxes = Axes.Both - } - }, - }, - }, - } - }, + playlist.Clear(); + playlist.Add(item); }, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize), - new Dimension(GridSizeMode.AutoSize), - new Dimension(GridSizeMode.Distributed), - } - }, - new Container + }), + RequestExit = () => { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Top = Components.Header.HEIGHT }, - Child = settings = new MatchSettingsOverlay(room) { RelativeSizeAxes = Axes.Both }, - }, - }; - - header.OnRequestSelectBeatmap = () => this.Push(new MatchSongSelect - { - Selected = addPlaylistItem, - }); - - header.Tabs.Current.ValueChanged += t => - { - const float fade_duration = 500; - if (t is SettingsMatchPage) - { - settings.Show(); - info.FadeOut(fade_duration, Easing.OutQuint); - bottomRow.FadeOut(fade_duration, Easing.OutQuint); - } - else - { - settings.Hide(); - info.FadeIn(fade_duration, Easing.OutQuint); - bottomRow.FadeIn(fade_duration, Easing.OutQuint); + if (this.IsCurrentScreen()) + this.Exit(); } }; - - chat.Exit += () => - { - if (this.IsCurrentScreen()) - this.Exit(); - }; - } - - [BackgroundDependencyLoader] - private void load() - { - beatmapManager.ItemAdded += beatmapAdded; } public override bool OnExiting(IScreen next) @@ -154,73 +63,191 @@ namespace osu.Game.Screens.Multi.Match return base.OnExiting(next); } - protected override void LoadComplete() + private class Match : MultiplayerComposite { - base.LoadComplete(); + public Action RequestBeatmapSelection; + public Action RequestExit; - bindings.CurrentBeatmap.BindValueChanged(setBeatmap, true); - bindings.CurrentRuleset.BindValueChanged(setRuleset, true); - } + private readonly Action pushGameplayScreen; - private void setBeatmap(BeatmapInfo beatmap) - { - // Retrieve the corresponding local beatmap, since we can't directly use the playlist's beatmap info - var localBeatmap = beatmap == null ? null : beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == beatmap.OnlineBeatmapID); + private MatchLeaderboard leaderboard; - Game?.ForcefullySetBeatmap(beatmapManager.GetWorkingBeatmap(localBeatmap)); - } + [Resolved] + private IBindableBeatmap gameBeatmap { get; set; } - private void setRuleset(RulesetInfo ruleset) - { - if (ruleset == null) - return; + [Resolved] + private BeatmapManager beatmapManager { get; set; } - Game?.ForcefullySetRuleset(ruleset); - } + [Resolved(CanBeNull = true)] + private OsuGame game { get; set; } - private void beatmapAdded(BeatmapSetInfo model, bool existing, bool silent) => Schedule(() => - { - if (Beatmap.Value != beatmapManager.DefaultBeatmap) - return; - - if (bindings.CurrentBeatmap.Value == null) - return; - - // Try to retrieve the corresponding local beatmap - var localBeatmap = beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == bindings.CurrentBeatmap.Value.OnlineBeatmapID); - - if (localBeatmap != null) - Game?.ForcefullySetBeatmap(beatmapManager.GetWorkingBeatmap(localBeatmap)); - }); - - private void addPlaylistItem(PlaylistItem item) - { - bindings.Playlist.Clear(); - bindings.Playlist.Add(item); - } - - private void onStart() - { - Beatmap.Value.Mods.Value = bindings.CurrentMods.Value.ToArray(); - - switch (bindings.Type.Value) + public Match(Action pushGameplayScreen) { - default: - case GameTypeTimeshift _: - pushGameplayScreen?.Invoke(new PlayerLoader(() => new TimeshiftPlayer(room, room.Playlist.First().ID) - { - Exited = () => leaderboard.RefreshScores() - })); - break; + this.pushGameplayScreen = pushGameplayScreen; } - } - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); + [BackgroundDependencyLoader] + private void load() + { + MatchChatDisplay chat; + Components.Header header; + Info info; + GridContainer bottomRow; + MatchSettingsOverlay settings; - if (beatmapManager != null) - beatmapManager.ItemAdded -= beatmapAdded; + InternalChildren = new Drawable[] + { + new GridContainer + { + RelativeSizeAxes = Axes.Both, + Content = new[] + { + new Drawable[] + { + header = new Components.Header + { + Depth = -1, + RequestBeatmapSelection = () => RequestBeatmapSelection?.Invoke() + } + }, + new Drawable[] { info = new Info { OnStart = onStart } }, + new Drawable[] + { + bottomRow = new GridContainer + { + RelativeSizeAxes = Axes.Both, + Content = new[] + { + new Drawable[] + { + leaderboard = new MatchLeaderboard + { + Padding = new MarginPadding + { + Left = 10 + OsuScreen.HORIZONTAL_OVERFLOW_PADDING, + Right = 10, + Vertical = 10, + }, + RelativeSizeAxes = Axes.Both + }, + new Container + { + Padding = new MarginPadding + { + Left = 10, + Right = 10 + OsuScreen.HORIZONTAL_OVERFLOW_PADDING, + Vertical = 10, + }, + RelativeSizeAxes = Axes.Both, + Child = chat = new MatchChatDisplay + { + RelativeSizeAxes = Axes.Both + } + }, + }, + }, + } + }, + }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.Distributed), + } + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Top = Components.Header.HEIGHT }, + Child = settings = new MatchSettingsOverlay { RelativeSizeAxes = Axes.Both }, + }, + }; + + header.Tabs.Current.ValueChanged += t => + { + const float fade_duration = 500; + if (t is SettingsMatchPage) + { + settings.Show(); + info.FadeOut(fade_duration, Easing.OutQuint); + bottomRow.FadeOut(fade_duration, Easing.OutQuint); + } + else + { + settings.Hide(); + info.FadeIn(fade_duration, Easing.OutQuint); + bottomRow.FadeIn(fade_duration, Easing.OutQuint); + } + }; + + chat.Exit += () => RequestExit?.Invoke(); + + beatmapManager.ItemAdded += beatmapAdded; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + CurrentBeatmap.BindValueChanged(setBeatmap, true); + CurrentRuleset.BindValueChanged(setRuleset, true); + } + + private void setBeatmap(BeatmapInfo beatmap) + { + // Retrieve the corresponding local beatmap, since we can't directly use the playlist's beatmap info + var localBeatmap = beatmap == null ? null : beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == beatmap.OnlineBeatmapID); + + game?.ForcefullySetBeatmap(beatmapManager.GetWorkingBeatmap(localBeatmap)); + } + + private void setRuleset(RulesetInfo ruleset) + { + if (ruleset == null) + return; + + game?.ForcefullySetRuleset(ruleset); + } + + private void beatmapAdded(BeatmapSetInfo model, bool existing, bool silent) => Schedule(() => + { + if (gameBeatmap.Value != beatmapManager.DefaultBeatmap) + return; + + if (CurrentBeatmap.Value == null) + return; + + // Try to retrieve the corresponding local beatmap + var localBeatmap = beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == CurrentBeatmap.Value.OnlineBeatmapID); + + if (localBeatmap != null) + game?.ForcefullySetBeatmap(beatmapManager.GetWorkingBeatmap(localBeatmap)); + }); + + private void onStart() + { + gameBeatmap.Value.Mods.Value = CurrentMods.Value.ToArray(); + + switch (Type.Value) + { + default: + case GameTypeTimeshift _: + pushGameplayScreen?.Invoke(new PlayerLoader(() => new TimeshiftPlayer(Playlist.First().ID) + { + Exited = () => leaderboard.RefreshScores() + })); + break; + } + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (beatmapManager != null) + beatmapManager.ItemAdded -= beatmapAdded; + } } } } diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index f0297cdfa9..6851bc2a2a 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.cs @@ -123,7 +123,7 @@ namespace osu.Game.Screens.Multi Right = 10 + OsuScreen.HORIZONTAL_OVERFLOW_PADDING, }, Text = "Create room", - Action = () => loungeSubScreen.Push(new Room + Action = () => loungeSubScreen.Open(new Room { Name = { Value = $"{api.LocalUser}'s awesome room" } }), diff --git a/osu.Game/Screens/Multi/MultiplayerComposite.cs b/osu.Game/Screens/Multi/MultiplayerComposite.cs index f09750b112..1e68727438 100644 --- a/osu.Game/Screens/Multi/MultiplayerComposite.cs +++ b/osu.Game/Screens/Multi/MultiplayerComposite.cs @@ -18,52 +18,52 @@ namespace osu.Game.Screens.Multi public class MultiplayerComposite : CompositeDrawable { [Resolved] - public Room Room { get; private set; } + protected Room Room { get; private set; } [Resolved(typeof(Room))] - public Bindable RoomID { get; private set; } + protected Bindable RoomID { get; private set; } [Resolved(typeof(Room))] - public Bindable Name { get; private set; } + protected Bindable Name { get; private set; } [Resolved(typeof(Room))] - public Bindable Host { get; private set; } + protected Bindable Host { get; private set; } [Resolved(typeof(Room))] - public Bindable Status { get; private set; } + protected Bindable Status { get; private set; } [Resolved(typeof(Room))] - public Bindable Type { get; private set; } + protected Bindable Type { get; private set; } [Resolved(typeof(Room))] - public BindableList Playlist { get; private set; } + protected BindableList Playlist { get; private set; } [Resolved(typeof(Room))] - public Bindable> Participants { get; private set; } + protected Bindable> Participants { get; private set; } [Resolved(typeof(Room))] - public Bindable ParticipantCount { get; private set; } + protected Bindable ParticipantCount { get; private set; } [Resolved(typeof(Room))] - public Bindable MaxParticipants { get; private set; } + protected Bindable MaxParticipants { get; private set; } [Resolved(typeof(Room))] - public Bindable EndDate { get; private set; } + protected Bindable EndDate { get; private set; } [Resolved(typeof(Room))] - public Bindable Availability { get; private set; } + protected Bindable Availability { get; private set; } [Resolved(typeof(Room))] - public Bindable Duration { get; private set; } + protected Bindable Duration { get; private set; } private readonly Bindable currentBeatmap = new Bindable(); - public IBindable CurrentBeatmap => currentBeatmap; + protected IBindable CurrentBeatmap => currentBeatmap; private readonly Bindable> currentMods = new Bindable>(); - public IBindable> CurrentMods => currentMods; + protected IBindable> CurrentMods => currentMods; private readonly Bindable currentRuleset = new Bindable(); - public IBindable CurrentRuleset => currentRuleset; + protected IBindable CurrentRuleset => currentRuleset; protected override void LoadComplete() { diff --git a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs index 36cf0f0282..6cddd235e1 100644 --- a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs +++ b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs @@ -5,6 +5,7 @@ using System; using System.Diagnostics; using System.Threading; using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Framework.Logging; using osu.Framework.Screens; using osu.Game.Online.API; @@ -21,15 +22,16 @@ namespace osu.Game.Screens.Multi.Play { public Action Exited; - private readonly Room room; + [Resolved(typeof(Room), nameof(Room.RoomID))] + private Bindable roomId { get; set; } + private readonly int playlistItemId; [Resolved] private APIAccess api { get; set; } - public TimeshiftPlayer(Room room, int playlistItemId) + public TimeshiftPlayer(int playlistItemId) { - this.room = room; this.playlistItemId = playlistItemId; } @@ -42,7 +44,7 @@ namespace osu.Game.Screens.Multi.Play bool failed = false; - var req = new CreateRoomScoreRequest(room.RoomID.Value ?? 0, playlistItemId); + var req = new CreateRoomScoreRequest(roomId.Value ?? 0, playlistItemId); req.Success += r => token = r.ID; req.Failure += e => { @@ -87,7 +89,7 @@ namespace osu.Game.Screens.Multi.Play Debug.Assert(token != null); - var request = new SubmitRoomScoreRequest(token.Value, room.RoomID.Value ?? 0, playlistItemId, score); + var request = new SubmitRoomScoreRequest(token.Value, roomId.Value ?? 0, playlistItemId, score); request.Failure += e => Logger.Error(e, "Failed to submit score"); api.Queue(request); } @@ -99,6 +101,6 @@ namespace osu.Game.Screens.Multi.Play Exited = null; } - protected override Results CreateResults(ScoreInfo score) => new MatchResults(score, room); + protected override Results CreateResults(ScoreInfo score) => new MatchResults(score); } } diff --git a/osu.Game/Screens/Multi/Ranking/MatchResults.cs b/osu.Game/Screens/Multi/Ranking/MatchResults.cs index d14d94928d..db52a79ccb 100644 --- a/osu.Game/Screens/Multi/Ranking/MatchResults.cs +++ b/osu.Game/Screens/Multi/Ranking/MatchResults.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using osu.Game.Online.Multiplayer; using osu.Game.Scoring; using osu.Game.Screens.Multi.Ranking.Types; using osu.Game.Screens.Ranking; @@ -12,19 +11,16 @@ namespace osu.Game.Screens.Multi.Ranking { public class MatchResults : Results { - private readonly Room room; - - public MatchResults(ScoreInfo score, Room room) + public MatchResults(ScoreInfo score) : base(score) { - this.room = room; } protected override IEnumerable CreateResultPages() => new IResultPageInfo[] { new ScoreOverviewPageInfo(Score, Beatmap), new LocalLeaderboardPageInfo(Score, Beatmap), - new RoomLeaderboardPageInfo(Score, Beatmap, room), + new RoomLeaderboardPageInfo(Score, Beatmap), }; } } diff --git a/osu.Game/Screens/Multi/Ranking/Pages/RoomLeaderboardPage.cs b/osu.Game/Screens/Multi/Ranking/Pages/RoomLeaderboardPage.cs index a1d0a1ea44..1b4c99d972 100644 --- a/osu.Game/Screens/Multi/Ranking/Pages/RoomLeaderboardPage.cs +++ b/osu.Game/Screens/Multi/Ranking/Pages/RoomLeaderboardPage.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using Microsoft.EntityFrameworkCore.Internal; using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -23,16 +24,15 @@ namespace osu.Game.Screens.Multi.Ranking.Pages { public class RoomLeaderboardPage : ResultsPage { - private readonly Room room; - private OsuColour colours; - private TextFlowContainer rankText; - public RoomLeaderboardPage(ScoreInfo score, WorkingBeatmap beatmap, Room room) + [Resolved(typeof(Room), nameof(Room.Name))] + private Bindable name { get; set; } + + public RoomLeaderboardPage(ScoreInfo score, WorkingBeatmap beatmap) : base(score, beatmap) { - this.room = room; } [BackgroundDependencyLoader] @@ -53,7 +53,7 @@ namespace osu.Game.Screens.Multi.Ranking.Pages { RelativeSizeAxes = Axes.Both, BackgroundColour = colours.Gray6, - Child = leaderboard = CreateLeaderboard(room) + Child = leaderboard = CreateLeaderboard() }, rankText = new TextFlowContainer { @@ -84,7 +84,7 @@ namespace osu.Game.Screens.Multi.Ranking.Pages s.Colour = colours.GrayF; }; - rankText.AddText(room.Name + "\n", white); + rankText.AddText(name + "\n", white); rankText.AddText("You are placed ", gray); int index = scores.IndexOf(new APIRoomScoreInfo { User = Score.User }, new FuncEqualityComparer((s1, s2) => s1.User.Id.Equals(s2.User.Id))); @@ -98,15 +98,10 @@ namespace osu.Game.Screens.Multi.Ranking.Pages rankText.AddText("in the room!", gray); } - protected virtual MatchLeaderboard CreateLeaderboard(Room room) => new ResultsMatchLeaderboard(room); + protected virtual MatchLeaderboard CreateLeaderboard() => new ResultsMatchLeaderboard(); public class ResultsMatchLeaderboard : MatchLeaderboard { - public ResultsMatchLeaderboard(Room room) - { - Room = room; - } - protected override bool FadeTop => true; protected override LeaderboardScore CreateDrawableScore(APIRoomScoreInfo model, int index) diff --git a/osu.Game/Screens/Multi/Ranking/Types/RoomLeaderboardPageInfo.cs b/osu.Game/Screens/Multi/Ranking/Types/RoomLeaderboardPageInfo.cs index 09ebef4ee2..6cc13f88a5 100644 --- a/osu.Game/Screens/Multi/Ranking/Types/RoomLeaderboardPageInfo.cs +++ b/osu.Game/Screens/Multi/Ranking/Types/RoomLeaderboardPageInfo.cs @@ -3,7 +3,6 @@ using osu.Game.Beatmaps; using osu.Game.Graphics; -using osu.Game.Online.Multiplayer; using osu.Game.Scoring; using osu.Game.Screens.Multi.Ranking.Pages; using osu.Game.Screens.Ranking; @@ -14,19 +13,17 @@ namespace osu.Game.Screens.Multi.Ranking.Types { private readonly ScoreInfo score; private readonly WorkingBeatmap beatmap; - private readonly Room room; - public RoomLeaderboardPageInfo(ScoreInfo score, WorkingBeatmap beatmap, Room room) + public RoomLeaderboardPageInfo(ScoreInfo score, WorkingBeatmap beatmap) { this.score = score; this.beatmap = beatmap; - this.room = room; } public FontAwesome Icon => FontAwesome.fa_users; public string Name => "Room Leaderboard"; - public virtual ResultsPage CreatePage() => new RoomLeaderboardPage(score, beatmap, room); + public virtual ResultsPage CreatePage() => new RoomLeaderboardPage(score, beatmap); } } diff --git a/osu.Game/Screens/Multi/RoomBindings.cs b/osu.Game/Screens/Multi/RoomBindings.cs deleted file mode 100644 index e3114c768b..0000000000 --- a/osu.Game/Screens/Multi/RoomBindings.cs +++ /dev/null @@ -1,106 +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 System; -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Configuration; -using osu.Game.Beatmaps; -using osu.Game.Online.Multiplayer; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Mods; -using osu.Game.Users; - -namespace osu.Game.Screens.Multi -{ - /// - /// Helper class which binds to values from a . - /// - public class RoomBindings - { - public RoomBindings() - { - Playlist.ItemsAdded += _ => updatePlaylist(); - Playlist.ItemsRemoved += _ => updatePlaylist(); - } - - private Room room; - - /// - /// The to bind to. - /// - public Room Room - { - get => room; - set - { - if (room == value) - return; - - if (room != null) - { - RoomID.UnbindFrom(room.RoomID); - Name.UnbindFrom(room.Name); - Host.UnbindFrom(room.Host); - Status.UnbindFrom(room.Status); - Type.UnbindFrom(room.Type); - Playlist.UnbindFrom(room.Playlist); - Participants.UnbindFrom(room.Participants); - ParticipantCount.UnbindFrom(room.ParticipantCount); - MaxParticipants.UnbindFrom(room.MaxParticipants); - EndDate.UnbindFrom(room.EndDate); - Availability.UnbindFrom(room.Availability); - Duration.UnbindFrom(room.Duration); - } - - room = value ?? new Room(); - - RoomID.BindTo(room.RoomID); - Name.BindTo(room.Name); - Host.BindTo(room.Host); - Status.BindTo(room.Status); - Type.BindTo(room.Type); - Playlist.BindTo(room.Playlist); - Participants.BindTo(room.Participants); - ParticipantCount.BindTo(room.ParticipantCount); - MaxParticipants.BindTo(room.MaxParticipants); - EndDate.BindTo(room.EndDate); - Availability.BindTo(room.Availability); - Duration.BindTo(room.Duration); - } - } - - private void updatePlaylist() - { - // Todo: We only ever have one playlist item for now. In the future, this will be user-settable - - var playlistItem = Playlist.FirstOrDefault(); - - currentBeatmap.Value = playlistItem?.Beatmap; - currentMods.Value = playlistItem?.RequiredMods ?? Enumerable.Empty(); - currentRuleset.Value = playlistItem?.Ruleset; - } - - public readonly Bindable RoomID = new Bindable(); - public readonly Bindable Name = new Bindable(); - public readonly Bindable Host = new Bindable(); - public readonly Bindable Status = new Bindable(); - public readonly Bindable Type = new Bindable(); - public readonly BindableList Playlist = new BindableList(); - public readonly Bindable> Participants = new Bindable>(); - public readonly Bindable ParticipantCount = new Bindable(); - public readonly Bindable MaxParticipants = new Bindable(); - public readonly Bindable EndDate = new Bindable(); - public readonly Bindable Availability = new Bindable(); - public readonly Bindable Duration = new Bindable(); - - private readonly Bindable currentBeatmap = new Bindable(); - public IBindable CurrentBeatmap => currentBeatmap; - - private readonly Bindable> currentMods = new Bindable>(); - public IBindable> CurrentMods => currentMods; - - private readonly Bindable currentRuleset = new Bindable(); - public IBindable CurrentRuleset => currentRuleset; - } -} diff --git a/osu.Game/Screens/Multi/RoomManager.cs b/osu.Game/Screens/Multi/RoomManager.cs index d9ddd6f867..8cf50245e0 100644 --- a/osu.Game/Screens/Multi/RoomManager.cs +++ b/osu.Game/Screens/Multi/RoomManager.cs @@ -23,7 +23,7 @@ namespace osu.Game.Screens.Multi private readonly BindableList rooms = new BindableList(); public IBindableList Rooms => rooms; - public readonly Bindable CurrentRoom = new Bindable(); + public Bindable CurrentRoom { get; } = new Bindable(); [Resolved] private APIAccess api { get; set; } @@ -51,6 +51,8 @@ namespace osu.Game.Screens.Multi update(room, result); addRoom(room); + CurrentRoom.Value = room; + RoomsUpdated?.Invoke(); onSuccess?.Invoke(room); diff --git a/osu.Game/Tests/Visual/MultiplayerTestCase.cs b/osu.Game/Tests/Visual/MultiplayerTestCase.cs new file mode 100644 index 0000000000..5ed4f012c6 --- /dev/null +++ b/osu.Game/Tests/Visual/MultiplayerTestCase.cs @@ -0,0 +1,36 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Game.Online.Multiplayer; + +namespace osu.Game.Tests.Visual +{ + public class MultiplayerTestCase : OsuTestCase + { + private Room room; + + protected Room Room + { + get => room; + set + { + if (room == value) + return; + room = value; + + if (dependencies != null) + dependencies.Model.Value = value; + } + } + + private CachedModelDependencyContainer dependencies; + + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) + { + dependencies = new CachedModelDependencyContainer(base.CreateChildDependencies(parent)); + dependencies.Model.Value = room; + return dependencies; + } + } +}