Browse Source

Improved redirect opcode support, fixed connect deadlock on unstable connections

tags/docs-0.9
Brandon Smith 9 years ago
parent
commit
600ebdaa66
12 changed files with 155 additions and 81 deletions
  1. +1
    -1
      src/Discord.Net.Commands.Net45/Discord.Net.Commands.csproj
  2. +8
    -5
      src/Discord.Net.Net45/Discord.Net.csproj
  3. +40
    -35
      src/Discord.Net/DiscordClient.cs
  4. +21
    -0
      src/Discord.Net/Helpers/TaskHelper.cs
  5. +17
    -0
      src/Discord.Net/Net/API/DiscordAPIClient.cs
  6. +6
    -2
      src/Discord.Net/Net/API/Endpoints.cs
  7. +1
    -1
      src/Discord.Net/Net/API/HttpException.cs
  8. +22
    -23
      src/Discord.Net/Net/WebSockets/DataWebSocket.cs
  9. +5
    -0
      src/Discord.Net/Net/WebSockets/Events.cs
  10. +8
    -8
      src/Discord.Net/Net/WebSockets/VoiceWebSocket.cs
  11. +10
    -6
      src/Discord.Net/Net/WebSockets/WebSocket.cs
  12. +16
    -0
      src/Discord.Net/TimeoutException.cs

+ 1
- 1
src/Discord.Net.Commands.Net45/Discord.Net.Commands.csproj View File

@@ -7,7 +7,7 @@
<ProjectGuid>{1B5603B4-6F8F-4289-B945-7BAAE523D740}</ProjectGuid> <ProjectGuid>{1B5603B4-6F8F-4289-B945-7BAAE523D740}</ProjectGuid>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Discord.Net</RootNamespace>
<RootNamespace>Discord</RootNamespace>
<AssemblyName>Discord.Net.Commands</AssemblyName> <AssemblyName>Discord.Net.Commands</AssemblyName>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>


+ 8
- 5
src/Discord.Net.Net45/Discord.Net.csproj View File

@@ -7,7 +7,7 @@
<ProjectGuid>{8D71A857-879A-4A10-859E-5FF824ED6688}</ProjectGuid> <ProjectGuid>{8D71A857-879A-4A10-859E-5FF824ED6688}</ProjectGuid>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Discord.Net</RootNamespace>
<RootNamespace>Discord</RootNamespace>
<AssemblyName>Discord.Net</AssemblyName> <AssemblyName>Discord.Net</AssemblyName>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
@@ -159,6 +159,9 @@
<Compile Include="..\Discord.Net\Net\API\Endpoints.cs"> <Compile Include="..\Discord.Net\Net\API\Endpoints.cs">
<Link>Net\API\Endpoints.cs</Link> <Link>Net\API\Endpoints.cs</Link>
</Compile> </Compile>
<Compile Include="..\Discord.Net\Net\API\HttpException.cs">
<Link>Net\API\HttpException.cs</Link>
</Compile>
<Compile Include="..\Discord.Net\Net\API\Requests.cs"> <Compile Include="..\Discord.Net\Net\API\Requests.cs">
<Link>Net\API\Requests.cs</Link> <Link>Net\API\Requests.cs</Link>
</Compile> </Compile>
@@ -177,9 +180,6 @@
<Compile Include="..\Discord.Net\Net\API\RestClient.SharpRest.cs"> <Compile Include="..\Discord.Net\Net\API\RestClient.SharpRest.cs">
<Link>Net\API\RestClient.SharpRest.cs</Link> <Link>Net\API\RestClient.SharpRest.cs</Link>
</Compile> </Compile>
<Compile Include="..\Discord.Net\Net\HttpException.cs">
<Link>Net\HttpException.cs</Link>
</Compile>
<Compile Include="..\Discord.Net\Net\WebSockets\Commands.cs"> <Compile Include="..\Discord.Net\Net\WebSockets\Commands.cs">
<Link>Net\WebSockets\Commands.cs</Link> <Link>Net\WebSockets\Commands.cs</Link>
</Compile> </Compile>
@@ -187,7 +187,7 @@
<Link>Net\WebSockets\DataWebSocket.cs</Link> <Link>Net\WebSockets\DataWebSocket.cs</Link>
</Compile> </Compile>
<Compile Include="..\Discord.Net\Net\WebSockets\DataWebSockets.Events.cs"> <Compile Include="..\Discord.Net\Net\WebSockets\DataWebSockets.Events.cs">
<Link>Net\DataWebSockets.Events.cs</Link>
<Link>Net\WebSockets\DataWebSockets.Events.cs</Link>
</Compile> </Compile>
<Compile Include="..\Discord.Net\Net\WebSockets\Events.cs"> <Compile Include="..\Discord.Net\Net\WebSockets\Events.cs">
<Link>Net\WebSockets\Events.cs</Link> <Link>Net\WebSockets\Events.cs</Link>
@@ -216,6 +216,9 @@
<Compile Include="..\Discord.Net\Net\WebSockets\WebSocketMessage.cs"> <Compile Include="..\Discord.Net\Net\WebSockets\WebSocketMessage.cs">
<Link>Net\WebSockets\WebSocketMessage.cs</Link> <Link>Net\WebSockets\WebSocketMessage.cs</Link>
</Compile> </Compile>
<Compile Include="..\Discord.Net\TimeoutException.cs">
<Link>TimeoutException.cs</Link>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup /> <ItemGroup />


