1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 05:32:54 +08:00

Make Room.RecentParticipants non-bindable

This commit is contained in:
Dan Balasescu 2024-11-14 15:07:16 +09:00
parent dc5337d771
commit b16edbbf52
No known key found for this signature in database
11 changed files with 100 additions and 79 deletions

View File

@ -199,11 +199,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
if (room.RecentParticipants.Count == 0)
{
room.RecentParticipants.AddRange(Enumerable.Range(0, 20).Select(i => new APIUser
room.RecentParticipants = Enumerable.Range(0, 20).Select(i => new APIUser
{
Id = i,
Username = $"User {i}"
}));
}).ToArray();
}
return new DrawableLoungeRoom(room)

View File

@ -138,17 +138,17 @@ namespace osu.Game.Tests.Visual.Multiplayer
private void addUser(int id)
{
SelectedRoom.Value.RecentParticipants.Add(new APIUser
SelectedRoom.Value.RecentParticipants = SelectedRoom.Value.RecentParticipants.Append(new APIUser
{
Id = id,
Username = $"User {id}"
});
}).ToArray();
SelectedRoom.Value.ParticipantCount++;
}
private void removeUserAt(int index)
{
SelectedRoom.Value.RecentParticipants.RemoveAt(index);
SelectedRoom.Value.RecentParticipants = SelectedRoom.Value.RecentParticipants.Where(u => !u.Equals(SelectedRoom.Value.RecentParticipants[index])).ToArray();
SelectedRoom.Value.ParticipantCount--;
}
}

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Game.Online.API.Requests.Responses;
@ -19,17 +20,16 @@ namespace osu.Game.Tests.Visual.Playlists
AddStep("create list", () =>
{
SelectedRoom.Value = new Room { RoomID = 7 };
for (int i = 0; i < 50; i++)
SelectedRoom.Value = new Room
{
SelectedRoom.Value.RecentParticipants.Add(new APIUser
RoomID = 7,
RecentParticipants = Enumerable.Range(0, 50).Select(_ => new APIUser
{
Username = "peppy",
Statistics = new UserStatistics { GlobalRank = 1234 },
Id = 2
});
}
}).ToArray()
};
});
}

View File

