diff --git a/ExamplePlugin/ExamplePlugin.cs b/ExamplePlugin/ExamplePlugin.cs
index 2118df5..c9e6095 100644
--- a/ExamplePlugin/ExamplePlugin.cs
+++ b/ExamplePlugin/ExamplePlugin.cs
@@ -19,6 +19,8 @@
using System;
+using VoiceAttack;
+
namespace alterNERDtive.Yavapf.Example
{
public class ExamplePlugin
@@ -27,13 +29,17 @@ namespace alterNERDtive.Yavapf.Example
static ExamplePlugin()
{
- Plugin = new ()
- {
- Name = "Example Plugin",
- Version = "0.0.1",
- Info = "This is a description",
- Guid = "{76FE674F-F729-45FD-A1DD-E53E9E66B360}",
- };
+ Plugin = new (
+ name: "Example Plugin",
+ version: "0.0.1",
+ info: "This is a description",
+ guid: "{76FE674F-F729-45FD-A1DD-E53E9E66B360}");
+
+ Plugin.Init += Init;
+ Plugin.Exit += Exit;
+ Plugin.Stop += Stop;
+
+ Plugin.Contexts += Test;
}
public static string VA_DisplayName() => Plugin.VA_DisplayName();
@@ -49,5 +55,24 @@ namespace alterNERDtive.Yavapf.Example
public static void VA_Exit1(dynamic vaProxy) => Plugin.VA_Exit1(vaProxy);
public static void VA_StopCommand() => Plugin.VA_StopCommand();
+
+ private static void Init(VoiceAttackInitProxyClass vaProxy)
+ {
+ }
+
+ private static void Exit(VoiceAttackProxyClass vaProxy)
+ {
+ }
+
+ private static void Stop()
+ {
+ }
+
+ [Context("test")]
+ [Context("other name")]
+ private static void Test(VoiceAttackInvokeProxyClass vaProxy)
+ {
+ Plugin.Log.Notice($"Plugin context '{vaProxy.Context}' invoked.");
+ }
}
}
diff --git a/ExamplePlugin/ExamplePlugin.csproj b/ExamplePlugin/ExamplePlugin.csproj
index 37855b7..c9ca55f 100644
--- a/ExamplePlugin/ExamplePlugin.csproj
+++ b/ExamplePlugin/ExamplePlugin.csproj
@@ -6,10 +6,21 @@
enable
+
+
+
+
+
+
+ C:\Program Files\VoiceAttack\VoiceAttack.exe
+ False
+
+
+
True
diff --git a/VoiceAttack-Framework/VoiceAttack-Framework.csproj b/VoiceAttack-Framework/VoiceAttack-Framework.csproj
index a963370..afb663c 100644
--- a/VoiceAttack-Framework/VoiceAttack-Framework.csproj
+++ b/VoiceAttack-Framework/VoiceAttack-Framework.csproj
@@ -1,8 +1,8 @@
-
+
net48
- alterNERDtive.YAVAPF
+ alterNERDtive.Yavapf
alterNERDtive.YAVAPF
0.0.1
alterNERDtive
@@ -38,7 +38,11 @@
-
+
+
+ C:\Program Files\VoiceAttack\VoiceAttack.exe
+ False
+
diff --git a/VoiceAttack-Framework/VoiceAttackLog.cs b/VoiceAttack-Framework/VoiceAttackLog.cs
new file mode 100644
index 0000000..33491c8
--- /dev/null
+++ b/VoiceAttack-Framework/VoiceAttackLog.cs
@@ -0,0 +1,117 @@
+//
+// 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/>.
+//
+
+using System;
+
+using VoiceAttack;
+
+namespace alterNERDtive.Yavapf
+{
+ public class VoiceAttackLog
+ {
+ private static readonly string[] LogColour = { "red", "yellow", "green", "blue", "gray" };
+
+ private static LogLevel? logLevel;
+
+ private readonly VoiceAttackInitProxyClass vaProxy;
+ private readonly string id;
+
+ public VoiceAttackLog(VoiceAttackInitProxyClass vaProxy, string id)
+ {
+ this.vaProxy = vaProxy;
+ this.id = id;
+ }
+
+ public LogLevel? LogLevel
+ {
+ get => logLevel ?? Yavapf.LogLevel.NOTICE;
+ set
+ {
+ logLevel = value;
+ this.Notice($"Log level set to {value ?? Yavapf.LogLevel.NOTICE}.");
+ }
+ }
+
+ public void SetLogLevel(string level)
+ {
+ if (level == null)
+ {
+ this.LogLevel = null;
+ }
+ else
+ {
+ this.LogLevel = (LogLevel)Enum.Parse(typeof(LogLevel), level.ToUpper());
+ }
+ }
+
+ 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]);
+ }
+ }
+
+ public void Error(string message) => this.Log(message, Yavapf.LogLevel.ERROR);
+
+ public void Warn(string message) => this.Log(message, Yavapf.LogLevel.WARN);
+
+ public void Notice(string message) => this.Log(message, Yavapf.LogLevel.NOTICE);
+
+ public void Info(string message) => this.Log(message, Yavapf.LogLevel.INFO);
+
+ public void Debug(string message) => this.Log(message, Yavapf.LogLevel.DEBUG);
+ }
+
+ ///
+ /// Log levels that can be used when writing to the VoiceAttack log.
+ ///
+ public enum LogLevel
+ {
+ ///
+ /// Log level for error messages. Errors cause execution to abort.
+ ///
+ ERROR,
+
+ ///
+ /// Log level for warning messages. Warnings should not cause execution
+ /// to abort.
+ ///
+ WARN,
+
+ ///
+ /// Log level for notices. Notices should be noteworthy.
+ ///
+ NOTICE,
+
+ ///
+ /// Log level for informational messages. These should not be
+ /// noteworthy.
+ ///
+ INFO,
+
+ ///
+ /// Log level for debug messages. They should be useful only for
+ /// debugging.
+ ///
+ DEBUG,
+ }
+}
diff --git a/VoiceAttack-Framework/VoiceAttackPlugin.cs b/VoiceAttack-Framework/VoiceAttackPlugin.cs
index 08e02ba..e2fe78f 100644
--- a/VoiceAttack-Framework/VoiceAttackPlugin.cs
+++ b/VoiceAttack-Framework/VoiceAttackPlugin.cs
@@ -18,64 +18,228 @@
//
using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using VoiceAttack;
namespace alterNERDtive.Yavapf
{
public class VoiceAttackPlugin
{
- public string Name { get; set; }
+ public VoiceAttackPlugin(string name, string version, string info, string guid)
+ => (this.Name, this.Version, this.Info, this.Guid)
+ = (name, version, info, new (guid));
- public string Version { get; set; }
+ // this just hides the default constructor to make sure Properties are set.
+ private VoiceAttackPlugin()
+ {
+ }
- public string Info { get; set; }
+ public event Action? Init;
- public string Guid { get; set; }
+ public event Action? Exit;
- private dynamic? vaProxy;
+ public event Action? Stop;
+
+ public HandlerList> Contexts { get; set; } = new ();
+
+ public VoiceAttackInitProxyClass Proxy
+ {
+ get => this.vaProxy;
+ }
+
+ public string Name { get; }
+
+ public string Version { get; }
+
+ public string Info { get; }
+
+ public Guid Guid { get; }
+
+ public VoiceAttackLog Log {
+ get => this.log ??= new VoiceAttackLog(this.vaProxy, this.Name);
+ }
+
+ private VoiceAttackLog log;
+
+ protected VoiceAttackInitProxyClass vaProxy;
+
+ public void Set(string name, T? value)
+ {
+ switch (value)
+ {
+ case bool b:
+ this.vaProxy.SetBoolean(name, b);
+ break;
+ case DateTime d:
+ this.vaProxy.SetDate(name, d);
+ break;
+ case decimal d:
+ this.vaProxy.SetDecimal(name, d);
+ break;
+ case int i:
+ this.vaProxy.SetInt(name, i);
+ break;
+ case short s:
+ this.vaProxy.SetSmallInt(name, s);
+ break;
+ case string s:
+ this.vaProxy.SetText(name, s);
+ break;
+ default:
+ throw new InvalidDataException($"Cannot set variable '{name}': invalid type '{typeof(T).Name}'.");
+ }
+ }
+
+ public T? Get(string name)
+ {
+ dynamic? ret = null;
+
+ switch (default(T))
+ {
+ case bool _:
+ ret = this.vaProxy.GetBoolean(name);
+ break;
+ case DateTime _:
+ ret = this.vaProxy.GetDate(name);
+ break;
+ case decimal _:
+ ret = this.vaProxy.GetDecimal(name);
+ break;
+ case int _:
+ ret = this.vaProxy.GetInt(name);
+ break;
+ case short _:
+ ret = this.vaProxy.GetSmallInt(name);
+ break;
+ case string _:
+ ret = this.vaProxy.GetText(name);
+ break;
+ default:
+ throw new InvalidDataException($"Cannot get variable '{name}': invalid type '{typeof(T).Name}'.");
+ }
+
+ return ret;
+ }
public string VA_DisplayName() => $"{this.Name} v{this.Version}";
public string VA_DisplayInfo() => this.Info;
- public Guid VA_Id() => new (this.Guid);
+ public Guid VA_Id() => this.Guid;
- public void VA_Init1(dynamic vaProxy)
+ public void VA_Init1(VoiceAttackInitProxyClass vaProxy)
{
this.vaProxy = vaProxy;
- this.vaProxy.TextVariableChanged += new Action(this.TextVariableChanged);
- this.vaProxy.IntegerVariableChanged += new Action(this.IntegerVariableChanged);
- this.vaProxy.DecimalVariableChanged += new Action(this.DecimalVariableChanged);
- this.vaProxy.BooleanVariableChanged += new Action(this.BooleanVariableChanged);
- this.vaProxy.DateVariableChanged += new Action(this.DateVariableChanged);
+
+ this.Set($"{this.Name}.version", this.Version);
+ this.Log.Debug($"Initializing v{this.Version} …");
+
+ this.vaProxy.TextVariableChanged += this.TextVariableChanged;
+ this.vaProxy.IntegerVariableChanged += this.IntegerVariableChanged;
+ this.vaProxy.DecimalVariableChanged += this.DecimalVariableChanged;
+ this.vaProxy.BooleanVariableChanged += this.BooleanVariableChanged;
+ this.vaProxy.DateVariableChanged += this.DateVariableChanged;
+
+ this.Log.Debug("Running Init handlers …");
+ this.Init?.Invoke(vaProxy);
+ this.Log.Debug("Finished running Init handlers.");
+
+ this.Set($"{this.Name}.initialized", true);
+ this.Log.Debug("Initialized.");
}
- public void VA_Invoke1(dynamic vaProxy)
+ public void VA_Invoke1(VoiceAttackInvokeProxyClass vaProxy)
{
this.vaProxy = vaProxy;
+
+ string context = vaProxy.Context.ToLower();
+
+ try
+ {
+ bool exists = false;
+ foreach (Action action in Contexts)
+ {
+ foreach (ContextAttribute attr in action.Method.GetCustomAttributes())
+ {
+ if (attr.Name == context)
+ {
+ exists = true;
+ action.Invoke(vaProxy);
+ }
+ }
+ }
+
+ if (!exists)
+ {
+ this.Log.Error($"Invalid plugin context '{vaProxy.Context}'.");
+ }
+ }
+ catch (ArgumentNullException e)
+ {
+ this.Log.Error($"Missing parameter '{e.ParamName}' for context '{context}'");
+ }
+ catch (Exception e)
+ {
+ this.Log.Error($"Unhandled exception while executing plugin context '{context}'. ({e.Message})");
+ }
}
- public void VA_Exit1(dynamic vaProxy)
+ public void VA_Exit1(VoiceAttackProxyClass vaProxy)
{
- this.vaProxy = vaProxy;
+ this.Exit?.Invoke(vaProxy);
}
public void VA_StopCommand()
{
+ this.Stop?.Invoke();
}
private void TextVariableChanged(string name, string from, string to, Guid? internalID = null)
- { }
+ {
+ }
private void IntegerVariableChanged(string name, int? from, int? to, Guid? internalID = null)
- { }
+ {
+ }
- private void DecimalVariableChanged(string name, decimal? from, decimal? to, Guid? internalID = null)
- { }
+ protected void DecimalVariableChanged(string name, decimal? from, decimal? to, Guid? internalID = null)
+ {
+ }
- private void BooleanVariableChanged(string name, bool? from, bool? to, Guid? internalID = null)
- { }
+ protected void BooleanVariableChanged(string name, bool? from, bool? to, Guid? internalID = null)
+ {
+ }
- private void DateVariableChanged(string name, DateTime? from, DateTime? to, Guid? internalID = null)
- { }
+ protected void DateVariableChanged(string name, DateTime? from, DateTime? to, Guid? internalID = null)
+ {
+ }
+
+ public class HandlerList : List
+ {
+ public static HandlerList operator +(HandlerList handlers, T item)
+ {
+ handlers.Add(item);
+ return handlers;
+ }
+
+ public static HandlerList operator -(HandlerList handlers, T item)
+ {
+ handlers.Remove(item);
+ return handlers;
+ }
+ }
+ }
+
+ [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
+ public class ContextAttribute : Attribute
+ {
+ public string Name { get; }
+
+ public ContextAttribute(string context)
+ {
+ this.Name = context;
+ }
}
}