+ 40
- 35
src/Discord.Net/DiscordClient.cs View File

@@ -95,7 +95,7 @@ namespace Discord
_api = new DiscordAPIClient(_config.LogLevel); _api = new DiscordAPIClient(_config.LogLevel);
_dataSocket = new DataWebSocket(this); _dataSocket = new DataWebSocket(this);
_dataSocket.Connected += (s, e) => { if (_state == (int)DiscordClientState.Connecting) CompleteConnect(); }; _dataSocket.Connected += (s, e) => { if (_state == (int)DiscordClientState.Connecting) CompleteConnect(); };
_dataSocket.Disconnected += async (s, e) => { RaiseDisconnected(e); if (e.WasUnexpected) await Reconnect(_token); };
_dataSocket.Disconnected += async (s, e) => { RaiseDisconnected(e); if (e.WasUnexpected) await _dataSocket.Login(_token); };
if (_config.EnableVoice) if (_config.EnableVoice)
{ {
_voiceSocket = new VoiceWebSocket(this); _voiceSocket = new VoiceWebSocket(this);
@@ -112,7 +112,7 @@ namespace Discord
} }
RaiseVoiceDisconnected(e); RaiseVoiceDisconnected(e);
if (e.WasUnexpected) if (e.WasUnexpected)
await _voiceSocket.Reconnect(_cancelToken);
await _voiceSocket.Reconnect();
}; };
_voiceSocket.IsSpeaking += (s, e) => _voiceSocket.IsSpeaking += (s, e) =>
{ {
@@ -292,6 +292,8 @@ namespace Discord
} }
} }
break; break;
case "RESUMED":
break;


//Servers //Servers
case "GUILD_CREATE": case "GUILD_CREATE":
@@ -358,7 +360,7 @@ namespace Discord
var user = _users.GetOrAdd(data.User.Id); var user = _users.GetOrAdd(data.User.Id);
user.Update(data.User); user.Update(data.User);
if (_config.TrackActivity) if (_config.TrackActivity)
user.UpdateActivity(DateTime.UtcNow);
user.UpdateActivity();
var member = _members.GetOrAdd(data.User.Id, data.GuildId); var member = _members.GetOrAdd(data.User.Id, data.GuildId);
member.Update(data); member.Update(data);
RaiseUserAdded(member); RaiseUserAdded(member);
@@ -536,7 +538,7 @@ namespace Discord
if (user != null) if (user != null)
{ {
if (_config.TrackActivity) if (_config.TrackActivity)
user.UpdateActivity(DateTime.UtcNow);
user.UpdateActivity();
if (channel != null) if (channel != null)
RaiseUserIsTyping(user, channel); RaiseUserIsTyping(user, channel);
} }
@@ -550,8 +552,8 @@ namespace Discord
var server = _servers[data.GuildId]; var server = _servers[data.GuildId];
if (_config.EnableVoice) if (_config.EnableVoice)
{ {
string host = "wss://" + data.Endpoint.Split(':')[0];
await _voiceSocket.Login(host, data.GuildId, _currentUserId, _dataSocket.SessionId, data.Token, _cancelToken).ConfigureAwait(false);
_voiceSocket.Host = "wss://" + data.Endpoint.Split(':')[0];
await _voiceSocket.Login(data.GuildId, _currentUserId, _dataSocket.SessionId, data.Token, _cancelToken).ConfigureAwait(false);
} }
} }
break; break;
@@ -582,11 +584,6 @@ namespace Discord
}; };
} }


