private async Task <EFClient> HandleNewCreate(EFClient entity) { await using var context = _contextFactory.CreateContext(true); using (LogContext.PushProperty("Server", entity.CurrentServer?.ToString())) { var existingAlias = await context.Aliases .Select(alias => new { alias.AliasId, alias.LinkId, alias.IPAddress, alias.Name }) .Where(alias => alias.IPAddress != null && alias.IPAddress == entity.IPAddress && alias.Name == entity.Name) .FirstOrDefaultAsync(); var client = new EFClient { Level = Permission.User, FirstConnection = DateTime.UtcNow, LastConnection = DateTime.UtcNow, NetworkId = entity.NetworkId }; if (existingAlias == null) { _logger.LogDebug("[{Method}] creating new Link and Alias for {Entity}", nameof(HandleNewCreate), entity.ToString()); var link = new EFAliasLink(); var alias = new EFAlias { Name = entity.Name, SearchableName = entity.Name.StripColors().ToLower(), DateAdded = DateTime.UtcNow, IPAddress = entity.IPAddress, Link = link }; client.CurrentAlias = alias; client.AliasLink = link; } else { _logger.LogDebug("[{Method}] associating new GUID {Guid} with new exact alias match with linkId {LinkId} for {Entity}", nameof(HandleNewCreate), entity.GuidString, existingAlias.LinkId, entity.ToString()); var alias = new EFAlias { Name = existingAlias.Name, SearchableName = entity.Name.StripColors().ToLower(), DateAdded = DateTime.UtcNow, IPAddress = entity.IPAddress, LinkId = existingAlias.LinkId }; client.CurrentAlias = alias; client.AliasLinkId = existingAlias.LinkId; } context.Clients.Add(client); await context.SaveChangesAsync(); return(client); } }
public static void ImportClients(IList <Player> clients) { DatabaseContext context = null; try { context = new DatabaseContext(); context.Configuration.AutoDetectChangesEnabled = false; context.Configuration.LazyLoadingEnabled = false; context.Configuration.ProxyCreationEnabled = 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, 1000, true); } context.SaveChanges(); } finally { if (context != null) { context.Dispose(); } } }
private async Task UpdateAliasNew(string originalName, int?ip, Data.Models.Client.EFClient entity, DatabaseContext context) { var name = originalName.CapClientName(EFAlias.MAX_NAME_LENGTH); var existingAliases = await context.Aliases .Where(alias => alias.Name == name && alias.LinkId == entity.AliasLinkId || alias.Name == name && alias.IPAddress != null && alias.IPAddress == ip) .ToListAsync(); var defaultAlias = existingAliases.FirstOrDefault(alias => alias.IPAddress == null); var existingExactAlias = existingAliases.FirstOrDefault(alias => alias.IPAddress != null && alias.IPAddress == ip); if (defaultAlias != null && existingExactAlias == null) { defaultAlias.IPAddress = ip; entity.CurrentAlias = defaultAlias; entity.CurrentAliasId = defaultAlias.AliasId; await context.SaveChangesAsync(); return; } if (existingExactAlias != null && entity.AliasLinkId == existingExactAlias.LinkId) { entity.CurrentAlias = existingExactAlias; entity.CurrentAliasId = existingExactAlias.AliasId; await context.SaveChangesAsync(); _logger.LogDebug("[{Method}] client {Client} already has an existing exact alias, so we are not making changes", nameof(UpdateAliasNew), entity.ToString()); return; } _logger.LogDebug("[{Method}] {Entity} is using a new alias", nameof(UpdateAliasNew), entity.ToString()); var newAlias = new EFAlias() { DateAdded = DateTime.UtcNow, IPAddress = ip, LinkId = entity.AliasLinkId, Name = name, SearchableName = name.StripColors().ToLower(), Active = true, }; entity.CurrentAlias = newAlias; await context.SaveChangesAsync(); entity.CurrentAliasId = newAlias.AliasId; }
public async Task Seed() { // make sure database exists //context.Database.EnsureCreated(); context.Database.Migrate(); if (context.AliasLinks.Count() == 0) { context.AliasLinks.Add(new EFAliasLink() { AliasLinkId = 1 }); var currentAlias = new EFAlias() { AliasId = 1, Active = true, DateAdded = DateTime.UtcNow, IPAddress = 0, Name = "IW4MAdmin", LinkId = 1 }; context.Aliases.Add(currentAlias); context.Clients.Add(new EFClient() { ClientId = 1, Active = false, Connections = 0, FirstConnection = DateTime.UtcNow, LastConnection = DateTime.UtcNow, Level = Objects.Player.Permission.Console, Masked = true, NetworkId = 0, AliasLinkId = 1, CurrentAliasId = 1, }); await context.SaveChangesAsync(); } }
private async Task UpdateAlias(string name, int?ip, EFClient entity, DatabaseContext context) { // entity is the tracked db context item // get all aliases by IP address and LinkId var iqAliases = context.Aliases .Include(a => a.Link) // we only want alias that have the same IP address or share a link .Where(_alias => _alias.IPAddress == ip || (_alias.LinkId == entity.AliasLinkId)); #if DEBUG == true var aliasSql = iqAliases.ToSql(); #endif var aliases = await iqAliases.ToListAsync(); // update each of the aliases where this is no IP but the name is identical foreach (var alias in aliases.Where(_alias => (_alias.IPAddress == null || _alias.IPAddress == 0))) { alias.IPAddress = ip; } await context.SaveChangesAsync(); // see if they have a matching IP + Name but new NetworkId var existingExactAlias = aliases.FirstOrDefault(a => a.Name == name && a.IPAddress == ip); bool hasExactAliasMatch = existingExactAlias != null; // if existing alias matches link them var newAliasLink = existingExactAlias?.Link; // if no exact matches find the first IP or LinkId that matches newAliasLink = newAliasLink ?? aliases.FirstOrDefault()?.Link; // if no matches are found, use our current one ( it will become permanent ) newAliasLink = newAliasLink ?? entity.AliasLink; bool hasExistingAlias = aliases.Count > 0; bool isAliasLinkUpdated = newAliasLink.AliasLinkId != entity.AliasLink.AliasLinkId; // this happens when the link we found is different than the one we create before adding an IP if (isAliasLinkUpdated) { entity.CurrentServer.Logger.WriteDebug($"[updatealias] found a link for {entity} so we are updating link from {entity.AliasLink.AliasLinkId} to {newAliasLink.AliasLinkId}"); var oldAliasLink = entity.AliasLink; // update all the clients that have the old alias link await context.Clients .Where(_client => _client.AliasLinkId == oldAliasLink.AliasLinkId) .ForEachAsync(_client => _client.AliasLinkId = newAliasLink.AliasLinkId); entity.AliasLink = newAliasLink; entity.AliasLinkId = newAliasLink.AliasLinkId; // update all previous aliases await context.Aliases .Where(_alias => _alias.LinkId == oldAliasLink.AliasLinkId) .ForEachAsync(_alias => _alias.LinkId = newAliasLink.AliasLinkId); await context.SaveChangesAsync(); // we want to delete the now inactive alias context.AliasLinks.Remove(oldAliasLink); await context.SaveChangesAsync(); } // the existing alias matches ip and name, so we can just ignore the temporary one if (hasExactAliasMatch) { entity.CurrentServer.Logger.WriteDebug($"[updatealias] {entity} has exact alias match"); var oldAlias = entity.CurrentAlias; entity.CurrentAliasId = existingExactAlias.AliasId; entity.CurrentAlias = existingExactAlias; await context.SaveChangesAsync(); // the alias is the same so we can just remove it if (oldAlias.AliasId != existingExactAlias.AliasId && oldAlias.AliasId > 0) { await context.Clients .Where(_client => _client.CurrentAliasId == oldAlias.AliasId) .ForEachAsync(_client => _client.CurrentAliasId = existingExactAlias.AliasId); await context.SaveChangesAsync(); entity.CurrentServer.Logger.WriteDebug($"[updatealias] {entity} has exact alias match, so we're going to try to remove aliasId {oldAlias.AliasId} with linkId {oldAlias.AliasId}"); context.Aliases.Remove(oldAlias); await context.SaveChangesAsync(); } } // theres no exact match, but they've played before with the GUID or IP else { entity.CurrentServer.Logger.WriteDebug($"[updatealias] {entity} is using a new alias"); var newAlias = new EFAlias() { DateAdded = DateTime.UtcNow, IPAddress = ip, LinkId = newAliasLink.AliasLinkId, Name = name, Active = true, }; entity.CurrentAlias = newAlias; entity.CurrentAliasId = 0; await context.SaveChangesAsync(); } }
public async Task <EFClient> Create(EFClient entity) { using (var context = new DatabaseContext()) { int?linkId = null; int?aliasId = null; if (entity.IPAddress != null) { var existingAlias = await context.Aliases .Select(_alias => new { _alias.AliasId, _alias.LinkId, _alias.IPAddress, _alias.Name }) .FirstOrDefaultAsync(_alias => _alias.IPAddress == entity.IPAddress); if (existingAlias != null) { entity.CurrentServer.Logger.WriteDebug($"[create] client with new GUID {entity} has existing link {existingAlias.LinkId}"); linkId = existingAlias.LinkId; if (existingAlias.Name == entity.Name) { entity.CurrentServer.Logger.WriteDebug($"[create] client with new GUID {entity} has existing alias {existingAlias.AliasId}"); aliasId = existingAlias.AliasId; } } } var client = new EFClient() { Level = Permission.User, FirstConnection = DateTime.UtcNow, LastConnection = DateTime.UtcNow, NetworkId = entity.NetworkId }; context.Clients.Add(client); // they're just using a new GUID if (aliasId.HasValue) { entity.CurrentServer.Logger.WriteDebug($"[create] setting {entity}'s alias id and linkid to ({aliasId.Value}, {linkId.Value})"); client.CurrentAliasId = aliasId.Value; client.AliasLinkId = linkId.Value; } // link was found but they don't have an exact alias else if (!aliasId.HasValue && linkId.HasValue) { entity.CurrentServer.Logger.WriteDebug($"[create] setting {entity}'s linkid to {linkId.Value}, but creating new alias"); client.AliasLinkId = linkId.Value; client.CurrentAlias = new EFAlias() { Name = entity.Name, DateAdded = DateTime.UtcNow, IPAddress = entity.IPAddress, LinkId = linkId.Value }; } // brand new players (supposedly) else { entity.CurrentServer.Logger.WriteDebug($"[create] creating new Link and Alias for {entity}"); var link = new EFAliasLink(); var alias = new EFAlias() { Name = entity.Name, DateAdded = DateTime.UtcNow, IPAddress = entity.IPAddress, Link = link }; link.Children.Add(alias); client.AliasLink = link; client.CurrentAlias = alias; } await context.SaveChangesAsync(); return(client); } }
public async Task OnLoadAsync(IManager manager) { // #if DO_IMPORT var svc = new GenericRepository <EFServer>(); svc.Insert(new EFServer() { Active = true, Port = 28960, ServerId = Math.Abs("127.0.0.1:28960".GetHashCode()), }); svc.Insert(new EFServer() { Active = true, Port = 28965, ServerId = Math.Abs("127.0.0.1:28965".GetHashCode()), }); svc.Insert(new EFServer() { Active = true, Port = 28970, ServerId = Math.Abs("127.0.0.1:28970".GetHashCode()), }); svc.SaveChanges(); // #endif Interval = DateTime.Now; var clients = new List <Player>(); var oldClients = new Dictionary <int, Player>(); #region CLIENTS if (File.Exists("import_clients.csv")) { manager.GetLogger().WriteVerbose("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].Substring(0, 5) == "01100" || fields[0] == string.Empty || fields[1] == string.Empty || fields[6] == string.Empty) { continue; } if (!Regex.Match(fields[6], @"^\d+\.\d+\.\d+.\d+$").Success) { fields[6] = "0"; } var client = new Player() { Name = fields[0], NetworkId = fields[1].ConvertLong(), IPAddress = fields[6].ConvertToIP(), Level = (Player.Permission)Convert.ToInt32(fields[3]), Connections = Convert.ToInt32(fields[5]), LastConnection = DateTime.Parse(fields[7]), }; clients.Add(client); oldClients.Add(Convert.ToInt32(fields[2]), client); } clients = clients.Distinct().ToList(); // #if DO_IMPORT /*clients = clients * .GroupBy(c => new { c.Name, c.IPAddress }) * .Select(c => c.FirstOrDefault()) * .ToList();*/ //newClients = clients.ToList(); //newClients.ForEach(c => c.ClientId = 0); manager.GetLogger().WriteVerbose($"Read {clients.Count} clients for import"); try { SharedLibrary.Database.Importer.ImportClients(clients); } catch (Exception e) { manager.GetLogger().WriteError("Saving imported clients failed"); } // #endif } #endregion // load the entire database lol var ctx = new DatabaseContext(); ctx.Configuration.ProxyCreationEnabled = false; var cls = ctx.Clients.Include("AliasLink.Children").ToList(); //manager.GetClientService().Find(c => c.Active).Result; ctx.Dispose(); #region ALIASES if (File.Exists("import_aliases.csv")) { manager.GetLogger().WriteVerbose("Beginning import of existing aliases"); var aliases = new List <EFAlias>(); var lines = File.ReadAllLines("import_aliases.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 != 3) { manager.GetLogger().WriteError("Invalid alias import file... aborting import"); return; } try { int number = Int32.Parse(fields[0]); var names = fields[1].Split(';').Where(n => n != String.Empty && n.Length > 2); var oldClient = oldClients[number]; var newClient = cls.FirstOrDefault(c => c.NetworkId == oldClient.NetworkId); foreach (string name in names) { // this is slow :D if (newClient.AliasLink.Children.FirstOrDefault(n => n.Name == name) != null) { continue; } var alias = new EFAlias() { Active = true, DateAdded = DateTime.UtcNow, Name = name, LinkId = newClient.AliasLinkId, IPAddress = newClient.IPAddress }; aliases.Add(alias); } } catch (KeyNotFoundException) { continue; } catch (Exception) { manager.GetLogger().WriteVerbose($"Could not import alias with line {line}"); } } SharedLibrary.Database.Importer.ImportSQLite(aliases); } #endregion #region PENALTIES if (File.Exists("import_penalties.csv")) { var penalties = new List <Penalty>(); manager.GetLogger().WriteVerbose("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].Substring(0, 5) == "01100" || fields[2].Contains("0000000")) { continue; } try { var expires = DateTime.Parse(fields[6]); var when = DateTime.Parse(fields[5]); var penaltyType = (Penalty.PenaltyType)Int32.Parse(fields[0]); if (penaltyType == Penalty.PenaltyType.Ban) { expires = DateTime.MaxValue; } var penalty = new Penalty() { Type = penaltyType, Expires = expires == DateTime.MinValue ? when : expires, Punisher = new SharedLibrary.Database.Models.EFClient() { NetworkId = fields[3].ConvertLong() }, Offender = new SharedLibrary.Database.Models.EFClient() { NetworkId = fields[2].ConvertLong() }, Offense = fields[1].Replace("\"", "").Trim(), Active = true, When = when, }; penalties.Add(penalty); } catch (Exception e) { manager.GetLogger().WriteVerbose($"Could not import penalty with line {line}"); } } //#if DO_IMPORT SharedLibrary.Database.Importer.ImportPenalties(penalties); manager.GetLogger().WriteVerbose($"Imported {penalties.Count} penalties"); //#endif } #endregion #region CHATHISTORY if (File.Exists("import_chathistory.csv")) { var chatHistory = new List <EFClientMessage>(); manager.GetLogger().WriteVerbose("Beginning import of existing messages"); foreach (string line in File.ReadAllLines("import_chathistory.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 != 4) { manager.GetLogger().WriteError("Invalid chat history import file... aborting import"); return; } try { int cId = Convert.ToInt32(fields[0]); var linkedClient = oldClients[cId]; var newcl = cls.FirstOrDefault(c => c.NetworkId == linkedClient.NetworkId); if (newcl == null) { newcl = cls.FirstOrDefault(c => c.Name == linkedClient.Name && c.IPAddress == linkedClient.IPAddress); } int newCId = newcl.ClientId; var chatMessage = new EFClientMessage() { Active = true, ClientId = newCId, Message = fields[1], TimeSent = DateTime.Parse(fields[3]), ServerId = Math.Abs($"127.0.0.1:{Convert.ToInt32(fields[2]).ToString()}".GetHashCode()) }; chatHistory.Add(chatMessage); } catch (Exception e) { manager.GetLogger().WriteVerbose($"Could not import chatmessage with line {line}"); } } manager.GetLogger().WriteVerbose($"Read {chatHistory.Count} messages for import"); SharedLibrary.Database.Importer.ImportSQLite(chatHistory); } #endregion #region STATS foreach (string file in Directory.GetFiles(Environment.CurrentDirectory)) { if (Regex.Match(file, @"import_stats_[0-9]+.csv").Success) { int port = Int32.Parse(Regex.Match(file, "[0-9]{5}").Value); var stats = new List <EFClientStatistics>(); manager.GetLogger().WriteVerbose("Beginning import of existing client stats"); var lines = File.ReadAllLines(file).Skip(1); foreach (string line in lines) { string[] fields = line.Split(','); if (fields.Length != 9) { manager.GetLogger().WriteError("Invalid client import file... aborting import"); return; } try { if (fields[0].Substring(0, 5) == "01100") { continue; } long id = fields[0].ConvertLong(); var client = cls.Single(c => c.NetworkId == id); var time = Convert.ToInt32(fields[8]); double spm = time < 60 ? 0 : Math.Round(Convert.ToInt32(fields[1]) * 100.0 / time, 3); if (spm > 1000) { spm = 0; } var st = new EFClientStatistics() { Active = true, ClientId = client.ClientId, ServerId = Math.Abs($"127.0.0.1:{port}".GetHashCode()), Kills = Convert.ToInt32(fields[1]), Deaths = Convert.ToInt32(fields[2]), SPM = spm, Skill = 0, TimePlayed = time * 60 }; // client.TotalConnectionTime += time; stats.Add(st); stats = stats.AsEnumerable() .GroupBy(c => new { c.ClientId }) .Select(c => c.FirstOrDefault()).ToList(); var cl = await manager.GetClientService().Get(st.ClientId); cl.TotalConnectionTime += time * 60; await manager.GetClientService().Update(cl); } catch (Exception e) { continue; } } manager.GetLogger().WriteVerbose($"Read {stats.Count} clients stats for import"); try { SharedLibrary.Database.Importer.ImportSQLite(stats); } catch (Exception e) { manager.GetLogger().WriteError("Saving imported stats failed"); } } } #endregion }
public async Task <EFClient> Create(EFClient entity) { entity.Name = entity.Name.CapClientName(EFAlias.MAX_NAME_LENGTH); if (!_appConfig.EnableImplicitAccountLinking) { return(await HandleNewCreate(entity)); } await using var context = _contextFactory.CreateContext(true); using (LogContext.PushProperty("Server", entity?.CurrentServer?.ToString())) { int?linkId = null; int?aliasId = null; if (entity.IPAddress != null) { var existingAliases = await context.Aliases .Select(_alias => new { _alias.AliasId, _alias.LinkId, _alias.IPAddress, _alias.Name }) .Where(_alias => _alias.IPAddress == entity.IPAddress) .ToListAsync(); if (existingAliases.Count > 0) { linkId = existingAliases.OrderBy(_alias => _alias.LinkId).First().LinkId; _logger.LogDebug("[create] client with new GUID {entity} has existing link {linkId}", entity.ToString(), linkId); var existingExactAlias = existingAliases.FirstOrDefault(_alias => _alias.Name == entity.Name); if (existingExactAlias != null) { _logger.LogDebug("[create] client with new GUID {entity} has existing alias {aliasId}", entity.ToString(), existingExactAlias.AliasId); aliasId = existingExactAlias.AliasId; } } } var client = new EFClient() { Level = Permission.User, FirstConnection = DateTime.UtcNow, LastConnection = DateTime.UtcNow, NetworkId = entity.NetworkId }; _logger.LogDebug("[create] adding {entity} to context", entity.ToString()); // they're just using a new GUID if (aliasId.HasValue) { _logger.LogDebug("[create] setting {entity}'s alias id and linkid to ({aliasId}, {linkId})", entity.ToString(), aliasId, linkId); client.CurrentAliasId = aliasId.Value; client.AliasLinkId = linkId.Value; } // link was found but they don't have an exact alias else if (!aliasId.HasValue && linkId.HasValue) { _logger.LogDebug("[create] setting {entity}'s linkid to {linkId}, but creating new alias", entity.ToString(), linkId); client.AliasLinkId = linkId.Value; client.CurrentAlias = new EFAlias() { Name = entity.Name, SearchableName = entity.Name.StripColors().ToLower(), DateAdded = DateTime.UtcNow, IPAddress = entity.IPAddress, LinkId = linkId.Value }; } // brand new players (supposedly) else { _logger.LogDebug("[create] creating new Link and Alias for {entity}", entity.ToString()); var link = new EFAliasLink(); var alias = new EFAlias() { Name = entity.Name, SearchableName = entity.Name.StripColors().ToLower(), DateAdded = DateTime.UtcNow, IPAddress = entity.IPAddress, Link = link }; client.AliasLink = link; client.CurrentAlias = alias; } context.Clients.Add(client); await context.SaveChangesAsync(); return(client); } }
private async Task UpdateAlias(string originalName, int?ip, Data.Models.Client.EFClient entity, DatabaseContext context) { { string name = originalName.CapClientName(EFAlias.MAX_NAME_LENGTH); // entity is the tracked db context item // get all aliases by IP address and LinkId var iqAliases = context.Aliases .Include(a => a.Link) // we only want alias that have the same IP address or share a link .Where(_alias => _alias.IPAddress == ip || (_alias.LinkId == entity.AliasLinkId)); var aliases = await iqAliases.ToListAsync(); var currentIPs = aliases.Where(_a2 => _a2.IPAddress != null).Select(_a2 => _a2.IPAddress).Distinct(); var floatingIPAliases = await context.Aliases.Where(_alias => currentIPs.Contains(_alias.IPAddress)) .ToListAsync(); aliases.AddRange(floatingIPAliases); // see if they have a matching IP + Name but new NetworkId var existingExactAlias = aliases.OrderBy(_alias => _alias.LinkId) .FirstOrDefault(a => a.Name == name && a.IPAddress == ip); bool hasExactAliasMatch = existingExactAlias != null; // if existing alias matches link them var newAliasLink = existingExactAlias?.Link; // if no exact matches find the first IP or LinkId that matches newAliasLink = newAliasLink ?? aliases.OrderBy(_alias => _alias.LinkId).FirstOrDefault()?.Link; // if no matches are found, use our current one ( it will become permanent ) newAliasLink = newAliasLink ?? entity.AliasLink; bool hasExistingAlias = aliases.Count > 0; bool isAliasLinkUpdated = newAliasLink.AliasLinkId != entity.AliasLink.AliasLinkId; await context.SaveChangesAsync(); int distinctLinkCount = aliases.Select(_alias => _alias.LinkId).Distinct().Count(); // this happens when the link we found is different than the one we create before adding an IP if (isAliasLinkUpdated || distinctLinkCount > 1) { _logger.LogDebug( "[updatealias] found a link for {entity} so we are updating link from {oldAliasLinkId} to {newAliasLinkId}", entity.ToString(), entity.AliasLink.AliasLinkId, newAliasLink.AliasLinkId); var completeAliasLinkIds = aliases.Select(_item => _item.LinkId) .Append(entity.AliasLinkId) .Distinct() .ToList(); _logger.LogDebug("[updatealias] updating aliasLinks {links} for IP {ip} to {linkId}", string.Join(',', completeAliasLinkIds), ip, newAliasLink.AliasLinkId); // update all the clients that have the old alias link await context.Clients .Where(_client => completeAliasLinkIds.Contains(_client.AliasLinkId)) .ForEachAsync(_client => _client.AliasLinkId = newAliasLink.AliasLinkId); // we also need to update all the penalties or they get deleted // scenario // link1 joins with ip1 // link2 joins with ip2, // link2 receives penalty // link2 joins with ip1 // pre existing link for link2 detected // link2 is deleted // link2 penalties are orphaned await context.Penalties .Where(_penalty => completeAliasLinkIds.Contains(_penalty.LinkId)) .ForEachAsync(_penalty => _penalty.LinkId = newAliasLink.AliasLinkId); entity.AliasLink = newAliasLink; entity.AliasLinkId = newAliasLink.AliasLinkId; // update all previous aliases await context.Aliases .Where(_alias => completeAliasLinkIds.Contains(_alias.LinkId)) .ForEachAsync(_alias => _alias.LinkId = newAliasLink.AliasLinkId); await context.SaveChangesAsync(); // we want to delete the now inactive alias if (newAliasLink.AliasLinkId != entity.AliasLinkId) { context.AliasLinks.Remove(entity.AliasLink); await context.SaveChangesAsync(); } } // the existing alias matches ip and name, so we can just ignore the temporary one if (hasExactAliasMatch) { _logger.LogDebug("[updatealias] {entity} has exact alias match", entity.ToString()); var oldAlias = entity.CurrentAlias; entity.CurrentAliasId = existingExactAlias.AliasId; entity.CurrentAlias = existingExactAlias; await context.SaveChangesAsync(); // the alias is the same so we can just remove it if (oldAlias.AliasId != existingExactAlias.AliasId && oldAlias.AliasId > 0) { await context.Clients .Where(_client => _client.CurrentAliasId == oldAlias.AliasId) .ForEachAsync(_client => _client.CurrentAliasId = existingExactAlias.AliasId); await context.SaveChangesAsync(); if (context.Entry(oldAlias).State != EntityState.Deleted) { _logger.LogDebug( "[updatealias] {entity} has exact alias match, so we're going to try to remove aliasId {aliasId} with linkId {linkId}", entity.ToString(), oldAlias.AliasId, oldAlias.LinkId); context.Aliases.Remove(oldAlias); await context.SaveChangesAsync(); } } } // theres no exact match, but they've played before with the GUID or IP else { _logger.LogDebug("[updatealias] {entity} is using a new alias", entity.ToString()); var newAlias = new EFAlias() { DateAdded = DateTime.UtcNow, IPAddress = ip, LinkId = newAliasLink.AliasLinkId, Name = name, SearchableName = name.StripColors().ToLower(), Active = true, }; entity.CurrentAlias = newAlias; entity.CurrentAliasId = 0; await context.SaveChangesAsync(); } } }