diff --git a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs
index 68dbe0314..dc02d2a43 100644
--- a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs
+++ b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs
@@ -13,28 +13,38 @@ namespace Discord.Commands
public static readonly TypeInfo ModuleTypeInfo = typeof(IModuleBase).GetTypeInfo();
///Value1 - ready types, Value2 - types dependant on Value1.
- public static async Task, IReadOnlyList>> SearchAsync(Assembly assembly, CommandService service)
+ public static async Task<(IReadOnlyList, IReadOnlyDictionary>)> SearchAsync(Assembly assembly, CommandService service)
{
//store bad parent modules to avoid duplicate error logs.
var badModules = new List();
var standardModules = new List();
- var queuedModules = new List();
+ var queuedModules = new Dictionary>();
foreach (var typeInfo in assembly.DefinedTypes)
{
if (badModules.Contains(typeInfo))
continue;
- if(typeInfo.IsOuterSubTypeCandidate(out Type parentType))
+ if (typeInfo.IsOuterSubTypeCandidate(out var parentType))
{
bool subTypeIsValid = await typeInfo.IsValidOuterSubTypeAsync(
parentType,
autoLoadable: true,
logger: service._cmdLogger);
+ var parentTypeInfo = parentType.GetTypeInfo();
+
if (subTypeIsValid)
{
- queuedModules.Add(typeInfo);
+ //add outer subtypes to a dependency container.
+ if(queuedModules.TryGetValue(parentTypeInfo, out var dependencyContainer))
+ {
+ dependencyContainer.Add(typeInfo);
+ }
+ else
+ {
+ queuedModules.Add(parentTypeInfo, new List { typeInfo });
+ }
}
else
{
@@ -44,31 +54,31 @@ namespace Discord.Commands
}
bool typeInfoIsValid = await typeInfo.IsValidModuleType(autoLoadable: true, logger: service._cmdLogger);
- if(typeInfoIsValid)
+ if (typeInfoIsValid)
{
standardModules.Add(typeInfo);
}
}
- return Tuple.Create, IReadOnlyList>(standardModules, queuedModules);
+ return (standardModules, queuedModules);
}
- public static Task> BuildAsync(CommandService service, IServiceProvider services, IDictionary moduleDefs, params TypeInfo[] validTypes) =>
- BuildAsync(validTypes, service, services, moduleDefs);
+ public static Task> BuildAsync(CommandService service, IServiceProvider services, params TypeInfo[] validTypes) =>
+ BuildAsync(validTypes, service, services);
- public static async Task> BuildAsync(IEnumerable validTypes, CommandService service, IServiceProvider services, IDictionary moduleDefs = null)
+ public static async Task> BuildAsync(
+ IEnumerable topLevelTypes,
+ CommandService service,
+ IServiceProvider services,
+ IReadOnlyDictionary> dependencies = null)
{
- /*if (!validTypes.Any())
- throw new InvalidOperationException("Could not find any valid modules from the given selection");*/
-
- var topLevelGroups = validTypes
- .Where(x => x.DeclaringType == null || !x.DeclaringType.GetTypeInfo().IsValidModuleDefinition());
-
- var builtTypes = new List();
+ if (!topLevelTypes.Any())
+ throw new InvalidOperationException("Could not find any valid modules from the given selection");
var result = new Dictionary();
+ var builtTypes = new List();
- foreach (var typeInfo in topLevelGroups)
+ foreach (var typeInfo in topLevelTypes)
{
// TODO: This shouldn't be the case; may be safe to remove?
if (result.ContainsKey(typeInfo.AsType()))
@@ -76,41 +86,62 @@ namespace Discord.Commands
var module = new ModuleBuilder(service, null);
- BuildModule(module, typeInfo, service, services, moduleDefs);
- BuildSubTypes(module, typeInfo.DeclaredNestedTypes, builtTypes, service, services, moduleDefs);
+ BuildModule(module, typeInfo, service, services);
+ BuildSubTypes(module, ResolveDependencies(typeInfo, dependencies), builtTypes, service, services, dependencies);
builtTypes.Add(typeInfo);
//dont build yet, return as a module dictonary?
result[typeInfo.AsType()] = module.Build(service, services);
}
- string entriesName = moduleDefs == null ? "modules" : "submodules";
- await service._cmdLogger.DebugAsync($"Successfully built {builtTypes.Count} {entriesName}.").ConfigureAwait(false);
+ await service._cmdLogger.DebugAsync($"Successfully built {result.Count} modules.").ConfigureAwait(false);
return result;
}
- private static void BuildSubTypes(ModuleBuilder builder, IEnumerable subTypes, List builtTypes, CommandService service, IServiceProvider services, IDictionary moduleDefs)
+ private static void BuildSubTypes(ModuleBuilder builder, IEnumerable subTypes, List builtTypes, CommandService service, IServiceProvider services, IReadOnlyDictionary> dependencies = null)
{
foreach (var typeInfo in subTypes)
{
if (!typeInfo.IsValidModuleDefinition())
continue;
-
+
if (builtTypes.Contains(typeInfo))
continue;
-
- builder.AddModule((module) =>
+
+ builder.AddModule((module) =>
{
- BuildModule(module, typeInfo, service, services, moduleDefs);
- BuildSubTypes(module, typeInfo.DeclaredNestedTypes, builtTypes, service, services, moduleDefs);
+ BuildModule(module, typeInfo, service, services);
+ BuildSubTypes(module, ResolveDependencies(typeInfo, dependencies), builtTypes, service, services);
});
builtTypes.Add(typeInfo);
}
}
- private static void BuildModule(ModuleBuilder builder, TypeInfo typeInfo, CommandService service, IServiceProvider services, IDictionary moduleDefs)
+ ///Just a helper method to avoid duplicate code
+ private static IEnumerable ResolveDependencies(TypeInfo typeInfo, IReadOnlyDictionary> dependencies)
+ {
+ IEnumerable childTypes = null;
+
+ if (dependencies != null)
+ {
+ if (dependencies.TryGetValue(typeInfo, out var linkedChildTypes))
+ {
+ childTypes = linkedChildTypes;
+ }
+ }
+
+ //??= pls when 8.0
+ if (childTypes == null)
+ {
+ childTypes = typeInfo.DeclaredNestedTypes;
+ }
+
+ return childTypes;
+ }
+
+ private static void BuildModule(ModuleBuilder builder, TypeInfo typeInfo, CommandService service, IServiceProvider services)
{
var attributes = typeInfo.GetCustomAttributes();
builder.TypeInfo = typeInfo;
@@ -134,20 +165,6 @@ namespace Discord.Commands
case GroupAttribute group:
builder.Name = builder.Name ?? group.Prefix;
builder.Group = group.Prefix;
- if(group.ParentModule != null)
- {
- if(moduleDefs.TryGetValue(group.ParentModule, out var mInfo))
- {
- foreach (string parentAlias in mInfo.Aliases)
- {
- builder.AddAliases(parentAlias);
- }
- }
- else
- {
- throw new ArgumentNullException($"Parent module was not defined for outer sub type: {typeInfo.FullName}.");
- }
- }
builder.AddAliases(group.Prefix);
break;
case PreconditionAttribute precondition:
@@ -169,7 +186,7 @@ namespace Discord.Commands
foreach (var method in validCommands)
{
- builder.AddCommand((command) =>
+ builder.AddCommand((command) =>
{
BuildCommand(command, typeInfo, method, service, services);
});
@@ -179,7 +196,7 @@ namespace Discord.Commands
private static void BuildCommand(CommandBuilder builder, TypeInfo typeInfo, MethodInfo method, CommandService service, IServiceProvider serviceprovider)
{
var attributes = method.GetCustomAttributes();
-
+
foreach (var attribute in attributes)
{
switch (attribute)
@@ -221,7 +238,7 @@ namespace Discord.Commands
int pos = 0, count = parameters.Length;
foreach (var paramInfo in parameters)
{
- builder.AddParameter((parameter) =>
+ builder.AddParameter((parameter) =>
{
BuildParameter(parameter, paramInfo, pos++, count, service, serviceprovider);
});
diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs
index c93dec009..02e855d0f 100644
--- a/src/Discord.Net.Commands/CommandService.cs
+++ b/src/Discord.Net.Commands/CommandService.cs
@@ -203,7 +203,7 @@ namespace Discord.Commands
throw new InvalidOperationException($"Type {typeInfo.FullName} cannot be transformed into a module. Look at the logs for more details.");
}
- var module = (await ModuleClassBuilder.BuildAsync(this, services, _typedModuleDefs, typeInfo).ConfigureAwait(false)).FirstOrDefault();
+ var module = (await ModuleClassBuilder.BuildAsync(this, services, typeInfo).ConfigureAwait(false)).FirstOrDefault();
if (module.Value == default(ModuleInfo))
throw new InvalidOperationException($"Could not build the module {type.FullName}, did you pass an invalid type?");
@@ -233,8 +233,8 @@ namespace Discord.Commands
await _moduleLock.WaitAsync().ConfigureAwait(false);
try
{
- var types = await ModuleClassBuilder.SearchAsync(assembly, this).ConfigureAwait(false);
- var standardModuleDefs = await ModuleClassBuilder.BuildAsync(types.Item1, this, services).ConfigureAwait(false);
+ (var types, var dependencies) = await ModuleClassBuilder.SearchAsync(assembly, this).ConfigureAwait(false);
+ var standardModuleDefs = await ModuleClassBuilder.BuildAsync(types, this, services, dependencies).ConfigureAwait(false);
foreach (var info in standardModuleDefs)
{
@@ -242,17 +242,7 @@ namespace Discord.Commands
LoadModuleInternal(info.Value);
}
- var outsideParentGroupModuleDefs = await ModuleClassBuilder.BuildAsync(types.Item2, this, services, standardModuleDefs).ConfigureAwait(false);
-
- foreach (var info in outsideParentGroupModuleDefs)
- {
- _typedModuleDefs[info.Key] = info.Value;
- LoadModuleInternal(info.Value);
- }
-
var moduleDefs = new List(standardModuleDefs.Values);
- moduleDefs.AddRange(outsideParentGroupModuleDefs.Values);
-
return moduleDefs.ToImmutableArray();
}
finally