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. /// You can use this class and this project as base for your own implementation.
/// </summary> /// </summary>
public class ExamplePlugin public class ExamplePlugin : VoiceAttackPlugin
{ {
private static readonly VoiceAttackPlugin Plugin; private static readonly ExamplePlugin Plugin;
/// <summary> /// <summary>
/// Initializes static members of the <see cref="ExamplePlugin"/> class. /// 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 // You can generate a GUID in Visual Studio under “Tools” → “Create
// GUID”. Choose “Registry Format”. // GUID”. Choose “Registry Format”.
Plugin = new ( Plugin = new ()
name: "Example Plugin", {
version: "0.0.1", Name = "Example Plugin",
info: "This is a description", Version = "0.0.1",
guid: "{76FE674F-F729-45FD-A1DD-E53E9E66B360}"); Info = "This is a description",
Guid = "{76FE674F-F729-45FD-A1DD-E53E9E66B360}",
};
// Add handlers for the Init, Exit and Stop events here. The plugin // Event handlers are mainly added via attributes, see the end of
// will execute them whenever VA_Init1, VA_Exit1 or VA_StopCommand // the file.
// 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;
// You can even add lambdas! This one logs an explicit error for // You can even add lambdas! This one logs an explicit error for
// invoking the plugin without a context. // invoking the plugin without a context.
@ -85,7 +75,7 @@ namespace alterNERDtive.Yavapf.Example
/// class for VoiceAttack to pick it up as a plugin. /// class for VoiceAttack to pick it up as a plugin.
/// </summary> /// </summary>
/// <returns>The display name.</returns> /// <returns>The display name.</returns>
public static string VA_DisplayName() => Plugin.VA_DisplayName(); public static string VA_DisplayName() => Plugin.VaDisplayName();
/// <summary> /// <summary>
/// The plugins description, as required by the VoiceAttack plugin API. /// 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. /// class for VoiceAttack to pick it up as a plugin.
/// </summary> /// </summary>
/// <returns>The description.</returns> /// <returns>The description.</returns>
public static string VA_DisplayInfo() => Plugin.VA_DisplayInfo(); public static string VA_DisplayInfo() => Plugin.VaDisplayInfo();
/// <summary> /// <summary>
/// The plugins GUID, as required by the VoiceAttack plugin API. /// 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. /// class for VoiceAttack to pick it up as a plugin.
/// </summary> /// </summary>
/// <returns>The GUID.</returns> /// <returns>The GUID.</returns>
public static Guid VA_Id() => Plugin.VA_Id(); public static Guid VA_Id() => Plugin.VaId();
/// <summary> /// <summary>
/// The Init method, as required by the VoiceAttack plugin API. /// 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. /// class for VoiceAttack to pick it up as a plugin.
/// </summary> /// </summary>
/// <param name="vaProxy">The VoiceAttack proxy object.</param> /// <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> /// <summary>
/// The Invoke method, as required by the VoiceAttack plugin API. /// 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. /// class for VoiceAttack to pick it up as a plugin.
/// </summary> /// </summary>
/// <param name="vaProxy">The VoiceAttack proxy object.</param> /// <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> /// <summary>
/// The Exit method, as required by the VoiceAttack plugin API. /// 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. /// class for VoiceAttack to pick it up as a plugin.
/// </summary> /// </summary>
/// <param name="vaProxy">The VoiceAttack proxy object.</param> /// <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> /// <summary>
/// The StopCommand method, as required by the VoiceAttack plugin API. /// 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 /// Since it is required to be static, it must be defined in your plugin
/// class for VoiceAttack to pick it up as a plugin. /// class for VoiceAttack to pick it up as a plugin.
/// </summary> /// </summary>
public static void VA_StopCommand() => Plugin.VA_StopCommand(); public static void VA_StopCommand() => Plugin.VaStopCommand();
/// <summary> /// <summary>
/// An example handler for VA_Init1. Init handlers are the place to do /// 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. /// loaded at VoiceAttack start.
/// </summary> /// </summary>
/// <param name="vaProxy">The current VoiceAttack proxy object.</param> /// <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."); 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. /// there is a default timeout of 5s.
/// </summary> /// </summary>
/// <param name="vaProxy">The current VoiceAttack proxy object.</param> /// <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."); 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 /// An example handler for VA_StopCommand. If your plugin needs to
/// execute anything when all commands are stopped this is the place. /// execute anything when all commands are stopped this is the place.
/// </summary> /// </summary>
private static void Stop() [Stop]
public static void Stop()
{ {
Plugin.Log.Notice("This is the example Stop handler method."); 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> /// <param name="vaProxy">The current VoiceAttack proxy object.</param>
[Context("test")] [Context("test")]
[Context("different test")] [Context("different test")]
private static void Test(VoiceAttackInvokeProxyClass vaProxy) public static void Test(VoiceAttackInvokeProxyClass vaProxy)
{ {
Plugin.Log.Notice( Plugin.Log.Notice(
$"This is the example handler for the plugin contexts “test” and “different test”. It has been invoked with '{vaProxy.Context}'."); $"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> /// <param name="vaProxy">The current VoiceAttack proxy object.</param>
[Context("^foo.*")] [Context("^foo.*")]
[Context("^.*bar.*")] [Context("^.*bar.*")]
private static void RegexContext(VoiceAttackInvokeProxyClass vaProxy) public static void RegexContext(VoiceAttackInvokeProxyClass vaProxy)
{ {
Plugin.Log.Notice( Plugin.Log.Notice(
$"This is the example handler for the plugin contexts “^foo.*” and “^.*bar.*”. It has been invoked with '{vaProxy.Context}'."); $"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;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using VoiceAttack; using VoiceAttack;
@ -34,38 +35,27 @@ namespace alterNERDtive.Yavapf
private VoiceAttackLog? log; private VoiceAttackLog? log;
private VoiceAttackInitProxyClass? vaProxy; 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> /// <summary>
/// Invoked when VoiceAttack initializes the plugin. /// Invoked when VoiceAttack initializes the plugin.
/// </summary> /// </summary>
public event Action<VoiceAttackInitProxyClass>? Init; protected event Action<VoiceAttackInitProxyClass>? InitActions;
/// <summary> /// <summary>
/// Invoked when VoiceAttack closes. /// Invoked when VoiceAttack closes.
/// </summary> /// </summary>
public event Action<VoiceAttackProxyClass>? Exit; protected event Action<VoiceAttackProxyClass>? ExitActions;
/// <summary> /// <summary>
/// Invoked when VoiceAttack stops all commands. /// Invoked when VoiceAttack stops all commands.
/// </summary> /// </summary>
public event Action? Stop; protected event Action? StopActions;
/// <summary> /// <summary>
/// Gets or sets the Actions to be run when the plugin is invoked from a /// Gets or sets the Actions to be run when the plugin is invoked from a
/// VoiceAttack command. Only Actions with a matching “Context” /// VoiceAttack command. Only Actions with a matching “Context”
/// attribute will be invoked. /// attribute will be invoked.
/// </summary> /// </summary>
public HandlerList<Action<VoiceAttackInvokeProxyClass>> Contexts { get; set; } = new (); protected HandlerList<Action<VoiceAttackInvokeProxyClass>> Contexts { get; set; } = new ();
/// <summary> /// <summary>
/// Gets the currently stored VoiceAttackInitProxyClass object which is /// 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 /// You will usually want to use the provided methods and Properties
/// instead. /// instead.
/// </summary> /// </summary>
public VoiceAttackInitProxyClass Proxy protected VoiceAttackInitProxyClass Proxy
{ {
get => this.vaProxy!; get => this.vaProxy!;
} }
/// <summary> /// <summary>
/// Gets the name of the plugin. /// Gets or sets the name of the plugin.
/// </summary> /// </summary>
public string Name { get; } public string? Name { get; set; }
/// <summary> /// <summary>
/// Gets the current version of the plugin. /// Gets or sets the current version of the plugin.
/// </summary> /// </summary>
public string Version { get; } public string? Version { get; set; }
/// <summary> /// <summary>
/// Gets the description of the plugin. /// Gets or sets the description of the plugin.
/// </summary> /// </summary>
public string Info { get; } public string? Info { get; set; }
/// <summary> /// <summary>
/// Gets the GUID of the plugin. /// Gets or sets the GUID of the plugin.
/// </summary> /// </summary>
public Guid Guid { get; } public string? Guid { get; set; }
/// <summary> /// <summary>
/// Gets the <see cref="VoiceAttackLog"/> instance the plugin uses to /// 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. /// The plugins display name, as required by the VoiceAttack plugin API.
/// </summary> /// </summary>
/// <returns>The display name.</returns> /// <returns>The display name.</returns>
public string VA_DisplayName() => $"{this.Name} v{this.Version}"; public string VaDisplayName() => $"{this.Name} v{this.Version}";
/// <summary> /// <summary>
/// The plugins description, as required by the VoiceAttack plugin API. /// The plugins description, as required by the VoiceAttack plugin API.
/// </summary> /// </summary>
/// <returns>The description.</returns> /// <returns>The description.</returns>
public string VA_DisplayInfo() => this.Info; public string VaDisplayInfo() => this.Info;
/// <summary> /// <summary>
/// The plugins GUID, as required by the VoiceAttack plugin API. /// The plugins GUID, as required by the VoiceAttack plugin API.
/// </summary> /// </summary>
/// <returns>The GUID.</returns> /// <returns>The GUID.</returns>
public Guid VA_Id() => this.Guid; public Guid VaId() => new (this.Guid);
/// <summary> /// <summary>
/// The Init method, as required by the VoiceAttack plugin API. /// The Init method, as required by the VoiceAttack plugin API.
/// Runs when the plugin is initially loaded. /// Runs when the plugin is initially loaded.
/// </summary> /// </summary>
/// <param name="vaProxy">The VoiceAttack proxy object.</param> /// <param name="vaProxy">The VoiceAttack proxy object.</param>
public void VA_Init1(VoiceAttackInitProxyClass vaProxy) public void VaInit1(VoiceAttackInitProxyClass vaProxy)
{ {
this.vaProxy = vaProxy; this.vaProxy = vaProxy;
@ -254,8 +244,28 @@ namespace alterNERDtive.Yavapf
this.vaProxy.BooleanVariableChanged += this.BooleanVariableChanged; this.vaProxy.BooleanVariableChanged += this.BooleanVariableChanged;
this.vaProxy.DateVariableChanged += this.DateVariableChanged; 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.Log.Debug("Running Init handlers …");
this.Init?.Invoke(vaProxy); this.InitActions?.Invoke(vaProxy);
this.Log.Debug("Finished running Init handlers."); this.Log.Debug("Finished running Init handlers.");
this.Set<bool>($"{this.Name}.initialized", true); this.Set<bool>($"{this.Name}.initialized", true);
@ -267,7 +277,7 @@ namespace alterNERDtive.Yavapf
/// Runs whenever a plugin context is invoked. /// Runs whenever a plugin context is invoked.
/// </summary> /// </summary>
/// <param name="vaProxy">The VoiceAttack proxy object.</param> /// <param name="vaProxy">The VoiceAttack proxy object.</param>
public void VA_Invoke1(VoiceAttackInvokeProxyClass vaProxy) public void VaInvoke1(VoiceAttackInvokeProxyClass vaProxy)
{ {
this.vaProxy = vaProxy; this.vaProxy = vaProxy;
@ -314,9 +324,9 @@ namespace alterNERDtive.Yavapf
/// Runs when VoiceAttack is shut down. /// Runs when VoiceAttack is shut down.
/// </summary> /// </summary>
/// <param name="vaProxy">The VoiceAttack proxy object.</param> /// <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> /// <summary>
@ -324,9 +334,9 @@ namespace alterNERDtive.Yavapf
/// Runs whenever all commands are stopped using the “Stop All Commands” /// Runs whenever all commands are stopped using the “Stop All Commands”
/// button or action. /// button or action.
/// </summary> /// </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) protected void TextVariableChanged(string name, string from, string to, Guid? internalID = null)
@ -363,16 +373,94 @@ namespace alterNERDtive.Yavapf
return handlers; return handlers;
} }
} }
protected class InitAttribute : Attribute
{
}
protected class ExitAttribute : Attribute
{
}
protected class StopAttribute : Attribute
{
} }
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class ContextAttribute : Attribute protected class ContextAttribute : Attribute
{ {
public string Name { get; } public string Name { get; }
public ContextAttribute(string context) public ContextAttribute(string name)
{ {
this.Name = context; 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;
}
} }
} }
} }