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, }); }