From b9872823ed47df47ddb0c3fd7e312dc78f5874a1 Mon Sep 17 00:00:00 2001 From: Dankrushen Date: Thu, 6 Aug 2020 01:04:37 -0400 Subject: [PATCH 01/11] Add MultiAdmin arg & better output detection --- MultiAdmin/Server.cs | 1 + MultiAdmin/ServerIO/OutputHandler.cs | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/MultiAdmin/Server.cs b/MultiAdmin/Server.cs index 9b684ea..679bfbc 100644 --- a/MultiAdmin/Server.cs +++ b/MultiAdmin/Server.cs @@ -295,6 +295,7 @@ public void StartServer(bool restartOnCrash = true) List scpslArgs = new List { + "-multiadmin", "-batchmode", "-nographics", "-silent-crashes", diff --git a/MultiAdmin/ServerIO/OutputHandler.cs b/MultiAdmin/ServerIO/OutputHandler.cs index 85cfc41..23dbd16 100644 --- a/MultiAdmin/ServerIO/OutputHandler.cs +++ b/MultiAdmin/ServerIO/OutputHandler.cs @@ -9,6 +9,7 @@ public class OutputHandler { public static readonly Regex SmodRegex = new Regex(@"\[(DEBUG|INFO|WARN|ERROR)\] (\[.*?\]) (.*)", RegexOptions.Compiled | RegexOptions.Singleline); + public static readonly char[] TrimList = { '.', ' ', '\t', '!', '?', ',' }; private readonly Server server; @@ -78,28 +79,33 @@ public void HandleMessage(object source, string message) } } - switch (coloredMessage.text.Trim('.', ' ', '\t')) + switch (coloredMessage.text.Trim(TrimList).ToLower()) { - case "The round is about to restart! Please wait": + case "multiadmin:round-end-event": + case "the round is about to restart! please wait": server.ForEachHandler(roundEnd => roundEnd.OnRoundEnd()); break; - case "Waiting for players": + case "multiadmin:waiting-for-players-event": + case "waiting for players": server.IsLoading = false; server.ForEachHandler(waitingForPlayers => waitingForPlayers.OnWaitingForPlayers()); break; - case "New round has been started": + case "multiadmin:round-start-event": + case "new round has been started": server.ForEachHandler(roundStart => roundStart.OnRoundStart()); break; - case "Level loaded. Creating match": + case "multiadmin:server-start-event": + case "level loaded. creating match": server.ForEachHandler(serverStart => serverStart.OnServerStart()); break; - case "Server full": + case "multiadmin:server-full-event": + case "server full": server.ForEachHandler(serverFull => serverFull.OnServerFull()); break; } From c213097aac4b1cbe87dc04462183c1f6c043454a Mon Sep 17 00:00:00 2001 From: Dankrushen Date: Thu, 6 Aug 2020 01:12:11 -0400 Subject: [PATCH 02/11] Add version to argument --- MultiAdmin/Server.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MultiAdmin/Server.cs b/MultiAdmin/Server.cs index 679bfbc..aad53bf 100644 --- a/MultiAdmin/Server.cs +++ b/MultiAdmin/Server.cs @@ -295,7 +295,7 @@ public void StartServer(bool restartOnCrash = true) List scpslArgs = new List { - "-multiadmin", + $"-multiadmin{Program.MaVersion}", "-batchmode", "-nographics", "-silent-crashes", From c1ce457f23aca13f53c31889e78268146cd30f20 Mon Sep 17 00:00:00 2001 From: Dankrushen Date: Thu, 6 Aug 2020 01:31:26 -0400 Subject: [PATCH 03/11] Use IsEmpty in OutputHandler --- MultiAdmin/ServerIO/OutputHandler.cs | 7 +++++-- MultiAdmin/Utility/EmptyExtensions.cs | 10 ++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/MultiAdmin/ServerIO/OutputHandler.cs b/MultiAdmin/ServerIO/OutputHandler.cs index 23dbd16..efc08ad 100644 --- a/MultiAdmin/ServerIO/OutputHandler.cs +++ b/MultiAdmin/ServerIO/OutputHandler.cs @@ -2,6 +2,7 @@ using System.Globalization; using System.Text.RegularExpressions; using MultiAdmin.ConsoleTools; +using MultiAdmin.Utility; namespace MultiAdmin.ServerIO { @@ -25,8 +26,9 @@ public void HandleMessage(object source, string message) ColoredMessage coloredMessage = new ColoredMessage(message, ConsoleColor.White); - if (coloredMessage.text.Length > 0) + if (!coloredMessage.text.IsEmpty()) { + // Convert the first character to the corresponding color if (byte.TryParse(coloredMessage.text[0].ToString(), NumberStyles.HexNumber, NumberFormatInfo.CurrentInfo, out byte consoleColor)) { @@ -79,7 +81,8 @@ public void HandleMessage(object source, string message) } } - switch (coloredMessage.text.Trim(TrimList).ToLower()) + string trimmedMessage = coloredMessage.text.Trim(TrimList); + switch (trimmedMessage.ToLower()) { case "multiadmin:round-end-event": case "the round is about to restart! please wait": diff --git a/MultiAdmin/Utility/EmptyExtensions.cs b/MultiAdmin/Utility/EmptyExtensions.cs index 98b469c..be39677 100644 --- a/MultiAdmin/Utility/EmptyExtensions.cs +++ b/MultiAdmin/Utility/EmptyExtensions.cs @@ -76,5 +76,15 @@ public static bool IsNullOrEmpty(this StringBuilder stringBuilder) { return stringBuilder?.IsEmpty() ?? true; } + + public static bool IsEmpty(this string @string) + { + return @string.Length <= 0; + } + + public static bool IsNullOrEmpty(this string @string) + { + return @string?.IsEmpty() ?? true; + } } } From 2ccde02822bd763a1e5702ebdccb3918d160ad14 Mon Sep 17 00:00:00 2001 From: Dankrushen Date: Thu, 6 Aug 2020 23:21:31 -0400 Subject: [PATCH 04/11] Add ModFeatures enum & update argument --- MultiAdmin/ModFeatures.cs | 15 +++++++++++++++ MultiAdmin/Server.cs | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 MultiAdmin/ModFeatures.cs diff --git a/MultiAdmin/ModFeatures.cs b/MultiAdmin/ModFeatures.cs new file mode 100644 index 0000000..0043e03 --- /dev/null +++ b/MultiAdmin/ModFeatures.cs @@ -0,0 +1,15 @@ +using System; + +namespace MultiAdmin +{ + [Flags] + public enum ModFeatures + { + None = 0, + + // Ex. Feature1 = 1 << 0, + // Feature2 = 1 << 1, + + All = ~(~0 << 0) // All = ~(~0 << 2) + } +} diff --git a/MultiAdmin/Server.cs b/MultiAdmin/Server.cs index aad53bf..4e23880 100644 --- a/MultiAdmin/Server.cs +++ b/MultiAdmin/Server.cs @@ -295,7 +295,7 @@ public void StartServer(bool restartOnCrash = true) List scpslArgs = new List { - $"-multiadmin{Program.MaVersion}", + $"-multiadmin:{Program.MaVersion}:{(int)ModFeatures.All}", "-batchmode", "-nographics", "-silent-crashes", From f231723324dd756430336aa71eb1fd40dd72c605 Mon Sep 17 00:00:00 2001 From: Dankrushen Date: Fri, 7 Aug 2020 17:45:50 -0400 Subject: [PATCH 05/11] Add custom events & mod supported features --- MultiAdmin/ModFeatures.cs | 5 +- MultiAdmin/Server.cs | 2 + MultiAdmin/ServerIO/OutputHandler.cs | 102 +++++++++++++++++++-------- 3 files changed, 76 insertions(+), 33 deletions(-) diff --git a/MultiAdmin/ModFeatures.cs b/MultiAdmin/ModFeatures.cs index 0043e03..9c742fa 100644 --- a/MultiAdmin/ModFeatures.cs +++ b/MultiAdmin/ModFeatures.cs @@ -7,9 +7,8 @@ public enum ModFeatures { None = 0, - // Ex. Feature1 = 1 << 0, - // Feature2 = 1 << 1, + CustomEvents = 1 << 0, - All = ~(~0 << 0) // All = ~(~0 << 2) + All = ~(~0 << 1) } } diff --git a/MultiAdmin/Server.cs b/MultiAdmin/Server.cs index 4e23880..33e2be3 100644 --- a/MultiAdmin/Server.cs +++ b/MultiAdmin/Server.cs @@ -34,6 +34,8 @@ public class Server private DateTime initStopTimeoutTime; private DateTime initRestartTimeoutTime; + public ModFeatures supportedModFeatures = ModFeatures.None; + public Server(string serverId = null, string configLocation = null, uint? port = null) { this.serverId = serverId; diff --git a/MultiAdmin/ServerIO/OutputHandler.cs b/MultiAdmin/ServerIO/OutputHandler.cs index efc08ad..5792384 100644 --- a/MultiAdmin/ServerIO/OutputHandler.cs +++ b/MultiAdmin/ServerIO/OutputHandler.cs @@ -10,7 +10,8 @@ public class OutputHandler { public static readonly Regex SmodRegex = new Regex(@"\[(DEBUG|INFO|WARN|ERROR)\] (\[.*?\]) (.*)", RegexOptions.Compiled | RegexOptions.Singleline); - public static readonly char[] TrimList = { '.', ' ', '\t', '!', '?', ',' }; + public static readonly char[] TrimChars = { '.', ' ', '\t', '!', '?', ',' }; + public static readonly char[] EventSplitChars = new char[] {':'}; private readonly Server server; @@ -81,36 +82,77 @@ public void HandleMessage(object source, string message) } } - string trimmedMessage = coloredMessage.text.Trim(TrimList); - switch (trimmedMessage.ToLower()) + string lowerMessage = coloredMessage.text.ToLower(); + if (!server.supportedModFeatures.HasFlag(ModFeatures.CustomEvents)) { - case "multiadmin:round-end-event": - case "the round is about to restart! please wait": - server.ForEachHandler(roundEnd => roundEnd.OnRoundEnd()); - break; - - case "multiadmin:waiting-for-players-event": - case "waiting for players": - server.IsLoading = false; - - server.ForEachHandler(waitingForPlayers => - waitingForPlayers.OnWaitingForPlayers()); - break; - - case "multiadmin:round-start-event": - case "new round has been started": - server.ForEachHandler(roundStart => roundStart.OnRoundStart()); - break; - - case "multiadmin:server-start-event": - case "level loaded. creating match": - server.ForEachHandler(serverStart => serverStart.OnServerStart()); - break; - - case "multiadmin:server-full-event": - case "server full": - server.ForEachHandler(serverFull => serverFull.OnServerFull()); - break; + switch (lowerMessage.Trim(TrimChars)) + { + case "the round is about to restart! please wait": + server.ForEachHandler(roundEnd => roundEnd.OnRoundEnd()); + break; + + case "waiting for players": + server.IsLoading = false; + + server.ForEachHandler(waitingForPlayers => waitingForPlayers.OnWaitingForPlayers()); + break; + + case "new round has been started": + server.ForEachHandler(roundStart => roundStart.OnRoundStart()); + break; + + case "level loaded. creating match": + server.ForEachHandler(serverStart => serverStart.OnServerStart()); + break; + + case "server full": + server.ForEachHandler(serverFull => serverFull.OnServerFull()); + break; + } + } + + if (lowerMessage.StartsWith("multiadmin:")) + { + // 11 chars in "multiadmin:" + string eventMessage = coloredMessage.text.Substring(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 + + switch (@event) + { + case "round-end-event": + server.ForEachHandler(roundEnd => roundEnd.OnRoundEnd()); + break; + + case "waiting-for-players-event": + server.IsLoading = false; + + server.ForEachHandler(waitingForPlayers => waitingForPlayers.OnWaitingForPlayers()); + break; + + case "round-start-event": + server.ForEachHandler(roundStart => roundStart.OnRoundStart()); + break; + + case "server-start-event": + server.ForEachHandler(serverStart => serverStart.OnServerStart()); + break; + + case "server-full-event": + server.ForEachHandler(serverFull => serverFull.OnServerFull()); + break; + + case "set-supported-features": + if (int.TryParse(eventData, out int supportedFeatures)) + { + server.supportedModFeatures = (ModFeatures)supportedFeatures; + } + break; + } } } From e58ef7c5aae1eec9817bea86c7fd45e12dfe8e1b Mon Sep 17 00:00:00 2001 From: Dankrushen Date: Fri, 7 Aug 2020 17:55:42 -0400 Subject: [PATCH 06/11] Bump version --- MultiAdmin/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MultiAdmin/Program.cs b/MultiAdmin/Program.cs index 5935e4f..1a273d9 100644 --- a/MultiAdmin/Program.cs +++ b/MultiAdmin/Program.cs @@ -15,7 +15,7 @@ namespace MultiAdmin { public static class Program { - public const string MaVersion = "3.3.0.1"; + public const string MaVersion = "3.3.0.2"; public const string RecommendedMonoVersion = "5.18"; private static readonly List InstantiatedServers = new List(); From 01a0dcede9d6d9934053939bd39fdd534d607c85 Mon Sep 17 00:00:00 2001 From: Dankrushen Date: Fri, 7 Aug 2020 18:16:10 -0400 Subject: [PATCH 07/11] Don't print MultiAdmin events --- MultiAdmin/ServerIO/OutputHandler.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/MultiAdmin/ServerIO/OutputHandler.cs b/MultiAdmin/ServerIO/OutputHandler.cs index 5792384..d96acf6 100644 --- a/MultiAdmin/ServerIO/OutputHandler.cs +++ b/MultiAdmin/ServerIO/OutputHandler.cs @@ -153,6 +153,9 @@ public void HandleMessage(object source, string message) } break; } + + // Don't print any MultiAdmin events + return; } } From 74e6ff6affab6188ef6a565f7a4b2317341f4fcb Mon Sep 17 00:00:00 2001 From: Dankrushen Date: Fri, 7 Aug 2020 18:50:40 -0400 Subject: [PATCH 08/11] Add feature explanations --- MultiAdmin/ModFeatures.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MultiAdmin/ModFeatures.cs b/MultiAdmin/ModFeatures.cs index 9c742fa..2fcbe45 100644 --- a/MultiAdmin/ModFeatures.cs +++ b/MultiAdmin/ModFeatures.cs @@ -7,8 +7,10 @@ public enum ModFeatures { None = 0, + // Replaces detecting game output with MultiAdmin events for game events CustomEvents = 1 << 0, + // Supporting all current features All = ~(~0 << 1) } } From 56ca146cb029a34d262cec96c06751535ac3bcee Mon Sep 17 00:00:00 2001 From: Dankrushen Date: Wed, 19 Aug 2020 00:48:43 -0400 Subject: [PATCH 09/11] Add argument passing to game --- MultiAdmin/Features/NewCommand.cs | 4 +- MultiAdmin/Program.cs | 90 +++++++++++-------- MultiAdmin/Server.cs | 37 +++++--- .../Utility/StringEnumerableExtensions.cs | 37 ++++++++ 4 files changed, 117 insertions(+), 51 deletions(-) create mode 100644 MultiAdmin/Utility/StringEnumerableExtensions.cs diff --git a/MultiAdmin/Features/NewCommand.cs b/MultiAdmin/Features/NewCommand.cs index 6137f9f..2b0d355 100644 --- a/MultiAdmin/Features/NewCommand.cs +++ b/MultiAdmin/Features/NewCommand.cs @@ -28,7 +28,7 @@ public void OnCall(string[] args) Server.Write($"Launching new server with Server ID: \"{serverId}\"..."); - Program.StartServer(new Server(serverId)); + Program.StartServer(new Server(serverId, args: Program.Args)); } } @@ -86,7 +86,7 @@ public void OnServerFull() Server.Write($"Launching new server with Server ID: \"{onFullServerId}\" due to this server being full..."); - onFullServerInstance = Program.StartServer(new Server(onFullServerId)); + onFullServerInstance = Program.StartServer(new Server(onFullServerId, args: Program.Args)); } } } diff --git a/MultiAdmin/Program.cs b/MultiAdmin/Program.cs index 1a273d9..dbcabd2 100644 --- a/MultiAdmin/Program.cs +++ b/MultiAdmin/Program.cs @@ -28,6 +28,7 @@ public static class Program : null; private static uint? portArg; + public static readonly string[] Args = Environment.GetCommandLineArgs(); private static IExitSignal exitSignalListener; @@ -37,7 +38,7 @@ public static class Program #region Server Properties public static Server[] Servers => ServerDirectories - .Select(serverDir => new Server(Path.GetFileName(serverDir), serverDir, portArg)).ToArray(); + .Select(serverDir => new Server(Path.GetFileName(serverDir), serverDir, portArg, Args)).ToArray(); public static string[] ServerDirectories { @@ -206,20 +207,24 @@ public static void Main() exitSignalListener.Exit += OnExit; } - Headless = GetFlagFromArgs("headless", "h"); + // Remove executable path + if (Args.Length > 0) + Args[0] = null; + + Headless = GetFlagFromArgs(Args, "headless", "h"); if (!Headless) CheckMonoVersion(); - string serverIdArg = GetParamFromArgs("server-id", "id"); - string configArg = GetParamFromArgs("config", "c"); - portArg = uint.TryParse(GetParamFromArgs("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) ? (uint?)port : null; Server server = null; if (!string.IsNullOrEmpty(serverIdArg) || !string.IsNullOrEmpty(configArg)) { - server = new Server(serverIdArg, configArg, portArg); + server = new Server(serverIdArg, configArg, portArg, Args); InstantiatedServers.Add(server); } @@ -227,7 +232,7 @@ public static void Main() { if (Servers.IsEmpty()) { - server = new Server(port: portArg); + server = new Server(port: portArg, args: Args); InstantiatedServers.Add(server); } @@ -240,7 +245,7 @@ public static void Main() Write("No servers are set to automatically start, please enter a Server ID to start:"); InputHandler.InputPrefix?.Write(); - server = new Server(Console.ReadLine(), port: portArg); + server = new Server(Console.ReadLine(), port: portArg, args: Args); InstantiatedServers.Add(server); } @@ -284,15 +289,13 @@ public static void Main() } } - public static string GetParamFromArgs(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(); if (!hasKeys && !hasAliases) return null; - string[] args = Environment.GetCommandLineArgs(); - for (int i = 0; i < args.Length - 1; i++) { string lowArg = args[i]?.ToLower(); @@ -301,17 +304,27 @@ public static string GetParamFromArgs(string[] keys = null, string[] aliases = n if (hasKeys) { - if (keys.Any(key => !string.IsNullOrEmpty(key) && lowArg == $"--{key.ToLower()}")) + if (keys.Any(key => lowArg == $"--{key?.ToLower()}")) { - return args[i + 1]; + string value = args[i + 1]; + + args[i] = null; + args[i + 1] = null; + + return value; } } if (hasAliases) { - if (aliases.Any(alias => !string.IsNullOrEmpty(alias) && lowArg == $"-{alias.ToLower()}")) + if (aliases.Any(alias => lowArg == $"-{alias?.ToLower()}")) { - return args[i + 1]; + string value = args[i + 1]; + + args[i] = null; + args[i + 1] = null; + + return value; } } } @@ -319,26 +332,33 @@ public static string GetParamFromArgs(string[] keys = null, string[] aliases = n return null; } - public static bool ArgsContainsParam(string[] keys = null, string[] aliases = null) + public static bool ArgsContainsParam(string[] args, string[] keys = null, string[] aliases = null) { - foreach (string arg in Environment.GetCommandLineArgs()) + bool hasKeys = !keys.IsNullOrEmpty(); + bool hasAliases = !aliases.IsNullOrEmpty(); + + if (!hasKeys && !hasAliases) return false; + + for (int i = 0; i < args.Length; i++) { - string lowArg = arg?.ToLower(); + string lowArg = args[i]?.ToLower(); if (string.IsNullOrEmpty(lowArg)) continue; - if (!keys.IsNullOrEmpty()) + if (hasKeys) { - if (keys.Any(key => !string.IsNullOrEmpty(key) && lowArg == $"--{key.ToLower()}")) + if (keys.Any(key => lowArg == $"--{key?.ToLower()}")) { + args[i] = null; return true; } } - if (!aliases.IsNullOrEmpty()) + if (hasAliases) { - if (aliases.Any(alias => !string.IsNullOrEmpty(alias) && lowArg == $"-{alias.ToLower()}")) + if (aliases.Any(alias => lowArg == $"-{alias?.ToLower()}")) { + args[i] = null; return true; } } @@ -347,28 +367,28 @@ public static bool ArgsContainsParam(string[] keys = null, string[] aliases = nu return false; } - public static bool GetFlagFromArgs(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; - return bool.TryParse(GetParamFromArgs(keys, aliases), out bool result) + return bool.TryParse(GetParamFromArgs(args, keys, aliases), out bool result) ? result - : ArgsContainsParam(keys, aliases); + : ArgsContainsParam(args, keys, aliases); } - public static string GetParamFromArgs(string key = null, string alias = null) + public static string GetParamFromArgs(string[] args, string key = null, string alias = null) { - return GetParamFromArgs(new string[] {key}, new string[] {alias}); + return GetParamFromArgs(args, new string[] {key}, new string[] {alias}); } - public static bool ArgsContainsParam(string key = null, string alias = null) + public static bool ArgsContainsParam(string[] args, string key = null, string alias = null) { - return ArgsContainsParam(new string[] {key}, new string[] {alias}); + return ArgsContainsParam(args, new string[] {key}, new string[] {alias}); } - public static bool GetFlagFromArgs(string key = null, string alias = null) + public static bool GetFlagFromArgs(string[] args, string key = null, string alias = null) { - return GetFlagFromArgs(new string[] {key}, new string[] {alias}); + return GetFlagFromArgs(args, new string[] {key}, new string[] {alias}); } public static Process StartServer(Server server) @@ -380,7 +400,7 @@ public static Process StartServer(Server server) Write("Error while starting new server: Could not find the executable location!", ConsoleColor.Red); } - List args = new List(); + List args = new List(server.args); if (!string.IsNullOrEmpty(server.serverId)) args.Add($"-id \"{server.serverId}\""); @@ -391,11 +411,7 @@ public static Process StartServer(Server server) if (Headless) args.Add("-h"); - args.RemoveAll(string.IsNullOrEmpty); - - string stringArgs = string.Join(" ", args); - - ProcessStartInfo startInfo = new ProcessStartInfo(assemblyLocation, stringArgs); + ProcessStartInfo startInfo = new ProcessStartInfo(assemblyLocation, args.JoinArgs()); Write($"Launching \"{startInfo.FileName}\" with arguments \"{startInfo.Arguments}\"..."); diff --git a/MultiAdmin/Server.cs b/MultiAdmin/Server.cs index 33e2be3..89b2361 100644 --- a/MultiAdmin/Server.cs +++ b/MultiAdmin/Server.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Reflection; +using System.Text; using System.Threading; using MultiAdmin.Config; using MultiAdmin.ConsoleTools; @@ -28,6 +29,7 @@ public class Server public readonly string serverId; public readonly string configLocation; public readonly uint? port; + public readonly string[] args; public readonly string serverDir; public readonly string logDir; @@ -36,7 +38,7 @@ public class Server public ModFeatures supportedModFeatures = ModFeatures.None; - public Server(string serverId = null, string configLocation = null, uint? port = null) + public Server(string serverId = null, string configLocation = null, uint? port = null, string[] args = null) { this.serverId = serverId; serverDir = string.IsNullOrEmpty(this.serverId) @@ -73,6 +75,9 @@ public Server(string serverId = null, string configLocation = null, uint? port = // Set port this.port = port; + // Set args + this.args = args; + logDir = Utils.GetFullPathSafe(Path.Combine(string.IsNullOrEmpty(serverDir) ? "" : serverDir, serverConfig.LogLocation.Value)); @@ -312,13 +317,20 @@ public void StartServer(bool restartOnCrash = true) scpslArgs.Add("-nolog"); if (Utils.IsUnix) - scpslArgs.Add("-logFile \"/dev/null\""); + { + scpslArgs.Add("-logFile"); + scpslArgs.Add("/dev/null"); + } else if (Utils.IsWindows) - scpslArgs.Add("-logFile \"NUL\""); + { + scpslArgs.Add("-logFile"); + scpslArgs.Add("NUL"); + } } else { - scpslArgs.Add($"-logFile \"{ScpLogFile}\""); + scpslArgs.Add("-logFile"); + scpslArgs.Add(ScpLogFile); } if (ServerConfig.DisableConfigValidation.Value) @@ -333,26 +345,27 @@ public void StartServer(bool restartOnCrash = true) if (!string.IsNullOrEmpty(configLocation)) { - scpslArgs.Add($"-configpath \"{configLocation}\""); + scpslArgs.Add("-configpath"); + scpslArgs.Add(configLocation); } string appDataPath = Utils.GetFullPathSafe(ServerConfig.AppDataLocation.Value); if (!string.IsNullOrEmpty(appDataPath)) { - scpslArgs.Add($"-appdatapath \"{appDataPath}\""); + scpslArgs.Add("-appdatapath"); + scpslArgs.Add(appDataPath); } - scpslArgs.RemoveAll(string.IsNullOrEmpty); - - string argsString = string.Join(" ", scpslArgs); + // Add custom arguments + scpslArgs.AddRange(args); - Write($"Starting server with the following parameters:\n{scpslExe} {argsString}"); - - ProcessStartInfo startInfo = new ProcessStartInfo(scpslExe, argsString) + ProcessStartInfo startInfo = new ProcessStartInfo(scpslExe, scpslArgs.JoinArgs()) { CreateNoWindow = true, UseShellExecute = false }; + Write($"Starting server with the following parameters:\n{scpslExe} {startInfo.Arguments}"); + ForEachHandler(eventPreStart => eventPreStart.OnServerPreStart()); // Start the input reader diff --git a/MultiAdmin/Utility/StringEnumerableExtensions.cs b/MultiAdmin/Utility/StringEnumerableExtensions.cs new file mode 100644 index 0000000..55cc06e --- /dev/null +++ b/MultiAdmin/Utility/StringEnumerableExtensions.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MultiAdmin.Utility +{ + public static class StringEnumerableExtensions + { + public static string JoinArgs(this IEnumerable args) + { + StringBuilder argsStringBuilder = new StringBuilder(); + foreach (string arg in args) + { + if (arg.IsNullOrEmpty()) + continue; + + // Separate with spaces + if (!argsStringBuilder.IsEmpty()) + argsStringBuilder.Append(' '); + + // Handle spaces by surrounding with quotes + if (arg.Contains(' ')) + { + argsStringBuilder.Append('"'); + argsStringBuilder.Append(arg); + argsStringBuilder.Append('"'); + } + else + { + argsStringBuilder.Append(arg); + } + } + + return argsStringBuilder.ToString(); + } + } +} From a39bcb39c9f1e0db241366ad79d418ab28d557d6 Mon Sep 17 00:00:00 2001 From: Dankrushen Date: Wed, 19 Aug 2020 00:53:37 -0400 Subject: [PATCH 10/11] Escape quotes in arguments --- MultiAdmin/Utility/StringEnumerableExtensions.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/MultiAdmin/Utility/StringEnumerableExtensions.cs b/MultiAdmin/Utility/StringEnumerableExtensions.cs index 55cc06e..1039884 100644 --- a/MultiAdmin/Utility/StringEnumerableExtensions.cs +++ b/MultiAdmin/Utility/StringEnumerableExtensions.cs @@ -14,20 +14,23 @@ public static string JoinArgs(this IEnumerable args) if (arg.IsNullOrEmpty()) continue; + // Escape quotation marks + string escapedArg = arg.Replace("\"", "\\\""); + // Separate with spaces if (!argsStringBuilder.IsEmpty()) argsStringBuilder.Append(' '); // Handle spaces by surrounding with quotes - if (arg.Contains(' ')) + if (escapedArg.Contains(' ')) { argsStringBuilder.Append('"'); - argsStringBuilder.Append(arg); + argsStringBuilder.Append(escapedArg); argsStringBuilder.Append('"'); } else { - argsStringBuilder.Append(arg); + argsStringBuilder.Append(escapedArg); } } From 5c33ab69f8a17e4e7ee837f625e64cf07e57df6a Mon Sep 17 00:00:00 2001 From: Dankrushen Date: Wed, 19 Aug 2020 17:21:52 -0400 Subject: [PATCH 11/11] Add config generator, update README, & update feature names --- MultiAdmin/Features/ConfigGenerator.cs | 92 ++++++++++++++++++++++++++ MultiAdmin/Features/GithubGenerator.cs | 6 +- MultiAdmin/Program.cs | 2 +- README.md | 3 + 4 files changed, 98 insertions(+), 5 deletions(-) create mode 100644 MultiAdmin/Features/ConfigGenerator.cs diff --git a/MultiAdmin/Features/ConfigGenerator.cs b/MultiAdmin/Features/ConfigGenerator.cs new file mode 100644 index 0000000..fd989cb --- /dev/null +++ b/MultiAdmin/Features/ConfigGenerator.cs @@ -0,0 +1,92 @@ +using System.Collections.Generic; +using System.IO; +using System.Text; +using MultiAdmin.Config; +using MultiAdmin.Config.ConfigHandler; +using MultiAdmin.Features.Attributes; +using MultiAdmin.Utility; + +namespace MultiAdmin.Features +{ + [Feature] + internal class ConfigGenerator : Feature, ICommand + { + + public ConfigGenerator(Server server) : base(server) + { + } + + public string GetCommand() + { + return "CONFIGGEN"; + } + + public string GetCommandDescription() + { + return "Generates a full default MultiAdmin config file"; + } + + public string GetUsage() + { + return "[FILE LOCATION]"; + } + + public void OnCall(string[] args) + { + if (args.IsEmpty()) + { + Server.Write("You must specify the location of the file."); + return; + } + + string path = Utils.GetFullPathSafe(string.Join(" ", args)); + + ConfigEntry[] registeredConfigs = MultiAdminConfig.GlobalConfig.GetRegisteredConfigs(); + + List lines = new List(registeredConfigs.Length); + foreach (ConfigEntry configEntry in registeredConfigs) + { + switch (configEntry) + { + case ConfigEntry config: + { + lines.Add($"{config.Key}: {(config.Default == null ? "" : string.Join(", ", config.Default))}"); + break; + } + + default: + { + lines.Add($"{configEntry.Key}: {configEntry.ObjectDefault ?? ""}"); + break; + } + } + } + + File.WriteAllLines(path, lines); + Server.Write($"Default config written to \"{path}\""); + } + + public bool PassToGame() + { + return false; + } + + public override void OnConfigReload() + { + } + + public override string GetFeatureDescription() + { + return "Generates a full default MultiAdmin config file"; + } + + public override string GetFeatureName() + { + return "Config Generator"; + } + + public override void Init() + { + } + } +} diff --git a/MultiAdmin/Features/GithubGenerator.cs b/MultiAdmin/Features/GithubGenerator.cs index 61aac71..156532c 100644 --- a/MultiAdmin/Features/GithubGenerator.cs +++ b/MultiAdmin/Features/GithubGenerator.cs @@ -47,8 +47,6 @@ public void OnCall(string[] args) foreach (Feature feature in Server.features) { - if (feature.Equals(this)) continue; - lines.Add($"- {feature.GetFeatureName()}: {feature.GetFeatureDescription()}"); } @@ -152,12 +150,12 @@ public override void OnConfigReload() public override string GetFeatureDescription() { - return "NOT INCLUDED IN FILE"; + return "Generates a GitHub README file outlining all the features/commands"; } public override string GetFeatureName() { - return "GITHUB GEN"; + return "GitHub Generator"; } public override void Init() diff --git a/MultiAdmin/Program.cs b/MultiAdmin/Program.cs index dbcabd2..9b821e8 100644 --- a/MultiAdmin/Program.cs +++ b/MultiAdmin/Program.cs @@ -15,7 +15,7 @@ namespace MultiAdmin { public static class Program { - public const string MaVersion = "3.3.0.2"; + public const string MaVersion = "3.3.0.3"; public const string RecommendedMonoVersion = "5.18"; private static readonly List InstantiatedServers = new List(); diff --git a/README.md b/README.md index 9771673..e2d7514 100644 --- a/README.md +++ b/README.md @@ -23,9 +23,11 @@ Make sure that you are running Mono 5.18.0 or higher, otherwise you might have i 4. Optional: Create a file named `scp_multiadmin.cfg` within your server's folder for configuring MultiAdmin specifically for that server ## Features +- 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 +- 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 - MultiAdminInfo: Prints MultiAdmin license and version information @@ -39,6 +41,7 @@ Make sure that you are running Mono 5.18.0 or higher, otherwise you might have i ## MultiAdmin Commands This does not include ingame commands, for a full list type `HELP` in MultiAdmin which will produce all commands. +- CONFIGGEN [FILE LOCATION]: Generates a full default MultiAdmin config file - CONFIG : Reloads the configuration file - EXIT: Exits the server - GITHUBGEN [FILE LOCATION]: Generates a GitHub README file outlining all the features/commands