lots of fixes :)

This commit is contained in:
RaidMax 2018-05-10 00:34:29 -05:00
parent 9ff7f39e8d
commit e964013700
21 changed files with 291 additions and 248 deletions

View File

@ -21,7 +21,7 @@ namespace IW4MAdmin.Application.EventParsers
{
return new GameEvent()
{
Type = GameEvent.EventType.Script,
Type = GameEvent.EventType.Kill,
Data = logLine,
Origin = server.GetPlayersAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 6)),
Target = server.GetPlayersAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 2)),
@ -32,13 +32,27 @@ namespace IW4MAdmin.Application.EventParsers
if (cleanedEventLine == "say" || cleanedEventLine == "sayteam")
{
string message = lineSplit[4].Replace("\x15", "");
if (message[0] == '!' || message[1] == '@')
{
return new GameEvent()
{
Type = GameEvent.EventType.Command,
Data = message,
Origin = server.GetPlayersAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 2)),
Owner = server,
Message = message
};
}
return new GameEvent()
{
Type = GameEvent.EventType.Say,
Data = lineSplit[4].Replace("\x15", ""),
Data = message,
Origin = server.GetPlayersAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 2)),
Owner = server,
Message = lineSplit[4].Replace("\x15", "")
Message = message
};
}
@ -46,7 +60,7 @@ namespace IW4MAdmin.Application.EventParsers
{
return new GameEvent()
{
Type = GameEvent.EventType.Script,
Type = GameEvent.EventType.ScriptKill,
Data = logLine,
Origin = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[1].ConvertLong()),
Target = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[2].ConvertLong()),

View File

@ -20,7 +20,7 @@ namespace IW4MAdmin.Application.EventParsers
{
return new GameEvent()
{
Type = GameEvent.EventType.Script,
Type = GameEvent.EventType.Kill,
Data = cleanedEventLine,
Origin = server.GetPlayersAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 6)),
Target = server.GetPlayersAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 2)),

View File

@ -3,20 +3,23 @@ using SharedLibraryCore.Interfaces;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace IW4MAdmin.Application
{
class GameEventHandler : IEventHandler
{
private ConcurrentQueue<GameEvent> EventQueue;
private ConcurrentQueue<GameEvent> StatusSensitiveQueue;
private Queue<GameEvent> StatusSensitiveQueue;
private IManager Manager;
public GameEventHandler(IManager mgr)
{
EventQueue = new ConcurrentQueue<GameEvent>();
StatusSensitiveQueue = new ConcurrentQueue<GameEvent>();
StatusSensitiveQueue = new Queue<GameEvent>();
Manager = mgr;
}
@ -26,25 +29,30 @@ namespace IW4MAdmin.Application
Manager.GetLogger().WriteDebug($"Got new event of type {gameEvent.Type} for {gameEvent.Owner}");
#endif
// we need this to keep accurate track of the score
if (gameEvent.Type == GameEvent.EventType.Script ||
gameEvent.Type == GameEvent.EventType.Kill ||
if (gameEvent.Type == GameEvent.EventType.Kill ||
gameEvent.Type == GameEvent.EventType.Damage ||
gameEvent.Type == GameEvent.EventType.ScriptDamage ||
gameEvent.Type == GameEvent.EventType.ScriptKill ||
gameEvent.Type == GameEvent.EventType.MapChange)
{
#if DEBUG
Manager.GetLogger().WriteDebug($"Added sensitive event to queue");
#endif
lock (StatusSensitiveQueue)
{
StatusSensitiveQueue.Enqueue(gameEvent);
}
return;
}
else
{
EventQueue.Enqueue(gameEvent);
Manager.SetHasEvent();
}
#if DEBUG
Manager.GetLogger().WriteDebug($"There are now {EventQueue.Count} events in queue");
#endif
Manager.SetHasEvent();
}
public string[] GetEventOutput()
@ -55,6 +63,8 @@ namespace IW4MAdmin.Application
public GameEvent GetNextSensitiveEvent()
{
if (StatusSensitiveQueue.Count > 0)
{
lock (StatusSensitiveQueue)
{
if (!StatusSensitiveQueue.TryDequeue(out GameEvent newEvent))
{
@ -66,6 +76,7 @@ namespace IW4MAdmin.Application
return newEvent;
}
}
}
return null;
}

View File

@ -1,10 +1,7 @@
using SharedLibraryCore;
using SharedLibraryCore.Interfaces;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace IW4MAdmin.Application.IO
@ -14,7 +11,6 @@ namespace IW4MAdmin.Application.IO
Server Server;
long PreviousFileSize;
GameLogReader Reader;
Timer RefreshInfoTimer;
string GameLogFile;
class EventState
@ -28,11 +24,19 @@ namespace IW4MAdmin.Application.IO
GameLogFile = gameLogPath;
Reader = new GameLogReader(gameLogPath, server.EventParser);
Server = server;
RefreshInfoTimer = new Timer(OnEvent, new EventState()
Task.Run(async () =>
{
while (!server.Manager.ShutdownRequested())
{
OnEvent(new EventState()
{
Log = server.Manager.GetLogger(),
ServerId = server.ToString()
}, 0, 100);
});
await Task.Delay(100);
}
});
}
private void OnEvent(object state)

