more ground work
This commit is contained in:
parent
64d4b26d42
commit
feb0206951
3 changed files with 266 additions and 79 deletions
|
@ -64,8 +64,17 @@ namespace alterNERDtive.Yavapf.Example
|
|||
// they are to be executed for.
|
||||
// See the definition of the “Test” method below.
|
||||
Plugin.Contexts += Test;
|
||||
Plugin.Contexts += EmptyContext;
|
||||
Plugin.Contexts += RegexContext;
|
||||
|
||||
// You can even add lambdas! This one logs an explicit error for
|
||||
// invoking the plugin without a context.
|
||||
Plugin.Contexts +=
|
||||
[Context("")]
|
||||
(vaProxy)
|
||||
=>
|
||||
{
|
||||
Plugin.Log.Error($"Invoking this plugin without a context is not allowed.");
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -182,7 +191,9 @@ namespace alterNERDtive.Yavapf.Example
|
|||
{
|
||||
Plugin.Log.Notice(
|
||||
$"This is the example handler for the plugin contexts “test” and “different test”. It has been invoked with '{vaProxy.Context}'.");
|
||||
_ = Plugin.Get<string?>("~test") ?? throw new ArgumentNullException("~test");
|
||||
|
||||
string test = Plugin.Get<string?>("~test") ?? throw new ArgumentNullException("~test");
|
||||
Plugin.Log.Notice($"The value of 'TXT:~test' is '{test}'");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -190,7 +201,7 @@ namespace alterNERDtive.Yavapf.Example
|
|||
/// specified with “Context” attributes. Contexts must be all lower case.
|
||||
///
|
||||
/// This example demonstrates using regular expressions. It handles all
|
||||
/// contexts that begin with “foo” or contain.
|
||||
/// contexts that begin with “foo” or contain “bar”.
|
||||
/// </summary>
|
||||
/// <param name="vaProxy">The current VoiceAttack proxy object.</param>
|
||||
[Context("^foo.*")]
|
||||
|
@ -200,19 +211,5 @@ namespace alterNERDtive.Yavapf.Example
|
|||
Plugin.Log.Notice(
|
||||
$"This is the example handler for the plugin contexts “^foo.*” and “^.*bar.*”. It has been invoked with '{vaProxy.Context}'.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An example handler for plugin contexts. The context(s) must be
|
||||
/// specified with “Context” attributes. Contexts must be all lower case.
|
||||
///
|
||||
/// This example displays a custom error message when the plugin is
|
||||
/// invoked with no context (= empty context).
|
||||
/// </summary>
|
||||
/// <param name="vaProxy">The current VoiceAttack proxy object.</param>
|
||||
[Context("")]
|
||||
private static void EmptyContext(VoiceAttackInvokeProxyClass vaProxy)
|
||||
{
|
||||
Plugin.Log.Error($"Invoking this plugin without a context is not allowed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,9 @@ using VoiceAttack;
|
|||
|
||||
namespace alterNERDtive.Yavapf
|
||||
{
|
||||
/// <summary>
|
||||
/// Exposes methods for writing to the VoiceAttack event log.
|
||||
/// </summary>
|
||||
public class VoiceAttackLog
|
||||
{
|
||||
private static readonly string[] LogColour = { "red", "yellow", "green", "blue", "gray" };
|
||||
|
@ -32,12 +35,20 @@ namespace alterNERDtive.Yavapf
|
|||
private readonly VoiceAttackInitProxyClass vaProxy;
|
||||
private readonly string id;
|
||||
|
||||
public VoiceAttackLog(VoiceAttackInitProxyClass vaProxy, string id)
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="VoiceAttackLog"/> class.
|
||||
/// </summary>
|
||||
/// <param name="vaProxy">A VoiceAttackInitProxyClass object to interface with.</param>
|
||||
/// <param name="name">The plugin name.</param>
|
||||
public VoiceAttackLog(VoiceAttackInitProxyClass vaProxy, string name)
|
||||
{
|
||||
this.vaProxy = vaProxy;
|
||||
this.id = id;
|
||||
this.id = name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the current log level. Default is NOTICE.
|
||||
/// </summary>
|
||||
public LogLevel? LogLevel
|
||||
{
|
||||
get => logLevel ?? Yavapf.LogLevel.NOTICE;
|
||||
|
@ -48,6 +59,12 @@ namespace alterNERDtive.Yavapf
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the current log level.
|
||||
///
|
||||
/// Valid values are ERROR, WARN, NOTICE, INFO and DEBUG.
|
||||
/// </summary>
|
||||
/// <param name="level">The new log level.</param>
|
||||
public void SetLogLevel(string level)
|
||||
{
|
||||
if (level == null)
|
||||
|
@ -60,6 +77,15 @@ namespace alterNERDtive.Yavapf
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs a given message with the specified log level.
|
||||
///
|
||||
/// If the current log level is higher than the message’s log level it
|
||||
/// will not be logged.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to be logged.</param>
|
||||
/// <param name="level">The desired log level.</param>
|
||||
/// <exception cref="ArgumentNullException">Thrown when the message is null.</exception>
|
||||
public void Log(string message, LogLevel level = Yavapf.LogLevel.INFO)
|
||||
{
|
||||
_ = message ?? throw new ArgumentNullException("message");
|
||||
|
@ -70,20 +96,51 @@ namespace alterNERDtive.Yavapf
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs a given message with the ERROR log level.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to be logged.</param>
|
||||
public void Error(string message) => this.Log(message, Yavapf.LogLevel.ERROR);
|
||||
|
||||
/// <summary>
|
||||
/// Logs a given message with the WARN log level.
|
||||
///
|
||||
/// If the current log level is ERROR the message will not be logged.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to be logged.</param>
|
||||
public void Warn(string message) => this.Log(message, Yavapf.LogLevel.WARN);
|
||||
|
||||
/// <summary>
|
||||
/// Logs a given message with the NOTICE log level.
|
||||
///
|
||||
/// If the current log level is ERROR or WARN the message will not be
|
||||
/// logged.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to be logged.</param>
|
||||
public void Notice(string message) => this.Log(message, Yavapf.LogLevel.NOTICE);
|
||||
|
||||
/// <summary>
|
||||
/// Logs a given message with the INFO log level.
|
||||
///
|
||||
/// If the current log level is ERROR, WARN or NOTICE the message will
|
||||
/// not be logged.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to be logged.</param>
|
||||
public void Info(string message) => this.Log(message, Yavapf.LogLevel.INFO);
|
||||
|
||||
/// <summary>
|
||||
/// Logs a given message with the DEBUG log level.
|
||||
///
|
||||
/// If the current log level is not DEBUG the message will not be logged.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to be logged.</param>
|
||||
public void Debug(string message) => this.Log(message, Yavapf.LogLevel.DEBUG);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log levels that can be used when writing to the VoiceAttack log.
|
||||
/// Log levels that can be used when writing to the VoiceAttack event log.
|
||||
/// </summary>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1201:Elements should appear in the correct order", Justification = "Well it’s this or wrong file name, isn’t it?")]
|
||||
public enum LogLevel
|
||||
{
|
||||
/// <summary>
|
||||
|
|
|
@ -21,114 +21,226 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using VoiceAttack;
|
||||
|
||||
namespace alterNERDtive.Yavapf
|
||||
{
|
||||
/// <summary>
|
||||
/// Framework class for implementing a VoiceAttack plugin.
|
||||
/// </summary>
|
||||
public class VoiceAttackPlugin
|
||||
{
|
||||
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));
|
||||
|
||||
// this just hides the default constructor to make sure Properties are set.
|
||||
private VoiceAttackPlugin()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when VoiceAttack initializes the plugin.
|
||||
/// </summary>
|
||||
public event Action<VoiceAttackInitProxyClass>? Init;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when VoiceAttack closes.
|
||||
/// </summary>
|
||||
public event Action<VoiceAttackProxyClass>? Exit;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when VoiceAttack stops all commands.
|
||||
/// </summary>
|
||||
public event Action? Stop;
|
||||
|
||||
/// <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 ();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the currently stored VoiceAttackInitProxyClass object which is
|
||||
/// used to interface with VoiceAttack.
|
||||
///
|
||||
/// You will usually want to use the provided methods and Properties
|
||||
/// instead.
|
||||
/// </summary>
|
||||
public VoiceAttackInitProxyClass Proxy
|
||||
{
|
||||
get => this.vaProxy;
|
||||
get => this.vaProxy!;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the plugin.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current version of the plugin.
|
||||
/// </summary>
|
||||
public string Version { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the description of the plugin.
|
||||
/// </summary>
|
||||
public string Info { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the GUID of the plugin.
|
||||
/// </summary>
|
||||
public Guid Guid { get; }
|
||||
|
||||
public VoiceAttackLog Log {
|
||||
get => this.log ??= new VoiceAttackLog(this.vaProxy, this.Name);
|
||||
/// <summary>
|
||||
/// Gets the <see cref="VoiceAttackLog"/> instance the plugin uses to
|
||||
/// log to the VoiceAttack event log.
|
||||
///
|
||||
/// You can use this to log your own messages.
|
||||
/// </summary>
|
||||
public VoiceAttackLog Log
|
||||
{
|
||||
get => this.log ??= new VoiceAttackLog(this.Proxy, this.Name);
|
||||
}
|
||||
|
||||
private VoiceAttackLog log;
|
||||
|
||||
protected VoiceAttackInitProxyClass vaProxy;
|
||||
/// <summary>
|
||||
/// Gets the value of a variable from VoiceAttack.
|
||||
///
|
||||
/// Valid varible types are <see cref="bool"/>, <see cref="DateTime"/>,
|
||||
/// <see cref="decimal"/>, <see cref="int"/>, <see cref="short"/> and
|
||||
/// <see cref="string"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the variable.</typeparam>
|
||||
/// <param name="name">The name of the variable.</param>
|
||||
/// <returns>The value of the variable. Can be null.</returns>
|
||||
/// <exception cref="InvalidDataException">Thrown when the variable is of an invalid type.</exception>
|
||||
public T? Get<T>(string name)
|
||||
{
|
||||
dynamic? ret = typeof(T) switch
|
||||
{
|
||||
Type boolType when boolType == typeof(bool) => this.Proxy.GetBoolean(name),
|
||||
Type dateType when dateType == typeof(DateTime) => this.Proxy.GetDate(name),
|
||||
Type decType when decType == typeof(decimal) => this.Proxy.GetDecimal(name),
|
||||
Type intType when intType == typeof(int) => this.Proxy.GetInt(name),
|
||||
Type shortType when shortType == typeof(short) => this.Proxy.GetSmallInt(name),
|
||||
Type stringType when stringType == typeof(string) => this.Proxy.GetText(name),
|
||||
_ => throw new InvalidDataException($"Cannot get variable '{name}': invalid type '{typeof(T).Name}'."),
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a variable for use in VoiceAttack.
|
||||
///
|
||||
/// Valid varible types are <see cref="bool"/>, <see cref="DateTime"/>,
|
||||
/// <see cref="decimal"/>, <see cref="int"/>, <see cref="short"/> and
|
||||
/// <see cref="string"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the variable.</typeparam>
|
||||
/// <param name="name">The name of the variable.</param>
|
||||
/// <param name="value">The value of the variable. Can not be null.</param>
|
||||
/// <exception cref="InvalidDataException">Thrown when the variable is of an invalid type.</exception>
|
||||
public void Set<T>(string name, T? value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
this.Unset<T>(name);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case bool b:
|
||||
this.vaProxy.SetBoolean(name, b);
|
||||
this.Proxy.SetBoolean(name, b);
|
||||
break;
|
||||
case DateTime d:
|
||||
this.vaProxy.SetDate(name, d);
|
||||
this.Proxy.SetDate(name, d);
|
||||
break;
|
||||
case decimal d:
|
||||
this.vaProxy.SetDecimal(name, d);
|
||||
this.Proxy.SetDecimal(name, d);
|
||||
break;
|
||||
case int i:
|
||||
this.vaProxy.SetInt(name, i);
|
||||
this.Proxy.SetInt(name, i);
|
||||
break;
|
||||
case short s:
|
||||
this.vaProxy.SetSmallInt(name, s);
|
||||
this.Proxy.SetSmallInt(name, s);
|
||||
break;
|
||||
case string s:
|
||||
this.vaProxy.SetText(name, s);
|
||||
this.Proxy.SetText(name, s);
|
||||
break;
|
||||
default:
|
||||
throw new InvalidDataException($"Cannot set variable '{name}': invalid type '{typeof(T).Name}'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsets a variable for use in VoiceAttack (= sets it to null).
|
||||
///
|
||||
/// Valid varible types are <see cref="bool"/>, <see cref="DateTime"/>,
|
||||
/// <see cref="decimal"/>, <see cref="int"/>, <see cref="short"/> and
|
||||
/// <see cref="string"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the variable.</typeparam>
|
||||
/// <param name="name">The name of the variable.</param>
|
||||
/// <exception cref="InvalidDataException">Thrown when the variable is of an invalid type.</exception>
|
||||
public void Unset<T>(string name)
|
||||
{
|
||||
switch (typeof(T))
|
||||
{
|
||||
case Type boolType when boolType == typeof(bool):
|
||||
this.Proxy.SetBoolean(name, null);
|
||||
break;
|
||||
case Type dateType when dateType == typeof(DateTime):
|
||||
this.Proxy.SetDate(name, null);
|
||||
break;
|
||||
case Type decType when decType == typeof(decimal):
|
||||
this.Proxy.SetDecimal(name, null);
|
||||
break;
|
||||
case Type intType when intType == typeof(int):
|
||||
this.Proxy.SetInt(name, null);
|
||||
break;
|
||||
case Type shortType when shortType == typeof(short):
|
||||
this.Proxy.SetSmallInt(name, null);
|
||||
break;
|
||||
case Type stringType when stringType == typeof(string):
|
||||
this.Proxy.SetText(name, null);
|
||||
break;
|
||||
default:
|
||||
throw new InvalidDataException($"Cannot set variable '{name}': invalid type '{typeof(T).Name}'.");
|
||||
}
|
||||
}
|
||||
|
||||
public T? Get<T>(string name)
|
||||
{
|
||||
dynamic? ret = null;
|
||||
|
||||
switch (default(T))
|
||||
{
|
||||
case bool _:
|
||||
ret = this.vaProxy.GetBoolean(name);
|
||||
break;
|
||||
case DateTime _:
|
||||
ret = this.vaProxy.GetDate(name);
|
||||
break;
|
||||
case decimal _:
|
||||
ret = this.vaProxy.GetDecimal(name);
|
||||
break;
|
||||
case int _:
|
||||
ret = this.vaProxy.GetInt(name);
|
||||
break;
|
||||
case short _:
|
||||
ret = this.vaProxy.GetSmallInt(name);
|
||||
break;
|
||||
case string _:
|
||||
ret = this.vaProxy.GetText(name);
|
||||
break;
|
||||
default:
|
||||
throw new InvalidDataException($"Cannot get variable '{name}': invalid type '{typeof(T).Name}'.");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The plugin’s display name, as required by the VoiceAttack plugin API.
|
||||
/// </summary>
|
||||
/// <returns>The display name.</returns>
|
||||
public string VA_DisplayName() => $"{this.Name} v{this.Version}";
|
||||
|
||||
/// <summary>
|
||||
/// The plugin’s description, as required by the VoiceAttack plugin API.
|
||||
/// </summary>
|
||||
/// <returns>The description.</returns>
|
||||
public string VA_DisplayInfo() => this.Info;
|
||||
|
||||
/// <summary>
|
||||
/// The plugin’s GUID, as required by the VoiceAttack plugin API.
|
||||
/// </summary>
|
||||
/// <returns>The GUID.</returns>
|
||||
public Guid VA_Id() => 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)
|
||||
{
|
||||
this.vaProxy = vaProxy;
|
||||
|
@ -150,6 +262,11 @@ namespace alterNERDtive.Yavapf
|
|||
this.Log.Debug("Initialized.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Invoke method, as required by the VoiceAttack plugin API.
|
||||
/// Runs whenever a plugin context is invoked.
|
||||
/// </summary>
|
||||
/// <param name="vaProxy">The VoiceAttack proxy object.</param>
|
||||
public void VA_Invoke1(VoiceAttackInvokeProxyClass vaProxy)
|
||||
{
|
||||
this.vaProxy = vaProxy;
|
||||
|
@ -159,14 +276,20 @@ namespace alterNERDtive.Yavapf
|
|||
try
|
||||
{
|
||||
bool exists = false;
|
||||
foreach (Action<VoiceAttackInvokeProxyClass> action in Contexts)
|
||||
foreach (Action<VoiceAttackInvokeProxyClass> action in this.Contexts)
|
||||
{
|
||||
foreach (ContextAttribute attr in action.Method.GetCustomAttributes<ContextAttribute>())
|
||||
{
|
||||
if (attr.Name == context)
|
||||
if (attr.Name == context ||
|
||||
(attr.Name.StartsWith("^") && Regex.Match(context, attr.Name).Success))
|
||||
{
|
||||
exists = true;
|
||||
action.Invoke(vaProxy);
|
||||
|
||||
// Make sure that we don’t run the same Action
|
||||
// multiple times just because it has e.g. multiple
|
||||
// matching context regexes.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -186,21 +309,31 @@ namespace alterNERDtive.Yavapf
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Exit method, as required by the VoiceAttack plugin API.
|
||||
/// Runs when VoiceAttack is shut down.
|
||||
/// </summary>
|
||||
/// <param name="vaProxy">The VoiceAttack proxy object.</param>
|
||||
public void VA_Exit1(VoiceAttackProxyClass vaProxy)
|
||||
{
|
||||
this.Exit?.Invoke(vaProxy);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The StopCommand method, as required by the VoiceAttack plugin API.
|
||||
/// Runs whenever all commands are stopped using the “Stop All Commands”
|
||||
/// button or action.
|
||||
/// </summary>
|
||||
public void VA_StopCommand()
|
||||
{
|
||||
this.Stop?.Invoke();
|
||||
}
|
||||
|
||||
private void TextVariableChanged(string name, string from, string to, Guid? internalID = null)
|
||||
protected void TextVariableChanged(string name, string from, string to, Guid? internalID = null)
|
||||
{
|
||||
}
|
||||
|
||||
private void IntegerVariableChanged(string name, int? from, int? to, Guid? internalID = null)
|
||||
protected void IntegerVariableChanged(string name, int? from, int? to, Guid? internalID = null)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue