| @@ -61,6 +61,9 @@ namespace Discord.Collections | |||||
| protected TValue Get(string key) | protected TValue Get(string key) | ||||
| { | { | ||||
| if (key == null) | |||||
| return null; | |||||
| TValue result; | TValue result; | ||||
| if (!_dictionary.TryGetValue(key, out result)) | if (!_dictionary.TryGetValue(key, out result)) | ||||
| return null; | return null; | ||||
| @@ -1,7 +1,6 @@ | |||||
| using System; | using System; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Linq; | using System.Linq; | ||||
| using System.Threading.Tasks; | |||||
| namespace Discord.Collections | namespace Discord.Collections | ||||
| { | { | ||||
| @@ -15,7 +14,8 @@ namespace Discord.Collections | |||||
| protected override void OnCreated(Channel item) | protected override void OnCreated(Channel item) | ||||
| { | { | ||||
| item.Server.AddChannel(item.Id); | |||||
| if (item.ServerId != null) | |||||
| item.Server.AddChannel(item.Id); | |||||
| if (item.RecipientId != null) | if (item.RecipientId != null) | ||||
| { | { | ||||
| var user = item.Recipient; | var user = item.Recipient; | ||||
| @@ -27,8 +27,8 @@ namespace Discord.Collections | |||||
| } | } | ||||
| protected override void OnRemoved(Channel item) | protected override void OnRemoved(Channel item) | ||||
| { | { | ||||
| item.Server.RemoveChannel(item.Id); | |||||
| if (item.ServerId != null) | |||||
| item.Server.RemoveChannel(item.Id); | |||||
| if (item.RecipientId != null) | if (item.RecipientId != null) | ||||
| { | { | ||||
| var user = item.Recipient; | var user = item.Recipient; | ||||
| @@ -376,7 +376,7 @@ namespace Discord | |||||
| case "PRESENCE_UPDATE": | case "PRESENCE_UPDATE": | ||||
| { | { | ||||
| var data = e.Payload.ToObject<Events.PresenceUpdate>(_serializer); | var data = e.Payload.ToObject<Events.PresenceUpdate>(_serializer); | ||||
| var member = _members[data.UserId, data.GuildId]; | |||||
| var member = _members[data.User.Id, data.GuildId]; | |||||
| /*if (_config.TrackActivity) | /*if (_config.TrackActivity) | ||||
| { | { | ||||
| var user = _users[data.User.Id]; | var user = _users[data.User.Id]; | ||||
| @@ -539,8 +539,8 @@ namespace Discord | |||||
| } | } | ||||
| /// <summary> Disconnects from the Discord server, canceling any pending requests. </summary> | /// <summary> Disconnects from the Discord server, canceling any pending requests. </summary> | ||||
| public Task Disconnect() => DisconnectInternal(new Exception("Disconnect was requested by user.")); | |||||
| protected async Task DisconnectInternal(Exception ex, bool isUnexpected = true) | |||||
| public Task Disconnect() => DisconnectInternal(new Exception("Disconnect was requested by user."), isUnexpected: false); | |||||
| protected async Task DisconnectInternal(Exception ex, bool isUnexpected = true, bool skipAwait = false) | |||||
| { | { | ||||
| int oldState; | int oldState; | ||||
| bool hasWriterLock; | bool hasWriterLock; | ||||
| @@ -563,9 +563,12 @@ namespace Discord | |||||
| _cancelToken.Cancel(); | _cancelToken.Cancel(); | ||||
| } | } | ||||
| Task task = _runTask; | |||||
| if (task != null) | |||||
| await task.ConfigureAwait(false); | |||||
| if (!skipAwait) | |||||
| { | |||||
| Task task = _runTask; | |||||
| if (task != null) | |||||
| await task.ConfigureAwait(false); | |||||
| } | |||||
| if (hasWriterLock) | if (hasWriterLock) | ||||
| { | { | ||||
| @@ -587,7 +590,7 @@ namespace Discord | |||||
| { | { | ||||
| await task.ConfigureAwait(false); | await task.ConfigureAwait(false); | ||||
| } | } | ||||
| catch (Exception ex) { await DisconnectInternal(ex).ConfigureAwait(false); } | |||||
| catch (Exception ex) { await DisconnectInternal(ex, skipAwait: true).ConfigureAwait(false); } | |||||
| await Cleanup().ConfigureAwait(false); | await Cleanup().ConfigureAwait(false); | ||||
| _runTask = null; | _runTask = null; | ||||
| @@ -129,8 +129,8 @@ namespace Discord | |||||
| var members = _client.Members; | var members = _client.Members; | ||||
| foreach (var subModel in model.Members) | foreach (var subModel in model.Members) | ||||
| { | { | ||||
| var user = users.GetOrAdd(subModel.UserId); | |||||
| var member = members.GetOrAdd(subModel.UserId, Id); | |||||
| var user = users.GetOrAdd(subModel.User.Id); | |||||
| var member = members.GetOrAdd(subModel.User.Id, Id); | |||||
| user.Update(subModel.User); | user.Update(subModel.User); | ||||
| member.Update(subModel); | member.Update(subModel); | ||||
| } | } | ||||
| @@ -141,7 +141,7 @@ namespace Discord | |||||
| } | } | ||||
| foreach (var subModel in model.Presences) | foreach (var subModel in model.Presences) | ||||
| { | { | ||||
| var member = members.GetOrAdd(subModel.UserId, Id); | |||||
| var member = members.GetOrAdd(subModel.User.Id, Id); | |||||
| member.Update(subModel); | member.Update(subModel); | ||||
| } | } | ||||
| } | } | ||||
| @@ -11,12 +11,12 @@ namespace Discord.Net.WebSockets | |||||
| { | { | ||||
| internal static class Commands | internal static class Commands | ||||
| { | { | ||||
| public sealed class KeepAlive : WebSocketMessage<int> | |||||
| public sealed class KeepAlive : WebSocketMessage<ulong> | |||||
| { | { | ||||
| public KeepAlive() : base(1, GetTimestamp()) { } | public KeepAlive() : base(1, GetTimestamp()) { } | ||||
| private static DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); | private static DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); | ||||
| private static int GetTimestamp() | |||||
| => (int)(DateTime.UtcNow - epoch).TotalMilliseconds; | |||||
| private static ulong GetTimestamp() | |||||
| => (ulong)(DateTime.UtcNow - epoch).TotalMilliseconds; | |||||
| } | } | ||||
| public sealed class Login : WebSocketMessage<Login.Data> | public sealed class Login : WebSocketMessage<Login.Data> | ||||
| { | { | ||||
| @@ -24,11 +24,7 @@ namespace Discord.Net.WebSockets | |||||
| Commands.Login msg = new Commands.Login(); | Commands.Login msg = new Commands.Login(); | ||||
| msg.Payload.Token = token; | msg.Payload.Token = token; | ||||
| //msg.Payload.Properties["$os"] = ""; | |||||
| //msg.Payload.Properties["$browser"] = ""; | |||||
| msg.Payload.Properties["$device"] = "Discord.Net"; | msg.Payload.Properties["$device"] = "Discord.Net"; | ||||
| //msg.Payload.Properties["$referrer"] = ""; | |||||
| //msg.Payload.Properties["$referring_domain"] = ""; | |||||
| QueueMessage(msg); | QueueMessage(msg); | ||||
| } | } | ||||
| @@ -1,6 +1,7 @@ | |||||
| using Discord.Helpers; | using Discord.Helpers; | ||||
| using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
| using System; | using System; | ||||
| using System.Linq; | |||||
| using System.Runtime.ExceptionServices; | using System.Runtime.ExceptionServices; | ||||
| using System.Text; | using System.Text; | ||||
| using System.Threading; | using System.Threading; | ||||
| @@ -96,8 +97,8 @@ namespace Discord.Net.WebSockets | |||||
| public Task Reconnect() | public Task Reconnect() | ||||
| => Connect(_host); | => Connect(_host); | ||||
| public Task Disconnect() => DisconnectInternal(new Exception("Disconnect was requested by user.")); | |||||
| protected async Task DisconnectInternal(Exception ex, bool isUnexpected = true) | |||||
| public Task Disconnect() => DisconnectInternal(new Exception("Disconnect was requested by user."), isUnexpected: false); | |||||
| protected async Task DisconnectInternal(Exception ex, bool isUnexpected = true, bool skipAwait = false) | |||||
| { | { | ||||
| int oldState; | int oldState; | ||||
| bool hasWriterLock; | bool hasWriterLock; | ||||
| @@ -120,9 +121,12 @@ namespace Discord.Net.WebSockets | |||||
| _cancelToken.Cancel(); | _cancelToken.Cancel(); | ||||
| } | } | ||||
| Task task = _runTask; | |||||
| if (task != null) | |||||
| await task.ConfigureAwait(false); | |||||
| if (!skipAwait) | |||||
| { | |||||
| Task task = _runTask; | |||||
| if (task != null) | |||||
| await task.ConfigureAwait(false); | |||||
| } | |||||
| if (hasWriterLock) | if (hasWriterLock) | ||||
| { | { | ||||
| @@ -133,12 +137,13 @@ namespace Discord.Net.WebSockets | |||||
| protected virtual async Task RunTasks() | protected virtual async Task RunTasks() | ||||
| { | { | ||||
| Task[] tasks = Run(); | |||||
| Task task = Task.WhenAll(Run()); | |||||
| try | try | ||||
| { | { | ||||
| await Task.WhenAll(tasks).ConfigureAwait(false); | |||||
| await task.ConfigureAwait(false); | |||||
| } | } | ||||
| catch (Exception ex) { await DisconnectInternal(ex).ConfigureAwait(false); } | |||||
| catch (Exception ex) { await DisconnectInternal(ex, skipAwait: true).ConfigureAwait(false); } | |||||
| bool wasUnexpected = _wasDisconnectUnexpected; | bool wasUnexpected = _wasDisconnectUnexpected; | ||||
| _wasDisconnectUnexpected = false; | _wasDisconnectUnexpected = false; | ||||
| @@ -147,7 +152,13 @@ namespace Discord.Net.WebSockets | |||||
| await Cleanup().ConfigureAwait(false); | await Cleanup().ConfigureAwait(false); | ||||
| _runTask = null; | _runTask = null; | ||||
| } | } | ||||
| protected virtual Task[] Run() { return _engine.RunTasks(_cancelToken.Token); } | |||||
| protected virtual Task[] Run() | |||||
| { | |||||
| var cancelToken = _cancelToken.Token; | |||||
| return _engine.RunTasks(cancelToken) | |||||
| .Concat(new Task[] { HeartbeatAsync(cancelToken) }) | |||||
| .ToArray(); | |||||
| } | |||||
| protected virtual Task Cleanup() { return TaskHelper.CompletedTask; } | protected virtual Task Cleanup() { return TaskHelper.CompletedTask; } | ||||
| protected abstract Task ProcessMessage(string json); | protected abstract Task ProcessMessage(string json); | ||||