private void _dataSocket_Connected(object sender, EventArgs e)
{
throw new NotImplementedException();
}

//Connection //Connection
/// <summary> Connects to the Discord server with the provided token. </summary> /// <summary> Connects to the Discord server with the provided token. </summary>
public async Task Connect(string token) public async Task Connect(string token)
@@ -594,46 +591,54 @@ namespace Discord
if (_state != (int)DiscordClientState.Disconnected) if (_state != (int)DiscordClientState.Disconnected)
await Disconnect().ConfigureAwait(false); await Disconnect().ConfigureAwait(false);


if (_config.LogLevel >= LogMessageSeverity.Verbose)
RaiseOnLog(LogMessageSeverity.Verbose, LogMessageSource.Authentication, $"Using cached token.");

await ConnectInternal(token).ConfigureAwait(false);
}
await ConnectInternal(token)
.Timeout(_config.ConnectionTimeout)
.ConfigureAwait(false);
}
/// <summary> Connects to the Discord server with the provided email and password. </summary> /// <summary> Connects to the Discord server with the provided email and password. </summary>
/// <returns> Returns a token for future connections. </returns> /// <returns> Returns a token for future connections. </returns>
public async Task<string> Connect(string email, string password) public async Task<string> Connect(string email, string password)
{ {
if (_state != (int)DiscordClientState.Disconnected) if (_state != (int)DiscordClientState.Disconnected)
await Disconnect().ConfigureAwait(false); await Disconnect().ConfigureAwait(false);
var response = await _api.Login(email, password).ConfigureAwait(false);
if (_config.LogLevel >= LogMessageSeverity.Verbose)
RaiseOnLog(LogMessageSeverity.Verbose, LogMessageSource.Authentication, "Login successful, got token.");
return await ConnectInternal(response.Token).ConfigureAwait(false);
}
private Task Reconnect(string token)
{
if (_config.LogLevel >= LogMessageSeverity.Verbose)
RaiseOnLog(LogMessageSeverity.Verbose, LogMessageSource.Authentication, $"Using cached token.");


return ConnectInternal(token);
string token;
try
{
var cancelToken = new CancellationTokenSource();
cancelToken.CancelAfter(5000);
_api.CancelToken = cancelToken.Token;
var response = await _api.Login(email, password).ConfigureAwait(false);
token = response.Token;
if (_config.LogLevel >= LogMessageSeverity.Verbose)
RaiseOnLog(LogMessageSeverity.Verbose, LogMessageSource.Authentication, "Login successful, got token.");
}
catch (TaskCanceledException) { throw new TimeoutException(); }

return await ConnectInternal(token)
.Timeout(_config.ConnectionTimeout)
.ConfigureAwait(false);
} }
private async Task<string> ConnectInternal(string token) private async Task<string> ConnectInternal(string token)
{ {
try
try
{ {
_disconnectedEvent.Reset(); _disconnectedEvent.Reset();
_cancelTokenSource = new CancellationTokenSource(); _cancelTokenSource = new CancellationTokenSource();
_cancelToken = _cancelTokenSource.Token; _cancelToken = _cancelTokenSource.Token;
_state = (int)DiscordClientState.Connecting;

_api.Token = token; _api.Token = token;
_api.CancelToken = _cancelToken;
_token = token;
_state = (int)DiscordClientState.Connecting;
string url = (await _api.GetWebSocketEndpoint().ConfigureAwait(false)).Url; string url = (await _api.GetWebSocketEndpoint().ConfigureAwait(false)).Url;
url = "wss://gateway-besaid.discord.gg/";
if (_config.LogLevel >= LogMessageSeverity.Verbose) if (_config.LogLevel >= LogMessageSeverity.Verbose)
RaiseOnLog(LogMessageSeverity.Verbose, LogMessageSource.Authentication, $"Websocket endpoint: {url}"); RaiseOnLog(LogMessageSeverity.Verbose, LogMessageSource.Authentication, $"Websocket endpoint: {url}");
await _dataSocket.Login(url, token, _cancelToken).ConfigureAwait(false);

_dataSocket.Host = url;
_dataSocket.ParentCancelToken = _cancelToken;
await _dataSocket.Login(token).ConfigureAwait(false);


_runTask = RunTasks(); _runTask = RunTasks();


@@ -641,8 +646,7 @@ namespace Discord
{ {
//Cancel if either Disconnect is called, data socket errors or timeout is reached //Cancel if either Disconnect is called, data socket errors or timeout is reached
var cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_cancelToken, _dataSocket.CancelToken).Token; var cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_cancelToken, _dataSocket.CancelToken).Token;
if (!_connectedEvent.Wait(_config.ConnectionTimeout, cancelToken))
throw new Exception("Operation timed out.");
_connectedEvent.Wait(cancelToken);
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
@@ -656,6 +660,7 @@ namespace Discord
} }
catch catch
{ {

await Disconnect().ConfigureAwait(false); await Disconnect().ConfigureAwait(false);
throw; throw;
} }


+ 21
- 0
src/Discord.Net/Helpers/TaskHelper.cs View File

@@ -13,5 +13,26 @@ namespace Discord.Helpers
CompletedTask = Task.Delay(0); CompletedTask = Task.Delay(0);
#endif #endif
} }

