update change tracking and elo

master shows monitoring server count
master can provide individual localizations
This commit is contained in:
RaidMax 2018-05-20 21:35:56 -05:00
parent 4d585e6ab2
commit be68335f70
14 changed files with 176 additions and 97 deletions

View File

@ -8,7 +8,7 @@ using RestEase;
namespace IW4MAdmin.Application.API.Master
{
public class AuthenticationId
{
{
[JsonProperty("id")]
public string Id { get; set; }
}
@ -62,6 +62,9 @@ namespace IW4MAdmin.Application.API.Master
Task<VersionInfo> GetVersion();
[Get("localization")]
Task<List<SharedLibraryCore.Localization.Layout>> GetLocalization();
Task<List<SharedLibraryCore.Localization.Layout>> GetLocalization();
[Get("localization/{languageTag}")]
Task<SharedLibraryCore.Localization.Layout> GetLocalization([Path("languageTag")] string languageTag);
}
}

View File

@ -19,13 +19,8 @@ namespace IW4MAdmin.Application.Localization
try
{
var api = Endpoint.Get();
var localizations = api.GetLocalization().Result;
var usingLocale = localizations.FirstOrDefault(l => l.LocalizationName == currentLocale
|| l.LocalizationName.Substring(0, 2) == currentLocale.Substring(0, 2)) ??
localizations.First();
Utilities.CurrentLocalization = usingLocale;
var localization = api.GetLocalization(currentLocale).Result;
Utilities.CurrentLocalization = localization;
return;
}

View File

@ -127,7 +127,7 @@ namespace Application.RconParsers
}
// this happens if status is requested while map is rotating
if (Status[1] == "Server Initialization")
if (Status.Contains("Server Initialization"))
{
throw new ServerException("Server is rotating map");
}
@ -139,6 +139,7 @@ namespace Application.RconParsers
{
IW4MAdmin.Application.Program.ServerManager.Logger.WriteDebug(s);
}
throw new ServerException("Bad status received");
}
return StatusPlayers;

View File

@ -58,25 +58,25 @@
<Compile Include="master\models\__init__.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="Master\resources\authenticate.py">
<Compile Include="master\resources\authenticate.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="master\resources\history_graph.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="Master\resources\instance.py">
<Compile Include="master\resources\instance.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="Master\resources\localization.py">
<Compile Include="master\resources\localization.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="Master\resources\null.py">
<Compile Include="master\resources\null.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="Master\resources\version.py">
<Compile Include="master\resources\version.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="Master\resources\__init__.py">
<Compile Include="master\resources\__init__.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="master\routes.py">
@ -98,22 +98,21 @@
<Compile Include="master\views.py" />
</ItemGroup>
<ItemGroup>
<Folder Include="C:\Projects\IW4M-Admin\Master\master\" />
<Folder Include="master\" />
<Folder Include="master\context\" />
<Folder Include="master\models\" />
<Folder Include="master\config\" />
<Folder Include="master\schema\" />
<Folder Include="Master\resources\" />
<Folder Include="Master\static\" />
<Folder Include="Master\templates\" />
<Folder Include="master\static\" />
<Folder Include="master\templates\" />
</ItemGroup>
<ItemGroup>
<None Include="FolderProfile.pubxml" />
<Content Include="master\config\master.json" />
<Content Include="requirements.txt" />
<Content Include="Master\templates\index.html" />
<Content Include="Master\templates\layout.html" />
<Content Include="master\templates\index.html" />
<Content Include="master\templates\layout.html" />
</ItemGroup>
<ItemGroup>
<Interpreter Include="dev_env\">

View File

