diff --git a/Admin/Server.cs b/Admin/Server.cs index c30ced944..33bf53f0b 100644 --- a/Admin/Server.cs +++ b/Admin/Server.cs @@ -38,7 +38,7 @@ namespace IW4MAdmin try { Player player = null; - var client = (await Manager.GetClientService().GetUnique(polledPlayer.NetworkId)); + var client = await Manager.GetClientService().GetUnique(polledPlayer.NetworkId); // first time client is connecting to server if (client == null) @@ -52,28 +52,21 @@ namespace IW4MAdmin { client.Connections += 1; bool aliasExists = client.AliasLink.Children - .FirstOrDefault(a => a.Name == polledPlayer.Name && a.IP == polledPlayer.IPAddress) != null; + .FirstOrDefault(a => a.Name == polledPlayer.Name && a.IPAddress == polledPlayer.IPAddress) != null; if (!aliasExists) { Logger.WriteDebug($"Client {polledPlayer} has connected previously under a different alias"); - await Manager.GetAliasService().Create(new SharedLibrary.Database.Models.EFAlias() + client.CurrentAlias = new SharedLibrary.Database.Models.EFAlias() { - Active = true, - IP = polledPlayer.IPAddress, + IPAddress = polledPlayer.IPAddress, Name = polledPlayer.Name, - DateAdded = DateTime.UtcNow, - Link = client.AliasLink, - }); + }; + await Manager.GetClientService().Update(client); } - - client.Name = polledPlayer.Name; - client.IPAddress = polledPlayer.IPAddress; player = client.AsPlayer(); } - - /*var Admins = Manager.GetDatabase().GetPrivilegedClients(); if (Admins.Where(x => x.Name == polledPlayer.Name).Count() > 0) { @@ -81,6 +74,10 @@ namespace IW4MAdmin await this.ExecuteCommandAsync("clientkick " + polledPlayer.ClientNumber + " \"Please do not impersonate an admin^7\""); }*/ player.CurrentServer = this; +#if DEBUG + player.ClientNumber = polledPlayer.ClientNumber; + Players[player.ClientNumber] = player; +#endif var ban = (await Manager.GetPenaltyService().Find(p => p.LinkId == player.AliasLink.AliasLinkId && p.Expires > DateTime.UtcNow)).FirstOrDefault(); @@ -92,12 +89,12 @@ namespace IW4MAdmin autoKickClient.CurrentServer = this; if (ban.Type == Penalty.PenaltyType.TempBan) - await this.ExecuteCommandAsync($"clientkick {player.ClientNumber} \"You are temporarily banned. ({(ban.Expires - DateTime.Now).TimeSpanText()} left)\""); + await this.ExecuteCommandAsync($"clientkick {player.ClientNumber} \"You are temporarily banned. ({(ban.Expires - DateTime.UtcNow).TimeSpanText()} left)\""); else - await player.Kick($"previously banned for {ban.Offense}", autoKickClient); + await player.Kick($"Previously banned for {ban.Offense}", autoKickClient); if (player.Level != Player.Permission.Banned && ban.Type == Penalty.PenaltyType.Ban) - await player.Ban($"previously banned for {ban.Offense}", autoKickClient); + await player.Ban($"Previously banned for {ban.Offense}", autoKickClient); return true; } @@ -106,8 +103,7 @@ namespace IW4MAdmin // Do the player specific stuff player.ClientNumber = polledPlayer.ClientNumber; Players[player.ClientNumber] = player; - await Manager.GetClientService().Update(player); - Logger.WriteInfo($"Client {player} connecting..."); // they're clean + Logger.WriteInfo($"Client {player} connecting..."); await ExecuteEvent(new Event(Event.GType.Connect, "", player, null, this)); @@ -128,7 +124,7 @@ namespace IW4MAdmin //Remove player by CLIENT NUMBER override public async Task RemovePlayer(int cNum) { - if (cNum >= 0) + if (cNum >= 0 && Players[cNum] != null) { Player Leaving = Players[cNum]; Leaving.TotalConnectionTime += (int)(DateTime.UtcNow - Leaving.ConnectionTime).TotalSeconds; @@ -217,6 +213,7 @@ namespace IW4MAdmin E.Target = found.AsPlayer(); E.Target.CurrentServer = this as IW4MServer; E.Owner = this as IW4MServer; + E.Data = String.Join(" ", Args.Skip(1)); } } @@ -242,7 +239,7 @@ namespace IW4MAdmin else if (matchingPlayers.Count == 1) { E.Target = matchingPlayers.First(); - E.Data = Regex.Replace(E.Data, $"\"{E.Target.Name}\"", "", RegexOptions.IgnoreCase).Trim(); + E.Data = Regex.Replace(E.Data, $"\"{E.Target.Name}\"", "", RegexOptions.IgnoreCase).Trim(); } } @@ -642,53 +639,114 @@ namespace IW4MAdmin public override async Task Warn(String Reason, Player Target, Player Origin) { - if (Target.Warnings >= 4) - await Target.Kick("Too many warnings!", (await Manager.GetClientService().Get(1)).AsPlayer()); + // ensure player gets warned if command not performed on them in game + if (Target.ClientNumber < 0) + { + Player ingameClient = null; + + ingameClient = Manager.GetServers() + .Select(s => s.GetPlayersAsList()) + .FirstOrDefault(l => l.FirstOrDefault(c => c.ClientId == Target.ClientId) != null) + ?.First(c => c.ClientId == Target.ClientId); + + if (ingameClient != null) + { + await Warn(Reason, ingameClient, Origin); + return; + } + } + else { - Penalty newPenalty = new Penalty() - { - Type = Penalty.PenaltyType.Warning, - Expires = DateTime.UtcNow, - Offender = Target, - Offense = Reason, - Punisher = Origin, - Active = true, - When = DateTime.UtcNow, - Link = Target.AliasLink - }; + if (Target.Warnings >= 4) + await Target.Kick("Too many warnings!", (await Manager.GetClientService().Get(1)).AsPlayer()); - await Manager.GetPenaltyService().Create(newPenalty); Target.Warnings++; - String Message = String.Format("^1WARNING ^7[^3{0}^7]: ^3{1}^7, {2}", Target.Warnings, Target.Name, Reason); - await Broadcast(Message); + await Target.CurrentServer.Broadcast(Message); } + + Penalty newPenalty = new Penalty() + { + Type = Penalty.PenaltyType.Warning, + Expires = DateTime.UtcNow, + Offender = Target, + Offense = Reason, + Punisher = Origin, + Active = true, + When = DateTime.UtcNow, + Link = Target.AliasLink + }; + + await Manager.GetPenaltyService().Create(newPenalty); } public override async Task Kick(String Reason, Player Target, Player Origin) { - if (Target.ClientNumber > -1) + // ensure player gets kicked if command not performed on them in game + if (Target.ClientNumber < 0) { - String Message = "^1Player Kicked: ^5" + Reason; - await Manager.GetPenaltyService().Create(new Penalty() + Player ingameClient = null; + + ingameClient = Manager.GetServers() + .Select(s => s.GetPlayersAsList()) + .FirstOrDefault(l => l.FirstOrDefault(c => c.ClientId == Target.ClientId) != null) + ?.First(c => c.ClientId == Target.ClientId); + + if (ingameClient != null) { - Type = Penalty.PenaltyType.Kick, - Expires = DateTime.UtcNow, - Offender = Target, - Offense = Reason, - Punisher = Origin, - Active = true, - When = DateTime.UtcNow, - Link = Target.AliasLink - }); - await this.ExecuteCommandAsync($"clientkick {Target.ClientNumber} \"{Message}^7\""); + await Kick(Reason, ingameClient, Origin); + return; + } } + else + await Target.CurrentServer.ExecuteCommandAsync($"clientkick {Target.ClientNumber} \"Player Kicked: ^5{Reason}^7\""); + +#if DEBUG + await Target.CurrentServer.RemovePlayer(Target.ClientNumber); +#endif + + var newPenalty = new Penalty() + { + Type = Penalty.PenaltyType.Kick, + Expires = DateTime.UtcNow, + Offender = Target, + Offense = Reason, + Punisher = Origin, + Active = true, + When = DateTime.UtcNow, + Link = Target.AliasLink + }; + + await Manager.GetPenaltyService().Create(newPenalty); } public override async Task TempBan(String Reason, TimeSpan length, Player Target, Player Origin) { - await this.ExecuteCommandAsync($"clientkick {Target.ClientNumber } \"^1Player Temporarily Banned: ^5{ Reason }\""); + // ensure player gets banned if command not performed on them in game + if (Target.ClientNumber < 0) + { + Player ingameClient = null; + + ingameClient = Manager.GetServers() + .Select(s => s.GetPlayersAsList()) + .FirstOrDefault(l => l.FirstOrDefault(c => c.ClientId == Target.ClientId) != null) + ?.First(c => c.ClientId == Target.ClientId); + + if (ingameClient != null) + { + await TempBan(Reason, length, ingameClient, Origin); + return; + } + } + + else + await Target.CurrentServer.ExecuteCommandAsync($"clientkick {Target.ClientNumber } \"^1Player Temporarily Banned: ^5{ Reason }\""); + +#if DEBUG + await Target.CurrentServer.RemovePlayer(Target.ClientNumber); +#endif + Penalty newPenalty = new Penalty() { Type = Penalty.PenaltyType.TempBan, @@ -706,6 +764,33 @@ namespace IW4MAdmin override public async Task Ban(String Message, Player Target, Player Origin) { + // ensure player gets banned if command not performed on them in game + if (Target.ClientNumber < 0) + { + Player ingameClient = null; + + ingameClient = Manager.GetServers() + .Select(s => s.GetPlayersAsList()) + .FirstOrDefault(l => l.FirstOrDefault(c => c.ClientId == Target.ClientId) != null) + ?.First(c => c.ClientId == Target.ClientId); + + if (ingameClient != null) + { + await Ban(Message, ingameClient, Origin); + return; + } + } + + else + { + // this is set only because they're still in the server. + Target.Level = Player.Permission.Banned; + await Target.CurrentServer.ExecuteCommandAsync($"clientkick {Target.ClientNumber} \"Player Banned: ^5{Message} ^7(appeal at {Website}) ^7\""); +#if DEBUG + await Target.CurrentServer.RemovePlayer(Target.ClientNumber); +#endif + } + Penalty newPenalty = new Penalty() { Type = Penalty.PenaltyType.Ban, @@ -717,39 +802,8 @@ namespace IW4MAdmin When = DateTime.UtcNow, Link = Target.AliasLink }; + await Manager.GetPenaltyService().Create(newPenalty); - Target.Level = Player.Permission.Banned; - await Manager.GetClientService().Update(Target); - - lock (Reports) // threading seems to do something weird here - { - List toRemove = new List(); - foreach (Report R in Reports) - { - if (R.Target.NetworkId == Target.NetworkId) - toRemove.Add(R); - } - - foreach (Report R in toRemove) - { - Reports.Remove(R); - Logger.WriteInfo("Removing report for banned GUID - " + R.Origin.NetworkId); - } - } - - // banned from all servers if active - foreach (var server in Manager.GetServers()) - { - if (server.GetPlayersAsList().Count > 0) - { - var activeClient = server.GetPlayersAsList().SingleOrDefault(x => x.NetworkId == Target.NetworkId); - if (activeClient != null) - { - await server.ExecuteCommandAsync($"clientkick {activeClient.ClientNumber} \"Player Banned: ^5{Message} ^7(appeal at {Website}) ^7\""); - break; - } - } - } } override public async Task Unban(Player Target) diff --git a/Admin/WebService.cs b/Admin/WebService.cs index 73d376c09..036c0bfca 100644 --- a/Admin/WebService.cs +++ b/Admin/WebService.cs @@ -227,16 +227,17 @@ namespace IW4MAdmin PlayerHistory = S.PlayerHistory.ToArray() }; - bool authed = (await (ApplicationManager.GetInstance().GetClientService() as ClientService).GetPrivilegedClients()) + bool authed = querySet["IP"] == "127.0.0.1" + || (await (ApplicationManager.GetInstance().GetClientService() as ClientService).GetPrivilegedClients()) .Where(x => x.IPAddress == querySet["IP"]) - .Where(x => x.Level > Player.Permission.Trusted).Count() > 0 - || querySet["IP"] == "127.0.0.1"; + .Where(x => x.Level > Player.Permission.Trusted).Count() > 0; + foreach (Player P in S.GetPlayersAsList()) { PlayerInfo pInfo = new PlayerInfo() { - playerID = P.ClientNumber, + playerID = P.ClientId, playerName = P.Name, playerLevel = authed ? P.Level.ToString() : Player.Permission.User.ToString() }; @@ -381,10 +382,10 @@ namespace IW4MAdmin { // fixme Func predicate = c => c.IPAddress == querySet["IP"]; - Player admin = (await ApplicationManager.GetInstance().GetClientService().Find(predicate)).First()?.AsPlayer(); + Player admin = (await ApplicationManager.GetInstance().GetClientService().Find(predicate)).FirstOrDefault()?.AsPlayer(); if (admin == null) - admin = new Player() { Name = "RestUser", Level = Player.Permission.User }; + admin = new Player() { Name = "RestUser"}; Event remoteEvent = new Event(Event.GType.Say, querySet["command"], admin, null, S) { @@ -465,7 +466,7 @@ namespace IW4MAdmin penaltyType = penalty.Type.ToString(), playerName = penalty.Offender.Name, playerID = penalty.Offender.ClientId, - Expires = penalty.Expires > DateTime.Now ? (penalty.Expires - DateTime.Now).TimeSpanText() : "" + Expires = penalty.Expires > DateTime.UtcNow ? (penalty.Expires - DateTime.UtcNow).TimeSpanText() : "" }; info.Add(pInfo); } @@ -613,13 +614,19 @@ namespace IW4MAdmin public async Task GetPage(System.Collections.Specialized.NameValueCollection querySet, IDictionary headers) { - var Admins = (await (ApplicationManager.GetInstance().GetClientService() as ClientService) + var Admins = (await ApplicationManager.GetInstance().GetClientService() .GetPrivilegedClients()) - .OrderByDescending(a => a.Level).ToList(); + .OrderByDescending(a => a.Level); + HttpResponse resp = new HttpResponse() { contentType = GetContentType(), - content = Newtonsoft.Json.JsonConvert.SerializeObject(Admins, Newtonsoft.Json.Formatting.Indented), + content = Admins.Select(a => new + { + ClientId = a.ClientId, + Level = a.Level, + Name = a.Name + }), additionalHeaders = new Dictionary() }; return resp; @@ -795,15 +802,23 @@ namespace IW4MAdmin playerIPs = new List() }; - if (!recent && individual && authed) + if (!recent) { + eachPlayer.playerAliases = pp.AliasLink.Children + .OrderBy(a => a.Name) + .Select(a => a.Name) + .Distinct() + .ToList(); - eachPlayer.playerAliases = pp.AliasLink.Children.OrderBy(a => a.Name).Select(a => a.Name).ToList(); - eachPlayer.playerIPs = pp.AliasLink.Children.Select(a => a.IP).ToList(); + if (authed) + eachPlayer.playerIPs = pp.AliasLink.Children + .Select(a => a.IPAddress) + .Distinct() + .ToList(); } - eachPlayer.playerAliases = eachPlayer.playerAliases.Distinct().ToList(); - eachPlayer.playerIPs = eachPlayer.playerIPs.Distinct().ToList(); + //eachPlayer.playerAliases = eachPlayer.playerAliases.Distinct().ToList(); + // eachPlayer.playerIPs = eachPlayer.playerIPs.Distinct().ToList(); eachPlayer.playerConnections = pp.Connections; eachPlayer.lastSeen = Utilities.GetTimePassed(pp.LastConnection); diff --git a/Admin/lib/SharedLibrary.dll b/Admin/lib/SharedLibrary.dll index c6107f87f..884bd955f 100644 Binary files a/Admin/lib/SharedLibrary.dll and b/Admin/lib/SharedLibrary.dll differ diff --git a/Plugins/Tests/Plugin.cs b/Plugins/Tests/Plugin.cs index f920433af..1df1e95ce 100644 --- a/Plugins/Tests/Plugin.cs +++ b/Plugins/Tests/Plugin.cs @@ -4,11 +4,13 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using System.IO; using SharedLibrary; using SharedLibrary.Interfaces; using SharedLibrary.Helpers; using SharedLibrary.Objects; +using System.Text.RegularExpressions; namespace IW4MAdmin.Plugins { @@ -57,22 +59,140 @@ namespace IW4MAdmin.Plugins public async Task OnLoadAsync(IManager manager) { Interval = DateTime.Now; + #region CLIENTS + if (File.Exists("import_clients.csv")) + { + var clients = new List(); + manager.GetLogger().WriteInfo("Beginning import of existing clients"); + + var lines = File.ReadAllLines("import_clients.csv").Skip(1); + foreach (string line in lines) + { + string[] fields = Regex.Replace(line, "\".*\"", "").Split(','); + fields.All(f => + { + f = f.StripColors().Trim(); + return true; + }); + + if (fields.Length != 11) + { + manager.GetLogger().WriteError("Invalid client import file... aborting import"); + return; + } + + if (fields[1].Contains("0110") || fields[0] == string.Empty || fields[1] == string.Empty || fields[6] == string.Empty) + continue; + + if (!Regex.Match(fields[6], @"^\d+\.\d+\.\d+.\d+$").Success) + continue; + + var client = new Player() + { + Name = fields[0], + NetworkId = fields[1], + IPAddress = fields[6], + Level = (Player.Permission)Convert.ToInt32(fields[3]), + Connections = Convert.ToInt32(fields[5]), + LastConnection = DateTime.Parse(fields[7]), + }; + + clients.Add(client); + } + + clients = clients + .GroupBy(c => c.NetworkId, (key, c) => c.FirstOrDefault()) + .ToList(); + + clients = clients + .GroupBy(c => new { c.Name, c.IPAddress }) + .Select(c => c.FirstOrDefault()) + .ToList(); + + manager.GetLogger().WriteInfo($"Read {clients.Count} clients for import"); + + try + { + SharedLibrary.Database.Importer.ImportClients(clients); + } + + catch(Exception e) + { + manager.GetLogger().WriteError("Saving imported clients failed"); + } + } + #endregion + #region PENALTIES + if (File.Exists("import_penalties.csv")) + { + int importedPenalties = 0; + var penalties = new List(); + manager.GetLogger().WriteInfo("Beginning import of existing penalties"); + foreach (string line in File.ReadAllLines("import_penalties.csv").Skip(1)) + { + string comma = Regex.Match(line, "\".*,.*\"").Value.Replace(",", ""); + string[] fields = Regex.Replace(line, "\".*,.*\"", comma).Split(','); + + fields.All(f => + { + f = f.StripColors().Trim(); + return true; + }); + + if (fields.Length != 7) + { + manager.GetLogger().WriteError("Invalid penalty import file... aborting import"); + return; + } + + if (fields[2].Contains("0110") || fields[2].Contains("0000000") || fields.Any(p => p == string.Empty)) + continue; + try + { + + var expires = DateTime.Parse(fields[6]); + var when = DateTime.Parse(fields[5]); + + var penalty = new Penalty() + { + Type = (Penalty.PenaltyType)Int32.Parse(fields[0]), + Expires = expires == DateTime.MinValue ? when : expires, + Punisher = new SharedLibrary.Database.Models.EFClient() { NetworkId = fields[3]}, + Offender = new SharedLibrary.Database.Models.EFClient() { NetworkId = fields[2]}, + Offense = fields[1], + Active = true, + When = when, + }; + + + penalties.Add(penalty); + } + + catch (Exception e) + { + manager.GetLogger().WriteWarning($"Could not import client with line {line}"); + } + } + SharedLibrary.Database.Importer.ImportPenalties(penalties); + manager.GetLogger().WriteInfo($"Imported {penalties.Count} clients"); + } + #endregion } public async Task OnTickAsync(Server S) { + return; if ((DateTime.Now - Interval).TotalSeconds > 5) { var rand = new Random(); int index = rand.Next(0, 17); var p = new Player() { - Name = $"Test{index}", - NetworkId = $"_test{index}", + Name = $"Test_{index}", + NetworkId = $"_test_{index}", ClientNumber = index, - Level = Player.Permission.User, Ping = 1, - IPAddress = "127.0.0.1" + IPAddress = $"127.0.0.{index}" }; if (S.Players.ElementAt(index) != null) diff --git a/SharedLibrary/Commands/NativeCommands.cs b/SharedLibrary/Commands/NativeCommands.cs index 1a80af5b9..abb49836a 100644 --- a/SharedLibrary/Commands/NativeCommands.cs +++ b/SharedLibrary/Commands/NativeCommands.cs @@ -65,7 +65,7 @@ namespace SharedLibrary.Commands if (E.Origin.Level <= E.Target.Level) await E.Origin.Tell($"You do not have the required privileges to warn {E.Target.Name}"); else - await E.Target.Warn(E.Data.RemoveWords(1), E.Origin); + await E.Target.Warn(E.Data, E.Origin); } } @@ -113,7 +113,7 @@ namespace SharedLibrary.Commands if (E.Origin.Level > E.Target.Level) { await E.Owner.ExecuteEvent(new Event(Event.GType.Kick, E.Data, E.Origin, E.Target, E.Owner)); - await E.Target.Kick(E.Data.RemoveWords(1), E.Origin); + await E.Target.Kick(E.Data, E.Origin); await E.Origin.Tell($"^5{E.Target} ^7has been kicked"); } else @@ -165,11 +165,8 @@ namespace SharedLibrary.Commands public override async Task ExecuteAsync(Event E) { - String Message = Utilities.RemoveWords(E.Data, 1); - var length = Message.ParseTimespan(); - - if (length.TotalHours != 1) - Message = Utilities.RemoveWords(Message, 1); + String Message = Utilities.RemoveWords(E.Data, 1).Trim(); + var length = E.Data.Split(' ')[0].ParseTimespan(); if (E.Origin.Level > E.Target.Level) { @@ -402,7 +399,7 @@ namespace SharedLibrary.Commands return; } - Player.Permission newPerm = Utilities.MatchPermission(Utilities.RemoveWords(E.Data, 1)); + Player.Permission newPerm = Utilities.MatchPermission(E.Data); if (newPerm == Player.Permission.Owner && E.Origin.Level != Player.Permission.Console) newPerm = Player.Permission.Banned; @@ -566,7 +563,8 @@ namespace SharedLibrary.Commands return; } - var db_aliases = await E.Owner.Manager.GetAliasService().Find(a => a.Name.Contains(E.Data)); + var db_aliases = await E.Owner.Manager.GetAliasService() + .Find(a => a.Name.ToLower().Contains(E.Data.ToLower())); if (db_aliases.Count == 0) { @@ -575,9 +573,7 @@ namespace SharedLibrary.Commands } foreach (var P in db_aliases) - { await E.Origin.Tell($"^4{P.Name} ^7now goes by ^5{P.Link.Children.OrderByDescending(a => a.DateAdded).First().Name}"); - } } } @@ -630,7 +626,6 @@ namespace SharedLibrary.Commands public override async Task ExecuteAsync(Event E) { - E.Data = Utilities.RemoveWords(E.Data, 1); await E.Target.Tell("^1" + E.Origin.Name + " ^3[PM]^7 - " + E.Data); await E.Origin.Tell(String.Format("To ^3{0} ^7-> {1}", E.Target.Name, E.Data)); } @@ -680,13 +675,12 @@ namespace SharedLibrary.Commands if (E.Target.Level == Player.Permission.Flagged) { E.Target.Level = Player.Permission.User; - //E.Owner.Manager.GetClientPenalties().RemovePenalty(new Penalty(Penalty.PenaltyType.Flag, "", E.Target.NetworkId, "", DateTime.Now, "", DateTime.Now)); + await E.Owner.Manager.GetClientService().Update(E.Target); await E.Origin.Tell("You have ^5unflagged ^7" + E.Target.Name); } else { - E.Data = Utilities.RemoveWords(E.Data, 1); E.Target.Level = Player.Permission.Flagged; Penalty newPenalty = new Penalty() @@ -697,7 +691,8 @@ namespace SharedLibrary.Commands Offense = E.Data, Punisher = E.Origin, Active = true, - When = DateTime.UtcNow + When = DateTime.UtcNow, + Link = E.Target.AliasLink }; await E.Owner.Manager.GetPenaltyService().Create(newPenalty); @@ -705,7 +700,6 @@ namespace SharedLibrary.Commands await E.Origin.Tell("You have ^5flagged ^7" + E.Target.Name); } - await E.Owner.Manager.GetClientService().Update(E.Target); } } @@ -792,7 +786,7 @@ namespace SharedLibrary.Commands public class CMask : Command { public CMask() : - base("mask", "hide your online presence from online admin list", "hide", Player.Permission.Administrator, false) + base("mask", "hide your presence as an administrator", "hide", Player.Permission.Moderator, false) { } public override async Task ExecuteAsync(Event E) @@ -837,7 +831,7 @@ namespace SharedLibrary.Commands return; } - await E.Origin.Tell(String.Format("^1{0} ^7was banned by ^5{1} ^7for: {2} {3}", E.Target.Name, penalty.Punisher.Name, penalty.Offense, penalty.Type == Penalty.PenaltyType.TempBan ? $"({(penalty.Expires - DateTime.Now).TimeSpanText()} remaining)" : "")); + await E.Origin.Tell(String.Format("^1{0} ^7was banned by ^5{1} ^7for: {2} {3}", E.Target.Name, penalty.Punisher.Name, penalty.Offense, penalty.Type == Penalty.PenaltyType.TempBan ? $"({(penalty.Expires - DateTime.UtcNow).TimeSpanText()} remaining)" : "")); } } @@ -858,7 +852,7 @@ namespace SharedLibrary.Commands { StringBuilder message = new StringBuilder(); var names = new List(E.Target.AliasLink.Children.Select(a => a.Name)); - var IPs = new List(E.Target.AliasLink.Children.Select(a => a.IP)); + var IPs = new List(E.Target.AliasLink.Children.Select(a => a.IPAddress).Distinct()); await E.Target.Tell($"[^3{E.Target}^7]"); diff --git a/SharedLibrary/Database/IW4MAdminDatabaseContext.cs b/SharedLibrary/Database/DatabaseContext.cs similarity index 54% rename from SharedLibrary/Database/IW4MAdminDatabaseContext.cs rename to SharedLibrary/Database/DatabaseContext.cs index 9ad8d5918..568ed44bf 100644 --- a/SharedLibrary/Database/IW4MAdminDatabaseContext.cs +++ b/SharedLibrary/Database/DatabaseContext.cs @@ -6,34 +6,37 @@ using System.Text; using System.Threading.Tasks; using SharedLibrary.Database.Models; using System.Data.SqlServerCe; +using System.Data.Entity.ModelConfiguration.Conventions; namespace SharedLibrary.Database { - public class IW4MAdminDatabaseContext : DbContext + public class DatabaseContext : DbContext { public DbSet Clients { get; set; } public DbSet Aliases { get; set; } public DbSet AliasLinks { get; set; } public DbSet Penalties { get; set; } - public IW4MAdminDatabaseContext() : base("DefaultConnection") + public DatabaseContext() : base("DefaultConnection") { System.Data.Entity.Database.SetInitializer(new Initializer()); + Configuration.LazyLoadingEnabled = false; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity() - .HasRequired(p => p.Punisher) - .WithMany(c => c.AdministeredPenalties) - .HasForeignKey(c => c.PunisherId) - .WillCascadeOnDelete(false); + .HasRequired(p => p.Offender) + .WithMany(c => c.ReceivedPenalties) + .HasForeignKey(c => c.OffenderId) + .WillCascadeOnDelete(false); + modelBuilder.Entity() - .HasRequired(p => p.Offender) - .WithMany(c => c.ReceivedPenalties) - .HasForeignKey(c => c.OffenderId) - .WillCascadeOnDelete(false); + .HasRequired(p => p.Punisher) + .WithMany(c => c.AdministeredPenalties) + .HasForeignKey(c => c.PunisherId) + .WillCascadeOnDelete(false); modelBuilder.Entity() .HasMany(e => e.Children) @@ -41,7 +44,17 @@ namespace SharedLibrary.Database .HasForeignKey(a => a.LinkId) .WillCascadeOnDelete(true); - // todo custom load DBSets from plugins + /* modelBuilder.Entity() + .HasIndex(a => new { a.IP, a.Name }) + .IsUnique(); + + modelBuilder.Entity() + .HasIndex(p => new { p.Name, p.IPAddress }).IsUnique();*/ + + + modelBuilder.Conventions.Remove(); + + // todo: custom load DBSets from plugins // https://aleemkhan.wordpress.com/2013/02/28/dynamically-adding-dbset-properties-in-dbcontext-for-entity-framework-code-first/ base.OnModelCreating(modelBuilder); } diff --git a/SharedLibrary/Database/Importer.cs b/SharedLibrary/Database/Importer.cs new file mode 100644 index 000000000..c55650721 --- /dev/null +++ b/SharedLibrary/Database/Importer.cs @@ -0,0 +1,165 @@ +using SharedLibrary.Database.Models; +using SharedLibrary.Objects; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SharedLibrary.Database +{ + //https://stackoverflow.com/questions/5940225/fastest-way-of-inserting-in-entity-framework + public static class Importer + { + public static void ImportClients(IList clients) + { + DatabaseContext context = null; + + try + { + context = new DatabaseContext(); + context.Configuration.AutoDetectChangesEnabled = false; + + int count = 0; + foreach (var entityToInsert in clients) + { + ++count; + + var link = new EFAliasLink() { Active = true }; + + var alias = new EFAlias() + { + Active = true, + DateAdded = entityToInsert.LastConnection, + IPAddress = entityToInsert.IPAddress, + Link = link, + Name = entityToInsert.Name, + }; + + var client = new EFClient() + { + Active = true, + AliasLink = link, + Connections = entityToInsert.Connections, + CurrentAlias = alias, + FirstConnection = entityToInsert.LastConnection, + Level = entityToInsert.Level, + LastConnection = entityToInsert.LastConnection, + TotalConnectionTime = entityToInsert.TotalConnectionTime, + Masked = entityToInsert.Masked, + NetworkId = entityToInsert.NetworkId + }; + + context = AddClient(context, client, count, 100, true); + } + + context.SaveChanges(); + } + finally + { + if (context != null) + context.Dispose(); + } + } + + private static DatabaseContext AddClient(DatabaseContext context, EFClient client, int count, int commitCount, bool recreateContext) + { + context.Clients.Add(client); + if (count % commitCount == 0) + { + try + { + context.SaveChanges(); + } + + catch (Exception e) + { + var a = 1; + } + + if (recreateContext) + { + context.Dispose(); + context = new DatabaseContext(); + context.Configuration.AutoDetectChangesEnabled = false; + } + } + + return context; + } + + public static void ImportPenalties(IList penalties) + { + DatabaseContext context = null; + + try + { + context = new DatabaseContext(); + context.Configuration.AutoDetectChangesEnabled = false; + + int count = 0; + foreach (var entityToInsert in penalties) + { + ++count; + var punisher = entityToInsert.Offender.NetworkId == entityToInsert.Punisher.NetworkId ? + context.Clients.SingleOrDefault(c => c.ClientId == 1) : + context.Clients.SingleOrDefault(c => c.NetworkId == entityToInsert.Punisher.NetworkId); + if (punisher == null) + continue; + var offender = context.Clients.Include("AliasLink").SingleOrDefault(c => c.NetworkId == entityToInsert.Offender.NetworkId); + + if (offender == null) + continue; + + + var penalty = new EFPenalty() + { + Active = true, + Expires = entityToInsert.Expires.Year == 9999 ? DateTime.Parse(System.Data.SqlTypes.SqlDateTime.MinValue.ToString()) : entityToInsert.Expires, + Offender = offender, + Punisher = punisher, + Offense = entityToInsert.Offense, + Type = entityToInsert.Type, + When = entityToInsert.When == DateTime.MinValue ? DateTime.UtcNow : entityToInsert.When, + Link = offender.AliasLink + }; + + context = AddPenalty(context, penalty, count, 100, true); + } + + context.SaveChanges(); + } + finally + { + if (context != null) + context.Dispose(); + } + } + + private static DatabaseContext AddPenalty(DatabaseContext context, EFPenalty penalty, int count, int commitCount, bool recreateContext) + { + context.Penalties.Add(penalty); + if (count % commitCount == 0) + { + try + { + context.SaveChanges(); + } + + catch (Exception e) + { + var a = 1; + } + + if (recreateContext) + { + context.Dispose(); + context = new DatabaseContext(); + context.Configuration.AutoDetectChangesEnabled = false; + } + } + + return context; + } + } +} diff --git a/SharedLibrary/Database/Initializer.cs b/SharedLibrary/Database/Initializer.cs index a15e6af29..afffe33cc 100644 --- a/SharedLibrary/Database/Initializer.cs +++ b/SharedLibrary/Database/Initializer.cs @@ -7,22 +7,32 @@ using System.Threading.Tasks; namespace SharedLibrary.Database { - public class Initializer : DropCreateDatabaseIfModelChanges + public class Initializer : DropCreateDatabaseIfModelChanges { - protected override void Seed(IW4MAdminDatabaseContext context) + protected override void Seed(DatabaseContext context) { + var aliasLink = new Models.EFAliasLink(); + + var currentAlias = new Models.EFAlias() + { + Active = true, + DateAdded = DateTime.UtcNow, + IPAddress = "0.0.0.0", + Name = "IW4MAdmin", + Link = aliasLink + }; + context.Clients.Add(new Models.EFClient() { Active = false, Connections = 0, - AliasLink = new Models.EFAliasLink(), FirstConnection = DateTime.UtcNow, - IPAddress = "127.0.0.1", LastConnection = DateTime.UtcNow, Level = Objects.Player.Permission.Console, - Name = "IW4MAdmin", Masked = true, NetworkId = "0000000000000000", + AliasLink = aliasLink, + CurrentAlias = currentAlias }); base.Seed(context); diff --git a/SharedLibrary/Database/Models/EFAlias.cs b/SharedLibrary/Database/Models/EFAlias.cs index 49d035b67..adcea7341 100644 --- a/SharedLibrary/Database/Models/EFAlias.cs +++ b/SharedLibrary/Database/Models/EFAlias.cs @@ -1,10 +1,6 @@ using System; -using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace SharedLibrary.Database.Models { @@ -16,10 +12,12 @@ namespace SharedLibrary.Database.Models public int LinkId { get; set; } [ForeignKey("LinkId")] public virtual EFAliasLink Link { get; set; } - [Required] + // [Index("IX_IPandName", 0, IsUnique = true)] + //[MaxLength(24)] public string Name { get; set; } - [Required] - public string IP { get; set; } + // [Index("IX_IPandName", 1, IsUnique = true)] + // [MaxLength(24)] + public string IPAddress { get; set; } [Required] public DateTime DateAdded { get; set; } } diff --git a/SharedLibrary/Database/Models/EFAliasLink.cs b/SharedLibrary/Database/Models/EFAliasLink.cs index fcbc1f838..05d869a37 100644 --- a/SharedLibrary/Database/Models/EFAliasLink.cs +++ b/SharedLibrary/Database/Models/EFAliasLink.cs @@ -1,5 +1,7 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; namespace SharedLibrary.Database.Models { @@ -8,10 +10,12 @@ namespace SharedLibrary.Database.Models [Key] public int AliasLinkId { get; set; } public virtual ICollection Children { get; set; } + public virtual ICollection ReceivedPenalties { get; set; } public EFAliasLink() { Children = new List(); + ReceivedPenalties = new List(); } } } diff --git a/SharedLibrary/Database/Models/EFClient.cs b/SharedLibrary/Database/Models/EFClient.cs index d8cfbfa24..1cb058017 100644 --- a/SharedLibrary/Database/Models/EFClient.cs +++ b/SharedLibrary/Database/Models/EFClient.cs @@ -15,17 +15,11 @@ namespace SharedLibrary.Database.Models [Index(IsUnique = true)] public string NetworkId { get; set; } - [Required] - public string Name { get; set; } - [Required] - public Objects.Player.Permission Level { get; set; } [Required] public int Connections { get; set; } [Required] public int TotalConnectionTime { get; set; } [Required] - public string IPAddress { get; set; } - [Required] public DateTime FirstConnection { get; set; } [Required] public DateTime LastConnection { get; set; } @@ -34,6 +28,27 @@ namespace SharedLibrary.Database.Models public int AliasLinkId { get; set; } [ForeignKey("AliasLinkId")] public virtual EFAliasLink AliasLink { get; set; } + [Required] + public Objects.Player.Permission Level { get; set; } + + [Required] + public int CurrentAliasId { get; set; } + [ForeignKey("CurrentAliasId")] + public virtual EFAlias CurrentAlias { get; set; } + + [NotMapped] + public virtual string Name + { + get { return CurrentAlias.Name; } + set { } + } + [NotMapped] + public virtual string IPAddress + { + get { return CurrentAlias.IPAddress; } + set { } + } + public virtual ICollection ReceivedPenalties { get; set; } public virtual ICollection AdministeredPenalties { get; set; } diff --git a/SharedLibrary/Objects/Player.cs b/SharedLibrary/Objects/Player.cs index a642b9bb9..d6edb3bd7 100644 --- a/SharedLibrary/Objects/Player.cs +++ b/SharedLibrary/Objects/Player.cs @@ -5,7 +5,7 @@ using System.Linq; using System.Threading.Tasks; namespace SharedLibrary.Objects -{ +{ public class Player : Database.Models.EFClient { public enum Permission @@ -25,8 +25,9 @@ namespace SharedLibrary.Objects public Player() { ConnectionTime = DateTime.UtcNow; + ClientNumber = -1; } - + public override string ToString() { return $"{Name}::{NetworkId}"; @@ -63,7 +64,7 @@ namespace SharedLibrary.Objects } [NotMapped] - public int ClientNumber { get; set; } + public int ClientNumber { get; set; } [NotMapped] public int Ping { get; set; } [NotMapped] @@ -72,5 +73,18 @@ namespace SharedLibrary.Objects public DateTime ConnectionTime { get; set; } [NotMapped] public Server CurrentServer { get; set; } + + private string _ipaddress; + public override string IPAddress + { + get { return _ipaddress; } + set { _ipaddress = value; } + } + private string _name; + public override string Name + { + get { return _name; } + set { _name = value; } + } } } diff --git a/SharedLibrary/Services/AliasService.cs b/SharedLibrary/Services/AliasService.cs index a19c17fe9..97f6e184e 100644 --- a/SharedLibrary/Services/AliasService.cs +++ b/SharedLibrary/Services/AliasService.cs @@ -16,12 +16,22 @@ namespace SharedLibrary.Services { public async Task Create(EFAlias entity) { - using (var context = new IW4MAdminDatabaseContext()) + throw new Exception(); + using (var context = new DatabaseContext()) { - entity.Link = await context.AliasLinks.FirstAsync(a => a.AliasLinkId == entity.Link.AliasLinkId); - var addedEntity = context.Aliases.Add(entity); + var alias = new EFAlias() + { + Active = true, + DateAdded = DateTime.UtcNow, + IPAddress = entity.IPAddress, + Name = entity.Name + }; + + entity.Link = await context.AliasLinks + .FirstAsync(a => a.AliasLinkId == entity.Link.AliasLinkId); + context.Aliases.Add(entity); await context.SaveChangesAsync(); - return addedEntity; + return entity; } } @@ -32,11 +42,11 @@ namespace SharedLibrary.Services public async Task Delete(EFAlias entity) { - using (var context = new IW4MAdminDatabaseContext()) + using (var context = new DatabaseContext()) { - entity = context.Aliases.Single(e => e.AliasId == entity.AliasId); - entity.Active = false; - context.Entry(entity).State = EntityState.Modified; + var alias = context.Aliases + .Single(e => e.AliasId == entity.AliasId); + alias.Active = false; await context.SaveChangesAsync(); return entity; } @@ -44,14 +54,18 @@ namespace SharedLibrary.Services public async Task> Find(Func expression) { - using (var context = new IW4MAdminDatabaseContext()) - return await Task.Run(() => context.Aliases.Where(expression).ToList()); + using (var context = new DatabaseContext()) + return await Task.Run(() => context.Aliases + .AsNoTracking() + .Include(a => a.Link.Children) + .Where(expression).ToList()); } public async Task Get(int entityID) { - using (var context = new IW4MAdminDatabaseContext()) + using (var context = new DatabaseContext()) return await context.Aliases + .AsNoTracking() .SingleOrDefaultAsync(e => e.AliasId == entityID); } @@ -62,7 +76,8 @@ namespace SharedLibrary.Services public async Task Update(EFAlias entity) { - using (var context = new IW4MAdminDatabaseContext()) + throw new Exception(); + using (var context = new DatabaseContext()) { entity = context.Aliases.Attach(entity); context.Entry(entity).State = EntityState.Modified; @@ -73,7 +88,7 @@ namespace SharedLibrary.Services public async Task CreateLink(EFAliasLink link) { - using (var context = new IW4MAdminDatabaseContext()) + using (var context = new DatabaseContext()) { context.AliasLinks.Add(link); await context.SaveChangesAsync(); diff --git a/SharedLibrary/Services/ClientService.cs b/SharedLibrary/Services/ClientService.cs index 214fe9c99..60eba54dc 100644 --- a/SharedLibrary/Services/ClientService.cs +++ b/SharedLibrary/Services/ClientService.cs @@ -11,46 +11,61 @@ using System.Linq.Expressions; namespace SharedLibrary.Services { + public class ClientService : Interfaces.IEntityService { - private Dictionary _context; - - public ClientService() - { - _context = new Dictionary(); - } public async Task Create(EFClient entity) { - using (var context = new IW4MAdminDatabaseContext()) + using (var context = new DatabaseContext()) { - + bool hasExistingAlias = false; // get all aliases by IP - var alias = await context.Aliases.FirstOrDefaultAsync(a => a.IP == entity.IPAddress); - EFAliasLink link = alias?.Link; + var aliases = await context.Aliases + .Include(a => a.Link) + .Where(a => a.IPAddress == entity.IPAddress) + .ToListAsync(); + + // see if they have a matching IP + Name but new NetworkId + var existingAlias = aliases.SingleOrDefault(a => a.Name == entity.Name); + // if existing alias matches link them + EFAliasLink aliasLink = existingAlias?.Link; + // if no exact matches find the first IP that matches + aliasLink = aliasLink ?? aliases.FirstOrDefault()?.Link; + // if no exact or IP matches, create new link + aliasLink = aliasLink ?? new EFAliasLink() + { + Active = true, + }; + + // this has to be set here because we can't evalute it properly later + hasExistingAlias = existingAlias != null; + + // if no existing alias create new alias + existingAlias = existingAlias ?? new EFAlias() + { + Active = true, + DateAdded = DateTime.UtcNow, + IPAddress = entity.IPAddress, + Link = aliasLink, + Name = entity.Name, + }; var client = new EFClient() { Active = true, - Name = entity.Name, + // set the level to the level of the existing client if they have the same IP + Name but new NetworkId + Level = hasExistingAlias ? + context.Clients.First(c => c.CurrentAliasId == existingAlias.AliasId).Level : + Objects.Player.Permission.User, FirstConnection = DateTime.UtcNow, Connections = 1, - IPAddress = entity.IPAddress, LastConnection = DateTime.UtcNow, - Level = Objects.Player.Permission.User, Masked = false, NetworkId = entity.NetworkId, - AliasLink = link ?? new EFAliasLink() { Active = true } + AliasLink = aliasLink, + CurrentAlias = existingAlias }; - client.AliasLink.Children.Add(new EFAlias() - { - Active = true, - DateAdded = DateTime.UtcNow, - IP = entity.IPAddress, - Link = client.AliasLink, - Name = entity.Name - }); - context.Clients.Add(client); await context.SaveChangesAsync(); @@ -60,11 +75,11 @@ namespace SharedLibrary.Services public async Task Delete(EFClient entity) { - using (var context = new IW4MAdminDatabaseContext()) + using (var context = new DatabaseContext()) { - entity = context.Clients.Single(e => e.ClientId == entity.ClientId); + var client = context.Clients + .Single(e => e.ClientId == entity.ClientId); entity.Active = false; - entity.Level = Objects.Player.Permission.User; context.Entry(entity).State = EntityState.Modified; await context.SaveChangesAsync(); return entity; @@ -73,25 +88,31 @@ namespace SharedLibrary.Services public async Task> Find(Func e) { - using (var context = new IW4MAdminDatabaseContext()) + using (var context = new DatabaseContext()) return await Task.Run(() => context.Clients + .AsNoTracking() + .Include(c => c.CurrentAlias) .Include(c => c.AliasLink.Children) .Where(e).ToList()); } public async Task Get(int entityID) { - using (var context = new IW4MAdminDatabaseContext()) - return await new IW4MAdminDatabaseContext().Clients + using (var context = new DatabaseContext()) + return await new DatabaseContext().Clients + .AsNoTracking() + .Include(c => c.CurrentAlias) .Include(c => c.AliasLink.Children) .SingleOrDefaultAsync(e => e.ClientId == entityID); } public async Task GetUnique(string entityAttribute) { - using (var context = new IW4MAdminDatabaseContext()) + using (var context = new DatabaseContext()) { return await context.Clients + .AsNoTracking() + .Include(c => c.CurrentAlias) .Include(c => c.AliasLink.Children) .SingleOrDefaultAsync(c => c.NetworkId == entityAttribute); } @@ -99,42 +120,97 @@ namespace SharedLibrary.Services public async Task Update(EFClient entity) { - using (var context = new IW4MAdminDatabaseContext()) + using (var context = new DatabaseContext()) { - entity = context.Clients.Attach(entity); - context.Entry(entity).State = EntityState.Modified; + // grab the context version of the entity + var client = context.Clients + .Include(c => c.AliasLink) + .Include(c=> c.CurrentAlias) + .Single(e => e.ClientId == entity.ClientId); + + // if their level has been changed + if (entity.Level != client.Level) + { + // get all clients that use the same aliasId + var matchingClients = await context.Clients + .Where(c => c.CurrentAliasId == client.CurrentAliasId) + .ToListAsync(); + + // update all related clients level + matchingClients.ForEach(c => c.Level = (client.Level == Objects.Player.Permission.Banned) ? + client.Level : entity.Level); + } + + // their alias has been updated and not yet saved + if (entity.CurrentAlias.AliasId == 0) + { + client.CurrentAlias = new EFAlias() + { + Active = true, + DateAdded = DateTime.UtcNow, + IPAddress = entity.CurrentAlias.IPAddress, + Name = entity.CurrentAlias.Name, + Link = client.AliasLink + }; + } + + // set remaining non-navigation properties that may have been updated + client.Level = entity.Level; + client.LastConnection = entity.LastConnection; + client.Connections = entity.Connections; + client.FirstConnection = entity.FirstConnection; + client.Masked = entity.Masked; + client.TotalConnectionTime = entity.TotalConnectionTime; + + // update in database await context.SaveChangesAsync(); - return entity; + + // this is set so future updates don't trigger a new alias add + if (entity.CurrentAlias.AliasId == 0) + entity.CurrentAlias.AliasId = client.CurrentAlias.AliasId; + return client; } } - #region ServiceSpecific +#region ServiceSpecific public async Task> GetOwners() { - using (var context = new IW4MAdminDatabaseContext()) - return await context.Clients.Where(c => c.Level == Objects.Player.Permission.Owner).ToListAsync(); + using (var context = new DatabaseContext()) + return await context.Clients + .Where(c => c.Level == Objects.Player.Permission.Owner) + .ToListAsync(); } public async Task> GetPrivilegedClients() { - using (var context = new IW4MAdminDatabaseContext()) - return await new IW4MAdminDatabaseContext().Clients - .Where(c => c.Level >= Objects.Player.Permission.Trusted) - .ToListAsync(); + using (var context = new DatabaseContext()) + return await new DatabaseContext().Clients + .AsNoTracking() + .Include(c => c.CurrentAlias) + .Where(c => c.Level >= Objects.Player.Permission.Trusted) + .ToListAsync(); } public async Task> GetRecentClients(int offset, int count) { - using (var context = new IW4MAdminDatabaseContext()) - return await context.Clients.OrderByDescending(p => p.ClientId).Skip(offset).Take(count).ToListAsync(); + using (var context = new DatabaseContext()) + return await context.Clients + .AsNoTracking() + .Include(c => c.CurrentAlias) + .Include(p => p.AliasLink) + .OrderByDescending(p => p.ClientId) + .Skip(offset) + .Take(count) + .ToListAsync(); } public async Task> PruneInactivePrivilegedClients(int inactiveDays) { - using (var context = new IW4MAdminDatabaseContext()) + using (var context = new DatabaseContext()) { var inactive = await context.Clients.Where(c => c.Level > Objects.Player.Permission.Flagged) + .AsNoTracking() .Where(c => (DateTime.UtcNow - c.LastConnection).TotalDays >= inactiveDays) .ToListAsync(); inactive.ForEach(c => c.Level = Objects.Player.Permission.User); @@ -145,14 +221,15 @@ namespace SharedLibrary.Services public async Task GetTotalClientsAsync() { - using (var context = new IW4MAdminDatabaseContext()) - return await context.Clients.CountAsync(); + using (var context = new DatabaseContext()) + return await context.Clients + .CountAsync(); } public Task CreateProxy() { throw new NotImplementedException(); } - #endregion +#endregion } } diff --git a/SharedLibrary/Services/PenaltyService.cs b/SharedLibrary/Services/PenaltyService.cs index d2237b36b..b29b90965 100644 --- a/SharedLibrary/Services/PenaltyService.cs +++ b/SharedLibrary/Services/PenaltyService.cs @@ -15,13 +15,29 @@ namespace SharedLibrary.Services { public async Task Create(EFPenalty entity) { - using (var context = new IW4MAdminDatabaseContext()) + using (var context = new DatabaseContext()) { entity.Offender = context.Clients.First(e => e.ClientId == entity.Offender.ClientId); entity.Punisher = context.Clients.First(e => e.ClientId == entity.Punisher.ClientId); entity.Link = context.AliasLinks.First(l => l.AliasLinkId == entity.Link.AliasLinkId); - if (entity.Expires == DateTime.MinValue) + + // make bans propogate to all aliases + if (entity.Type == Objects.Penalty.PenaltyType.Ban) + { entity.Expires = DateTime.Parse(System.Data.SqlTypes.SqlDateTime.MaxValue.ToString()); + await context.Clients + .Where(c => c.AliasLinkId == entity.Link.AliasLinkId) + .ForEachAsync(c => c.Level = Objects.Player.Permission.Banned); + } + + // make flags propogate to all aliases + else if (entity.Type == Objects.Penalty.PenaltyType.Flag) + { + await context.Clients + .Where(c => c.AliasLinkId == entity.Link.AliasLinkId) + .ForEachAsync(c => c.Level = Objects.Player.Permission.Flagged); + } + context.Penalties.Add(entity); await context.SaveChangesAsync(); return entity; @@ -40,7 +56,7 @@ namespace SharedLibrary.Services public async Task> Find(Func expression) { - using (var context = new IW4MAdminDatabaseContext()) + using (var context = new DatabaseContext()) { return await Task.Run(() => context.Penalties .Include(p => p.Offender) @@ -68,11 +84,12 @@ namespace SharedLibrary.Services public async Task> GetRecentPenalties(int count, int offset) { - using (var context = new IW4MAdminDatabaseContext()) + using (var context = new DatabaseContext()) return await context.Penalties - .Include(p => p.Offender) - .Include(p => p.Punisher) - .Where(p => p.Active) + .AsNoTracking() + .Include(p => p.Offender.CurrentAlias) + .Include(p => p.Punisher.CurrentAlias) + .Where(p => p.Active) .OrderByDescending(p => p.When) .Skip(offset) .Take(count) @@ -81,18 +98,19 @@ namespace SharedLibrary.Services public async Task> GetClientPenaltiesAsync(int clientId) { - using (var context = new IW4MAdminDatabaseContext()) + using (var context = new DatabaseContext()) return await context.Penalties - .Where(p => p.OffenderId == clientId) - .Where(p => p.Active) - .Include(p => p.Offender) - .Include(p => p.Punisher) - .ToListAsync(); + .AsNoTracking() + .Where(p => p.OffenderId == clientId) + .Where(p => p.Active) + .Include(p => p.Offender.CurrentAlias) + .Include(p => p.Punisher.CurrentAlias) + .ToListAsync(); } public async Task RemoveActivePenalties(int aliasLinkId) { - using (var context = new IW4MAdminDatabaseContext()) + using (var context = new DatabaseContext()) { var now = DateTime.UtcNow; var penalties = await context.Penalties @@ -104,10 +122,11 @@ namespace SharedLibrary.Services penalties.ForEach(async p => { p.Active = false; - var clients = await context.Clients.Where(cl => cl.AliasLinkId == p.LinkId).ToListAsync(); - foreach (var c in clients) - if (c.Level == Objects.Player.Permission.Banned) - c.Level = Objects.Player.Permission.User; + // reset the player levels + if (p.Type == Objects.Penalty.PenaltyType.Ban) + await context.Clients + .Where(c => c.AliasLinkId == p.LinkId) + .ForEachAsync(c => c.Level = Objects.Player.Permission.User); }); await context.SaveChangesAsync(); diff --git a/SharedLibrary/SharedLibrary.csproj b/SharedLibrary/SharedLibrary.csproj index 51b5e1f02..0ba366054 100644 --- a/SharedLibrary/SharedLibrary.csproj +++ b/SharedLibrary/SharedLibrary.csproj @@ -94,8 +94,9 @@ + - + diff --git a/SharedLibrary/Utilities.cs b/SharedLibrary/Utilities.cs index 9dddeb6c4..8fd17f04c 100644 --- a/SharedLibrary/Utilities.cs +++ b/SharedLibrary/Utilities.cs @@ -90,7 +90,7 @@ namespace SharedLibrary { if (str == null) return ""; - return Regex.Replace(str, @"\^([0-9]|\:)", ""); + return Regex.Replace(str, @"(\^+((?![a-z]|[A-Z]).){0,1})+", ""); } /// @@ -207,7 +207,7 @@ namespace SharedLibrary public static String GetTimePassed(DateTime start, bool includeAgo) { - TimeSpan Elapsed = DateTime.Now - start; + TimeSpan Elapsed = DateTime.UtcNow - start; string ago = includeAgo ? " ago" : ""; if (Elapsed.TotalSeconds < 30 && includeAgo) @@ -305,19 +305,20 @@ namespace SharedLibrary return client == null ? null : new Player() { Active = client.Active, - AliasLink =client.AliasLink, + AliasLink = client.AliasLink, AliasLinkId = client.AliasLinkId, ClientId = client.ClientId, ClientNumber = 0, FirstConnection = client.FirstConnection, Connections = client.Connections, - IPAddress = client.IPAddress, NetworkId = client.NetworkId, - Name = client.Name, - Level = client.Level, TotalConnectionTime = client.TotalConnectionTime, Masked = client.Masked, - LastConnection = DateTime.UtcNow + Name = client.CurrentAlias.Name, + IPAddress = client.CurrentAlias.IPAddress, + Level = client.Level, + LastConnection = DateTime.UtcNow, + CurrentAlias = client.CurrentAlias }; } }