From ff4eac7d3ffa089b6f7f97c4fc0377dc993fd644 Mon Sep 17 00:00:00 2001 From: Butterscotch! <bscotchvanilla@gmail.com> Date: Sun, 1 Jan 2023 03:39:50 -0500 Subject: [PATCH 01/16] Update for .NET 7 --- .github/workflows/main.yml | 2 +- MultiAdmin.Tests/MultiAdmin.Tests.csproj | 12 ++++++------ MultiAdmin/MultiAdmin.csproj | 12 ++++++------ 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 61d4882..7feaaa7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -9,7 +9,7 @@ jobs: strategy: matrix: os: [ubuntu-18.04, windows-latest] - framework: ['6.0'] + framework: ['7.0'] include: - os: ubuntu-18.04 target: linux-x64 diff --git a/MultiAdmin.Tests/MultiAdmin.Tests.csproj b/MultiAdmin.Tests/MultiAdmin.Tests.csproj index e87b34b..445d9ec 100644 --- a/MultiAdmin.Tests/MultiAdmin.Tests.csproj +++ b/MultiAdmin.Tests/MultiAdmin.Tests.csproj @@ -1,7 +1,7 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <TargetFramework>net6.0</TargetFramework> - <LangVersion>8</LangVersion> + <TargetFramework>net7.0</TargetFramework> + <LangVersion>11</LangVersion> <IsPackable>false</IsPackable> <RootNamespace>MultiAdmin.Tests</RootNamespace> @@ -15,16 +15,16 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" /> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" /> - <PackageReference Include="xunit" Version="2.4.1" /> + <PackageReference Include="xunit" Version="2.4.2" /> - <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"> + <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="coverlet.collector" Version="3.1.0"> + <PackageReference Include="coverlet.collector" Version="3.2.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> diff --git a/MultiAdmin/MultiAdmin.csproj b/MultiAdmin/MultiAdmin.csproj index 3aded0a..d313540 100644 --- a/MultiAdmin/MultiAdmin.csproj +++ b/MultiAdmin/MultiAdmin.csproj @@ -1,8 +1,8 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> - <TargetFramework>net6.0</TargetFramework> - <LangVersion>8</LangVersion> + <TargetFramework>net7.0</TargetFramework> + <LangVersion>11</LangVersion> <RootNamespace>MultiAdmin</RootNamespace> <ApplicationIcon>Icon.ico</ApplicationIcon> @@ -19,6 +19,10 @@ <Optimize>true</Optimize> + <!-- AOT Compilation --> + <PublishAot>true</PublishAot> + <StripSymbols>true</StripSymbols> + <!-- Workaround to ILCompiler error code 139 --> <DebugSymbols>false</DebugSymbols> <DebugType>none</DebugType> @@ -31,8 +35,4 @@ <ItemGroup Condition="$([MSBuild]::IsOSPlatform('Linux'))"> <PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" /> </ItemGroup> - - <ItemGroup> - <PackageReference Include="Microsoft.DotNet.ILCompiler" Version="7.0.0-*" /> - </ItemGroup> </Project> From 1d21e774f5f7e3db8ffd65e3f909dbdb6901bfe5 Mon Sep 17 00:00:00 2001 From: Butterscotch! <bscotchvanilla@gmail.com> Date: Sun, 1 Jan 2023 03:42:42 -0500 Subject: [PATCH 02/16] Reformat all and simplify `new` --- .../ServerIO/ShiftingListTests.cs | 8 +- .../ServerIO/StringSectionsTests.cs | 4 +- MultiAdmin/Config/Config.cs | 2 +- .../Config/ConfigHandler/ConfigRegister.cs | 2 +- .../InheritableConfigRegister.cs | 4 +- MultiAdmin/Config/MultiAdminConfig.cs | 90 +++++++++---------- MultiAdmin/ConsoleTools/ColoredConsole.cs | 6 +- MultiAdmin/ConsoleTools/ConsolePositioning.cs | 14 +-- MultiAdmin/Features/ConfigGenerator.cs | 18 ++-- MultiAdmin/Features/FolderCopyRoundQueue.cs | 2 +- MultiAdmin/Features/GithubGenerator.cs | 90 +++++++++---------- MultiAdmin/Features/HelpCommand.cs | 4 +- MultiAdmin/Features/TitleBar.cs | 2 +- MultiAdmin/Program.cs | 22 ++--- MultiAdmin/Server.cs | 67 +++++++------- MultiAdmin/ServerIO/InputHandler.cs | 21 +++-- MultiAdmin/ServerIO/OutputHandler.cs | 6 +- MultiAdmin/ServerIO/ServerSocket.cs | 4 +- MultiAdmin/ServerIO/StringSections.cs | 6 +- MultiAdmin/Utility/CommandUtils.cs | 4 +- .../Utility/StringEnumerableExtensions.cs | 2 +- MultiAdmin/Utility/Utils.cs | 4 +- 22 files changed, 191 insertions(+), 191 deletions(-) diff --git a/MultiAdmin.Tests/ServerIO/ShiftingListTests.cs b/MultiAdmin.Tests/ServerIO/ShiftingListTests.cs index de287cb..8b00360 100644 --- a/MultiAdmin.Tests/ServerIO/ShiftingListTests.cs +++ b/MultiAdmin.Tests/ServerIO/ShiftingListTests.cs @@ -10,7 +10,7 @@ public class ShiftingListTests public void ShiftingListTest() { const int maxCount = 2; - ShiftingList shiftingList = new ShiftingList(maxCount); + ShiftingList shiftingList = new(maxCount); Assert.Equal(maxCount, shiftingList.MaxCount); } @@ -20,7 +20,7 @@ public void AddTest() { const int maxCount = 2; const int entriesToAdd = 6; - ShiftingList shiftingList = new ShiftingList(maxCount); + ShiftingList shiftingList = new(maxCount); for (int i = 0; i < entriesToAdd; i++) { @@ -40,7 +40,7 @@ public void RemoveFromEndTest() { const int maxCount = 6; const int entriesToRemove = 2; - ShiftingList shiftingList = new ShiftingList(maxCount); + ShiftingList shiftingList = new(maxCount); for (int i = 0; i < maxCount; i++) { @@ -65,7 +65,7 @@ public void ReplaceTest() { const int maxCount = 6; const int indexToReplace = 2; - ShiftingList shiftingList = new ShiftingList(maxCount); + ShiftingList shiftingList = new(maxCount); for (int i = 0; i < maxCount; i++) { diff --git a/MultiAdmin.Tests/ServerIO/StringSectionsTests.cs b/MultiAdmin.Tests/ServerIO/StringSectionsTests.cs index dee6d8c..c435aaa 100644 --- a/MultiAdmin.Tests/ServerIO/StringSectionsTests.cs +++ b/MultiAdmin.Tests/ServerIO/StringSectionsTests.cs @@ -16,8 +16,8 @@ public StringSectionsTests(ITestOutputHelper output) } [Theory] - [InlineData("test string", new[] {"te", "st", " s", "tr", "in", "g"}, 2)] - [InlineData("test string", new[] {"tes..", ".t ..", ".st..", ".ring"}, 5, ".", "..")] + [InlineData("test string", new[] { "te", "st", " s", "tr", "in", "g" }, 2)] + [InlineData("test string", new[] { "tes..", ".t ..", ".st..", ".ring" }, 5, ".", "..")] public void FromStringTest(string testString, string[] expectedSections, int sectionLength, string leftIndictator = null, string rightIndictator = null) { diff --git a/MultiAdmin/Config/Config.cs b/MultiAdmin/Config/Config.cs index 133de2e..6bab93e 100644 --- a/MultiAdmin/Config/Config.cs +++ b/MultiAdmin/Config/Config.cs @@ -67,7 +67,7 @@ public void ReadConfigFile() public bool Contains(string key) { return rawData != null && - rawData.Any(entry => entry.StartsWith($"{key}:", StringComparison.CurrentCultureIgnoreCase)); + rawData.Any(entry => entry.StartsWith($"{key}:", StringComparison.CurrentCultureIgnoreCase)); } private static string CleanValue(string value, bool removeQuotes = true) diff --git a/MultiAdmin/Config/ConfigHandler/ConfigRegister.cs b/MultiAdmin/Config/ConfigHandler/ConfigRegister.cs index 37cfc70..eb12762 100644 --- a/MultiAdmin/Config/ConfigHandler/ConfigRegister.cs +++ b/MultiAdmin/Config/ConfigHandler/ConfigRegister.cs @@ -10,7 +10,7 @@ public abstract class ConfigRegister /// <summary> /// A list of registered <see cref="ConfigEntry"/>s. /// </summary> - protected readonly List<ConfigEntry> registeredConfigs = new List<ConfigEntry>(); + protected readonly List<ConfigEntry> registeredConfigs = new(); /// <summary> /// Returns an array of registered <see cref="ConfigEntry"/>s. diff --git a/MultiAdmin/Config/ConfigHandler/InheritableConfigRegister.cs b/MultiAdmin/Config/ConfigHandler/InheritableConfigRegister.cs index ad6d52e..99e143e 100644 --- a/MultiAdmin/Config/ConfigHandler/InheritableConfigRegister.cs +++ b/MultiAdmin/Config/ConfigHandler/InheritableConfigRegister.cs @@ -40,7 +40,7 @@ protected InheritableConfigRegister(ConfigRegister parentConfigRegister = null) public override void UpdateConfigValue(ConfigEntry configEntry) { if (configEntry != null && configEntry.Inherit && ParentConfigRegister != null && - ShouldInheritConfigEntry(configEntry)) + ShouldInheritConfigEntry(configEntry)) { ParentConfigRegister.UpdateConfigValue(configEntry); } @@ -56,7 +56,7 @@ public override void UpdateConfigValue(ConfigEntry configEntry) /// <param name="highestToLowest">Whether to order the returned array from highest <see cref="ConfigRegister"/> in the hierarchy to the lowest.</param> public ConfigRegister[] GetConfigRegisterHierarchy(bool highestToLowest = true) { - List<ConfigRegister> configRegisterHierarchy = new List<ConfigRegister>(); + List<ConfigRegister> configRegisterHierarchy = new(); ConfigRegister configRegister = this; while (configRegister != null && !configRegisterHierarchy.Contains(configRegister)) diff --git a/MultiAdmin/Config/MultiAdminConfig.cs b/MultiAdmin/Config/MultiAdminConfig.cs index 7911b7e..d9c1d7b 100644 --- a/MultiAdmin/Config/MultiAdminConfig.cs +++ b/MultiAdmin/Config/MultiAdminConfig.cs @@ -43,7 +43,7 @@ public class MultiAdminConfig : InheritableConfigRegister "MultiAdmin Debug Logging", "Enables MultiAdmin debug logging, this logs to a separate file than any other logs"); public ConfigEntry<string[]> DebugLogBlacklist { get; } = - new ConfigEntry<string[]>("multiadmin_debug_log_blacklist", new string[] {nameof(OutputHandler.HandleMessage), nameof(Utils.StringMatches), nameof(ServerSocket.MessageListener) }, + new ConfigEntry<string[]>("multiadmin_debug_log_blacklist", new string[] { nameof(OutputHandler.HandleMessage), nameof(Utils.StringMatches), nameof(ServerSocket.MessageListener) }, "MultiAdmin Debug Logging Blacklist", "Which tags to block for MultiAdmin debug logging"); public ConfigEntry<string[]> DebugLogWhitelist { get; } = @@ -199,7 +199,7 @@ public InputHandler.ConsoleInputSystem ActualConsoleInputSystem public const string ConfigFileName = "scp_multiadmin.cfg"; public static readonly string GlobalConfigFilePath = Utils.GetFullPathSafe(ConfigFileName); - public static readonly MultiAdminConfig GlobalConfig = new MultiAdminConfig(GlobalConfigFilePath, null); + public static readonly MultiAdminConfig GlobalConfig = new(GlobalConfigFilePath, null); public MultiAdminConfig ParentConfig { @@ -276,65 +276,65 @@ public override void UpdateConfigValueInheritable(ConfigEntry configEntry) switch (configEntry) { case ConfigEntry<string> config: - { - config.Value = Config.GetString(config.Key, config.Default); - break; - } + { + config.Value = Config.GetString(config.Key, config.Default); + break; + } case ConfigEntry<string[]> config: - { - config.Value = Config.GetStringArray(config.Key, config.Default); - break; - } + { + config.Value = Config.GetStringArray(config.Key, config.Default); + break; + } case ConfigEntry<int> config: - { - config.Value = Config.GetInt(config.Key, config.Default); - break; - } + { + config.Value = Config.GetInt(config.Key, config.Default); + break; + } case ConfigEntry<uint> config: - { - config.Value = Config.GetUInt(config.Key, config.Default); - break; - } + { + config.Value = Config.GetUInt(config.Key, config.Default); + break; + } case ConfigEntry<float> config: - { - config.Value = Config.GetFloat(config.Key, config.Default); - break; - } + { + config.Value = Config.GetFloat(config.Key, config.Default); + break; + } case ConfigEntry<double> config: - { - config.Value = Config.GetDouble(config.Key, config.Default); - break; - } + { + config.Value = Config.GetDouble(config.Key, config.Default); + break; + } case ConfigEntry<decimal> config: - { - config.Value = Config.GetDecimal(config.Key, config.Default); - break; - } + { + config.Value = Config.GetDecimal(config.Key, config.Default); + break; + } case ConfigEntry<bool> config: - { - config.Value = Config.GetBool(config.Key, config.Default); - break; - } + { + config.Value = Config.GetBool(config.Key, config.Default); + break; + } case ConfigEntry<InputHandler.ConsoleInputSystem> config: - { - config.Value = Config.GetConsoleInputSystem(config.Key, config.Default); - break; - } + { + config.Value = Config.GetConsoleInputSystem(config.Key, config.Default); + break; + } default: - { - throw new ArgumentException( - $"Config type unsupported (Config: Key = \"{configEntry.Key ?? "Null"}\" Type = \"{configEntry.ValueType.FullName ?? "Null"}\" Name = \"{configEntry.Name ?? "Null"}\" Description = \"{configEntry.Description ?? "Null"}\").", - nameof(configEntry)); - } + { + throw new ArgumentException( + $"Config type unsupported (Config: Key = \"{configEntry.Key ?? "Null"}\" Type = \"{configEntry.ValueType.FullName ?? "Null"}\" Name = \"{configEntry.Name ?? "Null"}\" Description = \"{configEntry.Description ?? "Null"}\").", + nameof(configEntry)); + } } } @@ -365,7 +365,7 @@ public bool ConfigOrGlobalConfigContains(string key) public MultiAdminConfig[] GetConfigHierarchy(bool highestToLowest = true) { - List<MultiAdminConfig> configHierarchy = new List<MultiAdminConfig>(); + List<MultiAdminConfig> configHierarchy = new(); foreach (ConfigRegister configRegister in GetConfigRegisterHierarchy(highestToLowest)) { @@ -381,7 +381,7 @@ public bool ConfigHierarchyContainsPath(string path) string fullPath = Utils.GetFullPathSafe(path); return !string.IsNullOrEmpty(fullPath) && - GetConfigHierarchy().Any(config => config.Config?.ConfigPath == path); + GetConfigHierarchy().Any(config => config.Config?.ConfigPath == path); } } } diff --git a/MultiAdmin/ConsoleTools/ColoredConsole.cs b/MultiAdmin/ConsoleTools/ColoredConsole.cs index a54a886..50f85bc 100644 --- a/MultiAdmin/ConsoleTools/ColoredConsole.cs +++ b/MultiAdmin/ConsoleTools/ColoredConsole.cs @@ -5,7 +5,7 @@ namespace MultiAdmin.ConsoleTools { public static class ColoredConsole { - public static readonly object WriteLock = new object(); + public static readonly object WriteLock = new(); public static void Write(string text, ConsoleColor? textColor = null, ConsoleColor? backgroundColor = null) { @@ -95,7 +95,7 @@ public ColoredMessage(string text, ConsoleColor? textColor = null, ConsoleColor? public bool Equals(ColoredMessage other) { return string.Equals(text, other.text) && textColor == other.textColor && - backgroundColor == other.backgroundColor; + backgroundColor == other.backgroundColor; } public override bool Equals(object obj) @@ -181,7 +181,7 @@ public static class ColoredMessageArrayExtensions { private static string JoinTextIgnoreNull(ColoredMessage[] coloredMessages) { - StringBuilder builder = new StringBuilder(""); + StringBuilder builder = new(""); foreach (ColoredMessage coloredMessage in coloredMessages) { diff --git a/MultiAdmin/ConsoleTools/ConsolePositioning.cs b/MultiAdmin/ConsoleTools/ConsolePositioning.cs index dcea3a9..028bac7 100644 --- a/MultiAdmin/ConsoleTools/ConsolePositioning.cs +++ b/MultiAdmin/ConsoleTools/ConsolePositioning.cs @@ -8,7 +8,7 @@ public static class ConsolePositioning public static BufferPoint BufferCursor { - get => new BufferPoint(Console.CursorLeft, Console.CursorTop); + get => new(Console.CursorLeft, Console.CursorTop); set => Console.SetCursorPosition(value.x, value.y); } @@ -20,22 +20,22 @@ public static ConsolePoint ConsoleCursor public static BufferPoint BufferLeft { - get => new BufferPoint(0, 0); + get => new(0, 0); } public static BufferPoint BufferRight { - get => new BufferPoint(Console.BufferWidth - 1, 0); + get => new(Console.BufferWidth - 1, 0); } public static BufferPoint BufferTop { - get => new BufferPoint(0, 0); + get => new(0, 0); } public static BufferPoint BufferBottom { - get => new BufferPoint(0, Console.BufferHeight - 1); + get => new(0, Console.BufferHeight - 1); } #endregion @@ -45,7 +45,7 @@ public struct ConsolePoint { public readonly int x, y; - public BufferPoint BufferPoint => new BufferPoint(this); + public BufferPoint BufferPoint => new(this); public ConsolePoint(int x, int y) { @@ -78,7 +78,7 @@ public struct BufferPoint { public readonly int x, y; - public ConsolePoint ConsolePoint => new ConsolePoint(this); + public ConsolePoint ConsolePoint => new(this); public BufferPoint(int x, int y) { diff --git a/MultiAdmin/Features/ConfigGenerator.cs b/MultiAdmin/Features/ConfigGenerator.cs index 0c54baa..4e5ee74 100644 --- a/MultiAdmin/Features/ConfigGenerator.cs +++ b/MultiAdmin/Features/ConfigGenerator.cs @@ -72,22 +72,22 @@ public void OnCall(string[] args) ConfigEntry[] registeredConfigs = MultiAdminConfig.GlobalConfig.GetRegisteredConfigs(); - List<string> lines = new List<string>(registeredConfigs.Length); + List<string> lines = new(registeredConfigs.Length); foreach (ConfigEntry configEntry in registeredConfigs) { switch (configEntry) { case ConfigEntry<string[]> config: - { - lines.Add($"{config.Key}: {(config.Default == null ? "" : string.Join(", ", config.Default))}"); - break; - } + { + lines.Add($"{config.Key}: {(config.Default == null ? "" : string.Join(", ", config.Default))}"); + break; + } default: - { - lines.Add($"{configEntry.Key}: {configEntry.ObjectDefault ?? ""}"); - break; - } + { + lines.Add($"{configEntry.Key}: {configEntry.ObjectDefault ?? ""}"); + break; + } } } diff --git a/MultiAdmin/Features/FolderCopyRoundQueue.cs b/MultiAdmin/Features/FolderCopyRoundQueue.cs index 6f91266..bbe9fb1 100644 --- a/MultiAdmin/Features/FolderCopyRoundQueue.cs +++ b/MultiAdmin/Features/FolderCopyRoundQueue.cs @@ -60,7 +60,7 @@ private int GetNextRandomIndex() { if (!HasValidQueue) return 0; - Random random = new Random(); + Random random = new(); int index; do diff --git a/MultiAdmin/Features/GithubGenerator.cs b/MultiAdmin/Features/GithubGenerator.cs index f779cec..5c01a75 100644 --- a/MultiAdmin/Features/GithubGenerator.cs +++ b/MultiAdmin/Features/GithubGenerator.cs @@ -74,7 +74,7 @@ public void OnCall(string[] args) // Ignore, any proper exceptions will be presented when the file is written } - List<string> lines = new List<string> {"# MultiAdmin", "", "## Features", ""}; + List<string> lines = new() { "# MultiAdmin", "", "## Features", "" }; foreach (Feature feature in Server.features) { @@ -99,72 +99,72 @@ public void OnCall(string[] args) foreach (ConfigEntry configEntry in MultiAdminConfig.GlobalConfig.GetRegisteredConfigs()) { StringBuilder stringBuilder = - new StringBuilder($"{configEntry.Key ?? EmptyIndicator}{ColumnSeparator}"); + new($"{configEntry.Key ?? EmptyIndicator}{ColumnSeparator}"); switch (configEntry) { case ConfigEntry<string> config: - { - stringBuilder.Append( - $"String{ColumnSeparator}{(string.IsNullOrEmpty(config.Default) ? EmptyIndicator : config.Default)}"); - break; - } + { + stringBuilder.Append( + $"String{ColumnSeparator}{(string.IsNullOrEmpty(config.Default) ? EmptyIndicator : config.Default)}"); + break; + } case ConfigEntry<string[]> config: - { - stringBuilder.Append( - $"String List{ColumnSeparator}{(config.Default?.IsEmpty() ?? true ? EmptyIndicator : string.Join(", ", config.Default))}"); - break; - } + { + stringBuilder.Append( + $"String List{ColumnSeparator}{(config.Default?.IsEmpty() ?? true ? EmptyIndicator : string.Join(", ", config.Default))}"); + break; + } case ConfigEntry<int> config: - { - stringBuilder.Append($"Integer{ColumnSeparator}{config.Default}"); - break; - } + { + stringBuilder.Append($"Integer{ColumnSeparator}{config.Default}"); + break; + } case ConfigEntry<uint> config: - { - stringBuilder.Append($"Unsigned Integer{ColumnSeparator}{config.Default}"); - break; - } + { + stringBuilder.Append($"Unsigned Integer{ColumnSeparator}{config.Default}"); + break; + } case ConfigEntry<float> config: - { - stringBuilder.Append($"Float{ColumnSeparator}{config.Default}"); - break; - } + { + stringBuilder.Append($"Float{ColumnSeparator}{config.Default}"); + break; + } case ConfigEntry<double> config: - { - stringBuilder.Append($"Double{ColumnSeparator}{config.Default}"); - break; - } + { + stringBuilder.Append($"Double{ColumnSeparator}{config.Default}"); + break; + } case ConfigEntry<decimal> config: - { - stringBuilder.Append($"Decimal{ColumnSeparator}{config.Default}"); - break; - } + { + stringBuilder.Append($"Decimal{ColumnSeparator}{config.Default}"); + break; + } case ConfigEntry<bool> config: - { - stringBuilder.Append($"Boolean{ColumnSeparator}{config.Default}"); - break; - } + { + stringBuilder.Append($"Boolean{ColumnSeparator}{config.Default}"); + break; + } case ConfigEntry<InputHandler.ConsoleInputSystem> config: - { - stringBuilder.Append($"[ConsoleInputSystem](#ConsoleInputSystem){ColumnSeparator}{config.Default}"); - break; - } + { + stringBuilder.Append($"[ConsoleInputSystem](#ConsoleInputSystem){ColumnSeparator}{config.Default}"); + break; + } default: - { - stringBuilder.Append( - $"{configEntry.ValueType?.Name ?? EmptyIndicator}{ColumnSeparator}{configEntry.ObjectDefault ?? EmptyIndicator}"); - break; - } + { + stringBuilder.Append( + $"{configEntry.ValueType?.Name ?? EmptyIndicator}{ColumnSeparator}{configEntry.ObjectDefault ?? EmptyIndicator}"); + break; + } } stringBuilder.Append($"{ColumnSeparator}{configEntry.Description ?? EmptyIndicator}"); diff --git a/MultiAdmin/Features/HelpCommand.cs b/MultiAdmin/Features/HelpCommand.cs index f2e42f1..aec5c09 100644 --- a/MultiAdmin/Features/HelpCommand.cs +++ b/MultiAdmin/Features/HelpCommand.cs @@ -9,7 +9,7 @@ namespace MultiAdmin.Features [Feature] public class HelpCommand : Feature, ICommand { - private static readonly ColoredMessage helpPrefix = new ColoredMessage("Commands from MultiAdmin:\n", ConsoleColor.Yellow); + private static readonly ColoredMessage helpPrefix = new("Commands from MultiAdmin:\n", ConsoleColor.Yellow); public HelpCommand(Server server) : base(server) { @@ -31,7 +31,7 @@ public void OnCall(string[] args) message[0] = helpPrefix; - List<string> helpOutput = new List<string>(); + List<string> helpOutput = new(); foreach (KeyValuePair<string, ICommand> command in Server.commands) { string usage = command.Value.GetUsage(); diff --git a/MultiAdmin/Features/TitleBar.cs b/MultiAdmin/Features/TitleBar.cs index 8bedad4..3b04790 100644 --- a/MultiAdmin/Features/TitleBar.cs +++ b/MultiAdmin/Features/TitleBar.cs @@ -53,7 +53,7 @@ private void UpdateTitlebar() { if (Program.Headless || !Server.ServerConfig.SetTitleBar.Value) return; - List<string> titleBar = new List<string> {$"MultiAdmin {Program.MaVersion}"}; + List<string> titleBar = new() { $"MultiAdmin {Program.MaVersion}" }; if (!string.IsNullOrEmpty(Server.serverId)) { diff --git a/MultiAdmin/Program.cs b/MultiAdmin/Program.cs index 38d170d..624ca83 100644 --- a/MultiAdmin/Program.cs +++ b/MultiAdmin/Program.cs @@ -17,7 +17,7 @@ public static class Program { public const string MaVersion = "3.4.1.0"; - private static readonly List<Server> InstantiatedServers = new List<Server>(); + private static readonly List<Server> InstantiatedServers = new(); private static readonly string MaDebugLogDir = Utils.GetFullPathSafe(MultiAdminConfig.GlobalConfig.LogLocation.Value); @@ -34,7 +34,7 @@ public static class Program private static IExitSignal exitSignalListener; private static bool exited = false; - private static readonly object ExitLock = new object(); + private static readonly object ExitLock = new(); #region Server Properties @@ -86,8 +86,8 @@ public static void Write(string message, ConsoleColor color = ConsoleColor.DarkY private static bool IsDebugLogTagAllowed(string tag) { return (!MultiAdminConfig.GlobalConfig?.DebugLogBlacklist?.Value?.Contains(tag) ?? true) && - ((MultiAdminConfig.GlobalConfig?.DebugLogWhitelist?.Value?.IsEmpty() ?? true) || - MultiAdminConfig.GlobalConfig.DebugLogWhitelist.Value.Contains(tag)); + ((MultiAdminConfig.GlobalConfig?.DebugLogWhitelist?.Value?.IsEmpty() ?? true) || + MultiAdminConfig.GlobalConfig.DebugLogWhitelist.Value.Contains(tag)); } public static void LogDebugException(string tag, Exception exception) @@ -107,7 +107,7 @@ public static void LogDebug(string tag, string message) try { if ((!MultiAdminConfig.GlobalConfig?.DebugLog?.Value ?? true) || - string.IsNullOrEmpty(MaDebugLogFile) || tag == null || !IsDebugLogTagAllowed(tag)) return; + string.IsNullOrEmpty(MaDebugLogFile) || tag == null || !IsDebugLogTagAllowed(tag)) return; // Assign debug log stream as needed if (debugLogStream == null) @@ -221,7 +221,7 @@ public static void Main() Headless = GetFlagFromArgs(Args, "headless", "h"); - string serverIdArg = GetParamFromArgs(Args, "server-id", "id"); + string serverIdArg = GetParamFromArgs(Args, "server-id", "id"); string configArg = GetParamFromArgs(Args, "config", "c"); portArg = uint.TryParse(GetParamFromArgs(Args, "port", "p"), out uint port) ? (uint?)port : null; @@ -383,17 +383,17 @@ public static bool GetFlagFromArgs(string[] args, string[] keys = null, string[] public static string GetParamFromArgs(string[] args, string key = null, string alias = null) { - return GetParamFromArgs(args, new string[] {key}, new string[] {alias}); + return GetParamFromArgs(args, new string[] { key }, new string[] { alias }); } public static bool ArgsContainsParam(string[] args, string key = null, string alias = null) { - return ArgsContainsParam(args, new string[] {key}, new string[] {alias}); + return ArgsContainsParam(args, new string[] { key }, new string[] { alias }); } public static bool GetFlagFromArgs(string[] args, string key = null, string alias = null) { - return GetFlagFromArgs(args, new string[] {key}, new string[] {alias}); + return GetFlagFromArgs(args, new string[] { key }, new string[] { alias }); } public static Process StartServer(Server server) @@ -405,7 +405,7 @@ public static Process StartServer(Server server) Write("Error while starting new server: Could not find the executable location!", ConsoleColor.Red); } - List<string> args = new List<string>(server.args); + List<string> args = new(server.args); if (!string.IsNullOrEmpty(server.serverId)) { @@ -422,7 +422,7 @@ public static Process StartServer(Server server) if (Headless) args.Add("-h"); - ProcessStartInfo startInfo = new ProcessStartInfo(assemblyLocation, args.JoinArgs()); + ProcessStartInfo startInfo = new(assemblyLocation, args.JoinArgs()); Write($"Launching \"{startInfo.FileName}\" with arguments \"{startInfo.Arguments}\"..."); diff --git a/MultiAdmin/Server.cs b/MultiAdmin/Server.cs index daa4439..b0dddec 100644 --- a/MultiAdmin/Server.cs +++ b/MultiAdmin/Server.cs @@ -16,12 +16,12 @@ namespace MultiAdmin { public class Server { - public readonly Dictionary<string, ICommand> commands = new Dictionary<string, ICommand>(); + public readonly Dictionary<string, ICommand> commands = new(); - public readonly List<Feature> features = new List<Feature>(); + public readonly List<Feature> features = new(); // We want a tick only list since its the only event that happens constantly, all the rest can be in a single list - private readonly List<IEventTick> tick = new List<IEventTick>(); + private readonly List<IEventTick> tick = new(); private readonly MultiAdminConfig serverConfig; public MultiAdminConfig ServerConfig => serverConfig ?? MultiAdminConfig.GlobalConfig; @@ -48,8 +48,8 @@ public Server(string serverId = null, string configLocation = null, uint? port = : Utils.GetFullPathSafe(Path.Combine(MultiAdminConfig.GlobalConfig.ServersFolder.Value, this.serverId)); this.configLocation = Utils.GetFullPathSafe(configLocation) ?? - Utils.GetFullPathSafe(MultiAdminConfig.GlobalConfig.ConfigLocation.Value) ?? - Utils.GetFullPathSafe(serverDir); + Utils.GetFullPathSafe(MultiAdminConfig.GlobalConfig.ConfigLocation.Value) ?? + Utils.GetFullPathSafe(serverDir); // Load config serverConfig = MultiAdminConfig.GlobalConfig; @@ -104,7 +104,7 @@ private set } public bool IsStopped => Status == ServerStatus.NotStarted || Status == ServerStatus.Stopped || - Status == ServerStatus.StoppedUnexpectedly; + Status == ServerStatus.StoppedUnexpectedly; public bool IsRunning => !IsStopped; public bool IsStarted => !IsStopped && !IsStarting; @@ -112,7 +112,7 @@ private set public bool IsStarting => Status == ServerStatus.Starting; public bool IsStopping => Status == ServerStatus.Stopping || Status == ServerStatus.ForceStopping || - Status == ServerStatus.Restarting; + Status == ServerStatus.Restarting; public bool IsLoading { get; set; } @@ -191,7 +191,7 @@ public bool IsGameProcessRunning private void MainLoop() { // Creates and starts a timer - Stopwatch timer = new Stopwatch(); + Stopwatch timer = new(); timer.Restart(); while (IsGameProcessRunning) @@ -255,7 +255,7 @@ public void WriteConfigInformation() foreach (MultiAdminConfig config in ServerConfig.GetConfigHierarchy()) { if (!string.IsNullOrEmpty(config?.Config?.ConfigPath) && - MultiAdminConfig.GlobalConfigFilePath != config.Config.ConfigPath) + MultiAdminConfig.GlobalConfigFilePath != config.Config.ConfigPath) Write($"Using server config \"{config.Config.ConfigPath}\"..."); } } @@ -316,13 +316,13 @@ public void StartServer(bool restartOnCrash = true) Write($"Executing \"{scpslExe}\"...", ConsoleColor.DarkGreen); // Start the console socket connection to the game server - ServerSocket consoleSocket = new ServerSocket(); + ServerSocket consoleSocket = new(); // Start the connection before the game to find an open port for communication consoleSocket.Connect(); SessionSocket = consoleSocket; - List<string> scpslArgs = new List<string> + List<string> scpslArgs = new() { $"-multiadmin:{Program.MaVersion}:{(int)ModFeatures.All}", "-batchmode", @@ -381,9 +381,10 @@ public void StartServer(bool restartOnCrash = true) // Add custom arguments scpslArgs.AddRange(args); - ProcessStartInfo startInfo = new ProcessStartInfo(scpslExe, scpslArgs.JoinArgs()) + ProcessStartInfo startInfo = new(scpslExe, scpslArgs.JoinArgs()) { - CreateNoWindow = true, UseShellExecute = false + CreateNoWindow = true, + UseShellExecute = false }; Write($"Starting server with the following parameters:\n{scpslExe} {startInfo.Arguments}"); @@ -397,7 +398,7 @@ public void StartServer(bool restartOnCrash = true) ForEachHandler<IEventServerPreStart>(eventPreStart => eventPreStart.OnServerPreStart()); // Start the input reader - CancellationTokenSource inputHandlerCancellation = new CancellationTokenSource(); + CancellationTokenSource inputHandlerCancellation = new(); Task inputHandler = null; if (!Program.Headless) @@ -406,7 +407,7 @@ public void StartServer(bool restartOnCrash = true) } // Start the output reader - OutputHandler outputHandler = new OutputHandler(this); + OutputHandler outputHandler = new(this); // Assign the socket events to the OutputHandler consoleSocket.OnReceiveMessage += outputHandler.HandleMessage; consoleSocket.OnReceiveAction += outputHandler.HandleAction; @@ -568,25 +569,25 @@ private void RegisterFeature(Feature feature) break; case ICommand command: - { - string commandKey = command.GetCommand().ToLower().Trim(); - - // If the command was already registered - if (commands.ContainsKey(commandKey)) { - string message = - $"Warning, {nameof(MultiAdmin)} tried to register duplicate command \"{commandKey}\""; + string commandKey = command.GetCommand().ToLower().Trim(); - Program.LogDebug(nameof(RegisterFeature), message); - Write(message); - } - else - { - commands.Add(commandKey, command); - } + // If the command was already registered + if (commands.ContainsKey(commandKey)) + { + string message = + $"Warning, {nameof(MultiAdmin)} tried to register duplicate command \"{commandKey}\""; - break; - } + Program.LogDebug(nameof(RegisterFeature), message); + Write(message); + } + else + { + commands.Add(commandKey, command); + } + + break; + } } features.Add(feature); @@ -664,7 +665,7 @@ public void Write(ColoredMessage message, ConsoleColor? timeStampColor = null) { lock (ColoredConsole.WriteLock) { - Write(new ColoredMessage[] {message}, timeStampColor ?? message.textColor); + Write(new ColoredMessage[] { message }, timeStampColor ?? message.textColor); } } @@ -712,7 +713,7 @@ public void ReloadConfig(bool copyFiles = true, bool runEvent = true) // Handle directory copying string copyFromDir; if (copyFiles && !string.IsNullOrEmpty(configLocation) && - !string.IsNullOrEmpty(copyFromDir = ServerConfig.CopyFromFolderOnReload.Value)) + !string.IsNullOrEmpty(copyFromDir = ServerConfig.CopyFromFolderOnReload.Value)) { CopyFromDir(copyFromDir, ServerConfig.FolderCopyWhitelist.Value, ServerConfig.FolderCopyBlacklist.Value); diff --git a/MultiAdmin/ServerIO/InputHandler.cs b/MultiAdmin/ServerIO/InputHandler.cs index 32520e2..d90d0ed 100644 --- a/MultiAdmin/ServerIO/InputHandler.cs +++ b/MultiAdmin/ServerIO/InputHandler.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -11,13 +10,13 @@ namespace MultiAdmin.ServerIO { public static class InputHandler { - private static readonly char[] Separator = {' '}; + private static readonly char[] Separator = { ' ' }; - public static readonly ColoredMessage BaseSection = new ColoredMessage(null, ConsoleColor.White); + public static readonly ColoredMessage BaseSection = new(null, ConsoleColor.White); - public static readonly ColoredMessage InputPrefix = new ColoredMessage("> ", ConsoleColor.Yellow); - public static readonly ColoredMessage LeftSideIndicator = new ColoredMessage("...", ConsoleColor.Yellow); - public static readonly ColoredMessage RightSideIndicator = new ColoredMessage("...", ConsoleColor.Yellow); + public static readonly ColoredMessage InputPrefix = new("> ", ConsoleColor.Yellow); + public static readonly ColoredMessage LeftSideIndicator = new("...", ConsoleColor.Yellow); + public static readonly ColoredMessage RightSideIndicator = new("...", ConsoleColor.Yellow); public static int InputPrefixLength => InputPrefix?.Length ?? 0; @@ -43,14 +42,14 @@ public static int SectionBufferWidth } public static string CurrentMessage { get; private set; } - public static ColoredMessage[] CurrentInput { get; private set; } = {InputPrefix}; + public static ColoredMessage[] CurrentInput { get; private set; } = { InputPrefix }; public static int CurrentCursor { get; private set; } public static async void Write(Server server, CancellationToken cancellationToken) { try { - ShiftingList prevMessages = new ShiftingList(25); + ShiftingList prevMessages = new(25); while (server.IsRunning && !server.IsStopping) { @@ -124,7 +123,7 @@ public static async Task WaitForKey(CancellationToken cancellationToken) public static async Task<string> GetInputLineOld(Server server, CancellationToken cancellationToken) { - StringBuilder message = new StringBuilder(); + StringBuilder message = new(); while (true) { await WaitForKey(cancellationToken); @@ -356,7 +355,7 @@ public static void ResetInputParams() public static void SetCurrentInput(params ColoredMessage[] coloredMessages) { - List<ColoredMessage> message = new List<ColoredMessage> {InputPrefix}; + List<ColoredMessage> message = new() { InputPrefix }; if (coloredMessages != null) message.AddRange(coloredMessages); @@ -444,7 +443,7 @@ public static void RandomizeInputColors() { try { - Random random = new Random(); + Random random = new(); Array colors = Enum.GetValues(typeof(ConsoleColor)); ConsoleColor random1 = (ConsoleColor)colors.GetValue(random.Next(colors.Length)); diff --git a/MultiAdmin/ServerIO/OutputHandler.cs b/MultiAdmin/ServerIO/OutputHandler.cs index e8b9883..b689632 100644 --- a/MultiAdmin/ServerIO/OutputHandler.cs +++ b/MultiAdmin/ServerIO/OutputHandler.cs @@ -8,9 +8,9 @@ namespace MultiAdmin.ServerIO public class OutputHandler { public static readonly Regex SmodRegex = - new Regex(@"\[(DEBUG|INFO|WARN|ERROR)\] (\[.*?\]) (.*)", RegexOptions.Compiled | RegexOptions.Singleline); + new(@"\[(DEBUG|INFO|WARN|ERROR)\] (\[.*?\]) (.*)", RegexOptions.Compiled | RegexOptions.Singleline); public static readonly char[] TrimChars = { '.', ' ', '\t', '!', '?', ',' }; - public static readonly char[] EventSplitChars = new char[] {':'}; + public static readonly char[] EventSplitChars = new char[] { ':' }; private readonly Server server; @@ -41,7 +41,7 @@ public void HandleMessage(object source, ServerSocket.MessageEventArgs message) if (message.message == null) return; - ColoredMessage coloredMessage = new ColoredMessage(message.message, ConsoleColor.White); + ColoredMessage coloredMessage = new(message.message, ConsoleColor.White); if (!coloredMessage.text.IsEmpty()) { diff --git a/MultiAdmin/ServerIO/ServerSocket.cs b/MultiAdmin/ServerIO/ServerSocket.cs index 6a176cd..81c2d36 100644 --- a/MultiAdmin/ServerIO/ServerSocket.cs +++ b/MultiAdmin/ServerIO/ServerSocket.cs @@ -10,9 +10,9 @@ namespace MultiAdmin.ServerIO public class ServerSocket : IDisposable { private const int IntBytes = sizeof(int); - public static readonly UTF8Encoding Encoding = new UTF8Encoding(false, true); + public static readonly UTF8Encoding Encoding = new(false, true); - private readonly CancellationTokenSource disposeCancellationSource = new CancellationTokenSource(); + private readonly CancellationTokenSource disposeCancellationSource = new(); private bool disposed = false; private readonly TcpListener listener; diff --git a/MultiAdmin/ServerIO/StringSections.cs b/MultiAdmin/ServerIO/StringSections.cs index be27d92..f60d969 100644 --- a/MultiAdmin/ServerIO/StringSections.cs +++ b/MultiAdmin/ServerIO/StringSections.cs @@ -56,7 +56,7 @@ public static StringSections FromString(string fullString, int sectionLength, $"{nameof(sectionLength)} must be greater than the total length of {nameof(leftIndicator)} and {nameof(rightIndicator)}", nameof(sectionLength)); - List<StringSection> sections = new List<StringSection>(); + List<StringSection> sections = new(); if (string.IsNullOrEmpty(fullString)) return new StringSections(sections.ToArray()); @@ -69,7 +69,7 @@ public static StringSections FromString(string fullString, int sectionLength, int sectionStartIndex = 0; // The text of the current section being created - StringBuilder curSecBuilder = new StringBuilder(); + StringBuilder curSecBuilder = new(); for (int i = 0; i < fullString.Length; i++) { @@ -128,7 +128,7 @@ public struct StringSection public ColoredMessage LeftIndicator { get; } public ColoredMessage RightIndicator { get; } - public ColoredMessage[] Section => new ColoredMessage[] {LeftIndicator, Text, RightIndicator}; + public ColoredMessage[] Section => new ColoredMessage[] { LeftIndicator, Text, RightIndicator }; public int MinIndex { get; } public int MaxIndex { get; } diff --git a/MultiAdmin/Utility/CommandUtils.cs b/MultiAdmin/Utility/CommandUtils.cs index be0380a..238e111 100644 --- a/MultiAdmin/Utility/CommandUtils.cs +++ b/MultiAdmin/Utility/CommandUtils.cs @@ -80,8 +80,8 @@ public static string[] StringToArgs(string inString, int startIndex, int count, if (inString.IsEmpty()) return Array.Empty<string>(); - List<string> args = new List<string>(); - StringBuilder strBuilder = new StringBuilder(); + List<string> args = new(); + StringBuilder strBuilder = new(); bool inQuotes = false; bool escaped = false; diff --git a/MultiAdmin/Utility/StringEnumerableExtensions.cs b/MultiAdmin/Utility/StringEnumerableExtensions.cs index 1d9aadb..e509b34 100644 --- a/MultiAdmin/Utility/StringEnumerableExtensions.cs +++ b/MultiAdmin/Utility/StringEnumerableExtensions.cs @@ -8,7 +8,7 @@ public static class StringEnumerableExtensions { public static string JoinArgs(this IEnumerable<string> args) { - StringBuilder argsStringBuilder = new StringBuilder(); + StringBuilder argsStringBuilder = new(); foreach (string arg in args) { if (arg.IsNullOrEmpty()) diff --git a/MultiAdmin/Utility/Utils.cs b/MultiAdmin/Utility/Utils.cs index 137b54e..83cad0c 100644 --- a/MultiAdmin/Utility/Utils.cs +++ b/MultiAdmin/Utility/Utils.cs @@ -48,7 +48,7 @@ public static ColoredMessage[] TimeStampMessage(ColoredMessage[] message, Consol public static ColoredMessage[] TimeStampMessage(ColoredMessage message, ConsoleColor? color = null, bool cloneMessages = false) { - return TimeStampMessage(new ColoredMessage[] {message}, color, cloneMessages); + return TimeStampMessage(new ColoredMessage[] { message }, color, cloneMessages); } public static string GetFullPathSafe(string path) @@ -144,7 +144,7 @@ private static bool PassesWhitelistAndBlacklist(string toCheck, string[] whiteli string[] blacklist = null) { return (whitelist.IsNullOrEmpty() || InputMatchesAnyPattern(toCheck, whitelist)) && - (blacklist.IsNullOrEmpty() || !InputMatchesAnyPattern(toCheck, blacklist)); + (blacklist.IsNullOrEmpty() || !InputMatchesAnyPattern(toCheck, blacklist)); } public static void CopyAll(DirectoryInfo source, DirectoryInfo target, string[] fileWhitelist = null, From a140f9e3b3c2ceabbabe76a3e6d0a9d14e5a0c9a Mon Sep 17 00:00:00 2001 From: Butterscotch! <bscotchvanilla@gmail.com> Date: Sun, 1 Jan 2023 04:02:45 -0500 Subject: [PATCH 03/16] Update GitHub Actions workflows --- .github/workflows/codeql-analysis.yml | 43 +++++++++++++++------------ .github/workflows/main.yml | 8 ++--- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 870f972..c2dc1ce 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -13,12 +13,12 @@ name: "CodeQL" on: push: - branches: [ master ] + branches: [ "main" ] pull_request: # The branches below must be a subset of the branches above - branches: [ master ] + branches: [ "main" ] schedule: - - cron: '44 20 * * 2' + - cron: '40 7 * * 5' jobs: analyze: @@ -33,39 +33,44 @@ jobs: fail-fast: false matrix: language: [ 'csharp' ] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] - # Learn more: - # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Use only 'java' to analyze code written in Java, Kotlin or both + # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + + # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v1 + uses: github/codeql-action/autobuild@v2 # ℹ️ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun - # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. - #- run: | - # make bootstrap - # make release + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7feaaa7..c57a079 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -18,7 +18,7 @@ jobs: timeout-minutes: 30 steps: - - uses: actions/checkout@v2.3.4 + - uses: actions/checkout@v3 - if: matrix.os == 'ubuntu-18.04' name: Install Linux packages @@ -27,7 +27,7 @@ jobs: sudo apt install -y clang zlib1g-dev libkrb5-dev libtinfo5 - name: Setup .NET - uses: actions/setup-dotnet@v1.7.2 + uses: actions/setup-dotnet@v3 with: dotnet-version: ${{matrix.framework}} @@ -41,13 +41,13 @@ jobs: run: dotnet test - name: Upload ${{matrix.target}} build - uses: actions/upload-artifact@v2.2.2 + uses: actions/upload-artifact@v3 with: name: MultiAdmin-${{matrix.target}}-${{matrix.framework}} path: ${{github.workspace}}/Builds/${{matrix.framework}}/${{matrix.target}} - name: Upload ${{matrix.target}} build to bundle - uses: actions/upload-artifact@v2.2.2 + uses: actions/upload-artifact@v3 with: name: MultiAdmin-all-${{matrix.framework}} path: ${{github.workspace}}/Builds/${{matrix.framework}} From fbd8e70de8b618a6a67a373a0df290c18d006e04 Mon Sep 17 00:00:00 2001 From: Butterscotch! <bscotchvanilla@gmail.com> Date: Sun, 1 Jan 2023 04:09:26 -0500 Subject: [PATCH 04/16] Fix potential error with AOT for Enum.GetValues --- MultiAdmin/ServerIO/InputHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MultiAdmin/ServerIO/InputHandler.cs b/MultiAdmin/ServerIO/InputHandler.cs index d90d0ed..745c82e 100644 --- a/MultiAdmin/ServerIO/InputHandler.cs +++ b/MultiAdmin/ServerIO/InputHandler.cs @@ -444,7 +444,7 @@ public static void RandomizeInputColors() try { Random random = new(); - Array colors = Enum.GetValues(typeof(ConsoleColor)); + Array colors = Enum.GetValues<ConsoleColor>(); ConsoleColor random1 = (ConsoleColor)colors.GetValue(random.Next(colors.Length)); ConsoleColor random2 = (ConsoleColor)colors.GetValue(random.Next(colors.Length)); From d9962e3fcacb5db8a23b27489170e70e24f42f4c Mon Sep 17 00:00:00 2001 From: Butterscotch! <bscotchvanilla@gmail.com> Date: Sun, 1 Jan 2023 04:11:16 -0500 Subject: [PATCH 05/16] Minor cleanup for newer .NET --- MultiAdmin/Server.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MultiAdmin/Server.cs b/MultiAdmin/Server.cs index b0dddec..f9ace0f 100644 --- a/MultiAdmin/Server.cs +++ b/MultiAdmin/Server.cs @@ -261,7 +261,7 @@ public void WriteConfigInformation() } } - public string GetExecutablePath() + public static string GetExecutablePath() { string scpslExe; @@ -329,7 +329,7 @@ public void StartServer(bool restartOnCrash = true) "-nographics", "-silent-crashes", "-nodedicateddelete", - $"-id{Process.GetCurrentProcess().Id}", + $"-id{Environment.ProcessId}", $"-console{consoleSocket.Port}", $"-port{Port}" }; From 1f802cfaee89930f1edf3524a83f57d4fc93e44c Mon Sep 17 00:00:00 2001 From: Butterscotch! <bscotchvanilla@gmail.com> Date: Sun, 1 Jan 2023 07:36:21 -0500 Subject: [PATCH 06/16] Enable nullable on the project --- MultiAdmin.Tests/MultiAdmin.Tests.csproj | 1 + .../ServerIO/StringSectionsTests.cs | 6 +- .../Utility/StringExtensionsTests.cs | 6 +- MultiAdmin/Config/Config.cs | 35 ++++---- .../Config/ConfigHandler/ConfigEntry.cs | 27 ++++--- .../Config/ConfigHandler/ConfigRegister.cs | 22 ++---- .../InheritableConfigRegister.cs | 12 +-- MultiAdmin/Config/MultiAdminConfig.cs | 36 ++++----- MultiAdmin/ConsoleTools/ColoredConsole.cs | 46 +++++------ MultiAdmin/ConsoleTools/ConsolePositioning.cs | 4 +- MultiAdmin/ConsoleTools/ConsoleUtils.cs | 8 +- MultiAdmin/Features/FolderCopyRoundQueue.cs | 12 +-- MultiAdmin/Features/NewCommand.cs | 4 +- MultiAdmin/MultiAdmin.csproj | 1 + MultiAdmin/NativeExitSignal/IExitSignal.cs | 2 +- MultiAdmin/NativeExitSignal/UnixExitSignal.cs | 2 +- MultiAdmin/NativeExitSignal/WinExitSignal.cs | 2 +- MultiAdmin/Program.cs | 79 ++++++++++--------- MultiAdmin/Server.cs | 62 +++++++-------- MultiAdmin/ServerIO/InputHandler.cs | 31 ++++---- MultiAdmin/ServerIO/OutputHandler.cs | 14 ++-- MultiAdmin/ServerIO/ServerSocket.cs | 26 +++--- MultiAdmin/ServerIO/StringSections.cs | 20 ++--- MultiAdmin/Utility/CommandUtils.cs | 5 -- MultiAdmin/Utility/EmptyExtensions.cs | 20 ++--- .../Utility/StringEnumerableExtensions.cs | 6 +- MultiAdmin/Utility/StringExtensions.cs | 6 +- MultiAdmin/Utility/Utils.cs | 34 ++++---- 28 files changed, 257 insertions(+), 272 deletions(-) diff --git a/MultiAdmin.Tests/MultiAdmin.Tests.csproj b/MultiAdmin.Tests/MultiAdmin.Tests.csproj index 445d9ec..c48508d 100644 --- a/MultiAdmin.Tests/MultiAdmin.Tests.csproj +++ b/MultiAdmin.Tests/MultiAdmin.Tests.csproj @@ -4,6 +4,7 @@ <LangVersion>11</LangVersion> <IsPackable>false</IsPackable> <RootNamespace>MultiAdmin.Tests</RootNamespace> + <Nullable>enable</Nullable> <GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> <GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute> diff --git a/MultiAdmin.Tests/ServerIO/StringSectionsTests.cs b/MultiAdmin.Tests/ServerIO/StringSectionsTests.cs index c435aaa..ab4e08c 100644 --- a/MultiAdmin.Tests/ServerIO/StringSectionsTests.cs +++ b/MultiAdmin.Tests/ServerIO/StringSectionsTests.cs @@ -19,7 +19,7 @@ public StringSectionsTests(ITestOutputHelper output) [InlineData("test string", new[] { "te", "st", " s", "tr", "in", "g" }, 2)] [InlineData("test string", new[] { "tes..", ".t ..", ".st..", ".ring" }, 5, ".", "..")] public void FromStringTest(string testString, string[] expectedSections, int sectionLength, - string leftIndictator = null, string rightIndictator = null) + string? leftIndictator = null, string? rightIndictator = null) { StringSections sections = StringSections.FromString(testString, sectionLength, leftIndictator != null ? new ColoredMessage(leftIndictator) : null, @@ -43,8 +43,8 @@ public void FromStringTest(string testString, string[] expectedSections, int sec [Theory] // No further characters can be output because of the prefix and suffix [InlineData("test string", 2, ".", ".")] - public void FromStringThrowsTest(string testString, int sectionLength, string leftIndictator = null, - string rightIndictator = null) + public void FromStringThrowsTest(string testString, int sectionLength, string? leftIndictator = null, + string? rightIndictator = null) { Assert.Throws<ArgumentException>(() => { diff --git a/MultiAdmin.Tests/Utility/StringExtensionsTests.cs b/MultiAdmin.Tests/Utility/StringExtensionsTests.cs index 52b622e..1a68c29 100644 --- a/MultiAdmin.Tests/Utility/StringExtensionsTests.cs +++ b/MultiAdmin.Tests/Utility/StringExtensionsTests.cs @@ -14,7 +14,7 @@ public class StringExtensionsTests [InlineData("test", "es", 1, 2)] [InlineData(null, null, 0)] [InlineData(null, null, 0, 1)] - public void EqualsTest(string main, string section, int startIndex, int count = -1) + public void EqualsTest(string? main, string? section, int startIndex, int count = -1) { Assert.True(count < 0 ? main.Equals(section, startIndex) : main.Equals(section, startIndex, count)); } @@ -27,7 +27,7 @@ public void EqualsTest(string main, string section, int startIndex, int count = [InlineData(null, "test", 0)] [InlineData("test", null, 0, 1)] [InlineData(null, "test", 0, 1)] - public void NotEqualsTest(string main, string section, int startIndex, int count = -1) + public void NotEqualsTest(string? main, string? section, int startIndex, int count = -1) { Assert.False(count < 0 ? main.Equals(section, startIndex) : main.Equals(section, startIndex, count)); } @@ -37,7 +37,7 @@ public void NotEqualsTest(string main, string section, int startIndex, int count [InlineData(typeof(ArgumentOutOfRangeException), "test", "st", 3)] [InlineData(typeof(ArgumentOutOfRangeException), "test", "te", -1)] [InlineData(typeof(ArgumentOutOfRangeException), "test", "es", 4)] - public void EqualsThrowsTest(Type expected, string main, string section, int startIndex, int count = -1) + public void EqualsThrowsTest(Type expected, string? main, string? section, int startIndex, int count = -1) { Assert.Throws(expected, () => { diff --git a/MultiAdmin/Config/Config.cs b/MultiAdmin/Config/Config.cs index 6bab93e..c118dd9 100644 --- a/MultiAdmin/Config/Config.cs +++ b/MultiAdmin/Config/Config.cs @@ -10,10 +10,11 @@ namespace MultiAdmin.Config { public class Config { - public string[] rawData = { }; + public string[] rawData = Array.Empty<string>(); public Config(string path) { + internalConfigPath = path; ReadConfigFile(path); } @@ -26,7 +27,7 @@ private set { try { - internalConfigPath = Utils.GetFullPathSafe(value); + internalConfigPath = Utils.GetFullPathSafe(value) ?? value; } catch (Exception e) { @@ -38,13 +39,11 @@ private set public void ReadConfigFile(string configPath) { - if (string.IsNullOrEmpty(configPath)) return; - ConfigPath = configPath; try { - rawData = File.Exists(ConfigPath) ? File.ReadAllLines(ConfigPath, Encoding.UTF8) : new string[] { }; + rawData = File.Exists(ConfigPath) ? File.ReadAllLines(ConfigPath, Encoding.UTF8) : Array.Empty<string>(); } catch (Exception e) { @@ -52,7 +51,7 @@ public void ReadConfigFile(string configPath) new ColoredMessage[] { - new ColoredMessage($"Error while reading config (Path = {ConfigPath ?? "Null"}):", + new ColoredMessage($"Error while reading config (Path = {ConfigPath}):", ConsoleColor.Red), new ColoredMessage(e.ToString(), ConsoleColor.Red) }.WriteLines(); @@ -79,7 +78,7 @@ private static string CleanValue(string value, bool removeQuotes = true) try { if (removeQuotes && newValue.StartsWith("\"") && newValue.EndsWith("\"")) - return newValue.Substring(1, newValue.Length - 2); + return newValue[1..^1]; } catch (Exception e) { @@ -89,7 +88,7 @@ private static string CleanValue(string value, bool removeQuotes = true) return newValue; } - public string GetString(string key, string def = null, bool removeQuotes = true) + public string? GetString(string key, string? def = null, bool removeQuotes = true) { try { @@ -99,7 +98,7 @@ public string GetString(string key, string def = null, bool removeQuotes = true) try { - return CleanValue(line.Substring(key.Length + 1), removeQuotes); + return CleanValue(line[(key.Length + 1)..], removeQuotes); } catch (Exception e) { @@ -115,11 +114,11 @@ public string GetString(string key, string def = null, bool removeQuotes = true) return def; } - public string[] GetStringArray(string key, string[] def = null) + public string[]? GetStringArray(string key, string[]? def = null) { try { - string value = GetString(key, removeQuotes: false); + string? value = GetString(key, removeQuotes: false); if (!string.IsNullOrEmpty(value)) { @@ -145,7 +144,7 @@ public int GetInt(string key, int def = 0) { try { - string value = GetString(key); + string? value = GetString(key); if (!string.IsNullOrEmpty(value) && int.TryParse(value, out int parseValue)) return parseValue; @@ -162,7 +161,7 @@ public uint GetUInt(string key, uint def = 0) { try { - string value = GetString(key); + string? value = GetString(key); if (!string.IsNullOrEmpty(value) && uint.TryParse(value, out uint parseValue)) return parseValue; @@ -179,7 +178,7 @@ public float GetFloat(string key, float def = 0) { try { - string value = GetString(key); + string? value = GetString(key); if (!string.IsNullOrEmpty(value) && float.TryParse(value, out float parsedValue)) return parsedValue; @@ -196,7 +195,7 @@ public double GetDouble(string key, double def = 0) { try { - string value = GetString(key); + string? value = GetString(key); if (!string.IsNullOrEmpty(value) && double.TryParse(value, out double parsedValue)) return parsedValue; @@ -213,7 +212,7 @@ public decimal GetDecimal(string key, decimal def = 0) { try { - string value = GetString(key); + string? value = GetString(key); if (!string.IsNullOrEmpty(value) && decimal.TryParse(value, out decimal parsedValue)) return parsedValue; @@ -230,7 +229,7 @@ public bool GetBool(string key, bool def = false) { try { - string value = GetString(key); + string? value = GetString(key); if (!string.IsNullOrEmpty(value) && bool.TryParse(value, out bool parsedValue)) return parsedValue; @@ -247,7 +246,7 @@ public InputHandler.ConsoleInputSystem GetConsoleInputSystem(string key, InputHa { try { - string value = GetString(key); + string? value = GetString(key); if (!string.IsNullOrEmpty(value) && Enum.TryParse<InputHandler.ConsoleInputSystem>(value, out var consoleInputSystem)) return consoleInputSystem; diff --git a/MultiAdmin/Config/ConfigHandler/ConfigEntry.cs b/MultiAdmin/Config/ConfigHandler/ConfigEntry.cs index 70e170a..6411a6b 100644 --- a/MultiAdmin/Config/ConfigHandler/ConfigEntry.cs +++ b/MultiAdmin/Config/ConfigHandler/ConfigEntry.cs @@ -20,12 +20,12 @@ public abstract class ConfigEntry /// <summary> /// The value of the <see cref="ConfigEntry"/>. /// </summary> - public abstract object ObjectValue { get; set; } + public abstract object? ObjectValue { get; set; } /// <summary> /// The default value of the <see cref="ConfigEntry"/>. /// </summary> - public abstract object ObjectDefault { get; set; } + public abstract object? ObjectDefault { get; set; } /// <summary> /// Whether to inherit this config value from the <see cref="ConfigRegister"/>'s parent <see cref="ConfigRegister"/>s if they support value inheritance. @@ -35,17 +35,17 @@ public abstract class ConfigEntry /// <summary> /// The name of the <see cref="ConfigEntry"/>. /// </summary> - public string Name { get; } + public string? Name { get; } /// <summary> /// The description of the <see cref="ConfigEntry"/>. /// </summary> - public string Description { get; } + public string? Description { get; } /// <summary> /// Creates a basic <see cref="ConfigEntry"/> with no values and indication for whether to inherit the value. /// </summary> - public ConfigEntry(string key, bool inherit = true, string name = null, string description = null) + public ConfigEntry(string key, bool inherit = true, string? name = null, string? description = null) { Key = key; @@ -58,7 +58,7 @@ public ConfigEntry(string key, bool inherit = true, string name = null, string d /// <summary> /// Creates a basic <see cref="ConfigEntry"/> with no values. /// </summary> - public ConfigEntry(string key, string name = null, string description = null) : this(key, true, name, + public ConfigEntry(string key, string? name = null, string? description = null) : this(key, true, name, description) { } @@ -82,25 +82,26 @@ public class ConfigEntry<T> : ConfigEntry /// </summary> public T Default { get; set; } - public override object ObjectValue + public override object? ObjectValue { get => Value; - set => Value = (T)value; + set => Value = (T)value!; } - public override object ObjectDefault + public override object? ObjectDefault { get => Default; - set => Default = (T)value; + set => Default = (T)value!; } /// <inheritdoc /> /// <summary> /// Creates a <see cref="ConfigEntry{T}" /> with the provided type, default value, and indication for whether to inherit the value. /// </summary> - public ConfigEntry(string key, T defaultValue = default, bool inherit = true, string name = null, - string description = null) : base(key, inherit, name, description) + public ConfigEntry(string key, T defaultValue, bool inherit = true, string? name = null, + string? description = null) : base(key, inherit, name, description) { + Value = defaultValue; Default = defaultValue; } @@ -108,7 +109,7 @@ public ConfigEntry(string key, T defaultValue = default, bool inherit = true, st /// <summary> /// Creates a <see cref="ConfigEntry{T}" /> with the provided type and default value. /// </summary> - public ConfigEntry(string key, T defaultValue = default, string name = null, string description = null) : this( + public ConfigEntry(string key, T defaultValue, string? name = null, string? description = null) : this( key, defaultValue, true, name, description) { } diff --git a/MultiAdmin/Config/ConfigHandler/ConfigRegister.cs b/MultiAdmin/Config/ConfigHandler/ConfigRegister.cs index eb12762..81311c1 100644 --- a/MultiAdmin/Config/ConfigHandler/ConfigRegister.cs +++ b/MultiAdmin/Config/ConfigHandler/ConfigRegister.cs @@ -24,7 +24,7 @@ public ConfigEntry[] GetRegisteredConfigs() /// Returns the first <see cref="ConfigEntry"/> with a key matching <paramref name="key"/>. /// </summary> /// <param name="key">The key of the <see cref="ConfigEntry"/> to retrieve.</param> - public ConfigEntry GetRegisteredConfig(string key) + public ConfigEntry? GetRegisteredConfig(string key) { if (string.IsNullOrEmpty(key)) return null; @@ -47,7 +47,7 @@ public ConfigEntry GetRegisteredConfig(string key) /// <param name="updateValue">Whether to update the value of the config after registration.</param> public void RegisterConfig(ConfigEntry configEntry, bool updateValue = true) { - if (configEntry == null || string.IsNullOrEmpty(configEntry.Key)) + if (string.IsNullOrEmpty(configEntry.Key)) return; registeredConfigs.Add(configEntry); @@ -63,9 +63,6 @@ public void RegisterConfig(ConfigEntry configEntry, bool updateValue = true) /// <param name="updateValue">Whether to update the value of the config after registration.</param> public void RegisterConfigs(ConfigEntry[] configEntries, bool updateValue = true) { - if (configEntries == null) - return; - foreach (ConfigEntry configEntry in configEntries) { RegisterConfig(configEntry, updateValue); @@ -78,7 +75,7 @@ public void RegisterConfigs(ConfigEntry[] configEntries, bool updateValue = true /// <param name="configEntry">The <see cref="ConfigEntry"/> to be un-registered.</param> public void UnRegisterConfig(ConfigEntry configEntry) { - if (configEntry == null || string.IsNullOrEmpty(configEntry.Key)) + if (string.IsNullOrEmpty(configEntry.Key)) return; registeredConfigs.Remove(configEntry); @@ -90,7 +87,9 @@ public void UnRegisterConfig(ConfigEntry configEntry) /// <param name="key">The key of the <see cref="ConfigEntry"/> to be un-registered.</param> public void UnRegisterConfig(string key) { - UnRegisterConfig(GetRegisteredConfig(key)); + ConfigEntry? entry = GetRegisteredConfig(key); + if (entry != null) + UnRegisterConfig(entry); } /// <summary> @@ -99,9 +98,6 @@ public void UnRegisterConfig(string key) /// <param name="configEntries">The <see cref="ConfigEntry"/>s to be un-registered.</param> public void UnRegisterConfigs(params ConfigEntry[] configEntries) { - if (configEntries == null) - return; - foreach (ConfigEntry configEntry in configEntries) { UnRegisterConfig(configEntry); @@ -114,9 +110,6 @@ public void UnRegisterConfigs(params ConfigEntry[] configEntries) /// <param name="keys">The keys of the <see cref="ConfigEntry"/>s to be un-registered.</param> public void UnRegisterConfigs(params string[] keys) { - if (keys == null) - return; - foreach (string key in keys) { UnRegisterConfig(key); @@ -146,9 +139,6 @@ public void UnRegisterConfigs() /// <param name="configEntries">The <see cref="ConfigEntry"/>s to be assigned values.</param> public void UpdateConfigValues(params ConfigEntry[] configEntries) { - if (configEntries == null) - return; - foreach (ConfigEntry configEntry in configEntries) { UpdateConfigValue(configEntry); diff --git a/MultiAdmin/Config/ConfigHandler/InheritableConfigRegister.cs b/MultiAdmin/Config/ConfigHandler/InheritableConfigRegister.cs index 99e143e..e08ecbd 100644 --- a/MultiAdmin/Config/ConfigHandler/InheritableConfigRegister.cs +++ b/MultiAdmin/Config/ConfigHandler/InheritableConfigRegister.cs @@ -11,7 +11,7 @@ public abstract class InheritableConfigRegister : ConfigRegister /// Creates an <see cref="InheritableConfigRegister"/> with the parent <paramref name="parentConfigRegister"/> to inherit unset config values from. /// </summary> /// <param name="parentConfigRegister">The <see cref="ConfigRegister"/> to inherit unset config values from.</param> - protected InheritableConfigRegister(ConfigRegister parentConfigRegister = null) + protected InheritableConfigRegister(ConfigRegister? parentConfigRegister = null) { ParentConfigRegister = parentConfigRegister; } @@ -19,7 +19,7 @@ protected InheritableConfigRegister(ConfigRegister parentConfigRegister = null) /// <summary> /// The parent <see cref="ConfigRegister"/> to inherit from. /// </summary> - public ConfigRegister ParentConfigRegister { get; protected set; } + public ConfigRegister? ParentConfigRegister { get; protected set; } /// <summary> /// Returns whether <paramref name="configEntry"/> should be inherited from the parent <see cref="ConfigRegister"/>. @@ -34,7 +34,7 @@ protected InheritableConfigRegister(ConfigRegister parentConfigRegister = null) public abstract void UpdateConfigValueInheritable(ConfigEntry configEntry); /// <summary> - /// Updates the value of <paramref name="configEntry"/> from this <see cref="InheritableConfigRegister"/> if the <see cref="ParentConfigRegister"/> is null or if <seealso cref="ShouldInheritConfigEntry"/> returns true. + /// Updates the value of <paramref name="configEntry"/> from this <see cref="InheritableConfigRegister"/> if the <see cref="ParentConfigRegister"/> is null or if <seealso cref="ShouldInheritConfigEntry"/> returns false. /// </summary> /// <param name="configEntry">The <see cref="ConfigEntry"/> to be assigned a value.</param> public override void UpdateConfigValue(ConfigEntry configEntry) @@ -44,7 +44,7 @@ public override void UpdateConfigValue(ConfigEntry configEntry) { ParentConfigRegister.UpdateConfigValue(configEntry); } - else + else if (configEntry != null) { UpdateConfigValueInheritable(configEntry); } @@ -59,12 +59,12 @@ public ConfigRegister[] GetConfigRegisterHierarchy(bool highestToLowest = true) List<ConfigRegister> configRegisterHierarchy = new(); ConfigRegister configRegister = this; - while (configRegister != null && !configRegisterHierarchy.Contains(configRegister)) + while (!configRegisterHierarchy.Contains(configRegister)) { configRegisterHierarchy.Add(configRegister); // If there's another InheritableConfigRegister as a parent, then get the parent of that, otherwise, break the loop as there are no more parents - if (configRegister is InheritableConfigRegister inheritableConfigRegister) + if (configRegister is InheritableConfigRegister inheritableConfigRegister && inheritableConfigRegister.ParentConfigRegister != null) { configRegister = inheritableConfigRegister.ParentConfigRegister; } diff --git a/MultiAdmin/Config/MultiAdminConfig.cs b/MultiAdmin/Config/MultiAdminConfig.cs index d9c1d7b..edf9a7b 100644 --- a/MultiAdmin/Config/MultiAdminConfig.cs +++ b/MultiAdmin/Config/MultiAdminConfig.cs @@ -47,7 +47,7 @@ public class MultiAdminConfig : InheritableConfigRegister "MultiAdmin Debug Logging Blacklist", "Which tags to block for MultiAdmin debug logging"); public ConfigEntry<string[]> DebugLogWhitelist { get; } = - new ConfigEntry<string[]>("multiadmin_debug_log_whitelist", new string[0], + new ConfigEntry<string[]>("multiadmin_debug_log_whitelist", Array.Empty<string>(), "MultiAdmin Debug Logging Whitelist", "Which tags to log for MultiAdmin debug logging (Defaults to logging all if none are provided)"); public ConfigEntry<bool> UseNewInputSystem { get; } = @@ -71,23 +71,23 @@ public class MultiAdminConfig : InheritableConfigRegister "Copy from Folder on Reload", "The location of a folder to copy files from into the folder defined by `config_location` whenever the configuration file is reloaded"); public ConfigEntry<string[]> FolderCopyWhitelist { get; } = - new ConfigEntry<string[]>("folder_copy_whitelist", new string[0], + new ConfigEntry<string[]>("folder_copy_whitelist", Array.Empty<string>(), "Folder Copy Whitelist", "The list of file names to copy from the folder defined by `copy_from_folder_on_reload` (accepts `*` wildcards)"); public ConfigEntry<string[]> FolderCopyBlacklist { get; } = - new ConfigEntry<string[]>("folder_copy_blacklist", new string[0], + new ConfigEntry<string[]>("folder_copy_blacklist", Array.Empty<string>(), "Folder Copy Blacklist", "The list of file names to not copy from the folder defined by `copy_from_folder_on_reload` (accepts `*` wildcards)"); public ConfigEntry<string[]> FolderCopyRoundQueue { get; } = - new ConfigEntry<string[]>("folder_copy_round_queue", new string[0], + new ConfigEntry<string[]>("folder_copy_round_queue", Array.Empty<string>(), "Folder Copy Round Queue", "The location of a folder to copy files from into the folder defined by `config_location` after each round, looping through the locations"); public ConfigEntry<string[]> FolderCopyRoundQueueWhitelist { get; } = - new ConfigEntry<string[]>("folder_copy_round_queue_whitelist", new string[0], + new ConfigEntry<string[]>("folder_copy_round_queue_whitelist", Array.Empty<string>(), "Folder Copy Round Queue Whitelist", "The list of file names to copy from the folders defined by `folder_copy_round_queue` (accepts `*` wildcards)"); public ConfigEntry<string[]> FolderCopyRoundQueueBlacklist { get; } = - new ConfigEntry<string[]>("folder_copy_round_queue_blacklist", new string[0], + new ConfigEntry<string[]>("folder_copy_round_queue_blacklist", Array.Empty<string>(), "Folder Copy Round Queue Blacklist", "The list of file names to not copy from the folders defined by `folder_copy_round_queue` (accepts `*` wildcards)"); public ConfigEntry<bool> RandomizeFolderCopyRoundQueue { get; } = @@ -197,19 +197,19 @@ public InputHandler.ConsoleInputSystem ActualConsoleInputSystem } public const string ConfigFileName = "scp_multiadmin.cfg"; - public static readonly string GlobalConfigFilePath = Utils.GetFullPathSafe(ConfigFileName); + public static readonly string GlobalConfigFilePath = Utils.GetFullPathSafe(ConfigFileName) ?? throw new FileNotFoundException($"Config file \"{nameof(GlobalConfigFilePath)}\" was not set", ConfigFileName); public static readonly MultiAdminConfig GlobalConfig = new(GlobalConfigFilePath, null); - public MultiAdminConfig ParentConfig + public MultiAdminConfig? ParentConfig { get => ParentConfigRegister as MultiAdminConfig; protected set => ParentConfigRegister = value; } - public Config Config { get; } + public Config? Config { get; } - public MultiAdminConfig(Config config, MultiAdminConfig parentConfig, bool createConfig = true) + public MultiAdminConfig(Config? config, MultiAdminConfig? parentConfig, bool createConfig = true) { Config = config; ParentConfig = parentConfig; @@ -247,16 +247,16 @@ public MultiAdminConfig(Config config, MultiAdminConfig parentConfig, bool creat ReloadConfig(); } - public MultiAdminConfig(Config config, bool createConfig = true) : this(config, GlobalConfig, createConfig) + public MultiAdminConfig(Config? config, bool createConfig = true) : this(config, GlobalConfig, createConfig) { } - public MultiAdminConfig(string path, MultiAdminConfig parentConfig, bool createConfig = true) : this( - new Config(path), parentConfig, createConfig) + public MultiAdminConfig(string? path, MultiAdminConfig? parentConfig, bool createConfig = true) : this( + path != null ? new Config(path) : null, parentConfig, createConfig) { } - public MultiAdminConfig(string path, bool createConfig = true) : this(path, GlobalConfig, createConfig) + public MultiAdminConfig(string? path, bool createConfig = true) : this(path, GlobalConfig, createConfig) { } @@ -277,13 +277,13 @@ public override void UpdateConfigValueInheritable(ConfigEntry configEntry) { case ConfigEntry<string> config: { - config.Value = Config.GetString(config.Key, config.Default); + config.Value = Config.GetString(config.Key, config.Default) ?? config.Default; break; } case ConfigEntry<string[]> config: { - config.Value = Config.GetStringArray(config.Key, config.Default); + config.Value = Config.GetStringArray(config.Key, config.Default) ?? config.Default; break; } @@ -376,9 +376,9 @@ public MultiAdminConfig[] GetConfigHierarchy(bool highestToLowest = true) return configHierarchy.ToArray(); } - public bool ConfigHierarchyContainsPath(string path) + public bool ConfigHierarchyContainsPath(string? path) { - string fullPath = Utils.GetFullPathSafe(path); + string? fullPath = Utils.GetFullPathSafe(path) ?? path; return !string.IsNullOrEmpty(fullPath) && GetConfigHierarchy().Any(config => config.Config?.ConfigPath == path); diff --git a/MultiAdmin/ConsoleTools/ColoredConsole.cs b/MultiAdmin/ConsoleTools/ColoredConsole.cs index 50f85bc..0706753 100644 --- a/MultiAdmin/ConsoleTools/ColoredConsole.cs +++ b/MultiAdmin/ConsoleTools/ColoredConsole.cs @@ -7,7 +7,7 @@ public static class ColoredConsole { public static readonly object WriteLock = new(); - public static void Write(string text, ConsoleColor? textColor = null, ConsoleColor? backgroundColor = null) + public static void Write(string? text, ConsoleColor? textColor = null, ConsoleColor? backgroundColor = null) { lock (WriteLock) { @@ -36,7 +36,7 @@ public static void Write(string text, ConsoleColor? textColor = null, ConsoleCol } } - public static void WriteLine(string text, ConsoleColor? textColor = null, ConsoleColor? backgroundColor = null) + public static void WriteLine(string? text, ConsoleColor? textColor = null, ConsoleColor? backgroundColor = null) { lock (WriteLock) { @@ -46,11 +46,11 @@ public static void WriteLine(string text, ConsoleColor? textColor = null, Consol } } - public static void Write(params ColoredMessage[] message) + public static void Write(params ColoredMessage?[] message) { lock (WriteLock) { - foreach (ColoredMessage coloredMessage in message) + foreach (ColoredMessage? coloredMessage in message) { if (coloredMessage != null) Write(coloredMessage.text, coloredMessage.textColor, coloredMessage.backgroundColor); @@ -58,7 +58,7 @@ public static void Write(params ColoredMessage[] message) } } - public static void WriteLine(params ColoredMessage[] message) + public static void WriteLine(params ColoredMessage?[] message) { lock (WriteLock) { @@ -68,24 +68,24 @@ public static void WriteLine(params ColoredMessage[] message) } } - public static void WriteLines(params ColoredMessage[] message) + public static void WriteLines(params ColoredMessage?[] message) { lock (WriteLock) { - foreach (ColoredMessage coloredMessage in message) WriteLine(coloredMessage); + foreach (ColoredMessage? coloredMessage in message) WriteLine(coloredMessage); } } } public class ColoredMessage : ICloneable { - public string text; + public string? text; public ConsoleColor? textColor; public ConsoleColor? backgroundColor; public int Length => text?.Length ?? 0; - public ColoredMessage(string text, ConsoleColor? textColor = null, ConsoleColor? backgroundColor = null) + public ColoredMessage(string? text, ConsoleColor? textColor = null, ConsoleColor? backgroundColor = null) { this.text = text; this.textColor = textColor; @@ -98,7 +98,7 @@ public bool Equals(ColoredMessage other) backgroundColor == other.backgroundColor; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { if (ReferenceEquals(null, obj)) { @@ -120,16 +120,10 @@ public override bool Equals(object obj) public override int GetHashCode() { - unchecked - { - int hashCode = text != null ? text.GetHashCode() : 0; - hashCode = (hashCode * 397) ^ textColor.GetHashCode(); - hashCode = (hashCode * 397) ^ backgroundColor.GetHashCode(); - return hashCode; - } + return HashCode.Combine(text, textColor, backgroundColor); } - public static bool operator ==(ColoredMessage firstMessage, ColoredMessage secondMessage) + public static bool operator ==(ColoredMessage? firstMessage, ColoredMessage? secondMessage) { if (ReferenceEquals(firstMessage, secondMessage)) return true; @@ -140,12 +134,12 @@ public override int GetHashCode() return firstMessage.Equals(secondMessage); } - public static bool operator !=(ColoredMessage firstMessage, ColoredMessage secondMessage) + public static bool operator !=(ColoredMessage? firstMessage, ColoredMessage? secondMessage) { return !(firstMessage == secondMessage); } - public override string ToString() + public override string? ToString() { return text; } @@ -179,11 +173,11 @@ public void WriteLine(bool clearConsoleLine = false) public static class ColoredMessageArrayExtensions { - private static string JoinTextIgnoreNull(ColoredMessage[] coloredMessages) + private static string JoinTextIgnoreNull(ColoredMessage?[] coloredMessages) { StringBuilder builder = new(""); - foreach (ColoredMessage coloredMessage in coloredMessages) + foreach (ColoredMessage? coloredMessage in coloredMessages) { if (coloredMessage != null) builder.Append(coloredMessage); @@ -192,12 +186,12 @@ private static string JoinTextIgnoreNull(ColoredMessage[] coloredMessages) return builder.ToString(); } - public static string GetText(this ColoredMessage[] message) + public static string GetText(this ColoredMessage?[] message) { return JoinTextIgnoreNull(message); } - public static void Write(this ColoredMessage[] message, bool clearConsoleLine = false) + public static void Write(this ColoredMessage?[] message, bool clearConsoleLine = false) { lock (ColoredConsole.WriteLock) { @@ -205,7 +199,7 @@ public static void Write(this ColoredMessage[] message, bool clearConsoleLine = } } - public static void WriteLine(this ColoredMessage[] message, bool clearConsoleLine = false) + public static void WriteLine(this ColoredMessage?[] message, bool clearConsoleLine = false) { lock (ColoredConsole.WriteLock) { @@ -213,7 +207,7 @@ public static void WriteLine(this ColoredMessage[] message, bool clearConsoleLin } } - public static void WriteLines(this ColoredMessage[] message, bool clearConsoleLine = false) + public static void WriteLines(this ColoredMessage?[] message, bool clearConsoleLine = false) { lock (ColoredConsole.WriteLock) { diff --git a/MultiAdmin/ConsoleTools/ConsolePositioning.cs b/MultiAdmin/ConsoleTools/ConsolePositioning.cs index 028bac7..3767cd1 100644 --- a/MultiAdmin/ConsoleTools/ConsolePositioning.cs +++ b/MultiAdmin/ConsoleTools/ConsolePositioning.cs @@ -41,7 +41,7 @@ public static BufferPoint BufferBottom #endregion } - public struct ConsolePoint + public readonly struct ConsolePoint { public readonly int x, y; @@ -74,7 +74,7 @@ public void SetAsCursorY() } } - public struct BufferPoint + public readonly struct BufferPoint { public readonly int x, y; diff --git a/MultiAdmin/ConsoleTools/ConsoleUtils.cs b/MultiAdmin/ConsoleTools/ConsoleUtils.cs index 28d94ef..c05ff16 100644 --- a/MultiAdmin/ConsoleTools/ConsoleUtils.cs +++ b/MultiAdmin/ConsoleTools/ConsoleUtils.cs @@ -45,7 +45,7 @@ public static void ClearConsoleLine(int index, bool returnCursorPos = false) } } - public static string ClearConsoleLine(string message) + public static string? ClearConsoleLine(string? message) { if (!string.IsNullOrEmpty(message)) ClearConsoleLine(message.Contains(Environment.NewLine) ? 0 : message.Length); @@ -55,16 +55,16 @@ public static string ClearConsoleLine(string message) return message; } - public static ColoredMessage ClearConsoleLine(ColoredMessage message) + public static ColoredMessage? ClearConsoleLine(ColoredMessage? message) { ClearConsoleLine(message?.text); return message; } - public static ColoredMessage[] ClearConsoleLine(ColoredMessage[] message) + public static ColoredMessage?[] ClearConsoleLine(ColoredMessage?[] message) { ClearConsoleLine(message?.GetText()); - return message; + return message!; } #endregion diff --git a/MultiAdmin/Features/FolderCopyRoundQueue.cs b/MultiAdmin/Features/FolderCopyRoundQueue.cs index bbe9fb1..f0eb36a 100644 --- a/MultiAdmin/Features/FolderCopyRoundQueue.cs +++ b/MultiAdmin/Features/FolderCopyRoundQueue.cs @@ -7,9 +7,9 @@ namespace MultiAdmin.Features [Feature] internal class FileCopyRoundQueue : Feature, IEventRoundEnd { - private string[] queue; - private string[] whitelist; - private string[] blacklist; + private string[] queue = Array.Empty<string>(); + private string[] whitelist = Array.Empty<string>(); + private string[] blacklist = Array.Empty<string>(); private bool randomizeQueue; private int queueIndex; @@ -80,9 +80,9 @@ public override void Init() public override void OnConfigReload() { - queue = Server.ServerConfig.FolderCopyRoundQueue.Value; - whitelist = Server.ServerConfig.FolderCopyRoundQueueWhitelist.Value; - blacklist = Server.ServerConfig.FolderCopyRoundQueueBlacklist.Value; + queue = Server.ServerConfig.FolderCopyRoundQueue.Value ?? Array.Empty<string>(); + whitelist = Server.ServerConfig.FolderCopyRoundQueueWhitelist.Value ?? Array.Empty<string>(); + blacklist = Server.ServerConfig.FolderCopyRoundQueueBlacklist.Value ?? Array.Empty<string>(); randomizeQueue = Server.ServerConfig.RandomizeFolderCopyRoundQueue.Value; } diff --git a/MultiAdmin/Features/NewCommand.cs b/MultiAdmin/Features/NewCommand.cs index 2b0d355..c660ae2 100644 --- a/MultiAdmin/Features/NewCommand.cs +++ b/MultiAdmin/Features/NewCommand.cs @@ -7,8 +7,8 @@ namespace MultiAdmin.Features [Feature] internal class NewCommand : Feature, ICommand, IEventServerFull { - private string onFullServerId; - private Process onFullServerInstance; + private string? onFullServerId; + private Process? onFullServerInstance; public NewCommand(Server server) : base(server) { diff --git a/MultiAdmin/MultiAdmin.csproj b/MultiAdmin/MultiAdmin.csproj index d313540..ed63baf 100644 --- a/MultiAdmin/MultiAdmin.csproj +++ b/MultiAdmin/MultiAdmin.csproj @@ -5,6 +5,7 @@ <LangVersion>11</LangVersion> <RootNamespace>MultiAdmin</RootNamespace> <ApplicationIcon>Icon.ico</ApplicationIcon> + <Nullable>enable</Nullable> <GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> <GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute> diff --git a/MultiAdmin/NativeExitSignal/IExitSignal.cs b/MultiAdmin/NativeExitSignal/IExitSignal.cs index 00b353f..d3acd1f 100644 --- a/MultiAdmin/NativeExitSignal/IExitSignal.cs +++ b/MultiAdmin/NativeExitSignal/IExitSignal.cs @@ -4,6 +4,6 @@ namespace MultiAdmin.NativeExitSignal { public interface IExitSignal { - event EventHandler Exit; + event EventHandler? Exit; } } diff --git a/MultiAdmin/NativeExitSignal/UnixExitSignal.cs b/MultiAdmin/NativeExitSignal/UnixExitSignal.cs index 715e9e5..ac5cbaa 100644 --- a/MultiAdmin/NativeExitSignal/UnixExitSignal.cs +++ b/MultiAdmin/NativeExitSignal/UnixExitSignal.cs @@ -8,7 +8,7 @@ namespace MultiAdmin.NativeExitSignal { public class UnixExitSignal : IExitSignal { - public event EventHandler Exit; + public event EventHandler? Exit; private static readonly UnixSignal[] Signals = { new UnixSignal(Signum.SIGINT), // CTRL + C pressed diff --git a/MultiAdmin/NativeExitSignal/WinExitSignal.cs b/MultiAdmin/NativeExitSignal/WinExitSignal.cs index a8ac2c8..8a1c7f7 100644 --- a/MultiAdmin/NativeExitSignal/WinExitSignal.cs +++ b/MultiAdmin/NativeExitSignal/WinExitSignal.cs @@ -5,7 +5,7 @@ namespace MultiAdmin.NativeExitSignal { public class WinExitSignal : IExitSignal { - public event EventHandler Exit; + public event EventHandler? Exit; [DllImport("Kernel32")] public static extern bool SetConsoleCtrlHandler(HandlerRoutine handler, bool add); diff --git a/MultiAdmin/Program.cs b/MultiAdmin/Program.cs index 624ca83..08500cf 100644 --- a/MultiAdmin/Program.cs +++ b/MultiAdmin/Program.cs @@ -3,7 +3,6 @@ using System.Diagnostics; using System.IO; using System.Linq; -using System.Reflection; using System.Threading; using MultiAdmin.Config; using MultiAdmin.ConsoleTools; @@ -15,23 +14,23 @@ namespace MultiAdmin { public static class Program { - public const string MaVersion = "3.4.1.0"; + public const string MaVersion = "3.5.0.0"; private static readonly List<Server> InstantiatedServers = new(); private static readonly string MaDebugLogDir = - Utils.GetFullPathSafe(MultiAdminConfig.GlobalConfig.LogLocation.Value); + Utils.GetFullPathSafe(MultiAdminConfig.GlobalConfig.LogLocation.Value) ?? throw new FileNotFoundException($"Log file \"{nameof(MaDebugLogDir)}\" was not set", MultiAdminConfig.GlobalConfig.LogLocation.Value); - private static readonly string MaDebugLogFile = !string.IsNullOrEmpty(MaDebugLogDir) + private static readonly string? MaDebugLogFile = !string.IsNullOrEmpty(MaDebugLogDir) ? Utils.GetFullPathSafe(Path.Combine(MaDebugLogDir, $"{Utils.DateTime}_MA_{MaVersion}_debug_log.txt")) : null; - private static StreamWriter debugLogStream = null; + private static StreamWriter? debugLogStream = null; private static uint? portArg; - public static readonly string[] Args = Environment.GetCommandLineArgs(); + public static readonly string?[] Args = Environment.GetCommandLineArgs(); - private static IExitSignal exitSignalListener; + private static IExitSignal? exitSignalListener; private static bool exited = false; private static readonly object ExitLock = new(); @@ -45,14 +44,14 @@ public static string[] ServerDirectories { get { - string globalServersFolder = MultiAdminConfig.GlobalConfig.ServersFolder.Value; + string globalServersFolder = MultiAdminConfig.GlobalConfig.ServersFolder.Value!; return !Directory.Exists(globalServersFolder) - ? new string[] { } + ? Array.Empty<string>() : Directory.GetDirectories(globalServersFolder); } } - public static string[] ServerIds => Servers.Select(server => server.serverId).ToArray(); + public static string[] ServerIds => Servers.Select(server => server.serverId).OfType<string>().ToArray(); #endregion @@ -62,10 +61,10 @@ public static string[] ServerDirectories Servers.Where(server => !server.ServerConfig.ManualStart.Value).ToArray(); public static string[] AutoStartServerDirectories => - AutoStartServers.Select(autoStartServer => autoStartServer.serverDir).ToArray(); + AutoStartServers.Select(autoStartServer => autoStartServer.serverDir).OfType<string>().ToArray(); public static string[] AutoStartServerIds => - AutoStartServers.Select(autoStartServer => autoStartServer.serverId).ToArray(); + AutoStartServers.Select(autoStartServer => autoStartServer.serverId).OfType<string>().ToArray(); #endregion @@ -92,6 +91,7 @@ private static bool IsDebugLogTagAllowed(string tag) public static void LogDebugException(string tag, Exception exception) { + if (MaDebugLogFile == null) return; lock (MaDebugLogFile) { if (tag == null || !IsDebugLogTagAllowed(tag)) return; @@ -102,6 +102,7 @@ public static void LogDebugException(string tag, Exception exception) public static void LogDebug(string tag, string message) { + if (MaDebugLogFile == null) return; lock (MaDebugLogFile) { try @@ -135,7 +136,7 @@ public static void LogDebug(string tag, string message) #endregion - private static void OnExit(object sender, EventArgs e) + private static void OnExit(object? sender, EventArgs e) { lock (ExitLock) { @@ -221,11 +222,11 @@ public static void Main() Headless = GetFlagFromArgs(Args, "headless", "h"); - string serverIdArg = GetParamFromArgs(Args, "server-id", "id"); - string configArg = GetParamFromArgs(Args, "config", "c"); - portArg = uint.TryParse(GetParamFromArgs(Args, "port", "p"), out uint port) ? (uint?)port : null; + string? serverIdArg = GetParamFromArgs(Args, "server-id", "id"); + string? configArg = GetParamFromArgs(Args, "config", "c"); + portArg = uint.TryParse(GetParamFromArgs(Args, "port", "p"), out uint port) ? port : null; - Server server = null; + Server? server = null; if (!string.IsNullOrEmpty(serverIdArg) || !string.IsNullOrEmpty(configArg)) { @@ -294,7 +295,7 @@ public static void Main() } } - public static string GetParamFromArgs(string[] args, string[] keys = null, string[] aliases = null) + public static string? GetParamFromArgs(string?[] args, string[]? keys = null, string[]? aliases = null) { bool hasKeys = !keys.IsNullOrEmpty(); bool hasAliases = !aliases.IsNullOrEmpty(); @@ -303,15 +304,15 @@ public static string GetParamFromArgs(string[] args, string[] keys = null, strin for (int i = 0; i < args.Length - 1; i++) { - string lowArg = args[i]?.ToLower(); + string? lowArg = args[i]?.ToLower(); if (string.IsNullOrEmpty(lowArg)) continue; if (hasKeys) { - if (keys.Any(key => lowArg == $"--{key?.ToLower()}")) + if (keys?.Any(key => lowArg == $"--{key?.ToLower()}") == true) { - string value = args[i + 1]; + string? value = args[i + 1]; args[i] = null; args[i + 1] = null; @@ -322,9 +323,9 @@ public static string GetParamFromArgs(string[] args, string[] keys = null, strin if (hasAliases) { - if (aliases.Any(alias => lowArg == $"-{alias?.ToLower()}")) + if (aliases?.Any(alias => lowArg == $"-{alias?.ToLower()}") == true) { - string value = args[i + 1]; + string? value = args[i + 1]; args[i] = null; args[i + 1] = null; @@ -337,7 +338,7 @@ public static string GetParamFromArgs(string[] args, string[] keys = null, strin return null; } - public static bool ArgsContainsParam(string[] args, string[] keys = null, string[] aliases = null) + public static bool ArgsContainsParam(string?[] args, string[]? keys = null, string[]? aliases = null) { bool hasKeys = !keys.IsNullOrEmpty(); bool hasAliases = !aliases.IsNullOrEmpty(); @@ -346,13 +347,13 @@ public static bool ArgsContainsParam(string[] args, string[] keys = null, string for (int i = 0; i < args.Length; i++) { - string lowArg = args[i]?.ToLower(); + string? lowArg = args[i]?.ToLower(); if (string.IsNullOrEmpty(lowArg)) continue; if (hasKeys) { - if (keys.Any(key => lowArg == $"--{key?.ToLower()}")) + if (keys?.Any(key => lowArg == $"--{key?.ToLower()}") == true) { args[i] = null; return true; @@ -361,7 +362,7 @@ public static bool ArgsContainsParam(string[] args, string[] keys = null, string if (hasAliases) { - if (aliases.Any(alias => lowArg == $"-{alias?.ToLower()}")) + if (aliases?.Any(alias => lowArg == $"-{alias?.ToLower()}") == true) { args[i] = null; return true; @@ -372,7 +373,7 @@ public static bool ArgsContainsParam(string[] args, string[] keys = null, string return false; } - public static bool GetFlagFromArgs(string[] args, string[] keys = null, string[] aliases = null) + public static bool GetFlagFromArgs(string?[] args, string[]? keys = null, string[]? aliases = null) { if (keys.IsNullOrEmpty() && aliases.IsNullOrEmpty()) return false; @@ -381,31 +382,31 @@ public static bool GetFlagFromArgs(string[] args, string[] keys = null, string[] : ArgsContainsParam(args, keys, aliases); } - public static string GetParamFromArgs(string[] args, string key = null, string alias = null) + public static string? GetParamFromArgs(string?[] args, string? key = null, string? alias = null) { - return GetParamFromArgs(args, new string[] { key }, new string[] { alias }); + return GetParamFromArgs(args, key != null ? new string[] { key } : null, alias != null ? new string[] { alias } : null); } - public static bool ArgsContainsParam(string[] args, string key = null, string alias = null) + public static bool ArgsContainsParam(string?[] args, string? key = null, string? alias = null) { - return ArgsContainsParam(args, new string[] { key }, new string[] { alias }); + return ArgsContainsParam(args, key != null ? new string[] { key } : null, alias != null ? new string[] { alias } : null); } - public static bool GetFlagFromArgs(string[] args, string key = null, string alias = null) + public static bool GetFlagFromArgs(string?[] args, string? key = null, string? alias = null) { - return GetFlagFromArgs(args, new string[] { key }, new string[] { alias }); + return GetFlagFromArgs(args, key != null ? new string[] { key } : null, alias != null ? new string[] { alias } : null); } - public static Process StartServer(Server server) + public static Process? StartServer(Server server) { - string assemblyLocation = Assembly.GetEntryAssembly()?.Location; + string assemblyLocation = AppContext.BaseDirectory; if (string.IsNullOrEmpty(assemblyLocation)) { Write("Error while starting new server: Could not find the executable location!", ConsoleColor.Red); } - List<string> args = new(server.args); + List<string?> args = server.args != null ? new(server.args) : new(); if (!string.IsNullOrEmpty(server.serverId)) { @@ -426,9 +427,9 @@ public static Process StartServer(Server server) Write($"Launching \"{startInfo.FileName}\" with arguments \"{startInfo.Arguments}\"..."); - Process serverProcess = Process.Start(startInfo); + Process? serverProcess = Process.Start(startInfo); - InstantiatedServers.Add(server); + if (serverProcess != null) InstantiatedServers.Add(server); return serverProcess; } diff --git a/MultiAdmin/Server.cs b/MultiAdmin/Server.cs index f9ace0f..83624df 100644 --- a/MultiAdmin/Server.cs +++ b/MultiAdmin/Server.cs @@ -26,11 +26,11 @@ public class Server private readonly MultiAdminConfig serverConfig; public MultiAdminConfig ServerConfig => serverConfig ?? MultiAdminConfig.GlobalConfig; - public readonly string serverId; - public readonly string configLocation; + public readonly string? serverId; + public readonly string? configLocation; private readonly uint? port; - public readonly string[] args; - public readonly string serverDir; + public readonly string?[]? args; + public readonly string? serverDir; public readonly string logDir; public uint Port => port ?? ServerConfig.Port.Value; @@ -40,12 +40,12 @@ public class Server public ModFeatures supportedModFeatures = ModFeatures.None; - public Server(string serverId = null, string configLocation = null, uint? port = null, string[] args = null) + public Server(string? serverId = null, string? configLocation = null, uint? port = null, string?[]? args = null) { this.serverId = serverId; - serverDir = string.IsNullOrEmpty(this.serverId) + serverDir = string.IsNullOrEmpty(serverId) ? null - : Utils.GetFullPathSafe(Path.Combine(MultiAdminConfig.GlobalConfig.ServersFolder.Value, this.serverId)); + : Utils.GetFullPathSafe(Path.Combine(MultiAdminConfig.GlobalConfig.ServersFolder.Value, serverId)); this.configLocation = Utils.GetFullPathSafe(configLocation) ?? Utils.GetFullPathSafe(MultiAdminConfig.GlobalConfig.ConfigLocation.Value) ?? @@ -55,7 +55,7 @@ public Server(string serverId = null, string configLocation = null, uint? port = serverConfig = MultiAdminConfig.GlobalConfig; // Load config hierarchy - string serverConfigLocation = this.configLocation; + string? serverConfigLocation = this.configLocation; while (!string.IsNullOrEmpty(serverConfigLocation)) { // Update the Server object's config location with the valid config location @@ -81,7 +81,7 @@ public Server(string serverId = null, string configLocation = null, uint? port = this.args = args; logDir = Utils.GetFullPathSafe(Path.Combine(string.IsNullOrEmpty(serverDir) ? "" : serverDir, - serverConfig.LogLocation.Value)); + serverConfig.LogLocation.Value)) ?? throw new FileNotFoundException($"Log file \"{nameof(logDir)}\" was not set"); // Register all features RegisterFeatures(); @@ -131,9 +131,9 @@ public bool SetServerRequestedStatus(ServerStatus status) #endregion - private string startDateTime; + private string? startDateTime; - public string StartDateTime + public string? StartDateTime { get => startDateTime; @@ -160,13 +160,13 @@ private set public bool CheckRestartTimeout => (DateTime.Now - initRestartTimeoutTime).Seconds > ServerConfig.ServerRestartTimeout.Value; - public string LogDirFile { get; private set; } - public string MaLogFile { get; private set; } - public string ScpLogFile { get; private set; } + public string? LogDirFile { get; private set; } + public string? MaLogFile { get; private set; } + public string? ScpLogFile { get; private set; } - private StreamWriter maLogStream; + private StreamWriter? maLogStream; - public Process GameProcess { get; private set; } + public Process? GameProcess { get; private set; } public bool IsGameProcessRunning { @@ -182,9 +182,9 @@ public bool IsGameProcessRunning } - public static readonly string DedicatedDir = Utils.GetFullPathSafe(Path.Combine("SCPSL_Data", "Dedicated")); + public static readonly string? DedicatedDir = Utils.GetFullPathSafe(Path.Combine("SCPSL_Data", "Dedicated")); - public ServerSocket SessionSocket { get; private set; } + public ServerSocket? SessionSocket { get; private set; } #region Server Core @@ -297,7 +297,7 @@ public void StartServer(bool restartOnCrash = true) // Set up logging maLogStream?.Close(); Directory.CreateDirectory(logDir); - maLogStream = File.AppendText(MaLogFile); + maLogStream = File.AppendText(MaLogFile ?? throw new FileNotFoundException($"Log file \"{nameof(MaLogFile)}\" was not set")); #region Startup Info Printing & Logging @@ -322,7 +322,7 @@ public void StartServer(bool restartOnCrash = true) SessionSocket = consoleSocket; - List<string> scpslArgs = new() + List<string?> scpslArgs = new() { $"-multiadmin:{Program.MaVersion}:{(int)ModFeatures.All}", "-batchmode", @@ -371,7 +371,7 @@ public void StartServer(bool restartOnCrash = true) scpslArgs.Add(configLocation); } - string appDataPath = Utils.GetFullPathSafe(ServerConfig.AppDataLocation.Value); + string? appDataPath = Utils.GetFullPathSafe(ServerConfig.AppDataLocation.Value); if (!string.IsNullOrEmpty(appDataPath)) { scpslArgs.Add("-appdatapath"); @@ -379,7 +379,7 @@ public void StartServer(bool restartOnCrash = true) } // Add custom arguments - scpslArgs.AddRange(args); + if (args != null) scpslArgs.AddRange(args); ProcessStartInfo startInfo = new(scpslExe, scpslArgs.JoinArgs()) { @@ -399,7 +399,7 @@ public void StartServer(bool restartOnCrash = true) // Start the input reader CancellationTokenSource inputHandlerCancellation = new(); - Task inputHandler = null; + Task? inputHandler = null; if (!Program.Headless) { @@ -448,7 +448,7 @@ public void StartServer(bool restartOnCrash = true) } // Cleanup after exit from MainLoop - GameProcess.Dispose(); + GameProcess?.Dispose(); GameProcess = null; // Stop the input handler if it's running @@ -535,7 +535,7 @@ public void StopServer(bool killGame = false) SetStopStatus(killGame); if ((killGame || !SendMessage("QUIT")) && IsGameProcessRunning) - GameProcess.Kill(); + GameProcess?.Kill(); } public void SetRestartStatus() @@ -553,7 +553,7 @@ public void RestartServer(bool killGame = false) SetRestartStatus(); if ((killGame || !SendMessage("SOFTRESTART")) && IsGameProcessRunning) - GameProcess.Kill(); + GameProcess?.Kill(); } #endregion @@ -612,7 +612,7 @@ private void RegisterFeatures() { try { - object featureInstance = Activator.CreateInstance(type, this); + object? featureInstance = Activator.CreateInstance(type, this); if (featureInstance is Feature feature) RegisterFeature(feature); } catch (Exception e) @@ -642,7 +642,7 @@ public void ForEachHandler<T>(Action<T> action) where T : IMAEvent #region Console Output and Logging - public void Write(ColoredMessage[] messages, ConsoleColor? timeStampColor = null) + public void Write(ColoredMessage?[] messages, ConsoleColor? timeStampColor = null) { lock (ColoredConsole.WriteLock) { @@ -652,7 +652,7 @@ public void Write(ColoredMessage[] messages, ConsoleColor? timeStampColor = null if (Program.Headless) return; - ColoredMessage[] timeStampedMessage = Utils.TimeStampMessage(messages, timeStampColor); + ColoredMessage?[] timeStampedMessage = Utils.TimeStampMessage(messages, timeStampColor); timeStampedMessage.WriteLine(ServerConfig.ActualConsoleInputSystem == InputHandler.ConsoleInputSystem.New); @@ -682,7 +682,7 @@ public void Log(string message) { lock (ColoredConsole.WriteLock) { - if (message == null || string.IsNullOrEmpty(MaLogFile) || ServerConfig.NoLog.Value) return; + if (maLogStream == null || string.IsNullOrEmpty(MaLogFile) || ServerConfig.NoLog.Value) return; try { @@ -725,7 +725,7 @@ public void ReloadConfig(bool copyFiles = true, bool runEvent = true) feature.OnConfigReload(); } - public bool CopyFromDir(string sourceDir, string[] fileWhitelist = null, string[] fileBlacklist = null) + public bool CopyFromDir(string? sourceDir, string[]? fileWhitelist = null, string[]? fileBlacklist = null) { if (string.IsNullOrEmpty(configLocation) || string.IsNullOrEmpty(sourceDir)) return false; diff --git a/MultiAdmin/ServerIO/InputHandler.cs b/MultiAdmin/ServerIO/InputHandler.cs index 745c82e..6993f23 100644 --- a/MultiAdmin/ServerIO/InputHandler.cs +++ b/MultiAdmin/ServerIO/InputHandler.cs @@ -41,8 +41,8 @@ public static int SectionBufferWidth } } - public static string CurrentMessage { get; private set; } - public static ColoredMessage[] CurrentInput { get; private set; } = { InputPrefix }; + public static string? CurrentMessage { get; private set; } + public static ColoredMessage?[]? CurrentInput { get; private set; } = { InputPrefix }; public static int CurrentCursor { get; private set; } public static async void Write(Server server, CancellationToken cancellationToken) @@ -58,10 +58,10 @@ public static async void Write(Server server, CancellationToken cancellationToke break; } - string message; + string? message; if (server.ServerConfig.ActualConsoleInputSystem == ConsoleInputSystem.New && SectionBufferWidth - TotalIndicatorLength > 0) { - message = await GetInputLineNew(server, cancellationToken, prevMessages); + message = await GetInputLineNew(server, prevMessages, cancellationToken); } else if (server.ServerConfig.ActualConsoleInputSystem == ConsoleInputSystem.Old) { @@ -77,12 +77,11 @@ public static async void Write(Server server, CancellationToken cancellationToke server.Write($">>> {message}", ConsoleColor.DarkMagenta); int separatorIndex = message.IndexOfAny(Separator); - string commandName = (separatorIndex < 0 ? message : message.Substring(0, separatorIndex)).ToLower().Trim(); + string commandName = (separatorIndex < 0 ? message : message[..separatorIndex]).ToLower().Trim(); if (commandName.IsNullOrEmpty()) continue; bool callServer = true; - server.commands.TryGetValue(commandName, out ICommand command); - if (command != null) + if (server.commands.TryGetValue(commandName, out ICommand? command)) { try { @@ -147,7 +146,7 @@ public static async Task<string> GetInputLineOld(Server server, CancellationToke } } - public static async Task<string> GetInputLineNew(Server server, CancellationToken cancellationToken, ShiftingList prevMessages) + public static async Task<string> GetInputLineNew(Server server, ShiftingList prevMessages, CancellationToken cancellationToken) { if (server.ServerConfig.RandomInputColors.Value) RandomizeInputColors(); @@ -156,7 +155,7 @@ public static async Task<string> GetInputLineNew(Server server, CancellationToke string message = ""; int messageCursor = 0; int prevMessageCursor = -1; - StringSections curSections = null; + StringSections? curSections = null; int lastSectionIndex = -1; bool exitLoop = false; while (!exitLoop) @@ -353,9 +352,9 @@ public static void ResetInputParams() CurrentCursor = 0; } - public static void SetCurrentInput(params ColoredMessage[] coloredMessages) + public static void SetCurrentInput(params ColoredMessage?[]? coloredMessages) { - List<ColoredMessage> message = new() { InputPrefix }; + List<ColoredMessage?> message = new() { InputPrefix }; if (coloredMessages != null) message.AddRange(coloredMessages); @@ -365,7 +364,7 @@ public static void SetCurrentInput(params ColoredMessage[] coloredMessages) public static void SetCurrentInput(string message) { - ColoredMessage baseSection = BaseSection?.Clone(); + ColoredMessage? baseSection = BaseSection?.Clone(); if (baseSection == null) baseSection = new ColoredMessage(message); @@ -411,7 +410,7 @@ public static void SetCursor() SetCursor(CurrentCursor); } - public static void WriteInput(ColoredMessage[] message, bool clearConsoleLine = false) + public static void WriteInput(ColoredMessage?[]? message, bool clearConsoleLine = false) { lock (ColoredConsole.WriteLock) { @@ -444,10 +443,10 @@ public static void RandomizeInputColors() try { Random random = new(); - Array colors = Enum.GetValues<ConsoleColor>(); + ConsoleColor[] colors = Enum.GetValues<ConsoleColor>(); - ConsoleColor random1 = (ConsoleColor)colors.GetValue(random.Next(colors.Length)); - ConsoleColor random2 = (ConsoleColor)colors.GetValue(random.Next(colors.Length)); + ConsoleColor random1 = colors[random.Next(colors.Length)]; + ConsoleColor random2 = colors[random.Next(colors.Length)]; BaseSection.textColor = random1; diff --git a/MultiAdmin/ServerIO/OutputHandler.cs b/MultiAdmin/ServerIO/OutputHandler.cs index b689632..168c251 100644 --- a/MultiAdmin/ServerIO/OutputHandler.cs +++ b/MultiAdmin/ServerIO/OutputHandler.cs @@ -36,20 +36,20 @@ public OutputHandler(Server server) this.server = server; } - public void HandleMessage(object source, ServerSocket.MessageEventArgs message) + public void HandleMessage(object? source, ServerSocket.MessageEventArgs message) { if (message.message == null) return; ColoredMessage coloredMessage = new(message.message, ConsoleColor.White); - if (!coloredMessage.text.IsEmpty()) + if (!coloredMessage.text.IsNullOrEmpty()) { // Parse the color byte coloredMessage.textColor = (ConsoleColor)message.color; // Smod2 loggers pretty printing - Match match = SmodRegex.Match(coloredMessage.text); + Match match = SmodRegex.Match(coloredMessage.text!); if (match.Success) { if (match.Groups.Count >= 3) @@ -93,7 +93,7 @@ public void HandleMessage(object source, ServerSocket.MessageEventArgs message) } } - string lowerMessage = coloredMessage.text.ToLower(); + string lowerMessage = coloredMessage.text!.ToLower(); if (!server.supportedModFeatures.HasFlag(ModFeatures.CustomEvents)) { switch (lowerMessage.Trim(TrimChars)) @@ -127,13 +127,13 @@ public void HandleMessage(object source, ServerSocket.MessageEventArgs message) if (lowerMessage.StartsWith("multiadmin:")) { // 11 chars in "multiadmin:" - string eventMessage = coloredMessage.text.Substring(11); + string eventMessage = coloredMessage.text[11..]; // Split event and event data string[] eventSplit = eventMessage.Split(EventSplitChars, 2); string @event = eventSplit[0].ToLower(); - string eventData = eventSplit.Length > 1 ? eventSplit[1] : null; // Handle events with no data + string? eventData = eventSplit.Length > 1 ? eventSplit[1] : null; // Handle events with no data switch (@event) { @@ -177,7 +177,7 @@ public void HandleMessage(object source, ServerSocket.MessageEventArgs message) server.Write(coloredMessage); } - public void HandleAction(object source, byte action) + public void HandleAction(object? source, byte action) { switch ((OutputCodes)action) { diff --git a/MultiAdmin/ServerIO/ServerSocket.cs b/MultiAdmin/ServerIO/ServerSocket.cs index 81c2d36..25e25a5 100644 --- a/MultiAdmin/ServerIO/ServerSocket.cs +++ b/MultiAdmin/ServerIO/ServerSocket.cs @@ -17,23 +17,23 @@ public class ServerSocket : IDisposable private readonly TcpListener listener; - private TcpClient client; - private NetworkStream networkStream; + private TcpClient? client; + private NetworkStream? networkStream; - public struct MessageEventArgs + public readonly struct MessageEventArgs { - public MessageEventArgs(string message, byte color) + public MessageEventArgs(string? message, byte color) { this.message = message; this.color = color; } - public readonly string message; + public readonly string? message; public readonly byte color; } - public event EventHandler<MessageEventArgs> OnReceiveMessage; - public event EventHandler<byte> OnReceiveAction; + public event EventHandler<MessageEventArgs>? OnReceiveMessage; + public event EventHandler<byte>? OnReceiveAction; public int Port => ((IPEndPoint)listener.LocalEndpoint).Port; @@ -74,13 +74,17 @@ public void Connect() public async void MessageListener() { byte[] typeBuffer = new byte[1]; + Memory<byte> typeBufferMemory = new(typeBuffer); + byte[] intBuffer = new byte[IntBytes]; + Memory<byte> intBufferMemory = new(intBuffer); + while (!disposed && networkStream != null) { try { int messageTypeBytesRead = - await networkStream.ReadAsync(typeBuffer, 0, 1, disposeCancellationSource.Token); + await networkStream.ReadAsync(typeBufferMemory, disposeCancellationSource.Token); // Socket has been disconnected if (messageTypeBytesRead <= 0) @@ -99,7 +103,7 @@ public async void MessageListener() } int lengthBytesRead = - await networkStream.ReadAsync(intBuffer, 0, IntBytes, disposeCancellationSource.Token); + await networkStream.ReadAsync(intBufferMemory, disposeCancellationSource.Token); // Socket has been disconnected or integer read is invalid if (lengthBytesRead != IntBytes) @@ -123,7 +127,7 @@ public async void MessageListener() byte[] messageBuffer = new byte[length]; int messageBytesRead = - await networkStream.ReadAsync(messageBuffer, 0, length, disposeCancellationSource.Token); + await networkStream.ReadAsync(messageBuffer.AsMemory(0, length), disposeCancellationSource.Token); // Socket has been disconnected if (messageBytesRead <= 0) @@ -176,6 +180,8 @@ public void Dispose() if (disposed) return; + GC.SuppressFinalize(this); + disposed = true; disposeCancellationSource.Cancel(); disposeCancellationSource.Dispose(); diff --git a/MultiAdmin/ServerIO/StringSections.cs b/MultiAdmin/ServerIO/StringSections.cs index f60d969..49d2aa6 100644 --- a/MultiAdmin/ServerIO/StringSections.cs +++ b/MultiAdmin/ServerIO/StringSections.cs @@ -45,8 +45,8 @@ public StringSections(StringSection[] sections) } public static StringSections FromString(string fullString, int sectionLength, - ColoredMessage leftIndicator = null, ColoredMessage rightIndicator = null, - ColoredMessage sectionBase = null) + ColoredMessage? leftIndicator = null, ColoredMessage? rightIndicator = null, + ColoredMessage? sectionBase = null) { int rightIndicatorLength = rightIndicator?.Length ?? 0; int totalIndicatorLength = (leftIndicator?.Length ?? 0) + rightIndicatorLength; @@ -79,9 +79,9 @@ public static StringSections FromString(string fullString, int sectionLength, if (curSecBuilder.Length < sectionLength - totalIndicatorLength) continue; // Decide what the left indicator text should be accounting for the leftmost section - ColoredMessage leftIndicatorSection = sections.Count > 0 ? leftIndicator : null; + ColoredMessage? leftIndicatorSection = sections.Count > 0 ? leftIndicator : null; // Decide what the right indicator text should be accounting for the rightmost section - ColoredMessage rightIndicatorSection = + ColoredMessage? rightIndicatorSection = i < fullString.Length - (1 + rightIndicatorLength) ? rightIndicator : null; // Check the section length against the final section length @@ -106,7 +106,7 @@ public static StringSections FromString(string fullString, int sectionLength, if (!curSecBuilder.IsEmpty()) { // Only decide for the left indicator, as this last section will always be the rightmost section - ColoredMessage leftIndicatorSection = sections.Count > 0 ? leftIndicator : null; + ColoredMessage? leftIndicatorSection = sections.Count > 0 ? leftIndicator : null; // Copy the section base message and replace the text ColoredMessage section = sectionBase.Clone(); @@ -121,19 +121,19 @@ public static StringSections FromString(string fullString, int sectionLength, } } - public struct StringSection + public readonly struct StringSection { public ColoredMessage Text { get; } - public ColoredMessage LeftIndicator { get; } - public ColoredMessage RightIndicator { get; } + public ColoredMessage? LeftIndicator { get; } + public ColoredMessage? RightIndicator { get; } - public ColoredMessage[] Section => new ColoredMessage[] { LeftIndicator, Text, RightIndicator }; + public ColoredMessage?[] Section => new ColoredMessage?[] { LeftIndicator, Text, RightIndicator }; public int MinIndex { get; } public int MaxIndex { get; } - public StringSection(ColoredMessage text, ColoredMessage leftIndicator, ColoredMessage rightIndicator, + public StringSection(ColoredMessage text, ColoredMessage? leftIndicator, ColoredMessage? rightIndicator, int minIndex, int maxIndex) { Text = text; diff --git a/MultiAdmin/Utility/CommandUtils.cs b/MultiAdmin/Utility/CommandUtils.cs index 238e111..8dd2f80 100644 --- a/MultiAdmin/Utility/CommandUtils.cs +++ b/MultiAdmin/Utility/CommandUtils.cs @@ -62,11 +62,6 @@ public static int IndexOfNonEscaped(string inString, char inChar, char escapeCha public static string[] StringToArgs(string inString, int startIndex, int count, char separator = ' ', char escapeChar = '\\', char quoteChar = '\"', bool keepQuotes = false) { - if (inString == null) - { - return null; - } - if (startIndex < 0 || startIndex >= inString.Length) { throw new ArgumentOutOfRangeException(nameof(startIndex)); diff --git a/MultiAdmin/Utility/EmptyExtensions.cs b/MultiAdmin/Utility/EmptyExtensions.cs index be39677..f7cf5d3 100644 --- a/MultiAdmin/Utility/EmptyExtensions.cs +++ b/MultiAdmin/Utility/EmptyExtensions.cs @@ -12,7 +12,7 @@ public static bool IsEmpty<T>(this IEnumerable<T> enumerable) return !enumerable.Any(); } - public static bool IsNullOrEmpty<T>(this IEnumerable<T> enumerable) + public static bool IsNullOrEmpty<T>(this IEnumerable<T?>? enumerable) { return enumerable?.IsEmpty() ?? true; } @@ -22,17 +22,17 @@ public static bool IsEmpty(this Array array) return array.Length <= 0; } - public static bool IsNullOrEmpty(this Array array) + public static bool IsNullOrEmpty(this Array? array) { return array?.IsEmpty() ?? true; } - public static bool IsEmpty<T>(this T[] array) + public static bool IsEmpty<T>(this T?[] array) { return array.Length <= 0; } - public static bool IsNullOrEmpty<T>(this T[] array) + public static bool IsNullOrEmpty<T>(this T?[]? array) { return array?.IsEmpty() ?? true; } @@ -42,7 +42,7 @@ public static bool IsEmpty<T>(this ICollection<T> collection) return collection.Count <= 0; } - public static bool IsNullOrEmpty<T>(this ICollection<T> collection) + public static bool IsNullOrEmpty<T>(this ICollection<T?>? collection) { return collection?.IsEmpty() ?? true; } @@ -52,17 +52,17 @@ public static bool IsEmpty<T>(this List<T> list) return list.Count <= 0; } - public static bool IsNullOrEmpty<T>(this List<T> list) + public static bool IsNullOrEmpty<T>(this List<T?>? list) { return list?.IsEmpty() ?? true; } - public static bool IsEmpty<TKey, TValue>(this Dictionary<TKey, TValue> dictionary) + public static bool IsEmpty<TKey, TValue>(this Dictionary<TKey, TValue> dictionary) where TKey : notnull { return dictionary.Count <= 0; } - public static bool IsNullOrEmpty<TKey, TValue>(this Dictionary<TKey, TValue> dictionary) + public static bool IsNullOrEmpty<TKey, TValue>(this Dictionary<TKey, TValue?>? dictionary) where TKey : notnull { return dictionary?.IsEmpty() ?? true; } @@ -72,7 +72,7 @@ public static bool IsEmpty(this StringBuilder stringBuilder) return stringBuilder.Length <= 0; } - public static bool IsNullOrEmpty(this StringBuilder stringBuilder) + public static bool IsNullOrEmpty(this StringBuilder? stringBuilder) { return stringBuilder?.IsEmpty() ?? true; } @@ -82,7 +82,7 @@ public static bool IsEmpty(this string @string) return @string.Length <= 0; } - public static bool IsNullOrEmpty(this string @string) + public static bool IsNullOrEmpty(this string? @string) { return @string?.IsEmpty() ?? true; } diff --git a/MultiAdmin/Utility/StringEnumerableExtensions.cs b/MultiAdmin/Utility/StringEnumerableExtensions.cs index e509b34..e4fc972 100644 --- a/MultiAdmin/Utility/StringEnumerableExtensions.cs +++ b/MultiAdmin/Utility/StringEnumerableExtensions.cs @@ -6,12 +6,12 @@ namespace MultiAdmin.Utility { public static class StringEnumerableExtensions { - public static string JoinArgs(this IEnumerable<string> args) + public static string JoinArgs(this IEnumerable<string?> args) { StringBuilder argsStringBuilder = new(); - foreach (string arg in args) + foreach (string? arg in args) { - if (arg.IsNullOrEmpty()) + if (string.IsNullOrEmpty(arg)) continue; // Escape escape characters (if not on Windows) and quotation marks diff --git a/MultiAdmin/Utility/StringExtensions.cs b/MultiAdmin/Utility/StringExtensions.cs index eadb1f0..66007bd 100644 --- a/MultiAdmin/Utility/StringExtensions.cs +++ b/MultiAdmin/Utility/StringExtensions.cs @@ -4,7 +4,7 @@ namespace MultiAdmin.Utility { public static class StringExtensions { - public static bool Equals(this string input, string value, int startIndex, int count) + public static bool Equals(this string? input, string? value, int startIndex, int count) { if (input == null && value == null) return true; @@ -25,7 +25,7 @@ public static bool Equals(this string input, string value, int startIndex, int c return true; } - public static bool Equals(this string input, string value, int startIndex) + public static bool Equals(this string? input, string? value, int startIndex) { if (input == null && value == null) return true; @@ -47,7 +47,7 @@ public static bool Equals(this string input, string value, int startIndex) /// <returns>A <see cref="string"/> escaped for use with <see cref="string.Format"/></returns> public static string EscapeFormat(this string input) { - return input?.Replace("{", "{{").Replace("}", "}}"); + return input.Replace("{", "{{").Replace("}", "}}"); } } } diff --git a/MultiAdmin/Utility/Utils.cs b/MultiAdmin/Utility/Utils.cs index 83cad0c..606b425 100644 --- a/MultiAdmin/Utility/Utils.cs +++ b/MultiAdmin/Utility/Utils.cs @@ -23,12 +23,10 @@ public static string TimeStampMessage(string message) return string.IsNullOrEmpty(message) ? message : $"{TimeStamp} {message}"; } - public static ColoredMessage[] TimeStampMessage(ColoredMessage[] message, ConsoleColor? color = null, + public static ColoredMessage?[] TimeStampMessage(ColoredMessage?[] message, ConsoleColor? color = null, bool cloneMessages = false) { - if (message == null) return null; - - ColoredMessage[] newMessage = new ColoredMessage[message.Length + 1]; + ColoredMessage?[] newMessage = new ColoredMessage?[message.Length + 1]; newMessage[0] = new ColoredMessage($"{TimeStamp} ", color); if (cloneMessages) @@ -45,13 +43,13 @@ public static ColoredMessage[] TimeStampMessage(ColoredMessage[] message, Consol return newMessage; } - public static ColoredMessage[] TimeStampMessage(ColoredMessage message, ConsoleColor? color = null, + public static ColoredMessage?[] TimeStampMessage(ColoredMessage? message, ConsoleColor? color = null, bool cloneMessages = false) { - return TimeStampMessage(new ColoredMessage[] { message }, color, cloneMessages); + return TimeStampMessage(new ColoredMessage?[] { message }, color, cloneMessages); } - public static string GetFullPathSafe(string path) + public static string? GetFullPathSafe(string? path) { return string.IsNullOrWhiteSpace(path) ? null : Path.GetFullPath(path); } @@ -93,7 +91,7 @@ public static bool StringMatches(string input, string pattern, char wildCard = W return false; Program.LogDebug(nameof(StringMatches), - $"Matching \"{wildCardSection}\" with \"{input.Substring(matchIndex)}\"..."); + $"Matching \"{wildCardSection}\" with \"{input[matchIndex..]}\"..."); if (matchIndex <= 0 && pattern[0] != wildCard) { @@ -130,25 +128,25 @@ public static bool StringMatches(string input, string pattern, char wildCard = W } Program.LogDebug(nameof(StringMatches), - $"Done matching. Matches = {matchIndex == input.Length || wildCardSections[wildCardSections.Length - 1].IsEmpty()}."); + $"Done matching. Matches = {matchIndex == input.Length || wildCardSections[^1].IsEmpty()}."); - return matchIndex == input.Length || wildCardSections[wildCardSections.Length - 1].IsEmpty(); + return matchIndex == input.Length || wildCardSections[^1].IsEmpty(); } - public static bool InputMatchesAnyPattern(string input, params string[] namePatterns) + public static bool InputMatchesAnyPattern(string input, params string[]? namePatterns) { - return !namePatterns.IsNullOrEmpty() && namePatterns.Any(namePattern => StringMatches(input, namePattern)); + return namePatterns != null && namePatterns.Length > 0 && namePatterns.Any(namePattern => StringMatches(input, namePattern)); } - private static bool PassesWhitelistAndBlacklist(string toCheck, string[] whitelist = null, - string[] blacklist = null) + private static bool PassesWhitelistAndBlacklist(string toCheck, string[]? whitelist = null, + string[]? blacklist = null) { return (whitelist.IsNullOrEmpty() || InputMatchesAnyPattern(toCheck, whitelist)) && (blacklist.IsNullOrEmpty() || !InputMatchesAnyPattern(toCheck, blacklist)); } - public static void CopyAll(DirectoryInfo source, DirectoryInfo target, string[] fileWhitelist = null, - string[] fileBlacklist = null) + public static void CopyAll(DirectoryInfo source, DirectoryInfo target, string[]? fileWhitelist = null, + string[]? fileBlacklist = null) { // If the target directory is the same as the source directory if (source.FullName == target.FullName) @@ -176,8 +174,8 @@ public static void CopyAll(DirectoryInfo source, DirectoryInfo target, string[] } } - public static void CopyAll(string source, string target, string[] fileWhitelist = null, - string[] fileBlacklist = null) + public static void CopyAll(string source, string target, string[]? fileWhitelist = null, + string[]? fileBlacklist = null) { CopyAll(new DirectoryInfo(source), new DirectoryInfo(target), fileWhitelist, fileBlacklist); } From 9b35bb29c95a030f72a97adc9cef633f7f665826 Mon Sep 17 00:00:00 2001 From: Butterscotch! <bscotchvanilla@gmail.com> Date: Sun, 1 Jan 2023 08:13:09 -0500 Subject: [PATCH 07/16] Manually register all features to support trimming --- .../Features/Attributes/FeatureAttribute.cs | 10 ----- MultiAdmin/Features/ConfigGenerator.cs | 2 - MultiAdmin/Features/ConfigReload.cs | 2 - MultiAdmin/Features/ExitCommand.cs | 3 -- ...opyRoundQueue.cs => FileCopyRoundQueue.cs} | 4 +- MultiAdmin/Features/GithubGenerator.cs | 2 - MultiAdmin/Features/HelpCommand.cs | 2 - MultiAdmin/Features/MemoryChecker.cs | 2 - MultiAdmin/Features/MultiAdminInfo.cs | 2 - MultiAdmin/Features/NewCommand.cs | 2 - MultiAdmin/Features/Restart.cs | 3 -- MultiAdmin/Features/RestartRoundCounter.cs | 3 -- MultiAdmin/Features/TitleBar.cs | 2 - MultiAdmin/Server.cs | 41 ++++++------------- 14 files changed, 14 insertions(+), 66 deletions(-) delete mode 100644 MultiAdmin/Features/Attributes/FeatureAttribute.cs rename MultiAdmin/Features/{FolderCopyRoundQueue.cs => FileCopyRoundQueue.cs} (95%) diff --git a/MultiAdmin/Features/Attributes/FeatureAttribute.cs b/MultiAdmin/Features/Attributes/FeatureAttribute.cs deleted file mode 100644 index ce37dff..0000000 --- a/MultiAdmin/Features/Attributes/FeatureAttribute.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; - -namespace MultiAdmin.Features.Attributes -{ - [AttributeUsage(AttributeTargets.Class) - ] - public class FeatureAttribute : Attribute - { - } -} diff --git a/MultiAdmin/Features/ConfigGenerator.cs b/MultiAdmin/Features/ConfigGenerator.cs index 4e5ee74..c612d54 100644 --- a/MultiAdmin/Features/ConfigGenerator.cs +++ b/MultiAdmin/Features/ConfigGenerator.cs @@ -3,12 +3,10 @@ using System.IO; using MultiAdmin.Config; using MultiAdmin.Config.ConfigHandler; -using MultiAdmin.Features.Attributes; using MultiAdmin.Utility; namespace MultiAdmin.Features { - [Feature] internal class ConfigGenerator : Feature, ICommand { diff --git a/MultiAdmin/Features/ConfigReload.cs b/MultiAdmin/Features/ConfigReload.cs index 76c5747..d053d79 100644 --- a/MultiAdmin/Features/ConfigReload.cs +++ b/MultiAdmin/Features/ConfigReload.cs @@ -1,9 +1,7 @@ -using MultiAdmin.Features.Attributes; using MultiAdmin.Utility; namespace MultiAdmin.Features { - [Feature] internal class ConfigReload : Feature, ICommand { public ConfigReload(Server server) : base(server) diff --git a/MultiAdmin/Features/ExitCommand.cs b/MultiAdmin/Features/ExitCommand.cs index e59aa12..b6eda58 100644 --- a/MultiAdmin/Features/ExitCommand.cs +++ b/MultiAdmin/Features/ExitCommand.cs @@ -1,8 +1,5 @@ -using MultiAdmin.Features.Attributes; - namespace MultiAdmin.Features { - [Feature] internal class ExitCommand : Feature, ICommand { public ExitCommand(Server server) : base(server) diff --git a/MultiAdmin/Features/FolderCopyRoundQueue.cs b/MultiAdmin/Features/FileCopyRoundQueue.cs similarity index 95% rename from MultiAdmin/Features/FolderCopyRoundQueue.cs rename to MultiAdmin/Features/FileCopyRoundQueue.cs index f0eb36a..b78bc2e 100644 --- a/MultiAdmin/Features/FolderCopyRoundQueue.cs +++ b/MultiAdmin/Features/FileCopyRoundQueue.cs @@ -1,10 +1,8 @@ using System; -using MultiAdmin.Features.Attributes; using MultiAdmin.Utility; namespace MultiAdmin.Features { - [Feature] internal class FileCopyRoundQueue : Feature, IEventRoundEnd { private string[] queue = Array.Empty<string>(); @@ -93,7 +91,7 @@ public override string GetFeatureDescription() public override string GetFeatureName() { - return "Folder Copy Round Queue"; + return "File Copy Round Queue"; } } } diff --git a/MultiAdmin/Features/GithubGenerator.cs b/MultiAdmin/Features/GithubGenerator.cs index 5c01a75..1291858 100644 --- a/MultiAdmin/Features/GithubGenerator.cs +++ b/MultiAdmin/Features/GithubGenerator.cs @@ -4,13 +4,11 @@ using System.Text; using MultiAdmin.Config; using MultiAdmin.Config.ConfigHandler; -using MultiAdmin.Features.Attributes; using MultiAdmin.ServerIO; using MultiAdmin.Utility; namespace MultiAdmin.Features { - [Feature] internal class GithubGenerator : Feature, ICommand { public const string EmptyIndicator = "**Empty**"; diff --git a/MultiAdmin/Features/HelpCommand.cs b/MultiAdmin/Features/HelpCommand.cs index aec5c09..f2200aa 100644 --- a/MultiAdmin/Features/HelpCommand.cs +++ b/MultiAdmin/Features/HelpCommand.cs @@ -1,12 +1,10 @@ using System; using System.Collections.Generic; using MultiAdmin.ConsoleTools; -using MultiAdmin.Features.Attributes; using MultiAdmin.Utility; namespace MultiAdmin.Features { - [Feature] public class HelpCommand : Feature, ICommand { private static readonly ColoredMessage helpPrefix = new("Commands from MultiAdmin:\n", ConsoleColor.Yellow); diff --git a/MultiAdmin/Features/MemoryChecker.cs b/MultiAdmin/Features/MemoryChecker.cs index 2280929..4ba2380 100644 --- a/MultiAdmin/Features/MemoryChecker.cs +++ b/MultiAdmin/Features/MemoryChecker.cs @@ -1,9 +1,7 @@ using System; -using MultiAdmin.Features.Attributes; namespace MultiAdmin.Features { - [Feature] internal class MemoryChecker : Feature, IEventTick, IEventRoundEnd { private const decimal BytesInMegabyte = 1048576; diff --git a/MultiAdmin/Features/MultiAdminInfo.cs b/MultiAdmin/Features/MultiAdminInfo.cs index e07effb..f9bcdb9 100644 --- a/MultiAdmin/Features/MultiAdminInfo.cs +++ b/MultiAdmin/Features/MultiAdminInfo.cs @@ -1,9 +1,7 @@ using System; -using MultiAdmin.Features.Attributes; namespace MultiAdmin.Features { - [Feature] internal class MultiAdminInfo : Feature, IEventServerPreStart, ICommand { public MultiAdminInfo(Server server) : base(server) diff --git a/MultiAdmin/Features/NewCommand.cs b/MultiAdmin/Features/NewCommand.cs index c660ae2..87bfe7b 100644 --- a/MultiAdmin/Features/NewCommand.cs +++ b/MultiAdmin/Features/NewCommand.cs @@ -1,10 +1,8 @@ using System.Diagnostics; -using MultiAdmin.Features.Attributes; using MultiAdmin.Utility; namespace MultiAdmin.Features { - [Feature] internal class NewCommand : Feature, ICommand, IEventServerFull { private string? onFullServerId; diff --git a/MultiAdmin/Features/Restart.cs b/MultiAdmin/Features/Restart.cs index 67fad4c..cb36724 100644 --- a/MultiAdmin/Features/Restart.cs +++ b/MultiAdmin/Features/Restart.cs @@ -1,8 +1,5 @@ -using MultiAdmin.Features.Attributes; - namespace MultiAdmin.Features { - [Feature] internal class Restart : Feature, ICommand { public Restart(Server server) : base(server) diff --git a/MultiAdmin/Features/RestartRoundCounter.cs b/MultiAdmin/Features/RestartRoundCounter.cs index a9517e9..f972cfa 100644 --- a/MultiAdmin/Features/RestartRoundCounter.cs +++ b/MultiAdmin/Features/RestartRoundCounter.cs @@ -1,8 +1,5 @@ -using MultiAdmin.Features.Attributes; - namespace MultiAdmin.Features { - [Feature] internal class RestartRoundCounter : Feature, IEventRoundEnd { private int count; diff --git a/MultiAdmin/Features/TitleBar.cs b/MultiAdmin/Features/TitleBar.cs index 3b04790..dfe8528 100644 --- a/MultiAdmin/Features/TitleBar.cs +++ b/MultiAdmin/Features/TitleBar.cs @@ -1,10 +1,8 @@ using System; using System.Collections.Generic; -using MultiAdmin.Features.Attributes; namespace MultiAdmin.Features { - [Feature] internal class Titlebar : Feature, IEventServerStart { private int ServerProcessId diff --git a/MultiAdmin/Server.cs b/MultiAdmin/Server.cs index 83624df..34d0a2b 100644 --- a/MultiAdmin/Server.cs +++ b/MultiAdmin/Server.cs @@ -2,13 +2,11 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.Linq; -using System.Reflection; using System.Threading; using System.Threading.Tasks; using MultiAdmin.Config; using MultiAdmin.ConsoleTools; -using MultiAdmin.Features.Attributes; +using MultiAdmin.Features; using MultiAdmin.ServerIO; using MultiAdmin.Utility; @@ -593,33 +591,20 @@ private void RegisterFeature(Feature feature) features.Add(feature); } - private static IEnumerable<Type> GetTypesWithAttribute(Type attribute) - { - foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) - { - foreach (Type type in assembly.GetTypes()) - { - object[] attributes = type.GetCustomAttributes(attribute, true); - if (!attributes.IsEmpty()) yield return type; - } - } - } - private void RegisterFeatures() { - Type[] assembly = GetTypesWithAttribute(typeof(FeatureAttribute)).ToArray(); - foreach (Type type in assembly) - { - try - { - object? featureInstance = Activator.CreateInstance(type, this); - if (featureInstance is Feature feature) RegisterFeature(feature); - } - catch (Exception e) - { - Program.LogDebugException(nameof(RegisterFeatures), e); - } - } + RegisterFeature(new ConfigGenerator(this)); + RegisterFeature(new ConfigReload(this)); + RegisterFeature(new ExitCommand(this)); + RegisterFeature(new FileCopyRoundQueue(this)); + RegisterFeature(new GithubGenerator(this)); + RegisterFeature(new HelpCommand(this)); + RegisterFeature(new MemoryChecker(this)); + RegisterFeature(new MultiAdminInfo(this)); + RegisterFeature(new NewCommand(this)); + RegisterFeature(new Restart(this)); + RegisterFeature(new RestartRoundCounter(this)); + RegisterFeature(new Titlebar(this)); } private void InitFeatures() From 4c7ca1bc0f4a31b5588602a38b7310cac326e699 Mon Sep 17 00:00:00 2001 From: Butterscotch! <bscotchvanilla@gmail.com> Date: Sun, 1 Jan 2023 08:25:31 -0500 Subject: [PATCH 08/16] Manually register all configs to support trimming --- MultiAdmin/Config/MultiAdminConfig.cs | 48 ++++++++++++++++++++++----- README.md | 2 +- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/MultiAdmin/Config/MultiAdminConfig.cs b/MultiAdmin/Config/MultiAdminConfig.cs index edf9a7b..df74f7e 100644 --- a/MultiAdmin/Config/MultiAdminConfig.cs +++ b/MultiAdmin/Config/MultiAdminConfig.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Reflection; using MultiAdmin.Config.ConfigHandler; using MultiAdmin.ConsoleTools; using MultiAdmin.ServerIO; @@ -234,13 +233,46 @@ public MultiAdminConfig(Config? config, MultiAdminConfig? parentConfig, bool cre #region MultiAdmin Config Register - foreach (PropertyInfo property in GetType().GetProperties()) - { - if (property.GetValue(this) is ConfigEntry entry) - { - RegisterConfig(entry); - } - } + RegisterConfig(ConfigLocation); + RegisterConfig(AppDataLocation); + RegisterConfig(DisableConfigValidation); + RegisterConfig(ShareNonConfigs); + RegisterConfig(LogLocation); + RegisterConfig(NoLog); + RegisterConfig(DebugLog); + RegisterConfig(DebugLogBlacklist); + RegisterConfig(DebugLogWhitelist); + RegisterConfig(UseNewInputSystem); + RegisterConfig(ConsoleInputSystem); + RegisterConfig(HideInput); + RegisterConfig(Port); + RegisterConfig(CopyFromFolderOnReload); + RegisterConfig(FolderCopyWhitelist); + RegisterConfig(FolderCopyBlacklist); + RegisterConfig(FolderCopyRoundQueue); + RegisterConfig(FolderCopyRoundQueueWhitelist); + RegisterConfig(FolderCopyRoundQueueBlacklist); + RegisterConfig(RandomizeFolderCopyRoundQueue); + RegisterConfig(ManualStart); + RegisterConfig(MaxMemory); + RegisterConfig(RestartLowMemory); + RegisterConfig(RestartLowMemoryTicks); + RegisterConfig(RestartLowMemoryRoundEnd); + RegisterConfig(RestartLowMemoryRoundEndTicks); + RegisterConfig(RandomInputColors); + RegisterConfig(RestartEveryNumRounds); + RegisterConfig(RestartEveryNumRoundsCounting); + RegisterConfig(SafeServerShutdown); + RegisterConfig(SafeShutdownCheckDelay); + RegisterConfig(SafeShutdownTimeout); + RegisterConfig(ServerRestartTimeout); + RegisterConfig(ServerStopTimeout); + RegisterConfig(ServerStartRetry); + RegisterConfig(ServerStartRetryDelay); + RegisterConfig(MultiAdminTickDelay); + RegisterConfig(ServersFolder); + RegisterConfig(SetTitleBar); + RegisterConfig(StartConfigOnFull); #endregion diff --git a/README.md b/README.md index 3378da4..660463f 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Make sure that you are running Mono 5.18.0 or higher, otherwise you might have i - Config Generator: Generates a full default MultiAdmin config file - Config Reload: Reloads the MultiAdmin configuration file - Exit Command: Adds a graceful exit command -- Folder Copy Round Queue: Copies files from folders in a queue +- File Copy Round Queue: Copies files from folders in a queue - GitHub Generator: Generates a GitHub README file outlining all the features/commands - Help: Display a full list of MultiAdmin commands and in game commands - Restart On Low Memory: Restarts the server if the working memory becomes too low From 7cc7721974f71e082184f2ccdff4c5e22def4e6f Mon Sep 17 00:00:00 2001 From: Butterscotch! <bscotchvanilla@gmail.com> Date: Mon, 2 Jan 2023 01:39:10 -0500 Subject: [PATCH 09/16] Fix project file formatting & update gitignores --- .gitignore | 67 ++++++++++++++++++------ MultiAdmin.Tests/.gitignore | 67 ++++++++++++++++++------ MultiAdmin.Tests/MultiAdmin.Tests.csproj | 6 +-- MultiAdmin.sln | 5 +- MultiAdmin/.gitignore | 67 ++++++++++++++++++------ MultiAdmin/MultiAdmin.csproj | 4 +- MultiAdmin/nuget.config | 9 ---- 7 files changed, 160 insertions(+), 65 deletions(-) delete mode 100644 MultiAdmin/nuget.config diff --git a/.gitignore b/.gitignore index 920d1cd..154e127 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## -## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore # User-specific files *.rsuser @@ -23,6 +23,7 @@ mono_crash.* [Rr]eleases/ x64/ x86/ +[Ww][Ii][Nn]32/ [Aa][Rr][Mm]/ [Aa][Rr][Mm]64/ bld/ @@ -56,11 +57,17 @@ dlldata.c # Benchmark Results BenchmarkDotNet.Artifacts/ -# .NET Core +# .NET project.lock.json project.fragment.lock.json artifacts/ +# Tye +.tye/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + # StyleCop StyleCopReport.xml @@ -86,6 +93,7 @@ StyleCopReport.xml *.tmp_proj *_wpftmp.csproj *.log +*.tlog *.vspscc *.vssscc .builds @@ -138,7 +146,9 @@ _TeamCity* !.axoCover/settings.json # Coverlet is a free, cross platform Code Coverage Tool -coverage*[.json, .xml, .info] +coverage*.json +coverage*.xml +coverage*.info # Visual Studio code coverage results *.coverage @@ -287,6 +297,17 @@ node_modules/ # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) *.vbw +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts @@ -343,6 +364,9 @@ ASALocalRun/ # Local History for Visual Studio .localhistory/ +# Visual Studio History (VSHistory) files +.vshistory/ + # BeatPulse healthcheck temp database healthchecksdb @@ -352,6 +376,30 @@ MigrationBackup/ # Ionide (cross platform F# VS Code tools) working folder .ionide/ +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml + ## ## Visual studio for Mac ## @@ -427,16 +475,3 @@ $RECYCLE.BIN/ # Windows shortcuts *.lnk - -# JetBrains Rider -.idea/ -*.sln.iml - -## -## Visual Studio Code -## -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json diff --git a/MultiAdmin.Tests/.gitignore b/MultiAdmin.Tests/.gitignore index 920d1cd..154e127 100644 --- a/MultiAdmin.Tests/.gitignore +++ b/MultiAdmin.Tests/.gitignore @@ -1,7 +1,7 @@ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## -## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore # User-specific files *.rsuser @@ -23,6 +23,7 @@ mono_crash.* [Rr]eleases/ x64/ x86/ +[Ww][Ii][Nn]32/ [Aa][Rr][Mm]/ [Aa][Rr][Mm]64/ bld/ @@ -56,11 +57,17 @@ dlldata.c # Benchmark Results BenchmarkDotNet.Artifacts/ -# .NET Core +# .NET project.lock.json project.fragment.lock.json artifacts/ +# Tye +.tye/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + # StyleCop StyleCopReport.xml @@ -86,6 +93,7 @@ StyleCopReport.xml *.tmp_proj *_wpftmp.csproj *.log +*.tlog *.vspscc *.vssscc .builds @@ -138,7 +146,9 @@ _TeamCity* !.axoCover/settings.json # Coverlet is a free, cross platform Code Coverage Tool -coverage*[.json, .xml, .info] +coverage*.json +coverage*.xml +coverage*.info # Visual Studio code coverage results *.coverage @@ -287,6 +297,17 @@ node_modules/ # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) *.vbw +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts @@ -343,6 +364,9 @@ ASALocalRun/ # Local History for Visual Studio .localhistory/ +# Visual Studio History (VSHistory) files +.vshistory/ + # BeatPulse healthcheck temp database healthchecksdb @@ -352,6 +376,30 @@ MigrationBackup/ # Ionide (cross platform F# VS Code tools) working folder .ionide/ +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml + ## ## Visual studio for Mac ## @@ -427,16 +475,3 @@ $RECYCLE.BIN/ # Windows shortcuts *.lnk - -# JetBrains Rider -.idea/ -*.sln.iml - -## -## Visual Studio Code -## -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json diff --git a/MultiAdmin.Tests/MultiAdmin.Tests.csproj b/MultiAdmin.Tests/MultiAdmin.Tests.csproj index c48508d..f5f3663 100644 --- a/MultiAdmin.Tests/MultiAdmin.Tests.csproj +++ b/MultiAdmin.Tests/MultiAdmin.Tests.csproj @@ -1,11 +1,11 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net7.0</TargetFramework> - <LangVersion>11</LangVersion> + <LangVersion>11</LangVersion> <IsPackable>false</IsPackable> <RootNamespace>MultiAdmin.Tests</RootNamespace> <Nullable>enable</Nullable> - + <GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> <GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute> <GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute> @@ -30,4 +30,4 @@ <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> </ItemGroup> -</Project> +</Project> \ No newline at end of file diff --git a/MultiAdmin.sln b/MultiAdmin.sln index 692e8c1..294569f 100644 --- a/MultiAdmin.sln +++ b/MultiAdmin.sln @@ -1,5 +1,4 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 +Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26124.0 MinimumVisualStudioVersion = 15.0.26124.0 @@ -45,4 +44,4 @@ Global {314971BB-616B-4FAE-B375-5A4A670D8626}.Release|x86.ActiveCfg = Release|Any CPU {314971BB-616B-4FAE-B375-5A4A670D8626}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection -EndGlobal +EndGlobal \ No newline at end of file diff --git a/MultiAdmin/.gitignore b/MultiAdmin/.gitignore index 920d1cd..154e127 100644 --- a/MultiAdmin/.gitignore +++ b/MultiAdmin/.gitignore @@ -1,7 +1,7 @@ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## -## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore # User-specific files *.rsuser @@ -23,6 +23,7 @@ mono_crash.* [Rr]eleases/ x64/ x86/ +[Ww][Ii][Nn]32/ [Aa][Rr][Mm]/ [Aa][Rr][Mm]64/ bld/ @@ -56,11 +57,17 @@ dlldata.c # Benchmark Results BenchmarkDotNet.Artifacts/ -# .NET Core +# .NET project.lock.json project.fragment.lock.json artifacts/ +# Tye +.tye/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + # StyleCop StyleCopReport.xml @@ -86,6 +93,7 @@ StyleCopReport.xml *.tmp_proj *_wpftmp.csproj *.log +*.tlog *.vspscc *.vssscc .builds @@ -138,7 +146,9 @@ _TeamCity* !.axoCover/settings.json # Coverlet is a free, cross platform Code Coverage Tool -coverage*[.json, .xml, .info] +coverage*.json +coverage*.xml +coverage*.info # Visual Studio code coverage results *.coverage @@ -287,6 +297,17 @@ node_modules/ # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) *.vbw +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts @@ -343,6 +364,9 @@ ASALocalRun/ # Local History for Visual Studio .localhistory/ +# Visual Studio History (VSHistory) files +.vshistory/ + # BeatPulse healthcheck temp database healthchecksdb @@ -352,6 +376,30 @@ MigrationBackup/ # Ionide (cross platform F# VS Code tools) working folder .ionide/ +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml + ## ## Visual studio for Mac ## @@ -427,16 +475,3 @@ $RECYCLE.BIN/ # Windows shortcuts *.lnk - -# JetBrains Rider -.idea/ -*.sln.iml - -## -## Visual Studio Code -## -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json diff --git a/MultiAdmin/MultiAdmin.csproj b/MultiAdmin/MultiAdmin.csproj index ed63baf..abafc12 100644 --- a/MultiAdmin/MultiAdmin.csproj +++ b/MultiAdmin/MultiAdmin.csproj @@ -2,7 +2,7 @@ <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net7.0</TargetFramework> - <LangVersion>11</LangVersion> + <LangVersion>11</LangVersion> <RootNamespace>MultiAdmin</RootNamespace> <ApplicationIcon>Icon.ico</ApplicationIcon> <Nullable>enable</Nullable> @@ -36,4 +36,4 @@ <ItemGroup Condition="$([MSBuild]::IsOSPlatform('Linux'))"> <PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" /> </ItemGroup> -</Project> +</Project> \ No newline at end of file diff --git a/MultiAdmin/nuget.config b/MultiAdmin/nuget.config deleted file mode 100644 index 7c26348..0000000 --- a/MultiAdmin/nuget.config +++ /dev/null @@ -1,9 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<configuration> - <packageSources> - <!--To inherit the global NuGet package sources remove the <clear/> line below --> - <clear /> - <add key="dotnet-experimental" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-experimental/nuget/v3/index.json" /> - <add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" /> - </packageSources> -</configuration> \ No newline at end of file From 84ff6a1629bfa42ece60c4d3170650f4eecf4936 Mon Sep 17 00:00:00 2001 From: Butterscotch! <bscotchvanilla@gmail.com> Date: Mon, 16 Jan 2023 23:07:09 -0500 Subject: [PATCH 10/16] Update workflow to ubuntu-20.04 https://github.com/actions/runner-images/issues/6002 --- .github/workflows/main.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c57a079..cf8128e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -8,10 +8,10 @@ jobs: runs-on: ${{matrix.os}} strategy: matrix: - os: [ubuntu-18.04, windows-latest] + os: [ubuntu-20.04, windows-latest] framework: ['7.0'] include: - - os: ubuntu-18.04 + - os: ubuntu-20.04 target: linux-x64 - os: windows-latest target: win-x64 @@ -20,7 +20,7 @@ jobs: steps: - uses: actions/checkout@v3 - - if: matrix.os == 'ubuntu-18.04' + - if: matrix.os == 'ubuntu-20.04' name: Install Linux packages run: | sudo apt update From 1b26b378fa46cf30ffda896f033a6ed11ecf28c4 Mon Sep 17 00:00:00 2001 From: Butterscotch! <bscotchvanilla@gmail.com> Date: Mon, 16 Jan 2023 23:18:10 -0500 Subject: [PATCH 11/16] Fix ConsoleInputSystem link --- MultiAdmin/Features/GithubGenerator.cs | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MultiAdmin/Features/GithubGenerator.cs b/MultiAdmin/Features/GithubGenerator.cs index 1291858..17d58d7 100644 --- a/MultiAdmin/Features/GithubGenerator.cs +++ b/MultiAdmin/Features/GithubGenerator.cs @@ -153,7 +153,7 @@ public void OnCall(string[] args) case ConfigEntry<InputHandler.ConsoleInputSystem> config: { - stringBuilder.Append($"[ConsoleInputSystem](#ConsoleInputSystem){ColumnSeparator}{config.Default}"); + stringBuilder.Append($"[ConsoleInputSystem](#consoleinputsystem){ColumnSeparator}{config.Default}"); break; } diff --git a/README.md b/README.md index 660463f..9c40690 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ multiadmin_debug_log | Boolean | True | Enables MultiAdmin debug logging, this l multiadmin_debug_log_blacklist | String List | HandleMessage, StringMatches, MessageListener | Which tags to block for MultiAdmin debug logging multiadmin_debug_log_whitelist | String List | **Empty** | Which tags to log for MultiAdmin debug logging (Defaults to logging all if none are provided) use_new_input_system | Boolean | True | **OBSOLETE: Use `console_input_system` instead, this config option may be removed in a future version of MultiAdmin.** Whether to use the new input system, if false, the original input system will be used -console_input_system | [ConsoleInputSystem](#ConsoleInputSystem) | New | Which console input system to use +console_input_system | [ConsoleInputSystem](#consoleinputsystem) | New | Which console input system to use hide_input | Boolean | False | Whether to hide console input, if true, typed input will not be printed port | Unsigned Integer | 7777 | The port for the server to use copy_from_folder_on_reload | String | **Empty** | The location of a folder to copy files from into the folder defined by `config_location` whenever the configuration file is reloaded From f9dff6cbf90e8dd4a0618e9dc7c1e31f0eb33308 Mon Sep 17 00:00:00 2001 From: Butterscotch! <bscotchvanilla@gmail.com> Date: Mon, 16 Jan 2023 23:39:45 -0500 Subject: [PATCH 12/16] Add `input-system` execution argument --- MultiAdmin/Config/MultiAdminConfig.cs | 5 +++++ MultiAdmin/Program.cs | 10 ++++++---- README.md | 1 + 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/MultiAdmin/Config/MultiAdminConfig.cs b/MultiAdmin/Config/MultiAdminConfig.cs index df74f7e..f3cba36 100644 --- a/MultiAdmin/Config/MultiAdminConfig.cs +++ b/MultiAdmin/Config/MultiAdminConfig.cs @@ -179,6 +179,11 @@ public InputHandler.ConsoleInputSystem ActualConsoleInputSystem { get { + // If defined through execution arguments, use that as an override + var programInputSystem = Program.ConsoleInputSystem; + if (programInputSystem != null) + return (InputHandler.ConsoleInputSystem)programInputSystem; + if (UseNewInputSystem.Value) { switch (ConsoleInputSystem.Value) diff --git a/MultiAdmin/Program.cs b/MultiAdmin/Program.cs index 08500cf..ff4d99c 100644 --- a/MultiAdmin/Program.cs +++ b/MultiAdmin/Program.cs @@ -69,6 +69,7 @@ public static string[] ServerDirectories #endregion public static bool Headless { get; private set; } + public static InputHandler.ConsoleInputSystem? ConsoleInputSystem { get; private set; } = null; #region Output Printing & Logging @@ -78,14 +79,14 @@ public static void Write(string message, ConsoleColor color = ConsoleColor.DarkY { if (Headless) return; - new ColoredMessage(Utils.TimeStampMessage(message), color).WriteLine(MultiAdminConfig.GlobalConfig?.ActualConsoleInputSystem == InputHandler.ConsoleInputSystem.New); + new ColoredMessage(Utils.TimeStampMessage(message), color).WriteLine(MultiAdminConfig.GlobalConfig.ActualConsoleInputSystem == InputHandler.ConsoleInputSystem.New); } } private static bool IsDebugLogTagAllowed(string tag) { - return (!MultiAdminConfig.GlobalConfig?.DebugLogBlacklist?.Value?.Contains(tag) ?? true) && - ((MultiAdminConfig.GlobalConfig?.DebugLogWhitelist?.Value?.IsEmpty() ?? true) || + return (!MultiAdminConfig.GlobalConfig.DebugLogBlacklist.Value.Contains(tag)) && + (MultiAdminConfig.GlobalConfig.DebugLogWhitelist.Value.IsEmpty() || MultiAdminConfig.GlobalConfig.DebugLogWhitelist.Value.Contains(tag)); } @@ -107,7 +108,7 @@ public static void LogDebug(string tag, string message) { try { - if ((!MultiAdminConfig.GlobalConfig?.DebugLog?.Value ?? true) || + if ((!MultiAdminConfig.GlobalConfig.DebugLog?.Value ?? true) || string.IsNullOrEmpty(MaDebugLogFile) || tag == null || !IsDebugLogTagAllowed(tag)) return; // Assign debug log stream as needed @@ -221,6 +222,7 @@ public static void Main() Args[0] = null; Headless = GetFlagFromArgs(Args, "headless", "h"); + ConsoleInputSystem = Enum.TryParse(GetParamFromArgs(Args, "input-system", "is"), out InputHandler.ConsoleInputSystem inputSystem) ? inputSystem : null; string? serverIdArg = GetParamFromArgs(Args, "server-id", "id"); string? configArg = GetParamFromArgs(Args, "config", "c"); diff --git a/README.md b/README.md index 9c40690..3c1de2b 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ This does not include ingame commands, for a full list type `HELP` in MultiAdmin The arguments available for running MultiAdmin with - `--headless` or `-h`: Runs MultiAdmin in headless mode, this makes MultiAdmin not accept any input at all and only output to log files, not in console (Note: This argument is inherited by processes started by this MultiAdmin process) +- `--input-system <ConsoleInputSystem>` or `-is <ConsoleInputSystem>`: The [ConsoleInputSystem](#consoleinputsystem) to use for this MultiAdmin instance (Note: This is used over the config option `console_input_system` and is inherited by processes started by this MultiAdmin process) - `--server-id <Server ID>` or `-id <Server ID>`: The Server ID to run this MultiAdmin instance with a config location (`--config` or `-c`) so that it reads the configs from the location, but stores the logs in the Server ID's folder - `--config <Config Location>` or `-c <Config Location>`: The config location to use for this MultiAdmin instance (Note: This is used over the config option `config_location`) - `--port <Server Port>` or `-p <Server Port>`: The port to use for this MultiAdmin instance (Note: This is used over the config option `port` and is inherited by processes started by this MultiAdmin process) From 804de92177790eeb901b955c8be799114b0e210d Mon Sep 17 00:00:00 2001 From: Butterscotch! <bscotchvanilla@gmail.com> Date: Mon, 16 Jan 2023 23:43:09 -0500 Subject: [PATCH 13/16] Only run workflow on pushes to main --- .github/workflows/main.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index cf8128e..43bfc18 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,6 +1,12 @@ name: MultiAdmin Build -on: [push, pull_request] +on: + push: + branches: + - main + pull_request: + workflow_dispatch: + create: jobs: build: From 48e6dfd1efb37b4e43d60b6d5b3b2b95f17d617c Mon Sep 17 00:00:00 2001 From: Butterscotch! <bscotchvanilla@gmail.com> Date: Mon, 16 Jan 2023 23:45:56 -0500 Subject: [PATCH 14/16] Use `cache-apt-pkgs-action` --- .github/workflows/main.yml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 43bfc18..5199b39 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -27,10 +27,16 @@ jobs: - uses: actions/checkout@v3 - if: matrix.os == 'ubuntu-20.04' - name: Install Linux packages - run: | - sudo apt update - sudo apt install -y clang zlib1g-dev libkrb5-dev libtinfo5 + name: Set up Linux dependencies + uses: awalsh128/cache-apt-pkgs-action@latest + with: + packages: clang zlib1g-dev libkrb5-dev libtinfo5 + # Increment to invalidate the cache + version: 1.0 + # Enables a workaround to attempt to run pre and post install scripts + execute_install_scripts: true + # Disables uploading logs as a build artifact + debug: false - name: Setup .NET uses: actions/setup-dotnet@v3 From 6c0a64004e925d516c93b071bfb9bd7d6d41cad0 Mon Sep 17 00:00:00 2001 From: Butterscotch! <bscotchvanilla@gmail.com> Date: Tue, 17 Jan 2023 00:44:02 -0500 Subject: [PATCH 15/16] Also build on develop branch --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5199b39..3e07afb 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -4,6 +4,7 @@ on: push: branches: - main + - develop pull_request: workflow_dispatch: create: From d9802ddb0fe5ed40778f2ba52150d2296ccaa913 Mon Sep 17 00:00:00 2001 From: Butterscotch! <bscotchvanilla@gmail.com> Date: Tue, 17 Jan 2023 00:46:59 -0500 Subject: [PATCH 16/16] Test exit output --- MultiAdmin/Program.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MultiAdmin/Program.cs b/MultiAdmin/Program.cs index ff4d99c..72eabcc 100644 --- a/MultiAdmin/Program.cs +++ b/MultiAdmin/Program.cs @@ -295,6 +295,8 @@ public static void Main() server.StartServer(); } + + LogDebug(nameof(Main), "Exiting MultiAdmin..."); } public static string? GetParamFromArgs(string?[] args, string[]? keys = null, string[]? aliases = null)