1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-22 05:49:55 +08:00

Merge pull request #36349 from peppy/spectator-connection-disconnect-notify

Fix notification spam on websocket connection handshake failures
This commit is contained in:
Bartłomiej Dach
2026-01-15 13:06:19 +01:00
committed by GitHub
Unverified
3 changed files with 52 additions and 36 deletions
@@ -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);
/// <summary>
/// "Connection to API was lost. Can't continue with online play."
/// "Connection to online services was interrupted. osu! will be operating with limited functionality."
/// </summary>
public static LocalisableString APIDisconnect => new TranslatableString(getKey(@"api_disconnect"), @"Connection to API was lost. Can't continue with online play.");
/// <summary>
/// "Connection to the multiplayer server was lost. Exiting multiplayer."
/// </summary>
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.");
/// <summary>
/// "You have been logged out on this device due to a login to your account on another device."
@@ -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
{
+36 -26
View File
@@ -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,
});
}