From a786541484ff9c41c624e2af77e09742ce42ab15 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 9d9c84940..d06635524 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 98932ef91..db0ed34ae 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 0a4c00ec3..dceb1b326 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 49599c2f4..000000000 --- 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 0fb40c348..905e88740 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 f8020fba8..c3ed35f4f 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 319250726..efe8e8387 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 000000000..b9954b86d --- /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 7951080c7..323c89531 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 0fc9794ab..33be8cf50 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 7784cbcaf..269158546 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 000000000..6e4eaea12 --- /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 4fc37e5e2..a8fbac192 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 59be67b38..1bde1db78 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 c38024983..362b51317 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 000000000..4db53a4f1 --- /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 000000000..2ccf0c2a9 --- /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 d1f923a98..9e579405d 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 13a997242..2e0177ce0 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 04068ecf5..3522e9b87 100644 --- a/SharedLibraryCore/SharedLibraryCore.csproj +++ b/SharedLibraryCore/SharedLibraryCore.csproj @@ -36,7 +36,7 @@ - +