1
0
mirror of https://github.com/RaidMax/IW4M-Admin.git synced 2025-07-05 11:28:54 -05:00

SPM fix for negative/teamdamage

added localization as downloaded from the Master API
interupted network communication no longer treated as unknown exception
topstats prints the right message if no one qualifies
angle adjustments
move unflag to seperate command
This commit is contained in:
RaidMax
2018-05-07 23:58:46 -05:00
parent f4e8a960be
commit 5d0d57bbd2
36 changed files with 1313 additions and 1120 deletions

View File

@ -16,6 +16,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
int AboveThresholdCount;
double AverageKillTime;
Dictionary<IW4Info.HitLocation, int> HitLocationCount;
double AngleDifferenceAverage;
EFClientStatistics ClientStats;
DateTime LastKill;
long LastOffset;
@ -32,6 +33,11 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
Strain = new Strain();
}
public void ProcessScriptDamage(string damageLine)
{
}
public void ProcessDamage(string damageLine)
{
string regex = @"^(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]|_)+)$";
@ -57,7 +63,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
/// </summary>
/// <param name="kill">kill performed by the player</param>
/// <returns>true if detection reached thresholds, false otherwise</returns>
public DetectionPenaltyResult ProcessKill(EFClientKill kill)
public DetectionPenaltyResult ProcessKill(EFClientKill kill, bool isDamage)
{
if ((kill.DeathType != IW4Info.MeansOfDeath.MOD_PISTOL_BULLET &&
kill.DeathType != IW4Info.MeansOfDeath.MOD_RIFLE_BULLET &&
@ -72,59 +78,49 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
LastKill = DateTime.UtcNow;
HitLocationCount[kill.HitLoc]++;
Kills++;
AverageKillTime = (AverageKillTime + (DateTime.UtcNow - LastKill).TotalSeconds) / Kills;
if (!isDamage)
{
Kills++;
AverageKillTime = (AverageKillTime + (DateTime.UtcNow - LastKill).TotalSeconds) / Kills;
}
#region VIEWANGLES
// make sure it's divisible by 2
if (kill.AnglesList.Count % 2 == 0)
if (kill.AnglesList.Count >= 2)
{
/*
double maxDistance = 0;
for (int i = 0; i < kill.AnglesList.Count - 1; i += 1)
{
// Log.WriteDebug($"Fixed 1 {kill.AnglesList[i]}");
// Log.WriteDebug($"Fixed 2 {kill.AnglesList[i + 1]}");
// fix max distance
double currDistance = Vector3.AbsoluteDistance(kill.AnglesList[i], kill.AnglesList[i + 1]);
//Log.WriteDebug($"Distance {currDistance}");
if (currDistance > maxDistance)
{
maxDistance = currDistance;
}
if (maxDistance > hitLoc.MaxAngleDistance)
hitLoc.MaxAngleDistance = (float)maxDistance;
}*/
double realAgainstPredict = Vector3.AbsoluteDistance(kill.ViewAngles, kill.AnglesList[10]);
double realAgainstPredict = Math.Abs(Vector3.AbsoluteDistance(kill.AnglesList[0], kill.AnglesList[1]) -
(Vector3.AbsoluteDistance(kill.AnglesList[0], kill.ViewAngles) +
Vector3.AbsoluteDistance(kill.AnglesList[1], kill.ViewAngles)));
// LIFETIME
var hitLoc = ClientStats.HitLocations
.First(hl => hl.Location == kill.HitLoc);
float previousAverage = hitLoc.HitOffsetAverage;
double newAverage = (previousAverage * (hitLoc.HitCount - 1) + realAgainstPredict) / hitLoc.HitCount;
hitLoc.HitOffsetAverage = (float)newAverage;
if (double.IsNaN(hitLoc.HitOffsetAverage))
{
Log.WriteWarning("[Detection::ProcessKill] HitOffsetAvgerage NaN");
Log.WriteDebug($"{previousAverage}-{hitLoc.HitCount}-{hitLoc}-{newAverage}");
hitLoc.HitOffsetAverage = 0f;
}
var hitlocations = ClientStats.HitLocations
.Where(hl => new List<int>() { 4, 5, 2, 3, }.Contains((int)hl.Location))
.Where(hl => ClientStats.SessionKills > Thresholds.MediumSampleMinKills + 30);
var validOffsets = ClientStats.HitLocations.Where(hl => hl.HitCount > 0);
double hitOffsetAverage = validOffsets.Sum(o => o.HitCount * o.HitOffsetAverage) / (double)validOffsets.Sum(o => o.HitCount);
if (hitOffsetAverage > Thresholds.MaxOffset)
if (hitLoc.HitOffsetAverage > Thresholds.MaxOffset)
{
return new DetectionPenaltyResult()
{
ClientPenalty = Penalty.PenaltyType.Ban,
RatioAmount = hitOffsetAverage,
RatioAmount = hitLoc.HitOffsetAverage,
KillCount = ClientStats.SessionKills,
};
}
// SESSION
int sessHitLocCount = HitLocationCount[kill.HitLoc];
double sessAverage = (AngleDifferenceAverage * (sessHitLocCount - 1)) + realAgainstPredict / sessHitLocCount;
AngleDifferenceAverage = sessAverage;
if (sessAverage > Thresholds.MaxOffset)
{
return new DetectionPenaltyResult()
{
ClientPenalty = Penalty.PenaltyType.Ban,
RatioAmount = sessHitLocCount,
KillCount = ClientStats.SessionKills,
};
}

View File

@ -40,14 +40,14 @@ namespace IW4MAdmin.Plugins.Stats.Commands
where client.Level != Player.Permission.Banned
where client.LastConnection >= thirtyDaysAgo
orderby stats.Skill descending
select $"^3{client.Name}^7 - ^5{stats.KDR} ^7KDR | ^5{stats.Skill} ^7{Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_TEXT_SKILL"]}")
select $"^3{alias.Name}^7 - ^5{stats.KDR} ^7KDR | ^5{stats.Skill} ^7{Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_TEXT_SKILL"]}")
.Take(5);
topStatsText.AddRange(await iqStats.ToListAsync());
}
// no one qualified
if (topStatsText.Count == 0)
if (topStatsText.Count == 1)
{
topStatsText = new List<string>()
{

View File

@ -1,10 +1,6 @@
using SharedLibraryCore.Configuration;
using SharedLibraryCore;
using SharedLibraryCore.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IW4MAdmin.Plugins.Stats.Config
{
@ -16,12 +12,8 @@ namespace IW4MAdmin.Plugins.Stats.Config
public string Name() => "Stats";
public IBaseConfiguration Generate()
{
var config = new StatsConfiguration();
Console.Write("Enable server-side anti-cheat? [y/n]: ");
config.EnableAntiCheat = (Console.ReadLine().ToLower().FirstOrDefault() as char?) == 'y';
config.KillstreakMessages = new List<StreakMessageConfiguration>()
EnableAntiCheat = Utilities.PromptBool(Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_STATS_SETUP_ENABLEAC"]);
KillstreakMessages = new List<StreakMessageConfiguration>()
{
new StreakMessageConfiguration(){
Count = -1,
@ -42,7 +34,7 @@ namespace IW4MAdmin.Plugins.Stats.Config
}
};
config.DeathstreakMessages = new List<StreakMessageConfiguration>()
DeathstreakMessages = new List<StreakMessageConfiguration>()
{
new StreakMessageConfiguration()
{
@ -55,7 +47,7 @@ namespace IW4MAdmin.Plugins.Stats.Config
},
};
return config;
return this;
}
}
}

View File

@ -73,7 +73,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
catch (Exception e)
{
Log.WriteError($"Could not add server to ServerStats - {e.Message}");
Log.WriteError($"{Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_STATS_ERROR_ADD"]} - {e.Message}");
}
}
@ -217,7 +217,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
if (Plugin.Config.Configuration().EnableAntiCheat)
{
var clientDetection = Servers[serverId].PlayerDetections[clientId];
clientDetection.ProcessDamage(eventLine);
clientDetection.ProcessScriptDamage(eventLine);
}
}
@ -225,7 +225,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
/// Process stats for kill event
/// </summary>
/// <returns></returns>
public async Task AddScriptKill(DateTime time, Player attacker, Player victim, int serverId, string map, string hitLoc, string type,
public async Task AddScriptKill(bool isDamage, DateTime time, Player attacker, Player victim, int serverId, string map, string hitLoc, string type,
string damage, string weapon, string killOrigin, string deathOrigin, string viewAngles, string offset, string isKillstreakKill, string Ads, string snapAngles)
{
var statsSvc = ContextThreads[serverId];
@ -252,18 +252,18 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
try
{
foreach(string angle in snapAngles.Split(':', StringSplitOptions.RemoveEmptyEntries))
foreach (string angle in snapAngles.Split(':', StringSplitOptions.RemoveEmptyEntries))
{
snapshotAngles.Add(Vector3.Parse(angle).FixIW4Angles());
}
}
catch (FormatException)
{
Log.WriteWarning("Could not parse snapshot angles");
return;
}
var kill = new EFClientKill()
{
Active = true,
@ -292,7 +292,10 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
return;
}
await AddStandardKill(attacker, victim);
if (!isDamage)
{
await AddStandardKill(attacker, victim);
}
if (kill.IsKillstreakKill)
{
@ -310,7 +313,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
clientStats.HitLocations.Single(hl => hl.Location == kill.HitLoc).HitCount += 1;
statsSvc.ClientStatSvc.Update(clientStats);
// await statsSvc.ClientStatSvc.SaveChangesAsync();
// await statsSvc.ClientStatSvc.SaveChangesAsync();
}
//statsSvc.KillStatsSvc.Insert(kill);
@ -320,10 +323,6 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
{
async Task executePenalty(Cheat.DetectionPenaltyResult penalty)
{
#if DEBUG
Log.WriteVerbose("Player Banned");
return;
#endif
// prevent multiple bans from occuring
if (attacker.Level == Player.Permission.Banned)
{
@ -333,7 +332,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
switch (penalty.ClientPenalty)
{
case Penalty.PenaltyType.Ban:
await attacker.Ban("You appear to be cheating", new Player() { ClientId = 1 });
await attacker.Ban(Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_STATS_CHEAT_DETECTED"], new Player() { ClientId = 1 });
break;
case Penalty.PenaltyType.Flag:
if (attacker.Level != Player.Permission.User)
@ -350,7 +349,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
}
}
await executePenalty(clientDetection.ProcessKill(kill));
await executePenalty(clientDetection.ProcessKill(kill, isDamage));
await executePenalty(clientDetection.ProcessTotalRatio(clientStats));
#if DEBUG
@ -495,12 +494,16 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
double timeSinceLastCalc = (DateTime.UtcNow - clientStats.LastStatCalculation).TotalSeconds / 60.0;
double timeSinceLastActive = (DateTime.UtcNow - clientStats.LastActive).TotalSeconds / 60.0;
// calculate the players Score Per Minute for the current session
int scoreDifference = clientStats.RoundScore - clientStats.LastScore;
// todo: fix the SPM for TEAMDAMAGE
if (scoreDifference < 0)
scoreDifference = clientStats.RoundScore;
int scoreDifference = 0;
// this means they've been tking or suicide and is the only time they can have a negative SPM
if (clientStats.RoundScore < 0)
{
scoreDifference = clientStats.RoundScore + clientStats.LastScore;
}
else
{
scoreDifference = clientStats.RoundScore - clientStats.LastScore;
}
double killSPM = scoreDifference / timeSinceLastCalc;
@ -564,7 +567,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
var ieClientStats = statsSvc.ClientStatSvc.Find(cs => cs.ServerId == serverId);
// set these incase they've we've imported settings
// set these incase we've imported settings
serverStats.TotalKills = ieClientStats.Sum(cs => cs.Kills);
serverStats.TotalPlayTime = Manager.GetClientService().GetTotalPlayTime().Result;
@ -612,16 +615,10 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
int serverId = sv.GetHashCode();
var statsSvc = ContextThreads[serverId];
Log.WriteDebug("Syncing server stats");
Log.WriteDebug("Syncing stats contexts");
await statsSvc.ServerStatsSvc.SaveChangesAsync();
Log.WriteDebug("Syncing client stats");
await statsSvc.ClientStatSvc.SaveChangesAsync();
Log.WriteDebug("Syncing kill stats");
await statsSvc.KillStatsSvc.SaveChangesAsync();
Log.WriteDebug("Syncing servers");
await statsSvc.ServerSvc.SaveChangesAsync();
statsSvc = null;

View File

@ -74,15 +74,19 @@ namespace IW4MAdmin.Plugins.Stats
case GameEvent.EventType.Kill:
string[] killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0];
if (killInfo.Length >= 9 && killInfo[0].Contains("ScriptKill") && E.Owner.CustomCallback)
await Manager.AddScriptKill(E.Time, E.Origin, E.Target, S.GetHashCode(), S.CurrentMap.Name, killInfo[7], killInfo[8],
await Manager.AddScriptKill(false, E.Time, E.Origin, E.Target, S.GetHashCode(), S.CurrentMap.Name, killInfo[7], killInfo[8],
killInfo[5], killInfo[6], killInfo[3], killInfo[4], killInfo[9], killInfo[10], killInfo[11], killInfo[12], killInfo[13]);
else if (!E.Owner.CustomCallback)
await Manager.AddStandardKill(E.Origin, E.Target);
break;
case GameEvent.EventType.Death:
break;
case GameEvent.EventType.Damage:
Manager.AddDamageEvent(E.Data, E.Origin.ClientId, E.Owner.GetHashCode());
//case GameEvent.EventType.Damage:
case GameEvent.EventType.ScriptDamage:
killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0];
if (killInfo.Length >= 9 && E.Owner.CustomCallback)
await Manager.AddScriptKill(true, E.Time, E.Origin, E.Target, S.GetHashCode(), S.CurrentMap.Name, killInfo[7], killInfo[8],
killInfo[5], killInfo[6], killInfo[3], killInfo[4], killInfo[9], killInfo[10], killInfo[11], killInfo[12], killInfo[13]);
break;
}
}
@ -113,27 +117,27 @@ namespace IW4MAdmin.Plugins.Stats
{
new ProfileMeta()
{
Key = "Kills",
Key = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_TEXT_KILLS"],
Value = kills
},
new ProfileMeta()
{
Key = "Deaths",
Key = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_TEXT_DEATHS"],
Value = deaths
},
new ProfileMeta()
{
Key = "KDR",
Key = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_TEXT_KDR"],
Value = kdr
},
new ProfileMeta()
{
Key = "Skill",
Key = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_TEXT_SKILL"],
Value = skill
},
new ProfileMeta()
{
Key = "Score Per Minute",
Key = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_META_SPM"],
Value = spm
}
};