Additional CSGO compatibility improvements
This commit is contained in:
parent
dbceb23823
commit
af4630ecb9
@ -217,10 +217,15 @@ namespace IW4MAdmin.Application.RConParsers
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int clientNumber = int.Parse(match.Values[Configuration.Status.GroupMapping[ParserRegex.GroupType.RConClientNumber]]);
|
var clientNumber = int.Parse(match.Values[Configuration.Status.GroupMapping[ParserRegex.GroupType.RConClientNumber]]);
|
||||||
int score = int.Parse(match.Values[Configuration.Status.GroupMapping[ParserRegex.GroupType.RConScore]]);
|
var score = 0;
|
||||||
|
|
||||||
|
if (Configuration.Status.GroupMapping[ParserRegex.GroupType.RConScore] > 0)
|
||||||
|
{
|
||||||
|
score = int.Parse(match.Values[Configuration.Status.GroupMapping[ParserRegex.GroupType.RConScore]]);
|
||||||
|
}
|
||||||
|
|
||||||
int ping = 999;
|
var ping = 999;
|
||||||
|
|
||||||
// their state can be CNCT, ZMBI etc
|
// their state can be CNCT, ZMBI etc
|
||||||
if (match.Values[Configuration.Status.GroupMapping[ParserRegex.GroupType.RConPing]].Length <= 3)
|
if (match.Values[Configuration.Status.GroupMapping[ParserRegex.GroupType.RConPing]].Length <= 3)
|
||||||
@ -229,7 +234,7 @@ namespace IW4MAdmin.Application.RConParsers
|
|||||||
}
|
}
|
||||||
|
|
||||||
long networkId;
|
long networkId;
|
||||||
string name = match.Values[Configuration.Status.GroupMapping[ParserRegex.GroupType.RConName]].TrimNewLine();
|
var name = match.Values[Configuration.Status.GroupMapping[ParserRegex.GroupType.RConName]].TrimNewLine();
|
||||||
string networkIdString;
|
string networkIdString;
|
||||||
var ip = match.Values[Configuration.Status.GroupMapping[ParserRegex.GroupType.RConIpAddress]].Split(':')[0].ConvertToIP();
|
var ip = match.Values[Configuration.Status.GroupMapping[ParserRegex.GroupType.RConIpAddress]].Split(':')[0].ConvertToIP();
|
||||||
|
|
||||||
|
@ -26,9 +26,12 @@ namespace Integrations.Source
|
|||||||
private readonly SemaphoreSlim _activeQuery;
|
private readonly SemaphoreSlim _activeQuery;
|
||||||
|
|
||||||
private static readonly TimeSpan FloodDelay = TimeSpan.FromMilliseconds(250);
|
private static readonly TimeSpan FloodDelay = TimeSpan.FromMilliseconds(250);
|
||||||
|
private static readonly TimeSpan ConnectionTimeout = TimeSpan.FromSeconds(30);
|
||||||
|
|
||||||
private DateTime _lastQuery = DateTime.Now;
|
private DateTime _lastQuery = DateTime.Now;
|
||||||
private RconClient _rconClient;
|
private RconClient _rconClient;
|
||||||
|
private bool _authenticated;
|
||||||
|
private bool _needNewSocket = true;
|
||||||
|
|
||||||
public SourceRConConnection(ILogger<SourceRConConnection> logger, IRConClientFactory rconClientFactory,
|
public SourceRConConnection(ILogger<SourceRConConnection> logger, IRConClientFactory rconClientFactory,
|
||||||
string hostname, int port, string password)
|
string hostname, int port, string password)
|
||||||
@ -38,7 +41,6 @@ namespace Integrations.Source
|
|||||||
_hostname = hostname;
|
_hostname = hostname;
|
||||||
_port = port;
|
_port = port;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_rconClient = _rconClientFactory.CreateClient(_hostname, _port);
|
|
||||||
_activeQuery = new SemaphoreSlim(1, 1);
|
_activeQuery = new SemaphoreSlim(1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,10 +54,22 @@ namespace Integrations.Source
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _activeQuery.WaitAsync();
|
await _activeQuery.WaitAsync();
|
||||||
var diff = DateTime.Now - _lastQuery;
|
await WaitForAvailable();
|
||||||
if (diff < FloodDelay)
|
|
||||||
|
if (_needNewSocket)
|
||||||
{
|
{
|
||||||
await Task.Delay(FloodDelay - diff);
|
try
|
||||||
|
{
|
||||||
|
_rconClient?.Disconnect();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
|
||||||
|
_rconClient = _rconClientFactory.CreateClient(_hostname, _port);
|
||||||
|
_authenticated = false;
|
||||||
|
_needNewSocket = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
|
using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
|
||||||
@ -63,64 +77,14 @@ namespace Integrations.Source
|
|||||||
_logger.LogDebug("Connecting to RCon socket");
|
_logger.LogDebug("Connecting to RCon socket");
|
||||||
}
|
}
|
||||||
|
|
||||||
await _rconClient.ConnectAsync();
|
await TryConnectAndAuthenticate().WithTimeout(ConnectionTimeout);
|
||||||
|
|
||||||
bool authenticated;
|
var multiPacket = false;
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
|
|
||||||
{
|
|
||||||
_logger.LogDebug("Authenticating to RCon socket");
|
|
||||||
}
|
|
||||||
|
|
||||||
authenticated = await _rconClient.AuthenticateAsync(_password);
|
|
||||||
}
|
|
||||||
catch (SocketException ex)
|
|
||||||
{
|
|
||||||
// occurs when the server comes back from hibernation
|
|
||||||
// this is probably a bug in the library
|
|
||||||
if (ex.ErrorCode == 10053 || ex.ErrorCode == 10054)
|
|
||||||
{
|
|
||||||
using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
|
|
||||||
{
|
|
||||||
_logger.LogWarning(ex,
|
|
||||||
"Server appears to resumed from hibernation, so we are using a new socket");
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_rconClient.Disconnect();
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// ignored
|
|
||||||
}
|
|
||||||
|
|
||||||
_rconClient = _rconClientFactory.CreateClient(_hostname, _port);
|
|
||||||
}
|
|
||||||
|
|
||||||
using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error occurred authenticating with server");
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new NetworkException("Error occurred authenticating with server");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!authenticated)
|
|
||||||
{
|
|
||||||
using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
|
|
||||||
{
|
|
||||||
_logger.LogError("Could not login to server");
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new ServerException("Could not authenticate to server with provided password");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type == StaticHelpers.QueryType.COMMAND_STATUS)
|
if (type == StaticHelpers.QueryType.COMMAND_STATUS)
|
||||||
{
|
{
|
||||||
parameters = "status";
|
parameters = "status";
|
||||||
|
multiPacket = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
parameters = parameters.ReplaceUnfriendlyCharacters();
|
parameters = parameters.ReplaceUnfriendlyCharacters();
|
||||||
@ -131,9 +95,10 @@ namespace Integrations.Source
|
|||||||
_logger.LogDebug("Sending query {Type} with parameters \"{Parameters}\"", type, parameters);
|
_logger.LogDebug("Sending query {Type} with parameters \"{Parameters}\"", type, parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
var response = await _rconClient.ExecuteCommandAsync(parameters, true);
|
var response = await _rconClient.ExecuteCommandAsync(parameters, multiPacket)
|
||||||
|
.WithTimeout(ConnectionTimeout);
|
||||||
|
|
||||||
using (LogContext.PushProperty("Server", $"{_rconClient.Host}:{_rconClient.Port}"))
|
using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
|
||||||
{
|
{
|
||||||
_logger.LogDebug("Received RCon response {Response}", response);
|
_logger.LogDebug("Received RCon response {Response}", response);
|
||||||
}
|
}
|
||||||
@ -142,6 +107,24 @@ namespace Integrations.Source
|
|||||||
return split.Take(split.Length - 1).ToArray();
|
return split.Take(split.Length - 1).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
catch (TaskCanceledException)
|
||||||
|
{
|
||||||
|
_needNewSocket = true;
|
||||||
|
throw new NetworkException("Timeout while attempting to communicate with server");
|
||||||
|
}
|
||||||
|
|
||||||
|
catch (SocketException ex)
|
||||||
|
{
|
||||||
|
using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Socket exception encountered while attempting to communicate with server");
|
||||||
|
}
|
||||||
|
|
||||||
|
_needNewSocket = true;
|
||||||
|
|
||||||
|
throw new NetworkException("Socket exception encountered while attempting to communicate with server");
|
||||||
|
}
|
||||||
|
|
||||||
catch (Exception ex) when (ex.GetType() != typeof(NetworkException) &&
|
catch (Exception ex) when (ex.GetType() != typeof(NetworkException) &&
|
||||||
ex.GetType() != typeof(ServerException))
|
ex.GetType() != typeof(ServerException))
|
||||||
{
|
{
|
||||||
@ -164,6 +147,39 @@ namespace Integrations.Source
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task WaitForAvailable()
|
||||||
|
{
|
||||||
|
var diff = DateTime.Now - _lastQuery;
|
||||||
|
if (diff < FloodDelay)
|
||||||
|
{
|
||||||
|
await Task.Delay(FloodDelay - diff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task TryConnectAndAuthenticate()
|
||||||
|
{
|
||||||
|
if (!_authenticated)
|
||||||
|
{
|
||||||
|
using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
|
||||||
|
{
|
||||||
|
_logger.LogDebug("Authenticating to RCon socket");
|
||||||
|
}
|
||||||
|
|
||||||
|
await _rconClient.ConnectAsync().WithTimeout(ConnectionTimeout);
|
||||||
|
_authenticated = await _rconClient.AuthenticateAsync(_password).WithTimeout(ConnectionTimeout);
|
||||||
|
|
||||||
|
if (!_authenticated)
|
||||||
|
{
|
||||||
|
using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
|
||||||
|
{
|
||||||
|
_logger.LogError("Could not login to server");
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ServerException("Could not authenticate to server with provided password");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void SetConfiguration(IRConParser config)
|
public void SetConfiguration(IRConParser config)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ const plugin = {
|
|||||||
|
|
||||||
rconParser.Configuration.Status.Pattern = '^#\\s*(\\d+) (\\d+) "(.+)" (\\S+) +(\\d+:\\d+(?::\\d+)?) (\\d+) (\\S+) (\\S+) (\\d+) (\\d+\\.\\d+\\.\\d+\\.\\d+:\\d+)$';
|
rconParser.Configuration.Status.Pattern = '^#\\s*(\\d+) (\\d+) "(.+)" (\\S+) +(\\d+:\\d+(?::\\d+)?) (\\d+) (\\S+) (\\S+) (\\d+) (\\d+\\.\\d+\\.\\d+\\.\\d+:\\d+)$';
|
||||||
rconParser.Configuration.Status.AddMapping(100, 2);
|
rconParser.Configuration.Status.AddMapping(100, 2);
|
||||||
rconParser.Configuration.Status.AddMapping(101, 7);
|
rconParser.Configuration.Status.AddMapping(101, -1);
|
||||||
rconParser.Configuration.Status.AddMapping(102, 6);
|
rconParser.Configuration.Status.AddMapping(102, 6);
|
||||||
rconParser.Configuration.Status.AddMapping(103, 4)
|
rconParser.Configuration.Status.AddMapping(103, 4)
|
||||||
rconParser.Configuration.Status.AddMapping(104, 3);
|
rconParser.Configuration.Status.AddMapping(104, 3);
|
||||||
@ -90,6 +90,7 @@ const plugin = {
|
|||||||
rconParser.GameName = 10; // CSGO
|
rconParser.GameName = 10; // CSGO
|
||||||
eventParser.Version = 'CSGO';
|
eventParser.Version = 'CSGO';
|
||||||
eventParser.GameName = 10; // CSGO
|
eventParser.GameName = 10; // CSGO
|
||||||
|
eventParser.URLProtocolFormat = 'steam://connect/{{ip}}:{{port}}';
|
||||||
},
|
},
|
||||||
|
|
||||||
onUnloadAsync: function () {
|
onUnloadAsync: function () {
|
||||||
|
@ -35,7 +35,7 @@ const plugin = {
|
|||||||
|
|
||||||
rconParser.Configuration.Status.Pattern = '^#\\s*(\\d+) (\\d+) "(.+)" (\\S+) +(\\d+:\\d+(?::\\d+)?) (\\d+) (\\S+) (\\S+) (\\d+) (\\d+\\.\\d+\\.\\d+\\.\\d+:\\d+)$';
|
rconParser.Configuration.Status.Pattern = '^#\\s*(\\d+) (\\d+) "(.+)" (\\S+) +(\\d+:\\d+(?::\\d+)?) (\\d+) (\\S+) (\\S+) (\\d+) (\\d+\\.\\d+\\.\\d+\\.\\d+:\\d+)$';
|
||||||
rconParser.Configuration.Status.AddMapping(100, 2);
|
rconParser.Configuration.Status.AddMapping(100, 2);
|
||||||
rconParser.Configuration.Status.AddMapping(101, 7);
|
rconParser.Configuration.Status.AddMapping(101, -1);
|
||||||
rconParser.Configuration.Status.AddMapping(102, 6);
|
rconParser.Configuration.Status.AddMapping(102, 6);
|
||||||
rconParser.Configuration.Status.AddMapping(103, 4)
|
rconParser.Configuration.Status.AddMapping(103, 4)
|
||||||
rconParser.Configuration.Status.AddMapping(104, 3);
|
rconParser.Configuration.Status.AddMapping(104, 3);
|
||||||
@ -90,6 +90,7 @@ const plugin = {
|
|||||||
rconParser.GameName = 10; // CSGO
|
rconParser.GameName = 10; // CSGO
|
||||||
eventParser.Version = 'CSGOSM';
|
eventParser.Version = 'CSGOSM';
|
||||||
eventParser.GameName = 10; // CSGO
|
eventParser.GameName = 10; // CSGO
|
||||||
|
eventParser.URLProtocolFormat = 'steam://connect/{{ip}}:{{port}}';
|
||||||
},
|
},
|
||||||
|
|
||||||
onUnloadAsync: function () {
|
onUnloadAsync: function () {
|
||||||
|
@ -11,6 +11,7 @@ using Data.Models.Client.Stats.Reference;
|
|||||||
using Data.Models.Server;
|
using Data.Models.Server;
|
||||||
using IW4MAdmin.Plugins.Stats.Client.Abstractions;
|
using IW4MAdmin.Plugins.Stats.Client.Abstractions;
|
||||||
using IW4MAdmin.Plugins.Stats.Client.Game;
|
using IW4MAdmin.Plugins.Stats.Client.Game;
|
||||||
|
using IW4MAdmin.Plugins.Stats.Helpers;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using SharedLibraryCore;
|
using SharedLibraryCore;
|
||||||
@ -147,7 +148,7 @@ namespace IW4MAdmin.Plugins.Stats.Client
|
|||||||
foreach (var client in gameEvent.Owner.GetClientsAsList())
|
foreach (var client in gameEvent.Owner.GetClientsAsList())
|
||||||
{
|
{
|
||||||
var scores = client.GetAdditionalProperty<List<(int, DateTime)>>(SessionScores);
|
var scores = client.GetAdditionalProperty<List<(int, DateTime)>>(SessionScores);
|
||||||
scores?.Add((client.Score, DateTime.Now));
|
scores?.Add((client.GetAdditionalProperty<int?>(StatManager.ESTIMATED_SCORE) ?? client.Score, DateTime.Now));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -590,7 +591,7 @@ namespace IW4MAdmin.Plugins.Stats.Client
|
|||||||
|
|
||||||
if (sessionScores == null)
|
if (sessionScores == null)
|
||||||
{
|
{
|
||||||
_logger.LogWarning($"No session scores available for {client}");
|
_logger.LogWarning("No session scores available for {Client}", client.ToString());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -600,7 +601,7 @@ namespace IW4MAdmin.Plugins.Stats.Client
|
|||||||
|
|
||||||
if (sessionScores.Count == 0)
|
if (sessionScores.Count == 0)
|
||||||
{
|
{
|
||||||
stat.Score += client.Score;
|
stat.Score += client.Score > 0 ? client.Score : client.GetAdditionalProperty<int?>(Helpers.StatManager.ESTIMATED_SCORE) ?? 0 * 50;
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
|
@ -38,6 +38,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
private static List<EFServer> serverModels;
|
private static List<EFServer> serverModels;
|
||||||
public static string CLIENT_STATS_KEY = "ClientStats";
|
public static string CLIENT_STATS_KEY = "ClientStats";
|
||||||
public static string CLIENT_DETECTIONS_KEY = "ClientDetections";
|
public static string CLIENT_DETECTIONS_KEY = "ClientDetections";
|
||||||
|
public static string ESTIMATED_SCORE = "EstimatedScore";
|
||||||
private readonly SemaphoreSlim _addPlayerWaiter = new SemaphoreSlim(1, 1);
|
private readonly SemaphoreSlim _addPlayerWaiter = new SemaphoreSlim(1, 1);
|
||||||
private readonly IServerDistributionCalculator _serverDistributionCalculator;
|
private readonly IServerDistributionCalculator _serverDistributionCalculator;
|
||||||
|
|
||||||
@ -859,7 +860,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
|
|
||||||
// update the total stats
|
// update the total stats
|
||||||
_servers[serverId].ServerStatistics.TotalKills += 1;
|
_servers[serverId].ServerStatistics.TotalKills += 1;
|
||||||
|
|
||||||
// this happens when the round has changed
|
// this happens when the round has changed
|
||||||
if (attackerStats.SessionScore == 0)
|
if (attackerStats.SessionScore == 0)
|
||||||
{
|
{
|
||||||
@ -871,18 +872,24 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
victimStats.LastScore = 0;
|
victimStats.LastScore = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
attackerStats.SessionScore = attacker.Score;
|
var estimatedAttackerScore = attacker.Score > 0 ? attacker.Score : attackerStats.SessionKills * 50;
|
||||||
victimStats.SessionScore = victim.Score;
|
var estimatedVictimScore = victim.Score > 0 ? victim.Score : victimStats.SessionKills * 50;
|
||||||
|
|
||||||
|
attackerStats.SessionScore = estimatedAttackerScore;
|
||||||
|
victimStats.SessionScore = estimatedVictimScore;
|
||||||
|
|
||||||
|
attacker.SetAdditionalProperty(ESTIMATED_SCORE, estimatedAttackerScore);
|
||||||
|
victim.SetAdditionalProperty(ESTIMATED_SCORE, estimatedVictimScore);
|
||||||
|
|
||||||
// calculate for the clients
|
// calculate for the clients
|
||||||
CalculateKill(attackerStats, victimStats);
|
CalculateKill(attackerStats, victimStats);
|
||||||
// this should fix the negative SPM
|
// this should fix the negative SPM
|
||||||
// updates their last score after being calculated
|
// updates their last score after being calculated
|
||||||
attackerStats.LastScore = attacker.Score;
|
attackerStats.LastScore = estimatedAttackerScore;
|
||||||
victimStats.LastScore = victim.Score;
|
victimStats.LastScore = estimatedVictimScore;
|
||||||
|
|
||||||
// show encouragement/discouragement
|
// show encouragement/discouragement
|
||||||
string streakMessage = (attackerStats.ClientId != victimStats.ClientId)
|
var streakMessage = (attackerStats.ClientId != victimStats.ClientId)
|
||||||
? StreakMessage.MessageOnStreak(attackerStats.KillStreak, attackerStats.DeathStreak)
|
? StreakMessage.MessageOnStreak(attackerStats.KillStreak, attackerStats.DeathStreak)
|
||||||
: StreakMessage.MessageOnStreak(-1, -1);
|
: StreakMessage.MessageOnStreak(-1, -1);
|
||||||
|
|
||||||
@ -1248,41 +1255,10 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
// process the attacker's stats after the kills
|
// process the attacker's stats after the kills
|
||||||
attackerStats = UpdateStats(attackerStats);
|
attackerStats = UpdateStats(attackerStats);
|
||||||
|
|
||||||
#region DEPRECATED
|
|
||||||
|
|
||||||
/* var validAttackerLobbyRatings = Servers[attackerStats.ServerId].PlayerStats
|
|
||||||
.Where(cs => cs.Value.ClientId != attackerStats.ClientId)
|
|
||||||
.Where(cs =>
|
|
||||||
Servers[attackerStats.ServerId].IsTeamBased ?
|
|
||||||
cs.Value.Team != attackerStats.Team :
|
|
||||||
cs.Value.Team != IW4Info.Team.Spectator)
|
|
||||||
.Where(cs => cs.Value.Team != IW4Info.Team.Spectator);
|
|
||||||
|
|
||||||
double attackerLobbyRating = validAttackerLobbyRatings.Count() > 0 ?
|
|
||||||
validAttackerLobbyRatings.Average(cs => cs.Value.EloRating) :
|
|
||||||
attackerStats.EloRating;
|
|
||||||
|
|
||||||
var validVictimLobbyRatings = Servers[victimStats.ServerId].PlayerStats
|
|
||||||
.Where(cs => cs.Value.ClientId != victimStats.ClientId)
|
|
||||||
.Where(cs =>
|
|
||||||
Servers[attackerStats.ServerId].IsTeamBased ?
|
|
||||||
cs.Value.Team != victimStats.Team :
|
|
||||||
cs.Value.Team != IW4Info.Team.Spectator)
|
|
||||||
.Where(cs => cs.Value.Team != IW4Info.Team.Spectator);
|
|
||||||
|
|
||||||
double victimLobbyRating = validVictimLobbyRatings.Count() > 0 ?
|
|
||||||
validVictimLobbyRatings.Average(cs => cs.Value.EloRating) :
|
|
||||||
victimStats.EloRating;*/
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
// calculate elo
|
// calculate elo
|
||||||
double attackerEloDifference = Math.Log(Math.Max(1, victimStats.EloRating)) -
|
var attackerEloDifference = Math.Log(Math.Max(1, victimStats.EloRating)) -
|
||||||
Math.Log(Math.Max(1, attackerStats.EloRating));
|
Math.Log(Math.Max(1, attackerStats.EloRating));
|
||||||
double winPercentage = 1.0 / (1 + Math.Pow(10, attackerEloDifference / Math.E));
|
var winPercentage = 1.0 / (1 + Math.Pow(10, attackerEloDifference / Math.E));
|
||||||
|
|
||||||
// double victimEloDifference = Math.Log(Math.Max(1, attackerStats.EloRating)) - Math.Log(Math.Max(1, victimStats.EloRating));
|
|
||||||
// double lossPercentage = 1.0 / (1 + Math.Pow(10, victimEloDifference/ Math.E));
|
|
||||||
|
|
||||||
attackerStats.EloRating += 6.0 * (1 - winPercentage);
|
attackerStats.EloRating += 6.0 * (1 - winPercentage);
|
||||||
victimStats.EloRating -= 6.0 * (1 - winPercentage);
|
victimStats.EloRating -= 6.0 * (1 - winPercentage);
|
||||||
@ -1314,10 +1290,9 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
return clientStats;
|
return clientStats;
|
||||||
}
|
}
|
||||||
|
|
||||||
double timeSinceLastCalc = (DateTime.UtcNow - clientStats.LastStatCalculation).TotalSeconds / 60.0;
|
var timeSinceLastCalc = (DateTime.UtcNow - clientStats.LastStatCalculation).TotalSeconds / 60.0;
|
||||||
double timeSinceLastActive = (DateTime.UtcNow - clientStats.LastActive).TotalSeconds / 60.0;
|
|
||||||
|
|
||||||
int scoreDifference = 0;
|
var scoreDifference = 0;
|
||||||
// this means they've been tking or suicide and is the only time they can have a negative SPM
|
// this means they've been tking or suicide and is the only time they can have a negative SPM
|
||||||
if (clientStats.RoundScore < 0)
|
if (clientStats.RoundScore < 0)
|
||||||
{
|
{
|
||||||
@ -1329,17 +1304,17 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
scoreDifference = clientStats.RoundScore - clientStats.LastScore;
|
scoreDifference = clientStats.RoundScore - clientStats.LastScore;
|
||||||
}
|
}
|
||||||
|
|
||||||
double killSPM = scoreDifference / timeSinceLastCalc;
|
var killSpm = scoreDifference / timeSinceLastCalc;
|
||||||
double spmMultiplier = 2.934 *
|
var spmMultiplier = 2.934 *
|
||||||
Math.Pow(
|
Math.Pow(
|
||||||
_servers[clientStats.ServerId]
|
_servers[clientStats.ServerId]
|
||||||
.TeamCount((IW4Info.Team) clientStats.Team == IW4Info.Team.Allies
|
.TeamCount((IW4Info.Team) clientStats.Team == IW4Info.Team.Allies
|
||||||
? IW4Info.Team.Axis
|
? IW4Info.Team.Axis
|
||||||
: IW4Info.Team.Allies), -0.454);
|
: IW4Info.Team.Allies), -0.454);
|
||||||
killSPM *= Math.Max(1, spmMultiplier);
|
killSpm *= Math.Max(1, spmMultiplier);
|
||||||
|
|
||||||
// update this for ac tracking
|
// update this for ac tracking
|
||||||
clientStats.SessionSPM = killSPM;
|
clientStats.SessionSPM = killSpm;
|
||||||
|
|
||||||
// calculate how much the KDR should weigh
|
// calculate how much the KDR should weigh
|
||||||
// 1.637 is a Eddie-Generated number that weights the KDR nicely
|
// 1.637 is a Eddie-Generated number that weights the KDR nicely
|
||||||
@ -1358,7 +1333,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
double SPMAgainstPlayWeight = timeSinceLastCalc / Math.Min(600, (totalPlayTime / 60.0));
|
double SPMAgainstPlayWeight = timeSinceLastCalc / Math.Min(600, (totalPlayTime / 60.0));
|
||||||
|
|
||||||
// calculate the new weight against average times the weight against play time
|
// calculate the new weight against average times the weight against play time
|
||||||
clientStats.SPM = (killSPM * SPMAgainstPlayWeight) + (clientStats.SPM * (1 - SPMAgainstPlayWeight));
|
clientStats.SPM = (killSpm * SPMAgainstPlayWeight) + (clientStats.SPM * (1 - SPMAgainstPlayWeight));
|
||||||
|
|
||||||
if (clientStats.SPM < 0)
|
if (clientStats.SPM < 0)
|
||||||
{
|
{
|
||||||
@ -1373,7 +1348,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
if (double.IsNaN(clientStats.SPM) || double.IsNaN(clientStats.Skill))
|
if (double.IsNaN(clientStats.SPM) || double.IsNaN(clientStats.Skill))
|
||||||
{
|
{
|
||||||
_log.LogWarning("clientStats SPM/Skill NaN {@killInfo}",
|
_log.LogWarning("clientStats SPM/Skill NaN {@killInfo}",
|
||||||
new {killSPM, KDRWeight, totalPlayTime, SPMAgainstPlayWeight, clientStats, scoreDifference});
|
new {killSPM = killSpm, KDRWeight, totalPlayTime, SPMAgainstPlayWeight, clientStats, scoreDifference});
|
||||||
clientStats.SPM = 0;
|
clientStats.SPM = 0;
|
||||||
clientStats.Skill = 0;
|
clientStats.Skill = 0;
|
||||||
}
|
}
|
||||||
|
@ -83,6 +83,7 @@ namespace IW4MAdmin.Plugins.Stats
|
|||||||
await Manager.Sync(S);
|
await Manager.Sync(S);
|
||||||
break;
|
break;
|
||||||
case GameEvent.EventType.MapEnd:
|
case GameEvent.EventType.MapEnd:
|
||||||
|
Manager.ResetKillstreaks(S);
|
||||||
await Manager.Sync(S);
|
await Manager.Sync(S);
|
||||||
break;
|
break;
|
||||||
case GameEvent.EventType.Command:
|
case GameEvent.EventType.Command:
|
||||||
|
@ -917,6 +917,18 @@ namespace SharedLibraryCore
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task<T> WithTimeout<T>(this Task<T> task, TimeSpan timeout)
|
||||||
|
{
|
||||||
|
await Task.WhenAny(task, Task.Delay(timeout));
|
||||||
|
return await task;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task WithTimeout(this Task task, TimeSpan timeout)
|
||||||
|
{
|
||||||
|
await Task.WhenAny(task, Task.Delay(timeout));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static bool ShouldHideLevel(this Permission perm) => perm == Permission.Flagged;
|
public static bool ShouldHideLevel(this Permission perm) => perm == Permission.Flagged;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
Loading…
Reference in New Issue
Block a user