more stat SPM fixes

prevent null say event from executing when exiting
adjusted rcon and socket timeout
fixed bug with login/setpassword not working after claiming ownership
This commit is contained in:
RaidMax 2018-05-03 00:25:49 -05:00
parent 3a463be7f8
commit f442f251f6
26 changed files with 1382 additions and 169 deletions

View File

@ -27,6 +27,10 @@
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.4.0" />
</ItemGroup>
<PropertyGroup>
<ServerGarbageCollection>true</ServerGarbageCollection>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\SharedLibraryCore\SharedLibraryCore.csproj">
<Private>true</Private>

View File

@ -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()

View File

@ -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");

View File

@ -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);

View File

@ -23,11 +23,11 @@ namespace Application.Misc
string response = await RequestClient.GetStringAsync($"http://v2.api.iphub.info/ip/{ip}");
var responseJson = JsonConvert.DeserializeObject<JObject>(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;
}
}

View File

@ -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;
}
}

View File

@ -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
}

View File

@ -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<IW4Info.HitLocation, int>();
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<IW4Info.MeansOfDeath>.Get(match.Groups[12].Value, typeof(IW4Info.MeansOfDeath));
var hitLocation = ParseEnum<IW4Info.HitLocation>.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;
}
}
}
/// <summary>
@ -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

View File

@ -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);
}
}

View File

@ -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 };
}
}
}

View File

@ -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<IW4Info.HitLocation>().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);
}
}
/// <summary>
/// Process stats for kill event
/// </summary>
/// <returns></returns>
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<Vector3>();
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<IW4Info.WeaponName>.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();
}
/// <summary>
@ -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

View File

@ -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<Vector3> AnglesList { get; set; }
}
}

View File

@ -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

View File

@ -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; }

View File

@ -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
}*/
};
}

View File

@ -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

View File

@ -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();
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; }
}
}

View File

@ -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);

View File

@ -0,0 +1,434 @@
// <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("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<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<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
}
}
}

View File

@ -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<float>(
name: "MaxAngleDistance",
table: "EFHitLocationCounts",
nullable: false,
defaultValue: 0f);
migrationBuilder.AddColumn<double>(
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");
}
}
}

View File

@ -0,0 +1,433 @@
// <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))]
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<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<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
}
}
}

View File

@ -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<ManualResetEventSlim> 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<string[]> 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}");
}
else
{
if (FailedSends >= 4)
if (!OnConnected.WaitOne(StaticHelpers.SocketTimeout))
throw new SocketException((int)SocketError.TimedOut);
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;
}
}
}
}

View File

@ -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);
}
}

View File

@ -12,6 +12,10 @@
<Configurations>Debug;Release;Prerelease</Configurations>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Migrations\20180502195240_Update.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.0.2" />

View File

@ -28,7 +28,6 @@ namespace WebfrontCore.Controllers
public async Task<IActionResult> 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

View File

@ -22,6 +22,10 @@
<Configurations>Debug;Release;Prerelease</Configurations>
</PropertyGroup>
<PropertyGroup>
<ServerGarbageCollection>true</ServerGarbageCollection>
</PropertyGroup>
<ItemGroup>
<Content Remove="bower.json" />
<Content Remove="bundleconfig.json" />