diff --git a/Application/IW4MServer.cs b/Application/IW4MServer.cs index 592df9af4..cd765a623 100644 --- a/Application/IW4MServer.cs +++ b/Application/IW4MServer.cs @@ -24,39 +24,13 @@ namespace IW4MAdmin { private static readonly Index loc = Utilities.CurrentLocalization.LocalizationIndex; private GameLogEventDetection LogEvent; + private DateTime SessionStart; public int Id { get; private set; } public IW4MServer(IManager mgr, ServerConfiguration cfg) : base(mgr, cfg) { } - //public override int EndPoint - //{ - // // hack: my laziness - // if ($"{IP}:{Port.ToString()}" == "66.150.121.184:28965") - // { - // return 886229536; - // } - - // if ($"{IP}:{Port.ToString()}" == "66.150.121.184:28960") - // { - // return 1645744423; - // } - - // if ($"{IP}:{Port.ToString()}" == "66.150.121.184:28970") - // { - // return 1645809959; - // } - - // if (Id == 0) - // { - // Id = HashCode.Combine(IP, Port); - // Id = Id < 0 ? Math.Abs(Id) : Id; - // } - - // return Id; - //} - override public async Task OnClientConnected(EFClient clientFromLog) { Logger.WriteDebug($"Client slot #{clientFromLog.ClientNumber} now reserved"); @@ -327,6 +301,13 @@ namespace IW4MAdmin else if (E.Type == GameEvent.EventType.PreDisconnect) { + if ((DateTime.UtcNow - SessionStart).TotalSeconds < 5) + { + Logger.WriteInfo($"Cancelling pre disconnect for {E.Origin} as it occured too close to map end"); + E.FailReason = GameEvent.EventFailReason.Invalid; + return false; + } + // predisconnect comes from minimal rcon polled players and minimal log players // so we need to disconnect the "full" version of the client var client = GetClientsAsList().FirstOrDefault(_client => _client.Equals(E.Origin)); @@ -421,6 +402,7 @@ namespace IW4MAdmin if (E.Type == GameEvent.EventType.MapEnd) { Logger.WriteInfo("Game ending..."); + SessionStart = DateTime.UtcNow; } if (E.Type == GameEvent.EventType.Tell) @@ -439,9 +421,12 @@ namespace IW4MAdmin #endif } - while (ChatHistory.Count > Math.Ceiling((double)ClientNum / 2)) + lock (ChatHistory) { - ChatHistory.RemoveAt(0); + while (ChatHistory.Count > Math.Ceiling(ClientNum / 2.0)) + { + ChatHistory.RemoveAt(0); + } } // the last client hasn't fully disconnected yet @@ -954,7 +939,7 @@ namespace IW4MAdmin ingameClient = Manager.GetServers() .Select(s => s.GetClientsAsList()) - .FirstOrDefault(l => l.FirstOrDefault(c => c.ClientId == Target.ClientId) != null) + .FirstOrDefault(l => l.FirstOrDefault(c => c.ClientId == Target?.ClientId) != null) ?.First(c => c.ClientId == Target.ClientId); if (ingameClient != null) @@ -987,7 +972,7 @@ namespace IW4MAdmin Active = true, When = DateTime.UtcNow, Link = Target.AliasLink, - AutomatedOffense = Origin.AdministeredPenalties.FirstOrDefault()?.AutomatedOffense + AutomatedOffense = Origin.AdministeredPenalties?.FirstOrDefault()?.AutomatedOffense }; await Manager.GetPenaltyService().Create(newPenalty); diff --git a/Application/RconParsers/T6MRConParser.cs b/Application/RconParsers/T6MRConParser.cs index 9f38ae7fd..f848e6849 100644 --- a/Application/RconParsers/T6MRConParser.cs +++ b/Application/RconParsers/T6MRConParser.cs @@ -19,9 +19,9 @@ namespace IW4MAdmin.Application.RconParsers { Tell = "tell {0} {1}", Say = "say {0}", - Kick = "clientKick {0}", - Ban = "clientKick {0}", - TempBan = "clientKick {0}" + Kick = "clientkick_for_reason {0} \"{1}\"", + Ban = "clientkick_for_reason {0} \"{1}\"", + TempBan = "clientkick_for_reason {0} \"{1}\"" }; public CommandPrefix GetCommandPrefixes() => Prefixes; diff --git a/DiscordWebhook/DiscordWebhook.pyproj b/DiscordWebhook/DiscordWebhook.pyproj index 786fe607c..1a4c2b85d 100644 --- a/DiscordWebhook/DiscordWebhook.pyproj +++ b/DiscordWebhook/DiscordWebhook.pyproj @@ -47,7 +47,6 @@ - True diff --git a/Plugins/Stats/Helpers/StatManager.cs b/Plugins/Stats/Helpers/StatManager.cs index 9ce1c9e46..2fade33f1 100644 --- a/Plugins/Stats/Helpers/StatManager.cs +++ b/Plugins/Stats/Helpers/StatManager.cs @@ -802,8 +802,8 @@ namespace IW4MAdmin.Plugins.Stats.Helpers { var clientStatsSet = ctx.Set(); - clientStatsSet.Update(attackerStats); - clientStatsSet.Update(victimStats); + clientStatsSet.Attach(attackerStats).State = EntityState.Modified; + clientStatsSet.Attach(victimStats).State = EntityState.Modified; await ctx.SaveChangesAsync(); } } diff --git a/RunPublishPre.cmd b/RunPublishPre.cmd index cbbd8baa3..0ffc3688e 100644 --- a/RunPublishPre.cmd +++ b/RunPublishPre.cmd @@ -1,8 +1,9 @@ -dotnet publish WebfrontCore/WebfrontCore.csproj -c Prerelease -o X:\IW4MAdmin\Publish\WindowsPrerelease -dotnet publish Application/Application.csproj -c Prerelease -o X:\IW4MAdmin\Publish\WindowsPrerelease +dotnet publish WebfrontCore/WebfrontCore.csproj -c Prerelease -o X:\IW4MAdmin\Publish\WindowsPrerelease /p:PublishProfile=Prerelease +dotnet publish Application/Application.csproj -c Prerelease -o X:\IW4MAdmin\Publish\WindowsPrerelease /p:PublishProfile=Prerelease dotnet publish GameLogServer/GameLogServer.pyproj -c Release -o X:\IW4MAdmin\Publish\WindowsPrerelease\GameLogServer +dotnet publish GameLogServer/DiscordWebhook.pyproj -c Release -o X:\IW4MAdmin\Publish\WindowsPrerelease\DiscordWebhook call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\Tools\VsDevCmd.bat" -msbuild GameLogServer/GameLogServer.pyproj /p:PublishProfile=FolderProfile /p:DeployOnBuild=true /p:PublishProfileRootFolder=X:\IW4MAdmin\GameLogServer\ -msbuild DiscordWebhook/DiscordWebhook.pyproj /p:PublishProfile=FolderProfile /p:DeployOnBuild=true /p:PublishProfileRootFolder=X:\IW4MAdmin\DiscordWebhook\ +msbuild GameLogServer/GameLogServer.pyproj /p:PublishProfile=PreRelease /p:DeployOnBuild=true /p:PublishProfileRootFolder=X:\IW4MAdmin\GameLogServer\ +msbuild DiscordWebhook/DiscordWebhook.pyproj /p:PublishProfile=PreRelease /p:DeployOnBuild=true /p:PublishProfileRootFolder=X:\IW4MAdmin\DiscordWebhook\ cd "X:\IW4MAdmin\DEPLOY\" PowerShell ".\upload_prerelease.ps1" \ No newline at end of file diff --git a/SharedLibraryCore/Objects/EFClient.cs b/SharedLibraryCore/Objects/EFClient.cs index 552f1ce92..64be97a29 100644 --- a/SharedLibraryCore/Objects/EFClient.cs +++ b/SharedLibraryCore/Objects/EFClient.cs @@ -75,11 +75,11 @@ namespace SharedLibraryCore.Database.Models { ConnectionTime = DateTime.UtcNow; ClientNumber = -1; - DelayedEvents = new Queue(); _additionalProperties = new Dictionary { { "_reportCount", 0 } }; + CurrentAlias = new EFAlias(); } public override string ToString() @@ -481,6 +481,7 @@ namespace SharedLibraryCore.Database.Models if (Level != Permission.Banned && currentBan.Type == Penalty.PenaltyType.Ban) { + CurrentServer.Logger.WriteInfo($"Banned client {this} connected using a new GUID"); // hack: re apply the automated offense to the reban if (currentBan.AutomatedOffense != null) { @@ -557,8 +558,7 @@ namespace SharedLibraryCore.Database.Models [NotMapped] public ClientState State { get; set; } - [NotMapped] - public Queue DelayedEvents { get; set; } + [NotMapped] // this is kinda dirty, but I need localizable level names public ClientPermission ClientPermission => new ClientPermission() diff --git a/SharedLibraryCore/Services/ClientService.cs b/SharedLibraryCore/Services/ClientService.cs index 59d0012a3..f7adb47ec 100644 --- a/SharedLibraryCore/Services/ClientService.cs +++ b/SharedLibraryCore/Services/ClientService.cs @@ -79,6 +79,8 @@ namespace SharedLibraryCore.Services if (hasExistingAlias && !entity.AliasLink.Active) { + entity.CurrentServer.Logger.WriteDebug($"Removing temporary alias for ${entity}"); + // we want to delete the temporary alias context.Entry(entity.CurrentAlias).State = EntityState.Deleted; entity.CurrentAlias = null; @@ -97,6 +99,8 @@ namespace SharedLibraryCore.Services // update the temporary alias to permanent one else if (!entity.AliasLink.Active) { + entity.CurrentServer.Logger.WriteDebug($"Linking permanent alias for ${entity}"); + // we want to track the current alias and link var alias = context.Update(entity.CurrentAlias).Entity; var _aliasLink = context.Update(entity.AliasLink).Entity; @@ -119,16 +123,15 @@ namespace SharedLibraryCore.Services Name = name, }; - var iqExistingPermission = context.Clients.Where(c => c.AliasLinkId == existingAlias.LinkId) - .OrderByDescending(client => client.Level) - .Select(c => new EFClient() { Level = c.Level }); + if (!hasExistingAlias) + { + entity.CurrentServer.Logger.WriteDebug($"Connecting player does not have an existing alias {entity}"); + } entity.Level = hasExistingAlias ? - (await iqExistingPermission.FirstOrDefaultAsync())?.Level ?? Permission.User : + await context.Clients.Where(c => c.AliasLinkId == existingAlias.LinkId) + .MaxAsync(c => c.Level) : Permission.User; -#if DEBUG - string sql = iqExistingPermission.AsQueryable().ToSql(); -#endif if (entity.CurrentAlias != existingAlias || entity.AliasLink != aliasLink) diff --git a/SharedLibraryCore/Services/PenaltyService.cs b/SharedLibraryCore/Services/PenaltyService.cs index 6f3824bd1..b024b48a9 100644 --- a/SharedLibraryCore/Services/PenaltyService.cs +++ b/SharedLibraryCore/Services/PenaltyService.cs @@ -1,13 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - +using Microsoft.EntityFrameworkCore; using SharedLibraryCore.Database; using SharedLibraryCore.Database.Models; using SharedLibraryCore.Dtos; -using Microsoft.EntityFrameworkCore; using SharedLibraryCore.Objects; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; using static SharedLibraryCore.Database.Models.EFClient; namespace SharedLibraryCore.Services @@ -87,6 +87,7 @@ namespace SharedLibraryCore.Services public async Task> GetRecentPenalties(int count, int offset, Penalty.PenaltyType showOnly = Penalty.PenaltyType.Any) { using (var context = new DatabaseContext(true)) + { return await context.Penalties .Include(p => p.Offender.CurrentAlias) .Include(p => p.Punisher.CurrentAlias) @@ -96,17 +97,20 @@ namespace SharedLibraryCore.Services .Skip(offset) .Take(count) .ToListAsync(); + } } public async Task> GetClientPenaltiesAsync(int clientId) { using (var context = new DatabaseContext(true)) + { return await context.Penalties .Where(p => p.OffenderId == clientId) .Where(p => p.Active) .Include(p => p.Offender.CurrentAlias) .Include(p => p.Punisher.CurrentAlias) .ToListAsync(); + } } /// @@ -146,7 +150,7 @@ namespace SharedLibraryCore.Services PunisherId = penalty.PunisherId, Offense = penalty.Offense, Type = penalty.Type.ToString(), - TimeRemaining = penalty.Expires.HasValue ? (now > penalty.Expires ? "" : penalty.Expires.ToString()) : DateTime.MaxValue.ToString(), + TimeRemaining = penalty.Expires.HasValue ? (now > penalty.Expires ? "" : penalty.Expires.ToString()) : DateTime.MaxValue.ToString(), AutomatedOffense = penalty.AutomatedOffense }, When = penalty.When, @@ -158,12 +162,15 @@ namespace SharedLibraryCore.Services { // todo: why does this have to be done? if (((PenaltyInfo)p.Value).Type.Length < 2) + { ((PenaltyInfo)p.Value).Type = ((Penalty.PenaltyType)Convert.ToInt32(((PenaltyInfo)p.Value).Type)).ToString(); + } var pi = ((PenaltyInfo)p.Value); if (pi.TimeRemaining?.Length > 0) + { pi.TimeRemaining = (DateTime.Parse(((PenaltyInfo)p.Value).TimeRemaining) - now).TimeSpanText(); - + } }); return list; } @@ -205,7 +212,9 @@ namespace SharedLibraryCore.Services { // todo: why does this have to be done? if (((PenaltyInfo)p.Value).Type.Length < 2) + { ((PenaltyInfo)p.Value).Type = ((Penalty.PenaltyType)Convert.ToInt32(((PenaltyInfo)p.Value).Type)).ToString(); + } }); return list; @@ -217,24 +226,33 @@ namespace SharedLibraryCore.Services { var now = DateTime.UtcNow; + Expression> filter = (p) => new Penalty.PenaltyType[] + { + Penalty.PenaltyType.TempBan, + Penalty.PenaltyType.Ban, Penalty.PenaltyType.Flag + }.Contains(p.Type) && + p.Active && + (p.Expires == null || p.Expires > now); + using (var context = new DatabaseContext(true)) { - var iqPenalties = context.Penalties - .Where(p => p.LinkId == linkId || - ip.HasValue ? p.Link.Children.Any(a => a.IPAddress == ip) : false) - .Where(p => p.Type == Penalty.PenaltyType.TempBan || - p.Type == Penalty.PenaltyType.Ban || - p.Type == Penalty.PenaltyType.Flag) - .Where(p => p.Active) - .Where(p => p.Expires == null || p.Expires > now); - + var iqLinkPenalties = context.Penalties + .Where(p => p.LinkId == linkId) + .Where(filter); + + var iqIPPenalties = context.Aliases + .Where(a => a.IPAddress == ip) + .SelectMany(a => a.Link.ReceivedPenalties) + .Where(filter); + #if DEBUG == true - var penaltiesSql = iqPenalties.ToSql(); + var penaltiesSql = iqLinkPenalties.ToSql(); + var ipPenaltiesSql = iqIPPenalties.ToSql(); #endif - var activePenalties = await iqPenalties.ToListAsync(); + var activePenalties = (await iqLinkPenalties.ToListAsync()).Union(await iqIPPenalties.ToListAsync()); // this is a bit more performant in memory (ordering) - return activePenalties.OrderByDescending(p =>p.When).ToList(); + return activePenalties.OrderByDescending(p => p.When).ToList(); } } diff --git a/WebfrontCore/Controllers/ClientController.cs b/WebfrontCore/Controllers/ClientController.cs index 49985bf30..ee78921fd 100644 --- a/WebfrontCore/Controllers/ClientController.cs +++ b/WebfrontCore/Controllers/ClientController.cs @@ -120,7 +120,8 @@ namespace WebfrontCore.Controllers public async Task PrivilegedAsync() { var admins = (await Manager.GetClientService().GetPrivilegedClients()) - .GroupBy(a => a.AliasLinkId).Select(_clients => _clients.OrderBy(_client => _client.LastConnection).LastOrDefault()) + .GroupBy(a => a.AliasLinkId).Where(_clients => _clients.FirstOrDefault(_client => _client.LastConnection == _clients.Max(c => c.LastConnection)) != null) + .Select(_client => _client.First()) .OrderByDescending(_client => _client.Level); var adminsDict = new Dictionary>(); diff --git a/WebfrontCore/Controllers/ConsoleController.cs b/WebfrontCore/Controllers/ConsoleController.cs index 45e53d766..4f7fd2064 100644 --- a/WebfrontCore/Controllers/ConsoleController.cs +++ b/WebfrontCore/Controllers/ConsoleController.cs @@ -34,7 +34,10 @@ namespace WebfrontCore.Controllers ClientId = Client.ClientId, Level = Client.Level, CurrentServer = server, - Name = Client.Name + CurrentAlias = new EFAlias() + { + Name = Client.Name + } }; var remoteEvent = new GameEvent() @@ -55,7 +58,9 @@ namespace WebfrontCore.Controllers // remove the added command response for (int i = 0; i < response.Count; i++) + { server.CommandResult.Remove(response[i]); + } } else