@ -8,7 +8,7 @@ import csv
from io import StringIO
class Localization(Resource):
def get(self):
def list(self):
response = urllib.request.urlopen('https://docs.google.com/spreadsheets/d/e/2PACX-1vRQjCqPvd0Xqcn86WqpFqp_lx4KKpel9O4OV13NycmV8rmqycorgJQm-8qXMfw37QJHun3pqVZFUKG-/pub?gid=0&single=true&output=csv')
data = response.read().decode('utf-8')
@ -16,14 +16,12 @@ class Localization(Resource):
csv_data = csv.DictReader(StringIO(data))
for language in csv_data.fieldnames[1:]:
localization.append(
{
localization.append({
'LocalizationName' : language,
'LocalizationIndex' : {
'Set' : {}
}
}
)
})
for row in csv_data:
localization_string = row['STRING']
@ -33,3 +31,29 @@ class Localization(Resource):
count += 1
return localization, 200
def get(self, language_tag=None):
response = urllib.request.urlopen('https://docs.google.com/spreadsheets/d/e/2PACX-1vRQjCqPvd0Xqcn86WqpFqp_lx4KKpel9O4OV13NycmV8rmqycorgJQm-8qXMfw37QJHun3pqVZFUKG-/pub?gid=0&single=true&output=csv')
data = response.read().decode('utf-8')
csv_data = csv.DictReader(StringIO(data))
if language_tag != None:
valid_language_tag = next((l for l in csv_data.fieldnames[1:] if l == language_tag), None)
if valid_language_tag is None:
valid_language_tag = next((l for l in csv_data.fieldnames[1:] if l.startswith(language_tag[:2])), None)
if valid_language_tag is None:
valid_language_tag = 'en-US'
localization = {
'LocalizationName' : valid_language_tag,
'LocalizationIndex' : {
'Set' : {}
}
}
for row in csv_data:
localization_string = row['STRING']
localization['LocalizationIndex']['Set'][localization_string] = row[valid_language_tag]
return localization, 200
else:
return self.list()[0][0], 200

View File

@ -12,4 +12,4 @@ api.add_resource(Instance, '/instance/', '/instance/<string:id>')
api.add_resource(Version, '/version')
api.add_resource(Authenticate, '/authenticate')
api.add_resource(HistoryGraph, '/history/', '/history/<int:history_count>')
api.add_resource(Localization, '/localization')
api.add_resource(Localization, '/localization/', '/localization/<string:language_tag>')

View File

