enhance script plugin features

(support service resolver with generic args)
(support requiresTarget for command)
This commit is contained in:
RaidMax 2020-09-28 20:32:53 -05:00
parent 70cae976a0
commit 7f11921757
10 changed files with 147 additions and 10 deletions

View File

@ -25,7 +25,7 @@ namespace IW4MAdmin.Application.Factories
}
/// <inheritdoc/>
public IManagerCommand CreateScriptCommand(string name, string alias, string description, string permission, IEnumerable<(string, bool)> args, Action<GameEvent> executeAction)
public IManagerCommand CreateScriptCommand(string name, string alias, string description, string permission, bool isTargetRequired, IEnumerable<(string, bool)> args, Action<GameEvent> executeAction)
{
var permissionEnum = Enum.Parse<Permission>(permission);
var argsArray = args.Select(_arg => new CommandArgument
@ -34,7 +34,7 @@ namespace IW4MAdmin.Application.Factories
Required = _arg.Item2
}).ToArray();
return new ScriptCommand(name, alias, description, permissionEnum, argsArray, executeAction, _config, _transLookup);
return new ScriptCommand(name, alias, description, isTargetRequired, permissionEnum, argsArray, executeAction, _config, _transLookup);
}
}
}

View File

@ -15,7 +15,7 @@ namespace IW4MAdmin.Application.Misc
{
private readonly Action<GameEvent> _executeAction;
public ScriptCommand(string name, string alias, string description, Permission permission,
public ScriptCommand(string name, string alias, string description, bool isTargetRequired, Permission permission,
CommandArgument[] args, Action<GameEvent> executeAction, CommandConfiguration config, ITranslationLookup layout)
: base(config, layout)
{
@ -24,6 +24,7 @@ namespace IW4MAdmin.Application.Misc
Name = name;
Alias = alias;
Description = description;
RequiresTarget = isTargetRequired;
Permission = permission;
Arguments = args;
}

View File

@ -252,6 +252,7 @@ namespace IW4MAdmin.Application.Misc
string alias = dynamicCommand.alias;
string description = dynamicCommand.description;
string permission = dynamicCommand.permission;
bool targetRequired = false;
List<(string, bool)> args = new List<(string, bool)>();
dynamic arguments = null;
@ -266,6 +267,16 @@ namespace IW4MAdmin.Application.Misc
// arguments are optional
}
try
{
targetRequired = dynamicCommand.targetRequired;
}
catch (RuntimeBinderException)
{
// arguments are optional
}
if (arguments != null)
{
foreach (var arg in dynamicCommand.arguments)
@ -290,7 +301,7 @@ namespace IW4MAdmin.Application.Misc
}
}
commandList.Add(scriptCommandFactory.CreateScriptCommand(name, alias, description, permission, args, execute));
commandList.Add(scriptCommandFactory.CreateScriptCommand(name, alias, description, permission, targetRequired, args, execute));
}
return commandList;

View File

