added command support
This commit is contained in:
parent
6277efd759
commit
d38e2718bb
8 changed files with 320 additions and 3 deletions
|
@ -141,6 +141,7 @@ namespace alterNERDtive.Example
|
|||
/// </summary>
|
||||
/// <param name="vaProxy">The current VoiceAttack proxy object.</param>
|
||||
[Init]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Required by plugin API")]
|
||||
public static void Init(VoiceAttackInitProxyClass vaProxy)
|
||||
{
|
||||
Plugin.Log.Notice("This is the example Init handler method.");
|
||||
|
@ -153,6 +154,7 @@ namespace alterNERDtive.Example
|
|||
/// </summary>
|
||||
/// <param name="vaProxy">The current VoiceAttack proxy object.</param>
|
||||
[Exit]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Required by plugin API")]
|
||||
public static void Exit(VoiceAttackProxyClass vaProxy)
|
||||
{
|
||||
Plugin.Log.Notice("This is the example Exit handler method.");
|
||||
|
@ -204,6 +206,28 @@ namespace alterNERDtive.Example
|
|||
$"This is the example handler for the plugin contexts “^foo.*” and “^.*bar.*”. It has been invoked with '{vaProxy.Context}'.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An example handler for a context that runs additional VoiceAttack
|
||||
/// commands.
|
||||
/// </summary>
|
||||
/// <param name="vaProxy">The <see cref="VoiceAttackInvokeProxyClass"/>
|
||||
/// proxy object.</param>
|
||||
[Context("runcommandtest")]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Required by plugin API")]
|
||||
public static void RunCommandTestContext(VoiceAttackInvokeProxyClass vaProxy)
|
||||
{
|
||||
// “Just” runs a command with no further options
|
||||
Plugin.Commands.Run("test command");
|
||||
|
||||
// Runs a command as a subcommand to the current command, and blocks
|
||||
// until command execution finishes
|
||||
Plugin.Commands.Run("test command", wait: true, subcommand: true);
|
||||
|
||||
Plugin.Commands.Run(
|
||||
"command with parameters",
|
||||
parameters: new dynamic[] { new string[] { "foo", "bar" }, new DateTime[] { DateTime.Now } });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An example handler for changed <see cref="bool"/> variables. It only
|
||||
/// applies to the “isDay#” variable.
|
||||
|
@ -212,6 +236,7 @@ namespace alterNERDtive.Example
|
|||
/// <param name="from">The old value of the variable.</param>
|
||||
/// <param name="to">The new value of the variable.</param>
|
||||
[Bool("isDay#")]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Required by plugin API")]
|
||||
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")}.");
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
|
|
|
@ -45,8 +45,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{3FE46F37-0
|
|||
docs\contexts.md = docs\contexts.md
|
||||
docs\events.md = docs\events.md
|
||||
docs\extra.css = docs\extra.css
|
||||
docs\gettingstarted.md = docs\gettingstarted.md
|
||||
docs\faq.md = docs\faq.md
|
||||
docs\gettingstarted.md = docs\gettingstarted.md
|
||||
docs\index.md = docs\index.md
|
||||
docs\logging.md = docs\logging.md
|
||||
docs\variables.md = docs\variables.md
|
||||
|
|
|
@ -24,3 +24,4 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
[assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "because that’s my name", Scope = "namespace", Target = "~N:alterNERDtive.Yavapf")]
|
||||
[assembly: SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1011:Closing square brackets should be spaced correctly", Justification = "have to do that to make arrays nullable, fam", Scope = "type", Target = "~T:alterNERDtive.Yavapf.VoiceAttackCommands")]
|
||||
|
|
187
VoiceAttack-Framework/VoiceAttackCommands.cs
Normal file
187
VoiceAttack-Framework/VoiceAttackCommands.cs
Normal file
|
@ -0,0 +1,187 @@
|
|||
// <copyright file="VoiceAttackCommands.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 VoiceAttack;
|
||||
|
||||
namespace alterNERDtive.Yavapf
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides an interface to run VoiceAttack commands from a plugin.
|
||||
/// </summary>
|
||||
public class VoiceAttackCommands
|
||||
{
|
||||
private readonly VoiceAttackInitProxyClass vaProxy;
|
||||
private readonly VoiceAttackLog log;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="VoiceAttackCommands"/>
|
||||
/// class.
|
||||
/// </summary>
|
||||
/// <param name="vaProxy">The <see cref="VoiceAttackInitProxyClass"/>
|
||||
/// proxy object.</param>
|
||||
/// <param name="log">The <see cref="VoiceAttackLog"/> object.</param>
|
||||
internal VoiceAttackCommands(VoiceAttackInitProxyClass vaProxy, VoiceAttackLog log) => (this.vaProxy, this.log) = (vaProxy, log);
|
||||
|
||||
/// <summary>
|
||||
/// Runs a VoiceAttack command.
|
||||
/// </summary>
|
||||
/// <param name="command">The name of the command.</param>
|
||||
/// <param name="logMissing">Whether or not to log a message if the
|
||||
/// command in question does not exist in the current profile.</param>
|
||||
/// <param name="wait">Whether to wait for the command to finish
|
||||
/// executing before returning.</param>
|
||||
/// <param name="subcommand">Whether the called command should be run as
|
||||
/// a subcommand to the current command context.</param>
|
||||
/// <param name="parameters">The parameters for the command. Has to be
|
||||
/// an array of arrays, with the inner arrays being of a valid
|
||||
/// Voiceattack variable type.</param>
|
||||
/// <exception cref="ArgumentNullException">Thrown if the name of the
|
||||
/// command is missing.</exception>
|
||||
public void Run(string command, bool logMissing = true, bool wait = false, bool subcommand = false, dynamic[]? parameters = null)
|
||||
{
|
||||
_ = command ?? throw new ArgumentNullException("command");
|
||||
|
||||
if (this.vaProxy.Command.Exists(command))
|
||||
{
|
||||
this.log.Debug($"Parsing arguments for command '{command}' …");
|
||||
|
||||
string[]? strings = null;
|
||||
int[]? integers = null;
|
||||
decimal[]? decimals = null;
|
||||
bool[]? booleans = null;
|
||||
DateTime[]? dates = null; // this might not work!
|
||||
|
||||
foreach (var values in parameters ?? Enumerable.Empty<object>())
|
||||
{
|
||||
switch (values)
|
||||
{
|
||||
case bool[] b:
|
||||
booleans = b;
|
||||
break;
|
||||
case DateTime[] d:
|
||||
dates = d;
|
||||
break;
|
||||
case decimal[] d:
|
||||
decimals = d;
|
||||
break;
|
||||
case int[] i:
|
||||
integers = i;
|
||||
break;
|
||||
case string[] s:
|
||||
strings = s;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.log.Debug($"Running command '{command}' …");
|
||||
|
||||
this.vaProxy.Command.Execute(
|
||||
CommandPhrase: command,
|
||||
WaitForReturn: wait,
|
||||
AsSubcommand: subcommand,
|
||||
CompletedAction: null,
|
||||
PassedText: strings == null ? null : $"\"{string.Join<string>("\";\"", strings)}\"",
|
||||
PassedIntegers: integers == null ? null : string.Join<int>(";", integers),
|
||||
PassedDecimals: decimals == null ? null : string.Join<decimal>(";", decimals),
|
||||
PassedBooleans: booleans == null ? null : string.Join<bool>(";", booleans),
|
||||
PassedDates: dates == null ? null : string.Join<DateTime>(";", dates));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (logMissing)
|
||||
{
|
||||
this.log.Warn($"Tried running missing command '{command}'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs a VoiceAttack command with a given list of prefixes. Will run
|
||||
/// `prefix.command` for each prefix.
|
||||
/// </summary>
|
||||
/// <param name="prefixes">The list of prefixes.</param>
|
||||
/// <param name="command">The name of the command.</param>
|
||||
/// <param name="logMissing">Whether or not to log a message if the
|
||||
/// command in question does not exist in the current profile.</param>
|
||||
/// <param name="wait">Whether to wait for the command to finish
|
||||
/// executing before returning.</param>
|
||||
/// <param name="subcommand">Whether the called command should be run as
|
||||
/// a subcommand to the current command context.</param>
|
||||
/// <param name="parameters">The parameters for the command.</param>
|
||||
/// <exception cref="ArgumentNullException">Thrown if the name of the
|
||||
/// command is missing.</exception>
|
||||
public void RunAll(IEnumerable<string> prefixes, string command, bool logMissing = true, bool wait = false, bool subcommand = false, dynamic[][]? parameters = null)
|
||||
{
|
||||
foreach (string prefix in prefixes)
|
||||
{
|
||||
this.Run($"{prefix}.{command}", logMissing, wait, subcommand, parameters);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs a VoiceAttack event command. Event commands are enclosed in
|
||||
/// double paratheses by convention, they will be added automatically.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the event.</param>
|
||||
/// <param name="logMissing">Whether or not to log a message if the
|
||||
/// command in question does not exist in the current profile.</param>
|
||||
/// <param name="wait">Whether to wait for the command to finish
|
||||
/// executing before returning.</param>
|
||||
/// <param name="subcommand">Whether the called command should be run as
|
||||
/// a subcommand to the current command context.</param>
|
||||
/// <param name="parameters">The parameters for the command. Has to be
|
||||
/// an array of arrays, with the inner arrays being of a valid
|
||||
/// Voiceattack variable type.</param>
|
||||
/// <exception cref="ArgumentNullException">Thrown if the name of the
|
||||
/// command is missing.</exception>
|
||||
public void TriggerEvent(string name, bool logMissing = true, bool wait = false, bool subcommand = false, dynamic[]? parameters = null)
|
||||
{
|
||||
this.Run($"(({name}))", logMissing, wait, subcommand, parameters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs a VoiceAttack event command with a given list of prefixes.
|
||||
/// Event commands are enclosed in double paratheses by convention, they
|
||||
/// will be added automatically. Will run `((prefix.name))` for each
|
||||
/// prefix.
|
||||
/// </summary>
|
||||
/// <param name="prefixes">The list of prefixes.</param>
|
||||
/// <param name="name">The name of the event.</param>
|
||||
/// <param name="logMissing">Whether or not to log a message if the
|
||||
/// command in question does not exist in the current profile.</param>
|
||||
/// <param name="wait">Whether to wait for the command to finish
|
||||
/// executing before returning.</param>
|
||||
/// <param name="subcommand">Whether the called command should be run as
|
||||
/// a subcommand to the current command context.</param>
|
||||
/// <param name="parameters">The parameters for the command. Has to be
|
||||
/// an array of arrays, with the inner arrays being of a valid
|
||||
/// Voiceattack variable type.</param>
|
||||
/// <exception cref="ArgumentNullException">Thrown if the name of the
|
||||
/// command is missing.</exception>
|
||||
public void TriggerEventAll(IEnumerable<string> prefixes, string name, bool logMissing = true, bool wait = false, bool subcommand = false, dynamic[][]? parameters = null)
|
||||
{
|
||||
foreach (string prefix in prefixes)
|
||||
{
|
||||
this.Run($"(({prefix}.{name}))", logMissing, wait, subcommand, parameters);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -29,6 +29,7 @@ namespace alterNERDtive.Yavapf
|
|||
/// </summary>
|
||||
public class VoiceAttackPlugin
|
||||
{
|
||||
private VoiceAttackCommands? commands;
|
||||
private VoiceAttackLog? log;
|
||||
private VoiceAttackInitProxyClass? vaProxy;
|
||||
|
||||
|
@ -116,6 +117,17 @@ namespace alterNERDtive.Yavapf
|
|||
/// </summary>
|
||||
protected string? Guid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="VoiceAttackCommands"/> instance the plugin uses
|
||||
/// to run commands.
|
||||
///
|
||||
/// You can use this to run your own commands.
|
||||
/// </summary>
|
||||
protected VoiceAttackCommands Commands
|
||||
{
|
||||
get => this.commands ??= new VoiceAttackCommands(this.Proxy, this.Log);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="VoiceAttackLog"/> instance the plugin uses to
|
||||
/// log to the VoiceAttack event log.
|
||||
|
|
|
@ -1,3 +1,93 @@
|
|||
# Executing VoiceAttack Commands
|
||||
|
||||
Not implemented yet.
|
||||
VoicAttack’s plugin API allows you to run commands from your plugin:
|
||||
|
||||
```csharp
|
||||
vaProxy.Command.Execute(
|
||||
string CommandPhrase,
|
||||
bool WaitForReturn,
|
||||
bool AsSubcommand,
|
||||
Action<Guid?> CompletedAction,
|
||||
string PassedText,
|
||||
srting PassedIntegers,
|
||||
string PassedDecimals,
|
||||
string PassedBooleans,
|
||||
string PassedDates);
|
||||
```
|
||||
|
||||
None of those parameters are optional, and the arguments have to be passed as
|
||||
semicolon delimited strings. With extra hassle around quoting `string`
|
||||
arguments.
|
||||
|
||||
YAVAPF aims to make this API a little more comfortable to work with.
|
||||
|
||||
## Running a Command
|
||||
|
||||
```csharp
|
||||
Plugin.Commands.Run(
|
||||
string command,
|
||||
bool logMissing = true,
|
||||
bool wait = false,
|
||||
bool subcommand = false,
|
||||
dynamic[]? parameters = null);
|
||||
```
|
||||
|
||||
The main difference here is that all parameters apart from the command name are
|
||||
now _optional_. You can also now pass arguments to the command as typed arrays:
|
||||
|
||||
```csharp
|
||||
Plugin.Commands.Run(
|
||||
"example command",
|
||||
parameters: new dynamic[]
|
||||
{
|
||||
new string[] { "text value", "other text value" },
|
||||
new bool[] { true }
|
||||
});
|
||||
```
|
||||
|
||||
The ability to pass a callback `Action<Guid?>` is not exposed. The only
|
||||
information this `Action` can ever receive is the `Guid` of the command that has
|
||||
been run; setting such a `Guid` in a profile though is a fairly involved
|
||||
process, and exporting / importing the profile might not even preserve it.
|
||||
Tl;dr: it is kind of useless right now.
|
||||
|
||||
## Running a Command with Prefixes
|
||||
|
||||
The `RunAll` method lets you run a command with multiple prefixes. This is
|
||||
especially useful if your plugin accompanies a set of multiple VoiceAttack
|
||||
profiles, [like my Elite Dangerous profiles and their correspondingp
|
||||
plugins](https://alterNERDtive.github.io/VoiceAttack-profiles).
|
||||
|
||||
```csharp
|
||||
Plugin.Commands.RunAll(new string[] { "EliteAttack", "RatAttack" }, "startup", …);
|
||||
```
|
||||
|
||||
Prefixes are prepended to the command name with a dot. The line above would run
|
||||
the `EliteAttack.startup` and `RatAttack.startup` commands.
|
||||
|
||||
## Running an Event
|
||||
|
||||
VoiceAttack does not actually have a concept of running commands on events. The
|
||||
workaround is to have a naming convention for VoiceAttack “event” commands.
|
||||
|
||||
YAVAPF borrows the [`((command))` convention from
|
||||
EDDI](https://github.com/EDCD/EDDI/wiki/VoiceAttack-Integration#running-commands-on-eddi-events).
|
||||
|
||||
```csharp
|
||||
Plugin.Commands.TriggerEvent("some event", …);
|
||||
```
|
||||
|
||||
This would run the `((some event))` command in VoiceAttack. Optional parameters
|
||||
are the same as for [the `Run` method](commands.md#running-a-command).
|
||||
|
||||
## Running an Event with Prefixes
|
||||
|
||||
Similar to [`RunAll`](commands.md#running-a-command-with-prefixes),
|
||||
`TriggerEventAll` exists.
|
||||
|
||||
```csharp
|
||||
Plugin.Commands.TriggerEventAll(new string[] { "profile", "otherprofile" }, "event", …);
|
||||
```
|
||||
|
||||
This would run the `((profile.event))` and `((otherprofile.event))` comands in
|
||||
VoiceAttack.
|
||||
|
|
|
@ -24,7 +24,7 @@ Github](https://github.com/alterNERDtive/YAVAPF/tree/release/ExamplePlugin).
|
|||
* [x] Logging to the VoiceAttack event log
|
||||
* [ ] Logging to a log file
|
||||
* [ ] separate full debug log
|
||||
* [ ] Wrapper for executing commands
|
||||
* [x] Wrapper for executing commands
|
||||
* [ ] Plugin options, separate from handling “normal” variables
|
||||
* [ ] default values
|
||||
* [ ] descriptions
|
||||
|
|
Loading…
Reference in a new issue