small updates to stat handling
various little tweaks
This commit is contained in:
parent
58a73e581f
commit
198f596ab3
@ -72,6 +72,7 @@ namespace IW4MAdmin.Application.IO
|
||||
foreach (var ev in events)
|
||||
{
|
||||
_server.Manager.GetEventHandler().AddEvent(ev);
|
||||
await ev.WaitAsync(Utilities.DefaultCommandTimeout, ev.Owner.Manager.CancellationToken);
|
||||
}
|
||||
|
||||
previousFileSize = fileSize;
|
||||
|
@ -36,14 +36,15 @@ namespace IW4MAdmin.Application.IO
|
||||
List<string> logLines = new List<string>();
|
||||
|
||||
// open the file as a stream
|
||||
using (var rd = new StreamReader(new FileStream(LogFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), Utilities.EncodingType))
|
||||
using (FileStream fs = new FileStream(LogFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
char[] buff = new char[fileSizeDiff];
|
||||
rd.BaseStream.Seek(startPosition, SeekOrigin.Begin);
|
||||
await rd.ReadAsync(buff, 0, (int)fileSizeDiff);
|
||||
|
||||
byte[] buff = new byte[fileSizeDiff];
|
||||
fs.Seek(startPosition, SeekOrigin.Begin);
|
||||
await fs.ReadAsync(buff, 0, (int)fileSizeDiff, server.Manager.CancellationToken);
|
||||
var stringBuilder = new StringBuilder();
|
||||
foreach (char c in buff)
|
||||
char[] charBuff = Utilities.EncodingType.GetChars(buff);
|
||||
|
||||
foreach (char c in charBuff)
|
||||
{
|
||||
if (c == '\n')
|
||||
{
|
||||
|
@ -1,7 +1,6 @@
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
@ -1,4 +1,5 @@
|
||||
using IW4MAdmin.Plugins.Stats.Models;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Helpers;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
@ -22,7 +23,8 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
};
|
||||
|
||||
public ChangeTracking<EFACSnapshot> Tracker { get; private set; }
|
||||
public const int MAX_TRACKED_HIT_COUNT = 10;
|
||||
public const int MIN_HITS_TO_RUN_DETECTION = 5;
|
||||
private const int MIN_ANGLE_COUNT = 5;
|
||||
|
||||
public List<EFClientKill> TrackedHits { get; set; }
|
||||
int Kills;
|
||||
@ -36,6 +38,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
Strain Strain;
|
||||
readonly DateTime ConnectionTime = DateTime.UtcNow;
|
||||
private double sessionAverageRecoilAmount;
|
||||
private EFClientKill lastHit;
|
||||
|
||||
private class HitInfo
|
||||
{
|
||||
@ -344,7 +347,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
Tracker.OnChange(new EFACSnapshot()
|
||||
var snapshot = new EFACSnapshot()
|
||||
{
|
||||
When = hit.When,
|
||||
ClientId = ClientStats.ClientId,
|
||||
@ -372,7 +375,9 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
StrainAngleBetween = Strain.LastDistance,
|
||||
TimeSinceLastEvent = (int)Strain.LastDeltaTime,
|
||||
WeaponId = hit.Weapon
|
||||
});
|
||||
};
|
||||
|
||||
Tracker.OnChange(snapshot);
|
||||
|
||||
return results.FirstOrDefault(_result => _result.ClientPenalty == EFPenalty.PenaltyType.Ban) ??
|
||||
results.FirstOrDefault(_result => _result.ClientPenalty == EFPenalty.PenaltyType.Flag) ??
|
||||
|
@ -44,14 +44,24 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
||||
|
||||
long serverId = StatManager.GetIdForServer(E.Owner);
|
||||
|
||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||
{
|
||||
|
||||
if (E.Target != null)
|
||||
{
|
||||
int performanceRanking = await StatManager.GetClientOverallRanking(E.Target.ClientId);
|
||||
string performanceRankingString = performanceRanking == 0 ? loc["WEBFRONT_STATS_INDEX_UNRANKED"] : $"{loc["WEBFRONT_STATS_INDEX_RANKED"]} #{performanceRanking}";
|
||||
|
||||
if (E.Owner.GetClientsAsList().Any(_client => _client.Equals(E.Target)))
|
||||
{
|
||||
pStats = Plugin.Manager.GetClientStats(E.Target.ClientId, serverId);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
using (var ctx = new DatabaseContext(true))
|
||||
{
|
||||
pStats = (await ctx.Set<EFClientStatistics>().FirstAsync(c => c.ServerId == serverId && c.ClientId == E.Target.ClientId));
|
||||
}
|
||||
}
|
||||
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()} | {performanceRankingString}";
|
||||
}
|
||||
|
||||
@ -60,9 +70,19 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
||||
int performanceRanking = await StatManager.GetClientOverallRanking(E.Origin.ClientId);
|
||||
string performanceRankingString = performanceRanking == 0 ? loc["WEBFRONT_STATS_INDEX_UNRANKED"] : $"{loc["WEBFRONT_STATS_INDEX_RANKED"]} #{performanceRanking}";
|
||||
|
||||
pStats = (await ctx.Set<EFClientStatistics>().FirstAsync((c => c.ServerId == serverId && c.ClientId == E.Origin.ClientId)));
|
||||
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()} | {performanceRankingString}";
|
||||
if (E.Owner.GetClientsAsList().Any(_client => _client.Equals(E.Origin)))
|
||||
{
|
||||
pStats = Plugin.Manager.GetClientStats(E.Origin.ClientId, serverId);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
using (var ctx = new DatabaseContext(true))
|
||||
{
|
||||
pStats = (await ctx.Set<EFClientStatistics>().FirstAsync(c => c.ServerId == serverId && c.ClientId == E.Origin.ClientId));
|
||||
}
|
||||
}
|
||||
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()} | {performanceRankingString}";
|
||||
}
|
||||
|
||||
if (E.Message.IsBroadcastCommand())
|
||||
|
@ -2,6 +2,7 @@
|
||||
using IW4MAdmin.Plugins.Stats.Models;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
@ -9,6 +10,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
class ServerStats {
|
||||
public ConcurrentDictionary<int, EFClientStatistics> PlayerStats { get; set; }
|
||||
public ConcurrentDictionary<int, Detection> PlayerDetections { get; set; }
|
||||
public IList<EFClientKill> HitCache { get; private set; }
|
||||
public EFServerStatistics ServerStatistics { get; private set; }
|
||||
public EFServer Server { get; private set; }
|
||||
public bool IsTeamBased { get; set; }
|
||||
@ -17,6 +19,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
{
|
||||
PlayerStats = new ConcurrentDictionary<int, EFClientStatistics>();
|
||||
PlayerDetections = new ConcurrentDictionary<int, Detection>();
|
||||
HitCache = new List<EFClientKill>();
|
||||
ServerStatistics = st;
|
||||
Server = sv;
|
||||
}
|
||||
|
@ -19,20 +19,15 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
{
|
||||
public class StatManager
|
||||
{
|
||||
private const int MAX_CACHED_HITS = 100;
|
||||
private readonly ConcurrentDictionary<long, ServerStats> _servers;
|
||||
private readonly ILogger _log;
|
||||
private static List<EFServer> serverModels;
|
||||
private readonly SemaphoreSlim OnProcessingPenalty;
|
||||
private readonly SemaphoreSlim OnProcessingSensitive;
|
||||
private readonly List<EFClientKill> _hitCache;
|
||||
|
||||
public StatManager(IManager mgr)
|
||||
{
|
||||
_servers = new ConcurrentDictionary<long, ServerStats>();
|
||||
_hitCache = new List<EFClientKill>();
|
||||
_log = mgr.GetLogger(0);
|
||||
OnProcessingPenalty = new SemaphoreSlim(1, 1);
|
||||
OnProcessingSensitive = new SemaphoreSlim(1, 1);
|
||||
}
|
||||
|
||||
private void SetupServerIds()
|
||||
@ -143,6 +138,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
var iqStatsInfo = (from stat in context.Set<EFClientStatistics>()
|
||||
where clientIds.Contains(stat.ClientId)
|
||||
where stat.Kills > 0 || stat.Deaths > 0
|
||||
where serverId == null ? true : stat.ServerId == serverId
|
||||
group stat by stat.ClientId into s
|
||||
select new
|
||||
{
|
||||
@ -150,7 +146,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
Kills = s.Sum(c => c.Kills),
|
||||
Deaths = s.Sum(c => c.Deaths),
|
||||
KDR = s.Sum(c => (c.Kills / (double)(c.Deaths == 0 ? 1 : c.Deaths)) * c.TimePlayed) / s.Sum(c => c.TimePlayed),
|
||||
TotalTimePlayed = s.Sum(c => c.TimePlayed)
|
||||
TotalTimePlayed = s.Sum(c => c.TimePlayed),
|
||||
});
|
||||
|
||||
#if DEBUG == true
|
||||
@ -275,8 +271,6 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
/// <returns>EFClientStatistic of specified player</returns>
|
||||
public async Task<EFClientStatistics> AddPlayer(EFClient pl)
|
||||
{
|
||||
await OnProcessingSensitive.WaitAsync();
|
||||
|
||||
try
|
||||
{
|
||||
long serverId = GetIdForServer(pl.CurrentServer);
|
||||
@ -396,11 +390,6 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
_log.WriteDebug(ex.GetExceptionInfo());
|
||||
}
|
||||
|
||||
finally
|
||||
{
|
||||
OnProcessingSensitive.Release(1);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -466,7 +455,10 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
Vector3 vDeathOrigin = null;
|
||||
Vector3 vKillOrigin = null;
|
||||
Vector3 vViewAngles = null;
|
||||
SemaphoreSlim waiter = null;
|
||||
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
vDeathOrigin = Vector3.Parse(deathOrigin);
|
||||
@ -504,7 +496,6 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
AttackerId = attacker.ClientId,
|
||||
VictimId = victim.ClientId,
|
||||
ServerId = serverId,
|
||||
//Map = ParseEnum<IW4Info.MapName>.Get(map, typeof(IW4Info.MapName)),
|
||||
DeathOrigin = vDeathOrigin,
|
||||
KillOrigin = vKillOrigin,
|
||||
DeathType = ParseEnum<IW4Info.MeansOfDeath>.Get(type, typeof(IW4Info.MeansOfDeath)),
|
||||
@ -528,20 +519,23 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isDamage)
|
||||
{
|
||||
await AddStandardKill(attacker, victim);
|
||||
}
|
||||
|
||||
// incase the add player event get delayed
|
||||
if (!_servers[serverId].PlayerStats.ContainsKey(attacker.ClientId))
|
||||
{
|
||||
await AddPlayer(attacker);
|
||||
}
|
||||
|
||||
if (!isDamage)
|
||||
{
|
||||
await AddStandardKill(attacker, victim);
|
||||
}
|
||||
|
||||
var clientDetection = _servers[serverId].PlayerDetections[attacker.ClientId];
|
||||
var clientStats = _servers[serverId].PlayerStats[attacker.ClientId];
|
||||
|
||||
waiter = clientStats.ProcessingHit;
|
||||
await waiter.WaitAsync();
|
||||
|
||||
// increment their hit count
|
||||
if (hit.DeathType == IW4Info.MeansOfDeath.MOD_PISTOL_BULLET ||
|
||||
hit.DeathType == IW4Info.MeansOfDeath.MOD_RIFLE_BULLET ||
|
||||
@ -550,82 +544,55 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
clientStats.HitLocations.First(hl => hl.Location == hit.HitLoc).HitCount += 1;
|
||||
}
|
||||
|
||||
if (clientStats.SessionKills % Detection.MAX_TRACKED_HIT_COUNT == 0)
|
||||
{
|
||||
await OnProcessingPenalty.WaitAsync();
|
||||
await SaveClientStats(clientStats);
|
||||
OnProcessingPenalty.Release(1);
|
||||
}
|
||||
|
||||
if (hit.IsKillstreakKill)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (Plugin.Config.Configuration().StoreClientKills)
|
||||
{
|
||||
await OnProcessingPenalty.WaitAsync();
|
||||
_hitCache.Add(hit);
|
||||
var cache = _servers[serverId].HitCache;
|
||||
cache.Add(hit);
|
||||
|
||||
if (_hitCache.Count > Detection.MAX_TRACKED_HIT_COUNT)
|
||||
if (cache.Count > MAX_CACHED_HITS)
|
||||
{
|
||||
|
||||
using (var ctx = new DatabaseContext())
|
||||
{
|
||||
ctx.AddRange(_hitCache);
|
||||
await ctx.SaveChangesAsync();
|
||||
await SaveHitCache(serverId);
|
||||
}
|
||||
|
||||
_hitCache.Clear();
|
||||
}
|
||||
OnProcessingPenalty.Release(1);
|
||||
}
|
||||
|
||||
if (Plugin.Config.Configuration().EnableAntiCheat && !attacker.IsBot && attacker.ClientId != victim.ClientId)
|
||||
{
|
||||
DetectionPenaltyResult result = new DetectionPenaltyResult() { ClientPenalty = EFPenalty.PenaltyType.Any };
|
||||
await OnProcessingPenalty.WaitAsync();
|
||||
clientDetection.TrackedHits.Add(hit);
|
||||
|
||||
#if DEBUG
|
||||
if (clientDetection.TrackedHits.Count > 0)
|
||||
#else
|
||||
if (clientDetection.TrackedHits.Count > Detection.MAX_TRACKED_HIT_COUNT)
|
||||
#endif
|
||||
if (clientDetection.TrackedHits.Count >= Detection.MIN_HITS_TO_RUN_DETECTION)
|
||||
{
|
||||
while (clientDetection.TrackedHits.Count > 0)
|
||||
{
|
||||
var oldestHit = clientDetection.TrackedHits
|
||||
.OrderBy(_hits => _hits.TimeOffset)
|
||||
.First();
|
||||
|
||||
|
||||
var oldestHit = clientDetection.TrackedHits.OrderBy(_hits => _hits.TimeOffset).First();
|
||||
clientDetection.TrackedHits.Remove(oldestHit);
|
||||
|
||||
result = clientDetection.ProcessHit(oldestHit, isDamage);
|
||||
#if !DEBUG
|
||||
await ApplyPenalty(result, attacker);
|
||||
#endif
|
||||
|
||||
if (clientDetection.Tracker.HasChanges && result.ClientPenalty != EFPenalty.PenaltyType.Any)
|
||||
{
|
||||
using (var ctx = new DatabaseContext())
|
||||
{
|
||||
SaveTrackedSnapshots(clientDetection, ctx);
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
await SaveTrackedSnapshots(clientDetection);
|
||||
|
||||
if (result.ClientPenalty == EFPenalty.PenaltyType.Ban)
|
||||
{
|
||||
// we don't care about any additional hits now that they're banned
|
||||
clientDetection.TrackedHits.Clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
clientDetection.TrackedHits.Add(hit);
|
||||
}
|
||||
|
||||
OnProcessingPenalty.Release(1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -633,11 +600,22 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
{
|
||||
_log.WriteError("Could not save hit or AC info");
|
||||
_log.WriteDebug(ex.GetExceptionInfo());
|
||||
|
||||
if (OnProcessingPenalty.CurrentCount == 0)
|
||||
{
|
||||
OnProcessingPenalty.Release(1);
|
||||
}
|
||||
|
||||
finally
|
||||
{
|
||||
waiter?.Release(1);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SaveHitCache(long serverId)
|
||||
{
|
||||
using (var ctx = new DatabaseContext(true))
|
||||
{
|
||||
var server = _servers[serverId];
|
||||
ctx.AddRange(server.HitCache);
|
||||
await ctx.SaveChangesAsync();
|
||||
server.HitCache.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
@ -679,60 +657,18 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
}
|
||||
}
|
||||
|
||||
void SaveTrackedSnapshots(Detection clientDetection, DatabaseContext ctx)
|
||||
async Task SaveTrackedSnapshots(Detection clientDetection)
|
||||
{
|
||||
// todo: why does this cause duplicate primary key
|
||||
_ = clientDetection.Tracker.GetNextChange();
|
||||
EFACSnapshot change;
|
||||
|
||||
using (var ctx = new DatabaseContext(true))
|
||||
{
|
||||
while ((change = clientDetection.Tracker.GetNextChange()) != default(EFACSnapshot))
|
||||
{
|
||||
|
||||
if (change.HitOrigin.Vector3Id > 0)
|
||||
{
|
||||
change.HitOriginId = change.HitOrigin.Vector3Id;
|
||||
ctx.Attach(change.HitOrigin);
|
||||
}
|
||||
|
||||
else if (change.HitOrigin.Vector3Id == 0)
|
||||
{
|
||||
ctx.Add(change.HitOrigin);
|
||||
}
|
||||
|
||||
if (change.HitDestination.Vector3Id > 0)
|
||||
{
|
||||
change.HitDestinationId = change.HitDestination.Vector3Id;
|
||||
ctx.Attach(change.HitDestination);
|
||||
}
|
||||
|
||||
else if (change.HitDestination.Vector3Id == 0)
|
||||
{
|
||||
ctx.Add(change.HitOrigin);
|
||||
}
|
||||
|
||||
if (change.CurrentViewAngle.Vector3Id > 0)
|
||||
{
|
||||
change.CurrentViewAngleId = change.CurrentViewAngle.Vector3Id;
|
||||
ctx.Attach(change.CurrentViewAngle);
|
||||
}
|
||||
|
||||
else if (change.CurrentViewAngle.Vector3Id == 0)
|
||||
{
|
||||
ctx.Add(change.HitOrigin);
|
||||
}
|
||||
|
||||
if (change.LastStrainAngle.Vector3Id > 0)
|
||||
{
|
||||
change.LastStrainAngleId = change.LastStrainAngle.Vector3Id;
|
||||
ctx.Attach(change.LastStrainAngle);
|
||||
}
|
||||
|
||||
else if (change.LastStrainAngle.Vector3Id == 0)
|
||||
{
|
||||
ctx.Add(change.HitOrigin);
|
||||
}
|
||||
|
||||
ctx.Add(change);
|
||||
}
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task AddStandardKill(EFClient attacker, EFClient victim)
|
||||
@ -815,11 +751,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
}
|
||||
|
||||
// update their performance
|
||||
#if !DEBUG
|
||||
if ((DateTime.UtcNow - attackerStats.LastStatHistoryUpdate).TotalMinutes >= 2.5)
|
||||
#else
|
||||
if ((DateTime.UtcNow - attackerStats.LastStatHistoryUpdate).TotalMinutes >= 0.1)
|
||||
#endif
|
||||
{
|
||||
attackerStats.LastStatHistoryUpdate = DateTime.UtcNow;
|
||||
await UpdateStatHistory(attacker, attackerStats);
|
||||
@ -837,12 +769,10 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
int currentSessionTime = (int)(DateTime.UtcNow - client.LastConnection).TotalSeconds;
|
||||
|
||||
// don't update their stat history if they haven't played long
|
||||
//#if DEBUG == false
|
||||
if (currentSessionTime < 60)
|
||||
{
|
||||
return;
|
||||
}
|
||||
//#endif
|
||||
|
||||
int currentServerTotalPlaytime = clientStats.TimePlayed + currentSessionTime;
|
||||
|
||||
@ -1240,9 +1170,18 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
|
||||
var serverStatsSet = ctx.Set<EFServerStatistics>();
|
||||
serverStatsSet.Update(_servers[serverId].ServerStatistics);
|
||||
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
|
||||
foreach (var client in sv.GetClientsAsList())
|
||||
{
|
||||
var stats = GetClientStats(client.ClientId, serverId);
|
||||
if (stats != null)
|
||||
{
|
||||
await SaveClientStats(stats);
|
||||
}
|
||||
}
|
||||
|
||||
await SaveHitCache(serverId);
|
||||
}
|
||||
|
||||
public void SetTeamBased(long serverId, bool isTeamBased)
|
||||
|
@ -4,6 +4,7 @@ using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using SharedLibraryCore.Database.Models;
|
||||
@ -12,6 +13,16 @@ namespace IW4MAdmin.Plugins.Stats.Models
|
||||
{
|
||||
public class EFClientStatistics : SharedEntity
|
||||
{
|
||||
public EFClientStatistics()
|
||||
{
|
||||
ProcessingHit = new SemaphoreSlim(1, 1);
|
||||
}
|
||||
|
||||
~EFClientStatistics()
|
||||
{
|
||||
ProcessingHit.Dispose();
|
||||
}
|
||||
|
||||
public int ClientId { get; set; }
|
||||
[ForeignKey("ClientId")]
|
||||
public virtual EFClient Client { get; set; }
|
||||
@ -96,12 +107,14 @@ namespace IW4MAdmin.Plugins.Stats.Models
|
||||
}
|
||||
}
|
||||
[NotMapped]
|
||||
private List<int> SessionScores = new List<int>() { 0 };
|
||||
private readonly List<int> SessionScores = new List<int>() { 0 };
|
||||
[NotMapped]
|
||||
public IW4Info.Team Team { get; set; }
|
||||
[NotMapped]
|
||||
public DateTime LastStatHistoryUpdate { get; set; } = DateTime.UtcNow;
|
||||
[NotMapped]
|
||||
public double SessionSPM { get; set; }
|
||||
[NotMapped]
|
||||
public SemaphoreSlim ProcessingHit { get; private set; }
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,10 @@ namespace IW4MAdmin.Plugins.Stats
|
||||
public static StatManager Manager { get; private set; }
|
||||
public static IManager ServerManager;
|
||||
public static BaseConfigurationHandler<StatsConfiguration> Config { get; private set; }
|
||||
#if DEBUG
|
||||
int scriptDamageCount;
|
||||
int scriptKillCount;
|
||||
#endif
|
||||
|
||||
public async Task OnEventAsync(GameEvent E, Server S)
|
||||
{
|
||||
@ -44,7 +48,6 @@ namespace IW4MAdmin.Plugins.Stats
|
||||
break;
|
||||
case GameEvent.EventType.Disconnect:
|
||||
await Manager.RemovePlayer(E.Origin);
|
||||
await Manager.Sync(S);
|
||||
break;
|
||||
case GameEvent.EventType.Say:
|
||||
if (!string.IsNullOrEmpty(E.Data) &&
|
||||
@ -56,8 +59,10 @@ namespace IW4MAdmin.Plugins.Stats
|
||||
case GameEvent.EventType.MapChange:
|
||||
Manager.SetTeamBased(StatManager.GetIdForServer(E.Owner), E.Owner.Gametype != "dm");
|
||||
Manager.ResetKillstreaks(StatManager.GetIdForServer(E.Owner));
|
||||
await Manager.Sync(E.Owner);
|
||||
break;
|
||||
case GameEvent.EventType.MapEnd:
|
||||
await Manager.Sync(E.Owner);
|
||||
break;
|
||||
case GameEvent.EventType.JoinTeam:
|
||||
break;
|
||||
@ -85,8 +90,17 @@ namespace IW4MAdmin.Plugins.Stats
|
||||
E.Origin = E.Target;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
scriptKillCount++;
|
||||
S.Logger.WriteInfo($"Start ScriptKill {scriptKillCount}");
|
||||
#endif
|
||||
|
||||
await Manager.AddScriptHit(false, E.Time, E.Origin, E.Target, StatManager.GetIdForServer(E.Owner), 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], killInfo[14], killInfo[15]);
|
||||
|
||||
#if DEBUG
|
||||
S.Logger.WriteInfo($"End ScriptKill {scriptKillCount}");
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case GameEvent.EventType.Kill:
|
||||
@ -123,8 +137,21 @@ namespace IW4MAdmin.Plugins.Stats
|
||||
E.Origin = E.Target;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
scriptDamageCount++;
|
||||
S.Logger.WriteInfo($"Start ScriptDamage {scriptDamageCount}");
|
||||
#endif
|
||||
|
||||
await Manager.AddScriptHit(true, E.Time, E.Origin, E.Target, StatManager.GetIdForServer(E.Owner), 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], killInfo[14], killInfo[15]);
|
||||
|
||||
#if DEBUG
|
||||
S.Logger.WriteInfo($"End ScriptDamage {scriptDamageCount}");
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -342,7 +369,7 @@ namespace IW4MAdmin.Plugins.Stats
|
||||
Order = 5,
|
||||
Extra = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_TITLE_ACM7"],
|
||||
Sensitive = true
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,13 @@ namespace SharedLibraryCore.RCon
|
||||
{
|
||||
class ConnectionState
|
||||
{
|
||||
~ConnectionState()
|
||||
{
|
||||
OnComplete.Dispose();
|
||||
OnSentData.Dispose();
|
||||
OnReceivedData.Dispose();
|
||||
}
|
||||
|
||||
public int ConnectionAttempts { get; set; }
|
||||
const int BufferSize = 4096;
|
||||
public readonly byte[] ReceiveBuffer = new byte[BufferSize];
|
||||
@ -123,7 +130,7 @@ namespace SharedLibraryCore.RCon
|
||||
catch (OverflowException)
|
||||
{
|
||||
connectionState.OnComplete.Release(1);
|
||||
throw new NetworkException($"Invalid character expected when converting encodings - {parameters}");
|
||||
throw new NetworkException($"Invalid character encountered when converting encodings - {parameters}");
|
||||
}
|
||||
|
||||
byte[] response = null;
|
||||
@ -152,7 +159,6 @@ namespace SharedLibraryCore.RCon
|
||||
throw new NetworkException("Expected response but got 0 bytes back");
|
||||
}
|
||||
|
||||
connectionState.OnComplete.Release(1);
|
||||
connectionState.ConnectionAttempts = 0;
|
||||
}
|
||||
|
||||
@ -164,9 +170,16 @@ namespace SharedLibraryCore.RCon
|
||||
goto retrySend;
|
||||
}
|
||||
|
||||
connectionState.OnComplete.Release(1);
|
||||
throw new NetworkException(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMUNICATION"].FormatExt(Endpoint));
|
||||
}
|
||||
|
||||
finally
|
||||
{
|
||||
if (connectionState.OnComplete.CurrentCount == 0)
|
||||
{
|
||||
connectionState.OnComplete.Release(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string responseString = defaultEncoding.GetString(response, 0, response.Length) + '\n';
|
||||
@ -258,7 +271,7 @@ namespace SharedLibraryCore.RCon
|
||||
private void OnDataSent(object sender, SocketAsyncEventArgs e)
|
||||
{
|
||||
#if DEBUG == true
|
||||
Log.WriteDebug($"Sent {e.Buffer.Length} bytes to {e.ConnectSocket.RemoteEndPoint.ToString()}");
|
||||
Log.WriteDebug($"Sent {e.Buffer?.Length} bytes to {e.ConnectSocket?.RemoteEndPoint?.ToString()}");
|
||||
#endif
|
||||
ActiveQueries[this.Endpoint].OnSentData.Set();
|
||||
}
|
||||
|
@ -46,8 +46,8 @@
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.2.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
|
||||
<PackageReference Include="Npgsql" Version="4.0.6" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="2.2.0" />
|
||||
<PackageReference Include="Npgsql" Version="4.0.9" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="2.2.4" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="2.2.0" />
|
||||
<PackageReference Include="SimpleCrypto.NetCore" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
|
Loading…
Reference in New Issue
Block a user