mirror of
https://github.com/RaidMax/IW4M-Admin.git
synced 2025-06-10 23:31:13 -05:00
Fixed non player killstreak kills counting as suicide
(custom callbacks) RCON tweaks to hopefully prevent RCON flooding Stats reimp added IW4x extra weapons
This commit is contained in:
@ -102,10 +102,15 @@ namespace StatsPlugin.Helpers
|
||||
clientStats = ClientStatSvc.Insert(clientStats);
|
||||
}
|
||||
|
||||
else
|
||||
|
||||
lock (playerStats)
|
||||
{
|
||||
if (playerStats.ContainsKey(pl.ClientNumber))
|
||||
{
|
||||
Log.WriteWarning($"Duplicate clientnumber in stats {pl.ClientId} vs {playerStats[pl.ClientNumber].ClientId}");
|
||||
playerStats.Remove(pl.ClientNumber);
|
||||
}
|
||||
playerStats.Add(pl.ClientNumber, clientStats);
|
||||
}
|
||||
return clientStats;
|
||||
}
|
||||
|
||||
@ -113,29 +118,32 @@ namespace StatsPlugin.Helpers
|
||||
{
|
||||
int serverId = pl.CurrentServer.GetHashCode();
|
||||
var playerStats = Servers[serverId].PlayerStats;
|
||||
|
||||
// get individual client's stats
|
||||
var clientStats = playerStats[pl.ClientNumber];
|
||||
// remove the client from the stats dictionary as they're leaving
|
||||
lock (playerStats)
|
||||
playerStats.Remove(pl.ClientNumber);
|
||||
|
||||
// allow accessing certain properties
|
||||
//clientStats.Client = pl;
|
||||
// update skill
|
||||
// clientStats = UpdateStats(clientStats);
|
||||
// reset for EF cache
|
||||
//clientStats.SessionDeaths = 0;
|
||||
// clientStats.SessionKills = 0;
|
||||
// prevent mismatched primary key
|
||||
//clientStats.Client = null;
|
||||
// update in database
|
||||
await ClientStatSvc.SaveChangesAsync();
|
||||
//await ClientStatSvc.SaveChangesAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process stats for kill event
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task AddKill(Player attacker, Player victim, int serverId, string map, string hitLoc, string type,
|
||||
public async Task AddScriptKill(Player attacker, Player victim, int serverId, string map, string hitLoc, string type,
|
||||
string damage, string weapon, string killOrigin, string deathOrigin)
|
||||
{
|
||||
var attackerStats = Servers[serverId].PlayerStats[attacker.ClientNumber];
|
||||
attackerStats.Kills += 1;
|
||||
|
||||
var victimStats = Servers[serverId].PlayerStats[victim.ClientNumber];
|
||||
victimStats.Deaths += 1;
|
||||
AddStandardKill(attacker, victim);
|
||||
|
||||
var kill = new EFClientKill()
|
||||
{
|
||||
@ -156,10 +164,88 @@ namespace StatsPlugin.Helpers
|
||||
await KillSvc.SaveChangesAsync();
|
||||
}
|
||||
|
||||
private EFClientStatistics UpdateStats(EFClientStatistics cs)
|
||||
public void AddStandardKill(Player attacker, Player victim)
|
||||
{
|
||||
// todo: everything
|
||||
return cs;
|
||||
var attackerStats = Servers[attacker.CurrentServer.GetHashCode()].PlayerStats[attacker.ClientNumber];
|
||||
// set to access total time
|
||||
attackerStats.Client = attacker;
|
||||
var victimStats = Servers[victim.CurrentServer.GetHashCode()].PlayerStats[victim.ClientNumber];
|
||||
|
||||
CalculateKill(attackerStats, victimStats);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs the incrementation of kills and deaths for client statistics
|
||||
/// </summary>
|
||||
/// <param name="attackerStats">Stats of the attacker</param>
|
||||
/// <param name="victimStats">Stats of the victim</param>
|
||||
public void CalculateKill(EFClientStatistics attackerStats, EFClientStatistics victimStats)
|
||||
{
|
||||
attackerStats.Kills += 1;
|
||||
attackerStats.SessionKills += 1;
|
||||
attackerStats.KillStreak += 1;
|
||||
attackerStats.DeathStreak = 0;
|
||||
|
||||
victimStats.Deaths += 1;
|
||||
victimStats.SessionDeaths += 1;
|
||||
victimStats.DeathStreak += 1;
|
||||
victimStats.KillStreak = 0;
|
||||
|
||||
// process the attacker's stats after the kills
|
||||
UpdateStats(attackerStats);
|
||||
attackerStats.Client = null;
|
||||
|
||||
// immediately write changes in debug
|
||||
#if DEBUG
|
||||
ClientStatSvc.SaveChanges();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the client stats (skill etc)
|
||||
/// </summary>
|
||||
/// <param name="clientStats">Client statistics</param>
|
||||
/// <returns></returns>
|
||||
private EFClientStatistics UpdateStats(EFClientStatistics clientStats)
|
||||
{
|
||||
// if it's their first kill we need to set the last kill as the time they joined
|
||||
clientStats.LastStatCalculation = (clientStats.LastStatCalculation == DateTime.MinValue) ? DateTime.UtcNow : clientStats.LastStatCalculation;
|
||||
double timeSinceLastCalc = (DateTime.UtcNow - clientStats.LastStatCalculation).TotalSeconds / 60.0;
|
||||
|
||||
// each 'session' is one minute
|
||||
if (timeSinceLastCalc >= 1)
|
||||
{
|
||||
Log.WriteDebug($"Updated stats for {clientStats.ClientId} ({clientStats.SessionKills})");
|
||||
// calculate the players Score Per Minute for the current session
|
||||
// todo: score should be based on gamemode
|
||||
double killSPM = clientStats.SessionKills * 100.0;
|
||||
|
||||
// calculate how much the KDR should weigh
|
||||
// 1.637 is a Eddie-Generated number that weights the KDR nicely
|
||||
double KDRWeight = Math.Round(Math.Pow(clientStats.KDR, 1.637 / Math.E), 3);
|
||||
|
||||
// if no SPM, weight is 1 else the weight ishe current session's spm / lifetime average score per minute
|
||||
double SPMWeightAgainstAverage = (clientStats.SPM < 1) ? 1 : killSPM / clientStats.SPM;
|
||||
|
||||
// calculate the weight of the new play time against last 10 hours of gameplay
|
||||
int totalConnectionTime = (clientStats.Client.TotalConnectionTime == 0) ?
|
||||
(int)(DateTime.UtcNow - clientStats.Client.FirstConnection).TotalSeconds :
|
||||
clientStats.Client.TotalConnectionTime + (int)(DateTime.UtcNow - clientStats.Client.LastConnection).TotalSeconds;
|
||||
|
||||
double SPMAgainstPlayWeight = timeSinceLastCalc / Math.Min(600, (totalConnectionTime / 60.0));
|
||||
|
||||
// calculate the new weight against average times the weight against play time
|
||||
clientStats.SPM = (killSPM * SPMAgainstPlayWeight) + (clientStats.SPM * (1 - SPMAgainstPlayWeight));
|
||||
clientStats.SPM = Math.Round(clientStats.SPM, 3);
|
||||
clientStats.Skill = Math.Round((clientStats.SPM * KDRWeight) / 10.0, 3);
|
||||
|
||||
clientStats.SessionKills = 0;
|
||||
clientStats.SessionDeaths = 0;
|
||||
|
||||
clientStats.LastStatCalculation = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
return clientStats;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1225,7 +1225,118 @@ namespace StatsPlugin
|
||||
nuke_mp = 1190,
|
||||
barrel_mp = 1191,
|
||||
lightstick_mp = 1192,
|
||||
throwingknife_rhand_mp = 1193
|
||||
throwingknife_rhand_mp = 1193,
|
||||
deserteaglegold_akimbo_mp,
|
||||
deserteaglegold_fmj_mp,
|
||||
deserteaglegold_tactical_mp,
|
||||
deserteaglegold_akimbo_fmj_mp,
|
||||
deserteaglegold_fmj_tactical_mp,
|
||||
ak47classic_mp,
|
||||
ak47classic_acog_mp,
|
||||
ak47classic_eotech_mp,
|
||||
ak47classic_fmj_mp,
|
||||
ak47classic_gl_mp,
|
||||
gl_ak47classic_mp,
|
||||
ak47classic_heartbeat_mp,
|
||||
ak47classic_reflex_mp,
|
||||
ak47classic_shotgun_mp,
|
||||
ak47classic_shotgun_attach_mp,
|
||||
ak47classic_silencer_mp,
|
||||
ak47classic_thermal_mp,
|
||||
ak47classic_xmags_mp,
|
||||
ak47classic_acog_fmj_mp,
|
||||
ak47classic_acog_gl_mp,
|
||||
ak47classic_acog_heartbeat_mp,
|
||||
ak47classic_acog_shotgun_mp,
|
||||
ak47classic_acog_silencer_mp,
|
||||
ak47classic_acog_xmags_mp,
|
||||
ak47classic_eotech_fmj_mp,
|
||||
ak47classic_eotech_gl_mp,
|
||||
ak47classic_eotech_heartbeat_mp,
|
||||
ak47classic_eotech_shotgun_mp,
|
||||
ak47classic_eotech_silencer_mp,
|
||||
ak47classic_eotech_xmags_mp,
|
||||
ak47classic_fmj_gl_mp,
|
||||
ak47classic_fmj_heartbeat_mp,
|
||||
ak47classic_fmj_reflex_mp,
|
||||
ak47classic_fmj_shotgun_mp,
|
||||
ak47classic_fmj_silencer_mp,
|
||||
ak47classic_fmj_thermal_mp,
|
||||
ak47classic_fmj_xmags_mp,
|
||||
ak47classic_gl_heartbeat_mp,
|
||||
ak47classic_gl_reflex_mp,
|
||||
ak47classic_gl_silencer_mp,
|
||||
ak47classic_gl_thermal_mp,
|
||||
ak47classic_gl_xmags_mp,
|
||||
ak47classic_heartbeat_reflex_mp,
|
||||
ak47classic_heartbeat_shotgun_mp,
|
||||
ak47classic_heartbeat_silencer_mp,
|
||||
ak47classic_heartbeat_thermal_mp,
|
||||
ak47classic_heartbeat_xmags_mp,
|
||||
ak47classic_reflex_shotgun_mp,
|
||||
ak47classic_reflex_silencer_mp,
|
||||
ak47classic_reflex_xmags_mp,
|
||||
ak47classic_shotgun_silencer_mp,
|
||||
ak47classic_shotgun_thermal_mp,
|
||||
ak47classic_shotgun_xmags_mp,
|
||||
ak47classic_silencer_thermal_mp,
|
||||
ak47classic_silencer_xmags_mp,
|
||||
ak47classic_thermal_xmags_mp,
|
||||
ak74u_mp,
|
||||
ak74u_acog_mp,
|
||||
ak74u_eotech_mp,
|
||||
ak74u_fmj_mp,
|
||||
ak74u_gl_mp,
|
||||
gl_ak74u_mp,
|
||||
ak74u_heartbeat_mp,
|
||||
ak74u_reflex_mp,
|
||||
ak74u_shotgun_mp,
|
||||
ak74u_shotgun_attach_mp,
|
||||
ak74u_silencer_mp,
|
||||
ak74u_thermal_mp,
|
||||
ak74u_xmags_mp,
|
||||
ak74u_acog_fmj_mp,
|
||||
ak74u_acog_gl_mp,
|
||||
ak74u_acog_heartbeat_mp,
|
||||
ak74u_acog_shotgun_mp,
|
||||
ak74u_acog_silencer_mp,
|
||||
ak74u_acog_xmags_mp,
|
||||
ak74u_eotech_fmj_mp,
|
||||
ak74u_eotech_gl_mp,
|
||||
ak74u_eotech_heartbeat_mp,
|
||||
ak74u_eotech_shotgun_mp,
|
||||
ak74u_eotech_silencer_mp,
|
||||
ak74u_eotech_xmags_mp,
|
||||
ak74u_fmj_gl_mp,
|
||||
ak74u_fmj_heartbeat_mp,
|
||||
ak74u_fmj_reflex_mp,
|
||||
ak74u_fmj_shotgun_mp,
|
||||
ak74u_fmj_silencer_mp,
|
||||
ak74u_fmj_thermal_mp,
|
||||
ak74u_fmj_xmags_mp,
|
||||
ak74u_gl_heartbeat_mp,
|
||||
ak74u_gl_reflex_mp,
|
||||
ak74u_gl_silencer_mp,
|
||||
ak74u_gl_thermal_mp,
|
||||
ak74u_gl_xmags_mp,
|
||||
ak74u_heartbeat_reflex_mp,
|
||||
ak74u_heartbeat_shotgun_mp,
|
||||
ak74u_heartbeat_silencer_mp,
|
||||
ak74u_heartbeat_thermal_mp,
|
||||
ak74u_heartbeat_xmags_mp,
|
||||
ak74u_reflex_shotgun_mp,
|
||||
ak74u_reflex_silencer_mp,
|
||||
ak74u_reflex_xmags_mp,
|
||||
ak74u_shotgun_silencer_mp,
|
||||
ak74u_shotgun_thermal_mp,
|
||||
ak74u_shotgun_xmags_mp,
|
||||
ak74u_silencer_thermal_mp,
|
||||
ak74u_silencer_xmags_mp,
|
||||
ak74u_thermal_xmags_mp,
|
||||
m40a3_mp = 1194,
|
||||
peacekeeper_mp,
|
||||
dragunov_mp,
|
||||
cobra_player_minigun_mp
|
||||
}
|
||||
|
||||
public enum MapName
|
||||
|
@ -28,11 +28,22 @@ namespace StatsPlugin.Models
|
||||
[NotMapped]
|
||||
public double KDR
|
||||
{
|
||||
get => Deaths == 0 ? 0.0 : Math.Round((float)Kills / (float)Deaths, 2);
|
||||
get => Deaths == 0 ? Kills : Math.Round((float)Kills / (float)Deaths, 2);
|
||||
}
|
||||
[Required]
|
||||
public double SPM { get; set; }
|
||||
[Required]
|
||||
public double Skill { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public int SessionKills { get; set; }
|
||||
[NotMapped]
|
||||
public int SessionDeaths { get; set; }
|
||||
[NotMapped]
|
||||
public int KillStreak { get; set; }
|
||||
[NotMapped]
|
||||
public int DeathStreak { get; set; }
|
||||
[NotMapped]
|
||||
public DateTime LastStatCalculation { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ namespace StatsPlugin
|
||||
case Event.GType.Kill:
|
||||
string[] killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0];
|
||||
if (killInfo.Length >= 9 && killInfo[0].Contains("ScriptKill"))
|
||||
await Manager.AddKill(E.Origin, E.Target, S.GetHashCode(), S.CurrentMap.Name, killInfo[7], killInfo[8], killInfo[5], killInfo[6], killInfo[3], killInfo[4]);
|
||||
await Manager.AddScriptKill(E.Origin, E.Target, S.GetHashCode(), S.CurrentMap.Name, killInfo[7], killInfo[8], killInfo[5], killInfo[6], killInfo[3], killInfo[4]);
|
||||
break;
|
||||
case Event.GType.Death:
|
||||
break;
|
||||
|
Reference in New Issue
Block a user