@ -57,6 +57,8 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
ClientPenalty = Penalty.PenaltyType.Any,
};
DetectionPenaltyResult result = null;
if (LastHit == DateTime.MinValue)
LastHit = DateTime.UtcNow;
@ -90,7 +92,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
Log.WriteDebug($"HitCount = {hitLoc.HitCount}");
Log.WriteDebug($"ID = {kill.AttackerId}");
return new DetectionPenaltyResult()
result = new DetectionPenaltyResult()
{
ClientPenalty = Penalty.PenaltyType.Ban,
Value = hitLoc.HitOffsetAverage,
@ -111,7 +113,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
Log.WriteDebug($"HitCount = {HitCount}");
Log.WriteDebug($"ID = {kill.AttackerId}");
return new DetectionPenaltyResult()
result = new DetectionPenaltyResult()
{
ClientPenalty = Penalty.PenaltyType.Ban,
Value = sessAverage,
@ -125,8 +127,9 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
Log.WriteDebug($"PredictVsReal={realAgainstPredict}");
#endif
}
double currentStrain = Strain.GetStrain(isDamage, kill.Damage, kill.ViewAngles, Math.Max(50, kill.TimeOffset - LastOffset));
double currentWeightedStrain = (currentStrain * ClientStats.SPM) / 170.0;
double currentStrain = Strain.GetStrain(isDamage, kill.Damage, kill.Distance / 0.0254, kill.ViewAngles, Math.Max(50, kill.TimeOffset - LastOffset));
//double currentWeightedStrain = (currentStrain * ClientStats.SPM) / 170.0;
LastOffset = kill.TimeOffset;
if (currentStrain > ClientStats.MaxStrain)
@ -134,42 +137,25 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
ClientStats.MaxStrain = currentStrain;
}
if (currentWeightedStrain > Thresholds.MaxStrainFlag)
{
Tracker.OnChange(Strain);
foreach (string change in Tracker.GetChanges())
{
Log.WriteDebug(change);
}
Log.WriteDebug(ClientStats.RoundScore.ToString());
}
else
{
Tracker.ClearChanges();
}
// flag
if (currentWeightedStrain > Thresholds.MaxStrainFlag)
if (currentStrain > Thresholds.MaxStrainFlag)
{
return new DetectionPenaltyResult()
result = new DetectionPenaltyResult()
{
ClientPenalty = Penalty.PenaltyType.Flag,
Value = currentWeightedStrain,
Value = currentStrain,
HitCount = HitCount,
Type = DetectionType.Strain
};
}
// ban
if (currentWeightedStrain > Thresholds.MaxStrainBan
&& Kills > Thresholds.LowSampleMinKills)
if (currentStrain > Thresholds.MaxStrainBan)
{
return new DetectionPenaltyResult()
result = new DetectionPenaltyResult()
{
ClientPenalty = Penalty.PenaltyType.Ban,
Value = currentWeightedStrain,
Value = currentStrain,
HitCount = HitCount,
Type = DetectionType.Strain
};
@ -217,7 +203,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
sb.Append($"HitLocation: {kvp.Key} -> {kvp.Value}\r\n");
Log.WriteDebug(sb.ToString());
return new DetectionPenaltyResult()
result = new DetectionPenaltyResult()
{
ClientPenalty = Penalty.PenaltyType.Ban,
Value = currentHeadshotRatio,
@ -238,7 +224,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
sb.Append($"HitLocation: {kvp.Key} -> {kvp.Value}\r\n");
Log.WriteDebug(sb.ToString());
return new DetectionPenaltyResult()
result = new DetectionPenaltyResult()
{
ClientPenalty = Penalty.PenaltyType.Flag,
Value = currentHeadshotRatio,
@ -267,7 +253,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
sb.Append($"HitLocation: {kvp.Key} -> {kvp.Value}\r\n");
Log.WriteDebug(sb.ToString());
return new DetectionPenaltyResult()
result = new DetectionPenaltyResult()
{
ClientPenalty = Penalty.PenaltyType.Ban,
Value = currentMaxBoneRatio,
@ -288,7 +274,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
sb.Append($"HitLocation: {kvp.Key} -> {kvp.Value}\r\n");
Log.WriteDebug(sb.ToString());
return new DetectionPenaltyResult()
result = new DetectionPenaltyResult()
{
ClientPenalty = Penalty.PenaltyType.Flag,
Value = currentMaxBoneRatio,
@ -329,7 +315,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
sb.Append($"HitLocation: {kvp.Key} -> {kvp.Value}\r\n");
Log.WriteDebug(sb.ToString());
return new DetectionPenaltyResult()
result = new DetectionPenaltyResult()
{
ClientPenalty = Penalty.PenaltyType.Ban,
Value = currentChestAbdomenRatio,
@ -351,7 +337,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
Log.WriteDebug(sb.ToString());
// Log.WriteDebug($"ThresholdReached: {AboveThresholdCount}");
return new DetectionPenaltyResult()
result = new DetectionPenaltyResult()
{
ClientPenalty = Penalty.PenaltyType.Flag,
Value = currentChestAbdomenRatio,
@ -364,7 +350,19 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
}
#endregion
#endregion
return new DetectionPenaltyResult()
Tracker.OnChange(new DetectionTracking(ClientStats, kill, Strain));
if (result != null)
{
foreach (string change in Tracker.GetChanges())
{
Log.WriteDebug(change);
Log.WriteDebug("--------------SNAPSHOT END-----------");
}
}
return result ?? new DetectionPenaltyResult()
{
ClientPenalty = Penalty.PenaltyType.Any,
};

View File

@ -0,0 +1,57 @@
using IW4MAdmin.Plugins.Stats.Cheat;
using IW4MAdmin.Plugins.Stats.Models;
using SharedLibraryCore.Interfaces;
using System;
using System.Collections.Generic;
using System.Text;
namespace IW4MAdmin.Plugins.Stats.Cheat
{
class DetectionTracking : ITrackable
{
EFClientStatistics Stats;
EFClientKill Hit;
Strain Strain;
public DetectionTracking(EFClientStatistics stats, EFClientKill hit, Strain strain)
{
Stats = stats;
Hit = hit;
Strain = strain;
}
public string GetTrackableValue()
{
var sb = new StringBuilder();
sb.AppendLine($"SPM = {Stats.SPM}");
sb.AppendLine($"KDR = {Stats.KDR}");
sb.AppendLine($"Kills = {Stats.Kills}");
sb.AppendLine($"Session Score = {Stats.SessionScore}");
sb.AppendLine($"Elo = {Stats.EloRating}");
sb.AppendLine($"Max Sess Strain = {Stats.MaxSessionStrain}");
sb.AppendLine($"MaxStrain = {Stats.MaxStrain}");
sb.AppendLine($"Avg Offset = {Stats.AverageHitOffset}");
sb.AppendLine($"TimePlayed, {Stats.TimePlayed}");
sb.AppendLine($"HitDamage = {Hit.Damage}");
sb.AppendLine($"HitOrigin = {Hit.KillOrigin}");
sb.AppendLine($"DeathOrigin = {Hit.DeathOrigin}");
sb.AppendLine($"ViewAngles = {Hit.ViewAngles}");
sb.AppendLine($"WeaponId = {Hit.Weapon.ToString()}");
sb.AppendLine($"Timeoffset = {Hit.TimeOffset}");
sb.AppendLine($"HitLocation = {Hit.HitLoc.ToString()}");
sb.AppendLine($"Distance = {Hit.Distance / 0.0254}");
sb.AppendLine($"HitType = {Hit.DeathType.ToString()}");
int i = 0;
foreach (var predictedAngle in Hit.AnglesList)
{
sb.AppendLine($"Predicted Angle [{i}] {predictedAngle}");
i++;
}
sb.AppendLine(Strain.GetTrackableValue());
sb.AppendLine($"VictimId = {Hit.VictimId}");
sb.AppendLine($"AttackerId = {Hit.AttackerId}");
return sb.ToString();
}
}
}

View File

@ -16,7 +16,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
public int TimesReachedMaxStrain { get; private set; }
public double GetStrain(bool isDamage, int damage, Vector3 newAngle, double deltaTime)
public double GetStrain(bool isDamage, int damage, double killDistance, Vector3 newAngle, double deltaTime)
{
if (LastAngle == null)
LastAngle = newAngle;
@ -42,16 +42,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
}
double newStrain = Math.Pow(distance[0] + distance[1], 0.99) / deltaTime;
if (damage < 100 && isDamage)
{
newStrain *= Math.Pow(damage, 2) / 10000.0;
}
else if (damage > 100)
{
newStrain *= damage / 100.0;
}
newStrain *= killDistance / 1000.0;
CurrentStrain += newStrain;
@ -64,7 +55,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
public string GetTrackableValue()
{
return $"Strain - {CurrentStrain}, Angle - {LastAngle}, Delta Time - {LastDeltaTime}, Distance - {LastDistance}";
return $"Strain = {CurrentStrain}\r\n, Angle = {LastAngle}\r\n, Delta Time = {LastDeltaTime}\r\n, Angle Between = {LastDistance}";
}
private double GetDecay(double deltaTime) => Math.Pow(StrainDecayBase, Math.Pow(2.0, deltaTime / 250.0) / 1000.0);

View File

@ -27,9 +27,9 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
public const int HighSampleMinKills = 100;
public const double KillTimeThreshold = 0.2;
public const double MaxStrainBan = 2.5;
public const double MaxStrainBan = 0.4;
public const double MaxOffset = 1.2;
public const double MaxStrainFlag = 2.0;
public const double MaxStrainFlag = 0.36;
public static double GetMarginOfError(int numKills) => 1.6455 / Math.Sqrt(numKills);

View File

@ -502,11 +502,11 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
.Where(cs => cs.Value.ClientId != victimStats.ClientId)
.Average(cs => cs.Value.EloRating);
double attackerEloDifference = Math.Log(attackerLobbyRating) - Math.Log(attackerStats.EloRating);
double winPercentage = 1.0 / (1 + Math.Pow(10, attackerEloDifference / 0.5));
double attackerEloDifference = Math.Log(attackerLobbyRating <= 0 ? 1 : attackerLobbyRating) - Math.Log(attackerStats.EloRating <= 0 ? 1 : attackerStats.EloRating);
double winPercentage = 1.0 / (1 + Math.Pow(10, attackerEloDifference / Math.E));
double victimEloDifference = Math.Log(victimLobbyRating) - Math.Log(victimStats.EloRating);
double lossPercentage = 1.0 / (1 + Math.Pow(10, victimEloDifference / 0.5));
double victimEloDifference = Math.Log(victimLobbyRating <= 0 ? 1 : victimLobbyRating) - Math.Log(victimStats.EloRating <= 0 ? 1 : victimStats.EloRating);
double lossPercentage = 1.0 / (1 + Math.Pow(10, victimEloDifference / Math.E));
attackerStats.EloRating += 24.0 * (1 - winPercentage);
victimStats.EloRating -= 24.0 * winPercentage;
@ -559,7 +559,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
// 1.637 is a Eddie-Generated number that weights the KDR nicely
double currentKDR = clientStats.SessionDeaths == 0 ? clientStats.SessionKills : clientStats.SessionKills / clientStats.SessionDeaths;
double alpha = Math.Sqrt(2) / Math.Min(600, clientStats.Kills + clientStats.Deaths);
clientStats.RollingWeightedKDR = (alpha * currentKDR) + (1.0 - alpha) * currentKDR;
clientStats.RollingWeightedKDR = (alpha * currentKDR) + (1.0 - alpha) * clientStats.KDR;
double KDRWeight = Math.Round(Math.Pow(clientStats.RollingWeightedKDR, 1.637 / Math.E), 3);
// calculate the weight of the new play time against last 10 hours of gameplay

View File

@ -14,6 +14,10 @@
<Configurations>Debug;Release;Prerelease</Configurations>
</PropertyGroup>
<ItemGroup>
<None Remove="Cheat\Strain.cs~RF16f7b3.TMP" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\SharedLibraryCore\SharedLibraryCore.csproj" />
</ItemGroup>

View File

@ -16,7 +16,9 @@ namespace SharedLibraryCore.Helpers
public void OnChange(ITrackable value)
{
Values.Add(value.GetTrackableValue());
if (Values.Count > 30)
Values.RemoveAt(0);
Values.Add($"{DateTime.Now.ToString("HH:mm:ss.fff")} {value.GetTrackableValue()}");
}
public void ClearChanges()
@ -24,18 +26,6 @@ namespace SharedLibraryCore.Helpers
Values.Clear();
}
public string[] GetChanges()
{
List<string> values = new List<string>();
int number = 1;
foreach (string change in Values)
{
values.Add($"{number} {change}");
number++;
}
return values.ToArray();
}
public string[] GetChanges() => Values.ToArray();
}
}

View File

@ -1,9 +1,26 @@
Version 2.1:
CHANGELOG:
-add support for localization
-add support for localization (Russian, Spanish, and Portuguese)
-upgraded projects to .NET Core 2.0.7
-redid the event system to haev a single line of execution
-added support for MySQL provider via "ConnectrionString"
-added support for MySQL provider via "ConnectionString" in IW4MAdminSettings.json
-refactored some stats code to provide a better representation of player skill as "performance"
-added most played command which shows players who have played the most
-added unflag command to more intuitively unflag a client
-added multi-line tokens: {{TOPSTATS}} {{MOSTPLAYED}}
-able to view linked accounts on webfront via dropdown (privileged only)
-multiple privileged accouns are consolidated in the admin list
-Added IW5m/Pluto IW5, T5m/V2, CoD4, and WaW support
-changed event system to use a better pipeline
-IW4x anti-cheat further refined
-kick and temban required privileges adjusted
-fixed issues with RCon responding improperly
-improved IW4x frequency of IW4x servers going offline
-profanity plugin now kicks players with offensive names (if enabled)
-fixed critical bug with CPU usage over time
-discord link has been generalized into a "social link" (website/facebook/vk etc...)
-untold bug fixes
-introduced new bugs to fix in the next version
Version 2.0:
CHANGELOG: