1
0
mirror of https://github.com/ppy/osu.git synced 2026-06-04 13:44:26 +08:00

Implement client-side disconnection flow

This commit is contained in:
Bartłomiej Dach
2023-11-13 14:35:07 +09:00
Unverified
parent bb2f38d189
commit 1c612e2e0c
12 changed files with 77 additions and 3 deletions
+2
View File
@@ -102,6 +102,8 @@ namespace osu.Game.Online
return Task.FromResult((PersistentEndpointClient)new HubClient(newConnection));
}
Task IHubClientConnector.Disconnect() => base.Disconnect();
protected override string ClientName { get; }
}
}
+5
View File
@@ -30,6 +30,11 @@ namespace osu.Game.Online
/// </summary>
public Action<HubConnection>? ConfigureConnection { get; set; }
/// <summary>
/// Forcefully disconnects the client from the server.
/// </summary>
Task Disconnect();
/// <summary>
/// Reconnect if already connected.
/// </summary>
+18
View File
@@ -0,0 +1,18 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Threading.Tasks;
namespace osu.Game.Online
{
/// <summary>
/// Common interface for clients of "stateful user hubs", i.e. server-side hubs
/// that preserve user state.
/// In the case of such hubs, concurrency constraints are enforced (only one client
/// can be connected at a time).
/// </summary>
public interface IStatefulUserHubClient
{
Task DisconnectRequested();
}
}
@@ -13,7 +13,7 @@ namespace osu.Game.Online.Multiplayer
/// <summary>
/// An interface defining a multiplayer client instance.
/// </summary>
public interface IMultiplayerClient
public interface IMultiplayerClient : IStatefulUserHubClient
{
/// <summary>
/// Signals that the room has changed state.
@@ -357,6 +357,8 @@ namespace osu.Game.Online.Multiplayer
public abstract Task ChangeBeatmapAvailability(BeatmapAvailability newBeatmapAvailability);
public abstract Task DisconnectInternal();
/// <summary>
/// Change the local user's mods in the currently joined room.
/// </summary>
@@ -876,5 +878,11 @@ namespace osu.Game.Online.Multiplayer
return tcs.Task;
}
Task IStatefulUserHubClient.DisconnectRequested()
{
Schedule(() => DisconnectInternal());
return Task.CompletedTask;
}
}
}
@@ -68,6 +68,7 @@ namespace osu.Game.Online.Multiplayer
connection.On<MultiplayerPlaylistItem>(nameof(IMultiplayerClient.PlaylistItemAdded), ((IMultiplayerClient)this).PlaylistItemAdded);
connection.On<long>(nameof(IMultiplayerClient.PlaylistItemRemoved), ((IMultiplayerClient)this).PlaylistItemRemoved);
connection.On<MultiplayerPlaylistItem>(nameof(IMultiplayerClient.PlaylistItemChanged), ((IMultiplayerClient)this).PlaylistItemChanged);
connection.On(nameof(IStatefulUserHubClient.DisconnectRequested), ((IMultiplayerClient)this).DisconnectRequested);
};
IsConnected.BindTo(connector.IsConnected);
@@ -255,6 +256,14 @@ namespace osu.Game.Online.Multiplayer
return connection.InvokeAsync(nameof(IMultiplayerServer.RemovePlaylistItem), playlistItemId);
}
public override Task DisconnectInternal()
{
if (connector == null)
return Task.CompletedTask;
return connector.Disconnect();
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
@@ -159,6 +159,8 @@ namespace osu.Game.Online
await Task.Run(connect, default).ConfigureAwait(false);
}
protected Task Disconnect() => disconnect(true);
private async Task disconnect(bool takeLock)
{
cancelExistingConnect();
@@ -8,7 +8,7 @@ namespace osu.Game.Online.Spectator
/// <summary>
/// An interface defining a spectator client instance.
/// </summary>
public interface ISpectatorClient
public interface ISpectatorClient : IStatefulUserHubClient
{
/// <summary>
/// Signals that a user has begun a new play session.
@@ -42,6 +42,7 @@ namespace osu.Game.Online.Spectator
connection.On<int, FrameDataBundle>(nameof(ISpectatorClient.UserSentFrames), ((ISpectatorClient)this).UserSentFrames);
connection.On<int, SpectatorState>(nameof(ISpectatorClient.UserFinishedPlaying), ((ISpectatorClient)this).UserFinishedPlaying);
connection.On<int, long>(nameof(ISpectatorClient.UserScoreProcessed), ((ISpectatorClient)this).UserScoreProcessed);
connection.On(nameof(IStatefulUserHubClient.DisconnectRequested), ((IStatefulUserHubClient)this).DisconnectRequested);
};
IsConnected.BindTo(connector.IsConnected);
@@ -113,5 +114,13 @@ namespace osu.Game.Online.Spectator
return connection.InvokeAsync(nameof(ISpectatorServer.EndWatchingUser), userId);
}
protected override Task DisconnectInternal()
{
if (connector == null)
return Task.CompletedTask;
return connector.Disconnect();
}
}
}
@@ -174,6 +174,12 @@ namespace osu.Game.Online.Spectator
return Task.CompletedTask;
}
Task IStatefulUserHubClient.DisconnectRequested()
{
Schedule(() => DisconnectInternal());
return Task.CompletedTask;
}
public void BeginPlaying(long? scoreToken, GameplayState state, Score score)
{
// This schedule is only here to match the one below in `EndPlaying`.
@@ -291,6 +297,8 @@ namespace osu.Game.Online.Spectator
protected abstract Task StopWatchingUserInternal(int userId);
protected abstract Task DisconnectInternal();
protected override void Update()
{
base.Update();
@@ -658,5 +658,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
PlayedAt = item.PlayedAt,
StarRating = item.Beatmap.StarRating,
};
public override Task DisconnectInternal()
{
isConnected.Value = false;
return Task.CompletedTask;
}
}
}
@@ -33,7 +33,8 @@ namespace osu.Game.Tests.Visual.Spectator
public int FrameSendAttempts { get; private set; }
public override IBindable<bool> IsConnected { get; } = new Bindable<bool>(true);
public override IBindable<bool> IsConnected => isConnected;
private readonly BindableBool isConnected = new BindableBool(true);
public IReadOnlyDictionary<int, ReplayFrame> LastReceivedUserFrames => lastReceivedUserFrames;
@@ -179,5 +180,11 @@ namespace osu.Game.Tests.Visual.Spectator
State = SpectatedUserState.Playing
});
}
protected override Task DisconnectInternal()
{
isConnected.Value = false;
return Task.CompletedTask;
}
}
}