diff --git a/Application/Application.csproj b/Application/Application.csproj index 0f99fece9..6aeab0162 100644 --- a/Application/Application.csproj +++ b/Application/Application.csproj @@ -6,7 +6,7 @@ 2.1.5 false RaidMax.IW4MAdmin.Application - 2.2.4.6 + 2.2.4.7 RaidMax Forever None IW4MAdmin @@ -31,8 +31,8 @@ true true - 2.2.4.6 - 2.2.4.6 + 2.2.4.7 + 2.2.4.7 diff --git a/Application/ApplicationManager.cs b/Application/ApplicationManager.cs index b8dcbe301..7d13f754a 100644 --- a/Application/ApplicationManager.cs +++ b/Application/ApplicationManager.cs @@ -286,6 +286,8 @@ namespace IW4MAdmin.Application foreach (var serverConfig in config.Servers) { + Migration.ConfigurationMigration.ModifyLogPath020919(serverConfig); + if (serverConfig.RConParserVersion == null || serverConfig.EventParserVersion == null) { foreach (var parser in AdditionalRConParsers) @@ -299,8 +301,8 @@ namespace IW4MAdmin.Application } serverConfig.ModifyParsers(); - await ConfigHandler.Save(); } + await ConfigHandler.Save(); } } diff --git a/Application/EventParsers/BaseEventParser.cs b/Application/EventParsers/BaseEventParser.cs index 642018210..5ded70a43 100644 --- a/Application/EventParsers/BaseEventParser.cs +++ b/Application/EventParsers/BaseEventParser.cs @@ -17,26 +17,26 @@ namespace IW4MAdmin.Application.EventParsers GameDirectory = "main", }; - Configuration.Say.Pattern = @"^(say|sayteam);(.{8,32});([0-9]+);(.+);(.*)$"; + Configuration.Say.Pattern = @"^(say|sayteam);(-?[A-Fa-f0-9_]{8,32});([0-9]+);(.+);(.*)$"; Configuration.Say.AddMapping(ParserRegex.GroupType.EventType, 1); Configuration.Say.AddMapping(ParserRegex.GroupType.OriginNetworkId, 2); Configuration.Say.AddMapping(ParserRegex.GroupType.OriginClientNumber, 3); Configuration.Say.AddMapping(ParserRegex.GroupType.OriginName, 4); Configuration.Say.AddMapping(ParserRegex.GroupType.Message, 5); - Configuration.Quit.Pattern = @"^(Q);(.{8,32}|bot[0-9]+);([0-9]+);(.*)$"; + Configuration.Quit.Pattern = @"^(Q);(-?[A-Fa-f0-9_]{8,32}|bot[0-9]+);([0-9]+);(.*)$"; Configuration.Quit.AddMapping(ParserRegex.GroupType.EventType, 1); Configuration.Quit.AddMapping(ParserRegex.GroupType.OriginNetworkId, 2); Configuration.Quit.AddMapping(ParserRegex.GroupType.OriginClientNumber, 3); Configuration.Quit.AddMapping(ParserRegex.GroupType.OriginName, 4); - Configuration.Join.Pattern = @"^(J);(.{8,32}|bot[0-9]+);([0-9]+);(.*)$"; + Configuration.Join.Pattern = @"^(J);(-?[A-Fa-f0-9_]{8,32}|bot[0-9]+);([0-9]+);(.*)$"; Configuration.Join.AddMapping(ParserRegex.GroupType.EventType, 1); Configuration.Join.AddMapping(ParserRegex.GroupType.OriginNetworkId, 2); Configuration.Join.AddMapping(ParserRegex.GroupType.OriginClientNumber, 3); Configuration.Join.AddMapping(ParserRegex.GroupType.OriginName, 4); - Configuration.Damage.Pattern = @"^(D);([A-Fa-f0-9_]{8,32}|bot[0-9]+);(-?[0-9]+);(axis|allies|world);(.{1,24});([A-Fa-f0-9_]{8,32}|bot[0-9]+)?;-?([0-9]+);(axis|allies|world);(.{1,24})?;((?:[0-9]+|[a-z]+|_)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$"; + Configuration.Damage.Pattern = @"^(D);(-?[A-Fa-f0-9_]{8,32}|bot[0-9]+);(-?[0-9]+);(axis|allies|world);(.{1,24});(-?[A-Fa-f0-9_]{8,32}|bot[0-9]+)?;-?([0-9]+);(axis|allies|world);(.{1,24})?;((?:[0-9]+|[a-z]+|_)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$"; Configuration.Damage.AddMapping(ParserRegex.GroupType.EventType, 1); Configuration.Damage.AddMapping(ParserRegex.GroupType.TargetNetworkId, 2); Configuration.Damage.AddMapping(ParserRegex.GroupType.TargetClientNumber, 3); @@ -51,7 +51,7 @@ namespace IW4MAdmin.Application.EventParsers Configuration.Damage.AddMapping(ParserRegex.GroupType.MeansOfDeath, 12); Configuration.Damage.AddMapping(ParserRegex.GroupType.HitLocation, 13); - Configuration.Kill.Pattern = @"^(K);([A-Fa-f0-9_]{8,32}|bot[0-9]+);(-?[0-9]+);(axis|allies|world);(.{1,24});([A-Fa-f0-9_]{8,32}|bot[0-9]+)?;-?([0-9]+);(axis|allies|world);(.{1,24})?;((?:[0-9]+|[a-z]+|_)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$"; + Configuration.Kill.Pattern = @"^(K);(-?[A-Fa-f0-9_]{8,32}|bot[0-9]+);(-?[0-9]+);(axis|allies|world);(.{1,24});(-?[A-Fa-f0-9_]{8,32}|bot[0-9]+)?;-?([0-9]+);(axis|allies|world);(.{1,24})?;((?:[0-9]+|[a-z]+|_)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$"; Configuration.Kill.AddMapping(ParserRegex.GroupType.EventType, 1); Configuration.Kill.AddMapping(ParserRegex.GroupType.TargetNetworkId, 2); Configuration.Kill.AddMapping(ParserRegex.GroupType.TargetClientNumber, 3); diff --git a/Application/IO/GameLogEventDetection.cs b/Application/IO/GameLogEventDetection.cs index c14587887..7e7aec0bd 100644 --- a/Application/IO/GameLogEventDetection.cs +++ b/Application/IO/GameLogEventDetection.cs @@ -19,19 +19,10 @@ namespace IW4MAdmin.Application.IO public string ServerId { get; set; } } - public GameLogEventDetection(Server server, string gameLogPath, string gameLogName) + public GameLogEventDetection(Server server, string gameLogPath, Uri gameLogServerUri) { GameLogFile = gameLogPath; - // todo: abtract this more - if (gameLogPath.StartsWith("http")) - { - Reader = new GameLogReaderHttp(gameLogPath, server.EventParser); - } - else - { - Reader = new GameLogReader(gameLogPath, server.EventParser); - } - + Reader = gameLogServerUri != null ? new GameLogReaderHttp(gameLogServerUri, gameLogPath, server.EventParser) : Reader = new GameLogReader(gameLogPath, server.EventParser); Server = server; } diff --git a/Application/IO/GameLogReaderHttp.cs b/Application/IO/GameLogReaderHttp.cs index 2cffb451a..de8ba742c 100644 --- a/Application/IO/GameLogReaderHttp.cs +++ b/Application/IO/GameLogReaderHttp.cs @@ -17,13 +17,13 @@ namespace IW4MAdmin.Application.IO { readonly IEventParser Parser; readonly IGameLogServer Api; - readonly string LogFile; + readonly string logPath; - public GameLogReaderHttp(string logFile, IEventParser parser) + public GameLogReaderHttp(Uri gameLogServerUri, string logPath, IEventParser parser) { - LogFile = logFile; + this.logPath = logPath.ToBase64UrlSafeString(); ; Parser = parser; - Api = RestClient.For(logFile); + Api = RestClient.For(gameLogServerUri); } public long Length => -1; @@ -36,12 +36,12 @@ namespace IW4MAdmin.Application.IO server.Logger.WriteDebug($"Begin reading from http log"); #endif var events = new List(); - string b64Path = server.LogPath.ToBase64UrlSafeString(); + string b64Path = logPath; var response = await Api.Log(b64Path); if (!response.Success) { - server.Logger.WriteError($"Could not get log server info of {LogFile}/{b64Path} ({server.LogPath})"); + server.Logger.WriteError($"Could not get log server info of {logPath}/{b64Path} ({server.LogPath})"); return events; } diff --git a/Application/IW4MServer.cs b/Application/IW4MServer.cs index 405abeb56..b747ae18c 100644 --- a/Application/IW4MServer.cs +++ b/Application/IW4MServer.cs @@ -750,47 +750,36 @@ namespace IW4MAdmin } CustomCallback = await ScriptLoaded(); - string mainPath = EventParser.Configuration.GameDirectory; - string logPath = string.Empty; - - LogPath = string.IsNullOrEmpty(game) ? - $"{basepath?.Value?.Replace('\\', Path.DirectorySeparatorChar)}{Path.DirectorySeparatorChar}{mainPath}{Path.DirectorySeparatorChar}{logfile?.Value}" : - $"{basepath?.Value?.Replace('\\', Path.DirectorySeparatorChar)}{Path.DirectorySeparatorChar}{game?.Replace('/', Path.DirectorySeparatorChar)}{Path.DirectorySeparatorChar}{logfile?.Value}"; - - bool remoteLog = false; - if (GameName == Game.IW5 || ServerConfig.ManualLogPath?.Length > 0) + + // they've manually specified the log path + if (!string.IsNullOrEmpty(ServerConfig.ManualLogPath)) { - logPath = ServerConfig.ManualLogPath; - remoteLog = logPath.StartsWith("http"); - } - else - { - logPath = LogPath; - } - - if (remoteLog) - { - LogEvent = new GameLogEventDetection(this, logPath, logfile.Value); + LogPath = ServerConfig.ManualLogPath; } else { + string mainPath = EventParser.Configuration.GameDirectory; + + LogPath = string.IsNullOrEmpty(game) ? + $"{basepath?.Value?.Replace('\\', Path.DirectorySeparatorChar)}{Path.DirectorySeparatorChar}{mainPath}{Path.DirectorySeparatorChar}{logfile?.Value}" : + $"{basepath?.Value?.Replace('\\', Path.DirectorySeparatorChar)}{Path.DirectorySeparatorChar}{game?.Replace('/', Path.DirectorySeparatorChar)}{Path.DirectorySeparatorChar}{logfile?.Value}"; + // fix wine drive name mangling if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - logPath = Regex.Replace($"{Path.DirectorySeparatorChar}{LogPath}", @"[A-Z]:/", ""); + LogPath = Regex.Replace($"{Path.DirectorySeparatorChar}{LogPath}", @"[A-Z]:/", ""); } - if (!File.Exists(logPath)) + if (!File.Exists(LogPath)) { - Logger.WriteError($"{logPath} {loc["SERVER_ERROR_DNE"]}"); - throw new ServerException($"{loc["SERVER_ERROR_LOG"]} {logPath}"); + Logger.WriteError($"{LogPath} {loc["SERVER_ERROR_DNE"]}"); + throw new ServerException($"{loc["SERVER_ERROR_LOG"]} {LogPath}"); } - - LogEvent = new GameLogEventDetection(this, logPath, logfile.Value); } - Logger.WriteInfo($"Log file is {logPath}"); + LogEvent = new GameLogEventDetection(this, LogPath, ServerConfig.GameLogServerUrl); + Logger.WriteInfo($"Log file is {LogPath}"); _ = Task.Run(() => LogEvent.PollForChanges()); #if !DEBUG diff --git a/Application/Migration/ConfigurationMigration.cs b/Application/Migration/ConfigurationMigration.cs index 496aa2e55..8edd25152 100644 --- a/Application/Migration/ConfigurationMigration.cs +++ b/Application/Migration/ConfigurationMigration.cs @@ -56,5 +56,14 @@ namespace IW4MAdmin.Application.Migration } } } + + public static void ModifyLogPath020919(SharedLibraryCore.Configuration.ServerConfiguration config) + { + if (config.ManualLogPath.IsRemoteLog()) + { + config.GameLogServerUrl = new Uri(config.ManualLogPath); + config.ManualLogPath = null; + } + } } } diff --git a/Application/RconParsers/BaseRConParser.cs b/Application/RconParsers/BaseRConParser.cs index 299cc4e97..e2688de5b 100644 --- a/Application/RconParsers/BaseRConParser.cs +++ b/Application/RconParsers/BaseRConParser.cs @@ -100,7 +100,7 @@ namespace IW4MAdmin.Application.RconParsers public async Task> GetStatusAsync(Connection connection) { - string[] response = await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND, "status"); + string[] response = await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND_STATUS); return ClientsFromStatus(response); } diff --git a/GameLogServer/GameLogServer.pyproj b/GameLogServer/GameLogServer.pyproj index 31350450b..8361eddba 100644 --- a/GameLogServer/GameLogServer.pyproj +++ b/GameLogServer/GameLogServer.pyproj @@ -11,13 +11,15 @@ . - Web launcher + Standard Python launcher http://localhost . true GameLogServer GameLogServer MSBuild|env|$(MSBuildProjectFullPath) + False + DEBUG=True true diff --git a/GameLogServer/GameLogServer/log_reader.py b/GameLogServer/GameLogServer/log_reader.py index 171dc3229..db1b38d05 100644 --- a/GameLogServer/GameLogServer/log_reader.py +++ b/GameLogServer/GameLogServer/log_reader.py @@ -15,7 +15,7 @@ class LogReader(object): if re.search('r^.+\.\.\\.+$', path): return False # must be a valid log path and log file - if not re.search(r'^.+[\\|\/](userraw|mods|main)[\\|\/].+.log$', path): + if not re.search(r'^.+[\\|\/](.+)[\\|\/].+.log$', path): return False # set the initialze size to the current file size file_size = 0 diff --git a/Plugins/Welcome/Plugin.cs b/Plugins/Welcome/Plugin.cs index 54f069eaa..84d200c76 100644 --- a/Plugins/Welcome/Plugin.cs +++ b/Plugins/Welcome/Plugin.cs @@ -139,13 +139,14 @@ namespace IW4MAdmin.Plugins.Welcome { string response = await wc.DownloadStringTaskAsync(new Uri($"http://extreme-ip-lookup.com/json/{ip}")); var responseObj = JObject.Parse(response); + response = responseObj["country"].ToString(); - return responseObj["country"].ToString(); + return string.IsNullOrEmpty(response) ? Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_WELCOME_UNKNOWN_COUNTRY"] : response; } catch { - return "a third world country"; + return Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_WELCOME_UNKNOWN_IP"]; } } } diff --git a/SharedLibraryCore/Configuration/ServerConfiguration.cs b/SharedLibraryCore/Configuration/ServerConfiguration.cs index c91fa5f26..e23343bb9 100644 --- a/SharedLibraryCore/Configuration/ServerConfiguration.cs +++ b/SharedLibraryCore/Configuration/ServerConfiguration.cs @@ -16,6 +16,7 @@ namespace SharedLibraryCore.Configuration public string RConParserVersion { get; set; } public string EventParserVersion { get; set; } public int ReservedSlotNumber { get; set; } + public Uri GameLogServerUrl { get; set; } private readonly IList rconParsers; private readonly IList eventParsers; diff --git a/SharedLibraryCore/RCon/Connection.cs b/SharedLibraryCore/RCon/Connection.cs index d39dd7738..db129bccd 100644 --- a/SharedLibraryCore/RCon/Connection.cs +++ b/SharedLibraryCore/RCon/Connection.cs @@ -78,16 +78,14 @@ namespace SharedLibraryCore.RCon byte[] payload = null; bool waitForResponse = Config.WaitForResponse; - string converterEncoding(string text) - { - var destinationEncoding = Encoding.GetEncoding("windows-1252"); - byte[] originalEncodedBytes = Utilities.EncodingType.GetBytes(text); - byte[] convertedBytes = Encoding.Convert(Utilities.EncodingType, destinationEncoding, originalEncodedBytes); - return destinationEncoding.GetString(convertedBytes); + string convertEncoding(string text) + { + byte[] convertedBytes = Utilities.EncodingType.GetBytes(text); + return Utilities.EncodingType.GetString(convertedBytes); } - string convertedRConPassword = converterEncoding(RConPassword); - string convertedParameters = converterEncoding(parameters); + string convertedRConPassword = convertEncoding(RConPassword); + string convertedParameters = convertEncoding(parameters); switch (type) { @@ -109,6 +107,10 @@ namespace SharedLibraryCore.RCon waitForResponse |= true; payload = (Config.CommandPrefixes.RConGetInfo + '\0').Select(Convert.ToByte).ToArray(); break; + case StaticHelpers.QueryType.COMMAND_STATUS: + waitForResponse |= true; + payload = string.Format(Config.CommandPrefixes.RConCommand, convertedRConPassword, "status\0").Select(Convert.ToByte).ToArray(); + break; } byte[] response = null; diff --git a/SharedLibraryCore/RCon/StaticHelpers.cs b/SharedLibraryCore/RCon/StaticHelpers.cs index 1da36c071..9ee9fd714 100644 --- a/SharedLibraryCore/RCon/StaticHelpers.cs +++ b/SharedLibraryCore/RCon/StaticHelpers.cs @@ -35,6 +35,11 @@ namespace SharedLibraryCore.RCon /// RCon password is required /// COMMAND, + /// + /// get the full server command information + /// RCon password is required + /// + COMMAND_STATUS } /// diff --git a/SharedLibraryCore/Utilities.cs b/SharedLibraryCore/Utilities.cs index 9de8dadc5..96850087c 100644 --- a/SharedLibraryCore/Utilities.cs +++ b/SharedLibraryCore/Utilities.cs @@ -266,11 +266,16 @@ namespace SharedLibraryCore public static long ConvertLong(this string str) { str = str.Substring(0, Math.Min(str.Length, 16)); - if (Int64.TryParse(str, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out long id)) + if (long.TryParse(str, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out long id)) { return id; } + if (long.TryParse(str, NumberStyles.Integer, CultureInfo.InvariantCulture, out id)) + { + return (uint)id; + } + var bot = Regex.Match(str, @"bot[0-9]+").Value; if (!string.IsNullOrEmpty(bot)) { @@ -640,6 +645,13 @@ namespace SharedLibraryCore return cmdLine.Length > 1 ? cmdLine[1] : cmdLine[0]; } + /// + /// indicates if the given log path is a remote (http) uri + /// + /// + /// + public static bool IsRemoteLog(this string log) => (log ?? "").StartsWith("http"); + public static string ToBase64UrlSafeString(this string src) { return Convert.ToBase64String(src.Select(c => Convert.ToByte(c)).ToArray()).Replace('+', '-').Replace('/', '_');