YAVAPF/VoiceAttack-Framework/VoiceAttackPlugin.cs

762 lines
30 KiB
C#
Raw Permalink Normal View History

2022-07-05 00:19:04 +02:00
// <copyright file="VoiceAttackPlugin.cs" company="alterNERDtive">
// Copyright 2022 alterNERDtive.
//
// This file is part of YAVAPF.
//
// YAVAPF is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// YAVAPF is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with YAVAPF. If not, see &lt;https://www.gnu.org/licenses/&gt;.
// </copyright>
using System;
2022-07-07 14:58:12 +02:00
using System.Collections.Generic;
using System.IO;
using System.Linq;
2022-07-07 14:58:12 +02:00
using System.Reflection;
2022-07-08 20:22:50 +02:00
using System.Text.RegularExpressions;
2022-07-09 00:30:51 +02:00
2022-07-07 14:58:12 +02:00
using VoiceAttack;
2022-07-05 00:19:04 +02:00
namespace alterNERDtive.Yavapf
{
2022-07-08 20:22:50 +02:00
/// <summary>
/// Framework class for implementing a VoiceAttack plugin.
/// </summary>
2022-07-05 00:19:04 +02:00
public class VoiceAttackPlugin
{
2022-07-08 20:22:50 +02:00
private VoiceAttackLog? log;
private VoiceAttackInitProxyClass? vaProxy;
/// <summary>
/// Invoked when VoiceAttack initializes the plugin.
/// </summary>
protected event Action<VoiceAttackInitProxyClass>? InitActions;
2022-07-07 14:58:12 +02:00
2022-07-08 20:22:50 +02:00
/// <summary>
/// Invoked when VoiceAttack closes.
/// </summary>
protected event Action<VoiceAttackProxyClass>? ExitActions;
2022-07-07 14:58:12 +02:00
2022-07-08 20:22:50 +02:00
/// <summary>
/// Invoked when VoiceAttack stops all commands.
/// </summary>
protected event Action? StopActions;
2022-07-07 14:58:12 +02:00
2022-07-08 20:22:50 +02:00
/// <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>
protected HandlerList<Action<VoiceAttackInvokeProxyClass>> Contexts { get; set; } = new ();
2022-07-05 00:19:04 +02:00
2022-07-09 00:30:51 +02:00
/// <summary>
/// Gets or sets the Actions to be run when a <see cref="bool"/>
/// variable changed.
/// </summary>
protected HandlerList<Action<string, bool?, bool?>> BoolChangedHandlers { get; set; } = new ();
2022-07-09 00:30:51 +02:00
/// <summary>
/// Gets or sets the Actions to be run when a <see cref="DateTime"/>
/// variable changed.
/// </summary>
protected HandlerList<Action<string, DateTime?, DateTime?>> DateTimeChangedHandlers { get; set; } = new ();
2022-07-09 00:30:51 +02:00
/// <summary>
/// Gets or sets the Actions to be run when a <see cref="decimal"/>
/// variable changed.
/// </summary>
protected HandlerList<Action<string, decimal?, decimal?>> DecimalChangedHandlers { get; set; } = new ();
2022-07-09 00:30:51 +02:00
/// <summary>
/// Gets or sets the Actions to be run when a <see cref="int"/>
/// variable changed.
/// </summary>
protected HandlerList<Action<string, int?, int?>> IntChangedHandlers { get; set; } = new ();
2022-07-09 00:30:51 +02:00
/// <summary>
/// Gets or sets the Actions to be run when a <see cref="string"/>
/// variable changed.
/// </summary>
protected HandlerList<Action<string, string?, string?>> StringChangedHandlers { get; set; } = new ();
2022-07-09 00:30:51 +02:00
2022-07-08 20:22:50 +02:00
/// <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>
protected VoiceAttackInitProxyClass Proxy
2022-07-07 14:58:12 +02:00
{
2022-07-08 20:22:50 +02:00
get => this.vaProxy!;
2022-07-07 14:58:12 +02:00
}
2022-07-08 20:22:50 +02:00
/// <summary>
/// Gets or sets the name of the plugin.
2022-07-08 20:22:50 +02:00
/// </summary>
2022-07-09 00:30:51 +02:00
protected string? Name { get; set; }
2022-07-05 00:19:04 +02:00
2022-07-08 20:22:50 +02:00
/// <summary>
/// Gets or sets the current version of the plugin.
2022-07-08 20:22:50 +02:00
/// </summary>
2022-07-09 00:30:51 +02:00
protected string? Version { get; set; }
2022-07-05 00:19:04 +02:00
2022-07-08 20:22:50 +02:00
/// <summary>
/// Gets or sets the description of the plugin.
2022-07-08 20:22:50 +02:00
/// </summary>
2022-07-09 00:30:51 +02:00
protected string? Info { get; set; }
2022-07-07 14:58:12 +02:00
2022-07-08 20:22:50 +02:00
/// <summary>
/// Gets or sets the GUID of the plugin.
2022-07-08 20:22:50 +02:00
/// </summary>
2022-07-09 00:30:51 +02:00
protected string? Guid { get; set; }
2022-07-07 14:58:12 +02:00
2022-07-08 20:22:50 +02:00
/// <summary>
/// Gets the <see cref="VoiceAttackLog"/> instance the plugin uses to
/// log to the VoiceAttack event log.
2022-07-09 00:30:51 +02:00
///
2022-07-08 20:22:50 +02:00
/// You can use this to log your own messages.
/// </summary>
2022-07-09 00:30:51 +02:00
protected VoiceAttackLog Log
2022-07-08 20:22:50 +02:00
{
2022-07-09 00:30:51 +02:00
get => this.log ??= new VoiceAttackLog(this.Proxy, this.Name!);
2022-07-07 14:58:12 +02:00
}
2022-07-08 20:22:50 +02:00
/// <summary>
/// Gets the value of a variable from VoiceAttack.
///
/// Valid varible types are <see cref="bool"/>, <see cref="DateTime"/>,
2022-07-09 00:30:51 +02:00
/// <see cref="decimal"/>, <see cref="int"/> and <see cref="string"/>.
2022-07-08 20:22:50 +02:00
/// </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>
2022-07-09 00:30:51 +02:00
protected T? Get<T>(string name)
2022-07-08 20:22:50 +02:00
{
2022-07-11 10:25:06 +02:00
if (name.StartsWith("~"))
2022-07-08 20:22:50 +02:00
{
2022-07-11 10:25:06 +02:00
this.Log.Warn(
$"Accessing command scoped variable '{name}' outside of its context proxy object. This might lead to race conditions.");
}
return this.Proxy.Get<T>(name);
2022-07-08 20:22:50 +02:00
}
2022-07-07 14:58:12 +02:00
2022-07-08 20:22:50 +02:00
/// <summary>
/// Sets a variable for use in VoiceAttack.
///
/// Valid varible types are <see cref="bool"/>, <see cref="DateTime"/>,
2022-07-09 00:30:51 +02:00
/// <see cref="decimal"/>, <see cref="int"/> and <see cref="string"/>.
2022-07-08 20:22:50 +02:00
/// </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>
2022-07-09 00:30:51 +02:00
protected void Set<T>(string name, T? value)
2022-07-07 14:58:12 +02:00
{
2022-07-11 10:25:06 +02:00
if (name.StartsWith("~"))
2022-07-08 20:22:50 +02:00
{
2022-07-11 10:25:06 +02:00
this.Log.Warn(
$"Accessing command scoped variable '{name}' outside of its context proxy object. This might lead to race conditions.");
2022-07-07 14:58:12 +02:00
}
2022-07-11 10:25:06 +02:00
this.Proxy.Set<T>(name, value);
2022-07-07 14:58:12 +02:00
}
2022-07-08 20:22:50 +02:00
/// <summary>
/// Unsets a variable for use in VoiceAttack (= sets it to null).
///
/// Valid varible types are <see cref="bool"/>, <see cref="DateTime"/>,
2022-07-09 00:30:51 +02:00
/// <see cref="decimal"/>, <see cref="int"/> and <see cref="string"/>.
2022-07-08 20:22:50 +02:00
/// </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>
2022-07-09 00:30:51 +02:00
protected void Unset<T>(string name)
2022-07-07 14:58:12 +02:00
{
2022-07-11 10:25:06 +02:00
if (name.StartsWith("~"))
2022-07-07 14:58:12 +02:00
{
2022-07-11 10:25:06 +02:00
this.Log.Warn(
$"Accessing command scoped variable '{name}' outside of its context proxy object. This might lead to race conditions.");
2022-07-07 14:58:12 +02:00
}
2022-07-11 10:25:06 +02:00
this.Proxy.Unset<T>(name);
2022-07-07 14:58:12 +02:00
}
2022-07-05 00:19:04 +02:00
2022-07-08 20:22:50 +02:00
/// <summary>
/// The plugins display name, as required by the VoiceAttack plugin API.
/// </summary>
/// <returns>The display name.</returns>
2022-07-09 00:30:51 +02:00
protected string VaDisplayName() => $"{this.Name} v{this.Version}";
2022-07-05 00:19:04 +02:00
2022-07-08 20:22:50 +02:00
/// <summary>
/// The plugins description, as required by the VoiceAttack plugin API.
/// </summary>
/// <returns>The description.</returns>
2022-07-09 00:30:51 +02:00
protected string VaDisplayInfo() => this.Info!;
2022-07-05 00:19:04 +02:00
2022-07-08 20:22:50 +02:00
/// <summary>
/// The plugins GUID, as required by the VoiceAttack plugin API.
/// </summary>
/// <returns>The GUID.</returns>
2022-07-09 00:30:51 +02:00
protected Guid VaId() => new (this.Guid);
2022-07-05 00:19:04 +02:00
2022-07-08 20:22:50 +02:00
/// <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>
2022-07-09 00:30:51 +02:00
protected void VaInit1(VoiceAttackInitProxyClass vaProxy)
2022-07-05 00:19:04 +02:00
{
this.vaProxy = vaProxy;
2022-07-07 14:58:12 +02:00
this.Set<string>($"{this.Name}.version", this.Version);
this.Log.Debug($"Initializing v{this.Version} …");
this.vaProxy.BooleanVariableChanged += this.BooleanVariableChanged;
this.vaProxy.DateVariableChanged += this.DateVariableChanged;
2022-07-09 00:30:51 +02:00
this.vaProxy.DecimalVariableChanged += this.DecimalVariableChanged;
this.vaProxy.IntegerVariableChanged += this.IntegerVariableChanged;
this.vaProxy.TextVariableChanged += this.TextVariableChanged;
2022-07-07 14:58:12 +02:00
2022-07-09 16:33:32 +02:00
this.GetType().GetMethods().Where(m => m.GetCustomAttributes<InitAttribute>().Any()).ToList().ForEach(
m => this.InitActions += (Action<VoiceAttackInitProxyClass>)m.CreateDelegate(typeof(Action<VoiceAttackInitProxyClass>)));
2022-07-09 16:33:32 +02:00
this.GetType().GetMethods().Where(m => m.GetCustomAttributes<ExitAttribute>().Any()).ToList().ForEach(
m => this.ExitActions += (Action<VoiceAttackProxyClass>)m.CreateDelegate(typeof(Action<VoiceAttackProxyClass>)));
this.GetType().GetMethods().Where(m => m.GetCustomAttributes<StopCommandAttribute>().Any()).ToList().ForEach(
2022-07-09 16:33:32 +02:00
m => this.StopActions += (Action)m.CreateDelegate(typeof(Action)));
2022-07-09 16:33:32 +02:00
this.GetType().GetMethods().Where(m => m.GetCustomAttributes<ContextAttribute>().Any()).ToList().ForEach(
m => this.Contexts += (Action<VoiceAttackInvokeProxyClass>)m.CreateDelegate(typeof(Action<VoiceAttackInvokeProxyClass>)));
2022-07-09 16:33:32 +02:00
this.GetType().GetMethods().Where(m => m.GetCustomAttributes<BoolAttribute>().Any()).ToList().ForEach(
m => this.BoolChangedHandlers += (Action<string, bool?, bool?>)m.CreateDelegate(typeof(Action<string, bool?, bool?>)));
2022-07-09 00:30:51 +02:00
2022-07-09 16:33:32 +02:00
this.GetType().GetMethods().Where(m => m.GetCustomAttributes<DateTimeAttribute>().Any()).ToList().ForEach(
m => this.DateTimeChangedHandlers += (Action<string, DateTime?, DateTime?>)m.CreateDelegate(typeof(Action<string, DateTime?, DateTime?>)));
2022-07-09 00:30:51 +02:00
2022-07-09 16:33:32 +02:00
this.GetType().GetMethods().Where(m => m.GetCustomAttributes<DecimalAttribute>().Any()).ToList().ForEach(
m => this.DecimalChangedHandlers += (Action<string, decimal?, decimal?>)m.CreateDelegate(typeof(Action<string, decimal?, decimal?>)));
2022-07-09 00:30:51 +02:00
2022-07-09 16:33:32 +02:00
this.GetType().GetMethods().Where(m => m.GetCustomAttributes<IntAttribute>().Any()).ToList().ForEach(
m => this.IntChangedHandlers += (Action<string, int?, int?>)m.CreateDelegate(typeof(Action<string, int?, int?>)));
2022-07-09 00:30:51 +02:00
2022-07-09 16:33:32 +02:00
this.GetType().GetMethods().Where(m => m.GetCustomAttributes<StringAttribute>().Any()).ToList().ForEach(
m => this.StringChangedHandlers += (Action<string, string?, string?>)m.CreateDelegate(typeof(Action<string, string?, string?>)));
2022-07-09 00:30:51 +02:00
2022-07-07 14:58:12 +02:00
this.Log.Debug("Running Init handlers …");
this.InitActions?.Invoke(vaProxy);
2022-07-07 14:58:12 +02:00
this.Log.Debug("Finished running Init handlers.");
this.Set<bool>($"{this.Name}.initialized", true);
this.Log.Debug("Initialized.");
2022-07-05 00:19:04 +02:00
}
2022-07-08 20:22:50 +02:00
/// <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>
2022-07-09 00:30:51 +02:00
protected void VaInvoke1(VoiceAttackInvokeProxyClass vaProxy)
2022-07-05 00:19:04 +02:00
{
this.vaProxy = vaProxy;
2022-07-07 14:58:12 +02:00
string context = vaProxy.Context.ToLower();
if (context.StartsWith("log."))
2022-07-07 14:58:12 +02:00
{
try
2022-07-07 14:58:12 +02:00
{
string message = this.Get<string>("~message") ?? throw new ArgumentNullException("~message");
switch (context)
2022-07-09 00:30:51 +02:00
{
case "log.error":
this.Log.Error(message);
break;
case "log.warn":
this.Log.Warn(message);
break;
case "log.notice":
this.Log.Notice(message);
break;
case "log.info":
this.Log.Info(message);
break;
case "log.debug":
this.Log.Debug(message);
break;
default:
throw new ArgumentException("invalid context", "context");
2022-07-07 14:58:12 +02:00
}
}
catch (ArgumentNullException e)
{
this.Log.Error($"Missing parameter '{e.ParamName}' for context '{context}'");
}
catch (ArgumentException e) when (e.ParamName == "context")
{
this.Log.Error($"Invalid plugin context '{vaProxy.Context}'.");
}
catch (Exception e)
{
this.Log.Error($"Unhandled exception while executing plugin context '{context}': {e.Message}");
}
2022-07-07 14:58:12 +02:00
}
2022-07-09 00:30:51 +02:00
else
2022-07-07 14:58:12 +02:00
{
List<Action<VoiceAttackInvokeProxyClass>> actions = this.Contexts.Where(
action => action.Method.GetCustomAttributes<ContextAttribute>().Where(
attr => attr.Name == context ||
(attr.Name.StartsWith("^") && Regex.Match(context, attr.Name).Success))
.Any()).ToList();
if (actions.Any())
{
foreach (Action<VoiceAttackInvokeProxyClass> action in actions)
{
try
{
action.Invoke(vaProxy);
}
catch (ArgumentNullException e)
{
this.Log.Error($"Missing parameter '{e.ParamName}' for context '{context}'");
}
catch (ArgumentException e) when (e.ParamName == "context")
{
this.Log.Error($"Invalid plugin context '{vaProxy.Context}'.");
}
catch (Exception e)
{
this.Log.Error($"Unhandled exception while executing plugin context '{context}': {e.Message}");
}
}
}
else
{
this.Log.Error($"Invalid plugin context '{vaProxy.Context}'.");
}
2022-07-07 14:58:12 +02:00
}
2022-07-05 00:19:04 +02:00
}
2022-07-08 20:22:50 +02:00
/// <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>
2022-07-09 00:30:51 +02:00
protected void VaExit1(VoiceAttackProxyClass vaProxy)
2022-07-05 00:19:04 +02:00
{
this.ExitActions?.Invoke(vaProxy);
2022-07-05 00:19:04 +02:00
}
2022-07-08 20:22:50 +02:00
/// <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>
2022-07-09 00:30:51 +02:00
protected void VaStopCommand()
2022-07-05 00:19:04 +02:00
{
this.StopActions?.Invoke();
2022-07-05 00:19:04 +02:00
}
2022-07-09 00:30:51 +02:00
/// <summary>
/// Invoked when a <see cref="bool"/> variable changed.
/// </summary>
/// <param name="name">The name of the variable.</param>
/// <param name="from">The old value of the variable.</param>
/// <param name="to">The new value of the variable.</param>
/// <param name="internalID">The internal GUID of the variable.</param>
private void BooleanVariableChanged(string name, bool? from, bool? to, Guid? internalID = null)
2022-07-07 14:58:12 +02:00
{
foreach (Action<string, bool?, bool?> action in this.BoolChangedHandlers.Where(
2022-07-09 00:30:51 +02:00
action => action.Method.GetCustomAttributes<BoolAttribute>().Where(
attr => attr.Name == name ||
2022-07-09 00:42:57 +02:00
(attr.Name.StartsWith("^") && Regex.Match(name, attr.Name).Success))
.Any()).ToList())
2022-07-09 00:30:51 +02:00
{
try
{
action.Invoke(name, from, to);
2022-07-09 00:30:51 +02:00
}
catch (Exception e)
{
this.Log.Error($"Unhandled exception while handling changed bool variable '{name}': {e.Message}");
}
}
2022-07-07 14:58:12 +02:00
}
2022-07-05 00:19:04 +02:00
2022-07-09 00:30:51 +02:00
/// <summary>
/// Invoked when a <see cref="DateTime"/> variable changed.
/// </summary>
/// <param name="name">The name of the variable.</param>
/// <param name="from">The old value of the variable.</param>
/// <param name="to">The new value of the variable.</param>
/// <param name="internalID">The internal GUID of the variable.</param>
private void DateVariableChanged(string name, DateTime? from, DateTime? to, Guid? internalID = null)
2022-07-07 14:58:12 +02:00
{
foreach (Action<string, DateTime?, DateTime?> action in this.DateTimeChangedHandlers.Where(
2022-07-09 00:30:51 +02:00
action => action.Method.GetCustomAttributes<DateTimeAttribute>().Where(
attr => attr.Name == name ||
2022-07-09 00:42:57 +02:00
(attr.Name.StartsWith("^") && Regex.Match(name, attr.Name).Success))
.Any()).ToList())
2022-07-09 00:30:51 +02:00
{
try
{
action.Invoke(name, from, to);
2022-07-09 00:30:51 +02:00
}
catch (Exception e)
{
this.Log.Error($"Unhandled exception while handling changed DateTime variable '{name}': {e.Message}");
}
}
2022-07-07 14:58:12 +02:00
}
2022-07-09 00:30:51 +02:00
/// <summary>
/// Invoked when a <see cref="decimal"/> variable changed.
/// </summary>
/// <param name="name">The name of the variable.</param>
/// <param name="from">The old value of the variable.</param>
/// <param name="to">The new value of the variable.</param>
/// <param name="internalID">The internal GUID of the variable.</param>
private void DecimalVariableChanged(string name, decimal? from, decimal? to, Guid? internalID = null)
2022-07-07 14:58:12 +02:00
{
foreach (Action<string, decimal?, decimal?> action in this.DecimalChangedHandlers.Where(
2022-07-09 00:30:51 +02:00
action => action.Method.GetCustomAttributes<DecimalAttribute>().Where(
attr => attr.Name == name ||
2022-07-09 00:42:57 +02:00
(attr.Name.StartsWith("^") && Regex.Match(name, attr.Name).Success))
.Any()).ToList())
2022-07-09 00:30:51 +02:00
{
try
{
action.Invoke(name, from, to);
2022-07-09 00:30:51 +02:00
}
catch (Exception e)
{
this.Log.Error($"Unhandled exception while handling changed decimal variable '{name}': {e.Message}");
}
}
2022-07-07 14:58:12 +02:00
}
2022-07-09 00:30:51 +02:00
/// <summary>
/// Invoked when a <see cref="int"/> variable changed.
/// </summary>
/// <param name="name">The name of the variable.</param>
/// <param name="from">The old value of the variable.</param>
/// <param name="to">The new value of the variable.</param>
/// <param name="internalID">The internal GUID of the variable.</param>
private void IntegerVariableChanged(string name, int? from, int? to, Guid? internalID = null)
2022-07-07 14:58:12 +02:00
{
foreach (Action<string, int?, int?> action in this.IntChangedHandlers.Where(
2022-07-09 00:30:51 +02:00
action => action.Method.GetCustomAttributes<IntAttribute>().Where(
attr => attr.Name == name ||
2022-07-09 00:42:57 +02:00
(attr.Name.StartsWith("^") && Regex.Match(name, attr.Name).Success))
.Any()).ToList())
2022-07-09 00:30:51 +02:00
{
try
{
action.Invoke(name, from, to);
2022-07-09 00:30:51 +02:00
}
catch (Exception e)
{
this.Log.Error($"Unhandled exception while handling changed int variable '{name}': {e.Message}");
}
}
2022-07-07 14:58:12 +02:00
}
2022-07-05 00:19:04 +02:00
2022-07-09 00:30:51 +02:00
/// <summary>
/// Invoked when a <see cref="string"/> variable changed.
/// </summary>
/// <param name="name">The name of the variable.</param>
/// <param name="from">The old value of the variable.</param>
/// <param name="to">The new value of the variable.</param>
/// <param name="internalID">The internal GUID of the variable.</param>
private void TextVariableChanged(string name, string? from, string? to, Guid? internalID = null)
2022-07-07 14:58:12 +02:00
{
if (name == $"{this.Name}.loglevel#")
{
try
{
this.Log.SetLogLevel(to);
}
catch (ArgumentException)
{
this.Log.Error($"Error setting log level: '{to!}' is not a valid log level.");
}
}
foreach (Action<string, string?, string?> action in this.StringChangedHandlers.Where(
2022-07-09 00:30:51 +02:00
action => action.Method.GetCustomAttributes<StringAttribute>().Where(
attr => attr.Name == name ||
2022-07-09 00:42:57 +02:00
(attr.Name.StartsWith("^") && Regex.Match(name, attr.Name).Success))
.Any()).ToList())
2022-07-09 00:30:51 +02:00
{
try
{
action.Invoke(name, from, to);
2022-07-09 00:30:51 +02:00
}
catch (Exception e)
{
this.Log.Error($"Unhandled exception while handling changed string variable '{name}': {e.Message}");
}
}
2022-07-07 14:58:12 +02:00
}
2022-07-05 00:19:04 +02:00
2022-07-09 00:30:51 +02:00
/// <summary>
/// A list of event handlers (Actions). Basically just a list that
/// implements the + and - operators because they look nice.
/// </summary>
/// <typeparam name="T">The type of the list.</typeparam>
protected class HandlerList<T> : List<T>
2022-07-07 14:58:12 +02:00
{
2022-07-09 16:33:32 +02:00
/// <summary>
/// Adds a handler to the list.
/// </summary>
/// <param name="handlers">The list to add to.</param>
/// <param name="item">The handler to add.</param>
/// <returns>The sum of both.</returns>
2022-07-07 14:58:12 +02:00
public static HandlerList<T> operator +(HandlerList<T> handlers, T item)
{
handlers.Add(item);
return handlers;
}
2022-07-09 16:33:32 +02:00
/// <summary>
/// Removes a handler from the list.
/// </summary>
/// <param name="handlers">The list to remove from.</param>
/// <param name="item">The handler to remove.</param>
/// <returns>The list without the handler.</returns>
2022-07-07 14:58:12 +02:00
public static HandlerList<T> operator -(HandlerList<T> handlers, T item)
{
handlers.Remove(item);
return handlers;
}
}
2022-07-09 00:30:51 +02:00
/// <summary>
/// Denotes a handler for <see
/// cref="VaInit1(VoiceAttackInitProxyClass)"/>.
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
protected class InitAttribute : Attribute
{
}
2022-07-09 00:30:51 +02:00
/// <summary>
/// Denotes a handler for <see
2022-07-09 00:42:57 +02:00
/// cref="VaExit1(VoiceAttackProxyClass)"/>.
2022-07-09 00:30:51 +02:00
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
protected class ExitAttribute : Attribute
{
}
2022-07-09 00:30:51 +02:00
/// <summary>
/// Denotes a handler for <see cref="VaStopCommand()"/>.
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
protected class StopCommandAttribute : Attribute
{
}
2022-07-09 00:30:51 +02:00
/// <summary>
/// Denotes a handler for a plugin contexts.
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
protected class ContextAttribute : Attribute
{
2022-07-09 00:30:51 +02:00
/// <summary>
/// Initializes a new instance of the <see cref="ContextAttribute"/>
/// class.
/// </summary>
/// <param name="name">The name of or regex for the context.</param>
public ContextAttribute(string name)
{
this.Name = name.ToLower();
}
2022-07-09 00:30:51 +02:00
/// <summary>
/// Initializes a new instance of the <see cref="ContextAttribute"/>
/// class that will be invoked for all contexts.
/// </summary>
public ContextAttribute()
{
this.Name = "^.*";
}
/// <summary>
/// Gets the name of or regex for the context.
/// </summary>
public string Name { get; }
}
2022-07-09 00:30:51 +02:00
/// <summary>
/// Denotes a handler for changed <see cref="bool"/> variables.
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
protected class BoolAttribute : Attribute
{
2022-07-09 00:30:51 +02:00
/// <summary>
/// Initializes a new instance of the <see cref="BoolAttribute"/>
/// class.
/// </summary>
/// <param name="name">The name of or regex for the variable.</param>
public BoolAttribute(string name)
{
this.Name = name;
}
2022-07-09 00:30:51 +02:00
/// <summary>
/// Initializes a new instance of the <see cref="BoolAttribute"/>
/// class that will be invoked for all variables.
/// </summary>
public BoolAttribute()
{
this.Name = "^.*";
}
/// <summary>
/// Gets the name of or regex for the variable name.
/// </summary>
public string Name { get; }
}
2022-07-09 00:30:51 +02:00
/// <summary>
/// Denotes a handler for changed <see cref="DateTime"/> variables.
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
protected class DateTimeAttribute : Attribute
{
2022-07-09 00:30:51 +02:00
/// <summary>
/// Initializes a new instance of the <see
/// cref="DateTimeAttribute"/> class.
/// </summary>
/// <param name="name">The name of or regex for the variable.</param>
public DateTimeAttribute(string name)
{
this.Name = name;
}
2022-07-09 00:30:51 +02:00
/// <summary>
/// Initializes a new instance of the <see
/// cref="DateTimeAttribute"/> class that will be invoked for all
/// variables.
/// </summary>
public DateTimeAttribute()
{
this.Name = "^.*";
}
/// <summary>
/// Gets the name of or regex for the variable.
/// </summary>
public string Name { get; }
}
2022-07-09 00:30:51 +02:00
/// <summary>
/// Denotes a handler for changed <see cref="decimal"/> variables.
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
protected class DecimalAttribute : Attribute
{
2022-07-09 00:30:51 +02:00
/// <summary>
/// Initializes a new instance of the <see cref="DecimalAttribute"/>
/// class.
/// </summary>
/// <param name="name">The name of or regex for the variable.</param>
public DecimalAttribute(string name)
{
this.Name = name;
}
2022-07-09 00:30:51 +02:00
/// <summary>
/// Initializes a new instance of the <see cref="DecimalAttribute"/>
/// class that will be invoked for all variables.
/// </summary>
public DecimalAttribute()
{
this.Name = "^.*";
}
/// <summary>
/// Gets the name of or regex for the variable.
/// </summary>
public string Name { get; }
}
2022-07-09 00:30:51 +02:00
/// <summary>
/// Denotes a handler for changed <see cref="int"/> variables.
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
protected class IntAttribute : Attribute
{
2022-07-09 00:30:51 +02:00
/// <summary>
/// Initializes a new instance of the <see cref="IntAttribute"/>
/// class.
/// </summary>
/// <param name="name">The name of or regex for the variable.</param>
public IntAttribute(string name)
{
this.Name = name;
}
2022-07-09 00:30:51 +02:00
/// <summary>
/// Initializes a new instance of the <see cref="IntAttribute"/>
/// class that will be invoked for all variables.
/// </summary>
public IntAttribute()
{
2022-07-09 00:30:51 +02:00
this.Name = "^.*";
}
2022-07-09 00:30:51 +02:00
/// <summary>
/// Gets the name of or regex for the variable.
/// </summary>
public string Name { get; }
}
2022-07-05 00:19:04 +02:00
2022-07-09 00:30:51 +02:00
/// <summary>
/// Denotes a handler for changed <see cref="string"/> variables.
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
protected class StringAttribute : Attribute
2022-07-07 14:58:12 +02:00
{
2022-07-09 00:30:51 +02:00
/// <summary>
/// Initializes a new instance of the <see cref="StringAttribute"/>
/// class.
/// </summary>
/// <param name="name">The name of or regex for the variable.</param>
public StringAttribute(string name)
{
this.Name = name;
}
2022-07-09 00:30:51 +02:00
/// <summary>
/// Initializes a new instance of the <see cref="StringAttribute"/>
/// class that is invoked for all variables.
/// </summary>
public StringAttribute()
{
this.Name = "^.*";
}
/// <summary>
/// Gets the name of or regex for the variable.
/// </summary>
public string Name { get; }
2022-07-07 14:58:12 +02:00
}
2022-07-05 00:19:04 +02:00
}
}