@@ -5,6 +5,8 @@ VisualStudioVersion = 14.0.25123.0 | |||||
MinimumVisualStudioVersion = 10.0.40219.1 | MinimumVisualStudioVersion = 10.0.40219.1 | ||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net", "src\Discord.Net\Discord.Net.xproj", "{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}" | Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net", "src\Discord.Net\Discord.Net.xproj", "{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}" | ||||
EndProject | EndProject | ||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net.Commands", "src\Discord.Net.Commands\Discord.Net.Commands.xproj", "{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}" | |||||
EndProject | |||||
Global | Global | ||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||||
Debug|Any CPU = Debug|Any CPU | Debug|Any CPU = Debug|Any CPU | ||||
@@ -15,6 +17,10 @@ Global | |||||
{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Debug|Any CPU.Build.0 = Debug|Any CPU | {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Release|Any CPU.ActiveCfg = Release|Any CPU | {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Release|Any CPU.Build.0 = Release|Any CPU | {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||||
{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Release|Any CPU.Build.0 = Release|Any CPU | |||||
EndGlobalSection | EndGlobalSection | ||||
GlobalSection(SolutionProperties) = preSolution | GlobalSection(SolutionProperties) = preSolution | ||||
HideSolutionNode = FALSE | HideSolutionNode = FALSE | ||||
@@ -0,0 +1,14 @@ | |||||
using System; | |||||
namespace Discord.Commands | |||||
{ | |||||
[AttributeUsage(AttributeTargets.Method)] | |||||
public class CommandAttribute : Attribute | |||||
{ | |||||
public string Name { get; } | |||||
public CommandAttribute(string name) | |||||
{ | |||||
Name = name; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,14 @@ | |||||
using System; | |||||
namespace Discord.Commands | |||||
{ | |||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Parameter)] | |||||
public class DescriptionAttribute : Attribute | |||||
{ | |||||
public string Text { get; } | |||||
public DescriptionAttribute(string text) | |||||
{ | |||||
Text = text; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,14 @@ | |||||
using System; | |||||
namespace Discord.Commands | |||||
{ | |||||
[AttributeUsage(AttributeTargets.Class)] | |||||
public class GroupAttribute : Attribute | |||||
{ | |||||
public string Name { get; } | |||||
public GroupAttribute(string name) | |||||
{ | |||||
Name = name; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,9 @@ | |||||
using System; | |||||
namespace Discord.Commands | |||||
{ | |||||
[AttributeUsage(AttributeTargets.Class)] | |||||
public class ModuleAttribute : Attribute | |||||
{ | |||||
} | |||||
} |
@@ -0,0 +1,9 @@ | |||||
using System; | |||||
namespace Discord.Commands | |||||
{ | |||||
[AttributeUsage(AttributeTargets.Parameter)] | |||||
public class UnparsedAttribute : Attribute | |||||
{ | |||||
} | |||||
} |
@@ -0,0 +1,106 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Reflection; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | |||||
namespace Discord.Commands | |||||
{ | |||||
public class Module | |||||
{ | |||||
public string Name { get; } | |||||
public IEnumerable<Command> Commands { get; } | |||||
internal Module(object module, TypeInfo typeInfo) | |||||
{ | |||||
List<Command> commands = new List<Command>(); | |||||
SearchClass(commands); | |||||
Commands = commands; | |||||
} | |||||
private void SearchClass(List<Command> commands) | |||||
{ | |||||
//TODO: Implement | |||||
} | |||||
} | |||||
public class Command | |||||
{ | |||||
public string SourceName { get; } | |||||
} | |||||
public class CommandParser | |||||
{ | |||||
private readonly SemaphoreSlim _moduleLock; | |||||
private readonly Dictionary<object, Module> _modules; | |||||
public CommandParser() | |||||
{ | |||||
_modules = new Dictionary<object, Module>(); | |||||
_moduleLock = new SemaphoreSlim(1, 1); | |||||
} | |||||
public async Task<Module> Load(object module) | |||||
{ | |||||
await _moduleLock.WaitAsync().ConfigureAwait(false); | |||||
try | |||||
{ | |||||
if (_modules.ContainsKey(module)) | |||||
throw new ArgumentException($"This module has already been loaded."); | |||||
return LoadInternal(module, module.GetType().GetTypeInfo()); | |||||
} | |||||
finally | |||||
{ | |||||
_moduleLock.Release(); | |||||
} | |||||
} | |||||
private Module LoadInternal(object module, TypeInfo typeInfo) | |||||
{ | |||||
var loadedModule = new Module(module, typeInfo); | |||||
_modules[module] = loadedModule; | |||||
return loadedModule; | |||||
} | |||||
public async Task<IEnumerable<Module>> LoadAssembly(Assembly assembly) | |||||
{ | |||||
List<Module> modules = new List<Module>(); | |||||
await _moduleLock.WaitAsync().ConfigureAwait(false); | |||||
try | |||||
{ | |||||
foreach (var type in assembly.ExportedTypes) | |||||
{ | |||||
var typeInfo = type.GetTypeInfo(); | |||||
if (typeInfo.GetCustomAttribute<ModuleAttribute>() != null) | |||||
{ | |||||
var constructor = typeInfo.DeclaredConstructors.Where(x => x.GetParameters().Length == 0).FirstOrDefault(); | |||||
if (constructor == null) | |||||
throw new InvalidOperationException($"Failed to find a valid constructor for \"{typeInfo.FullName}\""); | |||||
object module; | |||||
try { module = constructor.Invoke(null); } | |||||
catch (Exception ex) | |||||
{ | |||||
throw new InvalidOperationException($"Failed to create \"{typeInfo.FullName}\"", ex); | |||||
} | |||||
modules.Add(LoadInternal(module, typeInfo)); | |||||
} | |||||
} | |||||
return modules; | |||||
} | |||||
finally | |||||
{ | |||||
_moduleLock.Release(); | |||||
} | |||||
} | |||||
public async Task<bool> Unload(object module) | |||||
{ | |||||
await _moduleLock.WaitAsync().ConfigureAwait(false); | |||||
try | |||||
{ | |||||
return _modules.Remove(module); | |||||
} | |||||
finally | |||||
{ | |||||
_moduleLock.Release(); | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,19 @@ | |||||
<?xml version="1.0" encoding="utf-8"?> | |||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||||
<PropertyGroup> | |||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion> | |||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> | |||||
</PropertyGroup> | |||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" /> | |||||
<PropertyGroup Label="Globals"> | |||||
<ProjectGuid>078dd7e6-943d-4d09-afc2-d2ba58b76c9c</ProjectGuid> | |||||
<RootNamespace>Discord.Commands</RootNamespace> | |||||
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath> | |||||
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath> | |||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion> | |||||
</PropertyGroup> | |||||
<PropertyGroup> | |||||
<SchemaVersion>2.0</SchemaVersion> | |||||
</PropertyGroup> | |||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" /> | |||||
</Project> |
@@ -0,0 +1,34 @@ | |||||
{ | |||||
"version": "1.0.0-dev", | |||||
"description": "A Discord.Net extension adding command support.", | |||||
"authors": [ "RogueException" ], | |||||
"packOptions": { | |||||
"tags": [ "discord", "discordapp" ], | |||||
"licenseUrl": "http://opensource.org/licenses/MIT", | |||||
"projectUrl": "https://github.com/RogueException/Discord.Net", | |||||
"repository": { | |||||
"type": "git", | |||||
"url": "git://github.com/RogueException/Discord.Net" | |||||
} | |||||
}, | |||||
"buildOptions": { | |||||
"allowUnsafe": true, | |||||
"warningsAsErrors": false | |||||
}, | |||||
"dependencies": { | |||||
"Discord.Net": "1.0.0-dev" | |||||
}, | |||||
"frameworks": { | |||||
"netstandard1.3": { | |||||
"imports": [ | |||||
"dotnet5.4", | |||||
"dnxcore50", | |||||
"portable-net45+win8" | |||||
] | |||||
} | |||||
} | |||||
} |