1
0
mirror of https://github.com/ppy/osu.git synced 2025-02-14 04:52:55 +08:00

Merge pull request #11316 from peppy/multiplayer-client-threading-fixes

Fix join room failures on large lobbies
This commit is contained in:
Dean Herbert 2020-12-26 20:38:47 +09:00 committed by GitHub
commit 36dfff64c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -4,8 +4,10 @@
#nullable enable #nullable enable
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
@ -108,8 +110,9 @@ namespace osu.Game.Online.Multiplayer
Debug.Assert(Room != null); Debug.Assert(Room != null);
foreach (var user in Room.Users) var users = getRoomUsers();
await PopulateUser(user);
await Task.WhenAll(users.Select(PopulateUser));
updateLocalRoomSettings(Room.Settings); updateLocalRoomSettings(Room.Settings);
} }
@ -123,13 +126,16 @@ namespace osu.Game.Online.Multiplayer
public virtual Task LeaveRoom() public virtual Task LeaveRoom()
{ {
if (Room == null) Scheduler.Add(() =>
return Task.CompletedTask; {
if (Room == null)
return;
apiRoom = null; apiRoom = null;
Room = null; Room = null;
Schedule(() => RoomChanged?.Invoke()); RoomChanged?.Invoke();
}, false);
return Task.CompletedTask; return Task.CompletedTask;
} }
@ -184,7 +190,7 @@ namespace osu.Game.Online.Multiplayer
if (Room == null) if (Room == null)
return Task.CompletedTask; return Task.CompletedTask;
Schedule(() => Scheduler.Add(() =>
{ {
if (Room == null) if (Room == null)
return; return;
@ -209,7 +215,7 @@ namespace osu.Game.Online.Multiplayer
} }
RoomChanged?.Invoke(); RoomChanged?.Invoke();
}); }, false);
return Task.CompletedTask; return Task.CompletedTask;
} }
@ -221,7 +227,7 @@ namespace osu.Game.Online.Multiplayer
await PopulateUser(user); await PopulateUser(user);
Schedule(() => Scheduler.Add(() =>
{ {
if (Room == null) if (Room == null)
return; return;
@ -233,7 +239,7 @@ namespace osu.Game.Online.Multiplayer
Room.Users.Add(user); Room.Users.Add(user);
RoomChanged?.Invoke(); RoomChanged?.Invoke();
}); }, false);
} }
Task IMultiplayerClient.UserLeft(MultiplayerRoomUser user) Task IMultiplayerClient.UserLeft(MultiplayerRoomUser user)
@ -241,7 +247,7 @@ namespace osu.Game.Online.Multiplayer
if (Room == null) if (Room == null)
return Task.CompletedTask; return Task.CompletedTask;
Schedule(() => Scheduler.Add(() =>
{ {
if (Room == null) if (Room == null)
return; return;
@ -250,7 +256,7 @@ namespace osu.Game.Online.Multiplayer
PlayingUsers.Remove(user.UserID); PlayingUsers.Remove(user.UserID);
RoomChanged?.Invoke(); RoomChanged?.Invoke();
}); }, false);
return Task.CompletedTask; return Task.CompletedTask;
} }
@ -260,7 +266,7 @@ namespace osu.Game.Online.Multiplayer
if (Room == null) if (Room == null)
return Task.CompletedTask; return Task.CompletedTask;
Schedule(() => Scheduler.Add(() =>
{ {
if (Room == null) if (Room == null)
return; return;
@ -273,7 +279,7 @@ namespace osu.Game.Online.Multiplayer
apiRoom.Host.Value = user?.User; apiRoom.Host.Value = user?.User;
RoomChanged?.Invoke(); RoomChanged?.Invoke();
}); }, false);
return Task.CompletedTask; return Task.CompletedTask;
} }
@ -289,7 +295,7 @@ namespace osu.Game.Online.Multiplayer
if (Room == null) if (Room == null)
return Task.CompletedTask; return Task.CompletedTask;
Schedule(() => Scheduler.Add(() =>
{ {
if (Room == null) if (Room == null)
return; return;
@ -300,7 +306,7 @@ namespace osu.Game.Online.Multiplayer
PlayingUsers.Remove(userId); PlayingUsers.Remove(userId);
RoomChanged?.Invoke(); RoomChanged?.Invoke();
}); }, false);
return Task.CompletedTask; return Task.CompletedTask;
} }
@ -310,13 +316,13 @@ namespace osu.Game.Online.Multiplayer
if (Room == null) if (Room == null)
return Task.CompletedTask; return Task.CompletedTask;
Schedule(() => Scheduler.Add(() =>
{ {
if (Room == null) if (Room == null)
return; return;
LoadRequested?.Invoke(); LoadRequested?.Invoke();
}); }, false);
return Task.CompletedTask; return Task.CompletedTask;
} }
@ -326,7 +332,7 @@ namespace osu.Game.Online.Multiplayer
if (Room == null) if (Room == null)
return Task.CompletedTask; return Task.CompletedTask;
Schedule(() => Scheduler.Add(() =>
{ {
if (Room == null) if (Room == null)
return; return;
@ -334,7 +340,7 @@ namespace osu.Game.Online.Multiplayer
PlayingUsers.AddRange(Room.Users.Where(u => u.State == MultiplayerUserState.Playing).Select(u => u.UserID)); PlayingUsers.AddRange(Room.Users.Where(u => u.State == MultiplayerUserState.Playing).Select(u => u.UserID));
MatchStarted?.Invoke(); MatchStarted?.Invoke();
}); }, false);
return Task.CompletedTask; return Task.CompletedTask;
} }
@ -344,13 +350,13 @@ namespace osu.Game.Online.Multiplayer
if (Room == null) if (Room == null)
return Task.CompletedTask; return Task.CompletedTask;
Schedule(() => Scheduler.Add(() =>
{ {
if (Room == null) if (Room == null)
return; return;
ResultsReady?.Invoke(); ResultsReady?.Invoke();
}); }, false);
return Task.CompletedTask; return Task.CompletedTask;
} }
@ -361,6 +367,31 @@ namespace osu.Game.Online.Multiplayer
/// <param name="multiplayerUser">The <see cref="MultiplayerRoomUser"/> to populate.</param> /// <param name="multiplayerUser">The <see cref="MultiplayerRoomUser"/> to populate.</param>
protected async Task PopulateUser(MultiplayerRoomUser multiplayerUser) => multiplayerUser.User ??= await userLookupCache.GetUserAsync(multiplayerUser.UserID); protected async Task PopulateUser(MultiplayerRoomUser multiplayerUser) => multiplayerUser.User ??= await userLookupCache.GetUserAsync(multiplayerUser.UserID);
/// <summary>
/// Retrieve a copy of users currently in the joined <see cref="Room"/> in a thread-safe manner.
/// This should be used whenever accessing users from outside of an Update thread context (ie. when not calling <see cref="Drawable.Schedule"/>).
/// </summary>
/// <returns>A copy of users in the current room, or null if unavailable.</returns>
private List<MultiplayerRoomUser>? getRoomUsers()
{
List<MultiplayerRoomUser>? users = null;
ManualResetEventSlim resetEvent = new ManualResetEventSlim();
// at some point we probably want to replace all these schedule calls with Room.LockForUpdate.
// for now, as this would require quite some consideration due to the number of accesses to the room instance,
// let's just add a manual schedule for the non-scheduled usages instead.
Scheduler.Add(() =>
{
users = Room?.Users.ToList();
resetEvent.Set();
}, false);
resetEvent.Wait(100);
return users;
}
/// <summary> /// <summary>
/// Updates the local room settings with the given <see cref="MultiplayerRoomSettings"/>. /// Updates the local room settings with the given <see cref="MultiplayerRoomSettings"/>.
/// </summary> /// </summary>
@ -373,7 +404,7 @@ namespace osu.Game.Online.Multiplayer
if (Room == null) if (Room == null)
return; return;
Schedule(() => Scheduler.Add(() =>
{ {
if (Room == null) if (Room == null)
return; return;
@ -394,7 +425,7 @@ namespace osu.Game.Online.Multiplayer
req.Success += res => updatePlaylist(settings, res); req.Success += res => updatePlaylist(settings, res);
api.Queue(req); api.Queue(req);
}); }, false);
} }
private void updatePlaylist(MultiplayerRoomSettings settings, APIBeatmapSet onlineSet) private void updatePlaylist(MultiplayerRoomSettings settings, APIBeatmapSet onlineSet)