From 8677ff464d483fd79e41411883aea467fd428ff6 Mon Sep 17 00:00:00 2001 From: That_One_Nerd Date: Tue, 18 Apr 2023 16:03:01 -0400 Subject: [PATCH 01/12] Made the display system of the config data a gazillion times better. --- SrcMod/Shell/Modules/ConfigModule.cs | 124 ++++++++++++++------------- SrcMod/Shell/ObjectModels/Config.cs | 1 + 2 files changed, 66 insertions(+), 59 deletions(-) diff --git a/SrcMod/Shell/Modules/ConfigModule.cs b/SrcMod/Shell/Modules/ConfigModule.cs index 58e88a0..f9b617f 100644 --- a/SrcMod/Shell/Modules/ConfigModule.cs +++ b/SrcMod/Shell/Modules/ConfigModule.cs @@ -5,24 +5,20 @@ public static class ConfigModule { [Command("display")] [Command("list")] - public static void DisplayConfig(ConfigDisplayMode mode = ConfigDisplayMode.All) + public static void DisplayConfig(string display = "all") { - switch (mode) + switch (display.Trim().ToLower()) { - case ConfigDisplayMode.Raw: - DisplayConfigRaw(); - break; - - case ConfigDisplayMode.All: + case "all": DisplayConfigAll(); break; - case ConfigDisplayMode.GameDirectories: - DisplayConfigGameDirectories(); + case "raw": + DisplayConfigRaw(); break; - case ConfigDisplayMode.RunUnsafeCommands: - DisplayConfigUnsafeCommands(); + default: + DisplayConfigName(display); break; } } @@ -129,66 +125,76 @@ public static class ConfigModule private static void DisplayConfigAll() { - DisplayConfigGameDirectories(); - DisplayConfigUnsafeCommands(); + FieldInfo[] validFields = (from field in typeof(Config).GetFields() + let isPublic = field.IsPublic + let isStatic = field.IsStatic + where isPublic && !isStatic + select field).ToArray(); + + foreach (FieldInfo field in validFields) + DisplayConfigItem(field.GetValue(Config.LoadedConfig), name: field.Name); } - private static void DisplayConfigRaw() + private static void DisplayConfigItem(T item, int indents = 0, string name = "", bool newLine = true) { - // This is definitely a bit inefficient, but shouldn't be too much of an issue. + Write(new string(' ', indents * 4), newLine: false); + if (!string.IsNullOrWhiteSpace(name)) Write($"{name}: ", newLine: false); - MemoryStream ms = new(); - StreamWriter writer = new(ms, leaveOpen: true); - JsonTextWriter jsonWriter = new(writer); - - Serializer.Serialize(jsonWriter, Config.LoadedConfig); - - jsonWriter.Close(); - writer.Close(); - ms.Position = 0; - - StreamReader reader = new(ms); - string msg = reader.ReadToEnd(); - - Write(msg); - - reader.Close(); - ms.Close(); - } - private static void DisplayConfigGameDirectories() - { - Write("Steam Game Directories: ", null, false); - if (Config.LoadedConfig.GameDirectories is null || Config.LoadedConfig.GameDirectories.Length <= 0) - Write("None", ConsoleColor.DarkGray); - else + if (item is Array itemArray) { - Write("[", ConsoleColor.DarkGray); - for (int i = 0; i < Config.LoadedConfig.GameDirectories.Length; i++) + if (itemArray.Length < 1) { - Write(" \"", ConsoleColor.DarkGray, false); - Write(Config.LoadedConfig.GameDirectories[i], ConsoleColor.White, false); - if (i < Config.LoadedConfig.GameDirectories.Length - 1) Write("\",", ConsoleColor.DarkGray); - else Write("\"", ConsoleColor.DarkGray); + Write("[]", ConsoleColor.DarkGray, newLine); + return; } - Write("]", ConsoleColor.DarkGray); + + Write("[", ConsoleColor.DarkGray); + for (int i = 0; i < itemArray.Length; i++) + { + DisplayConfigItem(itemArray.GetValue(i), indents + 1, newLine: false); + if (i < itemArray.Length - 1) Write(',', newLine: false); + Write('\n', newLine: false); + } + Write(new string(' ', indents * 4) + "]", ConsoleColor.DarkGray, newLine); } - } - private static void DisplayConfigUnsafeCommands() - { - Write("Run Unsafe Commands: ", null, false); - ConsoleColor color = Config.LoadedConfig.RunUnsafeCommands switch + else if (item is byte itemByte) Write($"0x{itemByte:X2}", ConsoleColor.Yellow, newLine); + else if (item is sbyte or short or ushort or int or uint or long or ulong) + Write(item, ConsoleColor.Yellow, newLine); + else if (item is char) + { + Write("\'", ConsoleColor.DarkGray, false); + Write(item, ConsoleColor.Blue, false); + Write("\'", ConsoleColor.DarkGray, newLine); + } + else if (item is string) + { + Write("\"", ConsoleColor.DarkGray, false); + Write(item, ConsoleColor.DarkCyan, false); + Write("\"", ConsoleColor.DarkGray, newLine); + } + else if (item is AskMode) Write(item, item switch { AskMode.Never => ConsoleColor.Red, AskMode.Always => ConsoleColor.Green, AskMode.Ask or _ => ConsoleColor.DarkGray - }; - Write(Config.LoadedConfig.RunUnsafeCommands, color); + }, newLine); + else Write(item, newLine: newLine); } - - public enum ConfigDisplayMode + private static void DisplayConfigName(string name) { - Raw, - All, - GameDirectories, - RunUnsafeCommands + FieldInfo[] validFields = (from field in typeof(Config).GetFields() + let isPublic = field.IsPublic + let isStatic = field.IsStatic + where isPublic && !isStatic + select field).ToArray(); + + FieldInfo? chosenField = validFields.FirstOrDefault(x => x.Name.Trim().ToLower() == name.Trim().ToLower()); + if (chosenField is null) throw new($"No config variable named \"{name}\"."); + + DisplayConfigItem(chosenField.GetValue(Config.LoadedConfig), name: chosenField.Name); + } + private static void DisplayConfigRaw() + { + string json = JsonConvert.SerializeObject(Config.LoadedConfig, Formatting.Indented); + Write(json); } } diff --git a/SrcMod/Shell/ObjectModels/Config.cs b/SrcMod/Shell/ObjectModels/Config.cs index a14e6df..f66f5e7 100644 --- a/SrcMod/Shell/ObjectModels/Config.cs +++ b/SrcMod/Shell/ObjectModels/Config.cs @@ -79,6 +79,7 @@ public struct Config StreamWriter writer = new(fullPath); JsonTextWriter jsonWriter = new(writer); + jsonWriter.Indentation = 4; Serializer.Serialize(jsonWriter, p_changes); jsonWriter.Close(); writer.Close(); From 318c8119a0eaa3cf554f7da31f7f3e477fbb6a70 Mon Sep 17 00:00:00 2001 From: That_One_Nerd Date: Wed, 19 Apr 2023 07:21:59 -0400 Subject: [PATCH 02/12] Small change in the config display system. --- SrcMod/Shell/Modules/ConfigModule.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/SrcMod/Shell/Modules/ConfigModule.cs b/SrcMod/Shell/Modules/ConfigModule.cs index f9b617f..4835833 100644 --- a/SrcMod/Shell/Modules/ConfigModule.cs +++ b/SrcMod/Shell/Modules/ConfigModule.cs @@ -157,8 +157,9 @@ public static class ConfigModule Write(new string(' ', indents * 4) + "]", ConsoleColor.DarkGray, newLine); } else if (item is byte itemByte) Write($"0x{itemByte:X2}", ConsoleColor.Yellow, newLine); - else if (item is sbyte or short or ushort or int or uint or long or ulong) + else if (item is sbyte or short or ushort or int or uint or long or ulong or float or double or decimal) Write(item, ConsoleColor.Yellow, newLine); + else if (item is bool itemBool) Write(item, itemBool ? ConsoleColor.Green : ConsoleColor.Red, newLine); else if (item is char) { Write("\'", ConsoleColor.DarkGray, false); From 647993ef1ff9a09d77422bdee55727479cafa68b Mon Sep 17 00:00:00 2001 From: That_One_Nerd Date: Wed, 19 Apr 2023 07:28:40 -0400 Subject: [PATCH 03/12] One teeny tiny more thing. --- SrcMod/Shell/GlobalUsings.cs | 1 + SrcMod/Shell/Modules/ConfigModule.cs | 22 ++++++++++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/SrcMod/Shell/GlobalUsings.cs b/SrcMod/Shell/GlobalUsings.cs index 8ae847e..d5d9a6f 100644 --- a/SrcMod/Shell/GlobalUsings.cs +++ b/SrcMod/Shell/GlobalUsings.cs @@ -7,6 +7,7 @@ global using SrcMod.Shell.Interop; global using SrcMod.Shell.Modules.ObjectModels; global using SrcMod.Shell.ObjectModels; global using System; +global using System.Collections; global using System.Collections.Generic; global using System.ComponentModel; global using System.Diagnostics; diff --git a/SrcMod/Shell/Modules/ConfigModule.cs b/SrcMod/Shell/Modules/ConfigModule.cs index 4835833..98608c7 100644 --- a/SrcMod/Shell/Modules/ConfigModule.cs +++ b/SrcMod/Shell/Modules/ConfigModule.cs @@ -1,4 +1,6 @@ -namespace SrcMod.Shell.Modules; +using SharpCompress; + +namespace SrcMod.Shell.Modules; [Module("config")] public static class ConfigModule @@ -139,19 +141,27 @@ public static class ConfigModule Write(new string(' ', indents * 4), newLine: false); if (!string.IsNullOrWhiteSpace(name)) Write($"{name}: ", newLine: false); - if (item is Array itemArray) + if (item is IEnumerable itemEnumerable) { - if (itemArray.Length < 1) + // This is a bit inefficient. + int count = 0; + foreach (object _ in itemEnumerable) count++; + + object[] itemData = new object[count]; + count = 0; + foreach (object obj in itemEnumerable) itemData[count] = obj; + + if (itemData.Length < 1) { Write("[]", ConsoleColor.DarkGray, newLine); return; } Write("[", ConsoleColor.DarkGray); - for (int i = 0; i < itemArray.Length; i++) + for (int i = 0; i < itemData.Length; i++) { - DisplayConfigItem(itemArray.GetValue(i), indents + 1, newLine: false); - if (i < itemArray.Length - 1) Write(',', newLine: false); + DisplayConfigItem(itemData.GetValue(i), indents + 1, newLine: false); + if (i < itemData.Length - 1) Write(',', newLine: false); Write('\n', newLine: false); } Write(new string(' ', indents * 4) + "]", ConsoleColor.DarkGray, newLine); From dd62e66871cab8c55670cba34b2c7b2d27f91c5a Mon Sep 17 00:00:00 2001 From: That_One_Nerd Date: Wed, 19 Apr 2023 14:02:23 -0400 Subject: [PATCH 04/12] Added automated config setting. Next up resetting. --- SrcMod/Shell/Modules/ConfigModule.cs | 38 +++++++++------------- SrcMod/Shell/ObjectModels/Config.cs | 30 ++++++++++++----- SrcMod/Shell/ObjectModels/ConfigChanges.cs | 2 +- 3 files changed, 37 insertions(+), 33 deletions(-) diff --git a/SrcMod/Shell/Modules/ConfigModule.cs b/SrcMod/Shell/Modules/ConfigModule.cs index 98608c7..ffc5ca0 100644 --- a/SrcMod/Shell/Modules/ConfigModule.cs +++ b/SrcMod/Shell/Modules/ConfigModule.cs @@ -97,32 +97,24 @@ public static class ConfigModule [Command("set")] public static void SetConfigVariable(string name, string value) { - Config config = Config.LoadedConfig; + FieldInfo[] validFields = (from field in typeof(Config).GetFields() + let isPublic = field.IsPublic + let isStatic = field.IsStatic + where isPublic && !isStatic + select field).ToArray(); - switch (name.Trim().ToLower()) - { - case "gamedirectories": - throw new($"The config variable \"{name}\" is a list and must be added or removed to."); + FieldInfo? chosenField = validFields.FirstOrDefault(x => x.Name.Trim().ToLower() == name.Trim().ToLower()); + if (chosenField is null) throw new($"No valid config variable named \"{name}\"."); + else if (chosenField.FieldType.IsArray) throw new($"The variable \"{name}\" is an array and cannot be" + + " directly set. Instead, add or remove items from it."); - case "rununsafecommands": - if (int.TryParse(value, out int intRes)) - { - AskMode mode = (AskMode)intRes; - if (!Enum.IsDefined(mode)) throw new($"(AskMode){value} is not a valid AskMode."); - config.RunUnsafeCommands = mode; - } - else if (Enum.TryParse(value, true, out AskMode modeRes)) - { - if (!Enum.IsDefined(modeRes)) throw new($"\"{value}\" is not a valid AskMode."); - config.RunUnsafeCommands = modeRes; - } - else throw new($"\"{value}\" is not a valid AskMode."); - break; + object parsed = TypeParsers.ParseAll(value); + if (parsed is string parsedStr + && chosenField.FieldType.IsEnum + && Enum.TryParse(chosenField.FieldType, parsedStr, true, out object? obj)) parsed = obj; - default: throw new($"Unknown config variable \"{name}\""); - } - - Config.LoadedConfig = config; + chosenField.SetValue(Config.LoadedConfig, parsed); + DisplayConfigItem(chosenField.GetValue(Config.LoadedConfig), name: chosenField.Name); } private static void DisplayConfigAll() diff --git a/SrcMod/Shell/ObjectModels/Config.cs b/SrcMod/Shell/ObjectModels/Config.cs index f66f5e7..0c272f8 100644 --- a/SrcMod/Shell/ObjectModels/Config.cs +++ b/SrcMod/Shell/ObjectModels/Config.cs @@ -1,6 +1,6 @@ namespace SrcMod.Shell.ObjectModels; -public struct Config +public class Config { public const string FilePath = "config.json"; @@ -26,16 +26,26 @@ public struct Config GameDirectories = Array.Empty(), RunUnsafeCommands = AskMode.Ask }; + p_applied = Defaults; } public string[] GameDirectories; public AskMode RunUnsafeCommands; - public Config ApplyChanges(ConfigChanges changes) => this with + internal Config() { - GameDirectories = GameDirectories.Union(changes.GameDirectories ?? Array.Empty()).ToArray(), - RunUnsafeCommands = changes.RunUnsafeCommands ?? RunUnsafeCommands - }; + GameDirectories = Array.Empty(); + } + + public Config ApplyChanges(ConfigChanges changes) + { + if (changes.GameDirectories is not null) + GameDirectories = GameDirectories.Union(changes.GameDirectories).ToArray(); + + if (changes.RunUnsafeCommands is not null) RunUnsafeCommands = changes.RunUnsafeCommands.Value; + + return this; + } public ConfigChanges GetChanges(Config? baseConfig = null) { Config reference = baseConfig ?? Defaults; @@ -65,21 +75,23 @@ public struct Config jsonReader.Close(); reader.Close(); - p_applied = p_changes is null ? Defaults : Defaults.ApplyChanges(p_changes.Value); + p_applied = p_changes is null ? Defaults : Defaults.ApplyChanges(p_changes); } public static void SaveConfig(string basePath) { string fullPath = Path.Combine(basePath, FilePath); - if (p_changes is null || !p_changes.Value.HasChange) + if (p_changes is null || !p_changes.HasChange) { if (File.Exists(fullPath)) File.Delete(fullPath); return; } StreamWriter writer = new(fullPath); - JsonTextWriter jsonWriter = new(writer); - jsonWriter.Indentation = 4; + JsonTextWriter jsonWriter = new(writer) + { + Indentation = 4 + }; Serializer.Serialize(jsonWriter, p_changes); jsonWriter.Close(); writer.Close(); diff --git a/SrcMod/Shell/ObjectModels/ConfigChanges.cs b/SrcMod/Shell/ObjectModels/ConfigChanges.cs index 8328796..e527d5f 100644 --- a/SrcMod/Shell/ObjectModels/ConfigChanges.cs +++ b/SrcMod/Shell/ObjectModels/ConfigChanges.cs @@ -1,6 +1,6 @@ namespace SrcMod.Shell.ObjectModels; -public record struct ConfigChanges +public record class ConfigChanges { [JsonIgnore] public bool HasChange => GameDirectories is not null || RunUnsafeCommands is not null; From b5517e724ae8ce9b76efb5f99efd25b3f4dab674 Mon Sep 17 00:00:00 2001 From: That_One_Nerd Date: Wed, 19 Apr 2023 19:28:30 -0400 Subject: [PATCH 05/12] Added automatic config resetting capabilities. --- SrcMod/Shell/Modules/ConfigModule.cs | 34 ++++++++++++++++------------ SrcMod/Shell/ObjectModels/Config.cs | 6 +++-- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/SrcMod/Shell/Modules/ConfigModule.cs b/SrcMod/Shell/Modules/ConfigModule.cs index ffc5ca0..0977a3e 100644 --- a/SrcMod/Shell/Modules/ConfigModule.cs +++ b/SrcMod/Shell/Modules/ConfigModule.cs @@ -72,26 +72,17 @@ public static class ConfigModule [Command("reset")] public static void ResetConfig(string name = "all") { - Config config = Config.LoadedConfig; - switch (name.Trim().ToLower()) { - case "gamedirectories": - config.GameDirectories = Config.Defaults.GameDirectories; - break; - - case "rununsafecommands": - config.RunUnsafeCommands = Config.Defaults.RunUnsafeCommands; - break; - case "all": - config = Config.Defaults; + Config.LoadedConfig = Config.Defaults; + DisplayConfig("all"); break; - default: throw new($"Unknown config variable \"{name}\""); + default: + ResetConfigVar(name); + break; } - - Config.LoadedConfig = config; } [Command("set")] @@ -200,4 +191,19 @@ public static class ConfigModule string json = JsonConvert.SerializeObject(Config.LoadedConfig, Formatting.Indented); Write(json); } + + private static void ResetConfigVar(string name) + { + FieldInfo[] validFields = (from field in typeof(Config).GetFields() + let isPublic = field.IsPublic + let isStatic = field.IsStatic + where isPublic && !isStatic + select field).ToArray(); + + FieldInfo? chosenField = validFields.FirstOrDefault(x => x.Name.Trim().ToLower() == name.Trim().ToLower()); + if (chosenField is null) throw new($"No valid config variable named \"{name}\"."); + + chosenField.SetValue(Config.LoadedConfig, chosenField.GetValue(Config.Defaults)); + DisplayConfigItem(chosenField.GetValue(Config.LoadedConfig), name: chosenField.Name); + } } diff --git a/SrcMod/Shell/ObjectModels/Config.cs b/SrcMod/Shell/ObjectModels/Config.cs index 0c272f8..e3cba13 100644 --- a/SrcMod/Shell/ObjectModels/Config.cs +++ b/SrcMod/Shell/ObjectModels/Config.cs @@ -4,7 +4,7 @@ public class Config { public const string FilePath = "config.json"; - public static readonly Config Defaults; + public static Config Defaults => (Config)p_defaults.MemberwiseClone(); public static Config LoadedConfig { @@ -19,9 +19,11 @@ public class Config private static Config p_applied; private static ConfigChanges? p_changes; + private static readonly Config p_defaults; + static Config() { - Defaults = new() + p_defaults = new() { GameDirectories = Array.Empty(), RunUnsafeCommands = AskMode.Ask From 2420e16fa40f61bd19c84cc2453fa8af1802419a Mon Sep 17 00:00:00 2001 From: That_One_Nerd Date: Wed, 19 Apr 2023 20:37:54 -0400 Subject: [PATCH 06/12] Made the config append system fully automated. --- .../Shell/Extensions/ConversionExtension.cs | 20 +++++++ SrcMod/Shell/GlobalUsings.cs | 1 + SrcMod/Shell/Modules/ConfigModule.cs | 55 ++++++++++--------- SrcMod/Shell/Shell.csproj | 4 ++ 4 files changed, 54 insertions(+), 26 deletions(-) create mode 100644 SrcMod/Shell/Extensions/ConversionExtension.cs diff --git a/SrcMod/Shell/Extensions/ConversionExtension.cs b/SrcMod/Shell/Extensions/ConversionExtension.cs new file mode 100644 index 0000000..599c6ea --- /dev/null +++ b/SrcMod/Shell/Extensions/ConversionExtension.cs @@ -0,0 +1,20 @@ +namespace SrcMod.Shell.Extensions; + +public static class ConversionExtension +{ + public static T Cast(this object obj) => (T)Cast(obj, typeof(T)); + public static object Cast(this object obj, Type newType) => Convert.ChangeType(obj, newType); + + public static object CastArray(this object[] obj, Type newElementType) + { + Array result = Array.CreateInstance(newElementType, obj.Length); + for (int i = 0; i < obj.Length; i++) result.SetValue(obj[i].Cast(newElementType), i); + return result; + } + public static T[] CastArray(this object[] obj) + { + Array result = Array.CreateInstance(typeof(T), obj.Length); + for (int i = 0; i < obj.Length; i++) result.SetValue(obj[i].Cast(), i); + return (T[])result; + } +} diff --git a/SrcMod/Shell/GlobalUsings.cs b/SrcMod/Shell/GlobalUsings.cs index d5d9a6f..19bbcbb 100644 --- a/SrcMod/Shell/GlobalUsings.cs +++ b/SrcMod/Shell/GlobalUsings.cs @@ -3,6 +3,7 @@ global using Newtonsoft.Json; global using SharpCompress.Archives.Rar; global using SharpCompress.Archives.SevenZip; global using SharpCompress.Readers; +global using SrcMod.Shell.Extensions; global using SrcMod.Shell.Interop; global using SrcMod.Shell.Modules.ObjectModels; global using SrcMod.Shell.ObjectModels; diff --git a/SrcMod/Shell/Modules/ConfigModule.cs b/SrcMod/Shell/Modules/ConfigModule.cs index 0977a3e..cea4cba 100644 --- a/SrcMod/Shell/Modules/ConfigModule.cs +++ b/SrcMod/Shell/Modules/ConfigModule.cs @@ -29,21 +29,30 @@ public static class ConfigModule [Command("append")] public static void AppendConfigVariable(string name, string value) { - Config config = Config.LoadedConfig; + FieldInfo[] validFields = (from field in typeof(Config).GetFields() + let isPublic = field.IsPublic + let isStatic = field.IsStatic + where isPublic && !isStatic + select field).ToArray(); - switch (name.Trim().ToLower()) - { - case "gamedirectories": - config.GameDirectories = config.GameDirectories.Append(value).ToArray(); - break; + FieldInfo? chosenField = validFields.FirstOrDefault(x => x.Name.Trim().ToLower() == name.Trim().ToLower()); + if (chosenField is null) throw new($"No valid config variable named \"{name}\"."); + else if (!chosenField.FieldType.IsArray) throw new($"The variable \"{chosenField.Name}\" is not an array" + + " and cannot have data added or removed from it." + + " Instead, set or reset the variable."); - case "rununsafecommands": - throw new($"The config variable \"{name}\" is a single variable and cannot be appended to."); + object parsed = TypeParsers.ParseAll(value); + if (parsed is string parsedStr + && chosenField.FieldType.IsEnum + && Enum.TryParse(chosenField.FieldType, parsedStr, true, out object? obj)) parsed = obj; - default: throw new($"Unknown config variable \"{name}\""); - } + Type arrayType = chosenField.FieldType.GetElementType()!; - Config.LoadedConfig = config; + Array arrayValue = (Array)chosenField.GetValue(Config.LoadedConfig)!; + ArrayList collection = new(arrayValue) { parsed }; + + chosenField.SetValue(Config.LoadedConfig, collection.ToArray()!.CastArray(arrayType)); + DisplayConfigItem(chosenField.GetValue(Config.LoadedConfig), name: chosenField.Name); } [Command("delete")] @@ -96,8 +105,9 @@ public static class ConfigModule FieldInfo? chosenField = validFields.FirstOrDefault(x => x.Name.Trim().ToLower() == name.Trim().ToLower()); if (chosenField is null) throw new($"No valid config variable named \"{name}\"."); - else if (chosenField.FieldType.IsArray) throw new($"The variable \"{name}\" is an array and cannot be" + - " directly set. Instead, add or remove items from it."); + else if (chosenField.FieldType.IsArray) throw new($"The variable \"{chosenField.Name}\" is an array and" + + " cannot be directly set. Instead, add or remove items" + + " from it."); object parsed = TypeParsers.ParseAll(value); if (parsed is string parsedStr @@ -124,27 +134,20 @@ public static class ConfigModule Write(new string(' ', indents * 4), newLine: false); if (!string.IsNullOrWhiteSpace(name)) Write($"{name}: ", newLine: false); - if (item is IEnumerable itemEnumerable) + if (item is null) Write("null", ConsoleColor.DarkRed, newLine); + else if (item is Array itemArray) { - // This is a bit inefficient. - int count = 0; - foreach (object _ in itemEnumerable) count++; - - object[] itemData = new object[count]; - count = 0; - foreach (object obj in itemEnumerable) itemData[count] = obj; - - if (itemData.Length < 1) + if (itemArray.Length < 1) { Write("[]", ConsoleColor.DarkGray, newLine); return; } Write("[", ConsoleColor.DarkGray); - for (int i = 0; i < itemData.Length; i++) + for (int i = 0; i < itemArray.Length; i++) { - DisplayConfigItem(itemData.GetValue(i), indents + 1, newLine: false); - if (i < itemData.Length - 1) Write(',', newLine: false); + DisplayConfigItem(itemArray.GetValue(i), indents + 1, newLine: false); + if (i < itemArray.Length - 1) Write(',', newLine: false); Write('\n', newLine: false); } Write(new string(' ', indents * 4) + "]", ConsoleColor.DarkGray, newLine); diff --git a/SrcMod/Shell/Shell.csproj b/SrcMod/Shell/Shell.csproj index 253835f..804ac4f 100644 --- a/SrcMod/Shell/Shell.csproj +++ b/SrcMod/Shell/Shell.csproj @@ -36,4 +36,8 @@ + + + + From b446feefe759b74ab60597b67095ca55d845ce4a Mon Sep 17 00:00:00 2001 From: That_One_Nerd Date: Thu, 20 Apr 2023 07:27:38 -0400 Subject: [PATCH 07/12] Made the config remove system automated. --- SrcMod/Shell/Modules/ConfigModule.cs | 35 +++++++++++++++++----------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/SrcMod/Shell/Modules/ConfigModule.cs b/SrcMod/Shell/Modules/ConfigModule.cs index cea4cba..25838ad 100644 --- a/SrcMod/Shell/Modules/ConfigModule.cs +++ b/SrcMod/Shell/Modules/ConfigModule.cs @@ -59,23 +59,32 @@ public static class ConfigModule [Command("remove")] public static void RemoveConfigVariable(string name, string value) { - Config config = Config.LoadedConfig; + FieldInfo[] validFields = (from field in typeof(Config).GetFields() + let isPublic = field.IsPublic + let isStatic = field.IsStatic + where isPublic && !isStatic + select field).ToArray(); - switch (name.Trim().ToLower()) - { - case "gamedirectories": - config.GameDirectories = config.GameDirectories - .Where(x => x.Trim().ToLower() != value.Trim().ToLower()) - .ToArray(); - break; + FieldInfo? chosenField = validFields.FirstOrDefault(x => x.Name.Trim().ToLower() == name.Trim().ToLower()); + if (chosenField is null) throw new($"No valid config variable named \"{name}\"."); + else if (!chosenField.FieldType.IsArray) throw new($"The variable \"{chosenField.Name}\" is not an array" + + " and cannot have data added or removed from it." + + " Instead, set or reset the variable."); - case "rununsafecommands": - throw new($"The config variable \"{name}\" is a single variable and cannot be appended to."); + object parsed = TypeParsers.ParseAll(value); + if (parsed is string parsedStr + && chosenField.FieldType.IsEnum + && Enum.TryParse(chosenField.FieldType, parsedStr, true, out object? obj)) parsed = obj; - default: throw new($"Unknown config variable \"{name}\""); - } + Type arrayType = chosenField.FieldType.GetElementType()!; - Config.LoadedConfig = config; + Array arrayValue = (Array)chosenField.GetValue(Config.LoadedConfig)!; + ArrayList collection = new(arrayValue); + if (!collection.Contains(parsed)) throw new($"The value \"{value}\" is not contained in this variable."); + collection.Remove(parsed); + + chosenField.SetValue(Config.LoadedConfig, collection.ToArray()!.CastArray(arrayType)); + DisplayConfigItem(chosenField.GetValue(Config.LoadedConfig), name: chosenField.Name); } [Command("reset")] From 35885b8724d3ea5c4daa8350b6de6080c853dcbe Mon Sep 17 00:00:00 2001 From: That_One_Nerd Date: Thu, 20 Apr 2023 08:20:18 -0400 Subject: [PATCH 08/12] Fixed the config not saving to its file. --- SrcMod/Shell/Modules/ConfigModule.cs | 4 +++ SrcMod/Shell/ObjectModels/Config.cs | 37 +++++++++++++--------- SrcMod/Shell/ObjectModels/ConfigChanges.cs | 10 ------ 3 files changed, 26 insertions(+), 25 deletions(-) delete mode 100644 SrcMod/Shell/ObjectModels/ConfigChanges.cs diff --git a/SrcMod/Shell/Modules/ConfigModule.cs b/SrcMod/Shell/Modules/ConfigModule.cs index 25838ad..27bb4bc 100644 --- a/SrcMod/Shell/Modules/ConfigModule.cs +++ b/SrcMod/Shell/Modules/ConfigModule.cs @@ -52,6 +52,7 @@ public static class ConfigModule ArrayList collection = new(arrayValue) { parsed }; chosenField.SetValue(Config.LoadedConfig, collection.ToArray()!.CastArray(arrayType)); + Config.UpdateChanges(); DisplayConfigItem(chosenField.GetValue(Config.LoadedConfig), name: chosenField.Name); } @@ -84,6 +85,7 @@ public static class ConfigModule collection.Remove(parsed); chosenField.SetValue(Config.LoadedConfig, collection.ToArray()!.CastArray(arrayType)); + Config.UpdateChanges(); DisplayConfigItem(chosenField.GetValue(Config.LoadedConfig), name: chosenField.Name); } @@ -124,6 +126,7 @@ public static class ConfigModule && Enum.TryParse(chosenField.FieldType, parsedStr, true, out object? obj)) parsed = obj; chosenField.SetValue(Config.LoadedConfig, parsed); + Config.UpdateChanges(); DisplayConfigItem(chosenField.GetValue(Config.LoadedConfig), name: chosenField.Name); } @@ -216,6 +219,7 @@ public static class ConfigModule if (chosenField is null) throw new($"No valid config variable named \"{name}\"."); chosenField.SetValue(Config.LoadedConfig, chosenField.GetValue(Config.Defaults)); + Config.UpdateChanges(); DisplayConfigItem(chosenField.GetValue(Config.LoadedConfig), name: chosenField.Name); } } diff --git a/SrcMod/Shell/ObjectModels/Config.cs b/SrcMod/Shell/ObjectModels/Config.cs index e3cba13..60d1e90 100644 --- a/SrcMod/Shell/ObjectModels/Config.cs +++ b/SrcMod/Shell/ObjectModels/Config.cs @@ -4,7 +4,7 @@ public class Config { public const string FilePath = "config.json"; - public static Config Defaults => (Config)p_defaults.MemberwiseClone(); + public static Config Defaults => new(); public static Config LoadedConfig { @@ -12,22 +12,15 @@ public class Config set { p_applied = value; - p_changes = p_applied.GetChanges(Defaults); + UpdateChanges(); } } private static Config p_applied; - private static ConfigChanges? p_changes; - - private static readonly Config p_defaults; + private static Changes? p_changes; static Config() { - p_defaults = new() - { - GameDirectories = Array.Empty(), - RunUnsafeCommands = AskMode.Ask - }; p_applied = Defaults; } @@ -37,9 +30,10 @@ public class Config internal Config() { GameDirectories = Array.Empty(); + RunUnsafeCommands = AskMode.Ask; } - public Config ApplyChanges(ConfigChanges changes) + public Config ApplyChanges(Changes changes) { if (changes.GameDirectories is not null) GameDirectories = GameDirectories.Union(changes.GameDirectories).ToArray(); @@ -48,10 +42,10 @@ public class Config return this; } - public ConfigChanges GetChanges(Config? baseConfig = null) + public Changes GetChanges(Config? baseConfig = null) { Config reference = baseConfig ?? Defaults; - ConfigChanges changes = new() + Changes changes = new() { GameDirectories = reference.GameDirectories == GameDirectories ? null : GameDirectories.Where(x => !reference.GameDirectories.Contains(x)).ToArray(), @@ -73,7 +67,7 @@ public class Config } StreamReader reader = new(fullPath); JsonTextReader jsonReader = new(reader); - p_changes = Serializer.Deserialize(jsonReader); + p_changes = Serializer.Deserialize(jsonReader); jsonReader.Close(); reader.Close(); @@ -83,7 +77,7 @@ public class Config { string fullPath = Path.Combine(basePath, FilePath); - if (p_changes is null || !p_changes.HasChange) + if (p_changes is null || !p_changes.Any()) { if (File.Exists(fullPath)) File.Delete(fullPath); return; @@ -98,4 +92,17 @@ public class Config jsonWriter.Close(); writer.Close(); } + + public static void UpdateChanges() + { + p_changes = p_applied.GetChanges(Defaults); + } + + public class Changes + { + public string[]? GameDirectories; + public AskMode? RunUnsafeCommands; + + public bool Any() => typeof(Changes).GetFields().Any(x => x.GetValue(this) is not null); + } } diff --git a/SrcMod/Shell/ObjectModels/ConfigChanges.cs b/SrcMod/Shell/ObjectModels/ConfigChanges.cs deleted file mode 100644 index e527d5f..0000000 --- a/SrcMod/Shell/ObjectModels/ConfigChanges.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace SrcMod.Shell.ObjectModels; - -public record class ConfigChanges -{ - [JsonIgnore] - public bool HasChange => GameDirectories is not null || RunUnsafeCommands is not null; - - public string[]? GameDirectories; - public AskMode? RunUnsafeCommands; -} From d8df50a928ac94e4929aaf6bb7fa4dda5aa46552 Mon Sep 17 00:00:00 2001 From: That-One-Nerd Date: Mon, 24 Apr 2023 17:33:51 -0400 Subject: [PATCH 09/12] Tiny change. --- SrcMod/Shell/LoadingBar.cs | 2 +- SrcMod/Shell/Tools.cs | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/SrcMod/Shell/LoadingBar.cs b/SrcMod/Shell/LoadingBar.cs index f658c8e..b4102e8 100644 --- a/SrcMod/Shell/LoadingBar.cs +++ b/SrcMod/Shell/LoadingBar.cs @@ -1,6 +1,6 @@ namespace SrcMod.Shell; -internal static class LoadingBar +public static class LoadingBar { public static int position = -1; public static int bufferSize = 0; diff --git a/SrcMod/Shell/Tools.cs b/SrcMod/Shell/Tools.cs index ef6cc12..34c63b5 100644 --- a/SrcMod/Shell/Tools.cs +++ b/SrcMod/Shell/Tools.cs @@ -1,8 +1,6 @@ -using System.Text; +namespace SrcMod.Shell; -namespace SrcMod.Shell; - -internal static class Tools +public static class Tools { public static void DisplayWithPages(IEnumerable lines, ConsoleColor? color = null) { From 2dc37cd6accbda656fd2829431a931bd6f62d52b Mon Sep 17 00:00:00 2001 From: That_One_Nerd Date: Tue, 25 Apr 2023 17:19:07 -0400 Subject: [PATCH 10/12] Debug mode gives better errors now (tiny patch) --- SrcMod/Shell/Shell.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/SrcMod/Shell/Shell.cs b/SrcMod/Shell/Shell.cs index 58cd52e..161cfa0 100644 --- a/SrcMod/Shell/Shell.cs +++ b/SrcMod/Shell/Shell.cs @@ -237,24 +237,27 @@ public class Shell void runCommand(object? sender, DoWorkEventArgs e) { -#if RELEASE try { -#endif command.Invoke(args); -#if RELEASE } +#if RELEASE catch (TargetInvocationException ex) { Write($"[ERROR] {ex.InnerException!.Message}", ConsoleColor.Red); if (LoadingBar.Enabled) LoadingBar.End(); } +#endif catch (Exception ex) { +#if RELEASE Write($"[ERROR] {ex.Message}", ConsoleColor.Red); if (LoadingBar.Enabled) LoadingBar.End(); - } +#else + Write($"[ERROR] {ex}", ConsoleColor.Red); + if (LoadingBar.Enabled) LoadingBar.End(); #endif + } } activeCommand = new(); From 2e61bcfbec911f0aa69f375dd00407e2fbf98d87 Mon Sep 17 00:00:00 2001 From: That_One_Nerd Date: Tue, 25 Apr 2023 17:27:14 -0400 Subject: [PATCH 11/12] Automated as much of the config system as possible. --- .../Shell/Extensions/ConversionExtension.cs | 13 +++ SrcMod/Shell/ObjectModels/Config.cs | 85 ++++++++++++++++--- SrcMod/Shell/Shell.csproj | 4 - 3 files changed, 88 insertions(+), 14 deletions(-) diff --git a/SrcMod/Shell/Extensions/ConversionExtension.cs b/SrcMod/Shell/Extensions/ConversionExtension.cs index 599c6ea..72a8e95 100644 --- a/SrcMod/Shell/Extensions/ConversionExtension.cs +++ b/SrcMod/Shell/Extensions/ConversionExtension.cs @@ -17,4 +17,17 @@ public static class ConversionExtension for (int i = 0; i < obj.Length; i++) result.SetValue(obj[i].Cast(), i); return (T[])result; } + + public static object CastArray(this Array obj, Type newElementType) + { + Array result = Array.CreateInstance(newElementType, obj.Length); + for (int i = 0; i < obj.Length; i++) result.SetValue(obj.GetValue(i)!.Cast(newElementType), i); + return result; + } + public static T[] CastArray(this Array obj) + { + Array result = Array.CreateInstance(typeof(T), obj.Length); + for (int i = 0; i < obj.Length; i++) result.SetValue(obj.GetValue(i)!.Cast(), i); + return (T[])result; + } } diff --git a/SrcMod/Shell/ObjectModels/Config.cs b/SrcMod/Shell/ObjectModels/Config.cs index 60d1e90..197b7e6 100644 --- a/SrcMod/Shell/ObjectModels/Config.cs +++ b/SrcMod/Shell/ObjectModels/Config.cs @@ -6,6 +6,9 @@ public class Config public static Config Defaults => new(); + private static readonly FieldInfo[] p_configSharedFields; + private static readonly FieldInfo[] p_changeSharedFields; + public static Config LoadedConfig { get => p_applied; @@ -22,6 +25,39 @@ public class Config static Config() { p_applied = Defaults; + + FieldInfo[] configFields = (from field in typeof(Config).GetFields() + let isPublic = field.IsPublic + let isStatic = field.IsStatic + where isPublic && !isStatic + select field).ToArray(), + changeFields = (from field in typeof(Changes).GetFields() + let isPublic = field.IsPublic + let isStatic = field.IsStatic + where isPublic && !isStatic + select field).ToArray(); + + List sharedConfigFields = new(), + sharedChangeFields = new(); + foreach (FieldInfo field in configFields) + { + FieldInfo? changeEquivalent = changeFields.FirstOrDefault( + x => x.Name == field.Name && + (x.FieldType == field.FieldType || Nullable.GetUnderlyingType(x.FieldType) == field.FieldType)); + + if (changeEquivalent is null) continue; + + sharedConfigFields.Add(field); + sharedChangeFields.Add(changeEquivalent); + } + + static int sortByName(FieldInfo a, FieldInfo b) => a.Name.CompareTo(b.Name); + + sharedConfigFields.Sort(sortByName); + sharedChangeFields.Sort(sortByName); + + p_configSharedFields = sharedConfigFields.ToArray(); + p_changeSharedFields = sharedChangeFields.ToArray(); } public string[] GameDirectories; @@ -35,22 +71,51 @@ public class Config public Config ApplyChanges(Changes changes) { - if (changes.GameDirectories is not null) - GameDirectories = GameDirectories.Union(changes.GameDirectories).ToArray(); + for (int i = 0; i < p_configSharedFields.Length; i++) + { + FieldInfo configField = p_configSharedFields[i], + changeField = p_changeSharedFields[i]; - if (changes.RunUnsafeCommands is not null) RunUnsafeCommands = changes.RunUnsafeCommands.Value; + object? toChange = changeField.GetValue(changes); + + if (toChange is null) continue; + + if (configField.FieldType.IsArray) + { + object[] currentArray = ((Array)configField.GetValue(this)!).CastArray(), + changeArray = ((Array)toChange).CastArray(); + + currentArray = currentArray.Union(changeArray).ToArray(); + configField.SetValue(this, currentArray.CastArray(configField.FieldType.GetElementType()!)); + } + else configField.SetValue(this, toChange); + } return this; } - public Changes GetChanges(Config? baseConfig = null) + public Changes GetChanges(Config? reference = null) { - Config reference = baseConfig ?? Defaults; - Changes changes = new() + reference ??= Defaults; + Changes changes = new(); + + for (int i = 0; i < p_configSharedFields.Length; i++) { - GameDirectories = reference.GameDirectories == GameDirectories ? null : - GameDirectories.Where(x => !reference.GameDirectories.Contains(x)).ToArray(), - RunUnsafeCommands = reference.RunUnsafeCommands == RunUnsafeCommands ? null : RunUnsafeCommands - }; + FieldInfo configField = p_configSharedFields[i], + changeField = p_changeSharedFields[i]; + + object? toSet = configField.GetValue(this); + + if (toSet is null) continue; + + if (configField.FieldType.IsArray) + { + object[] configArray = ((Array)toSet).CastArray(), + referenceArray = ((Array)configField.GetValue(Defaults)!).CastArray(), + changesArray = configArray.Where(x => !referenceArray.Contains(x)).ToArray(); + changeField.SetValue(changes, changesArray.CastArray(configField.FieldType.GetElementType()!)); + } + else changeField.SetValue(changes, toSet); + } return changes; } diff --git a/SrcMod/Shell/Shell.csproj b/SrcMod/Shell/Shell.csproj index 804ac4f..253835f 100644 --- a/SrcMod/Shell/Shell.csproj +++ b/SrcMod/Shell/Shell.csproj @@ -36,8 +36,4 @@ - - - - From a0c50c4cabb1d41fe58570a2141022e04b4c5951 Mon Sep 17 00:00:00 2001 From: That_One_Nerd Date: Tue, 25 Apr 2023 17:42:02 -0400 Subject: [PATCH 12/12] Changed version to 0.4.0 --- SrcMod/Shell/Shell.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SrcMod/Shell/Shell.cs b/SrcMod/Shell/Shell.cs index 372d084..7fe1d15 100644 --- a/SrcMod/Shell/Shell.cs +++ b/SrcMod/Shell/Shell.cs @@ -4,7 +4,7 @@ public class Shell { public const string Author = "That_One_Nerd"; public const string Name = "SrcMod"; - public const string Version = "Alpha 0.3.3"; + public const string Version = "Alpha 0.4.0"; public readonly string? ShellDirectory;