Pass through MultiplayerRoomUsers instead of ints to avoid re-retrieval

This commit is contained in:
Dean Herbert
2021-08-10 18:39:20 +09:00
parent 53d03745e0
commit a503274e1d
10 changed files with 59 additions and 51 deletions

View File

@ -6,6 +6,7 @@ using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Framework.Timing; using osu.Framework.Timing;
using osu.Game.Online.Multiplayer;
using osu.Game.Rulesets.Osu.Scoring; using osu.Game.Rulesets.Osu.Scoring;
using osu.Game.Screens.OnlinePlay.Multiplayer.Spectate; using osu.Game.Screens.OnlinePlay.Multiplayer.Spectate;
using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Play.HUD;
@ -45,7 +46,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
var scoreProcessor = new OsuScoreProcessor(); var scoreProcessor = new OsuScoreProcessor();
scoreProcessor.ApplyBeatmap(playable); scoreProcessor.ApplyBeatmap(playable);
LoadComponentAsync(leaderboard = new MultiSpectatorLeaderboard(scoreProcessor, clocks.Keys.ToArray()) { Expanded = { Value = true } }, Add); LoadComponentAsync(leaderboard = new MultiSpectatorLeaderboard(scoreProcessor, clocks.Keys.Select(id => new MultiplayerRoomUser(id)).ToArray()) { Expanded = { Value = true } }, Add);
}); });
AddUntilStep("wait for load", () => leaderboard.IsLoaded); AddUntilStep("wait for load", () => leaderboard.IsLoaded);

View File

@ -8,6 +8,7 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus; using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Screens.OnlinePlay.Multiplayer.Spectate; using osu.Game.Screens.OnlinePlay.Multiplayer.Spectate;
@ -27,7 +28,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
private MultiSpectatorScreen spectatorScreen; private MultiSpectatorScreen spectatorScreen;
private readonly List<int> playingUserIds = new List<int>(); private readonly List<MultiplayerRoomUser> playingUsers = new List<MultiplayerRoomUser>();
private BeatmapSetInfo importedSet; private BeatmapSetInfo importedSet;
private BeatmapInfo importedBeatmap; private BeatmapInfo importedBeatmap;
@ -42,7 +43,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
} }
[SetUp] [SetUp]
public new void Setup() => Schedule(() => playingUserIds.Clear()); public new void Setup() => Schedule(() => playingUsers.Clear());
[Test] [Test]
public void TestDelayedStart() public void TestDelayedStart()
@ -52,8 +53,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
OnlinePlayDependencies.Client.AddUser(new User { Id = PLAYER_1_ID }, true); OnlinePlayDependencies.Client.AddUser(new User { Id = PLAYER_1_ID }, true);
OnlinePlayDependencies.Client.AddUser(new User { Id = PLAYER_2_ID }, true); OnlinePlayDependencies.Client.AddUser(new User { Id = PLAYER_2_ID }, true);
playingUserIds.Add(PLAYER_1_ID); playingUsers.Add(new MultiplayerRoomUser(PLAYER_1_ID));
playingUserIds.Add(PLAYER_2_ID); playingUsers.Add(new MultiplayerRoomUser(PLAYER_2_ID));
}); });
loadSpectateScreen(false); loadSpectateScreen(false);
@ -99,8 +100,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
SpectatorClient.StartPlay(PLAYER_1_ID, importedBeatmapId); SpectatorClient.StartPlay(PLAYER_1_ID, importedBeatmapId);
SpectatorClient.StartPlay(PLAYER_2_ID, importedBeatmapId); SpectatorClient.StartPlay(PLAYER_2_ID, importedBeatmapId);
playingUserIds.Add(PLAYER_1_ID); playingUsers.Add(new MultiplayerRoomUser(PLAYER_1_ID));
playingUserIds.Add(PLAYER_2_ID); playingUsers.Add(new MultiplayerRoomUser(PLAYER_2_ID));
}); });
loadSpectateScreen(); loadSpectateScreen();
@ -287,7 +288,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
Beatmap.Value = beatmapManager.GetWorkingBeatmap(importedBeatmap); Beatmap.Value = beatmapManager.GetWorkingBeatmap(importedBeatmap);
Ruleset.Value = importedBeatmap.Ruleset; Ruleset.Value = importedBeatmap.Ruleset;
LoadScreen(spectatorScreen = new MultiSpectatorScreen(playingUserIds.ToArray())); LoadScreen(spectatorScreen = new MultiSpectatorScreen(playingUsers.ToArray()));
}); });
AddUntilStep("wait for screen load", () => spectatorScreen.LoadState == LoadState.Loaded && (!waitForPlayerLoad || spectatorScreen.AllPlayersLoaded)); AddUntilStep("wait for screen load", () => spectatorScreen.LoadState == LoadState.Loaded && (!waitForPlayerLoad || spectatorScreen.AllPlayersLoaded));
@ -302,7 +303,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
OnlinePlayDependencies.Client.AddUser(new User { Id = id }, true); OnlinePlayDependencies.Client.AddUser(new User { Id = id }, true);
SpectatorClient.StartPlay(id, beatmapId ?? importedBeatmapId); SpectatorClient.StartPlay(id, beatmapId ?? importedBeatmapId);
playingUserIds.Add(id); playingUsers.Add(new MultiplayerRoomUser(id));
} }
}); });
} }