public static async Task Timeout(this Task self, int milliseconds)
{
Task timeoutTask = Task.Delay(milliseconds);
Task finishedTask = await Task.WhenAny(self, timeoutTask);
if (finishedTask == timeoutTask)
{
throw new TimeoutException();
}
else
await self;
}
public static async Task<T> Timeout<T>(this Task<T> self, int milliseconds)
{
Task timeoutTask = Task.Delay(milliseconds);
Task finishedTask = await Task.WhenAny(self, timeoutTask).ConfigureAwait(false);
if (finishedTask == timeoutTask)
throw new TimeoutException();
else
return await self.ConfigureAwait(false);
}
} }
} }

+ 17
- 0
src/Discord.Net/Net/API/DiscordAPIClient.cs View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;


namespace Discord.Net.API namespace Discord.Net.API
@@ -21,6 +22,12 @@ namespace Discord.Net.API
get { return _token; } get { return _token; }
set { _token = value; _rest.SetToken(value); } set { _token = value; _rest.SetToken(value); }
} }
private CancellationToken _cancelToken;
public CancellationToken CancelToken
{
get { return _cancelToken; }
set { _cancelToken = value; _rest.SetCancelToken(value); }
}


//Auth //Auth
public Task<Responses.Gateway> GetWebSocketEndpoint() public Task<Responses.Gateway> GetWebSocketEndpoint()
@@ -191,5 +198,15 @@ namespace Discord.Net.API
var request = new Requests.ChangeAvatar { Avatar = $"data:{type},/9j/{base64}", CurrentEmail = currentEmail, CurrentPassword = currentPassword }; var request = new Requests.ChangeAvatar { Avatar = $"data:{type},/9j/{base64}", CurrentEmail = currentEmail, CurrentPassword = currentPassword };
return _rest.Patch<Responses.ChangeProfile>(Endpoints.UserMe, request); return _rest.Patch<Responses.ChangeProfile>(Endpoints.UserMe, request);
} }

