2018-04-08 17:50:58 -04:00
|
|
|
|
using SharedLibraryCore.Helpers;
|
|
|
|
|
using SharedLibraryCore.Interfaces;
|
|
|
|
|
using SharedLibraryCore.Objects;
|
|
|
|
|
using IW4MAdmin.Plugins.Stats.Models;
|
2018-03-18 22:24:06 -04:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Text;
|
|
|
|
|
|
2018-04-08 17:50:58 -04:00
|
|
|
|
namespace IW4MAdmin.Plugins.Stats.Cheat
|
2018-03-18 22:24:06 -04:00
|
|
|
|
{
|
|
|
|
|
class Detection
|
|
|
|
|
{
|
2018-05-11 00:52:20 -04:00
|
|
|
|
public enum DetectionType
|
|
|
|
|
{
|
|
|
|
|
Bone,
|
|
|
|
|
Chest,
|
|
|
|
|
Offset,
|
|
|
|
|
Strain
|
|
|
|
|
};
|
|
|
|
|
|
2018-06-05 17:31:36 -04:00
|
|
|
|
public ChangeTracking<EFACSnapshot> Tracker { get; private set; }
|
|
|
|
|
|
2018-03-18 22:24:06 -04:00
|
|
|
|
int Kills;
|
2018-05-10 01:34:29 -04:00
|
|
|
|
int HitCount;
|
2018-03-18 22:24:06 -04:00
|
|
|
|
Dictionary<IW4Info.HitLocation, int> HitLocationCount;
|
2018-05-08 00:58:46 -04:00
|
|
|
|
double AngleDifferenceAverage;
|
2018-03-27 20:27:01 -04:00
|
|
|
|
EFClientStatistics ClientStats;
|
2018-05-05 16:36:26 -04:00
|
|
|
|
long LastOffset;
|
2018-10-06 16:31:05 -04:00
|
|
|
|
IW4Info.WeaponName LastWeapon;
|
2018-03-18 22:24:06 -04:00
|
|
|
|
ILogger Log;
|
2018-05-03 01:25:49 -04:00
|
|
|
|
Strain Strain;
|
2018-08-03 22:11:58 -04:00
|
|
|
|
readonly DateTime ConnectionTime = DateTime.UtcNow;
|
2018-03-18 22:24:06 -04:00
|
|
|
|
|
2018-03-27 20:27:01 -04:00
|
|
|
|
public Detection(ILogger log, EFClientStatistics clientStats)
|
2018-03-18 22:24:06 -04:00
|
|
|
|
{
|
|
|
|
|
Log = log;
|
|
|
|
|
HitLocationCount = new Dictionary<IW4Info.HitLocation, int>();
|
|
|
|
|
foreach (var loc in Enum.GetValues(typeof(IW4Info.HitLocation)))
|
|
|
|
|
HitLocationCount.Add((IW4Info.HitLocation)loc, 0);
|
2018-03-27 20:27:01 -04:00
|
|
|
|
ClientStats = clientStats;
|
2018-05-03 01:25:49 -04:00
|
|
|
|
Strain = new Strain();
|
2018-06-05 17:31:36 -04:00
|
|
|
|
Tracker = new ChangeTracking<EFACSnapshot>();
|
2018-03-18 22:24:06 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Analyze kill and see if performed by a cheater
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="kill">kill performed by the player</param>
|
|
|
|
|
/// <returns>true if detection reached thresholds, false otherwise</returns>
|
2018-05-08 00:58:46 -04:00
|
|
|
|
public DetectionPenaltyResult ProcessKill(EFClientKill kill, bool isDamage)
|
2018-03-18 22:24:06 -04:00
|
|
|
|
{
|
|
|
|
|
if ((kill.DeathType != IW4Info.MeansOfDeath.MOD_PISTOL_BULLET &&
|
2018-03-22 14:50:09 -04:00
|
|
|
|
kill.DeathType != IW4Info.MeansOfDeath.MOD_RIFLE_BULLET &&
|
|
|
|
|
kill.DeathType != IW4Info.MeansOfDeath.MOD_HEAD_SHOT) ||
|
2018-10-06 16:31:05 -04:00
|
|
|
|
kill.HitLoc == IW4Info.HitLocation.none || kill.TimeOffset - LastOffset < 0 ||
|
|
|
|
|
// hack: prevents false positives
|
|
|
|
|
(LastWeapon != kill.Weapon && (kill.TimeOffset - LastOffset) == 50))
|
2018-03-18 22:24:06 -04:00
|
|
|
|
return new DetectionPenaltyResult()
|
|
|
|
|
{
|
|
|
|
|
ClientPenalty = Penalty.PenaltyType.Any,
|
|
|
|
|
};
|
|
|
|
|
|
2018-05-20 22:35:56 -04:00
|
|
|
|
DetectionPenaltyResult result = null;
|
2018-10-06 16:31:05 -04:00
|
|
|
|
LastWeapon = kill.Weapon;
|
2018-05-03 01:25:49 -04:00
|
|
|
|
|
2018-03-18 22:24:06 -04:00
|
|
|
|
HitLocationCount[kill.HitLoc]++;
|
2018-05-08 00:58:46 -04:00
|
|
|
|
if (!isDamage)
|
|
|
|
|
{
|
|
|
|
|
Kills++;
|
|
|
|
|
}
|
2018-03-18 22:24:06 -04:00
|
|
|
|
|
2018-05-10 01:34:29 -04:00
|
|
|
|
HitCount++;
|
|
|
|
|
|
2018-05-04 00:22:10 -04:00
|
|
|
|
#region VIEWANGLES
|
2018-05-08 00:58:46 -04:00
|
|
|
|
if (kill.AnglesList.Count >= 2)
|
2018-03-18 22:24:06 -04:00
|
|
|
|
{
|
2018-05-10 01:34:29 -04:00
|
|
|
|
double realAgainstPredict = Vector3.ViewAngleDistance(kill.AnglesList[0], kill.AnglesList[1], kill.ViewAngles);
|
2018-05-03 01:25:49 -04:00
|
|
|
|
|
2018-05-08 00:58:46 -04:00
|
|
|
|
// LIFETIME
|
2018-03-27 20:27:01 -04:00
|
|
|
|
var hitLoc = ClientStats.HitLocations
|
|
|
|
|
.First(hl => hl.Location == kill.HitLoc);
|
2018-05-08 00:58:46 -04:00
|
|
|
|
|
2018-03-27 20:27:01 -04:00
|
|
|
|
float previousAverage = hitLoc.HitOffsetAverage;
|
2018-05-03 01:25:49 -04:00
|
|
|
|
double newAverage = (previousAverage * (hitLoc.HitCount - 1) + realAgainstPredict) / hitLoc.HitCount;
|
2018-03-27 20:27:01 -04:00
|
|
|
|
hitLoc.HitOffsetAverage = (float)newAverage;
|
2018-04-11 18:24:21 -04:00
|
|
|
|
|
2018-06-26 21:17:24 -04:00
|
|
|
|
if (hitLoc.HitOffsetAverage > Thresholds.MaxOffset(hitLoc.HitCount) &&
|
2018-05-16 00:57:37 -04:00
|
|
|
|
hitLoc.HitCount > 100)
|
2018-04-11 18:24:21 -04:00
|
|
|
|
{
|
2018-05-10 01:34:29 -04:00
|
|
|
|
Log.WriteDebug("*** Reached Max Lifetime Average for Angle Difference ***");
|
|
|
|
|
Log.WriteDebug($"Lifetime Average = {newAverage}");
|
|
|
|
|
Log.WriteDebug($"Bone = {hitLoc.Location}");
|
|
|
|
|
Log.WriteDebug($"HitCount = {hitLoc.HitCount}");
|
|
|
|
|
Log.WriteDebug($"ID = {kill.AttackerId}");
|
|
|
|
|
|
2018-05-20 22:35:56 -04:00
|
|
|
|
result = new DetectionPenaltyResult()
|
2018-05-08 00:58:46 -04:00
|
|
|
|
{
|
2018-05-14 13:55:10 -04:00
|
|
|
|
ClientPenalty = Penalty.PenaltyType.Ban,
|
2018-05-11 00:52:20 -04:00
|
|
|
|
Value = hitLoc.HitOffsetAverage,
|
|
|
|
|
HitCount = hitLoc.HitCount,
|
2018-05-14 13:55:10 -04:00
|
|
|
|
Type = DetectionType.Offset
|
2018-05-08 00:58:46 -04:00
|
|
|
|
};
|
2018-04-11 18:24:21 -04:00
|
|
|
|
}
|
2018-05-03 01:25:49 -04:00
|
|
|
|
|
2018-05-08 00:58:46 -04:00
|
|
|
|
// SESSION
|
2018-05-10 01:34:29 -04:00
|
|
|
|
double sessAverage = (AngleDifferenceAverage * (HitCount - 1) + realAgainstPredict) / HitCount;
|
2018-05-08 00:58:46 -04:00
|
|
|
|
AngleDifferenceAverage = sessAverage;
|
2018-05-04 00:22:10 -04:00
|
|
|
|
|
2018-06-26 21:17:24 -04:00
|
|
|
|
if (sessAverage > Thresholds.MaxOffset(HitCount) &&
|
2018-05-16 00:57:37 -04:00
|
|
|
|
HitCount > 30)
|
2018-05-04 00:22:10 -04:00
|
|
|
|
{
|
2018-05-10 01:34:29 -04:00
|
|
|
|
Log.WriteDebug("*** Reached Max Session Average for Angle Difference ***");
|
|
|
|
|
Log.WriteDebug($"Session Average = {sessAverage}");
|
|
|
|
|
Log.WriteDebug($"HitCount = {HitCount}");
|
|
|
|
|
Log.WriteDebug($"ID = {kill.AttackerId}");
|
|
|
|
|
|
2018-05-20 22:35:56 -04:00
|
|
|
|
result = new DetectionPenaltyResult()
|
2018-05-04 00:22:10 -04:00
|
|
|
|
{
|
2018-05-11 00:52:20 -04:00
|
|
|
|
ClientPenalty = Penalty.PenaltyType.Ban,
|
|
|
|
|
Value = sessAverage,
|
|
|
|
|
HitCount = HitCount,
|
|
|
|
|
Type = DetectionType.Offset,
|
|
|
|
|
Location = hitLoc.Location
|
2018-05-04 00:22:10 -04:00
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-03 01:25:49 -04:00
|
|
|
|
#if DEBUG
|
2018-05-05 16:36:26 -04:00
|
|
|
|
Log.WriteDebug($"PredictVsReal={realAgainstPredict}");
|
2018-05-03 01:25:49 -04:00
|
|
|
|
#endif
|
|
|
|
|
}
|
2018-05-20 22:35:56 -04:00
|
|
|
|
|
|
|
|
|
double currentStrain = Strain.GetStrain(isDamage, kill.Damage, kill.Distance / 0.0254, kill.ViewAngles, Math.Max(50, kill.TimeOffset - LastOffset));
|
2018-05-05 16:36:26 -04:00
|
|
|
|
LastOffset = kill.TimeOffset;
|
2018-05-03 01:25:49 -04:00
|
|
|
|
|
|
|
|
|
if (currentStrain > ClientStats.MaxStrain)
|
|
|
|
|
{
|
|
|
|
|
ClientStats.MaxStrain = currentStrain;
|
2018-05-05 16:36:26 -04:00
|
|
|
|
}
|
2018-05-04 00:22:10 -04:00
|
|
|
|
|
2018-05-16 00:57:37 -04:00
|
|
|
|
// flag
|
2018-09-29 15:52:22 -04:00
|
|
|
|
if (currentStrain > Thresholds.MaxStrainFlag &&
|
2018-09-23 20:45:54 -04:00
|
|
|
|
HitCount >= 10)
|
2018-05-16 00:57:37 -04:00
|
|
|
|
{
|
2018-05-20 22:35:56 -04:00
|
|
|
|
result = new DetectionPenaltyResult()
|
2018-05-16 00:57:37 -04:00
|
|
|
|
{
|
|
|
|
|
ClientPenalty = Penalty.PenaltyType.Flag,
|
2018-05-20 22:35:56 -04:00
|
|
|
|
Value = currentStrain,
|
2018-05-16 00:57:37 -04:00
|
|
|
|
HitCount = HitCount,
|
|
|
|
|
Type = DetectionType.Strain
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ban
|
2018-07-05 22:04:34 -04:00
|
|
|
|
if (currentStrain > Thresholds.MaxStrainBan &&
|
2018-07-29 15:43:42 -04:00
|
|
|
|
HitCount >= 15)
|
2018-05-05 16:36:26 -04:00
|
|
|
|
{
|
2018-05-20 22:35:56 -04:00
|
|
|
|
result = new DetectionPenaltyResult()
|
2018-05-04 00:22:10 -04:00
|
|
|
|
{
|
2018-05-17 19:31:58 -04:00
|
|
|
|
ClientPenalty = Penalty.PenaltyType.Ban,
|
2018-05-20 22:35:56 -04:00
|
|
|
|
Value = currentStrain,
|
2018-05-11 00:52:20 -04:00
|
|
|
|
HitCount = HitCount,
|
|
|
|
|
Type = DetectionType.Strain
|
2018-05-05 16:36:26 -04:00
|
|
|
|
};
|
2018-03-26 00:51:25 -04:00
|
|
|
|
}
|
2018-03-18 22:24:06 -04:00
|
|
|
|
|
2018-05-03 01:25:49 -04:00
|
|
|
|
#if DEBUG
|
|
|
|
|
Log.WriteDebug($"Current Strain: {currentStrain}");
|
|
|
|
|
#endif
|
|
|
|
|
|
2018-04-05 00:38:45 -04:00
|
|
|
|
#endregion
|
2018-03-18 22:24:06 -04:00
|
|
|
|
|
2018-04-05 00:38:45 -04:00
|
|
|
|
#region SESSION_RATIOS
|
|
|
|
|
if (Kills >= Thresholds.LowSampleMinKills)
|
|
|
|
|
{
|
2018-05-10 01:34:29 -04:00
|
|
|
|
double marginOfError = Thresholds.GetMarginOfError(HitCount);
|
2018-04-05 00:38:45 -04:00
|
|
|
|
// determine what the max headshot percentage can be for current number of kills
|
2018-05-10 01:34:29 -04:00
|
|
|
|
double lerpAmount = Math.Min(1.0, (HitCount - Thresholds.LowSampleMinKills) / (double)(/*Thresholds.HighSampleMinKills*/ 60 - Thresholds.LowSampleMinKills));
|
2018-04-05 00:38:45 -04:00
|
|
|
|
double maxHeadshotLerpValueForFlag = Thresholds.Lerp(Thresholds.HeadshotRatioThresholdLowSample(2.0), Thresholds.HeadshotRatioThresholdHighSample(2.0), lerpAmount) + marginOfError;
|
2018-06-02 00:48:10 -04:00
|
|
|
|
double maxHeadshotLerpValueForBan = Thresholds.Lerp(Thresholds.HeadshotRatioThresholdLowSample(3.5), Thresholds.HeadshotRatioThresholdHighSample(3.5), lerpAmount) + marginOfError;
|
2018-04-05 00:38:45 -04:00
|
|
|
|
// determine what the max bone percentage can be for current number of kills
|
|
|
|
|
double maxBoneRatioLerpValueForFlag = Thresholds.Lerp(Thresholds.BoneRatioThresholdLowSample(2.25), Thresholds.BoneRatioThresholdHighSample(2.25), lerpAmount) + marginOfError;
|
|
|
|
|
double maxBoneRatioLerpValueForBan = Thresholds.Lerp(Thresholds.BoneRatioThresholdLowSample(3.25), Thresholds.BoneRatioThresholdHighSample(3.25), lerpAmount) + marginOfError;
|
2018-03-18 22:24:06 -04:00
|
|
|
|
|
2018-04-05 00:38:45 -04:00
|
|
|
|
// calculate headshot ratio
|
2018-05-10 01:34:29 -04:00
|
|
|
|
double currentHeadshotRatio = ((HitLocationCount[IW4Info.HitLocation.head] + HitLocationCount[IW4Info.HitLocation.helmet] + HitLocationCount[IW4Info.HitLocation.neck]) / (double)HitCount);
|
2018-04-09 23:33:42 -04:00
|
|
|
|
|
2018-04-05 00:38:45 -04:00
|
|
|
|
// calculate maximum bone
|
2018-05-10 01:34:29 -04:00
|
|
|
|
double currentMaxBoneRatio = (HitLocationCount.Values.Select(v => v / (double)HitCount).Max());
|
2018-04-05 00:38:45 -04:00
|
|
|
|
var bone = HitLocationCount.FirstOrDefault(b => b.Value == HitLocationCount.Values.Max()).Key;
|
2018-04-09 23:33:42 -04:00
|
|
|
|
|
2018-04-05 00:38:45 -04:00
|
|
|
|
#region HEADSHOT_RATIO
|
|
|
|
|
// flag on headshot
|
|
|
|
|
if (currentHeadshotRatio > maxHeadshotLerpValueForFlag)
|
|
|
|
|
{
|
|
|
|
|
// ban on headshot
|
2018-09-02 23:09:25 -04:00
|
|
|
|
if (currentHeadshotRatio > maxHeadshotLerpValueForBan)
|
2018-04-05 00:38:45 -04:00
|
|
|
|
{
|
|
|
|
|
Log.WriteDebug("**Maximum Headshot Ratio Reached For Ban**");
|
|
|
|
|
Log.WriteDebug($"ClientId: {kill.AttackerId}");
|
2018-05-11 00:52:20 -04:00
|
|
|
|
Log.WriteDebug($"**HitCount: {HitCount}");
|
2018-04-05 00:38:45 -04:00
|
|
|
|
Log.WriteDebug($"**Ratio {currentHeadshotRatio}");
|
|
|
|
|
Log.WriteDebug($"**MaxRatio {maxHeadshotLerpValueForFlag}");
|
|
|
|
|
var sb = new StringBuilder();
|
|
|
|
|
foreach (var kvp in HitLocationCount)
|
|
|
|
|
sb.Append($"HitLocation: {kvp.Key} -> {kvp.Value}\r\n");
|
|
|
|
|
Log.WriteDebug(sb.ToString());
|
2018-03-18 22:24:06 -04:00
|
|
|
|
|
2018-05-20 22:35:56 -04:00
|
|
|
|
result = new DetectionPenaltyResult()
|
2018-04-05 00:38:45 -04:00
|
|
|
|
{
|
|
|
|
|
ClientPenalty = Penalty.PenaltyType.Ban,
|
2018-05-11 00:52:20 -04:00
|
|
|
|
Value = currentHeadshotRatio,
|
|
|
|
|
Location = IW4Info.HitLocation.head,
|
|
|
|
|
HitCount = HitCount,
|
|
|
|
|
Type = DetectionType.Bone
|
2018-04-05 00:38:45 -04:00
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Log.WriteDebug("**Maximum Headshot Ratio Reached For Flag**");
|
|
|
|
|
Log.WriteDebug($"ClientId: {kill.AttackerId}");
|
2018-05-11 00:52:20 -04:00
|
|
|
|
Log.WriteDebug($"**HitCount: {HitCount}");
|
2018-04-05 00:38:45 -04:00
|
|
|
|
Log.WriteDebug($"**Ratio {currentHeadshotRatio}");
|
|
|
|
|
Log.WriteDebug($"**MaxRatio {maxHeadshotLerpValueForFlag}");
|
|
|
|
|
var sb = new StringBuilder();
|
|
|
|
|
foreach (var kvp in HitLocationCount)
|
|
|
|
|
sb.Append($"HitLocation: {kvp.Key} -> {kvp.Value}\r\n");
|
|
|
|
|
Log.WriteDebug(sb.ToString());
|
2018-03-18 22:24:06 -04:00
|
|
|
|
|
2018-05-20 22:35:56 -04:00
|
|
|
|
result = new DetectionPenaltyResult()
|
2018-04-05 00:38:45 -04:00
|
|
|
|
{
|
|
|
|
|
ClientPenalty = Penalty.PenaltyType.Flag,
|
2018-05-11 00:52:20 -04:00
|
|
|
|
Value = currentHeadshotRatio,
|
|
|
|
|
Location = IW4Info.HitLocation.head,
|
|
|
|
|
HitCount = HitCount,
|
|
|
|
|
Type = DetectionType.Bone
|
2018-04-05 00:38:45 -04:00
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
2018-03-26 00:51:25 -04:00
|
|
|
|
|
2018-04-05 00:38:45 -04:00
|
|
|
|
#region BONE_RATIO
|
|
|
|
|
// flag on bone ratio
|
|
|
|
|
else if (currentMaxBoneRatio > maxBoneRatioLerpValueForFlag)
|
|
|
|
|
{
|
|
|
|
|
// ban on bone ratio
|
|
|
|
|
if (currentMaxBoneRatio > maxBoneRatioLerpValueForBan)
|
|
|
|
|
{
|
|
|
|
|
Log.WriteDebug("**Maximum Bone Ratio Reached For Ban**");
|
|
|
|
|
Log.WriteDebug($"ClientId: {kill.AttackerId}");
|
2018-05-11 00:52:20 -04:00
|
|
|
|
Log.WriteDebug($"**HitCount: {HitCount}");
|
2018-04-05 00:38:45 -04:00
|
|
|
|
Log.WriteDebug($"**Ratio {currentMaxBoneRatio}");
|
|
|
|
|
Log.WriteDebug($"**MaxRatio {maxBoneRatioLerpValueForBan}");
|
|
|
|
|
var sb = new StringBuilder();
|
|
|
|
|
foreach (var kvp in HitLocationCount)
|
|
|
|
|
sb.Append($"HitLocation: {kvp.Key} -> {kvp.Value}\r\n");
|
|
|
|
|
Log.WriteDebug(sb.ToString());
|
2018-03-18 22:24:06 -04:00
|
|
|
|
|
2018-05-20 22:35:56 -04:00
|
|
|
|
result = new DetectionPenaltyResult()
|
2018-04-05 00:38:45 -04:00
|
|
|
|
{
|
|
|
|
|
ClientPenalty = Penalty.PenaltyType.Ban,
|
2018-05-11 00:52:20 -04:00
|
|
|
|
Value = currentMaxBoneRatio,
|
|
|
|
|
Location = bone,
|
|
|
|
|
HitCount = HitCount,
|
|
|
|
|
Type = DetectionType.Bone
|
2018-04-05 00:38:45 -04:00
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Log.WriteDebug("**Maximum Bone Ratio Reached For Flag**");
|
|
|
|
|
Log.WriteDebug($"ClientId: {kill.AttackerId}");
|
2018-05-11 00:52:20 -04:00
|
|
|
|
Log.WriteDebug($"**HitCount: {HitCount}");
|
2018-04-05 00:38:45 -04:00
|
|
|
|
Log.WriteDebug($"**Ratio {currentMaxBoneRatio}");
|
|
|
|
|
Log.WriteDebug($"**MaxRatio {maxBoneRatioLerpValueForFlag}");
|
|
|
|
|
var sb = new StringBuilder();
|
|
|
|
|
foreach (var kvp in HitLocationCount)
|
|
|
|
|
sb.Append($"HitLocation: {kvp.Key} -> {kvp.Value}\r\n");
|
|
|
|
|
Log.WriteDebug(sb.ToString());
|
2018-03-18 22:24:06 -04:00
|
|
|
|
|
2018-05-20 22:35:56 -04:00
|
|
|
|
result = new DetectionPenaltyResult()
|
2018-04-05 00:38:45 -04:00
|
|
|
|
{
|
|
|
|
|
ClientPenalty = Penalty.PenaltyType.Flag,
|
2018-05-11 00:52:20 -04:00
|
|
|
|
Value = currentMaxBoneRatio,
|
|
|
|
|
Location = bone,
|
|
|
|
|
HitCount = HitCount,
|
|
|
|
|
Type = DetectionType.Bone
|
2018-04-05 00:38:45 -04:00
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
}
|
2018-03-18 22:24:06 -04:00
|
|
|
|
|
2018-04-05 00:38:45 -04:00
|
|
|
|
#region CHEST_ABDOMEN_RATIO_SESSION
|
2018-05-11 00:52:20 -04:00
|
|
|
|
int chestHits = HitLocationCount[IW4Info.HitLocation.torso_upper];
|
2018-03-18 22:24:06 -04:00
|
|
|
|
|
2018-05-11 00:52:20 -04:00
|
|
|
|
if (chestHits >= Thresholds.MediumSampleMinKills)
|
2018-04-05 00:38:45 -04:00
|
|
|
|
{
|
2018-05-11 00:52:20 -04:00
|
|
|
|
double marginOfError = Thresholds.GetMarginOfError(chestHits);
|
|
|
|
|
double lerpAmount = Math.Min(1.0, (chestHits - Thresholds.MediumSampleMinKills) / (double)(Thresholds.HighSampleMinKills - Thresholds.LowSampleMinKills));
|
2018-04-05 00:38:45 -04:00
|
|
|
|
// determine max acceptable ratio of chest to abdomen kills
|
|
|
|
|
double chestAbdomenRatioLerpValueForFlag = Thresholds.Lerp(Thresholds.ChestAbdomenRatioThresholdLowSample(3), Thresholds.ChestAbdomenRatioThresholdHighSample(3), lerpAmount) + marginOfError;
|
|
|
|
|
double chestAbdomenLerpValueForBan = Thresholds.Lerp(Thresholds.ChestAbdomenRatioThresholdLowSample(4), Thresholds.ChestAbdomenRatioThresholdHighSample(4), lerpAmount) + marginOfError;
|
2018-03-18 22:24:06 -04:00
|
|
|
|
|
2018-04-05 00:38:45 -04:00
|
|
|
|
double currentChestAbdomenRatio = HitLocationCount[IW4Info.HitLocation.torso_upper] / (double)HitLocationCount[IW4Info.HitLocation.torso_lower];
|
2018-03-18 22:24:06 -04:00
|
|
|
|
|
2018-04-05 00:38:45 -04:00
|
|
|
|
if (currentChestAbdomenRatio > chestAbdomenRatioLerpValueForFlag)
|
|
|
|
|
{
|
2018-03-26 00:51:25 -04:00
|
|
|
|
|
2018-05-11 00:52:20 -04:00
|
|
|
|
if (currentChestAbdomenRatio > chestAbdomenLerpValueForBan && chestHits >= Thresholds.MediumSampleMinKills + 30)
|
2018-04-05 00:38:45 -04:00
|
|
|
|
{
|
|
|
|
|
Log.WriteDebug("**Maximum Chest/Abdomen Ratio Reached For Ban**");
|
|
|
|
|
Log.WriteDebug($"ClientId: {kill.AttackerId}");
|
2018-05-11 00:52:20 -04:00
|
|
|
|
Log.WriteDebug($"**Chest Hits: {chestHits}");
|
2018-04-05 00:38:45 -04:00
|
|
|
|
Log.WriteDebug($"**Ratio {currentChestAbdomenRatio}");
|
|
|
|
|
Log.WriteDebug($"**MaxRatio {chestAbdomenLerpValueForBan}");
|
|
|
|
|
var sb = new StringBuilder();
|
|
|
|
|
foreach (var kvp in HitLocationCount)
|
|
|
|
|
sb.Append($"HitLocation: {kvp.Key} -> {kvp.Value}\r\n");
|
|
|
|
|
Log.WriteDebug(sb.ToString());
|
2018-03-18 22:24:06 -04:00
|
|
|
|
|
2018-05-20 22:35:56 -04:00
|
|
|
|
result = new DetectionPenaltyResult()
|
2018-04-05 00:38:45 -04:00
|
|
|
|
{
|
|
|
|
|
ClientPenalty = Penalty.PenaltyType.Ban,
|
2018-05-11 00:52:20 -04:00
|
|
|
|
Value = currentChestAbdomenRatio,
|
|
|
|
|
Location = IW4Info.HitLocation.torso_upper,
|
|
|
|
|
Type = DetectionType.Chest,
|
|
|
|
|
HitCount = chestHits
|
2018-04-05 00:38:45 -04:00
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Log.WriteDebug("**Maximum Chest/Abdomen Ratio Reached For Flag**");
|
|
|
|
|
Log.WriteDebug($"ClientId: {kill.AttackerId}");
|
2018-05-11 00:52:20 -04:00
|
|
|
|
Log.WriteDebug($"**Chest Hits: {chestHits}");
|
2018-04-05 00:38:45 -04:00
|
|
|
|
Log.WriteDebug($"**Ratio {currentChestAbdomenRatio}");
|
|
|
|
|
Log.WriteDebug($"**MaxRatio {chestAbdomenRatioLerpValueForFlag}");
|
|
|
|
|
var sb = new StringBuilder();
|
|
|
|
|
foreach (var kvp in HitLocationCount)
|
|
|
|
|
sb.Append($"HitLocation: {kvp.Key} -> {kvp.Value}\r\n");
|
|
|
|
|
Log.WriteDebug(sb.ToString());
|
|
|
|
|
// Log.WriteDebug($"ThresholdReached: {AboveThresholdCount}");
|
2018-03-18 22:24:06 -04:00
|
|
|
|
|
2018-05-20 22:35:56 -04:00
|
|
|
|
result = new DetectionPenaltyResult()
|
2018-04-05 00:38:45 -04:00
|
|
|
|
{
|
|
|
|
|
ClientPenalty = Penalty.PenaltyType.Flag,
|
2018-05-11 00:52:20 -04:00
|
|
|
|
Value = currentChestAbdomenRatio,
|
|
|
|
|
Location = IW4Info.HitLocation.torso_upper,
|
|
|
|
|
Type = DetectionType.Chest,
|
|
|
|
|
HitCount = chestHits
|
2018-04-05 00:38:45 -04:00
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
#endregion
|
2018-05-20 22:35:56 -04:00
|
|
|
|
|
2018-06-05 17:31:36 -04:00
|
|
|
|
Tracker.OnChange(new EFACSnapshot()
|
2018-05-20 22:35:56 -04:00
|
|
|
|
{
|
2018-06-05 17:31:36 -04:00
|
|
|
|
When = kill.When,
|
|
|
|
|
ClientId = ClientStats.ClientId,
|
|
|
|
|
SessionAngleOffset = AngleDifferenceAverage,
|
|
|
|
|
CurrentSessionLength = (int)(DateTime.UtcNow - ConnectionTime).TotalSeconds,
|
|
|
|
|
CurrentStrain = currentStrain,
|
|
|
|
|
CurrentViewAngle = kill.ViewAngles,
|
|
|
|
|
Hits = HitCount,
|
|
|
|
|
Kills = Kills,
|
|
|
|
|
Deaths = ClientStats.SessionDeaths,
|
2018-09-23 20:45:54 -04:00
|
|
|
|
HitDestinationId = kill.DeathOrigin.Vector3Id,
|
2018-06-05 17:31:36 -04:00
|
|
|
|
HitDestination = kill.DeathOrigin,
|
2018-09-23 20:45:54 -04:00
|
|
|
|
HitOriginId = kill.KillOrigin.Vector3Id,
|
2018-06-05 17:31:36 -04:00
|
|
|
|
HitOrigin = kill.KillOrigin,
|
|
|
|
|
EloRating = ClientStats.EloRating,
|
|
|
|
|
HitLocation = kill.HitLoc,
|
|
|
|
|
LastStrainAngle = Strain.LastAngle,
|
|
|
|
|
PredictedViewAngles = kill.AnglesList,
|
|
|
|
|
// this is in "meters"
|
|
|
|
|
Distance = kill.Distance,
|
|
|
|
|
SessionScore = ClientStats.SessionScore,
|
|
|
|
|
HitType = kill.DeathType,
|
|
|
|
|
SessionSPM = ClientStats.SessionSPM,
|
|
|
|
|
StrainAngleBetween = Strain.LastDistance,
|
|
|
|
|
TimeSinceLastEvent = (int)Strain.LastDeltaTime,
|
|
|
|
|
WeaponId = kill.Weapon
|
|
|
|
|
});
|
2018-05-20 22:35:56 -04:00
|
|
|
|
|
|
|
|
|
return result ?? new DetectionPenaltyResult()
|
2018-03-18 22:24:06 -04:00
|
|
|
|
{
|
2018-04-05 00:38:45 -04:00
|
|
|
|
ClientPenalty = Penalty.PenaltyType.Any,
|
2018-03-26 00:51:25 -04:00
|
|
|
|
};
|
|
|
|
|
}
|
2018-03-18 22:24:06 -04:00
|
|
|
|
|
2018-04-05 00:38:45 -04:00
|
|
|
|
public DetectionPenaltyResult ProcessTotalRatio(EFClientStatistics stats)
|
|
|
|
|
{
|
2018-05-11 00:52:20 -04:00
|
|
|
|
int totalChestHits = stats.HitLocations.Single(c => c.Location == IW4Info.HitLocation.torso_upper).HitCount;
|
2018-03-18 22:24:06 -04:00
|
|
|
|
|
2018-05-11 00:52:20 -04:00
|
|
|
|
if (totalChestHits >= 60)
|
2018-04-05 00:38:45 -04:00
|
|
|
|
{
|
2018-05-11 00:52:20 -04:00
|
|
|
|
double marginOfError = Thresholds.GetMarginOfError(totalChestHits);
|
|
|
|
|
double lerpAmount = Math.Min(1.0, (totalChestHits - 60) / 250.0);
|
2018-04-05 00:38:45 -04:00
|
|
|
|
// determine max acceptable ratio of chest to abdomen kills
|
2018-04-28 21:11:13 -04:00
|
|
|
|
double chestAbdomenRatioLerpValueForFlag = Thresholds.Lerp(Thresholds.ChestAbdomenRatioThresholdHighSample(3.0), Thresholds.ChestAbdomenRatioThresholdHighSample(2.0), lerpAmount) + marginOfError;
|
|
|
|
|
double chestAbdomenLerpValueForBan = Thresholds.Lerp(Thresholds.ChestAbdomenRatioThresholdHighSample(4.0), Thresholds.ChestAbdomenRatioThresholdHighSample(3.0), lerpAmount) + marginOfError;
|
2018-03-18 22:24:06 -04:00
|
|
|
|
|
2018-05-11 00:52:20 -04:00
|
|
|
|
double currentChestAbdomenRatio = totalChestHits /
|
2018-03-28 23:01:09 -04:00
|
|
|
|
stats.HitLocations.Single(hl => hl.Location == IW4Info.HitLocation.torso_lower).HitCount;
|
2018-03-18 22:24:06 -04:00
|
|
|
|
|
2018-04-05 00:38:45 -04:00
|
|
|
|
if (currentChestAbdomenRatio > chestAbdomenRatioLerpValueForFlag)
|
|
|
|
|
{
|
2018-03-18 22:24:06 -04:00
|
|
|
|
|
2018-04-05 00:38:45 -04:00
|
|
|
|
if (currentChestAbdomenRatio > chestAbdomenLerpValueForBan)
|
|
|
|
|
{
|
|
|
|
|
Log.WriteDebug("**Maximum Lifetime Chest/Abdomen Ratio Reached For Ban**");
|
|
|
|
|
Log.WriteDebug($"ClientId: {stats.ClientId}");
|
2018-05-11 00:52:20 -04:00
|
|
|
|
Log.WriteDebug($"**Total Chest Hits: {totalChestHits}");
|
2018-04-05 00:38:45 -04:00
|
|
|
|
Log.WriteDebug($"**Ratio {currentChestAbdomenRatio}");
|
|
|
|
|
Log.WriteDebug($"**MaxRatio {chestAbdomenLerpValueForBan}");
|
|
|
|
|
var sb = new StringBuilder();
|
|
|
|
|
foreach (var location in stats.HitLocations)
|
|
|
|
|
sb.Append($"HitLocation: {location.Location} -> {location.HitCount}\r\n");
|
|
|
|
|
Log.WriteDebug(sb.ToString());
|
2018-03-26 00:51:25 -04:00
|
|
|
|
|
2018-04-05 00:38:45 -04:00
|
|
|
|
return new DetectionPenaltyResult()
|
|
|
|
|
{
|
|
|
|
|
ClientPenalty = Penalty.PenaltyType.Ban,
|
2018-05-11 00:52:20 -04:00
|
|
|
|
Value = currentChestAbdomenRatio,
|
|
|
|
|
Location = IW4Info.HitLocation.torso_upper,
|
|
|
|
|
HitCount = totalChestHits,
|
|
|
|
|
Type = DetectionType.Chest
|
2018-04-05 00:38:45 -04:00
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Log.WriteDebug("**Maximum Lifetime Chest/Abdomen Ratio Reached For Flag**");
|
|
|
|
|
Log.WriteDebug($"ClientId: {stats.ClientId}");
|
2018-05-11 00:52:20 -04:00
|
|
|
|
Log.WriteDebug($"**Total Chest Hits: {totalChestHits}");
|
2018-04-05 00:38:45 -04:00
|
|
|
|
Log.WriteDebug($"**Ratio {currentChestAbdomenRatio}");
|
|
|
|
|
Log.WriteDebug($"**MaxRatio {chestAbdomenRatioLerpValueForFlag}");
|
|
|
|
|
var sb = new StringBuilder();
|
|
|
|
|
foreach (var location in stats.HitLocations)
|
|
|
|
|
sb.Append($"HitLocation: {location.Location} -> {location.HitCount}\r\n");
|
|
|
|
|
Log.WriteDebug(sb.ToString());
|
|
|
|
|
|
|
|
|
|
return new DetectionPenaltyResult()
|
|
|
|
|
{
|
|
|
|
|
ClientPenalty = Penalty.PenaltyType.Flag,
|
2018-05-11 00:52:20 -04:00
|
|
|
|
Value = currentChestAbdomenRatio,
|
|
|
|
|
Location = IW4Info.HitLocation.torso_upper,
|
|
|
|
|
HitCount = totalChestHits,
|
|
|
|
|
Type = DetectionType.Chest
|
2018-04-05 00:38:45 -04:00
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-03-18 22:24:06 -04:00
|
|
|
|
|
|
|
|
|
return new DetectionPenaltyResult()
|
|
|
|
|
{
|
2018-04-05 00:38:45 -04:00
|
|
|
|
ClientPenalty = Penalty.PenaltyType.Any
|
2018-03-18 22:24:06 -04:00
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|