mirror of
https://github.com/ppy/osu.git
synced 2025-01-12 18:23:04 +08:00
Split online connectivity into OnlineSpectatorClient
This commit is contained in:
parent
6beeb7f7c4
commit
df80531a0a
89
osu.Game/Online/Spectator/OnlineSpectatorClient.cs
Normal file
89
osu.Game/Online/Spectator/OnlineSpectatorClient.cs
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.SignalR.Client;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Game.Online.API;
|
||||||
|
|
||||||
|
namespace osu.Game.Online.Spectator
|
||||||
|
{
|
||||||
|
public 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);
|
||||||
|
};
|
||||||
|
|
||||||
|
IsConnected.BindTo(connector.IsConnected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Task BeginPlayingInternal(SpectatorState state)
|
||||||
|
{
|
||||||
|
if (!IsConnected.Value)
|
||||||
|
return Task.CompletedTask;
|
||||||
|
|
||||||
|
return connection.SendAsync(nameof(ISpectatorServer.BeginPlaySession), state);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Task SendFramesInternal(FrameDataBundle data)
|
||||||
|
{
|
||||||
|
if (!IsConnected.Value)
|
||||||
|
return Task.CompletedTask;
|
||||||
|
|
||||||
|
return connection.SendAsync(nameof(ISpectatorServer.SendFrameData), data);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Task EndPlayingInternal(SpectatorState state)
|
||||||
|
{
|
||||||
|
if (!IsConnected.Value)
|
||||||
|
return Task.CompletedTask;
|
||||||
|
|
||||||
|
return connection.SendAsync(nameof(ISpectatorServer.EndPlaySession), state);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Task WatchUserInternal(int userId)
|
||||||
|
{
|
||||||
|
if (!IsConnected.Value)
|
||||||
|
return Task.CompletedTask;
|
||||||
|
|
||||||
|
return connection.SendAsync(nameof(ISpectatorServer.StartWatchingUser), userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Task StopWatchingUserInternal(int userId)
|
||||||
|
{
|
||||||
|
if (!IsConnected.Value)
|
||||||
|
return Task.CompletedTask;
|
||||||
|
|
||||||
|
return connection.SendAsync(nameof(ISpectatorServer.EndWatchingUser), userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,7 +7,6 @@ using System.Diagnostics;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Microsoft.AspNetCore.SignalR.Client;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -23,21 +22,18 @@ using osu.Game.Screens.Play;
|
|||||||
|
|
||||||
namespace osu.Game.Online.Spectator
|
namespace osu.Game.Online.Spectator
|
||||||
{
|
{
|
||||||
public class SpectatorClient : Component, ISpectatorClient
|
public abstract class SpectatorClient : Component, ISpectatorClient
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The maximum milliseconds between frame bundle sends.
|
/// The maximum milliseconds between frame bundle sends.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const double TIME_BETWEEN_SENDS = 200;
|
public const double TIME_BETWEEN_SENDS = 200;
|
||||||
|
|
||||||
private readonly string endpoint;
|
/// <summary>
|
||||||
|
/// Whether the <see cref="SpectatorClient"/> is currently connected.
|
||||||
[CanBeNull]
|
/// This is NOT thread safe and usage should be scheduled.
|
||||||
private IHubClientConnector connector;
|
/// </summary>
|
||||||
|
public abstract IBindable<bool> IsConnected { get; }
|
||||||
private readonly IBindable<bool> isConnected = new BindableBool();
|
|
||||||
|
|
||||||
private HubConnection connection => connector?.CurrentConnection;
|
|
||||||
|
|
||||||
private readonly List<int> watchingUsers = new List<int>();
|
private readonly List<int> watchingUsers = new List<int>();
|
||||||
|
|
||||||
@ -63,7 +59,7 @@ namespace osu.Game.Online.Spectator
|
|||||||
|
|
||||||
private readonly SpectatorState currentState = new SpectatorState();
|
private readonly SpectatorState currentState = new SpectatorState();
|
||||||
|
|
||||||
private bool isPlaying;
|
protected bool IsPlaying { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called whenever new frames arrive from the server.
|
/// Called whenever new frames arrive from the server.
|
||||||
@ -80,29 +76,10 @@ namespace osu.Game.Online.Spectator
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public event Action<int, SpectatorState> OnUserFinishedPlaying;
|
public event Action<int, SpectatorState> OnUserFinishedPlaying;
|
||||||
|
|
||||||
public SpectatorClient(EndpointConfiguration endpoints)
|
|
||||||
{
|
|
||||||
endpoint = endpoints.SpectatorEndpointUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(IAPIProvider api)
|
private void load()
|
||||||
{
|
{
|
||||||
connector = api.GetHubConnector(nameof(SpectatorClient), endpoint);
|
IsConnected.BindValueChanged(connected =>
|
||||||
|
|
||||||
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);
|
|
||||||
};
|
|
||||||
|
|
||||||
isConnected.BindTo(connector.IsConnected);
|
|
||||||
isConnected.BindValueChanged(connected =>
|
|
||||||
{
|
{
|
||||||
if (connected.NewValue)
|
if (connected.NewValue)
|
||||||
{
|
{
|
||||||
@ -120,8 +97,8 @@ namespace osu.Game.Online.Spectator
|
|||||||
WatchUser(userId);
|
WatchUser(userId);
|
||||||
|
|
||||||
// re-send state in case it wasn't received
|
// re-send state in case it wasn't received
|
||||||
if (isPlaying)
|
if (IsPlaying)
|
||||||
beginPlaying();
|
BeginPlayingInternal(currentState);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -133,7 +110,6 @@ namespace osu.Game.Online.Spectator
|
|||||||
}
|
}
|
||||||
}, true);
|
}, true);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Task ISpectatorClient.UserBeganPlaying(int userId, SpectatorState state)
|
Task ISpectatorClient.UserBeganPlaying(int userId, SpectatorState state)
|
||||||
{
|
{
|
||||||
@ -176,10 +152,10 @@ namespace osu.Game.Online.Spectator
|
|||||||
|
|
||||||
public void BeginPlaying(GameplayBeatmap beatmap, Score score)
|
public void BeginPlaying(GameplayBeatmap beatmap, Score score)
|
||||||
{
|
{
|
||||||
if (isPlaying)
|
if (IsPlaying)
|
||||||
throw new InvalidOperationException($"Cannot invoke {nameof(BeginPlaying)} when already playing");
|
throw new InvalidOperationException($"Cannot invoke {nameof(BeginPlaying)} when already playing");
|
||||||
|
|
||||||
isPlaying = true;
|
IsPlaying = true;
|
||||||
|
|
||||||
// transfer state at point of beginning play
|
// transfer state at point of beginning play
|
||||||
currentState.BeatmapID = beatmap.BeatmapInfo.OnlineBeatmapID;
|
currentState.BeatmapID = beatmap.BeatmapInfo.OnlineBeatmapID;
|
||||||
@ -189,36 +165,20 @@ namespace osu.Game.Online.Spectator
|
|||||||
currentBeatmap = beatmap.PlayableBeatmap;
|
currentBeatmap = beatmap.PlayableBeatmap;
|
||||||
currentScore = score;
|
currentScore = score;
|
||||||
|
|
||||||
beginPlaying();
|
BeginPlayingInternal(currentState);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void beginPlaying()
|
public void SendFrames(FrameDataBundle data) => lastSend = SendFramesInternal(data);
|
||||||
{
|
|
||||||
Debug.Assert(isPlaying);
|
|
||||||
|
|
||||||
if (!isConnected.Value) return;
|
|
||||||
|
|
||||||
connection.SendAsync(nameof(ISpectatorServer.BeginPlaySession), currentState);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SendFrames(FrameDataBundle data)
|
|
||||||
{
|
|
||||||
if (!isConnected.Value) return;
|
|
||||||
|
|
||||||
lastSend = connection.SendAsync(nameof(ISpectatorServer.SendFrameData), data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void EndPlaying()
|
public void EndPlaying()
|
||||||
{
|
{
|
||||||
isPlaying = false;
|
IsPlaying = false;
|
||||||
currentBeatmap = null;
|
currentBeatmap = null;
|
||||||
|
|
||||||
if (!isConnected.Value) return;
|
EndPlayingInternal(currentState);
|
||||||
|
|
||||||
connection.SendAsync(nameof(ISpectatorServer.EndPlaySession), currentState);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void WatchUser(int userId)
|
public void WatchUser(int userId)
|
||||||
{
|
{
|
||||||
lock (userLock)
|
lock (userLock)
|
||||||
{
|
{
|
||||||
@ -226,27 +186,31 @@ namespace osu.Game.Online.Spectator
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
watchingUsers.Add(userId);
|
watchingUsers.Add(userId);
|
||||||
|
|
||||||
if (!isConnected.Value)
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
connection.SendAsync(nameof(ISpectatorServer.StartWatchingUser), userId);
|
WatchUserInternal(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void StopWatchingUser(int userId)
|
public void StopWatchingUser(int userId)
|
||||||
{
|
{
|
||||||
lock (userLock)
|
lock (userLock)
|
||||||
{
|
{
|
||||||
watchingUsers.Remove(userId);
|
watchingUsers.Remove(userId);
|
||||||
|
|
||||||
if (!isConnected.Value)
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
connection.SendAsync(nameof(ISpectatorServer.EndWatchingUser), userId);
|
StopWatchingUserInternal(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected abstract Task BeginPlayingInternal(SpectatorState state);
|
||||||
|
|
||||||
|
protected abstract Task SendFramesInternal(FrameDataBundle data);
|
||||||
|
|
||||||
|
protected abstract Task EndPlayingInternal(SpectatorState state);
|
||||||
|
|
||||||
|
protected abstract Task WatchUserInternal(int userId);
|
||||||
|
|
||||||
|
protected abstract Task StopWatchingUserInternal(int userId);
|
||||||
|
|
||||||
private readonly Queue<LegacyReplayFrame> pendingFrames = new Queue<LegacyReplayFrame>();
|
private readonly Queue<LegacyReplayFrame> pendingFrames = new Queue<LegacyReplayFrame>();
|
||||||
|
|
||||||
private double lastSendTime;
|
private double lastSendTime;
|
||||||
|
@ -240,7 +240,7 @@ namespace osu.Game
|
|||||||
|
|
||||||
dependencies.CacheAs(API ??= new APIAccess(LocalConfig, endpoints, VersionHash));
|
dependencies.CacheAs(API ??= new APIAccess(LocalConfig, endpoints, VersionHash));
|
||||||
|
|
||||||
dependencies.CacheAs(spectatorClient = new SpectatorClient(endpoints));
|
dependencies.CacheAs(spectatorClient = new OnlineSpectatorClient(endpoints));
|
||||||
dependencies.CacheAs(multiplayerClient = new MultiplayerClient(endpoints));
|
dependencies.CacheAs(multiplayerClient = new MultiplayerClient(endpoints));
|
||||||
|
|
||||||
var defaultBeatmap = new DummyWorkingBeatmap(Audio, Textures);
|
var defaultBeatmap = new DummyWorkingBeatmap(Audio, Textures);
|
||||||
|
Loading…
Reference in New Issue
Block a user