//Other
/*public Task<Responses.Status> GetUnresolvedIncidents()
{
return _rest.Get<Responses.Status>(Endpoints.StatusUnresolvedMaintenance);
}
public Task<Responses.Status> GetActiveIncidents()
{
return _rest.Get<Responses.Status>(Endpoints.StatusActiveMaintenance);
}*/
} }
} }

+ 6
- 2
src/Discord.Net/Net/API/Endpoints.cs View File

@@ -1,7 +1,8 @@
namespace Discord.Net.API namespace Discord.Net.API
{ {
internal static class Endpoints internal static class Endpoints
{
{
public const string BaseStatusApi = "https://status.discordapp.com/api/v2/";
public const string BaseApi = "https://discordapp.com/api/"; public const string BaseApi = "https://discordapp.com/api/";
//public const string Track = "track"; //public const string Track = "track";
public const string Gateway = "gateway"; public const string Gateway = "gateway";
@@ -41,5 +42,8 @@
public const string Voice = "voice"; public const string Voice = "voice";
public const string VoiceRegions = "voice/regions"; public const string VoiceRegions = "voice/regions";
public const string VoiceIce = "voice/ice"; public const string VoiceIce = "voice/ice";
}

public const string StatusActiveMaintenance = "scheduled-maintenances/active.json";
public const string StatusUnresolvedMaintenance = "scheduled-maintenances/unresolved.json";
}
} }

src/Discord.Net/Net/HttpException.cs → src/Discord.Net/Net/API/HttpException.cs View File

@@ -1,7 +1,7 @@
using System; using System;
using System.Net; using System.Net;


