From e77ef69ee8a1ef8f74765a332261b643b0db7732 Mon Sep 17 00:00:00 2001 From: RaidMax Date: Sun, 2 Sep 2018 16:59:27 -0500 Subject: [PATCH] Added additional properties method to allow easier extension to client properties updated VPN plugin to use WebClient message is sent to client trying to execute commands before they are authenticated fixed rare issue with ToAdmins failing record bullet distance fraction for client kills (_customcallbacks) change client level/permissions through webfront ability to tempban through webfront --- Application/BuildScripts/PostBuild.bat | 6 +- Application/EventParsers/IW4EventParser.cs | 26 +- Application/GameEventHandler.cs | 86 ++- Application/Main.cs | 2 + Application/Manager.cs | 13 +- Application/Server.cs | 44 +- Plugins/ScriptPlugins/VPNDetection.js | 16 +- Plugins/Stats/Helpers/StatManager.cs | 28 +- Plugins/Stats/Models/EFClientKill.cs | 2 + Plugins/Stats/Plugin.cs | 12 +- .../Stats/Web/Controllers/StatsController.cs | 2 +- Plugins/Tests/ClientTests.cs | 29 + SharedLibraryCore/Database/DatabaseContext.cs | 13 +- SharedLibraryCore/Dtos/PlayerInfo.cs | 1 + ...902035612_AddFractionAndIsKill.Designer.cs | 665 ++++++++++++++++++ .../20180902035612_AddFractionAndIsKill.cs | 59 ++ .../DatabaseContextModelSnapshot.cs | 37 +- SharedLibraryCore/Objects/Player.cs | 46 +- SharedLibraryCore/ScriptPlugin.cs | 2 +- SharedLibraryCore/Server.cs | 8 +- SharedLibraryCore/Services/PenaltyService.cs | 8 +- SharedLibraryCore/SharedLibraryCore.csproj | 1 + WebfrontCore/Controllers/ActionController.cs | 78 +- WebfrontCore/Controllers/ClientController.cs | 4 + WebfrontCore/ViewModels/InputInfo.cs | 2 + WebfrontCore/Views/Action/_ActionForm.cshtml | 40 +- .../Views/Client/Profile/Index.cshtml | 12 +- WebfrontCore/Views/Console/_Response.cshtml | 3 +- WebfrontCore/wwwroot/css/bootstrap-custom.css | 5 +- .../wwwroot/css/bootstrap-custom.scss | 7 +- WebfrontCore/wwwroot/js/action.js | 4 +- WebfrontCore/wwwroot/js/console.js | 2 +- _customcallbacks.gsc | 13 +- 33 files changed, 1099 insertions(+), 177 deletions(-) create mode 100644 Plugins/Tests/ClientTests.cs create mode 100644 SharedLibraryCore/Migrations/20180902035612_AddFractionAndIsKill.Designer.cs create mode 100644 SharedLibraryCore/Migrations/20180902035612_AddFractionAndIsKill.cs diff --git a/Application/BuildScripts/PostBuild.bat b/Application/BuildScripts/PostBuild.bat index 4494c6ceb..9f2d44d89 100644 --- a/Application/BuildScripts/PostBuild.bat +++ b/Application/BuildScripts/PostBuild.bat @@ -17,4 +17,8 @@ xcopy /y "%SolutionDir%Build\Plugins" "%TargetDir%Plugins\" echo Copying plugins for publish del %SolutionDir%BUILD\Plugins\Tests.dll xcopy /Y "%SolutionDir%BUILD\Plugins" "%SolutionDir%Publish\Windows\Plugins\" -xcopy /Y "%SolutionDir%BUILD\Plugins" "%SolutionDir%Publish\WindowsPrerelease\Plugins\" \ No newline at end of file +xcopy /Y "%SolutionDir%BUILD\Plugins" "%SolutionDir%Publish\WindowsPrerelease\Plugins\" + +echo Copying script plugins for publish +xcopy /Y "%SolutionDir%Plugins\ScriptPlugins" "%SolutionDir%Publish\Windows\Plugins\" +xcopy /Y "%SolutionDir%Plugins\ScriptPlugins" "%SolutionDir%Publish\WindowsPrerelease\Plugins\" \ No newline at end of file diff --git a/Application/EventParsers/IW4EventParser.cs b/Application/EventParsers/IW4EventParser.cs index ebd5d93bd..0e320b6b9 100644 --- a/Application/EventParsers/IW4EventParser.cs +++ b/Application/EventParsers/IW4EventParser.cs @@ -78,6 +78,7 @@ namespace IW4MAdmin.Application.EventParsers Type = GameEvent.EventType.Kill, Data = logLine, Origin = origin, + Target = target, Owner = server }; } @@ -116,19 +117,22 @@ namespace IW4MAdmin.Application.EventParsers // damage if (eventType == "D") { - if (Regex.Match(eventType, @"^(D);((?:bot[0-9]+)|(?:[A-Z]|[0-9])+);([0-9]+);(axis|allies);(.+);((?:[A-Z]|[0-9])+);([0-9]+);(axis|allies);(.+);((?:[0-9]+|[a-z]+|_)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$").Success) + if (!server.CustomCallback) { - var origin = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[5].ConvertLong()); - var target = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[1].ConvertLong()); - - return new GameEvent() + if (Regex.Match(eventType, @"^(D);((?:bot[0-9]+)|(?:[A-Z]|[0-9])+);([0-9]+);(axis|allies);(.+);((?:[A-Z]|[0-9])+);([0-9]+);(axis|allies);(.+);((?:[0-9]+|[a-z]+|_)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$").Success) { - Type = GameEvent.EventType.Damage, - Data = eventType, - Origin = origin, - Target = target, - Owner = server - }; + var origin = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[5].ConvertLong()); + var target = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[1].ConvertLong()); + + return new GameEvent() + { + Type = GameEvent.EventType.Damage, + Data = eventType, + Origin = origin, + Target = target, + Owner = server + }; + } } } diff --git a/Application/GameEventHandler.cs b/Application/GameEventHandler.cs index 3d60d5fb0..8c3b2d7db 100644 --- a/Application/GameEventHandler.cs +++ b/Application/GameEventHandler.cs @@ -27,56 +27,54 @@ namespace IW4MAdmin.Application #if DEBUG Manager.GetLogger().WriteDebug($"Got new event of type {gameEvent.Type} for {gameEvent.Owner} with id {gameEvent.Id}"); #endif - GameEvent sortedEvent = null; - lock (OutOfOrderEvents) - { - sortedEvent = OutOfOrderEvents.Values.FirstOrDefault(); - } +// GameEvent sortedEvent = null; +// lock (OutOfOrderEvents) +// { +// sortedEvent = OutOfOrderEvents.Values.FirstOrDefault(); - while (sortedEvent?.Id == Interlocked.Read(ref NextEventId)) - { - lock (OutOfOrderEvents) - { - OutOfOrderEvents.RemoveAt(0); - } - AddEvent(sortedEvent); +// while (sortedEvent?.Id == Interlocked.Read(ref NextEventId)) +// { +// if (OutOfOrderEvents.Count > 0) +// { +// OutOfOrderEvents.RemoveAt(0); +// } - lock (OutOfOrderEvents) - { - sortedEvent = OutOfOrderEvents.Values.FirstOrDefault(); - } - } +// AddEvent(sortedEvent); +// sortedEvent = OutOfOrderEvents.Values.FirstOrDefault(); +// } +// } - // both the gameEvent Id and the LastEventId are thread safe because we want to synchronize when the - // event occurs - if (gameEvent.Id == Interlocked.Read(ref NextEventId)) - { -#if DEBUG == true - Manager.GetLogger().WriteDebug($"sent event with id {gameEvent.Id} to be processed"); -#endif +// // both the gameEvent Id and the LastEventId are thread safe because we want to synchronize when the +// // event occurs +// if (gameEvent.Id == Interlocked.Read(ref NextEventId)) +// { +//#if DEBUG == true +// Manager.GetLogger().WriteDebug($"sent event with id {gameEvent.Id} to be processed"); +//#endif ((Manager as ApplicationManager).OnServerEvent)(this, new GameEventArgs(null, false, gameEvent)); - Interlocked.Increment(ref NextEventId); - } + return true; +// Interlocked.Increment(ref NextEventId); +// } - // a "newer" event has been added before and "older" one has been added (due to threads and context switching) - // so me must wait until the next expected one arrives - else - { -#if DEBUG == true - Manager.GetLogger().WriteWarning("Incoming event is out of order"); - Manager.GetLogger().WriteDebug($"Expected event Id is {Interlocked.Read(ref NextEventId)}, but got {gameEvent.Id} instead"); -#endif +// // a "newer" event has been added before and "older" one has been added (due to threads and context switching) +// // so me must wait until the next expected one arrives +// else +// { +//#if DEBUG == true +// Manager.GetLogger().WriteWarning("Incoming event is out of order"); +// Manager.GetLogger().WriteDebug($"Expected event Id is {Interlocked.Read(ref NextEventId)}, but got {gameEvent.Id} instead"); +//#endif - // this prevents multiple threads from adding simultaneously - lock (OutOfOrderEvents) - { - if (!OutOfOrderEvents.TryGetValue(gameEvent.Id, out GameEvent discard)) - { - OutOfOrderEvents.Add(gameEvent.Id, gameEvent); - } - } - } - return true; +// // this prevents multiple threads from adding simultaneously +// lock (OutOfOrderEvents) +// { +// if (!OutOfOrderEvents.TryGetValue(gameEvent.Id, out GameEvent discard)) +// { +// OutOfOrderEvents.Add(gameEvent.Id, gameEvent); +// } +// } +// } +// return true; } } } diff --git a/Application/Main.cs b/Application/Main.cs index 9b6fba07c..d9354d05d 100644 --- a/Application/Main.cs +++ b/Application/Main.cs @@ -50,6 +50,8 @@ namespace IW4MAdmin.Application Localization.Configure.Initialize(ServerManager.GetApplicationSettings().Configuration()?.CustomLocale); loc = Utilities.CurrentLocalization.LocalizationIndex; + ServerManager.Logger.WriteInfo($"Version is {Version}"); + var api = API.Master.Endpoint.Get(); var version = new API.Master.VersionInfo() diff --git a/Application/Manager.cs b/Application/Manager.cs index 586c93d3a..a4b1eb7bd 100644 --- a/Application/Manager.cs +++ b/Application/Manager.cs @@ -37,7 +37,7 @@ namespace IW4MAdmin.Application // define what the delagate function looks like public delegate void OnServerEventEventHandler(object sender, GameEventArgs e); // expose the event handler so we can execute the events - public OnServerEventEventHandler OnServerEvent { get; set; } + public OnServerEventEventHandler OnServerEvent { get; set; } public DateTime StartTime { get; private set; } static ApplicationManager Instance; @@ -81,6 +81,11 @@ namespace IW4MAdmin.Application if (GameEvent.ShouldOriginEventBeDelayed(newEvent)) { Logger.WriteDebug($"Delaying origin execution of event type {newEvent.Type} for {newEvent.Origin} because they are not authed"); + if (newEvent.Type == GameEvent.EventType.Command) + { + await newEvent.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["SERVER_DELAYED_EVENT_WAIT"]); + } + // offload it to the player to keep newEvent.Origin.DelayedEvents.Enqueue(newEvent); return; @@ -95,7 +100,7 @@ namespace IW4MAdmin.Application return; } - + //// todo: this is a hacky mess if (newEvent.Origin?.DelayedEvents.Count > 0 && @@ -104,7 +109,7 @@ namespace IW4MAdmin.Application var events = newEvent.Origin.DelayedEvents; // add the delayed event to the queue - while(events.Count > 0) + while (events.Count > 0) { var oldEvent = events.Dequeue(); @@ -143,7 +148,7 @@ namespace IW4MAdmin.Application await newEvent.Owner.ExecuteEvent(newEvent); #if DEBUG - Logger.WriteDebug($"Processed event with id {newEvent.Id}"); + Logger.WriteDebug($"Processed event with id {newEvent.Id}"); #endif } diff --git a/Application/Server.cs b/Application/Server.cs index a76c99141..6cef99202 100644 --- a/Application/Server.cs +++ b/Application/Server.cs @@ -19,7 +19,6 @@ using SharedLibraryCore.Localization; using IW4MAdmin.Application.RconParsers; using IW4MAdmin.Application.EventParsers; using IW4MAdmin.Application.IO; -using IW4MAdmin.Application.Core; namespace IW4MAdmin { @@ -300,40 +299,6 @@ namespace IW4MAdmin } } - //// this allows us to catch exceptions but still run it parallel - //async Task pluginHandlingAsync(Task onEvent, string pluginName) - //{ - // try - // { - // await onEvent; - // } - - // // this happens if a plugin (login) wants to stop commands from executing - // catch (AuthorizationException e) - // { - // await E.Origin.Tell($"{loc["COMMAND_NOTAUTHORIZED"]} - {e.Message}"); - // canExecuteCommand = false; - // } - - // catch (Exception Except) - // { - // Logger.WriteError($"{loc["SERVER_PLUGIN_ERROR"]} [{pluginName}]"); - // Logger.WriteDebug(String.Format("Error Message: {0}", Except.Message)); - // Logger.WriteDebug(String.Format("Error Trace: {0}", Except.StackTrace)); - // while (Except.InnerException != null) - // { - // Except = Except.InnerException; - // Logger.WriteDebug($"Inner exception: {Except.Message}"); - // } - // } - //} - - //var pluginTasks = SharedLibraryCore.Plugins.PluginImporter.ActivePlugins. - // Select(p => pluginHandlingAsync(p.OnEventAsync(E, this), p.Name)); - - //// execute all the plugin updates simultaneously - //await Task.WhenAll(pluginTasks); - foreach (var plugin in SharedLibraryCore.Plugins.PluginImporter.ActivePlugins) { try @@ -629,6 +594,13 @@ namespace IW4MAdmin // this are our new connecting clients foreach (var client in polledClients[0]) { + // this prevents duplicate events from being sent to the event api + if (GetPlayersAsList().Count(c => c.NetworkId == client.NetworkId && + c.State == Player.ClientState.Connected) != 0) + { + continue; + } + var e = new GameEvent() { Type = GameEvent.EventType.Connect, @@ -810,7 +782,7 @@ namespace IW4MAdmin logfile = await this.GetDvarAsync("g_log"); } - CustomCallback = await ScriptLoaded(); + //CustomCallback = await ScriptLoaded(); string mainPath = EventParser.GetGameDir(); #if DEBUG basepath.Value = @"D:\"; diff --git a/Plugins/ScriptPlugins/VPNDetection.js b/Plugins/ScriptPlugins/VPNDetection.js index 5d9af92e9..9ae807902 100644 --- a/Plugins/ScriptPlugins/VPNDetection.js +++ b/Plugins/ScriptPlugins/VPNDetection.js @@ -24,20 +24,20 @@ const plugin = { let usingVPN = false; try { - let httpRequest = System.Net.WebRequest.Create('https://api.xdefcon.com/proxy/check/?ip=' + origin.IPAddressString); - let response = httpRequest.GetResponse(); - let data = response.GetResponseStream(); - let streamReader = new System.IO.StreamReader(data); - let jsonResponse = streamReader.ReadToEnd(); - streamReader.Dispose(); - response.Close(); - let parsedJSON = JSON.parse(jsonResponse); + let cl = new System.Net.Http.HttpClient(); + let re = cl.GetAsync('https://api.xdefcon.com/proxy/check/?ip=' + origin.IPAddressString).Result; + let co = re.Content; + let parsedJSON = JSON.parse(co.ReadAsStringAsync().Result); + //co.Dispose(); + //re.Dispose(); + //cl.Dispose(); usingVPN = parsedJSON['success'] && parsedJSON['proxy']; } catch (e) { this.logger.WriteError(e.message); } if (usingVPN) { + this.logger.WriteInfo(origin + ' is using a VPN (' + origin.IPAddressString + ')'); let library = importNamespace('SharedLibraryCore'); let kickOrigin = new library.Objects.Player(); kickOrigin.ClientId = 1; diff --git a/Plugins/Stats/Helpers/StatManager.cs b/Plugins/Stats/Helpers/StatManager.cs index 6eeec0bca..d16f8e2cb 100644 --- a/Plugins/Stats/Helpers/StatManager.cs +++ b/Plugins/Stats/Helpers/StatManager.cs @@ -324,7 +324,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers /// /// public async Task AddScriptHit(bool isDamage, DateTime time, Player attacker, Player victim, int serverId, string map, string hitLoc, string type, - string damage, string weapon, string killOrigin, string deathOrigin, string viewAngles, string offset, string isKillstreakKill, string Ads, string snapAngles) + string damage, string weapon, string killOrigin, string deathOrigin, string viewAngles, string offset, string isKillstreakKill, string Ads, string fraction, string snapAngles) { var statsSvc = ContextThreads[serverId]; Vector3 vDeathOrigin = null; @@ -362,7 +362,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers return; } - var kill = new EFClientKill() + var hit = new EFClientKill() { Active = true, AttackerId = attacker.ClientId, @@ -380,11 +380,13 @@ namespace IW4MAdmin.Plugins.Stats.Helpers When = time, IsKillstreakKill = isKillstreakKill[0] != '0', AdsPercent = float.Parse(Ads), + Fraction = double.Parse(fraction), + IsKill = !isDamage, AnglesList = snapshotAngles }; - if (kill.DeathType == IW4Info.MeansOfDeath.MOD_SUICIDE && - kill.Damage == 100000) + if (hit.DeathType == IW4Info.MeansOfDeath.MOD_SUICIDE && + hit.Damage == 100000) { // suicide by switching teams so let's not count it against them return; @@ -395,7 +397,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers await AddStandardKill(attacker, victim); } - if (kill.IsKillstreakKill) + if (hit.IsKillstreakKill) { return; } @@ -406,11 +408,11 @@ namespace IW4MAdmin.Plugins.Stats.Helpers clientStatsSvc.Update(clientStats); // increment their hit count - if (kill.DeathType == IW4Info.MeansOfDeath.MOD_PISTOL_BULLET || - kill.DeathType == IW4Info.MeansOfDeath.MOD_RIFLE_BULLET || - kill.DeathType == IW4Info.MeansOfDeath.MOD_HEAD_SHOT) + if (hit.DeathType == IW4Info.MeansOfDeath.MOD_PISTOL_BULLET || + hit.DeathType == IW4Info.MeansOfDeath.MOD_RIFLE_BULLET || + hit.DeathType == IW4Info.MeansOfDeath.MOD_HEAD_SHOT) { - clientStats.HitLocations.Single(hl => hl.Location == kill.HitLoc).HitCount += 1; + clientStats.HitLocations.Single(hl => hl.Location == hit.HitLoc).HitCount += 1; } if (Plugin.Config.Configuration().EnableAntiCheat) @@ -478,11 +480,17 @@ namespace IW4MAdmin.Plugins.Stats.Helpers } } - await executePenalty(clientDetection.ProcessKill(kill, isDamage)); + await executePenalty(clientDetection.ProcessKill(hit, isDamage)); await executePenalty(clientDetection.ProcessTotalRatio(clientStats)); await clientStatsSvc.SaveChangesAsync(); } + + using (var ctx = new DatabaseContext()) + { + ctx.Set().Add(hit); + await ctx.SaveChangesAsync(); + } } public async Task AddStandardKill(Player attacker, Player victim) diff --git a/Plugins/Stats/Models/EFClientKill.cs b/Plugins/Stats/Models/EFClientKill.cs index 688c73b47..14e4cb114 100644 --- a/Plugins/Stats/Models/EFClientKill.cs +++ b/Plugins/Stats/Models/EFClientKill.cs @@ -29,6 +29,8 @@ namespace IW4MAdmin.Plugins.Stats.Models public Vector3 DeathOrigin { get; set; } public Vector3 ViewAngles { get; set; } public DateTime When { get; set; } + public double Fraction { get; set; } + public bool IsKill { get; set; } // http://wiki.modsrepository.com/index.php?title=Call_of_Duty_5:_Gameplay_standards for conversion to meters [NotMapped] public double Distance => Vector3.Distance(KillOrigin, DeathOrigin) * 0.0254; diff --git a/Plugins/Stats/Plugin.cs b/Plugins/Stats/Plugin.cs index 328a19e62..c3fe6d9c5 100644 --- a/Plugins/Stats/Plugin.cs +++ b/Plugins/Stats/Plugin.cs @@ -73,10 +73,10 @@ namespace IW4MAdmin.Plugins.Stats break; case GameEvent.EventType.ScriptKill: string[] killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0]; - if (killInfo.Length >= 13) + if (killInfo.Length >= 14) { await Manager.AddScriptHit(false, E.Time, E.Origin, E.Target, S.GetHashCode(), S.CurrentMap.Name, killInfo[7], killInfo[8], - killInfo[5], killInfo[6], killInfo[3], killInfo[4], killInfo[9], killInfo[10], killInfo[11], killInfo[12], killInfo[13]); + killInfo[5], killInfo[6], killInfo[3], killInfo[4], killInfo[9], killInfo[10], killInfo[11], killInfo[12], killInfo[13], killInfo[14]); } break; case GameEvent.EventType.Kill: @@ -84,15 +84,15 @@ namespace IW4MAdmin.Plugins.Stats await Manager.AddStandardKill(E.Origin, E.Target); break; case GameEvent.EventType.Damage: - // if (!E.Owner.CustomCallback) - Manager.AddDamageEvent(E.Data, E.Origin.ClientId, E.Target.ClientId, E.Owner.GetHashCode()); + if (!E.Owner.CustomCallback) + Manager.AddDamageEvent(E.Data, E.Origin.ClientId, E.Target.ClientId, E.Owner.GetHashCode()); break; case GameEvent.EventType.ScriptDamage: killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0]; - if (killInfo.Length >= 13) + if (killInfo.Length >= 14) { await Manager.AddScriptHit(true, E.Time, E.Origin, E.Target, S.GetHashCode(), S.CurrentMap.Name, killInfo[7], killInfo[8], - killInfo[5], killInfo[6], killInfo[3], killInfo[4], killInfo[9], killInfo[10], killInfo[11], killInfo[12], killInfo[13]); + killInfo[5], killInfo[6], killInfo[3], killInfo[4], killInfo[9], killInfo[10], killInfo[11], killInfo[12], killInfo[13], killInfo[14]); } break; } diff --git a/Plugins/Stats/Web/Controllers/StatsController.cs b/Plugins/Stats/Web/Controllers/StatsController.cs index aea491eca..ca80bcd2b 100644 --- a/Plugins/Stats/Web/Controllers/StatsController.cs +++ b/Plugins/Stats/Web/Controllers/StatsController.cs @@ -59,7 +59,7 @@ namespace IW4MAdmin.Plugins.Stats.Web.Controllers } [HttpGet] - [Authorize] + // [Authorize] public async Task GetAutomatedPenaltyInfoAsync(int clientId) { using (var ctx = new SharedLibraryCore.Database.DatabaseContext()) diff --git a/Plugins/Tests/ClientTests.cs b/Plugins/Tests/ClientTests.cs new file mode 100644 index 000000000..83960cf58 --- /dev/null +++ b/Plugins/Tests/ClientTests.cs @@ -0,0 +1,29 @@ +using SharedLibraryCore.Objects; +using System; +using System.Collections.Generic; +using System.Text; +using Xunit; + +namespace Tests +{ + public class ClientTests + { + [Fact] + public void SetAdditionalPropertyShouldSucceed() + { + var client = new Player(); + int newProp = 5; + client.SetAdditionalProperty("NewProp", newProp); + } + + [Fact] + public void GetAdditionalPropertyShouldSucceed() + { + var client = new Player(); + int newProp = 5; + client.SetAdditionalProperty("NewProp", newProp); + + Assert.True(client.GetAdditionalProperty("NewProp") == 5, "added property does not match retrieved property"); + } + } +} diff --git a/SharedLibraryCore/Database/DatabaseContext.cs b/SharedLibraryCore/Database/DatabaseContext.cs index 50c3d461b..af9885b97 100644 --- a/SharedLibraryCore/Database/DatabaseContext.cs +++ b/SharedLibraryCore/Database/DatabaseContext.cs @@ -43,7 +43,6 @@ namespace SharedLibraryCore.Database currentPath = !RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? $"{Path.DirectorySeparatorChar}{currentPath}" : currentPath; - // todo: fix later var connectionStringBuilder = new SqliteConnectionStringBuilder { DataSource = $"{currentPath}{Path.DirectorySeparatorChar}Database.db".Substring(6) }; var connectionString = connectionStringBuilder.ToString(); @@ -100,10 +99,6 @@ namespace SharedLibraryCore.Database // adapted from // https://aleemkhan.wordpress.com/2013/02/28/dynamically-adding-dbset-properties-in-dbcontext-for-entity-framework-code-first/ -//#if DEBUG == TRUE -// // foreach (string dllPath in Directory.GetFiles($"{Utilities.OperatingDirectory}Plugins")) -//#else -//todo: fix the debug thingie for entity scanning IEnumerable directoryFiles; string pluginDir = $@"{Environment.CurrentDirectory}{Path.DirectorySeparatorChar}bin{Path.DirectorySeparatorChar}Debug{Path.DirectorySeparatorChar}netcoreapp2.0{Path.DirectorySeparatorChar}Plugins"; @@ -119,9 +114,11 @@ namespace SharedLibraryCore.Database } directoryFiles = Directory.GetFiles(pluginDir).Where(f => f.Contains(".dll")); - - foreach (string dllPath in directoryFiles) -//#endif +#if DEBUG == TRUE + foreach (string dllPath in Directory.GetFiles(@"C:\Projects\IW4M-Admin\Application\bin\Debug\netcoreapp2.1\Plugins")) +#else + foreach (string dllPath in directoryFiles) +#endif { Assembly library; try diff --git a/SharedLibraryCore/Dtos/PlayerInfo.cs b/SharedLibraryCore/Dtos/PlayerInfo.cs index 8ecdcb1df..dad64350b 100644 --- a/SharedLibraryCore/Dtos/PlayerInfo.cs +++ b/SharedLibraryCore/Dtos/PlayerInfo.cs @@ -16,6 +16,7 @@ namespace SharedLibraryCore.Dtos public long NetworkId { get; set; } public List Aliases { get; set; } public List IPs { get; set; } + public bool HasActivePenalty { get; set; } public int ConnectionCount { get; set; } public string LastSeen { get; set; } public string FirstSeen { get; set; } diff --git a/SharedLibraryCore/Migrations/20180902035612_AddFractionAndIsKill.Designer.cs b/SharedLibraryCore/Migrations/20180902035612_AddFractionAndIsKill.Designer.cs new file mode 100644 index 000000000..7ea59d814 --- /dev/null +++ b/SharedLibraryCore/Migrations/20180902035612_AddFractionAndIsKill.Designer.cs @@ -0,0 +1,665 @@ +// +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("20180902035612_AddFractionAndIsKill")] + partial class AddFractionAndIsKill + { + 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("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.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.HasKey("RatingId"); + + b.HasIndex("RatingHistoryId"); + + b.HasIndex("ServerId"); + + 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(); + + b.HasKey("AliasId"); + + b.HasIndex("IPAddress"); + + b.HasIndex("LinkId"); + + 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("OriginEntityId"); + + 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/20180902035612_AddFractionAndIsKill.cs b/SharedLibraryCore/Migrations/20180902035612_AddFractionAndIsKill.cs new file mode 100644 index 000000000..0ed888e5c --- /dev/null +++ b/SharedLibraryCore/Migrations/20180902035612_AddFractionAndIsKill.cs @@ -0,0 +1,59 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace SharedLibraryCore.Migrations +{ + public partial class AddFractionAndIsKill : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Fraction", + table: "EFClientKills", + nullable: false, + defaultValue: 0.0); + + migrationBuilder.AddColumn( + name: "IsKill", + table: "EFClientKills", + nullable: false, + defaultValue: false); + + migrationBuilder.CreateTable( + name: "EFChangeHistory", + columns: table => new + { + Active = table.Column(nullable: false), + ChangeHistoryId = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + OriginEntityId = table.Column(nullable: false), + TargetEntityId = table.Column(nullable: false), + TypeOfChange = table.Column(nullable: false), + TimeChanged = table.Column(nullable: false), + Comment = table.Column(maxLength: 128, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_EFChangeHistory", x => x.ChangeHistoryId); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "EFChangeHistory"); + + migrationBuilder.DropIndex( + name: "IX_Vector3_EFACSnapshotSnapshotId", + table: "Vector3"); + + migrationBuilder.DropColumn( + name: "Fraction", + table: "EFClientKills"); + + migrationBuilder.DropColumn( + name: "IsKill", + table: "EFClientKills"); + } + } +} diff --git a/SharedLibraryCore/Migrations/DatabaseContextModelSnapshot.cs b/SharedLibraryCore/Migrations/DatabaseContextModelSnapshot.cs index 5110c610a..718924cfd 100644 --- a/SharedLibraryCore/Migrations/DatabaseContextModelSnapshot.cs +++ b/SharedLibraryCore/Migrations/DatabaseContextModelSnapshot.cs @@ -1,13 +1,9 @@ // +using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage; -using Microsoft.EntityFrameworkCore.Storage.Internal; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using SharedLibraryCore.Database; -using SharedLibraryCore.Objects; -using System; namespace SharedLibraryCore.Migrations { @@ -18,7 +14,7 @@ namespace SharedLibraryCore.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "2.0.2-rtm-10011"); + .HasAnnotation("ProductVersion", "2.1.2-rtm-30932"); modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", b => { @@ -99,8 +95,12 @@ namespace SharedLibraryCore.Migrations b.Property("DeathType"); + b.Property("Fraction"); + b.Property("HitLoc"); + b.Property("IsKill"); + b.Property("KillOriginVector3Id"); b.Property("Map"); @@ -331,6 +331,29 @@ namespace SharedLibraryCore.Migrations b.ToTable("EFAliasLinks"); }); + modelBuilder.Entity("SharedLibraryCore.Database.Models.EFChangeHistory", b => + { + b.Property("ChangeHistoryId") + .ValueGeneratedOnAdd(); + + b.Property("Active"); + + b.Property("Comment") + .HasMaxLength(128); + + b.Property("OriginEntityId"); + + 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") diff --git a/SharedLibraryCore/Objects/Player.cs b/SharedLibraryCore/Objects/Player.cs index 39d8f9b98..1aa64087d 100644 --- a/SharedLibraryCore/Objects/Player.cs +++ b/SharedLibraryCore/Objects/Player.cs @@ -34,15 +34,45 @@ namespace SharedLibraryCore.Objects public enum Permission { + /// + /// client has been banned + /// Banned = -1, + /// + /// default client state upon first connect + /// User = 0, + /// + /// client has been flagged + /// Flagged = 1, + /// + /// client is trusted + /// Trusted = 2, + /// + /// client is a moderator + /// Moderator = 3, + /// + /// client is an administrator + /// Administrator = 4, + /// + /// client is a senior administrator + /// SeniorAdmin = 5, + /// + /// client is a owner + /// Owner = 6, + /// + /// not used + /// Creator = 7, + /// + /// reserved for default account + /// Console = 8 } @@ -51,12 +81,10 @@ namespace SharedLibraryCore.Objects ConnectionTime = DateTime.UtcNow; ClientNumber = -1; DelayedEvents = new Queue(); + _additionalProperties = new Dictionary(); } - public override string ToString() - { - return $"{Name}::{NetworkId}"; - } + public override string ToString() => $"{Name}::{NetworkId}"; public String GetLastConnection() { @@ -105,7 +133,10 @@ namespace SharedLibraryCore.Objects { await CurrentServer.Ban(Message, this, Sender); } - + [NotMapped] + Dictionary _additionalProperties; + public T GetAdditionalProperty(string name) => (T)_additionalProperties[name]; + public void SetAdditionalProperty(string name, object value) => _additionalProperties.Add(name, value); [NotMapped] public int ClientNumber { get; set; } [NotMapped] @@ -150,9 +181,6 @@ namespace SharedLibraryCore.Objects return ((Player)obj).NetworkId == NetworkId; } - public override int GetHashCode() - { - return NetworkId.GetHashCode(); - } + public override int GetHashCode() => (int)NetworkId; } } diff --git a/SharedLibraryCore/ScriptPlugin.cs b/SharedLibraryCore/ScriptPlugin.cs index ec4ca12f0..7bed5aee7 100644 --- a/SharedLibraryCore/ScriptPlugin.cs +++ b/SharedLibraryCore/ScriptPlugin.cs @@ -61,7 +61,7 @@ namespace SharedLibraryCore ScriptEngine = new Jint.Engine(cfg => cfg.AllowClr(new[] { - typeof(System.Net.WebRequest).Assembly, + typeof(System.Net.Http.HttpClient).Assembly, typeof(Objects.Player).Assembly, }) .CatchClrExceptions()); diff --git a/SharedLibraryCore/Server.cs b/SharedLibraryCore/Server.cs index 1ae5a2f6d..e02c4d066 100644 --- a/SharedLibraryCore/Server.cs +++ b/SharedLibraryCore/Server.cs @@ -179,13 +179,9 @@ namespace SharedLibraryCore /// Message to send out public async Task ToAdmins(String message) { - foreach (Player P in Players) + foreach (var client in GetPlayersAsList().Where(c => c.Level > Player.Permission.Flagged)) { - if (P == null) - continue; - - if (P.Level > Player.Permission.Flagged) - await P.Tell(message); + await client.Tell(message); } } diff --git a/SharedLibraryCore/Services/PenaltyService.cs b/SharedLibraryCore/Services/PenaltyService.cs index 55caf8fbd..13a997242 100644 --- a/SharedLibraryCore/Services/PenaltyService.cs +++ b/SharedLibraryCore/Services/PenaltyService.cs @@ -181,7 +181,7 @@ namespace SharedLibraryCore.Services var pi = ((PenaltyInfo)p.Value); if (pi.TimeRemaining.Length > 0) pi.TimeRemaining = (DateTime.Parse(((PenaltyInfo)p.Value).TimeRemaining) - now).TimeSpanText(); - + }); return list; } @@ -235,6 +235,8 @@ namespace SharedLibraryCore.Services public async Task> GetActivePenaltiesAsync(int aliasId, int ip = 0) { + var now = DateTime.UtcNow; + using (var context = new DatabaseContext()) { var iqPenalties = await (from link in context.AliasLinks @@ -242,6 +244,8 @@ namespace SharedLibraryCore.Services 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) { @@ -250,6 +254,8 @@ namespace SharedLibraryCore.Services 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; diff --git a/SharedLibraryCore/SharedLibraryCore.csproj b/SharedLibraryCore/SharedLibraryCore.csproj index c9da3cf31..539fcfad7 100644 --- a/SharedLibraryCore/SharedLibraryCore.csproj +++ b/SharedLibraryCore/SharedLibraryCore.csproj @@ -20,6 +20,7 @@ + diff --git a/WebfrontCore/Controllers/ActionController.cs b/WebfrontCore/Controllers/ActionController.cs index 3fe3438f7..9f7d559a7 100644 --- a/WebfrontCore/Controllers/ActionController.cs +++ b/WebfrontCore/Controllers/ActionController.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using SharedLibraryCore; using WebfrontCore.ViewModels; +using static SharedLibraryCore.Objects.Player; namespace WebfrontCore.Controllers { @@ -22,6 +23,20 @@ namespace WebfrontCore.Controllers { Name = "Reason", Label = Localization["WEBFRONT_ACTION_LABEL_REASON"], + }, + new InputInfo() + { + Name ="Duration", + Label=Localization["WEBFRONT_ACTION_LABEL_DURATION"], + Type="select", + Values = new Dictionary() + { + {"1", $"1 {Localization["GLOBAL_TIME_HOUR"]}" }, + {"2", $"6 {Localization["GLOBAL_TIME_HOURS"]}" }, + {"3", $"1 {Localization["GLOBAL_TIME_DAY"]}" }, + {"4", $"1 {Localization["GLOBAL_TIME_WEEK"]}" }, + {"5", $"{Localization["WEBFRONT_ACTION_SELECTION_PERMANENT"]}" }, + } } }, Action = "BanAsync" @@ -30,14 +45,36 @@ namespace WebfrontCore.Controllers return View("_ActionForm", info); } - public async Task BanAsync(int targetId, string Reason) + public async Task BanAsync(int targetId, string Reason, int Duration) { + string duration = string.Empty; + + switch (Duration) + { + case 1: + duration = "1h"; + break; + case 2: + duration = "6h"; + break; + case 3: + duration = "1d"; + break; + case 4: + duration = "1w"; + break; + } + + string command = Duration == 5 ? + $"!ban @{targetId} {Reason}" : + $"!tempban @{targetId} {duration} {Reason}"; + var server = Manager.GetServers().First(); return await Task.FromResult(RedirectToAction("ExecuteAsync", "Console", new { serverId = server.GetHashCode(), - command = $"!ban @{targetId} {Reason}" + command })); } @@ -102,5 +139,42 @@ namespace WebfrontCore.Controllers { return await Task.FromResult(RedirectToAction("LoginAsync", "Account", new { clientId, password })); } + + public IActionResult EditForm() + { + var info = new ActionInfo() + { + ActionButtonLabel = Localization["WEBFRONT_ACTION_LABEL_EDIT"], + Name = "Edit", + Inputs = new List() + { + new InputInfo() + { + Name ="level", + Label=Localization["WEBFRONT_PROFILE_LEVEL"], + Type="select", + Values = Enum.GetValues(typeof(Permission)).OfType() + .Where(p => p <= Client.Level) + .Where(p => p != Permission.Banned) + .Where(p => p != Permission.Flagged) + .ToDictionary(p => p.ToString(), p=> p.ToLocalizedLevelName()) + }, + }, + Action = "EditAsync" + }; + + return View("_ActionForm", info); + } + + public async Task EditAsync(int targetId, string level) + { + var server = Manager.GetServers().First(); + + return await Task.FromResult(RedirectToAction("ExecuteAsync", "Console", new + { + serverId = server.GetHashCode(), + command = $"!setlevel @{targetId} {level}" + })); + } } } diff --git a/WebfrontCore/Controllers/ClientController.cs b/WebfrontCore/Controllers/ClientController.cs index ce4cbae29..adeeb32bf 100644 --- a/WebfrontCore/Controllers/ClientController.cs +++ b/WebfrontCore/Controllers/ClientController.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using static SharedLibraryCore.Objects.Penalty; namespace WebfrontCore.Controllers { @@ -20,6 +21,8 @@ namespace WebfrontCore.Controllers return NotFound(); } + var activePenalties = await Manager.GetPenaltyService().GetActivePenaltiesAsync(client.AliasLinkId, client.IPAddress); + #if DEBUG Authorized = true; #endif @@ -48,6 +51,7 @@ namespace WebfrontCore.Controllers .Distinct() .OrderBy(i => i) .ToList(), + HasActivePenalty = activePenalties.Count > 0, Online = Manager.GetActiveClients().FirstOrDefault(c => c.ClientId == client.ClientId) != null, TimeOnline = (DateTime.UtcNow - client.LastConnection).TimeSpanText(), LinkedAccounts = client.LinkedAccounts diff --git a/WebfrontCore/ViewModels/InputInfo.cs b/WebfrontCore/ViewModels/InputInfo.cs index ac2d75f55..41de4d0be 100644 --- a/WebfrontCore/ViewModels/InputInfo.cs +++ b/WebfrontCore/ViewModels/InputInfo.cs @@ -12,5 +12,7 @@ namespace WebfrontCore.ViewModels public string Placeholder { get; set; } public string Type { get; set; } public string Value { get; set; } + public Dictionary Values { get; set; } + public bool Checked { get; set; } } } diff --git a/WebfrontCore/Views/Action/_ActionForm.cshtml b/WebfrontCore/Views/Action/_ActionForm.cshtml index 4bb28b798..5adc037e3 100644 --- a/WebfrontCore/Views/Action/_ActionForm.cshtml +++ b/WebfrontCore/Views/Action/_ActionForm.cshtml @@ -5,19 +5,41 @@
@foreach (var input in Model.Inputs) { -
+
-
- @input.Label -
- @{ - string inputType = input.Type ?? "text"; - string value = input.Value ?? ""; +
+ @input.Label +
+ @{ + string inputType = input.Type ?? "text"; + string value = input.Value ?? ""; - + if (inputType == "select") + { + } -
+ else if (inputType == "checkbox") + { +
+ +
+ } + + else + { + + } + } + +
}
\ No newline at end of file diff --git a/WebfrontCore/Views/Client/Profile/Index.cshtml b/WebfrontCore/Views/Client/Profile/Index.cshtml index 7929a439d..affacc556 100644 --- a/WebfrontCore/Views/Client/Profile/Index.cshtml +++ b/WebfrontCore/Views/Client/Profile/Index.cshtml @@ -33,18 +33,20 @@ {
- - @if (Model.LevelInt < (int)ViewBag.User.Level && -(SharedLibraryCore.Objects.Player.Permission)Model.LevelInt != SharedLibraryCore.Objects.Player.Permission.Banned) + @if (Model.LevelInt != -1) + { + + } + @if (Model.LevelInt < (int)ViewBag.User.Level && !Model.HasActivePenalty) { } - @if (Model.LevelInt < (int)ViewBag.User.Level && -(SharedLibraryCore.Objects.Player.Permission)Model.LevelInt == SharedLibraryCore.Objects.Player.Permission.Banned) + @if (Model.LevelInt < (int)ViewBag.User.Level && Model.HasActivePenalty) { } +
diff --git a/WebfrontCore/Views/Console/_Response.cshtml b/WebfrontCore/Views/Console/_Response.cshtml index cd7c7e649..639a37687 100644 --- a/WebfrontCore/Views/Console/_Response.cshtml +++ b/WebfrontCore/Views/Console/_Response.cshtml @@ -6,6 +6,7 @@ @{ foreach (var response in Model) { - @response.Response
+ @response.Response
} +
} \ No newline at end of file diff --git a/WebfrontCore/wwwroot/css/bootstrap-custom.css b/WebfrontCore/wwwroot/css/bootstrap-custom.css index e5d09d107..3c36c97cd 100644 --- a/WebfrontCore/wwwroot/css/bootstrap-custom.css +++ b/WebfrontCore/wwwroot/css/bootstrap-custom.css @@ -6321,6 +6321,9 @@ a.link-inverse:hover { #console_command_response { min-height: 20rem; } +#console_command_response hr { + border-color: #6c757d; } + #console .form-control, #console button { border-radius: 0; border-color: #007ACC; @@ -6339,7 +6342,7 @@ a.link-inverse:hover { form *, select { border-radius: 0 !important; } -select { +#penalty_filter_selection { border-left: none !important; border-right: none !important; border-bottom: none !important; diff --git a/WebfrontCore/wwwroot/css/bootstrap-custom.scss b/WebfrontCore/wwwroot/css/bootstrap-custom.scss index fcfdebe47..583b04b95 100644 --- a/WebfrontCore/wwwroot/css/bootstrap-custom.scss +++ b/WebfrontCore/wwwroot/css/bootstrap-custom.scss @@ -96,6 +96,10 @@ a.link-inverse:hover { min-height: 20rem; } +#console_command_response hr { + border-color: #6c757d; +} + #console .form-control, #console button { border-radius: 0; border-color: $primary; @@ -119,7 +123,7 @@ form *, select { border-radius: 0 !important; } -select { +#penalty_filter_selection { border-left: none !important; border-right: none !important; border-bottom: none !important; @@ -205,4 +209,3 @@ select { .client-message, .automated-penalty-info-detailed { cursor: pointer; } - diff --git a/WebfrontCore/wwwroot/js/action.js b/WebfrontCore/wwwroot/js/action.js index b7b7205e8..07fde586c 100644 --- a/WebfrontCore/wwwroot/js/action.js +++ b/WebfrontCore/wwwroot/js/action.js @@ -24,7 +24,7 @@ $(document).ready(function () { * hide loader when clicking */ $(document).click(function (e) { - hideLoader() + //hideLoader() }); /* @@ -39,7 +39,7 @@ $(document).ready(function () { $('#actionModal').modal(); }) .fail(function (jqxhr, textStatus, error) { - $('#actionModal .modal-message').text('Error &mdash ' + error); + $('#actionModal .modal-message').text('Error — ' + error); $('#actionModal').modal(); $('#actionModal .modal-message').fadeIn('fast'); }); diff --git a/WebfrontCore/wwwroot/js/console.js b/WebfrontCore/wwwroot/js/console.js index 416751d2f..56f2ad42c 100644 --- a/WebfrontCore/wwwroot/js/console.js +++ b/WebfrontCore/wwwroot/js/console.js @@ -14,7 +14,7 @@ $.get('/Console/ExecuteAsync', { serverId: serverId, command: command }) .done(function (response) { hideLoader(); - $('#console_command_response').html(response); + $('#console_command_response').append(response); $('#console_command_value').val(""); }) .fail(function (jqxhr, textStatus, error) { diff --git a/_customcallbacks.gsc b/_customcallbacks.gsc index 6e4d2fbf1..bda6876af 100644 --- a/_customcallbacks.gsc +++ b/_customcallbacks.gsc @@ -14,6 +14,7 @@ init() level.callbackPlayerDamage = ::Callback_PlayerDamage; } + onPlayerConnect(player) { for(;;) @@ -111,6 +112,11 @@ waitForAdditionalAngles(logString) logPrint(logString + ";" + anglesStr + "\n"); } +vectorScale(vector, scale) +{ + return (vector[0] * scale, vector[1] * scale, vector[2] * scale); +} + Process_Hit(type, attacker, sHitLoc, sMeansOfDeath, iDamage, sWeapon) { victim = self; @@ -123,7 +129,12 @@ Process_Hit(type, attacker, sHitLoc, sMeansOfDeath, iDamage, sWeapon) location = victim GetTagOrigin(hitLocationToBone(sHitLoc)); isKillstreakKill = !isPlayer(attacker) || isKillstreakWeapon(sWeapon); - logLine = "Script" + type + ";" + _attacker.guid + ";" + victim.guid + ";" + _attacker GetTagOrigin("tag_eye") + ";" + location + ";" + iDamage + ";" + sWeapon + ";" + sHitLoc + ";" + sMeansOfDeath + ";" + _attacker getPlayerAngles() + ";" + gettime() + ";" + isKillstreakKill + ";" + _attacker playerADS(); + // do the tracing stuff + start = _attacker getTagOrigin("tag_eye"); + end = location; + trace = bulletTrace(start, end, true, _attacker); + + logLine = "Script" + type + ";" + _attacker.guid + ";" + victim.guid + ";" + _attacker GetTagOrigin("tag_eye") + ";" + location + ";" + iDamage + ";" + sWeapon + ";" + sHitLoc + ";" + sMeansOfDeath + ";" + _attacker getPlayerAngles() + ";" + gettime() + ";" + isKillstreakKill + ";" + _attacker playerADS() + ";" + trace["fraction"]; attacker thread waitForAdditionalAngles(logLine); }