diff --git a/Application/Application.csproj b/Application/Application.csproj
index 3550af1e0..1f36e5a01 100644
--- a/Application/Application.csproj
+++ b/Application/Application.csproj
@@ -5,7 +5,7 @@
netcoreapp2.1
false
RaidMax.IW4MAdmin.Application
- 2.1.5
+ 2.1.6
RaidMax
Forever None
IW4MAdmin
diff --git a/Application/GameEventHandler.cs b/Application/GameEventHandler.cs
index d23d74d95..565afd459 100644
--- a/Application/GameEventHandler.cs
+++ b/Application/GameEventHandler.cs
@@ -1,27 +1,72 @@
using SharedLibraryCore;
+using SharedLibraryCore.Events;
using SharedLibraryCore.Interfaces;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
namespace IW4MAdmin.Application
{
class GameEventHandler : IEventHandler
{
private readonly IManager Manager;
+ static long NextEventId = 1;
+ private readonly SortedList OutOfOrderEvents;
+ private readonly SemaphoreSlim ProcessingEvent;
public GameEventHandler(IManager mgr)
{
Manager = mgr;
+ OutOfOrderEvents = new SortedList();
+ ProcessingEvent = new SemaphoreSlim(0);
+ ProcessingEvent.Release();
}
- public void AddEvent(GameEvent gameEvent)
+ public bool AddEvent(GameEvent gameEvent)
{
#if DEBUG
- Manager.GetLogger().WriteDebug($"Got new event of type {gameEvent.Type} for {gameEvent.Owner}");
+ Manager.GetLogger().WriteDebug($"Got new event of type {gameEvent.Type} for {gameEvent.Owner} with id {gameEvent.Id}");
#endif
- // todo: later
- ((Manager as ApplicationManager).OnServerEvent)(this, new ApplicationManager.GameEventArgs(null, false, gameEvent));
+ while (OutOfOrderEvents.Values.FirstOrDefault()?.Id == Interlocked.Read(ref NextEventId))
+ {
+ lock (OutOfOrderEvents)
+ {
+ var fixedEvent = OutOfOrderEvents.Values[0];
+ OutOfOrderEvents.RemoveAt(0);
+ AddEvent(fixedEvent);
+ }
+ }
+
+ // both the gameEvent Id and the LastEventId are thread safe because we want to synchronize when the
+ // event occurs
+ if (gameEvent.Id == Interlocked.Read(ref NextEventId))
+ {
+ Manager.GetLogger().WriteDebug($"Starting to wait for event with id {gameEvent.Id}");
+ ((Manager as ApplicationManager).OnServerEvent)(this, new GameEventArgs(null, false, gameEvent));
+ Manager.GetLogger().WriteDebug($"Finished waiting for event with id {gameEvent.Id}");
+ Interlocked.Increment(ref NextEventId);
+ }
+
+ // a "newer" event has been added before and "older" one has been added (due to threads and context switching)
+ // so me must wait until the next expected one arrives
+ else
+ {
+ Manager.GetLogger().WriteWarning("Incoming event is out of order");
+ Manager.GetLogger().WriteDebug($"Expected event Id is {Interlocked.Read(ref NextEventId)}, but got {gameEvent.Id} instead");
+
+ // this prevents multiple threads from adding simultaneously
+ lock (OutOfOrderEvents)
+ {
+ if (!OutOfOrderEvents.TryGetValue(gameEvent.Id, out GameEvent discard))
+ {
+ OutOfOrderEvents.Add(gameEvent.Id, gameEvent);
+ }
+ }
+ }
+ return true;
}
}
}
diff --git a/Application/IO/GameLogEventDetection.cs b/Application/IO/GameLogEventDetection.cs
index 61933bf2e..e2f056d96 100644
--- a/Application/IO/GameLogEventDetection.cs
+++ b/Application/IO/GameLogEventDetection.cs
@@ -1,7 +1,9 @@
using SharedLibraryCore;
using SharedLibraryCore.Interfaces;
using System;
+using System.Collections.Generic;
using System.IO;
+using System.Threading;
using System.Threading.Tasks;
namespace IW4MAdmin.Application.IO
@@ -31,44 +33,36 @@ namespace IW4MAdmin.Application.IO
{
Reader = new GameLogReader(gameLogPath, server.EventParser);
}
+
Server = server;
-
- Task.Run(async () =>
- {
- while (!server.Manager.ShutdownRequested())
- {
- if ((server.Manager as ApplicationManager).IsInitialized)
- {
- OnEvent(new EventState()
- {
- Log = server.Manager.GetLogger(),
- ServerId = server.ToString()
- });
- }
- await Task.Delay(100);
- }
- });
}
- private void OnEvent(object state)
+ public void PollForChanges()
{
- long newLength = Reader.Length;
-
- try
+ while (!Server.Manager.ShutdownRequested())
{
- UpdateLogEvents(newLength);
- }
+ if ((Server.Manager as ApplicationManager).IsInitialized)
+ {
+ try
+ {
+ UpdateLogEvents();
+ }
- catch (Exception e)
- {
- ((EventState)state).Log.WriteWarning($"Failed to update log event for {((EventState)state).ServerId}");
- ((EventState)state).Log.WriteDebug($"Exception: {e.Message}");
- ((EventState)state).Log.WriteDebug($"StackTrace: {e.StackTrace}");
+ catch (Exception e)
+ {
+ Server.Logger.WriteWarning($"Failed to update log event for {Server.GetHashCode()}");
+ Server.Logger.WriteDebug($"Exception: {e.Message}");
+ Server.Logger.WriteDebug($"StackTrace: {e.StackTrace}");
+ }
+ }
+ Thread.Sleep(Reader.UpdateInterval);
}
}
- private void UpdateLogEvents(long fileSize)
+ private void UpdateLogEvents()
{
+ long fileSize = Reader.Length;
+
if (PreviousFileSize == 0)
PreviousFileSize = fileSize;
@@ -79,9 +73,12 @@ namespace IW4MAdmin.Application.IO
PreviousFileSize = fileSize;
- var events = Reader.EventsFromLog(Server, fileDiff, 0);
+ var events = Reader.ReadEventsFromLog(Server, fileDiff, 0);
+
foreach (var ev in events)
+ {
Server.Manager.GetEventHandler().AddEvent(ev);
+ }
PreviousFileSize = fileSize;
}
diff --git a/Application/IO/GameLogReader.cs b/Application/IO/GameLogReader.cs
index 1b4ea78b9..9405a0642 100644
--- a/Application/IO/GameLogReader.cs
+++ b/Application/IO/GameLogReader.cs
@@ -14,7 +14,7 @@ namespace IW4MAdmin.Application.IO
public long Length => new FileInfo(LogFile).Length;
- public int UpdateInterval => 100;
+ public int UpdateInterval => 300;
public GameLogReader(string logFile, IEventParser parser)
{
@@ -22,7 +22,7 @@ namespace IW4MAdmin.Application.IO
Parser = parser;
}
- public ICollection EventsFromLog(Server server, long fileSizeDiff, long startPosition)
+ public ICollection ReadEventsFromLog(Server server, long fileSizeDiff, long startPosition)
{
// allocate the bytes for the new log lines
List logLines = new List();
diff --git a/Application/IO/GameLogReaderHttp.cs b/Application/IO/GameLogReaderHttp.cs
index b132e3144..f79e4428d 100644
--- a/Application/IO/GameLogReaderHttp.cs
+++ b/Application/IO/GameLogReaderHttp.cs
@@ -29,11 +29,12 @@ namespace IW4MAdmin.Application.IO
{
using (var cl = new HttpClient())
{
- using (var re = cl.GetAsync($"{LogFile}?length=1").Result)
+ using (var re = cl.GetAsync($"{LogFile}&length=1").Result)
{
using (var content = re.Content)
{
- return Convert.ToInt64(content.ReadAsStringAsync().Result ?? "0");
+ string response = content.ReadAsStringAsync().Result ?? "0";
+ return Convert.ToInt64(response);
}
}
}
@@ -42,12 +43,15 @@ namespace IW4MAdmin.Application.IO
public int UpdateInterval => 1000;
- public ICollection EventsFromLog(Server server, long fileSizeDiff, long startPosition)
+ public ICollection ReadEventsFromLog(Server server, long fileSizeDiff, long startPosition)
{
+#if DEBUG == true
+ server.Logger.WriteDebug($"Begin reading {fileSizeDiff} from http log");
+#endif
string log;
using (var cl = new HttpClient())
{
- using (var re = cl.GetAsync($"{LogFile}?start={fileSizeDiff}").Result)
+ using (var re = cl.GetAsync($"{LogFile}&start={fileSizeDiff}").Result)
{
using (var content = re.Content)
{
@@ -55,18 +59,29 @@ namespace IW4MAdmin.Application.IO
}
}
}
-
+#if DEBUG == true
+ server.Logger.WriteDebug($"retrieved events from http log");
+#endif
List events = new List();
+ string[] lines = log.Split(Environment.NewLine);
+
+#if DEBUG == true
+ server.Logger.WriteDebug($"Begin parse of {lines.Length} lines from http log");
+#endif
// parse each line
- foreach (string eventLine in log.Split(Environment.NewLine))
+ foreach (string eventLine in lines)
{
if (eventLine.Length > 0)
{
try
{
// todo: catch elsewhere
- events.Add(Parser.GetEvent(server, eventLine));
+ var e = Parser.GetEvent(server, eventLine);
+#if DEBUG == true
+ server.Logger.WriteDebug($"Parsed event with id {e.Id} from http");
+#endif
+ events.Add(e);
}
catch (Exception e)
diff --git a/Application/Main.cs b/Application/Main.cs
index 560993ce3..9b6fba07c 100644
--- a/Application/Main.cs
+++ b/Application/Main.cs
@@ -106,7 +106,7 @@ namespace IW4MAdmin.Application
ServerManager.Init().Wait();
- var consoleTask = Task.Run(() =>
+ var consoleTask = Task.Run(async () =>
{
String userInput;
Player Origin = ServerManager.GetClientService().Get(1).Result.AsPlayer();
@@ -136,7 +136,7 @@ namespace IW4MAdmin.Application
};
ServerManager.GetEventHandler().AddEvent(E);
- E.OnProcessed.Wait(5000);
+ await E.OnProcessed.WaitAsync(30 * 1000);
}
Console.Write('>');
diff --git a/Application/Manager.cs b/Application/Manager.cs
index b86e8b295..586c93d3a 100644
--- a/Application/Manager.cs
+++ b/Application/Manager.cs
@@ -22,6 +22,7 @@ using System.Text;
using IW4MAdmin.Application.API.Master;
using System.Reflection;
using SharedLibraryCore.Database;
+using SharedLibraryCore.Events;
namespace IW4MAdmin.Application
{
@@ -33,7 +34,6 @@ namespace IW4MAdmin.Application
public ILogger Logger { get; private set; }
public bool Running { get; private set; }
public bool IsInitialized { get; private set; }
- //public EventHandler ServerEventOccurred { get; private set; }
// define what the delagate function looks like
public delegate void OnServerEventEventHandler(object sender, GameEventArgs e);
// expose the event handler so we can execute the events
@@ -48,22 +48,10 @@ namespace IW4MAdmin.Application
readonly AliasService AliasSvc;
readonly PenaltyService PenaltySvc;
public BaseConfigurationHandler ConfigHandler;
- EventApi Api;
GameEventHandler Handler;
ManualResetEventSlim OnQuit;
readonly IPageList PageList;
- public class GameEventArgs : System.ComponentModel.AsyncCompletedEventArgs
- {
-
- public GameEventArgs(Exception error, bool cancelled, GameEvent userState) : base(error, cancelled, userState)
- {
- Event = userState;
- }
-
- public GameEvent Event { get; }
- }
-
private ApplicationManager()
{
Logger = new Logger($@"{Utilities.OperatingDirectory}IW4MAdmin.log");
@@ -75,13 +63,12 @@ namespace IW4MAdmin.Application
AliasSvc = new AliasService();
PenaltySvc = new PenaltyService();
PrivilegedClients = new Dictionary();
- Api = new EventApi();
- //ServerEventOccurred += Api.OnServerEvent;
ConfigHandler = new BaseConfigurationHandler("IW4MAdminSettings");
StartTime = DateTime.UtcNow;
OnQuit = new ManualResetEventSlim();
PageList = new PageList();
OnServerEvent += OnServerEventAsync;
+ OnServerEvent += EventApi.OnGameEvent;
}
private async void OnServerEventAsync(object sender, GameEventArgs args)
@@ -108,7 +95,7 @@ namespace IW4MAdmin.Application
return;
}
- await newEvent.Owner.ExecuteEvent(newEvent);
+
//// todo: this is a hacky mess
if (newEvent.Origin?.DelayedEvents.Count > 0 &&
@@ -119,7 +106,20 @@ namespace IW4MAdmin.Application
// add the delayed event to the queue
while(events.Count > 0)
{
- var e = events.Dequeue();
+ var oldEvent = events.Dequeue();
+
+ var e = new GameEvent()
+ {
+ Type = oldEvent.Type,
+ Origin = newEvent.Origin,
+ Data = oldEvent.Data,
+ Extra = oldEvent.Extra,
+ Owner = oldEvent.Owner,
+ Message = oldEvent.Message,
+ Target = oldEvent.Target,
+ OnProcessed = oldEvent.OnProcessed,
+ Remote = oldEvent.Remote
+ };
e.Origin = newEvent.Origin;
// check if the target was assigned
@@ -140,9 +140,10 @@ namespace IW4MAdmin.Application
}
}
- Api.OnServerEvent(this, newEvent);
+ await newEvent.Owner.ExecuteEvent(newEvent);
+
#if DEBUG
- Logger.WriteDebug("Processed Event");
+ Logger.WriteDebug($"Processed event with id {newEvent.Id}");
#endif
}
@@ -169,7 +170,7 @@ namespace IW4MAdmin.Application
Logger.WriteDebug("Error Trace: " + ex.StackTrace);
}
// tell anyone waiting for the output that we're done
- newEvent.OnProcessed.Set();
+ newEvent.OnProcessed.Release();
}
public IList GetServers()
@@ -423,7 +424,15 @@ namespace IW4MAdmin.Application
Logger.WriteVerbose($"{Utilities.CurrentLocalization.LocalizationIndex["MANAGER_MONITORING_TEXT"]} {ServerInstance.Hostname}");
// add the start event for this server
- Handler.AddEvent(new GameEvent(GameEvent.EventType.Start, "Server started", null, null, ServerInstance));
+
+ var e = new GameEvent()
+ {
+ Type = GameEvent.EventType.Start,
+ Data = $"{ServerInstance.GameName} started",
+ Owner = ServerInstance
+ };
+
+ Handler.AddEvent(e);
}
catch (ServerException e)
@@ -562,7 +571,6 @@ namespace IW4MAdmin.Application
public PenaltyService GetPenaltyService() => PenaltySvc;
public IConfigurationHandler GetApplicationSettings() => ConfigHandler;
public IDictionary GetPrivilegedClients() => PrivilegedClients;
- public IEventApi GetEventApi() => Api;
public bool ShutdownRequested() => !Running;
public IEventHandler GetEventHandler() => Handler;
diff --git a/Application/Server.cs b/Application/Server.cs
index 6fee44ce1..757abe17d 100644
--- a/Application/Server.cs
+++ b/Application/Server.cs
@@ -27,11 +27,9 @@ namespace IW4MAdmin
{
private static readonly Index loc = Utilities.CurrentLocalization.LocalizationIndex;
private GameLogEventDetection LogEvent;
- private ClientAuthentication AuthQueue;
public IW4MServer(IManager mgr, ServerConfiguration cfg) : base(mgr, cfg)
{
- AuthQueue = new ClientAuthentication();
}
public override int GetHashCode()
@@ -603,7 +601,7 @@ namespace IW4MAdmin
try
{
var polledClients = await PollPlayersAsync();
- var waiterList = new List();
+ var waiterList = new List();
foreach (var disconnectingClient in polledClients[1])
{
@@ -625,7 +623,7 @@ namespace IW4MAdmin
waiterList.Add(e.OnProcessed);
}
// wait for all the disconnect tasks to finish
- await Task.WhenAll(waiterList.Select(t => Task.Run(() => t.Wait(5000))));
+ await Task.WhenAll(waiterList.Select(t => t.WaitAsync()));
waiterList.Clear();
// this are our new connecting clients
@@ -643,7 +641,7 @@ namespace IW4MAdmin
}
// wait for all the connect tasks to finish
- await Task.WhenAll(waiterList.Select(t => Task.Run(() => t.Wait(5000))));
+ await Task.WhenAll(waiterList.Select(t => t.WaitAsync()));
if (ConnectionErrors > 0)
{
@@ -835,7 +833,7 @@ namespace IW4MAdmin
logPath = Regex.Replace($"{Path.DirectorySeparatorChar}{logPath}", @"[A-Z]:", "");
}
- if (!File.Exists(logPath))
+ if (!File.Exists(logPath) && !logPath.StartsWith("http"))
{
Logger.WriteError($"{logPath} {loc["SERVER_ERROR_DNE"]}");
#if !DEBUG
@@ -850,6 +848,8 @@ namespace IW4MAdmin
}
Logger.WriteInfo($"Log file is {logPath}");
+
+ Task.Run(() => LogEvent.PollForChanges());
#if !DEBUG
await Broadcast(loc["BROADCAST_ONLINE"]);
#endif
@@ -1002,15 +1002,17 @@ namespace IW4MAdmin
// this is set only because they're still in the server.
Target.Level = Player.Permission.Banned;
- // let the api know that a ban occured
- Manager.GetEventHandler().AddEvent(new GameEvent()
+ var e = new GameEvent()
{
Type = GameEvent.EventType.Ban,
Data = Message,
Origin = Origin,
Target = Target,
Owner = this
- });
+ };
+
+ // let the api know that a ban occured
+ Manager.GetEventHandler().AddEvent(e);
#if !DEBUG
string formattedString = String.Format(RconParser.GetCommandPrefixes().Kick, Target.ClientNumber, $"{loc["SERVER_BAN_TEXT"]} - ^5{Message} ^7({loc["SERVER_BAN_APPEAL"]} {Website})^7");
diff --git a/Plugins/ScriptPlugins/VPNDetection.js b/Plugins/ScriptPlugins/VPNDetection.js
index ef3604555..5d9af92e9 100644
--- a/Plugins/ScriptPlugins/VPNDetection.js
+++ b/Plugins/ScriptPlugins/VPNDetection.js
@@ -10,7 +10,7 @@ const plugin = {
checkForVpn(origin) {
let exempt = false;
// prevent players that are exempt from being kicked
- this.vpnExceptionIds.forEach(function(id) {
+ this.vpnExceptionIds.forEach(function (id) {
if (id === origin.ClientId) {
exempt = true;
return false;
@@ -48,7 +48,7 @@ const plugin = {
onEventAsync(gameEvent, server) {
// connect event
if (gameEvent.Type === 3) {
- this.checkForVpn(gameEvent.Origin)
+ this.checkForVpn(gameEvent.Origin);
}
},
@@ -57,7 +57,7 @@ const plugin = {
this.logger = manager.GetLogger();
},
- onUnloadAsync() {},
+ onUnloadAsync() { },
- onTickAsync(server) {}
-}
\ No newline at end of file
+ onTickAsync(server) { }
+};
\ No newline at end of file
diff --git a/Plugins/Stats/Cheat/Detection.cs b/Plugins/Stats/Cheat/Detection.cs
index 37ac214c4..d359479cc 100644
--- a/Plugins/Stats/Cheat/Detection.cs
+++ b/Plugins/Stats/Cheat/Detection.cs
@@ -53,7 +53,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
if ((kill.DeathType != IW4Info.MeansOfDeath.MOD_PISTOL_BULLET &&
kill.DeathType != IW4Info.MeansOfDeath.MOD_RIFLE_BULLET &&
kill.DeathType != IW4Info.MeansOfDeath.MOD_HEAD_SHOT) ||
- kill.HitLoc == IW4Info.HitLocation.none)
+ kill.HitLoc == IW4Info.HitLocation.none || kill.TimeOffset - LastOffset < 0)
return new DetectionPenaltyResult()
{
ClientPenalty = Penalty.PenaltyType.Any,
@@ -131,7 +131,6 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
}
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)
diff --git a/Plugins/Stats/Helpers/StatManager.cs b/Plugins/Stats/Helpers/StatManager.cs
index ad220b3a1..3e43035f6 100644
--- a/Plugins/Stats/Helpers/StatManager.cs
+++ b/Plugins/Stats/Helpers/StatManager.cs
@@ -470,6 +470,9 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
Owner = attacker.CurrentServer,
Type = GameEvent.EventType.Flag
};
+ // because we created an event it must be processed by the manager
+ // even if it didn't really do anything
+ Manager.GetEventHandler().AddEvent(e);
await new CFlag().ExecuteAsync(e);
break;
}
@@ -834,9 +837,9 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
// calculate how much the KDR should weigh
// 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);
+ double alpha = Math.Sqrt(2) / Math.Min(600, Math.Max(clientStats.Kills + clientStats.Deaths, 1));
clientStats.RollingWeightedKDR = (alpha * currentKDR) + (1.0 - alpha) * clientStats.KDR;
- double KDRWeight = clientStats.RollingWeightedKDR != 0 ? Math.Round(Math.Pow(clientStats.RollingWeightedKDR, 1.637 / Math.E), 3) : 0;
+ 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
int totalPlayTime = (clientStats.TimePlayed == 0) ?
diff --git a/Plugins/Stats/Plugin.cs b/Plugins/Stats/Plugin.cs
index a6bb26326..1c9d40b0f 100644
--- a/Plugins/Stats/Plugin.cs
+++ b/Plugins/Stats/Plugin.cs
@@ -76,8 +76,12 @@ namespace IW4MAdmin.Plugins.Stats
case GameEvent.EventType.ScriptKill:
string[] killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0];
if (killInfo.Length >= 13)
+ {
+ // todo: remove me
+ E.Owner.Logger.WriteDebug($"Starting Add script hit (kill) for event with id {E.Id}");
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:
if (!E.Owner.CustomCallback)
@@ -90,8 +94,12 @@ namespace IW4MAdmin.Plugins.Stats
case GameEvent.EventType.ScriptDamage:
killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0];
if (killInfo.Length >= 13)
+ {
+ // todo: remove me
+ E.Owner.Logger.WriteDebug($"Starting Add script hit (damage) for event with id {E.Id}");
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;
}
}
diff --git a/Plugins/Tests/ManagerTests.cs b/Plugins/Tests/ManagerTests.cs
index 1f08b075e..4b4d97cae 100644
--- a/Plugins/Tests/ManagerTests.cs
+++ b/Plugins/Tests/ManagerTests.cs
@@ -43,7 +43,7 @@ namespace Tests
public void AddAndRemoveClientsViaJoinShouldSucceed()
{
var server = Manager.GetServers().First();
- var waiters = new Queue();
+ var waiters = new Queue();
int clientStartIndex = 4;
int clientNum = 10;
@@ -103,7 +103,7 @@ namespace Tests
public void AddAndRemoveClientsViaRconShouldSucceed()
{
var server = Manager.GetServers().First();
- var waiters = new Queue();
+ var waiters = new Queue();
int clientIndexStart = 1;
int clientNum = 8;
@@ -187,7 +187,6 @@ namespace Tests
resetEvent.Wait(5000);
-
}
}
}
diff --git a/SharedLibraryCore/Commands/NativeCommands.cs b/SharedLibraryCore/Commands/NativeCommands.cs
index b7142e62a..4f08e1891 100644
--- a/SharedLibraryCore/Commands/NativeCommands.cs
+++ b/SharedLibraryCore/Commands/NativeCommands.cs
@@ -119,7 +119,17 @@ namespace SharedLibraryCore.Commands
{
await E.Target.Kick(E.Data, E.Origin);
await E.Origin.Tell($"^5{E.Target} ^7{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_KICK_SUCCESS"]}");
- E.Owner.Manager.GetEventHandler().AddEvent(new GameEvent(GameEvent.EventType.Kick, E.Data, E.Origin, E.Target, E.Owner));
+
+ var e = new GameEvent()
+ {
+ Type = GameEvent.EventType.Kick,
+ Data = E.Data,
+ Origin = E.Origin,
+ Target = E.Target,
+ Owner = E.Owner
+ };
+
+ E.Owner.Manager.GetEventHandler().AddEvent(e);
}
else
await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_KICK_FAIL"]} {E.Target.Name}");
@@ -179,14 +189,17 @@ namespace SharedLibraryCore.Commands
{
await E.Target.TempBan(Message, length, E.Origin);
await E.Origin.Tell($"^5{E.Target} ^7{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_TEMPBAN_SUCCESS"]} ^5{length.TimeSpanText()}");
- E.Owner.Manager.GetEventHandler().AddEvent(new GameEvent()
+
+ var e = new GameEvent()
{
Type = GameEvent.EventType.TempBan,
Data = E.Data,
Origin = E.Origin,
Target = E.Target,
Owner = E.Owner
- });
+ };
+
+ E.Owner.Manager.GetEventHandler().AddEvent(e);
}
else
await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_TEMPBAN_FAIL"]} {E.Target.Name}");
@@ -732,7 +745,17 @@ namespace SharedLibraryCore.Commands
};
await E.Owner.Manager.GetPenaltyService().Create(newPenalty);
- E.Owner.Manager.GetEventHandler().AddEvent(new GameEvent(GameEvent.EventType.Flag, E.Data, E.Origin, E.Target, E.Owner));
+
+ var e = new GameEvent()
+ {
+ Type = GameEvent.EventType.Flag,
+ Data = E.Data,
+ Origin = E.Origin,
+ Target = E.Target,
+ Owner = E.Owner
+ };
+
+ E.Owner.Manager.GetEventHandler().AddEvent(e);
await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_SUCCESS"]} ^5{E.Target.Name}");
}
@@ -766,15 +789,16 @@ namespace SharedLibraryCore.Commands
await E.Owner.Manager.GetClientService().Update(E.Target);
await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_UNFLAG"]} ^5{E.Target.Name}");
- E.Owner.Manager.GetEventHandler().AddEvent(new GameEvent()
+ var e = new GameEvent()
{
Data = E.Data,
Origin = E.Origin,
Target = E.Target,
Owner = E.Owner,
Type = GameEvent.EventType.Unflag
- });
+ };
+ E.Owner.Manager.GetEventHandler().AddEvent(e);
}
else
@@ -846,7 +870,17 @@ namespace SharedLibraryCore.Commands
await E.Owner.Manager.GetPenaltyService().Create(newReport);
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));
+
+ var e = new GameEvent()
+ {
+ Type = GameEvent.EventType.Report,
+ Data = E.Data,
+ Origin = E.Origin,
+ Target = E.Target,
+ Owner = E.Owner
+ };
+
+ E.Owner.Manager.GetEventHandler().AddEvent(e);
await E.Owner.ToAdmins(String.Format("^5{0}^7->^1{1}^7: {2}", E.Origin.Name, E.Target.Name, E.Data));
}
}
diff --git a/Application/API/EventAPI.cs b/SharedLibraryCore/Events/EventAPI.cs
similarity index 81%
rename from Application/API/EventAPI.cs
rename to SharedLibraryCore/Events/EventAPI.cs
index 5005bf2c0..4fc37e5e2 100644
--- a/Application/API/EventAPI.cs
+++ b/SharedLibraryCore/Events/EventAPI.cs
@@ -1,19 +1,15 @@
using System;
using System.Collections.Generic;
-
-using SharedLibraryCore;
using SharedLibraryCore.Dtos;
-using SharedLibraryCore.Interfaces;
-using SharedLibraryCore.Objects;
-namespace IW4MAdmin.Application.API
+namespace SharedLibraryCore.Events
{
- class EventApi : IEventApi
+ public class EventApi
{
- private const int MaxEvents = 100;
- private Queue RecentEvents = new Queue();
+ const int MaxEvents = 100;
+ static Queue RecentEvents = new Queue();
- public IEnumerable GetEvents(bool shouldConsume)
+ public static IEnumerable GetEvents(bool shouldConsume)
{
var eventList = RecentEvents.ToArray();
@@ -26,8 +22,9 @@ namespace IW4MAdmin.Application.API
return eventList;
}
- public void OnServerEvent(object sender, GameEvent E)
+ public static void OnGameEvent(object sender, GameEventArgs eventState)
{
+ var E = eventState.Event;
// don't want to clog up the api with unknown events
if (E.Type == GameEvent.EventType.Unknown)
return;
@@ -71,7 +68,7 @@ namespace IW4MAdmin.Application.API
/// Adds event to the list and removes first added if reached max capacity
///
/// EventInfo to add
- private void AddNewEvent(EventInfo info)
+ private static void AddNewEvent(EventInfo info)
{
// remove the first added event
if (RecentEvents.Count >= MaxEvents)
diff --git a/SharedLibraryCore/Event.cs b/SharedLibraryCore/Events/GameEvent.cs
similarity index 82%
rename from SharedLibraryCore/Event.cs
rename to SharedLibraryCore/Events/GameEvent.cs
index 8cb6d5733..141bebc25 100644
--- a/SharedLibraryCore/Event.cs
+++ b/SharedLibraryCore/Events/GameEvent.cs
@@ -44,29 +44,17 @@ namespace SharedLibraryCore
StatusUpdate
}
- public GameEvent(EventType t, string d, Player O, Player T, Server S)
- {
- Type = t;
- Data = d?.Trim();
- Origin = O;
- Target = T;
- Owner = S;
- OnProcessed = new ManualResetEventSlim();
- Time = DateTime.UtcNow;
- CurrentEventId++;
- Id = CurrentEventId;
- }
+ static long NextEventId;
+ static long GetNextEventId() => Interlocked.Increment(ref NextEventId);
public GameEvent()
{
- OnProcessed = new ManualResetEventSlim();
+ OnProcessed = new SemaphoreSlim(0);
+ OnProcessed.Release();
Time = DateTime.UtcNow;
- CurrentEventId++;
- Id = CurrentEventId;
+ Id = GetNextEventId();
}
- private static long CurrentEventId;
-
public EventType Type;
public string Data; // Data is usually the message sent by player
public string Message;
@@ -75,8 +63,8 @@ namespace SharedLibraryCore
public Server Owner;
public Boolean Remote = false;
public object Extra { get; set; }
- public ManualResetEventSlim OnProcessed { get; set; }
- public DateTime Time { get; private set; }
+ public SemaphoreSlim OnProcessed { get; set; }
+ public DateTime Time { get; set; }
public long Id { get; private set; }
///
diff --git a/SharedLibraryCore/Events/GameEventArgs.cs b/SharedLibraryCore/Events/GameEventArgs.cs
new file mode 100644
index 000000000..b3a795723
--- /dev/null
+++ b/SharedLibraryCore/Events/GameEventArgs.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace SharedLibraryCore.Events
+{
+ ///
+ /// represents the state of a game event for event processing
+ ///
+ public class GameEventArgs : System.ComponentModel.AsyncCompletedEventArgs
+ {
+
+ public GameEventArgs(Exception error, bool cancelled, GameEvent userState) : base(error, cancelled, userState)
+ {
+ Event = userState;
+ }
+
+ ///
+ /// Game event that occured on a server
+ ///
+ public GameEvent Event { get; }
+ }
+}
diff --git a/SharedLibraryCore/Interfaces/IEventApi.cs b/SharedLibraryCore/Interfaces/IEventApi.cs
deleted file mode 100644
index bd2128a0f..000000000
--- a/SharedLibraryCore/Interfaces/IEventApi.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using SharedLibraryCore.Dtos;
-using System.Collections.Generic;
-
-namespace SharedLibraryCore.Interfaces
-{
- public interface IEventApi
- {
- ///
- /// Processes event from server as event info
- ///
- /// Object state from Delegate method call
- /// Event to process
- void OnServerEvent(object sender, GameEvent E);
- ///
- /// Get list of recent events
- ///
- /// specify wether the request should clear all events after retrieving
- /// List of recent event
- IEnumerable GetEvents(bool shouldConsume);
- }
-}
diff --git a/SharedLibraryCore/Interfaces/IEventHandler.cs b/SharedLibraryCore/Interfaces/IEventHandler.cs
index c92c5afd7..3127c1eb8 100644
--- a/SharedLibraryCore/Interfaces/IEventHandler.cs
+++ b/SharedLibraryCore/Interfaces/IEventHandler.cs
@@ -13,6 +13,6 @@ namespace SharedLibraryCore.Interfaces
/// Add a game event event to the queue to be processed
///
/// Game event
- void AddEvent(GameEvent gameEvent);
+ bool AddEvent(GameEvent gameEvent);
}
}
diff --git a/SharedLibraryCore/Interfaces/IGameLogReader.cs b/SharedLibraryCore/Interfaces/IGameLogReader.cs
index 5e95f293e..56ad2d3ea 100644
--- a/SharedLibraryCore/Interfaces/IGameLogReader.cs
+++ b/SharedLibraryCore/Interfaces/IGameLogReader.cs
@@ -16,7 +16,7 @@ namespace SharedLibraryCore.Interfaces
///
///
///
- ICollection EventsFromLog(Server server, long fileSizeDiff, long startPosition);
+ ICollection ReadEventsFromLog(Server server, long fileSizeDiff, long startPosition);
///
/// how long the log file is
///
diff --git a/SharedLibraryCore/Interfaces/IManager.cs b/SharedLibraryCore/Interfaces/IManager.cs
index f14e0ff06..88198d143 100644
--- a/SharedLibraryCore/Interfaces/IManager.cs
+++ b/SharedLibraryCore/Interfaces/IManager.cs
@@ -23,7 +23,6 @@ namespace SharedLibraryCore.Interfaces
AliasService GetAliasService();
PenaltyService GetPenaltyService();
IDictionary GetPrivilegedClients();
- IEventApi GetEventApi();
///
/// Get the event handlers
///
diff --git a/SharedLibraryCore/RCon/Connection.cs b/SharedLibraryCore/RCon/Connection.cs
index 39779f730..2bb6e9980 100644
--- a/SharedLibraryCore/RCon/Connection.cs
+++ b/SharedLibraryCore/RCon/Connection.cs
@@ -150,7 +150,8 @@ namespace SharedLibraryCore.RCon
// will this really prevent flooding?
if ((DateTime.Now - LastQuery).TotalMilliseconds < 350)
{
- await Task.Delay(350);
+ //await Task.Delay(350);
+ Thread.Sleep(350);
}
LastQuery = DateTime.Now;
diff --git a/SharedLibraryCore/Server.cs b/SharedLibraryCore/Server.cs
index c7955ab5d..47818d125 100644
--- a/SharedLibraryCore/Server.cs
+++ b/SharedLibraryCore/Server.cs
@@ -118,25 +118,17 @@ namespace SharedLibraryCore
{
#if !DEBUG
string formattedMessage = String.Format(RconParser.GetCommandPrefixes().Say, Message);
-
- var e = new GameEvent()
- {
- Message = formattedMessage,
- Data = formattedMessage,
- Owner = this,
- Type = GameEvent.EventType.Broadcast,
- };
-
- Manager.GetEventHandler().AddEvent(e);
#else
Logger.WriteVerbose(Message.StripColors());
#endif
- Manager.GetEventHandler().AddEvent(new GameEvent()
+ var e = new GameEvent()
{
Type = GameEvent.EventType.Broadcast,
Data = Message,
Owner = this
- });
+ };
+
+ Manager.GetEventHandler().AddEvent(e);
await Task.CompletedTask;
}
diff --git a/WebfrontCore/Controllers/API/APIController.cs b/WebfrontCore/Controllers/API/APIController.cs
index 1f6990a0c..707329a2c 100644
--- a/WebfrontCore/Controllers/API/APIController.cs
+++ b/WebfrontCore/Controllers/API/APIController.cs
@@ -1,5 +1,6 @@
using Microsoft.AspNetCore.Mvc;
using SharedLibraryCore;
+using SharedLibraryCore.Events;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -14,7 +15,7 @@ namespace WebfrontCore.Controllers.API
[HttpGet]
public IActionResult Event(bool shouldConsume = true)
{
- var events = Manager.GetEventApi().GetEvents(shouldConsume);
+ var events = EventApi.GetEvents(shouldConsume);
return Json(events);
}
diff --git a/WebfrontCore/Controllers/ConsoleController.cs b/WebfrontCore/Controllers/ConsoleController.cs
index fb1187cec..b61ce1b7e 100644
--- a/WebfrontCore/Controllers/ConsoleController.cs
+++ b/WebfrontCore/Controllers/ConsoleController.cs
@@ -48,7 +48,7 @@ namespace WebfrontCore.Controllers
Manager.GetEventHandler().AddEvent(remoteEvent);
// wait for the event to process
- await Task.Run(() => remoteEvent.OnProcessed.Wait(5000));
+ await remoteEvent.OnProcessed.WaitAsync(60*1000);
var response = server.CommandResult.Where(c => c.ClientId == client.ClientId).ToList();
// remove the added command response