namespace Discord.Net
namespace Discord.Net.API
{ {
public class HttpException : Exception public class HttpException : Exception
{ {

+ 22
- 23
src/Discord.Net/Net/WebSockets/DataWebSocket.cs View File

@@ -7,8 +7,7 @@ using System.Threading.Tasks;
namespace Discord.Net.WebSockets namespace Discord.Net.WebSockets
{ {
internal partial class DataWebSocket : WebSocket internal partial class DataWebSocket : WebSocket
{
private string _redirectServer;
{
private int _lastSeq; private int _lastSeq;


public string SessionId => _sessionId; public string SessionId => _sessionId;
@@ -18,29 +17,25 @@ namespace Discord.Net.WebSockets
: base(client) : base(client)
{ {
} }
public async Task Login(string host, string token, CancellationToken cancelToken)
public async Task Login(string token)
{ {
await base.Connect(host, cancelToken);
await Connect();
Commands.Login msg = new Commands.Login(); Commands.Login msg = new Commands.Login();
msg.Payload.Token = token; msg.Payload.Token = token;
msg.Payload.Properties["$device"] = "Discord.Net"; msg.Payload.Properties["$device"] = "Discord.Net";
QueueMessage(msg); QueueMessage(msg);
} }

protected override Task[] Run()
private async Task Redirect(string server)
{ {
//Send resume session if we were transferred
if (_redirectServer != null)
{
var resumeMsg = new Commands.Resume();
resumeMsg.Payload.SessionId = _sessionId;
resumeMsg.Payload.Sequence = _lastSeq;
QueueMessage(resumeMsg);
_redirectServer = null;
}
return base.Run();
await DisconnectInternal(isUnexpected: false);
await Connect();

var resumeMsg = new Commands.Resume();
resumeMsg.Payload.SessionId = _sessionId;
resumeMsg.Payload.Sequence = _lastSeq;
QueueMessage(resumeMsg);
} }


protected override async Task ProcessMessage(string json) protected override async Task ProcessMessage(string json)
@@ -54,27 +49,31 @@ namespace Discord.Net.WebSockets
case 0: case 0:
{ {
JToken token = msg.Payload as JToken; JToken token = msg.Payload as JToken;
if (msg.Type == "READY")
if (msg.Type == "READY")
{ {
var payload = token.ToObject<Events.Ready>(); var payload = token.ToObject<Events.Ready>();
_sessionId = payload.SessionId; _sessionId = payload.SessionId;
_heartbeatInterval = payload.HeartbeatInterval; _heartbeatInterval = payload.HeartbeatInterval;
QueueMessage(new Commands.UpdateStatus()); QueueMessage(new Commands.UpdateStatus());
} }
else if (msg.Type == "RESUMED")
{
var payload = token.ToObject<Events.Resumed>();
_heartbeatInterval = payload.HeartbeatInterval;
QueueMessage(new Commands.UpdateStatus());
}
RaiseReceivedEvent(msg.Type, token); RaiseReceivedEvent(msg.Type, token);
if (msg.Type == "READY")
if (msg.Type == "READY" || msg.Type == "RESUMED")
CompleteConnect(); CompleteConnect();
/*if (_logLevel >= LogMessageSeverity.Info)
RaiseOnLog(LogMessageSeverity.Info, "Got Event: " + msg.Type);*/
} }
break; break;
case 7: //Redirect case 7: //Redirect
{ {
var payload = (msg.Payload as JToken).ToObject<Events.Redirect>(); var payload = (msg.Payload as JToken).ToObject<Events.Redirect>();
_host = payload.Url;
Host = payload.Url;
if (_logLevel >= LogMessageSeverity.Info) if (_logLevel >= LogMessageSeverity.Info)
RaiseOnLog(LogMessageSeverity.Info, "Redirected to " + payload.Url); RaiseOnLog(LogMessageSeverity.Info, "Redirected to " + payload.Url);
await DisconnectInternal(new Exception("Server is redirecting."), true);
await Redirect(payload.Url);
} }
break; break;
default: default:


+ 5
- 0
src/Discord.Net/Net/WebSockets/Events.cs View File

@@ -36,6 +36,11 @@ namespace Discord.Net.WebSockets
[JsonProperty(PropertyName = "heartbeat_interval")] [JsonProperty(PropertyName = "heartbeat_interval")]
public int HeartbeatInterval; public int HeartbeatInterval;
} }
public sealed class Resumed
{
[JsonProperty(PropertyName = "heartbeat_interval")]
public int HeartbeatInterval;
}


public sealed class Redirect public sealed class Redirect
{ {


+ 8
- 8
src/Discord.Net/Net/WebSockets/VoiceWebSocket.cs View File

@@ -53,13 +53,12 @@ namespace Discord.Net.WebSockets
_targetAudioBufferLength = client.Config.VoiceBufferLength / 20; //20 ms frames _targetAudioBufferLength = client.Config.VoiceBufferLength / 20; //20 ms frames
} }


public async Task Login(string host, string serverId, string userId, string sessionId, string token, CancellationToken cancelToken)
public async Task Login(string serverId, string userId, string sessionId, string token, CancellationToken cancelToken)
{ {
if (_serverId == serverId && _userId == userId && _sessionId == sessionId && _token == token) if (_serverId == serverId && _userId == userId && _sessionId == sessionId && _token == token)
{ {
//Adjust the host and tell the system to reconnect //Adjust the host and tell the system to reconnect
_host = host;
await DisconnectInternal(new Exception("Server transfer occurred."));
await DisconnectInternal(new Exception("Server transfer occurred."), isUnexpected: false);
return; return;
} }
@@ -68,18 +67,19 @@ namespace Discord.Net.WebSockets
_sessionId = sessionId; _sessionId = sessionId;
_token = token; _token = token;


await Connect(host, cancelToken);
await Connect();
} }
public async Task Reconnect(CancellationToken cancelToken)
public async Task Reconnect()
{ {
try try
{ {
await Task.Delay(_client.Config.ReconnectDelay, cancelToken).ConfigureAwait(false);
var cancelToken = ParentCancelToken;
await Task.Delay(_client.Config.ReconnectDelay, cancelToken).ConfigureAwait(false);
while (!cancelToken.IsCancellationRequested) while (!cancelToken.IsCancellationRequested)
{ {
try try
{ {
await Connect(_host, cancelToken).ConfigureAwait(false);
await Connect().ConfigureAwait(false);
break; break;
} }
catch (OperationCanceledException) { throw; } catch (OperationCanceledException) { throw; }
@@ -295,7 +295,7 @@ namespace Discord.Net.WebSockets
var payload = (msg.Payload as JToken).ToObject<VoiceEvents.Ready>(); var payload = (msg.Payload as JToken).ToObject<VoiceEvents.Ready>();
_heartbeatInterval = payload.HeartbeatInterval; _heartbeatInterval = payload.HeartbeatInterval;
_ssrc = payload.SSRC; _ssrc = payload.SSRC;
_endpoint = new IPEndPoint((await Dns.GetHostAddressesAsync(_host.Replace("wss://", "")).ConfigureAwait(false)).FirstOrDefault(), payload.Port);
_endpoint = new IPEndPoint((await Dns.GetHostAddressesAsync(Host.Replace("wss://", "")).ConfigureAwait(false)).FirstOrDefault(), payload.Port);
//_mode = payload.Modes.LastOrDefault(); //_mode = payload.Modes.LastOrDefault();
_isEncrypted = !payload.Modes.Contains("plain"); _isEncrypted = !payload.Modes.Contains("plain");
_udp.Connect(_endpoint); _udp.Connect(_endpoint);


+ 10
- 6
src/Discord.Net/Net/WebSockets/WebSocket.cs View File

@@ -38,7 +38,8 @@ namespace Discord.Net.WebSockets
protected readonly DiscordClient _client; protected readonly DiscordClient _client;
protected readonly LogMessageSeverity _logLevel; protected readonly LogMessageSeverity _logLevel;


protected string _host;
public string Host { get; set; }

protected int _loginTimeout, _heartbeatInterval; protected int _loginTimeout, _heartbeatInterval;
private DateTime _lastHeartbeat; private DateTime _lastHeartbeat;
private Task _runTask; private Task _runTask;
@@ -49,6 +50,7 @@ namespace Discord.Net.WebSockets
protected ExceptionDispatchInfo _disconnectReason; protected ExceptionDispatchInfo _disconnectReason;
private bool _wasDisconnectUnexpected; private bool _wasDisconnectUnexpected;


public CancellationToken ParentCancelToken { get; set; }
public CancellationToken CancelToken => _cancelToken; public CancellationToken CancelToken => _cancelToken;
private CancellationTokenSource _cancelTokenSource; private CancellationTokenSource _cancelTokenSource;
protected CancellationToken _cancelToken; protected CancellationToken _cancelToken;
@@ -69,22 +71,24 @@ namespace Discord.Net.WebSockets
}; };
} }


protected virtual async Task Connect(string host, CancellationToken cancelToken)
protected virtual async Task Connect()
{ {
if (_state != (int)WebSocketState.Disconnected) if (_state != (int)WebSocketState.Disconnected)
throw new InvalidOperationException("Client is already connected or connecting to the server."); throw new InvalidOperationException("Client is already connected or connecting to the server.");


try
try
{ {
await Disconnect().ConfigureAwait(false); await Disconnect().ConfigureAwait(false);
_state = (int)WebSocketState.Connecting; _state = (int)WebSocketState.Connecting;


_cancelTokenSource = new CancellationTokenSource(); _cancelTokenSource = new CancellationTokenSource();
_cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_cancelTokenSource.Token, cancelToken).Token;
if (ParentCancelToken != null)
_cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_cancelTokenSource.Token, ParentCancelToken).Token;
else
_cancelToken = _cancelTokenSource.Token;


await _engine.Connect(host, _cancelToken).ConfigureAwait(false);
_host = host;
await _engine.Connect(Host, _cancelToken).ConfigureAwait(false);
_lastHeartbeat = DateTime.UtcNow; _lastHeartbeat = DateTime.UtcNow;


_runTask = RunTasks(); _runTask = RunTasks();


+ 16
- 0
src/Discord.Net/TimeoutException.cs View File

@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Discord
{
public sealed class TimeoutException : Exception
{
internal TimeoutException()
: base("An operation has timed out.")
{
}
}
}

Loading…
Cancel
Save