diff --git a/Application/Application.csproj b/Application/Application.csproj index 38ee0c4a1..14970321c 100644 --- a/Application/Application.csproj +++ b/Application/Application.csproj @@ -6,7 +6,7 @@ 2.1.5 false RaidMax.IW4MAdmin.Application - 2.2.3.2 + 2.2.3.3 RaidMax Forever None IW4MAdmin @@ -31,8 +31,8 @@ true true - 2.2.3.2 - 2.2.3.2 + 2.2.3.3 + 2.2.3.3 diff --git a/Application/Manager.cs b/Application/ApplicationManager.cs similarity index 96% rename from Application/Manager.cs rename to Application/ApplicationManager.cs index 81dcf08f9..25e1e9b36 100644 --- a/Application/Manager.cs +++ b/Application/ApplicationManager.cs @@ -1,4 +1,6 @@ using IW4MAdmin.Application.API.Master; +using IW4MAdmin.Application.EventParsers; +using IW4MAdmin.Application.RconParsers; using SharedLibraryCore; using SharedLibraryCore.Commands; using SharedLibraryCore.Configuration; @@ -34,6 +36,13 @@ namespace IW4MAdmin.Application public DateTime StartTime { get; private set; } public string Version => Assembly.GetEntryAssembly().GetName().Version.ToString(); + public IList AdditionalRConParsers => _additionalRConParsers.ToList(); + + public IList AdditionalEventParsers => _additionalEventParsers.ToList(); + + private readonly IList _additionalRConParsers; + private readonly IList _additionalEventParsers; + static ApplicationManager Instance; readonly List TaskStatuses; List Commands; @@ -61,6 +70,8 @@ namespace IW4MAdmin.Application StartTime = DateTime.UtcNow; OnQuit = new ManualResetEventSlim(); PageList = new PageList(); + _additionalEventParsers = new List(); + _additionalRConParsers = new List(); OnServerEvent += OnGameEvent; OnServerEvent += EventApi.OnGameEvent; } @@ -552,5 +563,9 @@ namespace IW4MAdmin.Application { return PageList; } + + public IRConParser GenerateDynamicRConParser() => new DynamicRConParser(); + + public IEventParser GenerateDynamicEventParser() => new DynamicEventParser(); } } diff --git a/Application/EventParsers/DynamicEventParser.cs b/Application/EventParsers/DynamicEventParser.cs new file mode 100644 index 000000000..c8680b1d6 --- /dev/null +++ b/Application/EventParsers/DynamicEventParser.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; +using static SharedLibraryCore.Server; + +namespace IW4MAdmin.Application.EventParsers +{ + sealed internal class DynamicEventParser : IW4EventParser + { + public string Version { get; set; } + } +} diff --git a/Application/EventParsers/DynamicEventParserConfiguration.cs b/Application/EventParsers/DynamicEventParserConfiguration.cs new file mode 100644 index 000000000..c3d869659 --- /dev/null +++ b/Application/EventParsers/DynamicEventParserConfiguration.cs @@ -0,0 +1,14 @@ +using SharedLibraryCore.Interfaces; + +namespace IW4MAdmin.Application.EventParsers +{ + class DynamicEventParserConfiguration : IEventParserConfiguration + { + public string GameDirectory { get; set; } + public string SayRegex { get; set; } + public string JoinRegex { get; set; } + public string QuitRegex { get; set; } + public string KillRegex { get; set; } + public string DamageRegex { get; set; } + } +} diff --git a/Application/EventParsers/IW3EventParser.cs b/Application/EventParsers/IW3EventParser.cs index c0f1a3751..b7cd6acd8 100644 --- a/Application/EventParsers/IW3EventParser.cs +++ b/Application/EventParsers/IW3EventParser.cs @@ -6,6 +6,9 @@ namespace IW4MAdmin.Application.EventParsers { class IW3EventParser : IW4EventParser { - public override string GetGameDir() => "main"; + public IW3EventParser() : base() + { + Configuration.GameDirectory = "main"; + } } } diff --git a/Application/EventParsers/IW4EventParser.cs b/Application/EventParsers/IW4EventParser.cs index 9182ffc02..defa54dd6 100644 --- a/Application/EventParsers/IW4EventParser.cs +++ b/Application/EventParsers/IW4EventParser.cs @@ -9,7 +9,21 @@ namespace IW4MAdmin.Application.EventParsers { class IW4EventParser : IEventParser { - private const string SayRegex = @"(say|sayteam);(.{1,32});([0-9]+)(.*);(.*)"; + private IEventParserConfiguration _configuration; + + public IW4EventParser() + { + _configuration = new DynamicEventParserConfiguration() + { + GameDirectory = "userraw", + SayRegex = "(say|sayteam);(.{1,32});([0-9]+)(.*);(.*)", + QuitRegex = @"^(Q;)(.{1,32});([0-9]+);(.*)$", + JoinRegex = @"^(J;)(.{1,32});([0-9]+);(.*)$", + DamageRegex = @"^(D);((?:bot[0-9]+)|(?:[A-Z]|[0-9])+);([0-9]+);(axis|allies);(.+);((?:[A-Z]|[0-9])+);([0-9]+);(axis|allies);(.+);((?:[0-9]+|[a-z]+|_)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$" + }; + } + + public IEventParserConfiguration Configuration { get => _configuration; set => _configuration = value; } public virtual GameEvent GetEvent(Server server, string logLine) { @@ -32,7 +46,7 @@ namespace IW4MAdmin.Application.EventParsers if (eventType == "say" || eventType == "sayteam") { - var matchResult = Regex.Match(logLine, SayRegex); + var matchResult = Regex.Match(logLine, _configuration.SayRegex); if (matchResult.Success) { @@ -117,7 +131,7 @@ namespace IW4MAdmin.Application.EventParsers { if (!server.CustomCallback) { - if (Regex.Match(eventType, @"^(D);((?:bot[0-9]+)|(?:[A-Z]|[0-9])+);([0-9]+);(axis|allies);(.+);((?:[A-Z]|[0-9])+);([0-9]+);(axis|allies);(.+);((?:[0-9]+|[a-z]+|_)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$").Success) + if (Regex.Match(eventType, _configuration.DamageRegex).Success) { var origin = server.GetClientsAsList().First(c => c.NetworkId == lineSplit[5].ConvertLong()); var target = server.GetClientsAsList().First(c => c.NetworkId == lineSplit[1].ConvertLong()); @@ -137,7 +151,7 @@ namespace IW4MAdmin.Application.EventParsers // join if (eventType == "J") { - var regexMatch = Regex.Match(logLine, @"^(J;)(.{1,32});([0-9]+);(.*)$"); + var regexMatch = Regex.Match(logLine, _configuration.JoinRegex); if (regexMatch.Success) { return new GameEvent() @@ -163,7 +177,7 @@ namespace IW4MAdmin.Application.EventParsers if (eventType == "Q") { - var regexMatch = Regex.Match(logLine, @"^(Q;)(.{1,32});([0-9]+);(.*)$"); + var regexMatch = Regex.Match(logLine, _configuration.QuitRegex); if (regexMatch.Success) { return new GameEvent() @@ -221,11 +235,5 @@ namespace IW4MAdmin.Application.EventParsers Owner = server }; } - - // other parsers can derive from this parser so we make it virtual - public virtual string GetGameDir() - { - return "userraw"; - } } } diff --git a/Application/EventParsers/IW5EventParser.cs b/Application/EventParsers/IW5EventParser.cs deleted file mode 100644 index cc13094a4..000000000 --- a/Application/EventParsers/IW5EventParser.cs +++ /dev/null @@ -1,58 +0,0 @@ -using SharedLibraryCore; -using SharedLibraryCore.Database.Models; -using System; -using System.Text.RegularExpressions; - -namespace IW4MAdmin.Application.EventParsers -{ - class IW5EventParser : IW4EventParser - { - public override string GetGameDir() - { - return "logs"; - } - - public override GameEvent GetEvent(Server server, string logLine) - { - string cleanedEventLine = Regex.Replace(logLine, @"[0-9]+:[0-9]+\ ", "").Trim(); - - if (cleanedEventLine.Contains("J;")) - { - string[] lineSplit = cleanedEventLine.Split(';'); - - int clientNum = Int32.Parse(lineSplit[2]); - - var player = new EFClient() - { - NetworkId = lineSplit[1].ConvertLong(), - ClientNumber = clientNum, - CurrentAlias = new EFAlias() - { - Active = false, - Name = lineSplit[3] - } - }; - - return new GameEvent() - { - Type = GameEvent.EventType.PreConnect, - Origin = new EFClient() - { - ClientId = 1 - }, - Target = new EFClient() - { - ClientId = 1 - }, - Owner = server, - Extra = player - }; - } - - else - { - return base.GetEvent(server, logLine); - } - } - } -} diff --git a/Application/EventParsers/T5MEventParser.cs b/Application/EventParsers/T5MEventParser.cs index 0dc32290c..b804db335 100644 --- a/Application/EventParsers/T5MEventParser.cs +++ b/Application/EventParsers/T5MEventParser.cs @@ -6,6 +6,9 @@ namespace IW4MAdmin.Application.EventParsers { class T5MEventParser : IW4EventParser { - public override string GetGameDir() => "v2"; + public T5MEventParser() : base() + { + Configuration.GameDirectory = "v2"; + } } } diff --git a/Application/EventParsers/T6MEventParser.cs b/Application/EventParsers/T6MEventParser.cs index 1f016b0d9..761331ed4 100644 --- a/Application/EventParsers/T6MEventParser.cs +++ b/Application/EventParsers/T6MEventParser.cs @@ -1,16 +1,12 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text.RegularExpressions; -using SharedLibraryCore; -using SharedLibraryCore.Interfaces; -using SharedLibraryCore.Objects; +using System.IO; namespace IW4MAdmin.Application.EventParsers { class T6MEventParser : IW4EventParser { - public override string GetGameDir() => $"t6r{Path.DirectorySeparatorChar}data"; + public T6MEventParser() : base() + { + Configuration.GameDirectory = $"t6r{Path.DirectorySeparatorChar}data"; + } } } diff --git a/Application/IW4MServer.cs b/Application/IW4MServer.cs index 86c205ca2..65e21d868 100644 --- a/Application/IW4MServer.cs +++ b/Application/IW4MServer.cs @@ -669,32 +669,26 @@ namespace IW4MAdmin (IRConParser)new T6MRConParser() : new IW3RConParser(); - if (ServerConfig.UseIW5MParser) - { - RconParser = new IW5MRConParser(); - } - var version = await this.GetDvarAsync("version"); Version = version.Value; - GameName = Utilities.GetGame(version.Value); + //GameName = Utilities.GetGame(version.Value); if (GameName == Game.IW4) { EventParser = new IW4EventParser(); RconParser = new IW4RConParser(); } - else if (GameName == Game.IW5) - { - EventParser = new IW5EventParser(); - } + else if (GameName == Game.T5M) { EventParser = new T5MEventParser(); } + else if (GameName == Game.T6M) { EventParser = new T6MEventParser(); } + else { EventParser = new IW3EventParser(); // this uses the 'main' folder for log paths @@ -703,6 +697,9 @@ namespace IW4MAdmin if (GameName == Game.UKN) { Logger.WriteWarning($"Game name not recognized: {version}"); + + EventParser = Manager.AdditionalEventParsers.FirstOrDefault(_parser => (_parser as DynamicEventParser).Version == version.Value) ?? EventParser; + RconParser = Manager.AdditionalRConParsers.FirstOrDefault(_parser => (_parser as DynamicRConParser).Version == version.Value) ?? RconParser; } var infoResponse = await this.GetInfoAsync(); @@ -763,7 +760,7 @@ namespace IW4MAdmin } CustomCallback = await ScriptLoaded(); - string mainPath = EventParser.GetGameDir(); + string mainPath = EventParser.Configuration.GameDirectory; string logPath = string.Empty; LogPath = game == string.Empty ? diff --git a/Application/RconParsers/DynamicRConParser.cs b/Application/RconParsers/DynamicRConParser.cs new file mode 100644 index 000000000..4fe14f672 --- /dev/null +++ b/Application/RconParsers/DynamicRConParser.cs @@ -0,0 +1,12 @@ +using SharedLibraryCore; +using SharedLibraryCore.RCon; +using System; +using static SharedLibraryCore.Server; + +namespace IW4MAdmin.Application.RconParsers +{ + sealed internal class DynamicRConParser : IW4RConParser + { + public string Version { get; set; } + } +} diff --git a/Application/RconParsers/DynamicRConParserConfiguration.cs b/Application/RconParsers/DynamicRConParserConfiguration.cs new file mode 100644 index 000000000..dcee5d9bc --- /dev/null +++ b/Application/RconParsers/DynamicRConParserConfiguration.cs @@ -0,0 +1,13 @@ +using SharedLibraryCore; +using SharedLibraryCore.Interfaces; +using SharedLibraryCore.RCon; + +namespace IW4MAdmin.Application.RconParsers +{ + class DynamicRConParserConfiguration : IRConParserConfiguration + { + public CommandPrefix CommandPrefixes { get; set; } + public Server.Game GameName { get; set; } + public string StatusRegex { get; set; } + } +} diff --git a/Application/RconParsers/IW3RConParser.cs b/Application/RconParsers/IW3RConParser.cs index dfff63cc2..2fbac1713 100644 --- a/Application/RconParsers/IW3RConParser.cs +++ b/Application/RconParsers/IW3RConParser.cs @@ -1,4 +1,5 @@ -using SharedLibraryCore.RCon; +using SharedLibraryCore; +using SharedLibraryCore.RCon; using System; using System.Collections.Generic; using System.Text; @@ -7,15 +8,18 @@ namespace IW4MAdmin.Application.RconParsers { class IW3RConParser : IW4RConParser { - private static readonly CommandPrefix Prefixes = new CommandPrefix() + public IW3RConParser() : base() { - Tell = "tell {0} {1}", - Say = "say {0}", - Kick = "clientkick {0} \"{1}\"", - Ban = "clientkick {0} \"{1}\"", - TempBan = "tempbanclient {0} \"{1}\"" - }; + Configuration.CommandPrefixes = new CommandPrefix() + { + Tell = "tell {0} {1}", + Say = "say {0}", + Kick = "clientkick {0} \"{1}\"", + Ban = "clientkick {0} \"{1}\"", + TempBan = "tempbanclient {0} \"{1}\"" + }; - public override CommandPrefix GetCommandPrefixes() => Prefixes; + Configuration.GameName = Server.Game.IW3; + } } } diff --git a/Application/RconParsers/IW4RConParser.cs b/Application/RconParsers/IW4RConParser.cs index 95c0f8d81..9cb5ba346 100644 --- a/Application/RconParsers/IW4RConParser.cs +++ b/Application/RconParsers/IW4RConParser.cs @@ -13,16 +13,24 @@ namespace IW4MAdmin.Application.RconParsers { class IW4RConParser : IRConParser { - private static readonly CommandPrefix Prefixes = new CommandPrefix() + public IW4RConParser() { - Tell = "tellraw {0} {1}", - Say = "sayraw {0}", - Kick = "clientkick {0} \"{1}\"", - Ban = "clientkick {0} \"{1}\"", - TempBan = "tempbanclient {0} \"{1}\"" - }; + Configuration = new DynamicRConParserConfiguration() + { + CommandPrefixes = new CommandPrefix() + { + Tell = "tellraw {0} {1}", + Say = "sayraw {0}", + Kick = "clientkick {0} \"{1}\"", + Ban = "clientkick {0} \"{1}\"", + TempBan = "tempbanclient {0} \"{1}\"" + }, + StatusRegex = @"^( *[0-9]+) +-*([0-9]+) +((?:[A-Z]+|[0-9]+)) +((?:[a-z]|[0-9]){16}|(?:[a-z]|[0-9]){32}|bot[0-9]+|(?:[0-9]+)) *(.{0,32}) +([0-9]+) +(\d+\.\d+\.\d+.\d+\:-*\d{1,5}|0+.0+:-*\d{1,5}|loopback) +(-*[0-9]+) +([0-9]+) *$", + GameName = Server.Game.IW4 + }; + } - private static readonly string StatusRegex = @"^( *[0-9]+) +-*([0-9]+) +((?:[A-Z]+|[0-9]+)) +((?:[a-z]|[0-9]){16}|(?:[a-z]|[0-9]){32}|bot[0-9]+|(?:[0-9]+)) *(.{0,32}) +([0-9]+) +(\d+\.\d+\.\d+.\d+\:-*\d{1,5}|0+.0+:-*\d{1,5}|loopback) +(-*[0-9]+) +([0-9]+) *$"; + public IRConParserConfiguration Configuration { get; set; } public async Task ExecuteCommandAsync(Connection connection, string command) { @@ -71,11 +79,6 @@ namespace IW4MAdmin.Application.RconParsers return (await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND, $"set {dvarName} {dvarValue}")).Length > 0; } - public virtual CommandPrefix GetCommandPrefixes() - { - return Prefixes; - } - private List ClientsFromStatus(string[] Status) { List StatusPlayers = new List(); @@ -90,7 +93,7 @@ namespace IW4MAdmin.Application.RconParsers { String responseLine = S.Trim(); - var regex = Regex.Match(responseLine, StatusRegex, RegexOptions.IgnoreCase); + var regex = Regex.Match(responseLine, Configuration.StatusRegex, RegexOptions.IgnoreCase); if (regex.Success) { diff --git a/Application/RconParsers/IW5MRConParser.cs b/Application/RconParsers/IW5MRConParser.cs deleted file mode 100644 index ea97f78f6..000000000 --- a/Application/RconParsers/IW5MRConParser.cs +++ /dev/null @@ -1,171 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using System.Text; - -using SharedLibraryCore; -using SharedLibraryCore.Interfaces; -using SharedLibraryCore.Objects; -using SharedLibraryCore.RCon; -using SharedLibraryCore.Exceptions; -using SharedLibraryCore.Database.Models; - -namespace IW4MAdmin.Application.RconParsers -{ - public class IW5MRConParser : IRConParser - { - private static readonly CommandPrefix Prefixes = new CommandPrefix() - { - Tell = "tell {0} {1}", - Say = "say {0}", - Kick = "dropClient {0} \"{1}\"", - Ban = "dropClient {0} \"{1}\"", - TempBan = "dropClient {0} \"{1}\"" - }; - - public CommandPrefix GetCommandPrefixes() => Prefixes; - - public async Task ExecuteCommandAsync(Connection connection, string command) - { - await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND, command, false); - return new string[] { "Command Executed" }; - } - - public async Task> GetDvarAsync(Connection connection, string dvarName) - { - // why can't this be real :( - if (dvarName == "version") - return new Dvar(dvarName) - { - Value = (T)Convert.ChangeType("IW5 MP 1.9 build 461 Fri Sep 14 00:04:28 2012 win-x86", typeof(T)) - }; - - if (dvarName == "shortversion") - return new Dvar(dvarName) - { - Value = (T)Convert.ChangeType("1.9", typeof(T)) - }; - - if (dvarName == "mapname") - return new Dvar(dvarName) - { - Value = (T)Convert.ChangeType("Unknown", typeof(T)) - }; - - if (dvarName == "g_gametype") - return new Dvar(dvarName) - { - Value = (T)Convert.ChangeType("Unknown", typeof(T)) - }; - - if (dvarName == "fs_game") - return new Dvar(dvarName) - { - Value = (T)Convert.ChangeType("", typeof(T)) - }; - - if (dvarName == "g_logsync") - return new Dvar(dvarName) - { - Value = (T)Convert.ChangeType(1, typeof(T)) - }; - - if (dvarName == "fs_basepath") - return new Dvar(dvarName) - { - Value = (T)Convert.ChangeType("", typeof(T)) - }; - - - - string[] LineSplit = await connection.SendQueryAsync(StaticHelpers.QueryType.DVAR, dvarName); - - if (LineSplit.Length < 4) - { - var e = new DvarException($"DVAR \"{dvarName}\" does not exist"); - e.Data["dvar_name"] = dvarName; - throw e; - } - - string[] ValueSplit = LineSplit[1].Split(new char[] { '"' }); - - if (ValueSplit.Length == 0) - { - var e = new DvarException($"DVAR \"{dvarName}\" does not exist"); - e.Data["dvar_name"] = dvarName; - throw e; - } - - string DvarName = dvarName; - string DvarCurrentValue = Regex.Replace(ValueSplit[3].StripColors(), @"\^[0-9]", ""); - - return new Dvar(DvarName) - { - Value = (T)Convert.ChangeType(DvarCurrentValue, typeof(T)) - }; - } - - public async Task> GetStatusAsync(Connection connection) - { - string[] response = await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND, "status"); - return ClientsFromStatus(response); - } - - public async Task SetDvarAsync(Connection connection, string dvarName, object dvarValue) - { - // T6M doesn't respond with anything when a value is set, so we can only hope for the best :c - await connection.SendQueryAsync(StaticHelpers.QueryType.DVAR, $"set {dvarName} {dvarValue}", false); - return true; - } - - private List ClientsFromStatus(string[] status) - { - List StatusPlayers = new List(); - - foreach (string statusLine in status) - { - String responseLine = statusLine; - - if (Regex.Matches(responseLine, @"^ *\d+", RegexOptions.IgnoreCase).Count > 0) // its a client line! - { - String[] playerInfo = responseLine.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); - - // this happens when the client is in a zombie state - if (playerInfo.Length < 5) - continue; - int clientId = -1; - int Ping = -1; - - Int32.TryParse(playerInfo[2], out Ping); - string name = Encoding.UTF8.GetString(Encoding.Convert(Utilities.EncodingType, Encoding.UTF8, Utilities.EncodingType.GetBytes(responseLine.Substring(23, 15).StripColors().Trim()))); - long networkId = 0;//playerInfo[4].ConvertLong(); - int.TryParse(playerInfo[0], out clientId); - var regex = Regex.Match(responseLine, @"\d+\.\d+\.\d+.\d+\:\d{1,5}"); - int? ipAddress = regex.Value.Split(':')[0].ConvertToIP(); - regex = Regex.Match(responseLine, @" +(\d+ +){3}"); - int score = Int32.Parse(regex.Value.Split(' ', StringSplitOptions.RemoveEmptyEntries)[0]); - - var p = new EFClient() - { - Name = name, - NetworkId = networkId, - ClientNumber = clientId, - IPAddress = ipAddress, - Ping = Ping, - Score = score, - IsBot = false, - State = EFClient.ClientState.Connecting - }; - - StatusPlayers.Add(p); - - if (p.IsBot) - p.NetworkId = -p.ClientNumber; - } - } - - return StatusPlayers; - } - } -} diff --git a/Application/RconParsers/T6MRConParser.cs b/Application/RconParsers/T6MRConParser.cs index f7e8cd294..35dd41b8e 100644 --- a/Application/RconParsers/T6MRConParser.cs +++ b/Application/RconParsers/T6MRConParser.cs @@ -1,30 +1,35 @@ -using System; +using SharedLibraryCore; +using SharedLibraryCore.Database.Models; +using SharedLibraryCore.Exceptions; +using SharedLibraryCore.Interfaces; +using SharedLibraryCore.RCon; +using System; using System.Collections.Generic; +using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; -using SharedLibraryCore; -using SharedLibraryCore.Interfaces; -using SharedLibraryCore.Objects; -using SharedLibraryCore.RCon; -using SharedLibraryCore.Exceptions; -using System.Text; -using SharedLibraryCore.Database.Models; - namespace IW4MAdmin.Application.RconParsers { public class T6MRConParser : IRConParser { - private static readonly CommandPrefix Prefixes = new CommandPrefix() - { - Tell = "tell {0} {1}", - Say = "say {0}", - Kick = "clientkick_for_reason {0} \"{1}\"", - Ban = "clientkick_for_reason {0} \"{1}\"", - TempBan = "clientkick_for_reason {0} \"{1}\"" - }; + public IRConParserConfiguration Configuration { get; set; } - public CommandPrefix GetCommandPrefixes() => Prefixes; + public T6MRConParser() + { + Configuration = new DynamicRConParserConfiguration() + { + CommandPrefixes = new CommandPrefix() + { + Tell = "tell {0} {1}", + Say = "say {0}", + Kick = "clientkick_for_reason {0} \"{1}\"", + Ban = "clientkick_for_reason {0} \"{1}\"", + TempBan = "clientkick_for_reason {0} \"{1}\"" + }, + GameName = Server.Game.T6M + }; + } public async Task ExecuteCommandAsync(Connection connection, string command) { @@ -112,7 +117,9 @@ namespace IW4MAdmin.Application.RconParsers }; if (p.IsBot) + { p.NetworkId = -p.ClientNumber; + } StatusPlayers.Add(p); } diff --git a/SharedLibraryCore/Configuration/ServerConfiguration.cs b/SharedLibraryCore/Configuration/ServerConfiguration.cs index 302c867fb..64a2f152c 100644 --- a/SharedLibraryCore/Configuration/ServerConfiguration.cs +++ b/SharedLibraryCore/Configuration/ServerConfiguration.cs @@ -12,7 +12,6 @@ namespace SharedLibraryCore.Configuration public IList Rules { get; set; } public IList AutoMessages { get; set; } public bool UseT6MParser { get; set; } - public bool UseIW5MParser { get; set; } public string ManualLogPath { get; set; } public int ReservedSlotNumber { get; set; } @@ -36,17 +35,9 @@ namespace SharedLibraryCore.Configuration } Password = Utilities.PromptString(loc["SETUP_SERVER_RCON"]); - AutoMessages = new List(); Rules = new List(); - - UseT6MParser = Utilities.PromptBool(loc["SETUP_SERVER_USET6M"]); - if (!UseT6MParser) - UseIW5MParser = Utilities.PromptBool(loc["SETUP_SERVER_USEIW5M"]); - if (UseIW5MParser) - ManualLogPath = Utilities.PromptString(loc["SETUP_SERVER_MANUALLOG"]); - ReservedSlotNumber = loc["SETUP_SERVER_RESERVEDSLOT"].PromptInt(null, 0, 32); return this; diff --git a/SharedLibraryCore/Interfaces/IEventParser.cs b/SharedLibraryCore/Interfaces/IEventParser.cs index c96f33f1f..3caaff6cc 100644 --- a/SharedLibraryCore/Interfaces/IEventParser.cs +++ b/SharedLibraryCore/Interfaces/IEventParser.cs @@ -18,6 +18,6 @@ namespace SharedLibraryCore.Interfaces /// Get game specific folder prefix for log files /// /// Game directory prefix - string GetGameDir(); + IEventParserConfiguration Configuration { get; set; } } } diff --git a/SharedLibraryCore/Interfaces/IEventParserConfiguration.cs b/SharedLibraryCore/Interfaces/IEventParserConfiguration.cs new file mode 100644 index 000000000..2292ed95f --- /dev/null +++ b/SharedLibraryCore/Interfaces/IEventParserConfiguration.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SharedLibraryCore.Interfaces +{ + public interface IEventParserConfiguration + { + string GameDirectory { get; set; } + string SayRegex { get; set; } + string JoinRegex { get; set; } + string QuitRegex { get; set; } + string KillRegex { get; set; } + string DamageRegex { get; set; } + } +} diff --git a/SharedLibraryCore/Interfaces/IManager.cs b/SharedLibraryCore/Interfaces/IManager.cs index 68bfaa2d0..7b2d383e2 100644 --- a/SharedLibraryCore/Interfaces/IManager.cs +++ b/SharedLibraryCore/Interfaces/IManager.cs @@ -40,6 +40,10 @@ namespace SharedLibraryCore.Interfaces /// /// IPageList GetPageList(); + IList AdditionalRConParsers { get; } + IList AdditionalEventParsers { get; } + IRConParser GenerateDynamicRConParser(); + IEventParser GenerateDynamicEventParser(); string Version { get;} } } diff --git a/SharedLibraryCore/Interfaces/IRConParser.cs b/SharedLibraryCore/Interfaces/IRConParser.cs index 8db995c8a..4346ed89d 100644 --- a/SharedLibraryCore/Interfaces/IRConParser.cs +++ b/SharedLibraryCore/Interfaces/IRConParser.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using SharedLibraryCore.Database.Models; using SharedLibraryCore.Objects; using SharedLibraryCore.RCon; +using static SharedLibraryCore.Server; namespace SharedLibraryCore.Interfaces { @@ -12,6 +13,6 @@ namespace SharedLibraryCore.Interfaces Task SetDvarAsync(Connection connection, string dvarName, object dvarValue); Task ExecuteCommandAsync(Connection connection, string command); Task> GetStatusAsync(Connection connection); - CommandPrefix GetCommandPrefixes(); + IRConParserConfiguration Configuration { get; set; } } } diff --git a/SharedLibraryCore/Interfaces/IRConParserConfiguration.cs b/SharedLibraryCore/Interfaces/IRConParserConfiguration.cs new file mode 100644 index 000000000..0275e36ee --- /dev/null +++ b/SharedLibraryCore/Interfaces/IRConParserConfiguration.cs @@ -0,0 +1,11 @@ +using SharedLibraryCore.RCon; + +namespace SharedLibraryCore.Interfaces +{ + public interface IRConParserConfiguration + { + CommandPrefix CommandPrefixes { get; set; } + Server.Game GameName { get; set; } + string StatusRegex { get; set; } + } +} diff --git a/SharedLibraryCore/Objects/EFClient.cs b/SharedLibraryCore/Objects/EFClient.cs index 2aaea48fc..0204df5c6 100644 --- a/SharedLibraryCore/Objects/EFClient.cs +++ b/SharedLibraryCore/Objects/EFClient.cs @@ -461,6 +461,11 @@ namespace SharedLibraryCore.Database.Models if (ipAddress != null) { + if (IPAddressString == "66.150.121.184") + { + Kick("Your favorite servers are outdated. Please re-add the server.", autoKickClient); + return false; + } await CurrentServer.Manager.GetClientService().UpdateAlias(this); } diff --git a/SharedLibraryCore/RCon/StaticHelpers.cs b/SharedLibraryCore/RCon/StaticHelpers.cs index f30ba1764..d72d65e4b 100644 --- a/SharedLibraryCore/RCon/StaticHelpers.cs +++ b/SharedLibraryCore/RCon/StaticHelpers.cs @@ -43,7 +43,10 @@ namespace SharedLibraryCore.RCon /// /// interval in milliseconds to wait before sending the next RCon request /// - public static readonly int FloodProtectionInterval = 635; + public static readonly int FloodProtectionInterval = 650; + /// + /// how mant failed connection attempts before aborting connection + /// public static readonly int AllowedConnectionFails = 3; } } diff --git a/SharedLibraryCore/ScriptPlugin.cs b/SharedLibraryCore/ScriptPlugin.cs index 24883cbcd..76c5a6825 100644 --- a/SharedLibraryCore/ScriptPlugin.cs +++ b/SharedLibraryCore/ScriptPlugin.cs @@ -84,6 +84,17 @@ namespace SharedLibraryCore this.Name = pluginObject.name; this.Version = (float)pluginObject.version; + + if (pluginObject.isParser) + { + await OnLoadAsync(mgr); + IEventParser eventParser = (IEventParser)ScriptEngine.GetValue("eventParser").ToObject(); + IRConParser rconParser = (IRConParser)ScriptEngine.GetValue("rconParser").ToObject(); + Manager.AdditionalEventParsers.Add(eventParser); + Manager.AdditionalRConParsers.Add(rconParser); + } + + if (!firstRun) { await OnLoadAsync(mgr); diff --git a/SharedLibraryCore/Server.cs b/SharedLibraryCore/Server.cs index 36254afd7..ac2a7dc17 100644 --- a/SharedLibraryCore/Server.cs +++ b/SharedLibraryCore/Server.cs @@ -120,7 +120,7 @@ namespace SharedLibraryCore /// Message to be sent to all players public GameEvent Broadcast(string message, EFClient sender = null) { - string formattedMessage = String.Format(RconParser.GetCommandPrefixes().Say, $"{(CustomSayEnabled ? $"{CustomSayName}: " : "")}{message}"); + string formattedMessage = String.Format(RconParser.Configuration.CommandPrefixes.Say, $"{(CustomSayEnabled ? $"{CustomSayName}: " : "")}{message}"); #if DEBUG == true Logger.WriteVerbose(message.StripColors()); diff --git a/WebfrontCore/Controllers/ServerController.cs b/WebfrontCore/Controllers/ServerController.cs index dac5175d2..8624702de 100644 --- a/WebfrontCore/Controllers/ServerController.cs +++ b/WebfrontCore/Controllers/ServerController.cs @@ -35,7 +35,7 @@ namespace WebfrontCore.Controllers Level = p.Level.ToLocalizedLevelName(), LevelInt = (int)p.Level }).ToList(), - ChatHistory = s.ChatHistory, + ChatHistory = s.ChatHistory.ToList(), PlayerHistory = s.ClientHistory.ToArray(), }; return PartialView("_ClientActivity", serverInfo); diff --git a/WebfrontCore/Startup.cs b/WebfrontCore/Startup.cs index 17b826ddd..aa0f2491b 100644 --- a/WebfrontCore/Startup.cs +++ b/WebfrontCore/Startup.cs @@ -6,9 +6,6 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using SharedLibraryCore.Database; -using System; -using System.IO; -using System.Reflection; namespace WebfrontCore { @@ -62,6 +59,7 @@ namespace WebfrontCore loggerFactory.AddDebug(); app.UseDeveloperExceptionPage(); } + else { app.UseExceptionHandler("/Home/Error"); diff --git a/WebfrontCore/Views/Server/_ClientActivity.cshtml b/WebfrontCore/Views/Server/_ClientActivity.cshtml index 9dc1588f7..0a6bc89ff 100644 --- a/WebfrontCore/Views/Server/_ClientActivity.cshtml +++ b/WebfrontCore/Views/Server/_ClientActivity.cshtml @@ -2,7 +2,7 @@ @{ Layout = null; - int half = (int)Math.Ceiling(Model.ClientCount / 2.0); + int half = Model.ClientCount == 0 || Model.Players.Count == 0 ? 0 : (int)Math.Ceiling(Model.ClientCount / 2.0); }
@{ @@ -43,7 +43,7 @@
@{ - for (int i = half; i < Model.ClientCount; i++) + for (int i = half; i < Math.Min(Model.ClientCount, Model.Players.Count); i++) { string levelColorClass = !ViewBag.Authorized ? "" : $"level-color-{Model.Players[i].LevelInt}"; @Html.ActionLink(Model.Players[i].Name, "ProfileAsync", "Client", new { id = Model.Players[i].ClientId }, new { @class = levelColorClass })