Added remaining gateway events, added IAudioChannel, added CacheModes

This commit is contained in:
RogueException
2016-10-04 07:32:26 -03:00
parent e038475ab4
commit 4678544fed
58 changed files with 1685 additions and 855 deletions

View File

@@ -9,6 +9,11 @@ namespace Discord
{
//Based on https://github.com/dotnet/corefx/blob/master/src/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentDictionary.cs
//Copyright (c) .NET Foundation and Contributors
public static class ConcurrentHashSet
{
public static int DefaultConcurrencyLevel => PlatformHelper.ProcessorCount;
}
[DebuggerDisplay("Count = {Count}")]
internal class ConcurrentHashSet<T> : IReadOnlyCollection<T>
{

View File

@@ -1,39 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace Discord
{
internal static class CollectionExtensions
{
public static IReadOnlyCollection<TValue> ToReadOnlyCollection<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> source)
=> new ConcurrentDictionaryWrapper<TValue>(source.Select(x => x.Value), () => source.Count);
public static IReadOnlyCollection<TValue> ToReadOnlyCollection<TValue, TSource>(this IEnumerable<TValue> query, IReadOnlyCollection<TSource> source)
=> new ConcurrentDictionaryWrapper<TValue>(query, () => source.Count);
public static IReadOnlyCollection<TValue> ToReadOnlyCollection<TValue>(this IEnumerable<TValue> query, Func<int> countFunc)
=> new ConcurrentDictionaryWrapper<TValue>(query, countFunc);
}
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
internal struct ConcurrentDictionaryWrapper<TValue> : IReadOnlyCollection<TValue>
{
private readonly IEnumerable<TValue> _query;
private readonly Func<int> _countFunc;
//It's okay that this count is affected by race conditions - we're wrapping a concurrent collection and that's to be expected
public int Count => _countFunc();
public ConcurrentDictionaryWrapper(IEnumerable<TValue> query, Func<int> countFunc)
{
_query = query;
_countFunc = countFunc;
}
private string DebuggerDisplay => $"Count = {Count}";
public IEnumerator<TValue> GetEnumerator() => _query.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _query.GetEnumerator();
}
}

View File

@@ -1,152 +0,0 @@
using System.Runtime.CompilerServices;
namespace Discord.Extensions
{
internal static class Permissions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static PermValue GetValue(ulong allow, ulong deny, ChannelPermission bit)
=> GetValue(allow, deny, (byte)bit);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static PermValue GetValue(ulong allow, ulong deny, GuildPermission bit)
=> GetValue(allow, deny, (byte)bit);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static PermValue GetValue(ulong allow, ulong deny, byte bit)
{
if (HasBit(allow, bit))
return PermValue.Allow;
else if (HasBit(deny, bit))
return PermValue.Deny;
else
return PermValue.Inherit;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool GetValue(ulong value, ChannelPermission bit)
=> GetValue(value, (byte)bit);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool GetValue(ulong value, GuildPermission bit)
=> GetValue(value, (byte)bit);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool GetValue(ulong value, byte bit) => HasBit(value, bit);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SetValue(ref ulong rawValue, bool? value, ChannelPermission bit)
=> SetValue(ref rawValue, value, (byte)bit);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SetValue(ref ulong rawValue, bool? value, GuildPermission bit)
=> SetValue(ref rawValue, value, (byte)bit);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SetValue(ref ulong rawValue, bool? value, byte bit)
{
if (value.HasValue)
{
if (value == true)
SetBit(ref rawValue, bit);
else
UnsetBit(ref rawValue, bit);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SetValue(ref ulong allow, ref ulong deny, PermValue? value, ChannelPermission bit)
=> SetValue(ref allow, ref deny, value, (byte)bit);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SetValue(ref ulong allow, ref ulong deny, PermValue? value, GuildPermission bit)
=> SetValue(ref allow, ref deny, value, (byte)bit);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SetValue(ref ulong allow, ref ulong deny, PermValue? value, byte bit)
{
if (value.HasValue)
{
switch (value)
{
case PermValue.Allow:
SetBit(ref allow, bit);
UnsetBit(ref deny, bit);
break;
case PermValue.Deny:
UnsetBit(ref allow, bit);
SetBit(ref deny, bit);
break;
default:
UnsetBit(ref allow, bit);
UnsetBit(ref deny, bit);
break;
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool HasBit(ulong value, byte bit) => (value & (1U << bit)) != 0;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SetBit(ref ulong value, byte bit) => value |= (1U << bit);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void UnsetBit(ref ulong value, byte bit) => value &= ~(1U << bit);
public static ulong ResolveGuild(IGuild guild, IGuildUser user)
{
ulong resolvedPermissions = 0;
if (user.Id == guild.OwnerId)
resolvedPermissions = GuildPermissions.All.RawValue; //Owners always have all permissions
else
{
foreach (var role in user.RoleIds)
resolvedPermissions |= guild.GetRole(role).Permissions.RawValue;
if (GetValue(resolvedPermissions, GuildPermission.Administrator))
resolvedPermissions = GuildPermissions.All.RawValue; //Administrators always have all permissions
}
return resolvedPermissions;
}
/*public static ulong ResolveChannel(IGuildUser user, IGuildChannel channel)
{
return ResolveChannel(user, channel, ResolveGuild(user));
}*/
public static ulong ResolveChannel(IGuild guild, IGuildChannel channel, IGuildUser user, ulong guildPermissions)
{
ulong resolvedPermissions = 0;
ulong mask = ChannelPermissions.All(channel).RawValue;
if (/*user.Id == user.Guild.OwnerId || */GetValue(guildPermissions, GuildPermission.Administrator))
resolvedPermissions = mask; //Owners and administrators always have all permissions
else
{
//Start with this user's guild permissions
resolvedPermissions = guildPermissions;
OverwritePermissions? perms;
var roleIds = user.RoleIds;
if (roleIds.Count > 0)
{
ulong deniedPermissions = 0UL, allowedPermissions = 0UL;
foreach (var roleId in roleIds)
{
perms = channel.GetPermissionOverwrite(guild.GetRole(roleId));
if (perms != null)
{
deniedPermissions |= perms.Value.DenyValue;
allowedPermissions |= perms.Value.AllowValue;
}
}
resolvedPermissions = (resolvedPermissions & ~deniedPermissions) | allowedPermissions;
}
perms = channel.GetPermissionOverwrite(user);
if (perms != null)
resolvedPermissions = (resolvedPermissions & ~perms.Value.DenyValue) | perms.Value.AllowValue;
//TODO: C#7 Typeswitch candidate
var textChannel = channel as ITextChannel;
var voiceChannel = channel as IVoiceChannel;
if (textChannel != null && !GetValue(resolvedPermissions, ChannelPermission.ReadMessages))
resolvedPermissions = 0; //No read permission on a text channel removes all other permissions
else if (voiceChannel != null && !GetValue(resolvedPermissions, ChannelPermission.Connect))
resolvedPermissions = 0; //No connect permission on a voice channel removes all other permissions
resolvedPermissions &= mask; //Ensure we didnt get any permissions this channel doesnt support (from guildPerms, for example)
}
return resolvedPermissions;
}
}
}

View File

@@ -1,15 +0,0 @@
using System;
using System.Threading.Tasks;
namespace Discord
{
internal static class TaskCompletionSourceExtensions
{
public static Task<bool> TrySetResultAsync<T>(this TaskCompletionSource<T> source, T result)
=> Task.Run(() => source.TrySetResult(result));
public static Task<bool> TrySetExceptionAsync<T>(this TaskCompletionSource<T> source, Exception ex)
=> Task.Run(() => source.TrySetException(ex));
public static Task<bool> TrySetCanceledAsync<T>(this TaskCompletionSource<T> source)
=> Task.Run(() => source.TrySetCanceled());
}
}

View File

@@ -3,6 +3,7 @@ using System.Collections.Immutable;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace Discord
{
@@ -34,7 +35,7 @@ namespace Discord
{
if (userMention.Id == id)
{
user = channel?.GetCachedUser(id) as TUser;
user = channel?.GetUserAsync(id, CacheMode.CacheOnly).GetAwaiter().GetResult() as TUser;
if (user == null) //User not found, fallback to basic mention info
user = userMention;
break;
@@ -136,7 +137,7 @@ namespace Discord
return "";
case ChannelMentionHandling.Name:
IGuildChannel channel = null;
channel = guild.GetCachedChannel(id);
channel = guild.GetChannelAsync(id, CacheMode.CacheOnly).GetAwaiter().GetResult();
if (channel != null)
return $"#{channel.Name}";
else