diff --git a/Application/Configuration/LoggingConfiguration.json b/Application/Configuration/LoggingConfiguration.json index 9024132e7..44de65ee9 100644 --- a/Application/Configuration/LoggingConfiguration.json +++ b/Application/Configuration/LoggingConfiguration.json @@ -23,7 +23,7 @@ "Name": "Console", "Args": { "outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff} {Server} {Level:u3}] {Message:lj}{NewLine}{Exception}", - "RestrictedToMinimumLevel": "Critical" + "RestrictedToMinimumLevel": "Fatal" } } ], diff --git a/Application/RConParsers/DynamicRConParserConfiguration.cs b/Application/RConParsers/DynamicRConParserConfiguration.cs index f70f69fa8..87fb54d6d 100644 --- a/Application/RConParsers/DynamicRConParserConfiguration.cs +++ b/Application/RConParsers/DynamicRConParserConfiguration.cs @@ -28,6 +28,8 @@ namespace IW4MAdmin.Application.RConParsers public int NoticeMaximumLines { get; set; } = 8; public int NoticeMaxCharactersPerLine { get; set; } = 50; public string NoticeLineSeparator { get; set; } = Environment.NewLine; + public int? DefaultRConPort { get; set; } + public string DefaultInstallationDirectoryHint { get; set; } public DynamicRConParserConfiguration(IParserRegexFactory parserRegexFactory) { diff --git a/Plugins/ScriptPlugins/ParserCSGO.js b/Plugins/ScriptPlugins/ParserCSGO.js index 4c0e4a5f6..30a5b3f72 100644 --- a/Plugins/ScriptPlugins/ParserCSGO.js +++ b/Plugins/ScriptPlugins/ParserCSGO.js @@ -3,7 +3,7 @@ let eventParser; const plugin = { author: 'RaidMax', - version: 0.3, + version: 0.4, name: 'CS:GO Parser', engine: 'Source', isParser: true, @@ -58,6 +58,7 @@ const plugin = { rconParser.Configuration.OverrideDvarNameMapping.Add('g_password', 'sv_password'); rconParser.Configuration.NoticeLineSeparator = '. '; + rconParser.Configuration.DefaultRConPort = 27015; rconParser.CanGenerateLogPath = false; rconParser.Configuration.CommandPrefixes.RConGetInfo = undefined; diff --git a/Plugins/ScriptPlugins/ParserCSGOSM.js b/Plugins/ScriptPlugins/ParserCSGOSM.js index bc7307508..d6811057e 100644 --- a/Plugins/ScriptPlugins/ParserCSGOSM.js +++ b/Plugins/ScriptPlugins/ParserCSGOSM.js @@ -3,7 +3,7 @@ let eventParser; const plugin = { author: 'RaidMax', - version: 0.3, + version: 0.4, name: 'CS:GO (SourceMod) Parser', engine: 'Source', isParser: true, @@ -58,6 +58,7 @@ const plugin = { rconParser.Configuration.OverrideDvarNameMapping.Add('g_password', 'sv_password'); rconParser.Configuration.NoticeLineSeparator = '. '; + rconParser.Configuration.DefaultRConPort = 27015; rconParser.CanGenerateLogPath = false; rconParser.Configuration.CommandPrefixes.RConGetInfo = undefined; diff --git a/Plugins/ScriptPlugins/ParserCoD4x.js b/Plugins/ScriptPlugins/ParserCoD4x.js index 982acd44d..ff84200c1 100644 --- a/Plugins/ScriptPlugins/ParserCoD4x.js +++ b/Plugins/ScriptPlugins/ParserCoD4x.js @@ -3,7 +3,7 @@ var eventParser; var plugin = { author: 'FrenchFry, RaidMax', - version: 0.7, + version: 0.8, name: 'CoD4x Parser', isParser: true, @@ -25,6 +25,7 @@ var plugin = { rconParser.Configuration.Dvar.AddMapping(110, 4); // dvar info rconParser.Configuration.GuidNumberStyle = 7; // Integer rconParser.Configuration.NoticeLineSeparator = '. '; // CoD4x does not support \n in the client notice + rconParser.Configuration.DefaultRConPort = 28960; rconParser.Version = 'CoD4 X - win_mingw-x86 build 1056 Dec 12 2020'; rconParser.GameName = 1; // IW3 diff --git a/Plugins/ScriptPlugins/ParserIW4x.js b/Plugins/ScriptPlugins/ParserIW4x.js index 3be55cb38..4f9bbe466 100644 --- a/Plugins/ScriptPlugins/ParserIW4x.js +++ b/Plugins/ScriptPlugins/ParserIW4x.js @@ -3,7 +3,7 @@ var eventParser; var plugin = { author: 'RaidMax', - version: 0.4, + version: 0.5, name: 'IW4x Parser', isParser: true, @@ -18,7 +18,11 @@ var plugin = { rconParser.Configuration.CommandPrefixes.Say = 'sayraw {0}'; rconParser.Configuration.CommandPrefixes.Kick = 'clientkick {0} "{1}"'; rconParser.Configuration.CommandPrefixes.Ban = 'clientkick {0} "{1}"'; - rconParser.Configuration.CommandPrefixes.TempBan = 'tempbanclient {0} "{1}"'; + rconParser.Configuration.CommandPrefixes.TempBan = 'tempbanclient {0} "{1}"' + + rconParser.Configuration.DefaultRConPort = 28960; + rconParser.Configuration.DefaultInstallationDirectoryHint = 'HKEY_CURRENT_USER\\Software\\Classes\\iw4x\\shell\\open\\command'; + eventParser.Configuration.GameDirectory = 'userraw'; rconParser.Version = 'IW4x (v0.6.0)'; diff --git a/Plugins/ScriptPlugins/ParserIW6x.js b/Plugins/ScriptPlugins/ParserIW6x.js index bd0bf2fb8..dd177ee37 100644 --- a/Plugins/ScriptPlugins/ParserIW6x.js +++ b/Plugins/ScriptPlugins/ParserIW6x.js @@ -3,7 +3,7 @@ var eventParser; var plugin = { author: 'Xerxes, RaidMax, st0rm', - version: 0.3, + version: 0.4, name: 'IW6x Parser', isParser: true, @@ -24,6 +24,7 @@ var plugin = { rconParser.Configuration.Status.Pattern = '^ *([0-9]+) +-?([0-9]+) +(Yes|No) +((?:[A-Z]+|[0-9]+)) +((?:[a-z]|[0-9]){8,32}|(?:[a-z]|[0-9]){8,32}|bot[0-9]+|(?:[0-9]+)) *(.{0,32}) +(\\d+\\.\\d+\\.\\d+.\\d+\\:-*\\d{1,5}|0+.0+:-*\\d{1,5}|loopback|unknown|bot) +(-*[0-9]+) *$'; rconParser.Configuration.StatusHeader.Pattern = 'num +score +bot +ping +guid +name +address +qport *'; rconParser.Configuration.WaitForResponse = false; + rconParser.Configuration.DefaultRConPort = 28960; rconParser.Configuration.Status.AddMapping(102, 4); rconParser.Configuration.Status.AddMapping(103, 5); rconParser.Configuration.Status.AddMapping(104, 6); diff --git a/Plugins/ScriptPlugins/ParserPIW5.js b/Plugins/ScriptPlugins/ParserPIW5.js index 4aefb9e87..37c0f88ff 100644 --- a/Plugins/ScriptPlugins/ParserPIW5.js +++ b/Plugins/ScriptPlugins/ParserPIW5.js @@ -3,7 +3,7 @@ var eventParser; var plugin = { author: 'RaidMax', - version: 0.9, + version: 1.0, name: 'Plutonium IW5 Parser', isParser: true, @@ -28,6 +28,9 @@ var plugin = { rconParser.Configuration.WaitForResponse = true; rconParser.Configuration.CanGenerateLogPath = true; rconParser.Configuration.NoticeLineSeparator = '. '; + rconParser.Configuration.DefaultRConPort = 27016; + + rconParser.Configuration.DefaultInstallationDirectoryHint = '{LocalAppData}/Plutonium/storage/iw5'; rconParser.Configuration.StatusHeader.Pattern = 'num +score +bot +ping +guid +name +address +qport *'; rconParser.Configuration.Status.Pattern = '^ *([0-9]+) +-?([0-9]+) +(0|1) +((?:[A-Z]+|[0-9]+)) +((?:[a-z]|[0-9]){8,32}|(?:[a-z]|[0-9]){8,32}|bot[0-9]+|(?:[0-9]+)) *(.{0,32}) +(\\d+\\.\\d+\\.\\d+.\\d+\\:-*\\d{1,5}|0+.0+:-*\\d{1,5}|loopback|unknown|bot) +(-*[0-9]+) *$'; diff --git a/Plugins/ScriptPlugins/ParserPT6.js b/Plugins/ScriptPlugins/ParserPT6.js index 014206a62..43d7ce4a6 100644 --- a/Plugins/ScriptPlugins/ParserPT6.js +++ b/Plugins/ScriptPlugins/ParserPT6.js @@ -3,7 +3,7 @@ var eventParser; var plugin = { author: 'RaidMax, Xerxes', - version: 1.0, + version: 1.1, name: 'Plutonium T6 Parser', isParser: true, @@ -27,6 +27,8 @@ var plugin = { rconParser.Configuration.Dvar.AddMapping(107, 2); rconParser.Configuration.WaitForResponse = false; rconParser.Configuration.NoticeLineSeparator = '. '; + rconParser.Configuration.DefaultRConPort = 4976; + rconParser.Configuration.DefaultInstallationDirectoryHint = '{LocalAppData}/Plutonium/storage/t6'; rconParser.Configuration.StatusHeader.Pattern = 'num +score +bot +ping +guid +name +lastmsg +address +qport +rate *'; rconParser.Configuration.Status.Pattern = '^ *([0-9]+) +([0-9]+) +(?:[0-1]{1}) +([0-9]+) +([A-F0-9]+|0) +(.+?) +(?:[0-9]+) +(\\d+\\.\\d+\\.\\d+\\.\\d+\\:-?\\d{1,5}|0+\\.0+:-?\\d{1,5}|loopback) +(?:-?[0-9]+) +(?:[0-9]+) *$'; diff --git a/Plugins/ScriptPlugins/ParserPlutoniumT4.js b/Plugins/ScriptPlugins/ParserPlutoniumT4.js index fafcbfcbb..8a4b5ef5c 100644 --- a/Plugins/ScriptPlugins/ParserPlutoniumT4.js +++ b/Plugins/ScriptPlugins/ParserPlutoniumT4.js @@ -3,7 +3,7 @@ var eventParser; var plugin = { author: 'RaidMax, Chase', - version: 0.2, + version: 0.3, name: 'Plutonium T4 Parser', isParser: true, @@ -19,6 +19,9 @@ var plugin = { rconParser.Configuration.CommandPrefixes.TempBan = 'clientkick {0}'; rconParser.Configuration.CommandPrefixes.RConResponse = '\xff\xff\xff\xffprint\n'; rconParser.Configuration.GuidNumberStyle = 7; // Integer + rconParser.Configuration.DefaultRConPort = 28960; + + rconParser.Configuration.DefaultInstallationDirectoryHint = '{LocalAppData}/Plutonium/storage/t4'; rconParser.Version = 'Plutonium T4'; rconParser.GameName = 5; // T4 diff --git a/Plugins/ScriptPlugins/ParserRektT5M.js b/Plugins/ScriptPlugins/ParserRektT5M.js index a9ce84ec4..c7e0aab64 100644 --- a/Plugins/ScriptPlugins/ParserRektT5M.js +++ b/Plugins/ScriptPlugins/ParserRektT5M.js @@ -3,7 +3,7 @@ var eventParser; var plugin = { author: 'RaidMax', - version: 0.3, + version: 0.4, name: 'RektT5m Parser', isParser: true, @@ -19,6 +19,7 @@ var plugin = { rconParser.Configuration.CommandPrefixes.RConResponse = '\xff\xff\xff\xff\x01print\n'; rconParser.Configuration.CommandPrefixes.Tell = 'tell {0} {1}'; rconParser.Configuration.CommandPrefixes.RConGetInfo = undefined; + rconParser.Configuration.DefaultRConPort = 28960; rconParser.Version = 'Call of Duty Multiplayer - Ship COD_T5_S MP build 7.0.189 CL(1022875) CODPCAB-V64 CEG Wed Nov 02 18:02:23 2011 win-x86'; rconParser.GameName = 6; // T5 diff --git a/Plugins/ScriptPlugins/ParserS1x.js b/Plugins/ScriptPlugins/ParserS1x.js index 340d404a9..137b567b2 100644 --- a/Plugins/ScriptPlugins/ParserS1x.js +++ b/Plugins/ScriptPlugins/ParserS1x.js @@ -3,7 +3,7 @@ var eventParser; var plugin = { author: 'Diavolo, RaidMax', - version: 0.1, + version: 0.2, name: 'S1x Parser', isParser: true, @@ -24,6 +24,7 @@ var plugin = { rconParser.Configuration.Status.AddMapping(103, 5); rconParser.Configuration.Status.AddMapping(104, 6); rconParser.Configuration.WaitForResponse = false; + rconParser.Configuration.DefaultRConPort = 27016; eventParser.Configuration.GameDirectory = ''; diff --git a/Plugins/ScriptPlugins/ParserT4.js b/Plugins/ScriptPlugins/ParserT4.js index e3f68f204..896e1a269 100644 --- a/Plugins/ScriptPlugins/ParserT4.js +++ b/Plugins/ScriptPlugins/ParserT4.js @@ -3,7 +3,7 @@ var eventParser; var plugin = { author: 'RaidMax', - version: 0.1, + version: 0.2, name: 'Call of Duty 5: World at War Parser', isParser: true, @@ -15,6 +15,7 @@ var plugin = { eventParser = manager.GenerateDynamicEventParser(this.name); rconParser.Configuration.CommandPrefixes.RConResponse = '\xff\xff\xff\xffprint\n'; rconParser.Configuration.GuidNumberStyle = 7; // Integer + rconParser.Configuration.DefaultRConPort = 28960; rconParser.Version = 'Call of Duty Multiplayer COD_WaW MP build 1.7.1263 CL(350073) JADAMS2 Thu Oct 29 15:43:55 2009 win-x86'; eventParser.Configuration.GuidNumberStyle = 7; // Integer diff --git a/Plugins/ScriptPlugins/ParserT7.js b/Plugins/ScriptPlugins/ParserT7.js index 20ac95b2f..c009856d3 100644 --- a/Plugins/ScriptPlugins/ParserT7.js +++ b/Plugins/ScriptPlugins/ParserT7.js @@ -3,7 +3,7 @@ var eventParser; var plugin = { author: 'RaidMax', - version: 0.3, + version: 0.4, name: 'Black Ops 3 Parser', isParser: true, @@ -27,6 +27,7 @@ var plugin = { rconParser.Configuration.MapStatus.Pattern = 'Map: (.+)'; rconParser.Configuration.CommandPrefixes.RConGetInfo = undefined; // disables this, because it's useless on T7 rconParser.Configuration.ServerNotRunningResponse = 'this is here to prevent a hibernating server from being detected as not running'; + rconParser.Configuration.DefaultRConPort = 27016; rconParser.Configuration.OverrideDvarNameMapping.Add('sv_hostname', 'live_steam_server_name'); rconParser.Configuration.DefaultDvarValues.Add('sv_running', '1'); diff --git a/Plugins/ScriptPlugins/ParserTeknoMW3.js b/Plugins/ScriptPlugins/ParserTeknoMW3.js index fdeea4ac5..091f9de4e 100644 --- a/Plugins/ScriptPlugins/ParserTeknoMW3.js +++ b/Plugins/ScriptPlugins/ParserTeknoMW3.js @@ -3,7 +3,7 @@ var eventParser; var plugin = { author: 'RaidMax', - version: 0.8, + version: 0.9, name: 'Tekno MW3 Parser', isParser: true, @@ -28,6 +28,7 @@ var plugin = { rconParser.Configuration.Dvar.AddMapping(107, 1); // RCon DvarValue rconParser.Configuration.Dvar.Pattern = '^(.*)$'; rconParser.Configuration.NoticeLineSeparator = '. '; + rconParser.Configuration.DefaultRConPort = 8766; rconParser.Configuration.DefaultDvarValues.Add('sv_running', '1'); rconParser.Configuration.OverrideDvarNameMapping.Add('_website', 'sv_clanWebsite'); diff --git a/SharedLibraryCore/Configuration/Extensions/ConfigurationExtensions.cs b/SharedLibraryCore/Configuration/Extensions/ConfigurationExtensions.cs new file mode 100644 index 000000000..125fb81d3 --- /dev/null +++ b/SharedLibraryCore/Configuration/Extensions/ConfigurationExtensions.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.NetworkInformation; +using System.Net.Sockets; +using System.Text.RegularExpressions; +using Microsoft.Win32; +using SharedLibraryCore.Interfaces; + +namespace SharedLibraryCore.Configuration.Extensions +{ + public static class ConfigurationExtensions + { + public static void TrySetIpAddress(this ServerConfiguration config) + { + try + { + var interfaces = NetworkInterface.GetAllNetworkInterfaces().Where(nic => + nic.OperationalStatus == OperationalStatus.Up && + (nic.NetworkInterfaceType == NetworkInterfaceType.Wireless80211 || + nic.NetworkInterfaceType == NetworkInterfaceType.Ethernet && nic.GetIPProperties().UnicastAddresses + .Any(addr => addr.Address.AddressFamily == AddressFamily.InterNetwork))).ToList(); + + var publicInterfaces = interfaces.Where(nic => + nic.GetIPProperties().UnicastAddresses.Any(info => + info.Address.AddressFamily == AddressFamily.InterNetwork && !info.Address.IsInternal())) + .ToList(); + + config.IPAddress = publicInterfaces.Any() + ? publicInterfaces.First().GetIPProperties().UnicastAddresses.First().Address.ToString() + : IPAddress.Loopback.ToString(); + } + catch + { + config.IPAddress = IPAddress.Loopback.ToString(); + } + } + + public static (string, string)[] TryGetRConPasswords(this IRConParser parser) + { + string searchPath = null; + var isRegistryKey = parser.Configuration.DefaultInstallationDirectoryHint.Contains("HKEY_"); + + try + { + if (isRegistryKey) + { + var result = Registry.GetValue(parser.Configuration.DefaultInstallationDirectoryHint, null, null); + + if (result == null) + { + return new (string, string)[0]; + } + + searchPath = Path.Combine(result.ToString().Split(Path.DirectorySeparatorChar) + .Where(p => !p.Contains(".exe")) + .Select(p => p.Replace("\"", "")).ToArray()); + } + + else + { + var path = parser.Configuration.DefaultInstallationDirectoryHint.Replace("{LocalAppData}", + Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)); + + if (Directory.Exists(path)) + { + searchPath = path; + } + } + + if (string.IsNullOrEmpty(searchPath)) + { + return new (string, string)[0]; + } + + var possibleFiles = Directory.GetFiles(searchPath, "*.cfg", SearchOption.AllDirectories); + + if (!possibleFiles.Any()) + { + return new (string, string)[0]; + } + + var possiblePasswords = possibleFiles.SelectMany(File.ReadAllLines) + .Select(line => Regex.Match(line, "^(\\/\\/)?.*rcon_password +\"?([^\\/\"\n]+)\"?")) + .Where(match => match.Success) + .Select(match => + !string.IsNullOrEmpty(match.Groups[1].ToString()) + ? (match.Groups[2].ToString(), + Utilities.CurrentLocalization.LocalizationIndex["SETUP_RCON_PASSWORD_COMMENTED"]) + : (match.Groups[2].ToString(), null)); + + return possiblePasswords.ToArray(); + } + catch + { + return new (string, string)[0]; + } + } + } +} \ No newline at end of file diff --git a/SharedLibraryCore/Configuration/ServerConfiguration.cs b/SharedLibraryCore/Configuration/ServerConfiguration.cs index bc1e1ff01..08a32062f 100644 --- a/SharedLibraryCore/Configuration/ServerConfiguration.cs +++ b/SharedLibraryCore/Configuration/ServerConfiguration.cs @@ -3,6 +3,7 @@ using SharedLibraryCore.Interfaces; using System; using System.Collections.Generic; using System.Linq; +using SharedLibraryCore.Configuration.Extensions; namespace SharedLibraryCore.Configuration { @@ -10,96 +11,145 @@ namespace SharedLibraryCore.Configuration { [LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_IP")] public string IPAddress { get; set; } + [LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_PORT")] public int Port { get; set; } + [LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_PASSWORD")] public string Password { get; set; } + [LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_RULES")] public string[] Rules { get; set; } = new string[0]; + [LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_AUTO_MESSAGES")] public string[] AutoMessages { get; set; } = new string[0]; + [LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_PATH")] [ConfigurationOptional] public string ManualLogPath { get; set; } + [LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_RCON_PARSER")] public string RConParserVersion { get; set; } + [LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_EVENT_PARSER")] public string EventParserVersion { get; set; } + [LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_RESERVED_SLOT")] public int ReservedSlotNumber { get; set; } + [LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_GAME_LOG_SERVER")] [ConfigurationOptional] public Uri GameLogServerUrl { get; set; } + [LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_CUSTOM_HOSTNAME")] [ConfigurationOptional] public string CustomHostname { get; set; } - private readonly IList rconParsers; - private readonly IList eventParsers; + private readonly IList _rconParsers; + private IRConParser _selectedParser; public ServerConfiguration() { - rconParsers = new List(); - eventParsers = new List(); + _rconParsers = new List(); Rules = new string[0]; AutoMessages = new string[0]; } public void AddRConParser(IRConParser parser) { - rconParsers.Add(parser); + _rconParsers.Add(parser); } public void AddEventParser(IEventParser parser) { - eventParsers.Add(parser); } public void ModifyParsers() { var loc = Utilities.CurrentLocalization.LocalizationIndex; - var parserVersions = rconParsers.Select(_parser => _parser.Name).ToArray(); - var selection = Utilities.PromptSelection($"{loc["SETUP_SERVER_RCON_PARSER_VERSION"]} ({IPAddress}:{Port})", parserVersions[0], null, parserVersions); + var parserVersions = _rconParsers.Select(p => p.Name).ToArray(); + var (index, parser) = loc["SETUP_SERVER_RCON_PARSER_VERSION"].PromptSelection(parserVersions[0], + null, parserVersions); - if (selection.Item1 >= 0) + if (index < 0) { - RConParserVersion = rconParsers.FirstOrDefault(_parser => _parser.Name == selection.Item2)?.Version; - - if (selection.Item1 > 0 && !rconParsers[selection.Item1].CanGenerateLogPath) - { - Console.WriteLine(loc["SETUP_SERVER_NO_LOG"]); - ManualLogPath = Utilities.PromptString(loc["SETUP_SERVER_LOG_PATH"]); - } + return; } - parserVersions = eventParsers.Select(_parser => _parser.Name).ToArray(); - selection = Utilities.PromptSelection($"{loc["SETUP_SERVER_EVENT_PARSER_VERSION"]} ({IPAddress}:{Port})", parserVersions[0], null, parserVersions); + _selectedParser = _rconParsers.FirstOrDefault(p => p.Name == parser); + RConParserVersion = _selectedParser?.Version; + EventParserVersion = _selectedParser?.Version; - if (selection.Item1 >= 0) + if (index <= 0 || _rconParsers[index].CanGenerateLogPath) { - EventParserVersion = eventParsers.FirstOrDefault(_parser => _parser.Name == selection.Item2)?.Version; + return; } + + Console.WriteLine(loc["SETUP_SERVER_NO_LOG"]); + ManualLogPath = loc["SETUP_SERVER_LOG_PATH"].PromptString(); } public IBaseConfiguration Generate() { + ModifyParsers(); var loc = Utilities.CurrentLocalization.LocalizationIndex; + var shouldTryFindIp = loc["SETUP_SERVER_IP_AUTO"].PromptBool(defaultValue: true); - while (string.IsNullOrEmpty(IPAddress)) + if (shouldTryFindIp) { - var input = Utilities.PromptString(loc["SETUP_SERVER_IP"]); - IPAddress = input; + this.TrySetIpAddress(); + Console.WriteLine(loc["SETUP_SERVER_IP_AUTO_RESULT"].FormatExt(IPAddress)); + } + + else + { + while (string.IsNullOrEmpty(IPAddress)) + { + var input = loc["SETUP_SERVER_IP"].PromptString(); + IPAddress = input; + } + } + + var defaultPort = _selectedParser.Configuration.DefaultRConPort; + Port = loc["SETUP_SERVER_PORT"].PromptInt(null, 1, ushort.MaxValue, defaultValue:defaultPort); + + if (!string.IsNullOrEmpty(_selectedParser.Configuration.DefaultInstallationDirectoryHint)) + { + var shouldTryFindPassword = loc["SETUP_RCON_PASSWORD_AUTO"].PromptBool(defaultValue: true); + + if (shouldTryFindPassword) + { + var passwords = _selectedParser.TryGetRConPasswords(); + if (passwords.Length > 1) + { + var (index, value) = + loc["SETUP_RCON_PASSWORD_PROMPT"].PromptSelection("Other", null, + passwords.Select(pw => + $"{pw.Item1}{(string.IsNullOrEmpty(pw.Item2) ? "" : " " + pw.Item2)}").ToArray()); + + if (index > 0) + { + Password = passwords[index - 1].Item1; + } + } + + else if (passwords.Length > 0) + { + Password = passwords[0].Item1; + Console.WriteLine(loc["SETUP_RCON_PASSWORD_RESULT"].FormatExt(Password)); + } + } + } + + if (string.IsNullOrEmpty(Password)) + { + Password = loc["SETUP_SERVER_RCON"].PromptString(); } - Port = Utilities.PromptInt(loc["SETUP_SERVER_PORT"], null, 1, ushort.MaxValue); - Password = Utilities.PromptString(loc["SETUP_SERVER_RCON"]); AutoMessages = new string[0]; Rules = new string[0]; - ReservedSlotNumber = loc["SETUP_SERVER_RESERVEDSLOT"].PromptInt(null, 0, 32); ManualLogPath = null; - ModifyParsers(); - return this; } @@ -108,4 +158,4 @@ namespace SharedLibraryCore.Configuration return "ServerConfiguration"; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IRConParserConfiguration.cs b/SharedLibraryCore/Interfaces/IRConParserConfiguration.cs index 706e6a85a..6770ef824 100644 --- a/SharedLibraryCore/Interfaces/IRConParserConfiguration.cs +++ b/SharedLibraryCore/Interfaces/IRConParserConfiguration.cs @@ -87,5 +87,15 @@ namespace SharedLibraryCore.Interfaces /// specifies the characters used to split a line /// string NoticeLineSeparator { get; } + + /// + /// Default port the game listens to RCon requests on + /// + int? DefaultRConPort { get; } + + /// + /// Default Indicator of where the game is installed (ex file path or registry entry) + /// + string DefaultInstallationDirectoryHint { get; } } } diff --git a/SharedLibraryCore/Utilities.cs b/SharedLibraryCore/Utilities.cs index a5066776b..1e7324376 100644 --- a/SharedLibraryCore/Utilities.cs +++ b/SharedLibraryCore/Utilities.cs @@ -546,7 +546,7 @@ namespace SharedLibraryCore /// description of the question's value /// default value to set if no input is entered /// - public static bool PromptBool(string question, string description = null, bool defaultValue = true) + public static bool PromptBool(this string question, string description = null, bool defaultValue = true) { Console.Write($"{question}?{(string.IsNullOrEmpty(description) ? " " : $" ({description}) ")}[y/n]: "); char response = Console.ReadLine().ToLower().FirstOrDefault(); @@ -562,7 +562,7 @@ namespace SharedLibraryCore /// description of the question's value /// array of possible selections (should be able to convert to string) /// - public static Tuple PromptSelection(string question, T defaultValue, string description = null, params T[] selections) + public static Tuple PromptSelection(this string question, T defaultValue, string description = null, params T[] selections) { bool hasDefault = false; @@ -634,7 +634,7 @@ namespace SharedLibraryCore /// description of the question's value /// default value to set the return value to /// - public static string PromptString(string question, string description = null, string defaultValue = null) + public static string PromptString(this string question, string description = null, string defaultValue = null) { string inputOrDefault() {