diff --git a/Application/Application.csproj b/Application/Application.csproj
index 7522fd4d0..ca9bec2b6 100644
--- a/Application/Application.csproj
+++ b/Application/Application.csproj
@@ -19,6 +19,7 @@
IW4MAdmin
Debug;Release;Prerelease
+ IW4MAdmin.Application
diff --git a/Application/GameEventHandler.cs b/Application/GameEventHandler.cs
new file mode 100644
index 000000000..a6f55b5b3
--- /dev/null
+++ b/Application/GameEventHandler.cs
@@ -0,0 +1,46 @@
+using SharedLibraryCore;
+using SharedLibraryCore.Interfaces;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace IW4MAdmin.Application
+{
+ class GameEventHandler : IEventHandler
+ {
+ private Queue EventQueue;
+ private IManager Manager;
+
+ public GameEventHandler(IManager mgr)
+ {
+ EventQueue = new Queue();
+ Manager = mgr;
+ }
+
+ public void AddEvent(GameEvent gameEvent)
+ {
+#if DEBUG
+ Manager.GetLogger().WriteDebug($"Got new event of type {gameEvent.Type} for {gameEvent.Owner}");
+#endif
+ EventQueue.Enqueue(gameEvent);
+#if DEBUG
+ Manager.GetLogger().WriteDebug($"There are now {EventQueue.Count} events in queue");
+#endif
+ Manager.SetHasEvent();
+ }
+
+ public string[] GetEventOutput()
+ {
+ throw new NotImplementedException();
+ }
+
+ public GameEvent GetNextEvent()
+ {
+#if DEBUG
+ Manager.GetLogger().WriteDebug("Getting next event to be processed");
+#endif
+
+ return EventQueue.Count > 0 ? EventQueue.Dequeue() : null;
+ }
+ }
+}
diff --git a/Application/IO/GameLogEvent.cs b/Application/IO/GameLogEvent.cs
new file mode 100644
index 000000000..56e96d408
--- /dev/null
+++ b/Application/IO/GameLogEvent.cs
@@ -0,0 +1,72 @@
+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
+{
+ class GameLogEvent
+ {
+ FileSystemWatcher LogPathWatcher;
+ Server Server;
+ long PreviousFileSize;
+ GameLogReader Reader;
+ Timer RefreshInfoTimer;
+ string GameLogFile;
+ FileInfo Info;
+
+ public GameLogEvent(Server server, string gameLogPath, string gameLogName)
+ {
+ GameLogFile = gameLogPath;
+ Reader = new GameLogReader(gameLogPath, server.EventParser);
+ Server = server;
+ RefreshInfoTimer = new Timer((sender) =>
+ {
+ var newInfo = new FileInfo(GameLogFile);
+ if (newInfo.Length - Info?.Length > 0)
+ LogPathWatcher_Changed(this, new FileSystemEventArgs(WatcherChangeTypes.Changed, "", ""));
+ Info = newInfo;
+
+ }, null, 0, 100);
+ LogPathWatcher = new FileSystemWatcher()
+ {
+ Path = gameLogPath.Replace(gameLogName, ""),
+ Filter = gameLogName,
+ NotifyFilter = (NotifyFilters)383,
+ InternalBufferSize = 4096
+ };
+
+ LogPathWatcher.Changed += LogPathWatcher_Changed;
+ LogPathWatcher.EnableRaisingEvents = true;
+ }
+
+ ~GameLogEvent()
+ {
+ LogPathWatcher.EnableRaisingEvents = false;
+ }
+
+ private void LogPathWatcher_Changed(object sender, FileSystemEventArgs e)
+ {
+ // retrieve the new file size
+ long newFileSize = new FileInfo(GameLogFile).Length;
+
+ if (PreviousFileSize == 0)
+ PreviousFileSize = newFileSize;
+
+ long fileDiff = newFileSize - PreviousFileSize;
+
+ if (fileDiff < 1)
+ return;
+
+ var events = Reader.EventsFromLog(Server, fileDiff);
+ foreach (var ev in events)
+ Server.Manager.GetEventHandler().AddEvent(ev);
+
+ PreviousFileSize = newFileSize;
+ }
+ }
+}
diff --git a/Application/IO/GameLogReader.cs b/Application/IO/GameLogReader.cs
new file mode 100644
index 000000000..f6e60fae4
--- /dev/null
+++ b/Application/IO/GameLogReader.cs
@@ -0,0 +1,49 @@
+using SharedLibraryCore;
+using SharedLibraryCore.Interfaces;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+
+namespace IW4MAdmin.Application.IO
+{
+ class GameLogReader
+ {
+ IEventParser Parser;
+ string LogFile;
+
+ public GameLogReader(string logFile, IEventParser parser)
+ {
+ LogFile = logFile;
+ Parser = parser;
+ }
+
+ public ICollection EventsFromLog(Server server, long fileSizeDiff)
+ {
+ // allocate the bytes for the new log lines
+ byte[] fileBytes = new byte[fileSizeDiff];
+
+ // open the file as a stream
+ using (var rd = new BinaryReader(new FileStream(LogFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), Utilities.EncodingType))
+ {
+ rd.BaseStream.Seek(rd.BaseStream.Length - fileSizeDiff - 1, SeekOrigin.Begin);
+ // the difference should be in the range of a int :P
+ rd.Read(fileBytes, 0, (int)fileSizeDiff);
+ }
+
+ // convert to event line list
+ string[] logLines = Utilities.EncodingType.GetString(fileBytes).Replace("\r", "").Split('\n');
+
+ List events = new List();
+
+ // parse each line
+ foreach (string eventLine in logLines)
+ {
+ if (eventLine.Length > 0)
+ events.Add(Parser.GetEvent(server, eventLine));
+ }
+
+ return events;
+ }
+ }
+}
diff --git a/Application/Logger.cs b/Application/Logger.cs
index 9f0f1c694..90a63f0f6 100644
--- a/Application/Logger.cs
+++ b/Application/Logger.cs
@@ -30,17 +30,8 @@ namespace IW4MAdmin.Application
void Write(string msg, LogType type)
{
- string stringType;
-
- try
- {
- stringType = Utilities.CurrentLocalization.LocalizationSet[$"GLOBAL_{type.ToString().ToUpper()}"];
- }
-
- catch(KeyNotFoundException)
- {
+ if (!Utilities.CurrentLocalization.LocalizationSet.TryGetValue($"GLOBAL_{type.ToString().ToUpper()}", out string stringType))
stringType = type.ToString();
- }
string LogLine = $"[{DateTime.Now.ToString("HH:mm:ss")}] - {stringType}: {msg}";
lock (ThreadLock)
diff --git a/Application/Main.cs b/Application/Main.cs
index 082506c6d..cf96bbbc3 100644
--- a/Application/Main.cs
+++ b/Application/Main.cs
@@ -24,6 +24,7 @@ namespace IW4MAdmin.Application
Localization.Configure.Initialize();
var loc = Utilities.CurrentLocalization.LocalizationSet;
Console.OutputEncoding = Encoding.UTF8;
+ Console.ForegroundColor = ConsoleColor.Gray;
Version = Assembly.GetExecutingAssembly().GetName().Version.Major + Assembly.GetExecutingAssembly().GetName().Version.Minor / 10.0f;
Version = Math.Round(Version, 2);
@@ -40,7 +41,7 @@ namespace IW4MAdmin.Application
ServerManager = ApplicationManager.GetInstance();
- using (var db = new DatabaseContext(ServerManager.GetApplicationSettings().Configuration().ConnectionString))
+ using (var db = new DatabaseContext(ServerManager.GetApplicationSettings().Configuration().ConnectionString))
new ContextSeed(db).Seed().Wait();
var api = API.Master.Endpoint.Get();
@@ -69,7 +70,7 @@ namespace IW4MAdmin.Application
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(loc["MANAGER_VERSION_FAIL"]);
- Console.ForegroundColor = ConsoleColor.White;
+ Console.ForegroundColor = ConsoleColor.Gray;
}
#if !PRERELEASE
@@ -78,7 +79,7 @@ namespace IW4MAdmin.Application
Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.WriteLine($"IW4MAdmin {loc["MANAGER_VERSION_UPDATE"]} [v{version.CurrentVersionStable.ToString("0.0")}]");
Console.WriteLine($"{loc["MANAGER_VERSION_CURRENT"]} [v{Version.ToString("0.0")}]");
- Console.ForegroundColor = ConsoleColor.White;
+ Console.ForegroundColor = ConsoleColor.Gray;
}
#else
else if (version.CurrentVersionPrerelease > Version)
@@ -86,14 +87,14 @@ namespace IW4MAdmin.Application
Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.WriteLine($"IW4MAdmin-Prerelease {loc["MANAGER_VERSION_UPDATE"]} [v{version.CurrentVersionPrerelease.ToString("0.0")}-pr]");
Console.WriteLine($"{loc["MANAGER_VERSION_CURRENT"]} [v{Version.ToString("0.0")}-pr]");
- Console.ForegroundColor = ConsoleColor.White;
+ Console.ForegroundColor = ConsoleColor.Gray;
}
#endif
else
{
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine(loc["MANAGER_VERSION_SUCCESS"]);
- Console.ForegroundColor = ConsoleColor.White;
+ Console.ForegroundColor = ConsoleColor.Gray;
}
ServerManager.Init().Wait();
@@ -112,13 +113,13 @@ namespace IW4MAdmin.Application
if (ServerManager.Servers.Count == 0)
{
- Console.WriteLine("No servers are currently being monitored");
+ Console.WriteLine(loc["MANAGER_CONSOLE_NOSERV"]);
continue;
}
Origin.CurrentServer = ServerManager.Servers[0];
GameEvent E = new GameEvent(GameEvent.EventType.Say, userInput, Origin, null, ServerManager.Servers[0]);
- ServerManager.Servers[0].ExecuteEvent(E);
+ ServerManager.GetEventHandler().AddEvent(E);
Console.Write('>');
} while (ServerManager.Running);
@@ -128,10 +129,6 @@ namespace IW4MAdmin.Application
{
Task.Run(() => WebfrontCore.Program.Init(ServerManager));
}
-
- ServerManager.Start();
- ServerManager.Logger.WriteVerbose(loc["MANAGER_SHUTDOWN_SUCCESS"]);
-
}
catch (Exception e)
@@ -145,6 +142,10 @@ namespace IW4MAdmin.Application
Console.WriteLine(loc["MANAGER_EXIT"]);
Console.ReadKey();
}
+
+
+ ServerManager.Start();
+ ServerManager.Logger.WriteVerbose(loc["MANAGER_SHUTDOWN_SUCCESS"]);
}
static void CheckDirectories()
diff --git a/Application/Manager.cs b/Application/Manager.cs
index 4be917e9b..9561cafe7 100644
--- a/Application/Manager.cs
+++ b/Application/Manager.cs
@@ -41,10 +41,13 @@ namespace IW4MAdmin.Application
PenaltyService PenaltySvc;
BaseConfigurationHandler ConfigHandler;
EventApi Api;
+ GameEventHandler Handler;
+ ManualResetEventSlim OnEvent;
+ Timer StatusUpdateTimer;
#if FTP_LOG
const int UPDATE_FREQUENCY = 700;
#else
- const int UPDATE_FREQUENCY = 450;
+ const int UPDATE_FREQUENCY = 2000;
#endif
private ApplicationManager()
@@ -63,6 +66,7 @@ namespace IW4MAdmin.Application
ConfigHandler = new BaseConfigurationHandler("IW4MAdminSettings");
Console.CancelKeyPress += new ConsoleCancelEventHandler(OnCancelKey);
StartTime = DateTime.UtcNow;
+ OnEvent = new ManualResetEventSlim();
}
private void OnCancelKey(object sender, ConsoleCancelEventArgs args)
@@ -86,8 +90,18 @@ namespace IW4MAdmin.Application
return Instance ?? (Instance = new ApplicationManager());
}
+ public void UpdateStatus(object state)
+ {
+ foreach (var server in Servers)
+ {
+ Task.Run(() => server.ProcessUpdatesAsync(new CancellationToken()));
+ }
+ }
+
public async Task Init()
{
+ // setup the event handler after the class is initialized
+ Handler = new GameEventHandler(this);
#region DATABASE
var ipList = (await ClientSvc.Find(c => c.Level > Player.Permission.Trusted))
.Select(c => new
@@ -248,13 +262,8 @@ namespace IW4MAdmin.Application
}
Logger.WriteVerbose($"{Utilities.CurrentLocalization.LocalizationSet["MANAGER_MONITORING_TEXT"]} {ServerInstance.Hostname}");
-
- // this way we can keep track of execution time and see if problems arise.
- var Status = new AsyncStatus(ServerInstance, UPDATE_FREQUENCY);
- lock (TaskStatuses)
- {
- TaskStatuses.Add(Status);
- }
+ // add the start event for this server
+ Handler.AddEvent(new GameEvent(GameEvent.EventType.Start, "Server started", null, null, ServerInstance));
}
catch (ServerException e)
@@ -273,6 +282,8 @@ namespace IW4MAdmin.Application
}
await Task.WhenAll(config.Servers.Select(c => Init(c)).ToArray());
+ // start polling servers
+ StatusUpdateTimer = new Timer(UpdateStatus, null, 0, 10000);
#endregion
@@ -344,38 +355,32 @@ namespace IW4MAdmin.Application
public void Start()
{
Task.Run(() => HeartBeatThread());
- while (Running || TaskStatuses.Count > 0)
+ GameEvent newEvent;
+ while (Running)
{
- for (int i = 0; i < TaskStatuses.Count; i++)
+ // wait for new event to be added
+ OnEvent.Wait();
+
+ // todo: sequencially or parallelize?
+ while ((newEvent = Handler.GetNextEvent()) != null)
{
- var Status = TaskStatuses[i];
-
- // task is read to be rerun
- if (Status.RequestedTask == null || Status.RequestedTask.Status == TaskStatus.RanToCompletion)
+ try
{
- // remove the task when we want to quit and last run has finished
- if (!Running)
- {
- TaskStatuses.RemoveAt(i);
- continue;
- }
- // normal operation
- else
- {
- Status.Update(new Task(() => { return (Status.Dependant as Server).ProcessUpdatesAsync(Status.GetToken()).Result; }));
- if (Status.RunAverage > 1000 + UPDATE_FREQUENCY && !(Status.Dependant as Server).Throttled)
- Logger.WriteWarning($"Update task average execution is longer than desired for {(Status.Dependant as Server)} [{Status.RunAverage}ms]");
- }
+ Task.WaitAll(newEvent.Owner.ExecuteEvent(newEvent));
}
- if (Status.RequestedTask.Status == TaskStatus.Faulted)
+ catch (Exception E)
{
- Logger.WriteWarning($"Update task for {(Status.Dependant as Server)} faulted, restarting");
- Status.Abort();
+ Logger.WriteError($"{Utilities.CurrentLocalization.LocalizationSet["SERVER_ERROR_EXCEPTION"]} {newEvent.Owner}");
+ Logger.WriteDebug("Error Message: " + E.Message);
+ Logger.WriteDebug("Error Trace: " + E.StackTrace);
}
+ // tell anyone waiting for the output that we're done
+ newEvent.OnProcessed.Set();
}
- Thread.Sleep(UPDATE_FREQUENCY);
+ // signal that all events have been processed
+ OnEvent.Reset();
}
#if !DEBUG
foreach (var S in Servers)
@@ -388,6 +393,9 @@ namespace IW4MAdmin.Application
public void Stop()
{
Running = false;
+
+ // trigger the event processing loop to end
+ SetHasEvent();
}
public ILogger GetLogger()
@@ -417,5 +425,11 @@ namespace IW4MAdmin.Application
public IDictionary GetPrivilegedClients() => PrivilegedClients;
public IEventApi GetEventApi() => Api;
public bool ShutdownRequested() => !Running;
+ public IEventHandler GetEventHandler() => Handler;
+
+ public void SetHasEvent()
+ {
+ OnEvent.Set();
+ }
}
}
diff --git a/Application/RconParsers/IW4RConParser.cs b/Application/RconParsers/IW4RConParser.cs
index ca9caf0af..6cb5126d2 100644
--- a/Application/RconParsers/IW4RConParser.cs
+++ b/Application/RconParsers/IW4RConParser.cs
@@ -85,6 +85,8 @@ namespace Application.RconParsers
if (Regex.Matches(responseLine, @" *^\d+", RegexOptions.IgnoreCase).Count > 0)
{
String[] playerInfo = responseLine.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
+ if (playerInfo.Length < 4)
+ continue;
int cID = -1;
int Ping = -1;
Int32.TryParse(playerInfo[2], out Ping);
diff --git a/Application/Server.cs b/Application/Server.cs
index 2e1b4fe87..0c9bdc6ed 100644
--- a/Application/Server.cs
+++ b/Application/Server.cs
@@ -18,6 +18,7 @@ using SharedLibraryCore.Exceptions;
using Application.Misc;
using Application.RconParsers;
using IW4MAdmin.Application.EventParsers;
+using IW4MAdmin.Application.IO;
namespace IW4MAdmin
{
@@ -25,6 +26,8 @@ namespace IW4MAdmin
{
private CancellationToken cts;
private static Dictionary loc = Utilities.CurrentLocalization.LocalizationSet;
+ private GameLogEvent LogEvent;
+
public IW4MServer(IManager mgr, ServerConfiguration cfg) : base(mgr, cfg) { }
@@ -180,7 +183,8 @@ namespace IW4MAdmin
Logger.WriteInfo($"Client {player} connecting...");
- await ExecuteEvent(new GameEvent(GameEvent.EventType.Connect, "", player, null, this));
+
+ Manager.GetEventHandler().AddEvent(new GameEvent(GameEvent.EventType.Connect, "", player, null, this));
if (!Manager.GetApplicationSettings().Configuration().EnableClientVPNs &&
@@ -208,7 +212,7 @@ namespace IW4MAdmin
Player Leaving = Players[cNum];
Logger.WriteInfo($"Client {Leaving} disconnecting...");
- await ExecuteEvent(new GameEvent(GameEvent.EventType.Disconnect, "", Leaving, null, this));
+ Manager.GetEventHandler().AddEvent(new GameEvent(GameEvent.EventType.Disconnect, "", Leaving, null, this));
Leaving.TotalConnectionTime += (int)(DateTime.UtcNow - Leaving.ConnectionTime).TotalSeconds;
Leaving.LastConnection = DateTime.UtcNow;
@@ -255,16 +259,8 @@ namespace IW4MAdmin
if (C.RequiresTarget || Args.Length > 0)
{
- int cNum = -1;
- try
- {
- cNum = Convert.ToInt32(Args[0]);
- }
-
- catch (FormatException)
- {
-
- }
+ if (!Int32.TryParse(Args[0], out int cNum))
+ cNum = -1;
if (Args[0][0] == '@') // user specifying target by database ID
{
@@ -359,24 +355,27 @@ namespace IW4MAdmin
public override async Task ExecuteEvent(GameEvent E)
{
- //if (Throttled)
- // return;
-
+ bool canExecuteCommand = true;
await ProcessEvent(E);
Manager.GetEventApi().OnServerEvent(this, E);
foreach (IPlugin P in SharedLibraryCore.Plugins.PluginImporter.ActivePlugins)
{
-#if !DEBUG
try
-#endif
{
if (cts.IsCancellationRequested)
break;
await P.OnEventAsync(E, this);
}
-#if !DEBUG
+
+ // this happens if a plugin (login) wants to stop commands from executing
+ catch (AuthorizationException e)
+ {
+ await E.Origin.Tell($"{loc["COMMAND_NOTAUTHORIZED"]} - {e.Message}");
+ canExecuteCommand = false;
+ }
+
catch (Exception Except)
{
Logger.WriteError(String.Format("The plugin \"{0}\" generated an error. ( see log )", P.Name));
@@ -389,10 +388,168 @@ namespace IW4MAdmin
}
continue;
}
-#endif
+
}
+
+ // hack: this prevents commands from getting executing that 'shouldn't' be
+ if (E.Type == GameEvent.EventType.Command &&
+ E.Extra != null &&
+ (canExecuteCommand ||
+ E.Origin?.Level == Player.Permission.Console))
+ {
+ await (((Command)E.Extra).ExecuteAsync(E));
+ }
+
}
+ ///
+ /// Perform the server specific tasks when an event occurs
+ ///
+ ///
+ ///
+ override protected async Task ProcessEvent(GameEvent E)
+ {
+ if (E.Type == GameEvent.EventType.Connect)
+ {
+ // special case for IW5 when connect is from the log
+ if (E.Extra != null)
+ {
+ var logClient = (Player)E.Extra;
+ var client = (await this.GetStatusAsync())
+ .Single(c => c.ClientNumber == logClient.ClientNumber &&
+ c.Name == logClient.Name);
+ client.NetworkId = logClient.NetworkId;
+
+ await AddPlayer(client);
+
+ // hack: to prevent plugins from registering it as a real connect
+ E.Type = GameEvent.EventType.Unknown;
+ }
+
+ else
+ {
+ ChatHistory.Add(new ChatInfo()
+ {
+ Name = E.Origin.Name,
+ Message = "CONNECTED",
+ Time = DateTime.UtcNow
+ });
+
+ if (E.Origin.Level > Player.Permission.Moderator)
+ await E.Origin.Tell(string.Format(loc["SERVER_REPORT_COUNT"], E.Owner.Reports.Count));
+ }
+ }
+
+ else if (E.Type == GameEvent.EventType.Disconnect)
+ {
+ ChatHistory.Add(new ChatInfo()
+ {
+ Name = E.Origin.Name,
+ Message = "DISCONNECTED",
+ Time = DateTime.UtcNow
+ });
+ }
+
+ else if (E.Type == GameEvent.EventType.Script)
+ {
+ Manager.GetEventHandler().AddEvent(new GameEvent(GameEvent.EventType.Kill, E.Data, E.Origin, E.Target, this));
+ }
+
+ 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!");
+ }
+
+ Manager.GetEventHandler().AddEvent(new GameEvent()
+ {
+ Type = GameEvent.EventType.Command,
+ Data = E.Data,
+ Origin = E.Origin,
+ Target = E.Target,
+ Owner = this,
+ Extra = C,
+ Remote = E.Remote,
+ Message = E.Message
+ });
+ }
+ }
+
+ else // Not a command
+ {
+ E.Data = E.Data.StripColors();
+
+ ChatHistory.Add(new ChatInfo()
+ {
+ Name = E.Origin.Name,
+ Message = E.Data,
+ Time = DateTime.UtcNow
+ });
+ }
+ }
+
+ if (E.Type == GameEvent.EventType.MapChange)
+ {
+ Logger.WriteInfo($"New map loaded - {ClientNum} active players");
+
+ // iw4 doesn't log the game info
+ if (E.Extra == null)
+ {
+ var dict = await this.GetInfoAsync();
+
+ Gametype = dict["gametype"].StripColors();
+ Hostname = dict["hostname"].StripColors();
+
+ string mapname = dict["mapname"].StripColors();
+ CurrentMap = Maps.Find(m => m.Name == mapname) ?? new Map() { Alias = mapname, Name = mapname };
+ }
+
+ else
+ {
+ var dict = (Dictionary)E.Extra;
+ Gametype = dict["g_gametype"].StripColors();
+ Hostname = dict["sv_hostname"].StripColors();
+
+ string mapname = dict["mapname"].StripColors();
+ CurrentMap = Maps.Find(m => m.Name == mapname) ?? new Map() { Alias = mapname, Name = mapname };
+ }
+ }
+
+ if (E.Type == GameEvent.EventType.MapEnd)
+ {
+ Logger.WriteInfo("Game ending...");
+ }
+
+ //todo: move
+ while (ChatHistory.Count > Math.Ceiling((double)ClientNum / 2))
+ ChatHistory.RemoveAt(0);
+
+ // the last client hasn't fully disconnected yet
+ // so there will still be at least 1 client left
+ if (ClientNum < 2)
+ ChatHistory.Clear();
+ }
+
+
async Task PollPlayersAsync()
{
var now = DateTime.Now;
@@ -429,30 +586,16 @@ namespace IW4MAdmin
return CurrentPlayers.Count;
}
- long l_size = -1;
- String[] lines = new String[8];
- String[] oldLines = new String[8];
DateTime start = DateTime.Now;
DateTime playerCountStart = DateTime.Now;
DateTime lastCount = DateTime.Now;
DateTime tickTime = DateTime.Now;
- bool firstRun = true;
- int count = 0;
override public async Task ProcessUpdatesAsync(CancellationToken cts)
{
this.cts = cts;
- //#if DEBUG == false
try
- //#endif
{
- // first start
- if (firstRun)
- {
- await ExecuteEvent(new GameEvent(GameEvent.EventType.Start, "Server started", null, null, this));
- firstRun = false;
- }
-
if ((DateTime.Now - LastPoll).TotalMinutes < 2 && ConnectionErrors >= 1)
return true;
@@ -517,44 +660,6 @@ namespace IW4MAdmin
start = DateTime.Now;
}
- if (LogFile == null)
- return true;
-
- if (l_size != LogFile.Length())
- {
- lines = l_size != -1 ? await LogFile.Tail(12) : lines;
- if (lines != oldLines)
- {
- l_size = LogFile.Length();
- int end = (lines.Length == oldLines.Length) ? lines.Length - 1 : Math.Abs((lines.Length - oldLines.Length)) - 1;
-
- for (count = 0; count < lines.Length; count++)
- {
- if (lines.Length < 1 && oldLines.Length < 1)
- continue;
-
- if (lines[count] == oldLines[oldLines.Length - 1])
- continue;
-
- if (lines[count].Length < 10) // it's not a needed line
- continue;
-
- else
- {
- GameEvent event_ = EventParser.GetEvent(this, lines[count]);
- if (event_ != null)
- {
- if (event_.Origin == null)
- continue;
-
- await ExecuteEvent(event_);
- }
- }
- }
- }
- }
- oldLines = lines;
- l_size = LogFile.Length();
if (Manager.ShutdownRequested())
{
foreach (var plugin in SharedLibraryCore.Plugins.PluginImporter.ActivePlugins)
@@ -565,7 +670,6 @@ namespace IW4MAdmin
}
return true;
}
- //#if !DEBUG
catch (NetworkException)
{
Logger.WriteError($"{loc["SERVER_ERROR_COMMUNICATION"]} {IP}:{Port}");
@@ -575,7 +679,6 @@ namespace IW4MAdmin
catch (InvalidOperationException)
{
Logger.WriteWarning("Event could not parsed properly");
- Logger.WriteDebug($"Log Line: {lines[count]}");
return false;
}
@@ -586,7 +689,6 @@ namespace IW4MAdmin
Logger.WriteDebug("Error Trace: " + E.StackTrace);
return false;
}
- //#endif
}
public async Task Initialize()
@@ -657,7 +759,7 @@ namespace IW4MAdmin
CustomCallback = await ScriptLoaded();
string mainPath = EventParser.GetGameDir();
#if DEBUG
- basepath.Value = @"\\192.168.88.253\Call of Duty 4";
+ basepath.Value = @"\\192.168.88.253\mw2\";
#endif
string logPath;
if (GameName == Game.IW5)
@@ -671,6 +773,7 @@ namespace IW4MAdmin
$"{basepath.Value.Replace('\\', Path.DirectorySeparatorChar)}{Path.DirectorySeparatorChar}{game.Value.Replace('/', Path.DirectorySeparatorChar)}{Path.DirectorySeparatorChar}{logfile.Value}";
}
+
// hopefully fix wine drive name mangling
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
@@ -686,183 +789,18 @@ namespace IW4MAdmin
}
else
{
- LogFile = new IFile(logPath);
- }
+ // LogFile = new IFile(logPath);
+ }
+ LogEvent = new GameLogEvent(this, logPath, logfile.Value);
Logger.WriteInfo($"Log file is {logPath}");
#if DEBUG
- // LogFile = new RemoteFile("https://raidmax.org/IW4MAdmin/getlog.php");
+ // LogFile = new RemoteFile("https://raidmax.org/IW4MAdmin/getlog.php");
#else
await Broadcast(loc["BROADCAST_ONLINE"]);
#endif
}
- //Process any server event
- override protected async Task ProcessEvent(GameEvent E)
- {
- if (E.Type == GameEvent.EventType.Connect)
- {
- // special case for IW5 when connect is from the log
- if (E.Extra != null)
- {
- var logClient = (Player)E.Extra;
- var client = (await this.GetStatusAsync())
- .Single(c => c.ClientNumber == logClient.ClientNumber &&
- c.Name == logClient.Name);
- client.NetworkId = logClient.NetworkId;
-
- await AddPlayer(client);
-
- // hack: to prevent plugins from registering it as a real connect
- E.Type = GameEvent.EventType.Unknown;
- }
-
- else
- {
- ChatHistory.Add(new ChatInfo()
- {
- Name = E.Origin.Name,
- Message = "CONNECTED",
- Time = DateTime.UtcNow
- });
-
- if (E.Origin.Level > Player.Permission.Moderator)
- await E.Origin.Tell(string.Format(loc["SERVER_REPORT_COUNT"], E.Owner.Reports.Count));
- }
- }
-
- else if (E.Type == GameEvent.EventType.Disconnect)
- {
- ChatHistory.Add(new ChatInfo()
- {
- Name = E.Origin.Name,
- Message = "DISCONNECTED",
- Time = DateTime.UtcNow
- });
- }
-
- else if (E.Type == GameEvent.EventType.Script)
- {
- await ExecuteEvent(new GameEvent(GameEvent.EventType.Kill, E.Data, E.Origin, E.Target, this));
- }
-
- 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!");
- }
-
- try
- {
- if (!E.Remote && E.Origin.Level != Player.Permission.Console)
- {
- await ExecuteEvent(new GameEvent()
- {
- Type = GameEvent.EventType.Command,
- Data = string.Empty,
- Origin = E.Origin,
- Target = E.Target,
- Owner = this,
- Extra = C,
- Remote = E.Remote
- });
- }
-
- await C.ExecuteAsync(E);
- }
-
- catch (AuthorizationException e)
- {
- await E.Origin.Tell($"{loc["COMMAND_NOTAUTHORIZED"]} - {e.Message}");
- }
-
- catch (Exception Except)
- {
- Logger.WriteError(String.Format($"\"{0}\" {loc["SERVER_ERROR_COMMAND_LOG"]}", C.Name));
- Logger.WriteDebug(String.Format("Error Message: {0}", Except.Message));
- Logger.WriteDebug(String.Format("Error Trace: {0}", Except.StackTrace));
- await E.Origin.Tell($"^1{loc["SERVER_ERROR_COMMAND_INGAME"]}");
-#if DEBUG
- await E.Origin.Tell(Except.Message);
-#endif
- }
- }
- }
-
- else // Not a command
- {
- E.Data = E.Data.StripColors();
-
- ChatHistory.Add(new ChatInfo()
- {
- Name = E.Origin.Name,
- Message = E.Data,
- Time = DateTime.UtcNow
- });
- }
- }
-
- if (E.Type == GameEvent.EventType.MapChange)
- {
- Logger.WriteInfo($"New map loaded - {ClientNum} active players");
-
- // iw4 doesn't log the game info
- if (E.Extra == null)
- {
- var dict = await this.GetInfoAsync();
-
- Gametype = dict["gametype"].StripColors();
- Hostname = dict["hostname"].StripColors();
-
- string mapname = dict["mapname"].StripColors();
- CurrentMap = Maps.Find(m => m.Name == mapname) ?? new Map() { Alias = mapname, Name = mapname };
- }
-
- else
-
- {
- var dict = (Dictionary)E.Extra;
- Gametype = dict["g_gametype"].StripColors();
- Hostname = dict["sv_hostname"].StripColors();
-
- string mapname = dict["mapname"].StripColors();
- CurrentMap = Maps.Find(m => m.Name == mapname) ?? new Map() { Alias = mapname, Name = mapname };
- }
-
- }
-
- if (E.Type == GameEvent.EventType.MapEnd)
- {
- Logger.WriteInfo("Game ending...");
- }
-
- //todo: move
- while (ChatHistory.Count > Math.Ceiling((double)ClientNum / 2))
- ChatHistory.RemoveAt(0);
-
- // the last client hasn't fully disconnected yet
- // so there will still be at least 1 client left
- if (ClientNum < 2)
- ChatHistory.Clear();
- }
-
public override async Task Warn(String Reason, Player Target, Player Origin)
{
// ensure player gets warned if command not performed on them in game
diff --git a/Plugins/Login/Plugin.cs b/Plugins/Login/Plugin.cs
index 70c9d4eae..2e276af48 100644
--- a/Plugins/Login/Plugin.cs
+++ b/Plugins/Login/Plugin.cs
@@ -70,12 +70,8 @@ namespace IW4MAdmin.Plugins.Login
Config = cfg.Configuration();
}
- public Task OnTickAsync(Server S) => Utilities.CompletedTask;
+ public Task OnTickAsync(Server S) => Task.CompletedTask;
- public Task OnUnloadAsync()
- {
- AuthorizedClients.Clear();
- return Utilities.CompletedTask;
- }
+ public Task OnUnloadAsync() => Task.CompletedTask;
}
}
diff --git a/Plugins/ProfanityDeterment/Plugin.cs b/Plugins/ProfanityDeterment/Plugin.cs
index 68ac19abd..03fbccae1 100644
--- a/Plugins/ProfanityDeterment/Plugin.cs
+++ b/Plugins/ProfanityDeterment/Plugin.cs
@@ -20,7 +20,6 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment
BaseConfigurationHandler Settings;
ConcurrentDictionary ProfanityCounts;
IManager Manager;
- Task CompletedTask = Task.FromResult(false);
public async Task OnEventAsync(GameEvent E, Server S)
{
@@ -87,8 +86,8 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment
Manager = manager;
}
- public Task OnTickAsync(Server S) => CompletedTask;
+ public Task OnTickAsync(Server S) => Task.CompletedTask;
- public Task OnUnloadAsync() => CompletedTask;
+ public Task OnUnloadAsync() => Task.CompletedTask;
}
}
diff --git a/Plugins/Stats/Helpers/StatManager.cs b/Plugins/Stats/Helpers/StatManager.cs
index ccf17bf92..221d1dd12 100644
--- a/Plugins/Stats/Helpers/StatManager.cs
+++ b/Plugins/Stats/Helpers/StatManager.cs
@@ -187,7 +187,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
// get individual client's stats
var clientStats = playerStats[pl.ClientId];
// sync their score
- clientStats.SessionScore = pl.Score;
+ clientStats.SessionScore += (pl.Score - clientStats.LastScore);
// remove the client from the stats dictionary as they're leaving
playerStats.TryRemove(pl.ClientId, out EFClientStatistics removedValue);
detectionStats.TryRemove(pl.ClientId, out Cheat.Detection removedValue2);
@@ -213,11 +213,13 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
var statsSvc = ContextThreads[serverId];
Vector3 vDeathOrigin = null;
Vector3 vKillOrigin = null;
+ Vector3 vViewAngles = null;
try
{
vDeathOrigin = Vector3.Parse(deathOrigin);
vKillOrigin = Vector3.Parse(killOrigin);
+ vViewAngles = Vector3.Parse(viewAngles).FixIW4Angles();
}
catch (FormatException)
@@ -241,7 +243,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
Damage = Int32.Parse(damage),
HitLoc = ParseEnum.Get(hitLoc, typeof(IW4Info.HitLocation)),
Weapon = ParseEnum.Get(weapon, typeof(IW4Info.WeaponName)),
- ViewAngles = Vector3.Parse(viewAngles).FixIW4Angles(),
+ ViewAngles = vViewAngles,
TimeOffset = Int64.Parse(offset),
When = DateTime.UtcNow,
IsKillstreakKill = isKillstreakKill[0] != '0',
@@ -338,11 +340,14 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
// update the total stats
Servers[serverId].ServerStatistics.TotalKills += 1;
- attackerStats.SessionScore = attacker.Score;
- victimStats.SessionScore = victim.Score;
+ attackerStats.SessionScore += (attacker.Score - attackerStats.LastScore);
+ victimStats.SessionScore += (victim.Score - victimStats.LastScore);
// calculate for the clients
CalculateKill(attackerStats, victimStats);
+ // this should fix the negative SPM
+ attackerStats.LastScore = attacker.Score;
+ victimStats.LastScore = victim.Score;
// show encouragement/discouragement
string streakMessage = (attackerStats.ClientId != victimStats.ClientId) ?
@@ -457,7 +462,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
}
clientStats.LastStatCalculation = DateTime.UtcNow;
- clientStats.LastScore = clientStats.SessionScore;
+ //clientStats.LastScore = clientStats.SessionScore;
return clientStats;
}
diff --git a/Plugins/Stats/Plugin.cs b/Plugins/Stats/Plugin.cs
index 32d14be39..50c493919 100644
--- a/Plugins/Stats/Plugin.cs
+++ b/Plugins/Stats/Plugin.cs
@@ -253,7 +253,7 @@ namespace IW4MAdmin.Plugins.Stats
Manager = new StatManager(manager);
}
- public Task OnTickAsync(Server S) => Utilities.CompletedTask;
+ public Task OnTickAsync(Server S) => Task.CompletedTask;
public async Task OnUnloadAsync()
{
diff --git a/Plugins/Tests/Plugin.cs b/Plugins/Tests/Plugin.cs
index a6210dd50..76b194c56 100644
--- a/Plugins/Tests/Plugin.cs
+++ b/Plugins/Tests/Plugin.cs
@@ -51,8 +51,9 @@ namespace IW4MAdmin.Plugins
public Task OnLoadAsync(IManager manager) => Task.CompletedTask;
- public async Task OnTickAsync(Server S)
+ public Task OnTickAsync(Server S)
{
+ return Task.CompletedTask;
/*
if ((DateTime.Now - Interval).TotalSeconds > 1)
{
diff --git a/Plugins/Welcome/Plugin.cs b/Plugins/Welcome/Plugin.cs
index 4197e19f9..449714f2b 100644
--- a/Plugins/Welcome/Plugin.cs
+++ b/Plugins/Welcome/Plugin.cs
@@ -72,9 +72,9 @@ namespace IW4MAdmin.Plugins.Welcome
}
}
- public Task OnUnloadAsync() => Utilities.CompletedTask;
+ public Task OnUnloadAsync() => Task.CompletedTask;
- public Task OnTickAsync(Server S) => Utilities.CompletedTask;
+ public Task OnTickAsync(Server S) => Task.CompletedTask;
public async Task OnEventAsync(GameEvent E, Server S)
{
diff --git a/SharedLibraryCore/Commands/NativeCommands.cs b/SharedLibraryCore/Commands/NativeCommands.cs
index dd1accd45..302e98591 100644
--- a/SharedLibraryCore/Commands/NativeCommands.cs
+++ b/SharedLibraryCore/Commands/NativeCommands.cs
@@ -32,7 +32,7 @@ namespace SharedLibraryCore.Commands
public override async Task ExecuteAsync(GameEvent E)
{
- if ((await (E.Owner.Manager.GetClientService() as Services.ClientService).GetOwners()).Count == 0)
+ if ((await (E.Owner.Manager.GetClientService() as ClientService).GetOwners()).Count == 0)
{
E.Origin.Level = Player.Permission.Owner;
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationSet["COMMANDS_OWNER_SUCCESS"]);
@@ -94,7 +94,7 @@ namespace SharedLibraryCore.Commands
public class CKick : Command
{
public CKick() :
- base("kick", Utilities.CurrentLocalization.LocalizationSet["COMMANDS_KICK_DESC"], "k", Player.Permission.Trusted, true, new CommandArgument[]
+ base("kick", Utilities.CurrentLocalization.LocalizationSet["COMMANDS_KICK_DESC"], "k", Player.Permission.Moderator, true, new CommandArgument[]
{
new CommandArgument()
{
@@ -113,7 +113,7 @@ namespace SharedLibraryCore.Commands
{
if (E.Origin.Level > E.Target.Level)
{
- await E.Owner.ExecuteEvent(new GameEvent(GameEvent.EventType.Kick, E.Data, E.Origin, E.Target, E.Owner));
+ E.Owner.Manager.GetEventHandler().AddEvent(new GameEvent(GameEvent.EventType.Kick, E.Data, E.Origin, E.Target, E.Owner));
await E.Target.Kick(E.Data, E.Origin);
await E.Origin.Tell($"^5{E.Target} ^7{Utilities.CurrentLocalization.LocalizationSet["COMMANDS_KICK_SUCCESS"]}");
}
@@ -144,7 +144,7 @@ namespace SharedLibraryCore.Commands
public class CTempBan : Command
{
public CTempBan() :
- base("tempban", Utilities.CurrentLocalization.LocalizationSet["COMMANDS_TEMPBAN_DESC"], "tb", Player.Permission.Moderator, true, new CommandArgument[]
+ base("tempban", Utilities.CurrentLocalization.LocalizationSet["COMMANDS_TEMPBAN_DESC"], "tb", Player.Permission.Administrator, true, new CommandArgument[]
{
new CommandArgument()
{
@@ -716,7 +716,7 @@ namespace SharedLibraryCore.Commands
};
await E.Owner.Manager.GetPenaltyService().Create(newPenalty);
- await E.Owner.ExecuteEvent(new GameEvent(GameEvent.EventType.Flag, E.Data, E.Origin, E.Target, E.Owner));
+ E.Owner.Manager.GetEventHandler().AddEvent(new GameEvent(GameEvent.EventType.Flag, E.Data, E.Origin, E.Target, E.Owner));
await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationSet["COMMANDS_FLAG_SUCCESS"]} ^5{E.Target.Name}");
}
@@ -770,7 +770,7 @@ namespace SharedLibraryCore.Commands
E.Owner.Reports.Add(new Report(E.Target, E.Origin, E.Data));
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationSet["COMMANDS_REPORT_SUCCESS"]);
- await E.Owner.ExecuteEvent(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));
}
}
@@ -1023,7 +1023,8 @@ namespace SharedLibraryCore.Commands
E.Origin.PasswordSalt = hashedPassword[1];
// update the password for the client in privileged
- E.Owner.Manager.GetPrivilegedClients()[E.Origin.ClientId] = E.Origin;
+ E.Owner.Manager.GetPrivilegedClients()[E.Origin.ClientId].Password = hashedPassword[0];
+ E.Owner.Manager.GetPrivilegedClients()[E.Origin.ClientId].PasswordSalt = hashedPassword[1];
await E.Owner.Manager.GetClientService().Update(E.Origin);
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationSet["COMMANDS_PASSWORD_SUCCESS"]);
diff --git a/SharedLibraryCore/Event.cs b/SharedLibraryCore/Event.cs
index 16f5983fa..02d82034d 100644
--- a/SharedLibraryCore/Event.cs
+++ b/SharedLibraryCore/Event.cs
@@ -2,7 +2,7 @@
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
-
+using System.Threading;
using SharedLibraryCore.Objects;
namespace SharedLibraryCore
@@ -47,10 +47,13 @@ namespace SharedLibraryCore
Origin = O;
Target = T;
Owner = S;
+ OnProcessed = new ManualResetEventSlim();
}
- public GameEvent() { }
-
+ public GameEvent()
+ {
+ OnProcessed = new ManualResetEventSlim();
+ }
public EventType Type;
public string Data; // Data is usually the message sent by player
@@ -60,5 +63,6 @@ namespace SharedLibraryCore
public Server Owner;
public Boolean Remote = false;
public object Extra { get; set; }
+ public ManualResetEventSlim OnProcessed { get; private set; }
}
}
diff --git a/SharedLibraryCore/File.cs b/SharedLibraryCore/File.cs
index 31ffcb2a6..e555ace5d 100644
--- a/SharedLibraryCore/File.cs
+++ b/SharedLibraryCore/File.cs
@@ -5,6 +5,7 @@ using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
+using System.Linq;
namespace SharedLibraryCore
{
@@ -27,7 +28,12 @@ namespace SharedLibraryCore
public override long Length()
{
Retrieve();
- return FileCache[0].Length;
+ return FileCache.Sum(l => l.Length);
+ }
+
+ public override Task Tail(int lineCount)
+ {
+ return Task.FromResult(FileCache);
}
}
diff --git a/SharedLibraryCore/Interfaces/IEventHandler.cs b/SharedLibraryCore/Interfaces/IEventHandler.cs
new file mode 100644
index 000000000..7fe2efc5c
--- /dev/null
+++ b/SharedLibraryCore/Interfaces/IEventHandler.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace SharedLibraryCore.Interfaces
+{
+ ///
+ /// This class handle games events (from log, manual events, etc)
+ ///
+ public interface IEventHandler
+ {
+ ///
+ /// Add a game event event to the queue to be processed
+ ///
+ /// Game event
+ void AddEvent(GameEvent gameEvent);
+ ///
+ /// Get the next event to be processed
+ ///
+ /// Game event that needs to be processed
+ GameEvent GetNextEvent();
+ ///
+ /// If an event has output. Like executing a command wait until it's available
+ ///
+ /// List of output strings
+ string[] GetEventOutput();
+ }
+}
diff --git a/SharedLibraryCore/Interfaces/IEventParser.cs b/SharedLibraryCore/Interfaces/IEventParser.cs
index 968ad44a8..c96f33f1f 100644
--- a/SharedLibraryCore/Interfaces/IEventParser.cs
+++ b/SharedLibraryCore/Interfaces/IEventParser.cs
@@ -12,6 +12,7 @@ namespace SharedLibraryCore.Interfaces
/// server the event occurred on
/// single log line string
///
+ /// todo: make this integrate without needing the server
GameEvent GetEvent(Server server, string logLine);
///
/// Get game specific folder prefix for log files
diff --git a/SharedLibraryCore/Interfaces/IManager.cs b/SharedLibraryCore/Interfaces/IManager.cs
index af032a231..55eb0550f 100644
--- a/SharedLibraryCore/Interfaces/IManager.cs
+++ b/SharedLibraryCore/Interfaces/IManager.cs
@@ -23,6 +23,15 @@ namespace SharedLibraryCore.Interfaces
PenaltyService GetPenaltyService();
IDictionary GetPrivilegedClients();
IEventApi GetEventApi();
+ ///
+ /// Get the event handlers
+ ///
+ /// EventHandler for the manager
+ IEventHandler GetEventHandler();
+ ///
+ /// Signal to the manager that event(s) needs to be processed
+ ///
+ void SetHasEvent();
bool ShutdownRequested();
}
}
diff --git a/SharedLibraryCore/Server.cs b/SharedLibraryCore/Server.cs
index ede04b296..053b7b031 100644
--- a/SharedLibraryCore/Server.cs
+++ b/SharedLibraryCore/Server.cs
@@ -41,6 +41,7 @@ namespace SharedLibraryCore
PlayerHistory = new Queue();
ChatHistory = new List();
NextMessage = 0;
+ OnEvent = new ManualResetEventSlim();
CustomSayEnabled = Manager.GetApplicationSettings().Configuration().EnableCustomSayName;
CustomSayName = Manager.GetApplicationSettings().Configuration().CustomSayName;
InitializeTokens();
@@ -132,7 +133,7 @@ namespace SharedLibraryCore
await this.ExecuteCommandAsync(formattedMessage);
#else
Logger.WriteVerbose(Message.StripColors());
- await Utilities.CompletedTask;
+ await Task.CompletedTask;
#endif
}
@@ -150,7 +151,7 @@ namespace SharedLibraryCore
await this.ExecuteCommandAsync(formattedMessage);
#else
Logger.WriteVerbose($"{Target.ClientNumber}->{Message.StripColors()}");
- await Utilities.CompletedTask;
+ await Task.CompletedTask;
#endif
if (Target.Level == Player.Permission.Console)
@@ -308,6 +309,7 @@ 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;
diff --git a/SharedLibraryCore/Utilities.cs b/SharedLibraryCore/Utilities.cs
index 70e892631..38d294df3 100644
--- a/SharedLibraryCore/Utilities.cs
+++ b/SharedLibraryCore/Utilities.cs
@@ -16,7 +16,6 @@ namespace SharedLibraryCore
public static class Utilities
{
public static string OperatingDirectory = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + Path.DirectorySeparatorChar;
- public static readonly Task CompletedTask = Task.FromResult(false);
public static Encoding EncodingType;
public static Localization.Layout CurrentLocalization;
diff --git a/WebfrontCore/Controllers/BaseController.cs b/WebfrontCore/Controllers/BaseController.cs
index 044b78e1a..a409ebd01 100644
--- a/WebfrontCore/Controllers/BaseController.cs
+++ b/WebfrontCore/Controllers/BaseController.cs
@@ -16,24 +16,37 @@ namespace WebfrontCore.Controllers
protected IManager Manager;
protected readonly DatabaseContext Context;
protected bool Authorized { get; private set; }
- protected EFClient User { get; private set; }
+ protected EFClient Client { get; private set; }
+ private static byte[] LocalHost = { 127, 0, 0, 1 };
+ private static string DiscordLink;
+
+ public BaseController()
+ {
+ var Manager = Program.Manager;
+ if (Manager.GetApplicationSettings().Configuration().EnableDiscordLink)
+ {
+ string inviteLink = Manager.GetApplicationSettings().Configuration().DiscordInviteCode;
+ if (inviteLink != null)
+ DiscordLink = inviteLink.Contains("https") ? inviteLink : $"https://discordapp.com/invite/{inviteLink}";
+ else
+ DiscordLink = "";
+ }
+ }
public override void OnActionExecuting(ActionExecutingContext context)
{
- Manager = Program.Manager;
-
- User = User ?? new EFClient()
+ Client = Client ?? new EFClient()
{
ClientId = -1
};
- if (HttpContext.Connection.RemoteIpAddress.ToString() != "127.0.0.1")
+ if (!HttpContext.Connection.RemoteIpAddress.GetAddressBytes().SequenceEqual(LocalHost))
{
try
{
- User.ClientId = Convert.ToInt32(base.User.Claims.First(c => c.Type == ClaimTypes.Sid).Value);
- User.Level = (Player.Permission)Enum.Parse(typeof(Player.Permission), base.User.Claims.First(c => c.Type == ClaimTypes.Role).Value);
- User.CurrentAlias = new EFAlias() { Name = base.User.Claims.First(c => c.Type == ClaimTypes.NameIdentifier).Value };
+ Client.ClientId = Convert.ToInt32(base.User.Claims.First(c => c.Type == ClaimTypes.Sid).Value);
+ Client.Level = (Player.Permission)Enum.Parse(typeof(Player.Permission), User.Claims.First(c => c.Type == ClaimTypes.Role).Value);
+ Client.CurrentAlias = new EFAlias() { Name = User.Claims.First(c => c.Type == ClaimTypes.NameIdentifier).Value };
}
catch (InvalidOperationException)
@@ -44,24 +57,17 @@ namespace WebfrontCore.Controllers
else
{
- User.ClientId = 1;
- User.Level = Player.Permission.Console;
- User.CurrentAlias = new EFAlias() { Name = "IW4MAdmin" };
+ Client.ClientId = 1;
+ Client.Level = Player.Permission.Console;
+ Client.CurrentAlias = new EFAlias() { Name = "IW4MAdmin" };
}
- Authorized = User.ClientId >= 0;
+ Authorized = Client.ClientId >= 0;
ViewBag.Authorized = Authorized;
ViewBag.Url = Startup.Configuration["Web:Address"];
- ViewBag.User = User;
+ ViewBag.User = Client;
+ ViewBag.DiscordLink = DiscordLink;
- if (Manager.GetApplicationSettings().Configuration().EnableDiscordLink)
- {
- string inviteLink = Manager.GetApplicationSettings().Configuration().DiscordInviteCode;
- if (inviteLink != null)
- ViewBag.DiscordLink = inviteLink.Contains("https") ? inviteLink : $"https://discordapp.com/invite/{inviteLink}";
- else
- ViewBag.DiscordLink = "";
- }
base.OnActionExecuting(context);
}
}
diff --git a/WebfrontCore/Controllers/ConsoleController.cs b/WebfrontCore/Controllers/ConsoleController.cs
index c21434665..6868da350 100644
--- a/WebfrontCore/Controllers/ConsoleController.cs
+++ b/WebfrontCore/Controllers/ConsoleController.cs
@@ -32,10 +32,10 @@ namespace WebfrontCore.Controllers
var server = Manager.GetServers().First(s => s.GetHashCode() == serverId);
var client = new Player()
{
- ClientId = User.ClientId,
- Level = User.Level,
+ ClientId = Client.ClientId,
+ Level = Client.Level,
CurrentServer = server,
- CurrentAlias = new Alias() { Name = User.Name }
+ CurrentAlias = new Alias() { Name = Client.Name }
};
var remoteEvent = new GameEvent()
{
@@ -46,8 +46,9 @@ namespace WebfrontCore.Controllers
Remote = true
};
- await server.ExecuteEvent(remoteEvent);
-
+ Manager.GetEventHandler().AddEvent(remoteEvent);
+ // wait for the event to process
+ remoteEvent.OnProcessed.Wait();
var response = server.CommandResult.Where(c => c.ClientId == client.ClientId).ToList();
// remove the added command response
diff --git a/WebfrontCore/wwwroot/css/bootstrap-custom.scss b/WebfrontCore/wwwroot/css/bootstrap-custom.scss
index 1613aebe9..3f3ad42a0 100644
--- a/WebfrontCore/wwwroot/css/bootstrap-custom.scss
+++ b/WebfrontCore/wwwroot/css/bootstrap-custom.scss
@@ -33,7 +33,6 @@ a.nav-link {
}
.server-history,
-.server-header,
.server-activity,
#mobile_seperator,
.border-bottom {