View File

@ -32,7 +32,7 @@ namespace IW4MAdmin.Application.IO
string newLine;
while (!String.IsNullOrEmpty(newLine = rd.ReadLine()))
{
logLines.Add(newLine.Replace("\r\n", ""));
logLines.Add(newLine);
}
}
@ -49,7 +49,7 @@ namespace IW4MAdmin.Application.IO
events.Add(Parser.GetEvent(server, eventLine));
}
catch (Exception e)
catch (Exception)
{
}
}

View File

@ -18,6 +18,7 @@ namespace IW4MAdmin.Application
static public double Version { get; private set; }
static public ApplicationManager ServerManager = ApplicationManager.GetInstance();
public static string OperatingDirectory = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + Path.DirectorySeparatorChar;
private static ManualResetEventSlim OnShutdownComplete = new ManualResetEventSlim();
public static void Main(string[] args)
{
@ -43,6 +44,7 @@ namespace IW4MAdmin.Application
CheckDirectories();
ServerManager = ApplicationManager.GetInstance();
Console.CancelKeyPress += new ConsoleCancelEventHandler(OnCancelKey);
Localization.Configure.Initialize(ServerManager.GetApplicationSettings().Configuration()?.CustomLocale);
loc = Utilities.CurrentLocalization.LocalizationIndex;
@ -126,30 +128,21 @@ namespace IW4MAdmin.Application
if (userInput?.Length > 0)
{
Origin.CurrentServer = ServerManager.Servers[0];
GameEvent E = new GameEvent(GameEvent.EventType.Say, userInput, Origin, null, ServerManager.Servers[0]);
GameEvent E = new GameEvent()
{
Type = GameEvent.EventType.Command,
Data = userInput,
Origin = Origin,
Owner = ServerManager.Servers[0]
};
ServerManager.GetEventHandler().AddEvent(E);
E.OnProcessed.Wait();
}
Console.Write('>');
} while (ServerManager.Running);
});
if (ServerManager.GetApplicationSettings().Configuration().EnableWebFront)
{
Task.Run(() =>
{
try
{
WebfrontCore.Program.Init(ServerManager);
}
catch (Exception e)
{
ServerManager.Logger.WriteWarning("Webfront had unhandled exception");
ServerManager.Logger.WriteDebug(e.Message);
}
});
}
}
catch (Exception e)
@ -164,9 +157,21 @@ namespace IW4MAdmin.Application
Console.ReadKey();
}
if (ServerManager.GetApplicationSettings().Configuration().EnableWebFront)
{
Task.Run(() => WebfrontCore.Program.Init(ServerManager));
}
OnShutdownComplete.Reset();
ServerManager.Start().Wait();
ServerManager.Logger.WriteVerbose(loc["MANAGER_SHUTDOWN_SUCCESS"]);
OnShutdownComplete.Set();
}
private static void OnCancelKey(object sender, ConsoleCancelEventArgs e)
{
ServerManager.Stop();
OnShutdownComplete.Wait();
}
static void CheckDirectories()

View File

