diff --git a/osu.Game/Online/Multiplayer/IMultiplayerClient.cs b/osu.Game/Online/Multiplayer/IMultiplayerClient.cs
index 064065ab00..8f16d22c4c 100644
--- a/osu.Game/Online/Multiplayer/IMultiplayerClient.cs
+++ b/osu.Game/Online/Multiplayer/IMultiplayerClient.cs
@@ -31,6 +31,15 @@ namespace osu.Game.Online.Multiplayer
/// The user.
Task UserLeft(MultiplayerRoomUser user);
+ ///
+ /// Signals that a user has been kicked from the room.
+ ///
+ ///
+ /// This will also be sent to the user that was kicked.
+ ///
+ /// The user.
+ Task UserKicked(MultiplayerRoomUser user);
+
///
/// Signal that the host of the room has changed.
///
diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs
index 4607211cdf..57dbfbb507 100644
--- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs
+++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs
@@ -190,7 +190,8 @@ namespace osu.Game.Online.Multiplayer
return joinOrLeaveTaskChain.Add(async () =>
{
await scheduledReset.ConfigureAwait(false);
- await LeaveRoomInternal().ConfigureAwait(false);
+ if (Room != null)
+ await LeaveRoomInternal().ConfigureAwait(false);
});
}
@@ -389,6 +390,18 @@ namespace osu.Game.Online.Multiplayer
return Task.CompletedTask;
}
+ Task IMultiplayerClient.UserKicked(MultiplayerRoomUser user)
+ {
+ if (LocalUser == null)
+ return Task.CompletedTask;
+
+ if (user.Equals(LocalUser))
+ LeaveRoom();
+
+ // TODO: also inform users of the kick operation.
+ return ((IMultiplayerClient)this).UserLeft(user);
+ }
+
Task IMultiplayerClient.HostChanged(int userId)
{
if (Room == null)
diff --git a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs
index 55477a9fc7..c38a648a6a 100644
--- a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs
+++ b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs
@@ -50,6 +50,7 @@ namespace osu.Game.Online.Multiplayer
connection.On(nameof(IMultiplayerClient.RoomStateChanged), ((IMultiplayerClient)this).RoomStateChanged);
connection.On(nameof(IMultiplayerClient.UserJoined), ((IMultiplayerClient)this).UserJoined);
connection.On(nameof(IMultiplayerClient.UserLeft), ((IMultiplayerClient)this).UserLeft);
+ connection.On(nameof(IMultiplayerClient.UserKicked), ((IMultiplayerClient)this).UserKicked);
connection.On(nameof(IMultiplayerClient.HostChanged), ((IMultiplayerClient)this).HostChanged);
connection.On(nameof(IMultiplayerClient.SettingsChanged), ((IMultiplayerClient)this).SettingsChanged);
connection.On(nameof(IMultiplayerClient.UserStateChanged), ((IMultiplayerClient)this).UserStateChanged);
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs
index a8e44dd56c..1943ff668f 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs
@@ -274,7 +274,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
isConnected.BindValueChanged(connected =>
{
if (!connected.NewValue)
- Schedule(this.Exit);
+ handleRoomLost();
}, true);
currentRoom.BindValueChanged(room =>
@@ -284,7 +284,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
// the room has gone away.
// this could mean something happened during the join process, or an external connection issue occurred.
// one specific scenario is where the underlying room is created, but the signalr server returns an error during the join process. this triggers a PartRoom operation (see https://github.com/ppy/osu/blob/7654df94f6f37b8382be7dfcb4f674e03bd35427/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs#L97)
- Schedule(this.Exit);
+ handleRoomLost();
}
}, true);
}
@@ -448,9 +448,24 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
private void onRoomUpdated()
{
+ // may happen if the client is kicked or otherwise removed from the room.
+ if (client.Room == null)
+ {
+ handleRoomLost();
+ return;
+ }
+
Scheduler.AddOnce(UpdateMods);
}
+ private void handleRoomLost() => Schedule(() =>
+ {
+ if (this.IsCurrentScreen())
+ this.Exit();
+ else
+ ValidForResume = false;
+ });
+
private void onLoadRequested()
{
if (BeatmapAvailability.Value.State != DownloadState.LocallyAvailable)
diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs
index a28b4140ca..67b79d7390 100644
--- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs
+++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs
@@ -178,7 +178,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
Debug.Assert(Room != null);
- return ((IMultiplayerClient)this).UserLeft(Room.Users.Single(u => u.UserID == userId));
+ return ((IMultiplayerClient)this).UserKicked(Room.Users.Single(u => u.UserID == userId));
}
public override async Task ChangeSettings(MultiplayerRoomSettings settings)