diff --git a/Application/IW4MServer.cs b/Application/IW4MServer.cs index 7357d0e2c..922e8415e 100644 --- a/Application/IW4MServer.cs +++ b/Application/IW4MServer.cs @@ -197,6 +197,7 @@ namespace IW4MAdmin catch (Exception e) { + ServerLogger.LogError(e, "Unexpected exception occurred processing event"); if (E.Origin != null && E.Type == GameEvent.EventType.Command) { E.Origin.Tell(_translationLookup["SERVER_ERROR_COMMAND_INGAME"]); diff --git a/SharedLibraryCore/Commands/NativeCommands.cs b/SharedLibraryCore/Commands/NativeCommands.cs index 7429f16b7..f3c8afa00 100644 --- a/SharedLibraryCore/Commands/NativeCommands.cs +++ b/SharedLibraryCore/Commands/NativeCommands.cs @@ -101,7 +101,8 @@ namespace SharedLibraryCore.Commands /// public class WarnCommand : Command { - public WarnCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) + private readonly ApplicationConfiguration _appConfig; + public WarnCommand(ApplicationConfiguration appConfig, CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) { Name = "warn"; Description = _translationLookup["COMMANDS_WARN_DESC"]; @@ -121,13 +122,15 @@ namespace SharedLibraryCore.Commands Required = true } }; + _appConfig = appConfig; } - public override Task ExecuteAsync(GameEvent E) + public override Task ExecuteAsync(GameEvent gameEvent) { - if (E.Target.Warn(E.Data, E.Origin).Failed) + var reason = gameEvent.Data.FindRuleForReason(_appConfig, gameEvent.Owner); + if (gameEvent.Target.Warn(reason, gameEvent.Origin).Failed) { - E.Origin.Tell(_translationLookup["COMMANDS_WARN_FAIL"].FormatExt(E.Target.Name)); + gameEvent.Origin.Tell(_translationLookup["COMMANDS_WARN_FAIL"].FormatExt(gameEvent.Target.Name)); } return Task.CompletedTask; @@ -172,7 +175,9 @@ namespace SharedLibraryCore.Commands /// public class KickCommand : Command { - public KickCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) + private readonly ApplicationConfiguration _appConfig; + + public KickCommand(ApplicationConfiguration appConfig, CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) { Name = "kick"; Description = _translationLookup["COMMANDS_KICK_DESC"]; @@ -192,20 +197,22 @@ namespace SharedLibraryCore.Commands Required = true } }; + _appConfig = appConfig; } - public override async Task ExecuteAsync(GameEvent E) + public override async Task ExecuteAsync(GameEvent gameEvent) { - switch ((await E.Target.Kick(E.Data, E.Origin).WaitAsync(Utilities.DefaultCommandTimeout, E.Owner.Manager.CancellationToken)).FailReason) + var reason = gameEvent.Data.FindRuleForReason(_appConfig, gameEvent.Owner); + switch ((await gameEvent.Target.Kick(reason, gameEvent.Origin).WaitAsync(Utilities.DefaultCommandTimeout, gameEvent.Owner.Manager.CancellationToken)).FailReason) { case GameEvent.EventFailReason.None: - E.Origin.Tell(_translationLookup["COMMANDS_KICK_SUCCESS"].FormatExt(E.Target.Name)); + gameEvent.Origin.Tell(_translationLookup["COMMANDS_KICK_SUCCESS"].FormatExt(gameEvent.Target.Name)); break; case GameEvent.EventFailReason.Exception: - E.Origin.Tell(_translationLookup["SERVER_ERROR_COMMAND_INGAME"]); + gameEvent.Origin.Tell(_translationLookup["SERVER_ERROR_COMMAND_INGAME"]); break; default: - E.Origin.Tell(_translationLookup["COMMANDS_KICK_FAIL"].FormatExt(E.Target.Name)); + gameEvent.Origin.Tell(_translationLookup["COMMANDS_KICK_FAIL"].FormatExt(gameEvent.Target.Name)); break; } } @@ -282,7 +289,9 @@ namespace SharedLibraryCore.Commands /// public class TempBanCommand : Command { - public TempBanCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) + private readonly ApplicationConfiguration _appConfig; + + public TempBanCommand(ApplicationConfiguration appConfig, CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) { Name = "tempban"; Description = _translationLookup["COMMANDS_TEMPBAN_DESC"]; @@ -307,35 +316,36 @@ namespace SharedLibraryCore.Commands Required = true } }; + _appConfig = appConfig; } private static readonly string TempBanRegex = @"([0-9]+\w+)\ (.+)"; - public override async Task ExecuteAsync(GameEvent E) + public override async Task ExecuteAsync(GameEvent gameEvent) { - var match = Regex.Match(E.Data, TempBanRegex); + var match = Regex.Match(gameEvent.Data, TempBanRegex); if (match.Success) { - string tempbanReason = match.Groups[2].ToString(); + var tempbanReason = match.Groups[2].ToString().FindRuleForReason(_appConfig, gameEvent.Owner); var length = match.Groups[1].ToString().ParseTimespan(); - if (length > E.Owner.Manager.GetApplicationSettings().Configuration().MaximumTempBanTime) + if (length > gameEvent.Owner.Manager.GetApplicationSettings().Configuration().MaximumTempBanTime) { - E.Origin.Tell(_translationLookup["COMMANDS_TEMPBAN_FAIL_TOOLONG"]); + gameEvent.Origin.Tell(_translationLookup["COMMANDS_TEMPBAN_FAIL_TOOLONG"]); } else { - switch ((await E.Target.TempBan(tempbanReason, length, E.Origin).WaitAsync(Utilities.DefaultCommandTimeout, E.Owner.Manager.CancellationToken)).FailReason) + switch ((await gameEvent.Target.TempBan(tempbanReason, length, gameEvent.Origin).WaitAsync(Utilities.DefaultCommandTimeout, gameEvent.Owner.Manager.CancellationToken)).FailReason) { case GameEvent.EventFailReason.None: - E.Origin.Tell(_translationLookup["COMMANDS_TEMPBAN_SUCCESS"].FormatExt(E.Target, length.HumanizeForCurrentCulture())); + gameEvent.Origin.Tell(_translationLookup["COMMANDS_TEMPBAN_SUCCESS"].FormatExt(gameEvent.Target, length.HumanizeForCurrentCulture())); break; case GameEvent.EventFailReason.Exception: - E.Origin.Tell(_translationLookup["SERVER_ERROR_COMMAND_INGAME"]); + gameEvent.Origin.Tell(_translationLookup["SERVER_ERROR_COMMAND_INGAME"]); break; default: - E.Origin.Tell(_translationLookup["COMMANDS_TEMPBAN_FAIL"].FormatExt(E.Target.Name)); + gameEvent.Origin.Tell(_translationLookup["COMMANDS_TEMPBAN_FAIL"].FormatExt(gameEvent.Target.Name)); break; } } @@ -348,7 +358,8 @@ namespace SharedLibraryCore.Commands /// public class BanCommand : Command { - public BanCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) + private readonly ApplicationConfiguration _appConfig; + public BanCommand(ApplicationConfiguration appConfig, CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) { Name = "ban"; Description = _translationLookup["COMMANDS_BAN_DESC"]; @@ -368,20 +379,22 @@ namespace SharedLibraryCore.Commands Required = true } }; + _appConfig = appConfig; } - public override async Task ExecuteAsync(GameEvent E) + public override async Task ExecuteAsync(GameEvent gameEvent) { - switch ((await E.Target.Ban(E.Data, E.Origin, false).WaitAsync(Utilities.DefaultCommandTimeout, E.Owner.Manager.CancellationToken)).FailReason) + var reason = gameEvent.Data.FindRuleForReason(_appConfig, gameEvent.Owner); + switch ((await gameEvent.Target.Ban(reason, gameEvent.Origin, false).WaitAsync(Utilities.DefaultCommandTimeout, gameEvent.Owner.Manager.CancellationToken)).FailReason) { case GameEvent.EventFailReason.None: - E.Origin.Tell(_translationLookup["COMMANDS_BAN_SUCCESS"].FormatExt(E.Target.Name)); + gameEvent.Origin.Tell(_translationLookup["COMMANDS_BAN_SUCCESS"].FormatExt(gameEvent.Target.Name)); break; case GameEvent.EventFailReason.Exception: - E.Origin.Tell(_translationLookup["SERVER_ERROR_COMMAND_INGAME"]); + gameEvent.Origin.Tell(_translationLookup["SERVER_ERROR_COMMAND_INGAME"]); break; default: - E.Origin.Tell(_translationLookup["COMMANDS_BAN_FAIL"].FormatExt(E.Target.Name)); + gameEvent.Origin.Tell(_translationLookup["COMMANDS_BAN_FAIL"].FormatExt(gameEvent.Target.Name)); break; } } diff --git a/SharedLibraryCore/Utilities.cs b/SharedLibraryCore/Utilities.cs index de3640893..af3777b40 100644 --- a/SharedLibraryCore/Utilities.cs +++ b/SharedLibraryCore/Utilities.cs @@ -18,6 +18,7 @@ using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using SharedLibraryCore.Configuration; using static SharedLibraryCore.Database.Models.EFClient; using static SharedLibraryCore.Database.Models.EFPenalty; using static SharedLibraryCore.Server; @@ -997,5 +998,31 @@ namespace SharedLibraryCore { return CurrentLocalization.LocalizationIndex[$"META_TYPE_{metaType.ToString().ToUpper()}_NAME"]; } + + public static string FindRuleForReason(this string reason, ApplicationConfiguration appConfig, Server server) + { + var regex = Regex.Match(reason, @"(rule|serverrule)(\d+)", RegexOptions.IgnoreCase); + if (!regex.Success) + { + return reason; + } + + var serverConfig = appConfig.Servers? + .FirstOrDefault(configServer => + configServer.IPAddress == server.IP && configServer.Port == server.Port); + + var index = int.Parse(regex.Groups[2].ToString()) - 1; + + return regex.Groups[1].ToString().ToLower() switch + { + "rule" => appConfig.GlobalRules?.Length > 0 && appConfig.GlobalRules.Length >= index + ? appConfig.GlobalRules[index] : + reason, + "serverrule" => serverConfig?.Rules?.Length > 0 && serverConfig.Rules?.Length >= index + ? serverConfig.Rules[index] : + reason, + _ => reason + }; + } } } diff --git a/Tests/ApplicationTests/CommandTests.cs b/Tests/ApplicationTests/CommandTests.cs index 8de324bec..cc3ed8cf3 100644 --- a/Tests/ApplicationTests/CommandTests.cs +++ b/Tests/ApplicationTests/CommandTests.cs @@ -41,6 +41,10 @@ namespace ApplicationTests .AddSingleton() .AddSingleton() .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() .BuildServiceProvider() .SetupTestHooks(); @@ -628,5 +632,171 @@ namespace ApplicationTests .MustHaveHappened(); } #endregion + + #region REASON_FROM_RULE + [Test] + public async Task Test_Warn_WithGlobalRule() + { + var expectedReason = "testglobalrule"; + appConfig.GlobalRules = new[] {expectedReason}; + var command = serviceProvider.GetRequiredService(); + var server = serviceProvider.GetRequiredService(); + var gameEvent = EventGenerators.GenerateEvent(GameEvent.EventType.Command, "rule1", server); + gameEvent.Origin.CurrentServer = server; + gameEvent.Target = gameEvent.Origin; + + await command.ExecuteAsync(gameEvent); + + Assert.NotNull(mockEventHandler.Events + .FirstOrDefault(e => e.Data == expectedReason && + e.Type == GameEvent.EventType.Warn)); + } + + [Test] + public async Task Test_Warn_WithServerRule() + { + var server = serviceProvider.GetRequiredService(); + var expectedReason = "testserverrule"; + appConfig.Servers = new [] { new ServerConfiguration() + { + IPAddress = server.IP, + Port = server.Port, + Rules = new []{ expectedReason } + }}; + var command = serviceProvider.GetRequiredService(); + var gameEvent = EventGenerators.GenerateEvent(GameEvent.EventType.Command, "serverrule1", server); + gameEvent.Origin.CurrentServer = server; + gameEvent.Target = gameEvent.Origin; + + await command.ExecuteAsync(gameEvent); + + Assert.NotNull(mockEventHandler.Events + .FirstOrDefault(e => e.Data == expectedReason && + e.Type == GameEvent.EventType.Warn)); + } + + [Test] + public async Task Test_Kick_WithGlobalRule() + { + var expectedReason = "testglobalrule"; + appConfig.GlobalRules = new[] {expectedReason}; + var command = serviceProvider.GetRequiredService(); + var server = serviceProvider.GetRequiredService(); + var gameEvent = EventGenerators.GenerateEvent(GameEvent.EventType.Command, "rule1", server); + gameEvent.Origin.CurrentServer = server; + gameEvent.Target = gameEvent.Origin; + + await command.ExecuteAsync(gameEvent); + + Assert.NotNull(mockEventHandler.Events + .FirstOrDefault(e => e.Data == expectedReason && + e.Type == GameEvent.EventType.Kick)); + } + + [Test] + public async Task Test_Kick_WithServerRule() + { + var server = serviceProvider.GetRequiredService(); + var expectedReason = "testserverrule"; + appConfig.Servers = new [] { new ServerConfiguration() + { + IPAddress = server.IP, + Port = server.Port, + Rules = new []{ expectedReason } + }}; + var command = serviceProvider.GetRequiredService(); + var gameEvent = EventGenerators.GenerateEvent(GameEvent.EventType.Command, "serverrule1", server); + gameEvent.Origin.CurrentServer = server; + gameEvent.Target = gameEvent.Origin; + + await command.ExecuteAsync(gameEvent); + + Assert.NotNull(mockEventHandler.Events + .FirstOrDefault(e => e.Data == expectedReason && + e.Type == GameEvent.EventType.Kick)); + } + + [Test] + public async Task Test_TempBan_WithGlobalRule() + { + var expectedReason = "testglobalrule"; + appConfig.GlobalRules = new[] {expectedReason}; + var command = serviceProvider.GetRequiredService(); + var server = serviceProvider.GetRequiredService(); + var gameEvent = EventGenerators.GenerateEvent(GameEvent.EventType.Command, "1h rule1", server); + gameEvent.Origin.CurrentServer = server; + gameEvent.Target = gameEvent.Origin; + + await command.ExecuteAsync(gameEvent); + + Assert.NotNull(mockEventHandler.Events + .FirstOrDefault(e => e.Data == expectedReason && + e.Type == GameEvent.EventType.TempBan)); + } + + [Test] + public async Task Test_TempBan_WithServerRule() + { + var server = serviceProvider.GetRequiredService(); + var expectedReason = "testserverrule"; + appConfig.Servers = new [] { new ServerConfiguration() + { + IPAddress = server.IP, + Port = server.Port, + Rules = new []{ expectedReason } + }}; + var command = serviceProvider.GetRequiredService(); + var gameEvent = EventGenerators.GenerateEvent(GameEvent.EventType.Command, "1h serverrule1", server); + gameEvent.Origin.CurrentServer = server; + gameEvent.Target = gameEvent.Origin; + + await command.ExecuteAsync(gameEvent); + + Assert.NotNull(mockEventHandler.Events + .FirstOrDefault(e => e.Data == expectedReason && + e.Type == GameEvent.EventType.TempBan)); + } + + [Test] + public async Task Test_Ban_WithGlobalRule() + { + var expectedReason = "testglobalrule"; + appConfig.GlobalRules = new[] {expectedReason}; + var command = serviceProvider.GetRequiredService(); + var server = serviceProvider.GetRequiredService(); + var gameEvent = EventGenerators.GenerateEvent(GameEvent.EventType.Command, "rule1", server); + gameEvent.Origin.CurrentServer = server; + gameEvent.Target = gameEvent.Origin; + + await command.ExecuteAsync(gameEvent); + + Assert.NotNull(mockEventHandler.Events + .FirstOrDefault(e => e.Data == expectedReason && + e.Type == GameEvent.EventType.Ban)); + } + + [Test] + public async Task Test_Ban_WithServerRule() + { + var server = serviceProvider.GetRequiredService(); + var expectedReason = "testserverrule"; + appConfig.Servers = new [] { new ServerConfiguration() + { + IPAddress = server.IP, + Port = server.Port, + Rules = new []{ expectedReason } + }}; + var command = serviceProvider.GetRequiredService(); + var gameEvent = EventGenerators.GenerateEvent(GameEvent.EventType.Command, "serverrule1", server); + gameEvent.Origin.CurrentServer = server; + gameEvent.Target = gameEvent.Origin; + + await command.ExecuteAsync(gameEvent); + + Assert.NotNull(mockEventHandler.Events + .FirstOrDefault(e => e.Data == expectedReason && + e.Type == GameEvent.EventType.Ban)); + } + #endregion } } diff --git a/Tests/ApplicationTests/DependencyInjectionExtensions.cs b/Tests/ApplicationTests/DependencyInjectionExtensions.cs index f24962601..8085ed4cc 100644 --- a/Tests/ApplicationTests/DependencyInjectionExtensions.cs +++ b/Tests/ApplicationTests/DependencyInjectionExtensions.cs @@ -48,6 +48,7 @@ namespace ApplicationTests .AddSingleton() .AddSingleton(A.Fake()) .AddSingleton(A.Fake()) + .AddSingleton(A.Fake()) .AddSingleton(eventHandler) .AddSingleton(ConfigurationGenerators.CreateApplicationConfiguration()) .AddSingleton(ConfigurationGenerators.CreateCommandConfiguration())