diff --git a/Application/Main.cs b/Application/Main.cs index a44e586ff..aff42373f 100644 --- a/Application/Main.cs +++ b/Application/Main.cs @@ -156,7 +156,7 @@ namespace IW4MAdmin.Application } - ServerManager.Start(); + ServerManager.Start().Wait(); ServerManager.Logger.WriteVerbose(loc["MANAGER_SHUTDOWN_SUCCESS"]); } diff --git a/Application/Manager.cs b/Application/Manager.cs index 96916009f..81411dcbd 100644 --- a/Application/Manager.cs +++ b/Application/Manager.cs @@ -389,58 +389,55 @@ namespace IW4MAdmin.Application } } - public void Start() + public async Task Start() { #if !DEBUG // start heartbeat HeartbeatTimer = new Timer(SendHeartbeat, new HeartbeatState(), 0, 30000); #endif - // start polling servers - // StatusUpdateTimer = new Timer(UpdateStatus, null, 0, 5000); + // this needs to be run seperately from the main thread Task.Run(() => UpdateStatus(null)); + GameEvent newEvent; - Task.Run(async () => + while (Running) { - while (Running) + // wait for new event to be added + OnEvent.Wait(); + + // todo: sequencially or parallelize? + while ((newEvent = Handler.GetNextEvent()) != null) { - // wait for new event to be added - OnEvent.Wait(); - - // todo: sequencially or parallelize? - while ((newEvent = Handler.GetNextEvent()) != null) + try { - try - { - await newEvent.Owner.ExecuteEvent(newEvent); + await newEvent.Owner.ExecuteEvent(newEvent); #if DEBUG - Logger.WriteDebug("Processed Event"); + Logger.WriteDebug("Processed Event"); #endif - } - - catch (Exception E) - { - Logger.WriteError($"{Utilities.CurrentLocalization.LocalizationSet["SERVER_ERROR_EXCEPTION"]} {newEvent.Owner}"); - Logger.WriteDebug("Error Message: " + E.Message); - Logger.WriteDebug("Error Trace: " + E.StackTrace); - newEvent.OnProcessed.Set(); - continue; - } - // tell anyone waiting for the output that we're done - newEvent.OnProcessed.Set(); } - // signal that all events have been processed - OnEvent.Reset(); + catch (Exception E) + { + Logger.WriteError($"{Utilities.CurrentLocalization.LocalizationSet["SERVER_ERROR_EXCEPTION"]} {newEvent.Owner}"); + Logger.WriteDebug("Error Message: " + E.Message); + Logger.WriteDebug("Error Trace: " + E.StackTrace); + newEvent.OnProcessed.Set(); + continue; + } + // tell anyone waiting for the output that we're done + newEvent.OnProcessed.Set(); } + + // signal that all events have been processed + OnEvent.Reset(); + } #if !DEBUG HeartbeatTimer.Change(0, Timeout.Infinite); foreach (var S in Servers) S.Broadcast(Utilities.CurrentLocalization.LocalizationSet["BROADCAST_OFFLINE"]).Wait(); #endif - _servers.Clear(); - }).Wait(); + _servers.Clear(); } diff --git a/Application/Server.cs b/Application/Server.cs index b45b69e3c..f9ff61f36 100644 --- a/Application/Server.cs +++ b/Application/Server.cs @@ -36,7 +36,7 @@ namespace IW4MAdmin // todo: make this better with collisions int id = Math.Abs($"{IP}:{Port.ToString()}".Select(a => (int)a).Sum()); - // this is a nasty fix for get hashcode being changed + // hack: this is a nasty fix for get hashcode being changed switch (id) { case 765: @@ -455,7 +455,7 @@ namespace IW4MAdmin else if (E.Type == GameEvent.EventType.Script) { - Manager.GetEventHandler().AddEvent(new GameEvent(GameEvent.EventType.Kill, E.Data, E.Origin, E.Target, this)); + Manager.GetEventHandler().AddEvent(GameEvent.TranferWaiter(GameEvent.EventType.Kill, E)); } if (E.Type == GameEvent.EventType.Say && E.Data.Length >= 2) @@ -483,17 +483,12 @@ namespace IW4MAdmin 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 - }); + E.Extra = C; + + + + // reprocess event as a command + Manager.GetEventHandler().AddEvent(GameEvent.TranferWaiter(GameEvent.EventType.Command, E)); } } @@ -988,6 +983,8 @@ namespace IW4MAdmin }; await Manager.GetPenaltyService().Create(newPenalty); + // prevent them from logging in again + Manager.GetPrivilegedClients().Remove(Target.ClientId); } override public async Task Unban(string reason, Player Target, Player Origin) diff --git a/Plugins/Stats/Plugin.cs b/Plugins/Stats/Plugin.cs index 50c493919..27dd74c4e 100644 --- a/Plugins/Stats/Plugin.cs +++ b/Plugins/Stats/Plugin.cs @@ -103,8 +103,8 @@ namespace IW4MAdmin.Plugins.Stats int kills = clientStats.Sum(c => c.Kills); int deaths = clientStats.Sum(c => c.Deaths); double kdr = Math.Round(kills / (double)deaths, 2); - double skill = Math.Round(clientStats.Sum(c => c.Skill) / clientStats.Count, 2); - double spm = Math.Round(clientStats.Sum(c => c.SPM), 1); + double skill = Math.Round(clientStats.Sum(c => c.Skill) / clientStats.Where(c => c.Skill > 0).Count(), 2); + double spm = Math.Round(clientStats.Sum(c => c.SPM) / clientStats.Where(c => c.SPM > 0).Count(), 1); return new List() { diff --git a/SharedLibraryCore/Commands/NativeCommands.cs b/SharedLibraryCore/Commands/NativeCommands.cs index 0860ba144..0fdc34071 100644 --- a/SharedLibraryCore/Commands/NativeCommands.cs +++ b/SharedLibraryCore/Commands/NativeCommands.cs @@ -441,7 +441,7 @@ namespace SharedLibraryCore.Commands } } - if (newPerm > Player.Permission.Banned) + else if (newPerm > Player.Permission.Banned) { var ActiveClient = E.Owner.Manager.GetActiveClients() .FirstOrDefault(p => p.NetworkId == E.Target.NetworkId); @@ -465,6 +465,7 @@ namespace SharedLibraryCore.Commands catch (Exception) { + // this updates their privilege level to the webfront claims E.Owner.Manager.GetPrivilegedClients()[E.Target.ClientId] = E.Target; } diff --git a/SharedLibraryCore/Event.cs b/SharedLibraryCore/Event.cs index 02d82034d..2ae783e1f 100644 --- a/SharedLibraryCore/Event.cs +++ b/SharedLibraryCore/Event.cs @@ -55,6 +55,27 @@ namespace SharedLibraryCore OnProcessed = new ManualResetEventSlim(); } + public static GameEvent TranferWaiter(EventType newType, GameEvent e) + { + var newEvent = new GameEvent() + { + Data = e.Data, + Extra = e.Extra, + Message = e.Message, + OnProcessed = e.OnProcessed, + Origin = e.Origin, + Owner = e.Owner, + Remote = e.Remote, + Target = e.Target, + Type = newType + }; + + // hack: prevent the previous event from completing until this one is done + e.OnProcessed = new ManualResetEventSlim(); + + return newEvent; + } + public EventType Type; public string Data; // Data is usually the message sent by player public string Message; @@ -63,6 +84,6 @@ namespace SharedLibraryCore public Server Owner; public Boolean Remote = false; public object Extra { get; set; } - public ManualResetEventSlim OnProcessed { get; private set; } + public ManualResetEventSlim OnProcessed { get; set; } } } diff --git a/SharedLibraryCore/Interfaces/IManager.cs b/SharedLibraryCore/Interfaces/IManager.cs index 55eb0550f..2e0797d41 100644 --- a/SharedLibraryCore/Interfaces/IManager.cs +++ b/SharedLibraryCore/Interfaces/IManager.cs @@ -10,7 +10,7 @@ namespace SharedLibraryCore.Interfaces public interface IManager { Task Init(); - void Start(); + Task Start(); void Stop(); ILogger GetLogger(); IList GetServers(); diff --git a/SharedLibraryCore/RCon/Connection.cs b/SharedLibraryCore/RCon/Connection.cs index 3bc232fc9..ebb3be17c 100644 --- a/SharedLibraryCore/RCon/Connection.cs +++ b/SharedLibraryCore/RCon/Connection.cs @@ -255,7 +255,7 @@ namespace SharedLibraryCore.RCon if (FailedReceives >= 4) { - throw new NetworkException($"Could not receive data from the {ServerConnection.RemoteEndPoint}"); + throw new NetworkException($"Could not receive data from {ServerConnection.RemoteEndPoint}"); } } diff --git a/SharedLibraryCore/Services/PenaltyService.cs b/SharedLibraryCore/Services/PenaltyService.cs index 0c6288476..9a7e9c9cb 100644 --- a/SharedLibraryCore/Services/PenaltyService.cs +++ b/SharedLibraryCore/Services/PenaltyService.cs @@ -134,9 +134,10 @@ namespace SharedLibraryCore.Services { using (var context = new DatabaseContext()) { + context.ChangeTracker.AutoDetectChangesEnabled = false; + context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; if (victim) { - context.ChangeTracker.AutoDetectChangesEnabled = false; var now = DateTime.UtcNow; var iqPenalties = from penalty in context.Penalties.AsNoTracking() where penalty.OffenderId == clientId @@ -163,20 +164,21 @@ namespace SharedLibraryCore.Services TimeRemaining = now > penalty.Expires ? "" : penalty.Expires.ToString() }, When = penalty.When, - Sensitive = penalty.Type == Objects.Penalty.PenaltyType.Flag + Sensitive = penalty.Type == Penalty.PenaltyType.Flag }; // fixme: is this good and fast? var list = await iqPenalties.ToListAsync(); list.ForEach(p => { + // todo: why does this have to be done? + if (((PenaltyInfo)p.Value).Type.Length < 2) + ((PenaltyInfo)p.Value).Type = ((Penalty.PenaltyType)Convert.ToInt32(((PenaltyInfo)p.Value).Type)).ToString(); + var pi = ((PenaltyInfo)p.Value); if (pi.TimeRemaining.Length > 0) pi.TimeRemaining = (DateTime.Parse(((PenaltyInfo)p.Value).TimeRemaining) - now).TimeSpanText(); - // todo: why does this have to be done? - if (pi.Type.Length > 2) - pi.Type = ((Penalty.PenaltyType)Convert.ToInt32(pi.Type)).ToString(); - } - ); + + }); return list; } @@ -268,7 +270,7 @@ namespace SharedLibraryCore.Services { await internalContext.Clients .Where(c => c.AliasLinkId == p.LinkId) - .ForEachAsync(c => c.Level = Objects.Player.Permission.User); + .ForEachAsync(c => c.Level = Player.Permission.User); await internalContext.SaveChangesAsync(); } diff --git a/SharedLibraryCore/Utilities.cs b/SharedLibraryCore/Utilities.cs index afc4f3b35..b78d906ca 100644 --- a/SharedLibraryCore/Utilities.cs +++ b/SharedLibraryCore/Utilities.cs @@ -325,7 +325,9 @@ namespace SharedLibraryCore LastConnection = client.LastConnection == DateTime.MinValue ? DateTime.UtcNow : client.LastConnection, CurrentAlias = client.CurrentAlias, CurrentAliasId = client.CurrentAlias.AliasId, - IsBot = client.NetworkId == -1 + IsBot = client.NetworkId == -1, + Password = client.Password, + PasswordSalt = client.PasswordSalt }; } diff --git a/WebfrontCore/Controllers/BaseController.cs b/WebfrontCore/Controllers/BaseController.cs index 29718a905..a40aea365 100644 --- a/WebfrontCore/Controllers/BaseController.cs +++ b/WebfrontCore/Controllers/BaseController.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Security.Claims; +using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; @@ -47,12 +48,25 @@ namespace WebfrontCore.Controllers 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 }; + var stillExists = Manager.GetPrivilegedClients()[Client.ClientId]; + + // this happens if their level has been updated + if (stillExists.Level != Client.Level) + { + Client.Level = stillExists.Level; + } } catch (InvalidOperationException) { } + + catch (System.Collections.Generic.KeyNotFoundException) + { + // force the "banned" client to be signed out + HttpContext.SignOutAsync().Wait(); + } } else diff --git a/WebfrontCore/Controllers/ConsoleController.cs b/WebfrontCore/Controllers/ConsoleController.cs index 2ff1dfdd8..1a573ecd4 100644 --- a/WebfrontCore/Controllers/ConsoleController.cs +++ b/WebfrontCore/Controllers/ConsoleController.cs @@ -48,6 +48,7 @@ namespace WebfrontCore.Controllers Manager.GetEventHandler().AddEvent(remoteEvent); // wait for the event to process + await Task.Run(() => remoteEvent.OnProcessed.Wait()); var response = server.CommandResult.Where(c => c.ClientId == client.ClientId).ToList();