Browse Source

Added libsodium, set up UDP socket for voice.

tags/docs-0.9
Brandon Smith 9 years ago
parent
commit
a80355699a
12 changed files with 354 additions and 236 deletions
  1. +1
    -1
      global.json
  2. +15
    -2
      src/Discord.Net.Net45/Discord.Net.csproj
  3. +2
    -0
      src/Discord.Net.Net45/packages.config
  4. +1
    -2
      src/Discord.Net/API/Endpoints.cs
  5. +2
    -2
      src/Discord.Net/API/Models/VoiceWebSocketEvents.cs
  6. +4
    -4
      src/Discord.Net/DiscordClient.cs
  7. +4
    -0
      src/Discord.Net/DiscordClientConfig.cs
  8. +10
    -7
      src/Discord.Net/DiscordTextWebSocket.cs
  9. +240
    -0
      src/Discord.Net/DiscordVoiceSocket.cs
  10. +0
    -165
      src/Discord.Net/DiscordVoiceWebSocket.cs
  11. +24
    -7
      src/Discord.Net/DiscordWebSocket.cs
  12. +51
    -46
      src/Discord.Net/project.json

+ 1
- 1
global.json View File

@@ -3,6 +3,6 @@
"sdk": {
"version": "1.0.0-beta6",
"architecture": "x64",
"runtime": "coreclr"
"runtime": "clr"
}
}

+ 15
- 2
src/Discord.Net.Net45/Discord.Net.csproj View File

@@ -11,6 +11,8 @@
<AssemblyName>Discord.Net</AssemblyName>
<FileAlignment>512</FileAlignment>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@@ -36,6 +38,10 @@
<HintPath>..\..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Sodium, Version=0.8.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\libsodium-net.0.8.0\lib\Net40\Sodium.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Net.Http" />
</ItemGroup>
@@ -91,8 +97,8 @@
<Compile Include="..\Discord.Net\DiscordTextWebSocket.Events.cs">
<Link>DiscordTextWebSocket.Events.cs</Link>
</Compile>
<Compile Include="..\Discord.Net\DiscordVoiceWebSocket.cs">
<Link>DiscordVoiceWebSocket.cs</Link>
<Compile Include="..\Discord.Net\DiscordVoiceSocket.cs">
<Link>DiscordVoiceSocket.cs</Link>
</Compile>
<Compile Include="..\Discord.Net\DiscordWebSocket.cs">
<Link>DiscordWebSocket.cs</Link>
@@ -136,6 +142,13 @@
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\..\packages\Baseclass.Contrib.Nuget.Output.1.0.0\build\net40\Baseclass.Contrib.Nuget.Output.targets" Condition="Exists('..\..\packages\Baseclass.Contrib.Nuget.Output.1.0.0\build\net40\Baseclass.Contrib.Nuget.Output.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\packages\Baseclass.Contrib.Nuget.Output.1.0.0\build\net40\Baseclass.Contrib.Nuget.Output.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Baseclass.Contrib.Nuget.Output.1.0.0\build\net40\Baseclass.Contrib.Nuget.Output.targets'))" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">


+ 2
- 0
src/Discord.Net.Net45/packages.config View File

@@ -1,4 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Baseclass.Contrib.Nuget.Output" version="1.0.0" targetFramework="net45" />
<package id="libsodium-net" version="0.8.0" targetFramework="net45" />
<package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
</packages>

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

@@ -50,8 +50,7 @@
public static readonly string VoiceIce = $"{Voice}/ice";

//Web Sockets
public static readonly string BaseWss = "wss://" + BaseUrl;
public static readonly string WebSocket_Hub = $"{BaseWss}/hub";
public static readonly string WebSocket_Hub = $"{BaseUrl}/hub";

//Website
public static string InviteUrl(string code) => $"{BaseShortHttps}/{code}";


+ 2
- 2
src/Discord.Net/API/Models/VoiceWebSocketEvents.cs View File

