IW4M-Admin/Plugins/SimpleStats/Plugin.cs
RaidMax 6d8d021b16 auth cookie expires after 30 days
only check hit offset when distance > 3 meters
fix null reference on unauthorized user
fixed stats not showing on profile if anticheat disabled
server client history turns red server is unresponsive
2018-04-04 23:38:45 -05:00

265 lines
10 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using SharedLibrary;
using SharedLibrary.Configuration;
using SharedLibrary.Dtos;
using SharedLibrary.Helpers;
using SharedLibrary.Interfaces;
using SharedLibrary.Services;
using StatsPlugin.Config;
using StatsPlugin.Helpers;
using StatsPlugin.Models;
namespace StatsPlugin
{
class Plugin : IPlugin
{
public string Name => "Simple Stats";
public float Version => 1.0f;
public string Author => "RaidMax";
public static StatManager Manager { get; private set; }
private IManager ServerManager;
public static BaseConfigurationHandler<StatsConfiguration> Config { get; private set; }
public async Task OnEventAsync(Event E, Server S)
{
switch (E.Type)
{
case Event.GType.Start:
Manager.AddServer(S);
break;
case Event.GType.Stop:
break;
case Event.GType.Connect:
await Manager.AddPlayer(E.Origin);
break;
case Event.GType.Disconnect:
await Manager.RemovePlayer(E.Origin);
break;
case Event.GType.Say:
if (E.Data != string.Empty && E.Data.Trim().Length > 0 && E.Message.Trim()[0] != '!' && E.Origin.ClientId > 1)
await Manager.AddMessageAsync(E.Origin.ClientId, E.Owner.GetHashCode(), E.Data);
break;
case Event.GType.MapChange:
Manager.ResetKillstreaks(S.GetHashCode());
await Manager.Sync(S);
break;
case Event.GType.MapEnd:
break;
case Event.GType.Broadcast:
break;
case Event.GType.Tell:
break;
case Event.GType.Kick:
break;
case Event.GType.Ban:
break;
case Event.GType.Remote:
break;
case Event.GType.Unknown:
break;
case Event.GType.Report:
break;
case Event.GType.Flag:
break;
case Event.GType.Script:
break;
case Event.GType.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]);
else if (!E.Owner.CustomCallback)
await Manager.AddStandardKill(E.Origin, E.Target);
break;
case Event.GType.Death:
break;
}
}
public async Task OnLoadAsync(IManager manager)
{
// load custom configuration
Config = new BaseConfigurationHandler<StatsConfiguration>("StatsPluginSettings");
if (Config.Configuration() == null)
{
Config.Set((StatsConfiguration)new StatsConfiguration().Generate());
await Config.Save();
}
// meta data info
async Task<List<ProfileMeta>> getStats(int clientId)
{
var statsSvc = new GenericRepository<EFClientStatistics>();
var clientStats = await statsSvc.FindAsync(c => c.ClientId == clientId);
int kills = clientStats.Sum(c => c.Kills);
int deaths = clientStats.Sum(c => c.Deaths);
double kdr = Math.Round(kills / (double)deaths, 2);
double skill = Math.Round(clientStats.Sum(c => c.Skill) / clientStats.Count, 2);
double spm = Math.Round(clientStats.Sum(c => c.SPM), 1);
return new List<ProfileMeta>()
{
new ProfileMeta()
{
Key = "Kills",
Value = kills
},
new ProfileMeta()
{
Key = "Deaths",
Value = deaths
},
new ProfileMeta()
{
Key = "KDR",
Value = kdr
},
new ProfileMeta()
{
Key = "Skill",
Value = skill
},
new ProfileMeta()
{
Key = "Score Per Minute",
Value = spm
}
};
}
async Task<List<ProfileMeta>> getAnticheatInfo(int clientId)
{
var statsSvc = new GenericRepository<EFClientStatistics>();
var clientStats = await statsSvc.FindAsync(c => c.ClientId == clientId);
double headRatio = 0;
double chestRatio = 0;
double abdomenRatio = 0;
double chestAbdomenRatio = 0;
double hitOffsetAverage = 0;
if (clientStats.Where(cs => cs.HitLocations.Count > 0).FirstOrDefault() != null)
{
chestRatio = Math.Round(clientStats.Where(c => c.HitLocations.Count > 0).Sum(c =>
c.HitLocations.First(hl => hl.Location == IW4Info.HitLocation.torso_upper).HitCount) /
(double)clientStats.Where(c => c.HitLocations.Count > 0)
.Sum(c => c.HitLocations.Where(hl => hl.Location != IW4Info.HitLocation.none).Sum(f => f.HitCount)), 2);
abdomenRatio = Math.Round(clientStats.Where(c => c.HitLocations.Count > 0).Sum(c =>
c.HitLocations.First(hl => hl.Location == IW4Info.HitLocation.torso_lower).HitCount) /
(double)clientStats.Where(c => c.HitLocations.Count > 0).Sum(c => c.HitLocations.Where(hl => hl.Location != IW4Info.HitLocation.none).Sum(f => f.HitCount)), 2);
chestAbdomenRatio = Math.Round(clientStats.Where(c => c.HitLocations.Count > 0).Sum(cs => cs.HitLocations.First(hl => hl.Location == IW4Info.HitLocation.torso_upper).HitCount) /
(double)clientStats.Where(c => c.HitLocations.Count > 0).Sum(cs => cs.HitLocations.First(hl => hl.Location == IW4Info.HitLocation.torso_lower).HitCount), 2);
headRatio = Math.Round(clientStats.Where(c => c.HitLocations.Count > 0).Sum(cs => cs.HitLocations.First(hl => hl.Location == IW4Info.HitLocation.head).HitCount) /
(double)clientStats.Where(c => c.HitLocations.Count > 0)
.Sum(c => c.HitLocations.Where(hl => hl.Location != IW4Info.HitLocation.none).Sum(f => f.HitCount)), 2);
hitOffsetAverage = clientStats.Sum(c => c.AverageHitOffset) / Math.Max(1, clientStats.Where(c => c.AverageHitOffset > 0).Count());
}
return new List<ProfileMeta>()
{
new ProfileMeta()
{
Key = "Chest Ratio",
Value = chestRatio,
Sensitive = true
},
new ProfileMeta()
{
Key = "Abdomen Ratio",
Value = abdomenRatio,
Sensitive = true
},
new ProfileMeta()
{
Key = "Chest To Abdomen Ratio",
Value = chestAbdomenRatio,
Sensitive = true
},
new ProfileMeta()
{
Key = "Headshot Ratio",
Value = headRatio,
Sensitive = true
},
new ProfileMeta()
{
Key = "Hit Offset Average",
Value = $"{Math.Round(((float)hitOffsetAverage).ToDegrees(), 4)}°",
Sensitive = true
}
};
}
async Task<List<ProfileMeta>> getMessages(int clientId)
{
var messageSvc = new GenericRepository<EFClientMessage>();
var messages = await messageSvc.FindAsync(m => m.ClientId == clientId);
var messageMeta = messages.Select(m => new ProfileMeta()
{
Key = "EventMessage",
Value = m.Message,
When = m.TimeSent
}).ToList();
messageMeta.Add(new ProfileMeta()
{
Key = "Messages",
Value = messages.Count
});
return messageMeta;
}
MetaService.AddMeta(getStats);
if (Config.Configuration().EnableAntiCheat)
{
MetaService.AddMeta(getAnticheatInfo);
}
MetaService.AddMeta(getMessages);
// todo: is this fast? make async?
string totalKills()
{
var serverStats = new GenericRepository<EFServerStatistics>();
return serverStats.Find(s => s.Active)
.Sum(c => c.TotalKills).ToString("#,##0");
}
string totalPlayTime()
{
var serverStats = new GenericRepository<EFServerStatistics>();
return Math.Ceiling((serverStats.GetQuery(s => s.Active)
.Sum(c => c.TotalPlayTime) / 3600.0)).ToString("#,##0");
}
manager.GetMessageTokens().Add(new MessageToken("TOTALKILLS", totalKills));
manager.GetMessageTokens().Add(new MessageToken("TOTALPLAYTIME", totalPlayTime));
ServerManager = manager;
Manager = new StatManager(manager);
}
public Task OnTickAsync(Server S) => Utilities.CompletedTask;
public async Task OnUnloadAsync()
{
foreach (var sv in ServerManager.GetServers())
await Manager.Sync(sv);
}
}
}