@@ -59,6 +59,9 @@ | |||||
<Reference Include="RestSharp, Version=105.2.3.0, Culture=neutral, PublicKeyToken=null"> | <Reference Include="RestSharp, Version=105.2.3.0, Culture=neutral, PublicKeyToken=null"> | ||||
<HintPath>..\..\..\DiscordBot\packages\RestSharp.105.2.3\lib\net45\RestSharp.dll</HintPath> | <HintPath>..\..\..\DiscordBot\packages\RestSharp.105.2.3\lib\net45\RestSharp.dll</HintPath> | ||||
</Reference> | </Reference> | ||||
<Reference Include="websocket-sharp, Version=1.0.2.36589, Culture=neutral, PublicKeyToken=5660b08a1845a91e"> | |||||
<HintPath>..\..\..\DiscordBot\packages\WebSocketSharp.1.0.3-rc9\lib\websocket-sharp.dll</HintPath> | |||||
</Reference> | |||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<Content Include="lib\libopus.so" /> | <Content Include="lib\libopus.so" /> | ||||
@@ -241,6 +244,9 @@ | |||||
<Compile Include="..\Discord.Net\WebSockets\WebSocket.Events.cs"> | <Compile Include="..\Discord.Net\WebSockets\WebSocket.Events.cs"> | ||||
<Link>WebSockets\WebSocket.Events.cs</Link> | <Link>WebSockets\WebSocket.Events.cs</Link> | ||||
</Compile> | </Compile> | ||||
<Compile Include="..\Discord.Net\WebSockets\WebSocket.WebSocketSharp.cs"> | |||||
<Link>WebSockets\WebSocket.WebSocketSharp.cs</Link> | |||||
</Compile> | |||||
<Compile Include="..\Discord.Net\WebSockets\WebSocketMessage.cs"> | <Compile Include="..\Discord.Net\WebSockets\WebSocketMessage.cs"> | ||||
<Link>WebSockets\WebSocketMessage.cs</Link> | <Link>WebSockets\WebSocketMessage.cs</Link> | ||||
</Compile> | </Compile> | ||||
@@ -2,4 +2,5 @@ | |||||
<packages> | <packages> | ||||
<package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" /> | <package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" /> | ||||
<package id="RestSharp" version="105.2.3" targetFramework="net45" /> | <package id="RestSharp" version="105.2.3" targetFramework="net45" /> | ||||
<package id="WebSocketSharp" version="1.0.3-rc9" targetFramework="net45" /> | |||||
</packages> | </packages> |
@@ -40,7 +40,7 @@ namespace Discord.WebSockets.Data | |||||
{ | { | ||||
try | try | ||||
{ | { | ||||
var cancelToken = ParentCancelToken; | |||||
var cancelToken = ParentCancelToken.Value; | |||||
await Task.Delay(_client.Config.ReconnectDelay, cancelToken).ConfigureAwait(false); | await Task.Delay(_client.Config.ReconnectDelay, cancelToken).ConfigureAwait(false); | ||||
while (!cancelToken.IsCancellationRequested) | while (!cancelToken.IsCancellationRequested) | ||||
{ | { | ||||
@@ -85,7 +85,7 @@ namespace Discord.WebSockets.Voice | |||||
{ | { | ||||
try | try | ||||
{ | { | ||||
var cancelToken = ParentCancelToken; | |||||
var cancelToken = ParentCancelToken.Value; | |||||
await Task.Delay(_client.Config.ReconnectDelay, cancelToken).ConfigureAwait(false); | await Task.Delay(_client.Config.ReconnectDelay, cancelToken).ConfigureAwait(false); | ||||
while (!cancelToken.IsCancellationRequested) | while (!cancelToken.IsCancellationRequested) | ||||
{ | { | ||||
@@ -127,7 +127,8 @@ namespace Discord.WebSockets.Voice | |||||
{ | { | ||||
#if USE_THREAD | #if USE_THREAD | ||||
_sendThread = new Thread(new ThreadStart(() => SendVoiceAsync(_cancelToken))); | _sendThread = new Thread(new ThreadStart(() => SendVoiceAsync(_cancelToken))); | ||||
_sendThread.Start(); | |||||
_sendThread.IsBackground = true; | |||||
_sendThread.Start(); | |||||
#else | #else | ||||
tasks.Add(SendVoiceAsync()); | tasks.Add(SendVoiceAsync()); | ||||
#endif | #endif | ||||
@@ -138,6 +139,7 @@ namespace Discord.WebSockets.Voice | |||||
if ((_client.Config.VoiceMode & DiscordVoiceMode.Incoming) != 0) | if ((_client.Config.VoiceMode & DiscordVoiceMode.Incoming) != 0) | ||||
{ | { | ||||
_receiveThread = new Thread(new ThreadStart(() => ReceiveVoiceAsync(_cancelToken))); | _receiveThread = new Thread(new ThreadStart(() => ReceiveVoiceAsync(_cancelToken))); | ||||
_sendThread.IsBackground = true; | |||||
_receiveThread.Start(); | _receiveThread.Start(); | ||||
} | } | ||||
else //Dont make an OS thread if we only want to capture one packet... | else //Dont make an OS thread if we only want to capture one packet... | ||||
@@ -1,4 +1,5 @@ | |||||
using Discord.Helpers; | |||||
#if DNXCORE50 | |||||
using Discord.Helpers; | |||||
using System; | using System; | ||||
using System.Collections.Concurrent; | using System.Collections.Concurrent; | ||||
using System.ComponentModel; | using System.ComponentModel; | ||||
@@ -16,7 +17,7 @@ namespace Discord.WebSockets | |||||
private const int SendChunkSize = 4096; | private const int SendChunkSize = 4096; | ||||
private const int HR_TIMEOUT = -2147012894; | private const int HR_TIMEOUT = -2147012894; | ||||
private readonly ConcurrentQueue<byte[]> _sendQueue; | |||||
private readonly ConcurrentQueue<string> _sendQueue; | |||||
private readonly int _sendInterval; | private readonly int _sendInterval; | ||||
private ClientWebSocket _webSocket; | private ClientWebSocket _webSocket; | ||||
@@ -30,7 +31,7 @@ namespace Discord.WebSockets | |||||
public BuiltInWebSocketEngine(int sendInterval) | public BuiltInWebSocketEngine(int sendInterval) | ||||
{ | { | ||||
_sendInterval = sendInterval; | _sendInterval = sendInterval; | ||||
_sendQueue = new ConcurrentQueue<byte[]>(); | |||||
_sendQueue = new ConcurrentQueue<string>(); | |||||
} | } | ||||
public Task Connect(string host, CancellationToken cancelToken) | public Task Connect(string host, CancellationToken cancelToken) | ||||
@@ -42,7 +43,7 @@ namespace Discord.WebSockets | |||||
public Task Disconnect() | public Task Disconnect() | ||||
{ | { | ||||
byte[] ignored; | |||||
string ignored; | |||||
while (_sendQueue.TryDequeue(out ignored)) { } | while (_sendQueue.TryDequeue(out ignored)) { } | ||||
_webSocket.Dispose(); | _webSocket.Dispose(); | ||||
_webSocket = new ClientWebSocket(); | _webSocket = new ClientWebSocket(); | ||||
@@ -107,12 +108,13 @@ namespace Discord.WebSockets | |||||
{ | { | ||||
try | try | ||||
{ | { | ||||
byte[] bytes; | |||||
while (_webSocket.State == State.Open && !cancelToken.IsCancellationRequested) | while (_webSocket.State == State.Open && !cancelToken.IsCancellationRequested) | ||||
{ | { | ||||
while (_sendQueue.TryDequeue(out bytes)) | |||||
string json; | |||||
while (_sendQueue.TryDequeue(out json)) | |||||
{ | { | ||||
var frameCount = (int)Math.Ceiling((double)bytes.Length / SendChunkSize); | |||||
byte[] bytes = Encoding.UTF8.GetBytes(json); | |||||
int frameCount = (int)Math.Ceiling((double)bytes.Length / SendChunkSize); | |||||
int offset = 0; | int offset = 0; | ||||
for (var i = 0; i < frameCount; i++, offset += SendChunkSize) | for (var i = 0; i < frameCount; i++, offset += SendChunkSize) | ||||
@@ -142,9 +144,10 @@ namespace Discord.WebSockets | |||||
}); | }); | ||||
} | } | ||||
public void QueueMessage(byte[] message) | |||||
public void QueueMessage(string message) | |||||
{ | { | ||||
_sendQueue.Enqueue(message); | _sendQueue.Enqueue(message); | ||||
} | } | ||||
} | } | ||||
} | |||||
} | |||||
#endif |
@@ -0,0 +1,88 @@ | |||||
#if !DNXCORE50 | |||||
using Discord.Helpers; | |||||
using System; | |||||
using System.Collections.Concurrent; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | |||||
using WSSharpNWebSocket = WebSocketSharp.WebSocket; | |||||
namespace Discord.WebSockets | |||||
{ | |||||
public class WSSharpWebSocketEngine : IWebSocketEngine | |||||
{ | |||||
private readonly ConcurrentQueue<string> _sendQueue; | |||||
private readonly int _sendInterval; | |||||
private readonly string _userAgent; | |||||
private readonly WebSocket _parent; | |||||
private WSSharpNWebSocket _webSocket; | |||||
public event EventHandler<WebSocketMessageEventArgs> ProcessMessage; | |||||
private void RaiseProcessMessage(string msg) | |||||
{ | |||||
if (ProcessMessage != null) | |||||
ProcessMessage(this, new WebSocketMessageEventArgs(msg)); | |||||
} | |||||
internal WSSharpWebSocketEngine(WebSocket parent, string userAgent, int sendInterval) | |||||
{ | |||||
_parent = parent; | |||||
_userAgent = userAgent; | |||||
_sendInterval = sendInterval; | |||||
_sendQueue = new ConcurrentQueue<string>(); | |||||
} | |||||
public Task Connect(string host, CancellationToken cancelToken) | |||||
{ | |||||
_webSocket = new WSSharpNWebSocket(host); | |||||
_webSocket.EmitOnPing = false; | |||||
_webSocket.EnableRedirection = true; | |||||
_webSocket.Compression = WebSocketSharp.CompressionMethod.None; | |||||
_webSocket.OnMessage += (s, e) => RaiseProcessMessage(e.Data); | |||||
_webSocket.OnError += (s, e) => _parent.RaiseOnLog(LogMessageSeverity.Error, $"Websocket Error: {e.Message}"); | |||||
_webSocket.Connect(); | |||||
return TaskHelper.CompletedTask; | |||||
} | |||||
public Task Disconnect() | |||||
{ | |||||
string ignored; | |||||
while (_sendQueue.TryDequeue(out ignored)) { } | |||||
_webSocket.Close(); | |||||
return TaskHelper.CompletedTask; | |||||
} | |||||
public Task[] GetTasks(CancellationToken cancelToken) | |||||
{ | |||||
return new Task[] | |||||
{ | |||||
SendAsync(cancelToken) | |||||
}; | |||||
} | |||||
private Task SendAsync(CancellationToken cancelToken) | |||||
{ | |||||
return Task.Run(async () => | |||||
{ | |||||
try | |||||
{ | |||||
while (_webSocket.IsAlive && !cancelToken.IsCancellationRequested) | |||||
{ | |||||
string json; | |||||
while (_sendQueue.TryDequeue(out json)) | |||||
_webSocket.Send(json); | |||||
await Task.Delay(_sendInterval, cancelToken).ConfigureAwait(false); | |||||
} | |||||
} | |||||
catch (OperationCanceledException) { } | |||||
}); | |||||
} | |||||
public void QueueMessage(string message) | |||||
{ | |||||
_sendQueue.Enqueue(message); | |||||
} | |||||
} | |||||
} | |||||
#endif |
@@ -28,7 +28,7 @@ namespace Discord.WebSockets | |||||
Task Connect(string host, CancellationToken cancelToken); | Task Connect(string host, CancellationToken cancelToken); | ||||
Task Disconnect(); | Task Disconnect(); | ||||
void QueueMessage(byte[] message); | |||||
void QueueMessage(string message); | |||||
Task[] GetTasks(CancellationToken cancelToken); | Task[] GetTasks(CancellationToken cancelToken); | ||||
} | } | ||||
@@ -47,7 +47,7 @@ namespace Discord.WebSockets | |||||
private DateTime _lastHeartbeat; | private DateTime _lastHeartbeat; | ||||
private Task _runTask; | private Task _runTask; | ||||
public CancellationToken ParentCancelToken { get; set; } | |||||
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; | ||||
@@ -61,11 +61,16 @@ namespace Discord.WebSockets | |||||
{ | { | ||||
_client = client; | _client = client; | ||||
_logLevel = client.Config.LogLevel; | _logLevel = client.Config.LogLevel; | ||||
_loginTimeout = client.Config.ConnectionTimeout; | _loginTimeout = client.Config.ConnectionTimeout; | ||||
_cancelToken = new CancellationToken(true); | _cancelToken = new CancellationToken(true); | ||||
_connectedEvent = new ManualResetEventSlim(false); | _connectedEvent = new ManualResetEventSlim(false); | ||||
#if DNXCORE50 | |||||
_engine = new BuiltInWebSocketEngine(client.Config.WebSocketInterval); | _engine = new BuiltInWebSocketEngine(client.Config.WebSocketInterval); | ||||
#else | |||||
_engine = new WSSharpWebSocketEngine(this, client.Config.UserAgent, client.Config.WebSocketInterval); | |||||
#endif | |||||
_engine.ProcessMessage += async (s, e) => | _engine.ProcessMessage += async (s, e) => | ||||
{ | { | ||||
if (_logLevel >= LogMessageSeverity.Debug) | if (_logLevel >= LogMessageSeverity.Debug) | ||||
@@ -84,10 +89,11 @@ namespace Discord.WebSockets | |||||
await Disconnect().ConfigureAwait(false); | await Disconnect().ConfigureAwait(false); | ||||
_cancelTokenSource = new CancellationTokenSource(); | _cancelTokenSource = new CancellationTokenSource(); | ||||
if (ParentCancelToken != null) | |||||
_cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_cancelTokenSource.Token, ParentCancelToken).Token; | |||||
else | |||||
_cancelToken = _cancelTokenSource.Token; | |||||
if (ParentCancelToken == null) | |||||
throw new InvalidOperationException("Parent cancel token was never set."); | |||||
_cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_cancelTokenSource.Token, ParentCancelToken.Value).Token; | |||||
/*else | |||||
_cancelToken = _cancelTokenSource.Token;*/ | |||||
_lastHeartbeat = DateTime.UtcNow; | _lastHeartbeat = DateTime.UtcNow; | ||||
await _engine.Connect(Host, _cancelToken).ConfigureAwait(false); | await _engine.Connect(Host, _cancelToken).ConfigureAwait(false); | ||||
@@ -198,8 +204,7 @@ namespace Discord.WebSockets | |||||
string json = JsonConvert.SerializeObject(message); | string json = JsonConvert.SerializeObject(message); | ||||
if (_logLevel >= LogMessageSeverity.Debug) | if (_logLevel >= LogMessageSeverity.Debug) | ||||
RaiseOnLog(LogMessageSeverity.Debug, $"Out: " + json); | RaiseOnLog(LogMessageSeverity.Debug, $"Out: " + json); | ||||
var bytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(message)); | |||||
_engine.QueueMessage(bytes); | |||||
_engine.QueueMessage(json); | |||||
} | } | ||||
private Task HeartbeatAsync(CancellationToken cancelToken) | private Task HeartbeatAsync(CancellationToken cancelToken) | ||||
@@ -27,7 +27,8 @@ | |||||
"frameworks": { | "frameworks": { | ||||
"net45": { | "net45": { | ||||
"dependencies": { | "dependencies": { | ||||
"RestSharp": "105.2.3" | |||||
"RestSharp": "105.2.3", | |||||
"WebSocketSharp": "1.0.3-rc9" | |||||
}, | }, | ||||
"frameworkAssemblies": { | "frameworkAssemblies": { | ||||
"System.Net.Http": "4.0.0.0" | "System.Net.Http": "4.0.0.0" | ||||
@@ -35,7 +36,8 @@ | |||||
}, | }, | ||||
"dnx451": { | "dnx451": { | ||||
"dependencies": { | "dependencies": { | ||||
"RestSharp": "105.2.3" | |||||
"RestSharp": "105.2.3", | |||||
"WebSocketSharp": "1.0.3-rc9" | |||||
}, | }, | ||||
"frameworkAssemblies": { | "frameworkAssemblies": { | ||||
"System.Net.Http": "4.0.0.0" | "System.Net.Http": "4.0.0.0" | ||||
@@ -1,5 +1,5 @@ | |||||
<?xml version="1.0" encoding="utf-8"?> | <?xml version="1.0" encoding="utf-8"?> | ||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||||
<PropertyGroup> | <PropertyGroup> | ||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | ||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> | <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> | ||||