diff --git a/osu.Game/Localisation/NotificationsStrings.cs b/osu.Game/Localisation/NotificationsStrings.cs
index 0d22d98eb1..1d6b69a007 100644
--- a/osu.Game/Localisation/NotificationsStrings.cs
+++ b/osu.Game/Localisation/NotificationsStrings.cs
@@ -146,14 +146,9 @@ Click to see what's new!", version);
public static LocalisableString FriendOffline(string info) => new TranslatableString(getKey(@"friend_offline"), @"Offline: {0}", info);
///
- /// "Connection to API was lost. Can't continue with online play."
+ /// "Connection to online services was interrupted. osu! will be operating with limited functionality."
///
- public static LocalisableString APIDisconnect => new TranslatableString(getKey(@"api_disconnect"), @"Connection to API was lost. Can't continue with online play.");
-
- ///
- /// "Connection to the multiplayer server was lost. Exiting multiplayer."
- ///
- public static LocalisableString MultiplayerDisconnect => new TranslatableString(getKey(@"multiplayer_disconnect"), @"Connection to the multiplayer server was lost. Exiting multiplayer.");
+ public static LocalisableString APIConnectionInterrupted => new TranslatableString(getKey(@"api_connection_interrupted"), @"Connection to online services was interrupted. osu! will be operating with limited functionality.");
///
/// "You have been logged out on this device due to a login to your account on another device."
diff --git a/osu.Game/Online/Multiplayer/MultiplayerClientExtensions.cs b/osu.Game/Online/Multiplayer/MultiplayerClientExtensions.cs
index 1cc5a8e70a..61824914b5 100644
--- a/osu.Game/Online/Multiplayer/MultiplayerClientExtensions.cs
+++ b/osu.Game/Online/Multiplayer/MultiplayerClientExtensions.cs
@@ -3,6 +3,7 @@
using System;
using System.Diagnostics;
+using System.Net.WebSockets;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;
using osu.Framework.Extensions.ExceptionExtensions;
@@ -20,13 +21,23 @@ namespace osu.Game.Online.Multiplayer
Debug.Assert(t.Exception != null);
Exception exception = t.Exception.AsSingular();
+ onError?.Invoke(exception);
+
+ if (exception is WebSocketException wse && wse.Message == @"The remote party closed the WebSocket connection without completing the close handshake.")
+ {
+ // OnlineStatusNotifier is already letting users know about interruptions to connections.
+ // Silence these because it gets very spammy otherwise.
+ return;
+ }
+
if (exception.GetHubExceptionMessage() is string message)
+ {
// Hub exceptions generally contain something we can show the user directly.
Logger.Log(message, level: LogLevel.Important);
- else
- Logger.Error(exception, $"Unobserved exception occurred via {nameof(FireAndForget)} call: {exception.Message}");
+ return;
+ }
- onError?.Invoke(exception);
+ Logger.Error(exception, $"Unobserved exception occurred via {nameof(FireAndForget)} call: {exception.Message}");
}
else
{
diff --git a/osu.Game/Online/OnlineStatusNotifier.cs b/osu.Game/Online/OnlineStatusNotifier.cs
index da58dc3d46..f4c33c67aa 100644
--- a/osu.Game/Online/OnlineStatusNotifier.cs
+++ b/osu.Game/Online/OnlineStatusNotifier.cs
@@ -17,6 +17,7 @@ using osu.Game.Online.Spectator;
using osu.Game.Overlays;
using osu.Game.Overlays.Notifications;
using osu.Game.Screens.OnlinePlay;
+using osu.Game.Screens.Play;
namespace osu.Game.Online
{
@@ -75,22 +76,16 @@ namespace osu.Game.Online
apiState.BindValueChanged(state =>
{
- if (state.NewValue == APIState.Online)
+ switch (state.NewValue)
{
- userNotified = false;
- return;
- }
+ case APIState.Online:
+ userNotified = false;
+ return;
- if (userNotified) return;
-
- if (state.NewValue == APIState.Offline && getCurrentScreen() is OnlinePlayScreen)
- {
- userNotified = true;
- notificationOverlay?.Post(new SimpleErrorNotification
- {
- Icon = FontAwesome.Solid.ExclamationCircle,
- Text = NotificationsStrings.APIDisconnect,
- });
+ case APIState.Offline:
+ if (getCurrentScreen() is OnlinePlayScreen)
+ notifyApiDisconnection();
+ break;
}
});
@@ -102,22 +97,37 @@ namespace osu.Game.Online
return;
}
- if (userNotified) return;
-
if (multiplayerClient.Room != null)
- {
- userNotified = true;
- notificationOverlay?.Post(new SimpleErrorNotification
- {
- Icon = FontAwesome.Solid.ExclamationCircle,
- Text = NotificationsStrings.MultiplayerDisconnect,
- });
- }
+ notifyApiDisconnection();
}));
- spectatorState.BindValueChanged(_ =>
+ spectatorState.BindValueChanged(connected => Schedule(() =>
{
- // TODO: handle spectator server failure somehow?
+ if (connected.NewValue)
+ {
+ userNotified = false;
+ return;
+ }
+
+ switch (getCurrentScreen())
+ {
+ case SpectatorPlayer: // obvious issues
+ case SubmittingPlayer: // replay sending issues
+ notifyApiDisconnection();
+ break;
+ }
+ }));
+ }
+
+ private void notifyApiDisconnection()
+ {
+ if (userNotified) return;
+
+ userNotified = true;
+ notificationOverlay?.Post(new SimpleErrorNotification
+ {
+ Icon = FontAwesome.Solid.ExclamationCircle,
+ Text = NotificationsStrings.APIConnectionInterrupted,
});
}