@@ -6,6 +6,12 @@ namespace Discord.Rpc | |||||
public partial class DiscordRpcClient | public partial class DiscordRpcClient | ||||
{ | { | ||||
//General | //General | ||||
public event Func<Task> Connecting | |||||
{ | |||||
add { _connectingEvent.Add(value); } | |||||
remove { _connectingEvent.Remove(value); } | |||||
} | |||||
private readonly AsyncEvent<Func<Task>> _connectingEvent = new AsyncEvent<Func<Task>>(); | |||||
public event Func<Task> Connected | public event Func<Task> Connected | ||||
{ | { | ||||
add { _connectedEvent.Add(value); } | add { _connectedEvent.Add(value); } | ||||
@@ -29,7 +29,7 @@ namespace Discord.Rpc | |||||
public RestApplication ApplicationInfo { get; private set; } | public RestApplication ApplicationInfo { get; private set; } | ||||
/// <summary> Creates a new RPC discord client. </summary> | /// <summary> Creates a new RPC discord client. </summary> | ||||
public DiscordRpcClient(string clientId, string origin) | |||||
public DiscordRpcClient(string clientId, string origin) | |||||
: this(clientId, origin, new DiscordRpcConfig()) { } | : this(clientId, origin, new DiscordRpcConfig()) { } | ||||
/// <summary> Creates a new RPC discord client. </summary> | /// <summary> Creates a new RPC discord client. </summary> | ||||
public DiscordRpcClient(string clientId, string origin, DiscordRpcConfig config) | public DiscordRpcClient(string clientId, string origin, DiscordRpcConfig config) | ||||
@@ -38,8 +38,9 @@ namespace Discord.Rpc | |||||
_stateLock = new SemaphoreSlim(1, 1); | _stateLock = new SemaphoreSlim(1, 1); | ||||
_authorizeLock = new SemaphoreSlim(1, 1); | _authorizeLock = new SemaphoreSlim(1, 1); | ||||
_rpcLogger = LogManager.CreateLogger("RPC"); | _rpcLogger = LogManager.CreateLogger("RPC"); | ||||
_connection = new ConnectionManager(_stateLock, _rpcLogger, config.ConnectionTimeout, | |||||
OnConnectingAsync, OnDisconnectingAsync, x => ApiClient.Disconnected += x); | |||||
_connection = new ConnectionManager(_stateLock, _rpcLogger, config.ConnectionTimeout, | |||||
ConnectAsync, DisconnectAsync, x => ApiClient.Disconnected += x); | |||||
_connection.Connecting += () => _connectingEvent.InvokeAsync(); | |||||
_connection.Connected += () => _connectedEvent.InvokeAsync(); | _connection.Connected += () => _connectedEvent.InvokeAsync(); | ||||
_connection.Disconnected += (ex, recon) => _disconnectedEvent.InvokeAsync(ex); | _connection.Disconnected += (ex, recon) => _disconnectedEvent.InvokeAsync(ex); | ||||
@@ -49,7 +50,7 @@ namespace Discord.Rpc | |||||
_rpcLogger.WarningAsync(e.ErrorContext.Error).GetAwaiter().GetResult(); | _rpcLogger.WarningAsync(e.ErrorContext.Error).GetAwaiter().GetResult(); | ||||
e.ErrorContext.Handled = true; | e.ErrorContext.Handled = true; | ||||
}; | }; | ||||
ApiClient.SentRpcMessage += async opCode => await _rpcLogger.DebugAsync($"Sent {opCode}").ConfigureAwait(false); | ApiClient.SentRpcMessage += async opCode => await _rpcLogger.DebugAsync($"Sent {opCode}").ConfigureAwait(false); | ||||
ApiClient.ReceivedRpcEvent += ProcessMessageAsync; | ApiClient.ReceivedRpcEvent += ProcessMessageAsync; | ||||
} | } | ||||
@@ -68,14 +69,14 @@ namespace Discord.Rpc | |||||
public Task StartAsync() => _connection.StartAsync(); | public Task StartAsync() => _connection.StartAsync(); | ||||
public Task StopAsync() => _connection.StopAsync(); | public Task StopAsync() => _connection.StopAsync(); | ||||
private async Task OnConnectingAsync() | |||||
private async Task ConnectAsync() | |||||
{ | { | ||||
await _rpcLogger.DebugAsync("Connecting ApiClient").ConfigureAwait(false); | await _rpcLogger.DebugAsync("Connecting ApiClient").ConfigureAwait(false); | ||||
await ApiClient.ConnectAsync().ConfigureAwait(false); | await ApiClient.ConnectAsync().ConfigureAwait(false); | ||||
await _connection.WaitAsync().ConfigureAwait(false); | await _connection.WaitAsync().ConfigureAwait(false); | ||||
} | } | ||||
private async Task OnDisconnectingAsync(Exception ex) | |||||
private async Task DisconnectAsync(Exception ex) | |||||
{ | { | ||||
await _rpcLogger.DebugAsync("Disconnecting ApiClient").ConfigureAwait(false); | await _rpcLogger.DebugAsync("Disconnecting ApiClient").ConfigureAwait(false); | ||||
await ApiClient.DisconnectAsync().ConfigureAwait(false); | await ApiClient.DisconnectAsync().ConfigureAwait(false); | ||||
@@ -313,7 +314,7 @@ namespace Discord.Rpc | |||||
ApplicationInfo = RestApplication.Create(this, response.Application); | ApplicationInfo = RestApplication.Create(this, response.Application); | ||||
Scopes = response.Scopes; | Scopes = response.Scopes; | ||||
TokenExpiresAt = response.Expires; | TokenExpiresAt = response.Expires; | ||||
var __ = _connection.CompleteAsync(); | var __ = _connection.CompleteAsync(); | ||||
await _rpcLogger.InfoAsync("Ready").ConfigureAwait(false); | await _rpcLogger.InfoAsync("Ready").ConfigureAwait(false); | ||||
} | } | ||||
@@ -8,6 +8,8 @@ namespace Discord | |||||
{ | { | ||||
internal class ConnectionManager | internal class ConnectionManager | ||||
{ | { | ||||
public event Func<Task> Connecting { add { _connectingEvent.Add(value); } remove { _connectingEvent.Remove(value); } } | |||||
private readonly AsyncEvent<Func<Task>> _connectingEvent = new AsyncEvent<Func<Task>>(); | |||||
public event Func<Task> Connected { add { _connectedEvent.Add(value); } remove { _connectedEvent.Remove(value); } } | public event Func<Task> Connected { add { _connectedEvent.Add(value); } remove { _connectedEvent.Remove(value); } } | ||||
private readonly AsyncEvent<Func<Task>> _connectedEvent = new AsyncEvent<Func<Task>>(); | private readonly AsyncEvent<Func<Task>> _connectedEvent = new AsyncEvent<Func<Task>>(); | ||||
public event Func<Exception, bool, Task> Disconnected { add { _disconnectedEvent.Add(value); } remove { _disconnectedEvent.Remove(value); } } | public event Func<Exception, bool, Task> Disconnected { add { _disconnectedEvent.Add(value); } remove { _disconnectedEvent.Remove(value); } } | ||||
@@ -16,8 +18,8 @@ namespace Discord | |||||
private readonly SemaphoreSlim _stateLock; | private readonly SemaphoreSlim _stateLock; | ||||
private readonly Logger _logger; | private readonly Logger _logger; | ||||
private readonly int _connectionTimeout; | private readonly int _connectionTimeout; | ||||
private readonly Func<Task> _onConnecting; | |||||
private readonly Func<Exception, Task> _onDisconnecting; | |||||
private readonly Func<Task> _connect; | |||||
private readonly Func<Exception, Task> _disconnect; | |||||
private TaskCompletionSource<bool> _connectionPromise, _readyPromise; | private TaskCompletionSource<bool> _connectionPromise, _readyPromise; | ||||
private CancellationTokenSource _combinedCancelToken, _reconnectCancelToken, _connectionCancelToken; | private CancellationTokenSource _combinedCancelToken, _reconnectCancelToken, _connectionCancelToken; | ||||
@@ -26,14 +28,14 @@ namespace Discord | |||||
public ConnectionState State { get; private set; } | public ConnectionState State { get; private set; } | ||||
public CancellationToken CancelToken { get; private set; } | public CancellationToken CancelToken { get; private set; } | ||||
internal ConnectionManager(SemaphoreSlim stateLock, Logger logger, int connectionTimeout, | |||||
Func<Task> onConnecting, Func<Exception, Task> onDisconnecting, Action<Func<Exception, Task>> clientDisconnectHandler) | |||||
internal ConnectionManager(SemaphoreSlim stateLock, Logger logger, int connectionTimeout, | |||||
Func<Task> connect, Func<Exception, Task> disconnect, Action<Func<Exception, Task>> clientDisconnectHandler) | |||||
{ | { | ||||
_stateLock = stateLock; | _stateLock = stateLock; | ||||
_logger = logger; | _logger = logger; | ||||
_connectionTimeout = connectionTimeout; | _connectionTimeout = connectionTimeout; | ||||
_onConnecting = onConnecting; | |||||
_onDisconnecting = onDisconnecting; | |||||
_connect = connect; | |||||
_disconnect = disconnect; | |||||
clientDisconnectHandler(ex => | clientDisconnectHandler(ex => | ||||
{ | { | ||||
@@ -67,16 +69,16 @@ namespace Discord | |||||
try | try | ||||
{ | { | ||||
await ConnectAsync(reconnectCancelToken).ConfigureAwait(false); | await ConnectAsync(reconnectCancelToken).ConfigureAwait(false); | ||||
nextReconnectDelay = 1000; //Reset delay | |||||
nextReconnectDelay = 1000; //Reset delay | |||||
await _connectionPromise.Task.ConfigureAwait(false); | await _connectionPromise.Task.ConfigureAwait(false); | ||||
} | } | ||||
catch (OperationCanceledException ex) | |||||
{ | |||||
catch (OperationCanceledException ex) | |||||
{ | |||||
Cancel(); //In case this exception didn't come from another Error call | Cancel(); //In case this exception didn't come from another Error call | ||||
await DisconnectAsync(ex, !reconnectCancelToken.IsCancellationRequested).ConfigureAwait(false); | await DisconnectAsync(ex, !reconnectCancelToken.IsCancellationRequested).ConfigureAwait(false); | ||||
} | } | ||||
catch (Exception ex) | |||||
{ | |||||
catch (Exception ex) | |||||
{ | |||||
Error(ex); //In case this exception didn't come from another Error call | Error(ex); //In case this exception didn't come from another Error call | ||||
if (!reconnectCancelToken.IsCancellationRequested) | if (!reconnectCancelToken.IsCancellationRequested) | ||||
{ | { | ||||
@@ -120,9 +122,11 @@ namespace Discord | |||||
_connectionPromise = new TaskCompletionSource<bool>(); | _connectionPromise = new TaskCompletionSource<bool>(); | ||||
State = ConnectionState.Connecting; | State = ConnectionState.Connecting; | ||||
await _logger.InfoAsync("Connecting").ConfigureAwait(false); | await _logger.InfoAsync("Connecting").ConfigureAwait(false); | ||||
try | try | ||||
{ | { | ||||
await _connectingEvent.InvokeAsync().ConfigureAwait(false); | |||||
var readyPromise = new TaskCompletionSource<bool>(); | var readyPromise = new TaskCompletionSource<bool>(); | ||||
_readyPromise = readyPromise; | _readyPromise = readyPromise; | ||||
@@ -138,7 +142,7 @@ namespace Discord | |||||
catch (OperationCanceledException) { } | catch (OperationCanceledException) { } | ||||
}); | }); | ||||
await _onConnecting().ConfigureAwait(false); | |||||
await _connect().ConfigureAwait(false); | |||||
await _logger.InfoAsync("Connected").ConfigureAwait(false); | await _logger.InfoAsync("Connected").ConfigureAwait(false); | ||||
State = ConnectionState.Connected; | State = ConnectionState.Connected; | ||||
@@ -157,7 +161,7 @@ namespace Discord | |||||
State = ConnectionState.Disconnecting; | State = ConnectionState.Disconnecting; | ||||
await _logger.InfoAsync("Disconnecting").ConfigureAwait(false); | await _logger.InfoAsync("Disconnecting").ConfigureAwait(false); | ||||
await _onDisconnecting(ex).ConfigureAwait(false); | |||||
await _disconnect(ex).ConfigureAwait(false); | |||||
await _logger.InfoAsync("Disconnected").ConfigureAwait(false); | await _logger.InfoAsync("Disconnected").ConfigureAwait(false); | ||||
State = ConnectionState.Disconnected; | State = ConnectionState.Disconnected; | ||||
@@ -207,4 +211,4 @@ namespace Discord | |||||
} | } | ||||
} | } | ||||
} | } | ||||
} | |||||
} |
@@ -7,6 +7,12 @@ namespace Discord.WebSocket | |||||
public partial class DiscordSocketClient | public partial class DiscordSocketClient | ||||
{ | { | ||||
//General | //General | ||||
public event Func<Task> Connecting | |||||
{ | |||||
add { _connectingEvent.Add(value); } | |||||
remove { _connectingEvent.Remove(value); } | |||||
} | |||||
private readonly AsyncEvent<Func<Task>> _connectingEvent = new AsyncEvent<Func<Task>>(); | |||||
public event Func<Task> Connected | public event Func<Task> Connected | ||||
{ | { | ||||
add { _connectedEvent.Add(value); } | add { _connectedEvent.Add(value); } | ||||
@@ -63,9 +63,9 @@ namespace Discord.WebSocket | |||||
public new SocketSelfUser CurrentUser { get => base.CurrentUser as SocketSelfUser; private set => base.CurrentUser = value; } | public new SocketSelfUser CurrentUser { get => base.CurrentUser as SocketSelfUser; private set => base.CurrentUser = value; } | ||||
public IReadOnlyCollection<SocketGuild> Guilds => State.Guilds; | public IReadOnlyCollection<SocketGuild> Guilds => State.Guilds; | ||||
public IReadOnlyCollection<ISocketPrivateChannel> PrivateChannels => State.PrivateChannels; | public IReadOnlyCollection<ISocketPrivateChannel> PrivateChannels => State.PrivateChannels; | ||||
public IReadOnlyCollection<SocketDMChannel> DMChannels | |||||
public IReadOnlyCollection<SocketDMChannel> DMChannels | |||||
=> State.PrivateChannels.Select(x => x as SocketDMChannel).Where(x => x != null).ToImmutableArray(); | => State.PrivateChannels.Select(x => x as SocketDMChannel).Where(x => x != null).ToImmutableArray(); | ||||
public IReadOnlyCollection<SocketGroupChannel> GroupChannels | |||||
public IReadOnlyCollection<SocketGroupChannel> GroupChannels | |||||
=> State.PrivateChannels.Select(x => x as SocketGroupChannel).Where(x => x != null).ToImmutableArray(); | => State.PrivateChannels.Select(x => x as SocketGroupChannel).Where(x => x != null).ToImmutableArray(); | ||||
public IReadOnlyCollection<RestVoiceRegion> VoiceRegions => _voiceRegions.ToReadOnlyCollection(); | public IReadOnlyCollection<RestVoiceRegion> VoiceRegions => _voiceRegions.ToReadOnlyCollection(); | ||||
@@ -90,11 +90,12 @@ namespace Discord.WebSocket | |||||
_stateLock = new SemaphoreSlim(1, 1); | _stateLock = new SemaphoreSlim(1, 1); | ||||
_gatewayLogger = LogManager.CreateLogger(ShardId == 0 && TotalShards == 1 ? "Gateway" : $"Shard #{ShardId}"); | _gatewayLogger = LogManager.CreateLogger(ShardId == 0 && TotalShards == 1 ? "Gateway" : $"Shard #{ShardId}"); | ||||
_connection = new ConnectionManager(_stateLock, _gatewayLogger, config.ConnectionTimeout, | |||||
OnConnectingAsync, OnDisconnectingAsync, x => ApiClient.Disconnected += x); | |||||
_connection = new ConnectionManager(_stateLock, _gatewayLogger, config.ConnectionTimeout, | |||||
ConnectAsync, DisconnectAsync, x => ApiClient.Disconnected += x); | |||||
_connection.Connecting += () => TimedInvokeAsync(_connectingEvent, nameof(Connecting)); | |||||
_connection.Connected += () => TimedInvokeAsync(_connectedEvent, nameof(Connected)); | _connection.Connected += () => TimedInvokeAsync(_connectedEvent, nameof(Connected)); | ||||
_connection.Disconnected += (ex, recon) => TimedInvokeAsync(_disconnectedEvent, nameof(Disconnected), ex); | _connection.Disconnected += (ex, recon) => TimedInvokeAsync(_disconnectedEvent, nameof(Disconnected), ex); | ||||
_nextAudioId = 1; | _nextAudioId = 1; | ||||
_connectionGroupLock = groupLock; | _connectionGroupLock = groupLock; | ||||
_parentClient = parentClient; | _parentClient = parentClient; | ||||
@@ -105,7 +106,7 @@ namespace Discord.WebSocket | |||||
_gatewayLogger.WarningAsync("Serializer Error", e.ErrorContext.Error).GetAwaiter().GetResult(); | _gatewayLogger.WarningAsync("Serializer Error", e.ErrorContext.Error).GetAwaiter().GetResult(); | ||||
e.ErrorContext.Handled = true; | e.ErrorContext.Handled = true; | ||||
}; | }; | ||||
ApiClient.SentGatewayMessage += async opCode => await _gatewayLogger.DebugAsync($"Sent {opCode}").ConfigureAwait(false); | ApiClient.SentGatewayMessage += async opCode => await _gatewayLogger.DebugAsync($"Sent {opCode}").ConfigureAwait(false); | ||||
ApiClient.ReceivedGatewayEvent += ProcessMessageAsync; | ApiClient.ReceivedGatewayEvent += ProcessMessageAsync; | ||||
@@ -137,7 +138,7 @@ namespace Discord.WebSocket | |||||
ApiClient.Dispose(); | ApiClient.Dispose(); | ||||
} | } | ||||
} | } | ||||
internal override async Task OnLoginAsync(TokenType tokenType, string token) | internal override async Task OnLoginAsync(TokenType tokenType, string token) | ||||
{ | { | ||||
if (_parentClient == null) | if (_parentClient == null) | ||||
@@ -155,12 +156,12 @@ namespace Discord.WebSocket | |||||
_voiceRegions = ImmutableDictionary.Create<string, RestVoiceRegion>(); | _voiceRegions = ImmutableDictionary.Create<string, RestVoiceRegion>(); | ||||
} | } | ||||
public async Task StartAsync() | |||||
public async Task StartAsync() | |||||
=> await _connection.StartAsync().ConfigureAwait(false); | => await _connection.StartAsync().ConfigureAwait(false); | ||||
public async Task StopAsync() | |||||
public async Task StopAsync() | |||||
=> await _connection.StopAsync().ConfigureAwait(false); | => await _connection.StopAsync().ConfigureAwait(false); | ||||
private async Task OnConnectingAsync() | |||||
private async Task ConnectAsync() | |||||
{ | { | ||||
if (_connectionGroupLock != null) | if (_connectionGroupLock != null) | ||||
await _connectionGroupLock.WaitAsync(_connection.CancelToken).ConfigureAwait(false); | await _connectionGroupLock.WaitAsync(_connection.CancelToken).ConfigureAwait(false); | ||||
@@ -182,11 +183,11 @@ namespace Discord.WebSocket | |||||
//Wait for READY | //Wait for READY | ||||
await _connection.WaitAsync().ConfigureAwait(false); | await _connection.WaitAsync().ConfigureAwait(false); | ||||
await _gatewayLogger.DebugAsync("Sending Status").ConfigureAwait(false); | await _gatewayLogger.DebugAsync("Sending Status").ConfigureAwait(false); | ||||
await SendStatusAsync().ConfigureAwait(false); | await SendStatusAsync().ConfigureAwait(false); | ||||
} | } | ||||
finally | |||||
finally | |||||
{ | { | ||||
if (_connectionGroupLock != null) | if (_connectionGroupLock != null) | ||||
{ | { | ||||
@@ -195,7 +196,7 @@ namespace Discord.WebSocket | |||||
} | } | ||||
} | } | ||||
} | } | ||||
private async Task OnDisconnectingAsync(Exception ex) | |||||
private async Task DisconnectAsync(Exception ex) | |||||
{ | { | ||||
await _gatewayLogger.DebugAsync("Disconnecting ApiClient").ConfigureAwait(false); | await _gatewayLogger.DebugAsync("Disconnecting ApiClient").ConfigureAwait(false); | ||||
@@ -232,7 +233,7 @@ namespace Discord.WebSocket | |||||
/// <inheritdoc /> | /// <inheritdoc /> | ||||
public async Task<RestApplication> GetApplicationInfoAsync() | public async Task<RestApplication> GetApplicationInfoAsync() | ||||
{ | |||||
{ | |||||
return _applicationInfo ?? (_applicationInfo = await ClientHelper.GetApplicationInfoAsync(this, new RequestOptions())); | return _applicationInfo ?? (_applicationInfo = await ClientHelper.GetApplicationInfoAsync(this, new RequestOptions())); | ||||
} | } | ||||
@@ -391,7 +392,7 @@ namespace Discord.WebSocket | |||||
if (seq != null) | if (seq != null) | ||||
_lastSeq = seq.Value; | _lastSeq = seq.Value; | ||||
_lastMessageTime = Environment.TickCount; | _lastMessageTime = Environment.TickCount; | ||||
try | try | ||||
{ | { | ||||
switch (opCode) | switch (opCode) | ||||
@@ -407,7 +408,7 @@ namespace Discord.WebSocket | |||||
case GatewayOpCode.Heartbeat: | case GatewayOpCode.Heartbeat: | ||||
{ | { | ||||
await _gatewayLogger.DebugAsync("Received Heartbeat").ConfigureAwait(false); | await _gatewayLogger.DebugAsync("Received Heartbeat").ConfigureAwait(false); | ||||
await ApiClient.SendHeartbeatAsync(_lastSeq).ConfigureAwait(false); | await ApiClient.SendHeartbeatAsync(_lastSeq).ConfigureAwait(false); | ||||
} | } | ||||
break; | break; | ||||
@@ -498,7 +499,7 @@ namespace Discord.WebSocket | |||||
} | } | ||||
else if (_connection.CancelToken.IsCancellationRequested) | else if (_connection.CancelToken.IsCancellationRequested) | ||||
return; | return; | ||||
await TimedInvokeAsync(_readyEvent, nameof(Ready)).ConfigureAwait(false); | await TimedInvokeAsync(_readyEvent, nameof(Ready)).ConfigureAwait(false); | ||||
await _gatewayLogger.InfoAsync("Ready").ConfigureAwait(false); | await _gatewayLogger.InfoAsync("Ready").ConfigureAwait(false); | ||||
}); | }); | ||||
@@ -537,7 +538,7 @@ namespace Discord.WebSocket | |||||
if (guild != null) | if (guild != null) | ||||
{ | { | ||||
guild.Update(State, data); | guild.Update(State, data); | ||||
if (_unavailableGuildCount != 0) | if (_unavailableGuildCount != 0) | ||||
_unavailableGuildCount--; | _unavailableGuildCount--; | ||||
await GuildAvailableAsync(guild).ConfigureAwait(false); | await GuildAvailableAsync(guild).ConfigureAwait(false); | ||||
@@ -1050,7 +1051,7 @@ namespace Discord.WebSocket | |||||
SocketUser user = guild.GetUser(data.User.Id); | SocketUser user = guild.GetUser(data.User.Id); | ||||
if (user == null) | if (user == null) | ||||
user = SocketUnknownUser.Create(this, State, data.User); | |||||
user = SocketUnknownUser.Create(this, State, data.User); | |||||
await TimedInvokeAsync(_userBannedEvent, nameof(UserBanned), user, guild).ConfigureAwait(false); | await TimedInvokeAsync(_userBannedEvent, nameof(UserBanned), user, guild).ConfigureAwait(false); | ||||
} | } | ||||
else | else | ||||
@@ -1348,7 +1349,7 @@ namespace Discord.WebSocket | |||||
await TimedInvokeAsync(_userUpdatedEvent, nameof(UserUpdated), globalBefore, user).ConfigureAwait(false); | await TimedInvokeAsync(_userUpdatedEvent, nameof(UserUpdated), globalBefore, user).ConfigureAwait(false); | ||||
} | } | ||||
} | } | ||||
var before = user.Clone(); | var before = user.Clone(); | ||||
user.Update(State, data, true); | user.Update(State, data, true); | ||||
await TimedInvokeAsync(_guildMemberUpdatedEvent, nameof(GuildMemberUpdated), before, user).ConfigureAwait(false); | await TimedInvokeAsync(_guildMemberUpdatedEvent, nameof(GuildMemberUpdated), before, user).ConfigureAwait(false); | ||||