alterNERDtive-base: plugin now fully supports the new configuration approach

This commit is contained in:
alterNERDtive 2020-12-02 19:08:46 +01:00
parent ca52c39da3
commit 606f651eb6
4 changed files with 285 additions and 13 deletions

View file

@ -122,7 +122,7 @@ namespace RatAttack
}
private static void On_ProfileChanged(Guid? from, Guid? to, string fromName, string toName)
=> VA_Exit1(null);
=> VA_Exit1(VA);
/*================\
| plugin contexts |

View file

@ -4,10 +4,7 @@ using alterNERDtive.util;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.Remoting.Channels;
using System.Threading.Tasks;
using System.Timers;
using System.Text.RegularExpressions;
namespace alterNERDtive
{
@ -22,12 +19,17 @@ namespace alterNERDtive
};
private static readonly List<string> ActiveProfiles = new List<string>();
private static readonly Regex ConfigurationVariableRegex = new Regex(@$"(?<id>(alterNERDtive-base|{String.Join("|", Profiles.Values)}))\.(?<name>.+)#");
private static VoiceAttackLog Log => log ??= new VoiceAttackLog(VA, "alterNERDtive-base");
private static VoiceAttackLog? log;
private static VoiceAttackCommands Commands => commands ??= new VoiceAttackCommands(VA, Log);
private static VoiceAttackCommands? commands;
private static Configuration Config => config ??= new Configuration(VA, Log, "alterNERDtive-base");
private static Configuration? config;
private static void CheckProfiles(dynamic vaProxy)
{
ActiveProfiles.Clear();
@ -44,6 +46,24 @@ namespace alterNERDtive
| plugin contexts |
\================*/
private static void Context_Config_Setup(dynamic vaProxy)
{
Log.Debug("Loading default configuration …");
Config.ApplyAllDefaults();
foreach (System.Type type in new List<System.Type> { typeof(bool), typeof(DateTime), typeof(decimal), typeof(int), typeof(short), typeof(string) })
{
Config.SetVoiceTriggers(type);
}
Log.Debug("Finished loading configuration.");
}
private static void Context_Config_SetVariables(dynamic vaProxy)
{
string trigger = vaProxy.GetText("~trigger");
Log.Debug($"Loading variables for trigger '{trigger}' …");
Config.SetVariablesForTrigger(vaProxy, trigger);
}
private static void Context_DistanceBetween(dynamic vaProxy)
{
string fromSystem = vaProxy.GetText("~fromSystem");
@ -51,7 +71,7 @@ namespace alterNERDtive
int roundTo = vaProxy.GetInt("~roundTo") ?? 2;
string path = $"{vaProxy.GetText("Python.ScriptPath")}\\explorationtools.exe";
string arguments = $"distancebetween --roundto {roundTo} \"{fromSystem}\" \"{toSystem}\"";
string arguments = $@"distancebetween --roundto {roundTo} ""{fromSystem}"" ""{toSystem}""";
Process p = PythonProxy.SetupPythonScript(path, arguments);
@ -113,9 +133,10 @@ namespace alterNERDtive
{
Log.Log(sender, message, (LogLevel)Enum.Parse(typeof(LogLevel), level.ToUpper()));
}
catch (ArgumentNullException) { throw; }
catch (ArgumentException)
{
Log.Error($"Invalid log level '${level}'.");
Log.Error($"Invalid log level '{level}'.");
}
}
}
@ -123,7 +144,6 @@ namespace alterNERDtive
private static void Context_Startup(dynamic vaProxy)
{
Log.Notice("Starting up …");
VA = vaProxy;
CheckProfiles(VA);
Commands.RunAll(ActiveProfiles, "startup", logMissing: true);
Log.Notice("Finished startup.");
@ -142,6 +162,35 @@ namespace alterNERDtive
}
}
private static void ConfigurationChanged(string name, dynamic? from, dynamic? to, Guid? guid = null)
{
try
{
Match match = ConfigurationVariableRegex.Match(name);
if (match.Success)
{
string id = match.Groups["id"].Value;
string option = match.Groups["name"].Value;
Log.Debug($"Configuration has changed, '{id}.{option}': '{from}' → '{to}'");
// When loaded from profile but not explicitly set, will be null.
// Then load default.
// Same applies to resetting a saved option (= saving null to the profile).
_ = to ?? Config.ApplyDefault(id, option);
if (name == "alterNERDtive-base.eddi.quietMode#" && VA!.GetText("EDDI version") != null) // if null, EDDI isnt up yet
{
Log.Debug($"Resetting speech responder ({(to ?? false ? "off" : "on")}) …");
Commands.Run("alterNERDtive-base.setEDDISpeechResponder");
}
}
}
catch (Exception e)
{
Log.Error($"Unhandled exception while handling changed variable '{name}'. ({e.Message})");
}
}
/*========================================\
| required VoiceAttack plugin shenanigans |
\========================================*/
@ -159,12 +208,20 @@ namespace alterNERDtive
{
VA = vaProxy;
Log.Notice("Initializing …");
vaProxy.BooleanVariableChanged += new Action<String, Boolean?, Boolean?, Guid?>((name, from, to, id) => { ConfigurationChanged(name, from, to, id); });
vaProxy.DateVariableChanged += new Action<String, DateTime?, DateTime?, Guid?>((name, from, to, id) => { ConfigurationChanged(name, from, to, id); });
vaProxy.DecimalVariableChanged += new Action<String, decimal?, decimal?, Guid?>((name, from, to, id) => { ConfigurationChanged(name, from, to, id); });
vaProxy.IntegerVariableChanged += new Action<String, int?, int?, Guid?>((name, from, to, id) => { ConfigurationChanged(name, from, to, id); });
vaProxy.TextVariableChanged += new Action<String, String, String, Guid?>((name, from, to, id) => { ConfigurationChanged(name, from, to, id); });
VA.SetBoolean("alterNERDtive-base.initialized", true);
Commands.TriggerEvent("alterNERDtive-base.initialized", wait: false);
Log.Notice("Init successful.");
}
public static void VA_Invoke1(dynamic vaProxy)
{
VA = vaProxy;
string context = vaProxy.Context.ToLower();
Log.Debug($"Running context '{context}' …");
try
@ -172,6 +229,12 @@ namespace alterNERDtive
switch (context)
{
// plugin methods
case "config.setup":
Context_Config_Setup(vaProxy);
break;
case "config.getvariables":
Context_Config_SetVariables(vaProxy);
break;
case "distancebetween":
Context_DistanceBetween(vaProxy);
break;

View file

@ -5,10 +5,219 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Pipes;
using System.Threading;
using System.Linq;
namespace alterNERDtive.util
{
public class Configuration
{
private readonly dynamic VA;
private readonly string ID;
private readonly VoiceAttackLog Log;
private static readonly Dictionary<string, OptDict<string, Option>> Defaults = new Dictionary<string, OptDict<string, Option>>
{
{
"alterNERDtive-base",
new OptDict<string, Option>{
{ new Option("eddi.quietMode", true, voiceTrigger: "eddi quiet mode", description: "Whether or not to make EDDI shut up.") },
{ new Option("keyPressDuration", (decimal)0.01, voiceTrigger: "key press duration", description: "The time keys will be held down for.") },
{ new Option("elite.pasteKey", "v", voiceTrigger: "elite paste key", description: "The key used to paste in conjunction with CTRL. The key that would be 'V' on QWERTY.") }
}
},
{
"EliteAttack",
new OptDict<string, Option>{
}
},
{
"RatAttack",
new OptDict<string, Option>{
}
},
{
"SpanshAttack",
new OptDict<string, Option>{
}
},
{
"StreamAttack",
new OptDict<string, Option>{
}
}
};
private class Option
{
public readonly string Name;
public readonly dynamic DefaultValue;
public readonly string VoiceTrigger;
public string TtsDescription { get => ttsDescription ?? VoiceTrigger; }
private readonly string? ttsDescription;
public string Description { get => description ?? "No description available."; }
public readonly string? description;
public Option(string name, dynamic defaultValue, string voiceTrigger, string? ttsDescription = null, string? description = null)
=> (Name, DefaultValue, VoiceTrigger, this.ttsDescription, this.description) = (name, defaultValue, voiceTrigger, ttsDescription, description);
public static implicit operator (string, Option)(Option o) => ( o.Name, o );
public static explicit operator bool(Option o) => o.DefaultValue;
public static explicit operator DateTime(Option o) => o.DefaultValue;
public static explicit operator decimal(Option o) => o.DefaultValue;
public static explicit operator int(Option o) => o.DefaultValue;
public static explicit operator short(Option o) => o.DefaultValue;
public static explicit operator string(Option o) => o.DefaultValue;
public override string ToString() => DefaultValue.ToString();
}
private class OptDict<TKey, TValue> : Dictionary<TKey, TValue>
{
public OptDict() : base() { }
public OptDict(int capacity) : base(capacity) { }
public void Add((TKey,TValue) tuple)
{
base.Add(tuple.Item1, tuple.Item2);
}
}
public Configuration(dynamic vaProxy, VoiceAttackLog log, string id) => (VA, Log, ID) = (vaProxy, log, id);
public dynamic GetDefault(string name)
{
return GetDefault(ID, name);
}
public static dynamic GetDefault(string id, string name)
{
return Defaults[id][name];
}
public void SetVoiceTriggers(System.Type type)
{
List<string> triggers = new List<string>();
foreach (Dictionary<string,Option> options in Defaults.Values)
{
foreach (Option option in options.Values)
{
if (option.DefaultValue.GetType() == type)
{
if (triggers.Contains(option.VoiceTrigger))
{
throw new ArgumentException($"Voice trigger '{option.VoiceTrigger}' is not unique, aborting …");
}
triggers.Add(option.VoiceTrigger);
}
}
}
if (triggers.Count > 0)
{
string triggerString = string.Join(";", triggers);
VA.SetText($"alterNERDtive-base.triggers.{type.Name}", triggerString);
Log.Debug($"Voice triggers for {type.Name}: '{triggerString}'");
}
else
{
// make sure we dont accidentally have weird things happening with empty config voice triggers
string triggerString = $"tenuiadafesslejüsljlejutlesuivle{type.Name}";
VA.SetText($"alterNERDtive-base.triggers.{type.Name}", triggerString);
Log.Debug($"No voice triggers found for {type.Name}");
}
}
public void SetVariablesForTrigger(dynamic vaProxy, string trigger)
{
_ = trigger ?? throw new ArgumentNullException("trigger");
foreach (KeyValuePair<string,OptDict<string,Option>> options in Defaults)
{
try
{
Option option = options.Value.First(item => item.Value.VoiceTrigger == trigger).Value;
vaProxy.SetText("~name", $"{options.Key}.{option.Name}");
vaProxy.SetText("~ttsDescription", option.TtsDescription);
vaProxy.SetText("~description", option.Description);
break;
}
catch (InvalidOperationException) { }
}
}
public bool HasDefault(string name)
{
return HasDefault(ID, name);
}
public static bool HasDefault(string id, string name)
{
return Defaults[id].ContainsKey(name);
}
public dynamic? ApplyDefault(string name)
{
return ApplyDefault(ID, name);
}
public dynamic? ApplyDefault(string id, string name)
{
if (!HasDefault(id, name))
{
Log.Warn($"No default configuration value found for '{id}.{name}'");
return null;
}
dynamic value = Defaults[id][name].DefaultValue;
Log.Debug($"Loading default configuration value, '{id}.{name}': '{value}' …");
string variable = $"{id}.{name}#";
if (value is bool)
{
VA.SetBoolean(variable, value);
}
else if (value is DateTime)
{
VA.SetDate(variable, value);
}
else if (value is decimal)
{
VA.SetDecimal(variable, value);
}
else if (value is int)
{
VA.SetInt(variable, value);
}
else if (value is short)
{
VA.SetSmallInt(variable, value);
}
else if (value is string)
{
VA.SetText(variable, value);
}
else
{
throw new InvalidDataException($"Invalid data type for option '{id}.{name}': '{value}'");
}
return value;
}
public void ApplyDefaults()
{
ApplyDefaults(ID);
}
public void ApplyAllDefaults()
{
foreach (string id in Defaults.Keys)
{
ApplyDefaults(id);
}
}
public void ApplyDefaults(string id)
{
foreach (string key in Defaults[id].Keys)
{
ApplyDefault(id, key);
}
}
}
public class PythonProxy
{
public static Process SetupPythonScript(string path, string arguments)
@ -78,7 +287,7 @@ namespace alterNERDtive.util
WaitForReturn: wait,
AsSubcommand: subcommand,
CompletedAction: callback,
PassedText: strings == null ? null : $"\"{String.Join<string>("\";\"", strings)}\"",
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),
@ -178,7 +387,7 @@ namespace alterNERDtive.util
public PipeServer<Thing> Run()
{
Log.Debug($"Starting “{PipeName}” pipe …");
Log.Debug($"Starting '{PipeName}' pipe …");
if (!Running)
{
Running = true;
@ -189,7 +398,7 @@ namespace alterNERDtive.util
public PipeServer<Thing> Stop()
{
Log.Debug($"Stopping “{PipeName}” pipe …");
Log.Debug($"Stopping '{PipeName}' pipe …");
if (Running)
{
Running = false;
@ -225,7 +434,7 @@ namespace alterNERDtive.util
}
catch (ObjectDisposedException)
{
Log.Info("Ratsignal pipe has been closed.");
Log.Debug($"'{PipeName}' pipe has been closed.");
}
catch (Exception e)
{