diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs index 1e7003c612..3d0c1be22c 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs @@ -143,13 +143,13 @@ namespace osu.Game.Tests.Visual.Multiplayer Id = id, Username = $"User {id}" }); - SelectedRoom.Value.ParticipantCount.Value++; + SelectedRoom.Value.ParticipantCount++; } private void removeUserAt(int index) { SelectedRoom.Value.RecentParticipants.RemoveAt(index); - SelectedRoom.Value.ParticipantCount.Value--; + SelectedRoom.Value.ParticipantCount--; } } } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 6e9c18f040..e625d23779 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -248,7 +248,7 @@ namespace osu.Game.Tests.Visual.Multiplayer } }); - AddUntilStep("Check participant count correct", () => multiplayerClient.ClientAPIRoom?.ParticipantCount.Value == 1); + AddUntilStep("Check participant count correct", () => multiplayerClient.ClientAPIRoom?.ParticipantCount == 1); AddUntilStep("Check participant list contains user", () => multiplayerClient.ClientAPIRoom?.RecentParticipants.Count(u => u.Id == API.LocalUser.Value.Id) == 1); } @@ -308,7 +308,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("wait for room open", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); AddUntilStep("wait for join", () => multiplayerClient.RoomJoined); - AddUntilStep("Check participant count correct", () => multiplayerClient.ClientAPIRoom?.ParticipantCount.Value == 1); + AddUntilStep("Check participant count correct", () => multiplayerClient.ClientAPIRoom?.ParticipantCount == 1); AddUntilStep("Check participant list contains user", () => multiplayerClient.ClientAPIRoom?.RecentParticipants.Count(u => u.Id == API.LocalUser.Value.Id) == 1); } diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsParticipantsList.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsParticipantsList.cs index b9e5875e06..2368a2068d 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsParticipantsList.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsParticipantsList.cs @@ -38,7 +38,7 @@ namespace osu.Game.Tests.Visual.Playlists { AddStep("create component", () => { - Child = new ParticipantsDisplay(Direction.Horizontal) + Child = new ParticipantsDisplay(SelectedRoom.Value, Direction.Horizontal) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -52,7 +52,7 @@ namespace osu.Game.Tests.Visual.Playlists { AddStep("create component", () => { - Child = new ParticipantsDisplay(Direction.Vertical) + Child = new ParticipantsDisplay(SelectedRoom.Value, Direction.Vertical) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index a9c0108661..3a9f728b41 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -492,7 +492,7 @@ namespace osu.Game.Online.Multiplayer Id = user.UserID, Username = "[Unresolved]" }); - APIRoom.ParticipantCount.Value++; + APIRoom.ParticipantCount++; } private Task handleUserLeft(MultiplayerRoomUser user, Action? callback) @@ -507,7 +507,7 @@ namespace osu.Game.Online.Multiplayer Debug.Assert(APIRoom != null); APIRoom.RecentParticipants.RemoveAll(u => u.Id == user.UserID); - APIRoom.ParticipantCount.Value--; + APIRoom.ParticipantCount--; callback?.Invoke(user); RoomUpdated?.Invoke(); diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index b945d1b886..b63ebd107d 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -86,6 +86,24 @@ namespace osu.Game.Online.Rooms set => SetField(ref category, value); } + /// + /// The maximum number of users allowed in the room. + /// + public int? MaxParticipants + { + get => maxParticipants; + set => SetField(ref maxParticipants, value); + } + + /// + /// The current number of users in the room. + /// + public int ParticipantCount + { + get => participantCount; + set => SetField(ref participantCount, value); + } + /// /// The match type. /// @@ -162,6 +180,12 @@ namespace osu.Game.Online.Rooms [JsonConverter(typeof(SnakeCaseStringEnumConverter))] private RoomCategory category; + // Not yet serialised (not implemented). + private int? maxParticipants; + + [JsonProperty("participant_count")] + private int participantCount; + [JsonConverter(typeof(SnakeCaseStringEnumConverter))] [JsonProperty("type")] private MatchType type; @@ -211,9 +235,6 @@ namespace osu.Game.Online.Rooms set => AutoStartDuration.Value = TimeSpan.FromSeconds(value); } - [Cached] - public readonly Bindable MaxParticipants = new Bindable(); - [Cached] [JsonProperty("current_user_score")] public readonly Bindable UserScore = new Bindable(); @@ -222,10 +243,6 @@ namespace osu.Game.Online.Rooms [JsonProperty("recent_participants")] public readonly BindableList RecentParticipants = new BindableList(); - [Cached] - [JsonProperty("participant_count")] - public readonly Bindable ParticipantCount = new Bindable(); - #region Properties only used for room creation request [Cached] @@ -286,8 +303,8 @@ namespace osu.Game.Online.Rooms Availability = other.Availability; HasPassword = other.HasPassword; Type = other.Type; - MaxParticipants.Value = other.MaxParticipants.Value; - ParticipantCount.Value = other.ParticipantCount.Value; + MaxParticipants = other.MaxParticipants; + ParticipantCount = other.ParticipantCount; EndDate.Value = other.EndDate.Value; UserScore.Value = other.UserScore.Value; QueueMode = other.QueueMode; diff --git a/osu.Game/Screens/OnlinePlay/Components/ParticipantCountDisplay.cs b/osu.Game/Screens/OnlinePlay/Components/ParticipantCountDisplay.cs index 9f7e700ab3..f9744717d5 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ParticipantCountDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ParticipantCountDisplay.cs @@ -1,13 +1,13 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - +using System.ComponentModel; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Online.Rooms; namespace osu.Game.Screens.OnlinePlay.Components { @@ -16,18 +16,21 @@ namespace osu.Game.Screens.OnlinePlay.Components private const float text_size = 30; private const float transition_duration = 100; - private OsuSpriteText slash, maxText; + private readonly Room room; - public ParticipantCountDisplay() + private OsuSpriteText slash = null!; + private OsuSpriteText maxText = null!; + private OsuSpriteText count = null!; + + public ParticipantCountDisplay(Room room) { + this.room = room; AutoSizeAxes = Axes.Both; } [BackgroundDependencyLoader] private void load() { - OsuSpriteText count; - InternalChild = new FillFlowContainer { AutoSizeAxes = Axes.Both, @@ -50,14 +53,33 @@ namespace osu.Game.Screens.OnlinePlay.Components }, } }; - - MaxParticipants.BindValueChanged(_ => updateMax(), true); - ParticipantCount.BindValueChanged(c => count.Text = c.NewValue.ToString("#,0"), true); } - private void updateMax() + protected override void LoadComplete() { - if (MaxParticipants.Value == null) + base.LoadComplete(); + + room.PropertyChanged += onRoomPropertyChanged; + updateRoomParticipantCount(); + } + + private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e) + { + switch (e.PropertyName) + { + case nameof(Room.MaxParticipants): + updateRoomMaxParticipants(); + break; + + case nameof(Room.ParticipantCount): + updateRoomParticipantCount(); + break; + } + } + + private void updateRoomMaxParticipants() + { + if (room.MaxParticipants == null) { slash.FadeOut(transition_duration); maxText.FadeOut(transition_duration); @@ -65,9 +87,18 @@ namespace osu.Game.Screens.OnlinePlay.Components else { slash.FadeIn(transition_duration); - maxText.Text = MaxParticipants.Value.ToString(); + maxText.Text = room.MaxParticipants.ToString()!; maxText.FadeIn(transition_duration); } } + + private void updateRoomParticipantCount() + => count.Text = room.ParticipantCount.ToString("#,0"); + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + room.PropertyChanged -= onRoomPropertyChanged; + } } } diff --git a/osu.Game/Screens/OnlinePlay/Components/ParticipantsDisplay.cs b/osu.Game/Screens/OnlinePlay/Components/ParticipantsDisplay.cs index 5128bc4c14..1ae2756514 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ParticipantsDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ParticipantsDisplay.cs @@ -1,19 +1,23 @@ // 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 System.ComponentModel; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Online.Rooms; namespace osu.Game.Screens.OnlinePlay.Components { public partial class ParticipantsDisplay : OnlinePlayComposite { - public Bindable Details = new Bindable(); + public readonly Bindable Details = new Bindable(); - public ParticipantsDisplay(Direction direction) + private readonly Room room; + + public ParticipantsDisplay(Room room, Direction direction) { + this.room = room; OsuScrollContainer scroll; ParticipantsList list; @@ -46,14 +50,32 @@ namespace osu.Game.Screens.OnlinePlay.Components } } - [BackgroundDependencyLoader] - private void load() + protected override void LoadComplete() { - ParticipantCount.BindValueChanged(_ => setParticipantCount()); - MaxParticipants.BindValueChanged(_ => setParticipantCount(), true); + base.LoadComplete(); + + room.PropertyChanged += onRoomPropertyChanged; + updateRoomParticipantCount(); } - private void setParticipantCount() => - Details.Value = MaxParticipants.Value != null ? $"{ParticipantCount.Value}/{MaxParticipants.Value}" : ParticipantCount.Value.ToString(); + private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e) + { + switch (e.PropertyName) + { + case nameof(Room.MaxParticipants): + case nameof(Room.ParticipantCount): + updateRoomParticipantCount(); + break; + } + } + + private void updateRoomParticipantCount() + => Details.Value = room.MaxParticipants != null ? $"{room.ParticipantCount}/{room.MaxParticipants}" : room.ParticipantCount.ToString(); + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + room.PropertyChanged -= onRoomPropertyChanged; + } } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs index 2ffe740e24..06ae939842 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs @@ -166,14 +166,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components base.LoadComplete(); RecentParticipants.BindCollectionChanged(onParticipantsChanged, true); - ParticipantCount.BindValueChanged(_ => - { - updateHiddenUsers(); - totalCount.Text = ParticipantCount.Value.ToString(); - }, true); room.PropertyChanged += onRoomPropertyChanged; + updateRoomHost(); + updateRoomParticipantCount(); } private int numberOfCircles = 4; @@ -257,7 +254,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { int hiddenCount = 0; if (RecentParticipants.Count > NumberOfCircles) - hiddenCount = ParticipantCount.Value - NumberOfCircles + 1; + hiddenCount = room.ParticipantCount - NumberOfCircles + 1; hiddenUsers.Count = hiddenCount; @@ -272,8 +269,16 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e) { - if (e.PropertyName == nameof(Room.Host)) - updateRoomHost(); + switch (e.PropertyName) + { + case nameof(Room.Host): + updateRoomHost(); + break; + + case nameof(Room.ParticipantCount): + updateRoomParticipantCount(); + break; + } } private void updateRoomHost() @@ -288,6 +293,12 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components } } + private void updateRoomParticipantCount() + { + updateHiddenUsers(); + totalCount.Text = room.ParticipantCount.ToString(); + } + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index 62d6459487..141bddc4d4 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -350,7 +350,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match }; TypePicker.Current.BindValueChanged(type => typeLabel.Text = type.NewValue.GetLocalisableDescription(), true); - MaxParticipants.BindValueChanged(count => MaxParticipantsField.Text = count.NewValue?.ToString(), true); AutoStartDuration.BindValueChanged(duration => startModeDropdown.Current.Value = (StartMode)(int)duration.NewValue.TotalSeconds, true); operationInProgress.BindTo(ongoingOperationTracker.InProgress); @@ -377,6 +376,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match updateRoomQueueMode(); updateRoomPassword(); updateRoomAutoSkip(); + updateRoomMaxParticipants(); } private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e) @@ -402,6 +402,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match case nameof(Room.AutoSkip): updateRoomAutoSkip(); break; + + case nameof(Room.MaxParticipants): + updateRoomMaxParticipants(); + break; } } @@ -420,6 +424,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match private void updateRoomAutoSkip() => AutoSkipCheckbox.Current.Value = room.AutoSkip; + private void updateRoomMaxParticipants() + => MaxParticipantsField.Text = room.MaxParticipants?.ToString(); + protected override void Update() { base.Update(); @@ -469,9 +476,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match room.AutoSkip = AutoSkipCheckbox.Current.Value; if (int.TryParse(MaxParticipantsField.Text, out int max)) - room.MaxParticipants.Value = max; + room.MaxParticipants = max; else - room.MaxParticipants.Value = null; + room.MaxParticipants = null; manager.CreateRoom(room, onSuccess, onError); } diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs index 2b1c9cf00a..7623cbabbd 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs @@ -27,12 +27,6 @@ namespace osu.Game.Screens.OnlinePlay [Resolved(typeof(Room))] protected BindableList RecentParticipants { get; private set; } = null!; - [Resolved(typeof(Room))] - protected Bindable ParticipantCount { get; private set; } = null!; - - [Resolved(typeof(Room))] - protected Bindable MaxParticipants { get; private set; } = null!; - [Resolved(typeof(Room))] protected Bindable MaxAttempts { get; private set; } = null!; diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs index aee26e544a..20fbe42dd5 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs @@ -316,7 +316,6 @@ namespace osu.Game.Screens.OnlinePlay.Playlists loadingLayer = new LoadingLayer(true) }; - MaxParticipants.BindValueChanged(count => MaxParticipantsField.Text = count.NewValue?.ToString(), true); MaxAttempts.BindValueChanged(count => MaxAttemptsField.Text = count.NewValue?.ToString(), true); Duration.BindValueChanged(duration => DurationField.Current.Value = duration.NewValue ?? TimeSpan.FromMinutes(30), true); @@ -346,6 +345,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists updateRoomName(); updateRoomAvailability(); + updateRoomMaxParticipants(); } private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e) @@ -359,6 +359,10 @@ namespace osu.Game.Screens.OnlinePlay.Playlists case nameof(Room.Availability): updateRoomAvailability(); break; + + case nameof(Room.MaxParticipants): + updateRoomMaxParticipants(); + break; } } @@ -368,6 +372,9 @@ namespace osu.Game.Screens.OnlinePlay.Playlists private void updateRoomAvailability() => AvailabilityPicker.Current.Value = room.Availability; + private void updateRoomMaxParticipants() + => MaxParticipantsField.Text = room.MaxParticipants?.ToString(); + private void populateDurations(ValueChangedEvent user) { // roughly correct (see https://github.com/Humanizr/Humanizer/blob/18167e56c082449cc4fe805b8429e3127a7b7f93/readme.md?plain=1#L427) @@ -417,11 +424,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists room.Name = NameField.Text; room.Availability = AvailabilityPicker.Current.Value; - - if (int.TryParse(MaxParticipantsField.Text, out int max)) - MaxParticipants.Value = max; - else - MaxParticipants.Value = null; + room.MaxParticipants = int.TryParse(MaxParticipantsField.Text, out int max) ? max : null; if (int.TryParse(MaxAttemptsField.Text, out max)) MaxAttempts.Value = max;