diff --git a/MultiAdmin/Config/MultiAdminConfig.cs b/MultiAdmin/Config/MultiAdminConfig.cs index 2e9fcea..a1073a0 100644 --- a/MultiAdmin/Config/MultiAdminConfig.cs +++ b/MultiAdmin/Config/MultiAdminConfig.cs @@ -18,6 +18,10 @@ public class MultiAdminConfig : InheritableConfigRegister new ConfigEntry("config_location", "", false, "Config Location", "The default location for the game to use for storing configuration files (a directory)"); + public ConfigEntry AppDataLocation { get; } = + new ConfigEntry("appdata_location", "", + "AppData Location", "The location for the game to use for AppData (a directory)"); + public ConfigEntry DisableConfigValidation { get; } = new ConfigEntry("disable_config_validation", false, "Disable Config Validation", "Disable the config validator"); diff --git a/MultiAdmin/EventInterfaces.cs b/MultiAdmin/EventInterfaces.cs index 726d899..35111eb 100644 --- a/MultiAdmin/EventInterfaces.cs +++ b/MultiAdmin/EventInterfaces.cs @@ -4,47 +4,47 @@ public interface IMAEvent { } - public interface IEventServerPreStart: IMAEvent + public interface IEventServerPreStart : IMAEvent { void OnServerPreStart(); } - public interface IEventServerStart: IMAEvent + public interface IEventServerStart : IMAEvent { void OnServerStart(); } - public interface IEventServerStop: IMAEvent + public interface IEventServerStop : IMAEvent { void OnServerStop(); } - public interface IEventRoundEnd: IMAEvent + public interface IEventRoundEnd : IMAEvent { void OnRoundEnd(); } - public interface IEventWaitingForPlayers: IMAEvent + public interface IEventWaitingForPlayers : IMAEvent { void OnWaitingForPlayers(); } - public interface IEventRoundStart: IMAEvent + public interface IEventRoundStart : IMAEvent { void OnRoundStart(); } - public interface IEventCrash: IMAEvent + public interface IEventCrash : IMAEvent { void OnCrash(); } - public interface IEventTick: IMAEvent + public interface IEventTick : IMAEvent { void OnTick(); } - public interface IServerMod: IMAEvent + public interface IServerMod : IMAEvent { } diff --git a/MultiAdmin/Exceptions.cs b/MultiAdmin/Exceptions.cs index f807deb..e0593cc 100644 --- a/MultiAdmin/Exceptions.cs +++ b/MultiAdmin/Exceptions.cs @@ -4,6 +4,7 @@ namespace MultiAdmin { public static class Exceptions { + [Serializable] public class ServerNotRunningException : Exception { public ServerNotRunningException() : base("The server is not running") @@ -11,6 +12,7 @@ public ServerNotRunningException() : base("The server is not running") } } + [Serializable] public class ServerAlreadyRunningException : Exception { public ServerAlreadyRunningException() : base("The server is already running") diff --git a/MultiAdmin/Features/RestartRoundCounter.cs b/MultiAdmin/Features/RestartRoundCounter.cs index d46321a..4ba6a4c 100644 --- a/MultiAdmin/Features/RestartRoundCounter.cs +++ b/MultiAdmin/Features/RestartRoundCounter.cs @@ -1,4 +1,3 @@ -using System; using MultiAdmin.Features.Attributes; namespace MultiAdmin.Features diff --git a/MultiAdmin/Program.cs b/MultiAdmin/Program.cs index e7352b8..8dca438 100644 --- a/MultiAdmin/Program.cs +++ b/MultiAdmin/Program.cs @@ -15,7 +15,7 @@ namespace MultiAdmin { public static class Program { - public const string MaVersion = "3.2.4.3"; + public const string MaVersion = "3.2.5.1"; public const string RecommendedMonoVersion = "5.18"; private static readonly List InstantiatedServers = new List(); diff --git a/MultiAdmin/Server.cs b/MultiAdmin/Server.cs index 60a7a7c..2bfc497 100644 --- a/MultiAdmin/Server.cs +++ b/MultiAdmin/Server.cs @@ -175,9 +175,11 @@ private set private void MainLoop() { + Stopwatch timer = new Stopwatch(); while (IsGameProcessRunning) { - Stopwatch timer = Stopwatch.StartNew(); + timer.Reset(); + timer.Start(); foreach (IEventTick tickEvent in tick) tickEvent.OnTick(); @@ -345,13 +347,19 @@ public void StartServer(bool restartOnCrash = true) scpslArgs.Add($"-configpath \"{configLocation}\""); } + string appDataPath = Utils.GetFullPathSafe(ServerConfig.AppDataLocation.Value); + if (!string.IsNullOrEmpty(appDataPath)) + { + scpslArgs.Add($"-appdatapath \"{appDataPath}\""); + } + scpslArgs.RemoveAll(string.IsNullOrEmpty); string argsString = string.Join(" ", scpslArgs); Write($"Starting server with the following parameters:\n{scpslExe} {argsString}"); - ProcessStartInfo startInfo = new ProcessStartInfo(scpslExe, argsString); + ProcessStartInfo startInfo = new ProcessStartInfo(scpslExe, argsString) {CreateNoWindow = true}; ForEachHandler(eventPreStart => eventPreStart.OnServerPreStart()); diff --git a/MultiAdmin/ServerIO/StringSections.cs b/MultiAdmin/ServerIO/StringSections.cs index d324730..1642139 100644 --- a/MultiAdmin/ServerIO/StringSections.cs +++ b/MultiAdmin/ServerIO/StringSections.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Text; using MultiAdmin.ConsoleTools; @@ -45,6 +46,12 @@ public StringSections(StringSection[] sections) public static StringSections FromString(string fullString, int sectionLength, ColoredMessage leftIndicator = null, ColoredMessage rightIndicator = null, ColoredMessage sectionBase = null) { + int rightIndicatorLength = rightIndicator?.Length ?? 0; + int totalIndicatorLength = (leftIndicator?.Length ?? 0) + rightIndicatorLength; + + if (fullString.Length > sectionLength && sectionLength <= totalIndicatorLength) + throw new ArgumentException($"{nameof(sectionLength)} must be greater than the total length of {nameof(leftIndicator)} and {nameof(rightIndicator)}", nameof(sectionLength)); + List sections = new List(); if (string.IsNullOrEmpty(fullString)) @@ -65,12 +72,12 @@ public static StringSections FromString(string fullString, int sectionLength, Co curSecBuilder.Append(fullString[i]); // If the section is less than the smallest possible section size, skip processing - if (curSecBuilder.Length < sectionLength - ((leftIndicator?.Length ?? 0) + (rightIndicator?.Length ?? 0))) continue; + 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; // Decide what the right indicator text should be accounting for the rightmost section - ColoredMessage rightIndicatorSection = i < fullString.Length - (1 + (rightIndicator?.Length ?? 0)) ? rightIndicator : null; + ColoredMessage rightIndicatorSection = i < fullString.Length - (1 + rightIndicatorLength) ? rightIndicator : null; // Check the section length against the final section length if (curSecBuilder.Length >= sectionLength - ((leftIndicatorSection?.Length ?? 0) + (rightIndicatorSection?.Length ?? 0))) diff --git a/MultiAdminTests/ServerIO/StringSectionsTests.cs b/MultiAdminTests/ServerIO/StringSectionsTests.cs index 7e5abb3..15d4b2a 100644 --- a/MultiAdminTests/ServerIO/StringSectionsTests.cs +++ b/MultiAdminTests/ServerIO/StringSectionsTests.cs @@ -1,4 +1,6 @@ +using System; using Microsoft.VisualStudio.TestTools.UnitTesting; +using MultiAdmin.ConsoleTools; using MultiAdmin.ServerIO; namespace MultiAdminTests.ServerIO @@ -6,32 +8,63 @@ namespace MultiAdminTests.ServerIO [TestClass] public class StringSectionsTests { + private struct FromStringTemplate + { + public readonly string testString; + public readonly string[] expectedSections; + + public readonly int sectionLength; + public readonly ColoredMessage leftIndictator; + public readonly ColoredMessage rightIndictator; + + public FromStringTemplate(string testString, string[] expectedSections, int sectionLength, ColoredMessage leftIndictator = null, ColoredMessage rightIndictator = null) + { + this.testString = testString; + this.expectedSections = expectedSections; + + this.sectionLength = sectionLength; + this.leftIndictator = leftIndictator; + this.rightIndictator = rightIndictator; + } + } + [TestMethod] public void FromStringTest() { - string[] expectedSections = + try { - "te", - "st", - " s", - "tr", - "in", - "g" + StringSections.FromString("test string", 2, new ColoredMessage("."), new ColoredMessage(".")); + Assert.Fail("This case should not be allowed, no further characters can be output because of the prefix and suffix"); + } + catch (ArgumentException) + { + // Expected behaviour + } + + FromStringTemplate[] sectionTests = + { + new FromStringTemplate("test string", new string[] {"te", "st", " s", "tr", "in", "g"}, 2), + new FromStringTemplate("test string", new string[] {"tes..", ".t ..", ".st..", ".ring"}, 5, new ColoredMessage("."), new ColoredMessage("..")) }; - StringSections sections = StringSections.FromString("test string", 2); + for (int i = 0; i < sectionTests.Length; i++) + { + FromStringTemplate sectionTest = sectionTests[i]; + + StringSections sections = StringSections.FromString(sectionTest.testString, sectionTest.sectionLength, sectionTest.leftIndictator, sectionTest.rightIndictator); - Assert.IsNotNull(sections); - Assert.IsNotNull(sections.Sections); + Assert.IsNotNull(sections); + Assert.IsNotNull(sections.Sections); - Assert.IsTrue(sections.Sections.Length == expectedSections.Length, $"Expected sections length \"{expectedSections.Length}\", got \"{sections.Sections.Length}\""); + Assert.IsTrue(sections.Sections.Length == sectionTest.expectedSections.Length, $"Failed at index {i}: Expected sections length \"{sectionTest.expectedSections.Length}\", got \"{sections.Sections.Length}\""); - for (int i = 0; i < expectedSections.Length; i++) - { - string expected = expectedSections[i]; - string result = sections.Sections[i].Text?.text; + for (int j = 0; j < sectionTest.expectedSections.Length; j++) + { + string expected = sectionTest.expectedSections[j]; + string result = sections.Sections[j].Section.GetText(); - Assert.AreEqual(expected, result, $"Failed at section index {i}: Expected section text to be \"{expected ?? "null"}\", got \"{result ?? "null"}\""); + Assert.AreEqual(expected, result, $"Failed at index {i}: Failed at section index {j}: Expected section text to be \"{expected ?? "null"}\", got \"{result ?? "null"}\""); + } } } } diff --git a/README.md b/README.md index 095de76..dbc3797 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,7 @@ Any configuration files within the directory defined by `servers_folder` will ha Config Option | Value Type | Default Value | Description --- | :---: | :---: | :------: config_location | String | **Empty** | The default location for the game to use for storing configuration files (a directory) +appdata_location | String | **Empty** | The location for the game to use for AppData (a directory) disable_config_validation | Boolean | False | Disable the config validator share_non_configs | Boolean | True | Makes all files other than the config files store in AppData multiadmin_nolog | Boolean | False | Disable logging to file