Reduced command module lifetime to a single command execution. Removed ModuleAttribute.
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class DontAutoLoadAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
public class InAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class ModuleAttribute : Attribute
|
||||
{
|
||||
public string Prefix { get; }
|
||||
public bool AutoLoad { get; set; }
|
||||
|
||||
public ModuleAttribute()
|
||||
{
|
||||
Prefix = null;
|
||||
AutoLoad = true;
|
||||
}
|
||||
public ModuleAttribute(string prefix)
|
||||
{
|
||||
Prefix = prefix;
|
||||
AutoLoad = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,6 @@ namespace Discord.Commands
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
|
||||
public abstract class PreconditionAttribute : Attribute
|
||||
{
|
||||
public abstract Task<PreconditionResult> CheckPermissions(CommandContext context, Command executingCommand, object moduleInstance);
|
||||
public abstract Task<PreconditionResult> CheckPermissions(CommandContext context, CommandInfo command, IDependencyMap map);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Discord.Commands
|
||||
Contexts = contexts;
|
||||
}
|
||||
|
||||
public override Task<PreconditionResult> CheckPermissions(CommandContext context, Command executingCommand, object moduleInstance)
|
||||
public override Task<PreconditionResult> CheckPermissions(CommandContext context, CommandInfo command, IDependencyMap map)
|
||||
{
|
||||
bool isValid = false;
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Discord.Commands
|
||||
GuildPermission = null;
|
||||
}
|
||||
|
||||
public override Task<PreconditionResult> CheckPermissions(CommandContext context, Command executingCommand, object moduleInstance)
|
||||
public override Task<PreconditionResult> CheckPermissions(CommandContext context, CommandInfo command, IDependencyMap map)
|
||||
{
|
||||
var guildUser = context.User as IGuildUser;
|
||||
|
||||
|
||||
@@ -10,16 +10,15 @@ using System.Threading.Tasks;
|
||||
namespace Discord.Commands
|
||||
{
|
||||
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
||||
public class Command
|
||||
public class CommandInfo
|
||||
{
|
||||
private static readonly MethodInfo _convertParamsMethod = typeof(Command).GetTypeInfo().GetDeclaredMethod(nameof(ConvertParamsList));
|
||||
private static readonly MethodInfo _convertParamsMethod = typeof(CommandInfo).GetTypeInfo().GetDeclaredMethod(nameof(ConvertParamsList));
|
||||
private static readonly ConcurrentDictionary<Type, Func<IEnumerable<object>, object>> _arrayConverters = new ConcurrentDictionary<Type, Func<IEnumerable<object>, object>>();
|
||||
|
||||
private readonly object _instance;
|
||||
private readonly Func<CommandContext, IReadOnlyList<object>, Task> _action;
|
||||
|
||||
private readonly Func<CommandContext, object[], Task> _action;
|
||||
|
||||
public MethodInfo Source { get; }
|
||||
public Module Module { get; }
|
||||
public ModuleInfo Module { get; }
|
||||
public string Name { get; }
|
||||
public string Summary { get; }
|
||||
public string Remarks { get; }
|
||||
@@ -30,13 +29,12 @@ namespace Discord.Commands
|
||||
public IReadOnlyList<CommandParameter> Parameters { get; }
|
||||
public IReadOnlyList<PreconditionAttribute> Preconditions { get; }
|
||||
|
||||
internal Command(MethodInfo source, Module module, object instance, CommandAttribute attribute, string groupPrefix)
|
||||
internal CommandInfo(MethodInfo source, ModuleInfo module, CommandAttribute attribute, string groupPrefix)
|
||||
{
|
||||
try
|
||||
{
|
||||
Source = source;
|
||||
Module = module;
|
||||
_instance = instance;
|
||||
|
||||
Name = source.Name;
|
||||
|
||||
@@ -85,18 +83,18 @@ namespace Discord.Commands
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<PreconditionResult> CheckPreconditions(CommandContext context)
|
||||
public async Task<PreconditionResult> CheckPreconditions(CommandContext context, IDependencyMap map = null)
|
||||
{
|
||||
foreach (PreconditionAttribute precondition in Module.Preconditions)
|
||||
{
|
||||
var result = await precondition.CheckPermissions(context, this, Module.Instance).ConfigureAwait(false);
|
||||
var result = await precondition.CheckPermissions(context, this, map).ConfigureAwait(false);
|
||||
if (!result.IsSuccess)
|
||||
return result;
|
||||
}
|
||||
|
||||
foreach (PreconditionAttribute precondition in Preconditions)
|
||||
{
|
||||
var result = await precondition.CheckPermissions(context, this, Module.Instance).ConfigureAwait(false);
|
||||
var result = await precondition.CheckPermissions(context, this, map).ConfigureAwait(false);
|
||||
if (!result.IsSuccess)
|
||||
return result;
|
||||
}
|
||||
@@ -169,11 +167,9 @@ namespace Discord.Commands
|
||||
private IReadOnlyList<CommandParameter> BuildParameters(MethodInfo methodInfo)
|
||||
{
|
||||
var parameters = methodInfo.GetParameters();
|
||||
if (parameters.Length == 0 || parameters[0].ParameterType != typeof(CommandContext))
|
||||
throw new InvalidOperationException($"The first parameter of a command must be {nameof(CommandContext)}.");
|
||||
|
||||
var paramBuilder = ImmutableArray.CreateBuilder<CommandParameter>(parameters.Length - 1);
|
||||
for (int i = 1; i < parameters.Length; i++)
|
||||
var paramBuilder = ImmutableArray.CreateBuilder<CommandParameter>(parameters.Length);
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
var parameter = parameters[i];
|
||||
var type = parameter.ParameterType;
|
||||
@@ -209,19 +205,23 @@ namespace Discord.Commands
|
||||
}
|
||||
return paramBuilder.ToImmutable();
|
||||
}
|
||||
private Func<CommandContext, IReadOnlyList<object>, Task> BuildAction(MethodInfo methodInfo)
|
||||
private Func<CommandContext, object[], Task> BuildAction(MethodInfo methodInfo)
|
||||
{
|
||||
if (methodInfo.ReturnType != typeof(Task))
|
||||
throw new InvalidOperationException("Commands must return a non-generic Task.");
|
||||
|
||||
return (msg, args) =>
|
||||
return (context, args) =>
|
||||
{
|
||||
object[] newArgs = new object[args.Count + 1];
|
||||
newArgs[0] = msg;
|
||||
for (int i = 0; i < args.Count; i++)
|
||||
newArgs[i + 1] = args[i];
|
||||
var result = methodInfo.Invoke(_instance, newArgs);
|
||||
return result as Task ?? Task.CompletedTask;
|
||||
var instance = Module.CreateInstance();
|
||||
instance.Context = context;
|
||||
try
|
||||
{
|
||||
return methodInfo.Invoke(instance, args) as Task ?? Task.CompletedTask;
|
||||
}
|
||||
finally
|
||||
{
|
||||
(instance as IDisposable)?.Dispose();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Discord.Commands
|
||||
QuotedParameter
|
||||
}
|
||||
|
||||
public static async Task<ParseResult> ParseArgs(Command command, CommandContext context, string input, int startPos)
|
||||
public static async Task<ParseResult> ParseArgs(CommandInfo command, CommandContext context, string input, int startPos)
|
||||
{
|
||||
CommandParameter curParam = null;
|
||||
StringBuilder argBuilder = new StringBuilder(input.Length);
|
||||
|
||||
@@ -11,18 +11,20 @@ namespace Discord.Commands
|
||||
{
|
||||
public class CommandService
|
||||
{
|
||||
private static readonly TypeInfo _moduleTypeInfo = typeof(ModuleBase).GetTypeInfo();
|
||||
|
||||
private readonly SemaphoreSlim _moduleLock;
|
||||
private readonly ConcurrentDictionary<Type, Module> _modules;
|
||||
private readonly ConcurrentDictionary<Type, ModuleInfo> _moduleDefs;
|
||||
private readonly ConcurrentDictionary<Type, TypeReader> _typeReaders;
|
||||
private readonly CommandMap _map;
|
||||
|
||||
public IEnumerable<Module> Modules => _modules.Select(x => x.Value);
|
||||
public IEnumerable<Command> Commands => _modules.SelectMany(x => x.Value.Commands);
|
||||
public IEnumerable<ModuleInfo> Modules => _moduleDefs.Select(x => x.Value);
|
||||
public IEnumerable<CommandInfo> Commands => _moduleDefs.SelectMany(x => x.Value.Commands);
|
||||
|
||||
public CommandService()
|
||||
{
|
||||
_moduleLock = new SemaphoreSlim(1, 1);
|
||||
_modules = new ConcurrentDictionary<Type, Module>();
|
||||
_moduleDefs = new ConcurrentDictionary<Type, ModuleInfo>();
|
||||
_map = new CommandMap();
|
||||
_typeReaders = new ConcurrentDictionary<Type, TypeReader>
|
||||
{
|
||||
@@ -45,7 +47,6 @@ namespace Discord.Commands
|
||||
|
||||
[typeof(IMessage)] = new MessageTypeReader<IMessage>(),
|
||||
[typeof(IUserMessage)] = new MessageTypeReader<IUserMessage>(),
|
||||
//[typeof(ISystemMessage)] = new MessageTypeReader<ISystemMessage>(),
|
||||
[typeof(IChannel)] = new ChannelTypeReader<IChannel>(),
|
||||
[typeof(IDMChannel)] = new ChannelTypeReader<IDMChannel>(),
|
||||
[typeof(IGroupChannel)] = new ChannelTypeReader<IGroupChannel>(),
|
||||
@@ -55,19 +56,109 @@ namespace Discord.Commands
|
||||
[typeof(ITextChannel)] = new ChannelTypeReader<ITextChannel>(),
|
||||
[typeof(IVoiceChannel)] = new ChannelTypeReader<IVoiceChannel>(),
|
||||
|
||||
//[typeof(IGuild)] = new GuildTypeReader<IGuild>(),
|
||||
|
||||
[typeof(IRole)] = new RoleTypeReader<IRole>(),
|
||||
|
||||
//[typeof(IInvite)] = new InviteTypeReader<IInvite>(),
|
||||
//[typeof(IInviteMetadata)] = new InviteTypeReader<IInviteMetadata>(),
|
||||
|
||||
[typeof(IUser)] = new UserTypeReader<IUser>(),
|
||||
[typeof(IGroupUser)] = new UserTypeReader<IGroupUser>(),
|
||||
[typeof(IGuildUser)] = new UserTypeReader<IGuildUser>(),
|
||||
};
|
||||
}
|
||||
|
||||
//Modules
|
||||
public async Task<ModuleInfo> AddModule<T>(IDependencyMap dependencyMap = null)
|
||||
{
|
||||
await _moduleLock.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
if (_moduleDefs.ContainsKey(typeof(T)))
|
||||
throw new ArgumentException($"This module has already been added.");
|
||||
|
||||
var typeInfo = typeof(T).GetTypeInfo();
|
||||
if (!_moduleTypeInfo.IsAssignableFrom(typeInfo))
|
||||
throw new ArgumentException($"Modules must inherit ModuleBase.");
|
||||
|
||||
return AddModuleInternal(typeInfo, dependencyMap);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_moduleLock.Release();
|
||||
}
|
||||
}
|
||||
public async Task<IEnumerable<ModuleInfo>> AddModules(Assembly assembly, IDependencyMap dependencyMap = null)
|
||||
{
|
||||
var moduleDefs = ImmutableArray.CreateBuilder<ModuleInfo>();
|
||||
await _moduleLock.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
foreach (var type in assembly.ExportedTypes)
|
||||
{
|
||||
if (!_moduleDefs.ContainsKey(type))
|
||||
{
|
||||
var typeInfo = type.GetTypeInfo();
|
||||
if (_moduleTypeInfo.IsAssignableFrom(typeInfo))
|
||||
{
|
||||
var dontAutoLoad = typeInfo.GetCustomAttribute<DontAutoLoadAttribute>();
|
||||
if (dontAutoLoad == null)
|
||||
moduleDefs.Add(AddModuleInternal(typeInfo, dependencyMap));
|
||||
}
|
||||
}
|
||||
}
|
||||
return moduleDefs.ToImmutable();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_moduleLock.Release();
|
||||
}
|
||||
}
|
||||
private ModuleInfo AddModuleInternal(TypeInfo typeInfo, IDependencyMap dependencyMap)
|
||||
{
|
||||
var moduleDef = new ModuleInfo(typeInfo, this, dependencyMap);
|
||||
_moduleDefs[typeInfo.BaseType] = moduleDef;
|
||||
|
||||
foreach (var cmd in moduleDef.Commands)
|
||||
_map.AddCommand(cmd);
|
||||
|
||||
return moduleDef;
|
||||
}
|
||||
|
||||
public async Task<bool> RemoveModule(ModuleInfo module)
|
||||
{
|
||||
await _moduleLock.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
return RemoveModuleInternal(module.Source.BaseType);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_moduleLock.Release();
|
||||
}
|
||||
}
|
||||
public async Task<bool> RemoveModule<T>()
|
||||
{
|
||||
await _moduleLock.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
return RemoveModuleInternal(typeof(T));
|
||||
}
|
||||
finally
|
||||
{
|
||||
_moduleLock.Release();
|
||||
}
|
||||
}
|
||||
private bool RemoveModuleInternal(Type type)
|
||||
{
|
||||
ModuleInfo unloadedModule;
|
||||
if (_moduleDefs.TryRemove(type, out unloadedModule))
|
||||
{
|
||||
foreach (var cmd in unloadedModule.Commands)
|
||||
_map.RemoveCommand(cmd);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
//Type Readers
|
||||
public void AddTypeReader<T>(TypeReader reader)
|
||||
{
|
||||
_typeReaders[typeof(T)] = reader;
|
||||
@@ -84,100 +175,7 @@ namespace Discord.Commands
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<Module> Load(object moduleInstance)
|
||||
{
|
||||
await _moduleLock.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
if (_modules.ContainsKey(moduleInstance.GetType()))
|
||||
throw new ArgumentException($"This module has already been loaded.");
|
||||
|
||||
var typeInfo = moduleInstance.GetType().GetTypeInfo();
|
||||
var moduleAttr = typeInfo.GetCustomAttribute<ModuleAttribute>();
|
||||
if (moduleAttr == null)
|
||||
throw new ArgumentException($"Modules must be marked with ModuleAttribute.");
|
||||
|
||||
return LoadInternal(moduleInstance, moduleAttr, typeInfo, null);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_moduleLock.Release();
|
||||
}
|
||||
}
|
||||
private Module LoadInternal(object moduleInstance, ModuleAttribute moduleAttr, TypeInfo typeInfo, IDependencyMap dependencyMap)
|
||||
{
|
||||
if (_modules.ContainsKey(moduleInstance.GetType()))
|
||||
return _modules[moduleInstance.GetType()];
|
||||
|
||||
var loadedModule = new Module(typeInfo, this, moduleInstance, moduleAttr, dependencyMap);
|
||||
_modules[moduleInstance.GetType()] = loadedModule;
|
||||
|
||||
foreach (var cmd in loadedModule.Commands)
|
||||
_map.AddCommand(cmd);
|
||||
|
||||
return loadedModule;
|
||||
}
|
||||
public async Task<IEnumerable<Module>> LoadAssembly(Assembly assembly, IDependencyMap dependencyMap = null)
|
||||
{
|
||||
var modules = ImmutableArray.CreateBuilder<Module>();
|
||||
await _moduleLock.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
foreach (var type in assembly.ExportedTypes)
|
||||
{
|
||||
var typeInfo = type.GetTypeInfo();
|
||||
var moduleAttr = typeInfo.GetCustomAttribute<ModuleAttribute>();
|
||||
if (moduleAttr != null && moduleAttr.AutoLoad)
|
||||
{
|
||||
var moduleInstance = ReflectionUtils.CreateObject(typeInfo, this, dependencyMap);
|
||||
modules.Add(LoadInternal(moduleInstance, moduleAttr, typeInfo, dependencyMap));
|
||||
}
|
||||
}
|
||||
return modules.ToImmutable();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_moduleLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> Unload(Module module)
|
||||
{
|
||||
await _moduleLock.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
return UnloadInternal(module.Instance);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_moduleLock.Release();
|
||||
}
|
||||
}
|
||||
public async Task<bool> Unload(object moduleInstance)
|
||||
{
|
||||
await _moduleLock.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
return UnloadInternal(moduleInstance);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_moduleLock.Release();
|
||||
}
|
||||
}
|
||||
private bool UnloadInternal(object module)
|
||||
{
|
||||
Module unloadedModule;
|
||||
if (_modules.TryRemove(module.GetType(), out unloadedModule))
|
||||
{
|
||||
foreach (var cmd in unloadedModule.Commands)
|
||||
_map.RemoveCommand(cmd);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
//Execution
|
||||
public SearchResult Search(CommandContext context, int argPos) => Search(context, context.Message.Content.Substring(argPos));
|
||||
public SearchResult Search(CommandContext context, string input)
|
||||
{
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Discord.Commands
|
||||
_nodes = new ConcurrentDictionary<string, CommandMapNode>();
|
||||
}
|
||||
|
||||
public void AddCommand(Command command)
|
||||
public void AddCommand(CommandInfo command)
|
||||
{
|
||||
foreach (string text in command.Aliases)
|
||||
{
|
||||
@@ -35,7 +35,7 @@ namespace Discord.Commands
|
||||
}
|
||||
}
|
||||
}
|
||||
public void RemoveCommand(Command command)
|
||||
public void RemoveCommand(CommandInfo command)
|
||||
{
|
||||
foreach (string text in command.Aliases)
|
||||
{
|
||||
@@ -60,7 +60,7 @@ namespace Discord.Commands
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<Command> GetCommands(string text)
|
||||
public IEnumerable<CommandInfo> GetCommands(string text)
|
||||
{
|
||||
int nextSpace = NextWhitespace(text);
|
||||
string name;
|
||||
@@ -76,7 +76,7 @@ namespace Discord.Commands
|
||||
if (_nodes.TryGetValue(name, out nextNode))
|
||||
return nextNode.GetCommands(text, nextSpace + 1);
|
||||
else
|
||||
return Enumerable.Empty<Command>();
|
||||
return Enumerable.Empty<CommandInfo>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Discord.Commands
|
||||
private readonly ConcurrentDictionary<string, CommandMapNode> _nodes;
|
||||
private readonly string _name;
|
||||
private readonly object _lockObj = new object();
|
||||
private ImmutableArray<Command> _commands;
|
||||
private ImmutableArray<CommandInfo> _commands;
|
||||
|
||||
public bool IsEmpty => _commands.Length == 0 && _nodes.Count == 0;
|
||||
|
||||
@@ -17,10 +17,10 @@ namespace Discord.Commands
|
||||
{
|
||||
_name = name;
|
||||
_nodes = new ConcurrentDictionary<string, CommandMapNode>();
|
||||
_commands = ImmutableArray.Create<Command>();
|
||||
_commands = ImmutableArray.Create<CommandInfo>();
|
||||
}
|
||||
|
||||
public void AddCommand(string text, int index, Command command)
|
||||
public void AddCommand(string text, int index, CommandInfo command)
|
||||
{
|
||||
int nextSpace = text.IndexOf(' ', index);
|
||||
string name;
|
||||
@@ -41,7 +41,7 @@ namespace Discord.Commands
|
||||
}
|
||||
}
|
||||
}
|
||||
public void RemoveCommand(string text, int index, Command command)
|
||||
public void RemoveCommand(string text, int index, CommandInfo command)
|
||||
{
|
||||
int nextSpace = text.IndexOf(' ', index);
|
||||
string name;
|
||||
@@ -68,7 +68,7 @@ namespace Discord.Commands
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<Command> GetCommands(string text, int index)
|
||||
public IEnumerable<CommandInfo> GetCommands(string text, int index)
|
||||
{
|
||||
int nextSpace = text.IndexOf(' ', index);
|
||||
string name;
|
||||
|
||||
15
src/Discord.Net.Commands/ModuleBase.cs
Normal file
15
src/Discord.Net.Commands/ModuleBase.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
public abstract class ModuleBase
|
||||
{
|
||||
public IDiscordClient Client { get; internal set; }
|
||||
public CommandContext Context { get; internal set; }
|
||||
|
||||
protected virtual async Task ReplyAsync(string message, bool isTTS = false, RequestOptions options = null)
|
||||
{
|
||||
await Context.Channel.SendMessageAsync(message, isTTS, options).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
@@ -6,26 +7,31 @@ using System.Reflection;
|
||||
namespace Discord.Commands
|
||||
{
|
||||
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
||||
public class Module
|
||||
public class ModuleInfo
|
||||
{
|
||||
internal readonly Func<ModuleBase> _builder;
|
||||
|
||||
public TypeInfo Source { get; }
|
||||
public CommandService Service { get; }
|
||||
public string Name { get; }
|
||||
public string Prefix { get; }
|
||||
public string Summary { get; }
|
||||
public string Remarks { get; }
|
||||
public IEnumerable<Command> Commands { get; }
|
||||
internal object Instance { get; }
|
||||
|
||||
public IEnumerable<CommandInfo> Commands { get; }
|
||||
public IReadOnlyList<PreconditionAttribute> Preconditions { get; }
|
||||
|
||||
internal Module(TypeInfo source, CommandService service, object instance, ModuleAttribute moduleAttr, IDependencyMap dependencyMap)
|
||||
internal ModuleInfo(TypeInfo source, CommandService service, IDependencyMap dependencyMap)
|
||||
{
|
||||
Source = source;
|
||||
Service = service;
|
||||
Name = source.Name;
|
||||
Prefix = moduleAttr.Prefix ?? "";
|
||||
Instance = instance;
|
||||
_builder = ReflectionUtils.CreateBuilder<ModuleBase>(source, Service, dependencyMap);
|
||||
|
||||
var groupAttr = source.GetCustomAttribute<GroupAttribute>();
|
||||
if (groupAttr != null)
|
||||
Prefix = groupAttr.Prefix;
|
||||
else
|
||||
Prefix = "";
|
||||
|
||||
var nameAttr = source.GetCustomAttribute<NameAttribute>();
|
||||
if (nameAttr != null)
|
||||
@@ -39,20 +45,19 @@ namespace Discord.Commands
|
||||
if (remarksAttr != null)
|
||||
Remarks = remarksAttr.Text;
|
||||
|
||||
List<Command> commands = new List<Command>();
|
||||
SearchClass(source, instance, commands, Prefix, dependencyMap);
|
||||
List<CommandInfo> commands = new List<CommandInfo>();
|
||||
SearchClass(source, commands, Prefix, dependencyMap);
|
||||
Commands = commands;
|
||||
|
||||
Preconditions = BuildPreconditions();
|
||||
Preconditions = Source.GetCustomAttributes<PreconditionAttribute>().ToImmutableArray();
|
||||
}
|
||||
|
||||
private void SearchClass(TypeInfo parentType, object instance, List<Command> commands, string groupPrefix, IDependencyMap dependencyMap)
|
||||
private void SearchClass(TypeInfo parentType, List<CommandInfo> commands, string groupPrefix, IDependencyMap dependencyMap)
|
||||
{
|
||||
foreach (var method in parentType.DeclaredMethods)
|
||||
{
|
||||
var cmdAttr = method.GetCustomAttribute<CommandAttribute>();
|
||||
if (cmdAttr != null)
|
||||
commands.Add(new Command(method, this, instance, cmdAttr, groupPrefix));
|
||||
commands.Add(new CommandInfo(method, this, cmdAttr, groupPrefix));
|
||||
}
|
||||
foreach (var type in parentType.DeclaredNestedTypes)
|
||||
{
|
||||
@@ -66,15 +71,13 @@ namespace Discord.Commands
|
||||
else
|
||||
nextGroupPrefix = groupAttrib.Prefix ?? type.Name.ToLowerInvariant();
|
||||
|
||||
SearchClass(type, ReflectionUtils.CreateObject(type, Service, dependencyMap), commands, nextGroupPrefix, dependencyMap);
|
||||
SearchClass(type, commands, nextGroupPrefix, dependencyMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IReadOnlyList<PreconditionAttribute> BuildPreconditions()
|
||||
{
|
||||
return Source.GetCustomAttributes<PreconditionAttribute>().ToImmutableArray();
|
||||
}
|
||||
internal ModuleBase CreateInstance()
|
||||
=> _builder();
|
||||
|
||||
public override string ToString() => Name;
|
||||
private string DebuggerDisplay => Name;
|
||||
@@ -6,7 +6,10 @@ namespace Discord.Commands
|
||||
{
|
||||
internal class ReflectionUtils
|
||||
{
|
||||
internal static object CreateObject(TypeInfo typeInfo, CommandService service, IDependencyMap map = null)
|
||||
internal static T CreateObject<T>(TypeInfo typeInfo, CommandService service, IDependencyMap map = null)
|
||||
=> CreateBuilder<T>(typeInfo, service, map)();
|
||||
|
||||
internal static Func<T> CreateBuilder<T>(TypeInfo typeInfo, CommandService service, IDependencyMap map = null)
|
||||
{
|
||||
var constructors = typeInfo.DeclaredConstructors.Where(x => !x.IsStatic).ToArray();
|
||||
if (constructors.Length == 0)
|
||||
@@ -14,7 +17,7 @@ namespace Discord.Commands
|
||||
else if (constructors.Length > 1)
|
||||
throw new InvalidOperationException($"Multiple constructors found for \"{typeInfo.FullName}\"");
|
||||
|
||||
var constructor = constructors[0];
|
||||
var constructor = constructors[0];
|
||||
ParameterInfo[] parameters = constructor.GetParameters();
|
||||
object[] args = new object[parameters.Length];
|
||||
|
||||
@@ -34,14 +37,17 @@ namespace Discord.Commands
|
||||
args[i] = arg;
|
||||
}
|
||||
|
||||
try
|
||||
return () =>
|
||||
{
|
||||
return constructor.Invoke(args);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"Failed to create \"{typeInfo.FullName}\"", ex);
|
||||
}
|
||||
try
|
||||
{
|
||||
return (T)constructor.Invoke(args);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"Failed to create \"{typeInfo.FullName}\"", ex);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,14 +7,14 @@ namespace Discord.Commands
|
||||
public struct SearchResult : IResult
|
||||
{
|
||||
public string Text { get; }
|
||||
public IReadOnlyList<Command> Commands { get; }
|
||||
public IReadOnlyList<CommandInfo> Commands { get; }
|
||||
|
||||
public CommandError? Error { get; }
|
||||
public string ErrorReason { get; }
|
||||
|
||||
public bool IsSuccess => !Error.HasValue;
|
||||
|
||||
private SearchResult(string text, IReadOnlyList<Command> commands, CommandError? error, string errorReason)
|
||||
private SearchResult(string text, IReadOnlyList<CommandInfo> commands, CommandError? error, string errorReason)
|
||||
{
|
||||
Text = text;
|
||||
Commands = commands;
|
||||
@@ -22,7 +22,7 @@ namespace Discord.Commands
|
||||
ErrorReason = errorReason;
|
||||
}
|
||||
|
||||
public static SearchResult FromSuccess(string text, IReadOnlyList<Command> commands)
|
||||
public static SearchResult FromSuccess(string text, IReadOnlyList<CommandInfo> commands)
|
||||
=> new SearchResult(text, commands, null, null);
|
||||
public static SearchResult FromError(CommandError error, string reason)
|
||||
=> new SearchResult(null, null, error, reason);
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Discord
|
||||
{
|
||||
//Based on https://github.com/dotnet/corefx/blob/d0dc5fc099946adc1035b34a8b1f6042eddb0c75/src/System.Threading.Tasks.Parallel/src/System/Threading/PlatformHelper.cs
|
||||
//Copyright (c) .NET Foundation and Contributors
|
||||
public static class ConcurrentHashSet
|
||||
internal static class ConcurrentHashSet
|
||||
{
|
||||
private const int PROCESSOR_COUNT_REFRESH_INTERVAL_MS = 30000;
|
||||
private static volatile int s_processorCount;
|
||||
|
||||
Reference in New Issue
Block a user