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:
@ -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,
|
||||
};
|
||||
}
|
||||
|
@ -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>()
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
};
|
||||
|
Reference in New Issue
Block a user