parent
5b6db94bce
commit
6636a0ed4e
7 changed files with 228 additions and 66 deletions
|
@ -165,9 +165,9 @@ namespace alterNERDtive.Example
|
||||||
/// execute anything when all commands are stopped this is the place.
|
/// execute anything when all commands are stopped this is the place.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[StopCommand]
|
[StopCommand]
|
||||||
public static void Stop()
|
public static void StopCommand()
|
||||||
{
|
{
|
||||||
Plugin.Log.Notice("This is the example Stop handler method.");
|
Plugin.Log.Notice("This is the example StopCommand handler method.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -186,7 +186,7 @@ namespace alterNERDtive.Example
|
||||||
Plugin.Log.Notice(
|
Plugin.Log.Notice(
|
||||||
$"This is the example handler for the plugin contexts “test” and “different test”. It has been invoked with '{vaProxy.Context}'.");
|
$"This is the example handler for the plugin contexts “test” and “different test”. It has been invoked with '{vaProxy.Context}'.");
|
||||||
|
|
||||||
string test = Plugin.Get<string?>("~test") ?? throw new ArgumentNullException("~test");
|
string test = vaProxy.Get<string?>("~test") ?? throw new ArgumentNullException("~test");
|
||||||
Plugin.Log.Notice($"The value of 'TXT:~test' is '{test}'");
|
Plugin.Log.Notice($"The value of 'TXT:~test' is '{test}'");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1 +1,20 @@
|
||||||
|
// <copyright file="Usings.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>
|
||||||
|
|
||||||
global using Xunit;
|
global using Xunit;
|
134
VoiceAttack-Framework/VoiceAttackInitProxyExtension.cs
Normal file
134
VoiceAttack-Framework/VoiceAttackInitProxyExtension.cs
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
// <copyright file="VoiceAttackProxyExtension.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 System.IO;
|
||||||
|
|
||||||
|
using VoiceAttack;
|
||||||
|
|
||||||
|
namespace alterNERDtive.Yavapf
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Extends the <see cref="VoiceAttackInitProxyClass"/> class with generic
|
||||||
|
/// methods for getting and setting VoiceAttack variables.
|
||||||
|
/// </summary>
|
||||||
|
public static class VoiceAttackInitProxyExtension
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the value of a variable from VoiceAttack.
|
||||||
|
///
|
||||||
|
/// Valid varible types are <see cref="bool"/>, <see cref="DateTime"/>,
|
||||||
|
/// <see cref="decimal"/>, <see cref="int"/> and <see cref="string"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the variable.</typeparam>
|
||||||
|
/// <param name="vaProxy">The <see cref="VoiceAttackInitProxyClass"/> object.</param>
|
||||||
|
/// <param name="name">The name of the variable.</param>
|
||||||
|
/// <returns>The value of the variable. Can be null.</returns>
|
||||||
|
/// <exception cref="InvalidDataException">Thrown when the variable is of an invalid type.</exception>
|
||||||
|
public static T? Get<T>(this VoiceAttackInitProxyClass vaProxy, string name)
|
||||||
|
{
|
||||||
|
dynamic? ret = typeof(T) switch
|
||||||
|
{
|
||||||
|
Type boolType when boolType == typeof(bool) => vaProxy.GetBoolean(name),
|
||||||
|
Type dateType when dateType == typeof(DateTime) => vaProxy.GetDate(name),
|
||||||
|
Type decType when decType == typeof(decimal) => vaProxy.GetDecimal(name),
|
||||||
|
Type intType when intType == typeof(int) => vaProxy.GetInt(name),
|
||||||
|
Type stringType when stringType == typeof(string) => vaProxy.GetText(name),
|
||||||
|
_ => throw new InvalidDataException($"Cannot get variable '{name}': invalid type '{typeof(T).Name}'."),
|
||||||
|
};
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets a variable for use in VoiceAttack.
|
||||||
|
///
|
||||||
|
/// Valid varible types are <see cref="bool"/>, <see cref="DateTime"/>,
|
||||||
|
/// <see cref="decimal"/>, <see cref="int"/> and <see cref="string"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the variable.</typeparam>
|
||||||
|
/// <param name="vaProxy">The <see cref="VoiceAttackInitProxyClass"/> object.</param>
|
||||||
|
/// <param name="name">The name of the variable.</param>
|
||||||
|
/// <param name="value">The value of the variable. Can not be null.</param>
|
||||||
|
/// <exception cref="InvalidDataException">Thrown when the variable is of an invalid type.</exception>
|
||||||
|
public static void Set<T>(this VoiceAttackInitProxyClass vaProxy, string name, T? value)
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
vaProxy.Unset<T>(name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (value)
|
||||||
|
{
|
||||||
|
case bool b:
|
||||||
|
vaProxy.SetBoolean(name, b);
|
||||||
|
break;
|
||||||
|
case DateTime d:
|
||||||
|
vaProxy.SetDate(name, d);
|
||||||
|
break;
|
||||||
|
case decimal d:
|
||||||
|
vaProxy.SetDecimal(name, d);
|
||||||
|
break;
|
||||||
|
case int i:
|
||||||
|
vaProxy.SetInt(name, i);
|
||||||
|
break;
|
||||||
|
case string s:
|
||||||
|
vaProxy.SetText(name, s);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new InvalidDataException($"Cannot set variable '{name}': invalid type '{typeof(T).Name}'.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unsets a variable for use in VoiceAttack (= sets it to null).
|
||||||
|
///
|
||||||
|
/// Valid varible types are <see cref="bool"/>, <see cref="DateTime"/>,
|
||||||
|
/// <see cref="decimal"/>, <see cref="int"/> and <see cref="string"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the variable.</typeparam>
|
||||||
|
/// <param name="vaProxy">The <see cref="VoiceAttackInitProxyClass"/> object.</param>
|
||||||
|
/// <param name="name">The name of the variable.</param>
|
||||||
|
/// <exception cref="InvalidDataException">Thrown when the variable is of an invalid type.</exception>
|
||||||
|
public static void Unset<T>(this VoiceAttackInitProxyClass vaProxy, string name)
|
||||||
|
{
|
||||||
|
switch (typeof(T))
|
||||||
|
{
|
||||||
|
case Type boolType when boolType == typeof(bool):
|
||||||
|
vaProxy.SetBoolean(name, null);
|
||||||
|
break;
|
||||||
|
case Type dateType when dateType == typeof(DateTime):
|
||||||
|
vaProxy.SetDate(name, null);
|
||||||
|
break;
|
||||||
|
case Type decType when decType == typeof(decimal):
|
||||||
|
vaProxy.SetDecimal(name, null);
|
||||||
|
break;
|
||||||
|
case Type intType when intType == typeof(int):
|
||||||
|
vaProxy.SetInt(name, null);
|
||||||
|
break;
|
||||||
|
case Type stringType when stringType == typeof(string):
|
||||||
|
vaProxy.SetText(name, null);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new InvalidDataException($"Cannot set variable '{name}': invalid type '{typeof(T).Name}'.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -143,16 +143,13 @@ namespace alterNERDtive.Yavapf
|
||||||
/// <exception cref="InvalidDataException">Thrown when the variable is of an invalid type.</exception>
|
/// <exception cref="InvalidDataException">Thrown when the variable is of an invalid type.</exception>
|
||||||
protected T? Get<T>(string name)
|
protected T? Get<T>(string name)
|
||||||
{
|
{
|
||||||
dynamic? ret = typeof(T) switch
|
if (name.StartsWith("~"))
|
||||||
{
|
{
|
||||||
Type boolType when boolType == typeof(bool) => this.Proxy.GetBoolean(name),
|
this.Log.Warn(
|
||||||
Type dateType when dateType == typeof(DateTime) => this.Proxy.GetDate(name),
|
$"Accessing command scoped variable '{name}' outside of its context proxy object. This might lead to race conditions.");
|
||||||
Type decType when decType == typeof(decimal) => this.Proxy.GetDecimal(name),
|
}
|
||||||
Type intType when intType == typeof(int) => this.Proxy.GetInt(name),
|
|
||||||
Type stringType when stringType == typeof(string) => this.Proxy.GetText(name),
|
return this.Proxy.Get<T>(name);
|
||||||
_ => throw new InvalidDataException($"Cannot get variable '{name}': invalid type '{typeof(T).Name}'."),
|
|
||||||
};
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -167,33 +164,13 @@ namespace alterNERDtive.Yavapf
|
||||||
/// <exception cref="InvalidDataException">Thrown when the variable is of an invalid type.</exception>
|
/// <exception cref="InvalidDataException">Thrown when the variable is of an invalid type.</exception>
|
||||||
protected void Set<T>(string name, T? value)
|
protected void Set<T>(string name, T? value)
|
||||||
{
|
{
|
||||||
if (value == null)
|
if (name.StartsWith("~"))
|
||||||
{
|
{
|
||||||
this.Unset<T>(name);
|
this.Log.Warn(
|
||||||
}
|
$"Accessing command scoped variable '{name}' outside of its context proxy object. This might lead to race conditions.");
|
||||||
else
|
|
||||||
{
|
|
||||||
switch (value)
|
|
||||||
{
|
|
||||||
case bool b:
|
|
||||||
this.Proxy.SetBoolean(name, b);
|
|
||||||
break;
|
|
||||||
case DateTime d:
|
|
||||||
this.Proxy.SetDate(name, d);
|
|
||||||
break;
|
|
||||||
case decimal d:
|
|
||||||
this.Proxy.SetDecimal(name, d);
|
|
||||||
break;
|
|
||||||
case int i:
|
|
||||||
this.Proxy.SetInt(name, i);
|
|
||||||
break;
|
|
||||||
case string s:
|
|
||||||
this.Proxy.SetText(name, s);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new InvalidDataException($"Cannot set variable '{name}': invalid type '{typeof(T).Name}'.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.Proxy.Set<T>(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -207,26 +184,13 @@ namespace alterNERDtive.Yavapf
|
||||||
/// <exception cref="InvalidDataException">Thrown when the variable is of an invalid type.</exception>
|
/// <exception cref="InvalidDataException">Thrown when the variable is of an invalid type.</exception>
|
||||||
protected void Unset<T>(string name)
|
protected void Unset<T>(string name)
|
||||||
{
|
{
|
||||||
switch (typeof(T))
|
if (name.StartsWith("~"))
|
||||||
{
|
{
|
||||||
case Type boolType when boolType == typeof(bool):
|
this.Log.Warn(
|
||||||
this.Proxy.SetBoolean(name, null);
|
$"Accessing command scoped variable '{name}' outside of its context proxy object. This might lead to race conditions.");
|
||||||
break;
|
|
||||||
case Type dateType when dateType == typeof(DateTime):
|
|
||||||
this.Proxy.SetDate(name, null);
|
|
||||||
break;
|
|
||||||
case Type decType when decType == typeof(decimal):
|
|
||||||
this.Proxy.SetDecimal(name, null);
|
|
||||||
break;
|
|
||||||
case Type intType when intType == typeof(int):
|
|
||||||
this.Proxy.SetInt(name, null);
|
|
||||||
break;
|
|
||||||
case Type stringType when stringType == typeof(string):
|
|
||||||
this.Proxy.SetText(name, null);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new InvalidDataException($"Cannot set variable '{name}': invalid type '{typeof(T).Name}'.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.Proxy.Unset<T>(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -36,7 +36,8 @@ Separate functionality, separate handler method(s).
|
||||||
[Context("test")]
|
[Context("test")]
|
||||||
[Context("test context")]
|
[Context("test context")]
|
||||||
[Context("alternate context name")]
|
[Context("alternate context name")]
|
||||||
public static void TestContext(VoiceAttackInvokeProxyClass vaProxy) {
|
public static void TestContext(VoiceAttackInvokeProxyClass vaProxy)
|
||||||
|
{
|
||||||
[…]
|
[…]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -68,7 +69,8 @@ conditionals:
|
||||||
[Context(@"^.*bar.*")]
|
[Context(@"^.*bar.*")]
|
||||||
[Context(@"^.*baz")]
|
[Context(@"^.*baz")]
|
||||||
[Context("some name")]
|
[Context("some name")]
|
||||||
public static void RegexContext(VoiceAttackInvokeProxyClass vaProxy) {
|
public static void RegexContext(VoiceAttackInvokeProxyClass vaProxy)
|
||||||
|
{
|
||||||
string context = vaProxy.Context;
|
string context = vaProxy.Context;
|
||||||
if (context.StartsWith("foo")) {
|
if (context.StartsWith("foo")) {
|
||||||
[…]
|
[…]
|
||||||
|
@ -93,7 +95,8 @@ intended to be used in practice:
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
[Context(@"^edsm\..*")]
|
[Context(@"^edsm\..*")]
|
||||||
public static void EdsmContext(VoiceAttackInvokeProxyClass vaProxy) {
|
public static void EdsmContext(VoiceAttackInvokeProxyClass vaProxy)
|
||||||
|
{
|
||||||
switch(vaProxy.Context)
|
switch(vaProxy.Context)
|
||||||
{
|
{
|
||||||
case "edsm.findsystem":
|
case "edsm.findsystem":
|
||||||
|
@ -129,7 +132,8 @@ again, doesn’t matter.
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
[Context]
|
[Context]
|
||||||
public static void CatchallContext(VoiceAttackInvokeProxyClass vaProxy) {
|
public static void CatchallContext(VoiceAttackInvokeProxyClass vaProxy)
|
||||||
|
{
|
||||||
switch (vaProxy.Context)
|
switch (vaProxy.Context)
|
||||||
{
|
{
|
||||||
case "some context":
|
case "some context":
|
||||||
|
@ -158,14 +162,20 @@ invocations and their data.
|
||||||
This example accesses the `~test` text variable from plugin code:
|
This example accesses the `~test` text variable from plugin code:
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
string? testParameter = Plugin.Get<string>("~test");
|
string? testParameter = vaProxy.Get<string>("~test");
|
||||||
```
|
```
|
||||||
|
|
||||||
In case a parameter is missing that is _required_ for your context `throw` an
|
In case a parameter is missing that is _required_ for your context `throw` an
|
||||||
`ArgumentNullException` with the variable name as the parameter name:
|
`ArgumentNullException` with the variable name as the parameter name:
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
string testParameter = Plugin.Get<string>("~test") ?? throw new ArgumentNullException("~test");
|
string testParameter = vaProxy.Get<string>("~test") ?? throw new ArgumentNullException("~test");
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Note**: You should always use the `Get<T>(string)` and `Set<T>(string)`
|
||||||
|
extension methods of the `VoiceAttackInvokeProxyClass` object when accessing
|
||||||
|
command scoped variables. They are only available in the proxy object passed to
|
||||||
|
the corresponding context handler; the cached proxy object of your plugin object
|
||||||
|
might have changed already, leading to race conditions.
|
||||||
|
|
||||||
[More about variables](variables.md).
|
[More about variables](variables.md).
|
||||||
|
|
|
@ -22,7 +22,8 @@ must have an `InitAttribute`. `InitAttribute` does not have any properties.
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
[Init]
|
[Init]
|
||||||
public static void MyInitHandler(VoiceAttackInitProxyClass vaProxy) {
|
public static void MyInitHandler(VoiceAttackInitProxyClass vaProxy)
|
||||||
|
{
|
||||||
[…]
|
[…]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -38,7 +39,8 @@ have an `ExitAttribute`. `ExitAttribute` does not have any properties.
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
[Exit]
|
[Exit]
|
||||||
public static void MyExitHandler(VoiceAttackProxyClass vaProxy) {
|
public static void MyExitHandler(VoiceAttackProxyClass vaProxy)
|
||||||
|
{
|
||||||
[…]
|
[…]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -55,7 +57,8 @@ respond to that, use these.
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
[StopCommand]
|
[StopCommand]
|
||||||
public static void MyStopCommandHandler() {
|
public static void MyStopCommandHandler()
|
||||||
|
{
|
||||||
[…]
|
[…]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
|
@ -61,6 +61,38 @@ By default, YAVAPF automatically sets the following variables for your plugin:
|
||||||
| `<plugin name>.version` | string | The current version of your plugin.
|
| `<plugin name>.version` | string | The current version of your plugin.
|
||||||
| `<plugin name>.initialized` | bool | The plugin has been initialized correctly.
|
| `<plugin name>.initialized` | bool | The plugin has been initialized correctly.
|
||||||
|
|
||||||
|
## Using Proxy Methods vs. Using Plugin Methods
|
||||||
|
|
||||||
|
YAVAPF extends the `VoiceAttackInitProxyClass` and `VoiceAttackInvokeProxyClass`
|
||||||
|
classes with new generic methods for getting and setting variables. On top of
|
||||||
|
that `VoiceAttackPlugin` objects provide the same methods for ease of use and
|
||||||
|
when no proxy object is readily available to the current code path.
|
||||||
|
|
||||||
|
There is one **caveat** here: plugin objects cache a new proxy object every time
|
||||||
|
a plugin context is invoked. If you need to access command scoped variables (`~`
|
||||||
|
or `~~`), you should use the `VoiceAttackInvokeProxyClass` object directly.
|
||||||
|
Otherwise you might run into race conditions.
|
||||||
|
|
||||||
|
Correct:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[Context("test")]
|
||||||
|
private static void TestContext(VoiceAttackInvokeProxyClass vaProxy)
|
||||||
|
{
|
||||||
|
string test = vaProxy.Get<string>("~test");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Incorrect, might lead to race condition:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[Context("test")]
|
||||||
|
private static void TestContext(VoiceAttackInvokeProxyClass vaProxy)
|
||||||
|
{
|
||||||
|
string test = Plugin.Get<string>("~test");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Getting Variable Values
|
## Getting Variable Values
|
||||||
|
|
||||||
To get the value of a variable, invoke the `Get<T>(string name)` method of your
|
To get the value of a variable, invoke the `Get<T>(string name)` method of your
|
||||||
|
@ -69,7 +101,7 @@ its scope:
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
string? foo = Plugin.Get<string>("foo");
|
string? foo = Plugin.Get<string>("foo");
|
||||||
bool bar = Plugin.Get<bool>("~bar") ?? false;
|
bool bar = vaProxy.Get<bool>("~bar") ?? false;
|
||||||
```
|
```
|
||||||
|
|
||||||
Remember that variable values will be returned as `null` (“Not Set” in
|
Remember that variable values will be returned as `null` (“Not Set” in
|
||||||
|
|
Loading…
Reference in a new issue