2021-05-20 07:30:56 +00:00
|
|
|
// 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.
|
|
|
|
|
2022-10-05 05:31:12 +00:00
|
|
|
using System;
|
2022-02-15 04:40:58 +00:00
|
|
|
using System.Diagnostics;
|
2021-05-20 07:30:56 +00:00
|
|
|
using System.Threading.Tasks;
|
|
|
|
using Microsoft.AspNetCore.SignalR.Client;
|
|
|
|
using osu.Framework.Allocation;
|
|
|
|
using osu.Framework.Bindables;
|
|
|
|
using osu.Game.Online.API;
|
2022-05-26 09:03:41 +00:00
|
|
|
using osu.Game.Online.Multiplayer;
|
2021-05-20 07:30:56 +00:00
|
|
|
|
|
|
|
namespace osu.Game.Online.Spectator
|
|
|
|
{
|
|
|
|
public partial class OnlineSpectatorClient : SpectatorClient
|
|
|
|
{
|
|
|
|
private readonly string endpoint;
|
|
|
|
|
|
|
|
private IHubClientConnector? connector;
|
|
|
|
|
|
|
|
public override IBindable<bool> IsConnected { get; } = new BindableBool();
|
|
|
|
|
|
|
|
private HubConnection? connection => connector?.CurrentConnection;
|
|
|
|
|
|
|
|
public OnlineSpectatorClient(EndpointConfiguration endpoints)
|
|
|
|
{
|
|
|
|
endpoint = endpoints.SpectatorEndpointUrl;
|
|
|
|
}
|
|
|
|
|
|
|
|
[BackgroundDependencyLoader]
|
|
|
|
private void load(IAPIProvider api)
|
|
|
|
{
|
|
|
|
connector = api.GetHubConnector(nameof(SpectatorClient), endpoint);
|
|
|
|
|
|
|
|
if (connector != null)
|
|
|
|
{
|
|
|
|
connector.ConfigureConnection = connection =>
|
|
|
|
{
|
|
|
|
// until strong typed client support is added, each method must be manually bound
|
|
|
|
// (see https://github.com/dotnet/aspnetcore/issues/15198)
|
|
|
|
connection.On<int, SpectatorState>(nameof(ISpectatorClient.UserBeganPlaying), ((ISpectatorClient)this).UserBeganPlaying);
|
|
|
|
connection.On<int, FrameDataBundle>(nameof(ISpectatorClient.UserSentFrames), ((ISpectatorClient)this).UserSentFrames);
|
|
|
|
connection.On<int, SpectatorState>(nameof(ISpectatorClient.UserFinishedPlaying), ((ISpectatorClient)this).UserFinishedPlaying);
|
2022-12-20 20:23:50 +00:00
|
|
|
connection.On<int, long>(nameof(ISpectatorClient.UserScoreProcessed), ((ISpectatorClient)this).UserScoreProcessed);
|
2021-05-20 07:30:56 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
IsConnected.BindTo(connector.IsConnected);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-12 04:59:27 +00:00
|
|
|
protected override async Task BeginPlayingInternal(long? scoreToken, SpectatorState state)
|
2021-05-20 07:30:56 +00:00
|
|
|
{
|
|
|
|
if (!IsConnected.Value)
|
2022-05-26 09:03:41 +00:00
|
|
|
return;
|
2021-05-20 07:30:56 +00:00
|
|
|
|
2022-02-15 04:40:58 +00:00
|
|
|
Debug.Assert(connection != null);
|
|
|
|
|
2022-05-26 09:03:41 +00:00
|
|
|
try
|
|
|
|
{
|
2022-12-16 09:16:26 +00:00
|
|
|
await connection.InvokeAsync(nameof(ISpectatorServer.BeginPlaySession), scoreToken, state).ConfigureAwait(false);
|
2022-05-26 09:03:41 +00:00
|
|
|
}
|
2022-10-05 05:31:12 +00:00
|
|
|
catch (Exception exception)
|
2022-05-26 09:03:41 +00:00
|
|
|
{
|
|
|
|
if (exception.GetHubExceptionMessage() == HubClientConnector.SERVER_SHUTDOWN_MESSAGE)
|
2022-07-16 19:35:41 +00:00
|
|
|
{
|
|
|
|
Debug.Assert(connector != null);
|
|
|
|
|
2022-12-16 09:16:26 +00:00
|
|
|
await connector.Reconnect().ConfigureAwait(false);
|
|
|
|
await BeginPlayingInternal(scoreToken, state).ConfigureAwait(false);
|
2022-07-16 19:35:41 +00:00
|
|
|
}
|
|
|
|
|
2022-07-19 08:14:57 +00:00
|
|
|
// Exceptions can occur if, for instance, the locally played beatmap doesn't have a server-side counterpart.
|
|
|
|
// For now, let's ignore these so they don't cause unobserved exceptions to appear to the user (and sentry).
|
2022-05-26 09:03:41 +00:00
|
|
|
}
|
2021-05-20 07:30:56 +00:00
|
|
|
}
|
|
|
|
|
2022-02-23 17:19:37 +00:00
|
|
|
protected override Task SendFramesInternal(FrameDataBundle bundle)
|
2021-05-20 07:30:56 +00:00
|
|
|
{
|
|
|
|
if (!IsConnected.Value)
|
|
|
|
return Task.CompletedTask;
|
|
|
|
|
2022-02-15 04:40:58 +00:00
|
|
|
Debug.Assert(connection != null);
|
|
|
|
|
2022-07-29 03:24:53 +00:00
|
|
|
return connection.SendAsync(nameof(ISpectatorServer.SendFrameData), bundle);
|
2021-05-20 07:30:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
protected override Task EndPlayingInternal(SpectatorState state)
|
|
|
|
{
|
|
|
|
if (!IsConnected.Value)
|
|
|
|
return Task.CompletedTask;
|
|
|
|
|
2022-02-15 04:40:58 +00:00
|
|
|
Debug.Assert(connection != null);
|
|
|
|
|
2022-07-18 05:34:02 +00:00
|
|
|
return connection.InvokeAsync(nameof(ISpectatorServer.EndPlaySession), state);
|
2021-05-20 07:30:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
protected override Task WatchUserInternal(int userId)
|
|
|
|
{
|
|
|
|
if (!IsConnected.Value)
|
|
|
|
return Task.CompletedTask;
|
|
|
|
|
2022-02-15 04:40:58 +00:00
|
|
|
Debug.Assert(connection != null);
|
|
|
|
|
2022-07-18 05:34:02 +00:00
|
|
|
return connection.InvokeAsync(nameof(ISpectatorServer.StartWatchingUser), userId);
|
2021-05-20 07:30:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
protected override Task StopWatchingUserInternal(int userId)
|
|
|
|
{
|
|
|
|
if (!IsConnected.Value)
|
|
|
|
return Task.CompletedTask;
|
|
|
|
|
2022-02-15 04:40:58 +00:00
|
|
|
Debug.Assert(connection != null);
|
|
|
|
|
2022-07-18 05:34:02 +00:00
|
|
|
return connection.InvokeAsync(nameof(ISpectatorServer.EndWatchingUser), userId);
|
2021-05-20 07:30:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|