From b3bd8450e63578b2d2dfadccaae5f97e52d880fa Mon Sep 17 00:00:00 2001 From: alterNERDtive Date: Mon, 11 Jul 2022 13:04:32 +0200 Subject: [PATCH] now a YAVAPF plugin! --- CHANGELOG.md | 10 +- Properties/AssemblyInfo.cs | 31 +++- bindED.cs | 285 +++++++++++++++++++------------------ bindEDplugin.csproj | 11 +- 4 files changed, 193 insertions(+), 144 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1ee032..c1c8dfb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,12 @@ -# 4.2.2 (2022-05-31) +# devel + +## Changed + +* Now a [YAVAPF](https://alterNERDtive.github.io/YAVAPF) plugin! + +----- + +# 4.2.2 (2022-05-31) ## Fixed diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs index 6634b95..9b1c3fa 100644 --- a/Properties/AssemblyInfo.cs +++ b/Properties/AssemblyInfo.cs @@ -1,8 +1,27 @@ -using System.Reflection; +// +// Copyright 2020–2022 alterNERDtive. +// +// This file is part of bindED VoiceAttack plugin. +// +// bindED VoiceAttack plugin 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. +// +// bindED VoiceAttack plugin 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 bindED VoiceAttack plugin. If not, see <https://www.gnu.org/licenses/>. +// + +using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -// General Information about an assembly is controlled through the following +// General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("bindED plugin for VoiceAttack")] @@ -14,8 +33,8 @@ [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] @@ -25,11 +44,11 @@ // Version information for an assembly consists of the following four values: // // Major Version -// Minor Version +// Minor Version // Build Number // Revision // -// You can specify all the values or you can default the Build and Revision Numbers +// You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("2.0.0")] diff --git a/bindED.cs b/bindED.cs index 224f277..923f2e0 100644 --- a/bindED.cs +++ b/bindED.cs @@ -27,6 +27,9 @@ using System.Threading; using System.Xml.Linq; +using alterNERDtive.Yavapf; +using VoiceAttack; + namespace bindEDplugin { /// @@ -36,9 +39,9 @@ namespace bindEDplugin [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "historic, grandfathered in")] [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1649:File name should match first type name", Justification = "historic, grandfathered in")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "historic, grandfathered in")] - public class bindEDPlugin + public class bindEDPlugin : VoiceAttackPlugin { - private static readonly Version VERSION = new ("4.2.2"); + private static readonly bindEDPlugin Plugin; private static readonly string BindingsDir = Path.Combine( Environment.GetFolderPath( @@ -47,8 +50,6 @@ public class bindEDPlugin private static readonly Dictionary FileEventCount = new (); - [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1306:Field names should begin with lower-case letter", Justification = "just cause")] - private static dynamic? VA; private static string? pluginPath; private static FileSystemWatcher? bindsWatcher; private static FileSystemWatcher? mapWatcher; @@ -57,6 +58,17 @@ public class bindEDPlugin private static string? preset; private static Dictionary>? binds; + static bindEDPlugin() + { + Plugin = new () + { + Name = "bindED", + Version = "4.2.3", + Info = "bindED Plugin\n\n2016 VoiceAttack.com\n2020–2022 alterNERDtive", + Guid = "{524B4B9A-3965-4045-A39A-A239BF6E2838}", + }; + } + private static FileSystemWatcher BindsWatcher { get @@ -93,7 +105,7 @@ private static FileSystemWatcher MapWatcher private static string? Layout { - get => layout ??= VA?.GetText("bindED.layout#") ?? "en-us"; + get => layout ??= Plugin.Get("bindED.layout#") ?? "en-us"; set { layout = value; @@ -127,33 +139,61 @@ private static Dictionary>? Binds /// The plugin’s display name, as required by the VoiceAttack plugin API. /// /// The display name. - public static string VA_DisplayName() => $"bindED Plugin v{VERSION}-alterNERDtive"; + public static string VA_DisplayName() => Plugin.VaDisplayName(); /// /// The plugin’s description, as required by the VoiceAttack plugin API. /// /// The description. - public static string VA_DisplayInfo() => "bindED Plugin\r\n\r\n2016 VoiceAttack.com\r\n2020–2021 alterNERDtive"; + public static string VA_DisplayInfo() => Plugin.VaDisplayInfo(); /// /// The plugin’s GUID, as required by the VoiceAttack plugin API. /// /// The GUID. - public static Guid VA_Id() => new ("{524B4B9A-3965-4045-A39A-A239BF6E2838}"); + public static Guid VA_Id() => Plugin.VaId(); /// /// The Init method, as required by the VoiceAttack plugin API. /// Runs when the plugin is initially loaded. /// /// The VoiceAttack proxy object. - public static void VA_Init1(dynamic vaProxy) - { - VA = vaProxy; - VA.TextVariableChanged += new Action(TextVariableChanged); - pluginPath = Path.GetDirectoryName(VA.PluginPath()); + public static void VA_Init1(dynamic vaProxy) => Plugin.VaInit1(vaProxy); - VA.SetText("bindED.version", VERSION.ToString()); - VA.SetText("bindED.fork", "alterNERDtive"); + /// + /// The Invoke method, as required by the VoiceAttack plugin API. + /// Runs whenever a plugin context is invoked. + /// + /// The VoiceAttack proxy object. + public static void VA_Invoke1(dynamic vaProxy) => Plugin.VaInvoke1(vaProxy); + + /// + /// The Exit method, as required by the VoiceAttack plugin API. + /// Runs when VoiceAttack is shut down. + /// + /// The VoiceAttack proxy object. + public static void VA_Exit1(dynamic vaProxy) => Plugin.VaExit1(vaProxy); + + /// + /// The StopCommand method, as required by the VoiceAttack plugin API. + /// Runs whenever all commands are stopped using the “Stop All Commands” + /// button or action. + /// + public static void VA_StopCommand() => Plugin.VaStopCommand(); + + /// + /// Loads the current binds, enables watching the binds directory for + /// changes. + /// + /// The + /// proxy object. + [Init] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Required by plugin API")] + public static void Init(VoiceAttackInitProxyClass vaProxy) + { + Plugin.Log.LogLevel = LogLevel.INFO; + pluginPath = Path.GetDirectoryName(Plugin.Proxy.PluginPath()); + Plugin.Set("bindED.fork", "alterNERDtive"); try { @@ -161,7 +201,7 @@ public static void VA_Init1(dynamic vaProxy) } catch (Exception e) { - LogError(e.Message); + Plugin.Log.Error(e.Message); } finally { @@ -171,118 +211,114 @@ public static void VA_Init1(dynamic vaProxy) } /// - /// The Invoke method, as required by the VoiceAttack plugin API. - /// Runs whenever a plugin context is invoked. + /// Logs some diagnostics regarding the current state of the plugin. /// - /// The VoiceAttack proxy object. - public static void VA_Invoke1(dynamic vaProxy) + /// The + /// proxy object. + [Context("diagnostics")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Required by plugin API")] + public static void DiagnosticsContext(VoiceAttackInvokeProxyClass vaProxy) { - VA = vaProxy; - try - { - string context = VA.Context.ToLower(); - if (context == "diagnostics") - { - LogInfo($"current keybord layout: {Layout}"); - LogInfo($"current preset: {Preset}"); - LogInfo($"detected binds file: {new FileInfo(DetectBindsFile(Preset!)).Name}"); - LogInfo($"detected binds file (full path): {DetectBindsFile(Preset!)}"); - } - else if (context == "listbinds") - { - ListBinds(Binds, VA.GetText("bindED.separator") ?? "\r\n"); - } - else if (context == "loadbinds") - { - // force reset everything - Layout = null; - Preset = null; - if (!string.IsNullOrWhiteSpace(VA.GetText("~bindsFile"))) - { - Binds = ReadBinds(Path.Combine(BindingsDir, VA.GetText("~bindsFile"))); - } - - LoadBinds(Binds); - } - else if (context == "missingbinds") - { - MissingBinds(Binds); - } - else - { - if (string.IsNullOrWhiteSpace(context)) - { - LogError("Empty plugin context."); - } - else - { - LogError($"Invalid plugin context '{context}'."); - } - - LogError("You generally do not need to invoke the plugin manually."); - } - } - catch (Exception e) - { - LogError(e.Message); - } + Plugin.Log.Info($"Current keybord layout: {Layout}"); + Plugin.Log.Info($"Current preset: {Preset}"); + Plugin.Log.Info($"Detected binds file: {new FileInfo(DetectBindsFile(Preset!)).Name}"); + Plugin.Log.Info($"Detected binds file (full path): {DetectBindsFile(Preset!)}"); } /// - /// The Exit method, as required by the VoiceAttack plugin API. - /// Runs when VoiceAttack is shut down. + /// Lists all currently loaded binds. /// - /// The VoiceAttack proxy object. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "required by VoiceAttack plugin API")] - public static void VA_Exit1(dynamic vaProxy) + /// The + /// proxy object. + [Context("listbinds")] + public static void ListBindsContext(VoiceAttackInvokeProxyClass vaProxy) { + vaProxy.Set("~bindED.bindsList", string.Join(vaProxy.Get("bindED.separator") ?? "\n", Binds!.Keys)); + Plugin.Log.Info("List of Elite binds saved to TXT variable '~bindED.bindsList'."); } /// - /// The StopCommand method, as required by the VoiceAttack plugin API. - /// Runs whenever all commands are stopped using the “Stop All Commands” - /// button or action. + /// Forces a reload of all binds. /// - public static void VA_StopCommand() + /// The + /// proxy object. + [Context("loadbinds")] + public static void LoadBindsContext(VoiceAttackInvokeProxyClass vaProxy) { + // force reset everything + Layout = null; + Preset = null; + if (!string.IsNullOrWhiteSpace(vaProxy.Get("~bindsFile"))) + { + Binds = ReadBinds(Path.Combine(BindingsDir, vaProxy.Get("~bindsFile"))); + } + + LoadBinds(Binds); } - private static void TextVariableChanged(string name, string from, string to, Guid? internalID = null) + /// + /// Reports missing binds. + /// + /// The + /// proxy object. + [Context("missingbinds")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Required by plugin API")] + public static void MissingBindsContext(VoiceAttackInvokeProxyClass vaProxy) { - if (name == "bindED.layout#") + List missing = new (256); + foreach (KeyValuePair> bind in Binds!) { - LogInfo($"Keyboard layout changed to '{to}', reloading …"); - Layout = to; - try - { - LoadBinds(Binds); - } - catch (Exception e) + if (bind.Value.Count == 0) { - LogError(e.Message); + missing.Add(bind.Key); } } - } - private static void LogError(string message) - { - VA!.WriteToLog($"ERROR | bindED: {message}", "red"); - } - - private static void LogInfo(string message) - { - VA!.WriteToLog($"INFO | bindED: {message}", "blue"); + if (missing.Count > 0) + { + vaProxy.Set("~bindED.missingBinds", string.Join("\n", missing)); + vaProxy.Set("~bindED.missingBinds", true); + Plugin.Log.Info("List of missing Elite binds saved to TXT variable '~bindED.missingBinds'."); + } + else + { + Plugin.Log.Info($"No missing keyboard binds found."); + } } - private static void LogWarn(string message) + /// + /// Gives an explicit error message if the plugin is invoked without a + /// plugin context. + /// + /// The + /// proxy object. + [Context("")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Required by plugin API")] + public static void EmptyContext(VoiceAttackInvokeProxyClass vaProxy) { - VA!.WriteToLog($"WARN | bindED: {message}", "yellow"); + Plugin.Log.Error("Empty plugin context. You generally do not need to invoke the plugin manually."); } - private static void ListBinds(Dictionary>? binds, string separator) + /// + /// Handles changes to the keyboard layout. + /// + /// The variable name (not used). + /// The old keyboard layout. + /// The new keyboard layout. + [String("bindED.layout#")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Required by plugin API")] + public static void LayoutChanged(string name, string from, string to) { - VA!.SetText("~bindED.bindsList", string.Join(separator, binds!.Keys)); - LogInfo("List of Elite binds saved to TXT variable '~bindED.bindsList'."); + Plugin.Log.Info($"Keyboard layout changed to '{to}', reloading …"); + Layout = to; + try + { + LoadBinds(Binds); + } + catch (Exception e) + { + Plugin.Log.Error(e.Message); + } } private static void LoadBinds(Dictionary>? binds) @@ -306,41 +342,19 @@ private static void LoadBinds(Dictionary>? binds) else { valid = false; - LogError($"No valid key code for '{key}' found, skipping bind for '{bind.Key}' …"); + Plugin.Log.Warn($"No valid key code for '{key}' found, skipping bind for '{bind.Key}' …"); } } if (valid) { - VA!.SetText(bind.Key, value); + Plugin.Set(bind.Key, value); } } } - LogInfo($"Elite binds '{(string.IsNullOrWhiteSpace(VA!.GetText("~bindsFile")) ? Preset : VA!.GetText("~bindsFile"))}' for layout '{Layout}' loaded successfully."); - } - - private static void MissingBinds(Dictionary>? binds) - { - List missing = new (256); - foreach (KeyValuePair> bind in binds!) - { - if (bind.Value.Count == 0) - { - missing.Add(bind.Key); - } - } - - if (missing.Count > 0) - { - VA!.SetText("~bindED.missingBinds", string.Join("\r\n", missing)); - VA!.SetBoolean("~bindED.missingBinds", true); - LogInfo("List of missing Elite binds saved to TXT variable '~bindED.missingBinds'."); - } - else - { - LogInfo($"No missing keyboard binds found."); - } + //FIXXME: suppress warning for possible race condition + Plugin.Log.Info($"Elite binds '{(string.IsNullOrWhiteSpace(Plugin.Get("~bindsFile")) ? Preset : Plugin.Get("~bindsFile"))}' for layout '{Layout}' loaded successfully."); } private static Dictionary LoadKeyMap(string layout) @@ -357,8 +371,7 @@ private static Dictionary LoadKeyMap(string layout) string[] arItem = line.Split(";".ToCharArray(), 2, StringSplitOptions.RemoveEmptyEntries); if ((arItem.Count() == 2) && (!string.IsNullOrWhiteSpace(arItem[0])) && (!map.ContainsKey(arItem[0]))) { - ushort iKey; - if (ushort.TryParse(arItem[1], out iKey)) + if (ushort.TryParse(arItem[1], out ushort iKey)) { if (iKey > 0 && iKey < 256) { @@ -391,7 +404,7 @@ private static string DetectPreset() IEnumerable presets = File.ReadAllLines(startFile).Distinct(); if (presets.Count() > 1) { - LogError($"You have selected multiple control presets ('{string.Join("', '", presets)}'). " + Plugin.Log.Warn($"You have selected multiple control presets ('{string.Join("', '", presets)}'). " + $"Only binds from '{presets.First()}' will be used. Please refer to the documentation for more information."); } @@ -493,24 +506,24 @@ private static void FileChangedHandler(string name) // binds. if (name == $"EDMap-{Layout!.ToLower()}.txt") { - LogInfo($"Key map for layout '{Layout}' has changed, reloading …"); + Plugin.Log.Info($"Key map for layout '{Layout}' has changed, reloading …"); KeyMap = null; LoadBinds(Binds); } else if (name == "StartPreset.start") { - LogInfo("Controls preset has changed, reloading …"); + Plugin.Log.Info("Controls preset has changed, reloading …"); Preset = null; LoadBinds(Binds); } else if (Regex.Match(name, $@"{Preset}(\.[34]\.0)?\.binds$").Success) { - LogInfo($"Bindings file '{name}' has changed, reloading …"); + Plugin.Log.Info($"Bindings file '{name}' has changed, reloading …"); Binds = null; LoadBinds(Binds); // copy Odyssey -> Horizons - if (name == $"{Preset}.4.0.binds" && !VA!.GetBoolean("bindED.disableHorizonsSync#")) + if (name == $"{Preset}.4.0.binds" && !Plugin.Get("bindED.disableHorizonsSync#")) { File.WriteAllText( Path.Combine(BindingsDir, $"{Preset}.3.0.binds"), @@ -521,7 +534,7 @@ private static void FileChangedHandler(string name) } catch (Exception e) { - LogError(e.Message); + Plugin.Log.Error(e.Message); } } } diff --git a/bindEDplugin.csproj b/bindEDplugin.csproj index e042a52..1f815fb 100644 --- a/bindEDplugin.csproj +++ b/bindEDplugin.csproj @@ -37,6 +37,10 @@ + + C:\Program Files\VoiceAttack\VoiceAttack.exe + False + @@ -86,6 +90,11 @@ PreserveNewest + + + 0.1.0 + +