handlers now discovered via reflection!

This commit is contained in:
alterNERDtive 2022-07-08 22:31:25 +02:00
parent feb0206951
commit 8b6bc1d16a
2 changed files with 156 additions and 75 deletions

View file

@ -28,9 +28,9 @@ namespace alterNERDtive.Yavapf.Example
///
/// You can use this class and this project as base for your own implementation.
/// </summary>
public class ExamplePlugin
public class ExamplePlugin : VoiceAttackPlugin
{
private static readonly VoiceAttackPlugin Plugin;
private static readonly ExamplePlugin Plugin;
/// <summary>
/// Initializes static members of the <see cref="ExamplePlugin"/> class.
@ -45,26 +45,16 @@ namespace alterNERDtive.Yavapf.Example
{
// You can generate a GUID in Visual Studio under “Tools” → “Create
// GUID”. Choose “Registry Format”.
Plugin = new (
name: "Example Plugin",
version: "0.0.1",
info: "This is a description",
guid: "{76FE674F-F729-45FD-A1DD-E53E9E66B360}");
Plugin = new ()
{
Name = "Example Plugin",
Version = "0.0.1",
Info = "This is a description",
Guid = "{76FE674F-F729-45FD-A1DD-E53E9E66B360}",
};
// Add handlers for the Init, Exit and Stop events here. The plugin
// will execute them whenever VA_Init1, VA_Exit1 or VA_StopCommand
// are run by VoiceAttack.
// You can add none or as many as you need.
Plugin.Init += Init;
Plugin.Exit += Exit;
Plugin.Stop += Stop;
// Add handlers for plugin contexts. The methods added here must
// have at least one “Context” attribute specifying the contexts
// they are to be executed for.
// See the definition of the “Test” method below.
Plugin.Contexts += Test;
Plugin.Contexts += RegexContext;
// Event handlers are mainly added via attributes, see the end of
// the file.
// You can even add lambdas! This one logs an explicit error for
// invoking the plugin without a context.
@ -85,7 +75,7 @@ namespace alterNERDtive.Yavapf.Example
/// class for VoiceAttack to pick it up as a plugin.
/// </summary>
/// <returns>The display name.</returns>
public static string VA_DisplayName() => Plugin.VA_DisplayName();
public static string VA_DisplayName() => Plugin.VaDisplayName();
/// <summary>
/// The plugins description, as required by the VoiceAttack plugin API.
@ -94,7 +84,7 @@ namespace alterNERDtive.Yavapf.Example
/// class for VoiceAttack to pick it up as a plugin.
/// </summary>
/// <returns>The description.</returns>
public static string VA_DisplayInfo() => Plugin.VA_DisplayInfo();
public static string VA_DisplayInfo() => Plugin.VaDisplayInfo();
/// <summary>
/// The plugins GUID, as required by the VoiceAttack plugin API.
@ -103,7 +93,7 @@ namespace alterNERDtive.Yavapf.Example
/// class for VoiceAttack to pick it up as a plugin.
/// </summary>
/// <returns>The GUID.</returns>
public static Guid VA_Id() => Plugin.VA_Id();
public static Guid VA_Id() => Plugin.VaId();
/// <summary>
/// The Init method, as required by the VoiceAttack plugin API.
@ -113,7 +103,7 @@ namespace alterNERDtive.Yavapf.Example
/// class for VoiceAttack to pick it up as a plugin.
/// </summary>
/// <param name="vaProxy">The VoiceAttack proxy object.</param>
public static void VA_Init1(dynamic vaProxy) => Plugin.VA_Init1(vaProxy);
public static void VA_Init1(dynamic vaProxy) => Plugin.VaInit1(vaProxy);
/// <summary>
/// The Invoke method, as required by the VoiceAttack plugin API.
@ -123,7 +113,7 @@ namespace alterNERDtive.Yavapf.Example
/// class for VoiceAttack to pick it up as a plugin.
/// </summary>
/// <param name="vaProxy">The VoiceAttack proxy object.</param>
public static void VA_Invoke1(dynamic vaProxy) => Plugin.VA_Invoke1(vaProxy);
public static void VA_Invoke1(dynamic vaProxy) => Plugin.VaInvoke1(vaProxy);
/// <summary>
/// The Exit method, as required by the VoiceAttack plugin API.
@ -133,7 +123,7 @@ namespace alterNERDtive.Yavapf.Example
/// class for VoiceAttack to pick it up as a plugin.
/// </summary>
/// <param name="vaProxy">The VoiceAttack proxy object.</param>
public static void VA_Exit1(dynamic vaProxy) => Plugin.VA_Exit1(vaProxy);
public static void VA_Exit1(dynamic vaProxy) => Plugin.VaExit1(vaProxy);
/// <summary>
/// The StopCommand method, as required by the VoiceAttack plugin API.
@ -143,7 +133,7 @@ namespace alterNERDtive.Yavapf.Example
/// Since it is required to be static, it must be defined in your plugin
/// class for VoiceAttack to pick it up as a plugin.
/// </summary>
public static void VA_StopCommand() => Plugin.VA_StopCommand();
public static void VA_StopCommand() => Plugin.VaStopCommand();
/// <summary>
/// An example handler for VA_Init1. Init handlers are the place to do
@ -151,7 +141,8 @@ namespace alterNERDtive.Yavapf.Example
/// loaded at VoiceAttack start.
/// </summary>
/// <param name="vaProxy">The current VoiceAttack proxy object.</param>
private static void Init(VoiceAttackInitProxyClass vaProxy)
[Init]
public static void Init(VoiceAttackInitProxyClass vaProxy)
{
Plugin.Log.Notice("This is the example Init handler method.");
}
@ -162,7 +153,8 @@ namespace alterNERDtive.Yavapf.Example
/// there is a default timeout of 5s.
/// </summary>
/// <param name="vaProxy">The current VoiceAttack proxy object.</param>
private static void Exit(VoiceAttackProxyClass vaProxy)
[Exit]
public static void Exit(VoiceAttackProxyClass vaProxy)
{
Plugin.Log.Notice("This is the example Exit handler method.");
}
@ -171,7 +163,8 @@ namespace alterNERDtive.Yavapf.Example
/// An example handler for VA_StopCommand. If your plugin needs to
/// execute anything when all commands are stopped this is the place.
/// </summary>
private static void Stop()
[Stop]
public static void Stop()
{
Plugin.Log.Notice("This is the example Stop handler method.");
}
@ -187,7 +180,7 @@ namespace alterNERDtive.Yavapf.Example
/// <param name="vaProxy">The current VoiceAttack proxy object.</param>
[Context("test")]
[Context("different test")]
private static void Test(VoiceAttackInvokeProxyClass vaProxy)
public static void Test(VoiceAttackInvokeProxyClass vaProxy)
{
Plugin.Log.Notice(
$"This is the example handler for the plugin contexts “test” and “different test”. It has been invoked with '{vaProxy.Context}'.");
@ -206,7 +199,7 @@ namespace alterNERDtive.Yavapf.Example
/// <param name="vaProxy">The current VoiceAttack proxy object.</param>
[Context("^foo.*")]
[Context("^.*bar.*")]
private static void RegexContext(VoiceAttackInvokeProxyClass vaProxy)
public static void RegexContext(VoiceAttackInvokeProxyClass vaProxy)
{
Plugin.Log.Notice(
$"This is the example handler for the plugin contexts “^foo.*” and “^.*bar.*”. It has been invoked with '{vaProxy.Context}'.");

View file

@ -20,6 +20,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using VoiceAttack;
@ -34,38 +35,27 @@ namespace alterNERDtive.Yavapf
private VoiceAttackLog? log;
private VoiceAttackInitProxyClass? vaProxy;
/// <summary>
/// Initializes a new instance of the <see cref="VoiceAttackPlugin"/> class.
/// </summary>
/// <param name="name">The name of the plugin.</param>
/// <param name="version">The current version of the plugin.</param>
/// <param name="info">The description of the plugin.</param>
/// <param name="guid">The GUID of the plugin.</param>
public VoiceAttackPlugin(string name, string version, string info, string guid)
=> (this.Name, this.Version, this.Info, this.Guid)
= (name, version, info, new (guid));
/// <summary>
/// Invoked when VoiceAttack initializes the plugin.
/// </summary>
public event Action<VoiceAttackInitProxyClass>? Init;
protected event Action<VoiceAttackInitProxyClass>? InitActions;
/// <summary>
/// Invoked when VoiceAttack closes.
/// </summary>
public event Action<VoiceAttackProxyClass>? Exit;
protected event Action<VoiceAttackProxyClass>? ExitActions;
/// <summary>
/// Invoked when VoiceAttack stops all commands.
/// </summary>
public event Action? Stop;
protected event Action? StopActions;
/// <summary>
/// Gets or sets the Actions to be run when the plugin is invoked from a
/// VoiceAttack command. Only Actions with a matching “Context”
/// attribute will be invoked.
/// </summary>
public HandlerList<Action<VoiceAttackInvokeProxyClass>> Contexts { get; set; } = new ();
protected HandlerList<Action<VoiceAttackInvokeProxyClass>> Contexts { get; set; } = new ();
/// <summary>
/// Gets the currently stored VoiceAttackInitProxyClass object which is
@ -74,30 +64,30 @@ namespace alterNERDtive.Yavapf
/// You will usually want to use the provided methods and Properties
/// instead.
/// </summary>
public VoiceAttackInitProxyClass Proxy
protected VoiceAttackInitProxyClass Proxy
{
get => this.vaProxy!;
}
/// <summary>
/// Gets the name of the plugin.
/// Gets or sets the name of the plugin.
/// </summary>
public string Name { get; }
public string? Name { get; set; }
/// <summary>
/// Gets the current version of the plugin.
/// Gets or sets the current version of the plugin.
/// </summary>
public string Version { get; }
public string? Version { get; set; }
/// <summary>
/// Gets the description of the plugin.
/// Gets or sets the description of the plugin.
/// </summary>
public string Info { get; }
public string? Info { get; set; }
/// <summary>
/// Gets the GUID of the plugin.
/// Gets or sets the GUID of the plugin.
/// </summary>
public Guid Guid { get; }
public string? Guid { get; set; }
/// <summary>
/// Gets the <see cref="VoiceAttackLog"/> instance the plugin uses to
@ -222,26 +212,26 @@ namespace alterNERDtive.Yavapf
/// The plugins display name, as required by the VoiceAttack plugin API.
/// </summary>
/// <returns>The display name.</returns>
public string VA_DisplayName() => $"{this.Name} v{this.Version}";
public string VaDisplayName() => $"{this.Name} v{this.Version}";
/// <summary>
/// The plugins description, as required by the VoiceAttack plugin API.
/// </summary>
/// <returns>The description.</returns>
public string VA_DisplayInfo() => this.Info;
public string VaDisplayInfo() => this.Info;
/// <summary>
/// The plugins GUID, as required by the VoiceAttack plugin API.
/// </summary>
/// <returns>The GUID.</returns>
public Guid VA_Id() => this.Guid;
public Guid VaId() => new (this.Guid);
/// <summary>
/// The Init method, as required by the VoiceAttack plugin API.
/// Runs when the plugin is initially loaded.
/// </summary>
/// <param name="vaProxy">The VoiceAttack proxy object.</param>
public void VA_Init1(VoiceAttackInitProxyClass vaProxy)
public void VaInit1(VoiceAttackInitProxyClass vaProxy)
{
this.vaProxy = vaProxy;
@ -254,8 +244,28 @@ namespace alterNERDtive.Yavapf
this.vaProxy.BooleanVariableChanged += this.BooleanVariableChanged;
this.vaProxy.DateVariableChanged += this.DateVariableChanged;
foreach (MethodInfo m in this.GetType().GetMethods().Where(m => m.GetCustomAttributes<InitAttribute>().Any()).ToList())
{
this.InitActions += (Action<VoiceAttackInitProxyClass>)m.CreateDelegate(typeof(Action<VoiceAttackInitProxyClass>));
}
foreach (MethodInfo m in this.GetType().GetMethods().Where(m => m.GetCustomAttributes<ExitAttribute>().Any()).ToList())
{
this.ExitActions += (Action<VoiceAttackProxyClass>)m.CreateDelegate(typeof(Action<VoiceAttackProxyClass>));
}
foreach (MethodInfo m in this.GetType().GetMethods().Where(m => m.GetCustomAttributes<StopAttribute>().Any()).ToList())
{
this.StopActions += (Action)m.CreateDelegate(typeof(Action));
}
foreach (MethodInfo m in this.GetType().GetMethods().Where(m => m.GetCustomAttributes<ContextAttribute>().Any()).ToList())
{
this.Contexts += (Action<VoiceAttackInvokeProxyClass>)m.CreateDelegate(typeof(Action<VoiceAttackInvokeProxyClass>));
}
this.Log.Debug("Running Init handlers …");
this.Init?.Invoke(vaProxy);
this.InitActions?.Invoke(vaProxy);
this.Log.Debug("Finished running Init handlers.");
this.Set<bool>($"{this.Name}.initialized", true);
@ -267,7 +277,7 @@ namespace alterNERDtive.Yavapf
/// Runs whenever a plugin context is invoked.
/// </summary>
/// <param name="vaProxy">The VoiceAttack proxy object.</param>
public void VA_Invoke1(VoiceAttackInvokeProxyClass vaProxy)
public void VaInvoke1(VoiceAttackInvokeProxyClass vaProxy)
{
this.vaProxy = vaProxy;
@ -314,9 +324,9 @@ namespace alterNERDtive.Yavapf
/// Runs when VoiceAttack is shut down.
/// </summary>
/// <param name="vaProxy">The VoiceAttack proxy object.</param>
public void VA_Exit1(VoiceAttackProxyClass vaProxy)
public void VaExit1(VoiceAttackProxyClass vaProxy)
{
this.Exit?.Invoke(vaProxy);
this.ExitActions?.Invoke(vaProxy);
}
/// <summary>
@ -324,9 +334,9 @@ namespace alterNERDtive.Yavapf
/// Runs whenever all commands are stopped using the “Stop All Commands”
/// button or action.
/// </summary>
public void VA_StopCommand()
public void VaStopCommand()
{
this.Stop?.Invoke();
this.StopActions?.Invoke();
}
protected void TextVariableChanged(string name, string from, string to, Guid? internalID = null)
@ -363,16 +373,94 @@ namespace alterNERDtive.Yavapf
return handlers;
}
}
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class ContextAttribute : Attribute
{
public string Name { get; }
public ContextAttribute(string context)
protected class InitAttribute : Attribute
{
this.Name = context;
}
protected class ExitAttribute : Attribute
{
}
protected class StopAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
protected class ContextAttribute : Attribute
{
public string Name { get; }
public ContextAttribute(string name)
{
this.Name = name;
}
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
protected class BoolAttribute : Attribute
{
public string Name { get; }
public BoolAttribute(string name)
{
this.Name = name;
}
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
protected class DateTimeAttribute : Attribute
{
public string Name { get; }
public DateTimeAttribute(string name)
{
this.Name = name;
}
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
protected class DecimalAttribute : Attribute
{
public string Name { get; }
public DecimalAttribute(string name)
{
this.Name = name;
}
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
protected class IntAttribute : Attribute
{
public string Name { get; }
public IntAttribute(string name)
{
this.Name = name;
}
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
protected class ShortAttribute : Attribute
{
public string Name { get; }
public ShortAttribute(string name)
{
this.Name = name;
}
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
protected class StringAttribute : Attribute
{
public string Name { get; }
public StringAttribute(string name)
{
this.Name = name;
}
}
}
}