diff --git a/src/Discord.Net.Net45/Discord.Net.csproj b/src/Discord.Net.Net45/Discord.Net.csproj index 0340c2557..6c0149078 100644 --- a/src/Discord.Net.Net45/Discord.Net.csproj +++ b/src/Discord.Net.Net45/Discord.Net.csproj @@ -59,6 +59,9 @@ ..\..\..\DiscordBot\packages\RestSharp.105.2.3\lib\net45\RestSharp.dll + + ..\..\..\DiscordBot\packages\WebSocketSharp.1.0.3-rc9\lib\websocket-sharp.dll + @@ -241,6 +244,9 @@ WebSockets\WebSocket.Events.cs + + WebSockets\WebSocket.WebSocketSharp.cs + WebSockets\WebSocketMessage.cs diff --git a/src/Discord.Net.Net45/packages.config b/src/Discord.Net.Net45/packages.config index e09901c26..c5325909b 100644 --- a/src/Discord.Net.Net45/packages.config +++ b/src/Discord.Net.Net45/packages.config @@ -2,4 +2,5 @@ + \ No newline at end of file diff --git a/src/Discord.Net/WebSockets/Data/DataWebSocket.cs b/src/Discord.Net/WebSockets/Data/DataWebSocket.cs index faa2c8d22..5011ed99a 100644 --- a/src/Discord.Net/WebSockets/Data/DataWebSocket.cs +++ b/src/Discord.Net/WebSockets/Data/DataWebSocket.cs @@ -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) { diff --git a/src/Discord.Net/WebSockets/Voice/VoiceWebSocket.cs b/src/Discord.Net/WebSockets/Voice/VoiceWebSocket.cs index 6a13660e1..194fdbda7 100644 --- a/src/Discord.Net/WebSockets/Voice/VoiceWebSocket.cs +++ b/src/Discord.Net/WebSockets/Voice/VoiceWebSocket.cs @@ -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... diff --git a/src/Discord.Net/WebSockets/WebSocket.BuiltIn.cs b/src/Discord.Net/WebSockets/WebSocket.BuiltIn.cs index d177c9333..141f5839c 100644 --- a/src/Discord.Net/WebSockets/WebSocket.BuiltIn.cs +++ b/src/Discord.Net/WebSockets/WebSocket.BuiltIn.cs @@ -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 _sendQueue; + private readonly ConcurrentQueue _sendQueue; private readonly int _sendInterval; private ClientWebSocket _webSocket; @@ -30,7 +31,7 @@ namespace Discord.WebSockets public BuiltInWebSocketEngine(int sendInterval) { _sendInterval = sendInterval; - _sendQueue = new ConcurrentQueue(); + _sendQueue = new ConcurrentQueue(); } 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); } } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/Discord.Net/WebSockets/WebSocket.WebSocketSharp.cs b/src/Discord.Net/WebSockets/WebSocket.WebSocketSharp.cs new file mode 100644 index 000000000..93fd379de --- /dev/null +++ b/src/Discord.Net/WebSockets/WebSocket.WebSocketSharp.cs @@ -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 _sendQueue; + private readonly int _sendInterval; + private readonly string _userAgent; + private readonly WebSocket _parent; + private WSSharpNWebSocket _webSocket; + + public event EventHandler 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(); + } + + 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 \ No newline at end of file diff --git a/src/Discord.Net/WebSockets/WebSocket.cs b/src/Discord.Net/WebSockets/WebSocket.cs index 79537a3cf..7a74214e2 100644 --- a/src/Discord.Net/WebSockets/WebSocket.cs +++ b/src/Discord.Net/WebSockets/WebSocket.cs @@ -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) diff --git a/src/Discord.Net/project.json b/src/Discord.Net/project.json index 17726da0e..d3b42f940 100644 --- a/src/Discord.Net/project.json +++ b/src/Discord.Net/project.json @@ -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" diff --git a/test/Discord.Net.Tests/Discord.Net.Tests.csproj b/test/Discord.Net.Tests/Discord.Net.Tests.csproj index a4e5ea574..f1dbcb369 100644 --- a/test/Discord.Net.Tests/Discord.Net.Tests.csproj +++ b/test/Discord.Net.Tests/Discord.Net.Tests.csproj @@ -1,5 +1,5 @@  - + Debug AnyCPU