diff --git a/osu.Game/Online/HubClientConnector.cs b/osu.Game/Online/HubClientConnector.cs
index 5c11eed381..261724e315 100644
--- a/osu.Game/Online/HubClientConnector.cs
+++ b/osu.Game/Online/HubClientConnector.cs
@@ -20,6 +20,8 @@ namespace osu.Game.Online
{
public class HubClientConnector : IHubClientConnector
{
+ public const string SERVER_SHUTDOWN_MESSAGE = "Server is shutting down.";
+
///
/// Invoked whenever a new hub connection is built, to configure it before it's started.
///
diff --git a/osu.Game/Online/Multiplayer/MultiplayerClientExtensions.cs b/osu.Game/Online/Multiplayer/MultiplayerClientExtensions.cs
index 4729765084..cda313bd0a 100644
--- a/osu.Game/Online/Multiplayer/MultiplayerClientExtensions.cs
+++ b/osu.Game/Online/Multiplayer/MultiplayerClientExtensions.cs
@@ -25,12 +25,7 @@ namespace osu.Game.Online.Multiplayer
Debug.Assert(exception != null);
- string message = exception is HubException
- // HubExceptions arrive with additional message context added, but we want to display the human readable message:
- // "An unexpected error occurred invoking 'AddPlaylistItem' on the server.InvalidStateException: Can't enqueue more than 3 items at once."
- // We generally use the message field for a user-parseable error (eventually to be replaced), so drop the first part for now.
- ? exception.Message.Substring(exception.Message.IndexOf(':') + 1).Trim()
- : exception.Message;
+ string message = exception.GetHubExceptionMessage() ?? exception.Message;
Logger.Log(message, level: LogLevel.Important);
onError?.Invoke(exception);
@@ -40,5 +35,16 @@ namespace osu.Game.Online.Multiplayer
onSuccess?.Invoke();
}
});
+
+ public static string? GetHubExceptionMessage(this Exception exception)
+ {
+ if (exception is HubException hubException)
+ // HubExceptions arrive with additional message context added, but we want to display the human readable message:
+ // "An unexpected error occurred invoking 'AddPlaylistItem' on the server.InvalidStateException: Can't enqueue more than 3 items at once."
+ // We generally use the message field for a user-parseable error (eventually to be replaced), so drop the first part for now.
+ return hubException.Message.Substring(exception.Message.IndexOf(':') + 1).Trim();
+
+ return null;
+ }
}
}
diff --git a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs
index 4dc23d8b85..a3423d4189 100644
--- a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs
+++ b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs
@@ -3,10 +3,12 @@
#nullable enable
+using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
+using Microsoft.AspNetCore.SignalR;
using Microsoft.AspNetCore.SignalR.Client;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@@ -71,14 +73,23 @@ namespace osu.Game.Online.Multiplayer
}
}
- protected override Task JoinRoom(long roomId, string? password = null)
+ protected override async Task JoinRoom(long roomId, string? password = null)
{
if (!IsConnected.Value)
- return Task.FromCanceled(new CancellationToken(true));
+ throw new OperationCanceledException();
Debug.Assert(connection != null);
- return connection.InvokeAsync(nameof(IMultiplayerServer.JoinRoomWithPassword), roomId, password ?? string.Empty);
+ try
+ {
+ return await connection.InvokeAsync(nameof(IMultiplayerServer.JoinRoomWithPassword), roomId, password ?? string.Empty);
+ }
+ catch (HubException exception)
+ {
+ if (exception.GetHubExceptionMessage() == HubClientConnector.SERVER_SHUTDOWN_MESSAGE)
+ connector?.Reconnect();
+ throw;
+ }
}
protected override Task LeaveRoomInternal()
diff --git a/osu.Game/Online/Spectator/OnlineSpectatorClient.cs b/osu.Game/Online/Spectator/OnlineSpectatorClient.cs
index 4d6ca0b311..0f77f723db 100644
--- a/osu.Game/Online/Spectator/OnlineSpectatorClient.cs
+++ b/osu.Game/Online/Spectator/OnlineSpectatorClient.cs
@@ -5,10 +5,12 @@
using System.Diagnostics;
using System.Threading.Tasks;
+using Microsoft.AspNetCore.SignalR;
using Microsoft.AspNetCore.SignalR.Client;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Game.Online.API;
+using osu.Game.Online.Multiplayer;
namespace osu.Game.Online.Spectator
{
@@ -47,14 +49,23 @@ namespace osu.Game.Online.Spectator
}
}
- protected override Task BeginPlayingInternal(SpectatorState state)
+ protected override async Task BeginPlayingInternal(SpectatorState state)
{
if (!IsConnected.Value)
- return Task.CompletedTask;
+ return;
Debug.Assert(connection != null);
- return connection.SendAsync(nameof(ISpectatorServer.BeginPlaySession), state);
+ try
+ {
+ await connection.SendAsync(nameof(ISpectatorServer.BeginPlaySession), state);
+ }
+ catch (HubException exception)
+ {
+ if (exception.GetHubExceptionMessage() == HubClientConnector.SERVER_SHUTDOWN_MESSAGE)
+ connector?.Reconnect();
+ throw;
+ }
}
protected override Task SendFramesInternal(FrameDataBundle bundle)