Reduced command module lifetime to a single command execution. Removed ModuleAttribute.
This commit is contained in:
@@ -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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user