2017-05-14 12:02:51 +00:00
|
|
|
|
using Maki.Structures.Channels;
|
|
|
|
|
using Maki.Structures.Gateway;
|
|
|
|
|
using Maki.Structures.Guilds;
|
|
|
|
|
using Maki.Structures.Messages;
|
|
|
|
|
using Maki.Structures.Presences;
|
|
|
|
|
using Maki.Structures.Users;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
|
|
|
|
namespace Maki.Gateway
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gateway connection shard manager
|
|
|
|
|
/// </summary>
|
|
|
|
|
class GatewayShardClient : IDisposable
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Managed (active) shards
|
|
|
|
|
/// </summary>
|
2017-10-13 20:05:50 +00:00
|
|
|
|
private readonly List<GatewayShard> Shards = new List<GatewayShard>();
|
|
|
|
|
|
|
|
|
|
private readonly object Lock = new object();
|
2017-05-14 12:02:51 +00:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Parent DiscordClient instance
|
|
|
|
|
/// </summary>
|
2017-10-13 20:05:50 +00:00
|
|
|
|
private Discord Client;
|
2017-05-14 12:02:51 +00:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Number of active managed shards
|
|
|
|
|
/// </summary>
|
2017-10-13 20:05:50 +00:00
|
|
|
|
public int ShardCount
|
|
|
|
|
=> Shards.Count;
|
2017-05-14 12:02:51 +00:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Constructor
|
|
|
|
|
/// </summary>
|
2017-10-13 20:05:50 +00:00
|
|
|
|
/// <param name="client">Parent DiscordClient instance</param>
|
|
|
|
|
public GatewayShardClient(Discord client)
|
|
|
|
|
=> Client = client;
|
2017-05-14 12:02:51 +00:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Creates a new Gateway Shard
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="id">Id of the shard</param>
|
|
|
|
|
/// <returns>New Gateway Shard</returns>
|
|
|
|
|
public GatewayShard Create(int id)
|
|
|
|
|
{
|
2017-10-13 20:05:50 +00:00
|
|
|
|
GatewayShard shard = new GatewayShard(id, Client);
|
2017-11-13 11:39:27 +00:00
|
|
|
|
shard.OnSocketPayload += Shard_OnSocketPayload;
|
2017-10-13 20:05:50 +00:00
|
|
|
|
|
|
|
|
|
lock (Lock)
|
|
|
|
|
Shards.Add(shard);
|
|
|
|
|
|
2017-05-14 12:02:51 +00:00
|
|
|
|
return shard;
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-13 11:39:27 +00:00
|
|
|
|
private void Shard_OnSocketPayload(GatewayShard shard, GatewayPayload payload)
|
|
|
|
|
{
|
|
|
|
|
switch (payload.OPCode)
|
|
|
|
|
{
|
|
|
|
|
case GatewayOPCode.Dispatch:
|
|
|
|
|
switch (payload.Name)
|
|
|
|
|
{
|
|
|
|
|
#region Call
|
|
|
|
|
case "CALL_CREATE":
|
|
|
|
|
OnCallCreate?.Invoke(shard);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "CALL_DELETE":
|
|
|
|
|
OnCallDelete?.Invoke(shard);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "CALL_UPDATE":
|
|
|
|
|
OnCallUpdate?.Invoke(shard);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "VOICE_SERVER_UPDATE":
|
|
|
|
|
OnVoiceServerUpdate?.Invoke(shard);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "VOICE_STATE_UPDATE":
|
|
|
|
|
OnVoiceStateUpdate?.Invoke(shard);
|
|
|
|
|
break;
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Channel
|
|
|
|
|
case "CHANNEL_CREATE":
|
|
|
|
|
OnChannelCreate?.Invoke(shard, payload.DataAs<Channel>());
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "CHANNEL_DELETE":
|
|
|
|
|
OnChannelDelete?.Invoke(shard, payload.DataAs<Channel>());
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "CHANNEL_PINS_ACK":
|
|
|
|
|
OnChannelPinsAck?.Invoke(shard);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "CHANNEL_PINS_UPDATE":
|
|
|
|
|
OnChannelPinsUpdate?.Invoke(shard);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "CHANNEL_RECIPIENT_ADD":
|
|
|
|
|
OnChannelRecipientAdd?.Invoke(shard);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "CHANNEL_RECIPIENT_REMOVE":
|
|
|
|
|
OnChannelRecipientRemove?.Invoke(shard);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "CHANNEL_UPDATE":
|
|
|
|
|
OnChannelUpdate?.Invoke(shard, payload.DataAs<Channel>());
|
|
|
|
|
break;
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Guild
|
|
|
|
|
case "GUILD_BAN_ADD":
|
|
|
|
|
OnGuildBanAdd?.Invoke(shard, payload.DataAs<User>());
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "GUILD_BAN_REMOVE":
|
|
|
|
|
OnGuildBanRemove?.Invoke(shard, payload.DataAs<User>());
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "GUILD_CREATE":
|
|
|
|
|
OnGuildCreate?.Invoke(shard, payload.DataAs<Guild>());
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "GUILD_DELETE":
|
|
|
|
|
OnGuildDelete?.Invoke(shard, payload.DataAs<Guild>());
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "GUILD_EMOJIS_UPDATE":
|
|
|
|
|
OnGuildEmojisUpdate?.Invoke(shard, payload.DataAs<Guild>());
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "GUILD_INTEGRATIONS_UPDATE":
|
|
|
|
|
OnGuildIntegrationsUpdate?.Invoke(shard, payload.DataAs<GuildIntegration>());
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "GUILD_MEMBER_ADD":
|
|
|
|
|
OnGuildMemberAdd?.Invoke(shard, payload.DataAs<GuildMember>());
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "GUILD_MEMBER_UPDATE":
|
|
|
|
|
OnGuildMemberUpdate?.Invoke(shard, payload.DataAs<GuildMember>());
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "GUILD_MEMBER_REMOVE":
|
|
|
|
|
OnGuildMemberRemove?.Invoke(shard, payload.DataAs<GuildMember>());
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "GUILD_MEMBERS_CHUNK":
|
|
|
|
|
OnGuildMembersChunk?.Invoke(shard, payload.DataAs<GuildMembersChunk>());
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "GUILD_ROLE_CREATE":
|
|
|
|
|
OnGuildRoleCreate?.Invoke(shard, payload.DataAs<GuildRole>());
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "GUILD_ROLE_UPDATE":
|
|
|
|
|
OnGuildRoleUpdate?.Invoke(shard, payload.DataAs<GuildRole>());
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "GUILD_ROLE_DELETE":
|
|
|
|
|
OnGuildRoleDelete?.Invoke(shard, payload.DataAs<GuildRole>());
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "GUILD_UPDATE":
|
|
|
|
|
OnGuildUpdate?.Invoke(shard, payload.DataAs<Guild>());
|
|
|
|
|
break;
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Message
|
|
|
|
|
case "MESSAGE_ACK":
|
|
|
|
|
OnMessageAck?.Invoke(shard);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "MESSAGE_CREATE":
|
|
|
|
|
OnMessageCreate?.Invoke(shard, payload.DataAs<Message>());
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "MESSAGE_DELETE":
|
|
|
|
|
OnMessageDelete?.Invoke(shard, payload.DataAs<Message>());
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "MESSAGE_DELETE_BULK":
|
|
|
|
|
OnMessageDeleteBulk?.Invoke(shard);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "MESSAGE_REACTION_ADD":
|
|
|
|
|
OnMessageReactionAdd?.Invoke(shard);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "MESSAGE_REACTION_REMOVE":
|
|
|
|
|
OnMessageReactionRemove?.Invoke(shard);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "MESSAGE_REACTIONS_REMOVE_ALL":
|
|
|
|
|
OnMessageReactionsRemoveAll?.Invoke(shard);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "MESSAGE_UPDATE":
|
|
|
|
|
OnMessageUpdate?.Invoke(shard, payload.DataAs<Message>());
|
|
|
|
|
break;
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Presence
|
|
|
|
|
case "TYPING_START":
|
|
|
|
|
OnTypingStart?.Invoke(shard, payload.DataAs<TypingStart>());
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "PRESENCE_UPDATE":
|
|
|
|
|
OnPresenceUpdate?.Invoke(shard, payload.DataAs<Presence>());
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "PRESENCES_REPLACE":
|
|
|
|
|
OnPresencesReplace?.Invoke(shard);
|
|
|
|
|
break;
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Relations
|
|
|
|
|
case "FRIEND_SUGGESTION_DELETE":
|
|
|
|
|
OnFriendSuggestionDelete?.Invoke(shard);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "RELATIONSHIP_ADD":
|
|
|
|
|
OnRelationshipAdd?.Invoke(shard);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "RELATIONSHIP_REMOVE":
|
|
|
|
|
OnRelationshipRemove?.Invoke(shard);
|
|
|
|
|
break;
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region State
|
|
|
|
|
case "READY":
|
|
|
|
|
OnReady?.Invoke(shard, payload.DataAs<GatewayReady>());
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "RESUMED":
|
|
|
|
|
OnResumed?.Invoke(shard);
|
|
|
|
|
break;
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region User
|
|
|
|
|
case "USER_UPDATE":
|
|
|
|
|
OnUserUpdate?.Invoke(shard, payload.DataAs<User>());
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "USER_NOTE_UPDATE":
|
|
|
|
|
OnUserNoteUpdate?.Invoke(shard);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "USER_SETTINGS_UPDATE":
|
|
|
|
|
OnUserSettingsUpdate?.Invoke(shard);
|
|
|
|
|
break;
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#if DEBUG
|
|
|
|
|
default:
|
|
|
|
|
Console.WriteLine($"Unknown payload type: {payload.Name}");
|
|
|
|
|
break;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-14 12:02:51 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Destroys a Gateway Shard
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="shard">Shard to destroy</param>
|
|
|
|
|
public void Destroy(GatewayShard shard)
|
|
|
|
|
{
|
2017-10-13 20:05:50 +00:00
|
|
|
|
lock (Lock)
|
|
|
|
|
Shards.Remove(shard);
|
2017-05-14 12:02:51 +00:00
|
|
|
|
|
|
|
|
|
// Disconnect and Dispose are called separately so OnSocketClose is called properly
|
|
|
|
|
shard.Disconnect();
|
2017-11-13 11:39:27 +00:00
|
|
|
|
shard.OnSocketPayload -= Shard_OnSocketPayload;
|
2017-05-14 12:02:51 +00:00
|
|
|
|
shard.Dispose();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Destroys all shards
|
|
|
|
|
/// </summary>
|
2017-10-13 20:05:50 +00:00
|
|
|
|
public void Disconnect()
|
|
|
|
|
{
|
|
|
|
|
lock (Lock)
|
|
|
|
|
Shards.ToList().ForEach(x => Destroy(x));
|
|
|
|
|
}
|
2017-05-14 12:02:51 +00:00
|
|
|
|
|
|
|
|
|
#region Events
|
2017-11-13 11:39:27 +00:00
|
|
|
|
|
2017-05-14 12:02:51 +00:00
|
|
|
|
public event Action<GatewayShard> OnCallCreate;
|
|
|
|
|
public event Action<GatewayShard> OnCallDelete;
|
|
|
|
|
public event Action<GatewayShard> OnCallUpdate;
|
|
|
|
|
public event Action<GatewayShard> OnVoiceServerUpdate;
|
|
|
|
|
public event Action<GatewayShard> OnVoiceStateUpdate;
|
|
|
|
|
|
|
|
|
|
public event Action<GatewayShard, Channel> OnChannelCreate;
|
|
|
|
|
public event Action<GatewayShard, Channel> OnChannelDelete;
|
|
|
|
|
public event Action<GatewayShard> OnChannelPinsAck;
|
|
|
|
|
public event Action<GatewayShard> OnChannelPinsUpdate;
|
|
|
|
|
public event Action<GatewayShard> OnChannelRecipientAdd;
|
|
|
|
|
public event Action<GatewayShard> OnChannelRecipientRemove;
|
|
|
|
|
public event Action<GatewayShard, Channel> OnChannelUpdate;
|
|
|
|
|
|
|
|
|
|
public event Action<GatewayShard, User> OnGuildBanAdd;
|
|
|
|
|
public event Action<GatewayShard, User> OnGuildBanRemove;
|
|
|
|
|
public event Action<GatewayShard, Guild> OnGuildCreate;
|
|
|
|
|
public event Action<GatewayShard, Guild> OnGuildDelete;
|
|
|
|
|
public event Action<GatewayShard, Guild> OnGuildEmojisUpdate;
|
|
|
|
|
public event Action<GatewayShard, GuildIntegration> OnGuildIntegrationsUpdate;
|
|
|
|
|
public event Action<GatewayShard, GuildMember> OnGuildMemberAdd;
|
|
|
|
|
public event Action<GatewayShard, GuildMember> OnGuildMemberUpdate;
|
|
|
|
|
public event Action<GatewayShard, GuildMember> OnGuildMemberRemove;
|
|
|
|
|
public event Action<GatewayShard, GuildMembersChunk> OnGuildMembersChunk;
|
|
|
|
|
public event Action<GatewayShard, GuildRole> OnGuildRoleCreate;
|
|
|
|
|
public event Action<GatewayShard, GuildRole> OnGuildRoleUpdate;
|
|
|
|
|
public event Action<GatewayShard, GuildRole> OnGuildRoleDelete;
|
|
|
|
|
public event Action<GatewayShard, Guild> OnGuildUpdate;
|
|
|
|
|
|
|
|
|
|
public event Action<GatewayShard> OnMessageAck;
|
|
|
|
|
public event Action<GatewayShard, Message> OnMessageCreate;
|
|
|
|
|
public event Action<GatewayShard, Message> OnMessageDelete;
|
|
|
|
|
public event Action<GatewayShard> OnMessageDeleteBulk;
|
|
|
|
|
public event Action<GatewayShard> OnMessageReactionAdd;
|
|
|
|
|
public event Action<GatewayShard> OnMessageReactionRemove;
|
|
|
|
|
public event Action<GatewayShard> OnMessageReactionsRemoveAll;
|
|
|
|
|
public event Action<GatewayShard, Message> OnMessageUpdate;
|
|
|
|
|
|
|
|
|
|
public event Action<GatewayShard, TypingStart> OnTypingStart;
|
|
|
|
|
public event Action<GatewayShard, Presence> OnPresenceUpdate;
|
|
|
|
|
public event Action<GatewayShard> OnPresencesReplace;
|
|
|
|
|
|
|
|
|
|
public event Action<GatewayShard> OnFriendSuggestionDelete;
|
|
|
|
|
public event Action<GatewayShard> OnRelationshipAdd;
|
|
|
|
|
public event Action<GatewayShard> OnRelationshipRemove;
|
|
|
|
|
|
|
|
|
|
public event Action<GatewayShard, GatewayReady> OnReady;
|
|
|
|
|
public event Action<GatewayShard> OnResumed;
|
|
|
|
|
|
|
|
|
|
public event Action<GatewayShard, User> OnUserUpdate;
|
|
|
|
|
public event Action<GatewayShard> OnUserNoteUpdate;
|
|
|
|
|
public event Action<GatewayShard> OnUserSettingsUpdate;
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region IDisposable
|
|
|
|
|
|
|
|
|
|
private bool IsDisposed = false;
|
|
|
|
|
|
2017-10-13 20:05:50 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Releases all unmanaged resources used by this object
|
|
|
|
|
/// </summary>
|
2017-05-14 12:02:51 +00:00
|
|
|
|
private void Dispose(bool disposing)
|
|
|
|
|
{
|
2017-10-13 20:05:50 +00:00
|
|
|
|
if (IsDisposed)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
IsDisposed = true;
|
|
|
|
|
Disconnect();
|
|
|
|
|
|
|
|
|
|
if (disposing)
|
2017-11-13 09:42:07 +00:00
|
|
|
|
GC.SuppressFinalize(this);
|
2017-05-14 12:02:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~GatewayShardClient()
|
2017-10-13 20:05:50 +00:00
|
|
|
|
=> Dispose(false);
|
2017-05-14 12:02:51 +00:00
|
|
|
|
|
|
|
|
|
public void Dispose()
|
2017-10-13 20:05:50 +00:00
|
|
|
|
=> Dispose(true);
|
2017-05-14 12:02:51 +00:00
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
}
|
|
|
|
|
}
|