diff --git a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs
index aa182708e..c40a34336 100644
--- a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs
+++ b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs
@@ -13,27 +13,29 @@ namespace Discord.Commands
private static readonly TypeInfo ModuleTypeInfo = typeof(IModuleBase).GetTypeInfo();
///Value1 - ready types, Value2 - types dependant on Value1.
- ///
- ///
- ///
- public static async Task, IReadOnlyDictionary>> SearchAsync(Assembly assembly, CommandService service)
+ public static async Task, IReadOnlyList>> 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 Dictionary();
+ var queuedModules = new List();
foreach (var typeInfo in assembly.DefinedTypes)
{
- //SubTypes outside of parent class. Require
+ if (badModules.Contains(typeInfo))
+ continue;
+
+ //Find SubTypes outside of parent class.
var groupAttr = typeInfo.GetCustomAttribute();
if (groupAttr != null)
{
if (groupAttr.ParentModule != null)
{
var parentTypeInfo = groupAttr.ParentModule.GetTypeInfo();
- bool subTypeIsValid = await ConfirmTypeInfoAsync(service, parentTypeInfo);
+ bool subTypeIsValid = await ConfirmOuterSubTypeInfoAsync(service, typeInfo, parentTypeInfo, badModules);
if(subTypeIsValid)
{
- queuedModules.Add(typeInfo, parentTypeInfo);
+ queuedModules.Add(typeInfo);
}
continue;
}
@@ -46,10 +48,9 @@ namespace Discord.Commands
}
}
- return Tuple.Create, IReadOnlyDictionary>(standardModules, queuedModules);
+ return Tuple.Create, IReadOnlyList>(standardModules, queuedModules);
}
-
private static bool IsLoadableModule(TypeInfo info)
{
return info.DeclaredMethods.Any(
@@ -57,7 +58,10 @@ namespace Discord.Commands
info.GetCustomAttribute() == null;
}
- ///Confirm if type is valid. If so, add it to the specified modules list.
+ ///Checks whether is valid to be loaded.
+ ///CommandService object.
+ ///Target type to check against.
+ ///Checks parent type validity. Used with outer group modules.
private static async Task ConfirmTypeInfoAsync(CommandService service, TypeInfo typeInfo)
{
if (typeInfo.IsPublic || typeInfo.IsNestedPublic)
@@ -76,6 +80,24 @@ namespace Discord.Commands
return false;
}
+ ///Checks whether is valid to be loaded.
+ ///CommandService object.
+ ///Target type to check against.
+ private static async Task ConfirmOuterSubTypeInfoAsync(CommandService service, TypeInfo typeInfo, TypeInfo parentTypeInfo, List badModules)
+ {
+ bool targetValid = await ConfirmTypeInfoAsync(service, typeInfo);
+ if(targetValid)
+ {
+ bool parentValid = await ConfirmTypeInfoAsync(service, parentTypeInfo);
+ if (!parentValid)
+ {
+ await service._cmdLogger.WarningAsync($"Parent Class {parentTypeInfo.FullName} is not a module. Group module {typeInfo.FullName} was not loaded.").ConfigureAwait(false);
+ badModules.Add(parentTypeInfo);
+ return false;
+ }
+ }
+ return targetValid;
+ }
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, Dictionary moduleDefs = null)
@@ -104,7 +126,8 @@ namespace Discord.Commands
result[typeInfo.AsType()] = module.Build(service, services);
}
- await service._cmdLogger.DebugAsync($"Successfully built {builtTypes.Count} modules.").ConfigureAwait(false);
+ string entriesName = moduleDefs == null ? "modules" : "submodules";
+ await service._cmdLogger.DebugAsync($"Successfully built {builtTypes.Count} {entriesName}.").ConfigureAwait(false);
return result;
}
diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs
index ed7d40987..ad88a7b89 100644
--- a/src/Discord.Net.Commands/CommandService.cs
+++ b/src/Discord.Net.Commands/CommandService.cs
@@ -236,7 +236,7 @@ namespace Discord.Commands
LoadModuleInternal(info.Value);
}
- var outsideParentGroupModuleDefs = await ModuleClassBuilder.BuildAsync(types.Item2.Keys, this, services, standardModuleDefs).ConfigureAwait(false);
+ var outsideParentGroupModuleDefs = await ModuleClassBuilder.BuildAsync(types.Item2, this, services, standardModuleDefs).ConfigureAwait(false);
foreach (var info in outsideParentGroupModuleDefs)
{