add most played command
hopefully fixed thread lock? started work on elo rating
This commit is contained in:
parent
699c19cd4b
commit
4006c09045
@ -46,7 +46,7 @@ namespace IW4MAdmin.Application.API
|
|||||||
|
|
||||||
FlaggedMessageCount = 0;
|
FlaggedMessageCount = 0;
|
||||||
|
|
||||||
E.Owner.Broadcast(Utilities.CurrentLocalization.LocalizationIndex["GLOBAL_REPORT"]).Wait();
|
E.Owner.Broadcast(Utilities.CurrentLocalization.LocalizationIndex["GLOBAL_REPORT"]).Wait(5000);
|
||||||
Events.Enqueue(new EventInfo(
|
Events.Enqueue(new EventInfo(
|
||||||
EventInfo.EventType.ALERT,
|
EventInfo.EventType.ALERT,
|
||||||
EventInfo.EventVersion.IW4MAdmin,
|
EventInfo.EventVersion.IW4MAdmin,
|
||||||
|
@ -53,3 +53,6 @@ del "%SolutionDir%Publish\Windows\*pdb"
|
|||||||
|
|
||||||
if exist "%SolutionDir%Publish\WindowsPrerelease\web.config" del "%SolutionDir%Publish\WindowsPrerelease\web.config"
|
if exist "%SolutionDir%Publish\WindowsPrerelease\web.config" del "%SolutionDir%Publish\WindowsPrerelease\web.config"
|
||||||
del "%SolutionDir%Publish\WindowsPrerelease\*pdb"
|
del "%SolutionDir%Publish\WindowsPrerelease\*pdb"
|
||||||
|
|
||||||
|
echo making start script
|
||||||
|
@echo dotnet IW4MAdmin.dll > "%SolutionDir%Publish\WindowsPrerelease\StartIW4MAdmin.cmd"
|
||||||
|
@ -44,7 +44,6 @@ namespace IW4MAdmin.Application
|
|||||||
EventApi Api;
|
EventApi Api;
|
||||||
GameEventHandler Handler;
|
GameEventHandler Handler;
|
||||||
ManualResetEventSlim OnEvent;
|
ManualResetEventSlim OnEvent;
|
||||||
Timer HeartbeatTimer;
|
|
||||||
|
|
||||||
private ApplicationManager()
|
private ApplicationManager()
|
||||||
{
|
{
|
||||||
@ -337,66 +336,75 @@ namespace IW4MAdmin.Application
|
|||||||
{
|
{
|
||||||
var heartbeatState = (HeartbeatState)state;
|
var heartbeatState = (HeartbeatState)state;
|
||||||
|
|
||||||
if (!heartbeatState.Connected)
|
while (Running)
|
||||||
{
|
{
|
||||||
try
|
if (!heartbeatState.Connected)
|
||||||
{
|
{
|
||||||
Heartbeat.Send(this, true).Wait(5000);
|
try
|
||||||
heartbeatState.Connected = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
heartbeatState.Connected = false;
|
|
||||||
Logger.WriteWarning($"Could not connect to heartbeat server - {e.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Heartbeat.Send(this).Wait(5000);
|
|
||||||
}
|
|
||||||
catch (System.Net.Http.HttpRequestException e)
|
|
||||||
{
|
|
||||||
Logger.WriteWarning($"Could not send heartbeat - {e.Message}");
|
|
||||||
}
|
|
||||||
|
|
||||||
catch (AggregateException e)
|
|
||||||
{
|
|
||||||
Logger.WriteWarning($"Could not send heartbeat - {e.Message}");
|
|
||||||
var exceptions = e.InnerExceptions.Where(ex => ex.GetType() == typeof(RestEase.ApiException));
|
|
||||||
|
|
||||||
foreach (var ex in exceptions)
|
|
||||||
{
|
{
|
||||||
if (((RestEase.ApiException)ex).StatusCode == System.Net.HttpStatusCode.Unauthorized)
|
Heartbeat.Send(this, true).Wait(5000);
|
||||||
|
heartbeatState.Connected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
heartbeatState.Connected = false;
|
||||||
|
Logger.WriteWarning($"Could not connect to heartbeat server - {e.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Heartbeat.Send(this).Wait(5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
catch (System.Net.Http.HttpRequestException e)
|
||||||
|
{
|
||||||
|
Logger.WriteWarning($"Could not send heartbeat - {e.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
catch (AggregateException e)
|
||||||
|
{
|
||||||
|
Logger.WriteWarning($"Could not send heartbeat - {e.Message}");
|
||||||
|
var exceptions = e.InnerExceptions.Where(ex => ex.GetType() == typeof(RestEase.ApiException));
|
||||||
|
|
||||||
|
foreach (var ex in exceptions)
|
||||||
|
{
|
||||||
|
if (((RestEase.ApiException)ex).StatusCode == System.Net.HttpStatusCode.Unauthorized)
|
||||||
|
{
|
||||||
|
heartbeatState.Connected = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
catch (RestEase.ApiException e)
|
||||||
|
{
|
||||||
|
Logger.WriteWarning($"Could not send heartbeat - {e.Message}");
|
||||||
|
if (e.StatusCode == System.Net.HttpStatusCode.Unauthorized)
|
||||||
{
|
{
|
||||||
heartbeatState.Connected = false;
|
heartbeatState.Connected = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
catch (Exception e)
|
||||||
|
|
||||||
catch (RestEase.ApiException e)
|
|
||||||
{
|
|
||||||
Logger.WriteWarning($"Could not send heartbeat - {e.Message}");
|
|
||||||
if (e.StatusCode == System.Net.HttpStatusCode.Unauthorized)
|
|
||||||
{
|
{
|
||||||
heartbeatState.Connected = false;
|
Logger.WriteWarning($"Could not send heartbeat - {e.Message}");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
|
Task.Delay(30000).Wait();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Start()
|
public async Task Start()
|
||||||
{
|
{
|
||||||
#if !DEBUG
|
|
||||||
// start heartbeat
|
|
||||||
HeartbeatTimer = new Timer(SendHeartbeat, new HeartbeatState(), 0, 30000);
|
|
||||||
#endif
|
|
||||||
// this needs to be run seperately from the main thread
|
// this needs to be run seperately from the main thread
|
||||||
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||||
|
#if !DEBUG
|
||||||
|
// start heartbeat
|
||||||
|
Task.Run(() => SendHeartbeat(new HeartbeatState()));
|
||||||
|
#endif
|
||||||
Task.Run(() => UpdateStatus(null));
|
Task.Run(() => UpdateStatus(null));
|
||||||
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||||
|
|
||||||
@ -439,7 +447,7 @@ namespace IW4MAdmin.Application
|
|||||||
while (Running)
|
while (Running)
|
||||||
{
|
{
|
||||||
// wait for new event to be added
|
// wait for new event to be added
|
||||||
OnEvent.Wait();
|
OnEvent.Wait(5000);
|
||||||
|
|
||||||
// todo: sequencially or parallelize?
|
// todo: sequencially or parallelize?
|
||||||
while ((queuedEvent = Handler.GetNextEvent()) != null)
|
while ((queuedEvent = Handler.GetNextEvent()) != null)
|
||||||
@ -454,8 +462,6 @@ namespace IW4MAdmin.Application
|
|||||||
OnEvent.Reset();
|
OnEvent.Reset();
|
||||||
}
|
}
|
||||||
#if !DEBUG
|
#if !DEBUG
|
||||||
HeartbeatTimer.Change(0, Timeout.Infinite);
|
|
||||||
|
|
||||||
foreach (var S in _servers)
|
foreach (var S in _servers)
|
||||||
await S.Broadcast("^1" + Utilities.CurrentLocalization.LocalizationIndex["BROADCAST_OFFLINE"]);
|
await S.Broadcast("^1" + Utilities.CurrentLocalization.LocalizationIndex["BROADCAST_OFFLINE"]);
|
||||||
#endif
|
#endif
|
||||||
|
@ -230,7 +230,9 @@ namespace IW4MAdmin
|
|||||||
|
|
||||||
var e = new GameEvent(GameEvent.EventType.Disconnect, "", Leaving, null, this);
|
var e = new GameEvent(GameEvent.EventType.Disconnect, "", Leaving, null, this);
|
||||||
Manager.GetEventHandler().AddEvent(e);
|
Manager.GetEventHandler().AddEvent(e);
|
||||||
e.OnProcessed.WaitHandle.WaitOne(5000);
|
|
||||||
|
// wait until the disconnect event is complete
|
||||||
|
e.OnProcessed.Wait();
|
||||||
|
|
||||||
Leaving.TotalConnectionTime += (int)(DateTime.UtcNow - Leaving.ConnectionTime).TotalSeconds;
|
Leaving.TotalConnectionTime += (int)(DateTime.UtcNow - Leaving.ConnectionTime).TotalSeconds;
|
||||||
Leaving.LastConnection = DateTime.UtcNow;
|
Leaving.LastConnection = DateTime.UtcNow;
|
||||||
@ -487,7 +489,7 @@ namespace IW4MAdmin
|
|||||||
{
|
{
|
||||||
ChatHistory.Add(new ChatInfo()
|
ChatHistory.Add(new ChatInfo()
|
||||||
{
|
{
|
||||||
Name = E.Origin?.Name ?? "ERROR!",
|
Name = E.Origin.Name,
|
||||||
Message = "DISCONNECTED",
|
Message = "DISCONNECTED",
|
||||||
Time = DateTime.UtcNow
|
Time = DateTime.UtcNow
|
||||||
});
|
});
|
||||||
@ -505,8 +507,8 @@ namespace IW4MAdmin
|
|||||||
{
|
{
|
||||||
ChatHistory.Add(new ChatInfo()
|
ChatHistory.Add(new ChatInfo()
|
||||||
{
|
{
|
||||||
Name = E.Origin?.Name ?? "ERROR!",
|
Name = E.Origin.Name,
|
||||||
Message = E.Data,
|
Message = E.Data ?? "NULL",
|
||||||
Time = DateTime.UtcNow
|
Time = DateTime.UtcNow
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -82,7 +82,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
|||||||
hitLoc.HitOffsetAverage = (float)newAverage;
|
hitLoc.HitOffsetAverage = (float)newAverage;
|
||||||
|
|
||||||
if (hitLoc.HitOffsetAverage > Thresholds.MaxOffset &&
|
if (hitLoc.HitOffsetAverage > Thresholds.MaxOffset &&
|
||||||
hitLoc.HitCount > 15)
|
hitLoc.HitCount > 100)
|
||||||
{
|
{
|
||||||
Log.WriteDebug("*** Reached Max Lifetime Average for Angle Difference ***");
|
Log.WriteDebug("*** Reached Max Lifetime Average for Angle Difference ***");
|
||||||
Log.WriteDebug($"Lifetime Average = {newAverage}");
|
Log.WriteDebug($"Lifetime Average = {newAverage}");
|
||||||
@ -104,7 +104,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
|||||||
AngleDifferenceAverage = sessAverage;
|
AngleDifferenceAverage = sessAverage;
|
||||||
|
|
||||||
if (sessAverage > Thresholds.MaxOffset &&
|
if (sessAverage > Thresholds.MaxOffset &&
|
||||||
HitCount > 15)
|
HitCount > 30)
|
||||||
{
|
{
|
||||||
Log.WriteDebug("*** Reached Max Session Average for Angle Difference ***");
|
Log.WriteDebug("*** Reached Max Session Average for Angle Difference ***");
|
||||||
Log.WriteDebug($"Session Average = {sessAverage}");
|
Log.WriteDebug($"Session Average = {sessAverage}");
|
||||||
@ -125,7 +125,8 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
|||||||
Log.WriteDebug($"PredictVsReal={realAgainstPredict}");
|
Log.WriteDebug($"PredictVsReal={realAgainstPredict}");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
var currentStrain = Strain.GetStrain(kill.ViewAngles, Math.Max(50, kill.TimeOffset - LastOffset));
|
double currentStrain = Strain.GetStrain(isDamage, kill.Damage, kill.ViewAngles, Math.Max(50, kill.TimeOffset - LastOffset));
|
||||||
|
currentStrain *= ClientStats.SPM / 179.0;
|
||||||
LastOffset = kill.TimeOffset;
|
LastOffset = kill.TimeOffset;
|
||||||
|
|
||||||
if (currentStrain > ClientStats.MaxStrain)
|
if (currentStrain > ClientStats.MaxStrain)
|
||||||
@ -149,12 +150,26 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
|||||||
Tracker.ClearChanges();
|
Tracker.ClearChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Strain.TimesReachedMaxStrain >= 3)
|
// flag
|
||||||
|
if (currentStrain > Thresholds.MaxStrainFlag)
|
||||||
{
|
{
|
||||||
return new DetectionPenaltyResult()
|
return new DetectionPenaltyResult()
|
||||||
{
|
{
|
||||||
ClientPenalty = Penalty.PenaltyType.Flag,
|
ClientPenalty = Penalty.PenaltyType.Flag,
|
||||||
Value = ClientStats.MaxStrain,
|
Value = currentStrain,
|
||||||
|
HitCount = HitCount,
|
||||||
|
Type = DetectionType.Strain
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ban
|
||||||
|
if (currentStrain > Thresholds.MaxStrainBan
|
||||||
|
&& Kills > Thresholds.LowSampleMinKills)
|
||||||
|
{
|
||||||
|
return new DetectionPenaltyResult()
|
||||||
|
{
|
||||||
|
ClientPenalty = Penalty.PenaltyType.Flag,
|
||||||
|
Value = currentStrain,
|
||||||
HitCount = HitCount,
|
HitCount = HitCount,
|
||||||
Type = DetectionType.Strain
|
Type = DetectionType.Strain
|
||||||
};
|
};
|
||||||
|
@ -8,7 +8,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
|||||||
{
|
{
|
||||||
class Strain : ITrackable
|
class Strain : ITrackable
|
||||||
{
|
{
|
||||||
private static double StrainDecayBase = 0.15;
|
private const double StrainDecayBase = 0.9;
|
||||||
private double CurrentStrain;
|
private double CurrentStrain;
|
||||||
private Vector3 LastAngle;
|
private Vector3 LastAngle;
|
||||||
private double LastDeltaTime;
|
private double LastDeltaTime;
|
||||||
@ -16,7 +16,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
|||||||
|
|
||||||
public int TimesReachedMaxStrain { get; private set; }
|
public int TimesReachedMaxStrain { get; private set; }
|
||||||
|
|
||||||
public double GetStrain(Vector3 newAngle, double deltaTime)
|
public double GetStrain(bool isDamage, int damage, Vector3 newAngle, double deltaTime)
|
||||||
{
|
{
|
||||||
if (LastAngle == null)
|
if (LastAngle == null)
|
||||||
LastAngle = newAngle;
|
LastAngle = newAngle;
|
||||||
@ -26,21 +26,36 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
|||||||
double decayFactor = GetDecay(deltaTime);
|
double decayFactor = GetDecay(deltaTime);
|
||||||
CurrentStrain *= decayFactor;
|
CurrentStrain *= decayFactor;
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
Console.WriteLine($"Decay Factor = {decayFactor} ");
|
||||||
|
#endif
|
||||||
|
|
||||||
double[] distance = Helpers.Extensions.AngleStuff(newAngle, LastAngle);
|
double[] distance = Helpers.Extensions.AngleStuff(newAngle, LastAngle);
|
||||||
LastDistance = distance[0] + distance[1];
|
LastDistance = distance[0] + distance[1];
|
||||||
|
|
||||||
// this happens on first kill
|
// this happens on first kill
|
||||||
if ((distance[0] == 0 && distance[1] == 0) ||
|
if ((distance[0] == 0 && distance[1] == 0) ||
|
||||||
deltaTime == 0 ||
|
deltaTime == 0 ||
|
||||||
double.IsNaN(CurrentStrain))
|
double.IsNaN(CurrentStrain))
|
||||||
{
|
{
|
||||||
return CurrentStrain;
|
return CurrentStrain;
|
||||||
}
|
}
|
||||||
|
|
||||||
double newStrain = Math.Pow(distance[0] + distance[1], 0.99) / deltaTime;
|
double newStrain = Math.Pow(distance[0] + distance[1], 0.99) / deltaTime;
|
||||||
|
|
||||||
|
if (damage < 100 && isDamage)
|
||||||
|
{
|
||||||
|
newStrain *= Math.Pow(damage, 2) / 10000.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (damage > 100)
|
||||||
|
{
|
||||||
|
newStrain *= damage / 100.0;
|
||||||
|
}
|
||||||
|
|
||||||
CurrentStrain += newStrain;
|
CurrentStrain += newStrain;
|
||||||
|
|
||||||
if (CurrentStrain > Thresholds.MaxStrainFlag)
|
if (CurrentStrain > Thresholds.MaxStrainBan)
|
||||||
TimesReachedMaxStrain++;
|
TimesReachedMaxStrain++;
|
||||||
|
|
||||||
LastAngle = newAngle;
|
LastAngle = newAngle;
|
||||||
@ -52,6 +67,6 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
|||||||
return $"Strain - {CurrentStrain}, Angle - {LastAngle}, Delta Time - {LastDeltaTime}, Distance - {LastDistance}";
|
return $"Strain - {CurrentStrain}, Angle - {LastAngle}, Delta Time - {LastDeltaTime}, Distance - {LastDistance}";
|
||||||
}
|
}
|
||||||
|
|
||||||
private double GetDecay(double deltaTime) => Math.Pow(StrainDecayBase, deltaTime / 1000.0);
|
private double GetDecay(double deltaTime) => Math.Pow(StrainDecayBase, Math.Pow(2.0, deltaTime / 250.0) / 1000.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -27,9 +27,9 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
|||||||
public const int HighSampleMinKills = 100;
|
public const int HighSampleMinKills = 100;
|
||||||
public const double KillTimeThreshold = 0.2;
|
public const double KillTimeThreshold = 0.2;
|
||||||
|
|
||||||
public const double MaxStrainBan = 0.4399;
|
public const double MaxStrainBan = 2.5;
|
||||||
public const double MaxOffset = 1.2;
|
public const double MaxOffset = 1.2;
|
||||||
public const double MaxStrainFlag = 1;
|
public const double MaxStrainFlag = 2.0;
|
||||||
|
|
||||||
public static double GetMarginOfError(int numKills) => 1.6455 / Math.Sqrt(numKills);
|
public static double GetMarginOfError(int numKills) => 1.6455 / Math.Sqrt(numKills);
|
||||||
|
|
||||||
|
75
Plugins/Stats/Commands/MostPlayed.cs
Normal file
75
Plugins/Stats/Commands/MostPlayed.cs
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
using SharedLibraryCore;
|
||||||
|
using SharedLibraryCore.Objects;
|
||||||
|
using IW4MAdmin.Plugins.Stats.Models;
|
||||||
|
using SharedLibraryCore.Database;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace IW4MAdmin.Plugins.Stats.Commands
|
||||||
|
{
|
||||||
|
class MostPlayed : Command
|
||||||
|
{
|
||||||
|
public static async Task<List<string>> GetMostPlayed(Server s)
|
||||||
|
{
|
||||||
|
int serverId = s.GetHashCode();
|
||||||
|
List<string> mostPlayed = new List<string>()
|
||||||
|
{
|
||||||
|
$"^5--{Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_MOSTPLAYED_TEXT"]}--"
|
||||||
|
};
|
||||||
|
|
||||||
|
using (var db = new DatabaseContext())
|
||||||
|
{
|
||||||
|
db.ChangeTracker.AutoDetectChangesEnabled = false;
|
||||||
|
db.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
||||||
|
|
||||||
|
var thirtyDaysAgo = DateTime.UtcNow.AddMonths(-1);
|
||||||
|
|
||||||
|
var iqStats = (from stats in db.Set<EFClientStatistics>()
|
||||||
|
join client in db.Clients
|
||||||
|
on stats.ClientId equals client.ClientId
|
||||||
|
join alias in db.Aliases
|
||||||
|
on client.CurrentAliasId equals alias.AliasId
|
||||||
|
where stats.ServerId == serverId
|
||||||
|
where client.Level != Player.Permission.Banned
|
||||||
|
where client.LastConnection >= thirtyDaysAgo
|
||||||
|
orderby stats.Kills descending
|
||||||
|
select new
|
||||||
|
{
|
||||||
|
alias.Name,
|
||||||
|
client.TotalConnectionTime,
|
||||||
|
stats.Kills
|
||||||
|
})
|
||||||
|
.Take(5);
|
||||||
|
|
||||||
|
var iqList = await iqStats.ToListAsync();
|
||||||
|
|
||||||
|
mostPlayed.AddRange(iqList.Select(stats =>
|
||||||
|
$"^3{stats.Name}^7 - ^5{stats.Kills} ^7{Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_TEXT_KILLS"]} | ^5{Utilities.GetTimePassed(DateTime.UtcNow.AddSeconds(-stats.TotalConnectionTime), false)} ^7{Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PROFILE_PLAYER"].ToLower()}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return mostPlayed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MostPlayed() : base("mostplayed", Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_MOSTPLAYED_DESC"], "mp", Player.Permission.User, false) { }
|
||||||
|
|
||||||
|
public override async Task ExecuteAsync(GameEvent E)
|
||||||
|
{
|
||||||
|
var topStats = await GetMostPlayed(E.Owner);
|
||||||
|
if (!E.Message.IsBroadcastCommand())
|
||||||
|
{
|
||||||
|
foreach (var stat in topStats)
|
||||||
|
await E.Origin.Tell(stat);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var stat in topStats)
|
||||||
|
await E.Owner.Broadcast(stat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -40,10 +40,19 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
|||||||
where client.Level != Player.Permission.Banned
|
where client.Level != Player.Permission.Banned
|
||||||
where client.LastConnection >= thirtyDaysAgo
|
where client.LastConnection >= thirtyDaysAgo
|
||||||
orderby stats.Skill descending
|
orderby stats.Skill descending
|
||||||
select $"^3{alias.Name}^7 - ^5{stats.KDR} ^7KDR | ^5{stats.Skill} ^7{Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_TEXT_SKILL"]}")
|
select new
|
||||||
|
{
|
||||||
|
stats.KDR,
|
||||||
|
stats.Skill,
|
||||||
|
stats.EloRating,
|
||||||
|
alias.Name
|
||||||
|
})
|
||||||
.Take(5);
|
.Take(5);
|
||||||
|
|
||||||
topStatsText.AddRange(await iqStats.ToListAsync());
|
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"]}");
|
||||||
|
|
||||||
|
topStatsText.AddRange(statsList);
|
||||||
}
|
}
|
||||||
|
|
||||||
// no one qualified
|
// no one qualified
|
||||||
|
@ -53,13 +53,13 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
|||||||
if (E.Target != null)
|
if (E.Target != null)
|
||||||
{
|
{
|
||||||
pStats = clientStats.Find(c => c.ServerId == serverId && c.ClientId == E.Target.ClientId).First();
|
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"]}";
|
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()}";
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
pStats = pStats = clientStats.Find(c => c.ServerId == serverId && c.ClientId == E.Origin.ClientId).First();
|
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"]}";
|
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()}";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (E.Message.IsBroadcastCommand())
|
if (E.Message.IsBroadcastCommand())
|
||||||
|
@ -1,13 +1,6 @@
|
|||||||
using SharedLibraryCore;
|
using IW4MAdmin.Plugins.Stats.Cheat;
|
||||||
using IW4MAdmin.Plugins.Stats.Cheat;
|
|
||||||
using IW4MAdmin.Plugins.Stats.Models;
|
using IW4MAdmin.Plugins.Stats.Models;
|
||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using SharedLibraryCore.Services;
|
|
||||||
|
|
||||||
namespace IW4MAdmin.Plugins.Stats.Helpers
|
namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||||
{
|
{
|
||||||
|
@ -118,6 +118,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
ServerId = serverId,
|
ServerId = serverId,
|
||||||
Skill = 0.0,
|
Skill = 0.0,
|
||||||
SPM = 0.0,
|
SPM = 0.0,
|
||||||
|
EloRating = 200.0,
|
||||||
HitLocations = Enum.GetValues(typeof(IW4Info.HitLocation)).OfType<IW4Info.HitLocation>().Select(hl => new EFHitLocationCount()
|
HitLocations = Enum.GetValues(typeof(IW4Info.HitLocation)).OfType<IW4Info.HitLocation>().Select(hl => new EFHitLocationCount()
|
||||||
{
|
{
|
||||||
Active = true,
|
Active = true,
|
||||||
@ -145,6 +146,11 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
//await statsSvc.ClientStatSvc.SaveChangesAsync();
|
//await statsSvc.ClientStatSvc.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (clientStats.EloRating == 0.0)
|
||||||
|
{
|
||||||
|
clientStats.EloRating = clientStats.Skill;
|
||||||
|
}
|
||||||
|
|
||||||
// set these on connecting
|
// set these on connecting
|
||||||
clientStats.LastActive = DateTime.UtcNow;
|
clientStats.LastActive = DateTime.UtcNow;
|
||||||
clientStats.LastStatCalculation = DateTime.UtcNow;
|
clientStats.LastStatCalculation = DateTime.UtcNow;
|
||||||
@ -479,6 +485,30 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
// process the attacker's stats after the kills
|
// process the attacker's stats after the kills
|
||||||
attackerStats = UpdateStats(attackerStats);
|
attackerStats = UpdateStats(attackerStats);
|
||||||
|
|
||||||
|
// calulate elo
|
||||||
|
if (Servers[attackerStats.ServerId].PlayerStats.Count > 1)
|
||||||
|
{
|
||||||
|
double attackerLobbyRating = Servers[attackerStats.ServerId].PlayerStats
|
||||||
|
.Where(cs => cs.Value.ClientId != attackerStats.ClientId)
|
||||||
|
.Average(cs => cs.Value.EloRating);
|
||||||
|
|
||||||
|
double victimLobbyRating = Servers[attackerStats.ServerId].PlayerStats
|
||||||
|
.Where(cs => cs.Value.ClientId != victimStats.ClientId)
|
||||||
|
.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 victimEloDifference = Math.Log(victimLobbyRating) - Math.Log(victimStats.EloRating);
|
||||||
|
double lossPercentage = 1.0 / (1 + Math.Pow(10, victimEloDifference / 3.0));
|
||||||
|
|
||||||
|
attackerStats.EloRating += 24.0 * (1 - winPercentage);
|
||||||
|
victimStats.EloRating -= 24.0 * winPercentage;
|
||||||
|
|
||||||
|
attackerStats.EloRating = Math.Max(0, Math.Round(attackerStats.EloRating, 2));
|
||||||
|
victimStats.EloRating = Math.Max(0, Math.Round(victimStats.EloRating, 2));
|
||||||
|
}
|
||||||
|
|
||||||
// update after calculation
|
// update after calculation
|
||||||
attackerStats.TimePlayed += (int)(DateTime.UtcNow - attackerStats.LastActive).TotalSeconds;
|
attackerStats.TimePlayed += (int)(DateTime.UtcNow - attackerStats.LastActive).TotalSeconds;
|
||||||
victimStats.TimePlayed += (int)(DateTime.UtcNow - victimStats.LastActive).TotalSeconds;
|
victimStats.TimePlayed += (int)(DateTime.UtcNow - victimStats.LastActive).TotalSeconds;
|
||||||
|
@ -22,9 +22,8 @@ namespace IW4MAdmin.Plugins.Stats.Models
|
|||||||
public int Kills { get; set; }
|
public int Kills { get; set; }
|
||||||
[Required]
|
[Required]
|
||||||
public int Deaths { get; set; }
|
public int Deaths { get; set; }
|
||||||
|
public double EloRating { get; set; }
|
||||||
public virtual ICollection<EFHitLocationCount> HitLocations { get; set; }
|
public virtual ICollection<EFHitLocationCount> HitLocations { get; set; }
|
||||||
|
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public double KDR
|
public double KDR
|
||||||
{
|
{
|
||||||
|
@ -114,7 +114,9 @@ namespace IW4MAdmin.Plugins.Stats
|
|||||||
int kills = clientStats.Sum(c => c.Kills);
|
int kills = clientStats.Sum(c => c.Kills);
|
||||||
int deaths = clientStats.Sum(c => c.Deaths);
|
int deaths = clientStats.Sum(c => c.Deaths);
|
||||||
double kdr = Math.Round(kills / (double)deaths, 2);
|
double kdr = Math.Round(kills / (double)deaths, 2);
|
||||||
double skill = Math.Round(clientStats.Sum(c => c.Skill) / clientStats.Where(c => c.Skill > 0).Count(), 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);
|
||||||
double spm = Math.Round(clientStats.Sum(c => c.SPM) / clientStats.Where(c => c.SPM > 0).Count(), 1);
|
double spm = Math.Round(clientStats.Sum(c => c.SPM) / clientStats.Where(c => c.SPM > 0).Count(), 1);
|
||||||
|
|
||||||
return new List<ProfileMeta>()
|
return new List<ProfileMeta>()
|
||||||
@ -241,7 +243,7 @@ namespace IW4MAdmin.Plugins.Stats
|
|||||||
}).ToList();
|
}).ToList();
|
||||||
messageMeta.Add(new ProfileMeta()
|
messageMeta.Add(new ProfileMeta()
|
||||||
{
|
{
|
||||||
Key = "Messages",
|
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PROFILE_MESSAGES"],
|
||||||
Value = messages.Count
|
Value = messages.Count
|
||||||
});
|
});
|
||||||
|
|
||||||
|
436
SharedLibraryCore/Migrations/20180516023249_AddEloField.Designer.cs
generated
Normal file
436
SharedLibraryCore/Migrations/20180516023249_AddEloField.Designer.cs
generated
Normal file
@ -0,0 +1,436 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
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("20180516023249_AddEloField")]
|
||||||
|
partial class AddEloField
|
||||||
|
{
|
||||||
|
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<long>("KillId")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<bool>("Active");
|
||||||
|
|
||||||
|
b.Property<int>("AttackerId");
|
||||||
|
|
||||||
|
b.Property<int>("Damage");
|
||||||
|
|
||||||
|
b.Property<int?>("DeathOriginVector3Id");
|
||||||
|
|
||||||
|
b.Property<int>("DeathType");
|
||||||
|
|
||||||
|
b.Property<int>("HitLoc");
|
||||||
|
|
||||||
|
b.Property<int?>("KillOriginVector3Id");
|
||||||
|
|
||||||
|
b.Property<int>("Map");
|
||||||
|
|
||||||
|
b.Property<int>("ServerId");
|
||||||
|
|
||||||
|
b.Property<int>("VictimId");
|
||||||
|
|
||||||
|
b.Property<int?>("ViewAnglesVector3Id");
|
||||||
|
|
||||||
|
b.Property<int>("Weapon");
|
||||||
|
|
||||||
|
b.Property<DateTime>("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<long>("MessageId")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<bool>("Active");
|
||||||
|
|
||||||
|
b.Property<int>("ClientId");
|
||||||
|
|
||||||
|
b.Property<string>("Message");
|
||||||
|
|
||||||
|
b.Property<int>("ServerId");
|
||||||
|
|
||||||
|
b.Property<DateTime>("TimeSent");
|
||||||
|
|
||||||
|
b.HasKey("MessageId");
|
||||||
|
|
||||||
|
b.HasIndex("ClientId");
|
||||||
|
|
||||||
|
b.HasIndex("ServerId");
|
||||||
|
|
||||||
|
b.ToTable("EFClientMessages");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("ClientId");
|
||||||
|
|
||||||
|
b.Property<int>("ServerId");
|
||||||
|
|
||||||
|
b.Property<bool>("Active");
|
||||||
|
|
||||||
|
b.Property<int>("Deaths");
|
||||||
|
|
||||||
|
b.Property<double>("EloRating");
|
||||||
|
|
||||||
|
b.Property<int>("Kills");
|
||||||
|
|
||||||
|
b.Property<double>("MaxStrain");
|
||||||
|
|
||||||
|
b.Property<double>("SPM");
|
||||||
|
|
||||||
|
b.Property<double>("Skill");
|
||||||
|
|
||||||
|
b.Property<int>("TimePlayed");
|
||||||
|
|
||||||
|
b.HasKey("ClientId", "ServerId");
|
||||||
|
|
||||||
|
b.HasIndex("ServerId");
|
||||||
|
|
||||||
|
b.ToTable("EFClientStatistics");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFHitLocationCount", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("HitLocationCountId")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<bool>("Active");
|
||||||
|
|
||||||
|
b.Property<int>("ClientId")
|
||||||
|
.HasColumnName("EFClientStatistics_ClientId");
|
||||||
|
|
||||||
|
b.Property<int>("HitCount");
|
||||||
|
|
||||||
|
b.Property<float>("HitOffsetAverage");
|
||||||
|
|
||||||
|
b.Property<int>("Location");
|
||||||
|
|
||||||
|
b.Property<float>("MaxAngleDistance");
|
||||||
|
|
||||||
|
b.Property<int>("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<int>("ServerId");
|
||||||
|
|
||||||
|
b.Property<bool>("Active");
|
||||||
|
|
||||||
|
b.Property<int>("Port");
|
||||||
|
|
||||||
|
b.HasKey("ServerId");
|
||||||
|
|
||||||
|
b.ToTable("EFServers");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServerStatistics", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("StatisticId")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<bool>("Active");
|
||||||
|
|
||||||
|
b.Property<int>("ServerId");
|
||||||
|
|
||||||
|
b.Property<long>("TotalKills");
|
||||||
|
|
||||||
|
b.Property<long>("TotalPlayTime");
|
||||||
|
|
||||||
|
b.HasKey("StatisticId");
|
||||||
|
|
||||||
|
b.HasIndex("ServerId");
|
||||||
|
|
||||||
|
b.ToTable("EFServerStatistics");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAlias", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("AliasId")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<bool>("Active");
|
||||||
|
|
||||||
|
b.Property<DateTime>("DateAdded");
|
||||||
|
|
||||||
|
b.Property<int>("IPAddress");
|
||||||
|
|
||||||
|
b.Property<int>("LinkId");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasKey("AliasId");
|
||||||
|
|
||||||
|
b.HasIndex("LinkId");
|
||||||
|
|
||||||
|
b.ToTable("EFAlias");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAliasLink", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("AliasLinkId")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<bool>("Active");
|
||||||
|
|
||||||
|
b.HasKey("AliasLinkId");
|
||||||
|
|
||||||
|
b.ToTable("EFAliasLinks");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFClient", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("ClientId")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<bool>("Active");
|
||||||
|
|
||||||
|
b.Property<int>("AliasLinkId");
|
||||||
|
|
||||||
|
b.Property<int>("Connections");
|
||||||
|
|
||||||
|
b.Property<int>("CurrentAliasId");
|
||||||
|
|
||||||
|
b.Property<DateTime>("FirstConnection");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastConnection");
|
||||||
|
|
||||||
|
b.Property<int>("Level");
|
||||||
|
|
||||||
|
b.Property<bool>("Masked");
|
||||||
|
|
||||||
|
b.Property<long>("NetworkId");
|
||||||
|
|
||||||
|
b.Property<string>("Password");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordSalt");
|
||||||
|
|
||||||
|
b.Property<int>("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<int>("PenaltyId")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<bool>("Active");
|
||||||
|
|
||||||
|
b.Property<DateTime>("Expires");
|
||||||
|
|
||||||
|
b.Property<int>("LinkId");
|
||||||
|
|
||||||
|
b.Property<int>("OffenderId");
|
||||||
|
|
||||||
|
b.Property<string>("Offense")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Property<int>("PunisherId");
|
||||||
|
|
||||||
|
b.Property<int>("Type");
|
||||||
|
|
||||||
|
b.Property<DateTime>("When");
|
||||||
|
|
||||||
|
b.HasKey("PenaltyId");
|
||||||
|
|
||||||
|
b.HasIndex("LinkId");
|
||||||
|
|
||||||
|
b.HasIndex("OffenderId");
|
||||||
|
|
||||||
|
b.HasIndex("PunisherId");
|
||||||
|
|
||||||
|
b.ToTable("EFPenalties");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SharedLibraryCore.Helpers.Vector3", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Vector3Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<float>("X");
|
||||||
|
|
||||||
|
b.Property<float>("Y");
|
||||||
|
|
||||||
|
b.Property<float>("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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
25
SharedLibraryCore/Migrations/20180516023249_AddEloField.cs
Normal file
25
SharedLibraryCore/Migrations/20180516023249_AddEloField.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace SharedLibraryCore.Migrations
|
||||||
|
{
|
||||||
|
public partial class AddEloField : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<double>(
|
||||||
|
name: "EloRating",
|
||||||
|
table: "EFClientStatistics",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "EloRating",
|
||||||
|
table: "EFClientStatistics");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -102,6 +102,8 @@ namespace SharedLibraryCore.Migrations
|
|||||||
|
|
||||||
b.Property<int>("Deaths");
|
b.Property<int>("Deaths");
|
||||||
|
|
||||||
|
b.Property<double>("EloRating");
|
||||||
|
|
||||||
b.Property<int>("Kills");
|
b.Property<int>("Kills");
|
||||||
|
|
||||||
b.Property<double>("MaxStrain");
|
b.Property<double>("MaxStrain");
|
||||||
|
@ -250,7 +250,7 @@ namespace SharedLibraryCore.RCon
|
|||||||
|
|
||||||
if (FailedReceives >= 4)
|
if (FailedReceives >= 4)
|
||||||
{
|
{
|
||||||
throw new NetworkException($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMUNICATION"]} {socketConnection.RemoteEndPoint}");
|
throw new NetworkException($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMUNICATION"]} {socketConnection.RemoteEndPoint.ToString()}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,7 +258,7 @@ namespace SharedLibraryCore.RCon
|
|||||||
{
|
{
|
||||||
if (FailedReceives >= 4)
|
if (FailedReceives >= 4)
|
||||||
{
|
{
|
||||||
Log.WriteVerbose($"Resumed receive RCon connection from {socketConnection.RemoteEndPoint}");
|
Log.WriteVerbose($"Resumed receive RCon connection from {socketConnection.RemoteEndPoint.ToString()}");
|
||||||
FailedReceives = 0;
|
FailedReceives = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="profile_meta" class="text-center text-sm-right pt-2 mt-md-4 pt-md-3 mr-4 pr-4 mr-md-0 ml-4 pl-4 ml-md-0 pr-md-0 pl-md-0">
|
<div id="profile_meta" class="text-center text-sm-right pt-2 mt-md-4 pt-md-3 mr-4 pr-4 mr-md-0 ml-4 pl-4 ml-md-0 pr-md-0 pl-md-0">
|
||||||
|
<div class="profile-meta-entry">
|
||||||
|
<span class="profile-meta-value text-primary">@Model.ConnectionCount</span>
|
||||||
|
<span class="profile-meta-value text-muted"> @loc["WEBFRONT_CLIENT_META_CONNECTIONS"]</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -8,19 +8,24 @@
|
|||||||
@{
|
@{
|
||||||
for (int i = 0; i < Model.ChatHistory.Count; i++)
|
for (int i = 0; i < Model.ChatHistory.Count; i++)
|
||||||
{
|
{
|
||||||
string message = @Model.ChatHistory[i].Message;
|
if (Model.ChatHistory[i] == null ||
|
||||||
|
Model.ChatHistory[i].Message == null ||
|
||||||
|
Model.ChatHistory[i].Name == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (Model.ChatHistory[i].Message == "CONNECTED")
|
if (Model.ChatHistory[i].Message == "CONNECTED")
|
||||||
{
|
{
|
||||||
<span class="text-light"><span class="oi oi-account-login mr-2 text-success"> </span>@Model.ChatHistory[i].Name</span><br />
|
<span class="text-light"><span class="oi oi-account-login mr-2 text-success"> </span>@Model.ChatHistory[i].Name</span><br />
|
||||||
}
|
}
|
||||||
if (Model.ChatHistory[i].Message == "DISCONNECTED")
|
if (Model.ChatHistory[i].Message == "DISCONNECTED")
|
||||||
{
|
{
|
||||||
|
|
||||||
<span class="text-light"><span class="oi oi-account-logout mr-2 text-danger"> </span>@Model.ChatHistory[i].Name</span><br />
|
<span class="text-light"><span class="oi oi-account-logout mr-2 text-danger"> </span>@Model.ChatHistory[i].Name</span><br />
|
||||||
}
|
}
|
||||||
if (Model.ChatHistory[i].Message != "CONNECTED" && Model.ChatHistory[i].Message != "DISCONNECTED")
|
if (Model.ChatHistory[i].Message != "CONNECTED" && Model.ChatHistory[i].Message != "DISCONNECTED")
|
||||||
{
|
{
|
||||||
<span class="text-light">@Model.ChatHistory[i].Name</span><span> — @message.Substring(0, Math.Min(65, message.Length)) </span><br />
|
<span class="text-light">@Model.ChatHistory[i].Name</span><span> — @Model.ChatHistory[i].Message.Substring(0, Math.Min(65, Model.ChatHistory[i].Message.Length)) </span><br />
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -55,7 +60,13 @@
|
|||||||
@{
|
@{
|
||||||
for (int i = 0; i < Model.ChatHistory.Count; i++)
|
for (int i = 0; i < Model.ChatHistory.Count; i++)
|
||||||
{
|
{
|
||||||
string message = @Model.ChatHistory[i].Message;
|
if (Model.ChatHistory[i] == null ||
|
||||||
|
Model.ChatHistory[i].Message == null ||
|
||||||
|
Model.ChatHistory[i].Name == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (Model.ChatHistory[i].Message == "CONNECTED")
|
if (Model.ChatHistory[i].Message == "CONNECTED")
|
||||||
{
|
{
|
||||||
<span class="text-light"><span class="oi oi-account-login mr-2 text-success"> </span>@Model.ChatHistory[i].Name</span><br />
|
<span class="text-light"><span class="oi oi-account-login mr-2 text-success"> </span>@Model.ChatHistory[i].Name</span><br />
|
||||||
@ -67,7 +78,7 @@
|
|||||||
}
|
}
|
||||||
if (Model.ChatHistory[i].Message != "CONNECTED" && Model.ChatHistory[i].Message != "DISCONNECTED")
|
if (Model.ChatHistory[i].Message != "CONNECTED" && Model.ChatHistory[i].Message != "DISCONNECTED")
|
||||||
{
|
{
|
||||||
<span class="text-light">@Model.ChatHistory[i].Name</span><span> — @message.Substring(0, Math.Min(65, message.Length)) </span><br />
|
<span class="text-light">@Model.ChatHistory[i].Name</span><span> — @Model.ChatHistory[i].Message.Substring(0, Math.Min(65, Model.ChatHistory[i].Message.Length)) </span><br />
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user