diff --git a/Application/Application.csproj b/Application/Application.csproj
index 960042f2..370817d2 100644
--- a/Application/Application.csproj
+++ b/Application/Application.csproj
@@ -27,9 +27,9 @@
-
+
diff --git a/Application/Manager.cs b/Application/Manager.cs
index 26ded3da..894fbfee 100644
--- a/Application/Manager.cs
+++ b/Application/Manager.cs
@@ -332,7 +332,7 @@ namespace IW4MAdmin.Application
#endregion
}
- private void SendHeartbeat(object state)
+ private async Task SendHeartbeat(object state)
{
var heartbeatState = (HeartbeatState)state;
@@ -342,7 +342,7 @@ namespace IW4MAdmin.Application
{
try
{
- Heartbeat.Send(this, true).Wait(5000);
+ await Heartbeat.Send(this, true);
heartbeatState.Connected = true;
}
@@ -357,7 +357,7 @@ namespace IW4MAdmin.Application
{
try
{
- Heartbeat.Send(this).Wait(5000);
+ await Heartbeat.Send(this);
}
catch (System.Net.Http.HttpRequestException e)
@@ -393,7 +393,7 @@ namespace IW4MAdmin.Application
}
}
- Task.Delay(30000).Wait();
+ await Task.Delay(30000);
}
}
@@ -447,16 +447,16 @@ namespace IW4MAdmin.Application
while (Running)
{
// wait for new event to be added
- OnEvent.Wait(5000);
+ OnEvent.Wait();
// todo: sequencially or parallelize?
while ((queuedEvent = Handler.GetNextEvent()) != null)
{
- eventList.Add(processEvent(queuedEvent));
+ await processEvent(queuedEvent);
}
// this should allow parallel processing of events
- await Task.WhenAll(eventList);
+ // await Task.WhenAll(eventList);
// signal that all events have been processed
OnEvent.Reset();
diff --git a/Application/RconParsers/IW4RConParser.cs b/Application/RconParsers/IW4RConParser.cs
index 5791449c..028316b0 100644
--- a/Application/RconParsers/IW4RConParser.cs
+++ b/Application/RconParsers/IW4RConParser.cs
@@ -127,7 +127,7 @@ namespace Application.RconParsers
}
// this happens if status is requested while map is rotating
- if (Status[1] == "Rotating map...")
+ if (Status[1] == "Server Initialization")
{
throw new ServerException("Server is rotating map");
}
diff --git a/Plugins/Stats/Cheat/Detection.cs b/Plugins/Stats/Cheat/Detection.cs
index 96f7e5a2..aebc15d3 100644
--- a/Plugins/Stats/Cheat/Detection.cs
+++ b/Plugins/Stats/Cheat/Detection.cs
@@ -126,7 +126,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
#endif
}
double currentStrain = Strain.GetStrain(isDamage, kill.Damage, kill.ViewAngles, Math.Max(50, kill.TimeOffset - LastOffset));
- currentStrain *= ClientStats.SPM / 179.0;
+ double currentWeightedStrain = (currentStrain * ClientStats.SPM) / 170.0;
LastOffset = kill.TimeOffset;
if (currentStrain > ClientStats.MaxStrain)
@@ -134,7 +134,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
ClientStats.MaxStrain = currentStrain;
}
- if (currentStrain > Thresholds.MaxStrainFlag)
+ if (currentWeightedStrain > Thresholds.MaxStrainFlag)
{
Tracker.OnChange(Strain);
@@ -151,25 +151,25 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
}
// flag
- if (currentStrain > Thresholds.MaxStrainFlag)
+ if (currentWeightedStrain > Thresholds.MaxStrainFlag)
{
return new DetectionPenaltyResult()
{
ClientPenalty = Penalty.PenaltyType.Flag,
- Value = currentStrain,
+ Value = currentWeightedStrain,
HitCount = HitCount,
Type = DetectionType.Strain
};
}
// ban
- if (currentStrain > Thresholds.MaxStrainBan
+ if (currentWeightedStrain > Thresholds.MaxStrainBan
&& Kills > Thresholds.LowSampleMinKills)
{
return new DetectionPenaltyResult()
{
- ClientPenalty = Penalty.PenaltyType.Flag,
- Value = currentStrain,
+ ClientPenalty = Penalty.PenaltyType.Ban,
+ Value = currentWeightedStrain,
HitCount = HitCount,
Type = DetectionType.Strain
};
diff --git a/Plugins/Stats/Commands/ResetStats.cs b/Plugins/Stats/Commands/ResetStats.cs
index 8135ffc6..ce3e142d 100644
--- a/Plugins/Stats/Commands/ResetStats.cs
+++ b/Plugins/Stats/Commands/ResetStats.cs
@@ -26,6 +26,8 @@ namespace IW4MAdmin.Plugins.Stats.Commands
stats.SPM = 0.0;
stats.Skill = 0.0;
stats.TimePlayed = 0;
+ // todo: make this more dynamic
+ stats.EloRating = 200.0;
// reset the cached version
Plugin.Manager.ResetStats(E.Origin.ClientId, E.Owner.GetHashCode());
diff --git a/Plugins/Stats/Commands/TopStats.cs b/Plugins/Stats/Commands/TopStats.cs
index 719866a6..78582065 100644
--- a/Plugins/Stats/Commands/TopStats.cs
+++ b/Plugins/Stats/Commands/TopStats.cs
@@ -39,18 +39,17 @@ namespace IW4MAdmin.Plugins.Stats.Commands
where stats.TimePlayed >= 3600
where client.Level != Player.Permission.Banned
where client.LastConnection >= thirtyDaysAgo
- orderby stats.Skill descending
+ orderby stats.Performance descending
select new
{
stats.KDR,
- stats.Skill,
- stats.EloRating,
+ stats.Performance,
alias.Name
})
.Take(5);
var statsList = (await iqStats.ToListAsync())
- .Select(stats => $"^3{stats.Name}^7 - ^5{stats.KDR} ^7{Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_TEXT_KDR"]} | ^5{stats.Skill} ^7{Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_TEXT_SKILL"]} | ^5{stats.EloRating} ^7{Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_TOPSTATS_RATING"]}");
+ .Select(stats => $"^3{stats.Name}^7 - ^5{stats.KDR} ^7{Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_TEXT_KDR"]} | ^5{stats.Performance} ^7{Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_PERFORMANCE"]}");
topStatsText.AddRange(statsList);
}
diff --git a/Plugins/Stats/Commands/ViewStats.cs b/Plugins/Stats/Commands/ViewStats.cs
index 7a659ff2..8b37868a 100644
--- a/Plugins/Stats/Commands/ViewStats.cs
+++ b/Plugins/Stats/Commands/ViewStats.cs
@@ -26,7 +26,7 @@ namespace IW4MAdmin.Plugins.Stats.Commands
{
var loc = Utilities.CurrentLocalization.LocalizationIndex;
- if (E.Target?.ClientNumber < 0)
+ /*if (E.Target?.ClientNumber < 0)
{
await E.Origin.Tell(loc["PLUGINS_STATS_COMMANDS_VIEW_FAIL_INGAME"]);
return;
@@ -36,7 +36,7 @@ namespace IW4MAdmin.Plugins.Stats.Commands
{
await E.Origin.Tell(loc["PLUGINS_STATS_COMMANDS_VIEW_FAIL_INGAME_SELF"]);
return;
- }
+ }*/
String statLine;
EFClientStatistics pStats;
@@ -53,13 +53,13 @@ namespace IW4MAdmin.Plugins.Stats.Commands
if (E.Target != null)
{
pStats = clientStats.Find(c => c.ServerId == serverId && c.ClientId == E.Target.ClientId).First();
- statLine = $"^5{pStats.Kills} ^7{loc["PLUGINS_STATS_TEXT_KILLS"]} | ^5{pStats.Deaths} ^7{loc["PLUGINS_STATS_TEXT_DEATHS"]} | ^5{pStats.KDR} ^7KDR | ^5{pStats.Skill} ^7{loc["PLUGINS_STATS_TEXT_SKILL"]} | ^5{pStats.EloRating} ^7{loc["PLUGINS_STATS_COMMANDS_TOPSTATS_RATING"].ToUpper()}";
+ statLine = $"^5{pStats.Kills} ^7{loc["PLUGINS_STATS_TEXT_KILLS"]} | ^5{pStats.Deaths} ^7{loc["PLUGINS_STATS_TEXT_DEATHS"]} | ^5{pStats.KDR} ^7KDR | ^5{pStats.Performance} ^7{loc["PLUGINS_STATS_COMMANDS_PERFORMANCE"].ToUpper()}";
}
else
{
pStats = pStats = clientStats.Find(c => c.ServerId == serverId && c.ClientId == E.Origin.ClientId).First();
- statLine = $"^5{pStats.Kills} ^7{loc["PLUGINS_STATS_TEXT_KILLS"]} | ^5{pStats.Deaths} ^7{loc["PLUGINS_STATS_TEXT_DEATHS"]} | ^5{pStats.KDR} ^7KDR | ^5{pStats.Skill} ^7{loc["PLUGINS_STATS_TEXT_SKILL"]} | ^5{pStats.EloRating} ^7{loc["PLUGINS_STATS_COMMANDS_TOPSTATS_RATING"].ToUpper()}";
+ statLine = $"^5{pStats.Kills} ^7{loc["PLUGINS_STATS_TEXT_KILLS"]} | ^5{pStats.Deaths} ^7{loc["PLUGINS_STATS_TEXT_DEATHS"]} | ^5{pStats.KDR} ^7KDR | ^5{pStats.Performance} ^7{loc["PLUGINS_STATS_COMMANDS_PERFORMANCE"].ToUpper()}";
}
if (E.Message.IsBroadcastCommand())
diff --git a/Plugins/Stats/Helpers/StatManager.cs b/Plugins/Stats/Helpers/StatManager.cs
index 11d3826a..cdc16a5d 100644
--- a/Plugins/Stats/Helpers/StatManager.cs
+++ b/Plugins/Stats/Helpers/StatManager.cs
@@ -146,11 +146,17 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
//await statsSvc.ClientStatSvc.SaveChangesAsync();
}
+ // for stats before rating
if (clientStats.EloRating == 0.0)
{
clientStats.EloRating = clientStats.Skill;
}
+ if (clientStats.RollingWeightedKDR == 0)
+ {
+ clientStats.RollingWeightedKDR = clientStats.KDR;
+ }
+
// set these on connecting
clientStats.LastActive = DateTime.UtcNow;
clientStats.LastStatCalculation = DateTime.UtcNow;
@@ -497,10 +503,10 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
.Average(cs => cs.Value.EloRating);
double attackerEloDifference = Math.Log(attackerLobbyRating) - Math.Log(attackerStats.EloRating);
- double winPercentage = 1.0 / (1 + Math.Pow(10, attackerEloDifference / 3.0));
+ double winPercentage = 1.0 / (1 + Math.Pow(10, attackerEloDifference / 0.5));
double victimEloDifference = Math.Log(victimLobbyRating) - Math.Log(victimStats.EloRating);
- double lossPercentage = 1.0 / (1 + Math.Pow(10, victimEloDifference / 3.0));
+ double lossPercentage = 1.0 / (1 + Math.Pow(10, victimEloDifference / 0.5));
attackerStats.EloRating += 24.0 * (1 - winPercentage);
victimStats.EloRating -= 24.0 * winPercentage;
@@ -551,8 +557,10 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
// calculate how much the KDR should weigh
// 1.637 is a Eddie-Generated number that weights the KDR nicely
- double kdr = clientStats.Deaths == 0 ? clientStats.Kills : clientStats.KDR;
- double KDRWeight = Math.Round(Math.Pow(kdr, 1.637 / Math.E), 3);
+ double currentKDR = clientStats.SessionDeaths == 0 ? clientStats.SessionKills : clientStats.SessionKills / clientStats.SessionDeaths;
+ double alpha = Math.Sqrt(2) / Math.Min(600, clientStats.Kills + clientStats.Deaths);
+ clientStats.RollingWeightedKDR = (alpha * currentKDR) + (1.0 - alpha) * currentKDR;
+ double KDRWeight = Math.Round(Math.Pow(clientStats.RollingWeightedKDR, 1.637 / Math.E), 3);
// calculate the weight of the new play time against last 10 hours of gameplay
int totalPlayTime = (clientStats.TimePlayed == 0) ?
@@ -632,6 +640,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
stats.SPM = 0;
stats.Skill = 0;
stats.TimePlayed = 0;
+ stats.EloRating = 200;
}
public async Task AddMessageAsync(int clientId, int serverId, string message)
diff --git a/Plugins/Stats/Models/EFClientStatistics.cs b/Plugins/Stats/Models/EFClientStatistics.cs
index 72d32cc8..158e279f 100644
--- a/Plugins/Stats/Models/EFClientStatistics.cs
+++ b/Plugins/Stats/Models/EFClientStatistics.cs
@@ -24,6 +24,12 @@ namespace IW4MAdmin.Plugins.Stats.Models
public int Deaths { get; set; }
public double EloRating { get; set; }
public virtual ICollection HitLocations { get; set; }
+ public double RollingWeightedKDR { get; set; }
+ [NotMapped]
+ public double Performance
+ {
+ get => Math.Round((EloRating + Skill) / 2.0, 2);
+ }
[NotMapped]
public double KDR
{
@@ -57,6 +63,8 @@ namespace IW4MAdmin.Plugins.Stats.Models
public int LastScore { get; set; }
[NotMapped]
public DateTime LastActive { get; set; }
+ [NotMapped]
+ public double MaxSessionStrain { get; set; }
public void StartNewSession()
{
KillStreak = 0;
diff --git a/Plugins/Stats/Plugin.cs b/Plugins/Stats/Plugin.cs
index 44dc47ab..263983ee 100644
--- a/Plugins/Stats/Plugin.cs
+++ b/Plugins/Stats/Plugin.cs
@@ -114,9 +114,9 @@ namespace IW4MAdmin.Plugins.Stats
int kills = clientStats.Sum(c => c.Kills);
int deaths = clientStats.Sum(c => c.Deaths);
double kdr = Math.Round(kills / (double)deaths, 2);
- var validSkillValues = clientStats.Where(c => c.Skill > 0);
- int skillPlayTime = validSkillValues.Sum(s => s.TimePlayed);
- double skill = Math.Round(validSkillValues.Sum(c => c.Skill * c.TimePlayed / skillPlayTime), 2);
+ var validPerformanceValues = clientStats.Where(c => c.Performance > 0);
+ int performancePlayTime = validPerformanceValues.Sum(s => s.TimePlayed);
+ double performance = Math.Round(validPerformanceValues.Sum(c => c.Performance * c.TimePlayed / performancePlayTime), 2);
double spm = Math.Round(clientStats.Sum(c => c.SPM) / clientStats.Where(c => c.SPM > 0).Count(), 1);
return new List()
@@ -138,8 +138,8 @@ namespace IW4MAdmin.Plugins.Stats
},
new ProfileMeta()
{
- Key = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_TEXT_SKILL"],
- Value = skill
+ Key = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_PERFORMANCE"],
+ Value = performance
},
new ProfileMeta()
{
diff --git a/SharedLibraryCore/Migrations/20180517223349_AddRollingKDR.Designer.cs b/SharedLibraryCore/Migrations/20180517223349_AddRollingKDR.Designer.cs
new file mode 100644
index 00000000..c2def3fd
--- /dev/null
+++ b/SharedLibraryCore/Migrations/20180517223349_AddRollingKDR.Designer.cs
@@ -0,0 +1,438 @@
+//
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage;
+using Microsoft.EntityFrameworkCore.Storage.Internal;
+using SharedLibraryCore.Database;
+using SharedLibraryCore.Objects;
+using System;
+
+namespace SharedLibraryCore.Migrations
+{
+ [DbContext(typeof(DatabaseContext))]
+ [Migration("20180517223349_AddRollingKDR")]
+ partial class AddRollingKDR
+ {
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "2.0.2-rtm-10011");
+
+ modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientKill", b =>
+ {
+ b.Property("KillId")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Active");
+
+ b.Property("AttackerId");
+
+ b.Property("Damage");
+
+ b.Property("DeathOriginVector3Id");
+
+ b.Property("DeathType");
+
+ b.Property("HitLoc");
+
+ b.Property("KillOriginVector3Id");
+
+ b.Property("Map");
+
+ b.Property("ServerId");
+
+ b.Property("VictimId");
+
+ b.Property("ViewAnglesVector3Id");
+
+ b.Property("Weapon");
+
+ b.Property("When");
+
+ b.HasKey("KillId");
+
+ b.HasIndex("AttackerId");
+
+ b.HasIndex("DeathOriginVector3Id");
+
+ b.HasIndex("KillOriginVector3Id");
+
+ b.HasIndex("ServerId");
+
+ b.HasIndex("VictimId");
+
+ b.HasIndex("ViewAnglesVector3Id");
+
+ b.ToTable("EFClientKills");
+ });
+
+ modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientMessage", b =>
+ {
+ b.Property("MessageId")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Active");
+
+ b.Property("ClientId");
+
+ b.Property("Message");
+
+ b.Property("ServerId");
+
+ b.Property("TimeSent");
+
+ b.HasKey("MessageId");
+
+ b.HasIndex("ClientId");
+
+ b.HasIndex("ServerId");
+
+ b.ToTable("EFClientMessages");
+ });
+
+ modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", b =>
+ {
+ b.Property("ClientId");
+
+ b.Property("ServerId");
+
+ b.Property("Active");
+
+ b.Property("Deaths");
+
+ b.Property("EloRating");
+
+ b.Property("Kills");
+
+ b.Property("MaxStrain");
+
+ b.Property("RollingWeightedKDR");
+
+ b.Property("SPM");
+
+ b.Property("Skill");
+
+ b.Property("TimePlayed");
+
+ b.HasKey("ClientId", "ServerId");
+
+ b.HasIndex("ServerId");
+
+ b.ToTable("EFClientStatistics");
+ });
+
+ modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFHitLocationCount", b =>
+ {
+ b.Property("HitLocationCountId")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Active");
+
+ b.Property("ClientId")
+ .HasColumnName("EFClientStatistics_ClientId");
+
+ b.Property("HitCount");
+
+ b.Property("HitOffsetAverage");
+
+ b.Property("Location");
+
+ b.Property("MaxAngleDistance");
+
+ b.Property("ServerId")
+ .HasColumnName("EFClientStatistics_ServerId");
+
+ b.HasKey("HitLocationCountId");
+
+ b.HasIndex("ServerId");
+
+ b.HasIndex("ClientId", "ServerId");
+
+ b.ToTable("EFHitLocationCounts");
+ });
+
+ modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServer", b =>
+ {
+ b.Property("ServerId");
+
+ b.Property("Active");
+
+ b.Property("Port");
+
+ b.HasKey("ServerId");
+
+ b.ToTable("EFServers");
+ });
+
+ modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServerStatistics", b =>
+ {
+ b.Property("StatisticId")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Active");
+
+ b.Property("ServerId");
+
+ b.Property("TotalKills");
+
+ b.Property("TotalPlayTime");
+
+ b.HasKey("StatisticId");
+
+ b.HasIndex("ServerId");
+
+ b.ToTable("EFServerStatistics");
+ });
+
+ modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAlias", b =>
+ {
+ b.Property("AliasId")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Active");
+
+ b.Property("DateAdded");
+
+ b.Property("IPAddress");
+
+ b.Property("LinkId");
+
+ b.Property("Name")
+ .IsRequired();
+
+ b.HasKey("AliasId");
+
+ b.HasIndex("LinkId");
+
+ b.ToTable("EFAlias");
+ });
+
+ modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAliasLink", b =>
+ {
+ b.Property("AliasLinkId")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Active");
+
+ b.HasKey("AliasLinkId");
+
+ b.ToTable("EFAliasLinks");
+ });
+
+ modelBuilder.Entity("SharedLibraryCore.Database.Models.EFClient", b =>
+ {
+ b.Property("ClientId")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Active");
+
+ b.Property("AliasLinkId");
+
+ b.Property("Connections");
+
+ b.Property("CurrentAliasId");
+
+ b.Property("FirstConnection");
+
+ b.Property("LastConnection");
+
+ b.Property("Level");
+
+ b.Property("Masked");
+
+ b.Property("NetworkId");
+
+ b.Property("Password");
+
+ b.Property("PasswordSalt");
+
+ b.Property("TotalConnectionTime");
+
+ b.HasKey("ClientId");
+
+ b.HasIndex("AliasLinkId");
+
+ b.HasIndex("CurrentAliasId");
+
+ b.HasIndex("NetworkId")
+ .IsUnique();
+
+ b.ToTable("EFClients");
+ });
+
+ modelBuilder.Entity("SharedLibraryCore.Database.Models.EFPenalty", b =>
+ {
+ b.Property("PenaltyId")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Active");
+
+ b.Property("Expires");
+
+ b.Property("LinkId");
+
+ b.Property("OffenderId");
+
+ b.Property("Offense")
+ .IsRequired();
+
+ b.Property("PunisherId");
+
+ b.Property("Type");
+
+ b.Property("When");
+
+ b.HasKey("PenaltyId");
+
+ b.HasIndex("LinkId");
+
+ b.HasIndex("OffenderId");
+
+ b.HasIndex("PunisherId");
+
+ b.ToTable("EFPenalties");
+ });
+
+ modelBuilder.Entity("SharedLibraryCore.Helpers.Vector3", b =>
+ {
+ b.Property("Vector3Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("X");
+
+ b.Property("Y");
+
+ b.Property("Z");
+
+ b.HasKey("Vector3Id");
+
+ b.ToTable("Vector3");
+ });
+
+ modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientKill", b =>
+ {
+ b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Attacker")
+ .WithMany()
+ .HasForeignKey("AttackerId")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("SharedLibraryCore.Helpers.Vector3", "DeathOrigin")
+ .WithMany()
+ .HasForeignKey("DeathOriginVector3Id");
+
+ b.HasOne("SharedLibraryCore.Helpers.Vector3", "KillOrigin")
+ .WithMany()
+ .HasForeignKey("KillOriginVector3Id");
+
+ b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
+ .WithMany()
+ .HasForeignKey("ServerId")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Victim")
+ .WithMany()
+ .HasForeignKey("VictimId")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("SharedLibraryCore.Helpers.Vector3", "ViewAngles")
+ .WithMany()
+ .HasForeignKey("ViewAnglesVector3Id");
+ });
+
+ modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientMessage", b =>
+ {
+ b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
+ .WithMany()
+ .HasForeignKey("ClientId")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
+ .WithMany()
+ .HasForeignKey("ServerId")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
+ modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", b =>
+ {
+ b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
+ .WithMany()
+ .HasForeignKey("ClientId")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
+ .WithMany()
+ .HasForeignKey("ServerId")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
+ modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFHitLocationCount", b =>
+ {
+ b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
+ .WithMany()
+ .HasForeignKey("ClientId")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
+ .WithMany()
+ .HasForeignKey("ServerId")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics")
+ .WithMany("HitLocations")
+ .HasForeignKey("ClientId", "ServerId")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
+ modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServerStatistics", b =>
+ {
+ b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
+ .WithMany()
+ .HasForeignKey("ServerId")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
+ modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAlias", b =>
+ {
+ b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "Link")
+ .WithMany("Children")
+ .HasForeignKey("LinkId")
+ .OnDelete(DeleteBehavior.Restrict);
+ });
+
+ modelBuilder.Entity("SharedLibraryCore.Database.Models.EFClient", b =>
+ {
+ b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "AliasLink")
+ .WithMany()
+ .HasForeignKey("AliasLinkId")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("SharedLibraryCore.Database.Models.EFAlias", "CurrentAlias")
+ .WithMany()
+ .HasForeignKey("CurrentAliasId")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
+ modelBuilder.Entity("SharedLibraryCore.Database.Models.EFPenalty", b =>
+ {
+ b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "Link")
+ .WithMany("ReceivedPenalties")
+ .HasForeignKey("LinkId")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Offender")
+ .WithMany("ReceivedPenalties")
+ .HasForeignKey("OffenderId")
+ .OnDelete(DeleteBehavior.Restrict);
+
+ b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Punisher")
+ .WithMany("AdministeredPenalties")
+ .HasForeignKey("PunisherId")
+ .OnDelete(DeleteBehavior.Restrict);
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/SharedLibraryCore/Migrations/20180517223349_AddRollingKDR.cs b/SharedLibraryCore/Migrations/20180517223349_AddRollingKDR.cs
new file mode 100644
index 00000000..8c3a6c81
--- /dev/null
+++ b/SharedLibraryCore/Migrations/20180517223349_AddRollingKDR.cs
@@ -0,0 +1,25 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+using System;
+using System.Collections.Generic;
+
+namespace SharedLibraryCore.Migrations
+{
+ public partial class AddRollingKDR : Migration
+ {
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AddColumn(
+ name: "RollingWeightedKDR",
+ table: "EFClientStatistics",
+ nullable: false,
+ defaultValue: 0.0);
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropColumn(
+ name: "RollingWeightedKDR",
+ table: "EFClientStatistics");
+ }
+ }
+}
diff --git a/SharedLibraryCore/Migrations/DatabaseContextModelSnapshot.cs b/SharedLibraryCore/Migrations/DatabaseContextModelSnapshot.cs
index 4007141c..2c3d30d2 100644
--- a/SharedLibraryCore/Migrations/DatabaseContextModelSnapshot.cs
+++ b/SharedLibraryCore/Migrations/DatabaseContextModelSnapshot.cs
@@ -108,6 +108,8 @@ namespace SharedLibraryCore.Migrations
b.Property("MaxStrain");
+ b.Property("RollingWeightedKDR");
+
b.Property("SPM");
b.Property("Skill");