diff --git a/Application/IW4MServer.cs b/Application/IW4MServer.cs index 40c8c9228..f070d6817 100644 --- a/Application/IW4MServer.cs +++ b/Application/IW4MServer.cs @@ -148,12 +148,10 @@ namespace IW4MAdmin } // hack: this prevents commands from getting executing that 'shouldn't' be - if (E.Type == GameEvent.EventType.Command && - E.Extra != null && - (canExecuteCommand || - E.Origin?.Level == EFClient.Permission.Console)) + if (E.Type == GameEvent.EventType.Command && E.Extra is Command command && + (canExecuteCommand || E.Origin?.Level == Permission.Console)) { - await (((Command)E.Extra).ExecuteAsync(E)); + await command.ExecuteAsync(E); } } @@ -444,7 +442,7 @@ namespace IW4MAdmin // iw4 doesn't log the game info if (E.Extra == null) { - var dict = await this.GetInfoAsync(); + var dict = await this.GetInfoAsync(new TimeSpan(0, 0, 20)); if (dict == null) { @@ -609,7 +607,7 @@ namespace IW4MAdmin override public async Task ProcessUpdatesAsync(CancellationToken cts) { try - { + { if (cts.IsCancellationRequested) { await ShutdownInternal(); diff --git a/Plugins/Stats/Config/StatsConfiguration.cs b/Plugins/Stats/Config/StatsConfiguration.cs index fad552afe..5ad5818f9 100644 --- a/Plugins/Stats/Config/StatsConfiguration.cs +++ b/Plugins/Stats/Config/StatsConfiguration.cs @@ -15,6 +15,7 @@ namespace IW4MAdmin.Plugins.Stats.Config public int TopPlayersMinPlayTime { get; set; } public bool StoreClientKills { get; set; } public IDictionary DetectionDistributions { get; set; } + public IDictionary ServerDetectionTypes { get; set; } public string Name() => "Stats"; public IBaseConfiguration Generate() @@ -63,7 +64,7 @@ namespace IW4MAdmin.Plugins.Stats.Config TopPlayersMinPlayTime = 3600 * 3; StoreClientKills = false; - + return this; } } diff --git a/Plugins/Stats/Helpers/StatManager.cs b/Plugins/Stats/Helpers/StatManager.cs index d6106c9bc..8f1e9298a 100644 --- a/Plugins/Stats/Helpers/StatManager.cs +++ b/Plugins/Stats/Helpers/StatManager.cs @@ -14,6 +14,7 @@ using System.Linq; using System.Linq.Expressions; using System.Threading; using System.Threading.Tasks; +using static IW4MAdmin.Plugins.Stats.Cheat.Detection; namespace IW4MAdmin.Plugins.Stats.Helpers { @@ -566,8 +567,31 @@ namespace IW4MAdmin.Plugins.Stats.Helpers } } + private bool ShouldUseDetection(long serverId, DetectionType detectionType) + { + var detectionTypes = Plugin.Config.Configuration().ServerDetectionTypes; + + if (detectionTypes == null) + { + return true; + } + + if (!detectionTypes.ContainsKey(serverId)) + { + return true; + } + + return detectionTypes[serverId].Contains(detectionType); + } + async Task ApplyPenalty(DetectionPenaltyResult penalty, EFClient attacker) { + // allow disabling of certain detection types + if (!ShouldUseDetection(attacker.CurrentServer.EndPoint, penalty.Type)) + { + return; + } + var penaltyClient = Utilities.IW4MAdminClient(attacker.CurrentServer); switch (penalty.ClientPenalty) { diff --git a/Plugins/Stats/Plugin.cs b/Plugins/Stats/Plugin.cs index 169ed863c..c093b4275 100644 --- a/Plugins/Stats/Plugin.cs +++ b/Plugins/Stats/Plugin.cs @@ -82,7 +82,7 @@ namespace IW4MAdmin.Plugins.Stats break; case GameEvent.EventType.ScriptKill: string[] killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0]; - if (E.Owner.CustomCallback && killInfo.Length >= 14 && !ShouldIgnoreEvent(E.Origin, E.Target)) + if ((E.Owner.CustomCallback || ShouldOverrideAnticheatSetting(E.Owner)) && killInfo.Length >= 14 && !ShouldIgnoreEvent(E.Origin, E.Target)) { // this treats "world" damage as self damage if (IsWorldDamage(E.Origin)) @@ -129,7 +129,7 @@ namespace IW4MAdmin.Plugins.Stats break; case GameEvent.EventType.ScriptDamage: killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0]; - if (E.Owner.CustomCallback && killInfo.Length >= 14 && !ShouldIgnoreEvent(E.Origin, E.Target)) + if ((E.Owner.CustomCallback || ShouldOverrideAnticheatSetting(E.Owner)) && killInfo.Length >= 14 && !ShouldIgnoreEvent(E.Origin, E.Target)) { // this treats "world" damage as self damage if (IsWorldDamage(E.Origin)) @@ -515,5 +515,12 @@ namespace IW4MAdmin.Plugins.Stats /// /// private bool IsWorldDamage(EFClient origin) => origin?.NetworkId == 1; + + /// + /// Indicates if we should try to use anticheat even if sv_customcallbacks is not defined + /// + /// + /// + private bool ShouldOverrideAnticheatSetting(Server s) => Config.Configuration().EnableAntiCheat && s.GameName == Server.Game.IW5; } } diff --git a/SharedLibraryCore/Utilities.cs b/SharedLibraryCore/Utilities.cs index 2c6f51207..ee5968e58 100644 --- a/SharedLibraryCore/Utilities.cs +++ b/SharedLibraryCore/Utilities.cs @@ -723,19 +723,20 @@ namespace SharedLibraryCore return server.RconParser.GetStatusAsync(server.RemoteConnection); } - public static async Task> GetInfoAsync(this Server server) + /// + /// Retrieves the key value pairs for server information usually checked after map rotation + /// + /// + /// How long to wait after the map has rotated to query + /// + public static async Task> GetInfoAsync(this Server server, TimeSpan? delay = null) { - string[] response = new string[0]; - for (int i = 0; i < 4; i++) + if (delay != null) { - response = await server.RemoteConnection.SendQueryAsync(RCon.StaticHelpers.QueryType.GET_INFO); - if (response.Length == 2) - { - break; - } - - await Task.Delay(RCon.StaticHelpers.FloodProtectionInterval); + await Task.Delay(delay.Value); } + + var response = await server.RemoteConnection.SendQueryAsync(RCon.StaticHelpers.QueryType.GET_INFO); return response.FirstOrDefault(r => r[0] == '\\')?.DictionaryFromKeyValue(); }