| @@ -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> | ||||