@ -60,17 +60,10 @@ namespace IW4MAdmin.Application
Api = new EventApi();
ServerEventOccurred += Api.OnServerEvent;
ConfigHandler = new BaseConfigurationHandler<ApplicationConfiguration>("IW4MAdminSettings");
Console.CancelKeyPress += new ConsoleCancelEventHandler(OnCancelKey);
StartTime = DateTime.UtcNow;
OnEvent = new ManualResetEventSlim();
}
private void OnCancelKey(object sender, ConsoleCancelEventArgs args)
{
Stop();
}
public IList<Server> GetServers()
{
return Servers;
@ -141,9 +134,9 @@ namespace IW4MAdmin.Application
Logger.WriteError($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_EXCEPTION"]} {sensitiveEvent.Owner}");
Logger.WriteDebug("Error Message: " + E.Message);
Logger.WriteDebug("Error Trace: " + E.StackTrace);
sensitiveEvent.OnProcessed.Set();
continue;
}
sensitiveEvent.OnProcessed.Set();
}
await Task.Delay(5000);
@ -152,8 +145,8 @@ namespace IW4MAdmin.Application
public async Task Init()
{
// setup the event handler after the class is initialized
Handler = new GameEventHandler(this);
Running = true;
#region DATABASE
var ipList = (await ClientSvc.Find(c => c.Level > Player.Permission.Trusted))
.Select(c => new
@ -304,6 +297,8 @@ namespace IW4MAdmin.Application
#region INIT
async Task Init(ServerConfiguration Conf)
{
// setup the event handler after the class is initialized
Handler = new GameEventHandler(this);
try
{
var ServerInstance = new IW4MServer(this, Conf);
@ -336,8 +331,6 @@ namespace IW4MAdmin.Application
await Task.WhenAll(config.Servers.Select(c => Init(c)).ToArray());
#endregion
Running = true;
}
private void SendHeartbeat(object state)
@ -419,6 +412,12 @@ namespace IW4MAdmin.Application
#endif
}
// this happens if a plugin requires login
catch (AuthorizationException e)
{
await newEvent.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMAND_NOTAUTHORIZED"]} - {e.Message}");
}
catch (NetworkException e)
{
Logger.WriteError(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMUNICATION"]);
@ -449,9 +448,7 @@ namespace IW4MAdmin.Application
}
// this should allow parallel processing of events
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
Task.WhenAll(eventList);
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
await Task.WhenAll(eventList);
// signal that all events have been processed
OnEvent.Reset();
@ -459,8 +456,8 @@ namespace IW4MAdmin.Application
#if !DEBUG
HeartbeatTimer.Change(0, Timeout.Infinite);
foreach (var S in Servers)
S.Broadcast("^1" + Utilities.CurrentLocalization.LocalizationIndex["BROADCAST_OFFLINE"]).Wait();
foreach (var S in _servers)
await S.Broadcast("^1" + Utilities.CurrentLocalization.LocalizationIndex["BROADCAST_OFFLINE"]);
#endif
_servers.Clear();
}

View File

@ -23,11 +23,12 @@ namespace Application.RconParsers
TempBan = "tempbanclient {0} \"{1}\""
};
private static string StatusRegex = @"^( *[0-9]+) +-*([0-9]+) +((?:[A-Z]+|[0-9]+)) +((?:[a-z]|[0-9]){16}|bot[0-9]+) +(.{0,20}) +([0-9]+) +(\d+\.\d+\.\d+.\d+\:-*\d{1,5}|0+.0+:-*\d{1,5}) +(-*[0-9]+) +([0-9]+) *$";
private static string StatusRegex = @"^( *[0-9]+) +-*([0-9]+) +((?:[A-Z]+|[0-9]+)) +((?:[a-z]|[0-9]){16}|bot[0-9]+|(?:[0-9]+)) +(.{0,20}) +([0-9]+) +(\d+\.\d+\.\d+.\d+\:-*\d{1,5}|0+.0+:-*\d{1,5}) +(-*[0-9]+) +([0-9]+) *$";
public async Task<string[]> ExecuteCommandAsync(Connection connection, string command)
{
return (await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND, command)).Skip(1).ToArray();
var response = await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND, command);
return response.Skip(1).ToArray();
}
public async Task<Dvar<T>> GetDvarAsync<T>(Connection connection, string dvarName)

View File

@ -25,11 +25,9 @@ namespace IW4MAdmin
{
public class IW4MServer : Server
{
private CancellationToken cts;
private static Index loc = Utilities.CurrentLocalization.LocalizationIndex;
private GameLogEvent LogEvent;
public IW4MServer(IManager mgr, ServerConfiguration cfg) : base(mgr, cfg) { }
public override int GetHashCode()
@ -196,17 +194,22 @@ namespace IW4MAdmin
Logger.WriteInfo($"Client {player} connecting...");
var e = new GameEvent(GameEvent.EventType.Connect, "", player, null, this);
Manager.GetEventHandler().AddEvent(e);
e.OnProcessed.WaitHandle.WaitOne(5000);
if (!Manager.GetApplicationSettings().Configuration().EnableClientVPNs &&
await VPNCheck.UsingVPN(player.IPAddressString, Manager.GetApplicationSettings().Configuration().IPHubAPIKey))
{
await player.Kick(Utilities.CurrentLocalization.LocalizationIndex["SERVER_KICK_VPNS_NOTALLOWED"], new Player() { ClientId = 1 });
return true;
}
var e = new GameEvent()
{
Type = GameEvent.EventType.Connect,
Origin = player,
Owner = this
};
Manager.GetEventHandler().AddEvent(e);
return true;
}
@ -375,14 +378,31 @@ namespace IW4MAdmin
await ProcessEvent(E);
Manager.GetEventApi().OnServerEvent(this, E);
Command C = null;
if (E.Type == GameEvent.EventType.Command)
{
try
{
C = await ValidateCommand(E);
}
catch (CommandException e)
{
Logger.WriteInfo(e.Message);
}
if (C != null)
{
E.Extra = C;
}
}
// this allows us to catch exceptions but still run it parallel
async Task pluginHandlingAsync(Task onEvent, string pluginName)
{
try
{
if (cts.IsCancellationRequested)
return;
await onEvent;
}
@ -467,44 +487,7 @@ namespace IW4MAdmin
});
}
else if (E.Type == GameEvent.EventType.Script)
{
Manager.GetEventHandler().AddEvent(GameEvent.TransferWaiter(GameEvent.EventType.Kill, E));
}
if (E.Type == GameEvent.EventType.Say && E.Data?.Length >= 2)
{
if (E.Data.Substring(0, 1) == "!" ||
E.Data.Substring(0, 1) == "@" ||
E.Origin.Level == Player.Permission.Console)
{
Command C = null;
try
{
C = await ValidateCommand(E);
}
catch (CommandException e)
{
Logger.WriteInfo(e.Message);
}
if (C != null)
{
if (C.RequiresTarget && E.Target == null)
{
Logger.WriteWarning("Requested event (command) requiring target does not have a target!");
}
E.Extra = C;
// reprocess event as a command
Manager.GetEventHandler().AddEvent(GameEvent.TransferWaiter(GameEvent.EventType.Command, E));
}
}
else // Not a command
{
E.Data = E.Data.StripColors();
@ -515,7 +498,6 @@ namespace IW4MAdmin
Time = DateTime.UtcNow
});
}
}
if (E.Type == GameEvent.EventType.MapChange)
{
@ -533,7 +515,6 @@ namespace IW4MAdmin
else
{
Gametype = dict["gametype"].StripColors();
Hostname = dict["hostname"]?.StripColors();
@ -549,7 +530,11 @@ namespace IW4MAdmin
Hostname = dict["sv_hostname"].StripColors();
string mapname = dict["mapname"].StripColors();
CurrentMap = Maps.Find(m => m.Name == mapname) ?? new Map() { Alias = mapname, Name = mapname };
CurrentMap = Maps.Find(m => m.Name == mapname) ?? new Map()
{
Alias = mapname,
Name = mapname
};
}
}
@ -569,7 +554,6 @@ namespace IW4MAdmin
await E.Owner.ExecuteCommandAsync(E.Message);
}
//todo: move
while (ChatHistory.Count > Math.Ceiling((double)ClientNum / 2))
ChatHistory.RemoveAt(0);
@ -634,18 +618,15 @@ namespace IW4MAdmin
override public async Task<bool> ProcessUpdatesAsync(CancellationToken cts)
{
// this isn't really used anymore
this.cts = cts;
try
{
if (Manager.ShutdownRequested())
{
foreach (var plugin in SharedLibraryCore.Plugins.PluginImporter.ActivePlugins)
await plugin.OnUnloadAsync();
for (int i = 0; i < Players.Count; i++)
await RemovePlayer(i);
foreach (var plugin in SharedLibraryCore.Plugins.PluginImporter.ActivePlugins)
await plugin.OnUnloadAsync();
}
// only check every 2 minutes if the server doesn't seem to be responding
@ -746,7 +727,6 @@ namespace IW4MAdmin
if (ServerConfig.UseIW5MParser)
RconParser = new IW5MRConParser();
var version = await this.GetDvarAsync<string>("version");
GameName = Utilities.GetGame(version.Value);
@ -838,7 +818,6 @@ namespace IW4MAdmin
$"{basepath.Value.Replace('\\', Path.DirectorySeparatorChar)}{Path.DirectorySeparatorChar}{game.Replace('/', Path.DirectorySeparatorChar)}{Path.DirectorySeparatorChar}{logfile.Value}";
}
// hopefully fix wine drive name mangling
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{

View File

@ -13,12 +13,13 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
class Detection
{
int Kills;
int HitCount;
int AboveThresholdCount;
double AverageKillTime;
Dictionary<IW4Info.HitLocation, int> HitLocationCount;
double AngleDifferenceAverage;
EFClientStatistics ClientStats;
DateTime LastKill;
DateTime LastHit;
long LastOffset;
ILogger Log;
Strain Strain;
@ -74,22 +75,21 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
ClientPenalty = Penalty.PenaltyType.Any,
};
if (LastKill == DateTime.MinValue)
LastKill = DateTime.UtcNow;
if (LastHit == DateTime.MinValue)
LastHit = DateTime.UtcNow;
HitLocationCount[kill.HitLoc]++;
if (!isDamage)
{
Kills++;
AverageKillTime = (AverageKillTime + (DateTime.UtcNow - LastKill).TotalSeconds) / Kills;
}
HitCount++;
#region VIEWANGLES
if (kill.AnglesList.Count >= 2)
{
double realAgainstPredict = Math.Abs(Vector3.AbsoluteDistance(kill.AnglesList[0], kill.AnglesList[1]) -
(Vector3.AbsoluteDistance(kill.AnglesList[0], kill.ViewAngles) +
Vector3.AbsoluteDistance(kill.AnglesList[1], kill.ViewAngles)));
double realAgainstPredict = Vector3.ViewAngleDistance(kill.AnglesList[0], kill.AnglesList[1], kill.ViewAngles);
// LIFETIME
var hitLoc = ClientStats.HitLocations
@ -102,26 +102,37 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
if (hitLoc.HitOffsetAverage > Thresholds.MaxOffset)
{
Log.WriteDebug("*** Reached Max Lifetime Average for Angle Difference ***");
Log.WriteDebug($"Lifetime Average = {newAverage}");
Log.WriteDebug($"Bone = {hitLoc.Location}");
Log.WriteDebug($"HitCount = {hitLoc.HitCount}");
Log.WriteDebug($"ID = {kill.AttackerId}");
return new DetectionPenaltyResult()
{
ClientPenalty = Penalty.PenaltyType.Ban,
ClientPenalty = Penalty.PenaltyType.Flag,
RatioAmount = hitLoc.HitOffsetAverage,
KillCount = ClientStats.SessionKills,
KillCount = hitLoc.HitCount,
};
}
// SESSION
int sessHitLocCount = HitLocationCount[kill.HitLoc];
double sessAverage = (AngleDifferenceAverage * (sessHitLocCount - 1)) + realAgainstPredict / sessHitLocCount;
double sessAverage = (AngleDifferenceAverage * (HitCount - 1) + realAgainstPredict) / HitCount;
AngleDifferenceAverage = sessAverage;
if (sessAverage > Thresholds.MaxOffset)
{
Log.WriteDebug("*** Reached Max Session Average for Angle Difference ***");
Log.WriteDebug($"Session Average = {sessAverage}");
// Log.WriteDebug($"Bone = {hitLoc.Location}");
Log.WriteDebug($"HitCount = {HitCount}");
Log.WriteDebug($"ID = {kill.AttackerId}");
return new DetectionPenaltyResult()
{
ClientPenalty = Penalty.PenaltyType.Ban,
RatioAmount = sessHitLocCount,
KillCount = ClientStats.SessionKills,
ClientPenalty = Penalty.PenaltyType.Flag,
RatioAmount = sessAverage,
KillCount = HitCount,
};
}
@ -130,7 +141,9 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
#endif
}
var currentStrain = Strain.GetStrain(kill.ViewAngles, kill.TimeOffset - LastOffset);
double diff = Math.Max(50, kill.TimeOffset - LastOffset);
var currentStrain = Strain.GetStrain(kill.ViewAngles, diff);
//LastHit = kill.When;
LastOffset = kill.TimeOffset;
if (currentStrain > ClientStats.MaxStrain)
@ -138,29 +151,38 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
ClientStats.MaxStrain = currentStrain;
}
if (currentStrain > Thresholds.MaxStrain)
{
Log.WriteDebug("*** Reached Max Strain ***");
Log.WriteDebug($"Strain = {currentStrain}");
Log.WriteDebug($"Angles = {kill.ViewAngles} {kill.AnglesList[0]} {kill.AnglesList[1]}");
Log.WriteDebug($"Time = {diff}");
Log.WriteDebug($"HitCount = {HitCount}");
Log.WriteDebug($"ID = {kill.AttackerId}");
}
if (Strain.TimesReachedMaxStrain >= 3)
{
return new DetectionPenaltyResult()
{
ClientPenalty = Penalty.PenaltyType.Ban,
ClientPenalty = Penalty.PenaltyType.Flag,
RatioAmount = ClientStats.MaxStrain,
KillCount = ClientStats.SessionKills,
KillCount = HitCount,
};
}
#if DEBUG
Log.WriteDebug($"Current Strain: {currentStrain}");
#endif
LastKill = kill.When;
#endregion
#region SESSION_RATIOS
if (Kills >= Thresholds.LowSampleMinKills)
{
double marginOfError = Thresholds.GetMarginOfError(Kills);
double marginOfError = Thresholds.GetMarginOfError(HitCount);
// determine what the max headshot percentage can be for current number of kills
double lerpAmount = Math.Min(1.0, (Kills - Thresholds.LowSampleMinKills) / (double)(/*Thresholds.HighSampleMinKills*/ 60 - Thresholds.LowSampleMinKills));
double lerpAmount = Math.Min(1.0, (HitCount - Thresholds.LowSampleMinKills) / (double)(/*Thresholds.HighSampleMinKills*/ 60 - Thresholds.LowSampleMinKills));
double maxHeadshotLerpValueForFlag = Thresholds.Lerp(Thresholds.HeadshotRatioThresholdLowSample(2.0), Thresholds.HeadshotRatioThresholdHighSample(2.0), lerpAmount) + marginOfError;
double maxHeadshotLerpValueForBan = Thresholds.Lerp(Thresholds.HeadshotRatioThresholdLowSample(3.0), Thresholds.HeadshotRatioThresholdHighSample(3.0), lerpAmount) + marginOfError;
// determine what the max bone percentage can be for current number of kills
@ -168,10 +190,10 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
double maxBoneRatioLerpValueForBan = Thresholds.Lerp(Thresholds.BoneRatioThresholdLowSample(3.25), Thresholds.BoneRatioThresholdHighSample(3.25), lerpAmount) + marginOfError;
// calculate headshot ratio
double currentHeadshotRatio = ((HitLocationCount[IW4Info.HitLocation.head] + HitLocationCount[IW4Info.HitLocation.helmet] + HitLocationCount[IW4Info.HitLocation.neck]) / (double)Kills);
double currentHeadshotRatio = ((HitLocationCount[IW4Info.HitLocation.head] + HitLocationCount[IW4Info.HitLocation.helmet] + HitLocationCount[IW4Info.HitLocation.neck]) / (double)HitCount);
// calculate maximum bone
double currentMaxBoneRatio = (HitLocationCount.Values.Select(v => v / (double)Kills).Max());
double currentMaxBoneRatio = (HitLocationCount.Values.Select(v => v / (double)HitCount).Max());
var bone = HitLocationCount.FirstOrDefault(b => b.Value == HitLocationCount.Values.Max()).Key;
#region HEADSHOT_RATIO

View File

@ -132,7 +132,6 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
else
{
// todo: look at this more
statsSvc.ClientStatSvc.Update(clientStats);
}
@ -201,10 +200,9 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
playerStats.TryRemove(pl.ClientId, out EFClientStatistics removedValue3);
detectionStats.TryRemove(pl.ClientId, out Cheat.Detection removedValue4);
/* // sync their stats before they leave
clientStats = UpdateStats(clientStats);*/
// sync their stats before they leave
//clientStats = UpdateStats(clientStats);
// todo: should this be saved every disconnect?
statsSvc.ClientStatSvc.Update(clientStats);
await statsSvc.ClientStatSvc.SaveChangesAsync();
// increment the total play time
@ -225,7 +223,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
/// Process stats for kill event
/// </summary>
/// <returns></returns>
public async Task AddScriptKill(bool isDamage, DateTime time, Player attacker, Player victim, int serverId, string map, string hitLoc, string type,
public async Task AddScriptHit(bool isDamage, DateTime time, Player attacker, Player victim, int serverId, string map, string hitLoc, string type,
string damage, string weapon, string killOrigin, string deathOrigin, string viewAngles, string offset, string isKillstreakKill, string Ads, string snapAngles)
{
var statsSvc = ContextThreads[serverId];
@ -312,7 +310,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
{
clientStats.HitLocations.Single(hl => hl.Location == kill.HitLoc).HitCount += 1;
statsSvc.ClientStatSvc.Update(clientStats);
//statsSvc.ClientStatSvc.Update(clientStats);
// await statsSvc.ClientStatSvc.SaveChangesAsync();
}
@ -500,7 +498,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
{
scoreDifference = clientStats.RoundScore + clientStats.LastScore;
}
else
else if (clientStats.RoundScore > 0 && clientStats.LastScore < clientStats.RoundScore)
{
scoreDifference = clientStats.RoundScore - clientStats.LastScore;
}

View File

@ -44,7 +44,8 @@ namespace IW4MAdmin.Plugins.Stats
await Manager.RemovePlayer(E.Origin);
break;
case GameEvent.EventType.Say:
if (E.Data != string.Empty && E.Data.Trim().Length > 0 && E.Message.Trim()[0] != '!' && E.Origin.ClientId > 1)
if (!string.IsNullOrEmpty(E.Data) &&
E.Origin.ClientId > 1)
await Manager.AddMessageAsync(E.Origin.ClientId, E.Owner.GetHashCode(), E.Data);
break;
case GameEvent.EventType.MapChange:
@ -69,23 +70,26 @@ namespace IW4MAdmin.Plugins.Stats
break;
case GameEvent.EventType.Flag:
break;
case GameEvent.EventType.Script:
case GameEvent.EventType.ScriptKill:
string[] killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0];
if (killInfo.Length >= 13)
await Manager.AddScriptHit(false, E.Time, 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], killInfo[13]);
break;
case GameEvent.EventType.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(false, E.Time, 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], killInfo[13]);
else if (!E.Owner.CustomCallback)
if (!E.Owner.CustomCallback)
await Manager.AddStandardKill(E.Origin, E.Target);
break;
case GameEvent.EventType.Death:
break;
//case GameEvent.EventType.Damage:
case GameEvent.EventType.Damage:
if (!E.Owner.CustomCallback)
Manager.AddDamageEvent(E.Data, E.Origin.ClientId, E.Owner.GetHashCode());
break;
case GameEvent.EventType.ScriptDamage:
killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0];
if (killInfo.Length >= 9 && E.Owner.CustomCallback)
await Manager.AddScriptKill(true, E.Time, E.Origin, E.Target, S.GetHashCode(), S.CurrentMap.Name, killInfo[7], killInfo[8],
if (killInfo.Length >= 13)
await Manager.AddScriptHit(true, E.Time, 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], killInfo[13]);
break;
}

View File

@ -689,7 +689,6 @@ namespace SharedLibraryCore.Commands
public override async Task ExecuteAsync(GameEvent E)
{
// todo: move unflag to seperate command
if (E.Target.Level >= E.Origin.Level)
{
await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_FAIL"]} ^5{E.Target.Name}");

View File

@ -33,8 +33,8 @@ namespace SharedLibraryCore
Command,
// FROM GAME
Script,
ScriptDamage,
ScriptKill,
Kill,
Damage,
Death,
@ -49,35 +49,19 @@ namespace SharedLibraryCore
Owner = S;
OnProcessed = new ManualResetEventSlim();
Time = DateTime.UtcNow;
CurrentEventId++;
Id = CurrentEventId;
}
public GameEvent()
{
OnProcessed = new ManualResetEventSlim();
Time = DateTime.UtcNow;
CurrentEventId++;
Id = CurrentEventId;
}
public static GameEvent TransferWaiter(EventType newType, GameEvent e)
{
var newEvent = new GameEvent()
{
Data = e.Data,
Extra = e.Extra,
Message = e.Message,
OnProcessed = e.OnProcessed,
Origin = e.Origin,
Owner = e.Owner,
Remote = e.Remote,
Target = e.Target,
Type = newType,
};
// hack: prevent the previous event from completing until this one is done
e.OnProcessed = new ManualResetEventSlim();
newEvent.Time = e.Time;
return newEvent;
}
private static long CurrentEventId;
public EventType Type;
public string Data; // Data is usually the message sent by player
@ -89,5 +73,6 @@ namespace SharedLibraryCore
public object Extra { get; set; }
public ManualResetEventSlim OnProcessed { get; set; }
public DateTime Time { get; private set; }
public long Id { get; private set; }
}
}

View File

@ -48,16 +48,41 @@ namespace SharedLibraryCore.Helpers
{
double deltaX = Math.Abs(b.X -a.X);
double deltaY = Math.Abs(b.Y - a.Y);
// double deltaZ = Math.Abs(b.Z - a.Z);
double deltaZ = Math.Abs(b.Z - a.Z);
// this 'fixes' the roll-over angles
double dx = deltaX < 360.0 / 2 ? deltaX : 360.0 - deltaX;
double dy = deltaY < 360.0 / 2 ? deltaY : 360.0 - deltaY;
// double dz = deltaZ < 360.0 / 2 ? deltaZ : 360.0 - deltaZ;
double dz = deltaZ < 360.0 / 2 ? deltaZ : 360.0 - deltaZ;
return Math.Sqrt((dx * dx) + (dy * dy) /*+ (dz * dz)*/);
}
public static double ViewAngleDistance(Vector3 a, Vector3 b, Vector3 c)
{
double dabX = Math.Abs(a.X - b.X);
dabX = dabX < 360.0 / 2 ? dabX : 360.0 - dabX;
double dabY = Math.Abs(a.Y - b.Y);
dabY = dabY < 360.0 / 2 ? dabY : 360.0 - dabY;
double dacX = Math.Abs(a.X - c.X);
dacX = dacX < 360.0 / 2 ? dacX : 360.0 - dacX;
double dacY = Math.Abs(a.Y - c.Y);
dacY = dacY < 360.0 / 2 ? dacY : 360.0 - dacY;
double dbcX = Math.Abs(b.X - c.X);
dbcX = dbcX < 360.0 / 2 ? dbcX : 360.0 - dbcX;
double dbcY = Math.Abs(b.Y - c.Y);
dbcY = dbcY < 360.0 / 2 ? dbcY : 360.0 - dbcY;
double deltaX = (dabX - dacX - dbcX) / 2.0;
deltaX = deltaX < 360.0 / 2 ? deltaX : 360.0 - deltaX;
double deltaY = (dabY - dacY - dbcY) / 2.0;
deltaY = deltaY < 360.0 / 2 ? deltaY : 360.0 - deltaY;
return Math.Round(Math.Sqrt((deltaX * deltaX) + (deltaY * deltaY)), 4);
}
public static Vector3 Subtract(Vector3 a, Vector3 b) => new Vector3(b.X - a.X, b.Y - a.Y, b.Z - a.Z);
public double DotProduct(Vector3 a) => (a.X * this.X) + (a.Y * this.Y) + (a.Z * this.Z);

View File

@ -40,8 +40,14 @@ namespace SharedLibraryCore.Objects
public async Task Tell(String Message)
{
// this is console or remote so send immediately
if (ClientNumber < 0)
{
await CurrentServer.Tell(Message, this);
}
// await CurrentServer.Tell(Message, this);
else
{
var e = new GameEvent()
{
Message = Message,
@ -52,8 +58,7 @@ namespace SharedLibraryCore.Objects
};
CurrentServer.Manager.GetEventHandler().AddEvent(e);
// this ensures the output it sent before returning
await Task.Run(() => e.OnProcessed.Wait());
}
}
public async Task Kick(String Message, Player Sender)

View File

@ -56,8 +56,6 @@ namespace SharedLibraryCore.RCon
{
public IPEndPoint Endpoint { get; private set; }
public string RConPassword { get; private set; }
public ConcurrentQueue<ManualResetEventSlim> ResponseQueue;
//Socket ServerConnection;
ILogger Log;
int FailedSends;
int FailedReceives;
@ -79,13 +77,6 @@ namespace SharedLibraryCore.RCon
OnReceived = new ManualResetEvent(false);
}
~Connection()
{
/*ServerConnection.Shutdown(SocketShutdown.Both);
ServerConnection.Close();
ServerConnection.Dispose();*/
}
private void OnConnectedCallback(IAsyncResult ar)
{
var serverSocket = (Socket)ar.AsyncState;
@ -144,12 +135,12 @@ namespace SharedLibraryCore.RCon
if (!connectionState.Buffer.Take(4).ToArray().SequenceEqual(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF }))
throw new NetworkException("Unexpected packet received");
if (FailedReceives == 0 && serverConnection.Available > 0)
/* if (FailedReceives == 0 && serverConnection.Available > 0)
{
serverConnection.BeginReceive(connectionState.Buffer, 0, connectionState.Buffer.Length, 0,
new AsyncCallback(OnReceivedCallback), connectionState);
}
else
else*/
{
response = connectionState.ResponseString.ToString();
OnReceived.Set();
@ -169,7 +160,7 @@ namespace SharedLibraryCore.RCon
catch (ObjectDisposedException)
{
Log.WriteWarning($"Tried to check for more available bytes for disposed socket on {Endpoint}");
// Log.WriteWarning($"Tried to check for more available bytes for disposed socket on {Endpoint}");
}
}
@ -211,7 +202,9 @@ namespace SharedLibraryCore.RCon
retrySend:
try
{
#if DEBUG
Console.WriteLine($"Sending Command {parameters}");
#endif
if (!OnConnected.WaitOne(StaticHelpers.SocketTimeout))
throw new SocketException((int)SocketError.TimedOut);

View File

@ -41,7 +41,6 @@ namespace SharedLibraryCore
PlayerHistory = new Queue<PlayerHistory>();
ChatHistory = new List<ChatInfo>();
NextMessage = 0;
OnEvent = new ManualResetEventSlim();
CustomSayEnabled = Manager.GetApplicationSettings().Configuration().EnableCustomSayName;
CustomSayName = Manager.GetApplicationSettings().Configuration().CustomSayName;
InitializeTokens();
@ -139,8 +138,8 @@ namespace SharedLibraryCore
Manager.GetEventHandler().AddEvent(e);
#else
Logger.WriteVerbose(Message.StripColors());
await Task.CompletedTask;
#endif
await Task.CompletedTask;
}
/// <summary>
@ -162,19 +161,22 @@ namespace SharedLibraryCore
if (Target.Level == Player.Permission.Console)
{
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine(Utilities.StripColors(Message));
Console.WriteLine(Message.StripColors());
Console.ForegroundColor = ConsoleColor.Gray;
}
if (CommandResult.Count > 15)
CommandResult.RemoveAt(0);
if (Target.ClientNumber < 0)
{
CommandResult.Add(new CommandResponseInfo()
{
Response = Utilities.StripColors(Message),
Response = Message.StripColors(),
ClientId = Target.ClientId
});
}
}
/// <summary>
/// Send a message to all admins on the server
@ -315,7 +317,6 @@ namespace SharedLibraryCore
public RCon.Connection RemoteConnection { get; protected set; }
public IRConParser RconParser { get; protected set; }
public IEventParser EventParser { get; set; }
public ManualResetEventSlim OnEvent { get; private set; }
// Internal
protected string IP;
@ -327,6 +328,7 @@ namespace SharedLibraryCore
protected TimeSpan LastMessage;
protected IFile LogFile;
protected DateTime LastPoll;
protected ManualResetEventSlim OnRemoteCommandResponse;
// only here for performance
private bool CustomSayEnabled;

View File

@ -407,7 +407,7 @@ namespace SharedLibraryCore
public static Task SetDvarAsync(this Server server, string dvarName, object dvarValue) => server.RconParser.SetDvarAsync(server.RemoteConnection, dvarName, dvarValue);
public static Task<string[]> ExecuteCommandAsync(this Server server, string commandName) => server.RconParser.ExecuteCommandAsync(server.RemoteConnection, commandName);
public static async Task<string[]> ExecuteCommandAsync(this Server server, string commandName) => await server.RconParser.ExecuteCommandAsync(server.RemoteConnection, commandName);
public static Task<List<Player>> GetStatusAsync(this Server server) => server.RconParser.GetStatusAsync(server.RemoteConnection);

View File

@ -39,7 +39,7 @@ namespace WebfrontCore.Controllers
var remoteEvent = new GameEvent()
{
Type = GameEvent.EventType.Say,
Type = GameEvent.EventType.Command,
Data = command,
Origin = client,
Owner = server,
@ -48,8 +48,7 @@ namespace WebfrontCore.Controllers
Manager.GetEventHandler().AddEvent(remoteEvent);
// wait for the event to process
await Task.Run(() => remoteEvent.OnProcessed.Wait());
await Task.Run(() => remoteEvent.OnProcessed.Wait(5000));
var response = server.CommandResult.Where(c => c.ClientId == client.ClientId).ToList();
// remove the added command response

View File

@ -45,7 +45,7 @@
<div id="profile_aliases" class="pr-0 pr-sm-4 pb-2 mb-2 text-muted order-0">
@{
@Model.NetworkId.ToString("X") <br/>
<span class="text-secondary">@Model.NetworkId.ToString("X")</span> <br />
foreach (string alias in Model.Aliases)
{
@alias <br />