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