@ -62,7 +62,7 @@ namespace osu.Game.Tests.Visual.Playlists
{
room.Name = "my awesome room";
room.Host = API.LocalUser.Value;
room.RecentParticipants.Add(room.Host);
room.RecentParticipants = [room.Host];
room.EndDate = DateTimeOffset.Now.AddMinutes(5);
room.Playlist.Add(new PlaylistItem(importedBeatmap.Beatmaps.First())
{
@ -86,7 +86,7 @@ namespace osu.Game.Tests.Visual.Playlists
room.Name = "my awesome room";
room.MaxAttempts = 5;
room.Host = API.LocalUser.Value;
room.RecentParticipants.Add(room.Host);
room.RecentParticipants = [room.Host];
room.EndDate = DateTimeOffset.Now.AddMinutes(5);
room.Playlist.Add(new PlaylistItem(importedBeatmap.Beatmaps.First())
{

View File

@ -487,11 +487,11 @@ namespace osu.Game.Online.Multiplayer
{
Debug.Assert(APIRoom != null);
APIRoom.RecentParticipants.Add(user.User ?? new APIUser
APIRoom.RecentParticipants = APIRoom.RecentParticipants.Append(user.User ?? new APIUser
{
Id = user.UserID,
Username = "[Unresolved]"
});
}).ToArray();
APIRoom.ParticipantCount++;
}
@ -506,7 +506,7 @@ namespace osu.Game.Online.Multiplayer
PlayingUserIds.Remove(user.UserID);
Debug.Assert(APIRoom != null);
APIRoom.RecentParticipants.RemoveAll(u => u.Id == user.UserID);
APIRoom.RecentParticipants = APIRoom.RecentParticipants.Where(u => u.Id != user.UserID).ToArray();
APIRoom.ParticipantCount--;
callback?.Invoke(user);

View File

@ -137,6 +137,15 @@ namespace osu.Game.Online.Rooms
set => SetField(ref participantCount, value);
}
/// <summary>
/// The set of most recent participants in the room.
/// </summary>
public IReadOnlyList<APIUser> RecentParticipants
{
get => recentParticipants;
set => SetList(ref recentParticipants, value);
}
/// <summary>
/// The match type.
/// </summary>
@ -282,6 +291,9 @@ namespace osu.Game.Online.Rooms
[JsonProperty("participant_count")]
private int participantCount;
[JsonProperty("recent_participants")]
private IReadOnlyList<APIUser> recentParticipants = [];
[JsonProperty("max_attempts", DefaultValueHandling = DefaultValueHandling.Ignore)]
private int? maxAttempts;
@ -324,10 +336,6 @@ namespace osu.Game.Online.Rooms
[JsonProperty("playlist")]
public readonly BindableList<PlaylistItem> Playlist = new BindableList<PlaylistItem>();
[Cached]
[JsonProperty("recent_participants")]
public readonly BindableList<APIUser> RecentParticipants = new BindableList<APIUser>();
/// <summary>
/// Copies values from another <see cref="Room"/> into this one.
/// </summary>
@ -369,11 +377,7 @@ namespace osu.Game.Online.Rooms
Playlist.AddRange(other.Playlist);
}
if (!RecentParticipants.SequenceEqual(other.RecentParticipants))
{
RecentParticipants.Clear();
RecentParticipants.AddRange(other.RecentParticipants);
}
RecentParticipants = other.RecentParticipants;
}
public void RemoveExpiredPlaylistItems()
@ -411,6 +415,16 @@ namespace osu.Game.Online.Rooms
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null!)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
protected bool SetList<T>(ref IReadOnlyList<T> list, IReadOnlyList<T> value, [CallerMemberName] string propertyName = null!)
{
if (list.SequenceEqual(value))
return false;
list = value;
OnPropertyChanged(propertyName);
return true;
}
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null!)
{
if (EqualityComparer<T>.Default.Equals(field, value))

View File

@ -23,7 +23,7 @@ namespace osu.Game.Screens.OnlinePlay.Components
AddInternal(scroll = new OsuScrollContainer(direction)
{
Child = list = new ParticipantsList()
Child = list = new ParticipantsList(room)
});
switch (direction)

View File

@ -1,15 +1,14 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Allocation;
using System.ComponentModel;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Threading;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Rooms;
using osu.Game.Users.Drawables;
using osuTK;
@ -57,15 +56,29 @@ namespace osu.Game.Screens.OnlinePlay.Components
}
}
[BackgroundDependencyLoader]
private void load()
private readonly Room room;
public ParticipantsList(Room room)
{
RecentParticipants.CollectionChanged += (_, _) => updateParticipants();
this.room = room;
}
protected override void LoadComplete()
{
base.LoadComplete();
room.PropertyChanged += onRoomPropertyChanged;
updateParticipants();
}
private ScheduledDelegate scheduledUpdate;
private FillFlowContainer<UserTile> tiles;
private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(Room.RecentParticipants))
updateParticipants();
}
private ScheduledDelegate? scheduledUpdate;
private FillFlowContainer<UserTile>? tiles;
private void updateParticipants()
{
@ -83,8 +96,8 @@ namespace osu.Game.Screens.OnlinePlay.Components
Spacing = Vector2.One
};
for (int i = 0; i < RecentParticipants.Count; i++)
tiles.Add(new UserTile { User = RecentParticipants[i] });
for (int i = 0; i < room.RecentParticipants.Count; i++)
tiles.Add(new UserTile { User = room.RecentParticipants[i] });
AddInternal(tiles);
@ -92,9 +105,15 @@ namespace osu.Game.Screens.OnlinePlay.Components
});
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
room.PropertyChanged -= onRoomPropertyChanged;
}
private partial class UserTile : CompositeDrawable
{
public APIUser User
public APIUser? User
{
get => avatar.User;
set => avatar.User = value;

View File

@ -1,9 +1,8 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Specialized;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
@ -165,12 +164,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
{
base.LoadComplete();
RecentParticipants.BindCollectionChanged(onParticipantsChanged, true);
room.PropertyChanged += onRoomPropertyChanged;
updateRoomHost();
updateRoomParticipantCount();
updateRoomParticipants();
}
private int numberOfCircles = 4;
@ -190,43 +188,38 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
// Reinitialising the list looks janky, but this is unlikely to be used in a setting where it's visible.
clearUsers();
foreach (var u in RecentParticipants)
foreach (var u in room.RecentParticipants)
addUser(u);
updateHiddenUsers();
}
}
private void onParticipantsChanged(object? sender, NotifyCollectionChangedEventArgs e)
private void updateRoomParticipants()
{
switch (e.Action)
HashSet<APIUser> newUsers = room.RecentParticipants.ToHashSet();
avatarFlow.RemoveAll(a =>
{
case NotifyCollectionChangedAction.Add:
Debug.Assert(e.NewItems != null);
// Avatar with no user. Really shouldn't ever be the case but asserting it correctly is difficult.
if (a.User == null)
return false;
foreach (var added in e.NewItems.OfType<APIUser>())
addUser(added);
break;
// User was previously and still is a participant. Keep them around but remove them from the new set.
// This will be useful when we add all remaining users (now just the new participants) to the flow.
if (newUsers.Contains(a.User))
{
newUsers.Remove(a.User);
return false;
}
case NotifyCollectionChangedAction.Remove:
Debug.Assert(e.OldItems != null);
// User is no longer a participant. Remove them from the flow.
return true;
}, true);
foreach (var removed in e.OldItems.OfType<APIUser>())
removeUser(removed);
break;
case NotifyCollectionChangedAction.Reset:
clearUsers();
break;
case NotifyCollectionChangedAction.Replace:
case NotifyCollectionChangedAction.Move:
// Easiest is to just reinitialise the whole list. These are unlikely to ever be use cases.
clearUsers();
foreach (var u in RecentParticipants)
addUser(u);
break;
}
// Add all remaining users to the flow.
foreach (var u in newUsers)
addUser(u);
updateHiddenUsers();
}
@ -239,11 +232,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
avatarFlow.Add(new CircularAvatar { User = user });
}
private void removeUser(APIUser user)
{
avatarFlow.RemoveAll(a => a.User == user, true);
}
private void clearUsers()
{
avatarFlow.Clear();
@ -253,7 +241,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
private void updateHiddenUsers()
{
int hiddenCount = 0;
if (RecentParticipants.Count > NumberOfCircles)
if (room.RecentParticipants.Count > NumberOfCircles)
hiddenCount = room.ParticipantCount - NumberOfCircles + 1;
hiddenUsers.Count = hiddenCount;
@ -262,7 +250,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
avatarFlow.Remove(avatarFlow.Last(), true);
else if (displayedCircles < NumberOfCircles)
{
var nextUser = RecentParticipants.FirstOrDefault(u => avatarFlow.All(a => a.User != u));
var nextUser = room.RecentParticipants.FirstOrDefault(u => avatarFlow.All(a => a.User != u));
if (nextUser != null) addUser(nextUser);
}
}
@ -278,6 +266,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
case nameof(Room.ParticipantCount):
updateRoomParticipantCount();
break;
case nameof(Room.RecentParticipants):
updateRoomParticipants();
break;
}
}

View File

@ -4,7 +4,6 @@
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics.Containers;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Rooms;
namespace osu.Game.Screens.OnlinePlay
@ -16,8 +15,5 @@ namespace osu.Game.Screens.OnlinePlay
{
[Resolved(typeof(Room))]
protected BindableList<PlaylistItem> Playlist { get; private set; } = null!;
[Resolved(typeof(Room))]
protected BindableList<APIUser> RecentParticipants { get; private set; } = null!;
}
}

View File

@ -280,7 +280,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay
responseRoom.Password = responseRoom.HasPassword ? Guid.NewGuid().ToString() : null;
if (!withParticipants)
responseRoom.RecentParticipants.Clear();
responseRoom.RecentParticipants = [];
return responseRoom;
}