update change tracking and elo
master shows monitoring server count master can provide individual localizations
This commit is contained in:
parent
4d585e6ab2
commit
be68335f70
@ -63,5 +63,8 @@ namespace IW4MAdmin.Application.API.Master
|
||||
|
||||
[Get("localization")]
|
||||
Task<List<SharedLibraryCore.Localization.Layout>> GetLocalization();
|
||||
|
||||
[Get("localization/{languageTag}")]
|
||||
Task<SharedLibraryCore.Localization.Layout> GetLocalization([Path("languageTag")] string languageTag);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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\">
|
||||
|
@ -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
|
||||
|
@ -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>')
|
@ -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,
|
||||
};
|
||||
|
57
Plugins/Stats/Cheat/DetectionTracking.cs
Normal file
57
Plugins/Stats/Cheat/DetectionTracking.cs
Normal 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();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
23
version.txt
23
version.txt
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user