View File

@ -66,7 +66,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
scoreProcessor.ApplyBeatmap(playable); scoreProcessor.ApplyBeatmap(playable);
LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(scoreProcessor, users.ToArray()) LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(scoreProcessor, Client.Room?.Users.ToArray())
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,

View File

@ -75,7 +75,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
scoreProcessor.ApplyBeatmap(playable); scoreProcessor.ApplyBeatmap(playable);
LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(scoreProcessor, users.ToArray()) LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(scoreProcessor, Client.Room?.Users.ToArray())
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,

View File

@ -475,16 +475,18 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
protected override Screen CreateGameplayScreen() protected override Screen CreateGameplayScreen()
{ {
Debug.Assert(client.LocalUser != null); Debug.Assert(client.LocalUser != null);
Debug.Assert(client.Room != null);
int[] userIds = client.CurrentMatchPlayingUserIds.ToArray(); int[] userIds = client.CurrentMatchPlayingUserIds.ToArray();
MultiplayerRoomUser[] users = userIds.Select(id => client.Room.Users.First(u => u.UserID == id)).ToArray();
switch (client.LocalUser.State) switch (client.LocalUser.State)
{ {
case MultiplayerUserState.Spectating: case MultiplayerUserState.Spectating:
return new MultiSpectatorScreen(userIds); return new MultiSpectatorScreen(users.Take(PlayerGrid.MAX_PLAYERS).ToArray());
default: default:
return new PlayerLoader(() => new MultiplayerPlayer(SelectedItem.Value, userIds)); return new PlayerLoader(() => new MultiplayerPlayer(SelectedItem.Value, users));
} }
} }

View File

@ -37,7 +37,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
private MultiplayerGameplayLeaderboard leaderboard; private MultiplayerGameplayLeaderboard leaderboard;
private readonly int[] userIds; private readonly MultiplayerRoomUser[] users;
private LoadingLayer loadingDisplay; private LoadingLayer loadingDisplay;
private FillFlowContainer leaderboardFlow; private FillFlowContainer leaderboardFlow;
@ -46,8 +46,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
/// Construct a multiplayer player. /// Construct a multiplayer player.
/// </summary> /// </summary>
/// <param name="playlistItem">The playlist item to be played.</param> /// <param name="playlistItem">The playlist item to be played.</param>
/// <param name="userIds">The users which are participating in this game.</param> /// <param name="users">The users which are participating in this game.</param>
public MultiplayerPlayer(PlaylistItem playlistItem, int[] userIds) public MultiplayerPlayer(PlaylistItem playlistItem, MultiplayerRoomUser[] users)
: base(playlistItem, new PlayerConfiguration : base(playlistItem, new PlayerConfiguration
{ {
AllowPause = false, AllowPause = false,
@ -55,7 +55,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
AllowSkipping = false, AllowSkipping = false,
}) })
{ {
this.userIds = userIds; this.users = users;
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
@ -71,7 +71,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
}); });
// todo: this should be implemented via a custom HUD implementation, and correctly masked to the main content area. // todo: this should be implemented via a custom HUD implementation, and correctly masked to the main content area.
LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(ScoreProcessor, userIds), l => LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(ScoreProcessor, users), l =>
{ {
if (!LoadedBeatmapSuccessfully) if (!LoadedBeatmapSuccessfully)
return; return;

View File

@ -12,8 +12,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
{ {
public class MultiSpectatorLeaderboard : MultiplayerGameplayLeaderboard public class MultiSpectatorLeaderboard : MultiplayerGameplayLeaderboard
{ {
public MultiSpectatorLeaderboard([NotNull] ScoreProcessor scoreProcessor, int[] userIds) public MultiSpectatorLeaderboard([NotNull] ScoreProcessor scoreProcessor, MultiplayerRoomUser[] users)
: base(scoreProcessor, userIds) : base(scoreProcessor, users)
{ {
} }

View File

@ -46,14 +46,19 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
private PlayerArea currentAudioSource; private PlayerArea currentAudioSource;
private bool canStartMasterClock; private bool canStartMasterClock;
private readonly MultiplayerRoomUser[] users;
/// <summary> /// <summary>
/// Creates a new <see cref="MultiSpectatorScreen"/>. /// Creates a new <see cref="MultiSpectatorScreen"/>.
/// </summary> /// </summary>
/// <param name="userIds">The players to spectate.</param> /// <param name="users">The players to spectate.</param>
public MultiSpectatorScreen(int[] userIds) public MultiSpectatorScreen(MultiplayerRoomUser[] users)
: base(userIds.Take(PlayerGrid.MAX_PLAYERS).ToArray()) : base(users.Select(u => u.UserID).ToArray())
{ {
instances = new PlayerArea[UserIds.Count]; // todo: this is a bit ugly, but not sure on a better way to handle.
this.users = users;
instances = new PlayerArea[Users.Count];
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
@ -105,9 +110,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
}) })
}; };
for (int i = 0; i < UserIds.Count; i++) for (int i = 0; i < Users.Count; i++)
{ {
grid.Add(instances[i] = new PlayerArea(UserIds[i], masterClockContainer.GameplayClock)); grid.Add(instances[i] = new PlayerArea(Users[i], masterClockContainer.GameplayClock));
syncManager.AddPlayerClock(instances[i].GameplayClock); syncManager.AddPlayerClock(instances[i].GameplayClock);
} }
@ -116,7 +121,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
var scoreProcessor = Ruleset.Value.CreateInstance().CreateScoreProcessor(); var scoreProcessor = Ruleset.Value.CreateInstance().CreateScoreProcessor();
scoreProcessor.ApplyBeatmap(playableBeatmap); scoreProcessor.ApplyBeatmap(playableBeatmap);
LoadComponentAsync(leaderboard = new MultiSpectatorLeaderboard(scoreProcessor, UserIds.ToArray()) LoadComponentAsync(leaderboard = new MultiSpectatorLeaderboard(scoreProcessor, users)
{ {
Expanded = { Value = true }, Expanded = { Value = true },
Anchor = Anchor.CentreLeft, Anchor = Anchor.CentreLeft,

View File

@ -41,23 +41,24 @@ namespace osu.Game.Screens.Play.HUD
private UserLookupCache userLookupCache { get; set; } private UserLookupCache userLookupCache { get; set; }
private readonly ScoreProcessor scoreProcessor; private readonly ScoreProcessor scoreProcessor;
private readonly IBindableList<int> playingUsers; private readonly MultiplayerRoomUser[] playingUsers;
private Bindable<ScoringMode> scoringMode; private Bindable<ScoringMode> scoringMode;
private readonly IBindableList<int> playingUserIds = new BindableList<int>();
private bool hasTeams => TeamScores.Count > 0; private bool hasTeams => TeamScores.Count > 0;
/// <summary> /// <summary>
/// Construct a new leaderboard. /// Construct a new leaderboard.
/// </summary> /// </summary>
/// <param name="scoreProcessor">A score processor instance to handle score calculation for scores of users in the match.</param> /// <param name="scoreProcessor">A score processor instance to handle score calculation for scores of users in the match.</param>
/// <param name="userIds">IDs of all users in this match.</param> /// <param name="users">IDs of all users in this match.</param>
public MultiplayerGameplayLeaderboard(ScoreProcessor scoreProcessor, int[] userIds) public MultiplayerGameplayLeaderboard(ScoreProcessor scoreProcessor, MultiplayerRoomUser[] users)
{ {
// todo: this will eventually need to be created per user to support different mod combinations. // todo: this will eventually need to be created per user to support different mod combinations.
this.scoreProcessor = scoreProcessor; this.scoreProcessor = scoreProcessor;
// todo: this will likely be passed in as User instances. playingUsers = users;
playingUsers = new BindableList<int>(userIds);
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
@ -65,19 +66,17 @@ namespace osu.Game.Screens.Play.HUD
{ {
scoringMode = config.GetBindable<ScoringMode>(OsuSetting.ScoreDisplayMode); scoringMode = config.GetBindable<ScoringMode>(OsuSetting.ScoreDisplayMode);
foreach (var userId in playingUsers) foreach (var user in playingUsers)
{ {
var user = multiplayerClient.Room?.Users.FirstOrDefault(u => u.UserID == userId);
var trackedUser = CreateUserData(user, scoreProcessor); var trackedUser = CreateUserData(user, scoreProcessor);
trackedUser.ScoringMode.BindTo(scoringMode); trackedUser.ScoringMode.BindTo(scoringMode);
UserScores[userId] = trackedUser; UserScores[user.UserID] = trackedUser;
if (trackedUser.Team is int team && !TeamScores.ContainsKey(team)) if (trackedUser.Team is int team && !TeamScores.ContainsKey(team))
TeamScores.Add(team, new BindableInt()); TeamScores.Add(team, new BindableInt());
} }
userLookupCache.GetUsersAsync(playingUsers.ToArray()).ContinueWith(users => Schedule(() => userLookupCache.GetUsersAsync(playingUsers.Select(u => u.UserID).ToArray()).ContinueWith(users => Schedule(() =>
{ {
foreach (var user in users.Result) foreach (var user in users.Result)
{ {
@ -100,18 +99,18 @@ namespace osu.Game.Screens.Play.HUD
base.LoadComplete(); base.LoadComplete();
// BindableList handles binding in a really bad way (Clear then AddRange) so we need to do this manually.. // BindableList handles binding in a really bad way (Clear then AddRange) so we need to do this manually..
foreach (int userId in playingUsers) foreach (var user in playingUsers)
{ {
spectatorClient.WatchUser(userId); spectatorClient.WatchUser(user.UserID);
if (!multiplayerClient.CurrentMatchPlayingUserIds.Contains(userId)) if (!multiplayerClient.CurrentMatchPlayingUserIds.Contains(user.UserID))
usersChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, new[] { userId })); usersChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, new[] { user.UserID }));
} }
// bind here is to support players leaving the match. // bind here is to support players leaving the match.
// new players are not supported. // new players are not supported.
playingUsers.BindTo(multiplayerClient.CurrentMatchPlayingUserIds); playingUserIds.BindTo(multiplayerClient.CurrentMatchPlayingUserIds);
playingUsers.BindCollectionChanged(usersChanged); playingUserIds.BindCollectionChanged(usersChanged);
// this leaderboard should be guaranteed to be completely loaded before the gameplay starts (is a prerequisite in MultiplayerPlayer). // this leaderboard should be guaranteed to be completely loaded before the gameplay starts (is a prerequisite in MultiplayerPlayer).
spectatorClient.OnNewFrames += handleIncomingFrames; spectatorClient.OnNewFrames += handleIncomingFrames;
@ -197,7 +196,7 @@ namespace osu.Game.Screens.Play.HUD
{ {
foreach (var user in playingUsers) foreach (var user in playingUsers)
{ {
spectatorClient.StopWatchingUser(user); spectatorClient.StopWatchingUser(user.UserID);
} }
spectatorClient.OnNewFrames -= handleIncomingFrames; spectatorClient.OnNewFrames -= handleIncomingFrames;

View File

@ -24,9 +24,9 @@ namespace osu.Game.Screens.Spectate
/// </summary> /// </summary>
public abstract class SpectatorScreen : OsuScreen public abstract class SpectatorScreen : OsuScreen
{ {
protected IReadOnlyList<int> UserIds => userIds; protected IReadOnlyList<int> Users => users;
private readonly List<int> userIds = new List<int>(); private readonly List<int> users = new List<int>();
[Resolved] [Resolved]
private BeatmapManager beatmaps { get; set; } private BeatmapManager beatmaps { get; set; }
@ -50,17 +50,17 @@ namespace osu.Game.Screens.Spectate
/// <summary> /// <summary>
/// Creates a new <see cref="SpectatorScreen"/>. /// Creates a new <see cref="SpectatorScreen"/>.
/// </summary> /// </summary>
/// <param name="userIds">The users to spectate.</param> /// <param name="users">The users to spectate.</param>
protected SpectatorScreen(params int[] userIds) protected SpectatorScreen(params int[] users)
{ {
this.userIds.AddRange(userIds); this.users.AddRange(users);
} }
protected override void LoadComplete() protected override void LoadComplete()
{ {
base.LoadComplete(); base.LoadComplete();
userLookupCache.GetUsersAsync(userIds.ToArray()).ContinueWith(users => Schedule(() => userLookupCache.GetUsersAsync(users.ToArray()).ContinueWith(users => Schedule(() =>
{ {
foreach (var u in users.Result) foreach (var u in users.Result)
{ {
@ -207,7 +207,7 @@ namespace osu.Game.Screens.Spectate
{ {
onUserStateRemoved(userId); onUserStateRemoved(userId);
userIds.Remove(userId); users.Remove(userId);
userMap.Remove(userId); userMap.Remove(userId);
spectatorClient.StopWatchingUser(userId); spectatorClient.StopWatchingUser(userId);