From 9d946d1badd3e89e4c9f1d9822d1135b188ed16f Mon Sep 17 00:00:00 2001 From: RaidMax Date: Wed, 3 Oct 2018 21:20:49 -0500 Subject: [PATCH] more stability changes --- Application/Application.csproj | 1 + Application/Server.cs | 7 +++- Plugins/ProfanityDeterment/Plugin.cs | 11 +++--- Plugins/Stats/Helpers/StatManager.cs | 33 ++++++++++++----- .../Stats/Helpers/ThreadSafeStatsService.cs | 11 ++++-- Plugins/Stats/Models/EFHitLocationCount.cs | 1 - Plugins/Tests/ClientTests.cs | 21 ++++++++--- SharedLibraryCore/Commands/NativeCommands.cs | 10 ++++-- SharedLibraryCore/Events/GameEvent.cs | 6 +++- SharedLibraryCore/Objects/Player.cs | 35 ++++++++++++++++--- SharedLibraryCore/Server.cs | 2 +- .../Services/GenericRepository.cs | 2 +- 12 files changed, 108 insertions(+), 32 deletions(-) diff --git a/Application/Application.csproj b/Application/Application.csproj index 9c322e34b..e46d20c11 100644 --- a/Application/Application.csproj +++ b/Application/Application.csproj @@ -30,6 +30,7 @@ true true + 2.1.9.3 diff --git a/Application/Server.cs b/Application/Server.cs index d520eada5..211ec1d26 100644 --- a/Application/Server.cs +++ b/Application/Server.cs @@ -432,6 +432,11 @@ namespace IW4MAdmin await Kick(E.Data, E.Target, E.Origin); } + else if (E.Type == GameEvent.EventType.Warn) + { + await Warn(E.Data, E.Target, E.Origin); + } + else if (E.Type == GameEvent.EventType.Quit) { var origin = Players.FirstOrDefault(p => p != null && p.NetworkId == E.Origin.NetworkId); @@ -888,7 +893,7 @@ namespace IW4MAdmin #endif } - public override async Task Warn(String Reason, Player Target, Player Origin) + protected override async Task Warn(String Reason, Player Target, Player Origin) { // ensure player gets warned if command not performed on them in game if (Target.ClientNumber < 0) diff --git a/Plugins/ProfanityDeterment/Plugin.cs b/Plugins/ProfanityDeterment/Plugin.cs index 08e230b2e..6e058bece 100644 --- a/Plugins/ProfanityDeterment/Plugin.cs +++ b/Plugins/ProfanityDeterment/Plugin.cs @@ -25,7 +25,7 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment public Task OnEventAsync(GameEvent E, Server S) { if (!Settings.Configuration().EnableProfanityDeterment) - return Task.CompletedTask; ; + return Task.CompletedTask; if (E.Type == GameEvent.EventType.Connect) { @@ -50,7 +50,8 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment { E.Origin.Kick(Settings.Configuration().ProfanityKickMessage, new Player() { - ClientId = 1 + ClientId = 1, + CurrentServer = E.Owner }); }; } @@ -86,7 +87,8 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment { clientProfanity.Client.Kick(Settings.Configuration().ProfanityKickMessage, new Player() { - ClientId = 1 + ClientId = 1, + CurrentServer = E.Owner }); } @@ -96,7 +98,8 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment clientProfanity.Client.Warn(Settings.Configuration().ProfanityWarningMessage, new Player() { - ClientId = 1 + ClientId = 1, + CurrentServer = E.Owner }); } } diff --git a/Plugins/Stats/Helpers/StatManager.cs b/Plugins/Stats/Helpers/StatManager.cs index 1449e6607..3966672ac 100644 --- a/Plugins/Stats/Helpers/StatManager.cs +++ b/Plugins/Stats/Helpers/StatManager.cs @@ -194,8 +194,11 @@ namespace IW4MAdmin.Plugins.Stats.Helpers var statsSvc = new ThreadSafeStatsService(); ContextThreads.TryAdd(serverId, statsSvc); + var serverSvc = statsSvc.ServerSvc; + // get the server from the database if it exists, otherwise create and insert a new one var server = statsSvc.ServerSvc.Find(c => c.ServerId == serverId).FirstOrDefault(); + if (server == null) { server = new EFServer() @@ -205,11 +208,11 @@ namespace IW4MAdmin.Plugins.Stats.Helpers ServerId = serverId }; - statsSvc.ServerSvc.Insert(server); + serverSvc.Insert(server); } // this doesn't need to be async as it's during initialization - statsSvc.ServerSvc.SaveChanges(); + serverSvc.SaveChanges(); // check to see if the stats have ever been initialized InitializeServerStats(sv); statsSvc.ServerStatsSvc.SaveChanges(); @@ -278,7 +281,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers }; // insert if they've not been added - clientStats = clientStatsSvc.Insert(clientStats); + clientStatsSvc.Insert(clientStats); await clientStatsSvc.SaveChangesAsync(); } @@ -470,6 +473,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers var clientStats = Servers[serverId].PlayerStats[attacker.ClientId]; var clientStatsSvc = statsSvc.ClientStatSvc; clientStatsSvc.Update(clientStats); + // increment their hit count if (hit.DeathType == IW4Info.MeansOfDeath.MOD_PISTOL_BULLET || @@ -496,13 +500,25 @@ namespace IW4MAdmin.Plugins.Stats.Helpers await ApplyPenalty(clientDetection.ProcessTotalRatio(clientStats), clientDetection, attacker, ctx); } - await clientStatsSvc.SaveChangesAsync(); + ctx.Set().UpdateRange(clientStats.HitLocations); + await ctx.SaveChangesAsync(); } catch (Exception ex) { - Log.WriteError("AC ERROR"); + Log.WriteError("Could not save hit or AC info"); + Log.WriteDebug(ex.GetExceptionInfo()); + } + + try + { + await clientStatsSvc.SaveChangesAsync(); + } + + catch (Exception ex) + { + Log.WriteError("Could save save client stats"); Log.WriteDebug(ex.GetExceptionInfo()); } @@ -1116,10 +1132,11 @@ namespace IW4MAdmin.Plugins.Stats.Helpers { int serverId = sv.GetHashCode(); var statsSvc = ContextThreads[serverId]; + var serverSvc = statsSvc.ServerSvc; + + serverSvc.Update(Servers[serverId].Server); + await serverSvc.SaveChangesAsync(); - // Log.WriteDebug("Syncing stats contexts"); - await statsSvc.ServerStatsSvc.SaveChangesAsync(); - //await statsSvc.ClientStatSvc.SaveChangesAsync(); await statsSvc.KillStatsSvc.SaveChangesAsync(); await statsSvc.ServerSvc.SaveChangesAsync(); diff --git a/Plugins/Stats/Helpers/ThreadSafeStatsService.cs b/Plugins/Stats/Helpers/ThreadSafeStatsService.cs index 1863794c4..64cfb0074 100644 --- a/Plugins/Stats/Helpers/ThreadSafeStatsService.cs +++ b/Plugins/Stats/Helpers/ThreadSafeStatsService.cs @@ -14,10 +14,16 @@ namespace IW4MAdmin.Plugins.Stats.Helpers { get { - return new GenericRepository(true); + return new GenericRepository(false); + } + } + public GenericRepository ServerSvc + { + get + { + return new GenericRepository(false); } } - public GenericRepository ServerSvc { get; private set; } public GenericRepository KillStatsSvc { get; private set; } public GenericRepository ServerStatsSvc { get; private set; } public GenericRepository MessageSvc @@ -30,7 +36,6 @@ namespace IW4MAdmin.Plugins.Stats.Helpers public ThreadSafeStatsService() { - ServerSvc = new GenericRepository(); KillStatsSvc = new GenericRepository(); ServerStatsSvc = new GenericRepository(); } diff --git a/Plugins/Stats/Models/EFHitLocationCount.cs b/Plugins/Stats/Models/EFHitLocationCount.cs index 4d7fa192b..e5fea2f78 100644 --- a/Plugins/Stats/Models/EFHitLocationCount.cs +++ b/Plugins/Stats/Models/EFHitLocationCount.cs @@ -23,6 +23,5 @@ namespace IW4MAdmin.Plugins.Stats.Models public int ServerId { get; set; } [ForeignKey("ServerId"), Column(Order = 1)] public EFServer Server { get; set; } - } } diff --git a/Plugins/Tests/ClientTests.cs b/Plugins/Tests/ClientTests.cs index 30737a704..089dd512e 100644 --- a/Plugins/Tests/ClientTests.cs +++ b/Plugins/Tests/ClientTests.cs @@ -53,13 +53,15 @@ namespace Tests Assert.False(client == null, "no client found to warn"); var warnEvent = client.Warn("test warn", new Player() { ClientId = 1, Level = Player.Permission.Console, CurrentServer = client.CurrentServer }); - warnEvent.OnProcessed.Wait(TestTimeout); + warnEvent.OnProcessed.Wait(); - Assert.True(client.Warnings == 1 || - warnEvent.Failed, "warning did not get applied"); + Assert.True((client.Warnings == 1 || + warnEvent.Failed) && + Manager.GetPenaltyService().GetClientPenaltiesAsync(client.ClientId).Result.Count(p => p.Type == Penalty.PenaltyType.Warning) == 1, + "warning did not get applied"); warnEvent = client.Warn("test warn", new Player() { ClientId = 1, Level = Player.Permission.Banned, CurrentServer = client.CurrentServer }); - warnEvent.OnProcessed.Wait(TestTimeout); + warnEvent.OnProcessed.Wait(); Assert.True(warnEvent.FailReason == GameEvent.EventFailReason.Permission && client.Warnings == 1, "warning was applied without proper permissions"); @@ -86,8 +88,17 @@ namespace Tests var client = Manager.Servers.First().GetPlayersAsList().FirstOrDefault(); Assert.False(client == null, "no client found to report"); + // fail + var player = new Player() { ClientId = 1, Level = Player.Permission.Console, CurrentServer = client.CurrentServer }; + player.SetAdditionalProperty("_reportCount", 3); + var reportEvent = client.Report("test report", player); + reportEvent.OnProcessed.Wait(TestTimeout); + + Assert.True(reportEvent.FailReason == GameEvent.EventFailReason.Throttle & + client.CurrentServer.Reports.Count(r => r.Target.NetworkId == client.NetworkId) == 0, $"too many reports were applied [{reportEvent.FailReason.ToString()}]"); + // succeed - var reportEvent = client.Report("test report", new Player() { ClientId = 1, Level = Player.Permission.Console, CurrentServer = client.CurrentServer }); + reportEvent = client.Report("test report", new Player() { ClientId = 1, Level = Player.Permission.Console, CurrentServer = client.CurrentServer }); reportEvent.OnProcessed.Wait(TestTimeout); Assert.True(!reportEvent.Failed && diff --git a/SharedLibraryCore/Commands/NativeCommands.cs b/SharedLibraryCore/Commands/NativeCommands.cs index 42f179dab..99094f46c 100644 --- a/SharedLibraryCore/Commands/NativeCommands.cs +++ b/SharedLibraryCore/Commands/NativeCommands.cs @@ -773,12 +773,12 @@ namespace SharedLibraryCore.Commands else if (unflagEvent.FailReason == GameEvent.EventFailReason.Invalid) { - E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_UNFLAG"]} ^5{E.Target.Name}"); + E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNFLAG_NOTFLAGGED"]); } else { - E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNFLAG_NOTFLAGGED"]); + E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_UNFLAG"]} ^5{E.Target.Name}"); } return Task.CompletedTask; @@ -825,6 +825,11 @@ namespace SharedLibraryCore.Commands commandEvent.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_FAIL_SELF"]); } + else if (reportEvent.FailReason == GameEvent.EventFailReason.Throttle) + { + commandEvent.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_FAIL_TOOMANY"]); + } + else if (reportEvent.Failed) { commandEvent.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_FAIL_DUPLICATE"]); @@ -1324,6 +1329,7 @@ namespace SharedLibraryCore.Commands var nextMapMatch = currentMap.First().Index != lastMap.Index ? regexMatches[regexMatches.IndexOf(currentMap.First()) + 1] : regexMatches.First(); + nextMap = s.Maps.FirstOrDefault(m => m.Name == nextMapMatch.Groups[3].ToString()) ?? nextMap; string nextGametype = nextMapMatch.Groups[2].ToString().Length == 0 ? Utilities.GetLocalizedGametype(s.Gametype) : diff --git a/SharedLibraryCore/Events/GameEvent.cs b/SharedLibraryCore/Events/GameEvent.cs index 5edd1683c..3cc639957 100644 --- a/SharedLibraryCore/Events/GameEvent.cs +++ b/SharedLibraryCore/Events/GameEvent.cs @@ -26,7 +26,11 @@ namespace SharedLibraryCore /// /// executing the event would cause an invalid state /// - Invalid + Invalid, + /// + /// client is doing too much of something + /// + Throttle } public enum EventType diff --git a/SharedLibraryCore/Objects/Player.cs b/SharedLibraryCore/Objects/Player.cs index 43fb55e58..568f26839 100644 --- a/SharedLibraryCore/Objects/Player.cs +++ b/SharedLibraryCore/Objects/Player.cs @@ -81,7 +81,10 @@ namespace SharedLibraryCore.Objects ConnectionTime = DateTime.UtcNow; ClientNumber = -1; DelayedEvents = new Queue(); - _additionalProperties = new Dictionary(); + _additionalProperties = new Dictionary + { + { "_reportCount", 0 } + }; } public override string ToString() => $"{Name}::{NetworkId}"; @@ -116,19 +119,22 @@ namespace SharedLibraryCore.Objects { Type = GameEvent.EventType.Warn, Message = warnReason, + Data = warnReason, Origin = sender, Target = this, Owner = sender.CurrentServer }; // enforce level restrictions - if (sender.Level <= this.Level) + if (this.Level > sender.Level) { e.FailReason = GameEvent.EventFailReason.Permission; - return e; } - this.Warnings++; + else + { + this.Warnings++; + } sender.CurrentServer.Manager.GetEventHandler().AddEvent(e); return e; @@ -181,6 +187,8 @@ namespace SharedLibraryCore.Objects Owner = sender.CurrentServer }; + int reportCount = sender.GetAdditionalProperty("_reportCount"); + if (this.Level > sender.Level) { e.FailReason = GameEvent.EventFailReason.Permission; @@ -191,12 +199,18 @@ namespace SharedLibraryCore.Objects e.FailReason = GameEvent.EventFailReason.Invalid; } + else if (reportCount > 2) + { + e.FailReason = GameEvent.EventFailReason.Throttle; + } + else if (CurrentServer.Reports.Count(report => (report.Origin.NetworkId == sender.NetworkId && report.Target.NetworkId == this.NetworkId)) > 0) { e.FailReason = GameEvent.EventFailReason.Exception; } + sender.SetAdditionalProperty("_reportCount", reportCount + 1); sender.CurrentServer.Manager.GetEventHandler().AddEvent(e); return e; } @@ -391,7 +405,18 @@ namespace SharedLibraryCore.Objects [NotMapped] Dictionary _additionalProperties; public T GetAdditionalProperty(string name) => (T)_additionalProperties[name]; - public void SetAdditionalProperty(string name, object value) => _additionalProperties.Add(name, value); + public void SetAdditionalProperty(string name, object value) + { + if (_additionalProperties.ContainsKey(name)) + { + _additionalProperties[name] = value; + } + else + { + _additionalProperties.Add(name, value); + } + } + [NotMapped] public int ClientNumber { get; set; } [NotMapped] diff --git a/SharedLibraryCore/Server.cs b/SharedLibraryCore/Server.cs index 7c7fe04c5..c61d2eaf7 100644 --- a/SharedLibraryCore/Server.cs +++ b/SharedLibraryCore/Server.cs @@ -209,7 +209,7 @@ namespace SharedLibraryCore /// The person who banned the target abstract protected Task Ban(String Reason, Player Target, Player Origin); - abstract public Task Warn(String Reason, Player Target, Player Origin); + abstract protected Task Warn(String Reason, Player Target, Player Origin); /// /// Unban a player by npID / GUID diff --git a/SharedLibraryCore/Services/GenericRepository.cs b/SharedLibraryCore/Services/GenericRepository.cs index 6ae7727bc..0fe272484 100644 --- a/SharedLibraryCore/Services/GenericRepository.cs +++ b/SharedLibraryCore/Services/GenericRepository.cs @@ -28,7 +28,7 @@ namespace SharedLibraryCore.Services { if (_context == null) { - _context = new DatabaseContext(ShouldTrack); + _context = new DatabaseContext(!ShouldTrack); } return _context;