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('/', '_');