@@ -11,9 +11,9 @@ namespace Discord.API.Models
public sealed class Ready
{
[JsonProperty(PropertyName = "ssrc")]
public int SSRC;
public uint SSRC;
[JsonProperty(PropertyName = "port")]
public int Port;
public ushort Port;
[JsonProperty(PropertyName = "modes")]
public string[] Modes;
[JsonProperty(PropertyName = "heartbeat_interval")]


+ 4
- 4
src/Discord.Net/DiscordClient.cs View File

@@ -19,7 +19,7 @@ namespace Discord
{
private readonly DiscordClientConfig _config;
private readonly DiscordTextWebSocket _webSocket;
private readonly DiscordVoiceWebSocket _voiceWebSocket;
private readonly DiscordVoiceSocket _voiceWebSocket;
private readonly ManualResetEventSlim _blockEvent;
private readonly Regex _userRegex, _channelRegex;
private readonly MatchEvaluator _userRegexEvaluator, _channelRegexEvaluator;
@@ -287,7 +287,7 @@ namespace Discord
user => { }
);

_webSocket = new DiscordTextWebSocket(_config.WebSocketInterval);
_webSocket = new DiscordTextWebSocket(_config.ConnectionTimeout, _config.WebSocketInterval);
_webSocket.Connected += (s, e) => RaiseConnected();
_webSocket.Disconnected += async (s, e) =>
{
@@ -312,7 +312,7 @@ namespace Discord
};
_webSocket.OnDebugMessage += (s, e) => RaiseOnDebugMessage(e.Message);
_voiceWebSocket = new DiscordVoiceWebSocket(_config.WebSocketInterval);
_voiceWebSocket = new DiscordVoiceSocket(_config.VoiceConnectionTimeout, _config.WebSocketInterval);
_voiceWebSocket.Connected += (s, e) => RaiseVoiceConnected();
_voiceWebSocket.Disconnected += (s, e) =>
{
@@ -578,7 +578,7 @@ namespace Discord
{
_currentVoiceEndpoint = data.Endpoint.Split(':')[0];
_currentVoiceToken = data.Token;
await _voiceWebSocket.ConnectAsync("wss://" + _currentVoiceEndpoint);
await _voiceWebSocket.ConnectAsync(_currentVoiceEndpoint);
await _voiceWebSocket.Login(_currentVoiceServerId, UserId, SessionId, _currentVoiceToken);
}
}


+ 4
- 0
src/Discord.Net/DiscordClientConfig.cs View File

@@ -2,6 +2,10 @@
{
public class DiscordClientConfig
{
/// <summary> Max time in milliseconds to wait for the web socket to connect. </summary>
public int ConnectionTimeout { get; set; } = 5000;
/// <summary> Max time in milliseconds to wait for the voice web socket to connect. </summary>
public int VoiceConnectionTimeout { get; set; } = 10000;
/// <summary> Gets or sets the time (in milliseconds) to wait after an unexpected disconnect before reconnecting. </summary>
public int ReconnectDelay { get; set; } = 1000;
/// <summary> Gets or sets the time (in milliseconds) to wait after an reconnect fails before retrying. </summary>


+ 10
- 7
src/Discord.Net/DiscordTextWebSocket.cs View File

@@ -11,12 +11,10 @@ namespace Discord
{
internal sealed partial class DiscordTextWebSocket : DiscordWebSocket
{
private const int ReadyTimeout = 2500; //Max time in milliseconds between connecting to Discord and receiving a READY event

private ManualResetEventSlim _connectWaitOnLogin, _connectWaitOnLogin2;

public DiscordTextWebSocket(int interval)
: base(interval)
public DiscordTextWebSocket(int timeout, int interval)
: base(timeout, interval)
{
_connectWaitOnLogin = new ManualResetEventSlim(false);
_connectWaitOnLogin2 = new ManualResetEventSlim(false);
@@ -40,7 +38,7 @@ namespace Discord

try
{
if (!_connectWaitOnLogin.Wait(ReadyTimeout, cancelToken)) //Waiting on READY message
if (!_connectWaitOnLogin.Wait(_timeout, cancelToken)) //Waiting on READY message
throw new Exception("No reply from Discord server");
}
catch (OperationCanceledException)
@@ -53,7 +51,7 @@ namespace Discord
SetConnected();
}

protected override void ProcessMessage(string json)
protected override Task ProcessMessage(string json)
{
var msg = JsonConvert.DeserializeObject<WebSocketMessage>(json);
switch (msg.Operation)
@@ -65,7 +63,7 @@ namespace Discord
var payload = (msg.Payload as JToken).ToObject<TextWebSocketEvents.Ready>();
_heartbeatInterval = payload.HeartbeatInterval;
QueueMessage(new TextWebSocketCommands.UpdateStatus());
QueueMessage(GetKeepAlive());
//QueueMessage(GetKeepAlive());
_connectWaitOnLogin.Set(); //Pre-Event
}
RaiseGotEvent(msg.Type, msg.Payload as JToken);
@@ -77,6 +75,11 @@ namespace Discord
RaiseOnDebugMessage("Unknown WebSocket operation ID: " + msg.Operation);
break;
}
#if DNXCORE
return Task.CompletedTask
#else
return Task.Delay(0);
#endif
}

protected override object GetKeepAlive()


+ 240
- 0
src/Discord.Net/DiscordVoiceSocket.cs View File

@@ -0,0 +1,240 @@
using Discord.API.Models;
using Discord.Helpers;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using WebSocketMessage = Discord.API.Models.VoiceWebSocketCommands.WebSocketMessage;

namespace Discord
{
internal sealed partial class DiscordVoiceSocket : DiscordWebSocket
{
private ManualResetEventSlim _connectWaitOnLogin;
private UdpClient _udp;
private ConcurrentQueue<byte[]> _sendQueue;
private string _myIp;
private IPEndPoint _endpoint;
private byte[] _secretKey;
private string _mode;
private bool _isFirst;

public DiscordVoiceSocket(int timeout, int interval)
: base(timeout, interval)
{
_connectWaitOnLogin = new ManualResetEventSlim(false);
_sendQueue = new ConcurrentQueue<byte[]>();
}

protected override void OnConnect()
{
_udp = new UdpClient(new IPEndPoint(IPAddress.Any, 0));
_udp.AllowNatTraversal(true);
_isFirst = true;
}
protected override void OnDisconnect()
{
_udp = null;
}

protected override Task[] CreateTasks(CancellationToken cancelToken)
{
return new Task[]
{
Task.Factory.StartNew(ReceiveAsync, cancelToken, TaskCreationOptions.LongRunning, TaskScheduler.Default).Result,
Task.Factory.StartNew(SendAsync, cancelToken, TaskCreationOptions.LongRunning, TaskScheduler.Default).Result,
Task.Factory.StartNew(WatcherAsync, cancelToken, TaskCreationOptions.LongRunning, TaskScheduler.Default).Result
}.Concat(base.CreateTasks(cancelToken)).ToArray();
}

public async Task Login(string serverId, string userId, string sessionId, string token)
{
var cancelToken = _disconnectToken.Token;

_connectWaitOnLogin.Reset();

_myIp = (await Http.Get("http://ipinfo.io/ip")).Trim();

VoiceWebSocketCommands.Login msg = new VoiceWebSocketCommands.Login();
msg.Payload.ServerId = serverId;
msg.Payload.SessionId = sessionId;
msg.Payload.Token = token;
msg.Payload.UserId = userId;
await SendMessage(msg, cancelToken);

try
{
if (!_connectWaitOnLogin.Wait(_timeout, cancelToken)) //Waiting on JoinServer message
throw new Exception("No reply from Discord server");
}
catch (OperationCanceledException)
{
throw new InvalidOperationException("Bad Token");
}

SetConnected();
}

private async Task ReceiveAsync()
{
var cancelToken = _disconnectToken.Token;

try
{
while (!cancelToken.IsCancellationRequested)
{
var result = await _udp.ReceiveAsync();
ProcessUdpMessage(result);
}
}
catch { }
finally { _disconnectToken.Cancel(); }
}
private async Task SendAsync()
{
var cancelToken = _disconnectToken.Token;
try
{
byte[] bytes;
while (!cancelToken.IsCancellationRequested)
{
while (_sendQueue.TryDequeue(out bytes))
await _udp.SendAsync(bytes, bytes.Length);
await Task.Delay(_sendInterval);
}
}
catch { }
finally { _disconnectToken.Cancel(); }
}
private async Task WatcherAsync()
{
try
{
await Task.Delay(-1, _disconnectToken.Token);
}
catch (TaskCanceledException) { }
#if DNXCORE50
finally { _udp.Dispose(); }
#else
finally { _udp.Close(); }
#endif
}

protected override async Task ProcessMessage(string json)
{
var msg = JsonConvert.DeserializeObject<WebSocketMessage>(json);
switch (msg.Operation)
{
case 2: //READY
{
var payload = (msg.Payload as JToken).ToObject<VoiceWebSocketEvents.Ready>();
_heartbeatInterval = payload.HeartbeatInterval;
_endpoint = new IPEndPoint((await Dns.GetHostAddressesAsync(_host)).FirstOrDefault(), payload.Port);
//_mode = payload.Modes.LastOrDefault();
_mode = "plain";
_udp.Connect(_endpoint);
var ssrc = payload.SSRC;

_sendQueue.Enqueue(new byte[70] {
(byte)((ssrc >> 24) & 0xFF),
(byte)((ssrc >> 16) & 0xFF),
(byte)((ssrc >> 8) & 0xFF),
(byte)((ssrc >> 0) & 0xFF),
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0
});
}
break;
case 4: //SESSION_DESCRIPTION
{
var payload = (msg.Payload as JToken).ToObject<VoiceWebSocketEvents.JoinServer>();
_secretKey = payload.SecretKey;
_connectWaitOnLogin.Set();
}
break;
default:
RaiseOnDebugMessage("Unknown WebSocket operation ID: " + msg.Operation);
break;
}
}
private void ProcessUdpMessage(UdpReceiveResult msg)
{
if (msg.Buffer.Length > 0 && msg.RemoteEndPoint.Equals(_endpoint))
{
byte[] buffer = msg.Buffer;
int length = msg.Buffer.Length;
if (_isFirst)
{
_isFirst = false;
if (length != 70)
throw new Exception($"Unexpected message length. Expected 70, got {length}.");

int port = buffer[68] | buffer[69] << 8;

var login2 = new VoiceWebSocketCommands.Login2();
login2.Payload.Protocol = "udp";
login2.Payload.SocketData.Address = _myIp;
login2.Payload.SocketData.Mode = _mode;
login2.Payload.SocketData.Port = port;
QueueMessage(login2);
}
else
{
//Parse RTP Data
if (length < 12)
throw new Exception($"Unexpected message length. Expected >= 12, got {length}.");

byte flags = buffer[0];
if (flags != 0x80)
throw new Exception("Unexpected Flags");

byte payloadType = buffer[1];
if (payloadType != 0x78)
throw new Exception("Unexpected Payload Type");

ushort sequenceNumber = (ushort)((buffer[2] << 8) | buffer[3]);
uint timestamp = (uint)((buffer[4] << 24) | (buffer[5] << 16) |
(buffer[6] << 8) | (buffer[7] << 0));
uint ssrc = (uint)((buffer[8] << 24) | (buffer[9] << 16) |
(buffer[10] << 8) | (buffer[11] << 0));

//Decrypt
if (_mode == "xsalsa20_poly1305")
{
if (length < 36) //12 + 24
throw new Exception($"Unexpected message length. Expected >= 36, got {length}.");

#if !DNXCORE50
byte[] nonce = new byte[24]; //16 bytes static, 8 bytes incrementing?
Buffer.BlockCopy(buffer, 12, nonce, 0, 24);

byte[] cipherText = new byte[buffer.Length - 36];
Buffer.BlockCopy(buffer, 36, cipherText, 0, cipherText.Length);
Sodium.SecretBox.Open(cipherText, nonce, _secretKey);
#endif
}
else //Plain
{
byte[] newBuffer = new byte[buffer.Length - 12];
Buffer.BlockCopy(buffer, 12, newBuffer, 0, newBuffer.Length);
buffer = newBuffer;
}
}
}
}

protected override object GetKeepAlive()
{
return new VoiceWebSocketCommands.KeepAlive();
}
}
}

+ 0
- 165
src/Discord.Net/DiscordVoiceWebSocket.cs View File

@@ -1,165 +0,0 @@
using Discord.API.Models;
using Discord.Helpers;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using WebSocketMessage = Discord.API.Models.VoiceWebSocketCommands.WebSocketMessage;

namespace Discord
{
internal sealed partial class DiscordVoiceWebSocket : DiscordWebSocket
{
private const int ReadyTimeout = 2500; //Max time in milliseconds between connecting to Discord and receiving a READY event

private ManualResetEventSlim _connectWaitOnLogin;
private UdpClient _udp;
private ConcurrentQueue<byte[]> _sendQueue;
private string _ip;

public DiscordVoiceWebSocket(int interval)
: base(interval)
{
_connectWaitOnLogin = new ManualResetEventSlim(false);

_sendQueue = new ConcurrentQueue<byte[]>();
}

protected override void OnConnect()
{
_udp = new UdpClient(0);
}
protected override void OnDisconnect()
{
_udp = null;
}

protected override Task[] CreateTasks(CancellationToken cancelToken)
{
return new Task[]
{
Task.Factory.StartNew(ReceiveAsync, cancelToken, TaskCreationOptions.LongRunning, TaskScheduler.Default).Result,
Task.Factory.StartNew(SendAsync, cancelToken, TaskCreationOptions.LongRunning, TaskScheduler.Default).Result,
Task.Factory.StartNew(WatcherAsync, cancelToken, TaskCreationOptions.LongRunning, TaskScheduler.Default).Result
}.Concat(base.CreateTasks(cancelToken)).ToArray();
}

public async Task Login(string serverId, string userId, string sessionId, string token)
{
var cancelToken = _disconnectToken.Token;

_connectWaitOnLogin.Reset();

_ip = (await Http.Get("http://ipinfo.io/ip")).Trim();

VoiceWebSocketCommands.Login msg = new VoiceWebSocketCommands.Login();
msg.Payload.ServerId = serverId;
msg.Payload.SessionId = sessionId;
msg.Payload.Token = token;
msg.Payload.UserId = userId;
await SendMessage(msg, cancelToken);

try
{
if (!_connectWaitOnLogin.Wait(ReadyTimeout, cancelToken)) //Waiting on JoinServer message
throw new Exception("No reply from Discord server");
}
catch (OperationCanceledException)
{
throw new InvalidOperationException("Bad Token");
}

SetConnected();
}

private async Task ReceiveAsync()
{
var cancelToken = _disconnectToken.Token;

try
{
while (!cancelToken.IsCancellationRequested)
{
var result = await _udp.ReceiveAsync();
ProcessUdpMessage(result);
}
}
catch { }
finally { _disconnectToken.Cancel(); }
}
private async Task SendAsync()
{
var cancelToken = _disconnectToken.Token;
try
{
byte[] bytes;
while (!cancelToken.IsCancellationRequested)
{
while (_sendQueue.TryDequeue(out bytes))
await SendMessage(bytes, cancelToken);
await Task.Delay(_sendInterval);
}
}
catch { }
finally { _disconnectToken.Cancel(); }
}
private async Task WatcherAsync()
{
try
{
await Task.Delay(-1, _disconnectToken.Token);
}
catch (TaskCanceledException) { }
#if DNXCORE50
finally { _udp.Dispose(); }
#else
finally { _udp.Close(); }
#endif
}

protected override void ProcessMessage(string json)
{
var msg = JsonConvert.DeserializeObject<WebSocketMessage>(json);
switch (msg.Operation)
{
case 2:
{
var payload = (msg.Payload as JToken).ToObject<VoiceWebSocketEvents.Ready>();
_heartbeatInterval = payload.HeartbeatInterval;

var login2 = new VoiceWebSocketCommands.Login2();
login2.Payload.Protocol = "udp";
login2.Payload.SocketData.Address = _ip;
login2.Payload.SocketData.Mode = payload.Modes.Last();
login2.Payload.SocketData.Port = (_udp.Client.LocalEndPoint as IPEndPoint).Port;
QueueMessage(login2);
}
break;
case 4:
{
var payload = (msg.Payload as JToken).ToObject<VoiceWebSocketEvents.JoinServer>();
QueueMessage(GetKeepAlive());
_connectWaitOnLogin.Set();
}
break;
default:
RaiseOnDebugMessage("Unknown WebSocket operation ID: " + msg.Operation);
break;
}
}
private void ProcessUdpMessage(UdpReceiveResult msg)
{
System.Diagnostics.Debug.WriteLine($"Got {msg.Buffer.Length} bytes from {msg.RemoteEndPoint}.");
}

protected override object GetKeepAlive()
{
return new VoiceWebSocketCommands.KeepAlive();
}
}
}

+ 24
- 7
src/Discord.Net/DiscordWebSocket.cs View File

@@ -7,6 +7,8 @@ using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Net;
using System.Linq;

namespace Discord
{
@@ -16,8 +18,9 @@ namespace Discord
private const int SendChunkSize = 4096;

protected volatile CancellationTokenSource _disconnectToken;
protected int _heartbeatInterval;
protected int _timeout, _heartbeatInterval;
protected readonly int _sendInterval;
protected string _host;

private volatile ClientWebSocket _webSocket;
private volatile Task _tasks;
@@ -25,8 +28,9 @@ namespace Discord
private DateTime _lastHeartbeat;
private bool _isConnected;

public DiscordWebSocket(int interval)
public DiscordWebSocket(int timeout, int interval)
{
_timeout = timeout;
_sendInterval = interval;

_sendQueue = new ConcurrentQueue<byte[]>();
@@ -41,10 +45,12 @@ namespace Discord

_webSocket = new ClientWebSocket();
_webSocket.Options.KeepAliveInterval = TimeSpan.Zero;
await _webSocket.ConnectAsync(new Uri(url), cancelToken);
await _webSocket.ConnectAsync(new Uri("wss://" + url), cancelToken);
_host = url;

OnConnect();

_lastHeartbeat = DateTime.UtcNow;
_tasks = Task.WhenAll(CreateTasks(cancelToken))
.ContinueWith(x =>
{
@@ -123,7 +129,10 @@ namespace Discord
}
while (!result.EndOfMessage);

ProcessMessage(builder.ToString());
//TODO: Remove this
if (this is DiscordVoiceSocket)
System.Diagnostics.Debug.WriteLine(">>> " + builder.ToString());
await ProcessMessage(builder.ToString());

builder.Clear();
}
@@ -157,16 +166,24 @@ namespace Discord
finally { _disconnectToken.Cancel(); }
}

protected abstract void ProcessMessage(string json);
protected abstract Task ProcessMessage(string json);
protected abstract object GetKeepAlive();

protected void QueueMessage(object message)
{
var bytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(message));
//TODO: Remove this
if (this is DiscordVoiceSocket)
System.Diagnostics.Debug.WriteLine("<<< " + JsonConvert.SerializeObject(message));
var bytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(message));
_sendQueue.Enqueue(bytes);
}
protected Task SendMessage(object message, CancellationToken cancelToken)
=> SendMessage(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(message)), cancelToken);
{
//TODO: Remove this
if (this is DiscordVoiceSocket)
System.Diagnostics.Debug.WriteLine("<<< " + JsonConvert.SerializeObject(message));
return SendMessage(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(message)), cancelToken);
}
protected async Task SendMessage(byte[] message, CancellationToken cancelToken)
{
var frameCount = (int)Math.Ceiling((double)message.Length / SendChunkSize);


+ 51
- 46
src/Discord.Net/project.json View File

@@ -1,49 +1,54 @@
{
"version": "0.5.0-*",
"description": "An unofficial .Net API wrapper for the Discord client.",
"authors": [ "RogueException" ],
"tags": [ "discord", "discordapp" ],
"projectUrl": "https://github.com/RogueException/Discord.Net",
"licenseUrl": "http://opensource.org/licenses/MIT",
"repository": {
"type": "git",
"url": "git://github.com/RogueException/Discord.Net"
},
"configurations": {
"FullDebug": {
"compilationOptions": {
"define": ["DEBUG","TRACE","TEST_RESPONSES"]
}
}
},
{
"version": "0.5.0-*",
"description": "An unofficial .Net API wrapper for the Discord client.",
"authors": [ "RogueException" ],
"tags": [ "discord", "discordapp" ],
"projectUrl": "https://github.com/RogueException/Discord.Net",
"licenseUrl": "http://opensource.org/licenses/MIT",
"repository": {
"type": "git",
"url": "git://github.com/RogueException/Discord.Net"
},
"configurations": {
"FullDebug": {
"compilationOptions": {
"define": [ "DEBUG", "TRACE", "TEST_RESPONSES" ]
}
}
},

"dependencies": {
"Newtonsoft.Json": "7.0.1"
},
"dependencies": {
"Newtonsoft.Json": "7.0.1"
},

"frameworks": {
"net45": {
"dependencies": {
"Microsoft.Net.Http": "2.2.22"
}
},
"dnx451": {
"dependencies": {
"Microsoft.Net.Http": "2.2.22"
}
},
"dnxcore50": {
"dependencies": {
"System.Collections.Concurrent": "4.0.10",
"System.Diagnostics.Debug": "4.0.10",
"System.IO.Compression": "4.0.0",
"System.Linq": "4.0.0",
"System.Net.Requests": "4.0.10",
"System.Net.Sockets": "4.0.10-beta-23019",
"System.Net.WebSockets.Client": "4.0.0-beta-23123",
"System.Runtime": "4.0.20",
"System.Text.RegularExpressions": "4.0.10"
}
}
}
"frameworks": {
"net45": {
"dependencies": {
"Microsoft.Net.Http": "2.2.22",
"libsodium-net": "0.8.0",
"Baseclass.Contrib.Nuget.Output": "2.1.0"
}
},
"dnx451": {
"dependencies": {
"Microsoft.Net.Http": "2.2.22",
"libsodium-net": "0.8.0",
"Baseclass.Contrib.Nuget.Output": "2.1.0"
}
},
"dnxcore50": {
"dependencies": {
"System.Collections.Concurrent": "4.0.10",
"System.Diagnostics.Debug": "4.0.10",
"System.IO.Compression": "4.0.0",
"System.Linq": "4.0.0",
"System.Net.Requests": "4.0.10",
"System.Net.Sockets": "4.0.10-beta-23019",
"System.Net.WebSockets.Client": "4.0.0-beta-23123",
"System.Runtime": "4.0.20",
"System.Text.RegularExpressions": "4.0.10",
"System.Net.NameResolution": "4.0.0-beta-23019"
}
}
}
}

Loading…
Cancel
Save