documentation
plus some refactoring / added code on the way …
This commit is contained in:
parent
0e477058cc
commit
5b6db94bce
21 changed files with 971 additions and 92 deletions
|
@ -19,9 +19,10 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
|
using alterNERDtive.Yavapf;
|
||||||
using VoiceAttack;
|
using VoiceAttack;
|
||||||
|
|
||||||
namespace alterNERDtive.Yavapf.Example
|
namespace alterNERDtive.Example
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This is an example for a VoiceAttack plugin using YAVAPF.
|
/// This is an example for a VoiceAttack plugin using YAVAPF.
|
||||||
|
@ -163,7 +164,7 @@ 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>
|
||||||
[Stop]
|
[StopCommand]
|
||||||
public static void 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.");
|
||||||
|
@ -197,8 +198,8 @@ namespace alterNERDtive.Yavapf.Example
|
||||||
/// contexts that begin with “foo” or contain “bar”.
|
/// contexts that begin with “foo” or contain “bar”.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <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.*")]
|
||||||
public static void RegexContext(VoiceAttackInvokeProxyClass vaProxy)
|
public static void RegexContext(VoiceAttackInvokeProxyClass vaProxy)
|
||||||
{
|
{
|
||||||
Plugin.Log.Notice(
|
Plugin.Log.Notice(
|
||||||
|
@ -212,9 +213,8 @@ namespace alterNERDtive.Yavapf.Example
|
||||||
/// <param name="name">The name of the variable.</param>
|
/// <param name="name">The name of the variable.</param>
|
||||||
/// <param name="from">The old value 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="to">The new value of the variable.</param>
|
||||||
/// <param name="internalID">The GUID of the variable.</param>
|
|
||||||
[Bool("isDay#")]
|
[Bool("isDay#")]
|
||||||
public static void DayChanged(string name, bool? from, bool? to, Guid? internalID)
|
public static void DayChanged(string name, bool? from, bool? to)
|
||||||
{
|
{
|
||||||
Plugin.Log.Notice($"This is the example handler for changed bool variables. It is now {(to ?? false ? "day" : "night")}.");
|
Plugin.Log.Notice($"This is the example handler for changed bool variables. It is now {(to ?? false ? "day" : "night")}.");
|
||||||
}
|
}
|
||||||
|
@ -226,11 +226,14 @@ namespace alterNERDtive.Yavapf.Example
|
||||||
/// <param name="name">The name of the variable.</param>
|
/// <param name="name">The name of the variable.</param>
|
||||||
/// <param name="from">The old value 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="to">The new value of the variable.</param>
|
||||||
/// <param name="internalID">The GUID of the variable.</param>
|
|
||||||
[String]
|
[String]
|
||||||
public static void StringChanged(string name, string? from, string? to, Guid? internalID)
|
public static void StringChanged(string name, string? from, string? to)
|
||||||
|
{
|
||||||
|
// exclude log level changes
|
||||||
|
if (name != $"{Plugin.Name}.loglevel#")
|
||||||
{
|
{
|
||||||
Plugin.Log.Notice($"This is the example handler for changed string variables. '{name}' changed from '{from ?? "Not Set"}' to '{to ?? "Not Set"}'.");
|
Plugin.Log.Notice($"This is the example handler for changed string variables. '{name}' changed from '{from ?? "Not Set"}' to '{to ?? "Not Set"}'.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net48</TargetFramework>
|
<TargetFramework>net48</TargetFramework>
|
||||||
<RootNamespace>alterNERDtive.Yavapf.Example</RootNamespace>
|
<RootNamespace>alterNERDtive.Example</RootNamespace>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
|
@ -20,6 +20,7 @@
|
||||||
<Reference Include="VoiceAttack">
|
<Reference Include="VoiceAttack">
|
||||||
<HintPath>C:\Program Files\VoiceAttack\VoiceAttack.exe</HintPath>
|
<HintPath>C:\Program Files\VoiceAttack\VoiceAttack.exe</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
|
<EmbedInteropTypes>False</EmbedInteropTypes>
|
||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
|
@ -23,4 +23,4 @@
|
||||||
// a specific target and scoped to a namespace, type, member, etc.
|
// a specific target and scoped to a namespace, type, member, etc.
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
[assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "It’s my name, you prick :)", Scope = "namespace", Target = "~N:alterNERDtive.Yavapf.Example")]
|
[assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "It’s my name, you prick :)", Scope = "namespace", Target = "~N:alterNERDtive.Example")]
|
||||||
|
|
125
ExamplePlugin/MinimumViablePlugin.cs
Normal file
125
ExamplePlugin/MinimumViablePlugin.cs
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
// <copyright file="MinimumViablePlugin.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 <https://www.gnu.org/licenses/>.
|
||||||
|
// </copyright>
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
using alterNERDtive.Yavapf;
|
||||||
|
|
||||||
|
namespace alterNERDtive.Example
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This is an example for a VoiceAttack plugin using YAVAPF.
|
||||||
|
///
|
||||||
|
/// You can use this class and this project as base for your own implementation.
|
||||||
|
/// </summary>
|
||||||
|
public class MinimumViablePlugin : VoiceAttackPlugin
|
||||||
|
{
|
||||||
|
private static readonly MinimumViablePlugin Plugin;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes static members of the <see cref="MinimumViablePlugin"/> class.
|
||||||
|
///
|
||||||
|
/// Since VoiceAttack’s plugin API requires a bunch of static methods
|
||||||
|
/// instead of instantiating a plugin class, the “Constructor” here also
|
||||||
|
/// needs to be static. It is executed right before a static method is
|
||||||
|
/// used for the first time, which would usually be when VoiceAttack
|
||||||
|
/// calls the <see cref="VA_Init1(dynamic)"/> method.
|
||||||
|
/// </summary>
|
||||||
|
static MinimumViablePlugin()
|
||||||
|
{
|
||||||
|
// You can generate a GUID in Visual Studio under “Tools” → “Create
|
||||||
|
// GUID”. Choose “Registry Format”.
|
||||||
|
Plugin = new ()
|
||||||
|
{
|
||||||
|
Name = "Minimum Viable Plugin",
|
||||||
|
Version = "0.0.1",
|
||||||
|
Info = "This is a description",
|
||||||
|
Guid = "{2E5CDD74-0E05-4745-A791-76E8C5AABBC3}",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The plugin’s display name, as required by the VoiceAttack plugin
|
||||||
|
/// API.
|
||||||
|
///
|
||||||
|
/// 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>
|
||||||
|
/// <returns>The display name.</returns>
|
||||||
|
public static string VA_DisplayName() => Plugin.VaDisplayName();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The plugin’s description, as required by the VoiceAttack plugin API.
|
||||||
|
///
|
||||||
|
/// 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>
|
||||||
|
/// <returns>The description.</returns>
|
||||||
|
public static string VA_DisplayInfo() => Plugin.VaDisplayInfo();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The plugin’s GUID, as required by the VoiceAttack plugin API.
|
||||||
|
///
|
||||||
|
/// 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>
|
||||||
|
/// <returns>The GUID.</returns>
|
||||||
|
public static Guid VA_Id() => Plugin.VaId();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Init method, as required by the VoiceAttack plugin API.
|
||||||
|
/// Runs when the plugin is initially loaded.
|
||||||
|
///
|
||||||
|
/// 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>
|
||||||
|
/// <param name="vaProxy">The VoiceAttack proxy object.</param>
|
||||||
|
public static void VA_Init1(dynamic vaProxy) => Plugin.VaInit1(vaProxy);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Invoke method, as required by the VoiceAttack plugin API.
|
||||||
|
/// Runs whenever a plugin context is invoked.
|
||||||
|
///
|
||||||
|
/// 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>
|
||||||
|
/// <param name="vaProxy">The VoiceAttack proxy object.</param>
|
||||||
|
public static void VA_Invoke1(dynamic vaProxy) => Plugin.VaInvoke1(vaProxy);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Exit method, as required by the VoiceAttack plugin API.
|
||||||
|
/// Runs when VoiceAttack is shut down.
|
||||||
|
///
|
||||||
|
/// 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>
|
||||||
|
/// <param name="vaProxy">The VoiceAttack proxy object.</param>
|
||||||
|
public static void VA_Exit1(dynamic vaProxy) => Plugin.VaExit1(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.
|
||||||
|
///
|
||||||
|
/// 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.VaStopCommand();
|
||||||
|
}
|
||||||
|
}
|
2
ExamplePlugin/Properties/Settings.Designer.cs
generated
2
ExamplePlugin/Properties/Settings.Designer.cs
generated
|
@ -8,7 +8,7 @@
|
||||||
// </auto-generated>
|
// </auto-generated>
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace alterNERDtive.Yavapf.Example.Properties {
|
namespace alterNERDtive.Example.Properties {
|
||||||
|
|
||||||
|
|
||||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
|
|
|
@ -16,6 +16,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||||
LICENSE = LICENSE
|
LICENSE = LICENSE
|
||||||
mkdocs.yml = mkdocs.yml
|
mkdocs.yml = mkdocs.yml
|
||||||
README.md = README.md
|
README.md = README.md
|
||||||
|
requirements.txt = requirements.txt
|
||||||
stylecop.json = stylecop.json
|
stylecop.json = stylecop.json
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
|
@ -42,13 +43,15 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{3FE46F37-0
|
||||||
docs\commands.md = docs\commands.md
|
docs\commands.md = docs\commands.md
|
||||||
docs\contexts.md = docs\contexts.md
|
docs\contexts.md = docs\contexts.md
|
||||||
docs\events.md = docs\events.md
|
docs\events.md = docs\events.md
|
||||||
|
docs\extra.css = docs\extra.css
|
||||||
docs\gettingstarted.md = docs\gettingstarted.md
|
docs\gettingstarted.md = docs\gettingstarted.md
|
||||||
|
docs\faq.md = docs\faq.md
|
||||||
docs\index.md = docs\index.md
|
docs\index.md = docs\index.md
|
||||||
docs\troubleshooting.md = docs\troubleshooting.md
|
docs\logging.md = docs\logging.md
|
||||||
docs\variables.md = docs\variables.md
|
docs\variables.md = docs\variables.md
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VoiceAttack-Framework-Test", "VoiceAttack-Framework-Test\VoiceAttack-Framework-Test.csproj", "{D49B2D9A-1014-4554-977A-E57E9972D196}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VoiceAttack-Framework-Test", "VoiceAttack-Framework-Test\VoiceAttack-Framework-Test.csproj", "{D49B2D9A-1014-4554-977A-E57E9972D196}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<TargetFrameworks>net48</TargetFrameworks>
|
<TargetFrameworks>net48</TargetFrameworks>
|
||||||
<RootNamespace>alterNERDtive.Yavapf</RootNamespace>
|
<RootNamespace>alterNERDtive.Yavapf</RootNamespace>
|
||||||
<PackageId>alterNERDtive.YAVAPF</PackageId>
|
<PackageId>alterNERDtive.YAVAPF</PackageId>
|
||||||
<Version>0.0.1</Version>
|
<Version>0.1.0</Version>
|
||||||
<Company>alterNERDtive</Company>
|
<Company>alterNERDtive</Company>
|
||||||
<Authors>alterNERDtive</Authors>
|
<Authors>alterNERDtive</Authors>
|
||||||
<Description>YAVAPF is yet another VoiceAttack plugin framework.</Description>
|
<Description>YAVAPF is yet another VoiceAttack plugin framework.</Description>
|
||||||
|
@ -15,7 +15,7 @@
|
||||||
<PackageIconUrl />
|
<PackageIconUrl />
|
||||||
<RepositoryUrl>https://github.com/alterNERDtive/YAVAPF</RepositoryUrl>
|
<RepositoryUrl>https://github.com/alterNERDtive/YAVAPF</RepositoryUrl>
|
||||||
<RepositoryType>git</RepositoryType>
|
<RepositoryType>git</RepositoryType>
|
||||||
<PackageTags>VoiceAttack</PackageTags>
|
<PackageTags>VoiceAttack;plugin;framework</PackageTags>
|
||||||
<NeutralLanguage>en</NeutralLanguage>
|
<NeutralLanguage>en</NeutralLanguage>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||||
|
@ -44,6 +44,7 @@
|
||||||
<Reference Include="VoiceAttack">
|
<Reference Include="VoiceAttack">
|
||||||
<HintPath>C:\Program Files\VoiceAttack\VoiceAttack.exe</HintPath>
|
<HintPath>C:\Program Files\VoiceAttack\VoiceAttack.exe</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
|
<EmbedInteropTypes>False</EmbedInteropTypes>
|
||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
|
@ -53,11 +53,15 @@ namespace alterNERDtive.Yavapf
|
||||||
{
|
{
|
||||||
get => logLevel ?? Yavapf.LogLevel.NOTICE;
|
get => logLevel ?? Yavapf.LogLevel.NOTICE;
|
||||||
set
|
set
|
||||||
|
{
|
||||||
|
if (value != logLevel)
|
||||||
{
|
{
|
||||||
logLevel = value;
|
logLevel = value;
|
||||||
|
this.vaProxy.SetText($"{this.id}.loglevel#", value.ToString().ToLower());
|
||||||
this.Notice($"Log level set to {value ?? Yavapf.LogLevel.NOTICE}.");
|
this.Notice($"Log level set to {value ?? Yavapf.LogLevel.NOTICE}.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the current log level.
|
/// Sets the current log level.
|
||||||
|
@ -65,7 +69,9 @@ namespace alterNERDtive.Yavapf
|
||||||
/// Valid values are ERROR, WARN, NOTICE, INFO and DEBUG.
|
/// Valid values are ERROR, WARN, NOTICE, INFO and DEBUG.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="level">The new log level.</param>
|
/// <param name="level">The new log level.</param>
|
||||||
public void SetLogLevel(string level)
|
/// <exception cref="ArgumentException">Thrown when <paramref
|
||||||
|
/// name="level"/>is not a valid log level.</exception>
|
||||||
|
public void SetLogLevel(string? level)
|
||||||
{
|
{
|
||||||
if (level == null)
|
if (level == null)
|
||||||
{
|
{
|
||||||
|
@ -77,25 +83,6 @@ 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");
|
|
||||||
|
|
||||||
if (level <= this.LogLevel)
|
|
||||||
{
|
|
||||||
this.vaProxy.WriteToLog($"{level} | {this.id}: {message}", LogColour[(int)level]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Logs a given message with the ERROR log level.
|
/// Logs a given message with the ERROR log level.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -135,6 +122,25 @@ namespace alterNERDtive.Yavapf
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="message">The message to be logged.</param>
|
/// <param name="message">The message to be logged.</param>
|
||||||
public void Debug(string message) => this.Log(message, Yavapf.LogLevel.DEBUG);
|
public void Debug(string message) => this.Log(message, Yavapf.LogLevel.DEBUG);
|
||||||
|
|
||||||
|
/// <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>
|
||||||
|
private void Log(string message, LogLevel level = Yavapf.LogLevel.INFO)
|
||||||
|
{
|
||||||
|
_ = message ?? throw new ArgumentNullException("message");
|
||||||
|
|
||||||
|
if (level <= this.LogLevel)
|
||||||
|
{
|
||||||
|
this.vaProxy.WriteToLog($"{level} | {this.id}: {message}", LogColour[(int)level]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -62,31 +62,31 @@ namespace alterNERDtive.Yavapf
|
||||||
/// Gets or sets the Actions to be run when a <see cref="bool"/>
|
/// Gets or sets the Actions to be run when a <see cref="bool"/>
|
||||||
/// variable changed.
|
/// variable changed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected HandlerList<Action<string, bool?, bool?, Guid?>> BoolChangedHandlers { get; set; } = new ();
|
protected HandlerList<Action<string, bool?, bool?>> BoolChangedHandlers { get; set; } = new ();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the Actions to be run when a <see cref="DateTime"/>
|
/// Gets or sets the Actions to be run when a <see cref="DateTime"/>
|
||||||
/// variable changed.
|
/// variable changed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected HandlerList<Action<string, DateTime?, DateTime?, Guid?>> DateTimeChangedHandlers { get; set; } = new ();
|
protected HandlerList<Action<string, DateTime?, DateTime?>> DateTimeChangedHandlers { get; set; } = new ();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the Actions to be run when a <see cref="decimal"/>
|
/// Gets or sets the Actions to be run when a <see cref="decimal"/>
|
||||||
/// variable changed.
|
/// variable changed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected HandlerList<Action<string, decimal?, decimal?, Guid?>> DecimalChangedHandlers { get; set; } = new ();
|
protected HandlerList<Action<string, decimal?, decimal?>> DecimalChangedHandlers { get; set; } = new ();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the Actions to be run when a <see cref="int"/>
|
/// Gets or sets the Actions to be run when a <see cref="int"/>
|
||||||
/// variable changed.
|
/// variable changed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected HandlerList<Action<string, int?, int?, Guid?>> IntChangedHandlers { get; set; } = new ();
|
protected HandlerList<Action<string, int?, int?>> IntChangedHandlers { get; set; } = new ();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the Actions to be run when a <see cref="string"/>
|
/// Gets or sets the Actions to be run when a <see cref="string"/>
|
||||||
/// variable changed.
|
/// variable changed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected HandlerList<Action<string, string?, string?, Guid?>> StringChangedHandlers { get; set; } = new ();
|
protected HandlerList<Action<string, string?, string?>> StringChangedHandlers { get; set; } = new ();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the currently stored VoiceAttackInitProxyClass object which is
|
/// Gets the currently stored VoiceAttackInitProxyClass object which is
|
||||||
|
@ -271,26 +271,26 @@ namespace alterNERDtive.Yavapf
|
||||||
this.GetType().GetMethods().Where(m => m.GetCustomAttributes<ExitAttribute>().Any()).ToList().ForEach(
|
this.GetType().GetMethods().Where(m => m.GetCustomAttributes<ExitAttribute>().Any()).ToList().ForEach(
|
||||||
m => this.ExitActions += (Action<VoiceAttackProxyClass>)m.CreateDelegate(typeof(Action<VoiceAttackProxyClass>)));
|
m => this.ExitActions += (Action<VoiceAttackProxyClass>)m.CreateDelegate(typeof(Action<VoiceAttackProxyClass>)));
|
||||||
|
|
||||||
this.GetType().GetMethods().Where(m => m.GetCustomAttributes<StopAttribute>().Any()).ToList().ForEach(
|
this.GetType().GetMethods().Where(m => m.GetCustomAttributes<StopCommandAttribute>().Any()).ToList().ForEach(
|
||||||
m => this.StopActions += (Action)m.CreateDelegate(typeof(Action)));
|
m => this.StopActions += (Action)m.CreateDelegate(typeof(Action)));
|
||||||
|
|
||||||
this.GetType().GetMethods().Where(m => m.GetCustomAttributes<ContextAttribute>().Any()).ToList().ForEach(
|
this.GetType().GetMethods().Where(m => m.GetCustomAttributes<ContextAttribute>().Any()).ToList().ForEach(
|
||||||
m => this.Contexts += (Action<VoiceAttackInvokeProxyClass>)m.CreateDelegate(typeof(Action<VoiceAttackInvokeProxyClass>)));
|
m => this.Contexts += (Action<VoiceAttackInvokeProxyClass>)m.CreateDelegate(typeof(Action<VoiceAttackInvokeProxyClass>)));
|
||||||
|
|
||||||
this.GetType().GetMethods().Where(m => m.GetCustomAttributes<BoolAttribute>().Any()).ToList().ForEach(
|
this.GetType().GetMethods().Where(m => m.GetCustomAttributes<BoolAttribute>().Any()).ToList().ForEach(
|
||||||
m => this.BoolChangedHandlers += (Action<string, bool?, bool?, Guid?>)m.CreateDelegate(typeof(Action<string, bool?, bool?, Guid?>)));
|
m => this.BoolChangedHandlers += (Action<string, bool?, bool?>)m.CreateDelegate(typeof(Action<string, bool?, bool?>)));
|
||||||
|
|
||||||
this.GetType().GetMethods().Where(m => m.GetCustomAttributes<DateTimeAttribute>().Any()).ToList().ForEach(
|
this.GetType().GetMethods().Where(m => m.GetCustomAttributes<DateTimeAttribute>().Any()).ToList().ForEach(
|
||||||
m => this.DateTimeChangedHandlers += (Action<string, DateTime?, DateTime?, Guid?>)m.CreateDelegate(typeof(Action<string, DateTime?, DateTime?, Guid?>)));
|
m => this.DateTimeChangedHandlers += (Action<string, DateTime?, DateTime?>)m.CreateDelegate(typeof(Action<string, DateTime?, DateTime?>)));
|
||||||
|
|
||||||
this.GetType().GetMethods().Where(m => m.GetCustomAttributes<DecimalAttribute>().Any()).ToList().ForEach(
|
this.GetType().GetMethods().Where(m => m.GetCustomAttributes<DecimalAttribute>().Any()).ToList().ForEach(
|
||||||
m => this.DecimalChangedHandlers += (Action<string, decimal?, decimal?, Guid?>)m.CreateDelegate(typeof(Action<string, decimal?, decimal?, Guid?>)));
|
m => this.DecimalChangedHandlers += (Action<string, decimal?, decimal?>)m.CreateDelegate(typeof(Action<string, decimal?, decimal?>)));
|
||||||
|
|
||||||
this.GetType().GetMethods().Where(m => m.GetCustomAttributes<IntAttribute>().Any()).ToList().ForEach(
|
this.GetType().GetMethods().Where(m => m.GetCustomAttributes<IntAttribute>().Any()).ToList().ForEach(
|
||||||
m => this.IntChangedHandlers += (Action<string, int?, int?, Guid?>)m.CreateDelegate(typeof(Action<string, int?, int?, Guid?>)));
|
m => this.IntChangedHandlers += (Action<string, int?, int?>)m.CreateDelegate(typeof(Action<string, int?, int?>)));
|
||||||
|
|
||||||
this.GetType().GetMethods().Where(m => m.GetCustomAttributes<StringAttribute>().Any()).ToList().ForEach(
|
this.GetType().GetMethods().Where(m => m.GetCustomAttributes<StringAttribute>().Any()).ToList().ForEach(
|
||||||
m => this.StringChangedHandlers += (Action<string, string?, string?, Guid?>)m.CreateDelegate(typeof(Action<string, string?, string?, Guid?>)));
|
m => this.StringChangedHandlers += (Action<string, string?, string?>)m.CreateDelegate(typeof(Action<string, string?, string?>)));
|
||||||
|
|
||||||
this.Log.Debug("Running Init handlers …");
|
this.Log.Debug("Running Init handlers …");
|
||||||
this.InitActions?.Invoke(vaProxy);
|
this.InitActions?.Invoke(vaProxy);
|
||||||
|
@ -311,6 +311,47 @@ namespace alterNERDtive.Yavapf
|
||||||
|
|
||||||
string context = vaProxy.Context.ToLower();
|
string context = vaProxy.Context.ToLower();
|
||||||
|
|
||||||
|
if (context.StartsWith("log."))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string message = this.Get<string>("~message") ?? throw new ArgumentNullException("~message");
|
||||||
|
switch (context)
|
||||||
|
{
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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
|
||||||
|
{
|
||||||
List<Action<VoiceAttackInvokeProxyClass>> actions = this.Contexts.Where(
|
List<Action<VoiceAttackInvokeProxyClass>> actions = this.Contexts.Where(
|
||||||
action => action.Method.GetCustomAttributes<ContextAttribute>().Where(
|
action => action.Method.GetCustomAttributes<ContextAttribute>().Where(
|
||||||
attr => attr.Name == context ||
|
attr => attr.Name == context ||
|
||||||
|
@ -329,6 +370,10 @@ namespace alterNERDtive.Yavapf
|
||||||
{
|
{
|
||||||
this.Log.Error($"Missing parameter '{e.ParamName}' for context '{context}'");
|
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)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
this.Log.Error($"Unhandled exception while executing plugin context '{context}': {e.Message}");
|
this.Log.Error($"Unhandled exception while executing plugin context '{context}': {e.Message}");
|
||||||
|
@ -340,6 +385,7 @@ namespace alterNERDtive.Yavapf
|
||||||
this.Log.Error($"Invalid plugin context '{vaProxy.Context}'.");
|
this.Log.Error($"Invalid plugin context '{vaProxy.Context}'.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Exit method, as required by the VoiceAttack plugin API.
|
/// The Exit method, as required by the VoiceAttack plugin API.
|
||||||
|
@ -370,7 +416,7 @@ namespace alterNERDtive.Yavapf
|
||||||
/// <param name="internalID">The internal GUID 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)
|
private void BooleanVariableChanged(string name, bool? from, bool? to, Guid? internalID = null)
|
||||||
{
|
{
|
||||||
foreach (Action<string, bool?, bool?, Guid?> action in this.BoolChangedHandlers.Where(
|
foreach (Action<string, bool?, bool?> action in this.BoolChangedHandlers.Where(
|
||||||
action => action.Method.GetCustomAttributes<BoolAttribute>().Where(
|
action => action.Method.GetCustomAttributes<BoolAttribute>().Where(
|
||||||
attr => attr.Name == name ||
|
attr => attr.Name == name ||
|
||||||
(attr.Name.StartsWith("^") && Regex.Match(name, attr.Name).Success))
|
(attr.Name.StartsWith("^") && Regex.Match(name, attr.Name).Success))
|
||||||
|
@ -378,7 +424,7 @@ namespace alterNERDtive.Yavapf
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
action.Invoke(name, from, to, internalID);
|
action.Invoke(name, from, to);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
@ -396,7 +442,7 @@ namespace alterNERDtive.Yavapf
|
||||||
/// <param name="internalID">The internal GUID 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)
|
private void DateVariableChanged(string name, DateTime? from, DateTime? to, Guid? internalID = null)
|
||||||
{
|
{
|
||||||
foreach (Action<string, DateTime?, DateTime?, Guid?> action in this.DateTimeChangedHandlers.Where(
|
foreach (Action<string, DateTime?, DateTime?> action in this.DateTimeChangedHandlers.Where(
|
||||||
action => action.Method.GetCustomAttributes<DateTimeAttribute>().Where(
|
action => action.Method.GetCustomAttributes<DateTimeAttribute>().Where(
|
||||||
attr => attr.Name == name ||
|
attr => attr.Name == name ||
|
||||||
(attr.Name.StartsWith("^") && Regex.Match(name, attr.Name).Success))
|
(attr.Name.StartsWith("^") && Regex.Match(name, attr.Name).Success))
|
||||||
|
@ -404,7 +450,7 @@ namespace alterNERDtive.Yavapf
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
action.Invoke(name, from, to, internalID);
|
action.Invoke(name, from, to);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
@ -422,7 +468,7 @@ namespace alterNERDtive.Yavapf
|
||||||
/// <param name="internalID">The internal GUID 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)
|
private void DecimalVariableChanged(string name, decimal? from, decimal? to, Guid? internalID = null)
|
||||||
{
|
{
|
||||||
foreach (Action<string, decimal?, decimal?, Guid?> action in this.DecimalChangedHandlers.Where(
|
foreach (Action<string, decimal?, decimal?> action in this.DecimalChangedHandlers.Where(
|
||||||
action => action.Method.GetCustomAttributes<DecimalAttribute>().Where(
|
action => action.Method.GetCustomAttributes<DecimalAttribute>().Where(
|
||||||
attr => attr.Name == name ||
|
attr => attr.Name == name ||
|
||||||
(attr.Name.StartsWith("^") && Regex.Match(name, attr.Name).Success))
|
(attr.Name.StartsWith("^") && Regex.Match(name, attr.Name).Success))
|
||||||
|
@ -430,7 +476,7 @@ namespace alterNERDtive.Yavapf
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
action.Invoke(name, from, to, internalID);
|
action.Invoke(name, from, to);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
@ -448,7 +494,7 @@ namespace alterNERDtive.Yavapf
|
||||||
/// <param name="internalID">The internal GUID 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)
|
private void IntegerVariableChanged(string name, int? from, int? to, Guid? internalID = null)
|
||||||
{
|
{
|
||||||
foreach (Action<string, int?, int?, Guid?> action in this.IntChangedHandlers.Where(
|
foreach (Action<string, int?, int?> action in this.IntChangedHandlers.Where(
|
||||||
action => action.Method.GetCustomAttributes<IntAttribute>().Where(
|
action => action.Method.GetCustomAttributes<IntAttribute>().Where(
|
||||||
attr => attr.Name == name ||
|
attr => attr.Name == name ||
|
||||||
(attr.Name.StartsWith("^") && Regex.Match(name, attr.Name).Success))
|
(attr.Name.StartsWith("^") && Regex.Match(name, attr.Name).Success))
|
||||||
|
@ -456,7 +502,7 @@ namespace alterNERDtive.Yavapf
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
action.Invoke(name, from, to, internalID);
|
action.Invoke(name, from, to);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
@ -474,7 +520,19 @@ namespace alterNERDtive.Yavapf
|
||||||
/// <param name="internalID">The internal GUID 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)
|
private void TextVariableChanged(string name, string? from, string? to, Guid? internalID = null)
|
||||||
{
|
{
|
||||||
foreach (Action<string, string?, string?, Guid?> action in this.StringChangedHandlers.Where(
|
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(
|
||||||
action => action.Method.GetCustomAttributes<StringAttribute>().Where(
|
action => action.Method.GetCustomAttributes<StringAttribute>().Where(
|
||||||
attr => attr.Name == name ||
|
attr => attr.Name == name ||
|
||||||
(attr.Name.StartsWith("^") && Regex.Match(name, attr.Name).Success))
|
(attr.Name.StartsWith("^") && Regex.Match(name, attr.Name).Success))
|
||||||
|
@ -482,7 +540,7 @@ namespace alterNERDtive.Yavapf
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
action.Invoke(name, from, to, internalID);
|
action.Invoke(name, from, to);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
@ -545,7 +603,7 @@ namespace alterNERDtive.Yavapf
|
||||||
/// Denotes a handler for <see cref="VaStopCommand()"/>.
|
/// Denotes a handler for <see cref="VaStopCommand()"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AttributeUsage(AttributeTargets.Method)]
|
[AttributeUsage(AttributeTargets.Method)]
|
||||||
protected class StopAttribute : Attribute
|
protected class StopCommandAttribute : Attribute
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -562,7 +620,7 @@ namespace alterNERDtive.Yavapf
|
||||||
/// <param name="name">The name of or regex for the context.</param>
|
/// <param name="name">The name of or regex for the context.</param>
|
||||||
public ContextAttribute(string name)
|
public ContextAttribute(string name)
|
||||||
{
|
{
|
||||||
this.Name = name;
|
this.Name = name.ToLower();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Executing VoiceAttack Commands
|
||||||
|
|
||||||
|
Not implemented yet.
|
171
docs/contexts.md
171
docs/contexts.md
|
@ -0,0 +1,171 @@
|
||||||
|
# Defining Plugin Contexts
|
||||||
|
|
||||||
|
Plugin contexts are defined similarly to [event handlers](events.md).
|
||||||
|
|
||||||
|
They are `public static` methods of your plugin class that must have a
|
||||||
|
`ContextAttribute` and must accept a `VoiceAttackInvokeProxyClass` parameter.
|
||||||
|
|
||||||
|
`ContextAttribute` has a single property `Name`. `Name` can either be the name
|
||||||
|
of a plugin context or a regular expression defining all plugin contexts it
|
||||||
|
should be associated with. `Name` is set through an optional parameter of the
|
||||||
|
attribute constructor; if it is omitted, the method will be executed for any
|
||||||
|
plugin context.
|
||||||
|
|
||||||
|
A method can have multiple `ContextAttribute`s. It will be executed if any of
|
||||||
|
them matches the context of a plugin invocation. That also means that you can
|
||||||
|
have several methods that handle the same plugin context; as with [event
|
||||||
|
handlers](events.md), a specific order of execution cannot be guaranteed.
|
||||||
|
|
||||||
|
If your method handles multiple contexts, the context it was invoked with can be
|
||||||
|
found in the `Context` property of its `VoiceAttackInvokeProxyClass` parameter.
|
||||||
|
|
||||||
|
**Note**: The `log.*` context is reserved [for
|
||||||
|
logging](logging.md#from-a-voiceattack-command) and cannot be used for your
|
||||||
|
plugin.
|
||||||
|
|
||||||
|
## Named Plugin Contexts
|
||||||
|
|
||||||
|
For singular context names, add a `ContextAttribute` for each name. Context
|
||||||
|
names are to be lower case by convention.
|
||||||
|
|
||||||
|
This should be the default way to handle plugin contexts, and multiple contexts
|
||||||
|
handled by the same method should be alternate names for the same functionality.
|
||||||
|
Separate functionality, separate handler method(s).
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[Context("test")]
|
||||||
|
[Context("test context")]
|
||||||
|
[Context("alternate context name")]
|
||||||
|
public static void TestContext(VoiceAttackInvokeProxyClass vaProxy) {
|
||||||
|
[…]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Regular Expression Plugin Contexts
|
||||||
|
|
||||||
|
For contexts defined by regular expressions, the `Name` property must start with
|
||||||
|
a `^` to be recognized as a regular expression. Incidentally that means you have
|
||||||
|
to define your regular expression to match from the beginning of the context
|
||||||
|
string.
|
||||||
|
|
||||||
|
The main use case for regular expression contexts is grouping contexts that
|
||||||
|
logically belong together or behave in very similar ways. For example you could
|
||||||
|
have a single `^edsm\..*` context in an Elite Dangerous related plugin that
|
||||||
|
handles anything related to querying [EDSM](https://edsm.net).
|
||||||
|
|
||||||
|
As with [catchall contexts](#catchall-plugin-contexts), you should probably have
|
||||||
|
some kind of way to differentiate between contexts. For any contexts that match
|
||||||
|
the regular expression(s) but are not valid contexts for your plugin, `throw` an
|
||||||
|
`ArgumentException` with “context” as the parameter name. The exception message
|
||||||
|
can be anything, it will not be used.
|
||||||
|
|
||||||
|
Oh, and of course you can combine named and regular expression contexts. This
|
||||||
|
example features some different regular expressions and corresponding
|
||||||
|
conditionals:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[Context(@"^foo.*")]
|
||||||
|
[Context(@"^.*bar.*")]
|
||||||
|
[Context(@"^.*baz")]
|
||||||
|
[Context("some name")]
|
||||||
|
public static void RegexContext(VoiceAttackInvokeProxyClass vaProxy) {
|
||||||
|
string context = vaProxy.Context;
|
||||||
|
if (context.StartsWith("foo")) {
|
||||||
|
[…]
|
||||||
|
}
|
||||||
|
else if (context.Contains("bar")) {
|
||||||
|
[…]
|
||||||
|
}
|
||||||
|
else if (context.EndsWith("baz")) {
|
||||||
|
[…]
|
||||||
|
}
|
||||||
|
else if (context == "some name")) {
|
||||||
|
[…]
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new ArgumentException("", "context");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This example is more focused and closer to how regular expression contexts are
|
||||||
|
intended to be used in practice:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[Context(@"^edsm\..*")]
|
||||||
|
public static void EdsmContext(VoiceAttackInvokeProxyClass vaProxy) {
|
||||||
|
switch(vaProxy.Context)
|
||||||
|
{
|
||||||
|
case "edsm.findsystem":
|
||||||
|
[…]
|
||||||
|
break;
|
||||||
|
case "edsm.findcommander":
|
||||||
|
[…]
|
||||||
|
break;
|
||||||
|
case "edsm.trafficreport":
|
||||||
|
[…]
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentException("", "context");;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## “Catchall” Plugin Contexts
|
||||||
|
|
||||||
|
To have a method invoked on any plugin invocation regardless of context, add a
|
||||||
|
`ContextAttribute` and omit the `Name`.
|
||||||
|
|
||||||
|
**This is not recommended** and has similar issues to using the bare VoiceAttack
|
||||||
|
plugin API. It is mostly provided for backwards compatibility; you can easily
|
||||||
|
convert your old `VA_Invoke1(dynamic)` method to a catchall plugin context and
|
||||||
|
then modify from there.
|
||||||
|
|
||||||
|
As with [regular expression contexts](#regular-expression-plugin-contexts), you
|
||||||
|
should probably have some kind of way to differentiate between contexts. For any
|
||||||
|
contexts that are not valid contexts for your plugin, `throw` an
|
||||||
|
`ArgumentException` with “context” as the parameter name. The exception message,
|
||||||
|
again, doesn’t matter.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[Context]
|
||||||
|
public static void CatchallContext(VoiceAttackInvokeProxyClass vaProxy) {
|
||||||
|
switch (vaProxy.Context)
|
||||||
|
{
|
||||||
|
case "some context":
|
||||||
|
[…]
|
||||||
|
break;
|
||||||
|
case "some other context":
|
||||||
|
[…]
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentException("", "context");;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Context Parameters
|
||||||
|
|
||||||
|
VoiceAttack plugin contexts by design do not have any parameters. If you need
|
||||||
|
data passed from a VoiceAttack command to the plugin when a context is invoked,
|
||||||
|
you will have to set a variable in your VoiceAttack command and then retrieve
|
||||||
|
said variable from the context handler method.
|
||||||
|
|
||||||
|
In general it is recommended to provide context parameters as command scoped
|
||||||
|
variables (`~` prefix) in order not to interfere with other commands / plugin
|
||||||
|
invocations and their data.
|
||||||
|
|
||||||
|
This example accesses the `~test` text variable from plugin code:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
string? testParameter = Plugin.Get<string>("~test");
|
||||||
|
```
|
||||||
|
|
||||||
|
In case a parameter is missing that is _required_ for your context `throw` an
|
||||||
|
`ArgumentNullException` with the variable name as the parameter name:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
string testParameter = Plugin.Get<string>("~test") ?? throw new ArgumentNullException("~test");
|
||||||
|
```
|
||||||
|
|
||||||
|
[More about variables](variables.md).
|
|
@ -0,0 +1,61 @@
|
||||||
|
# Handling VoiceAttack Events
|
||||||
|
|
||||||
|
In order to handle VoiceAttack’s `Init`, `Exit` and `StopCommand` events, you
|
||||||
|
will have to define corresponding event handlers. [The `Invoke` event is handled
|
||||||
|
separately](contexts.md).
|
||||||
|
|
||||||
|
Generally speaking, event handlers in YAVAPF are `public static` methods of your
|
||||||
|
plugin class that must have certain attributes associated to them and must have
|
||||||
|
the correct method signature.
|
||||||
|
|
||||||
|
An event can have as many handlers as you require. Do note that a specific order
|
||||||
|
of execution cannot be guaranteed.
|
||||||
|
|
||||||
|
## Init
|
||||||
|
|
||||||
|
`Init` handlers are invoked when VoiceAttack inintializes your plugin. That
|
||||||
|
happens exactly once at application startup. Use these to setup your plugin for
|
||||||
|
use.
|
||||||
|
|
||||||
|
`Init` handlers must accept a single `VoiceAttackInitProxyClass` parameter and
|
||||||
|
must have an `InitAttribute`. `InitAttribute` does not have any properties.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[Init]
|
||||||
|
public static void MyInitHandler(VoiceAttackInitProxyClass vaProxy) {
|
||||||
|
[…]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Exit
|
||||||
|
|
||||||
|
`Exit` handlers are invoked when VoiceAttack closes. That happens exactly once
|
||||||
|
at application shutdown. Use these to gracefully tear down anything your plugin
|
||||||
|
has to tear down.
|
||||||
|
|
||||||
|
`Exit` handlers must accept a single `VoiceAttackProxyClass` parameter and must
|
||||||
|
have an `ExitAttribute`. `ExitAttribute` does not have any properties.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[Exit]
|
||||||
|
public static void MyExitHandler(VoiceAttackProxyClass vaProxy) {
|
||||||
|
[…]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## StopCommand
|
||||||
|
|
||||||
|
`StopCommand` handlers are invoked whenever VoiceAttack stops all commands. That
|
||||||
|
happens e.g. when a “Stop all commands” command action is executed or when the
|
||||||
|
“Stop Commands” button on the main interface is pressed. If your plugin has to
|
||||||
|
respond to that, use these.
|
||||||
|
|
||||||
|
`StopCommand` handlers must have no parameters and must have a
|
||||||
|
`StopCommandAttribute`. `StopCommandAttribute` does not have any properties.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[StopCommand]
|
||||||
|
public static void MyStopCommandHandler() {
|
||||||
|
[…]
|
||||||
|
}
|
||||||
|
```
|
5
docs/extra.css
Normal file
5
docs/extra.css
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
div.section > p,
|
||||||
|
div.section > ol,
|
||||||
|
div.section > ul {
|
||||||
|
text-align: justify
|
||||||
|
}
|
3
docs/faq.md
Normal file
3
docs/faq.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Frequently Asked Questions
|
||||||
|
|
||||||
|
There doesn’t seem to be anything here. Maybe you should ask one :)
|
|
@ -0,0 +1,193 @@
|
||||||
|
# Getting Started
|
||||||
|
|
||||||
|
First off, you can see [the Example plugin
|
||||||
|
project](https://github.com/alterNERDtive/YAVAPF/tree/release/ExamplePlugin) on
|
||||||
|
Github for reference.
|
||||||
|
|
||||||
|
Second off, this documentation assumes that you have at least cross read the
|
||||||
|
section about plugins [in the VoiceAttack
|
||||||
|
manual](https://voiceattack.com/VoiceAttackHelp.pdf). If any terminology is new
|
||||||
|
to you, it is probably introduced there. Unlike said manual though this will
|
||||||
|
provide step by step instructions to get your plugin set up.
|
||||||
|
|
||||||
|
## Creating a Visual Studio Project
|
||||||
|
|
||||||
|
I am going to assume for this part of the documentation that you are using
|
||||||
|
Visual Studio 2022 or later (_not_ Visual Studio Code!) as your development
|
||||||
|
environment. [The Community Edition is free for unlimited time personal
|
||||||
|
use](https://visualstudio.microsoft.com/vs/compare/).
|
||||||
|
|
||||||
|
VoiceAttack is a .Net Framework 4.8 application. Plugins targeting .Net 5+ or
|
||||||
|
.Net Core will not work. I still recommend creating a .Net project instead of a
|
||||||
|
.Net Framework project, then changing the “Target Framework” to .Net Framework
|
||||||
|
4.8. This allows you to use the full `dotnet` tool chain, which makes e.g. using
|
||||||
|
Github Actions to build / release your project much less painful. Trust me, I’ve
|
||||||
|
done it both ways.
|
||||||
|
|
||||||
|
So, create a new “Class Library” project, then use a text editor to change the
|
||||||
|
“TargetFrameworks” property to `net48`. While you’re there you might also want
|
||||||
|
to change the “LanguageVerison” to `10`. Most new features are backwards
|
||||||
|
compatible with .Net Framework. The compiler will assist you with errors for
|
||||||
|
those that are not.
|
||||||
|
|
||||||
|
## Adding YAVAPF as a Dependency
|
||||||
|
|
||||||
|
This one is the simple part, just install `alterNERDtive.YAVAPF` through NuGet.
|
||||||
|
Done.
|
||||||
|
|
||||||
|
Alternatively you can add it manually by cloning
|
||||||
|
`github.com/alterNERDtive/YAVAPF.git` as a git submodule and referencing
|
||||||
|
`VoiceAttack-Framework\VoiceAttack-Framework.csproj` as a project reference.
|
||||||
|
|
||||||
|
But seriously, use NuGet. I haven’t taught myself how to release NuGet packages
|
||||||
|
just for you to ignore it!
|
||||||
|
|
||||||
|
## Adding VoiceAttack as a Dependency
|
||||||
|
|
||||||
|
This is a little more involved. In order to use the actual proxy classes from
|
||||||
|
VoiceAttack instead of the “official” crutch of `dynamic` types, you will need
|
||||||
|
to add an assembly reference to `VoiceAttack.exe`.
|
||||||
|
|
||||||
|
Right click → “Add” → “Assembly Reference…” → “Browse” → browse to the
|
||||||
|
VoiceAttack installation folder → select `VoiceAttack.exe` → hit “Add” → make
|
||||||
|
sure it is ticked in the list → hit “OK”.
|
||||||
|
|
||||||
|
Now, we want to _reference_ `VoiceAttack.exe`, but we don’t want to _include_ it
|
||||||
|
when compiling the plugin. So select “VoiceAttack” in “Dependencies” →
|
||||||
|
“Assemblies” and make sure that both “Copy Local” and “Embed Interop Types” are
|
||||||
|
set to “No”.
|
||||||
|
|
||||||
|
Distributing `VoiceAttack.exe` with your plugin would technically be a copyright
|
||||||
|
violation. _Do_ make sure to take the steps outlined in the last paragraph to
|
||||||
|
prevent accidentally doing that! Using it as a reference assembly is generally
|
||||||
|
OK and I have received confirmation from Gary, the author of VoiceAttack.
|
||||||
|
|
||||||
|
## Setting Up Debugging Through VoiceAttack
|
||||||
|
|
||||||
|
In order to be able to run VoiceAttack when debugging and actually debug your
|
||||||
|
plugin, you will need to open “Debug” → “<your project\> Debug Properties”.
|
||||||
|
|
||||||
|
There you will need to “Create a new profile” → “Executable”. Set the path to
|
||||||
|
your VoiceAttack executable and any command line options you might prefer.
|
||||||
|
Personally I like to set a custom `-datadir` in order to not mess with my
|
||||||
|
regular profile database accidentally.
|
||||||
|
|
||||||
|
The example plugin project has a `Properties\launchSettings.sample.json` file
|
||||||
|
that you can copy to `Properties\launchSettings.json` and edit accordingly to
|
||||||
|
accomplish the same thing.
|
||||||
|
|
||||||
|
The last thing you’ll need to do is make your plugin available to VoiceAttack
|
||||||
|
in a place where it can find it. I have requested an equivalent `-appdir`
|
||||||
|
parameter, but as long as that is not available you will need to have your
|
||||||
|
plugin present inside the regular `Apps` folder of VoiceAttack. I recommend
|
||||||
|
creating a directory junction (`mklink /j`, or `New-Item -ItemType Junction` in
|
||||||
|
PowerShell) between an `Apps` subfolder and your project’s debug output
|
||||||
|
directory (usually `<project name>\bin\Debug\net48` inside your solution
|
||||||
|
folder).
|
||||||
|
|
||||||
|
## Building Through Github Actions
|
||||||
|
|
||||||
|
If you, like me, want to automate building/testing/releasing through [Github
|
||||||
|
Actions](https://docs.github.com/en/actions), you’ll need to have VoiceAttack
|
||||||
|
available while building on the worker. Obviously that will only work on a
|
||||||
|
Windows worker.
|
||||||
|
|
||||||
|
I have created the
|
||||||
|
[`alterNERDtive/setup-voiceattack-action`](https://github.com/alterNERDtive/setup-voiceattack-action)
|
||||||
|
to facilitate that. Usage example:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: Install VoiceAttack
|
||||||
|
uses: alterNERDtive/setup-voiceattack-action
|
||||||
|
with:
|
||||||
|
version: "1.10"
|
||||||
|
```
|
||||||
|
|
||||||
|
Make sure that the path to VoiceAttack on your machine (which is the path
|
||||||
|
referenced in the project file) matches the path where you install VoiceAttack
|
||||||
|
on the worker! Alternatively, if you have installed VoiceAttack in a custom
|
||||||
|
folder locally, you can create a symlink (`mklink`, or
|
||||||
|
`New-Item -ItemType SymbolicLink` in PowerShell) to your `VoiceAttack.exe`
|
||||||
|
location at `C:\Program Files\VoiceAttack\VoiceAttack.exe` and include that as
|
||||||
|
the assembly reference.
|
||||||
|
|
||||||
|
## Creating a Minimum Viable Plugin
|
||||||
|
|
||||||
|
A valid VoiceAttack plugin must implement a selection of public, static methods:
|
||||||
|
|
||||||
|
* `VA_DisplayName()`: Must return the name of the plugin.
|
||||||
|
* `VA_DisplayInfo()`: Must return the description of the plugin.
|
||||||
|
* `VA_Id()`: Must return the GUID of the plugin.
|
||||||
|
* `VA_Init1(dynamic)`: Is executed when the plugin is loaded into VoiceAttack.
|
||||||
|
* `VA_Invoke1(dynamic)`: Is executed whenever a plugin context is run from a
|
||||||
|
command.
|
||||||
|
* `VA_Exit1(dynamic)`: Is executed when VoiceAttack shuts down.
|
||||||
|
* `VA_StopCommand()`: Is executed when VoiceAttack stops all commands, e.g.
|
||||||
|
through the command action or main interface button.
|
||||||
|
|
||||||
|
When using YAVAPF these methods are to be passed straight to the corresponding
|
||||||
|
methods of a `VoiceAttackPlugin` object that handles most things for you. It has
|
||||||
|
a few required properties:
|
||||||
|
|
||||||
|
* `Name`: The name of the plugin.
|
||||||
|
* `Version`: The version of the plugin.
|
||||||
|
* `Info`: The description of the plugin.
|
||||||
|
* `Guid`: The GUID of the plugin.
|
||||||
|
|
||||||
|
All of those are `string`s for ease of use, though the `Guid` obviously has to
|
||||||
|
be a valid string representation of a GUID. You can generate one using “Tools” →
|
||||||
|
“Create GUID”. Make sure to select “Registry Format”.
|
||||||
|
|
||||||
|
For a YAVAPF plugin you will have to derive your plugin class from
|
||||||
|
`alterNERDtive.Yavapf.VoiceAttackPlugin`. Since VoiceAttack’s plugin API relies
|
||||||
|
entirely on static methods, you’ll need to instantiate your plugin object in its
|
||||||
|
static constructor and hold it in a static variable for future reference (no pun
|
||||||
|
intended).
|
||||||
|
|
||||||
|
So a minimum viable plugin using YAVAPF looks kind of like this:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using System;
|
||||||
|
|
||||||
|
using alterNERDtive.Yavapf;
|
||||||
|
|
||||||
|
namespace YourNamespace
|
||||||
|
{
|
||||||
|
public class YourPlugin : VoiceAttackPlugin
|
||||||
|
{
|
||||||
|
private static readonly YourPlugin Plugin;
|
||||||
|
|
||||||
|
static YourPlugin()
|
||||||
|
{
|
||||||
|
Plugin = new ()
|
||||||
|
{
|
||||||
|
Name = "Your Plugin",
|
||||||
|
Version = "0.0.1",
|
||||||
|
Info = "This is a description",
|
||||||
|
Guid = "{5E93F293-B2CB-4B3F-AFC5-AE500A7EEBA9}",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string VA_DisplayName() => Plugin.VaDisplayName();
|
||||||
|
|
||||||
|
public static string VA_DisplayInfo() => Plugin.VaDisplayInfo();
|
||||||
|
|
||||||
|
public static Guid VA_Id() => Plugin.VaId();
|
||||||
|
|
||||||
|
public static void VA_Init1(dynamic vaProxy) => Plugin.VaInit1(vaProxy);
|
||||||
|
|
||||||
|
public static void VA_Invoke1(dynamic vaProxy) => Plugin.VaInvoke1(vaProxy);
|
||||||
|
|
||||||
|
public static void VA_Exit1(dynamic vaProxy) => Plugin.VaExit1(vaProxy);
|
||||||
|
|
||||||
|
public static void VA_StopCommand() => Plugin.VaStopCommand();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
That’s it! Technically you’re done. Hit the debug button, and VoiceAttack should
|
||||||
|
find your plugin on startup, report loading it in the event log, and list it
|
||||||
|
under “Options” → “General” → “Plugin Manager”.
|
||||||
|
|
||||||
|
Of course you are only just getting started if you want your plugin to actually
|
||||||
|
_do_ something!
|
|
@ -13,13 +13,23 @@ The goal is to get you up & running with as little code and as little knowledge
|
||||||
the inner workings of VoiceAttack as possible.
|
the inner workings of VoiceAttack as possible.
|
||||||
|
|
||||||
You can find an [example plugin on
|
You can find an [example plugin on
|
||||||
Github](https://github.com/alterNERDtive/YAVAPF/tree/develop/ExamplePlugin).
|
Github](https://github.com/alterNERDtive/YAVAPF/tree/release/ExamplePlugin).
|
||||||
|
|
||||||
|
## Current Implementation Status
|
||||||
|
|
||||||
|
* [x] VoiceAttack plugin API
|
||||||
|
* [x] Handlers for Init/Invoke/Exit/StopCommand
|
||||||
|
* [x] Plugin contexts
|
||||||
|
* [x] Handlers for variable changed events
|
||||||
|
* [x] Logging to the VoiceAttack event log
|
||||||
|
* [ ] Logging to a log file
|
||||||
|
* [ ] Wrapper for executing commands
|
||||||
|
* [ ] Miscellaneous VoiceAttack proxy functionality
|
||||||
|
* [ ] Full unit test coverage 😬
|
||||||
|
|
||||||
## Need Help / Want to Contribute?
|
## Need Help / Want to Contribute?
|
||||||
|
|
||||||
Have a look at [the troubleshooting
|
Have a look at [the FAQ](faq.md). If your problem persists, please [file an
|
||||||
guide](https://alterNERDtive.github.io/YAVAPF/troubleshooting). If your problem
|
|
||||||
persists, please [file an
|
|
||||||
issue](https://github.com/alterNERDtive/YAVAPF/issues/new). Thanks! :)
|
issue](https://github.com/alterNERDtive/YAVAPF/issues/new). Thanks! :)
|
||||||
|
|
||||||
You can also [say “Hi” on Discord](https://discord.gg/3pWdJwfJc5) if that is
|
You can also [say “Hi” on Discord](https://discord.gg/3pWdJwfJc5) if that is
|
||||||
|
|
73
docs/logging.md
Normal file
73
docs/logging.md
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
# Logging
|
||||||
|
|
||||||
|
YAVAPF allows logging to the VoiceAttack event log.
|
||||||
|
|
||||||
|
Logging to a log file is planned, but not implemented yet.
|
||||||
|
|
||||||
|
## Write a Log Line
|
||||||
|
|
||||||
|
To write a log message from plugin code, use the methods provided by the
|
||||||
|
`VoiceAttackLog` object availabe in the `Log` property of your plugin object.
|
||||||
|
There is one per log level.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
Plugin.Log.Error("Example error message.");
|
||||||
|
Plugin.Log.Debug("Just sent an error message.");
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also log messages from a VoiceAttack command. Unlike a regular “Write to
|
||||||
|
Log” command action going through the plugin will enforce the correct format and
|
||||||
|
log level.
|
||||||
|
|
||||||
|
Your plugin will automatically provide the reserved plugin contexts
|
||||||
|
`log.<log level>` for each of the 5 log levels. Simply set a `~message` string
|
||||||
|
and call the appropriate plugin context. This should be equivalent to the code
|
||||||
|
above:
|
||||||
|
|
||||||
|
```
|
||||||
|
Set text [~message] to 'Example error message.'
|
||||||
|
Execute external plugin, 'Your Plugin v0.0.1' using context 'log.error' and wait for return
|
||||||
|
Set text [~message] to 'Just sent an error message.'
|
||||||
|
Execute external plugin, 'Your Plugin v0.0.1' using context 'log.debug' and wait for return
|
||||||
|
```
|
||||||
|
|
||||||
|
## Log Level
|
||||||
|
|
||||||
|
A message will be colour coded with the corresponding colour. If the log level
|
||||||
|
assigned to a message is below the current log level, it will not be displayed.
|
||||||
|
|
||||||
|
E.g. an `INFO` message will not be displayed by default since the default log
|
||||||
|
level is `NOTICE`. A `DEBUG` message will only ever be displayed if the current
|
||||||
|
log level is `DEBUG`.
|
||||||
|
|
||||||
|
| Log Level | Log Colour | Recommended Use
|
||||||
|
|-----------|---------------|----------------------------
|
||||||
|
| ERROR | 🟥 red | unrecoverable error
|
||||||
|
| WARN | 🟨 yellow | recoverable error, warning
|
||||||
|
| NOTICE | 🟩 green | noteworthy information
|
||||||
|
| INFO | 🟦 blue | miscellaneous information
|
||||||
|
| DEBUG | ⬜ gray | debugging
|
||||||
|
|
||||||
|
## Setting the Current Log Level
|
||||||
|
|
||||||
|
You can set the current log level by either setting the `LogLevel` property or
|
||||||
|
by invoking `SetLogLevel(string)` of your plugin’s `Log` property.
|
||||||
|
|
||||||
|
The latter is mostly useful when dealing with input from a VoiceAttack command.
|
||||||
|
Its parameter is not case sensitive.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
Plugin.Log.LogLevel = LogLevel.WARN;
|
||||||
|
Plugin.Log.SetLogLevel = "info";
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also set the log level from a VoiceAttack command directly by simply
|
||||||
|
setting `<plugin name>.loglevel#` to the desired log level.
|
||||||
|
|
||||||
|
```
|
||||||
|
Set text [Your Plugin.loglevel#] to 'debug'
|
||||||
|
```
|
||||||
|
|
||||||
|
The variable changed event for this specific variable name will be handled by
|
||||||
|
YAVAPF internally. You can of course still define your own handlers in addition
|
||||||
|
to it.
|
|
@ -0,0 +1,157 @@
|
||||||
|
# Using VoiceAttack Variables
|
||||||
|
|
||||||
|
VoiceAttack allows you to set a plethora of variables. Each variable has
|
||||||
|
|
||||||
|
* a name
|
||||||
|
* a type
|
||||||
|
* a scope
|
||||||
|
|
||||||
|
Variable names are unique to each variable type. E.g. you can have _both_ a text
|
||||||
|
variable `test` _and_ a boolean variable `test`.
|
||||||
|
|
||||||
|
In addition to that variable names are also unique to each _scope_; or,
|
||||||
|
technically speaking, the scope is part of the variable name. E.g. a text
|
||||||
|
variable `~test` is different from a text variable `>test`, and both are
|
||||||
|
different from a text variable `test`.
|
||||||
|
|
||||||
|
## Variable Types
|
||||||
|
|
||||||
|
| VoiceAttack type | .Net type | Note
|
||||||
|
|-----------------------|-----------|-------------------------------------
|
||||||
|
| Text | string |
|
||||||
|
| True/False (Boolean) | bool |
|
||||||
|
| Integer | int |
|
||||||
|
| Decimal | decimal |
|
||||||
|
| Date/Time | DateTime |
|
||||||
|
| Small Integer | short | deprecated; not supported by YAVAPF
|
||||||
|
|
||||||
|
Technical note: VoiceAttack internally holds a `Dictionary<string, T>` for each
|
||||||
|
variable type. That is why, unlike regular variables in a .Net scope, the names
|
||||||
|
are unique to their type.
|
||||||
|
|
||||||
|
## Variable Scopes
|
||||||
|
|
||||||
|
| Prefix | Scope | Accessibility
|
||||||
|
|-----------|-----------------------|-------------------------------------------------
|
||||||
|
| none | global | everywhere
|
||||||
|
| `>>` | profile, persistent | same profile, preserved across profile switches
|
||||||
|
| `>` | profile | same profile, reset on profile switch
|
||||||
|
| `~~` | command + subcommands | this command invocation and its subcommands
|
||||||
|
| `~` | command only | this command invocation
|
||||||
|
|
||||||
|
There is no scope that retains variable values across VoiceAttack restarts. You
|
||||||
|
can save variable values to / load them from the current profile using a “Set a
|
||||||
|
<Type\> Value” command action and ticking the corresponding box, but there is
|
||||||
|
currently no way to do it from a plugin. Feature request pending.
|
||||||
|
|
||||||
|
Restricting variable scope as far as possible is recommended. For communication
|
||||||
|
between commands and their plugin invocations scope should almost always be
|
||||||
|
command only (`~`).
|
||||||
|
|
||||||
|
For global commands used by your plugin having some unique prefix is a sensible
|
||||||
|
idea. For example, YAVAPF automatically sets the text variable
|
||||||
|
`<plugin name>.version` to the current version of your plugin.
|
||||||
|
|
||||||
|
## Default Variables
|
||||||
|
|
||||||
|
By default, YAVAPF automatically sets the following variables for your plugin:
|
||||||
|
|
||||||
|
| Variable | Type | Description
|
||||||
|
|-------------------------------|-----------|--------------------------------------------
|
||||||
|
| `<plugin name>.version` | string | The current version of your plugin.
|
||||||
|
| `<plugin name>.initialized` | bool | The plugin has been initialized correctly.
|
||||||
|
|
||||||
|
## Getting Variable Values
|
||||||
|
|
||||||
|
To get the value of a variable, invoke the `Get<T>(string name)` method of your
|
||||||
|
plugin where `T` is the type of the variable and `name` is its name including
|
||||||
|
its scope:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
string? foo = Plugin.Get<string>("foo");
|
||||||
|
bool bar = Plugin.Get<bool>("~bar") ?? false;
|
||||||
|
```
|
||||||
|
|
||||||
|
Remember that variable values will be returned as `null` (“Not Set” in
|
||||||
|
VoiceAttack terminology) if they are currently not holding a value.
|
||||||
|
|
||||||
|
## Setting Variable Values
|
||||||
|
|
||||||
|
To set the value of a variable, invoke the `Set<T>(string name, T value)` method
|
||||||
|
of your plugin where `T` is the type of the variable, `name` is its name
|
||||||
|
including its scope and `value` is the desired new value:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
Plugin.Set<DateTime>("current", DateTime.Now);
|
||||||
|
Plugin.Set<int>(">>deaths", (Plugin.Get<int>(">>deaths") ?? 0) + 1);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Clearing Variable Values
|
||||||
|
|
||||||
|
To clear a variable, invoke the `UnSet<T>(string name)` method of your plugin
|
||||||
|
where `T` is the type of the variable and `name` is its name including its
|
||||||
|
scope:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
Plugin.UnSet<decimal>("π");
|
||||||
|
```
|
||||||
|
|
||||||
|
Or `Set<T>(string, T)` it to `null`:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
Plugin.Set<string>(">fizzbang", null);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Subscribing to “Variable Changed” Events
|
||||||
|
|
||||||
|
VoiceAttack allows triggering plugins when a variable value changes. Handlers
|
||||||
|
must have an Attribute corresponding to the variable type (`BoolAttribute`,
|
||||||
|
`DateTimeAttribute`, `DecimalAttribute`, `IntAttribute`, `StringAttribute`) and
|
||||||
|
accept the following parameters:
|
||||||
|
|
||||||
|
* `string name`: the name of the variable that has changed
|
||||||
|
* `T? from`: the old value of the variable
|
||||||
|
* `T? to`: the new value of the variable
|
||||||
|
|
||||||
|
where `T` is the type of the variable. Remember that at any point either `to`
|
||||||
|
or `from` might be `null`.
|
||||||
|
|
||||||
|
**Note**: In order for a variable to trigger variable changed events, the
|
||||||
|
variable name **must** end with a number sign (`#`)! This is a limitation
|
||||||
|
enforced by VoiceAttack. The purpose of this constraint is to not constantly
|
||||||
|
invoke plugins whenever any variable changes.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[Bool("isDay#")]
|
||||||
|
public static void DayChanged(string name, bool? from, bool? to)
|
||||||
|
{
|
||||||
|
Plugin.Log.Notice($"It is now {(to ?? false ? "day" : "night")}.");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This constraint also applies to catchall handlers. Even though the following
|
||||||
|
method accepts any variable name, it will still only be invoked if the name of a
|
||||||
|
changed variable ends with a `#`. E.g. changing the text variable `foo#` will
|
||||||
|
invoke it while changing the text variable `foo` will not.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[String]
|
||||||
|
public static void StringChanged(string name, string? from, string? to)
|
||||||
|
{
|
||||||
|
Plugin.Log.Notice($"Text variable '{name}' changed from '{from ?? "Not Set"}' to '{to ?? "Not Set"}'.");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
[More on logging](logging.md).
|
||||||
|
|
||||||
|
Do be aware that changing the value of a variable from within its handler will
|
||||||
|
trigger another variable changed event and run the handler again. It is very
|
||||||
|
much possible to create an infinite loop.
|
||||||
|
|
||||||
|
Attribute names work in the same way as [context names](contexts.md). You can
|
||||||
|
have handlers for singular variable names, regular expressions, and catchall
|
||||||
|
handlers.
|
||||||
|
|
||||||
|
The text variable `<plugin name>.loglevel#` is handled by YAVAPF internally to
|
||||||
|
set the current log level. You can still add additional handlers for this
|
||||||
|
variable.
|
19
mkdocs.yml
19
mkdocs.yml
|
@ -1,4 +1,4 @@
|
||||||
site_name: "bindED VoiceAttack plugin"
|
site_name: "YAVAPF – Yet Another VoiceAttack Plugin Framework"
|
||||||
site_url: https://alterNERDtive.github.io/YAVAPF
|
site_url: https://alterNERDtive.github.io/YAVAPF
|
||||||
repo_url: https://github.com/alterNERDtive/YAVAPF
|
repo_url: https://github.com/alterNERDtive/YAVAPF
|
||||||
edit_uri: "edit/devel/docs/"
|
edit_uri: "edit/devel/docs/"
|
||||||
|
@ -6,6 +6,8 @@ site_description: "YAVAPF is yet another VoiceAttack plugin framework."
|
||||||
site_author: "alterNERDtive"
|
site_author: "alterNERDtive"
|
||||||
remote_name: "origin"
|
remote_name: "origin"
|
||||||
|
|
||||||
|
extra_css: [extra.css]
|
||||||
|
|
||||||
theme:
|
theme:
|
||||||
name: readthedocs
|
name: readthedocs
|
||||||
prev_next_buttons_location: both
|
prev_next_buttons_location: both
|
||||||
|
@ -18,15 +20,18 @@ markdown_extensions:
|
||||||
- toc:
|
- toc:
|
||||||
permalink: True
|
permalink: True
|
||||||
- sane_lists
|
- sane_lists
|
||||||
|
- pymdownx.tasklist
|
||||||
|
|
||||||
nav:
|
nav:
|
||||||
- 'Home': 'index.md'
|
- 'Home': 'index.md'
|
||||||
- 'Usage':
|
- 'Usage':
|
||||||
- 'Getting Started': 'gettingstarted.md'
|
- 'gettingstarted.md'
|
||||||
- 'Event Handlers': 'events.md'
|
- 'events.md'
|
||||||
- 'Plugin Contexts': 'contexts.md'
|
- 'contexts.md'
|
||||||
- 'Working with Variables': 'variables.md'
|
- 'variables.md'
|
||||||
- 'Running Commands': 'commands.md'
|
- 'commands.md'
|
||||||
- 'troubleshooting.md'
|
- 'logging.md'
|
||||||
|
- Troubleshooting:
|
||||||
|
- 'faq.md'
|
||||||
- '⎋ Changelog': 'https://github.com/alterNERDtive/YAVAPF/blob/release/CHANGELOG.md'
|
- '⎋ Changelog': 'https://github.com/alterNERDtive/YAVAPF/blob/release/CHANGELOG.md'
|
||||||
- '⎋ Report a Bug': 'https://github.com/alterNERDtive/YAVAPF/issues/'
|
- '⎋ Report a Bug': 'https://github.com/alterNERDtive/YAVAPF/issues/'
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
mkdocs-roamlinks-plugin
|
mkdocs-roamlinks-plugin
|
||||||
|
pymdown-extensions
|
||||||
|
|
Loading…
Reference in a new issue