stats tweaked to scale SPM based on team size
invalid client id results in 404 rather than exception page performance based on traditional elo rating fixed @ (broadcast commands) added reports to penalty list and profile
This commit is contained in:
parent
36d493f05b
commit
d9a601328c
@ -34,7 +34,7 @@ namespace IW4MAdmin.Application.EventParsers
|
|||||||
{
|
{
|
||||||
string message = lineSplit[4].Replace("\x15", "");
|
string message = lineSplit[4].Replace("\x15", "");
|
||||||
|
|
||||||
if (message[0] == '!' || message[1] == '@')
|
if (message[0] == '!' || message[0] == '@')
|
||||||
{
|
{
|
||||||
return new GameEvent()
|
return new GameEvent()
|
||||||
{
|
{
|
||||||
|
@ -138,7 +138,7 @@ namespace IW4MAdmin.Application
|
|||||||
sensitiveEvent.OnProcessed.Set();
|
sensitiveEvent.OnProcessed.Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
await Task.Delay(1000);
|
await Task.Delay(2500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,19 +127,9 @@ namespace Application.RconParsers
|
|||||||
}
|
}
|
||||||
|
|
||||||
// this happens if status is requested while map is rotating
|
// this happens if status is requested while map is rotating
|
||||||
if (Status.Contains("Server Initialization"))
|
|
||||||
{
|
|
||||||
throw new ServerException("Server is rotating map");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Status.Length > 5 && validMatches == 0)
|
if (Status.Length > 5 && validMatches == 0)
|
||||||
{
|
{
|
||||||
IW4MAdmin.Application.Program.ServerManager.Logger.WriteError("BAD STATUS!");
|
throw new ServerException("Server is rotating map");
|
||||||
foreach (var s in Status)
|
|
||||||
{
|
|
||||||
IW4MAdmin.Application.Program.ServerManager.Logger.WriteDebug(s);
|
|
||||||
}
|
|
||||||
throw new ServerException("Bad status received");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return StatusPlayers;
|
return StatusPlayers;
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
using IW4MAdmin.Plugins.Stats.Cheat;
|
using IW4MAdmin.Plugins.Stats.Cheat;
|
||||||
using IW4MAdmin.Plugins.Stats.Models;
|
using IW4MAdmin.Plugins.Stats.Models;
|
||||||
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace IW4MAdmin.Plugins.Stats.Helpers
|
namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||||
{
|
{
|
||||||
@ -9,6 +11,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
public ConcurrentDictionary<int, Detection> PlayerDetections { get; set; }
|
public ConcurrentDictionary<int, Detection> PlayerDetections { get; set; }
|
||||||
public EFServerStatistics ServerStatistics { get; private set; }
|
public EFServerStatistics ServerStatistics { get; private set; }
|
||||||
public EFServer Server { get; private set; }
|
public EFServer Server { get; private set; }
|
||||||
|
public bool IsTeamBased { get; set; }
|
||||||
|
|
||||||
public ServerStats(EFServer sv, EFServerStatistics st)
|
public ServerStats(EFServer sv, EFServerStatistics st)
|
||||||
{
|
{
|
||||||
@ -17,5 +20,18 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
ServerStatistics = st;
|
ServerStatistics = st;
|
||||||
Server = sv;
|
Server = sv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int TeamCount(IW4Info.Team teamName)
|
||||||
|
{
|
||||||
|
if (PlayerStats.Count(p => p.Value.Team == IW4Info.Team.Spectator) / (double)PlayerStats.Count <= 0.25)
|
||||||
|
{
|
||||||
|
return IsTeamBased ? Math.Max(PlayerStats.Count(p => p.Value.Team == teamName), 1) : Math.Max(PlayerStats.Count - 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return IsTeamBased ? (int)Math.Max(Math.Floor(PlayerStats.Count / 2.0), 1) : Math.Max(PlayerStats.Count - 1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ using SharedLibraryCore.Interfaces;
|
|||||||
using SharedLibraryCore.Objects;
|
using SharedLibraryCore.Objects;
|
||||||
using SharedLibraryCore.Commands;
|
using SharedLibraryCore.Commands;
|
||||||
using IW4MAdmin.Plugins.Stats.Models;
|
using IW4MAdmin.Plugins.Stats.Models;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace IW4MAdmin.Plugins.Stats.Helpers
|
namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||||
{
|
{
|
||||||
@ -68,7 +69,10 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
statsSvc.ServerStatsSvc.SaveChanges();
|
statsSvc.ServerStatsSvc.SaveChanges();
|
||||||
|
|
||||||
var serverStats = statsSvc.ServerStatsSvc.Find(c => c.ServerId == serverId).FirstOrDefault();
|
var serverStats = statsSvc.ServerStatsSvc.Find(c => c.ServerId == serverId).FirstOrDefault();
|
||||||
Servers.TryAdd(serverId, new ServerStats(server, serverStats));
|
Servers.TryAdd(serverId, new ServerStats(server, serverStats)
|
||||||
|
{
|
||||||
|
IsTeamBased = sv.Gametype != "dm"
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@ -214,24 +218,21 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
serverStats.TotalPlayTime += (int)(DateTime.UtcNow - pl.LastConnection).TotalSeconds;
|
serverStats.TotalPlayTime += (int)(DateTime.UtcNow - pl.LastConnection).TotalSeconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddDamageEvent(string eventLine, int clientId, int serverId)
|
public void AddDamageEvent(string eventLine, int attackerClientId, int victimClientId, int serverId)
|
||||||
{
|
{
|
||||||
/* 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]|_)+)$";
|
string regex = @"^(D);(.+);([0-9]+);(allies|axis);(.+);([0-9]+);(allies|axis);(.+);(.+);([0-9]+);(.+);(.+)$";
|
||||||
|
var match = Regex.Match(eventLine, regex, RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
var match = Regex.Match(damageLine, regex, RegexOptions.IgnoreCase);
|
if (match.Success)
|
||||||
|
{
|
||||||
if (match.Success)
|
// this gives us what time the player is on
|
||||||
{
|
var attackerStats = Servers[serverId].PlayerStats[attackerClientId];
|
||||||
var meansOfDeath = ParseEnum<IW4Info.MeansOfDeath>.Get(match.Groups[12].Value, typeof(IW4Info.MeansOfDeath));
|
var victimStats = Servers[serverId].PlayerStats[victimClientId];
|
||||||
var hitLocation = ParseEnum<IW4Info.HitLocation>.Get(match.Groups[13].Value, typeof(IW4Info.HitLocation));
|
IW4Info.Team victimTeam = (IW4Info.Team)Enum.Parse(typeof(IW4Info.Team), match.Groups[4].ToString());
|
||||||
|
IW4Info.Team attackerTeam = (IW4Info.Team)Enum.Parse(typeof(IW4Info.Team), match.Groups[7].ToString());
|
||||||
if (meansOfDeath == IW4Info.MeansOfDeath.MOD_PISTOL_BULLET ||
|
attackerStats.Team = attackerTeam;
|
||||||
meansOfDeath == IW4Info.MeansOfDeath.MOD_RIFLE_BULLET ||
|
victimStats.Team = victimTeam;
|
||||||
meansOfDeath == IW4Info.MeansOfDeath.MOD_HEAD_SHOT)
|
}
|
||||||
{
|
|
||||||
ClientStats.HitLocations.First(hl => hl.Location == hitLocation).HitCount += 1;
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -494,22 +495,38 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
// calulate elo
|
// calulate elo
|
||||||
if (Servers[attackerStats.ServerId].PlayerStats.Count > 1)
|
if (Servers[attackerStats.ServerId].PlayerStats.Count > 1)
|
||||||
{
|
{
|
||||||
double attackerLobbyRating = Servers[attackerStats.ServerId].PlayerStats
|
/* var validAttackerLobbyRatings = Servers[attackerStats.ServerId].PlayerStats
|
||||||
.Where(cs => cs.Value.ClientId != attackerStats.ClientId)
|
.Where(cs => cs.Value.ClientId != attackerStats.ClientId)
|
||||||
.Average(cs => cs.Value.EloRating);
|
.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 victimLobbyRating = Servers[attackerStats.ServerId].PlayerStats
|
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 => cs.Value.ClientId != victimStats.ClientId)
|
||||||
.Average(cs => cs.Value.EloRating);
|
.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 attackerEloDifference = Math.Log(attackerLobbyRating <= 0 ? 1 : attackerLobbyRating) - Math.Log(attackerStats.EloRating <= 0 ? 1 : attackerStats.EloRating);
|
double victimLobbyRating = validVictimLobbyRatings.Count() > 0 ?
|
||||||
|
validVictimLobbyRatings.Average(cs => cs.Value.EloRating) :
|
||||||
|
victimStats.EloRating;*/
|
||||||
|
|
||||||
|
double attackerEloDifference = Math.Log(Math.Max(1, victimStats.EloRating)) - Math.Log(Math.Max(1, attackerStats.EloRating));
|
||||||
double winPercentage = 1.0 / (1 + Math.Pow(10, attackerEloDifference / Math.E));
|
double winPercentage = 1.0 / (1 + Math.Pow(10, attackerEloDifference / Math.E));
|
||||||
|
|
||||||
double victimEloDifference = Math.Log(victimLobbyRating <= 0 ? 1 : victimLobbyRating) - Math.Log(victimStats.EloRating <= 0 ? 1 : victimStats.EloRating);
|
// 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));
|
// double lossPercentage = 1.0 / (1 + Math.Pow(10, victimEloDifference/ Math.E));
|
||||||
|
|
||||||
attackerStats.EloRating += 24.0 * (1 - winPercentage);
|
attackerStats.EloRating += 6.0 * (1 - winPercentage);
|
||||||
victimStats.EloRating -= 24.0 * winPercentage;
|
victimStats.EloRating -= 6.0 * (1 - winPercentage);
|
||||||
|
|
||||||
attackerStats.EloRating = Math.Max(0, Math.Round(attackerStats.EloRating, 2));
|
attackerStats.EloRating = Math.Max(0, Math.Round(attackerStats.EloRating, 2));
|
||||||
victimStats.EloRating = Math.Max(0, Math.Round(victimStats.EloRating, 2));
|
victimStats.EloRating = Math.Max(0, Math.Round(victimStats.EloRating, 2));
|
||||||
@ -548,12 +565,15 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
{
|
{
|
||||||
scoreDifference = clientStats.RoundScore + clientStats.LastScore;
|
scoreDifference = clientStats.RoundScore + clientStats.LastScore;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (clientStats.RoundScore > 0 && clientStats.LastScore < clientStats.RoundScore)
|
else if (clientStats.RoundScore > 0 && clientStats.LastScore < clientStats.RoundScore)
|
||||||
{
|
{
|
||||||
scoreDifference = clientStats.RoundScore - clientStats.LastScore;
|
scoreDifference = clientStats.RoundScore - clientStats.LastScore;
|
||||||
}
|
}
|
||||||
|
|
||||||
double killSPM = scoreDifference / timeSinceLastCalc;
|
double killSPM = scoreDifference / timeSinceLastCalc;
|
||||||
|
double spmMultiplier = 2.934 * Math.Pow(Servers[clientStats.ServerId].TeamCount(clientStats.Team == IW4Info.Team.Allies ? IW4Info.Team.Axis : IW4Info.Team.Allies), -0.454);
|
||||||
|
killSPM *= Math.Max(1, spmMultiplier);
|
||||||
|
|
||||||
// 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
|
||||||
@ -676,5 +696,10 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
// this should prevent the gunk for having a long lasting context.
|
// this should prevent the gunk for having a long lasting context.
|
||||||
ContextThreads[serverId] = new ThreadSafeStatsService();
|
ContextThreads[serverId] = new ThreadSafeStatsService();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetTeamBased(int serverId, bool isTeamBased)
|
||||||
|
{
|
||||||
|
Servers[serverId].IsTeamBased = isTeamBased;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,13 @@ namespace IW4MAdmin.Plugins.Stats
|
|||||||
{
|
{
|
||||||
public class IW4Info
|
public class IW4Info
|
||||||
{
|
{
|
||||||
|
public enum Team
|
||||||
|
{
|
||||||
|
Spectator,
|
||||||
|
Axis,
|
||||||
|
Allies
|
||||||
|
}
|
||||||
|
|
||||||
public enum MeansOfDeath
|
public enum MeansOfDeath
|
||||||
{
|
{
|
||||||
NONE,
|
NONE,
|
||||||
|
@ -94,5 +94,7 @@ namespace IW4MAdmin.Plugins.Stats.Models
|
|||||||
}
|
}
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
private List<int> SessionScores = new List<int>() { 0 };
|
private List<int> SessionScores = new List<int>() { 0 };
|
||||||
|
[NotMapped]
|
||||||
|
public IW4Info.Team Team { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,7 @@ namespace IW4MAdmin.Plugins.Stats
|
|||||||
await Manager.AddMessageAsync(E.Origin.ClientId, E.Owner.GetHashCode(), E.Data);
|
await Manager.AddMessageAsync(E.Origin.ClientId, E.Owner.GetHashCode(), E.Data);
|
||||||
break;
|
break;
|
||||||
case GameEvent.EventType.MapChange:
|
case GameEvent.EventType.MapChange:
|
||||||
|
Manager.SetTeamBased(E.Owner.GetHashCode(), E.Owner.Gametype != "dm");
|
||||||
Manager.ResetKillstreaks(S.GetHashCode());
|
Manager.ResetKillstreaks(S.GetHashCode());
|
||||||
await Manager.Sync(S);
|
await Manager.Sync(S);
|
||||||
break;
|
break;
|
||||||
@ -83,8 +84,8 @@ namespace IW4MAdmin.Plugins.Stats
|
|||||||
case GameEvent.EventType.Death:
|
case GameEvent.EventType.Death:
|
||||||
break;
|
break;
|
||||||
case GameEvent.EventType.Damage:
|
case GameEvent.EventType.Damage:
|
||||||
if (!E.Owner.CustomCallback)
|
// if (!E.Owner.CustomCallback)
|
||||||
Manager.AddDamageEvent(E.Data, E.Origin.ClientId, E.Owner.GetHashCode());
|
Manager.AddDamageEvent(E.Data, E.Origin.ClientId, E.Target.ClientId, E.Owner.GetHashCode());
|
||||||
break;
|
break;
|
||||||
case GameEvent.EventType.ScriptDamage:
|
case GameEvent.EventType.ScriptDamage:
|
||||||
killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0];
|
killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0];
|
||||||
|
@ -451,6 +451,7 @@ namespace SharedLibraryCore.Commands
|
|||||||
if (ActiveClient != null)
|
if (ActiveClient != null)
|
||||||
{
|
{
|
||||||
ActiveClient.Level = newPerm;
|
ActiveClient.Level = newPerm;
|
||||||
|
await E.Owner.Manager.GetClientService().Update(ActiveClient);
|
||||||
await ActiveClient.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_SUCCESS_TARGET"]} {newPerm}");
|
await ActiveClient.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_SUCCESS_TARGET"]} {newPerm}");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -806,6 +807,20 @@ namespace SharedLibraryCore.Commands
|
|||||||
|
|
||||||
E.Owner.Reports.Add(new Report(E.Target, E.Origin, E.Data));
|
E.Owner.Reports.Add(new Report(E.Target, E.Origin, E.Data));
|
||||||
|
|
||||||
|
Penalty newReport = new Penalty()
|
||||||
|
{
|
||||||
|
Type = Penalty.PenaltyType.Report,
|
||||||
|
Expires = DateTime.UtcNow,
|
||||||
|
Offender = E.Target,
|
||||||
|
Offense = E.Data,
|
||||||
|
Punisher = E.Origin,
|
||||||
|
Active = true,
|
||||||
|
When = DateTime.UtcNow,
|
||||||
|
Link = E.Target.AliasLink
|
||||||
|
};
|
||||||
|
|
||||||
|
await E.Owner.Manager.GetPenaltyService().Create(newReport);
|
||||||
|
|
||||||
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_SUCCESS"]);
|
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_SUCCESS"]);
|
||||||
E.Owner.Manager.GetEventHandler().AddEvent(new GameEvent(GameEvent.EventType.Report, E.Data, E.Origin, E.Target, E.Owner));
|
E.Owner.Manager.GetEventHandler().AddEvent(new GameEvent(GameEvent.EventType.Report, E.Data, E.Origin, E.Target, E.Owner));
|
||||||
await E.Owner.ToAdmins(String.Format("^5{0}^7->^1{1}^7: {2}", E.Origin.Name, E.Target.Name, E.Data));
|
await E.Owner.ToAdmins(String.Format("^5{0}^7->^1{1}^7: {2}", E.Origin.Name, E.Target.Name, E.Data));
|
||||||
|
@ -127,6 +127,9 @@ namespace SharedLibraryCore.Services
|
|||||||
};
|
};
|
||||||
var foundClient = await iqClient.FirstOrDefaultAsync();
|
var foundClient = await iqClient.FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
if (foundClient == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
foundClient.Client.LinkedAccounts = new Dictionary<int, long>();
|
foundClient.Client.LinkedAccounts = new Dictionary<int, long>();
|
||||||
// todo: find out the best way to do this
|
// todo: find out the best way to do this
|
||||||
// I'm doing this here because I don't know the best way to have multiple awaits in the query
|
// I'm doing this here because I don't know the best way to have multiple awaits in the query
|
||||||
@ -163,12 +166,11 @@ namespace SharedLibraryCore.Services
|
|||||||
if (entity.Level != client.Level)
|
if (entity.Level != client.Level)
|
||||||
{
|
{
|
||||||
// get all clients that use the same aliasId
|
// get all clients that use the same aliasId
|
||||||
var matchingClients = await context.Clients
|
var matchingClients = context.Clients
|
||||||
.Where(c => c.CurrentAliasId == client.CurrentAliasId)
|
.Where(c => c.CurrentAliasId == client.CurrentAliasId);
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
// update all related clients level
|
// update all related clients level
|
||||||
matchingClients.ForEach(c => c.Level = (client.Level == Player.Permission.Banned) ?
|
await matchingClients.ForEachAsync(c => c.Level = (client.Level == Player.Permission.Banned) ?
|
||||||
client.Level : entity.Level);
|
client.Level : entity.Level);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,10 @@ namespace WebfrontCore.Controllers
|
|||||||
public async Task<IActionResult> ProfileAsync(int id)
|
public async Task<IActionResult> ProfileAsync(int id)
|
||||||
{
|
{
|
||||||
var client = await Manager.GetClientService().Get(id);
|
var client = await Manager.GetClientService().Get(id);
|
||||||
|
if (client == null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
var clientDto = new PlayerInfo()
|
var clientDto = new PlayerInfo()
|
||||||
{
|
{
|
||||||
|
@ -94,8 +94,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.penalties-color-report {
|
.penalties-color-report {
|
||||||
color: #749363;
|
color: #b3ae8f;
|
||||||
color: rgba(116, 147, 99, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.penalties-color-warning {
|
.penalties-color-warning {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user