YAVAPF/docs/contexts.md
2022-07-11 10:25:06 +02:00

5.9 KiB
Raw Permalink Blame History

Defining Plugin Contexts

Plugin contexts are defined similarly to event handlers.

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 ContextAttributes. 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, 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 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).

[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.

As with catchall 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:

[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:

[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, 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, doesnt matter.

[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:

string? testParameter = vaProxy.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:

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.