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); }