diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapPanel.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapPanel.cs index 84ab6f9ccc..f014b08325 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapPanel.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapPanel.cs @@ -7,7 +7,6 @@ using osu.Game.Beatmaps; using osu.Game.Online.Multiplayer; using osu.Game.Screens.Multi.Match.Components; using osu.Framework.Graphics; -using osu.Framework.Utils; using osu.Game.Audio; using osu.Framework.Allocation; @@ -38,16 +37,5 @@ namespace osu.Game.Tests.Visual.Multiplayer Room.Playlist.Add(new PlaylistItem { Beatmap = { Value = new BeatmapInfo { OnlineBeatmapID = 2109801 } } }); Room.Playlist.Add(new PlaylistItem { Beatmap = { Value = new BeatmapInfo { OnlineBeatmapID = 1922035 } } }); } - - protected override void LoadComplete() - { - base.LoadComplete(); - - AddStep("Select random beatmap", () => - { - Room.CurrentItem.Value = Room.Playlist[RNG.Next(Room.Playlist.Count)]; - previewTrackManager.StopAnyPlaying(this); - }); - } } } diff --git a/osu.Game/Online/Multiplayer/PlaylistItem.cs b/osu.Game/Online/Multiplayer/PlaylistItem.cs index e243cfca08..69e1f0db13 100644 --- a/osu.Game/Online/Multiplayer/PlaylistItem.cs +++ b/osu.Game/Online/Multiplayer/PlaylistItem.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Linq; using Newtonsoft.Json; using osu.Framework.Bindables; @@ -12,7 +13,7 @@ using osu.Game.Rulesets.Mods; namespace osu.Game.Online.Multiplayer { - public class PlaylistItem + public class PlaylistItem : IEquatable { [JsonProperty("id")] public int ID { get; set; } @@ -90,5 +91,14 @@ namespace osu.Game.Online.Multiplayer public bool ShouldSerializeID() => false; public bool ShouldSerializeapiBeatmap() => false; + + public bool Equals(PlaylistItem other) => ID == other?.ID && BeatmapID == other.BeatmapID && RulesetID == other.RulesetID; + + public override int GetHashCode() + { + // ReSharper disable NonReadonlyMemberInGetHashCode + return HashCode.Combine(ID, BeatmapID, RulesetID); + // ReSharper restore NonReadonlyMemberInGetHashCode + } } } diff --git a/osu.Game/Online/Multiplayer/Room.cs b/osu.Game/Online/Multiplayer/Room.cs index 0f4abeafd4..400afb39a1 100644 --- a/osu.Game/Online/Multiplayer/Room.cs +++ b/osu.Game/Online/Multiplayer/Room.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using System.Linq; using Newtonsoft.Json; using osu.Framework.Allocation; @@ -31,10 +30,6 @@ namespace osu.Game.Online.Multiplayer [JsonProperty("playlist")] public BindableList Playlist { get; private set; } = new BindableList(); - [Cached] - [JsonIgnore] - public Bindable CurrentItem { get; private set; } = new Bindable(); - [Cached] [JsonProperty("channel_id")] public Bindable ChannelId { get; private set; } = new Bindable(); @@ -70,18 +65,6 @@ namespace osu.Game.Online.Multiplayer [Cached] public Bindable ParticipantCount { get; private set; } = new Bindable(); - public Room() - { - Playlist.ItemsAdded += updateCurrent; - Playlist.ItemsRemoved += updateCurrent; - updateCurrent(Playlist); - } - - private void updateCurrent(IEnumerable playlist) - { - CurrentItem.Value = playlist.FirstOrDefault(); - } - // todo: TEMPORARY [JsonProperty("participant_count")] private int? participantCount @@ -135,11 +118,9 @@ namespace osu.Game.Online.Multiplayer if (DateTimeOffset.Now >= EndDate.Value) Status.Value = new RoomStatusEnded(); - // Todo: Temporary, should only remove/add new items (requires framework changes) - if (Playlist.Count == 0) - Playlist.AddRange(other.Playlist); - else if (other.Playlist.Count > 0) - Playlist.First().ID = other.Playlist.First().ID; + foreach (var removedItem in Playlist.Except(other.Playlist).ToArray()) + Playlist.Remove(removedItem); + Playlist.AddRange(other.Playlist.Except(Playlist).ToArray()); foreach (var removedItem in Participants.Except(other.Participants).ToArray()) Participants.Remove(removedItem); diff --git a/osu.Game/Screens/Multi/Components/BeatmapTitle.cs b/osu.Game/Screens/Multi/Components/BeatmapTitle.cs index b991a3d68d..baf11dfe0d 100644 --- a/osu.Game/Screens/Multi/Components/BeatmapTitle.cs +++ b/osu.Game/Screens/Multi/Components/BeatmapTitle.cs @@ -1,6 +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.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Localisation; @@ -25,7 +26,10 @@ namespace osu.Game.Screens.Multi.Components [BackgroundDependencyLoader] private void load() { - CurrentItem.BindValueChanged(_ => updateText(), true); + Playlist.ItemsAdded += _ => updateText(); + Playlist.ItemsRemoved += _ => updateText(); + + updateText(); } private float textSize = OsuFont.DEFAULT_FONT_SIZE; @@ -54,7 +58,7 @@ namespace osu.Game.Screens.Multi.Components textFlow.Clear(); - var beatmap = CurrentItem.Value?.Beatmap; + var beatmap = Playlist.FirstOrDefault()?.Beatmap; if (beatmap == null) { diff --git a/osu.Game/Screens/Multi/Components/BeatmapTypeInfo.cs b/osu.Game/Screens/Multi/Components/BeatmapTypeInfo.cs index 21b228bb5c..a1334101b8 100644 --- a/osu.Game/Screens/Multi/Components/BeatmapTypeInfo.cs +++ b/osu.Game/Screens/Multi/Components/BeatmapTypeInfo.cs @@ -1,6 +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.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -12,6 +13,8 @@ namespace osu.Game.Screens.Multi.Components { public class BeatmapTypeInfo : MultiplayerComposite { + private LinkFlowContainer beatmapAuthor; + public BeatmapTypeInfo() { AutoSizeAxes = Axes.Both; @@ -20,8 +23,6 @@ namespace osu.Game.Screens.Multi.Components [BackgroundDependencyLoader] private void load() { - LinkFlowContainer beatmapAuthor; - InternalChild = new FillFlowContainer { AutoSizeAxes = Axes.Both, @@ -50,18 +51,23 @@ namespace osu.Game.Screens.Multi.Components } }; - CurrentItem.BindValueChanged(item => + Playlist.ItemsAdded += _ => updateInfo(); + Playlist.ItemsRemoved += _ => updateInfo(); + + updateInfo(); + } + + private void updateInfo() + { + beatmapAuthor.Clear(); + + var beatmap = Playlist.FirstOrDefault()?.Beatmap; + + if (beatmap != null) { - beatmapAuthor.Clear(); - - var beatmap = item.NewValue?.Beatmap; - - if (beatmap != null) - { - beatmapAuthor.AddText("mapped by ", s => s.Colour = OsuColour.Gray(0.8f)); - beatmapAuthor.AddUserLink(beatmap.Value.Metadata.Author); - } - }, true); + beatmapAuthor.AddText("mapped by ", s => s.Colour = OsuColour.Gray(0.8f)); + beatmapAuthor.AddUserLink(beatmap.Value.Metadata.Author); + } } } } diff --git a/osu.Game/Screens/Multi/Components/ModeTypeInfo.cs b/osu.Game/Screens/Multi/Components/ModeTypeInfo.cs index 5465463888..258541bbd6 100644 --- a/osu.Game/Screens/Multi/Components/ModeTypeInfo.cs +++ b/osu.Game/Screens/Multi/Components/ModeTypeInfo.cs @@ -1,11 +1,11 @@ // 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 osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps.Drawables; -using osu.Game.Online.Multiplayer; using osuTK; namespace osu.Game.Screens.Multi.Components @@ -46,13 +46,18 @@ namespace osu.Game.Screens.Multi.Components }, }; - CurrentItem.BindValueChanged(item => updateBeatmap(item.NewValue), true); - Type.BindValueChanged(type => gameTypeContainer.Child = new DrawableGameType(type.NewValue) { Size = new Vector2(height) }, true); + + Playlist.ItemsAdded += _ => updateBeatmap(); + Playlist.ItemsRemoved += _ => updateBeatmap(); + + updateBeatmap(); } - private void updateBeatmap(PlaylistItem item) + private void updateBeatmap() { + var item = Playlist.FirstOrDefault(); + if (item?.Beatmap != null) { drawableRuleset.FadeIn(transition_duration); diff --git a/osu.Game/Screens/Multi/Components/MultiplayerBackgroundSprite.cs b/osu.Game/Screens/Multi/Components/MultiplayerBackgroundSprite.cs index 9a1a482699..5e2f2e530a 100644 --- a/osu.Game/Screens/Multi/Components/MultiplayerBackgroundSprite.cs +++ b/osu.Game/Screens/Multi/Components/MultiplayerBackgroundSprite.cs @@ -1,6 +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.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Beatmaps.Drawables; @@ -10,6 +11,7 @@ namespace osu.Game.Screens.Multi.Components public class MultiplayerBackgroundSprite : MultiplayerComposite { private readonly BeatmapSetCoverType beatmapSetCoverType; + private UpdateableBeatmapBackgroundSprite sprite; public MultiplayerBackgroundSprite(BeatmapSetCoverType beatmapSetCoverType = BeatmapSetCoverType.Cover) { @@ -19,11 +21,17 @@ namespace osu.Game.Screens.Multi.Components [BackgroundDependencyLoader] private void load() { - UpdateableBeatmapBackgroundSprite sprite; - InternalChild = sprite = CreateBackgroundSprite(); - CurrentItem.BindValueChanged(item => sprite.Beatmap.Value = item.NewValue?.Beatmap.Value, true); + Playlist.ItemsAdded += _ => updateBeatmap(); + Playlist.ItemsRemoved += _ => updateBeatmap(); + + updateBeatmap(); + } + + private void updateBeatmap() + { + sprite.Beatmap.Value = Playlist.FirstOrDefault()?.Beatmap.Value; } protected virtual UpdateableBeatmapBackgroundSprite CreateBackgroundSprite() => new UpdateableBeatmapBackgroundSprite(beatmapSetCoverType) { RelativeSizeAxes = Axes.Both }; diff --git a/osu.Game/Screens/Multi/Match/Components/Header.cs b/osu.Game/Screens/Multi/Match/Components/Header.cs index 991060cac0..cf1eb6b6ed 100644 --- a/osu.Game/Screens/Multi/Match/Components/Header.cs +++ b/osu.Game/Screens/Multi/Match/Components/Header.cs @@ -33,6 +33,7 @@ namespace osu.Game.Screens.Multi.Match.Components public Action RequestBeatmapSelection; private MatchBeatmapPanel beatmapPanel; + private ModDisplay modDisplay; public Header() { @@ -44,7 +45,6 @@ namespace osu.Game.Screens.Multi.Match.Components private void load(OsuColour colours) { BeatmapSelectButton beatmapButton; - ModDisplay modDisplay; InternalChildren = new Drawable[] { @@ -120,9 +120,12 @@ namespace osu.Game.Screens.Multi.Match.Components }, }; - CurrentItem.BindValueChanged(item => modDisplay.Current.Value = item.NewValue?.RequiredMods?.ToArray() ?? Array.Empty(), true); - beatmapButton.Action = () => RequestBeatmapSelection?.Invoke(); + + Playlist.ItemsAdded += _ => updateMods(); + Playlist.ItemsRemoved += _ => updateMods(); + + updateMods(); } protected override void LoadComplete() @@ -131,6 +134,13 @@ namespace osu.Game.Screens.Multi.Match.Components ShowBeatmapPanel.BindValueChanged(value => beatmapPanel.FadeTo(value.NewValue ? 1 : 0, 200, Easing.OutQuint), true); } + private void updateMods() + { + var item = Playlist.FirstOrDefault(); + + modDisplay.Current.Value = item?.RequiredMods?.ToArray() ?? Array.Empty(); + } + private class BeatmapSelectButton : HeaderButton { [Resolved(typeof(Room), nameof(Room.RoomID))] diff --git a/osu.Game/Screens/Multi/Match/Components/Info.cs b/osu.Game/Screens/Multi/Match/Components/Info.cs index f0b54baa4c..a320b08cc4 100644 --- a/osu.Game/Screens/Multi/Match/Components/Info.cs +++ b/osu.Game/Screens/Multi/Match/Components/Info.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -18,6 +19,8 @@ namespace osu.Game.Screens.Multi.Match.Components { public Action OnStart; + private ReadyButton readyButton; + public Info() { RelativeSizeAxes = Axes.X; @@ -27,7 +30,6 @@ namespace osu.Game.Screens.Multi.Match.Components [BackgroundDependencyLoader] private void load() { - ReadyButton readyButton; HostInfo hostInfo; InternalChildren = new Drawable[] @@ -89,9 +91,17 @@ namespace osu.Game.Screens.Multi.Match.Components }, }; - CurrentItem.BindValueChanged(item => readyButton.Beatmap.Value = item.NewValue?.Beatmap.Value, true); - hostInfo.Host.BindTo(Host); + + Playlist.ItemsAdded += _ => updateBeatmap(); + Playlist.ItemsRemoved += _ => updateBeatmap(); + + updateBeatmap(); + } + + private void updateBeatmap() + { + readyButton.Beatmap.Value = Playlist.FirstOrDefault()?.Beatmap.Value; } } } diff --git a/osu.Game/Screens/Multi/Match/Components/MatchBeatmapPanel.cs b/osu.Game/Screens/Multi/Match/Components/MatchBeatmapPanel.cs index f67f6d9f43..c8de066caa 100644 --- a/osu.Game/Screens/Multi/Match/Components/MatchBeatmapPanel.cs +++ b/osu.Game/Screens/Multi/Match/Components/MatchBeatmapPanel.cs @@ -1,10 +1,10 @@ // 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 System.Threading; using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Game.Beatmaps; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Overlays.Direct; @@ -32,10 +32,13 @@ namespace osu.Game.Screens.Multi.Match.Components [BackgroundDependencyLoader] private void load() { - CurrentItem.BindValueChanged(item => loadNewPanel(item.NewValue?.Beatmap.Value), true); + Playlist.ItemsAdded += _ => loadNewPanel(); + Playlist.ItemsRemoved += _ => loadNewPanel(); + + loadNewPanel(); } - private void loadNewPanel(BeatmapInfo beatmap) + private void loadNewPanel() { loadCancellation?.Cancel(); request?.Cancel(); @@ -44,6 +47,8 @@ namespace osu.Game.Screens.Multi.Match.Components panel?.Expire(); panel = null; + var beatmap = Playlist.FirstOrDefault()?.Beatmap.Value; + if (beatmap?.OnlineBeatmapID == null) return; diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index 9165ef6d0f..890664e99b 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -41,9 +41,6 @@ namespace osu.Game.Screens.Multi.Match [Resolved(typeof(Room))] protected BindableList Playlist { get; private set; } - [Resolved(typeof(Room))] - protected Bindable CurrentItem { get; private set; } - [Resolved] private BeatmapManager beatmapManager { get; set; } @@ -53,6 +50,7 @@ namespace osu.Game.Screens.Multi.Match [Resolved(CanBeNull = true)] private OsuGame game { get; set; } + private readonly Bindable selectedItem = new Bindable(); private MatchLeaderboard leaderboard; public MatchSubScreen(Room room) @@ -166,7 +164,16 @@ namespace osu.Game.Screens.Multi.Match { base.LoadComplete(); - CurrentItem.BindValueChanged(currentItemChanged, true); + Playlist.ItemsAdded += _ => updateSelectedItem(); + Playlist.ItemsRemoved += _ => updateSelectedItem(); + + updateSelectedItem(); + } + + private void updateSelectedItem() + { + selectedItem.Value = Playlist.FirstOrDefault(); + currentItemChanged(); } public override bool OnExiting(IScreen next) @@ -181,16 +188,18 @@ namespace osu.Game.Screens.Multi.Match /// /// Handles propagation of the current playlist item's content to game-wide mechanisms. /// - private void currentItemChanged(ValueChangedEvent e) + private void currentItemChanged() { + var item = selectedItem.Value; + // Retrieve the corresponding local beatmap, since we can't directly use the playlist's beatmap info - var localBeatmap = e.NewValue?.Beatmap == null ? null : beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == e.NewValue.Beatmap.Value.OnlineBeatmapID); + var localBeatmap = item?.Beatmap == null ? null : beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == item.Beatmap.Value.OnlineBeatmapID); Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap); - Mods.Value = e.NewValue?.RequiredMods?.ToArray() ?? Array.Empty(); + Mods.Value = item?.RequiredMods?.ToArray() ?? Array.Empty(); - if (e.NewValue?.Ruleset != null) - Ruleset.Value = e.NewValue.Ruleset.Value; + if (item?.Ruleset != null) + Ruleset.Value = item.Ruleset.Value; previewTrackManager.StopAnyPlaying(this); } @@ -203,11 +212,11 @@ namespace osu.Game.Screens.Multi.Match if (Beatmap.Value != beatmapManager.DefaultBeatmap) return; - if (CurrentItem.Value == null) + if (selectedItem.Value == null) return; // Try to retrieve the corresponding local beatmap - var localBeatmap = beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == CurrentItem.Value.Beatmap.Value.OnlineBeatmapID); + var localBeatmap = beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == selectedItem.Value.Beatmap.Value.OnlineBeatmapID); if (localBeatmap != null) Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap); @@ -224,7 +233,7 @@ namespace osu.Game.Screens.Multi.Match { default: case GameTypeTimeshift _: - multiplayer?.Start(() => new TimeshiftPlayer(CurrentItem.Value) + multiplayer?.Start(() => new TimeshiftPlayer(selectedItem.Value) { Exited = () => leaderboard.RefreshScores() }); diff --git a/osu.Game/Screens/Multi/MultiplayerComposite.cs b/osu.Game/Screens/Multi/MultiplayerComposite.cs index 346f78f2c6..3f048eceab 100644 --- a/osu.Game/Screens/Multi/MultiplayerComposite.cs +++ b/osu.Game/Screens/Multi/MultiplayerComposite.cs @@ -30,9 +30,6 @@ namespace osu.Game.Screens.Multi [Resolved(typeof(Room))] protected BindableList Playlist { get; private set; } - [Resolved(typeof(Room))] - protected Bindable CurrentItem { get; private set; } - [Resolved(typeof(Room))] protected BindableList Participants { get; private set; } diff --git a/osu.Game/Screens/Select/MatchSongSelect.cs b/osu.Game/Screens/Select/MatchSongSelect.cs index ff0544d227..251456bf0d 100644 --- a/osu.Game/Screens/Select/MatchSongSelect.cs +++ b/osu.Game/Screens/Select/MatchSongSelect.cs @@ -22,11 +22,11 @@ namespace osu.Game.Screens.Select public string ShortTitle => "song selection"; public override string Title => ShortTitle.Humanize(); - [Resolved(typeof(Room))] - protected Bindable CurrentItem { get; private set; } - public override bool AllowEditing => false; + [Resolved(typeof(Room), nameof(Room.Playlist))] + protected BindableList Playlist { get; private set; } + [Resolved] private BeatmapManager beatmaps { get; set; } @@ -59,11 +59,13 @@ namespace osu.Game.Screens.Select if (base.OnExiting(next)) return true; - if (CurrentItem.Value != null) + var firstItem = Playlist.FirstOrDefault(); + + if (firstItem != null) { - Ruleset.Value = CurrentItem.Value.Ruleset.Value; - Beatmap.Value = beatmaps.GetWorkingBeatmap(CurrentItem.Value.Beatmap.Value); - Mods.Value = CurrentItem.Value.RequiredMods?.ToArray() ?? Array.Empty(); + Ruleset.Value = firstItem.Ruleset.Value; + Beatmap.Value = beatmaps.GetWorkingBeatmap(firstItem.Beatmap.Value); + Mods.Value = firstItem.RequiredMods?.ToArray() ?? Array.Empty(); } return false;