@ -18,14 +18,31 @@ namespace IW4MAdmin.Application.Misc
public object ResolveService(string serviceName)
{
var serviceType = typeof(IScriptPluginServiceResolver).Assembly.GetTypes().FirstOrDefault(_type => _type.Name == serviceName);
var serviceType = DetermineRootType(serviceName);
return _serviceProvider.GetService(serviceType);
}
public object ResolveService(string serviceName, string[] genericParameters)
{
var serviceType = DetermineRootType(serviceName, genericParameters.Length);
var genericTypes = genericParameters.Select(_genericTypeParam => DetermineRootType(_genericTypeParam));
var resolvedServiceType = serviceType.MakeGenericType(genericTypes.ToArray());
return _serviceProvider.GetService(resolvedServiceType);
}
private Type DetermineRootType(string serviceName, int genericParamCount = 0)
{
var typeCollection = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(t => t.GetTypes());
string generatedName = $"{serviceName}{(genericParamCount == 0 ? "" : $"`{genericParamCount}")}".ToLower();
var serviceType = typeCollection.FirstOrDefault(_type => _type.Name.ToLower() == generatedName);
if (serviceType == null)
{
throw new InvalidOperationException($"No service type '{serviceName}' defined in IW4MAdmin assembly");
throw new InvalidOperationException($"No object type '{serviceName}' defined in loaded assemblies");
}
return _serviceProvider.GetService(serviceType);
return serviceType;
}
}
}

View File

@ -7,6 +7,8 @@ let commands = [{
alias: "pp",
// required
permission: "User",
// optional (defaults to false)
targetRequired: false,
// optional
arguments: [{
name: "times to ping",

View File

@ -15,9 +15,10 @@ namespace SharedLibraryCore.Interfaces
/// <param name="alias">alias of command</param>
/// <param name="description">description of command</param>
/// <param name="permission">minimum required permission</param>
/// <param name="isTargetRequired">target required or not</param>
/// <param name="args">command arguments (name, is required)</param>
/// <param name="executeAction">action to peform when commmand is executed</param>
/// <returns></returns>
IManagerCommand CreateScriptCommand(string name, string alias, string description, string permission, IEnumerable<(string, bool)> args, Action<GameEvent> executeAction);
IManagerCommand CreateScriptCommand(string name, string alias, string description, string permission, bool isTargetRequired, IEnumerable<(string, bool)> args, Action<GameEvent> executeAction);
}
}

View File

@ -1,10 +1,25 @@
namespace SharedLibraryCore.Interfaces
using System.Collections.Generic;
namespace SharedLibraryCore.Interfaces
{
/// <summary>
/// interface used to dynamically resolve services by string name
/// </summary>
public interface IScriptPluginServiceResolver
{
/// <summary>
/// resolves a service with the given name
/// </summary>
/// <param name="serviceName">class name of service</param>
/// <returns></returns>
object ResolveService(string serviceName);
/// <summary>
/// resolves a service with the given name and generic params
/// </summary>
/// <param name="serviceName">class name of service</param>
/// <param name="genericParams">generic class names</param>
/// <returns></returns>
object ResolveService(string serviceName, string[] genericParameters);
}
}

View File

@ -0,0 +1,23 @@
namespace ApplicationTests.Mocks
{
public interface IScriptResolverMock
{
string Value { get; set; }
}
public class ScriptResolverMock : IScriptResolverMock
{
public string Value { get; set; }
}
public interface IScriptResolverGenericMock<T, V>
{
T Value { get; set; }
V Value2 { get; set; }
}
public class ScriptResolverGenericMock<T, V> : IScriptResolverGenericMock<T, V>
{
public T Value { get; set; }
public V Value2 { get; set; }
}
}

View File

@ -33,6 +33,7 @@ namespace ApplicationTests
serviceProvider = new ServiceCollection().BuildBase()
.AddSingleton(A.Fake<ClientService>())
.AddSingleton<IScriptCommandFactory, ScriptCommandFactory>()
.AddSingleton(A.Fake<IScriptPluginServiceResolver>())
.BuildServiceProvider();
fakeManager = serviceProvider.GetRequiredService<IManager>();
mockEventHandler = serviceProvider.GetRequiredService<EventHandlerMock>();
@ -66,7 +67,7 @@ namespace ApplicationTests
A.CallTo(() => fakeManager.GetClientService())
.Returns(fakeClientService);
await plugin.Initialize(serviceProvider.GetRequiredService<IManager>(), serviceProvider.GetRequiredService<IScriptCommandFactory>());
await plugin.Initialize(serviceProvider.GetRequiredService<IManager>(), serviceProvider.GetRequiredService<IScriptCommandFactory>(), serviceProvider.GetRequiredService<IScriptPluginServiceResolver>());
var gameEvent = new GameEvent()
{

View File

@ -0,0 +1,66 @@
using ApplicationTests.Mocks;
using IW4MAdmin.Application.Misc;
using Microsoft.Extensions.DependencyInjection;
using NUnit.Framework;
using System;
namespace ApplicationTests
{
public class ScriptPluginServiceResolverTests
{
private IServiceProvider serviceProvider;
[SetUp]
public void Setup()
{
serviceProvider = new ServiceCollection()
.BuildBase()
.AddSingleton<ScriptPluginServiceResolver>()
.AddSingleton<IScriptResolverMock, ScriptResolverMock>()
.AddSingleton(new ScriptResolverMock { Value = "test" })
.AddSingleton<IScriptResolverGenericMock<int, string>, ScriptResolverGenericMock<int,string>>()
.AddSingleton(new ScriptResolverGenericMock<int, string> { Value = 123, Value2 = "test" })
.BuildServiceProvider();
}
[Test]
public void Test_ResolveType()
{
var resolver = serviceProvider.GetService<ScriptPluginServiceResolver>();
var expectedResolvedService = serviceProvider.GetService<ScriptResolverMock>();
var resolvedService = resolver.ResolveService(nameof(ScriptResolverMock));
Assert.AreEqual(expectedResolvedService, resolvedService);
}
[Test]
public void Test_ResolveType_Interface()
{
var resolver = serviceProvider.GetService<ScriptPluginServiceResolver>();
var expectedResolvedService = serviceProvider.GetService<IScriptResolverMock>();
var resolvedService = resolver.ResolveService(nameof(IScriptResolverMock));
Assert.AreEqual(expectedResolvedService, resolvedService);
}
[Test]
public void Test_ResolveGenericType()
{
var resolver = serviceProvider.GetService<ScriptPluginServiceResolver>();
var expectedResolvedService = serviceProvider.GetService<ScriptResolverGenericMock<int, string>>();
var resolvedService = resolver.ResolveService("ScriptResolverGenericMock", new[] { "Int32", "String" });
Assert.AreEqual(expectedResolvedService, resolvedService);
}
[Test]
public void Test_ResolveGenericType_Interface()
{
var resolver = serviceProvider.GetService<ScriptPluginServiceResolver>();
var expectedResolvedService = serviceProvider.GetService<IScriptResolverGenericMock<int, string>>();
var resolvedService = resolver.ResolveService("IScriptResolverGenericMock", new[] { "Int32", "String" });
Assert.AreEqual(expectedResolvedService, resolvedService);
}
}
}