diff --git a/Application/Application.csproj b/Application/Application.csproj
index 718eb33f2..4b5d83804 100644
--- a/Application/Application.csproj
+++ b/Application/Application.csproj
@@ -27,6 +27,10 @@
+
+ true
+
+
true
diff --git a/Application/EventParsers/IW4EventParser.cs b/Application/EventParsers/IW4EventParser.cs
index 63c3c97e4..1a206a3ef 100644
--- a/Application/EventParsers/IW4EventParser.cs
+++ b/Application/EventParsers/IW4EventParser.cs
@@ -54,6 +54,18 @@ namespace IW4MAdmin.Application.EventParsers
};
}
+ if (cleanedEventLine[0] == 'D')
+ {
+ return new GameEvent()
+ {
+ Type = GameEvent.EventType.Damage,
+ Data = Regex.Replace(logLine, @"[0-9]+:[0-9]+\ ", "").Trim(),
+ Origin = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[5].ConvertLong()),
+ Target = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[1].ConvertLong()),
+ Owner = server
+ };
+ }
+
if (cleanedEventLine.Contains("ExitLevel"))
{
return new GameEvent()
diff --git a/Application/GameEventHandler.cs b/Application/GameEventHandler.cs
index 86842bceb..4717bf47d 100644
--- a/Application/GameEventHandler.cs
+++ b/Application/GameEventHandler.cs
@@ -27,7 +27,8 @@ namespace IW4MAdmin.Application
#endif
// we need this to keep accurate track of the score
if (gameEvent.Type == GameEvent.EventType.Script ||
- gameEvent.Type == GameEvent.EventType.Kill)
+ gameEvent.Type == GameEvent.EventType.Kill ||
+ gameEvent.Type == GameEvent.EventType.MapChange)
{
#if DEBUG
Manager.GetLogger().WriteDebug($"Added sensitive event to queue");
diff --git a/Application/Main.cs b/Application/Main.cs
index 4c216580e..adc554673 100644
--- a/Application/Main.cs
+++ b/Application/Main.cs
@@ -117,9 +117,12 @@ namespace IW4MAdmin.Application
continue;
}
- Origin.CurrentServer = ServerManager.Servers[0];
- GameEvent E = new GameEvent(GameEvent.EventType.Say, userInput, Origin, null, ServerManager.Servers[0]);
- ServerManager.GetEventHandler().AddEvent(E);
+ if (userInput?.Length > 0)
+ {
+ Origin.CurrentServer = ServerManager.Servers[0];
+ GameEvent E = new GameEvent(GameEvent.EventType.Say, userInput, Origin, null, ServerManager.Servers[0]);
+ ServerManager.GetEventHandler().AddEvent(E);
+ }
Console.Write('>');
} while (ServerManager.Running);
diff --git a/Application/Misc/VPNCheck.cs b/Application/Misc/VPNCheck.cs
index b01b82a1c..12a5e1bdb 100644
--- a/Application/Misc/VPNCheck.cs
+++ b/Application/Misc/VPNCheck.cs
@@ -23,11 +23,11 @@ namespace Application.Misc
string response = await RequestClient.GetStringAsync($"http://v2.api.iphub.info/ip/{ip}");
var responseJson = JsonConvert.DeserializeObject(response);
int blockType = Convert.ToInt32(responseJson["block"]);
- if (responseJson.ContainsKey("isp"))
+ /*if (responseJson.ContainsKey("isp"))
{
if (responseJson["isp"].ToString() == "TSF-IP-CORE")
return true;
- }
+ }*/
return blockType == 1;
}
}
diff --git a/Application/RconParsers/IW4RConParser.cs b/Application/RconParsers/IW4RConParser.cs
index b0a6c2cae..904a111cb 100644
--- a/Application/RconParsers/IW4RConParser.cs
+++ b/Application/RconParsers/IW4RConParser.cs
@@ -80,13 +80,16 @@ namespace Application.RconParsers
if (Status.Length < 4)
throw new ServerException("Unexpected status response received");
+ int validMatches = 0;
foreach (String S in Status)
{
String responseLine = S.Trim();
var regex = Regex.Match(responseLine, StatusRegex, RegexOptions.IgnoreCase);
+
if (regex.Success)
{
+ validMatches++;
int clientNumber = int.Parse(regex.Groups[1].Value);
int score = int.Parse(regex.Groups[2].Value);
@@ -113,10 +116,26 @@ namespace Application.RconParsers
IsBot = ip == 0
};
+ if (P.IsBot)
+ {
+ P.NetworkId = -(P.ClientNumber + 1);
+ P.IPAddress = P.ClientNumber + 1;
+ }
+
StatusPlayers.Add(P);
}
}
+
+ if (Status.Length > 5 && validMatches == 0)
+ {
+ IW4MAdmin.Application.Program.ServerManager.Logger.WriteError("BAD STATUS!");
+ foreach (var s in Status)
+ {
+ IW4MAdmin.Application.Program.ServerManager.Logger.WriteDebug(s);
+ }
+ }
+
return StatusPlayers;
}
}
diff --git a/Application/Server.cs b/Application/Server.cs
index 5cbe9ec1b..f0509d6a4 100644
--- a/Application/Server.cs
+++ b/Application/Server.cs
@@ -161,6 +161,15 @@ namespace IW4MAdmin
var activePenalties = await Manager.GetPenaltyService().GetActivePenaltiesAsync(player.AliasLinkId, player.IPAddress);
var currentBan = activePenalties.FirstOrDefault(b => b.Expires > DateTime.UtcNow);
+ var currentAutoFlag = activePenalties.Where(p => p.Type == Penalty.PenaltyType.Flag && p.PunisherId == 1)
+ .OrderByDescending(p => p.When)
+ .FirstOrDefault();
+
+ // remove their auto flag status after a week
+ if (currentAutoFlag != null && (DateTime.Now - currentAutoFlag.When).TotalDays > 7)
+ {
+ player.Level = Player.Permission.User;
+ }
if (currentBan != null)
{
@@ -189,7 +198,7 @@ namespace IW4MAdmin
var e = new GameEvent(GameEvent.EventType.Connect, "", player, null, this);
Manager.GetEventHandler().AddEvent(e);
- e.OnProcessed.Wait();
+ e.OnProcessed.WaitHandle.WaitOne(5000);
if (!Manager.GetApplicationSettings().Configuration().EnableClientVPNs &&
await VPNCheck.UsingVPN(player.IPAddressString, Manager.GetApplicationSettings().Configuration().IPHubAPIKey))
@@ -218,7 +227,7 @@ namespace IW4MAdmin
var e = new GameEvent(GameEvent.EventType.Disconnect, "", Leaving, null, this);
Manager.GetEventHandler().AddEvent(e);
- e.OnProcessed.Wait();
+ e.OnProcessed.WaitHandle.WaitOne(5000);
Leaving.TotalConnectionTime += (int)(DateTime.UtcNow - Leaving.ConnectionTime).TotalSeconds;
Leaving.LastConnection = DateTime.UtcNow;
@@ -459,10 +468,10 @@ namespace IW4MAdmin
else if (E.Type == GameEvent.EventType.Script)
{
- Manager.GetEventHandler().AddEvent(GameEvent.TranferWaiter(GameEvent.EventType.Kill, E));
+ Manager.GetEventHandler().AddEvent(GameEvent.TransferWaiter(GameEvent.EventType.Kill, E));
}
- if (E.Type == GameEvent.EventType.Say && E.Data.Length >= 2)
+ if (E.Type == GameEvent.EventType.Say && E.Data?.Length >= 2)
{
if (E.Data.Substring(0, 1) == "!" ||
E.Data.Substring(0, 1) == "@" ||
@@ -492,7 +501,7 @@ namespace IW4MAdmin
// reprocess event as a command
- Manager.GetEventHandler().AddEvent(GameEvent.TranferWaiter(GameEvent.EventType.Command, E));
+ Manager.GetEventHandler().AddEvent(GameEvent.TransferWaiter(GameEvent.EventType.Command, E));
}
}
@@ -518,11 +527,20 @@ namespace IW4MAdmin
{
var dict = await this.GetInfoAsync();
- Gametype = dict["gametype"].StripColors();
- Hostname = dict["hostname"].StripColors();
+ if (dict == null)
+ {
+ Logger.WriteWarning("Map change event response doesn't have any data");
+ }
- string mapname = dict["mapname"].StripColors();
- CurrentMap = Maps.Find(m => m.Name == mapname) ?? new Map() { Alias = mapname, Name = mapname };
+ else
+ {
+
+ Gametype = dict["gametype"].StripColors();
+ Hostname = dict["hostname"]?.StripColors();
+
+ string mapname = dict["mapname"]?.StripColors() ?? CurrentMap.Name;
+ CurrentMap = Maps.Find(m => m.Name == mapname) ?? new Map() { Alias = mapname, Name = mapname };
+ }
}
else
@@ -827,8 +845,6 @@ namespace IW4MAdmin
Logger.WriteInfo($"Log file is {logPath}");
#if DEBUG
- // LogFile = new RemoteFile("https://raidmax.org/IW4MAdmin/getlog.php");
-#else
await Broadcast(loc["BROADCAST_ONLINE"]);
#endif
}
diff --git a/Plugins/Stats/Cheat/Detection.cs b/Plugins/Stats/Cheat/Detection.cs
index 77d9feab6..425bdf117 100644
--- a/Plugins/Stats/Cheat/Detection.cs
+++ b/Plugins/Stats/Cheat/Detection.cs
@@ -1,12 +1,12 @@
using SharedLibraryCore.Helpers;
using SharedLibraryCore.Interfaces;
using SharedLibraryCore.Objects;
-using IW4MAdmin.Plugins.Stats.Helpers;
using IW4MAdmin.Plugins.Stats.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
+using System.Text.RegularExpressions;
namespace IW4MAdmin.Plugins.Stats.Cheat
{
@@ -19,6 +19,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
EFClientStatistics ClientStats;
DateTime LastKill;
ILogger Log;
+ Strain Strain;
public Detection(ILogger log, EFClientStatistics clientStats)
{
@@ -26,8 +27,28 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
HitLocationCount = new Dictionary();
foreach (var loc in Enum.GetValues(typeof(IW4Info.HitLocation)))
HitLocationCount.Add((IW4Info.HitLocation)loc, 0);
- LastKill = DateTime.UtcNow;
ClientStats = clientStats;
+ Strain = new Strain();
+ }
+
+ public void ProcessDamage(string damageLine)
+ {
+ string regex = @"^(D);((?:bot[0-9]+)|(?:[A-Z]|[0-9])+);([0-9]+);(axis|allies);(.+);((?:[A-Z]|[0-9])+);([0-9]+);(axis|allies);(.+);((?:[0-9]+|[a-z]+|_)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$";
+
+ var match = Regex.Match(damageLine, regex, RegexOptions.IgnoreCase);
+
+ if (match.Success)
+ {
+ var meansOfDeath = ParseEnum.Get(match.Groups[12].Value, typeof(IW4Info.MeansOfDeath));
+ var hitLocation = ParseEnum.Get(match.Groups[13].Value, typeof(IW4Info.HitLocation));
+
+ if (meansOfDeath == IW4Info.MeansOfDeath.MOD_PISTOL_BULLET ||
+ meansOfDeath == IW4Info.MeansOfDeath.MOD_RIFLE_BULLET ||
+ meansOfDeath == IW4Info.MeansOfDeath.MOD_HEAD_SHOT)
+ {
+ ClientStats.HitLocations.First(hl => hl.Location == hitLocation).HitCount += 1;
+ }
+ }
}
///
@@ -47,20 +68,82 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
RatioAmount = 0
};
+ if (LastKill == DateTime.MinValue)
+ LastKill = DateTime.UtcNow;
+
HitLocationCount[kill.HitLoc]++;
Kills++;
AverageKillTime = (AverageKillTime + (DateTime.UtcNow - LastKill).TotalSeconds) / Kills;
+ #region SNAPSHOTS
+
+
+
+ #endregion
+
#region VIEWANGLES
- double distance = Vector3.Distance(kill.KillOrigin, kill.DeathOrigin);
+ /*double distance = Vector3.Distance(kill.KillOrigin, kill.DeathOrigin);
double x = kill.KillOrigin.X + distance * Math.Cos(kill.ViewAngles.X.ToRadians()) * Math.Cos(kill.ViewAngles.Y.ToRadians());
double y = kill.KillOrigin.Y + (distance * Math.Sin(kill.ViewAngles.X.ToRadians()) * Math.Cos(kill.ViewAngles.Y.ToRadians()));
double z = kill.KillOrigin.Z + distance * Math.Sin((360.0f - kill.ViewAngles.Y).ToRadians());
var trueVector = Vector3.Subtract(kill.KillOrigin, kill.DeathOrigin);
var calculatedVector = Vector3.Subtract(kill.KillOrigin, new Vector3((float)x, (float)y, (float)z));
- double angle = trueVector.AngleBetween(calculatedVector);
+ double angle = trueVector.AngleBetween(calculatedVector);*/
- if (kill.AdsPercent > 0.5 && kill.Distance > 3)
+
+ // make sure it's divisible by 2
+ if (kill.AnglesList.Count % 2 == 0)
+ {
+ double maxDistance = 0;
+ for (int i = 0; i < kill.AnglesList.Count - 1; i += 1)
+ {
+ // Log.WriteDebug($"Fixed 1 {kill.AnglesList[i]}");
+ // Log.WriteDebug($"Fixed 2 {kill.AnglesList[i + 1]}");
+
+ // fix max distance
+ double currDistance = Vector3.AbsoluteDistance(kill.AnglesList[i], kill.AnglesList[i + 1]);
+ //Log.WriteDebug($"Distance {currDistance}");
+ if (currDistance > maxDistance)
+ {
+ maxDistance = currDistance;
+ }
+ }
+
+ double realAgainstPredict = Vector3.AbsoluteDistance(kill.ViewAngles, kill.AnglesList[10]);
+
+ var hitLoc = ClientStats.HitLocations
+ .First(hl => hl.Location == kill.HitLoc);
+ float previousAverage = hitLoc.HitOffsetAverage;
+ double newAverage = (previousAverage * (hitLoc.HitCount - 1) + realAgainstPredict) / hitLoc.HitCount;
+ hitLoc.HitOffsetAverage = (float)newAverage;
+
+ if (maxDistance > hitLoc.MaxAngleDistance)
+ hitLoc.MaxAngleDistance = (float)maxDistance;
+
+ if (double.IsNaN(hitLoc.HitOffsetAverage))
+ {
+ Log.WriteWarning("[Detection::ProcessKill] HitOffsetAvgerage NaN");
+ Log.WriteDebug($"{previousAverage}-{hitLoc.HitCount}-{hitLoc}-{newAverage}");
+ hitLoc.HitOffsetAverage = 0f;
+ }
+
+#if DEBUG
+ Log.WriteDebug($"MaxDistance={maxDistance}, PredictVsReal={realAgainstPredict}");
+#endif
+ }
+
+ var currentStrain = Strain.GetStrain(kill.ViewAngles, (kill.When - LastKill).TotalMilliseconds);
+
+ if (currentStrain > ClientStats.MaxStrain)
+ {
+ ClientStats.MaxStrain = currentStrain;
+ }
+
+#if DEBUG
+ Log.WriteDebug($"Current Strain: {currentStrain}");
+#endif
+
+ /*if (kill.AdsPercent > 0.5 && kill.Distance > 3)
{
var hitLoc = ClientStats.HitLocations
.First(hl => hl.Location == kill.HitLoc);
@@ -74,7 +157,9 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
Log.WriteDebug($"{previousAverage}-{hitLoc.HitCount}-{hitLoc}-{newAverage}");
hitLoc.HitOffsetAverage = 0f;
}
- }
+ }*/
+
+ LastKill = kill.When;
#endregion
diff --git a/Plugins/Stats/Cheat/Strain.cs b/Plugins/Stats/Cheat/Strain.cs
new file mode 100644
index 000000000..046d25a2f
--- /dev/null
+++ b/Plugins/Stats/Cheat/Strain.cs
@@ -0,0 +1,47 @@
+using SharedLibraryCore.Helpers;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace IW4MAdmin.Plugins.Stats.Cheat
+{
+ class Strain
+ {
+ private static double StrainDecayBase = 0.15;
+ private double CurrentStrain;
+ private Vector3 LastAngle;
+
+ public double GetStrain(Vector3 newAngle, double deltaTime)
+ {
+ if (LastAngle == null)
+ LastAngle = newAngle;
+
+ double decayFactor = GetDecay(deltaTime);
+ CurrentStrain *= decayFactor;
+
+ double[] distance = Helpers.Extensions.AngleStuff(newAngle, LastAngle);
+
+ // this happens on first kill
+ if ((distance[0] == 0 && distance[1] == 0) ||
+ deltaTime == 0 ||
+ double.IsNaN(CurrentStrain))
+ {
+ return CurrentStrain;
+ }
+
+ double newStrain = Math.Pow(distance[0] + distance[1], 0.99) / deltaTime;
+
+ if (newStrain + CurrentStrain > 0.25)
+ {
+ Console.WriteLine($"{LastAngle}-{newAngle}-{decayFactor}-{CurrentStrain}-{newStrain}-{distance[0]}-{distance[1]}-{deltaTime}");
+ }
+
+ CurrentStrain += newStrain;
+ LastAngle = newAngle;
+
+ return CurrentStrain;
+ }
+
+ private double GetDecay(double deltaTime) => Math.Pow(StrainDecayBase, deltaTime / 1000.0);
+ }
+}
\ No newline at end of file
diff --git a/Plugins/Stats/Helpers/Extensions.cs b/Plugins/Stats/Helpers/Extensions.cs
index 3caea8fcf..e771607ae 100644
--- a/Plugins/Stats/Helpers/Extensions.cs
+++ b/Plugins/Stats/Helpers/Extensions.cs
@@ -22,5 +22,13 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
public static float ToRadians(this float value) => (float)Math.PI * value / 180.0f;
public static float ToDegrees(this float value) => value * 180.0f / (float)Math.PI;
+
+ public static double[] AngleStuff(Vector3 a, Vector3 b)
+ {
+ double deltaX = 180.0 -Math.Abs(Math.Abs(a.X - b.X) - 180.0);
+ double deltaY = 180.0 - Math.Abs(Math.Abs(a.Y - b.Y) - 180.0);
+
+ return new[] { deltaX, deltaY };
+ }
}
}
diff --git a/Plugins/Stats/Helpers/StatManager.cs b/Plugins/Stats/Helpers/StatManager.cs
index dcd1a6150..6b530eca1 100644
--- a/Plugins/Stats/Helpers/StatManager.cs
+++ b/Plugins/Stats/Helpers/StatManager.cs
@@ -130,8 +130,14 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
await statsSvc.ClientStatSvc.SaveChangesAsync();
}
+ else
+ {
+ // todo: look at this more
+ statsSvc.ClientStatSvc.Update(clientStats);
+ }
+
// migration for previous existing stats
- else if (clientStats.HitLocations.Count == 0)
+ if (clientStats.HitLocations.Count == 0)
{
clientStats.HitLocations = Enum.GetValues(typeof(IW4Info.HitLocation)).OfType().Select(hl => new EFHitLocationCount()
{
@@ -140,7 +146,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
Location = hl
})
.ToList();
- await statsSvc.ClientStatSvc.SaveChangesAsync();
+ //await statsSvc.ClientStatSvc.SaveChangesAsync();
}
// set these on connecting
@@ -156,10 +162,8 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
if (!detectionStats.TryAdd(pl.ClientId, new Cheat.Detection(Log, clientStats)))
Log.WriteDebug("Could not add client to detection");
-
- // todo: look at this more
- statsSvc.ClientStatSvc.Update(clientStats);
- await statsSvc.ClientStatSvc.SaveChangesAsync();
+ /*
+ await statsSvc.ClientStatSvc.SaveChangesAsync();*/
return clientStats;
}
@@ -197,23 +201,32 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
playerStats.TryRemove(pl.ClientId, out EFClientStatistics removedValue3);
detectionStats.TryRemove(pl.ClientId, out Cheat.Detection removedValue4);
- /* // sync their stats before they leave
- clientStats = UpdateStats(clientStats);*/
+ /* // sync their stats before they leave
+ clientStats = UpdateStats(clientStats);*/
// todo: should this be saved every disconnect?
statsSvc.ClientStatSvc.Update(clientStats);
await statsSvc.ClientStatSvc.SaveChangesAsync();
// increment the total play time
serverStats.TotalPlayTime += (int)(DateTime.UtcNow - pl.LastConnection).TotalSeconds;
- await statsSvc.ServerStatsSvc.SaveChangesAsync();
+ //await statsSvc.ServerStatsSvc.SaveChangesAsync();
+ }
+
+ public void AddDamageEvent(string eventLine, int clientId, int serverId)
+ {
+ if (Plugin.Config.Configuration().EnableAntiCheat)
+ {
+ var clientDetection = Servers[serverId].PlayerDetections[clientId];
+ clientDetection.ProcessDamage(eventLine);
+ }
}
///
/// Process stats for kill event
///
///
- public async Task AddScriptKill(Player attacker, Player victim, int serverId, string map, string hitLoc, string type,
- string damage, string weapon, string killOrigin, string deathOrigin, string viewAngles, string offset, string isKillstreakKill, string Ads)
+ public async Task AddScriptKill(DateTime time, Player attacker, Player victim, int serverId, string map, string hitLoc, string type,
+ string damage, string weapon, string killOrigin, string deathOrigin, string viewAngles, string offset, string isKillstreakKill, string Ads, string snapAngles)
{
var statsSvc = ContextThreads[serverId];
Vector3 vDeathOrigin = null;
@@ -235,6 +248,22 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
return;
}
+ var snapshotAngles = new List();
+
+ try
+ {
+ foreach(string angle in snapAngles.Split(':', StringSplitOptions.RemoveEmptyEntries))
+ {
+ snapshotAngles.Add(Vector3.Parse(angle).FixIW4Angles());
+ }
+ }
+
+ catch (FormatException)
+ {
+ Log.WriteWarning("Could not parse snapshot angles");
+ return;
+ }
+
var kill = new EFClientKill()
{
Active = true,
@@ -250,9 +279,10 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
Weapon = ParseEnum.Get(weapon, typeof(IW4Info.WeaponName)),
ViewAngles = vViewAngles,
TimeOffset = Int64.Parse(offset),
- When = DateTime.UtcNow,
+ When = time,
IsKillstreakKill = isKillstreakKill[0] != '0',
- AdsPercent = float.Parse(Ads)
+ AdsPercent = float.Parse(Ads),
+ AnglesList = snapshotAngles
};
if (kill.DeathType == IW4Info.MeansOfDeath.MOD_SUICIDE &&
@@ -280,7 +310,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
clientStats.HitLocations.Single(hl => hl.Location == kill.HitLoc).HitCount += 1;
statsSvc.ClientStatSvc.Update(clientStats);
- await statsSvc.ClientStatSvc.SaveChangesAsync();
+ // await statsSvc.ClientStatSvc.SaveChangesAsync();
}
//statsSvc.KillStatsSvc.Insert(kill);
@@ -290,6 +320,12 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
{
async Task executePenalty(Cheat.DetectionPenaltyResult penalty)
{
+ // prevent multiple bans from occuring
+ if (attacker.Level == Player.Permission.Banned)
+ {
+ return;
+ }
+
switch (penalty.ClientPenalty)
{
case Penalty.PenaltyType.Ban:
@@ -312,6 +348,11 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
await executePenalty(clientDetection.ProcessKill(kill));
await executePenalty(clientDetection.ProcessTotalRatio(clientStats));
+
+#if DEBUG
+ statsSvc.ClientStatSvc.Update(clientStats);
+ await statsSvc.ClientStatSvc.SaveChangesAsync();
+#endif
}
}
@@ -326,7 +367,8 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
catch (KeyNotFoundException)
{
- Log.WriteError($"[Stats::AddStandardKill] kill attacker ClientId is invalid {attacker.ClientId}-{attacker}");
+ // happens when the client has disconnected before the last status update
+ Log.WriteWarning($"[Stats::AddStandardKill] kill attacker ClientId is invalid {attacker.ClientId}-{attacker}");
return;
}
@@ -338,7 +380,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
catch (KeyNotFoundException)
{
- Log.WriteError($"[Stats::AddStandardKill] kill victim ClientId is invalid {victim.ClientId}-{victim}");
+ Log.WriteWarning($"[Stats::AddStandardKill] kill victim ClientId is invalid {victim.ClientId}-{victim}");
return;
}
@@ -393,7 +435,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
var statsSvc = ContextThreads[serverId];
statsSvc.ClientStatSvc.Update(attackerStats);
statsSvc.ClientStatSvc.Update(victimStats);
- await statsSvc.ClientStatSvc.SaveChangesAsync();
+ //await statsSvc.ClientStatSvc.SaveChangesAsync();
}
///
@@ -451,6 +493,11 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
// calculate the players Score Per Minute for the current session
int scoreDifference = clientStats.RoundScore - clientStats.LastScore;
+
+ // todo: fix the SPM for TEAMDAMAGE
+ if (scoreDifference < 0)
+ scoreDifference = clientStats.RoundScore;
+
double killSPM = scoreDifference / timeSinceLastCalc;
// calculate how much the KDR should weigh
diff --git a/Plugins/Stats/Models/EFClientKill.cs b/Plugins/Stats/Models/EFClientKill.cs
index c483c0c10..688c73b47 100644
--- a/Plugins/Stats/Models/EFClientKill.cs
+++ b/Plugins/Stats/Models/EFClientKill.cs
@@ -4,6 +4,7 @@ using SharedLibraryCore.Database.Models;
using System.ComponentModel.DataAnnotations.Schema;
using SharedLibraryCore.Helpers;
using System.ComponentModel.DataAnnotations;
+using System.Collections.Generic;
namespace IW4MAdmin.Plugins.Stats.Models
{
@@ -38,5 +39,7 @@ namespace IW4MAdmin.Plugins.Stats.Models
public bool IsKillstreakKill { get; set; }
[NotMapped]
public float AdsPercent { get; set; }
+ [NotMapped]
+ public List AnglesList { get; set; }
}
}
diff --git a/Plugins/Stats/Models/EFClientStatistics.cs b/Plugins/Stats/Models/EFClientStatistics.cs
index 610f4ab9b..a462b872c 100644
--- a/Plugins/Stats/Models/EFClientStatistics.cs
+++ b/Plugins/Stats/Models/EFClientStatistics.cs
@@ -36,6 +36,8 @@ namespace IW4MAdmin.Plugins.Stats.Models
public double Skill { get; set; }
[Required]
public int TimePlayed { get; set; }
+ [Required]
+ public double MaxStrain { get; set; }
[NotMapped]
public float AverageHitOffset
diff --git a/Plugins/Stats/Models/EFHitLocationCount.cs b/Plugins/Stats/Models/EFHitLocationCount.cs
index 13495ea01..4d7fa192b 100644
--- a/Plugins/Stats/Models/EFHitLocationCount.cs
+++ b/Plugins/Stats/Models/EFHitLocationCount.cs
@@ -14,6 +14,9 @@ namespace IW4MAdmin.Plugins.Stats.Models
public int HitCount { get; set; }
[Required]
public float HitOffsetAverage { get; set; }
+ [Required]
+ public float MaxAngleDistance { get; set; }
+ [Required]
public int ClientId { get; set; }
[ForeignKey("ClientId"), Column(Order = 0 )]
public EFClient Client { get; set; }
diff --git a/Plugins/Stats/Plugin.cs b/Plugins/Stats/Plugin.cs
index 27dd74c4e..17f489554 100644
--- a/Plugins/Stats/Plugin.cs
+++ b/Plugins/Stats/Plugin.cs
@@ -74,13 +74,16 @@ namespace IW4MAdmin.Plugins.Stats
case GameEvent.EventType.Kill:
string[] killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0];
if (killInfo.Length >= 9 && killInfo[0].Contains("ScriptKill") && E.Owner.CustomCallback)
- await Manager.AddScriptKill(E.Origin, E.Target, S.GetHashCode(), S.CurrentMap.Name, killInfo[7], killInfo[8],
- killInfo[5], killInfo[6], killInfo[3], killInfo[4], killInfo[9], killInfo[10], killInfo[11], killInfo[12]);
+ await Manager.AddScriptKill(E.Time, E.Origin, E.Target, S.GetHashCode(), S.CurrentMap.Name, killInfo[7], killInfo[8],
+ killInfo[5], killInfo[6], killInfo[3], killInfo[4], killInfo[9], killInfo[10], killInfo[11], killInfo[12], killInfo[13]);
else if (!E.Owner.CustomCallback)
await Manager.AddStandardKill(E.Origin, E.Target);
break;
case GameEvent.EventType.Death:
break;
+ case GameEvent.EventType.Damage:
+ Manager.AddDamageEvent(E.Data, E.Origin.ClientId, E.Owner.GetHashCode());
+ break;
}
}
@@ -146,6 +149,8 @@ namespace IW4MAdmin.Plugins.Stats
double abdomenRatio = 0;
double chestAbdomenRatio = 0;
double hitOffsetAverage = 0;
+ double maxStrain = clientStats.Count(c=> c.MaxStrain > 0) == 0 ? 0 : clientStats.Max(cs => cs.MaxStrain);
+ //double maxAngle = clientStats.Max(cs => cs.HitLocations.Max(hl => hl.MaxAngleDistance));
if (clientStats.Where(cs => cs.HitLocations.Count > 0).FirstOrDefault() != null)
{
@@ -172,9 +177,9 @@ namespace IW4MAdmin.Plugins.Stats
{
new ProfileMeta()
{
- Key = "Chest Ratio",
- Value = chestRatio,
- Sensitive = true
+ Key = "Chest Ratio",
+ Value = chestRatio,
+ Sensitive = true
},
new ProfileMeta()
{
@@ -197,9 +202,21 @@ namespace IW4MAdmin.Plugins.Stats
new ProfileMeta()
{
Key = "Hit Offset Average",
- Value = $"{Math.Round(((float)hitOffsetAverage).ToDegrees(), 4)}°",
+ Value = $"{Math.Round(((float)hitOffsetAverage), 4)}°",
Sensitive = true
- }
+ },
+ new ProfileMeta()
+ {
+ Key = "Max Strain",
+ Value = Math.Round(maxStrain, 3),
+ Sensitive = true
+ },
+ /*new ProfileMeta()
+ {
+ Key = "Max Angle Distance",
+ Value = Math.Round(maxAngle, 1),
+ Sensitive = true
+ }*/
};
}
diff --git a/SharedLibraryCore/Commands/NativeCommands.cs b/SharedLibraryCore/Commands/NativeCommands.cs
index 0fdc34071..f49eb96a5 100644
--- a/SharedLibraryCore/Commands/NativeCommands.cs
+++ b/SharedLibraryCore/Commands/NativeCommands.cs
@@ -1,7 +1,6 @@
using Microsoft.EntityFrameworkCore;
using SharedLibraryCore.Database;
using SharedLibraryCore.Database.Models;
-using SharedLibraryCore.Exceptions;
using SharedLibraryCore.Objects;
using SharedLibraryCore.Services;
using System;
@@ -36,6 +35,8 @@ namespace SharedLibraryCore.Commands
{
E.Origin.Level = Player.Permission.Owner;
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationSet["COMMANDS_OWNER_SUCCESS"]);
+ // so setpassword/login works
+ E.Owner.Manager.GetPrivilegedClients().Add(E.Origin.ClientId, E.Origin);
await E.Owner.Manager.GetClientService().Update(E.Origin);
}
else
diff --git a/SharedLibraryCore/Event.cs b/SharedLibraryCore/Event.cs
index 3d7a36aad..29c013928 100644
--- a/SharedLibraryCore/Event.cs
+++ b/SharedLibraryCore/Event.cs
@@ -1,7 +1,4 @@
using System;
-using System.Linq;
-using System.Text;
-using System.Text.RegularExpressions;
using System.Threading;
using SharedLibraryCore.Objects;
@@ -50,14 +47,16 @@ namespace SharedLibraryCore
Target = T;
Owner = S;
OnProcessed = new ManualResetEventSlim();
+ Time = DateTime.UtcNow;
}
public GameEvent()
{
- OnProcessed = new ManualResetEventSlim();
+ OnProcessed = new ManualResetEventSlim();
+ Time = DateTime.UtcNow;
}
- public static GameEvent TranferWaiter(EventType newType, GameEvent e)
+ public static GameEvent TransferWaiter(EventType newType, GameEvent e)
{
var newEvent = new GameEvent()
{
@@ -69,11 +68,12 @@ namespace SharedLibraryCore
Owner = e.Owner,
Remote = e.Remote,
Target = e.Target,
- Type = newType
+ Type = newType,
};
// hack: prevent the previous event from completing until this one is done
e.OnProcessed = new ManualResetEventSlim();
+ newEvent.Time = e.Time;
return newEvent;
}
@@ -87,5 +87,6 @@ namespace SharedLibraryCore
public Boolean Remote = false;
public object Extra { get; set; }
public ManualResetEventSlim OnProcessed { get; set; }
+ public DateTime Time { get; private set; }
}
}
diff --git a/SharedLibraryCore/Helpers/Vector3.cs b/SharedLibraryCore/Helpers/Vector3.cs
index 6619ed8bf..00cb0fbf4 100644
--- a/SharedLibraryCore/Helpers/Vector3.cs
+++ b/SharedLibraryCore/Helpers/Vector3.cs
@@ -44,6 +44,19 @@ namespace SharedLibraryCore.Helpers
return Math.Sqrt(Math.Pow(b.X - a.X, 2) + Math.Pow(b.Y - a.Y, 2) + Math.Pow(b.Z - a.Z, 2));
}
+ public static double AbsoluteDistance(Vector3 a, Vector3 b)
+ {
+ double deltaX = Math.Abs(b.X -a.X);
+ double deltaY = Math.Abs(b.Y - a.Y);
+ double deltaZ = Math.Abs(b.Z - a.Z);
+
+ double dx = deltaX < 360.0 / 2 ? deltaX : 360.0 - deltaX;
+ double dy = deltaY < 360.0 / 2 ? deltaY : 360.0 - deltaY;
+ double dz = deltaZ < 360.0 / 2 ? deltaZ : 360.0 - deltaZ;
+
+ return Math.Sqrt((dx * dx) + (dy * dy) + (dz * dx));
+ }
+
public static Vector3 Subtract(Vector3 a, Vector3 b) => new Vector3(b.X - a.X, b.Y - a.Y, b.Z - a.Z);
public double DotProduct(Vector3 a) => (a.X * this.X) + (a.Y * this.Y) + (a.Z * this.Z);
diff --git a/SharedLibraryCore/Migrations/20180502195450_Update.Designer.cs b/SharedLibraryCore/Migrations/20180502195450_Update.Designer.cs
new file mode 100644
index 000000000..9e2be79da
--- /dev/null
+++ b/SharedLibraryCore/Migrations/20180502195450_Update.Designer.cs
@@ -0,0 +1,434 @@
+//
+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("20180502195450_Update")]
+ partial class Update
+ {
+ 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("Kills");
+
+ b.Property("MaxStrain");
+
+ 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/20180502195450_Update.cs b/SharedLibraryCore/Migrations/20180502195450_Update.cs
new file mode 100644
index 000000000..e446ed36f
--- /dev/null
+++ b/SharedLibraryCore/Migrations/20180502195450_Update.cs
@@ -0,0 +1,35 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+using System;
+using System.Collections.Generic;
+
+namespace SharedLibraryCore.Migrations
+{
+ public partial class Update : Migration
+ {
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AddColumn(
+ name: "MaxAngleDistance",
+ table: "EFHitLocationCounts",
+ nullable: false,
+ defaultValue: 0f);
+
+ migrationBuilder.AddColumn(
+ name: "MaxStrain",
+ table: "EFClientStatistics",
+ nullable: false,
+ defaultValue: 0.0);
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropColumn(
+ name: "MaxAngleDistance",
+ table: "EFHitLocationCounts");
+
+ migrationBuilder.DropColumn(
+ name: "MaxStrain",
+ table: "EFClientStatistics");
+ }
+ }
+}
diff --git a/SharedLibraryCore/Migrations/DatabaseContextModelSnapshot.cs b/SharedLibraryCore/Migrations/DatabaseContextModelSnapshot.cs
new file mode 100644
index 000000000..e3c6c51e9
--- /dev/null
+++ b/SharedLibraryCore/Migrations/DatabaseContextModelSnapshot.cs
@@ -0,0 +1,433 @@
+//
+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))]
+ partial class DatabaseContextModelSnapshot : ModelSnapshot
+ {
+ protected override void BuildModel(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("Kills");
+
+ b.Property("MaxStrain");
+
+ 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/RCon/Connection.cs b/SharedLibraryCore/RCon/Connection.cs
index ebb3be17c..04cf6561a 100644
--- a/SharedLibraryCore/RCon/Connection.cs
+++ b/SharedLibraryCore/RCon/Connection.cs
@@ -1,6 +1,7 @@
using SharedLibraryCore.Exceptions;
using SharedLibraryCore.Interfaces;
using System;
+using System.Collections.Concurrent;
using System.Linq;
using System.Net;
using System.Net.Sockets;
@@ -32,11 +33,31 @@ namespace SharedLibraryCore.RCon
}
}
+ class ResponseEvent
+ {
+ public int Id { get; set; }
+ public string[] Response { get; set; }
+ public Task Awaiter
+ {
+ get
+ {
+ return Task.Run(() => FinishedEvent.Wait());
+ }
+ }
+ private ManualResetEventSlim FinishedEvent;
+
+ public ResponseEvent()
+ {
+ FinishedEvent = new ManualResetEventSlim();
+ }
+ }
+
public class Connection
{
public IPEndPoint Endpoint { get; private set; }
public string RConPassword { get; private set; }
- Socket ServerConnection;
+ public ConcurrentQueue ResponseQueue;
+ //Socket ServerConnection;
ILogger Log;
int FailedSends;
int FailedReceives;
@@ -56,27 +77,13 @@ namespace SharedLibraryCore.RCon
OnConnected = new ManualResetEvent(false);
OnSent = new ManualResetEvent(false);
OnReceived = new ManualResetEvent(false);
-
- try
- {
- ServerConnection = new Socket(Endpoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
- ServerConnection.BeginConnect(Endpoint, new AsyncCallback(OnConnectedCallback), ServerConnection);
- if (!OnConnected.WaitOne(StaticHelpers.SocketTimeout))
- throw new SocketException((int)SocketError.TimedOut);
- FailedSends = 0;
- }
-
- catch (SocketException e)
- {
- throw new NetworkException(e.Message);
- }
}
~Connection()
{
- ServerConnection.Shutdown(SocketShutdown.Both);
+ /*ServerConnection.Shutdown(SocketShutdown.Both);
ServerConnection.Close();
- ServerConnection.Dispose();
+ ServerConnection.Dispose();*/
}
private void OnConnectedCallback(IAsyncResult ar)
@@ -106,8 +113,9 @@ namespace SharedLibraryCore.RCon
{
int sentByteNum = serverConnection.EndSend(ar);
#if DEBUG
- Log.WriteDebug($"Sent {sentByteNum} bytes to {ServerConnection.RemoteEndPoint}");
+ Log.WriteDebug($"Sent {sentByteNum} bytes to {serverConnection.RemoteEndPoint}");
#endif
+ // this is where we override our await to make it
OnSent.Set();
}
@@ -128,7 +136,7 @@ namespace SharedLibraryCore.RCon
if (bytesRead > 0)
{
#if DEBUG
- Log.WriteDebug($"Received {bytesRead} bytes from {ServerConnection.RemoteEndPoint}");
+ Log.WriteDebug($"Received {bytesRead} bytes from {serverConnection.RemoteEndPoint}");
#endif
FailedReceives = 0;
connectionState.ResponseString.Append(Utilities.EncodingType.GetString(connectionState.Buffer, 0, bytesRead).TrimEnd('\0') + '\n');
@@ -138,7 +146,7 @@ namespace SharedLibraryCore.RCon
if (serverConnection.Available > 0)
{
- ServerConnection.BeginReceive(connectionState.Buffer, 0, connectionState.Buffer.Length, 0,
+ serverConnection.BeginReceive(connectionState.Buffer, 0, connectionState.Buffer.Length, 0,
new AsyncCallback(OnReceivedCallback), connectionState);
}
else
@@ -158,14 +166,19 @@ namespace SharedLibraryCore.RCon
{
}
+
+ catch (ObjectDisposedException)
+ {
+ Log.WriteWarning($"Tried to check for more available bytes for disposed socket on {Endpoint}");
+ }
}
public async Task SendQueryAsync(StaticHelpers.QueryType type, string parameters = "", bool waitForResponse = true)
{
// will this really prevent flooding?
- if ((DateTime.Now - LastQuery).TotalMilliseconds < 150)
+ if ((DateTime.Now - LastQuery).TotalMilliseconds < 250)
{
- await Task.Delay(150);
+ await Task.Delay(250);
}
LastQuery = DateTime.Now;
@@ -191,119 +204,128 @@ namespace SharedLibraryCore.RCon
break;
}
- retrySend:
- try
+ using (var socketConnection = new Socket(Endpoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp))
{
- ServerConnection.BeginSend(payload, 0, payload.Length, 0, new AsyncCallback(OnSentCallback), ServerConnection);
- bool success = await Task.FromResult(OnSent.WaitOne(StaticHelpers.SocketTimeout));
+ socketConnection.BeginConnect(Endpoint, new AsyncCallback(OnConnectedCallback), socketConnection);
- if (!success)
+ retrySend:
+ try
{
- FailedSends++;
-#if DEBUG
- Log.WriteDebug($"{FailedSends} failed sends to {ServerConnection.RemoteEndPoint.ToString()}");
-#endif
- if (FailedSends < 4)
- goto retrySend;
- else if (FailedSends == 4)
- Log.WriteError($"Failed to send data to {ServerConnection.RemoteEndPoint}");
- }
+
+ if (!OnConnected.WaitOne(StaticHelpers.SocketTimeout))
+ throw new SocketException((int)SocketError.TimedOut);
- else
- {
- if (FailedSends >= 4)
+ socketConnection.BeginSend(payload, 0, payload.Length, 0, new AsyncCallback(OnSentCallback), socketConnection);
+ bool success = await Task.FromResult(OnSent.WaitOne(StaticHelpers.SocketTimeout));
+
+ if (!success)
{
- Log.WriteVerbose($"Resumed send RCon connection with {ServerConnection.RemoteEndPoint}");
- FailedSends = 0;
+ FailedSends++;
+#if DEBUG
+ Log.WriteDebug($"{FailedSends} failed sends to {socketConnection.RemoteEndPoint.ToString()}");
+#endif
+ if (FailedSends < 4)
+ goto retrySend;
+ else if (FailedSends == 4)
+ Log.WriteError($"Failed to send data to {socketConnection.RemoteEndPoint}");
+ }
+
+ else
+ {
+ if (FailedSends >= 4)
+ {
+ Log.WriteVerbose($"Resumed send RCon connection with {socketConnection.RemoteEndPoint}");
+ FailedSends = 0;
+ }
}
}
- }
- catch (SocketException e)
- {
- // this result is normal if the server is not listening
- if (e.NativeErrorCode != (int)SocketError.ConnectionReset &&
- e.NativeErrorCode != (int)SocketError.TimedOut)
- throw new NetworkException($"Unexpected error while sending data to server - {e.Message}");
- }
-
- if (!waitForResponse)
- return await Task.FromResult(new string[] { "" });
-
- var connectionState = new ConnectionState(ServerConnection);
-
- retryReceive:
- try
- {
- ServerConnection.BeginReceive(connectionState.Buffer, 0, connectionState.Buffer.Length, 0,
- new AsyncCallback(OnReceivedCallback), connectionState);
- bool success = await Task.FromResult(OnReceived.WaitOne(StaticHelpers.SocketTimeout));
-
- if (!success)
+ catch (SocketException e)
{
+ // this result is normal if the server is not listening
+ if (e.NativeErrorCode != (int)SocketError.ConnectionReset &&
+ e.NativeErrorCode != (int)SocketError.TimedOut)
+ throw new NetworkException($"Unexpected error while sending data to server - {e.Message}");
+ }
- FailedReceives++;
+ if (!waitForResponse)
+ return await Task.FromResult(new string[] { "" });
+
+ var connectionState = new ConnectionState(socketConnection);
+
+ retryReceive:
+ try
+ {
+ socketConnection.BeginReceive(connectionState.Buffer, 0, connectionState.Buffer.Length, 0,
+ new AsyncCallback(OnReceivedCallback), connectionState);
+ bool success = await Task.FromResult(OnReceived.WaitOne(StaticHelpers.SocketTimeout));
+
+ if (!success)
+ {
+
+ FailedReceives++;
#if DEBUG
- Log.WriteDebug($"{FailedReceives} failed receives from {ServerConnection.RemoteEndPoint.ToString()}");
+ Log.WriteDebug($"{FailedReceives} failed receives from {socketConnection.RemoteEndPoint.ToString()}");
#endif
- if (FailedReceives < 4)
- goto retrySend;
+ if (FailedReceives < 4)
+ goto retrySend;
+ else if (FailedReceives == 4)
+ {
+ Log.WriteError($"Failed to receive data from {socketConnection.RemoteEndPoint} after {FailedReceives} tries");
+ }
+
+ if (FailedReceives >= 4)
+ {
+ throw new NetworkException($"Could not receive data from {socketConnection.RemoteEndPoint}");
+ }
+ }
+
+ else
+ {
+ if (FailedReceives >= 4)
+ {
+ Log.WriteVerbose($"Resumed receive RCon connection from {socketConnection.RemoteEndPoint}");
+ FailedReceives = 0;
+ }
+ }
+ }
+
+ catch (SocketException e)
+ {
+ // this result is normal if the server is not listening
+ if (e.NativeErrorCode != (int)SocketError.ConnectionReset &&
+ e.NativeErrorCode != (int)SocketError.TimedOut)
+ throw new NetworkException($"Unexpected error while receiving data from server - {e.Message}");
+ else if (FailedReceives < 4)
+ {
+ goto retryReceive;
+ }
+
else if (FailedReceives == 4)
{
- Log.WriteError($"Failed to receive data from {ServerConnection.RemoteEndPoint} after {FailedReceives} tries");
+ Log.WriteError($"Failed to receive data from {socketConnection.RemoteEndPoint} after {FailedReceives} tries");
}
if (FailedReceives >= 4)
{
- throw new NetworkException($"Could not receive data from {ServerConnection.RemoteEndPoint}");
+ throw new NetworkException(e.Message);
}
}
- else
+ string queryResponse = response;
+
+ if (queryResponse.Contains("Invalid password"))
+ throw new NetworkException("RCON password is invalid");
+ if (queryResponse.ToString().Contains("rcon_password"))
+ throw new NetworkException("RCON password has not been set");
+
+ string[] splitResponse = queryResponse.Split(new char[]
{
- if (FailedReceives >= 4)
- {
- Log.WriteVerbose($"Resumed receive RCon connection from {ServerConnection.RemoteEndPoint}");
- FailedReceives = 0;
- }
- }
- }
-
- catch (SocketException e)
- {
- // this result is normal if the server is not listening
- if (e.NativeErrorCode != (int)SocketError.ConnectionReset &&
- e.NativeErrorCode != (int)SocketError.TimedOut)
- throw new NetworkException($"Unexpected error while receiving data from server - {e.Message}");
- else if (FailedReceives < 4)
- {
- goto retryReceive;
- }
-
- else if (FailedReceives == 4)
- {
- Log.WriteError($"Failed to receive data from {ServerConnection.RemoteEndPoint} after {FailedReceives} tries");
- }
-
- if (FailedReceives >= 4)
- {
- throw new NetworkException(e.Message);
- }
- }
-
- string queryResponse = response;
-
- if (queryResponse.Contains("Invalid password"))
- throw new NetworkException("RCON password is invalid");
- if (queryResponse.ToString().Contains("rcon_password"))
- throw new NetworkException("RCON password has not been set");
-
- string[] splitResponse = queryResponse.Split(new char[]
- {
'\n'
- }, StringSplitOptions.RemoveEmptyEntries)
- .Select(line => line.Trim()).ToArray();
- return splitResponse;
+ }, StringSplitOptions.RemoveEmptyEntries)
+ .Select(line => line.Trim()).ToArray();
+ return splitResponse;
+ }
}
}
}
diff --git a/SharedLibraryCore/RCon/StaticHelpers.cs b/SharedLibraryCore/RCon/StaticHelpers.cs
index 04d10d06d..0782d0391 100644
--- a/SharedLibraryCore/RCon/StaticHelpers.cs
+++ b/SharedLibraryCore/RCon/StaticHelpers.cs
@@ -13,6 +13,6 @@ namespace SharedLibraryCore.RCon
}
public static char SeperatorChar = (char)int.Parse("0a", System.Globalization.NumberStyles.AllowHexSpecifier);
- public static readonly TimeSpan SocketTimeout = new TimeSpan(0, 0, 2);
+ public static readonly TimeSpan SocketTimeout = new TimeSpan(0, 0, 10);
}
}
diff --git a/SharedLibraryCore/SharedLibraryCore.csproj b/SharedLibraryCore/SharedLibraryCore.csproj
index 109637529..13c869646 100644
--- a/SharedLibraryCore/SharedLibraryCore.csproj
+++ b/SharedLibraryCore/SharedLibraryCore.csproj
@@ -12,6 +12,10 @@
Debug;Release;Prerelease
+
+
+
+
diff --git a/WebfrontCore/Controllers/ConsoleController.cs b/WebfrontCore/Controllers/ConsoleController.cs
index 8014a2ec6..a9c604dad 100644
--- a/WebfrontCore/Controllers/ConsoleController.cs
+++ b/WebfrontCore/Controllers/ConsoleController.cs
@@ -28,7 +28,6 @@ namespace WebfrontCore.Controllers
public async Task ExecuteAsync(int serverId, string command)
{
-
var server = Manager.GetServers().First(s => s.GetHashCode() == serverId);
var client = new Player()
{
@@ -50,7 +49,7 @@ namespace WebfrontCore.Controllers
Manager.GetEventHandler().AddEvent(remoteEvent);
// wait for the event to process
- await Task.Run(() => remoteEvent.OnProcessed.Wait());
+ await Task.Run(() => remoteEvent.OnProcessed.WaitHandle.WaitOne(5000));
var response = server.CommandResult.Where(c => c.ClientId == client.ClientId).ToList();
// remove the added command response
diff --git a/WebfrontCore/WebfrontCore.csproj b/WebfrontCore/WebfrontCore.csproj
index 15ce95ab1..c0077afaf 100644
--- a/WebfrontCore/WebfrontCore.csproj
+++ b/WebfrontCore/WebfrontCore.csproj
@@ -22,6 +22,10 @@
Debug;Release;Prerelease
+
+ true
+
+