From d6996f96e684cc1048e62eb8ea357959839254d3 Mon Sep 17 00:00:00 2001 From: RaidMax Date: Wed, 12 Sep 2018 19:53:11 -0500 Subject: [PATCH] re-implemented auto-upload on publish fixed the max length migration for MySQL configure the python projects to be able to be published from command line optimize find active pentalties query add feature for issue #38 testing fix for concurrent dict access (in stats plugin) --- Application/Application.csproj | 12 +- Application/BuildScripts/PostBuild.bat | 3 + Application/GameEventHandler.cs | 7 +- .../PublishProfiles/Stable-Windows.pubxml | 12 - Application/Server.cs | 7 + DiscordWebhook/DiscordWebhook.pyproj | 36 +- IW4MAdmin.sln | 1 + RunPublishPre.cmd | 8 + SharedLibraryCore/Commands/NativeCommands.cs | 17 + .../Database/Models/EFChangeHistory.cs | 5 +- .../Database/Models/SharedEntity.cs | 2 +- SharedLibraryCore/Events/Change.cs | 21 + SharedLibraryCore/Events/EventAPI.cs | 92 ++- SharedLibraryCore/Events/GameEvent.cs | 83 ++- ...0180911190823_AddEFAliasNameMaxLength24.cs | 10 +- ...sCurrentValueToEFChangeHistory.Designer.cs | 684 ++++++++++++++++++ ...ddPreviousCurrentValueToEFChangeHistory.cs | 31 + .../DatabaseContextModelSnapshot.cs | 4 + SharedLibraryCore/Services/PenaltyService.cs | 51 +- SharedLibraryCore/SharedLibraryCore.csproj | 2 +- 20 files changed, 1029 insertions(+), 59 deletions(-) delete mode 100644 Application/Properties/PublishProfiles/Stable-Windows.pubxml create mode 100644 RunPublishPre.cmd create mode 100644 SharedLibraryCore/Events/Change.cs create mode 100644 SharedLibraryCore/Migrations/20180912015012_AddPreviousCurrentValueToEFChangeHistory.Designer.cs create mode 100644 SharedLibraryCore/Migrations/20180912015012_AddPreviousCurrentValueToEFChangeHistory.cs diff --git a/Application/Application.csproj b/Application/Application.csproj index 9d9c8494..d0663552 100644 --- a/Application/Application.csproj +++ b/Application/Application.csproj @@ -5,7 +5,7 @@ netcoreapp2.1 false RaidMax.IW4MAdmin.Application - 2.1.8 + 2.1.9 RaidMax Forever None IW4MAdmin @@ -80,15 +80,17 @@ - + - + + + + - + - diff --git a/Application/BuildScripts/PostBuild.bat b/Application/BuildScripts/PostBuild.bat index 98932ef9..db0ed34a 100644 --- a/Application/BuildScripts/PostBuild.bat +++ b/Application/BuildScripts/PostBuild.bat @@ -2,6 +2,9 @@ set SolutionDir=%1 set ProjectDir=%2 set TargetDir=%3 set OutDir=%4 +set Version=%5 + +echo %Version% > "%SolutionDir%DEPLOY\version.txt" echo Copying dependency configs copy "%SolutionDir%WebfrontCore\%OutDir%*.deps.json" "%TargetDir%" diff --git a/Application/GameEventHandler.cs b/Application/GameEventHandler.cs index 0a4c00ec..dceb1b32 100644 --- a/Application/GameEventHandler.cs +++ b/Application/GameEventHandler.cs @@ -18,20 +18,23 @@ namespace IW4MAdmin.Application { Manager = mgr; OutOfOrderEvents = new SortedList(); - IsProcessingEvent = new SemaphoreSlim(0); - IsProcessingEvent.Release(); + IsProcessingEvent = new SemaphoreSlim(2, 2); } public void AddEvent(GameEvent gameEvent) { + IsProcessingEvent.Wait(); ((Manager as ApplicationManager).OnServerEvent)(this, new GameEventArgs(null, false, gameEvent)); if (gameEvent.Type == GameEvent.EventType.Connect) { + IsProcessingEvent.Wait(); if (!gameEvent.OnProcessed.Wait(30 * 1000)) { Manager.GetLogger().WriteError($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMAND_TIMEOUT"]} [{gameEvent.Id}, {gameEvent.Type}]"); } + IsProcessingEvent.Release(1); } + IsProcessingEvent.Release(1); return; #if DEBUG diff --git a/Application/Properties/PublishProfiles/Stable-Windows.pubxml b/Application/Properties/PublishProfiles/Stable-Windows.pubxml deleted file mode 100644 index 49599c2f..00000000 --- a/Application/Properties/PublishProfiles/Stable-Windows.pubxml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - FileSystem - Release - netcoreapp2.0 - C:\Projects\IW4M-Admin\Publish\Windows - - \ No newline at end of file diff --git a/Application/Server.cs b/Application/Server.cs index 0fb40c34..905e8874 100644 --- a/Application/Server.cs +++ b/Application/Server.cs @@ -231,7 +231,14 @@ namespace IW4MAdmin // reban the "evading" guid if (player.Level != Player.Permission.Banned && currentBan.Type == Penalty.PenaltyType.Ban) + { + // hack: re apply the automated offense to the reban + if (currentBan.AutomatedOffense != null) + { + autoKickClient.AdministeredPenalties.Add(new EFPenalty() { AutomatedOffense = currentBan.AutomatedOffense }); + } await player.Ban($"{currentBan.Offense}", autoKickClient); + } // they didn't fully connect so empty their slot Players[player.ClientNumber] = null; diff --git a/DiscordWebhook/DiscordWebhook.pyproj b/DiscordWebhook/DiscordWebhook.pyproj index f8020fba..c3ed35f4 100644 --- a/DiscordWebhook/DiscordWebhook.pyproj +++ b/DiscordWebhook/DiscordWebhook.pyproj @@ -10,6 +10,7 @@ . . DiscordWebhook + true DiscordWebhook MSBuild|env|$(MSBuildProjectFullPath) False @@ -49,7 +50,7 @@ True - + @@ -58,4 +59,37 @@ + + + + + True + True + http://localhost + False + + + + + + + CurrentPage + True + False + False + False + + + + + + + + + False + False + + + + \ No newline at end of file diff --git a/IW4MAdmin.sln b/IW4MAdmin.sln index 31925072..efe8e838 100644 --- a/IW4MAdmin.sln +++ b/IW4MAdmin.sln @@ -10,6 +10,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution _commands.gsc = _commands.gsc _customcallbacks.gsc = _customcallbacks.gsc README.md = README.md + RunPublishPre.cmd = RunPublishPre.cmd version.txt = version.txt EndProjectSection EndProject diff --git a/RunPublishPre.cmd b/RunPublishPre.cmd new file mode 100644 index 00000000..b9954b86 --- /dev/null +++ b/RunPublishPre.cmd @@ -0,0 +1,8 @@ +dotnet publish WebfrontCore/WebfrontCore.csproj -c Prerelease -o C:\Projects\IW4M-Admin\Publish\WindowsPrerelease +dotnet publish Application/Application.csproj -c Prerelease -o C:\Projects\IW4M-Admin\Publish\WindowsPrerelease +dotnet publish GameLogServer/GameLogServer.pyproj -c Release -o C:\Projects\IW4M-Admin\Publish\WindowsPrerelease\GameLogServer +call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\Tools\VsDevCmd.bat" +msbuild GameLogServer/GameLogServer.pyproj /p:PublishProfile=FolderProfile /p:DeployOnBuild=true /p:PublishProfileRootFolder=C:\Projects\IW4M-Admin\GameLogServer\ +msbuild DiscordWebhook/DiscordWebhook.pyproj /p:PublishProfile=FolderProfile /p:DeployOnBuild=true /p:PublishProfileRootFolder=C:\Projects\IW4M-Admin\DiscordWebhook\ +cd "C:\Projects\IW4M-Admin\DEPLOY\" +PowerShell ".\upload_prerelease.ps1" \ No newline at end of file diff --git a/SharedLibraryCore/Commands/NativeCommands.cs b/SharedLibraryCore/Commands/NativeCommands.cs index 7951080c..323c8953 100644 --- a/SharedLibraryCore/Commands/NativeCommands.cs +++ b/SharedLibraryCore/Commands/NativeCommands.cs @@ -1,6 +1,7 @@ using Microsoft.EntityFrameworkCore; using SharedLibraryCore.Database; using SharedLibraryCore.Database.Models; +using SharedLibraryCore.Events; using SharedLibraryCore.Objects; using SharedLibraryCore.Services; using System; @@ -464,6 +465,7 @@ namespace SharedLibraryCore.Commands return; } + Player.Permission oldPerm = E.Target.Level; Player.Permission newPerm = Utilities.MatchPermission(E.Data); if (newPerm == Player.Permission.Owner && @@ -518,6 +520,21 @@ namespace SharedLibraryCore.Commands E.Owner.Manager.GetPrivilegedClients()[E.Target.ClientId] = E.Target; } + var e = new GameEvent() + { + Origin = E.Origin, + Target = E.Target, + Owner = E.Owner, + Type = GameEvent.EventType.ChangePermission, + Extra = new Change() + { + PreviousValue = oldPerm.ToString(), + NewValue = newPerm.ToString() + } + }; + + E.Owner.Manager.GetEventHandler().AddEvent(e); + await E.Origin.Tell($"{E.Target.Name} {Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_SUCCESS"]}"); } diff --git a/SharedLibraryCore/Database/Models/EFChangeHistory.cs b/SharedLibraryCore/Database/Models/EFChangeHistory.cs index 0fc9794a..33be8cf5 100644 --- a/SharedLibraryCore/Database/Models/EFChangeHistory.cs +++ b/SharedLibraryCore/Database/Models/EFChangeHistory.cs @@ -12,7 +12,8 @@ namespace SharedLibraryCore.Database.Models { public enum ChangeType { - Permission + Permission, + Ban } [Key] @@ -23,5 +24,7 @@ namespace SharedLibraryCore.Database.Models public DateTime TimeChanged { get; set; } = DateTime.UtcNow; [MaxLength(128)] public string Comment { get; set; } + public string PreviousValue { get; set; } + public string CurrentValue { get; set; } } } diff --git a/SharedLibraryCore/Database/Models/SharedEntity.cs b/SharedLibraryCore/Database/Models/SharedEntity.cs index 7784cbca..26915854 100644 --- a/SharedLibraryCore/Database/Models/SharedEntity.cs +++ b/SharedLibraryCore/Database/Models/SharedEntity.cs @@ -8,6 +8,6 @@ namespace SharedLibraryCore.Database.Models { public class SharedEntity { - public bool Active { get; set; } + public bool Active { get; set; } = true; } } diff --git a/SharedLibraryCore/Events/Change.cs b/SharedLibraryCore/Events/Change.cs new file mode 100644 index 00000000..6e4eaea1 --- /dev/null +++ b/SharedLibraryCore/Events/Change.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SharedLibraryCore.Events +{ + /// + /// represents change from one value to another + /// + class Change + { + /// + /// represents the previous value of the item + /// + public string PreviousValue { get; set; } + /// + /// represents the new/current value of the item + /// + public string NewValue { get; set; } + } +} diff --git a/SharedLibraryCore/Events/EventAPI.cs b/SharedLibraryCore/Events/EventAPI.cs index 4fc37e5e..a8fbac19 100644 --- a/SharedLibraryCore/Events/EventAPI.cs +++ b/SharedLibraryCore/Events/EventAPI.cs @@ -1,5 +1,8 @@ using System; using System.Collections.Generic; +using System.Threading.Tasks; +using SharedLibraryCore.Database; +using SharedLibraryCore.Database.Models; using SharedLibraryCore.Dtos; namespace SharedLibraryCore.Events @@ -22,7 +25,92 @@ namespace SharedLibraryCore.Events return eventList; } - public static void OnGameEvent(object sender, GameEventArgs eventState) + private static async Task SaveChangeHistory(GameEvent e) + { + EFChangeHistory change = null; + + switch (e.Type) + { + case GameEvent.EventType.Unknown: + break; + case GameEvent.EventType.Start: + break; + case GameEvent.EventType.Stop: + break; + case GameEvent.EventType.Connect: + break; + case GameEvent.EventType.Join: + break; + case GameEvent.EventType.Quit: + break; + case GameEvent.EventType.Disconnect: + break; + case GameEvent.EventType.MapEnd: + break; + case GameEvent.EventType.MapChange: + break; + case GameEvent.EventType.Say: + break; + case GameEvent.EventType.Warn: + break; + case GameEvent.EventType.Report: + break; + case GameEvent.EventType.Flag: + break; + case GameEvent.EventType.Unflag: + break; + case GameEvent.EventType.Kick: + break; + case GameEvent.EventType.TempBan: + break; + case GameEvent.EventType.Ban: + change = new EFChangeHistory() + { + OriginEntityId = e.Origin.ClientId, + TargetEntityId = e.Target.ClientId, + TypeOfChange = EFChangeHistory.ChangeType.Ban, + Comment = e.Data + }; + break; + case GameEvent.EventType.Command: + break; + case GameEvent.EventType.ChangePermission: + change = new EFChangeHistory() + { + OriginEntityId = e.Origin.ClientId, + TargetEntityId = e.Target.ClientId, + TypeOfChange = EFChangeHistory.ChangeType.Permission, + PreviousValue = ((Change)e.Extra).PreviousValue, + CurrentValue = ((Change)e.Extra).NewValue + }; + break; + case GameEvent.EventType.Broadcast: + break; + case GameEvent.EventType.Tell: + break; + case GameEvent.EventType.ScriptDamage: + break; + case GameEvent.EventType.ScriptKill: + break; + case GameEvent.EventType.Damage: + break; + case GameEvent.EventType.Kill: + break; + case GameEvent.EventType.JoinTeam: + break; + } + + if (change != null) + { + using (var ctx = new DatabaseContext(true)) + { + ctx.EFChangeHistory.Add(change); + await ctx.SaveChangesAsync(); + } + } + } + + public static async void OnGameEvent(object sender, GameEventArgs eventState) { var E = eventState.Event; // don't want to clog up the api with unknown events @@ -62,6 +150,8 @@ namespace SharedLibraryCore.Events // add the new event to the list AddNewEvent(apiEvent); + + await SaveChangeHistory(E); } /// diff --git a/SharedLibraryCore/Events/GameEvent.cs b/SharedLibraryCore/Events/GameEvent.cs index 59be67b3..1bde1db7 100644 --- a/SharedLibraryCore/Events/GameEvent.cs +++ b/SharedLibraryCore/Events/GameEvent.cs @@ -9,41 +9,118 @@ namespace SharedLibraryCore { public enum EventType { + /// + /// the event wasn't parsed properly + /// Unknown, // events "generated" by the server + /// + /// a server started being monitored + /// Start, + /// + /// a server stopped being monitored + /// Stop, + /// + /// a client was detecting as connecting via RCon + /// Connect, + /// + /// a client was detecting joining via log + /// Join, + /// + /// a client was detected leaving via log + /// Quit, + /// + /// a client was detected leaving by RCon + /// Disconnect, + /// + /// the current map ended + /// MapEnd, + /// + /// the current map changed + /// MapChange, - // events "generated" by clients + // events "generated" by clients + /// + /// a client sent a message + /// Say, + /// + /// a client was warned + /// Warn, + /// + /// a client was reported + /// Report, + /// + /// a client was flagged + /// Flag, + /// + /// a client was unflagged + /// Unflag, + /// + /// a client was kicked + /// Kick, + /// + /// a client was tempbanned + /// TempBan, + /// + /// a client was banned + /// Ban, + /// + /// a client entered a command + /// Command, + /// + /// a client's permission was changed + /// + ChangePermission, // events "generated" by IW4MAdmin + /// + /// a message is sent to all clients + /// Broadcast, + /// + /// a message is sent to a specific client + /// Tell, // events "generated" by script/log + /// + /// AC Damage Log + /// ScriptDamage, + /// + /// AC Kill Log + /// ScriptKill, + /// + /// damage info printed out by game script + /// Damage, + /// + /// kill info printed out by game script + /// Kill, + /// + /// team info printed out by game script + /// JoinTeam, - - StatusUpdate } static long NextEventId; diff --git a/SharedLibraryCore/Migrations/20180911190823_AddEFAliasNameMaxLength24.cs b/SharedLibraryCore/Migrations/20180911190823_AddEFAliasNameMaxLength24.cs index c3802498..362b5131 100644 --- a/SharedLibraryCore/Migrations/20180911190823_AddEFAliasNameMaxLength24.cs +++ b/SharedLibraryCore/Migrations/20180911190823_AddEFAliasNameMaxLength24.cs @@ -6,7 +6,15 @@ namespace SharedLibraryCore.Migrations { protected override void Up(MigrationBuilder migrationBuilder) { - + // hack: we can't alter the column on SQLite, but we need max length limit for the Index in MySQL etc + if (migrationBuilder.ActiveProvider != "Microsoft.EntityFrameworkCore.Sqlite") + { + migrationBuilder.AlterColumn( + name: "Name", + table: "EFAlias", + maxLength: 24, + nullable: false); + } } protected override void Down(MigrationBuilder migrationBuilder) diff --git a/SharedLibraryCore/Migrations/20180912015012_AddPreviousCurrentValueToEFChangeHistory.Designer.cs b/SharedLibraryCore/Migrations/20180912015012_AddPreviousCurrentValueToEFChangeHistory.Designer.cs new file mode 100644 index 00000000..4db53a4f --- /dev/null +++ b/SharedLibraryCore/Migrations/20180912015012_AddPreviousCurrentValueToEFChangeHistory.Designer.cs @@ -0,0 +1,684 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using SharedLibraryCore.Database; + +namespace SharedLibraryCore.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20180912015012_AddPreviousCurrentValueToEFChangeHistory")] + partial class AddPreviousCurrentValueToEFChangeHistory + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.1.2-rtm-30932"); + + modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", b => + { + b.Property("SnapshotId") + .ValueGeneratedOnAdd(); + + b.Property("Active"); + + b.Property("ClientId"); + + b.Property("CurrentSessionLength"); + + b.Property("CurrentStrain"); + + b.Property("CurrentViewAngleVector3Id"); + + b.Property("Deaths"); + + b.Property("Distance"); + + b.Property("EloRating"); + + b.Property("HitDestinationVector3Id"); + + b.Property("HitLocation"); + + b.Property("HitOriginVector3Id"); + + b.Property("HitType"); + + b.Property("Hits"); + + b.Property("Kills"); + + b.Property("LastStrainAngleVector3Id"); + + b.Property("SessionAngleOffset"); + + b.Property("SessionSPM"); + + b.Property("SessionScore"); + + b.Property("StrainAngleBetween"); + + b.Property("TimeSinceLastEvent"); + + b.Property("WeaponId"); + + b.Property("When"); + + b.HasKey("SnapshotId"); + + b.HasIndex("ClientId"); + + b.HasIndex("CurrentViewAngleVector3Id"); + + b.HasIndex("HitDestinationVector3Id"); + + b.HasIndex("HitOriginVector3Id"); + + b.HasIndex("LastStrainAngleVector3Id"); + + b.ToTable("EFACSnapshot"); + }); + + modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientKill", b => + { + b.Property("KillId") + .ValueGeneratedOnAdd(); + + b.Property("Active"); + + b.Property("AttackerId"); + + b.Property("Damage"); + + b.Property("DeathOriginVector3Id"); + + b.Property("DeathType"); + + b.Property("Fraction"); + + b.Property("HitLoc"); + + b.Property("IsKill"); + + b.Property("KillOriginVector3Id"); + + b.Property("Map"); + + b.Property("ServerId"); + + b.Property("VictimId"); + + b.Property("ViewAnglesVector3Id"); + + b.Property("VisibilityPercentage"); + + b.Property("Weapon"); + + b.Property("When"); + + b.HasKey("KillId"); + + b.HasIndex("AttackerId"); + + b.HasIndex("DeathOriginVector3Id"); + + b.HasIndex("KillOriginVector3Id"); + + b.HasIndex("ServerId"); + + b.HasIndex("VictimId"); + + b.HasIndex("ViewAnglesVector3Id"); + + b.ToTable("EFClientKills"); + }); + + modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientMessage", b => + { + b.Property("MessageId") + .ValueGeneratedOnAdd(); + + b.Property("Active"); + + b.Property("ClientId"); + + b.Property("Message"); + + b.Property("ServerId"); + + b.Property("TimeSent"); + + b.HasKey("MessageId"); + + b.HasIndex("ClientId"); + + b.HasIndex("ServerId"); + + b.ToTable("EFClientMessages"); + }); + + modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", b => + { + b.Property("RatingHistoryId") + .ValueGeneratedOnAdd(); + + b.Property("Active"); + + b.Property("ClientId"); + + b.HasKey("RatingHistoryId"); + + b.HasIndex("ClientId"); + + b.ToTable("EFClientRatingHistory"); + }); + + modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", b => + { + b.Property("ClientId"); + + b.Property("ServerId"); + + b.Property("Active"); + + b.Property("Deaths"); + + b.Property("EloRating"); + + b.Property("Kills"); + + b.Property("MaxStrain"); + + b.Property("RollingWeightedKDR"); + + b.Property("SPM"); + + b.Property("Skill"); + + b.Property("TimePlayed"); + + b.Property("VisionAverage"); + + b.HasKey("ClientId", "ServerId"); + + b.HasIndex("ServerId"); + + b.ToTable("EFClientStatistics"); + }); + + modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFHitLocationCount", b => + { + b.Property("HitLocationCountId") + .ValueGeneratedOnAdd(); + + b.Property("Active"); + + b.Property("ClientId") + .HasColumnName("EFClientStatistics_ClientId"); + + b.Property("HitCount"); + + b.Property("HitOffsetAverage"); + + b.Property("Location"); + + b.Property("MaxAngleDistance"); + + b.Property("ServerId") + .HasColumnName("EFClientStatistics_ServerId"); + + b.HasKey("HitLocationCountId"); + + b.HasIndex("ServerId"); + + b.HasIndex("ClientId", "ServerId"); + + b.ToTable("EFHitLocationCounts"); + }); + + modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFRating", b => + { + b.Property("RatingId") + .ValueGeneratedOnAdd(); + + b.Property("Active"); + + b.Property("ActivityAmount"); + + b.Property("Newest"); + + b.Property("Performance"); + + b.Property("Ranking"); + + b.Property("RatingHistoryId"); + + b.Property("ServerId"); + + b.Property("When"); + + b.HasKey("RatingId"); + + b.HasIndex("Performance"); + + b.HasIndex("Ranking"); + + b.HasIndex("RatingHistoryId"); + + b.HasIndex("ServerId"); + + b.HasIndex("When"); + + b.ToTable("EFRating"); + }); + + modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServer", b => + { + b.Property("ServerId"); + + b.Property("Active"); + + b.Property("Port"); + + b.HasKey("ServerId"); + + b.ToTable("EFServers"); + }); + + modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServerStatistics", b => + { + b.Property("StatisticId") + .ValueGeneratedOnAdd(); + + b.Property("Active"); + + b.Property("ServerId"); + + b.Property("TotalKills"); + + b.Property("TotalPlayTime"); + + b.HasKey("StatisticId"); + + b.HasIndex("ServerId"); + + b.ToTable("EFServerStatistics"); + }); + + modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAlias", b => + { + b.Property("AliasId") + .ValueGeneratedOnAdd(); + + b.Property("Active"); + + b.Property("DateAdded"); + + b.Property("IPAddress"); + + b.Property("LinkId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(24); + + b.HasKey("AliasId"); + + b.HasIndex("IPAddress"); + + b.HasIndex("LinkId"); + + b.HasIndex("Name"); + + b.ToTable("EFAlias"); + }); + + modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAliasLink", b => + { + b.Property("AliasLinkId") + .ValueGeneratedOnAdd(); + + b.Property("Active"); + + b.HasKey("AliasLinkId"); + + b.ToTable("EFAliasLinks"); + }); + + modelBuilder.Entity("SharedLibraryCore.Database.Models.EFChangeHistory", b => + { + b.Property("ChangeHistoryId") + .ValueGeneratedOnAdd(); + + b.Property("Active"); + + b.Property("Comment") + .HasMaxLength(128); + + b.Property("CurrentValue"); + + b.Property("OriginEntityId"); + + b.Property("PreviousValue"); + + b.Property("TargetEntityId"); + + b.Property("TimeChanged"); + + b.Property("TypeOfChange"); + + b.HasKey("ChangeHistoryId"); + + b.ToTable("EFChangeHistory"); + }); + + modelBuilder.Entity("SharedLibraryCore.Database.Models.EFClient", b => + { + b.Property("ClientId") + .ValueGeneratedOnAdd(); + + b.Property("Active"); + + b.Property("AliasLinkId"); + + b.Property("Connections"); + + b.Property("CurrentAliasId"); + + b.Property("FirstConnection"); + + b.Property("LastConnection"); + + b.Property("Level"); + + b.Property("Masked"); + + b.Property("NetworkId"); + + b.Property("Password"); + + b.Property("PasswordSalt"); + + b.Property("TotalConnectionTime"); + + b.HasKey("ClientId"); + + b.HasIndex("AliasLinkId"); + + b.HasIndex("CurrentAliasId"); + + b.HasIndex("NetworkId") + .IsUnique(); + + b.ToTable("EFClients"); + }); + + modelBuilder.Entity("SharedLibraryCore.Database.Models.EFMeta", b => + { + b.Property("MetaId") + .ValueGeneratedOnAdd(); + + b.Property("Active"); + + b.Property("ClientId"); + + b.Property("Created"); + + b.Property("Extra"); + + b.Property("Key") + .IsRequired(); + + b.Property("Updated"); + + b.Property("Value") + .IsRequired(); + + b.HasKey("MetaId"); + + b.HasIndex("ClientId"); + + b.ToTable("EFMeta"); + }); + + modelBuilder.Entity("SharedLibraryCore.Database.Models.EFPenalty", b => + { + b.Property("PenaltyId") + .ValueGeneratedOnAdd(); + + b.Property("Active"); + + b.Property("AutomatedOffense"); + + b.Property("Expires"); + + b.Property("LinkId"); + + b.Property("OffenderId"); + + b.Property("Offense") + .IsRequired(); + + b.Property("PunisherId"); + + b.Property("Type"); + + b.Property("When"); + + b.HasKey("PenaltyId"); + + b.HasIndex("LinkId"); + + b.HasIndex("OffenderId"); + + b.HasIndex("PunisherId"); + + b.ToTable("EFPenalties"); + }); + + modelBuilder.Entity("SharedLibraryCore.Helpers.Vector3", b => + { + b.Property("Vector3Id") + .ValueGeneratedOnAdd(); + + b.Property("EFACSnapshotSnapshotId"); + + b.Property("X"); + + b.Property("Y"); + + b.Property("Z"); + + b.HasKey("Vector3Id"); + + b.HasIndex("EFACSnapshotSnapshotId"); + + b.ToTable("Vector3"); + }); + + modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", b => + { + b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("SharedLibraryCore.Helpers.Vector3", "CurrentViewAngle") + .WithMany() + .HasForeignKey("CurrentViewAngleVector3Id"); + + b.HasOne("SharedLibraryCore.Helpers.Vector3", "HitDestination") + .WithMany() + .HasForeignKey("HitDestinationVector3Id"); + + b.HasOne("SharedLibraryCore.Helpers.Vector3", "HitOrigin") + .WithMany() + .HasForeignKey("HitOriginVector3Id"); + + b.HasOne("SharedLibraryCore.Helpers.Vector3", "LastStrainAngle") + .WithMany() + .HasForeignKey("LastStrainAngleVector3Id"); + }); + + modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientKill", b => + { + b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Attacker") + .WithMany() + .HasForeignKey("AttackerId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("SharedLibraryCore.Helpers.Vector3", "DeathOrigin") + .WithMany() + .HasForeignKey("DeathOriginVector3Id"); + + b.HasOne("SharedLibraryCore.Helpers.Vector3", "KillOrigin") + .WithMany() + .HasForeignKey("KillOriginVector3Id"); + + b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Victim") + .WithMany() + .HasForeignKey("VictimId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("SharedLibraryCore.Helpers.Vector3", "ViewAngles") + .WithMany() + .HasForeignKey("ViewAnglesVector3Id"); + }); + + modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientMessage", b => + { + b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", b => + { + b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", b => + { + b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFHitLocationCount", b => + { + b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics") + .WithMany("HitLocations") + .HasForeignKey("ClientId", "ServerId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFRating", b => + { + b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", "RatingHistory") + .WithMany("Ratings") + .HasForeignKey("RatingHistoryId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId"); + }); + + modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServerStatistics", b => + { + b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAlias", b => + { + b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "Link") + .WithMany("Children") + .HasForeignKey("LinkId") + .OnDelete(DeleteBehavior.Restrict); + }); + + modelBuilder.Entity("SharedLibraryCore.Database.Models.EFClient", b => + { + b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "AliasLink") + .WithMany() + .HasForeignKey("AliasLinkId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("SharedLibraryCore.Database.Models.EFAlias", "CurrentAlias") + .WithMany() + .HasForeignKey("CurrentAliasId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("SharedLibraryCore.Database.Models.EFMeta", b => + { + b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client") + .WithMany("Meta") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("SharedLibraryCore.Database.Models.EFPenalty", b => + { + b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "Link") + .WithMany("ReceivedPenalties") + .HasForeignKey("LinkId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Offender") + .WithMany("ReceivedPenalties") + .HasForeignKey("OffenderId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Punisher") + .WithMany("AdministeredPenalties") + .HasForeignKey("PunisherId") + .OnDelete(DeleteBehavior.Restrict); + }); + + modelBuilder.Entity("SharedLibraryCore.Helpers.Vector3", b => + { + b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot") + .WithMany("PredictedViewAngles") + .HasForeignKey("EFACSnapshotSnapshotId"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/SharedLibraryCore/Migrations/20180912015012_AddPreviousCurrentValueToEFChangeHistory.cs b/SharedLibraryCore/Migrations/20180912015012_AddPreviousCurrentValueToEFChangeHistory.cs new file mode 100644 index 00000000..2ccf0c2a --- /dev/null +++ b/SharedLibraryCore/Migrations/20180912015012_AddPreviousCurrentValueToEFChangeHistory.cs @@ -0,0 +1,31 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace SharedLibraryCore.Migrations +{ + public partial class AddPreviousCurrentValueToEFChangeHistory : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "CurrentValue", + table: "EFChangeHistory", + nullable: true); + + migrationBuilder.AddColumn( + name: "PreviousValue", + table: "EFChangeHistory", + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "CurrentValue", + table: "EFChangeHistory"); + + migrationBuilder.DropColumn( + name: "PreviousValue", + table: "EFChangeHistory"); + } + } +} diff --git a/SharedLibraryCore/Migrations/DatabaseContextModelSnapshot.cs b/SharedLibraryCore/Migrations/DatabaseContextModelSnapshot.cs index d1f923a9..9e579405 100644 --- a/SharedLibraryCore/Migrations/DatabaseContextModelSnapshot.cs +++ b/SharedLibraryCore/Migrations/DatabaseContextModelSnapshot.cs @@ -356,8 +356,12 @@ namespace SharedLibraryCore.Migrations b.Property("Comment") .HasMaxLength(128); + b.Property("CurrentValue"); + b.Property("OriginEntityId"); + b.Property("PreviousValue"); + b.Property("TargetEntityId"); b.Property("TimeChanged"); diff --git a/SharedLibraryCore/Services/PenaltyService.cs b/SharedLibraryCore/Services/PenaltyService.cs index 13a99724..2e0177ce 100644 --- a/SharedLibraryCore/Services/PenaltyService.cs +++ b/SharedLibraryCore/Services/PenaltyService.cs @@ -101,9 +101,8 @@ namespace SharedLibraryCore.Services public async Task> GetRecentPenalties(int count, int offset, Penalty.PenaltyType showOnly = Penalty.PenaltyType.Any) { - using (var context = new DatabaseContext()) + using (var context = new DatabaseContext(true)) return await context.Penalties - .AsNoTracking() .Include(p => p.Offender.CurrentAlias) .Include(p => p.Punisher.CurrentAlias) .Where(p => showOnly == Penalty.PenaltyType.Any ? p.Type != Penalty.PenaltyType.Any : p.Type == showOnly) @@ -116,9 +115,8 @@ namespace SharedLibraryCore.Services public async Task> GetClientPenaltiesAsync(int clientId) { - using (var context = new DatabaseContext()) + using (var context = new DatabaseContext(true)) return await context.Penalties - .AsNoTracking() .Where(p => p.OffenderId == clientId) .Where(p => p.Active) .Include(p => p.Offender.CurrentAlias) @@ -134,10 +132,9 @@ namespace SharedLibraryCore.Services /// public async Task> ReadGetClientPenaltiesAsync(int clientId, bool victim = true) { - using (var context = new DatabaseContext()) + using (var context = new DatabaseContext(true)) { - context.ChangeTracker.AutoDetectChangesEnabled = false; - context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; + // todo: clean this up if (victim) { var now = DateTime.UtcNow; @@ -233,32 +230,26 @@ namespace SharedLibraryCore.Services } } - public async Task> GetActivePenaltiesAsync(int aliasId, int ip = 0) + public async Task> GetActivePenaltiesAsync(int linkId, int ip = 0) { var now = DateTime.UtcNow; - using (var context = new DatabaseContext()) + using (var context = new DatabaseContext(true)) { - var iqPenalties = await (from link in context.AliasLinks - where link.AliasLinkId == aliasId - join penalty in context.Penalties - on link.AliasLinkId equals penalty.LinkId - where penalty.Active - where penalty.Expires > now - orderby penalty.When descending - select penalty).ToListAsync(); - if (ip != 0) - { - iqPenalties.AddRange(await (from alias in context.Aliases - where alias.IPAddress == ip - join penalty in context.Penalties - on alias.LinkId equals penalty.LinkId - where penalty.Active - where penalty.Expires > now - orderby penalty.When descending - select penalty).ToListAsync()); - } - return iqPenalties; + var iqPenalties = context.Penalties + .Where(p => p.LinkId == linkId || + p.Link.Children.Any(a => a.IPAddress == ip)) + .Where(p => p.Active) + .Where(p => p.Expires > now); + + +#if DEBUG == true + var penaltiesSql = iqPenalties.ToSql(); +#endif + + var activePenalties = await iqPenalties.ToListAsync(); + // this is a bit more performant in memory (ordering) + return activePenalties.OrderByDescending(p =>p.When).ToList(); } } @@ -286,8 +277,6 @@ namespace SharedLibraryCore.Services .ForEachAsync(c => c.Level = Player.Permission.User); await internalContext.SaveChangesAsync(); } - - } }); diff --git a/SharedLibraryCore/SharedLibraryCore.csproj b/SharedLibraryCore/SharedLibraryCore.csproj index 04068ecf..3522e9b8 100644 --- a/SharedLibraryCore/SharedLibraryCore.csproj +++ b/SharedLibraryCore/SharedLibraryCore.csproj @@ -36,7 +36,7 @@ - +