| @@ -59,6 +59,9 @@ | |||
| <Reference Include="RestSharp, Version=105.2.3.0, Culture=neutral, PublicKeyToken=null"> | |||
| <HintPath>..\..\..\DiscordBot\packages\RestSharp.105.2.3\lib\net45\RestSharp.dll</HintPath> | |||
| </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> | |||
| <Content Include="lib\libopus.so" /> | |||
| @@ -241,6 +244,9 @@ | |||
| <Compile Include="..\Discord.Net\WebSockets\WebSocket.Events.cs"> | |||
| <Link>WebSockets\WebSocket.Events.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\WebSockets\WebSocket.WebSocketSharp.cs"> | |||
| <Link>WebSockets\WebSocket.WebSocketSharp.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\WebSockets\WebSocketMessage.cs"> | |||
| <Link>WebSockets\WebSocketMessage.cs</Link> | |||
| </Compile> | |||
| @@ -2,4 +2,5 @@ | |||
| <packages> | |||
| <package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" /> | |||
| <package id="RestSharp" version="105.2.3" targetFramework="net45" /> | |||
| <package id="WebSocketSharp" version="1.0.3-rc9" targetFramework="net45" /> | |||
| </packages> | |||
| @@ -40,7 +40,7 @@ namespace Discord.WebSockets.Data | |||
| { | |||
| try | |||
| { | |||
| var cancelToken = ParentCancelToken; | |||
| var cancelToken = ParentCancelToken.Value; | |||
| await Task.Delay(_client.Config.ReconnectDelay, cancelToken).ConfigureAwait(false); | |||
| while (!cancelToken.IsCancellationRequested) | |||
| { | |||
| @@ -85,7 +85,7 @@ namespace Discord.WebSockets.Voice | |||
| { | |||
| try | |||
| { | |||
| var cancelToken = ParentCancelToken; | |||
| var cancelToken = ParentCancelToken.Value; | |||
| await Task.Delay(_client.Config.ReconnectDelay, cancelToken).ConfigureAwait(false); | |||
| while (!cancelToken.IsCancellationRequested) | |||
| { | |||
| @@ -127,7 +127,8 @@ namespace Discord.WebSockets.Voice | |||
| { | |||
| #if USE_THREAD | |||
| _sendThread = new Thread(new ThreadStart(() => SendVoiceAsync(_cancelToken))); | |||
| _sendThread.Start(); | |||
| _sendThread.IsBackground = true; | |||
| _sendThread.Start(); | |||
| #else | |||
| tasks.Add(SendVoiceAsync()); | |||
| #endif | |||
| @@ -138,6 +139,7 @@ namespace Discord.WebSockets.Voice | |||
| if ((_client.Config.VoiceMode & DiscordVoiceMode.Incoming) != 0) | |||
| { | |||
| _receiveThread = new Thread(new ThreadStart(() => ReceiveVoiceAsync(_cancelToken))); | |||
| _sendThread.IsBackground = true; | |||
| _receiveThread.Start(); | |||
| } | |||
| 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.Collections.Concurrent; | |||
| using System.ComponentModel; | |||
| @@ -16,7 +17,7 @@ namespace Discord.WebSockets | |||
| private const int SendChunkSize = 4096; | |||
| private const int HR_TIMEOUT = -2147012894; | |||
| private readonly ConcurrentQueue<byte[]> _sendQueue; | |||
| private readonly ConcurrentQueue<string> _sendQueue; | |||
| private readonly int _sendInterval; | |||
| private ClientWebSocket _webSocket; | |||
| @@ -30,7 +31,7 @@ namespace Discord.WebSockets | |||
| public BuiltInWebSocketEngine(int sendInterval) | |||
| { | |||
| _sendInterval = sendInterval; | |||
| _sendQueue = new ConcurrentQueue<byte[]>(); | |||
| _sendQueue = new ConcurrentQueue<string>(); | |||
| } | |||
| public Task Connect(string host, CancellationToken cancelToken) | |||
| @@ -42,7 +43,7 @@ namespace Discord.WebSockets | |||
| public Task Disconnect() | |||
| { | |||
| byte[] ignored; | |||
| string ignored; | |||
| while (_sendQueue.TryDequeue(out ignored)) { } | |||
| _webSocket.Dispose(); | |||
| _webSocket = new ClientWebSocket(); | |||
| @@ -107,12 +108,13 @@ namespace Discord.WebSockets | |||
| { | |||
| try | |||
| { | |||
| byte[] bytes; | |||
| 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; | |||
| 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); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| #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 Disconnect(); | |||
| void QueueMessage(byte[] message); | |||
| void QueueMessage(string message); | |||
| Task[] GetTasks(CancellationToken cancelToken); | |||
| } | |||
| @@ -47,7 +47,7 @@ namespace Discord.WebSockets | |||
| private DateTime _lastHeartbeat; | |||
| private Task _runTask; | |||
| public CancellationToken ParentCancelToken { get; set; } | |||
| public CancellationToken? ParentCancelToken { get; set; } | |||
| public CancellationToken CancelToken => _cancelToken; | |||
| private CancellationTokenSource _cancelTokenSource; | |||
| protected CancellationToken _cancelToken; | |||
| @@ -61,11 +61,16 @@ namespace Discord.WebSockets | |||
| { | |||
| _client = client; | |||
| _logLevel = client.Config.LogLevel; | |||
| _loginTimeout = client.Config.ConnectionTimeout; | |||
| _cancelToken = new CancellationToken(true); | |||
| _connectedEvent = new ManualResetEventSlim(false); | |||
| #if DNXCORE50 | |||
| _engine = new BuiltInWebSocketEngine(client.Config.WebSocketInterval); | |||
| #else | |||
| _engine = new WSSharpWebSocketEngine(this, client.Config.UserAgent, client.Config.WebSocketInterval); | |||
| #endif | |||
| _engine.ProcessMessage += async (s, e) => | |||
| { | |||
| if (_logLevel >= LogMessageSeverity.Debug) | |||
| @@ -84,10 +89,11 @@ namespace Discord.WebSockets | |||
| await Disconnect().ConfigureAwait(false); | |||
| _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; | |||
| await _engine.Connect(Host, _cancelToken).ConfigureAwait(false); | |||
| @@ -198,8 +204,7 @@ namespace Discord.WebSockets | |||
| string json = JsonConvert.SerializeObject(message); | |||
| if (_logLevel >= LogMessageSeverity.Debug) | |||
| RaiseOnLog(LogMessageSeverity.Debug, $"Out: " + json); | |||
| var bytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(message)); | |||
| _engine.QueueMessage(bytes); | |||
| _engine.QueueMessage(json); | |||
| } | |||
| private Task HeartbeatAsync(CancellationToken cancelToken) | |||
| @@ -27,7 +27,8 @@ | |||
| "frameworks": { | |||
| "net45": { | |||
| "dependencies": { | |||
| "RestSharp": "105.2.3" | |||
| "RestSharp": "105.2.3", | |||
| "WebSocketSharp": "1.0.3-rc9" | |||
| }, | |||
| "frameworkAssemblies": { | |||
| "System.Net.Http": "4.0.0.0" | |||
| @@ -35,7 +36,8 @@ | |||
| }, | |||
| "dnx451": { | |||
| "dependencies": { | |||
| "RestSharp": "105.2.3" | |||
| "RestSharp": "105.2.3", | |||
| "WebSocketSharp": "1.0.3-rc9" | |||
| }, | |||
| "frameworkAssemblies": { | |||
| "System.Net.Http": "4.0.0.0" | |||
| @@ -1,5 +1,5 @@ | |||
| <?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> | |||
| <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | |||
| <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> | |||