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 {