Improve async and reconnect stability. Added support for websocket 1012
This commit is contained in:
@@ -51,14 +51,14 @@ namespace Discord.Commands
|
|||||||
.Description("Returns information about commands.")
|
.Description("Returns information about commands.")
|
||||||
.Do(async e =>
|
.Do(async e =>
|
||||||
{
|
{
|
||||||
Channel replyChannel = _config.HelpMode == HelpMode.Public ? e.Channel : await e.User.CreateChannel();
|
Channel replyChannel = _config.HelpMode == HelpMode.Public ? e.Channel : await e.User.CreateChannel().ConfigureAwait(false);
|
||||||
if (e.Args.Length > 0) //Show command help
|
if (e.Args.Length > 0) //Show command help
|
||||||
{
|
{
|
||||||
var map = _map.GetItem(string.Join(" ", e.Args));
|
var map = _map.GetItem(string.Join(" ", e.Args));
|
||||||
if (map != null)
|
if (map != null)
|
||||||
await ShowCommandHelp(map, e.User, e.Channel, replyChannel);
|
await ShowCommandHelp(map, e.User, e.Channel, replyChannel).ConfigureAwait(false);
|
||||||
else
|
else
|
||||||
await replyChannel.SendMessage("Unable to display help: Unknown command.");
|
await replyChannel.SendMessage("Unable to display help: Unknown command.").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else //Show general help
|
else //Show general help
|
||||||
await ShowGeneralHelp(e.User, e.Channel, replyChannel);
|
await ShowGeneralHelp(e.User, e.Channel, replyChannel);
|
||||||
|
|||||||
@@ -509,6 +509,9 @@
|
|||||||
<Compile Include="..\Discord.Net\Net\TimeoutException.cs">
|
<Compile Include="..\Discord.Net\Net\TimeoutException.cs">
|
||||||
<Link>Net\TimeoutException.cs</Link>
|
<Link>Net\TimeoutException.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="..\Discord.Net\Net\WebSocketException.cs">
|
||||||
|
<Link>Net\WebSockets\WebSocketException.cs</Link>
|
||||||
|
</Compile>
|
||||||
<Compile Include="..\Discord.Net\Net\WebSockets\GatewaySocket.cs">
|
<Compile Include="..\Discord.Net\Net\WebSockets\GatewaySocket.cs">
|
||||||
<Link>Net\WebSockets\GatewaySocket.cs</Link>
|
<Link>Net\WebSockets\GatewaySocket.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
|||||||
@@ -19,6 +19,6 @@
|
|||||||
/// <summary> C←S - Used to notify a client that they must reconnect to another gateway. </summary>
|
/// <summary> C←S - Used to notify a client that they must reconnect to another gateway. </summary>
|
||||||
Redirect = 7,
|
Redirect = 7,
|
||||||
/// <summary> C→S - Used to request all members that were withheld by large_threshold </summary>
|
/// <summary> C→S - Used to request all members that were withheld by large_threshold </summary>
|
||||||
RequestGuildMembers = 8
|
RequestGuildMembers = 99
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ namespace Discord.Legacy
|
|||||||
if (messages == null) throw new ArgumentNullException(nameof(messages));
|
if (messages == null) throw new ArgumentNullException(nameof(messages));
|
||||||
|
|
||||||
foreach (var message in messages)
|
foreach (var message in messages)
|
||||||
await message.Delete();
|
await message.Delete().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Obsolete("Use Channel.DownloadMessages")]
|
[Obsolete("Use Channel.DownloadMessages")]
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ namespace Discord
|
|||||||
Connected += async (s, e) =>
|
Connected += async (s, e) =>
|
||||||
{
|
{
|
||||||
ClientAPI.CancelToken = CancelToken;
|
ClientAPI.CancelToken = CancelToken;
|
||||||
await SendStatus();
|
await SendStatus().ConfigureAwait(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
//Extensibility
|
//Extensibility
|
||||||
@@ -250,7 +250,7 @@ namespace Discord
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Cache other stuff
|
//Cache other stuff
|
||||||
var regionsResponse = (await ClientAPI.Send(new GetVoiceRegionsRequest()));
|
var regionsResponse = (await ClientAPI.Send(new GetVoiceRegionsRequest()).ConfigureAwait(false));
|
||||||
_regions = regionsResponse.Select(x => new Region(x.Id, x.Name, x.Hostname, x.Port))
|
_regions = regionsResponse.Select(x => new Region(x.Id, x.Name, x.Hostname, x.Port))
|
||||||
.ToDictionary(x => x.Id);
|
.ToDictionary(x => x.Id);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ namespace Discord
|
|||||||
#region Invites
|
#region Invites
|
||||||
/// <summary> Gets all active (non-expired) invites to this server. </summary>
|
/// <summary> Gets all active (non-expired) invites to this server. </summary>
|
||||||
public async Task<IEnumerable<Invite>> GetInvites()
|
public async Task<IEnumerable<Invite>> GetInvites()
|
||||||
=> (await Server.GetInvites()).Where(x => x.Channel.Id == Id);
|
=> (await Server.GetInvites().ConfigureAwait(false)).Where(x => x.Channel.Id == Id);
|
||||||
|
|
||||||
/// <summary> Creates a new invite to this channel. </summary>
|
/// <summary> Creates a new invite to this channel. </summary>
|
||||||
/// <param name="maxAge"> Time (in seconds) until the invite expires. Set to null to never expire. </param>
|
/// <param name="maxAge"> Time (in seconds) until the invite expires. Set to null to never expire. </param>
|
||||||
|
|||||||
25
src/Discord.Net/Net/WebSocketException.cs
Normal file
25
src/Discord.Net/Net/WebSocketException.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Discord.Net
|
||||||
|
{
|
||||||
|
public class WebSocketException : Exception
|
||||||
|
{
|
||||||
|
public int Code { get; }
|
||||||
|
public string Reason { get; }
|
||||||
|
|
||||||
|
public WebSocketException(int code, string reason)
|
||||||
|
: base(GenerateMessage(code, reason))
|
||||||
|
{
|
||||||
|
Code = code;
|
||||||
|
Reason = reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GenerateMessage(int? code, string reason)
|
||||||
|
{
|
||||||
|
if (!String.IsNullOrEmpty(reason))
|
||||||
|
return $"Received close code {code}: {reason}";
|
||||||
|
else
|
||||||
|
return $"Received close code {code}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,8 +13,10 @@ namespace Discord.Net.WebSockets
|
|||||||
{
|
{
|
||||||
private int _lastSequence;
|
private int _lastSequence;
|
||||||
private string _sessionId;
|
private string _sessionId;
|
||||||
|
private string _token;
|
||||||
|
private int _reconnects;
|
||||||
|
|
||||||
public string Token { get; internal set; }
|
public string Token { get { return _token; } internal set { _token = value; _sessionId = null; } }
|
||||||
|
|
||||||
public GatewaySocket(DiscordClient client, JsonSerializer serializer, Logger logger)
|
public GatewaySocket(DiscordClient client, JsonSerializer serializer, Logger logger)
|
||||||
: base(client, serializer, logger)
|
: base(client, serializer, logger)
|
||||||
@@ -29,20 +31,21 @@ namespace Discord.Net.WebSockets
|
|||||||
public async Task Connect()
|
public async Task Connect()
|
||||||
{
|
{
|
||||||
await BeginConnect().ConfigureAwait(false);
|
await BeginConnect().ConfigureAwait(false);
|
||||||
SendIdentify(Token);
|
if (_sessionId == null)
|
||||||
|
SendIdentify(Token);
|
||||||
|
else
|
||||||
|
SendResume();
|
||||||
}
|
}
|
||||||
private async Task Redirect()
|
|
||||||
{
|
|
||||||
await BeginConnect().ConfigureAwait(false);
|
|
||||||
SendResume();
|
|
||||||
}
|
|
||||||
private async Task Reconnect()
|
private async Task Reconnect()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var cancelToken = ParentCancelToken.Value;
|
var cancelToken = ParentCancelToken.Value;
|
||||||
await Task.Delay(_client.Config.ReconnectDelay, cancelToken).ConfigureAwait(false);
|
if (_reconnects++ == 0)
|
||||||
while (!cancelToken.IsCancellationRequested)
|
await Task.Delay(_client.Config.ReconnectDelay, cancelToken).ConfigureAwait(false);
|
||||||
|
else
|
||||||
|
await Task.Delay(_client.Config.FailedReconnectDelay, cancelToken).ConfigureAwait(false);
|
||||||
|
while (!cancelToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -69,8 +72,15 @@ namespace Discord.Net.WebSockets
|
|||||||
tasks.Add(HeartbeatAsync(CancelToken));
|
tasks.Add(HeartbeatAsync(CancelToken));
|
||||||
await _taskManager.Start(tasks, _cancelTokenSource).ConfigureAwait(false);
|
await _taskManager.Start(tasks, _cancelTokenSource).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
protected override Task Cleanup()
|
||||||
|
{
|
||||||
|
var ex = _taskManager.Exception;
|
||||||
|
if (ex != null && (ex as WebSocketException)?.Code != 1012)
|
||||||
|
_sessionId = null; //Reset session unless close code 1012
|
||||||
|
return base.Cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
protected override async Task ProcessMessage(string json)
|
protected override async Task ProcessMessage(string json)
|
||||||
{
|
{
|
||||||
await base.ProcessMessage(json).ConfigureAwait(false);
|
await base.ProcessMessage(json).ConfigureAwait(false);
|
||||||
var msg = JsonConvert.DeserializeObject<WebSocketMessage>(json);
|
var msg = JsonConvert.DeserializeObject<WebSocketMessage>(json);
|
||||||
@@ -85,9 +95,10 @@ namespace Discord.Net.WebSockets
|
|||||||
JToken token = msg.Payload as JToken;
|
JToken token = msg.Payload as JToken;
|
||||||
if (msg.Type == "READY")
|
if (msg.Type == "READY")
|
||||||
{
|
{
|
||||||
var payload = token.ToObject<ReadyEvent>(_serializer);
|
_reconnects = 0;
|
||||||
|
var payload = token.ToObject<ReadyEvent>(_serializer);
|
||||||
_sessionId = payload.SessionId;
|
_sessionId = payload.SessionId;
|
||||||
_heartbeatInterval = payload.HeartbeatInterval;
|
_heartbeatInterval = payload.HeartbeatInterval;
|
||||||
}
|
}
|
||||||
else if (msg.Type == "RESUMED")
|
else if (msg.Type == "RESUMED")
|
||||||
{
|
{
|
||||||
@@ -106,7 +117,7 @@ namespace Discord.Net.WebSockets
|
|||||||
{
|
{
|
||||||
Host = payload.Url;
|
Host = payload.Url;
|
||||||
Logger.Info("Redirected to " + payload.Url);
|
Logger.Info("Redirected to " + payload.Url);
|
||||||
await Redirect().ConfigureAwait(false);
|
await Reconnect().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -83,13 +83,7 @@ namespace Discord.Net.WebSockets
|
|||||||
{
|
{
|
||||||
Exception ex;
|
Exception ex;
|
||||||
if (e is ClosedEventArgs)
|
if (e is ClosedEventArgs)
|
||||||
{
|
ex = new WebSocketException((e as ClosedEventArgs).Code, (e as ClosedEventArgs).Reason);
|
||||||
int code = (e as ClosedEventArgs).Code;
|
|
||||||
string reason = (e as ClosedEventArgs).Reason;
|
|
||||||
if (String.IsNullOrEmpty(reason))
|
|
||||||
reason = "No reason";
|
|
||||||
ex = new Exception($"Received close code {code}: {reason}");
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
ex = new Exception($"Connection lost");
|
ex = new Exception($"Connection lost");
|
||||||
_taskManager.SignalError(ex, isUnexpected: true);
|
_taskManager.SignalError(ex, isUnexpected: true);
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ namespace Discord.Net.WebSockets
|
|||||||
_cancelTokenSource = null;
|
_cancelTokenSource = null;
|
||||||
_connectedEvent.Reset();
|
_connectedEvent.Reset();
|
||||||
|
|
||||||
if (oldState == ConnectionState.Connected)
|
if (oldState == ConnectionState.Connecting || oldState == ConnectionState.Connected)
|
||||||
{
|
{
|
||||||
var ex = _taskManager.Exception;
|
var ex = _taskManager.Exception;
|
||||||
if (ex == null)
|
if (ex == null)
|
||||||
|
|||||||
Reference in New Issue
Block a user