diff --git a/Application/Application.csproj b/Application/Application.csproj index 234f718bf..b42f32614 100644 --- a/Application/Application.csproj +++ b/Application/Application.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.1 + net6.0 false RaidMax.IW4MAdmin.Application 2020.0.0.0 @@ -25,13 +25,13 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + diff --git a/Application/ApplicationManager.cs b/Application/ApplicationManager.cs index fd9a4033a..37086a546 100644 --- a/Application/ApplicationManager.cs +++ b/Application/ApplicationManager.cs @@ -551,7 +551,7 @@ namespace IW4MAdmin.Application _servers.Add(ServerInstance); Console.WriteLine(Utilities.CurrentLocalization.LocalizationIndex["MANAGER_MONITORING_TEXT"].FormatExt(ServerInstance.Hostname.StripColors())); - _logger.LogInformation("Finishing initialization and now monitoring [{server}]", ServerInstance.Hostname, ServerInstance.ToString()); + _logger.LogInformation("Finishing initialization and now monitoring [{Server}]", ServerInstance.Hostname); } // add the start event for this server diff --git a/Application/Extensions/StartupExtensions.cs b/Application/Extensions/StartupExtensions.cs index 4e0e2242a..96517db95 100644 --- a/Application/Extensions/StartupExtensions.cs +++ b/Application/Extensions/StartupExtensions.cs @@ -78,8 +78,10 @@ namespace IW4MAdmin.Application.Extensions case "mysql": var appendTimeout = !appConfig.ConnectionString.Contains("default command timeout", StringComparison.InvariantCultureIgnoreCase); + var connectionString = + appConfig.ConnectionString + (appendTimeout ? ";default command timeout=0" : ""); services.AddSingleton(sp => (DbContextOptions) new DbContextOptionsBuilder() - .UseMySql(appConfig.ConnectionString + (appendTimeout ? ";default command timeout=0" : ""), + .UseMySql(connectionString, ServerVersion.AutoDetect(connectionString), mysqlOptions => mysqlOptions.EnableRetryOnFailure()) .UseLoggerFactory(sp.GetRequiredService()).Options); return services; @@ -101,4 +103,4 @@ namespace IW4MAdmin.Application.Extensions } } } -} \ No newline at end of file +} diff --git a/Application/IW4MServer.cs b/Application/IW4MServer.cs index a3898d83f..9ec82a5c4 100644 --- a/Application/IW4MServer.cs +++ b/Application/IW4MServer.cs @@ -53,7 +53,9 @@ namespace IW4MAdmin IServiceProvider serviceProvider, IClientNoticeMessageFormatter messageFormatter, ILookupCache serverCache) : base(serviceProvider.GetRequiredService>(), +#pragma warning disable CS0612 serviceProvider.GetRequiredService(), +#pragma warning restore CS0612 serverConfiguration, serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService(), diff --git a/Application/Main.cs b/Application/Main.cs index 70acbd4f1..11d784faa 100644 --- a/Application/Main.cs +++ b/Application/Main.cs @@ -412,7 +412,9 @@ namespace IW4MAdmin.Application .AddSingleton() .AddSingleton() .AddSingleton() +#pragma warning disable CS0612 .AddSingleton() +#pragma warning restore CS0612 .AddSingleton() .AddSingleton() .AddSingleton() @@ -449,4 +451,4 @@ namespace IW4MAdmin.Application return collection.GetRequiredService>(); } } -} \ No newline at end of file +} diff --git a/Application/Misc/BaseConfigurationHandler.cs b/Application/Misc/BaseConfigurationHandler.cs index 9aaedb061..17dc053d1 100644 --- a/Application/Misc/BaseConfigurationHandler.cs +++ b/Application/Misc/BaseConfigurationHandler.cs @@ -52,7 +52,7 @@ namespace IW4MAdmin.Application.Misc } } - public Task Save() + public async Task Save() { var settings = new JsonSerializerSettings() { @@ -60,8 +60,8 @@ namespace IW4MAdmin.Application.Misc }; settings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter()); - var appConfigJSON = JsonConvert.SerializeObject(_configuration, settings); - return File.WriteAllTextAsync(FileName, appConfigJSON); + var appConfigJson = JsonConvert.SerializeObject(_configuration, settings); + await File.WriteAllTextAsync(FileName, appConfigJson); } public T Configuration() diff --git a/Application/Misc/MasterCommunication.cs b/Application/Misc/MasterCommunication.cs index fcc21053e..74e2045b1 100644 --- a/Application/Misc/MasterCommunication.cs +++ b/Application/Misc/MasterCommunication.cs @@ -93,9 +93,6 @@ namespace IW4MAdmin.Application.Misc public async Task RunUploadStatus(CancellationToken token) { - // todo: clean up this logic - bool connected; - while (!token.IsCancellationRequested) { try @@ -103,40 +100,11 @@ namespace IW4MAdmin.Application.Misc await UploadStatus(); } - catch (System.Net.Http.HttpRequestException e) + catch (Exception ex) { - _logger.LogWarning(e, "Could not send heartbeat"); + _logger.LogWarning(ex, "Could not send heartbeat"); } - catch (AggregateException e) - { - _logger.LogWarning(e, "Could not send heartbeat"); - var exceptions = e.InnerExceptions.Where(ex => ex.GetType() == typeof(ApiException)); - - foreach (var ex in exceptions) - { - if (((ApiException)ex).StatusCode == System.Net.HttpStatusCode.Unauthorized) - { - connected = false; - } - } - } - - catch (ApiException e) - { - _logger.LogWarning(e, "Could not send heartbeat"); - if (e.StatusCode == System.Net.HttpStatusCode.Unauthorized) - { - connected = false; - } - } - - catch (Exception e) - { - _logger.LogWarning(e, "Could not send heartbeat"); - } - - try { await Task.Delay(30000, token); diff --git a/Application/Misc/PluginImporter.cs b/Application/Misc/PluginImporter.cs index 453b6b987..c4fb091a0 100644 --- a/Application/Misc/PluginImporter.cs +++ b/Application/Misc/PluginImporter.cs @@ -83,19 +83,47 @@ namespace IW4MAdmin.Application.Misc .GroupBy(_assembly => _assembly.FullName).Select(_assembly => _assembly.OrderByDescending(_assembly => _assembly.GetName().Version).First()); pluginTypes = assemblies - .SelectMany(_asm => _asm.GetTypes()) + .SelectMany(_asm => + { + try + { + return _asm.GetTypes(); + } + catch + { + return Enumerable.Empty(); + } + }) .Where(_assemblyType => _assemblyType.GetInterface(nameof(IPlugin), false) != null); _logger.LogDebug("Discovered {count} plugin implementations", pluginTypes.Count()); commandTypes = assemblies - .SelectMany(_asm => _asm.GetTypes()) + .SelectMany(_asm =>{ + try + { + return _asm.GetTypes(); + } + catch + { + return Enumerable.Empty(); + } + }) .Where(_assemblyType => _assemblyType.IsClass && _assemblyType.BaseType == typeof(Command)); _logger.LogDebug("Discovered {count} plugin commands", commandTypes.Count()); configurationTypes = assemblies - .SelectMany(asm => asm.GetTypes()) + .SelectMany(asm => { + try + { + return asm.GetTypes(); + } + catch + { + return Enumerable.Empty(); + } + }) .Where(asmType => asmType.IsClass && asmType.GetInterface(nameof(IBaseConfiguration), false) != null); diff --git a/Application/Misc/TokenAuthentication.cs b/Application/Misc/TokenAuthentication.cs index 1fbe99579..d87846d4e 100644 --- a/Application/Misc/TokenAuthentication.cs +++ b/Application/Misc/TokenAuthentication.cs @@ -7,26 +7,26 @@ using System.Text; namespace IW4MAdmin.Application.Misc { - class TokenAuthentication : ITokenAuthentication + internal class TokenAuthentication : ITokenAuthentication { private readonly ConcurrentDictionary _tokens; - private readonly RNGCryptoServiceProvider _random; - private readonly static TimeSpan _timeoutPeriod = new TimeSpan(0, 0, 120); - private const short TOKEN_LENGTH = 4; + private readonly RandomNumberGenerator _random; + private static readonly TimeSpan TimeoutPeriod = new TimeSpan(0, 0, 120); + private const short TokenLength = 4; public TokenAuthentication() { _tokens = new ConcurrentDictionary(); - _random = new RNGCryptoServiceProvider(); + _random = RandomNumberGenerator.Create(); } public bool AuthorizeToken(long networkId, string token) { - bool authorizeSuccessful = _tokens.ContainsKey(networkId) && _tokens[networkId].Token == token; + var authorizeSuccessful = _tokens.ContainsKey(networkId) && _tokens[networkId].Token == token; if (authorizeSuccessful) { - _tokens.TryRemove(networkId, out TokenState _); + _tokens.TryRemove(networkId, out _); } return authorizeSuccessful; @@ -34,15 +34,15 @@ namespace IW4MAdmin.Application.Misc public TokenState GenerateNextToken(long networkId) { - TokenState state = null; + TokenState state; if (_tokens.ContainsKey(networkId)) { state = _tokens[networkId]; - if ((DateTime.Now - state.RequestTime) > _timeoutPeriod) + if ((DateTime.Now - state.RequestTime) > TimeoutPeriod) { - _tokens.TryRemove(networkId, out TokenState _); + _tokens.TryRemove(networkId, out _); } else @@ -51,11 +51,11 @@ namespace IW4MAdmin.Application.Misc } } - state = new TokenState() + state = new TokenState { NetworkId = networkId, Token = _generateToken(), - TokenDuration = _timeoutPeriod + TokenDuration = TimeoutPeriod }; _tokens.TryAdd(networkId, state); @@ -63,31 +63,31 @@ namespace IW4MAdmin.Application.Misc // perform some housekeeping so we don't have built up tokens if they're not ever used foreach (var (key, value) in _tokens) { - if ((DateTime.Now - value.RequestTime) > _timeoutPeriod) + if ((DateTime.Now - value.RequestTime) > TimeoutPeriod) { - _tokens.TryRemove(key, out TokenState _); + _tokens.TryRemove(key, out _); } } return state; } - public string _generateToken() + private string _generateToken() { - bool validCharacter(char c) + bool ValidCharacter(char c) { // this ensure that the characters are 0-9, A-Z, a-z return (c > 47 && c < 58) || (c > 64 && c < 91) || (c > 96 && c < 123); } - StringBuilder token = new StringBuilder(); + var token = new StringBuilder(); - while (token.Length < TOKEN_LENGTH) + var charSet = new byte[1]; + while (token.Length < TokenLength) { - byte[] charSet = new byte[1]; _random.GetBytes(charSet); - if (validCharacter((char)charSet[0])) + if (ValidCharacter((char)charSet[0])) { token.Append((char)charSet[0]); } diff --git a/Data/Data.csproj b/Data/Data.csproj index 0b847b381..49f5e201d 100644 --- a/Data/Data.csproj +++ b/Data/Data.csproj @@ -1,30 +1,27 @@  - netcoreapp3.1 + net6.0 Debug;Release;Prerelease AnyCPU true RaidMax.IW4MAdmin.Data RaidMax.IW4MAdmin.Data - 1.1.0 + 1.2.0 - - + + + all runtime; build; native; contentfiles - - - - - - - - + + + + diff --git a/Data/MigrationContext/MySqlDatabaseContext.cs b/Data/MigrationContext/MySqlDatabaseContext.cs index 785869c82..85eb083be 100644 --- a/Data/MigrationContext/MySqlDatabaseContext.cs +++ b/Data/MigrationContext/MySqlDatabaseContext.cs @@ -24,10 +24,10 @@ namespace Data.MigrationContext { if (MigrationExtensions.IsMigration) { - optionsBuilder.UseMySql("Server=127.0.0.1;Database=IW4MAdmin_Migration;Uid=root;Pwd=password;") - .EnableDetailedErrors(true) - .EnableSensitiveDataLogging(true); + optionsBuilder.UseMySql(ServerVersion.AutoDetect("Server=127.0.0.1;Database=IW4MAdmin_Migration;Uid=root;Pwd=password;")) + .EnableDetailedErrors() + .EnableSensitiveDataLogging(); } } } -} \ No newline at end of file +} diff --git a/Data/Models/Vector3.cs b/Data/Models/Vector3.cs index c4c53af59..9b614210b 100644 --- a/Data/Models/Vector3.cs +++ b/Data/Models/Vector3.cs @@ -1,6 +1,7 @@ using System; using System.ComponentModel.DataAnnotations; using System.Text.RegularExpressions; +// ReSharper disable CompareOfFloatsByEqualityOperator namespace Data.Models { @@ -28,7 +29,9 @@ namespace Data.Models return $"({X}, {Y}, {Z})"; } +#pragma warning disable CS0659 public override bool Equals(object obj) +#pragma warning restore CS0659 { if (obj is Vector3 vec) { @@ -110,4 +113,4 @@ namespace Data.Models public double AngleBetween(Vector3 a) => Math.Acos(this.DotProduct(a) / (a.Magnitude() * this.Magnitude())); } -} \ No newline at end of file +} diff --git a/Integrations/Cod/Integrations.Cod.csproj b/Integrations/Cod/Integrations.Cod.csproj index fa9b57d76..d001723b0 100644 --- a/Integrations/Cod/Integrations.Cod.csproj +++ b/Integrations/Cod/Integrations.Cod.csproj @@ -1,7 +1,7 @@ - netcoreapp3.1 + net6.0 Integrations.Cod Integrations.Cod Debug;Release;Prerelease diff --git a/Integrations/Source/Integrations.Source.csproj b/Integrations/Source/Integrations.Source.csproj index da504ccd8..cd1746362 100644 --- a/Integrations/Source/Integrations.Source.csproj +++ b/Integrations/Source/Integrations.Source.csproj @@ -1,7 +1,7 @@ - netcoreapp3.1 + net6.0 Integrations.Source Integrations.Source Debug;Release;Prerelease diff --git a/Plugins/AutomessageFeed/AutomessageFeed.csproj b/Plugins/AutomessageFeed/AutomessageFeed.csproj index 84436442c..d371859c9 100644 --- a/Plugins/AutomessageFeed/AutomessageFeed.csproj +++ b/Plugins/AutomessageFeed/AutomessageFeed.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + net6.0 true Latest Debug;Release;Prerelease @@ -10,7 +10,7 @@ - + diff --git a/Plugins/IW4ScriptCommands/IW4ScriptCommands.csproj b/Plugins/IW4ScriptCommands/IW4ScriptCommands.csproj index 00beedf57..6527c6fcb 100644 --- a/Plugins/IW4ScriptCommands/IW4ScriptCommands.csproj +++ b/Plugins/IW4ScriptCommands/IW4ScriptCommands.csproj @@ -2,7 +2,7 @@ Library - netcoreapp3.1 + net6.0 Debug;Release;Prerelease @@ -10,7 +10,7 @@ - + diff --git a/Plugins/LiveRadar/LiveRadar.csproj b/Plugins/LiveRadar/LiveRadar.csproj index 32c46f165..c92da8c81 100644 --- a/Plugins/LiveRadar/LiveRadar.csproj +++ b/Plugins/LiveRadar/LiveRadar.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + net6.0 true true false @@ -23,7 +23,7 @@ - + diff --git a/Plugins/LiveRadar/RadarEvent.cs b/Plugins/LiveRadar/RadarEvent.cs index 3dcf8bfab..554d91063 100644 --- a/Plugins/LiveRadar/RadarEvent.cs +++ b/Plugins/LiveRadar/RadarEvent.cs @@ -2,6 +2,7 @@ using SharedLibraryCore; using System; using System.Linq; +// ReSharper disable CompareOfFloatsByEqualityOperator namespace LiveRadar { @@ -22,7 +23,9 @@ namespace LiveRadar public Vector3 RadianAngles => new Vector3(ViewAngles.X.ToRadians(), ViewAngles.Y.ToRadians(), ViewAngles.Z.ToRadians()); public int Id => GetHashCode(); +#pragma warning disable CS0659 public override bool Equals(object obj) +#pragma warning restore CS0659 { if (obj is RadarEvent re) { diff --git a/Plugins/Login/Login.csproj b/Plugins/Login/Login.csproj index 63d4f83ea..6ba24dd5d 100644 --- a/Plugins/Login/Login.csproj +++ b/Plugins/Login/Login.csproj @@ -2,7 +2,7 @@ Library - netcoreapp3.1 + net6.0 false @@ -19,7 +19,7 @@ - + diff --git a/Plugins/ProfanityDeterment/ProfanityDeterment.csproj b/Plugins/ProfanityDeterment/ProfanityDeterment.csproj index 0aa2fab4f..95a2497b5 100644 --- a/Plugins/ProfanityDeterment/ProfanityDeterment.csproj +++ b/Plugins/ProfanityDeterment/ProfanityDeterment.csproj @@ -2,7 +2,7 @@ Library - netcoreapp3.1 + net6.0 RaidMax.IW4MAdmin.Plugins.ProfanityDeterment @@ -16,7 +16,7 @@ - + diff --git a/Plugins/Stats/Client/HitCalculator.cs b/Plugins/Stats/Client/HitCalculator.cs index f70f50f74..276c8e285 100644 --- a/Plugins/Stats/Client/HitCalculator.cs +++ b/Plugins/Stats/Client/HitCalculator.cs @@ -201,7 +201,7 @@ namespace IW4MAdmin.Plugins.Stats.Client } catch (Exception ex) { - _logger.LogError(ex, "Could not retrieve previous hit data for client {client}"); + _logger.LogError(ex, "Could not retrieve previous hit data for client {Client}", hitInfo.EntityId); continue; } @@ -613,4 +613,4 @@ namespace IW4MAdmin.Plugins.Stats.Client } } } -} \ No newline at end of file +} diff --git a/Plugins/Stats/Commands/MostKillsCommand.cs b/Plugins/Stats/Commands/MostKillsCommand.cs index cddf45f1f..5ff543eb0 100644 --- a/Plugins/Stats/Commands/MostKillsCommand.cs +++ b/Plugins/Stats/Commands/MostKillsCommand.cs @@ -17,7 +17,6 @@ namespace IW4MAdmin.Plugins.Stats.Commands class MostKillsCommand : Command { private readonly IDatabaseContextFactory _contextFactory; - private readonly CommandConfiguration _config; public MostKillsCommand(CommandConfiguration config, ITranslationLookup translationLookup, IDatabaseContextFactory contextFactory) : base(config, translationLookup) @@ -28,7 +27,6 @@ namespace IW4MAdmin.Plugins.Stats.Commands Permission = EFClient.Permission.User; _contextFactory = contextFactory; - _config = config; } public override async Task ExecuteAsync(GameEvent E) @@ -81,4 +79,4 @@ namespace IW4MAdmin.Plugins.Stats.Commands .Prepend(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_MOSTKILLS_HEADER"]); } } -} \ No newline at end of file +} diff --git a/Plugins/Stats/Commands/MostPlayedCommand.cs b/Plugins/Stats/Commands/MostPlayedCommand.cs index 8966d4bd4..a45dc71b1 100644 --- a/Plugins/Stats/Commands/MostPlayedCommand.cs +++ b/Plugins/Stats/Commands/MostPlayedCommand.cs @@ -56,7 +56,6 @@ namespace IW4MAdmin.Plugins.Stats.Commands return mostPlayed; } - private readonly CommandConfiguration _config; private readonly IDatabaseContextFactory _contextFactory; public MostPlayedCommand(CommandConfiguration config, ITranslationLookup translationLookup, @@ -68,7 +67,6 @@ namespace IW4MAdmin.Plugins.Stats.Commands Permission = EFClient.Permission.User; RequiresTarget = false; - _config = config; _contextFactory = contextFactory; } @@ -91,4 +89,4 @@ namespace IW4MAdmin.Plugins.Stats.Commands } } } -} \ No newline at end of file +} diff --git a/Plugins/Stats/Helpers/StatManager.cs b/Plugins/Stats/Helpers/StatManager.cs index 4ec34fea6..3ea142cbc 100644 --- a/Plugins/Stats/Helpers/StatManager.cs +++ b/Plugins/Stats/Helpers/StatManager.cs @@ -21,7 +21,7 @@ using Data.Models.Server; using Humanizer.Localisation; using Microsoft.Data.Sqlite; using Microsoft.Extensions.Logging; -using MySql.Data.MySqlClient; +using MySqlConnector; using Npgsql; using Stats.Client.Abstractions; using Stats.Config; diff --git a/Plugins/Stats/Stats.csproj b/Plugins/Stats/Stats.csproj index 857431984..528984475 100644 --- a/Plugins/Stats/Stats.csproj +++ b/Plugins/Stats/Stats.csproj @@ -2,7 +2,7 @@ Library - netcoreapp3.1 + net6.0 RaidMax.IW4MAdmin.Plugins.Stats @@ -17,7 +17,7 @@ - + diff --git a/Plugins/Welcome/Plugin.cs b/Plugins/Welcome/Plugin.cs index 7015c031f..36e6210ab 100644 --- a/Plugins/Welcome/Plugin.cs +++ b/Plugins/Welcome/Plugin.cs @@ -6,6 +6,7 @@ using SharedLibraryCore.Database.Models; using System.Linq; using Microsoft.EntityFrameworkCore; using System.Net; +using System.Net.Http; using Newtonsoft.Json.Linq; using Humanizer; using Data.Abstractions; @@ -105,11 +106,11 @@ namespace IW4MAdmin.Plugins.Welcome /// private async Task GetCountryName(string ip) { - using var wc = new WebClient(); + using var wc = new HttpClient(); try { var response = - await wc.DownloadStringTaskAsync(new Uri($"http://extreme-ip-lookup.com/json/{ip}?key=demo")); + await wc.GetStringAsync(new Uri($"http://extreme-ip-lookup.com/json/{ip}?key=demo")); var responseObj = JObject.Parse(response); response = responseObj["country"]?.ToString(); @@ -124,4 +125,4 @@ namespace IW4MAdmin.Plugins.Welcome } } } -} \ No newline at end of file +} diff --git a/Plugins/Welcome/Welcome.csproj b/Plugins/Welcome/Welcome.csproj index 23970e317..eb70f19ba 100644 --- a/Plugins/Welcome/Welcome.csproj +++ b/Plugins/Welcome/Welcome.csproj @@ -2,7 +2,7 @@ Library - netcoreapp3.1 + net6.0 RaidMax.IW4MAdmin.Plugins.Welcome @@ -20,7 +20,7 @@ - + diff --git a/SharedLibraryCore/BaseController.cs b/SharedLibraryCore/BaseController.cs index 88f051567..ef4c674ff 100644 --- a/SharedLibraryCore/BaseController.cs +++ b/SharedLibraryCore/BaseController.cs @@ -1,38 +1,35 @@ -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authentication.Cookies; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Filters; -using SharedLibraryCore.Database.Models; -using SharedLibraryCore.Dtos; -using SharedLibraryCore.Interfaces; -using System; +using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Security.Claims; using System.Threading.Tasks; using Data.Context; using Data.Models; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; using SharedLibraryCore.Configuration; +using SharedLibraryCore.Database.Models; +using SharedLibraryCore.Dtos; +using SharedLibraryCore.Interfaces; +using SharedLibraryCore.Localization; namespace SharedLibraryCore { public class BaseController : Controller { /// - /// life span in months + /// life span in months /// private const int COOKIE_LIFESPAN = 3; - public IManager Manager { get; private set; } - protected readonly DatabaseContext Context; - protected bool Authorized { get; set; } - protected Localization.TranslationLookup Localization { get; private set; } - protected EFClient Client { get; private set; } private static readonly byte[] LocalHost = { 127, 0, 0, 1 }; private static string SocialLink; private static string SocialTitle; + protected readonly DatabaseContext Context; protected List Pages; - protected ApplicationConfiguration AppConfig { get; } public BaseController(IManager manager) { @@ -46,7 +43,7 @@ namespace SharedLibraryCore SocialTitle = AppConfig.SocialLinkTitle; } - + Pages = Manager.GetPageList().Pages .Select(page => new Page { @@ -59,23 +56,30 @@ namespace SharedLibraryCore ViewBag.EnableColorCodes = AppConfig.EnableColorCodes; ViewBag.Language = Utilities.CurrentLocalization.Culture.TwoLetterISOLanguageName; - Client ??= new EFClient() + Client ??= new EFClient { ClientId = -1, - Level = EFClient.Permission.Banned, - CurrentAlias = new EFAlias() { Name = "Webfront Guest" } + Level = Data.Models.Client.EFClient.Permission.Banned, + CurrentAlias = new EFAlias { Name = "Webfront Guest" } }; } + public IManager Manager { get; } + protected bool Authorized { get; set; } + protected TranslationLookup Localization { get; } + protected EFClient Client { get; } + protected ApplicationConfiguration AppConfig { get; } + protected async Task SignInAsync(ClaimsPrincipal claimsPrinciple) { - await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, claimsPrinciple, new AuthenticationProperties() - { - AllowRefresh = true, - ExpiresUtc = DateTime.UtcNow.AddMonths(COOKIE_LIFESPAN), - IsPersistent = true, - IssuedUtc = DateTime.UtcNow - }); + await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, claimsPrinciple, + new AuthenticationProperties + { + AllowRefresh = true, + ExpiresUtc = DateTime.UtcNow.AddMonths(COOKIE_LIFESPAN), + IsPersistent = true, + IssuedUtc = DateTime.UtcNow + }); } public override void OnActionExecuting(ActionExecutingContext context) @@ -84,21 +88,27 @@ namespace SharedLibraryCore { try { - int clientId = Convert.ToInt32(User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Sid)?.Value ?? "-1"); + var clientId = + Convert.ToInt32(User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Sid)?.Value ?? "-1"); if (clientId > 0) { Client.ClientId = clientId; - Client.NetworkId = clientId == 1 ? 0 : User.Claims.First(_claim => _claim.Type == ClaimTypes.PrimarySid).Value.ConvertGuidToLong(System.Globalization.NumberStyles.HexNumber); - Client.Level = (EFClient.Permission)Enum.Parse(typeof(EFClient.Permission), User.Claims.First(c => c.Type == ClaimTypes.Role).Value); - Client.CurrentAlias = new EFAlias() { Name = User.Claims.First(c => c.Type == ClaimTypes.NameIdentifier).Value }; + Client.NetworkId = clientId == 1 + ? 0 + : User.Claims.First(_claim => _claim.Type == ClaimTypes.PrimarySid).Value + .ConvertGuidToLong(NumberStyles.HexNumber); + Client.Level = (Data.Models.Client.EFClient.Permission)Enum.Parse( + typeof(Data.Models.Client.EFClient.Permission), + User.Claims.First(c => c.Type == ClaimTypes.Role).Value); + Client.CurrentAlias = new EFAlias + { Name = User.Claims.First(c => c.Type == ClaimTypes.NameIdentifier).Value }; Authorized = Client.ClientId >= 0; } } catch (InvalidOperationException) { - } catch (KeyNotFoundException) @@ -112,25 +122,25 @@ namespace SharedLibraryCore else if (!HttpContext.Request.Headers.ContainsKey("X-Forwarded-For")) { Client.ClientId = 1; - Client.Level = EFClient.Permission.Console; - Client.CurrentAlias = new EFAlias() { Name = "IW4MAdmin" }; + Client.Level = Data.Models.Client.EFClient.Permission.Console; + Client.CurrentAlias = new EFAlias { Name = "IW4MAdmin" }; Authorized = true; var claims = new[] { new Claim(ClaimTypes.NameIdentifier, Client.CurrentAlias.Name), - new Claim(ClaimTypes.Role, Client.Level.ToString()), - new Claim(ClaimTypes.Sid, Client.ClientId.ToString()), - new Claim(ClaimTypes.PrimarySid, Client.NetworkId.ToString("X")) + new Claim(ClaimTypes.Role, Client.Level.ToString()), + new Claim(ClaimTypes.Sid, Client.ClientId.ToString()), + new Claim(ClaimTypes.PrimarySid, Client.NetworkId.ToString("X")) }; var claimsIdentity = new ClaimsIdentity(claims, "login"); SignInAsync(new ClaimsPrincipal(claimsIdentity)).Wait(); } var communityName = AppConfig.CommunityInformation?.Name; - var shouldUseCommunityName = !string.IsNullOrWhiteSpace(communityName) - && !communityName.Contains("IW4MAdmin") + var shouldUseCommunityName = !string.IsNullOrWhiteSpace(communityName) + && !communityName.Contains("IW4MAdmin") && AppConfig.CommunityInformation.IsEnabled; - + ViewBag.Authorized = Authorized; ViewBag.Url = AppConfig.WebfrontUrl; ViewBag.User = Client; @@ -150,4 +160,4 @@ namespace SharedLibraryCore base.OnActionExecuting(context); } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Command.cs b/SharedLibraryCore/Command.cs index a81ddf750..ef72b036b 100644 --- a/SharedLibraryCore/Command.cs +++ b/SharedLibraryCore/Command.cs @@ -1,9 +1,9 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Data.Models.Client; using SharedLibraryCore.Commands; using SharedLibraryCore.Configuration; -using SharedLibraryCore.Database.Models; using SharedLibraryCore.Interfaces; using static SharedLibraryCore.Server; using ILogger = Microsoft.Extensions.Logging.ILogger; @@ -11,13 +11,17 @@ using ILogger = Microsoft.Extensions.Logging.ILogger; namespace SharedLibraryCore { /// - /// Abstract class for command + /// Abstract class for command /// public abstract class Command : IManagerCommand { protected readonly CommandConfiguration _config; protected readonly ITranslationLookup _translationLookup; + private string alias; protected ILogger logger; + private string name; + private EFClient.Permission permission; + private Game[] supportedGames; public Command(CommandConfiguration config, ITranslationLookup layout) { @@ -27,14 +31,25 @@ namespace SharedLibraryCore } /// - /// Executes the command + /// Helper property to determine the number of required args + /// + public int RequiredArgumentCount => Arguments.Count(c => c.Required); + + + /// + /// Argument list for the command + /// + public CommandArgument[] Arguments { get; protected set; } = new CommandArgument[0]; + + /// + /// Executes the command /// /// /// - abstract public Task ExecuteAsync(GameEvent gameEvent); + public abstract Task ExecuteAsync(GameEvent gameEvent); /// - /// Specifies the name and string that triggers the command + /// Specifies the name and string that triggers the command /// public string Name { @@ -52,20 +67,20 @@ namespace SharedLibraryCore } } } - private string name; /// - /// Specifies the command description + /// Specifies the command description /// public string Description { get; protected set; } /// - /// Helper property to provide the syntax of the command + /// Helper property to provide the syntax of the command /// - public string Syntax => $"{_translationLookup["COMMAND_HELP_SYNTAX"]} {_config.CommandPrefix ?? "!"}{Alias} {string.Join(" ", Arguments.Select(a => $"<{(a.Required ? "" : _translationLookup["COMMAND_HELP_OPTIONAL"] + " ")}{a.Name}>"))}"; + public string Syntax => + $"{_translationLookup["COMMAND_HELP_SYNTAX"]} {_config.CommandPrefix ?? "!"}{Alias} {string.Join(" ", Arguments.Select(a => $"<{(a.Required ? "" : _translationLookup["COMMAND_HELP_OPTIONAL"] + " ")}{a.Name}>"))}"; /// - /// Alternate name for this command to be executed by + /// Alternate name for this command to be executed by /// public string Alias { @@ -83,20 +98,14 @@ namespace SharedLibraryCore } } } - private string alias; /// - /// Helper property to determine the number of required args - /// - public int RequiredArgumentCount => Arguments.Count(c => c.Required); - - /// - /// Indicates if the command requires a target to execute on + /// Indicates if the command requires a target to execute on /// public bool RequiresTarget { get; protected set; } /// - /// Minimum permission level to execute command + /// Minimum permission level to execute command /// public EFClient.Permission Permission { @@ -114,7 +123,6 @@ namespace SharedLibraryCore } } } - private EFClient.Permission permission; public Game[] SupportedGames { @@ -124,7 +132,7 @@ namespace SharedLibraryCore try { var savedGames = _config?.Commands[GetType().Name].SupportedGames; - supportedGames = savedGames?.Length != 0 ? savedGames : value; + supportedGames = savedGames?.Length != 0 ? savedGames : value; } catch (KeyNotFoundException) @@ -133,19 +141,12 @@ namespace SharedLibraryCore } } } - private Game[] supportedGames; - /// - /// Argument list for the command - /// - public CommandArgument[] Arguments { get; protected set; } = new CommandArgument[0]; - - /// - /// indicates if this command allows impersonation (run as) + /// indicates if this command allows impersonation (run as) /// public bool AllowImpersonation { get; set; } public bool IsBroadcast { get; set; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Commands/AddClientTagCommand.cs b/SharedLibraryCore/Commands/AddClientTagCommand.cs index 9cd7c4084..664b51d6f 100644 --- a/SharedLibraryCore/Commands/AddClientTagCommand.cs +++ b/SharedLibraryCore/Commands/AddClientTagCommand.cs @@ -1,8 +1,8 @@ -using SharedLibraryCore.Configuration; -using SharedLibraryCore.Database.Models; -using SharedLibraryCore.Interfaces; -using System.Threading.Tasks; +using System.Threading.Tasks; using Data.Models; +using Data.Models.Client; +using SharedLibraryCore.Configuration; +using SharedLibraryCore.Interfaces; namespace SharedLibraryCore.Commands { @@ -10,7 +10,8 @@ namespace SharedLibraryCore.Commands { private readonly IMetaService _metaService; - public AddClientTagCommand(CommandConfiguration config, ITranslationLookup layout, IMetaService metaService) : base(config, layout) + public AddClientTagCommand(CommandConfiguration config, ITranslationLookup layout, IMetaService metaService) : + base(config, layout) { Name = "addclienttag"; Description = layout["COMMANDS_ADD_CLIENT_TAG_DESC"]; @@ -19,7 +20,7 @@ namespace SharedLibraryCore.Commands RequiresTarget = false; Arguments = new[] { - new CommandArgument() + new CommandArgument { Name = _translationLookup["COMMANDS_ARGUMENT_TAG"], Required = true @@ -31,8 +32,8 @@ namespace SharedLibraryCore.Commands public override async Task ExecuteAsync(GameEvent gameEvent) { - await _metaService.AddPersistentMeta(EFMeta.ClientTagName, gameEvent.Data); - gameEvent.Origin.Tell(_translationLookup["COMMANDS_ADD_CLIENT_TAG_SUCCESS"].FormatExt(gameEvent.Data)); + await _metaService.AddPersistentMeta(EFMeta.ClientTagName, gameEvent.Data); + gameEvent.Origin.Tell(_translationLookup["COMMANDS_ADD_CLIENT_TAG_SUCCESS"].FormatExt(gameEvent.Data)); } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Commands/CommandArgument.cs b/SharedLibraryCore/Commands/CommandArgument.cs index bd41170ec..9e020f26f 100644 --- a/SharedLibraryCore/Commands/CommandArgument.cs +++ b/SharedLibraryCore/Commands/CommandArgument.cs @@ -1,18 +1,18 @@ namespace SharedLibraryCore.Commands { /// - /// Holds information about command args + /// Holds information about command args /// public class CommandArgument { /// - /// Name of the argument + /// Name of the argument /// public string Name { get; set; } /// - /// Indicates if the argument is required + /// Indicates if the argument is required /// public bool Required { get; set; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Commands/CommandExtensions.cs b/SharedLibraryCore/Commands/CommandExtensions.cs index 09892ed19..33977b6dc 100644 --- a/SharedLibraryCore/Commands/CommandExtensions.cs +++ b/SharedLibraryCore/Commands/CommandExtensions.cs @@ -1,13 +1,15 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace SharedLibraryCore.Commands +namespace SharedLibraryCore.Commands { public static class CommandExtensions { - public static bool IsTargetingSelf(this GameEvent gameEvent) => gameEvent.Origin?.Equals(gameEvent.Target) ?? false; + public static bool IsTargetingSelf(this GameEvent gameEvent) + { + return gameEvent.Origin?.Equals(gameEvent.Target) ?? false; + } - public static bool CanPerformActionOnTarget(this GameEvent gameEvent) => gameEvent.Origin?.Level > gameEvent.Target?.Level; + public static bool CanPerformActionOnTarget(this GameEvent gameEvent) + { + return gameEvent.Origin?.Level > gameEvent.Target?.Level; + } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Commands/CommandProcessing.cs b/SharedLibraryCore/Commands/CommandProcessing.cs index 8678220b1..dd8c28e2b 100644 --- a/SharedLibraryCore/Commands/CommandProcessing.cs +++ b/SharedLibraryCore/Commands/CommandProcessing.cs @@ -1,36 +1,35 @@ -using SharedLibraryCore.Configuration; -using SharedLibraryCore.Database.Models; -using SharedLibraryCore.Exceptions; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; +using SharedLibraryCore.Configuration; +using SharedLibraryCore.Database.Models; +using SharedLibraryCore.Exceptions; namespace SharedLibraryCore.Commands { public class CommandProcessing { - public static async Task ValidateCommand(GameEvent E, ApplicationConfiguration appConfig, CommandConfiguration commandConfig) + public static async Task ValidateCommand(GameEvent E, ApplicationConfiguration appConfig, + CommandConfiguration commandConfig) { var loc = Utilities.CurrentLocalization.LocalizationIndex; var Manager = E.Owner.Manager; - bool isBroadcast = E.Data.StartsWith(appConfig.BroadcastCommandPrefix); - int prefixLength = isBroadcast ? appConfig.BroadcastCommandPrefix.Length : appConfig.CommandPrefix.Length; + var isBroadcast = E.Data.StartsWith(appConfig.BroadcastCommandPrefix); + var prefixLength = isBroadcast ? appConfig.BroadcastCommandPrefix.Length : appConfig.CommandPrefix.Length; - string CommandString = E.Data.Substring(prefixLength, E.Data.Length - prefixLength).Split(' ')[0]; + var CommandString = E.Data.Substring(prefixLength, E.Data.Length - prefixLength).Split(' ')[0]; E.Message = E.Data; Command C = null; foreach (Command cmd in Manager.GetCommands() - .Where(c => c.Name != null)) - { - if (cmd.Name.Equals(CommandString, StringComparison.OrdinalIgnoreCase) || + .Where(c => c.Name != null)) + if (cmd.Name.Equals(CommandString, StringComparison.OrdinalIgnoreCase) || (cmd.Alias ?? "").Equals(CommandString, StringComparison.OrdinalIgnoreCase)) { C = cmd; } - } if (C == null) { @@ -43,7 +42,7 @@ namespace SharedLibraryCore.Commands var allowImpersonation = commandConfig?.Commands?.ContainsKey(C.GetType().Name) ?? false ? commandConfig.Commands[C.GetType().Name].AllowImpersonation : C.AllowImpersonation; - + if (!allowImpersonation && E.ImpersonationOrigin != null) { E.ImpersonationOrigin.Tell(loc["COMMANDS_RUN_AS_FAIL"]); @@ -51,7 +50,7 @@ namespace SharedLibraryCore.Commands } E.Data = E.Data.RemoveWords(1); - String[] Args = E.Data.Trim().Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + var Args = E.Data.Trim().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); // todo: the code below can be cleaned up if (E.Origin.Level < C.Permission) @@ -60,7 +59,7 @@ namespace SharedLibraryCore.Commands throw new CommandException($"{E.Origin} does not have access to \"{C.Name}\""); } - if (Args.Length < (C.RequiredArgumentCount)) + if (Args.Length < C.RequiredArgumentCount) { E.Origin.Tell(loc["COMMAND_MISSINGARGS"]); E.Origin.Tell(C.Syntax); @@ -71,14 +70,14 @@ namespace SharedLibraryCore.Commands { if (Args.Length > 0) { - if (!Int32.TryParse(Args[0], out int cNum)) + if (!int.TryParse(Args[0], out var cNum)) { cNum = -1; } if (Args[0][0] == '@') // user specifying target by database ID { - int.TryParse(Args[0].Substring(1, Args[0].Length - 1), out int dbID); + int.TryParse(Args[0].Substring(1, Args[0].Length - 1), out var dbID); var found = await Manager.GetClientService().Get(dbID); if (found != null) @@ -90,12 +89,13 @@ namespace SharedLibraryCore.Commands } } - else if (Args[0].Length < 3 && cNum > -1 && cNum < E.Owner.MaxClients) // user specifying target by client num + else if (Args[0].Length < 3 && cNum > -1 && cNum < E.Owner.MaxClients + ) // user specifying target by client num { if (E.Owner.Clients[cNum] != null) { E.Target = E.Owner.Clients[cNum]; - E.Data = String.Join(" ", Args.Skip(1)); + E.Data = string.Join(" ", Args.Skip(1)); } } } @@ -111,11 +111,11 @@ namespace SharedLibraryCore.Commands throw new CommandException($"{E.Origin} had multiple players found for {C.Name}"); } - else if (matchingPlayers.Count == 1) + if (matchingPlayers.Count == 1) { E.Target = matchingPlayers.First(); - string escapedName = Regex.Escape(E.Target.CleanedName); + var escapedName = Regex.Escape(E.Target.CleanedName); var reg = new Regex($"(\"{escapedName}\")|({escapedName})", RegexOptions.IgnoreCase); E.Data = reg.Replace(E.Data, "", 1).Trim(); @@ -135,23 +135,21 @@ namespace SharedLibraryCore.Commands { E.Origin.Tell(loc["COMMAND_TARGET_MULTI"]); foreach (var p in matchingPlayers) - { E.Origin.Tell($"[(Color::Yellow){p.ClientNumber}(Color::White)] {p.Name}"); - } throw new CommandException($"{E.Origin} had multiple players found for {C.Name}"); } - else if (matchingPlayers.Count == 1) + if (matchingPlayers.Count == 1) { E.Target = matchingPlayers.First(); - string escapedName = Regex.Escape(E.Target.CleanedName); - string escapedArg = Regex.Escape(Args[0]); + var escapedName = Regex.Escape(E.Target.CleanedName); + var escapedArg = Regex.Escape(Args[0]); var reg = new Regex($"({escapedName})|({escapedArg})", RegexOptions.IgnoreCase); E.Data = reg.Replace(E.Data, "", 1).Trim(); if ((E.Data.Trim() == E.Target.CleanedName.ToLower().Trim() || - E.Data == String.Empty) && + E.Data == string.Empty) && C.RequiresTarget) { E.Origin.Tell(loc["COMMAND_MISSINGARGS"]); @@ -172,4 +170,4 @@ namespace SharedLibraryCore.Commands return C; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Commands/ListClientTags.cs b/SharedLibraryCore/Commands/ListClientTags.cs index 7ce5588c9..ee5b0555f 100644 --- a/SharedLibraryCore/Commands/ListClientTags.cs +++ b/SharedLibraryCore/Commands/ListClientTags.cs @@ -1,27 +1,25 @@ -using SharedLibraryCore.Configuration; -using SharedLibraryCore.Database.Models; -using SharedLibraryCore.Interfaces; -using System; -using System.Collections.Generic; -using System.Linq; +using System.Linq; using System.Threading.Tasks; using Data.Models; +using Data.Models.Client; +using SharedLibraryCore.Configuration; +using SharedLibraryCore.Interfaces; namespace SharedLibraryCore.Commands { - public class ListClientTags : Command { private readonly IMetaService _metaService; - public ListClientTags(CommandConfiguration config, ITranslationLookup layout, IMetaService metaService) : base(config, layout) + public ListClientTags(CommandConfiguration config, ITranslationLookup layout, IMetaService metaService) : base( + config, layout) { Name = "listclienttags"; Description = layout["COMMANDS_LIST_CLIENT_TAGS_DESC"]; Alias = "lct"; Permission = EFClient.Permission.Owner; RequiresTarget = false; - + _metaService = metaService; } @@ -31,4 +29,4 @@ namespace SharedLibraryCore.Commands gameEvent.Origin.Tell(tags.Select(tag => tag.Value)); } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Commands/NativeCommands.cs b/SharedLibraryCore/Commands/NativeCommands.cs index 7f17fc500..dce657858 100644 --- a/SharedLibraryCore/Commands/NativeCommands.cs +++ b/SharedLibraryCore/Commands/NativeCommands.cs @@ -1,30 +1,30 @@ -using Microsoft.EntityFrameworkCore; -using SharedLibraryCore.Configuration; -using SharedLibraryCore.Database.Models; -using SharedLibraryCore.Helpers; -using SharedLibraryCore.Interfaces; -using SharedLibraryCore.Services; -using System; +using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Security.Cryptography; -using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using Data.Abstractions; using Data.Models; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Serilog.Context; +using SharedLibraryCore.Configuration; +using SharedLibraryCore.Database.Models; +using SharedLibraryCore.Helpers; +using SharedLibraryCore.Interfaces; using static Data.Models.Client.EFClient; namespace SharedLibraryCore.Commands { /// - /// Quits IW4MAdmin + /// Quits IW4MAdmin /// public class QuitCommand : Command { - public QuitCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) + public QuitCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, + translationLookup) { Name = "quit"; Description = _translationLookup["COMMANDS_QUIT_DESC"]; @@ -41,11 +41,12 @@ namespace SharedLibraryCore.Commands } /// - /// Restarts IW4MAdmin + /// Restarts IW4MAdmin /// public class RestartCommand : Command { - public RestartCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) + public RestartCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, + translationLookup) { Name = "restart"; Description = _translationLookup["COMMANDS_RESTART_DESC"]; @@ -63,11 +64,12 @@ namespace SharedLibraryCore.Commands } /// - /// Claims ownership of the server + /// Claims ownership of the server /// public class OwnerCommand : Command { - public OwnerCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) + public OwnerCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, + translationLookup) { Name = "owner"; Description = _translationLookup["COMMANDS_OWNER_DESC"]; @@ -85,8 +87,8 @@ namespace SharedLibraryCore.Commands return; } - if (await (E.Owner.Manager.GetClientService() as ClientService).GetOwnerCount() == 0 && - !E.Origin.SetLevel(EFClient.Permission.Owner, Utilities.IW4MAdminClient(E.Owner)).Failed) + if (await E.Owner.Manager.GetClientService().GetOwnerCount() == 0 && + !E.Origin.SetLevel(Permission.Owner, Utilities.IW4MAdminClient(E.Owner)).Failed) { E.Origin.Tell(_translationLookup["COMMANDS_OWNER_SUCCESS"]); } @@ -98,12 +100,14 @@ namespace SharedLibraryCore.Commands } /// - /// Warns given client for reason + /// Warns given client for reason /// public class WarnCommand : Command { private readonly ApplicationConfiguration _appConfig; - public WarnCommand(ApplicationConfiguration appConfig, CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) + + public WarnCommand(ApplicationConfiguration appConfig, CommandConfiguration config, + ITranslationLookup translationLookup) : base(config, translationLookup) { Name = "warn"; Description = _translationLookup["COMMANDS_WARN_DESC"]; @@ -112,12 +116,12 @@ namespace SharedLibraryCore.Commands RequiresTarget = true; Arguments = new[] { - new CommandArgument() + new CommandArgument { Name = _translationLookup["COMMANDS_ARGS_PLAYER"], Required = true }, - new CommandArgument() + new CommandArgument { Name = _translationLookup["COMMANDS_ARGS_REASON"], Required = true @@ -139,11 +143,12 @@ namespace SharedLibraryCore.Commands } /// - /// Clears all warnings for given client + /// Clears all warnings for given client /// public class WarnClearCommand : Command { - public WarnClearCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) + public WarnClearCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, + translationLookup) { Name = "warnclear"; Description = _translationLookup["COMMANDS_WARNCLEAR_DESC"]; @@ -152,7 +157,7 @@ namespace SharedLibraryCore.Commands RequiresTarget = true; Arguments = new[] { - new CommandArgument() + new CommandArgument { Name = _translationLookup["COMMANDS_ARGS_PLAYER"], Required = true @@ -172,13 +177,14 @@ namespace SharedLibraryCore.Commands } /// - /// Kicks client for given reason + /// Kicks client for given reason /// public class KickCommand : Command { private readonly ApplicationConfiguration _appConfig; - - public KickCommand(ApplicationConfiguration appConfig, CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) + + public KickCommand(ApplicationConfiguration appConfig, CommandConfiguration config, + ITranslationLookup translationLookup) : base(config, translationLookup) { Name = "kick"; Description = _translationLookup["COMMANDS_KICK_DESC"]; @@ -187,12 +193,12 @@ namespace SharedLibraryCore.Commands RequiresTarget = true; Arguments = new[] { - new CommandArgument() + new CommandArgument { Name = _translationLookup["COMMANDS_ARGS_PLAYER"], Required = true }, - new CommandArgument() + new CommandArgument { Name = _translationLookup["COMMANDS_ARGS_REASON"], Required = true @@ -204,7 +210,8 @@ namespace SharedLibraryCore.Commands public override async Task ExecuteAsync(GameEvent gameEvent) { var reason = gameEvent.Data.FindRuleForReason(_appConfig, gameEvent.Owner); - switch ((await gameEvent.Target.Kick(reason, gameEvent.Origin).WaitAsync(Utilities.DefaultCommandTimeout, gameEvent.Owner.Manager.CancellationToken)).FailReason) + switch ((await gameEvent.Target.Kick(reason, gameEvent.Origin).WaitAsync(Utilities.DefaultCommandTimeout, + gameEvent.Owner.Manager.CancellationToken)).FailReason) { case GameEvent.EventFailReason.None: gameEvent.Origin.Tell(_translationLookup["COMMANDS_KICK_SUCCESS"].FormatExt(gameEvent.Target.Name)); @@ -220,13 +227,15 @@ namespace SharedLibraryCore.Commands } /// - /// Temporarily bans a client + /// Temporarily bans a client /// public class TempBanCommand : Command { + private static readonly string TempBanRegex = @"([0-9]+\w+)\ (.+)"; private readonly ApplicationConfiguration _appConfig; - - public TempBanCommand(ApplicationConfiguration appConfig, CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) + + public TempBanCommand(ApplicationConfiguration appConfig, CommandConfiguration config, + ITranslationLookup translationLookup) : base(config, translationLookup) { Name = "tempban"; Description = _translationLookup["COMMANDS_TEMPBAN_DESC"]; @@ -235,17 +244,17 @@ namespace SharedLibraryCore.Commands RequiresTarget = true; Arguments = new[] { - new CommandArgument() + new CommandArgument { Name = _translationLookup["COMMANDS_ARGS_PLAYER"], Required = true }, - new CommandArgument() + new CommandArgument { Name = _translationLookup["COMMANDS_ARGS_DURATION"], - Required = true, + Required = true }, - new CommandArgument() + new CommandArgument { Name = _translationLookup["COMMANDS_ARGS_REASON"], Required = true @@ -254,8 +263,6 @@ namespace SharedLibraryCore.Commands _appConfig = appConfig; } - private static readonly string TempBanRegex = @"([0-9]+\w+)\ (.+)"; - public override async Task ExecuteAsync(GameEvent gameEvent) { var match = Regex.Match(gameEvent.Data, TempBanRegex); @@ -271,16 +278,20 @@ namespace SharedLibraryCore.Commands else { - switch ((await gameEvent.Target.TempBan(tempbanReason, length, gameEvent.Origin).WaitAsync(Utilities.DefaultCommandTimeout, gameEvent.Owner.Manager.CancellationToken)).FailReason) + switch ((await gameEvent.Target.TempBan(tempbanReason, length, gameEvent.Origin) + .WaitAsync(Utilities.DefaultCommandTimeout, gameEvent.Owner.Manager.CancellationToken)) + .FailReason) { case GameEvent.EventFailReason.None: - gameEvent.Origin.Tell(_translationLookup["COMMANDS_TEMPBAN_SUCCESS"].FormatExt(gameEvent.Target, length.HumanizeForCurrentCulture())); + gameEvent.Origin.Tell(_translationLookup["COMMANDS_TEMPBAN_SUCCESS"] + .FormatExt(gameEvent.Target, length.HumanizeForCurrentCulture())); break; case GameEvent.EventFailReason.Exception: gameEvent.Origin.Tell(_translationLookup["SERVER_ERROR_COMMAND_INGAME"]); break; default: - gameEvent.Origin.Tell(_translationLookup["COMMANDS_TEMPBAN_FAIL"].FormatExt(gameEvent.Target.Name)); + gameEvent.Origin.Tell(_translationLookup["COMMANDS_TEMPBAN_FAIL"] + .FormatExt(gameEvent.Target.Name)); break; } } @@ -289,12 +300,14 @@ namespace SharedLibraryCore.Commands } /// - /// Permanently bans a client + /// Permanently bans a client /// public class BanCommand : Command { private readonly ApplicationConfiguration _appConfig; - public BanCommand(ApplicationConfiguration appConfig, CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) + + public BanCommand(ApplicationConfiguration appConfig, CommandConfiguration config, + ITranslationLookup translationLookup) : base(config, translationLookup) { Name = "ban"; Description = _translationLookup["COMMANDS_BAN_DESC"]; @@ -303,12 +316,12 @@ namespace SharedLibraryCore.Commands RequiresTarget = true; Arguments = new[] { - new CommandArgument() + new CommandArgument { Name = _translationLookup["COMMANDS_ARGS_PLAYER"], Required = true }, - new CommandArgument() + new CommandArgument { Name = _translationLookup["COMMANDS_ARGS_REASON"], Required = true @@ -320,7 +333,9 @@ namespace SharedLibraryCore.Commands public override async Task ExecuteAsync(GameEvent gameEvent) { var reason = gameEvent.Data.FindRuleForReason(_appConfig, gameEvent.Owner); - switch ((await gameEvent.Target.Ban(reason, gameEvent.Origin, false).WaitAsync(Utilities.DefaultCommandTimeout, gameEvent.Owner.Manager.CancellationToken)).FailReason) + switch ((await gameEvent.Target.Ban(reason, gameEvent.Origin, false) + .WaitAsync(Utilities.DefaultCommandTimeout, gameEvent.Owner.Manager.CancellationToken)) + .FailReason) { case GameEvent.EventFailReason.None: gameEvent.Origin.Tell(_translationLookup["COMMANDS_BAN_SUCCESS"].FormatExt(gameEvent.Target.Name)); @@ -336,11 +351,12 @@ namespace SharedLibraryCore.Commands } /// - /// Unbans a banned client + /// Unbans a banned client /// public class UnbanCommand : Command { - public UnbanCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) + public UnbanCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, + translationLookup) { Name = "unban"; Description = _translationLookup["COMMANDS_UNBAN_DESC"]; @@ -349,15 +365,15 @@ namespace SharedLibraryCore.Commands RequiresTarget = true; Arguments = new[] { - new CommandArgument() + new CommandArgument { Name = _translationLookup["COMMANDS_ARGS_CLIENTID"], - Required = true, + Required = true }, - new CommandArgument() + new CommandArgument { Name = _translationLookup["COMMANDS_ARGS_REASON"], - Required = true + Required = true } }; } @@ -366,9 +382,11 @@ namespace SharedLibraryCore.Commands { // todo: don't do the lookup here var penalties = await E.Owner.Manager.GetPenaltyService().GetActivePenaltiesAsync(E.Target.AliasLinkId); - if (penalties.Where(p => p.Type == EFPenalty.PenaltyType.Ban || p.Type == EFPenalty.PenaltyType.TempBan).FirstOrDefault() != null) + if (penalties.Where(p => p.Type == EFPenalty.PenaltyType.Ban || p.Type == EFPenalty.PenaltyType.TempBan) + .FirstOrDefault() != null) { - switch ((await E.Target.Unban(E.Data, E.Origin).WaitAsync(Utilities.DefaultCommandTimeout, E.Owner.Manager.CancellationToken)).FailReason) + switch ((await E.Target.Unban(E.Data, E.Origin) + .WaitAsync(Utilities.DefaultCommandTimeout, E.Owner.Manager.CancellationToken)).FailReason) { case GameEvent.EventFailReason.None: E.Origin.Tell(_translationLookup["COMMANDS_UNBAN_SUCCESS"].FormatExt(E.Target)); @@ -387,11 +405,12 @@ namespace SharedLibraryCore.Commands } /// - /// Fast restarts the map + /// Fast restarts the map /// public class FastRestartCommand : Command { - public FastRestartCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) + public FastRestartCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, + translationLookup) { Name = "fastrestart"; Description = _translationLookup["COMMANDS_FASTRESTART_DESC"]; @@ -404,18 +423,20 @@ namespace SharedLibraryCore.Commands { await E.Owner.ExecuteCommandAsync("fast_restart"); - var _ = !E.Origin.Masked ? - E.Owner.Broadcast($"(Color::Accent){E.Origin.Name} (Color::White){_translationLookup["COMMANDS_FASTRESTART_UNMASKED"]}") : - E.Owner.Broadcast(_translationLookup["COMMANDS_FASTRESTART_MASKED"]); + var _ = !E.Origin.Masked + ? E.Owner.Broadcast( + $"(Color::Accent){E.Origin.Name} (Color::White){_translationLookup["COMMANDS_FASTRESTART_UNMASKED"]}") + : E.Owner.Broadcast(_translationLookup["COMMANDS_FASTRESTART_MASKED"]); } } /// - /// Cycles to the next map in rotation + /// Cycles to the next map in rotation /// public class MapRotateCommand : Command { - public MapRotateCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) + public MapRotateCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, + translationLookup) { Name = "maprotate"; Description = _translationLookup["COMMANDS_MAPROTATE_DESC"]; @@ -426,9 +447,11 @@ namespace SharedLibraryCore.Commands public override async Task ExecuteAsync(GameEvent E) { - _ = !E.Origin.Masked ? - E.Owner.Broadcast($"{_translationLookup["COMMANDS_MAPROTATE"]} [(Color::Accent){E.Origin.Name}(Color::White)]", E.Origin) : - E.Owner.Broadcast(_translationLookup["COMMANDS_MAPROTATE"], E.Origin); + _ = !E.Origin.Masked + ? E.Owner.Broadcast( + $"{_translationLookup["COMMANDS_MAPROTATE"]} [(Color::Accent){E.Origin.Name}(Color::White)]", + E.Origin) + : E.Owner.Broadcast(_translationLookup["COMMANDS_MAPROTATE"], E.Origin); await Task.Delay(E.Owner.Manager.GetApplicationSettings().Configuration().MapChangeDelaySeconds * 1000); await E.Owner.ExecuteCommandAsync("map_rotate"); @@ -436,11 +459,12 @@ namespace SharedLibraryCore.Commands } /// - /// Sets the level of given client + /// Sets the level of given client /// public class SetLevelCommand : Command { - public SetLevelCommand(CommandConfiguration config, ITranslationLookup translationLookup, ILogger logger) : base(config, translationLookup) + public SetLevelCommand(CommandConfiguration config, ITranslationLookup translationLookup, + ILogger logger) : base(config, translationLookup) { Name = "setlevel"; Description = _translationLookup["COMMANDS_SETLEVEL_DESC"]; @@ -449,44 +473,44 @@ namespace SharedLibraryCore.Commands RequiresTarget = true; Arguments = new[] { - new CommandArgument() - { - Name = _translationLookup["COMMANDS_ARGS_PLAYER"], - Required = true - }, - new CommandArgument() - { - Name = _translationLookup["COMMANDS_ARGS_LEVEL"], - Required = true - } + new CommandArgument + { + Name = _translationLookup["COMMANDS_ARGS_PLAYER"], + Required = true + }, + new CommandArgument + { + Name = _translationLookup["COMMANDS_ARGS_LEVEL"], + Required = true + } }; this.logger = logger; } public override async Task ExecuteAsync(GameEvent gameEvent) { - Permission oldPerm = gameEvent.Target.Level; - Permission newPerm = Utilities.MatchPermission(gameEvent.Data); - bool allowMultiOwner = gameEvent.Owner.Manager.GetApplicationSettings().Configuration().EnableMultipleOwners; - bool steppedPrivileges = gameEvent.Owner.Manager.GetApplicationSettings().Configuration().EnableSteppedHierarchy; + var oldPerm = gameEvent.Target.Level; + var newPerm = Utilities.MatchPermission(gameEvent.Data); + var allowMultiOwner = gameEvent.Owner.Manager.GetApplicationSettings().Configuration().EnableMultipleOwners; + var steppedPrivileges = + gameEvent.Owner.Manager.GetApplicationSettings().Configuration().EnableSteppedHierarchy; var targetClient = gameEvent.Target; // pre setup logic - bool canPromoteSteppedPriv = gameEvent.Origin.Level > newPerm || gameEvent.Origin.Level == Permission.Owner; - bool hasOwner = await gameEvent.Owner.Manager.GetClientService().GetOwnerCount() > 0; + var canPromoteSteppedPriv = gameEvent.Origin.Level > newPerm || gameEvent.Origin.Level == Permission.Owner; + var hasOwner = await gameEvent.Owner.Manager.GetClientService().GetOwnerCount() > 0; // trying to set self if (gameEvent.Target == gameEvent.Origin) { gameEvent.Origin.Tell(_translationLookup["COMMANDS_SETLEVEL_SELF"]); - return; } // origin permission not high enough else if (gameEvent.Origin.Level < gameEvent.Target.Level) { - gameEvent.Origin.Tell(_translationLookup["COMMANDS_SETLEVEL_PERMISSION"].FormatExt(gameEvent.Target.Name)); - return; + gameEvent.Origin.Tell(_translationLookup["COMMANDS_SETLEVEL_PERMISSION"] + .FormatExt(gameEvent.Target.Name)); } // trying to set owner without enabling multiple owners @@ -494,21 +518,20 @@ namespace SharedLibraryCore.Commands { // only one owner is allowed gameEvent.Origin.Tell(_translationLookup["COMMANDS_SETLEVEL_OWNER"]); - return; } // trying to set level when only owner is allowed to else if (gameEvent.Origin.Level < Permission.Owner && !steppedPrivileges) { // only the owner is allowed to set levels - gameEvent.Origin.Tell($"{_translationLookup["COMMANDS_SETLEVEL_STEPPEDDISABLED"]} (Color::White){gameEvent.Target.Name}"); - return; + gameEvent.Origin.Tell( + $"{_translationLookup["COMMANDS_SETLEVEL_STEPPEDDISABLED"]} (Color::White){gameEvent.Target.Name}"); } - + else if (gameEvent.Target.Level == Permission.Flagged) { - gameEvent.Origin.Tell(_translationLookup["COMMANDS_SETLEVEL_FLAGGED"].FormatExt(gameEvent.Target.Name + "(Color::White)")); - return; + gameEvent.Origin.Tell(_translationLookup["COMMANDS_SETLEVEL_FLAGGED"] + .FormatExt(gameEvent.Target.Name + "(Color::White)")); } // stepped privilege is enabled, but the new level is too high @@ -523,13 +546,16 @@ namespace SharedLibraryCore.Commands // valid else if (newPerm > Permission.Banned) { - targetClient = targetClient.ClientNumber < 0 ? - gameEvent.Owner.Manager.GetActiveClients() - .FirstOrDefault(c => c.ClientId == targetClient?.ClientId) ?? targetClient : targetClient; + targetClient = targetClient.ClientNumber < 0 + ? gameEvent.Owner.Manager.GetActiveClients() + .FirstOrDefault(c => c.ClientId == targetClient?.ClientId) ?? targetClient + : targetClient; - logger.LogDebug("Beginning set level of client {origin} to {newPermission}", gameEvent.Origin.ToString(), newPerm); + logger.LogDebug("Beginning set level of client {origin} to {newPermission}", + gameEvent.Origin.ToString(), newPerm); - var result = await targetClient.SetLevel(newPerm, gameEvent.Origin).WaitAsync(Utilities.DefaultCommandTimeout, gameEvent.Owner.Manager.CancellationToken); + var result = await targetClient.SetLevel(newPerm, gameEvent.Origin) + .WaitAsync(Utilities.DefaultCommandTimeout, gameEvent.Owner.Manager.CancellationToken); if (result.Failed) { @@ -540,13 +566,14 @@ namespace SharedLibraryCore.Commands .FormatExt(gameEvent.Target.Name + "(Color::White)", newPerm.ToString())); return; } - + using (LogContext.PushProperty("Server", gameEvent.Origin.CurrentServer?.ToString())) { - logger.LogWarning("Failed to set level of client {origin} {reason}", - gameEvent.Origin.ToString(), + logger.LogWarning("Failed to set level of client {origin} {reason}", + gameEvent.Origin.ToString(), result.FailReason); } + gameEvent.Origin.Tell(_translationLookup["SERVER_ERROR_COMMAND_INGAME"]); return; } @@ -559,9 +586,11 @@ namespace SharedLibraryCore.Commands } // inform the origin that the client has been updated - _ = newPerm < oldPerm ? - gameEvent.Origin.Tell(_translationLookup["COMMANDS_SETLEVEL_DEMOTE_SUCCESS"].FormatExt(targetClient.Name)) : - gameEvent.Origin.Tell(_translationLookup["COMMANDS_SETLEVEL_SUCCESS"].FormatExt(targetClient.Name)); + _ = newPerm < oldPerm + ? gameEvent.Origin.Tell(_translationLookup["COMMANDS_SETLEVEL_DEMOTE_SUCCESS"] + .FormatExt(targetClient.Name)) + : gameEvent.Origin.Tell( + _translationLookup["COMMANDS_SETLEVEL_SUCCESS"].FormatExt(targetClient.Name)); } // all other tests failed so it's invalid group @@ -573,11 +602,12 @@ namespace SharedLibraryCore.Commands } /// - /// Prints the amount of memory IW4MAdmin is using + /// Prints the amount of memory IW4MAdmin is using /// public class MemoryUsageCommand : Command { - public MemoryUsageCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) + public MemoryUsageCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, + translationLookup) { Name = "usage"; Description = _translationLookup["COMMANDS_USAGE_DESC"]; @@ -588,17 +618,19 @@ namespace SharedLibraryCore.Commands public override Task ExecuteAsync(GameEvent E) { - E.Origin.Tell(_translationLookup["COMMANDS_USAGE_TEXT"].FormatExt(Math.Round(((System.Diagnostics.Process.GetCurrentProcess().PrivateMemorySize64 / 2048f) / 1200f), 1))); + E.Origin.Tell(_translationLookup["COMMANDS_USAGE_TEXT"] + .FormatExt(Math.Round(Process.GetCurrentProcess().PrivateMemorySize64 / 2048f / 1200f, 1))); return Task.CompletedTask; } } /// - /// Prints out how long IW4MAdmin has been running + /// Prints out how long IW4MAdmin has been running /// public class UptimeCommand : Command { - public UptimeCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) + public UptimeCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, + translationLookup) { Name = "uptime"; Description = _translationLookup["COMMANDS_UPTIME_DESC"]; @@ -609,7 +641,7 @@ namespace SharedLibraryCore.Commands public override Task ExecuteAsync(GameEvent E) { - var uptime = DateTime.Now - System.Diagnostics.Process.GetCurrentProcess().StartTime; + var uptime = DateTime.Now - Process.GetCurrentProcess().StartTime; var loc = _translationLookup; E.Origin.Tell(loc["COMMANDS_UPTIME_TEXT"].FormatExt(uptime.HumanizeForCurrentCulture(4))); return Task.CompletedTask; @@ -618,11 +650,12 @@ namespace SharedLibraryCore.Commands /// - /// Attempts to load the specified map + /// Attempts to load the specified map /// public class LoadMapCommand : Command { - public LoadMapCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) + public LoadMapCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, + translationLookup) { Name = "map"; Description = _translationLookup["COMMANDS_MAP_DESC"]; @@ -631,25 +664,26 @@ namespace SharedLibraryCore.Commands RequiresTarget = false; Arguments = new[] { - new CommandArgument() - { - Name = _translationLookup["COMMANDS_ARGS_MAP"], - Required = true - } + new CommandArgument + { + Name = _translationLookup["COMMANDS_ARGS_MAP"], + Required = true + } }; } public override async Task ExecuteAsync(GameEvent E) { - string newMap = E.Data.Trim(); - int delay = E.Owner.Manager.GetApplicationSettings().Configuration().MapChangeDelaySeconds * 1000; + var newMap = E.Data.Trim(); + var delay = E.Owner.Manager.GetApplicationSettings().Configuration().MapChangeDelaySeconds * 1000; - var foundMap = E.Owner.Maps.FirstOrDefault(_map => _map.Name.Equals(newMap, StringComparison.InvariantCultureIgnoreCase) || - _map.Alias.Equals(newMap, StringComparison.InvariantCultureIgnoreCase)); + var foundMap = E.Owner.Maps.FirstOrDefault(_map => + _map.Name.Equals(newMap, StringComparison.InvariantCultureIgnoreCase) || + _map.Alias.Equals(newMap, StringComparison.InvariantCultureIgnoreCase)); - _ = foundMap == null ? - E.Owner.Broadcast(_translationLookup["COMMANDS_MAP_UKN"].FormatExt(newMap)) : - E.Owner.Broadcast(_translationLookup["COMMANDS_MAP_SUCCESS"].FormatExt(foundMap.Alias)); + _ = foundMap == null + ? E.Owner.Broadcast(_translationLookup["COMMANDS_MAP_UKN"].FormatExt(newMap)) + : E.Owner.Broadcast(_translationLookup["COMMANDS_MAP_SUCCESS"].FormatExt(foundMap.Alias)); await Task.Delay(delay); await E.Owner.LoadMap(foundMap?.Name ?? newMap); @@ -658,11 +692,12 @@ namespace SharedLibraryCore.Commands /// - /// Lists server and global rules + /// Lists server and global rules /// public class ListRulesCommands : Command { - public ListRulesCommands(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) + public ListRulesCommands(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, + translationLookup) { Name = "rules"; Description = _translationLookup["COMMANDS_RULES_DESC"]; @@ -676,9 +711,9 @@ namespace SharedLibraryCore.Commands if (E.Owner.Manager.GetApplicationSettings().Configuration().GlobalRules?.Length < 1 && E.Owner.ServerConfig.Rules?.Length < 1) { - var _ = E.Message.IsBroadcastCommand(_config.BroadcastCommandPrefix) ? - E.Owner.Broadcast(_translationLookup["COMMANDS_RULES_NONE"]) : - E.Origin.Tell(_translationLookup["COMMANDS_RULES_NONE"]); + var _ = E.Message.IsBroadcastCommand(_config.BroadcastCommandPrefix) + ? E.Owner.Broadcast(_translationLookup["COMMANDS_RULES_NONE"]) + : E.Origin.Tell(_translationLookup["COMMANDS_RULES_NONE"]); } else @@ -705,14 +740,14 @@ namespace SharedLibraryCore.Commands } } - /// - /// Flag given client for specified reason + /// Flag given client for specified reason /// public class FlagClientCommand : Command { - public FlagClientCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) + public FlagClientCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, + translationLookup) { Name = "flag"; Description = _translationLookup["COMMANDS_FLAG_DESC"]; @@ -721,12 +756,12 @@ namespace SharedLibraryCore.Commands RequiresTarget = true; Arguments = new[] { - new CommandArgument() + new CommandArgument { Name = _translationLookup["COMMANDS_ARGS_PLAYER"], Required = true }, - new CommandArgument() + new CommandArgument { Name = _translationLookup["COMMANDS_ARGS_REASON"], Required = true @@ -736,7 +771,8 @@ namespace SharedLibraryCore.Commands public override async Task ExecuteAsync(GameEvent E) { - switch ((await E.Target.Flag(E.Data, E.Origin).WaitAsync(Utilities.DefaultCommandTimeout, E.Owner.Manager.CancellationToken)).FailReason) + switch ((await E.Target.Flag(E.Data, E.Origin) + .WaitAsync(Utilities.DefaultCommandTimeout, E.Owner.Manager.CancellationToken)).FailReason) { case GameEvent.EventFailReason.Permission: E.Origin.Tell(_translationLookup["COMMANDS_FLAG_FAIL"].FormatExt(E.Target.Name)); @@ -755,11 +791,12 @@ namespace SharedLibraryCore.Commands } /// - /// Unflag given client for specified reason + /// Unflag given client for specified reason /// public class UnflagClientCommand : Command { - public UnflagClientCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) + public UnflagClientCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, + translationLookup) { Name = "unflag"; Description = _translationLookup["COMMANDS_UNFLAG_DESC"]; @@ -768,12 +805,12 @@ namespace SharedLibraryCore.Commands RequiresTarget = true; Arguments = new[] { - new CommandArgument() + new CommandArgument { Name = _translationLookup["COMMANDS_ARGS_PLAYER"], Required = true }, - new CommandArgument() + new CommandArgument { Name = _translationLookup["COMMANDS_ARGS_REASON"], Required = true @@ -783,7 +820,8 @@ namespace SharedLibraryCore.Commands public override async Task ExecuteAsync(GameEvent E) { - switch ((await E.Target.Unflag(E.Data, E.Origin).WaitAsync(Utilities.DefaultCommandTimeout, E.Owner.Manager.CancellationToken)).FailReason) + switch ((await E.Target.Unflag(E.Data, E.Origin) + .WaitAsync(Utilities.DefaultCommandTimeout, E.Owner.Manager.CancellationToken)).FailReason) { case GameEvent.EventFailReason.None: E.Origin.Tell(_translationLookup["COMMANDS_FLAG_UNFLAG"].FormatExt(E.Target.Name)); @@ -802,16 +840,17 @@ namespace SharedLibraryCore.Commands } /// - /// Masks client from announcements and online admin list + /// Masks client from announcements and online admin list /// public class MaskCommand : Command { - public MaskCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) + public MaskCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, + translationLookup) { Name = "mask"; Description = _translationLookup["COMMANDS_MASK_DESC"]; Alias = "hide"; - Permission = EFClient.Permission.Moderator; + Permission = Permission.Moderator; RequiresTarget = false; } @@ -833,11 +872,12 @@ namespace SharedLibraryCore.Commands } /// - /// Lists ban information for given client + /// Lists ban information for given client /// public class ListBanInfoCommand : Command { - public ListBanInfoCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) + public ListBanInfoCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, + translationLookup) { Name = "baninfo"; Description = _translationLookup["COMMANDS_BANINFO_DESC"]; @@ -846,7 +886,7 @@ namespace SharedLibraryCore.Commands RequiresTarget = true; Arguments = new[] { - new CommandArgument() + new CommandArgument { Name = _translationLookup["COMMANDS_ARGS_PLAYER"], Required = true @@ -856,7 +896,8 @@ namespace SharedLibraryCore.Commands public override async Task ExecuteAsync(GameEvent E) { - var existingPenalties = await E.Owner.Manager.GetPenaltyService().GetActivePenaltiesAsync(E.Target.AliasLinkId, E.Target.IPAddress); + var existingPenalties = await E.Owner.Manager.GetPenaltyService() + .GetActivePenaltiesAsync(E.Target.AliasLinkId, E.Target.IPAddress); var penalty = existingPenalties.FirstOrDefault(b => b.Type > EFPenalty.PenaltyType.Kick); if (penalty == null) @@ -872,18 +913,20 @@ namespace SharedLibraryCore.Commands else { - string remainingTime = (penalty.Expires.Value - DateTime.UtcNow).HumanizeForCurrentCulture(); - E.Origin.Tell(_translationLookup["COMMANDS_BANINFO_TB_SUCCESS"].FormatExt(E.Target.Name, penalty.Offense, remainingTime)); + var remainingTime = (penalty.Expires.Value - DateTime.UtcNow).HumanizeForCurrentCulture(); + E.Origin.Tell(_translationLookup["COMMANDS_BANINFO_TB_SUCCESS"] + .FormatExt(E.Target.Name, penalty.Offense, remainingTime)); } } } /// - /// Executes RCon command + /// Executes RCon command /// public class ExecuteRConCommand : Command { - public ExecuteRConCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) + public ExecuteRConCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, + translationLookup) { Name = "rcon"; Description = _translationLookup["COMMANDS_RCON_DESC"]; @@ -892,7 +935,7 @@ namespace SharedLibraryCore.Commands RequiresTarget = false; Arguments = new[] { - new CommandArgument() + new CommandArgument { Name = _translationLookup["COMMANDS_ARGS_COMMANDS"], Required = true @@ -904,9 +947,7 @@ namespace SharedLibraryCore.Commands { var response = await E.Owner.ExecuteCommandAsync(E.Data.Trim()); foreach (var item in response) - { E.Origin.Tell(item); - } if (response.Length == 0) { @@ -916,11 +957,12 @@ namespace SharedLibraryCore.Commands } /// - /// Lists external IP + /// Lists external IP /// public class ListExternalIPCommand : Command { - public ListExternalIPCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) + public ListExternalIPCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, + translationLookup) { Name = "getexternalip"; Description = _translationLookup["COMMANDS_IP_DESC"]; @@ -937,13 +979,13 @@ namespace SharedLibraryCore.Commands } /// - /// Prunes inactive privileged clients + /// Prunes inactive privileged clients /// public class PruneAdminsCommand : Command { private readonly IDatabaseContextFactory _contextFactory; - - public PruneAdminsCommand(CommandConfiguration config, ITranslationLookup translationLookup, + + public PruneAdminsCommand(CommandConfiguration config, ITranslationLookup translationLookup, IDatabaseContextFactory contextFactory) : base(config, translationLookup) { Name = "prune"; @@ -954,7 +996,7 @@ namespace SharedLibraryCore.Commands _contextFactory = contextFactory; Arguments = new[] { - new CommandArgument() + new CommandArgument { Name = _translationLookup["COMMANDS_ARGS_INACTIVE"], Required = false @@ -964,7 +1006,7 @@ namespace SharedLibraryCore.Commands public override async Task ExecuteAsync(GameEvent E) { - int inactiveDays = 30; + var inactiveDays = 30; try { @@ -996,18 +1038,19 @@ namespace SharedLibraryCore.Commands .ToListAsync(); inactiveUsers.ForEach(c => c.SetLevel(Permission.User, E.Origin)); await context.SaveChangesAsync(); - + E.Origin.Tell(_translationLookup["COMMANDS_PRUNE_SUCCESS"].FormatExt(inactiveUsers.Count)); } } /// - /// Sets login password + /// Sets login password /// public class SetPasswordCommand : Command { - public SetPasswordCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) + public SetPasswordCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, + translationLookup) { Name = "setpassword"; Description = _translationLookup["COMMANDS_SETPASSWORD_DESC"]; @@ -1017,7 +1060,7 @@ namespace SharedLibraryCore.Commands AllowImpersonation = true; Arguments = new[] { - new CommandArgument() + new CommandArgument { Name = _translationLookup["COMMANDS_ARGS_PASSWORD"], Required = true @@ -1033,7 +1076,7 @@ namespace SharedLibraryCore.Commands return; } - string[] hashedPassword = Helpers.Hashing.Hash(E.Data); + var hashedPassword = Hashing.Hash(E.Data); E.Origin.Password = hashedPassword[0]; E.Origin.PasswordSalt = hashedPassword[1]; @@ -1044,11 +1087,12 @@ namespace SharedLibraryCore.Commands } /// - /// Gets the ping of a client + /// Gets the ping of a client /// public class GetClientPingCommand : Command { - public GetClientPingCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) + public GetClientPingCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, + translationLookup) { Name = "ping"; Description = _translationLookup["COMMANDS_PING_DESC"]; @@ -1057,7 +1101,7 @@ namespace SharedLibraryCore.Commands RequiresTarget = false; Arguments = new[] { - new CommandArgument() + new CommandArgument { Name = _translationLookup["COMMANDS_ARGS_PLAYER"], Required = false @@ -1086,13 +1130,14 @@ namespace SharedLibraryCore.Commands } /// - /// Sets the email for gravatar in webfront + /// Sets the email for gravatar in webfront /// public class SetGravatarCommand : Command { private readonly IMetaService _metaService; - public SetGravatarCommand(CommandConfiguration config, ITranslationLookup translationLookup, IMetaService metaService) : base(config, translationLookup) + public SetGravatarCommand(CommandConfiguration config, ITranslationLookup translationLookup, + IMetaService metaService) : base(config, translationLookup) { Name = "setgravatar"; Description = _translationLookup["COMMANDS_GRAVATAR_DESC"]; @@ -1101,7 +1146,7 @@ namespace SharedLibraryCore.Commands RequiresTarget = false; Arguments = new[] { - new CommandArgument() + new CommandArgument { Name = _translationLookup["COMMANDS_ARGS_GRAVATAR"], Required = true @@ -1115,8 +1160,9 @@ namespace SharedLibraryCore.Commands { using (var md5 = MD5.Create()) { - string gravatarEmail = string.Concat(md5.ComputeHash(E.Data.ToLower().Select(d => Convert.ToByte(d)).ToArray()) - .Select(h => h.ToString("x2"))); + var gravatarEmail = string.Concat(md5 + .ComputeHash(E.Data.ToLower().Select(d => Convert.ToByte(d)).ToArray()) + .Select(h => h.ToString("x2"))); await _metaService.AddPersistentMeta("GravatarEmail", gravatarEmail, E.Origin); } @@ -1125,23 +1171,26 @@ namespace SharedLibraryCore.Commands } /// - /// Retrieves the next map in rotation + /// Retrieves the next map in rotation /// public class NextMapCommand : Command { - public NextMapCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) + public NextMapCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, + translationLookup) { Name = "nextmap"; Description = _translationLookup["COMMANDS_NEXTMAP_DESC"]; Alias = "nm"; - Permission = EFClient.Permission.User; + Permission = Permission.User; RequiresTarget = false; } public static async Task GetNextMap(Server s, ITranslationLookup lookup) { - string mapRotation = (await s.GetDvarAsync("sv_mapRotation")).Value?.ToLower() ?? ""; - var regexMatches = Regex.Matches(mapRotation, @"((?:gametype|exec) +(?:([a-z]{1,4})(?:.cfg)?))? *map ([a-z|_|\d]+)", RegexOptions.IgnoreCase).ToList(); + var mapRotation = (await s.GetDvarAsync("sv_mapRotation")).Value?.ToLower() ?? ""; + var regexMatches = Regex.Matches(mapRotation, + @"((?:gametype|exec) +(?:([a-z]{1,4})(?:.cfg)?))? *map ([a-z|_|\d]+)", RegexOptions.IgnoreCase) + .ToList(); // find the current map in the rotation var currentMap = regexMatches.Where(m => m.Groups[3].ToString() == s.CurrentMap.Name); @@ -1151,7 +1200,8 @@ namespace SharedLibraryCore.Commands // no maprotation at all if (regexMatches.Count() == 0) { - return lookup["COMMANDS_NEXTMAP_SUCCESS"].FormatExt(s.CurrentMap.Alias, Utilities.GetLocalizedGametype(s.Gametype)); + return lookup["COMMANDS_NEXTMAP_SUCCESS"] + .FormatExt(s.CurrentMap.Alias, Utilities.GetLocalizedGametype(s.Gametype)); } // the current map is not in rotation @@ -1161,7 +1211,8 @@ namespace SharedLibraryCore.Commands } // there's duplicate maps in rotation - else if (currentMap.Count() > 1) + + if (currentMap.Count() > 1) { // gametype has been manually specified var duplicateMaps = currentMap.Where(m => !string.IsNullOrEmpty(m.Groups[1].ToString())); @@ -1176,16 +1227,17 @@ namespace SharedLibraryCore.Commands } // if the current map is the last map, the next map is the first map - var nextMapMatch = currentMap.First().Index != lastMap.Index ? - regexMatches[regexMatches.IndexOf(currentMap.First()) + 1] : - regexMatches.First(); + var nextMapMatch = currentMap.First().Index != lastMap.Index + ? regexMatches[regexMatches.IndexOf(currentMap.First()) + 1] + : regexMatches.First(); - string nextMapName = nextMapMatch.Groups[3].ToString(); + var nextMapName = nextMapMatch.Groups[3].ToString(); - nextMap = s.Maps.FirstOrDefault(m => m.Name == nextMapMatch.Groups[3].ToString()) ?? new Map() { Alias = nextMapName, Name = nextMapName }; - string nextGametype = nextMapMatch.Groups[2].ToString().Length == 0 ? - Utilities.GetLocalizedGametype(s.Gametype) : - Utilities.GetLocalizedGametype(nextMapMatch.Groups[2].ToString()); + nextMap = s.Maps.FirstOrDefault(m => m.Name == nextMapMatch.Groups[3].ToString()) ?? + new Map { Alias = nextMapName, Name = nextMapName }; + var nextGametype = nextMapMatch.Groups[2].ToString().Length == 0 + ? Utilities.GetLocalizedGametype(s.Gametype) + : Utilities.GetLocalizedGametype(nextMapMatch.Groups[2].ToString()); return lookup["COMMANDS_NEXTMAP_SUCCESS"].FormatExt(nextMap.Alias, nextGametype); } diff --git a/SharedLibraryCore/Commands/PrivateMessageAdminsCommand.cs b/SharedLibraryCore/Commands/PrivateMessageAdminsCommand.cs index 95db8b216..09b5e3709 100644 --- a/SharedLibraryCore/Commands/PrivateMessageAdminsCommand.cs +++ b/SharedLibraryCore/Commands/PrivateMessageAdminsCommand.cs @@ -1,16 +1,16 @@ -using SharedLibraryCore.Configuration; -using SharedLibraryCore.Database.Models; -using SharedLibraryCore.Interfaces; -using System; -using System.Linq; +using System.Linq; using System.Threading.Tasks; +using Data.Models.Client; +using SharedLibraryCore.Configuration; +using SharedLibraryCore.Interfaces; using static SharedLibraryCore.Server; namespace SharedLibraryCore.Commands { public class PrivateMessageAdminsCommand : Command { - public PrivateMessageAdminsCommand(CommandConfiguration config, ITranslationLookup lookup) : base(config, lookup) + public PrivateMessageAdminsCommand(CommandConfiguration config, ITranslationLookup lookup) : base(config, + lookup) { Name = "privatemessageadmin"; Description = lookup["COMMANDS_PMADMINS_DESC"]; @@ -21,12 +21,14 @@ namespace SharedLibraryCore.Commands public override Task ExecuteAsync(GameEvent E) { - bool isGameSupported = _config.Commands[nameof(PrivateMessageAdminsCommand)].SupportedGames.Length > 0 && - _config.Commands[nameof(PrivateMessageAdminsCommand)].SupportedGames.Contains(E.Owner.GameName); + var isGameSupported = _config.Commands[nameof(PrivateMessageAdminsCommand)].SupportedGames.Length > 0 && + _config.Commands[nameof(PrivateMessageAdminsCommand)].SupportedGames + .Contains(E.Owner.GameName); if (!isGameSupported) { - E.Origin.Tell(_translationLookup["COMMANDS_GAME_NOT_SUPPORTED"].FormatExt(nameof(PrivateMessageAdminsCommand))); + E.Origin.Tell(_translationLookup["COMMANDS_GAME_NOT_SUPPORTED"] + .FormatExt(nameof(PrivateMessageAdminsCommand))); return Task.CompletedTask; } @@ -34,4 +36,4 @@ namespace SharedLibraryCore.Commands return Task.CompletedTask; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Commands/RemoveClientTagCommand.cs b/SharedLibraryCore/Commands/RemoveClientTagCommand.cs index 95e7e16b2..405c89aa8 100644 --- a/SharedLibraryCore/Commands/RemoveClientTagCommand.cs +++ b/SharedLibraryCore/Commands/RemoveClientTagCommand.cs @@ -1,8 +1,8 @@ -using SharedLibraryCore.Configuration; -using SharedLibraryCore.Database.Models; -using SharedLibraryCore.Interfaces; -using System.Threading.Tasks; +using System.Threading.Tasks; using Data.Models; +using Data.Models.Client; +using SharedLibraryCore.Configuration; +using SharedLibraryCore.Interfaces; namespace SharedLibraryCore.Commands { @@ -10,7 +10,8 @@ namespace SharedLibraryCore.Commands { private readonly IMetaService _metaService; - public RemoveClientTag(CommandConfiguration config, ITranslationLookup layout, IMetaService metaService) : base(config, layout) + public RemoveClientTag(CommandConfiguration config, ITranslationLookup layout, IMetaService metaService) : base( + config, layout) { Name = "removeclienttag"; Description = layout["COMMANDS_REMOVE_CLIENT_TAG_DESC"]; @@ -19,7 +20,7 @@ namespace SharedLibraryCore.Commands RequiresTarget = false; Arguments = new[] { - new CommandArgument() + new CommandArgument { Name = _translationLookup["COMMANDS_ARGUMENT_TAG"], Required = true @@ -35,4 +36,4 @@ namespace SharedLibraryCore.Commands gameEvent.Origin.Tell(_translationLookup["COMMANDS_REMOVE_CLIENT_TAG_SUCCESS"].FormatExt(gameEvent.Data)); } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Commands/RequestTokenCommand.cs b/SharedLibraryCore/Commands/RequestTokenCommand.cs index 62733bb80..2409c53bb 100644 --- a/SharedLibraryCore/Commands/RequestTokenCommand.cs +++ b/SharedLibraryCore/Commands/RequestTokenCommand.cs @@ -1,12 +1,12 @@ -using SharedLibraryCore.Configuration; -using SharedLibraryCore.Database.Models; +using System.Threading.Tasks; +using Data.Models.Client; +using SharedLibraryCore.Configuration; using SharedLibraryCore.Interfaces; -using System.Threading.Tasks; namespace SharedLibraryCore.Commands { /// - /// Generates a token for use in webfront login + /// Generates a token for use in webfront login /// public class RequestTokenCommand : Command { @@ -22,9 +22,10 @@ namespace SharedLibraryCore.Commands public override Task ExecuteAsync(GameEvent E) { var state = E.Owner.Manager.TokenAuthenticator.GenerateNextToken(E.Origin.NetworkId); - E.Origin.Tell(string.Format(_translationLookup["COMMANDS_GENERATETOKEN_SUCCESS"], state.Token, $"{state.RemainingTime} {_translationLookup["GLOBAL_MINUTES"]}", E.Origin.ClientId)); + E.Origin.Tell(string.Format(_translationLookup["COMMANDS_GENERATETOKEN_SUCCESS"], state.Token, + $"{state.RemainingTime} {_translationLookup["GLOBAL_MINUTES"]}", E.Origin.ClientId)); return Task.CompletedTask; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Commands/RunAsCommand.cs b/SharedLibraryCore/Commands/RunAsCommand.cs index 290ac546b..c52209f7b 100644 --- a/SharedLibraryCore/Commands/RunAsCommand.cs +++ b/SharedLibraryCore/Commands/RunAsCommand.cs @@ -1,8 +1,8 @@ -using SharedLibraryCore.Configuration; -using SharedLibraryCore.Database.Models; -using SharedLibraryCore.Interfaces; -using System.Linq; +using System.Linq; using System.Threading.Tasks; +using Data.Models.Client; +using SharedLibraryCore.Configuration; +using SharedLibraryCore.Interfaces; namespace SharedLibraryCore.Commands { @@ -17,7 +17,7 @@ namespace SharedLibraryCore.Commands RequiresTarget = true; Arguments = new[] { - new CommandArgument() + new CommandArgument { Name = lookup["COMMANDS_ARGS_COMMANDS"], Required = true @@ -40,7 +40,7 @@ namespace SharedLibraryCore.Commands } var cmd = $"{Utilities.CommandPrefix}{gameEvent.Data}"; - var impersonatedCommandEvent = new GameEvent() + var impersonatedCommandEvent = new GameEvent { Type = GameEvent.EventType.Command, Origin = gameEvent.Target, @@ -52,7 +52,8 @@ namespace SharedLibraryCore.Commands }; gameEvent.Owner.Manager.AddEvent(impersonatedCommandEvent); - var result = await impersonatedCommandEvent.WaitAsync(Utilities.DefaultCommandTimeout, gameEvent.Owner.Manager.CancellationToken); + var result = await impersonatedCommandEvent.WaitAsync(Utilities.DefaultCommandTimeout, + gameEvent.Owner.Manager.CancellationToken); await result.WaitAsync(Utilities.DefaultCommandTimeout, gameEvent.Owner.Manager.CancellationToken); // remove the added command response @@ -61,11 +62,10 @@ namespace SharedLibraryCore.Commands .Where(ev => ev.Value.CorrelationId == impersonatedCommandEvent.CorrelationId) .SelectMany(ev => ev.Value.Output) .ToList(); - + foreach (var output in responses) - { - await gameEvent.Origin.Tell(_translationLookup["COMMANDS_RUN_AS_SUCCESS"].FormatExt(output)).WaitAsync(); - } + await gameEvent.Origin.Tell(_translationLookup["COMMANDS_RUN_AS_SUCCESS"].FormatExt(output)) + .WaitAsync(); } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Commands/SetClientTagCommand.cs b/SharedLibraryCore/Commands/SetClientTagCommand.cs index 52e3628ca..37959412a 100644 --- a/SharedLibraryCore/Commands/SetClientTagCommand.cs +++ b/SharedLibraryCore/Commands/SetClientTagCommand.cs @@ -1,9 +1,9 @@ -using SharedLibraryCore.Configuration; -using SharedLibraryCore.Database.Models; -using SharedLibraryCore.Interfaces; -using System.Linq; +using System.Linq; using System.Threading.Tasks; using Data.Models; +using Data.Models.Client; +using SharedLibraryCore.Configuration; +using SharedLibraryCore.Interfaces; namespace SharedLibraryCore.Commands { @@ -12,7 +12,8 @@ namespace SharedLibraryCore.Commands private readonly IMetaService _metaService; - public SetClientTagCommand(CommandConfiguration config, ITranslationLookup layout, IMetaService metaService) : base(config, layout) + public SetClientTagCommand(CommandConfiguration config, ITranslationLookup layout, IMetaService metaService) : + base(config, layout) { Name = "setclienttag"; Description = layout["COMMANDS_SET_CLIENT_TAG_DESC"]; @@ -21,7 +22,7 @@ namespace SharedLibraryCore.Commands RequiresTarget = true; Arguments = new[] { - new CommandArgument() + new CommandArgument { Name = _translationLookup["COMMANDS_ARGUMENT_TAG"], Required = true @@ -47,4 +48,4 @@ namespace SharedLibraryCore.Commands gameEvent.Origin.Tell(_translationLookup["COMMANDS_SET_CLIENT_TAG_SUCCESS"].FormatExt(matchingTag.Value)); } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Commands/UnlinkClientCommand.cs b/SharedLibraryCore/Commands/UnlinkClientCommand.cs index b81625e67..14014de68 100644 --- a/SharedLibraryCore/Commands/UnlinkClientCommand.cs +++ b/SharedLibraryCore/Commands/UnlinkClientCommand.cs @@ -1,16 +1,16 @@ -using SharedLibraryCore.Configuration; -using SharedLibraryCore.Database.Models; +using System.Threading.Tasks; +using Data.Models.Client; +using SharedLibraryCore.Configuration; using SharedLibraryCore.Interfaces; -using System.Threading.Tasks; namespace SharedLibraryCore.Commands { /// - /// Provides a way for administrators to "unlink" linked accounts - /// This problem is common in IW4x where the client identifier is a file - /// that is commonly transmitted when uploading and sharing the game files - /// This command creates a new link and assigns the guid, and all aliases with the current IP - /// associated to the provided client ID to the new link + /// Provides a way for administrators to "unlink" linked accounts + /// This problem is common in IW4x where the client identifier is a file + /// that is commonly transmitted when uploading and sharing the game files + /// This command creates a new link and assigns the guid, and all aliases with the current IP + /// associated to the provided client ID to the new link /// public class UnlinkClientCommand : Command { @@ -29,4 +29,4 @@ namespace SharedLibraryCore.Commands E.Origin.Tell(_translationLookup["COMMANDS_UNLINK_CLIENT_SUCCESS"].FormatExt(E.Target)); } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Commands/UnsetClientTagCommand.cs b/SharedLibraryCore/Commands/UnsetClientTagCommand.cs index 96737accb..64a9e40f5 100644 --- a/SharedLibraryCore/Commands/UnsetClientTagCommand.cs +++ b/SharedLibraryCore/Commands/UnsetClientTagCommand.cs @@ -1,9 +1,8 @@ -using SharedLibraryCore.Configuration; -using SharedLibraryCore.Database.Models; -using SharedLibraryCore.Interfaces; -using System.Linq; -using System.Threading.Tasks; +using System.Threading.Tasks; using Data.Models; +using Data.Models.Client; +using SharedLibraryCore.Configuration; +using SharedLibraryCore.Interfaces; namespace SharedLibraryCore.Commands { @@ -12,7 +11,8 @@ namespace SharedLibraryCore.Commands private readonly IMetaService _metaService; - public UnsetClientTagCommand(CommandConfiguration config, ITranslationLookup layout, IMetaService metaService) : base(config, layout) + public UnsetClientTagCommand(CommandConfiguration config, ITranslationLookup layout, IMetaService metaService) : + base(config, layout) { Name = "unsetclienttag"; Description = layout["COMMANDS_UNSET_CLIENT_TAG_DESC"]; @@ -21,7 +21,7 @@ namespace SharedLibraryCore.Commands RequiresTarget = true; Arguments = new[] { - new CommandArgument() + new CommandArgument { Name = _translationLookup["COMMANDS_ARGUMENT_TAG"], Required = true @@ -38,4 +38,4 @@ namespace SharedLibraryCore.Commands gameEvent.Origin.Tell(_translationLookup["COMMANDS_UNSET_CLIENT_TAG_SUCCESS"]); } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Configuration/ApplicationConfiguration.cs b/SharedLibraryCore/Configuration/ApplicationConfiguration.cs index 80869ef58..c397b172d 100644 --- a/SharedLibraryCore/Configuration/ApplicationConfiguration.cs +++ b/SharedLibraryCore/Configuration/ApplicationConfiguration.cs @@ -1,10 +1,10 @@ -using SharedLibraryCore.Configuration.Attributes; -using SharedLibraryCore.Interfaces; -using System; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using Newtonsoft.Json; +using SharedLibraryCore.Configuration.Attributes; +using SharedLibraryCore.Interfaces; using static Data.Models.Client.EFClient; namespace SharedLibraryCore.Configuration @@ -12,8 +12,9 @@ namespace SharedLibraryCore.Configuration public class ApplicationConfiguration : IBaseConfiguration { [ConfigurationIgnore] - public CommunityInformationConfiguration CommunityInformation { get; set; } = new CommunityInformationConfiguration(); - + public CommunityInformationConfiguration CommunityInformation { get; set; } = + new CommunityInformationConfiguration(); + [LocalizedDisplayName("SETUP_ENABLE_WEBFRONT")] [ConfigurationLinked("WebfrontBindUrl", "ManualWebfrontUrl", "WebfrontPrimaryColor", "WebfrontSecondaryColor", "WebfrontCustomBranding")] @@ -38,8 +39,7 @@ namespace SharedLibraryCore.Configuration [LocalizedDisplayName("WEBFRONT_CONFIGURATION_CUSTOM_BRANDING")] public string WebfrontCustomBranding { get; set; } - [ConfigurationIgnore] - public WebfrontConfiguration Webfront { get; set; } = new WebfrontConfiguration(); + [ConfigurationIgnore] public WebfrontConfiguration Webfront { get; set; } = new WebfrontConfiguration(); [LocalizedDisplayName("SETUP_ENABLE_MULTIOWN")] public bool EnableMultipleOwners { get; set; } @@ -116,8 +116,7 @@ namespace SharedLibraryCore.Configuration [LocalizedDisplayName("WEBFRONT_CONFIGURATION_ENABLE_COLOR_CODES")] public bool EnableColorCodes { get; set; } - [ConfigurationIgnore] - public string IngameAccentColorKey { get; set; } = "Cyan"; + [ConfigurationIgnore] public string IngameAccentColorKey { get; set; } = "Cyan"; [LocalizedDisplayName("WEBFRONT_CONFIGURATION_AUTOMESSAGE_PERIOD")] public int AutoMessagePeriod { get; set; } @@ -135,7 +134,8 @@ namespace SharedLibraryCore.Configuration public int MapChangeDelaySeconds { get; set; } = 5; [LocalizedDisplayName("WEBFRONT_CONFIGURATION_BAN_DURATIONS")] - public TimeSpan[] BanDurations { get; set; } = { + public TimeSpan[] BanDurations { get; set; } = + { TimeSpan.FromHours(1), TimeSpan.FromHours(6), TimeSpan.FromDays(1), @@ -147,36 +147,38 @@ namespace SharedLibraryCore.Configuration [ConfigurationIgnore] [LocalizedDisplayName("WEBFRONT_CONFIGURATION_PRESET_BAN_REASONS")] public Dictionary PresetPenaltyReasons { get; set; } = new Dictionary - {{"afk", "Away from keyboard"}, {"ci", "Connection interrupted. Reconnect"}}; - [LocalizedDisplayName(("WEBFRONT_CONFIGURATION_ENABLE_PRIVILEGED_USER_PRIVACY"))] + { { "afk", "Away from keyboard" }, { "ci", "Connection interrupted. Reconnect" } }; + + [LocalizedDisplayName("WEBFRONT_CONFIGURATION_ENABLE_PRIVILEGED_USER_PRIVACY")] public bool EnablePrivilegedUserPrivacy { get; set; } - [ConfigurationIgnore] - public bool EnableImplicitAccountLinking { get; set; } = false; + [ConfigurationIgnore] public bool EnableImplicitAccountLinking { get; set; } = false; - [ConfigurationIgnore] - public TimeSpan MaxClientHistoryTime { get; set; } = TimeSpan.FromHours(12); + [ConfigurationIgnore] public TimeSpan MaxClientHistoryTime { get; set; } = TimeSpan.FromHours(12); - [ConfigurationIgnore] - public TimeSpan ServerDataCollectionInterval { get; set; } = TimeSpan.FromMinutes(5); + [ConfigurationIgnore] public TimeSpan ServerDataCollectionInterval { get; set; } = TimeSpan.FromMinutes(5); public int ServerConnectionAttempts { get; set; } = 6; - + [ConfigurationIgnore] public Dictionary OverridePermissionLevelNames { get; set; } = Enum .GetValues(typeof(Permission)) .Cast() .ToDictionary(perm => perm, perm => perm.ToString()); - [UIHint("ServerConfiguration")] - public ServerConfiguration[] Servers { get; set; } + + [UIHint("ServerConfiguration")] public ServerConfiguration[] Servers { get; set; } [ConfigurationIgnore] public int MinimumNameLength { get; set; } = 3; [ConfigurationIgnore] public string Id { get; set; } [ConfigurationIgnore] public string SubscriptionId { get; set; } + [Obsolete("Moved to DefaultSettings")] - [ConfigurationIgnore] public MapConfiguration[] Maps { get; set; } + [ConfigurationIgnore] + public MapConfiguration[] Maps { get; set; } + [Obsolete("Moved to DefaultSettings")] - [ConfigurationIgnore] public QuickMessageConfiguration[] QuickMessages { get; set; } + [ConfigurationIgnore] + public QuickMessageConfiguration[] QuickMessages { get; set; } [ConfigurationIgnore] [JsonIgnore] @@ -192,30 +194,30 @@ namespace SharedLibraryCore.Configuration var loc = Utilities.CurrentLocalization.LocalizationIndex; Id = Guid.NewGuid().ToString(); - EnableWebFront = Utilities.PromptBool(loc["SETUP_ENABLE_WEBFRONT"]); - EnableMultipleOwners = Utilities.PromptBool(loc["SETUP_ENABLE_MULTIOWN"]); - EnableSteppedHierarchy = Utilities.PromptBool(loc["SETUP_ENABLE_STEPPEDPRIV"]); - EnableCustomSayName = Utilities.PromptBool(loc["SETUP_ENABLE_CUSTOMSAY"]); + EnableWebFront = loc["SETUP_ENABLE_WEBFRONT"].PromptBool(); + EnableMultipleOwners = loc["SETUP_ENABLE_MULTIOWN"].PromptBool(); + EnableSteppedHierarchy = loc["SETUP_ENABLE_STEPPEDPRIV"].PromptBool(); + EnableCustomSayName = loc["SETUP_ENABLE_CUSTOMSAY"].PromptBool(); - bool useCustomParserEncoding = Utilities.PromptBool(loc["SETUP_USE_CUSTOMENCODING"]); + var useCustomParserEncoding = loc["SETUP_USE_CUSTOMENCODING"].PromptBool(); if (useCustomParserEncoding) { - CustomParserEncoding = Utilities.PromptString(loc["SETUP_ENCODING_STRING"]); + CustomParserEncoding = loc["SETUP_ENCODING_STRING"].PromptString(); } WebfrontBindUrl = "http://0.0.0.0:1624"; if (EnableCustomSayName) { - CustomSayName = Utilities.PromptString(loc["SETUP_SAY_NAME"]); + CustomSayName = loc["SETUP_SAY_NAME"].PromptString(); } - EnableSocialLink = Utilities.PromptBool(loc["SETUP_DISPLAY_SOCIAL"]); + EnableSocialLink = loc["SETUP_DISPLAY_SOCIAL"].PromptBool(); if (EnableSocialLink) { - SocialLinkTitle = Utilities.PromptString(loc["SETUP_SOCIAL_TITLE"]); - SocialLinkAddress = Utilities.PromptString(loc["SETUP_SOCIAL_LINK"]); + SocialLinkTitle = loc["SETUP_SOCIAL_TITLE"].PromptString(); + SocialLinkAddress = loc["SETUP_SOCIAL_LINK"].PromptString(); } RConPollRate = 5000; diff --git a/SharedLibraryCore/Configuration/Attributes/ConfigurationIgnore.cs b/SharedLibraryCore/Configuration/Attributes/ConfigurationIgnore.cs index 53a0c3d38..d0c060e47 100644 --- a/SharedLibraryCore/Configuration/Attributes/ConfigurationIgnore.cs +++ b/SharedLibraryCore/Configuration/Attributes/ConfigurationIgnore.cs @@ -1,11 +1,9 @@ using System; -using System.Collections.Generic; -using System.Text; namespace SharedLibraryCore.Configuration.Attributes { - [AttributeUsage(AttributeTargets.Property, Inherited = false)] + [AttributeUsage(AttributeTargets.Property)] public class ConfigurationIgnore : Attribute { } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Configuration/Attributes/ConfigurationLinked.cs b/SharedLibraryCore/Configuration/Attributes/ConfigurationLinked.cs index 0c0efcd9d..b8c6acefa 100644 --- a/SharedLibraryCore/Configuration/Attributes/ConfigurationLinked.cs +++ b/SharedLibraryCore/Configuration/Attributes/ConfigurationLinked.cs @@ -2,14 +2,14 @@ namespace SharedLibraryCore.Configuration.Attributes { - [AttributeUsage(AttributeTargets.Property, Inherited = false)] + [AttributeUsage(AttributeTargets.Property)] public class ConfigurationLinked : Attribute { - public string[] LinkedPropertyNames { get; set; } - public ConfigurationLinked(params string[] linkedPropertyNames) { LinkedPropertyNames = linkedPropertyNames; } + + public string[] LinkedPropertyNames { get; set; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Configuration/Attributes/ConfigurationOptional.cs b/SharedLibraryCore/Configuration/Attributes/ConfigurationOptional.cs index 3ba98ae8f..731f3b68f 100644 --- a/SharedLibraryCore/Configuration/Attributes/ConfigurationOptional.cs +++ b/SharedLibraryCore/Configuration/Attributes/ConfigurationOptional.cs @@ -1,11 +1,9 @@ using System; -using System.Collections.Generic; -using System.Text; namespace SharedLibraryCore.Configuration.Attributes { - [AttributeUsage(AttributeTargets.Property, Inherited = false)] + [AttributeUsage(AttributeTargets.Property)] public class ConfigurationOptional : Attribute { } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Configuration/Attributes/LocalizedDisplayName.cs b/SharedLibraryCore/Configuration/Attributes/LocalizedDisplayName.cs index 39beb9504..b228d754b 100644 --- a/SharedLibraryCore/Configuration/Attributes/LocalizedDisplayName.cs +++ b/SharedLibraryCore/Configuration/Attributes/LocalizedDisplayName.cs @@ -1,13 +1,11 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Text; +using System.ComponentModel; namespace SharedLibraryCore.Configuration.Attributes { - class LocalizedDisplayName : DisplayNameAttribute + internal class LocalizedDisplayName : DisplayNameAttribute { private readonly string _localizationKey; + public LocalizedDisplayName(string localizationKey) { _localizationKey = localizationKey; @@ -15,4 +13,4 @@ namespace SharedLibraryCore.Configuration.Attributes public override string DisplayName => Utilities.CurrentLocalization.LocalizationIndex[_localizationKey]; } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Configuration/CommandConfiguration.cs b/SharedLibraryCore/Configuration/CommandConfiguration.cs index 508c66224..1461035e2 100644 --- a/SharedLibraryCore/Configuration/CommandConfiguration.cs +++ b/SharedLibraryCore/Configuration/CommandConfiguration.cs @@ -1,28 +1,29 @@ -using SharedLibraryCore.Interfaces; -using System; +using System; using System.Collections.Generic; using System.Text.Json.Serialization; +using SharedLibraryCore.Interfaces; namespace SharedLibraryCore.Configuration { /// - /// Basic command configuration + /// Basic command configuration /// public class CommandConfiguration : IBaseConfiguration { /// - /// Dict of command class names mapped to configurable properties + /// Dict of command class names mapped to configurable properties /// - public Dictionary Commands { get; set; } = new Dictionary(); + public Dictionary Commands { get; set; } = + new Dictionary(); /// - /// prefix indicated the chat message is a command + /// prefix indicated the chat message is a command /// [JsonIgnore] public string CommandPrefix { get; set; } /// - /// prefix indicating that the chat message is a broadcast command + /// prefix indicating that the chat message is a broadcast command /// [JsonIgnore] public string BroadcastCommandPrefix { get; set; } @@ -32,6 +33,9 @@ namespace SharedLibraryCore.Configuration throw new NotImplementedException(); } - public string Name() => nameof(CommandConfiguration); + public string Name() + { + return nameof(CommandConfiguration); + } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Configuration/CommandProperties.cs b/SharedLibraryCore/Configuration/CommandProperties.cs index 3643c8c5f..f6f559589 100644 --- a/SharedLibraryCore/Configuration/CommandProperties.cs +++ b/SharedLibraryCore/Configuration/CommandProperties.cs @@ -6,35 +6,35 @@ using static SharedLibraryCore.Server; namespace SharedLibraryCore.Configuration { /// - /// Config driven command properties + /// Config driven command properties /// public class CommandProperties { /// - /// Specifies the command name + /// Specifies the command name /// public string Name { get; set; } /// - /// Alias of this command + /// Alias of this command /// public string Alias { get; set; } /// - /// Specifies the minimum permission level needed to execute the + /// Specifies the minimum permission level needed to execute the /// [JsonConverter(typeof(StringEnumConverter))] public Permission MinimumPermission { get; set; } /// - /// Indicates if the command can be run by another user (impersonation) + /// Indicates if the command can be run by another user (impersonation) /// public bool AllowImpersonation { get; set; } /// - /// Specifies the games supporting the functionality of the command + /// Specifies the games supporting the functionality of the command /// [JsonProperty(ItemConverterType = typeof(StringEnumConverter))] public Game[] SupportedGames { get; set; } = new Game[0]; } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Configuration/CommunityInformationConfiguration.cs b/SharedLibraryCore/Configuration/CommunityInformationConfiguration.cs index 381ff0738..b3af44d9e 100644 --- a/SharedLibraryCore/Configuration/CommunityInformationConfiguration.cs +++ b/SharedLibraryCore/Configuration/CommunityInformationConfiguration.cs @@ -1,12 +1,12 @@ -using System.Linq; - -namespace SharedLibraryCore.Configuration +namespace SharedLibraryCore.Configuration { public class CommunityInformationConfiguration { public string Name { get; set; } = "IW4MAdmin - Configure In IW4MAdminSettings.json"; + public string Description { get; set; } = "IW4MAdmin is an administration tool for IW4x, Pluto T6, Pluto IW5, CoD4x, TeknoMW3, and most Call of Duty® dedicated servers. It allows complete control of your server; from changing maps, to banning players, IW4MAdmin monitors and records activity on your server(s). With plugin support, extending its functionality is a breeze."; + public bool EnableBanner { get; set; } = true; public SocialAccountConfiguration[] SocialAccounts { get; set; } = diff --git a/SharedLibraryCore/Configuration/DefaultSettings.cs b/SharedLibraryCore/Configuration/DefaultSettings.cs index 5f0e6bdda..18c833fd6 100644 --- a/SharedLibraryCore/Configuration/DefaultSettings.cs +++ b/SharedLibraryCore/Configuration/DefaultSettings.cs @@ -8,12 +8,18 @@ namespace SharedLibraryCore.Configuration public string[] GlobalRules { get; set; } public MapConfiguration[] Maps { get; set; } public GametypeConfiguration[] Gametypes { get; set; } - public QuickMessageConfiguration[] QuickMessages {get; set;} + public QuickMessageConfiguration[] QuickMessages { get; set; } public string[] DisallowedClientNames { get; set; } public GameStringConfiguration GameStrings { get; set; } - public IBaseConfiguration Generate() => this; + public IBaseConfiguration Generate() + { + return this; + } - public string Name() => "DefaultConfiguration"; + public string Name() + { + return "DefaultConfiguration"; + } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Configuration/Extensions/ConfigurationExtensions.cs b/SharedLibraryCore/Configuration/Extensions/ConfigurationExtensions.cs index 125fb81d3..ca5df9e07 100644 --- a/SharedLibraryCore/Configuration/Extensions/ConfigurationExtensions.cs +++ b/SharedLibraryCore/Configuration/Extensions/ConfigurationExtensions.cs @@ -1,10 +1,10 @@ using System; -using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; +using System.Runtime.InteropServices; using System.Text.RegularExpressions; using Microsoft.Win32; using SharedLibraryCore.Interfaces; @@ -43,6 +43,11 @@ namespace SharedLibraryCore.Configuration.Extensions string searchPath = null; var isRegistryKey = parser.Configuration.DefaultInstallationDirectoryHint.Contains("HKEY_"); + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return null; + } + try { if (isRegistryKey) @@ -51,10 +56,10 @@ namespace SharedLibraryCore.Configuration.Extensions if (result == null) { - return new (string, string)[0]; + return Array.Empty<(string, string)>(); } - searchPath = Path.Combine(result.ToString().Split(Path.DirectorySeparatorChar) + searchPath = Path.Combine(result.ToString()!.Split(Path.DirectorySeparatorChar) .Where(p => !p.Contains(".exe")) .Select(p => p.Replace("\"", "")).ToArray()); } @@ -72,14 +77,14 @@ namespace SharedLibraryCore.Configuration.Extensions if (string.IsNullOrEmpty(searchPath)) { - return new (string, string)[0]; + return Array.Empty<(string, string)>(); } var possibleFiles = Directory.GetFiles(searchPath, "*.cfg", SearchOption.AllDirectories); if (!possibleFiles.Any()) { - return new (string, string)[0]; + return Array.Empty<(string, string)>(); } var possiblePasswords = possibleFiles.SelectMany(File.ReadAllLines) @@ -95,8 +100,8 @@ namespace SharedLibraryCore.Configuration.Extensions } catch { - return new (string, string)[0]; + return Array.Empty<(string, string)>(); } } } -} \ No newline at end of file +} diff --git a/SharedLibraryCore/Configuration/GametypeConfiguration.cs b/SharedLibraryCore/Configuration/GametypeConfiguration.cs index 84f75edab..359a0ea5a 100644 --- a/SharedLibraryCore/Configuration/GametypeConfiguration.cs +++ b/SharedLibraryCore/Configuration/GametypeConfiguration.cs @@ -5,4 +5,4 @@ public Server.Game Game { get; set; } public Gametype[] Gametypes { get; set; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Configuration/MapConfiguration.cs b/SharedLibraryCore/Configuration/MapConfiguration.cs index 34b91aed1..62c2def00 100644 --- a/SharedLibraryCore/Configuration/MapConfiguration.cs +++ b/SharedLibraryCore/Configuration/MapConfiguration.cs @@ -5,4 +5,4 @@ public Server.Game Game { get; set; } public Map[] Maps { get; set; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Configuration/QuickMessageConfiguration.cs b/SharedLibraryCore/Configuration/QuickMessageConfiguration.cs index 852c04fe7..93ede3671 100644 --- a/SharedLibraryCore/Configuration/QuickMessageConfiguration.cs +++ b/SharedLibraryCore/Configuration/QuickMessageConfiguration.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; +using System.Collections.Generic; using static SharedLibraryCore.Server; namespace SharedLibraryCore.Configuration @@ -10,4 +8,4 @@ namespace SharedLibraryCore.Configuration public Game Game { get; set; } public Dictionary Messages { get; set; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Configuration/ServerConfiguration.cs b/SharedLibraryCore/Configuration/ServerConfiguration.cs index 270ff9db7..2c3e12624 100644 --- a/SharedLibraryCore/Configuration/ServerConfiguration.cs +++ b/SharedLibraryCore/Configuration/ServerConfiguration.cs @@ -1,14 +1,24 @@ -using SharedLibraryCore.Configuration.Attributes; -using SharedLibraryCore.Interfaces; -using System; +using System; using System.Collections.Generic; using System.Linq; +using SharedLibraryCore.Configuration.Attributes; using SharedLibraryCore.Configuration.Extensions; +using SharedLibraryCore.Interfaces; namespace SharedLibraryCore.Configuration { public class ServerConfiguration : IBaseConfiguration { + private readonly IList _rconParsers; + private IRConParser _selectedParser; + + public ServerConfiguration() + { + _rconParsers = new List(); + Rules = new string[0]; + AutoMessages = new string[0]; + } + [LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_IP")] public string IPAddress { get; set; } @@ -45,14 +55,74 @@ namespace SharedLibraryCore.Configuration [ConfigurationOptional] public string CustomHostname { get; set; } - private readonly IList _rconParsers; - private IRConParser _selectedParser; - - public ServerConfiguration() + public IBaseConfiguration Generate() { - _rconParsers = new List(); - Rules = new string[0]; + ModifyParsers(); + var loc = Utilities.CurrentLocalization.LocalizationIndex; + var shouldTryFindIp = loc["SETUP_SERVER_IP_AUTO"].PromptBool(defaultValue: true); + + if (shouldTryFindIp) + { + this.TrySetIpAddress(); + Console.WriteLine(loc["SETUP_SERVER_IP_AUTO_RESULT"].FormatExt(IPAddress)); + } + + else + { + while (string.IsNullOrEmpty(IPAddress)) + { + var input = loc["SETUP_SERVER_IP"].PromptString(); + IPAddress = input; + } + } + + var defaultPort = _selectedParser.Configuration.DefaultRConPort; + Port = loc["SETUP_SERVER_PORT"].PromptInt(null, 1, ushort.MaxValue, defaultPort); + + if (!string.IsNullOrEmpty(_selectedParser.Configuration.DefaultInstallationDirectoryHint)) + { + var shouldTryFindPassword = loc["SETUP_RCON_PASSWORD_AUTO"].PromptBool(defaultValue: true); + + if (shouldTryFindPassword) + { + var passwords = _selectedParser.TryGetRConPasswords(); + if (passwords.Length > 1) + { + var (index, value) = + loc["SETUP_RCON_PASSWORD_PROMPT"].PromptSelection(loc["SETUP_RCON_PASSWORD_MANUAL"], null, + passwords.Select(pw => + $"{pw.Item1}{(string.IsNullOrEmpty(pw.Item2) ? "" : " " + pw.Item2)}") + .ToArray()); + + if (index > 0) + { + Password = passwords[index - 1].Item1; + } + } + + else if (passwords.Length > 0) + { + Password = passwords[0].Item1; + Console.WriteLine(loc["SETUP_RCON_PASSWORD_RESULT"].FormatExt(Password)); + } + } + } + + if (string.IsNullOrEmpty(Password)) + { + Password = loc["SETUP_SERVER_RCON"].PromptString(); + } + AutoMessages = new string[0]; + Rules = new string[0]; + ManualLogPath = null; + + return this; + } + + public string Name() + { + return "ServerConfiguration"; } public void AddRConParser(IRConParser parser) @@ -88,74 +158,5 @@ namespace SharedLibraryCore.Configuration Console.WriteLine(loc["SETUP_SERVER_NO_LOG"]); ManualLogPath = loc["SETUP_SERVER_LOG_PATH"].PromptString(); } - - public IBaseConfiguration Generate() - { - ModifyParsers(); - var loc = Utilities.CurrentLocalization.LocalizationIndex; - var shouldTryFindIp = loc["SETUP_SERVER_IP_AUTO"].PromptBool(defaultValue: true); - - if (shouldTryFindIp) - { - this.TrySetIpAddress(); - Console.WriteLine(loc["SETUP_SERVER_IP_AUTO_RESULT"].FormatExt(IPAddress)); - } - - else - { - while (string.IsNullOrEmpty(IPAddress)) - { - var input = loc["SETUP_SERVER_IP"].PromptString(); - IPAddress = input; - } - } - - var defaultPort = _selectedParser.Configuration.DefaultRConPort; - Port = loc["SETUP_SERVER_PORT"].PromptInt(null, 1, ushort.MaxValue, defaultValue:defaultPort); - - if (!string.IsNullOrEmpty(_selectedParser.Configuration.DefaultInstallationDirectoryHint)) - { - var shouldTryFindPassword = loc["SETUP_RCON_PASSWORD_AUTO"].PromptBool(defaultValue: true); - - if (shouldTryFindPassword) - { - var passwords = _selectedParser.TryGetRConPasswords(); - if (passwords.Length > 1) - { - var (index, value) = - loc["SETUP_RCON_PASSWORD_PROMPT"].PromptSelection(loc["SETUP_RCON_PASSWORD_MANUAL"], null, - passwords.Select(pw => - $"{pw.Item1}{(string.IsNullOrEmpty(pw.Item2) ? "" : " " + pw.Item2)}").ToArray()); - - if (index > 0) - { - Password = passwords[index - 1].Item1; - } - } - - else if (passwords.Length > 0) - { - Password = passwords[0].Item1; - Console.WriteLine(loc["SETUP_RCON_PASSWORD_RESULT"].FormatExt(Password)); - } - } - } - - if (string.IsNullOrEmpty(Password)) - { - Password = loc["SETUP_SERVER_RCON"].PromptString(); - } - - AutoMessages = new string[0]; - Rules = new string[0]; - ManualLogPath = null; - - return this; - } - - public string Name() - { - return "ServerConfiguration"; - } } -} \ No newline at end of file +} diff --git a/SharedLibraryCore/Configuration/Validation/ApplicationConfigurationValidator.cs b/SharedLibraryCore/Configuration/Validation/ApplicationConfigurationValidator.cs index 69f7e46c4..4841d373d 100644 --- a/SharedLibraryCore/Configuration/Validation/ApplicationConfigurationValidator.cs +++ b/SharedLibraryCore/Configuration/Validation/ApplicationConfigurationValidator.cs @@ -1,11 +1,12 @@ -using FluentValidation; -using System; +using System; using System.Linq; +using System.Net; +using FluentValidation; namespace SharedLibraryCore.Configuration.Validation { /// - /// Validation class for main application configuration + /// Validation class for main application configuration /// public class ApplicationConfigurationValidator : AbstractValidator { @@ -23,27 +24,27 @@ namespace SharedLibraryCore.Configuration.Validation .When(_app => _app.EnableSocialLink); RuleFor(_app => _app.SocialLinkTitle) - .NotEmpty() - .When(_app => _app.EnableSocialLink); + .NotEmpty() + .When(_app => _app.EnableSocialLink); RuleFor(_app => _app.CustomParserEncoding) - .NotEmpty() - .When(_app => _app.EnableCustomParserEncoding); + .NotEmpty() + .When(_app => _app.EnableCustomParserEncoding); RuleFor(_app => _app.WebfrontConnectionWhitelist) .NotEmpty() .When(_app => _app.EnableWebfrontConnectionWhitelist); RuleForEach(_app => _app.WebfrontConnectionWhitelist) - .Must(_address => System.Net.IPAddress.TryParse(_address, out _)); + .Must(_address => IPAddress.TryParse(_address, out _)); RuleFor(_app => _app.CustomLocale) - .NotEmpty() - .When(_app => _app.EnableCustomLocale); + .NotEmpty() + .When(_app => _app.EnableCustomLocale); RuleFor(_app => _app.DatabaseProvider) - .NotEmpty() - .Must(_provider => new[] { "sqlite", "mysql", "postgresql" }.Contains(_provider)); + .NotEmpty() + .Must(_provider => new[] { "sqlite", "mysql", "postgresql" }.Contains(_provider)); RuleFor(_app => _app.ConnectionString) .NotEmpty() @@ -79,4 +80,4 @@ namespace SharedLibraryCore.Configuration.Validation .SetValidator(new ServerConfigurationValidator()); } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Configuration/Validation/ServerConfigurationValidator.cs b/SharedLibraryCore/Configuration/Validation/ServerConfigurationValidator.cs index decc448d6..cdd93bd32 100644 --- a/SharedLibraryCore/Configuration/Validation/ServerConfigurationValidator.cs +++ b/SharedLibraryCore/Configuration/Validation/ServerConfigurationValidator.cs @@ -1,10 +1,9 @@ using FluentValidation; -using System.Net; namespace SharedLibraryCore.Configuration.Validation { /// - /// Validation class for server configuration + /// Validation class for server configuration /// public class ServerConfigurationValidator : AbstractValidator { @@ -30,4 +29,4 @@ namespace SharedLibraryCore.Configuration.Validation .MaximumLength(128); } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Dtos/AuditInfo.cs b/SharedLibraryCore/Dtos/AuditInfo.cs index bf963f51b..849a2878c 100644 --- a/SharedLibraryCore/Dtos/AuditInfo.cs +++ b/SharedLibraryCore/Dtos/AuditInfo.cs @@ -3,55 +3,65 @@ namespace SharedLibraryCore.Dtos { /// - /// data transfer class for audit information + /// data transfer class for audit information /// public class AuditInfo { + private string newValue; + + private string oldValue; + /// - /// name of the origin entity + /// name of the origin entity /// public string OriginName { get; set; } /// - /// id of the origin entity + /// id of the origin entity /// public int OriginId { get; set; } /// - /// name of the target entity + /// name of the target entity /// public string TargetName { get; set; } /// - /// id of the target entity + /// id of the target entity /// public int? TargetId { get; set; } /// - /// when the audit event occured + /// when the audit event occured /// public DateTime When { get; set; } - + /// - /// what audit action occured + /// what audit action occured /// public string Action { get; set; } /// - /// additional comment data about the audit event + /// additional comment data about the audit event /// public string Data { get; set; } - private string oldValue; /// - /// previous value + /// previous value /// - public string OldValue { get => oldValue ?? "--"; set => oldValue = value; } + public string OldValue + { + get => oldValue ?? "--"; + set => oldValue = value; + } - private string newValue; /// - /// new value + /// new value /// - public string NewValue { get => newValue ?? "--"; set => newValue = value; } + public string NewValue + { + get => newValue ?? "--"; + set => newValue = value; + } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Dtos/ClientInfo.cs b/SharedLibraryCore/Dtos/ClientInfo.cs index da8ed6d4d..5cf4dcbf8 100644 --- a/SharedLibraryCore/Dtos/ClientInfo.cs +++ b/SharedLibraryCore/Dtos/ClientInfo.cs @@ -1,10 +1,12 @@ -namespace SharedLibraryCore.Dtos +using Data.Models.Client; + +namespace SharedLibraryCore.Dtos { public class ClientInfo { public string Name { get; set; } public int ClientId { get; set; } public int LinkId { get; set; } - public Database.Models.EFClient.Permission Level { get; set; } + public EFClient.Permission Level { get; set; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Dtos/CommandResponseInfo.cs b/SharedLibraryCore/Dtos/CommandResponseInfo.cs index 39f906c48..9a33f6009 100644 --- a/SharedLibraryCore/Dtos/CommandResponseInfo.cs +++ b/SharedLibraryCore/Dtos/CommandResponseInfo.cs @@ -1,14 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace SharedLibraryCore.Dtos +namespace SharedLibraryCore.Dtos { public class CommandResponseInfo { public string Response { get; set; } public int ClientId { get; set; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Dtos/EntityInfo.cs b/SharedLibraryCore/Dtos/EntityInfo.cs index 79b950010..b3c7753b4 100644 --- a/SharedLibraryCore/Dtos/EntityInfo.cs +++ b/SharedLibraryCore/Dtos/EntityInfo.cs @@ -1,15 +1,11 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace SharedLibraryCore.Dtos +namespace SharedLibraryCore.Dtos { /// - /// This class holds the basic info for api entities + /// This class holds the basic info for api entities /// public class EntityInfo { public long Id { get; set; } public string Name { get; set; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Dtos/ErrorResponse.cs b/SharedLibraryCore/Dtos/ErrorResponse.cs index ea9744b5e..065bd2aac 100644 --- a/SharedLibraryCore/Dtos/ErrorResponse.cs +++ b/SharedLibraryCore/Dtos/ErrorResponse.cs @@ -3,13 +3,13 @@ public class ErrorResponse { /// - /// todo: type of error + /// todo: type of error /// public string Type { get; set; } /// - /// relevant error messages + /// relevant error messages /// public string[] Messages { get; set; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Dtos/EventInfo.cs b/SharedLibraryCore/Dtos/EventInfo.cs index 09fa1f9e3..0b8aafbf1 100644 --- a/SharedLibraryCore/Dtos/EventInfo.cs +++ b/SharedLibraryCore/Dtos/EventInfo.cs @@ -1,10 +1,9 @@ using System; -using static SharedLibraryCore.GameEvent; namespace SharedLibraryCore.Dtos { /// - /// This class wraps the information related to a generated event for the API + /// This class wraps the information related to a generated event for the API /// public class EventInfo { @@ -15,6 +14,6 @@ namespace SharedLibraryCore.Dtos public EntityInfo OwnerEntity { get; set; } public DateTime EventTime { get; set; } public string ExtraInfo { get; set; } - public string Id { get; private set; } = Guid.NewGuid().ToString(); + public string Id { get; } = Guid.NewGuid().ToString(); } } \ No newline at end of file diff --git a/SharedLibraryCore/Dtos/FindClientRequest.cs b/SharedLibraryCore/Dtos/FindClientRequest.cs index b1869a72d..b5615607a 100644 --- a/SharedLibraryCore/Dtos/FindClientRequest.cs +++ b/SharedLibraryCore/Dtos/FindClientRequest.cs @@ -3,15 +3,18 @@ public class FindClientRequest : PaginationRequest { /// - /// name of client + /// name of client /// public string Name { get; set; } /// - /// network id of client + /// network id of client /// public string Xuid { get; set; } - public string ToDebugString() => $"[Name={Name}, Xuid={Xuid}]"; + public string ToDebugString() + { + return $"[Name={Name}, Xuid={Xuid}]"; + } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Dtos/FindClientResult.cs b/SharedLibraryCore/Dtos/FindClientResult.cs index a1995df3e..51a40a935 100644 --- a/SharedLibraryCore/Dtos/FindClientResult.cs +++ b/SharedLibraryCore/Dtos/FindClientResult.cs @@ -3,18 +3,18 @@ public class FindClientResult { /// - /// client identifier + /// client identifier /// public int ClientId { get; set; } /// - /// networkid of client + /// networkid of client /// public string Xuid { get; set; } /// - /// name of client + /// name of client /// public string Name { get; set; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Dtos/IW4MAdminInfo.cs b/SharedLibraryCore/Dtos/IW4MAdminInfo.cs index 0dab1eb96..bcb481a6b 100644 --- a/SharedLibraryCore/Dtos/IW4MAdminInfo.cs +++ b/SharedLibraryCore/Dtos/IW4MAdminInfo.cs @@ -13,13 +13,13 @@ namespace SharedLibraryCore.Dtos public DateTime MaxConcurrentClientsTime { get; set; } /// - /// specifies the game name filter + /// specifies the game name filter /// public Game? Game { get; set; } /// - /// collection of unique game names being monitored + /// collection of unique game names being monitored /// public Game[] ActiveServerGames { get; set; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Dtos/Meta/Requests/BaseClientMetaRequest.cs b/SharedLibraryCore/Dtos/Meta/Requests/BaseClientMetaRequest.cs index 25479339f..6b75e690d 100644 --- a/SharedLibraryCore/Dtos/Meta/Requests/BaseClientMetaRequest.cs +++ b/SharedLibraryCore/Dtos/Meta/Requests/BaseClientMetaRequest.cs @@ -1,11 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace SharedLibraryCore.Dtos.Meta.Requests +namespace SharedLibraryCore.Dtos.Meta.Requests { public class BaseClientMetaRequest : PaginationRequest { public int ClientId { get; set; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Dtos/Meta/Requests/ReceivedPenaltyRequest.cs b/SharedLibraryCore/Dtos/Meta/Requests/ReceivedPenaltyRequest.cs index 4c959894f..05bc0616d 100644 --- a/SharedLibraryCore/Dtos/Meta/Requests/ReceivedPenaltyRequest.cs +++ b/SharedLibraryCore/Dtos/Meta/Requests/ReceivedPenaltyRequest.cs @@ -1,11 +1,6 @@ -using SharedLibraryCore.QueryHelper; -using System; -using System.Collections.Generic; -using System.Text; - -namespace SharedLibraryCore.Dtos.Meta.Requests +namespace SharedLibraryCore.Dtos.Meta.Requests { public class ReceivedPenaltyRequest : BaseClientMetaRequest { } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Dtos/Meta/Responses/AdministeredPenaltyResponse.cs b/SharedLibraryCore/Dtos/Meta/Responses/AdministeredPenaltyResponse.cs index f985f6570..5f4731e8d 100644 --- a/SharedLibraryCore/Dtos/Meta/Responses/AdministeredPenaltyResponse.cs +++ b/SharedLibraryCore/Dtos/Meta/Responses/AdministeredPenaltyResponse.cs @@ -1,10 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace SharedLibraryCore.Dtos.Meta.Responses +namespace SharedLibraryCore.Dtos.Meta.Responses { public class AdministeredPenaltyResponse : ReceivedPenaltyResponse { } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Dtos/Meta/Responses/BaseMetaResponse.cs b/SharedLibraryCore/Dtos/Meta/Responses/BaseMetaResponse.cs index 28e0a167f..4d4a9c769 100644 --- a/SharedLibraryCore/Dtos/Meta/Responses/BaseMetaResponse.cs +++ b/SharedLibraryCore/Dtos/Meta/Responses/BaseMetaResponse.cs @@ -1,17 +1,17 @@ -using SharedLibraryCore.Interfaces; -using System; +using System; +using SharedLibraryCore.Interfaces; namespace SharedLibraryCore.Dtos.Meta.Responses { public class BaseMetaResponse : IClientMeta, IClientMetaResponse { - public long MetaId { get; set; } - public int ClientId { get; set; } public MetaType Type { get; set; } public DateTime When { get; set; } public bool IsSensitive { get; set; } public bool ShouldDisplay { get; set; } public int? Column { get; set; } public int? Order { get; set; } + public long MetaId { get; set; } + public int ClientId { get; set; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Dtos/Meta/Responses/InformationResponse.cs b/SharedLibraryCore/Dtos/Meta/Responses/InformationResponse.cs index adfd090ad..c95956768 100644 --- a/SharedLibraryCore/Dtos/Meta/Responses/InformationResponse.cs +++ b/SharedLibraryCore/Dtos/Meta/Responses/InformationResponse.cs @@ -1,9 +1,4 @@ -using SharedLibraryCore.Interfaces; -using System; -using System.Collections.Generic; -using System.Text; - -namespace SharedLibraryCore.Dtos.Meta.Responses +namespace SharedLibraryCore.Dtos.Meta.Responses { public class InformationResponse : BaseMetaResponse { @@ -11,4 +6,4 @@ namespace SharedLibraryCore.Dtos.Meta.Responses public string Value { get; set; } public string ToolTipText { get; set; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Dtos/Meta/Responses/MessageResponse.cs b/SharedLibraryCore/Dtos/Meta/Responses/MessageResponse.cs index 17f90e700..ee99c2bee 100644 --- a/SharedLibraryCore/Dtos/Meta/Responses/MessageResponse.cs +++ b/SharedLibraryCore/Dtos/Meta/Responses/MessageResponse.cs @@ -9,30 +9,30 @@ namespace SharedLibraryCore.Dtos.Meta.Responses public bool IsHidden { get; set; } /// - /// name of the client + /// name of the client /// public string ClientName { get; set; } /// - /// hostname of the server + /// hostname of the server /// public string ServerName { get; set; } /// - /// specifies the game the chat occured on + /// specifies the game the chat occured on /// public Server.Game GameName { get; set; } /// - /// indicates if the chat message is a quick message phrase + /// indicates if the chat message is a quick message phrase /// public bool IsQuickMessage { get; set; } - + /// - /// indicates if the message was sent ingame + /// indicates if the message was sent ingame /// public bool SentIngame { get; set; } public string HiddenMessage => string.Concat(Enumerable.Repeat('●', Message.Length)); } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Dtos/Meta/Responses/ReceivedPenaltyResponse.cs b/SharedLibraryCore/Dtos/Meta/Responses/ReceivedPenaltyResponse.cs index 9a796f01a..d5dc5f304 100644 --- a/SharedLibraryCore/Dtos/Meta/Responses/ReceivedPenaltyResponse.cs +++ b/SharedLibraryCore/Dtos/Meta/Responses/ReceivedPenaltyResponse.cs @@ -14,9 +14,16 @@ namespace SharedLibraryCore.Dtos.Meta.Responses public string Offense { get; set; } public string AutomatedOffense { get; set; } public DateTime? ExpirationDate { get; set; } - public string ExpiresInText => ExpirationDate.HasValue && ExpirationDate.Value > DateTime.UtcNow ? (ExpirationDate - DateTime.UtcNow).Value.HumanizeForCurrentCulture() : ""; - public string LengthText => ExpirationDate.HasValue ? (ExpirationDate.Value.AddMinutes(1) - When).HumanizeForCurrentCulture() : ""; + + public string ExpiresInText => ExpirationDate.HasValue && ExpirationDate.Value > DateTime.UtcNow + ? (ExpirationDate - DateTime.UtcNow).Value.HumanizeForCurrentCulture() + : ""; + + public string LengthText => ExpirationDate.HasValue + ? (ExpirationDate.Value.AddMinutes(1) - When).HumanizeForCurrentCulture() + : ""; + public bool IsLinked { get; set; } public int LinkedClientId { get; set; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Dtos/Meta/Responses/UpdatedAliasResponse.cs b/SharedLibraryCore/Dtos/Meta/Responses/UpdatedAliasResponse.cs index d3460d2a6..0c910e306 100644 --- a/SharedLibraryCore/Dtos/Meta/Responses/UpdatedAliasResponse.cs +++ b/SharedLibraryCore/Dtos/Meta/Responses/UpdatedAliasResponse.cs @@ -15,6 +15,9 @@ return false; } - public override int GetHashCode() => $"{Name.StripColors()}{IPAddress}".GetStableHashCode(); + public override int GetHashCode() + { + return $"{Name.StripColors()}{IPAddress}".GetStableHashCode(); + } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Dtos/Meta/WebfrontTranslationHelper.cs b/SharedLibraryCore/Dtos/Meta/WebfrontTranslationHelper.cs index eb3a692d7..faf2bd8e6 100644 --- a/SharedLibraryCore/Dtos/Meta/WebfrontTranslationHelper.cs +++ b/SharedLibraryCore/Dtos/Meta/WebfrontTranslationHelper.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace SharedLibraryCore.Dtos.Meta +namespace SharedLibraryCore.Dtos.Meta { public class WebfrontTranslationHelper { @@ -10,4 +6,4 @@ namespace SharedLibraryCore.Dtos.Meta public string MatchValue { get; set; } public string TranslationValue { get; set; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Dtos/Page.cs b/SharedLibraryCore/Dtos/Page.cs index e24cbc576..7336a310a 100644 --- a/SharedLibraryCore/Dtos/Page.cs +++ b/SharedLibraryCore/Dtos/Page.cs @@ -5,4 +5,4 @@ public string Name { get; set; } public string Location { get; set; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Dtos/PaginationRequest.cs b/SharedLibraryCore/Dtos/PaginationRequest.cs index e5c6fd364..bb6c7b398 100644 --- a/SharedLibraryCore/Dtos/PaginationRequest.cs +++ b/SharedLibraryCore/Dtos/PaginationRequest.cs @@ -3,27 +3,27 @@ namespace SharedLibraryCore.Dtos { /// - /// pagination information holder class + /// pagination information holder class /// public class PaginationRequest { /// - /// how many items to skip + /// how many items to skip /// public int Offset { get; set; } /// - /// how many itesm to take + /// how many itesm to take /// public int Count { get; set; } = 100; /// - /// filter query + /// filter query /// public string Filter { get; set; } /// - /// direction of ordering + /// direction of ordering /// public SortDirection Direction { get; set; } = SortDirection.Descending; @@ -35,4 +35,4 @@ namespace SharedLibraryCore.Dtos Ascending, Descending } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Dtos/PenaltyInfo.cs b/SharedLibraryCore/Dtos/PenaltyInfo.cs index f325227ca..449d5b195 100644 --- a/SharedLibraryCore/Dtos/PenaltyInfo.cs +++ b/SharedLibraryCore/Dtos/PenaltyInfo.cs @@ -22,11 +22,20 @@ namespace SharedLibraryCore.Dtos public string PenaltyTypeText => PenaltyType.ToString(); public DateTime TimePunished { get; set; } public string TimePunishedString => TimePunished.HumanizeForCurrentCulture(); - public string TimeRemaining => DateTime.UtcNow > Expires ? "" : $"{((Expires ?? DateTime.MaxValue).Year == DateTime.MaxValue.Year ? TimePunishedString : ((Expires ?? DateTime.MaxValue) - DateTime.UtcNow).HumanizeForCurrentCulture())}"; + + public string TimeRemaining => DateTime.UtcNow > Expires + ? "" + : $"{((Expires ?? DateTime.MaxValue).Year == DateTime.MaxValue.Year ? TimePunishedString : ((Expires ?? DateTime.MaxValue) - DateTime.UtcNow).HumanizeForCurrentCulture())}"; + public bool Expired => Expires.HasValue && Expires <= DateTime.UtcNow; public DateTime? Expires { get; set; } - public override bool Sensitive => PenaltyType == EFPenalty.PenaltyType.Flag || PenaltyType == EFPenalty.PenaltyType.Unflag; + + public override bool Sensitive => + PenaltyType == EFPenalty.PenaltyType.Flag || PenaltyType == EFPenalty.PenaltyType.Unflag; + public bool IsEvade { get; set; } - public string AdditionalPenaltyInformation => $"{(!string.IsNullOrEmpty(AutomatedOffense) ? $" ({AutomatedOffense})" : "")}{(IsEvade ? $" ({Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PENALTY_EVADE"]})" : "")}"; + + public string AdditionalPenaltyInformation => + $"{(!string.IsNullOrEmpty(AutomatedOffense) ? $" ({AutomatedOffense})" : "")}{(IsEvade ? $" ({Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PENALTY_EVADE"]})" : "")}"; } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Dtos/PlayerInfo.cs b/SharedLibraryCore/Dtos/PlayerInfo.cs index ba138fcfe..d44516b55 100644 --- a/SharedLibraryCore/Dtos/PlayerInfo.cs +++ b/SharedLibraryCore/Dtos/PlayerInfo.cs @@ -1,8 +1,8 @@ -using SharedLibraryCore.Dtos.Meta.Responses; -using SharedLibraryCore.Interfaces; -using System; +using System; using System.Collections.Generic; using Data.Models; +using SharedLibraryCore.Dtos.Meta.Responses; +using SharedLibraryCore.Interfaces; namespace SharedLibraryCore.Dtos { @@ -30,4 +30,4 @@ namespace SharedLibraryCore.Dtos public MetaType? MetaFilterType { get; set; } public double? ZScore { get; set; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Dtos/ServerInfo.cs b/SharedLibraryCore/Dtos/ServerInfo.cs index 94fc602df..7605dcad0 100644 --- a/SharedLibraryCore/Dtos/ServerInfo.cs +++ b/SharedLibraryCore/Dtos/ServerInfo.cs @@ -1,8 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; +using SharedLibraryCore.Helpers; namespace SharedLibraryCore.Dtos { @@ -16,7 +15,7 @@ namespace SharedLibraryCore.Dtos public int MaxClients { get; set; } public List ChatHistory { get; set; } public List Players { get; set; } - public Helpers.PlayerHistory[] PlayerHistory { get; set; } + public PlayerHistory[] PlayerHistory { get; set; } public List ClientCountHistory { get; set; } public long ID { get; set; } public bool Online { get; set; } @@ -31,7 +30,7 @@ namespace SharedLibraryCore.Dtos { var valid = Players.Where(player => player.ZScore != null && player.ZScore != 0) .ToList(); - + if (!valid.Any()) { return null; diff --git a/SharedLibraryCore/Dtos/SharedInfo.cs b/SharedLibraryCore/Dtos/SharedInfo.cs index 73e0cb419..3f0133448 100644 --- a/SharedLibraryCore/Dtos/SharedInfo.cs +++ b/SharedLibraryCore/Dtos/SharedInfo.cs @@ -1,5 +1,4 @@ - -namespace SharedLibraryCore.Dtos +namespace SharedLibraryCore.Dtos { public class SharedInfo { diff --git a/SharedLibraryCore/Dvar.cs b/SharedLibraryCore/Dvar.cs index dcbe09707..67eae3bfe 100644 --- a/SharedLibraryCore/Dvar.cs +++ b/SharedLibraryCore/Dvar.cs @@ -8,4 +8,4 @@ public T LatchedValue { get; set; } public string Domain { get; set; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Events/EventAPI.cs b/SharedLibraryCore/Events/EventAPI.cs index 720b4a4e7..136225a49 100644 --- a/SharedLibraryCore/Events/EventAPI.cs +++ b/SharedLibraryCore/Events/EventAPI.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Concurrent; +using System.Collections.Concurrent; using System.Collections.Generic; using SharedLibraryCore.Dtos; @@ -7,8 +6,8 @@ namespace SharedLibraryCore.Events { public class EventApi { - const int MaxEvents = 25; - static ConcurrentQueue RecentEvents = new ConcurrentQueue(); + private const int MaxEvents = 25; + private static readonly ConcurrentQueue RecentEvents = new ConcurrentQueue(); public static IEnumerable GetEvents(bool shouldConsume) { @@ -28,32 +27,38 @@ namespace SharedLibraryCore.Events var E = gameEvent; // don't want to clog up the api with unknown events if (E.Type == GameEvent.EventType.Unknown) + { return; + } - var apiEvent = new EventInfo() + var apiEvent = new EventInfo { ExtraInfo = E.Extra?.ToString() ?? E.Data, - GameInfo = new EntityInfo() + GameInfo = new EntityInfo { Name = E.Owner.GameName.ToString(), Id = (int)E.Owner.GameName }, - OwnerEntity = new EntityInfo() + OwnerEntity = new EntityInfo { Name = E.Owner.Hostname, Id = E.Owner.EndPoint }, - OriginEntity = E.Origin == null ? null : new EntityInfo() - { - Id = E.Origin.ClientId, - Name = E.Origin.Name - }, - TargetEntity = E.Target == null ? null : new EntityInfo() - { - Id = E.Target.ClientId, - Name = E.Target.Name - }, - EventType = new EntityInfo() + OriginEntity = E.Origin == null + ? null + : new EntityInfo + { + Id = E.Origin.ClientId, + Name = E.Origin.Name + }, + TargetEntity = E.Target == null + ? null + : new EntityInfo + { + Id = E.Target.ClientId, + Name = E.Target.Name + }, + EventType = new EntityInfo { Id = (int)E.Type, Name = E.Type.ToString() @@ -66,16 +71,18 @@ namespace SharedLibraryCore.Events } /// - /// Adds event to the list and removes first added if reached max capacity + /// Adds event to the list and removes first added if reached max capacity /// /// EventInfo to add private static void AddNewEvent(EventInfo info) { // remove the first added event if (RecentEvents.Count >= MaxEvents) + { RecentEvents.TryDequeue(out _); + } RecentEvents.Enqueue(info); } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Events/GameEvent.cs b/SharedLibraryCore/Events/GameEvent.cs index 2248b02ef..c3e48db3c 100644 --- a/SharedLibraryCore/Events/GameEvent.cs +++ b/SharedLibraryCore/Events/GameEvent.cs @@ -1,11 +1,10 @@ -using SharedLibraryCore.Database.Models; -using SharedLibraryCore.Events; -using System; +using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Serilog.Context; +using SharedLibraryCore.Database.Models; namespace SharedLibraryCore { @@ -14,189 +13,38 @@ namespace SharedLibraryCore public enum EventFailReason { /// - /// event execution did not fail + /// event execution did not fail /// None, + /// - /// an internal exception prevented the event - /// from executing + /// an internal exception prevented the event + /// from executing /// Exception, + /// - /// event origin didn't have the necessary privileges - /// to execute the command + /// event origin didn't have the necessary privileges + /// to execute the command /// Permission, + /// - /// executing the event would cause an invalid state + /// executing the event would cause an invalid state /// Invalid, + /// - /// client is doing too much of something + /// client is doing too much of something /// Throttle, + /// - /// the event timed out before completion + /// the event timed out before completion /// Timeout } - public enum EventType - { - /// - /// the event wasn't parsed properly - /// - Unknown, - - // events "generated" by the server - /// - /// a server started being monitored - /// - Start, - /// - /// a server stopped being monitored - /// - Stop, - /// - /// a client was detecting as connecting via log - /// - Connect, - /// - /// a client was detecting joining by RCon - /// - Join, - /// - /// a client was detected leaving via log - /// - Quit, - /// - /// a client was detected leaving by RCon - /// - Disconnect, - /// - /// the current map ended - /// - MapEnd, - /// - /// the current map changed - /// - MapChange, - /// - /// a client was detected as starting to connect - /// - PreConnect, - /// - /// a client was detecting as starting to disconnect - /// - PreDisconnect, - /// - /// a client's information was updated - /// - Update, - /// - /// connection was lost to a server (the server has not responded after a number of attempts) - /// - ConnectionLost, - /// - /// connection was restored to a server (the server began responding again) - /// - ConnectionRestored, - - // events "generated" by clients - /// - /// a client sent a message - /// - Say = 100, - /// - /// a client was warned - /// - Warn = 101, - /// - /// all warnings for a client were cleared - /// - WarnClear = 102, - /// - /// a client was reported - /// - Report = 103, - /// - /// a client was flagged - /// - Flag = 104, - /// - /// a client was unflagged - /// - Unflag = 105, - /// - /// a client was kicked - /// - Kick = 106, - /// - /// a client was tempbanned - /// - TempBan = 107, - /// - /// a client was banned - /// - Ban = 108, - /// - /// a client was unbanned - /// - Unban = 109, - /// - /// a client entered a command - /// - Command = 110, - /// - /// a client's permission was changed - /// - ChangePermission = 111, - /// - /// client logged in to webfront - /// - Login = 112, - /// - /// client logged out of webfront - /// - Logout = 113, - - // events "generated" by IW4MAdmin - /// - /// a message is sent to all clients - /// - Broadcast = 200, - /// - /// a message is sent to a specific client - /// - Tell = 201, - - // events "generated" by script/log - /// - /// AC Damage Log - /// - ScriptDamage = 300, - /// - /// AC Kill Log - /// - ScriptKill = 301, - /// - /// damage info printed out by game script - /// - Damage = 302, - /// - /// kill info printed out by game script - /// - Kill = 303, - /// - /// team info printed out by game script - /// - JoinTeam = 304, - /// - /// used for community generated plugin events - /// - Other - } - [Flags] public enum EventRequiredEntity { @@ -213,12 +61,203 @@ namespace SharedLibraryCore Internal } - static long NextEventId; - static long GetNextEventId() + public enum EventType { - return Interlocked.Increment(ref NextEventId); + /// + /// the event wasn't parsed properly + /// + Unknown, + + // events "generated" by the server + /// + /// a server started being monitored + /// + Start, + + /// + /// a server stopped being monitored + /// + Stop, + + /// + /// a client was detecting as connecting via log + /// + Connect, + + /// + /// a client was detecting joining by RCon + /// + Join, + + /// + /// a client was detected leaving via log + /// + Quit, + + /// + /// a client was detected leaving by RCon + /// + Disconnect, + + /// + /// the current map ended + /// + MapEnd, + + /// + /// the current map changed + /// + MapChange, + + /// + /// a client was detected as starting to connect + /// + PreConnect, + + /// + /// a client was detecting as starting to disconnect + /// + PreDisconnect, + + /// + /// a client's information was updated + /// + Update, + + /// + /// connection was lost to a server (the server has not responded after a number of attempts) + /// + ConnectionLost, + + /// + /// connection was restored to a server (the server began responding again) + /// + ConnectionRestored, + + // events "generated" by clients + /// + /// a client sent a message + /// + Say = 100, + + /// + /// a client was warned + /// + Warn = 101, + + /// + /// all warnings for a client were cleared + /// + WarnClear = 102, + + /// + /// a client was reported + /// + Report = 103, + + /// + /// a client was flagged + /// + Flag = 104, + + /// + /// a client was unflagged + /// + Unflag = 105, + + /// + /// a client was kicked + /// + Kick = 106, + + /// + /// a client was tempbanned + /// + TempBan = 107, + + /// + /// a client was banned + /// + Ban = 108, + + /// + /// a client was unbanned + /// + Unban = 109, + + /// + /// a client entered a command + /// + Command = 110, + + /// + /// a client's permission was changed + /// + ChangePermission = 111, + + /// + /// client logged in to webfront + /// + Login = 112, + + /// + /// client logged out of webfront + /// + Logout = 113, + + // events "generated" by IW4MAdmin + /// + /// a message is sent to all clients + /// + Broadcast = 200, + + /// + /// a message is sent to a specific client + /// + Tell = 201, + + // events "generated" by script/log + /// + /// AC Damage Log + /// + ScriptDamage = 300, + + /// + /// AC Kill Log + /// + ScriptKill = 301, + + /// + /// damage info printed out by game script + /// + Damage = 302, + + /// + /// kill info printed out by game script + /// + Kill = 303, + + /// + /// team info printed out by game script + /// + JoinTeam = 304, + + /// + /// used for community generated plugin events + /// + Other } + private static long NextEventId; + private readonly ManualResetEvent _eventFinishedWaiter; + public string Data; // Data is usually the message sent by player + public string Message; + public EFClient Origin; + public Server Owner; + public EFClient Target; + + public EventType Type; + public GameEvent() { _eventFinishedWaiter = new ManualResetEvent(false); @@ -226,44 +265,46 @@ namespace SharedLibraryCore Id = GetNextEventId(); } - ~GameEvent() - { - _eventFinishedWaiter.Set(); - _eventFinishedWaiter.Dispose(); - } - - public EventType Type; public EventSource Source { get; set; } + /// - /// suptype of the event for more detailed classification + /// suptype of the event for more detailed classification /// public string Subtype { get; set; } + public EventRequiredEntity RequiredEntity { get; set; } - public string Data; // Data is usually the message sent by player - public string Message; + /// - /// Specifies the game time offset as printed in the log + /// Specifies the game time offset as printed in the log /// public long? GameTime { get; set; } - public EFClient Origin; - public EFClient Target; + public EFClient ImpersonationOrigin { get; set; } - public Server Owner; public bool IsRemote { get; set; } public object Extra { get; set; } - private readonly ManualResetEvent _eventFinishedWaiter; public DateTime Time { get; set; } - public long Id { get; private set; } + public long Id { get; } public EventFailReason FailReason { get; set; } public bool Failed => FailReason != EventFailReason.None; public Guid CorrelationId { get; set; } = Guid.NewGuid(); public List Output { get; set; } = new List(); /// - /// Indicates if the event should block until it is complete + /// Indicates if the event should block until it is complete /// public bool IsBlocking { get; set; } + private static long GetNextEventId() + { + return Interlocked.Increment(ref NextEventId); + } + + ~GameEvent() + { + _eventFinishedWaiter.Set(); + _eventFinishedWaiter.Dispose(); + } + public void Complete() { _eventFinishedWaiter.Set(); @@ -275,12 +316,12 @@ namespace SharedLibraryCore } /// - /// asynchronously wait for GameEvent to be processed + /// asynchronously wait for GameEvent to be processed /// /// waitable task public async Task WaitAsync(TimeSpan timeSpan, CancellationToken token) { - bool processed = false; + var processed = false; Utilities.DefaultLogger.LogDebug("Begin wait for event {Id}", Id); try { @@ -294,9 +335,10 @@ namespace SharedLibraryCore if (!processed) { - using(LogContext.PushProperty("Server", Owner?.ToString())) + using (LogContext.PushProperty("Server", Owner?.ToString())) { - Utilities.DefaultLogger.LogError("Waiting for event to complete timed out {@eventData}", new { Event = this, Message, Origin = Origin?.ToString(), Target = Target?.ToString()}); + Utilities.DefaultLogger.LogError("Waiting for event to complete timed out {@eventData}", + new { Event = this, Message, Origin = Origin?.ToString(), Target = Target?.ToString() }); } } @@ -305,4 +347,4 @@ namespace SharedLibraryCore return this; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Events/GameEventArgs.cs b/SharedLibraryCore/Events/GameEventArgs.cs index b3a795723..4d52a6c0e 100644 --- a/SharedLibraryCore/Events/GameEventArgs.cs +++ b/SharedLibraryCore/Events/GameEventArgs.cs @@ -1,23 +1,21 @@ using System; -using System.Collections.Generic; -using System.Text; +using System.ComponentModel; namespace SharedLibraryCore.Events { /// - /// represents the state of a game event for event processing + /// represents the state of a game event for event processing /// - public class GameEventArgs : System.ComponentModel.AsyncCompletedEventArgs + public class GameEventArgs : AsyncCompletedEventArgs { - public GameEventArgs(Exception error, bool cancelled, GameEvent userState) : base(error, cancelled, userState) { Event = userState; } /// - /// Game event that occured on a server + /// Game event that occured on a server /// public GameEvent Event { get; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Exceptions/AuthorizationException.cs b/SharedLibraryCore/Exceptions/AuthorizationException.cs index 695a156f2..956d9df5e 100644 --- a/SharedLibraryCore/Exceptions/AuthorizationException.cs +++ b/SharedLibraryCore/Exceptions/AuthorizationException.cs @@ -1,11 +1,11 @@ using System; -using System.Collections.Generic; -using System.Text; namespace SharedLibraryCore.Exceptions { public class AuthorizationException : Exception { - public AuthorizationException(string message) : base (message) { } + public AuthorizationException(string message) : base(message) + { + } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Exceptions/CommandException.cs b/SharedLibraryCore/Exceptions/CommandException.cs index bdb51c085..e9ebe8ad2 100644 --- a/SharedLibraryCore/Exceptions/CommandException.cs +++ b/SharedLibraryCore/Exceptions/CommandException.cs @@ -1,15 +1,12 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace SharedLibraryCore.Exceptions +namespace SharedLibraryCore.Exceptions { public class CommandException : ServerException { - public CommandException(string msg) : base(msg) { } + public CommandException(string msg) : base(msg) + { + } + // .data contains // "command_name" } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Exceptions/ConfigurationException.cs b/SharedLibraryCore/Exceptions/ConfigurationException.cs index f0d33f955..921a88980 100644 --- a/SharedLibraryCore/Exceptions/ConfigurationException.cs +++ b/SharedLibraryCore/Exceptions/ConfigurationException.cs @@ -4,9 +4,11 @@ namespace SharedLibraryCore.Exceptions { public class ConfigurationException : Exception { + public ConfigurationException(string message) : base(message) + { + } + public string[] Errors { get; set; } public string ConfigurationFileName { get; set; } - - public ConfigurationException(string message) : base(message) { } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Exceptions/DatabaseException.cs b/SharedLibraryCore/Exceptions/DatabaseException.cs index 62dd02fbf..25bb27dd3 100644 --- a/SharedLibraryCore/Exceptions/DatabaseException.cs +++ b/SharedLibraryCore/Exceptions/DatabaseException.cs @@ -1,13 +1,11 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace SharedLibraryCore.Exceptions { public class DatabaseException : Exception { - public DatabaseException(string msg) : base(msg) { } + public DatabaseException(string msg) : base(msg) + { + } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Exceptions/DvarException.cs b/SharedLibraryCore/Exceptions/DvarException.cs index f4f8a7c82..13887a57c 100644 --- a/SharedLibraryCore/Exceptions/DvarException.cs +++ b/SharedLibraryCore/Exceptions/DvarException.cs @@ -1,13 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace SharedLibraryCore.Exceptions +namespace SharedLibraryCore.Exceptions { public class DvarException : ServerException { - public DvarException(string msg) : base(msg) { } + public DvarException(string msg) : base(msg) + { + } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Exceptions/NetworkException.cs b/SharedLibraryCore/Exceptions/NetworkException.cs index 237715560..1c03df654 100644 --- a/SharedLibraryCore/Exceptions/NetworkException.cs +++ b/SharedLibraryCore/Exceptions/NetworkException.cs @@ -1,18 +1,16 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Sockets; -using System.Text; -using System.Threading.Tasks; +using System.Net.Sockets; namespace SharedLibraryCore.Exceptions { public class NetworkException : ServerException { - public NetworkException(string msg) : base(msg) { } + public NetworkException(string msg) : base(msg) + { + } + public NetworkException(string msg, Socket s) : base(msg) { - this.Data.Add("socket", s); + Data.Add("socket", s); } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Exceptions/PluginException.cs b/SharedLibraryCore/Exceptions/PluginException.cs index b5a1afa72..d2d92a76e 100644 --- a/SharedLibraryCore/Exceptions/PluginException.cs +++ b/SharedLibraryCore/Exceptions/PluginException.cs @@ -4,8 +4,10 @@ namespace SharedLibraryCore.Exceptions { public class PluginException : Exception { - public PluginException(string message) : base(message) { } + public PluginException(string message) : base(message) + { + } public string PluginFile { get; set; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Exceptions/SerializationException.cs b/SharedLibraryCore/Exceptions/SerializationException.cs index e8641547b..892ecdc80 100644 --- a/SharedLibraryCore/Exceptions/SerializationException.cs +++ b/SharedLibraryCore/Exceptions/SerializationException.cs @@ -1,13 +1,11 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace SharedLibraryCore.Exceptions { public class SerializeException : Exception { - public SerializeException(string msg) : base(msg) { } + public SerializeException(string msg) : base(msg) + { + } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Exceptions/ServerException.cs b/SharedLibraryCore/Exceptions/ServerException.cs index 5b96ec0d8..131be74cd 100644 --- a/SharedLibraryCore/Exceptions/ServerException.cs +++ b/SharedLibraryCore/Exceptions/ServerException.cs @@ -1,13 +1,11 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace SharedLibraryCore.Exceptions { public class ServerException : Exception { - public ServerException(string msg) : base(msg) { } + public ServerException(string msg) : base(msg) + { + } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Formatting/ColorCodes.cs b/SharedLibraryCore/Formatting/ColorCodes.cs index 49d19cacb..ddcf5b738 100644 --- a/SharedLibraryCore/Formatting/ColorCodes.cs +++ b/SharedLibraryCore/Formatting/ColorCodes.cs @@ -21,6 +21,5 @@ namespace SharedLibraryCore.Formatting public class ColorCodeMapping : Dictionary { - } } \ No newline at end of file diff --git a/SharedLibraryCore/Game/Gametype.cs b/SharedLibraryCore/Game/Gametype.cs index f6f40f688..74b38d23e 100644 --- a/SharedLibraryCore/Game/Gametype.cs +++ b/SharedLibraryCore/Game/Gametype.cs @@ -5,4 +5,4 @@ public string Name { get; set; } public string Alias { get; set; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Game/Map.cs b/SharedLibraryCore/Game/Map.cs index 679cab797..8a3e4a587 100644 --- a/SharedLibraryCore/Game/Map.cs +++ b/SharedLibraryCore/Game/Map.cs @@ -5,6 +5,9 @@ public string Name { get; set; } public string Alias { get; set; } - public override string ToString() => Alias; + public override string ToString() + { + return Alias; + } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Helpers/BuildNumber.cs b/SharedLibraryCore/Helpers/BuildNumber.cs index c828262c9..801b0556f 100644 --- a/SharedLibraryCore/Helpers/BuildNumber.cs +++ b/SharedLibraryCore/Helpers/BuildNumber.cs @@ -6,12 +6,41 @@ namespace SharedLibraryCore.Helpers { public class BuildNumber : IComparable { + private BuildNumber() + { + } + public int Major { get; private set; } public int Minor { get; private set; } public int Build { get; private set; } public int Revision { get; private set; } - private BuildNumber() { } + public int CompareTo(object obj) + { + if (obj == null) + { + return 1; + } + + var buildNumber = obj as BuildNumber; + if (buildNumber == null) + { + return 1; + } + + if (ReferenceEquals(this, buildNumber)) + { + return 0; + } + + return Major == buildNumber.Major + ? Minor == buildNumber.Minor + ? Build == buildNumber.Build + ? Revision.CompareTo(buildNumber.Revision) + : Build.CompareTo(buildNumber.Build) + : Minor.CompareTo(buildNumber.Minor) + : Major.CompareTo(buildNumber.Major); + } public static bool TryParse(string input, out BuildNumber buildNumber) { @@ -28,23 +57,32 @@ namespace SharedLibraryCore.Helpers } /// - /// Parses a build number string into a BuildNumber class + /// Parses a build number string into a BuildNumber class /// /// The build number string to parse /// A new BuildNumber class set from the buildNumber string - /// Thrown if there are less than 2 or - /// more than 4 version parts to the build number - /// Thrown if string cannot be parsed - /// to a series of integers - /// Thrown if any version - /// integer is less than zero + /// + /// Thrown if there are less than 2 or + /// more than 4 version parts to the build number + /// + /// + /// Thrown if string cannot be parsed + /// to a series of integers + /// + /// + /// Thrown if any version + /// integer is less than zero + /// public static BuildNumber Parse(string buildNumber) { - if (buildNumber == null) throw new ArgumentNullException("buildNumber"); + if (buildNumber == null) + { + throw new ArgumentNullException("buildNumber"); + } var versions = buildNumber .Split(new[] { '.' }, - StringSplitOptions.RemoveEmptyEntries) + StringSplitOptions.RemoveEmptyEntries) .Select(v => v.Trim()) .ToList(); @@ -90,39 +128,23 @@ namespace SharedLibraryCore.Helpers public override string ToString() { return string.Format("{0}.{1}{2}{3}", Major, Minor, - Build < 0 ? "" : "." + Build, - Revision < 0 ? "" : "." + Revision); - } - - public int CompareTo(object obj) - { - if (obj == null) return 1; - var buildNumber = obj as BuildNumber; - if (buildNumber == null) return 1; - if (ReferenceEquals(this, buildNumber)) return 0; - - return (Major == buildNumber.Major) - ? (Minor == buildNumber.Minor) - ? (Build == buildNumber.Build) - ? Revision.CompareTo(buildNumber.Revision) - : Build.CompareTo(buildNumber.Build) - : Minor.CompareTo(buildNumber.Minor) - : Major.CompareTo(buildNumber.Major); + Build < 0 ? "" : "." + Build, + Revision < 0 ? "" : "." + Revision); } public static bool operator >(BuildNumber first, BuildNumber second) { - return (first.CompareTo(second) > 0); + return first.CompareTo(second) > 0; } public static bool operator <(BuildNumber first, BuildNumber second) { - return (first.CompareTo(second) < 0); + return first.CompareTo(second) < 0; } public override bool Equals(object obj) { - return (CompareTo(obj) == 0); + return CompareTo(obj) == 0; } public override int GetHashCode() @@ -138,4 +160,4 @@ namespace SharedLibraryCore.Helpers } } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Helpers/BuildNumberJsonConverter.cs b/SharedLibraryCore/Helpers/BuildNumberJsonConverter.cs index b5e639e4e..cdac5943b 100644 --- a/SharedLibraryCore/Helpers/BuildNumberJsonConverter.cs +++ b/SharedLibraryCore/Helpers/BuildNumberJsonConverter.cs @@ -1,10 +1,10 @@ -using Newtonsoft.Json; -using System; +using System; +using Newtonsoft.Json; namespace SharedLibraryCore.Helpers { /// - /// JSON converter for the build number + /// JSON converter for the build number /// public class BuildNumberJsonConverter : JsonConverter { @@ -13,7 +13,8 @@ namespace SharedLibraryCore.Helpers return objectType == typeof(string); } - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, + JsonSerializer serializer) { return BuildNumber.Parse(reader.Value.ToString()); } @@ -23,4 +24,4 @@ namespace SharedLibraryCore.Helpers writer.WriteValue(value.ToString()); } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Helpers/ChangeTracking.cs b/SharedLibraryCore/Helpers/ChangeTracking.cs index 99334e1a7..c289d2967 100644 --- a/SharedLibraryCore/Helpers/ChangeTracking.cs +++ b/SharedLibraryCore/Helpers/ChangeTracking.cs @@ -1,37 +1,36 @@ -using SharedLibraryCore.Interfaces; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Text; +using System.Collections.Concurrent; namespace SharedLibraryCore.Helpers { /// - /// This class provides a way to keep track of changes to an entity + /// This class provides a way to keep track of changes to an entity /// /// Type of entity to keep track of changes to public class ChangeTracking { - ConcurrentQueue Values; + private readonly ConcurrentQueue Values; public ChangeTracking() { Values = new ConcurrentQueue(); } + public bool HasChanges => Values.Count > 0; + public void OnChange(T value) { if (Values.Count > 30) - Values.TryDequeue(out T throwAway); + { + Values.TryDequeue(out var throwAway); + } + Values.Enqueue(value); } public T GetNextChange() { - bool itemDequeued = Values.TryDequeue(out T val); - return itemDequeued ? val : default(T); + var itemDequeued = Values.TryDequeue(out var val); + return itemDequeued ? val : default; } - - public bool HasChanges => Values.Count > 0; } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Helpers/Hashing.cs b/SharedLibraryCore/Helpers/Hashing.cs index 7ea47625c..183d51513 100644 --- a/SharedLibraryCore/Helpers/Hashing.cs +++ b/SharedLibraryCore/Helpers/Hashing.cs @@ -5,13 +5,12 @@ namespace SharedLibraryCore.Helpers public class Hashing { /// - /// Generate password hash and salt + /// Generate password hash and salt /// /// plaintext password /// public static string[] Hash(string password, string saltStr = null) { - string hash; string salt; var CryptoSvc = new PBKDF2(); @@ -21,22 +20,19 @@ namespace SharedLibraryCore.Helpers { hash = CryptoSvc.Compute(password); salt = CryptoSvc.Salt; - return new string[] + return new[] { hash, salt }; } - else + hash = CryptoSvc.Compute(password, saltStr); + return new[] { - hash = CryptoSvc.Compute(password, saltStr); - return new string[] - { - hash, - "" - }; - } + hash, + "" + }; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Helpers/MessageToken.cs b/SharedLibraryCore/Helpers/MessageToken.cs index 3239aac88..4f3899a40 100644 --- a/SharedLibraryCore/Helpers/MessageToken.cs +++ b/SharedLibraryCore/Helpers/MessageToken.cs @@ -1,12 +1,10 @@ using System; -using System.Threading; using System.Threading.Tasks; namespace SharedLibraryCore.Helpers { public class MessageToken { - public string Name { get; private set; } private readonly Func> _asyncValue; @@ -16,10 +14,12 @@ namespace SharedLibraryCore.Helpers _asyncValue = Value; } + public string Name { get; } + public async Task ProcessAsync(Server server) { - string result = await _asyncValue(server); + var result = await _asyncValue(server); return result; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Helpers/ParseEnum.cs b/SharedLibraryCore/Helpers/ParseEnum.cs index eb005c866..cfb4d52c8 100644 --- a/SharedLibraryCore/Helpers/ParseEnum.cs +++ b/SharedLibraryCore/Helpers/ParseEnum.cs @@ -13,8 +13,8 @@ namespace SharedLibraryCore.Helpers catch (Exception) { - return (T)(Enum.GetValues(type).GetValue(0)); + return (T)Enum.GetValues(type).GetValue(0); } } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Helpers/ParserRegex.cs b/SharedLibraryCore/Helpers/ParserRegex.cs index 2e083c99e..6d9f9712d 100644 --- a/SharedLibraryCore/Helpers/ParserRegex.cs +++ b/SharedLibraryCore/Helpers/ParserRegex.cs @@ -6,8 +6,8 @@ namespace SharedLibraryCore.Interfaces public sealed class ParserRegex { /// - /// represents the logical mapping of information provided by - /// game logs, get status, and get dvar information + /// represents the logical mapping of information provided by + /// game logs, get status, and get dvar information /// public enum GroupType { @@ -43,11 +43,18 @@ namespace SharedLibraryCore.Interfaces AdditionalGroup = 200 } - public IParserPatternMatcher PatternMatcher { get; private set; } - private string pattern; + + public ParserRegex(IParserPatternMatcher pattern) + { + GroupMapping = new Dictionary(); + PatternMatcher = pattern; + } + + public IParserPatternMatcher PatternMatcher { get; } + /// - /// stores the regular expression groups that will be mapped to group types + /// stores the regular expression groups that will be mapped to group types /// public string Pattern { @@ -60,20 +67,20 @@ namespace SharedLibraryCore.Interfaces } /// - /// stores the mapping from group type to group index in the regular expression + /// stores the mapping from group type to group index in the regular expression /// - public Dictionary GroupMapping { get; private set; } + public Dictionary GroupMapping { get; } /// - /// helper method to enable script parsers to app regex mapping - /// the first parameter specifies the group type contained in the regex pattern - /// the second parameter specifies the group index to retrieve in the matched regex pattern + /// helper method to enable script parsers to app regex mapping + /// the first parameter specifies the group type contained in the regex pattern + /// the second parameter specifies the group index to retrieve in the matched regex pattern /// /// group type /// group index public void AddMapping(object mapKey, object mapValue) { - if (int.TryParse(mapKey.ToString(), out int key) && int.TryParse(mapValue.ToString(), out int value)) + if (int.TryParse(mapKey.ToString(), out var key) && int.TryParse(mapValue.ToString(), out var value)) { if (GroupMapping.ContainsKey((GroupType)key)) { @@ -88,8 +95,8 @@ namespace SharedLibraryCore.Interfaces if (mapKey.GetType() == typeof(GroupType) && mapValue.GetType().ToString() == "System.Int32") { - GroupType k = (GroupType)Enum.Parse(typeof(GroupType), mapKey.ToString()); - int v = int.Parse(mapValue.ToString()); + var k = (GroupType)Enum.Parse(typeof(GroupType), mapKey.ToString()); + var v = int.Parse(mapValue.ToString()); if (GroupMapping.ContainsKey(k)) { @@ -102,11 +109,5 @@ namespace SharedLibraryCore.Interfaces } } } - - public ParserRegex(IParserPatternMatcher pattern) - { - GroupMapping = new Dictionary(); - PatternMatcher = pattern; - } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Helpers/PlayerHistory.cs b/SharedLibraryCore/Helpers/PlayerHistory.cs index 4039662f3..c61a2a60b 100644 --- a/SharedLibraryCore/Helpers/PlayerHistory.cs +++ b/SharedLibraryCore/Helpers/PlayerHistory.cs @@ -8,28 +8,23 @@ namespace SharedLibraryCore.Helpers // how many minutes between updates public static readonly int UpdateInterval = 5; + private readonly DateTime When; + public PlayerHistory(int cNum) { - DateTime t = DateTime.UtcNow; - When = new DateTime(t.Year, t.Month, t.Day, t.Hour, Math.Min(59, UpdateInterval * (int)Math.Round(t.Minute / (float)UpdateInterval)), 0); + var t = DateTime.UtcNow; + When = new DateTime(t.Year, t.Month, t.Day, t.Hour, + Math.Min(59, UpdateInterval * (int)Math.Round(t.Minute / (float)UpdateInterval)), 0); y = cNum; } - private DateTime When; - /// - /// Used by CanvasJS as a point on the x axis + /// Used by CanvasJS as a point on the x axis /// - public string x - { - get - { - return When.ToString("yyyy-MM-ddTHH:mm:ssZ"); - } - } + public string x => When.ToString("yyyy-MM-ddTHH:mm:ssZ"); /// - /// Used by CanvasJS as a point on the y axis + /// Used by CanvasJS as a point on the y axis /// public int y { get; } @@ -42,4 +37,4 @@ namespace SharedLibraryCore.Helpers }; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Helpers/Report.cs b/SharedLibraryCore/Helpers/Report.cs index 1a51e5c15..9a1b96aae 100644 --- a/SharedLibraryCore/Helpers/Report.cs +++ b/SharedLibraryCore/Helpers/Report.cs @@ -1,9 +1,4 @@ using SharedLibraryCore.Database.Models; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace SharedLibraryCore.Helpers { @@ -11,6 +6,6 @@ namespace SharedLibraryCore.Helpers { public EFClient Target { get; set; } public EFClient Origin { get; set; } - public string Reason { get; set; } + public string Reason { get; set; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Helpers/ResourceQueryHelperResult.cs b/SharedLibraryCore/Helpers/ResourceQueryHelperResult.cs index 8e6a69678..6d82dafe1 100644 --- a/SharedLibraryCore/Helpers/ResourceQueryHelperResult.cs +++ b/SharedLibraryCore/Helpers/ResourceQueryHelperResult.cs @@ -1,28 +1,26 @@ -using System; -using System.Collections.Generic; -using SharedLibraryCore.Dtos.Meta.Responses; +using System.Collections.Generic; namespace SharedLibraryCore.Helpers { /// - /// generic class for passing information about a resource query + /// generic class for passing information about a resource query /// /// Type of query result public class ResourceQueryHelperResult { /// - /// indicates the total number of results found + /// indicates the total number of results found /// public long TotalResultCount { get; set; } /// - /// indicates the total number of results retrieved + /// indicates the total number of results retrieved /// public int RetrievedResultCount { get; set; } /// - /// collection of results + /// collection of results /// public IEnumerable Results { get; set; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Helpers/TokenState.cs b/SharedLibraryCore/Helpers/TokenState.cs index c5f8afb89..432019cbf 100644 --- a/SharedLibraryCore/Helpers/TokenState.cs +++ b/SharedLibraryCore/Helpers/TokenState.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; namespace SharedLibraryCore.Helpers { @@ -10,6 +8,8 @@ namespace SharedLibraryCore.Helpers public DateTime RequestTime { get; set; } = DateTime.Now; public TimeSpan TokenDuration { get; set; } public string Token { get; set; } - public string RemainingTime => Math.Round(-(DateTime.Now - RequestTime).Subtract(TokenDuration).TotalMinutes, 1).ToString(); + + public string RemainingTime => Math.Round(-(DateTime.Now - RequestTime).Subtract(TokenDuration).TotalMinutes, 1) + .ToString(); } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IAuditInformationRepository.cs b/SharedLibraryCore/Interfaces/IAuditInformationRepository.cs index 1cc24a20e..22411de82 100644 --- a/SharedLibraryCore/Interfaces/IAuditInformationRepository.cs +++ b/SharedLibraryCore/Interfaces/IAuditInformationRepository.cs @@ -1,19 +1,19 @@ -using SharedLibraryCore.Dtos; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; +using SharedLibraryCore.Dtos; namespace SharedLibraryCore.Interfaces { /// - /// describes the capabilities of the audit info repository + /// describes the capabilities of the audit info repository /// public interface IAuditInformationRepository { /// - /// retrieves a list of audit information for given pagination params + /// retrieves a list of audit information for given pagination params /// /// pagination info /// Task> ListAuditInformation(PaginationRequest paginationInfo); } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IBaseConfiguration.cs b/SharedLibraryCore/Interfaces/IBaseConfiguration.cs index bc685e1c3..5902dfa0a 100644 --- a/SharedLibraryCore/Interfaces/IBaseConfiguration.cs +++ b/SharedLibraryCore/Interfaces/IBaseConfiguration.cs @@ -1,14 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace SharedLibraryCore.Interfaces +namespace SharedLibraryCore.Interfaces { public interface IBaseConfiguration { string Name(); IBaseConfiguration Generate(); } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IBasePathProvider.cs b/SharedLibraryCore/Interfaces/IBasePathProvider.cs index 63ab43fe4..980df134a 100644 --- a/SharedLibraryCore/Interfaces/IBasePathProvider.cs +++ b/SharedLibraryCore/Interfaces/IBasePathProvider.cs @@ -1,14 +1,14 @@ namespace SharedLibraryCore.Interfaces { /// - /// defines the capabilities for providing a base path - /// unused as of now, will be used later during refactorying + /// defines the capabilities for providing a base path + /// unused as of now, will be used later during refactorying /// public interface IBasePathProvider { /// - /// working directory of IW4MAdmin + /// working directory of IW4MAdmin /// string BasePath { get; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IClientMeta.cs b/SharedLibraryCore/Interfaces/IClientMeta.cs index 22f67a91a..413c5d35a 100644 --- a/SharedLibraryCore/Interfaces/IClientMeta.cs +++ b/SharedLibraryCore/Interfaces/IClientMeta.cs @@ -3,7 +3,7 @@ namespace SharedLibraryCore.Interfaces { /// - /// describes all the base attributes of a client meta object + /// describes all the base attributes of a client meta object /// public interface IClientMeta { @@ -29,4 +29,4 @@ namespace SharedLibraryCore.Interfaces QuickMessage, ConnectionHistory } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IClientMetaResponse.cs b/SharedLibraryCore/Interfaces/IClientMetaResponse.cs index 3cf9e23b2..08b866a69 100644 --- a/SharedLibraryCore/Interfaces/IClientMetaResponse.cs +++ b/SharedLibraryCore/Interfaces/IClientMetaResponse.cs @@ -1,12 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace SharedLibraryCore.Interfaces +namespace SharedLibraryCore.Interfaces { public interface IClientMetaResponse { - int ClientId { get;} + int ClientId { get; } long MetaId { get; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IClientNoticeMessageFormatter.cs b/SharedLibraryCore/Interfaces/IClientNoticeMessageFormatter.cs index b7af3ea7b..51ff2feee 100644 --- a/SharedLibraryCore/Interfaces/IClientNoticeMessageFormatter.cs +++ b/SharedLibraryCore/Interfaces/IClientNoticeMessageFormatter.cs @@ -5,12 +5,13 @@ namespace SharedLibraryCore.Interfaces public interface IClientNoticeMessageFormatter { /// - /// builds a game formatted notice message + /// builds a game formatted notice message /// /// current penalty the message is for /// previous penalty the current penalty relates to /// RCon parser config /// - string BuildFormattedMessage(IRConParserConfiguration config, EFPenalty currentPenalty, EFPenalty originalPenalty = null); + string BuildFormattedMessage(IRConParserConfiguration config, EFPenalty currentPenalty, + EFPenalty originalPenalty = null); } } \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IConfigurationHandler.cs b/SharedLibraryCore/Interfaces/IConfigurationHandler.cs index 38c1b0111..994d51faa 100644 --- a/SharedLibraryCore/Interfaces/IConfigurationHandler.cs +++ b/SharedLibraryCore/Interfaces/IConfigurationHandler.cs @@ -1,17 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Threading.Tasks; namespace SharedLibraryCore.Interfaces { public interface IConfigurationHandler where T : IBaseConfiguration { + string FileName { get; } Task Save(); void Build(); T Configuration(); void Set(T config); - string FileName { get; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IConfigurationHandlerFactory.cs b/SharedLibraryCore/Interfaces/IConfigurationHandlerFactory.cs index 3ab07de87..0108ceecd 100644 --- a/SharedLibraryCore/Interfaces/IConfigurationHandlerFactory.cs +++ b/SharedLibraryCore/Interfaces/IConfigurationHandlerFactory.cs @@ -1,17 +1,17 @@ namespace SharedLibraryCore.Interfaces { /// - /// defines the capabilities of the configuration handler factory - /// used to generate new instance of configuration handlers + /// defines the capabilities of the configuration handler factory + /// used to generate new instance of configuration handlers /// public interface IConfigurationHandlerFactory { /// - /// generates a new configuration handler + /// generates a new configuration handler /// /// base configuration type /// file name of configuration /// new configuration handler instance IConfigurationHandler GetConfigurationHandler(string name) where T : IBaseConfiguration; } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IEntityService.cs b/SharedLibraryCore/Interfaces/IEntityService.cs index 30e9d49eb..8df46be1a 100644 --- a/SharedLibraryCore/Interfaces/IEntityService.cs +++ b/SharedLibraryCore/Interfaces/IEntityService.cs @@ -1,8 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Text; using System.Threading.Tasks; namespace SharedLibraryCore.Interfaces @@ -16,4 +13,4 @@ namespace SharedLibraryCore.Interfaces Task GetUnique(long entityProperty); Task> Find(Func expression); } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IEventHandler.cs b/SharedLibraryCore/Interfaces/IEventHandler.cs index b8437f5e5..930081eb2 100644 --- a/SharedLibraryCore/Interfaces/IEventHandler.cs +++ b/SharedLibraryCore/Interfaces/IEventHandler.cs @@ -1,15 +1,15 @@ namespace SharedLibraryCore.Interfaces { /// - /// handles games events (from log, manual events, etc) + /// handles games events (from log, manual events, etc) /// public interface IEventHandler { /// - /// Add a game event event to the queue to be processed + /// Add a game event event to the queue to be processed /// /// application manager instance /// game event void HandleEvent(IManager manager, GameEvent gameEvent); } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IEventParser.cs b/SharedLibraryCore/Interfaces/IEventParser.cs index b0457920a..3ba7bbc3a 100644 --- a/SharedLibraryCore/Interfaces/IEventParser.cs +++ b/SharedLibraryCore/Interfaces/IEventParser.cs @@ -6,44 +6,46 @@ namespace SharedLibraryCore.Interfaces public interface IEventParser { /// - /// Generates a game event based on log line input - /// - /// single log line string - /// - /// todo: make this integrate without needing the server - GameEvent GenerateGameEvent(string logLine); - /// - /// Get game specific folder prefix for log files + /// Get game specific folder prefix for log files /// /// Game directory prefix IEventParserConfiguration Configuration { get; set; } /// - /// stores the game/client specific version (usually the value of the "version" DVAR) + /// stores the game/client specific version (usually the value of the "version" DVAR) /// string Version { get; set; } /// - /// specifies the game name (usually the internal studio iteration ie: IW4, T5 etc...) + /// specifies the game name (usually the internal studio iteration ie: IW4, T5 etc...) /// Game GameName { get; set; } /// - /// specifies the connect URI used to join game servers via web browser + /// specifies the connect URI used to join game servers via web browser /// string URLProtocolFormat { get; set; } /// - /// specifies the text name of the game the parser is for + /// specifies the text name of the game the parser is for /// string Name { get; set; } /// - /// registers a custom event subtype to be triggered when a value is detected + /// Generates a game event based on log line input + /// + /// single log line string + /// + /// todo: make this integrate without needing the server + GameEvent GenerateGameEvent(string logLine); + + /// + /// registers a custom event subtype to be triggered when a value is detected /// /// subtype assigned to the event when generated /// event keyword to trigger an event generation /// function pointer that modifies the generated game event - void RegisterCustomEvent(string eventSubtype, string eventTriggerValue, Func eventModifier); + void RegisterCustomEvent(string eventSubtype, string eventTriggerValue, + Func eventModifier); } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IEventParserConfiguration.cs b/SharedLibraryCore/Interfaces/IEventParserConfiguration.cs index 43975b7fa..d2cffc2d3 100644 --- a/SharedLibraryCore/Interfaces/IEventParserConfiguration.cs +++ b/SharedLibraryCore/Interfaces/IEventParserConfiguration.cs @@ -1,63 +1,62 @@ -using System.Collections.Generic; -using System.Globalization; +using System.Globalization; namespace SharedLibraryCore.Interfaces { public interface IEventParserConfiguration { /// - /// stores the fs_game directory (this folder may vary between different clients) + /// stores the fs_game directory (this folder may vary between different clients) /// string GameDirectory { get; set; } /// - /// stores the regex information for a say event printed in the game log + /// stores the regex information for a say event printed in the game log /// ParserRegex Say { get; set; } /// - /// stores the regex information for a join event printed in the game log + /// stores the regex information for a join event printed in the game log /// ParserRegex Join { get; set; } /// - /// stores the regex information for a quit event printed in the game log + /// stores the regex information for a quit event printed in the game log /// ParserRegex Quit { get; set; } /// - /// stores the regex information for a kill event printed in the game log + /// stores the regex information for a kill event printed in the game log /// ParserRegex Kill { get; set; } /// - /// stores the regex information for a damage event printed in the game log + /// stores the regex information for a damage event printed in the game log /// ParserRegex Damage { get; set; } /// - /// stores the regex information for an action event printed in the game log + /// stores the regex information for an action event printed in the game log /// ParserRegex Action { get; set; } /// - /// stores the regex information for the time prefix in game log + /// stores the regex information for the time prefix in game log /// ParserRegex Time { get; set; } /// - /// stores the regex information for the map change game log + /// stores the regex information for the map change game log /// ParserRegex MapChange { get; } - + /// - /// stores the regex information for the map end game log + /// stores the regex information for the map end game log /// ParserRegex MapEnd { get; } /// - /// indicates the format expected for parsed guids + /// indicates the format expected for parsed guids /// NumberStyles GuidNumberStyle { get; set; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IGameLogReader.cs b/SharedLibraryCore/Interfaces/IGameLogReader.cs index 155c1851b..f6783a091 100644 --- a/SharedLibraryCore/Interfaces/IGameLogReader.cs +++ b/SharedLibraryCore/Interfaces/IGameLogReader.cs @@ -4,26 +4,26 @@ using System.Threading.Tasks; namespace SharedLibraryCore.Interfaces { /// - /// represents the abstraction of game log reading + /// represents the abstraction of game log reading /// public interface IGameLogReader { /// - /// get new events that have occured since the last poll + /// how long the log file is + /// + long Length { get; } + + /// + /// how often to poll the log file + /// + int UpdateInterval { get; } + + /// + /// get new events that have occured since the last poll /// /// /// /// Task> ReadEventsFromLog(long fileSizeDiff, long startPosition); - - /// - /// how long the log file is - /// - long Length { get; } - - /// - /// how often to poll the log file - /// - int UpdateInterval { get; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IGameLogReaderFactory.cs b/SharedLibraryCore/Interfaces/IGameLogReaderFactory.cs index 54eb716bf..59140d2dd 100644 --- a/SharedLibraryCore/Interfaces/IGameLogReaderFactory.cs +++ b/SharedLibraryCore/Interfaces/IGameLogReaderFactory.cs @@ -3,16 +3,16 @@ namespace SharedLibraryCore.Interfaces { /// - /// factory interface to create game log readers based on the log file uri + /// factory interface to create game log readers based on the log file uri /// public interface IGameLogReaderFactory { /// - /// generates a new game log reader based on the provided Uri + /// generates a new game log reader based on the provided Uri /// /// collection of log uri used to generate the log reader /// event parser for the log reader /// IGameLogReader CreateGameLogReader(Uri[] logUris, IEventParser eventParser); } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IGameServer.cs b/SharedLibraryCore/Interfaces/IGameServer.cs index 0b5df51cd..1d63870b9 100644 --- a/SharedLibraryCore/Interfaces/IGameServer.cs +++ b/SharedLibraryCore/Interfaces/IGameServer.cs @@ -7,7 +7,7 @@ namespace SharedLibraryCore.Interfaces public interface IGameServer { /// - /// kicks target on behalf of origin for given reason + /// kicks target on behalf of origin for given reason /// /// reason client is being kicked /// client to kick diff --git a/SharedLibraryCore/Interfaces/IGameServerInstanceFactory.cs b/SharedLibraryCore/Interfaces/IGameServerInstanceFactory.cs index 856fd9f12..51748ae3f 100644 --- a/SharedLibraryCore/Interfaces/IGameServerInstanceFactory.cs +++ b/SharedLibraryCore/Interfaces/IGameServerInstanceFactory.cs @@ -3,16 +3,16 @@ namespace SharedLibraryCore.Interfaces { /// - /// defines the capabilities of game server instance factory + /// defines the capabilities of game server instance factory /// public interface IGameServerInstanceFactory { /// - /// creates the instance of a game server + /// creates the instance of a game server /// /// server configuration /// application manager /// Server CreateServer(ServerConfiguration config, IManager manager); } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/ILogger.cs b/SharedLibraryCore/Interfaces/ILogger.cs index 5afb04b86..efbd65c54 100644 --- a/SharedLibraryCore/Interfaces/ILogger.cs +++ b/SharedLibraryCore/Interfaces/ILogger.cs @@ -12,4 +12,4 @@ namespace SharedLibraryCore.Interfaces void WriteError(string msg); void WriteAssert(bool condition, string msg); } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IManager.cs b/SharedLibraryCore/Interfaces/IManager.cs index f4c8bcf43..d10f35f32 100644 --- a/SharedLibraryCore/Interfaces/IManager.cs +++ b/SharedLibraryCore/Interfaces/IManager.cs @@ -1,93 +1,106 @@ -using System.Collections.Generic; +using System; +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; -using SharedLibraryCore.Services; using SharedLibraryCore.Configuration; using SharedLibraryCore.Database.Models; -using System.Threading; -using System.Collections; -using System; -using System.Collections.Concurrent; +using SharedLibraryCore.Helpers; +using SharedLibraryCore.Services; namespace SharedLibraryCore.Interfaces { public interface IManager { + IReadOnlyList Commands { get; } + + /// + /// enumerates the registered plugin instances + /// + IEnumerable Plugins { get; } + + IList AdditionalRConParsers { get; } + IList AdditionalEventParsers { get; } + IMiddlewareActionHandler MiddlewareActionHandler { get; } + string Version { get; } + ITokenAuthentication TokenAuthenticator { get; } + string ExternalIPAddress { get; } + CancellationToken CancellationToken { get; } + bool IsRestartRequested { get; } + bool IsRunning { get; } + ConcurrentDictionary ProcessingEvents { get; } Task Init(); Task Start(); void Stop(); void Restart(); + [Obsolete] ILogger GetLogger(long serverId); + IList GetServers(); IList GetCommands(); - IReadOnlyList Commands { get; } - IList GetMessageTokens(); + IList GetMessageTokens(); IList GetActiveClients(); EFClient FindActiveClient(EFClient client); IConfigurationHandler GetApplicationSettings(); ClientService GetClientService(); PenaltyService GetPenaltyService(); + /// - /// enumerates the registered plugin instances - /// - IEnumerable Plugins { get; } - /// - /// provides a page list to add and remove from + /// provides a page list to add and remove from /// /// IPageList GetPageList(); - IList AdditionalRConParsers { get; } - IList AdditionalEventParsers { get; } + /// - /// provides a method to execute database operations by name without exposing the - /// service level methods - /// todo: this could be made obsolete by creating a seperate service library with more concrete definitions + /// provides a method to execute database operations by name without exposing the + /// service level methods + /// todo: this could be made obsolete by creating a seperate service library with more concrete definitions /// /// /// Task> ExecuteSharedDatabaseOperation(string operationName); + void RegisterSharedDatabaseOperation(Task operation, string operationName); - IMiddlewareActionHandler MiddlewareActionHandler { get; } /// - /// generates an rcon parser that can be configured by script plugins + /// generates an rcon parser that can be configured by script plugins /// /// name of the RCon parser /// new rcon parser instance IRConParser GenerateDynamicRConParser(string name); /// - /// Generates an event parser that can be configured by script plugins + /// Generates an event parser that can be configured by script plugins /// /// name of the event parser /// new event parser instance IEventParser GenerateDynamicEventParser(string name); - string Version { get;} - ITokenAuthentication TokenAuthenticator { get; } - string ExternalIPAddress { get; } - CancellationToken CancellationToken { get; } - bool IsRestartRequested { get; } - bool IsRunning { get; } + Task ExecuteEvent(GameEvent gameEvent); + /// - /// queues an event for processing + /// queues an event for processing /// /// event to be processed void AddEvent(GameEvent gameEvent); + /// - /// adds an additional (script) command to the command list + /// adds an additional (script) command to the command list /// /// void AddAdditionalCommand(IManagerCommand command); + /// - /// removes a command by its name + /// removes a command by its name /// /// name of command void RemoveCommandByName(string name); + /// - /// event executed when event has finished executing + /// event executed when event has finished executing /// event EventHandler OnGameEventExecuted; - ConcurrentDictionary ProcessingEvents { get; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IManagerCommand.cs b/SharedLibraryCore/Interfaces/IManagerCommand.cs index 2999cac56..9e6f7d978 100644 --- a/SharedLibraryCore/Interfaces/IManagerCommand.cs +++ b/SharedLibraryCore/Interfaces/IManagerCommand.cs @@ -5,60 +5,60 @@ using static SharedLibraryCore.Server; namespace SharedLibraryCore.Interfaces { /// - /// Defines the basic properties of a command + /// Defines the basic properties of a command /// public interface IManagerCommand { /// - /// Executes the command - /// - /// event corresponding to the command - /// - Task ExecuteAsync(GameEvent gameEvent); - - /// - /// Name of the command + /// Name of the command /// string Name { get; } /// - /// Description of the command + /// Description of the command /// string Description { get; } /// - /// Alternative name of the command + /// Alternative name of the command /// string Alias { get; } /// - /// Minimum permission required to execute the command + /// Minimum permission required to execute the command /// Permission Permission { get; } /// - /// Games the command is supported on + /// Games the command is supported on /// Game[] SupportedGames { get; } /// - /// Syntax for using the command + /// Syntax for using the command /// string Syntax { get; } /// - /// Indicates if target is required + /// Indicates if target is required /// bool RequiresTarget { get; } /// - /// Indicates if the commands can be run as another client + /// Indicates if the commands can be run as another client /// bool AllowImpersonation { get; } /// - /// Indicates if the command result should be broadcasted to all clients + /// Indicates if the command result should be broadcasted to all clients /// bool IsBroadcast { get; set; } + + /// + /// Executes the command + /// + /// event corresponding to the command + /// + Task ExecuteAsync(GameEvent gameEvent); } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IMasterCommunication.cs b/SharedLibraryCore/Interfaces/IMasterCommunication.cs index bb2782b30..9926100ab 100644 --- a/SharedLibraryCore/Interfaces/IMasterCommunication.cs +++ b/SharedLibraryCore/Interfaces/IMasterCommunication.cs @@ -4,21 +4,21 @@ using System.Threading.Tasks; namespace SharedLibraryCore.Interfaces { /// - /// defines the capabilities of the communication to the master server + /// defines the capabilities of the communication to the master server /// public interface IMasterCommunication { /// - /// checks the current version of IW4MAdmin against the master version + /// checks the current version of IW4MAdmin against the master version /// /// Task CheckVersion(); /// - /// Sends heart beats to the master + /// Sends heart beats to the master /// /// Cancellation token /// Task RunUploadStatus(CancellationToken token); } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IMatchResult.cs b/SharedLibraryCore/Interfaces/IMatchResult.cs index 3abe1bd0d..f10aa5bc6 100644 --- a/SharedLibraryCore/Interfaces/IMatchResult.cs +++ b/SharedLibraryCore/Interfaces/IMatchResult.cs @@ -1,18 +1,18 @@ namespace SharedLibraryCore.Interfaces { /// - /// represents a pattern match result + /// represents a pattern match result /// public interface IMatchResult { /// - /// array of matched pattern groups + /// array of matched pattern groups /// string[] Values { get; set; } /// - /// indicates if the match succeeded + /// indicates if the match succeeded /// bool Success { get; set; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IMetaRegistration.cs b/SharedLibraryCore/Interfaces/IMetaRegistration.cs index 6aa444122..b54abba04 100644 --- a/SharedLibraryCore/Interfaces/IMetaRegistration.cs +++ b/SharedLibraryCore/Interfaces/IMetaRegistration.cs @@ -1,11 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace SharedLibraryCore.Interfaces +namespace SharedLibraryCore.Interfaces { public interface IMetaRegistration { void Register(); } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IMetaService.cs b/SharedLibraryCore/Interfaces/IMetaService.cs index 8c124f943..f01e194ae 100644 --- a/SharedLibraryCore/Interfaces/IMetaService.cs +++ b/SharedLibraryCore/Interfaces/IMetaService.cs @@ -1,17 +1,17 @@ -using Data.Models; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Data.Models; using SharedLibraryCore.Database.Models; using SharedLibraryCore.Dtos; using SharedLibraryCore.QueryHelper; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; namespace SharedLibraryCore.Interfaces { public interface IMetaService { /// - /// adds or updates meta key and value to the database + /// adds or updates meta key and value to the database /// /// key of meta data /// value of the meta data @@ -20,7 +20,7 @@ namespace SharedLibraryCore.Interfaces Task AddPersistentMeta(string metaKey, string metaValue, EFClient client, EFMeta linkedMeta = null); /// - /// adds or updates meta key and value to the database + /// adds or updates meta key and value to the database /// /// key of meta data /// value of the meta data @@ -28,7 +28,7 @@ namespace SharedLibraryCore.Interfaces Task AddPersistentMeta(string metaKey, string metaValue); /// - /// removes meta key with given value + /// removes meta key with given value /// /// key of meta data /// client to delete the meta for @@ -36,7 +36,7 @@ namespace SharedLibraryCore.Interfaces Task RemovePersistentMeta(string metaKey, EFClient client); /// - /// removes meta key with given value + /// removes meta key with given value /// /// key of the meta data /// value of the meta data @@ -44,7 +44,7 @@ namespace SharedLibraryCore.Interfaces Task RemovePersistentMeta(string metaKey, string metaValue = null); /// - /// retrieves meta data for given client and key + /// retrieves meta data for given client and key /// /// key to retrieve value for /// client to retrieve meta for @@ -52,32 +52,34 @@ namespace SharedLibraryCore.Interfaces Task GetPersistentMeta(string metaKey, EFClient client); /// - /// retrieves collection of meta for given key + /// retrieves collection of meta for given key /// /// key to retrieve values for /// Task> GetPersistentMeta(string metaKey); /// - /// adds a meta task to the runtime meta list + /// adds a meta task to the runtime meta list /// /// type of meta /// action to perform - void AddRuntimeMeta(MetaType metaKey, Func>> metaAction) where V : IClientMeta where T: PaginationRequest; + void AddRuntimeMeta(MetaType metaKey, Func>> metaAction) + where V : IClientMeta where T : PaginationRequest; /// - /// retrieves all the runtime meta information for given client idea + /// retrieves all the runtime meta information for given client idea /// /// request information /// Task> GetRuntimeMeta(ClientPaginationRequest request); /// - /// retreives all the runtime of provided type + /// retreives all the runtime of provided type /// /// >request information /// type of meta to retreive /// - Task> GetRuntimeMeta(ClientPaginationRequest request, MetaType metaType) where T : IClientMeta; + Task> GetRuntimeMeta(ClientPaginationRequest request, MetaType metaType) + where T : IClientMeta; } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IMiddlewareAction.cs b/SharedLibraryCore/Interfaces/IMiddlewareAction.cs index 995efe598..5eb2911b7 100644 --- a/SharedLibraryCore/Interfaces/IMiddlewareAction.cs +++ b/SharedLibraryCore/Interfaces/IMiddlewareAction.cs @@ -3,16 +3,16 @@ namespace SharedLibraryCore.Interfaces { /// - /// represents an invokable middleware action + /// represents an invokable middleware action /// /// public interface IMiddlewareAction { /// - /// action to execute when the middleware action is invoked + /// action to execute when the middleware action is invoked /// /// /// modified original action type instance Task Invoke(T original); } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IMiddlewareActionHandler.cs b/SharedLibraryCore/Interfaces/IMiddlewareActionHandler.cs index f44e2e43e..5e4de7dda 100644 --- a/SharedLibraryCore/Interfaces/IMiddlewareActionHandler.cs +++ b/SharedLibraryCore/Interfaces/IMiddlewareActionHandler.cs @@ -1,17 +1,14 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading.Tasks; +using System.Threading.Tasks; namespace SharedLibraryCore.Interfaces { /// - /// used to handle middleware actions registered from arbitrary assemblies + /// used to handle middleware actions registered from arbitrary assemblies /// public interface IMiddlewareActionHandler { /// - /// registers an action with the middleware handler + /// registers an action with the middleware handler /// /// action return type /// class type of action @@ -20,7 +17,7 @@ namespace SharedLibraryCore.Interfaces void Register(T actionType, IMiddlewareAction action, string name = null); /// - /// executes the given action type or name + /// executes the given action type or name /// /// action return type /// instance member to perform the action on @@ -28,4 +25,4 @@ namespace SharedLibraryCore.Interfaces /// Task Execute(T value, string name = null); } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IModelConfiguration.cs b/SharedLibraryCore/Interfaces/IModelConfiguration.cs index 3170826a9..b34e30418 100644 --- a/SharedLibraryCore/Interfaces/IModelConfiguration.cs +++ b/SharedLibraryCore/Interfaces/IModelConfiguration.cs @@ -1,7 +1,4 @@ using Microsoft.EntityFrameworkCore; -using System; -using System.Collections.Generic; -using System.Text; namespace SharedLibraryCore.Interfaces { @@ -9,4 +6,4 @@ namespace SharedLibraryCore.Interfaces { void Configure(ModelBuilder builder); } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IPageList.cs b/SharedLibraryCore/Interfaces/IPageList.cs index fde5fac05..52385082c 100644 --- a/SharedLibraryCore/Interfaces/IPageList.cs +++ b/SharedLibraryCore/Interfaces/IPageList.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; +using System.Collections.Generic; namespace SharedLibraryCore.Interfaces { @@ -8,4 +6,4 @@ namespace SharedLibraryCore.Interfaces { IDictionary Pages { get; set; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IParserPatternMatcher.cs b/SharedLibraryCore/Interfaces/IParserPatternMatcher.cs index 4a1712c28..9bdb9b81e 100644 --- a/SharedLibraryCore/Interfaces/IParserPatternMatcher.cs +++ b/SharedLibraryCore/Interfaces/IParserPatternMatcher.cs @@ -1,21 +1,21 @@ namespace SharedLibraryCore.Interfaces { /// - /// defines the capabilities of a parser pattern + /// defines the capabilities of a parser pattern /// public interface IParserPatternMatcher { /// - /// converts input string into pattern groups + /// converts input string into pattern groups /// /// input string /// group matches IMatchResult Match(string input); /// - /// compiles the pattern to be used for matching + /// compiles the pattern to be used for matching /// /// void Compile(string pattern); } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IParserRegexFactory.cs b/SharedLibraryCore/Interfaces/IParserRegexFactory.cs index 4c6f9bc67..8dd633874 100644 --- a/SharedLibraryCore/Interfaces/IParserRegexFactory.cs +++ b/SharedLibraryCore/Interfaces/IParserRegexFactory.cs @@ -1,14 +1,14 @@ namespace SharedLibraryCore.Interfaces { /// - /// defines the capabilities of the parser regex factory + /// defines the capabilities of the parser regex factory /// public interface IParserRegexFactory { /// - /// creates a new ParserRegex instance + /// creates a new ParserRegex instance /// /// ParserRegex instance ParserRegex CreateParserRegex(); } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IPlugin.cs b/SharedLibraryCore/Interfaces/IPlugin.cs index 89d6e2857..7d573f8ef 100644 --- a/SharedLibraryCore/Interfaces/IPlugin.cs +++ b/SharedLibraryCore/Interfaces/IPlugin.cs @@ -4,14 +4,13 @@ namespace SharedLibraryCore.Interfaces { public interface IPlugin { + string Name { get; } + float Version { get; } + string Author { get; } + bool IsParser => false; Task OnLoadAsync(IManager manager); Task OnUnloadAsync(); Task OnEventAsync(GameEvent E, Server S); Task OnTickAsync(Server S); - - string Name { get; } - float Version { get; } - string Author { get; } - bool IsParser => false; } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IPluginImporter.cs b/SharedLibraryCore/Interfaces/IPluginImporter.cs index 4748506be..9086fc74a 100644 --- a/SharedLibraryCore/Interfaces/IPluginImporter.cs +++ b/SharedLibraryCore/Interfaces/IPluginImporter.cs @@ -4,20 +4,20 @@ using System.Collections.Generic; namespace SharedLibraryCore.Interfaces { /// - /// defines the capabilities of the plugin importer + /// defines the capabilities of the plugin importer /// public interface IPluginImporter - { + { /// - /// discovers C# assembly plugin and command types + /// discovers C# assembly plugin and command types /// /// tuple of IPlugin implementation type definitions, and IManagerCommand type definitions (IEnumerable, IEnumerable, IEnumerable) DiscoverAssemblyPluginImplementations(); /// - /// discovers the script plugins + /// discovers the script plugins /// /// initialized script plugin collection IEnumerable DiscoverScriptPlugins(); } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IRConConnection.cs b/SharedLibraryCore/Interfaces/IRConConnection.cs index 2d4a715b5..a7dfaa8b0 100644 --- a/SharedLibraryCore/Interfaces/IRConConnection.cs +++ b/SharedLibraryCore/Interfaces/IRConConnection.cs @@ -1,15 +1,15 @@ -using SharedLibraryCore.RCon; -using System.Threading.Tasks; +using System.Threading.Tasks; +using SharedLibraryCore.RCon; namespace SharedLibraryCore.Interfaces { /// - /// defines the capabilities of an RCon connection + /// defines the capabilities of an RCon connection /// public interface IRConConnection { /// - /// sends a query with the instance of the rcon connection + /// sends a query with the instance of the rcon connection /// /// type of RCon query to perform /// optional parameter list @@ -17,9 +17,9 @@ namespace SharedLibraryCore.Interfaces Task SendQueryAsync(StaticHelpers.QueryType type, string parameters = ""); /// - /// sets the rcon parser + /// sets the rcon parser /// /// parser void SetConfiguration(IRConParser config); } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IRConConnectionFactory.cs b/SharedLibraryCore/Interfaces/IRConConnectionFactory.cs index 3179417ae..964c34973 100644 --- a/SharedLibraryCore/Interfaces/IRConConnectionFactory.cs +++ b/SharedLibraryCore/Interfaces/IRConConnectionFactory.cs @@ -3,12 +3,12 @@ namespace SharedLibraryCore.Interfaces { /// - /// defines the capabilities of an RCon connection factory + /// defines the capabilities of an RCon connection factory /// public interface IRConConnectionFactory { /// - /// creates an rcon connection instance + /// creates an rcon connection instance /// /// ip address and port of the server /// password of the server @@ -16,4 +16,4 @@ namespace SharedLibraryCore.Interfaces /// instance of rcon connection IRConConnection CreateConnection(IPEndPoint ipEndpoint, string password, string rconEngine); } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IRConParser.cs b/SharedLibraryCore/Interfaces/IRConParser.cs index 20eb8f749..2b630c60e 100644 --- a/SharedLibraryCore/Interfaces/IRConParser.cs +++ b/SharedLibraryCore/Interfaces/IRConParser.cs @@ -7,7 +7,45 @@ namespace SharedLibraryCore.Interfaces public interface IRConParser { /// - /// retrieves the value of a given DVAR + /// stores the RCon configuration + /// + IRConParserConfiguration Configuration { get; set; } + + /// + /// stores the game/client specific version (usually the value of the "version" DVAR) + /// + string Version { get; } + + /// + /// specifies the game name (usually the internal studio iteration ie: IW4, T5 etc...) + /// + Game GameName { get; } + + /// + /// indicates if the game supports generating a log path from DVAR retrieval + /// of fs_game, fs_basepath, g_log + /// + bool CanGenerateLogPath { get; } + + /// + /// specifies the name of the parser + /// + string Name { get; } + + /// + /// specifies the type of rcon engine + /// eg: COD, Source + /// + string RConEngine { get; } + + /// + /// indicates that the game does not log to the mods folder (when mod is loaded), + /// but rather always to the fs_basegame directory + /// + bool IsOneLog { get; } + + /// + /// retrieves the value of a given DVAR /// /// type of DVAR expected (string, int, float etc...) /// RCon connection to retrieve with @@ -17,7 +55,7 @@ namespace SharedLibraryCore.Interfaces Task> GetDvarAsync(IRConConnection connection, string dvarName, T fallbackValue = default); /// - /// set value of DVAR by name + /// set value of DVAR by name /// /// RCon connection to use /// name of DVAR to set @@ -26,7 +64,7 @@ namespace SharedLibraryCore.Interfaces Task SetDvarAsync(IRConConnection connection, string dvarName, object dvarValue); /// - /// executes a console command on the server + /// executes a console command on the server /// /// RCon connection to use /// console command to execute @@ -34,71 +72,35 @@ namespace SharedLibraryCore.Interfaces Task ExecuteCommandAsync(IRConConnection connection, string command); /// - /// get the list of connected clients from status response + /// get the list of connected clients from status response /// /// RCon connection to use - /// + /// + /// + /// Task GetStatusAsync(IRConConnection connection); /// - /// stores the RCon configuration - /// - IRConParserConfiguration Configuration { get; set; } - - /// - /// stores the game/client specific version (usually the value of the "version" DVAR) - /// - string Version { get; } - - /// - /// specifies the game name (usually the internal studio iteration ie: IW4, T5 etc...) - /// - Game GameName { get; } - - /// - /// indicates if the game supports generating a log path from DVAR retrieval - /// of fs_game, fs_basepath, g_log - /// - bool CanGenerateLogPath { get; } - - /// - /// specifies the name of the parser - /// - string Name { get; } - - /// - /// specifies the type of rcon engine - /// eg: COD, Source - /// - string RConEngine { get; } - - /// - /// indicates that the game does not log to the mods folder (when mod is loaded), - /// but rather always to the fs_basegame directory - /// - bool IsOneLog { get; } - - /// - /// retrieves the value of given dvar key if it exists in the override dict - /// otherwise returns original + /// retrieves the value of given dvar key if it exists in the override dict + /// otherwise returns original /// /// name of dvar key /// string GetOverrideDvarName(string dvarName); /// - /// retrieves the configuration value of a dvar key for - /// games that do not support the given dvar + /// retrieves the configuration value of a dvar key for + /// games that do not support the given dvar /// /// dvar key name /// T GetDefaultDvarValue(string dvarName); /// - /// determines the amount of time to wait for the command to respond + /// determines the amount of time to wait for the command to respond /// /// name of command being executed /// TimeSpan OverrideTimeoutForCommand(string command); } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IRConParserConfiguration.cs b/SharedLibraryCore/Interfaces/IRConParserConfiguration.cs index 943185676..6d84606c4 100644 --- a/SharedLibraryCore/Interfaces/IRConParserConfiguration.cs +++ b/SharedLibraryCore/Interfaces/IRConParserConfiguration.cs @@ -1,105 +1,104 @@ -using SharedLibraryCore.RCon; -using System.Collections.Generic; +using System.Collections.Generic; using System.Globalization; using SharedLibraryCore.Formatting; -using SharedLibraryCore.Localization; +using SharedLibraryCore.RCon; namespace SharedLibraryCore.Interfaces { public interface IRConParserConfiguration { /// - /// stores the command format for console commands + /// stores the command format for console commands /// CommandPrefix CommandPrefixes { get; } /// - /// stores the regex info for parsing get status response + /// stores the regex info for parsing get status response /// ParserRegex Status { get; } /// - /// stores regex info for parsing the map line from rcon status response + /// stores regex info for parsing the map line from rcon status response /// ParserRegex MapStatus { get; } /// - /// stores regex info for parsing the gametype line from rcon status response + /// stores regex info for parsing the gametype line from rcon status response /// ParserRegex GametypeStatus { get; } - + /// - /// stores regex info for parsing hostname line from rcon status response + /// stores regex info for parsing hostname line from rcon status response /// ParserRegex HostnameStatus { get; } - + /// - /// stores regex info for parsing max players line from rcon status response + /// stores regex info for parsing max players line from rcon status response /// ParserRegex MaxPlayersStatus { get; } /// - /// stores the regex info for parsing get DVAR responses + /// stores the regex info for parsing get DVAR responses /// ParserRegex Dvar { get; } /// - /// stores the regex info for parsing the header of a status response + /// stores the regex info for parsing the header of a status response /// ParserRegex StatusHeader { get; } /// - /// Specifies the expected response message from rcon when the server is not running + /// Specifies the expected response message from rcon when the server is not running /// string ServerNotRunningResponse { get; } /// - /// indicates if the application should wait for response from server - /// when executing a command + /// indicates if the application should wait for response from server + /// when executing a command /// bool WaitForResponse { get; } /// - /// indicates the format expected for parsed guids + /// indicates the format expected for parsed guids /// NumberStyles GuidNumberStyle { get; } /// - /// specifies simple mappings for dvar names in scenarios where the needed - /// information is not stored in a traditional dvar name + /// specifies simple mappings for dvar names in scenarios where the needed + /// information is not stored in a traditional dvar name /// IDictionary OverrideDvarNameMapping { get; } /// - /// specifies the default dvar values for games that don't support certain dvars + /// specifies the default dvar values for games that don't support certain dvars /// IDictionary DefaultDvarValues { get; } /// - /// specifies how many lines can be used for ingame notice + /// specifies how many lines can be used for ingame notice /// int NoticeMaximumLines { get; set; } /// - /// specifies how many characters can be displayed per notice line + /// specifies how many characters can be displayed per notice line /// int NoticeMaxCharactersPerLine { get; } - + /// - /// specifies the characters used to split a line + /// specifies the characters used to split a line /// string NoticeLineSeparator { get; } - + /// - /// Default port the game listens to RCon requests on + /// Default port the game listens to RCon requests on /// int? DefaultRConPort { get; } - + /// - /// Default Indicator of where the game is installed (ex file path or registry entry) + /// Default Indicator of where the game is installed (ex file path or registry entry) /// string DefaultInstallationDirectoryHint { get; } ColorCodeMapping ColorCodeMapping { get; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IRegisterEvent.cs b/SharedLibraryCore/Interfaces/IRegisterEvent.cs index c8dfc2e43..119ddea6b 100644 --- a/SharedLibraryCore/Interfaces/IRegisterEvent.cs +++ b/SharedLibraryCore/Interfaces/IRegisterEvent.cs @@ -4,16 +4,16 @@ using System.Collections.Generic; namespace SharedLibraryCore.Interfaces { /// - /// interface defining the capabilities of a custom event registration + /// interface defining the capabilities of a custom event registration /// public interface IRegisterEvent { /// - /// collection of custom event registrations - /// - /// (Subtype, trigger value, event generator) - /// + /// collection of custom event registrations + /// + /// (Subtype, trigger value, event generator) + /// /// IEnumerable<(string, string, Func)> Events { get; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IRemoteAssemblyHandler.cs b/SharedLibraryCore/Interfaces/IRemoteAssemblyHandler.cs index 278d93d96..088720a54 100644 --- a/SharedLibraryCore/Interfaces/IRemoteAssemblyHandler.cs +++ b/SharedLibraryCore/Interfaces/IRemoteAssemblyHandler.cs @@ -8,4 +8,4 @@ namespace SharedLibraryCore.Interfaces IEnumerable DecryptAssemblies(string[] encryptedAssemblies); IEnumerable DecryptScripts(string[] encryptedScripts); } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IResourceQueryHelper.cs b/SharedLibraryCore/Interfaces/IResourceQueryHelper.cs index ede8251c6..34274af2c 100644 --- a/SharedLibraryCore/Interfaces/IResourceQueryHelper.cs +++ b/SharedLibraryCore/Interfaces/IResourceQueryHelper.cs @@ -1,20 +1,20 @@ -using SharedLibraryCore.Helpers; -using System.Threading.Tasks; +using System.Threading.Tasks; +using SharedLibraryCore.Helpers; namespace SharedLibraryCore.Interfaces { /// - /// defines the capabilities of a resource queryier + /// defines the capabilities of a resource queryier /// /// Type of query /// Type of result public interface IResourceQueryHelper { /// - /// queries a resource and returns the result of the query + /// queries a resource and returns the result of the query /// /// query params /// Task> QueryResource(QueryType query); } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IScriptCommandFactory.cs b/SharedLibraryCore/Interfaces/IScriptCommandFactory.cs index f6c3cf994..f8ce95655 100644 --- a/SharedLibraryCore/Interfaces/IScriptCommandFactory.cs +++ b/SharedLibraryCore/Interfaces/IScriptCommandFactory.cs @@ -4,12 +4,12 @@ using System.Collections.Generic; namespace SharedLibraryCore.Interfaces { /// - /// defines capabilities of script command factory + /// defines capabilities of script command factory /// public interface IScriptCommandFactory { /// - /// generate a new script command from parsed source + /// generate a new script command from parsed source /// /// name of command /// alias of command @@ -19,6 +19,7 @@ namespace SharedLibraryCore.Interfaces /// command arguments (name, is required) /// action to peform when commmand is executed /// - IManagerCommand CreateScriptCommand(string name, string alias, string description, string permission, bool isTargetRequired, IEnumerable<(string, bool)> args, Action executeAction); + IManagerCommand CreateScriptCommand(string name, string alias, string description, string permission, + bool isTargetRequired, IEnumerable<(string, bool)> args, Action executeAction); } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IScriptPluginServiceResolver.cs b/SharedLibraryCore/Interfaces/IScriptPluginServiceResolver.cs index 4a977703c..0ec29fa1d 100644 --- a/SharedLibraryCore/Interfaces/IScriptPluginServiceResolver.cs +++ b/SharedLibraryCore/Interfaces/IScriptPluginServiceResolver.cs @@ -1,25 +1,23 @@ -using System.Collections.Generic; - -namespace SharedLibraryCore.Interfaces +namespace SharedLibraryCore.Interfaces { /// - /// interface used to dynamically resolve services by string name + /// interface used to dynamically resolve services by string name /// public interface IScriptPluginServiceResolver { /// - /// resolves a service with the given name + /// resolves a service with the given name /// /// class name of service /// object ResolveService(string serviceName); /// - /// resolves a service with the given name and generic params + /// resolves a service with the given name and generic params /// /// class name of service /// generic class names /// object ResolveService(string serviceName, string[] genericParameters); } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IServerDataCollector.cs b/SharedLibraryCore/Interfaces/IServerDataCollector.cs index 10c9fb1d2..4176a1f97 100644 --- a/SharedLibraryCore/Interfaces/IServerDataCollector.cs +++ b/SharedLibraryCore/Interfaces/IServerDataCollector.cs @@ -7,7 +7,7 @@ namespace SharedLibraryCore.Interfaces public interface IServerDataCollector { /// - /// Begins to collection on servers for analytical purposes + /// Begins to collection on servers for analytical purposes /// /// interval at which to collect data /// Token diff --git a/SharedLibraryCore/Interfaces/IServerDataViewer.cs b/SharedLibraryCore/Interfaces/IServerDataViewer.cs index 652e948c1..2e562944d 100644 --- a/SharedLibraryCore/Interfaces/IServerDataViewer.cs +++ b/SharedLibraryCore/Interfaces/IServerDataViewer.cs @@ -3,26 +3,26 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using SharedLibraryCore.Dtos; -using SharedLibraryCore.Helpers; namespace SharedLibraryCore.Interfaces { /// - /// Exposes methods to get analytical data about server(s) + /// Exposes methods to get analytical data about server(s) /// public interface IServerDataViewer { /// - /// Retrieves the max concurrent clients over a give time period for all servers or given server id + /// Retrieves the max concurrent clients over a give time period for all servers or given server id /// /// ServerId to query on /// how far in the past to search /// CancellationToken /// - Task<(int?, DateTime?)> MaxConcurrentClientsAsync(long? serverId = null, TimeSpan? overPeriod = null, CancellationToken token = default); - + Task<(int?, DateTime?)> MaxConcurrentClientsAsync(long? serverId = null, TimeSpan? overPeriod = null, + CancellationToken token = default); + /// - /// Gets the total number of clients connected and total clients connected in the given time frame + /// Gets the total number of clients connected and total clients connected in the given time frame /// /// how far in the past to search /// CancellationToken @@ -30,11 +30,12 @@ namespace SharedLibraryCore.Interfaces Task<(int, int)> ClientCountsAsync(TimeSpan? overPeriod = null, CancellationToken token = default); /// - /// Retrieves the client count and history over the given period + /// Retrieves the client count and history over the given period /// /// how far in the past to search /// CancellationToken /// - Task> ClientHistoryAsync(TimeSpan? overPeriod = null, CancellationToken token = default); + Task> ClientHistoryAsync(TimeSpan? overPeriod = null, + CancellationToken token = default); } } \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/IStatusResponse.cs b/SharedLibraryCore/Interfaces/IStatusResponse.cs index 623126c17..6f53b2ffb 100644 --- a/SharedLibraryCore/Interfaces/IStatusResponse.cs +++ b/SharedLibraryCore/Interfaces/IStatusResponse.cs @@ -3,32 +3,32 @@ namespace SharedLibraryCore.Interfaces { /// - /// describes the collection of data returned from a status query + /// describes the collection of data returned from a status query /// public interface IStatusResponse { /// - /// name of the map + /// name of the map /// string Map { get; } - + /// - /// gametype/mode + /// gametype/mode /// string GameType { get; } - + /// - /// server name + /// server name /// string Hostname { get; } - + /// - /// max number of players + /// max number of players /// int? MaxClients { get; } - + /// - /// active clients + /// active clients /// EFClient[] Clients { get; } } diff --git a/SharedLibraryCore/Interfaces/ITokenAuthentication.cs b/SharedLibraryCore/Interfaces/ITokenAuthentication.cs index 58d0ce3dd..9ffbcdcd4 100644 --- a/SharedLibraryCore/Interfaces/ITokenAuthentication.cs +++ b/SharedLibraryCore/Interfaces/ITokenAuthentication.cs @@ -1,24 +1,21 @@ using SharedLibraryCore.Helpers; -using System; -using System.Collections.Generic; -using System.Text; namespace SharedLibraryCore.Interfaces { public interface ITokenAuthentication { /// - /// generates and returns a token for the given network id + /// generates and returns a token for the given network id /// /// network id of the players to generate the token for /// 4 character string token TokenState GenerateNextToken(long networkId); /// - /// authorizes given token + /// authorizes given token /// /// token to authorize /// true if token authorized successfully, false otherwise bool AuthorizeToken(long networkId, string token); } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Interfaces/ITranslationLookup.cs b/SharedLibraryCore/Interfaces/ITranslationLookup.cs index 4c3d2f95b..720826f42 100644 --- a/SharedLibraryCore/Interfaces/ITranslationLookup.cs +++ b/SharedLibraryCore/Interfaces/ITranslationLookup.cs @@ -1,15 +1,15 @@ namespace SharedLibraryCore.Interfaces { /// - /// Defines the translation lookup capabilities for DI + /// Defines the translation lookup capabilities for DI /// public interface ITranslationLookup { /// - /// Allows indexing + /// Allows indexing /// /// translation lookup key /// string this[string key] { get; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Localization/Layout.cs b/SharedLibraryCore/Localization/Layout.cs index 76e636fe2..a4b330e7e 100644 --- a/SharedLibraryCore/Localization/Layout.cs +++ b/SharedLibraryCore/Localization/Layout.cs @@ -1,12 +1,21 @@ -using SharedLibraryCore.Interfaces; -using System.Collections.Generic; +using System.Collections.Generic; using System.Globalization; +using SharedLibraryCore.Interfaces; namespace SharedLibraryCore.Localization { public class Layout { private string localizationName; + + public Layout(Dictionary set) + { + LocalizationIndex = new TranslationLookup + { + Set = set + }; + } + public string LocalizationName { get => localizationName; @@ -16,16 +25,9 @@ namespace SharedLibraryCore.Localization Culture = new CultureInfo(value); } } + public TranslationLookup LocalizationIndex { get; set; } public CultureInfo Culture { get; private set; } - - public Layout(Dictionary set) - { - LocalizationIndex = new TranslationLookup() - { - Set = set - }; - } } public class TranslationLookup : ITranslationLookup @@ -36,13 +38,13 @@ namespace SharedLibraryCore.Localization { get { - if (!Set.TryGetValue(key, out string value)) + if (!Set.TryGetValue(key, out var value)) { return key; } + return value; } } } - -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Localization/Permission.cs b/SharedLibraryCore/Localization/Permission.cs index 0fca389c2..c6dc17742 100644 --- a/SharedLibraryCore/Localization/Permission.cs +++ b/SharedLibraryCore/Localization/Permission.cs @@ -7,4 +7,4 @@ namespace SharedLibraryCore.Localization public Permission Level { get; set; } public string Name { get; set; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/PartialEntities/EFClient.cs b/SharedLibraryCore/PartialEntities/EFClient.cs index bda9d2a33..798ae3476 100644 --- a/SharedLibraryCore/PartialEntities/EFClient.cs +++ b/SharedLibraryCore/PartialEntities/EFClient.cs @@ -1,14 +1,14 @@ -using SharedLibraryCore.Localization; -using System; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; +using Data.Models; using Microsoft.Extensions.Logging; using Serilog.Context; -using Data.Models; +using SharedLibraryCore.Localization; namespace SharedLibraryCore.Database.Models { @@ -17,28 +17,30 @@ namespace SharedLibraryCore.Database.Models public enum ClientState { /// - /// default client state + /// default client state /// Unknown, /// - /// represents when the client has been detected as joining - /// by the log file, but has not be authenticated by RCon + /// represents when the client has been detected as joining + /// by the log file, but has not be authenticated by RCon /// Connecting, /// - /// represents when the client has been authenticated by RCon - /// and validated by the database + /// represents when the client has been authenticated by RCon + /// and validated by the database /// Connected, /// - /// represents when the client is leaving (either through RCon or log file) + /// represents when the client is leaving (either through RCon or log file) /// Disconnecting } + [NotMapped] private readonly SemaphoreSlim _processingEvent; + public EFClient() { ConnectionTime = DateTime.UtcNow; @@ -48,6 +50,84 @@ namespace SharedLibraryCore.Database.Models _processingEvent = new SemaphoreSlim(1, 1); } + [NotMapped] + public virtual string Name + { + get => CurrentAlias?.Name ?? "--"; + set + { + if (CurrentAlias != null) + { + CurrentAlias.Name = value; + } + } + } + + [NotMapped] public string CleanedName => Name?.StripColors(); + + [NotMapped] + public virtual int? IPAddress + { + get => CurrentAlias.IPAddress; + set => CurrentAlias.IPAddress = value; + } + + [NotMapped] public string IPAddressString => IPAddress.ConvertIPtoString(); + + [NotMapped] public bool IsIngame => ClientNumber >= 0; + + [NotMapped] public virtual IDictionary LinkedAccounts { get; set; } + + [NotMapped] public int ClientNumber { get; set; } + + [NotMapped] public int Ping { get; set; } + + [NotMapped] public int Warnings { get; set; } + + [NotMapped] public DateTime ConnectionTime { get; set; } + + [NotMapped] public int ConnectionLength => (int)(DateTime.UtcNow - ConnectionTime).TotalSeconds; + + [NotMapped] public Server CurrentServer { get; set; } + + [NotMapped] public int Score { get; set; } + + [NotMapped] public bool IsBot => NetworkId == Name.GenerateGuidFromString(); + + [NotMapped] public bool IsZombieClient => IsBot && Name == "Zombie"; + + [NotMapped] public string XuidString => (NetworkId + 0x110000100000000).ToString("x"); + + [NotMapped] public string GuidString => NetworkId.ToString("x"); + + [NotMapped] public ClientState State { get; set; } + + [NotMapped] + // this is kinda dirty, but I need localizable level names + public ClientPermission ClientPermission => new ClientPermission + { + Level = Level, + Name = Level.ToLocalizedLevelName() + }; + + [NotMapped] + public string Tag + { + get => GetAdditionalProperty(EFMeta.ClientTag); + set => SetAdditionalProperty(EFMeta.ClientTag, value); + } + + [NotMapped] + public int TemporalClientNumber + { + get + { + var temporalClientId = GetAdditionalProperty("ConnectionClientId"); + var parsedClientId = string.IsNullOrEmpty(temporalClientId) ? (int?)null : int.Parse(temporalClientId); + return parsedClientId ?? ClientNumber; + } + } + ~EFClient() { _processingEvent?.Dispose(); @@ -55,42 +135,17 @@ namespace SharedLibraryCore.Database.Models public override string ToString() { - return $"[Name={CurrentAlias?.Name ?? "--"}, NetworkId={NetworkId.ToString("X")}, IP={(string.IsNullOrEmpty(IPAddressString) ? "--" : IPAddressString)}, ClientSlot={ClientNumber}]"; + return + $"[Name={CurrentAlias?.Name ?? "--"}, NetworkId={NetworkId.ToString("X")}, IP={(string.IsNullOrEmpty(IPAddressString) ? "--" : IPAddressString)}, ClientSlot={ClientNumber}]"; } - [NotMapped] - public virtual string Name - { - get { return CurrentAlias?.Name ?? "--"; } - set { if (CurrentAlias != null) CurrentAlias.Name = value; } - } - - [NotMapped] - public string CleanedName => Name?.StripColors(); - - [NotMapped] - public virtual int? IPAddress - { - get { return CurrentAlias.IPAddress; } - set { CurrentAlias.IPAddress = value; } - } - - [NotMapped] - public string IPAddressString => IPAddress.ConvertIPtoString(); - - [NotMapped] - public bool IsIngame => ClientNumber >= 0; - - [NotMapped] - public virtual IDictionary LinkedAccounts { get; set; } - /// - /// send a message directly to the connected client + /// send a message directly to the connected client /// /// message content to send to client public GameEvent Tell(string message) { - var e = new GameEvent() + var e = new GameEvent { Message = message, Target = this, @@ -98,7 +153,10 @@ namespace SharedLibraryCore.Database.Models Type = GameEvent.EventType.Tell, Data = message, CorrelationId = CurrentServer.Manager.ProcessingEvents.Values - .FirstOrDefault(ev => ev.Type == GameEvent.EventType.Command && (ev.Origin?.ClientId == ClientId || ev.ImpersonationOrigin?.ClientId == ClientId))?.CorrelationId ?? Guid.NewGuid() + .FirstOrDefault(ev => + ev.Type == GameEvent.EventType.Command && (ev.Origin?.ClientId == ClientId || + ev.ImpersonationOrigin?.ClientId == ClientId)) + ?.CorrelationId ?? Guid.NewGuid() }; e.Output.Add(message.FormatMessageForEngine(CurrentServer?.RconParser.Configuration.ColorCodeMapping) @@ -110,7 +168,7 @@ namespace SharedLibraryCore.Database.Models public void Tell(IEnumerable messages) { - foreach(var message in messages) + foreach (var message in messages) { #pragma warning disable 4014 Tell(message).WaitAsync(); @@ -119,13 +177,13 @@ namespace SharedLibraryCore.Database.Models } /// - /// warn a client with given reason + /// warn a client with given reason /// /// reason for warn /// client performing the warn - public GameEvent Warn(String warnReason, EFClient sender) + public GameEvent Warn(string warnReason, EFClient sender) { - var e = new GameEvent() + var e = new GameEvent { Type = GameEvent.EventType.Warn, Message = warnReason, @@ -151,13 +209,13 @@ namespace SharedLibraryCore.Database.Models } /// - /// clear all warnings for a client + /// clear all warnings for a client /// /// client performing the warn clear /// public GameEvent WarnClear(EFClient sender) { - var e = new GameEvent() + var e = new GameEvent { Type = GameEvent.EventType.WarnClear, Origin = sender, @@ -179,14 +237,14 @@ namespace SharedLibraryCore.Database.Models } /// - /// report a client for a given reason + /// report a client for a given reason /// /// reason for the report /// client performing the report /// public GameEvent Report(string reportReason, EFClient sender) { - var e = new GameEvent() + var e = new GameEvent { Type = GameEvent.EventType.Report, Message = reportReason, @@ -196,7 +254,7 @@ namespace SharedLibraryCore.Database.Models Owner = sender.CurrentServer }; - int reportCount = sender.GetAdditionalProperty("_reportCount"); + var reportCount = sender.GetAdditionalProperty("_reportCount"); if (Equals(sender)) { @@ -208,8 +266,8 @@ namespace SharedLibraryCore.Database.Models e.FailReason = GameEvent.EventFailReason.Throttle; } - else if (CurrentServer.Reports.Count(report => (report.Origin.NetworkId == sender.NetworkId && - report.Target.NetworkId == NetworkId)) > 0) + else if (CurrentServer.Reports.Count(report => report.Origin.NetworkId == sender.NetworkId && + report.Target.NetworkId == NetworkId) > 0) { e.FailReason = GameEvent.EventFailReason.Exception; } @@ -220,14 +278,14 @@ namespace SharedLibraryCore.Database.Models } /// - /// flag a client for a given reason + /// flag a client for a given reason /// /// reason for flagging /// client performing the flag /// game event for the flag public GameEvent Flag(string flagReason, EFClient sender, TimeSpan? flagLength = null) { - var e = new GameEvent() + var e = new GameEvent { Type = GameEvent.EventType.Flag, Origin = sender, @@ -253,14 +311,14 @@ namespace SharedLibraryCore.Database.Models } /// - /// unflag a client for a given reason + /// unflag a client for a given reason /// /// reason to unflag a player for /// client performing the unflag /// game event for the un flug public GameEvent Unflag(string unflagReason, EFClient sender) { - var e = new GameEvent() + var e = new GameEvent { Type = GameEvent.EventType.Unflag, Origin = sender, @@ -285,21 +343,24 @@ namespace SharedLibraryCore.Database.Models } /// - /// kick a client for the given reason + /// kick a client for the given reason /// /// reason to kick for /// client performing the kick - public GameEvent Kick(string kickReason, EFClient sender) => Kick(kickReason, sender, null); + public GameEvent Kick(string kickReason, EFClient sender) + { + return Kick(kickReason, sender, null); + } /// - /// kick a client for the given reason + /// kick a client for the given reason /// /// reason to kick for /// client performing the kick /// original client penalty public GameEvent Kick(string kickReason, EFClient sender, EFPenalty originalPenalty) { - var e = new GameEvent() + var e = new GameEvent { Type = GameEvent.EventType.Kick, Message = kickReason, @@ -322,14 +383,14 @@ namespace SharedLibraryCore.Database.Models } /// - /// temporarily ban a client for the given time span + /// temporarily ban a client for the given time span /// /// reason for the temp ban /// how long the temp ban lasts /// client performing the tempban - public GameEvent TempBan(String tempbanReason, TimeSpan banLength, EFClient sender) + public GameEvent TempBan(string tempbanReason, TimeSpan banLength, EFClient sender) { - var e = new GameEvent() + var e = new GameEvent { Type = GameEvent.EventType.TempBan, Message = tempbanReason, @@ -352,13 +413,13 @@ namespace SharedLibraryCore.Database.Models } /// - /// permanently ban a client + /// permanently ban a client /// /// reason for the ban /// client performing the ban - public GameEvent Ban(String banReason, EFClient sender, bool isEvade) + public GameEvent Ban(string banReason, EFClient sender, bool isEvade) { - var e = new GameEvent() + var e = new GameEvent { Type = GameEvent.EventType.Ban, Message = banReason, @@ -386,14 +447,14 @@ namespace SharedLibraryCore.Database.Models } /// - /// unban a client + /// unban a client /// /// reason for the unban /// client performing the unban /// public GameEvent Unban(string unbanReason, EFClient sender) { - var e = new GameEvent() + var e = new GameEvent { Type = GameEvent.EventType.Unban, Message = unbanReason, @@ -414,14 +475,14 @@ namespace SharedLibraryCore.Database.Models } /// - /// sets the level of the client + /// sets the level of the client /// /// new permission to set client to /// user performing the set level /// public GameEvent SetLevel(Permission newPermission, EFClient sender) { - var e = new GameEvent() + var e = new GameEvent { Type = GameEvent.EventType.ChangePermission, Extra = newPermission, @@ -450,7 +511,7 @@ namespace SharedLibraryCore.Database.Models } /// - /// Handles any client related logic on connection + /// Handles any client related logic on connection /// public bool IsAbleToConnectSimple() { @@ -462,33 +523,38 @@ namespace SharedLibraryCore.Database.Models if (string.IsNullOrWhiteSpace(Name) || nameToCheck.Replace(" ", "").Length < (CurrentServer?.Manager?.GetApplicationSettings()?.Configuration()?.MinimumNameLength ?? 3)) { - Utilities.DefaultLogger.LogInformation("Kicking {Client} because their name is too short", ToString()); + Utilities.DefaultLogger.LogInformation("Kicking {Client} because their name is too short", + ToString()); Kick(loc["SERVER_KICK_MINNAME"], Utilities.IW4MAdminClient(CurrentServer)); return false; } if (CurrentServer.Manager.GetApplicationSettings().Configuration() - .DisallowedClientNames - ?.Any(_name => Regex.IsMatch(Name, _name)) ?? false) + .DisallowedClientNames + ?.Any(_name => Regex.IsMatch(Name, _name)) ?? false) { - Utilities.DefaultLogger.LogInformation("Kicking {Client} because their name is not allowed", ToString()); + Utilities.DefaultLogger.LogInformation("Kicking {Client} because their name is not allowed", + ToString()); Kick(loc["SERVER_KICK_GENERICNAME"], Utilities.IW4MAdminClient(CurrentServer)); return false; } if (Name.Where(c => char.IsControl(c)).Count() > 0) { - Utilities.DefaultLogger.LogInformation("Kicking {Client} because their name contains control characters", ToString()); + Utilities.DefaultLogger.LogInformation( + "Kicking {Client} because their name contains control characters", ToString()); Kick(loc["SERVER_KICK_CONTROLCHARS"], Utilities.IW4MAdminClient(CurrentServer)); return false; } // reserved slots stuff // todo: bots don't seem to honor party_maxplayers/sv_maxclients - if (CurrentServer.MaxClients - (CurrentServer.GetClientsAsList().Count(_client => !_client.IsPrivileged() && !_client.IsBot)) < CurrentServer.ServerConfig.ReservedSlotNumber && - !this.IsPrivileged() && - CurrentServer.GetClientsAsList().Count <= CurrentServer.MaxClients && - CurrentServer.MaxClients != 0) + if (CurrentServer.MaxClients - CurrentServer.GetClientsAsList() + .Count(_client => !_client.IsPrivileged() && !_client.IsBot) < + CurrentServer.ServerConfig.ReservedSlotNumber && + !this.IsPrivileged() && + CurrentServer.GetClientsAsList().Count <= CurrentServer.MaxClients && + CurrentServer.MaxClients != 0) { Utilities.DefaultLogger.LogInformation("Kicking {Client} their spot is reserved", ToString()); Kick(loc["SERVER_KICK_SLOT_IS_RESERVED"], Utilities.IW4MAdminClient(CurrentServer)); @@ -515,8 +581,8 @@ namespace SharedLibraryCore.Database.Models catch (Exception e) { - Utilities.DefaultLogger.LogError(e, "Could not update disconnected client {client}", - ToString()); + Utilities.DefaultLogger.LogError(e, "Could not update disconnected client {client}", + ToString()); } finally @@ -530,8 +596,9 @@ namespace SharedLibraryCore.Database.Models { using (LogContext.PushProperty("Server", CurrentServer?.ToString())) { - Utilities.DefaultLogger.LogInformation("Client {client} is joining the game from {source}", ToString(), ipAddress.HasValue ? "Status" : "Log"); - + Utilities.DefaultLogger.LogInformation("Client {client} is joining the game from {source}", ToString(), + ipAddress.HasValue ? "Status" : "Log"); + if (ipAddress != null) { IPAddress = ipAddress; @@ -550,12 +617,12 @@ namespace SharedLibraryCore.Database.Models else { Utilities.DefaultLogger.LogDebug("Creating join event for {client}", ToString()); - var e = new GameEvent() + var e = new GameEvent { Type = GameEvent.EventType.Join, Origin = this, Target = this, - Owner = CurrentServer, + Owner = CurrentServer }; CurrentServer.Manager.AddEvent(e); @@ -577,7 +644,7 @@ namespace SharedLibraryCore.Database.Models { var loc = Utilities.CurrentLocalization.LocalizationIndex; var autoKickClient = Utilities.IW4MAdminClient(CurrentServer); - bool isAbleToConnectSimple = IsAbleToConnectSimple(); + var isAbleToConnectSimple = IsAbleToConnectSimple(); if (!isAbleToConnectSimple) { @@ -604,7 +671,7 @@ namespace SharedLibraryCore.Database.Models await SetLevel(Permission.Banned, autoKickClient).WaitAsync(Utilities.DefaultCommandTimeout, CurrentServer.Manager.CancellationToken); } - + Utilities.DefaultLogger.LogInformation("Kicking {client} because they are banned", ToString()); Kick(loc["WEBFRONT_PENALTY_LIST_BANNED_REASON"], autoKickClient, banPenalty); return false; @@ -643,71 +710,16 @@ namespace SharedLibraryCore.Database.Models return true; } - [NotMapped] - public int ClientNumber { get; set; } - [NotMapped] - public int Ping { get; set; } - [NotMapped] - public int Warnings { get; set; } - [NotMapped] - public DateTime ConnectionTime { get; set; } - [NotMapped] - public int ConnectionLength => (int)(DateTime.UtcNow - ConnectionTime).TotalSeconds; - [NotMapped] - public Server CurrentServer { get; set; } - [NotMapped] - public int Score { get; set; } - [NotMapped] - public bool IsBot => NetworkId == Name.GenerateGuidFromString(); - [NotMapped] - public bool IsZombieClient => IsBot && Name == "Zombie"; - [NotMapped] - public string XuidString => (NetworkId + 0x110000100000000).ToString("x"); - [NotMapped] - public string GuidString => NetworkId.ToString("x"); - - [NotMapped] - public ClientState State { get; set; } - - [NotMapped] - // this is kinda dirty, but I need localizable level names - public ClientPermission ClientPermission => new ClientPermission() - { - Level = Level, - Name = Level.ToLocalizedLevelName() - }; - - [NotMapped] - public string Tag - { - get => GetAdditionalProperty(EFMeta.ClientTag); - set => SetAdditionalProperty(EFMeta.ClientTag, value); - } - - [NotMapped] - public int TemporalClientNumber - { - get - { - var temporalClientId = GetAdditionalProperty("ConnectionClientId"); - var parsedClientId = string.IsNullOrEmpty(temporalClientId) ? (int?) null : int.Parse(temporalClientId); - return parsedClientId ?? ClientNumber; - } - } - - [NotMapped] - private readonly SemaphoreSlim _processingEvent; - public async Task Lock() { - bool result = await _processingEvent.WaitAsync(Utilities.DefaultCommandTimeout); + var result = await _processingEvent.WaitAsync(Utilities.DefaultCommandTimeout); #if DEBUG if (!result) { throw new InvalidOperationException(); } -#endif +#endif } public void Unlock() @@ -720,7 +732,7 @@ namespace SharedLibraryCore.Database.Models public override bool Equals(object obj) { - return obj.GetType() == typeof(EFClient) && ((EFClient)obj).NetworkId == this.NetworkId; + return obj.GetType() == typeof(EFClient) && ((EFClient)obj).NetworkId == NetworkId; } public override int GetHashCode() @@ -728,4 +740,4 @@ namespace SharedLibraryCore.Database.Models return IsBot ? ClientNumber : (int)NetworkId; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/QueryHelper/ClientPaginationRequest.cs b/SharedLibraryCore/QueryHelper/ClientPaginationRequest.cs index 5eaef22e7..c40d55745 100644 --- a/SharedLibraryCore/QueryHelper/ClientPaginationRequest.cs +++ b/SharedLibraryCore/QueryHelper/ClientPaginationRequest.cs @@ -1,7 +1,4 @@ using SharedLibraryCore.Dtos; -using System; -using System.Collections.Generic; -using System.Text; namespace SharedLibraryCore.QueryHelper { @@ -9,4 +6,4 @@ namespace SharedLibraryCore.QueryHelper { public int ClientId { get; set; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/RCon/CommandPrefix.cs b/SharedLibraryCore/RCon/CommandPrefix.cs index 0f4d8324c..599471928 100644 --- a/SharedLibraryCore/RCon/CommandPrefix.cs +++ b/SharedLibraryCore/RCon/CommandPrefix.cs @@ -17,4 +17,4 @@ public string RConResponse { get; set; } public string RconGetInfoResponseHeader { get; set; } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/RCon/StaticHelpers.cs b/SharedLibraryCore/RCon/StaticHelpers.cs index 540fa0231..36407d141 100644 --- a/SharedLibraryCore/RCon/StaticHelpers.cs +++ b/SharedLibraryCore/RCon/StaticHelpers.cs @@ -1,53 +1,65 @@ using System; +using System.Globalization; namespace SharedLibraryCore.RCon { public static class StaticHelpers { /// - /// defines the type of RCon query sent to a server + /// defines the type of RCon query sent to a server /// public enum QueryType { /// - /// retrieve the status of a server - /// does not require RCon password + /// retrieve the status of a server + /// does not require RCon password /// GET_STATUS, + /// - /// retrieve the information of a server - /// server responds with key/value pairs - /// RCon password is required + /// retrieve the information of a server + /// server responds with key/value pairs + /// RCon password is required /// GET_INFO, + /// - /// retrieve the value of a DVAR - /// RCon password is required + /// retrieve the value of a DVAR + /// RCon password is required /// GET_DVAR, + /// - /// set the value of a DVAR - /// RCon password is required + /// set the value of a DVAR + /// RCon password is required /// SET_DVAR, + /// - /// execute a command - /// RCon password is required + /// execute a command + /// RCon password is required /// COMMAND, + /// - /// get the full server command information - /// RCon password is required + /// get the full server command information + /// RCon password is required /// COMMAND_STATUS } /// - /// line seperator char included in response from the server + /// line seperator char included in response from the server /// - public static char SeperatorChar = (char)int.Parse("0a", System.Globalization.NumberStyles.AllowHexSpecifier); + public static char SeperatorChar = (char)int.Parse("0a", NumberStyles.AllowHexSpecifier); + /// - /// timeout in seconds to wait for a socket send or receive before giving up + /// interval in milliseconds to wait before sending the next RCon request + /// + public static readonly int FloodProtectionInterval = 750; + + /// + /// timeout in seconds to wait for a socket send or receive before giving up /// public static TimeSpan SocketTimeout(int retryAttempt) { @@ -57,12 +69,8 @@ namespace SharedLibraryCore.RCon 2 => TimeSpan.FromMilliseconds(1000), 3 => TimeSpan.FromMilliseconds(2000), 4 => TimeSpan.FromMilliseconds(5000), - _ => TimeSpan.FromMilliseconds(10000), + _ => TimeSpan.FromMilliseconds(10000) }; } - /// - /// interval in milliseconds to wait before sending the next RCon request - /// - public static readonly int FloodProtectionInterval = 750; } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Repositories/AuditInformationRepository.cs b/SharedLibraryCore/Repositories/AuditInformationRepository.cs index 6a6548064..46a72f93d 100644 --- a/SharedLibraryCore/Repositories/AuditInformationRepository.cs +++ b/SharedLibraryCore/Repositories/AuditInformationRepository.cs @@ -1,15 +1,16 @@ -using Microsoft.EntityFrameworkCore; -using SharedLibraryCore.Dtos; -using SharedLibraryCore.Interfaces; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Data.Abstractions; +using Data.Models; +using Microsoft.EntityFrameworkCore; +using SharedLibraryCore.Dtos; +using SharedLibraryCore.Interfaces; namespace SharedLibraryCore.Repositories { /// - /// implementation if IAuditInformationRepository + /// implementation if IAuditInformationRepository /// public class AuditInformationRepository : IAuditInformationRepository { @@ -20,35 +21,35 @@ namespace SharedLibraryCore.Repositories _contextFactory = contextFactory; } - /// + /// public async Task> ListAuditInformation(PaginationRequest paginationInfo) { - await using var ctx = _contextFactory.CreateContext(enableTracking: false); + await using var ctx = _contextFactory.CreateContext(false); var iqItems = (from change in ctx.EFChangeHistory - where change.TypeOfChange != Data.Models.EFChangeHistory.ChangeType.Ban - orderby change.TimeChanged descending - join originClient in ctx.Clients - on (change.ImpersonationEntityId ?? change.OriginEntityId) equals originClient.ClientId - join targetClient in ctx.Clients - on change.TargetEntityId equals targetClient.ClientId - into targetChange - from targetClient in targetChange.DefaultIfEmpty() - select new AuditInfo() - { - Action = change.TypeOfChange.ToString(), - OriginName = originClient.CurrentAlias.Name, - OriginId = originClient.ClientId, - TargetName = targetClient == null ? "" : targetClient.CurrentAlias.Name, - TargetId = targetClient == null ? new int?() : targetClient.ClientId, - When = change.TimeChanged, - Data = change.Comment, - OldValue = change.PreviousValue, - NewValue = change.CurrentValue - }) + where change.TypeOfChange != EFChangeHistory.ChangeType.Ban + orderby change.TimeChanged descending + join originClient in ctx.Clients + on change.ImpersonationEntityId ?? change.OriginEntityId equals originClient.ClientId + join targetClient in ctx.Clients + on change.TargetEntityId equals targetClient.ClientId + into targetChange + from targetClient in targetChange.DefaultIfEmpty() + select new AuditInfo + { + Action = change.TypeOfChange.ToString(), + OriginName = originClient.CurrentAlias.Name, + OriginId = originClient.ClientId, + TargetName = targetClient == null ? "" : targetClient.CurrentAlias.Name, + TargetId = targetClient == null ? new int?() : targetClient.ClientId, + When = change.TimeChanged, + Data = change.Comment, + OldValue = change.PreviousValue, + NewValue = change.CurrentValue + }) .Skip(paginationInfo.Offset) .Take(paginationInfo.Count); return await iqItems.ToListAsync(); } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Server.cs b/SharedLibraryCore/Server.cs index 73cdd3c48..27f8bb9b4 100644 --- a/SharedLibraryCore/Server.cs +++ b/SharedLibraryCore/Server.cs @@ -4,22 +4,22 @@ using System.Linq; using System.Net; using System.Threading; using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Serilog.Context; -using SharedLibraryCore.Helpers; -using SharedLibraryCore.Dtos; -using SharedLibraryCore.Configuration; -using SharedLibraryCore.Interfaces; -using SharedLibraryCore.Database.Models; -using ILogger = Microsoft.Extensions.Logging.ILogger; using Data.Models; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Serilog.Context; +using SharedLibraryCore.Configuration; +using SharedLibraryCore.Database.Models; +using SharedLibraryCore.Dtos; +using SharedLibraryCore.Exceptions; +using SharedLibraryCore.Helpers; +using SharedLibraryCore.Interfaces; +using ILogger = Microsoft.Extensions.Logging.ILogger; namespace SharedLibraryCore { public abstract class Server : IGameServer { - protected readonly DefaultSettings DefaultSettings; public enum Game { COD = -1, @@ -36,15 +36,37 @@ namespace SharedLibraryCore CSGO = 10 } - public Server(ILogger logger, SharedLibraryCore.Interfaces.ILogger deprecatedLogger, - ServerConfiguration config, IManager mgr, IRConConnectionFactory rconConnectionFactory, + // only here for performance + private readonly bool CustomSayEnabled; + protected readonly string CustomSayName; + protected readonly DefaultSettings DefaultSettings; + protected readonly ILogger ServerLogger; + protected List BroadcastMessages; + protected int ConnectionErrors; + protected string FSGame; + protected IGameLogReaderFactory gameLogReaderFactory; + + // Info + private string hostname; + protected TimeSpan LastMessage; + protected DateTime LastPoll; + protected int NextMessage; + protected ManualResetEventSlim OnRemoteCommandResponse; + protected IRConConnectionFactory RConConnectionFactory; + +#pragma warning disable CS0612 + public Server(ILogger logger, Interfaces.ILogger deprecatedLogger, +#pragma warning restore CS0612 + ServerConfiguration config, IManager mgr, IRConConnectionFactory rconConnectionFactory, IGameLogReaderFactory gameLogReaderFactory, IServiceProvider serviceProvider) { Password = config.Password; IP = config.IPAddress; Port = config.Port; Manager = mgr; - Logger = deprecatedLogger; +#pragma warning disable CS0612 + Logger = deprecatedLogger ?? throw new ArgumentNullException(nameof(deprecatedLogger)); +#pragma warning restore CS0612 ServerConfig = config; EventProcessing = new SemaphoreSlim(1, 1); Clients = new List(new EFClient[64]); @@ -62,12 +84,70 @@ namespace SharedLibraryCore InitializeAutoMessages(); } - public long EndPoint => IPAddress.TryParse(IP, out _) - ? Convert.ToInt64($"{IP.Replace(".", "")}{Port}") + public long EndPoint => IPAddress.TryParse(IP, out _) + ? Convert.ToInt64($"{IP.Replace(".", "")}{Port}") : $"{IP.Replace(".", "")}{Port}".GetStableHashCode(); + // Objects + public IManager Manager { get; protected set; } + + [Obsolete] public Interfaces.ILogger Logger { get; } + + public ServerConfiguration ServerConfig { get; } + public List Maps { get; protected set; } = new List(); + public List Reports { get; set; } + public List ChatHistory { get; protected set; } + public Queue ClientHistory { get; } + public Game GameName { get; set; } + + public string Hostname + { + get => ServerConfig.CustomHostname ?? hostname; + protected set => hostname = value; + } + + public string Website { get; protected set; } + public string Gametype { get; set; } + + public string GametypeName => DefaultSettings.Gametypes.FirstOrDefault(gt => gt.Game == GameName)?.Gametypes + ?.FirstOrDefault(gt => gt.Name == Gametype)?.Alias ?? Gametype; + + public string GamePassword { get; protected set; } + public Map CurrentMap { get; set; } + + public int ClientNum + { + get { return Clients.ToArray().Count(p => p != null && !p.IsBot); } + } + + public int MaxClients { get; protected set; } + public List Clients { get; protected set; } + public string Password { get; } + public bool Throttled { get; protected set; } + public bool CustomCallback { get; protected set; } + public string WorkingDirectory { get; protected set; } + public IRConConnection RemoteConnection { get; protected set; } + public IRConParser RconParser { get; set; } + public IEventParser EventParser { get; set; } + public string LogPath { get; protected set; } + public bool RestartRequested { get; set; } + public SemaphoreSlim EventProcessing { get; } + + // Internal /// - /// Returns list of all current players + /// this is actually the hostname now + /// + public string IP { get; protected set; } + + public IPEndPoint ResolvedIpEndPoint { get; protected set; } + public string Version { get; protected set; } + public bool IsInitialized { get; set; } + + public int Port { get; } + public abstract Task Kick(string reason, EFClient target, EFClient origin, EFPenalty originalPenalty); + + /// + /// Returns list of all current players /// /// public List GetClientsAsList() @@ -76,26 +156,26 @@ namespace SharedLibraryCore } /// - /// Add a player to the server's player list + /// Add a player to the server's player list /// /// EFClient pulled from memory reading /// True if player added sucessfully, false otherwise public abstract Task OnClientConnected(EFClient P); /// - /// Remove player by client number + /// Remove player by client number /// /// Client ID of player to be removed /// true if removal succeded, false otherwise public abstract Task OnClientDisconnected(EFClient client); /// - /// Get a player by name - /// todo: make this an extension + /// Get a player by name + /// todo: make this an extension /// /// EFClient name to search for /// Matching player if found - public List GetClientByName(String pName) + public List GetClientByName(string pName) { if (string.IsNullOrEmpty(pName)) { @@ -104,33 +184,39 @@ namespace SharedLibraryCore pName = pName.Trim().StripColors(); - string[] QuoteSplit = pName.Split('"'); - bool literal = false; + var QuoteSplit = pName.Split('"'); + var literal = false; if (QuoteSplit.Length > 1) { pName = QuoteSplit[1]; literal = true; } + if (literal) { return GetClientsAsList().Where(p => p.Name?.StripColors()?.ToLower() == pName.ToLower()).ToList(); } - return GetClientsAsList().Where(p => (p.Name?.StripColors()?.ToLower() ?? "").Contains(pName.ToLower())).ToList(); + return GetClientsAsList().Where(p => (p.Name?.StripColors()?.ToLower() ?? "").Contains(pName.ToLower())) + .ToList(); } - virtual public Task ProcessUpdatesAsync(CancellationToken cts) => (Task)Task.CompletedTask; + public virtual Task ProcessUpdatesAsync(CancellationToken cts) + { + return (Task)Task.CompletedTask; + } /// - /// Process any server event + /// Process any server event /// /// Event /// True on sucess protected abstract Task ProcessEvent(GameEvent E); + public abstract Task ExecuteEvent(GameEvent E); /// - /// Send a message to all players + /// Send a message to all players /// /// Message to be sent to all players public GameEvent Broadcast(string message, EFClient sender = null) @@ -145,13 +231,13 @@ namespace SharedLibraryCore Type = GameEvent.EventType.Broadcast, Data = formattedMessage, Owner = this, - Origin = sender, + Origin = sender }; Manager.AddEvent(e); return e; } - + public void Broadcast(IEnumerable messages, EFClient sender = null) { foreach (var message in messages) @@ -161,17 +247,17 @@ namespace SharedLibraryCore #pragma warning restore 4014 } } - + /// - /// Send a message to a particular players + /// Send a message to a particular players /// /// Message to send /// EFClient to send message to protected async Task Tell(string message, EFClient targetClient) { var engineMessage = message.FormatMessageForEngine(RconParser.Configuration.ColorCodeMapping); - + if (!Utilities.IsDevelopment) { var temporalClientId = targetClient.GetAdditionalProperty("ConnectionClientId"); @@ -181,8 +267,11 @@ namespace SharedLibraryCore var formattedMessage = string.Format(RconParser.Configuration.CommandPrefixes.Tell, clientNumber, $"{(CustomSayEnabled && GameName == Game.IW4 ? $"{CustomSayName}: " : "")}{engineMessage}"); - if (targetClient.ClientNumber > -1 && message.Length > 0 && targetClient.Level != EFClient.Permission.Console) + if (targetClient.ClientNumber > -1 && message.Length > 0 && + targetClient.Level != Data.Models.Client.EFClient.Permission.Console) + { await this.ExecuteCommandAsync(formattedMessage); + } } else { @@ -190,7 +279,7 @@ namespace SharedLibraryCore message.FormatMessageForEngine(RconParser.Configuration.ColorCodeMapping).StripColors()); } - if (targetClient.Level == EFClient.Permission.Console) + if (targetClient.Level == Data.Models.Client.EFClient.Permission.Console) { Console.ForegroundColor = ConsoleColor.Green; using (LogContext.PushProperty("Server", ToString())) @@ -198,58 +287,60 @@ namespace SharedLibraryCore ServerLogger.LogInformation("Command output received: {Message}", engineMessage.StripColors()); } + Console.WriteLine(engineMessage.StripColors()); Console.ForegroundColor = ConsoleColor.Gray; } } /// - /// Send a message to all admins on the server + /// Send a message to all admins on the server /// /// Message to send out - public void ToAdmins(String message) + public void ToAdmins(string message) { - foreach (var client in GetClientsAsList().Where(c => c.Level > EFClient.Permission.Flagged)) - { + foreach (var client in GetClientsAsList() + .Where(c => c.Level > Data.Models.Client.EFClient.Permission.Flagged)) client.Tell(message); - } } /// - /// Kick a player from the server + /// Kick a player from the server /// /// Reason for kicking /// EFClient to kick - public Task Kick(String reason, EFClient Target, EFClient Origin) => Kick(reason, Target, Origin, null); - public abstract Task Kick(string reason, EFClient target, EFClient origin, EFPenalty originalPenalty); + public Task Kick(string reason, EFClient Target, EFClient Origin) + { + return Kick(reason, Target, Origin, null); + } /// - /// Temporarily ban a player ( default 1 hour ) from the server + /// Temporarily ban a player ( default 1 hour ) from the server /// /// Reason for banning the player /// The player to ban - abstract public Task TempBan(String reason, TimeSpan length, EFClient Target, EFClient Origin); + public abstract Task TempBan(string reason, TimeSpan length, EFClient Target, EFClient Origin); /// - /// Perm ban a player from the server + /// Perm ban a player from the server /// /// The reason for the ban /// The person to ban /// The person who banned the target - abstract public Task Ban(String Reason, EFClient Target, EFClient Origin, bool isEvade = false); + public abstract Task Ban(string Reason, EFClient Target, EFClient Origin, bool isEvade = false); - abstract public Task Warn(String Reason, EFClient Target, EFClient Origin); + public abstract Task Warn(string Reason, EFClient Target, EFClient Origin); /// - /// Unban a player by npID / GUID + /// Unban a player by npID / GUID /// /// npID of the player /// I don't remember what this is for /// - abstract public Task Unban(string reason, EFClient targetClient, EFClient originClient); + public abstract Task Unban(string reason, EFClient targetClient, EFClient originClient); /// - /// Change the current searver map + /// Change the current searver map /// /// Non-localized map name public async Task LoadMap(string mapName) @@ -258,19 +349,22 @@ namespace SharedLibraryCore } /// - /// Initalize the macro variables + /// Initalize the macro variables /// - abstract public void InitializeTokens(); + public abstract void InitializeTokens(); /// - /// Initialize the messages to be broadcasted + /// Initialize the messages to be broadcasted /// protected void InitializeAutoMessages() { - BroadcastMessages = new List(); + BroadcastMessages = new List(); if (ServerConfig.AutoMessages != null) + { BroadcastMessages.AddRange(ServerConfig.AutoMessages); + } + BroadcastMessages.AddRange(Manager.GetApplicationSettings().Configuration().AutoMessages); } @@ -286,77 +380,12 @@ namespace SharedLibraryCore return (await this.GetDvarAsync("sv_customcallbacks", "0")).Value == "1"; } - catch (Exceptions.DvarException) + catch (DvarException) { return false; } } public abstract Task GetIdForServer(Server server = null); - - // Objects - public IManager Manager { get; protected set; } - [Obsolete] - public SharedLibraryCore.Interfaces.ILogger Logger { get; private set; } - public ServerConfiguration ServerConfig { get; private set; } - public List Maps { get; protected set; } = new List(); - public List Reports { get; set; } - public List ChatHistory { get; protected set; } - public Queue ClientHistory { get; private set; } - public Game GameName { get; set; } - - // Info - private string hostname; - public string Hostname { get => ServerConfig.CustomHostname ?? hostname; protected set => hostname = value; } - public string Website { get; protected set; } - public string Gametype { get; set; } - public string GametypeName => DefaultSettings.Gametypes.FirstOrDefault(gt => gt.Game == GameName)?.Gametypes - ?.FirstOrDefault(gt => gt.Name == Gametype)?.Alias ?? Gametype; - public string GamePassword { get; protected set; } - public Map CurrentMap { get; set; } - public int ClientNum - { - get - { - return Clients.ToArray().Count(p => p != null && !p.IsBot); - } - } - public int MaxClients { get; protected set; } - public List Clients { get; protected set; } - public string Password { get; private set; } - public bool Throttled { get; protected set; } - public bool CustomCallback { get; protected set; } - public string WorkingDirectory { get; protected set; } - public IRConConnection RemoteConnection { get; protected set; } - public IRConParser RconParser { get; set; } - public IEventParser EventParser { get; set; } - public string LogPath { get; protected set; } - public bool RestartRequested { get; set; } - public SemaphoreSlim EventProcessing { get; private set; } - - // Internal - /// - /// this is actually the hostname now - /// - public string IP { get; protected set; } - public IPEndPoint ResolvedIpEndPoint { get; protected set; } - public string Version { get; protected set; } - public bool IsInitialized { get; set; } - protected readonly ILogger ServerLogger; - - public int Port { get; private set; } - protected string FSGame; - protected int NextMessage; - protected int ConnectionErrors; - protected List BroadcastMessages; - protected TimeSpan LastMessage; - protected DateTime LastPoll; - protected ManualResetEventSlim OnRemoteCommandResponse; - protected IGameLogReaderFactory gameLogReaderFactory; - protected IRConConnectionFactory RConConnectionFactory; - - // only here for performance - private readonly bool CustomSayEnabled; - protected readonly string CustomSayName; } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Services/ChangeHistoryService.cs b/SharedLibraryCore/Services/ChangeHistoryService.cs index 3f6580ec6..85d651916 100644 --- a/SharedLibraryCore/Services/ChangeHistoryService.cs +++ b/SharedLibraryCore/Services/ChangeHistoryService.cs @@ -1,19 +1,18 @@ -using SharedLibraryCore.Database.Models; -using System; +using System; using System.Linq; using System.Threading.Tasks; using Data.Abstractions; using Data.Models; +using Data.Models.Client; using Microsoft.Extensions.Logging; -using ILogger = Microsoft.Extensions.Logging.ILogger; namespace SharedLibraryCore.Services { public class ChangeHistoryService { - private readonly ILogger _logger; private readonly IDatabaseContextFactory _contextFactory; - + private readonly ILogger _logger; + public ChangeHistoryService(ILogger logger, IDatabaseContextFactory contextFactory) { _logger = logger; @@ -27,7 +26,7 @@ namespace SharedLibraryCore.Services switch (e.Type) { case GameEvent.EventType.Ban: - change = new EFChangeHistory() + change = new EFChangeHistory { OriginEntityId = e.Origin.ClientId, TargetEntityId = e.Target.ClientId, @@ -42,10 +41,12 @@ namespace SharedLibraryCore.Services { if (cmd.Name == "login" || cmd.Name == "setpassword") { - e.Message = string.Join(' ', e.Message.Split(" ").Select((arg, index) => index > 0 ? "*****" : arg)); + e.Message = string.Join(' ', + e.Message.Split(" ").Select((arg, index) => index > 0 ? "*****" : arg)); } } - change = new EFChangeHistory() + + change = new EFChangeHistory { OriginEntityId = e.Origin.ClientId, TargetEntityId = e.Target?.ClientId ?? 0, @@ -56,7 +57,7 @@ namespace SharedLibraryCore.Services }; break; case GameEvent.EventType.ChangePermission: - change = new EFChangeHistory() + change = new EFChangeHistory { OriginEntityId = e.Origin.ClientId, TargetEntityId = e.Target.ClientId, @@ -67,7 +68,7 @@ namespace SharedLibraryCore.Services }; break; case GameEvent.EventType.Login: - change = new EFChangeHistory() + change = new EFChangeHistory { OriginEntityId = e.Origin.ClientId, Comment = "Logged In To Webfront", @@ -76,7 +77,7 @@ namespace SharedLibraryCore.Services }; break; case GameEvent.EventType.Logout: - change = new EFChangeHistory() + change = new EFChangeHistory { OriginEntityId = e.Origin.ClientId, Comment = "Logged Out of Webfront", @@ -106,4 +107,4 @@ namespace SharedLibraryCore.Services } } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Services/ClientService.cs b/SharedLibraryCore/Services/ClientService.cs index 5cea8694c..c5eedfade 100644 --- a/SharedLibraryCore/Services/ClientService.cs +++ b/SharedLibraryCore/Services/ClientService.cs @@ -1,31 +1,53 @@ -using Microsoft.EntityFrameworkCore; -using SharedLibraryCore.Database.Models; -using SharedLibraryCore.Dtos; -using SharedLibraryCore.Helpers; -using SharedLibraryCore.Interfaces; -using System; +using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; using Data.Abstractions; using Data.Context; +using Data.Models; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Serilog.Context; +using SharedLibraryCore.Configuration; +using SharedLibraryCore.Database.Models; +using SharedLibraryCore.Dtos; +using SharedLibraryCore.Helpers; +using SharedLibraryCore.Interfaces; using static Data.Models.Client.EFClient; using ILogger = Microsoft.Extensions.Logging.ILogger; -using Data.Models; -using SharedLibraryCore.Configuration; namespace SharedLibraryCore.Services { public class ClientService : IEntityService, IResourceQueryHelper { + private static readonly Func> _getUniqueQuery = + EF.CompileAsyncQuery((DatabaseContext context, long networkId) => + context.Clients + .Select(_client => new EFClient + { + ClientId = _client.ClientId, + AliasLinkId = _client.AliasLinkId, + Level = _client.Level, + Connections = _client.Connections, + FirstConnection = _client.FirstConnection, + LastConnection = _client.LastConnection, + Masked = _client.Masked, + NetworkId = _client.NetworkId, + TotalConnectionTime = _client.TotalConnectionTime, + AliasLink = _client.AliasLink, + Password = _client.Password, + PasswordSalt = _client.PasswordSalt + }) + .FirstOrDefault(c => c.NetworkId == networkId) + ); + + private readonly ApplicationConfiguration _appConfig; private readonly IDatabaseContextFactory _contextFactory; private readonly ILogger _logger; - private readonly ApplicationConfiguration _appConfig; - public ClientService(ILogger logger, IDatabaseContextFactory databaseContextFactory, + public ClientService(ILogger logger, IDatabaseContextFactory databaseContextFactory, ApplicationConfiguration appConfig) { _contextFactory = databaseContextFactory; @@ -36,13 +58,13 @@ namespace SharedLibraryCore.Services public async Task 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); + + await using var context = _contextFactory.CreateContext(); using (LogContext.PushProperty("Server", entity?.CurrentServer?.ToString())) { int? linkId = null; @@ -59,19 +81,21 @@ namespace SharedLibraryCore.Services { linkId = existingAliases.OrderBy(_alias => _alias.LinkId).First().LinkId; - _logger.LogDebug("[create] client with new GUID {entity} has existing link {linkId}", entity.ToString(), 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); + _logger.LogDebug("[create] client with new GUID {entity} has existing alias {aliasId}", + entity.ToString(), existingExactAlias.AliasId); aliasId = existingExactAlias.AliasId; } } } - var client = new EFClient() + var client = new EFClient { Level = Permission.User, FirstConnection = DateTime.UtcNow, @@ -80,12 +104,13 @@ namespace SharedLibraryCore.Services }; _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); + _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; } @@ -93,9 +118,10 @@ namespace SharedLibraryCore.Services // 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); + _logger.LogDebug("[create] setting {entity}'s linkid to {linkId}, but creating new alias", + entity.ToString(), linkId); client.AliasLinkId = linkId.Value; - client.CurrentAlias = new EFAlias() + client.CurrentAlias = new EFAlias { Name = entity.Name, SearchableName = entity.Name.StripColors().ToLower(), @@ -110,7 +136,7 @@ namespace SharedLibraryCore.Services { _logger.LogDebug("[create] creating new Link and Alias for {entity}", entity.ToString()); var link = new EFAliasLink(); - var alias = new EFAlias() + var alias = new EFAlias { Name = entity.Name, SearchableName = entity.Name.StripColors().ToLower(), @@ -130,14 +156,200 @@ namespace SharedLibraryCore.Services } } + public Task Delete(EFClient entity) + { + throw new NotImplementedException(); + } + + public Task> Find(Func e) + { + throw new NotImplementedException(); + } + + public async Task Get(int entityId) + { + // todo: this needs to be optimized for large linked accounts + await using var context = _contextFactory.CreateContext(false); + + var client = context.Clients + .Select(_client => new EFClient + { + ClientId = _client.ClientId, + AliasLinkId = _client.AliasLinkId, + Level = _client.Level, + Connections = _client.Connections, + FirstConnection = _client.FirstConnection, + LastConnection = _client.LastConnection, + Masked = _client.Masked, + NetworkId = _client.NetworkId, + CurrentAlias = new EFAlias + { + Name = _client.CurrentAlias.Name, + IPAddress = _client.CurrentAlias.IPAddress + }, + TotalConnectionTime = _client.TotalConnectionTime + }) + .FirstOrDefault(_client => _client.ClientId == entityId); + + if (client == null) + { + return null; + } + + client.AliasLink = new EFAliasLink + { + AliasLinkId = client.AliasLinkId, + Children = await context.Aliases + .Where(_alias => _alias.LinkId == client.AliasLinkId) + .Select(_alias => new EFAlias + { + Name = _alias.Name, + IPAddress = _alias.IPAddress + }).ToListAsync() + }; + + var foundClient = new + { + Client = client, + LinkedAccounts = await context.Clients.Where(_client => _client.AliasLinkId == client.AliasLinkId) + .Select(_linkedClient => new + { + _linkedClient.ClientId, + _linkedClient.NetworkId + }) + .ToListAsync() + }; + + if (foundClient == null) + { + return null; + } + + foundClient.Client.LinkedAccounts = new Dictionary(); + // todo: find out the best way to do this + // I'm doing this here because I don't know the best way to have multiple awaits in the query + foreach (var linked in foundClient.LinkedAccounts) + foundClient.Client.LinkedAccounts.Add(linked.ClientId, linked.NetworkId); + + return foundClient.Client; + } + + public virtual async Task GetUnique(long entityAttribute) + { + await using var context = _contextFactory.CreateContext(false); + return await _getUniqueQuery(context, entityAttribute); + } + + public async Task Update(EFClient temporalClient) + { + if (temporalClient.ClientId < 1) + { + _logger.LogDebug( + "[update] {client} needs to be updated but they do not have a valid client id, ignoring..", + temporalClient.ToString()); + // note: we never do anything with the result of this so we can safely return null + return null; + } + + await using var context = _contextFactory.CreateContext(); + + // grab the context version of the entity + var entity = context.Clients + .First(client => client.ClientId == temporalClient.ClientId); + + if (temporalClient.LastConnection > entity.LastConnection) + { + entity.LastConnection = temporalClient.LastConnection; + } + + if (temporalClient.Connections > entity.Connections) + { + entity.Connections = temporalClient.Connections; + } + + entity.Masked = temporalClient.Masked; + + if (temporalClient.TotalConnectionTime > entity.TotalConnectionTime) + { + entity.TotalConnectionTime = temporalClient.TotalConnectionTime; + } + + if (temporalClient.Password != null) + { + entity.Password = temporalClient.Password; + } + + if (temporalClient.PasswordSalt != null) + { + entity.PasswordSalt = temporalClient.PasswordSalt; + } + + // update in database + await context.SaveChangesAsync(); + return entity.ToPartialClient(); + } + + /// + /// find clients matching the given query + /// + /// query filters + /// + public async Task> QueryResource(FindClientRequest query) + { + var result = new ResourceQueryHelperResult(); + await using var context = _contextFactory.CreateContext(false); + + IQueryable iqClients = null; + + if (!string.IsNullOrEmpty(query.Xuid)) + { + var networkId = query.Xuid.ConvertGuidToLong(NumberStyles.HexNumber); + iqClients = context.Clients.Where(_client => _client.NetworkId == networkId); + } + + else if (!string.IsNullOrEmpty(query.Name)) + { + iqClients = context.Clients + .Where(_client => + EF.Functions.Like(_client.CurrentAlias.Name.ToLower(), $"%{query.Name.ToLower()}%")); + } + + if (query.Direction == SortDirection.Ascending) + { + iqClients = iqClients.OrderBy(_client => _client.LastConnection); + } + + else + { + iqClients = iqClients.OrderByDescending(_client => _client.LastConnection); + } + + var queryResults = await iqClients + .Select(_client => new FindClientResult + { + ClientId = _client.ClientId, + Xuid = _client.NetworkId.ToString("X"), + Name = _client.CurrentAlias.Name + }) + .Skip(query.Offset) + .Take(query.Count) + .ToListAsync(); + + result.TotalResultCount = await iqClients.CountAsync(); + result.Results = queryResults; + result.RetrievedResultCount = queryResults.Count; + + return result; + } + private async Task HandleNewCreate(EFClient entity) { - await using var context = _contextFactory.CreateContext(true); + await using var context = _contextFactory.CreateContext(); 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 && + .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(); @@ -148,10 +360,11 @@ namespace SharedLibraryCore.Services LastConnection = DateTime.UtcNow, NetworkId = entity.NetworkId }; - + if (existingAlias == null) { - _logger.LogDebug("[{Method}] creating new Link and Alias for {Entity}", nameof(HandleNewCreate), entity.ToString()); + _logger.LogDebug("[{Method}] creating new Link and Alias for {Entity}", nameof(HandleNewCreate), + entity.ToString()); var link = new EFAliasLink(); var alias = new EFAlias { @@ -167,9 +380,10 @@ namespace SharedLibraryCore.Services else { - _logger.LogDebug("[{Method}] associating new GUID {Guid} with new exact alias match with linkId {LinkId} for {Entity}", + _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, @@ -181,24 +395,25 @@ namespace SharedLibraryCore.Services client.CurrentAlias = alias; client.AliasLinkId = existingAlias.LinkId; } - + context.Clients.Add(client); await context.SaveChangesAsync(); return client; } } - private async Task UpdateAlias(string originalName, int? ip, Data.Models.Client.EFClient entity, DatabaseContext context) + private async Task UpdateAlias(string originalName, int? ip, Data.Models.Client.EFClient entity, + DatabaseContext context) { { - string name = originalName.CapClientName(EFAlias.MAX_NAME_LENGTH); + var 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)); + .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(); @@ -209,7 +424,7 @@ namespace SharedLibraryCore.Services // 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; + var hasExactAliasMatch = existingExactAlias != null; // if existing alias matches link them var newAliasLink = existingExactAlias?.Link; @@ -218,11 +433,11 @@ namespace SharedLibraryCore.Services // 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; + var hasExistingAlias = aliases.Count > 0; + var isAliasLinkUpdated = newAliasLink.AliasLinkId != entity.AliasLink.AliasLinkId; await context.SaveChangesAsync(); - int distinctLinkCount = aliases.Select(_alias => _alias.LinkId).Distinct().Count(); + var 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) { @@ -308,14 +523,14 @@ namespace SharedLibraryCore.Services { _logger.LogDebug("[updatealias] {entity} is using a new alias", entity.ToString()); - var newAlias = new EFAlias() + var newAlias = new EFAlias { DateAdded = DateTime.UtcNow, IPAddress = ip, LinkId = newAliasLink.AliasLinkId, Name = name, SearchableName = name.StripColors().ToLower(), - Active = true, + Active = true }; entity.CurrentAlias = newAlias; @@ -329,9 +544,9 @@ namespace SharedLibraryCore.Services DatabaseContext context) { var name = originalName.CapClientName(EFAlias.MAX_NAME_LENGTH); - + var existingAliases = await context.Aliases - .Where(alias => alias.Name == name && alias.LinkId == entity.AliasLinkId || + .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); @@ -352,20 +567,22 @@ namespace SharedLibraryCore.Services 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()); + _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() + var newAlias = new EFAlias { DateAdded = DateTime.UtcNow, IPAddress = ip, LinkId = entity.AliasLinkId, Name = name, SearchableName = name.StripColors().ToLower(), - Active = true, + Active = true }; entity.CurrentAlias = newAlias; @@ -374,7 +591,7 @@ namespace SharedLibraryCore.Services } /// - /// updates the permission level of the given target to the given permission level + /// updates the permission level of the given target to the given permission level /// /// /// @@ -383,7 +600,7 @@ namespace SharedLibraryCore.Services /// public virtual async Task UpdateLevel(Permission newPermission, EFClient temporalClient, EFClient origin) { - await using var ctx = _contextFactory.CreateContext(true); + await using var ctx = _contextFactory.CreateContext(); var entity = await ctx.Clients .Where(_client => _client.ClientId == temporalClient.ClientId) .FirstAsync(); @@ -397,7 +614,7 @@ namespace SharedLibraryCore.Services { _logger.LogInformation("Updated {clientId} to {newPermission}", temporalClient.ClientId, newPermission); - var linkedPermissionSet = new[] {Permission.Banned, Permission.Flagged}; + var linkedPermissionSet = new[] { Permission.Banned, Permission.Flagged }; // if their permission level has been changed to level that needs to be updated on all accounts if (linkedPermissionSet.Contains(newPermission) || linkedPermissionSet.Contains(oldPermission)) { @@ -431,116 +648,9 @@ namespace SharedLibraryCore.Services temporalClient.Level = newPermission; } - public async Task Delete(EFClient entity) - { - throw new NotImplementedException(); - } - - public Task> Find(Func e) - { - throw new NotImplementedException(); - } - - public async Task Get(int entityId) - { - // todo: this needs to be optimized for large linked accounts - await using var context = _contextFactory.CreateContext(false); - - var client = context.Clients - .Select(_client => new EFClient() - { - ClientId = _client.ClientId, - AliasLinkId = _client.AliasLinkId, - Level = _client.Level, - Connections = _client.Connections, - FirstConnection = _client.FirstConnection, - LastConnection = _client.LastConnection, - Masked = _client.Masked, - NetworkId = _client.NetworkId, - CurrentAlias = new EFAlias() - { - Name = _client.CurrentAlias.Name, - IPAddress = _client.CurrentAlias.IPAddress - }, - TotalConnectionTime = _client.TotalConnectionTime - }) - .FirstOrDefault(_client => _client.ClientId == entityId); - - if (client == null) - { - return null; - } - - client.AliasLink = new EFAliasLink() - { - AliasLinkId = client.AliasLinkId, - Children = await context.Aliases - .Where(_alias => _alias.LinkId == client.AliasLinkId) - .Select(_alias => new EFAlias() - { - Name = _alias.Name, - IPAddress = _alias.IPAddress - }).ToListAsync() - }; - - var foundClient = new - { - Client = client, - LinkedAccounts = await context.Clients.Where(_client => _client.AliasLinkId == client.AliasLinkId) - .Select(_linkedClient => new - { - _linkedClient.ClientId, - _linkedClient.NetworkId - }) - .ToListAsync() - }; - - if (foundClient == null) - { - return null; - } - - foundClient.Client.LinkedAccounts = new Dictionary(); - // todo: find out the best way to do this - // I'm doing this here because I don't know the best way to have multiple awaits in the query - foreach (var linked in foundClient.LinkedAccounts) - { - foundClient.Client.LinkedAccounts.Add(linked.ClientId, linked.NetworkId); - } - - return foundClient.Client; - } - - private static readonly Func> _getUniqueQuery = - EF.CompileAsyncQuery((DatabaseContext context, long networkId) => - context.Clients - .Select(_client => new EFClient() - { - ClientId = _client.ClientId, - AliasLinkId = _client.AliasLinkId, - Level = _client.Level, - Connections = _client.Connections, - FirstConnection = _client.FirstConnection, - LastConnection = _client.LastConnection, - Masked = _client.Masked, - NetworkId = _client.NetworkId, - TotalConnectionTime = _client.TotalConnectionTime, - AliasLink = _client.AliasLink, - Password = _client.Password, - PasswordSalt = _client.PasswordSalt - }) - .FirstOrDefault(c => c.NetworkId == networkId) - ); - - public virtual async Task GetUnique(long entityAttribute) - { - await using var context = _contextFactory.CreateContext(false); - return await _getUniqueQuery(context, entityAttribute); - } - public async Task UpdateAlias(EFClient temporalClient) { - await using var context = _contextFactory.CreateContext(enableTracking:true); + await using var context = _contextFactory.CreateContext(); var entity = context.Clients .Include(c => c.AliasLink) @@ -563,237 +673,8 @@ namespace SharedLibraryCore.Services temporalClient.AliasLinkId = entity.AliasLinkId; } - public async Task Update(EFClient temporalClient) - { - if (temporalClient.ClientId < 1) - { - _logger.LogDebug("[update] {client} needs to be updated but they do not have a valid client id, ignoring..", temporalClient.ToString()); - // note: we never do anything with the result of this so we can safely return null - return null; - } - - await using var context = _contextFactory.CreateContext(); - - // grab the context version of the entity - var entity = context.Clients - .First(client => client.ClientId == temporalClient.ClientId); - - if (temporalClient.LastConnection > entity.LastConnection) - { - entity.LastConnection = temporalClient.LastConnection; - } - - if (temporalClient.Connections > entity.Connections) - { - entity.Connections = temporalClient.Connections; - } - - entity.Masked = temporalClient.Masked; - - if (temporalClient.TotalConnectionTime > entity.TotalConnectionTime) - { - entity.TotalConnectionTime = temporalClient.TotalConnectionTime; - } - - if (temporalClient.Password != null) - { - entity.Password = temporalClient.Password; - } - - if (temporalClient.PasswordSalt != null) - { - entity.PasswordSalt = temporalClient.PasswordSalt; - } - - // update in database - await context.SaveChangesAsync(); - return entity.ToPartialClient(); - } - - #region ServiceSpecific - public async Task> GetOwners() - { - await using var context = _contextFactory.CreateContext(false); - return await context.Clients - .Where(c => c.Level == Permission.Owner) - .Select(c => c.ToPartialClient()) - .ToListAsync(); - } - - public async Task HasOwnerAsync(CancellationToken token) - { - await using var context = _contextFactory.CreateContext(false); - return await context.Clients.AnyAsync(client => client.Level == Permission.Owner, token); - } - /// - /// retrieves the number of owners - /// (client level is owner) - /// - /// - public virtual async Task GetOwnerCount() - { - await using var context = _contextFactory.CreateContext(false); - return await context.Clients - .CountAsync(_client => _client.Level == Permission.Owner); - } - - public async Task GetClientForLogin(int clientId) - { - await using var context = _contextFactory.CreateContext(false); - return await context.Clients - .Select(_client => new EFClient() - { - NetworkId = _client.NetworkId, - ClientId = _client.ClientId, - CurrentAlias = new EFAlias() - { - Name = _client.CurrentAlias.Name - }, - Password = _client.Password, - PasswordSalt = _client.PasswordSalt, - Level = _client.Level - }) - .FirstAsync(_client => _client.ClientId == clientId); - } - - public async Task> GetPrivilegedClients(bool includeName = true) - { - await using var context = _contextFactory.CreateContext(false); - - var iqClients = from client in context.Clients.AsNoTracking() - where client.Level >= Permission.Trusted - where client.Active - select new EFClient() - { - AliasLinkId = client.AliasLinkId, - CurrentAlias = client.CurrentAlias, - ClientId = client.ClientId, - Level = client.Level, - Password = client.Password, - PasswordSalt = client.PasswordSalt, - NetworkId = client.NetworkId, - LastConnection = client.LastConnection - }; - - return await iqClients.ToListAsync(); - } - - public async Task> FindClientsByIdentifier(string identifier) - { - var trimmedIdentifier = identifier?.Trim(); - if (trimmedIdentifier?.Length < _appConfig.MinimumNameLength) - { - return new List(); - } - - await using var context = _contextFactory.CreateContext(false); - long? networkId = null; - try - { - networkId = trimmedIdentifier.ConvertGuidToLong(System.Globalization.NumberStyles.HexNumber); - } - catch { } - - int? ipAddress = trimmedIdentifier.ConvertToIP(); - - IQueryable iqLinkIds = context.Aliases.Where(_alias => _alias.Active); - - // we want to query for the IP ADdress - if (ipAddress != null) - { - iqLinkIds = iqLinkIds.Where(_alias => _alias.IPAddress == ipAddress); - } - - // want to find them by name (wildcard) - else - { - iqLinkIds = iqLinkIds.Where(_alias => EF.Functions.Like((_alias.SearchableName ?? _alias.Name.ToLower()), $"%{trimmedIdentifier.ToLower()}%")); - } - - var linkIds = await iqLinkIds - .Select(_alias => _alias.LinkId) - .ToListAsync(); - - // get all the clients that match the alias link or the network id - var iqClients = context.Clients - .Where(_client => _client.Active); - - - iqClients = iqClients.Where(_client => networkId == _client.NetworkId || linkIds.Contains(_client.AliasLinkId) - || !_appConfig.EnableImplicitAccountLinking && _client.CurrentAlias.IPAddress != null && _client.CurrentAlias.IPAddress == ipAddress); - - // we want to project our results - var iqClientProjection = iqClients.OrderByDescending(_client => _client.LastConnection) - .Select(_client => new PlayerInfo - { - Name = _client.CurrentAlias.Name, - LevelInt = (int) _client.Level, - LastConnection = _client.LastConnection, - ClientId = _client.ClientId, - IPAddress = _client.CurrentAlias.IPAddress.HasValue - ? _client.CurrentAlias.IPAddress.Value.ToString() - : "" - }); - - var clients = await iqClientProjection.ToListAsync(); - - // this is so we don't try to evaluate this in the linq to entities query - foreach (var client in clients) - { - client.Level = ((Permission)client.LevelInt).ToLocalizedLevelName(); - } - - return clients; - } - - public async Task GetTotalClientsAsync() - { - await using var context = _contextFactory.CreateContext(false); - return await context.Clients - .CountAsync(); - } - - /// - /// Returns the number of clients seen today - /// - /// - public async Task GetRecentClientCount() - { - await using var context = _contextFactory.CreateContext(false); - var startOfPeriod = DateTime.UtcNow.AddHours(-24); - var iqQuery = context.Clients.Where(_client => _client.LastConnection >= startOfPeriod); - - return await iqQuery.CountAsync(); - } - - /// - /// gets the 10 most recently added clients to IW4MAdmin - /// - /// - public async Task> GetRecentClients() - { - var startOfPeriod = DateTime.UtcNow.AddHours(-24); - - await using var context = _contextFactory.CreateContext(false); - var iqClients = context.Clients - .Where(_client => _client.CurrentAlias.IPAddress != null) - .Where(_client => _client.FirstConnection >= startOfPeriod) - .OrderByDescending(_client => _client.FirstConnection) - .Select(_client => new PlayerInfo() - { - ClientId = _client.ClientId, - Name = _client.CurrentAlias.Name, - IPAddress = _client.CurrentAlias.IPAddress.ConvertIPtoString(), - LastConnection = _client.FirstConnection - }); - - return await iqClients.ToListAsync(); - } - #endregion - - /// - /// retrieves the number of times the given client id has been reported + /// retrieves the number of times the given client id has been reported /// /// client id to search for report counts of /// @@ -808,7 +689,7 @@ namespace SharedLibraryCore.Services } /// - /// indicates if the given clientid can be autoflagged + /// indicates if the given clientid can be autoflagged /// /// /// @@ -817,7 +698,7 @@ namespace SharedLibraryCore.Services await using var context = _contextFactory.CreateContext(false); var now = DateTime.UtcNow; - var hasExistingAutoFlag = await context.Penalties + var hasExistingAutoFlag = await context.Penalties .Where(_penalty => _penalty.Active) .Where(_penalty => _penalty.OffenderId == clientId) .Where(_penalty => _penalty.Type == EFPenalty.PenaltyType.Flag) @@ -825,24 +706,24 @@ namespace SharedLibraryCore.Services .Where(_penalty => _penalty.Expires == null || _penalty.Expires > now) .AnyAsync(); - var hasUnflag = await context.Penalties - .Where(_penalty => _penalty.Active) - .Where(_penalty => _penalty.OffenderId == clientId) - .Where(_penalty => _penalty.Type == EFPenalty.PenaltyType.Unflag) - .AnyAsync(); + var hasUnflag = await context.Penalties + .Where(_penalty => _penalty.Active) + .Where(_penalty => _penalty.OffenderId == clientId) + .Where(_penalty => _penalty.Type == EFPenalty.PenaltyType.Unflag) + .AnyAsync(); - return !hasExistingAutoFlag && !hasUnflag; + return !hasExistingAutoFlag && !hasUnflag; } /// - /// Unlinks shared GUID account into its own separate account + /// Unlinks shared GUID account into its own separate account /// /// /// public async Task UnlinkClient(int clientId) { await using var ctx = _contextFactory.CreateContext(); - var newLink = new EFAliasLink() { Active = true }; + var newLink = new EFAliasLink { Active = true }; ctx.AliasLinks.Add(newLink); await ctx.SaveChangesAsync(); @@ -851,7 +732,8 @@ namespace SharedLibraryCore.Services client.AliasLinkId = newLink.AliasLinkId; client.Level = Permission.User; - await ctx.Aliases.Where(_alias => _alias.IPAddress == client.CurrentAlias.IPAddress && _alias.IPAddress != null) + await ctx.Aliases.Where(_alias => + _alias.IPAddress == client.CurrentAlias.IPAddress && _alias.IPAddress != null) .ForEachAsync(_alias => _alias.LinkId = newLink.AliasLinkId); if (!_appConfig.EnableImplicitAccountLinking) @@ -875,57 +757,192 @@ namespace SharedLibraryCore.Services await ctx.SaveChangesAsync(); } - /// - /// find clients matching the given query - /// - /// query filters - /// - public async Task> QueryResource(FindClientRequest query) + #region ServiceSpecific + + public async Task> GetOwners() { - var result = new ResourceQueryHelperResult(); - await using var context = _contextFactory.CreateContext(enableTracking: false); + await using var context = _contextFactory.CreateContext(false); + return await context.Clients + .Where(c => c.Level == Permission.Owner) + .Select(c => c.ToPartialClient()) + .ToListAsync(); + } - IQueryable iqClients = null; + public async Task HasOwnerAsync(CancellationToken token) + { + await using var context = _contextFactory.CreateContext(false); + return await context.Clients.AnyAsync(client => client.Level == Permission.Owner, token); + } - if (!string.IsNullOrEmpty(query.Xuid)) + /// + /// retrieves the number of owners + /// (client level is owner) + /// + /// + public virtual async Task GetOwnerCount() + { + await using var context = _contextFactory.CreateContext(false); + return await context.Clients + .CountAsync(_client => _client.Level == Permission.Owner); + } + + public async Task GetClientForLogin(int clientId) + { + await using var context = _contextFactory.CreateContext(false); + return await context.Clients + .Select(_client => new EFClient + { + NetworkId = _client.NetworkId, + ClientId = _client.ClientId, + CurrentAlias = new EFAlias + { + Name = _client.CurrentAlias.Name + }, + Password = _client.Password, + PasswordSalt = _client.PasswordSalt, + Level = _client.Level + }) + .FirstAsync(_client => _client.ClientId == clientId); + } + + public async Task> GetPrivilegedClients(bool includeName = true) + { + await using var context = _contextFactory.CreateContext(false); + + var iqClients = from client in context.Clients.AsNoTracking() + where client.Level >= Permission.Trusted + where client.Active + select new EFClient + { + AliasLinkId = client.AliasLinkId, + CurrentAlias = client.CurrentAlias, + ClientId = client.ClientId, + Level = client.Level, + Password = client.Password, + PasswordSalt = client.PasswordSalt, + NetworkId = client.NetworkId, + LastConnection = client.LastConnection + }; + + return await iqClients.ToListAsync(); + } + + public async Task> FindClientsByIdentifier(string identifier) + { + var trimmedIdentifier = identifier?.Trim(); + if (trimmedIdentifier?.Length < _appConfig.MinimumNameLength) { - var networkId = query.Xuid.ConvertGuidToLong(System.Globalization.NumberStyles.HexNumber); - iqClients = context.Clients.Where(_client => _client.NetworkId == networkId); + return new List(); } - else if (!string.IsNullOrEmpty(query.Name)) + await using var context = _contextFactory.CreateContext(false); + long? networkId = null; + try + { + networkId = trimmedIdentifier.ConvertGuidToLong(NumberStyles.HexNumber); + } + catch { - iqClients = context.Clients - .Where(_client => - EF.Functions.Like(_client.CurrentAlias.Name.ToLower(), $"%{query.Name.ToLower()}%")); } - if (query.Direction == SortDirection.Ascending) + var ipAddress = trimmedIdentifier.ConvertToIP(); + + var iqLinkIds = context.Aliases.Where(_alias => _alias.Active); + + // we want to query for the IP ADdress + if (ipAddress != null) { - iqClients = iqClients.OrderBy(_client => _client.LastConnection); + iqLinkIds = iqLinkIds.Where(_alias => _alias.IPAddress == ipAddress); } + // want to find them by name (wildcard) else { - iqClients = iqClients.OrderByDescending(_client => _client.LastConnection); + iqLinkIds = iqLinkIds.Where(_alias => EF.Functions.Like(_alias.SearchableName ?? _alias.Name.ToLower(), + $"%{trimmedIdentifier.ToLower()}%")); } - var queryResults = await iqClients - .Select(_client => new FindClientResult - { - ClientId = _client.ClientId, - Xuid = _client.NetworkId.ToString("X"), - Name = _client.CurrentAlias.Name - }) - .Skip(query.Offset) - .Take(query.Count) + var linkIds = await iqLinkIds + .Select(_alias => _alias.LinkId) .ToListAsync(); - result.TotalResultCount = await iqClients.CountAsync(); - result.Results = queryResults; - result.RetrievedResultCount = queryResults.Count; + // get all the clients that match the alias link or the network id + var iqClients = context.Clients + .Where(_client => _client.Active); - return result; + + iqClients = iqClients.Where(_client => networkId == _client.NetworkId || + linkIds.Contains(_client.AliasLinkId) + || !_appConfig.EnableImplicitAccountLinking && + _client.CurrentAlias.IPAddress != null && + _client.CurrentAlias.IPAddress == ipAddress); + + // we want to project our results + var iqClientProjection = iqClients.OrderByDescending(_client => _client.LastConnection) + .Select(_client => new PlayerInfo + { + Name = _client.CurrentAlias.Name, + LevelInt = (int)_client.Level, + LastConnection = _client.LastConnection, + ClientId = _client.ClientId, + IPAddress = _client.CurrentAlias.IPAddress.HasValue + ? _client.CurrentAlias.IPAddress.Value.ToString() + : "" + }); + + var clients = await iqClientProjection.ToListAsync(); + + // this is so we don't try to evaluate this in the linq to entities query + foreach (var client in clients) + client.Level = ((Permission)client.LevelInt).ToLocalizedLevelName(); + + return clients; } + + public async Task GetTotalClientsAsync() + { + await using var context = _contextFactory.CreateContext(false); + return await context.Clients + .CountAsync(); + } + + /// + /// Returns the number of clients seen today + /// + /// + public async Task GetRecentClientCount() + { + await using var context = _contextFactory.CreateContext(false); + var startOfPeriod = DateTime.UtcNow.AddHours(-24); + var iqQuery = context.Clients.Where(_client => _client.LastConnection >= startOfPeriod); + + return await iqQuery.CountAsync(); + } + + /// + /// gets the 10 most recently added clients to IW4MAdmin + /// + /// + public async Task> GetRecentClients() + { + var startOfPeriod = DateTime.UtcNow.AddHours(-24); + + await using var context = _contextFactory.CreateContext(false); + var iqClients = context.Clients + .Where(_client => _client.CurrentAlias.IPAddress != null) + .Where(_client => _client.FirstConnection >= startOfPeriod) + .OrderByDescending(_client => _client.FirstConnection) + .Select(_client => new PlayerInfo + { + ClientId = _client.ClientId, + Name = _client.CurrentAlias.Name, + IPAddress = _client.CurrentAlias.IPAddress.ConvertIPtoString(), + LastConnection = _client.FirstConnection + }); + + return await iqClients.ToListAsync(); + } + + #endregion } } diff --git a/SharedLibraryCore/Services/PenaltyService.cs b/SharedLibraryCore/Services/PenaltyService.cs index f392ccc1a..136ca0fef 100644 --- a/SharedLibraryCore/Services/PenaltyService.cs +++ b/SharedLibraryCore/Services/PenaltyService.cs @@ -1,34 +1,32 @@ -using Microsoft.EntityFrameworkCore; -using SharedLibraryCore.Database; -using SharedLibraryCore.Database.Models; -using SharedLibraryCore.Dtos; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Threading.Tasks; using Data.Abstractions; using Data.Models; +using Microsoft.EntityFrameworkCore; using SharedLibraryCore.Configuration; +using SharedLibraryCore.Dtos; using SharedLibraryCore.Interfaces; namespace SharedLibraryCore.Services { public class PenaltyService : IEntityService { - private readonly IDatabaseContextFactory _contextFactory; private readonly ApplicationConfiguration _appConfig; - + private readonly IDatabaseContextFactory _contextFactory; + public PenaltyService(IDatabaseContextFactory contextFactory, ApplicationConfiguration appConfig) { _contextFactory = contextFactory; _appConfig = appConfig; } - + public virtual async Task Create(EFPenalty newEntity) { await using var context = _contextFactory.CreateContext(); - var penalty = new EFPenalty() + var penalty = new EFPenalty { Active = true, OffenderId = newEntity.Offender.ClientId, @@ -38,7 +36,8 @@ namespace SharedLibraryCore.Services Expires = newEntity.Expires, Offense = newEntity.Offense, When = DateTime.UtcNow, - AutomatedOffense = newEntity.AutomatedOffense ?? newEntity.Punisher.AdministeredPenalties?.FirstOrDefault()?.AutomatedOffense, + AutomatedOffense = newEntity.AutomatedOffense ?? + newEntity.Punisher.AdministeredPenalties?.FirstOrDefault()?.AutomatedOffense, IsEvadedOffense = newEntity.IsEvadedOffense }; @@ -73,43 +72,47 @@ namespace SharedLibraryCore.Services throw new NotImplementedException(); } - public async Task> GetRecentPenalties(int count, int offset, EFPenalty.PenaltyType showOnly = EFPenalty.PenaltyType.Any, bool ignoreAutomated = true) + public async Task> GetRecentPenalties(int count, int offset, + EFPenalty.PenaltyType showOnly = EFPenalty.PenaltyType.Any, bool ignoreAutomated = true) { await using var context = _contextFactory.CreateContext(false); var iqPenalties = context.Penalties - .Where(p => showOnly == EFPenalty.PenaltyType.Any ? p.Type != EFPenalty.PenaltyType.Any : p.Type == showOnly) + .Where(p => showOnly == EFPenalty.PenaltyType.Any + ? p.Type != EFPenalty.PenaltyType.Any + : p.Type == showOnly) .Where(_penalty => !ignoreAutomated || _penalty.PunisherId != 1) .OrderByDescending(p => p.When) .Skip(offset) .Take(count) - .Select(_penalty => new PenaltyInfo() - { - Id = _penalty.PenaltyId, - Offense = _penalty.Offense, - AutomatedOffense = _penalty.AutomatedOffense, - OffenderId = _penalty.OffenderId, - OffenderName = _penalty.Offender.CurrentAlias.Name, - PunisherId = _penalty.PunisherId, - PunisherName = _penalty.Punisher.CurrentAlias.Name, - PunisherLevel = _penalty.Punisher.Level, - PenaltyType = _penalty.Type, - Expires = _penalty.Expires, - TimePunished = _penalty.When, - IsEvade = _penalty.IsEvadedOffense - }); + .Select(_penalty => new PenaltyInfo + { + Id = _penalty.PenaltyId, + Offense = _penalty.Offense, + AutomatedOffense = _penalty.AutomatedOffense, + OffenderId = _penalty.OffenderId, + OffenderName = _penalty.Offender.CurrentAlias.Name, + PunisherId = _penalty.PunisherId, + PunisherName = _penalty.Punisher.CurrentAlias.Name, + PunisherLevel = _penalty.Punisher.Level, + PenaltyType = _penalty.Type, + Expires = _penalty.Expires, + TimePunished = _penalty.When, + IsEvade = _penalty.IsEvadedOffense + }); return await iqPenalties.ToListAsync(); } /// - /// retrieves penalty information for meta service + /// retrieves penalty information for meta service /// /// database id of the client /// how many items to retrieve /// not used /// retreive penalties older than this /// - public async Task> GetClientPenaltyForMetaAsync(int clientId, int count, int offset, DateTime? startAt) + public async Task> GetClientPenaltyForMetaAsync(int clientId, int count, int offset, + DateTime? startAt) { var linkedPenaltyType = Utilities.LinkedPenaltyTypes(); @@ -120,12 +123,13 @@ namespace SharedLibraryCore.Services .FirstOrDefaultAsync(); var iqPenalties = context.Penalties.AsNoTracking() - .Where(_penalty => _penalty.OffenderId == clientId || _penalty.PunisherId == clientId || (linkedPenaltyType.Contains(_penalty.Type) && _penalty.LinkId == linkId)) + .Where(_penalty => _penalty.OffenderId == clientId || _penalty.PunisherId == clientId || + linkedPenaltyType.Contains(_penalty.Type) && _penalty.LinkId == linkId) .Where(_penalty => _penalty.When <= startAt) .OrderByDescending(_penalty => _penalty.When) .Skip(offset) .Take(count) - .Select(_penalty => new PenaltyInfo() + .Select(_penalty => new PenaltyInfo { Id = _penalty.PenaltyId, Offense = _penalty.Offense, @@ -144,18 +148,19 @@ namespace SharedLibraryCore.Services return await iqPenalties.Distinct().ToListAsync(); } - public async Task> GetActivePenaltiesAsync(int linkId, int? ip = null, bool includePunisherName = false) + public async Task> GetActivePenaltiesAsync(int linkId, int? ip = null, + bool includePunisherName = false) { var now = DateTime.UtcNow; - Expression> filter = (p) => (new [] - { - EFPenalty.PenaltyType.TempBan, - EFPenalty.PenaltyType.Ban, - EFPenalty.PenaltyType.Flag - }.Contains(p.Type) && - p.Active && - (p.Expires == null || p.Expires > now)); + Expression> filter = p => new[] + { + EFPenalty.PenaltyType.TempBan, + EFPenalty.PenaltyType.Ban, + EFPenalty.PenaltyType.Flag + }.Contains(p.Type) && + p.Active && + (p.Expires == null || p.Expires > now); await using var context = _contextFactory.CreateContext(false); var iqLinkPenalties = context.Penalties @@ -163,10 +168,10 @@ namespace SharedLibraryCore.Services .Where(filter); IQueryable iqIpPenalties; - + if (_appConfig.EnableImplicitAccountLinking) { - iqIpPenalties = context.Aliases + iqIpPenalties = context.Aliases .Where(a => a.IPAddress != null && a.IPAddress == ip) .SelectMany(a => a.Link.ReceivedPenalties) .Where(filter); @@ -206,14 +211,14 @@ namespace SharedLibraryCore.Services var penaltiesByIp = context.Penalties .Where(p => p.Offender.CurrentAlias.IPAddress != null && p.Offender.CurrentAlias.IPAddress == null) .Where(p => p.Expires > now || p.Expires == null); - + await penaltiesByLink.Union(penaltiesByIp).Distinct().ForEachAsync(p => { p.Active = false; p.Expires = now; }); - + await context.SaveChangesAsync(); } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/SharedLibraryCore.csproj b/SharedLibraryCore/SharedLibraryCore.csproj index acd82a5aa..f9c4eb754 100644 --- a/SharedLibraryCore/SharedLibraryCore.csproj +++ b/SharedLibraryCore/SharedLibraryCore.csproj @@ -2,9 +2,9 @@ Library - netcoreapp3.1 + net6.0 RaidMax.IW4MAdmin.SharedLibraryCore - 2022.01.20.1 + 2022.01.25.2 RaidMax Forever None Debug;Release;Prerelease @@ -19,7 +19,7 @@ true MIT Shared Library for IW4MAdmin - 2022.01.20.1 + 2022.01.25.2 @@ -28,23 +28,23 @@ - - - - - - + + + + + + - - - - - - - - - - + + + + + + + + + + diff --git a/SharedLibraryCore/TagHelpers/ColorCode.cs b/SharedLibraryCore/TagHelpers/ColorCode.cs index 1f765294d..e62c01932 100644 --- a/SharedLibraryCore/TagHelpers/ColorCode.cs +++ b/SharedLibraryCore/TagHelpers/ColorCode.cs @@ -8,6 +8,8 @@ namespace SharedLibraryCore [HtmlTargetElement("color-code")] public class ColorCode : TagHelper { + private readonly bool _allow; + public ColorCode(ApplicationConfiguration appConfig) { _allow = appConfig?.EnableColorCodes ?? false; @@ -15,8 +17,6 @@ namespace SharedLibraryCore public string Value { get; set; } - private readonly bool _allow; - public override void Process(TagHelperContext context, TagHelperOutput output) { output.TagName = "ColorCode"; @@ -29,7 +29,7 @@ namespace SharedLibraryCore { var colorCode = match.Groups[1].ToString().Last(); output.PreContent.AppendHtml( - $""); + $""); output.PreContent.Append(match.Groups[2].ToString()); output.PreContent.AppendHtml(""); } @@ -46,4 +46,4 @@ namespace SharedLibraryCore } } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Utilities.cs b/SharedLibraryCore/Utilities.cs index 06468ea26..071020d18 100644 --- a/SharedLibraryCore/Utilities.cs +++ b/SharedLibraryCore/Utilities.cs @@ -1,28 +1,32 @@ -using Humanizer; -using Humanizer.Localisation; -using SharedLibraryCore.Database.Models; -using SharedLibraryCore.Dtos.Meta; -using SharedLibraryCore.Interfaces; -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Net; +using System.Net.Http; using System.Reflection; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; +using Data.Models; +using Humanizer; +using Humanizer.Localisation; using Microsoft.Extensions.Logging; using SharedLibraryCore.Configuration; -using static SharedLibraryCore.Server; -using ILogger = Microsoft.Extensions.Logging.ILogger; -using static Data.Models.Client.EFClient; -using Data.Models; +using SharedLibraryCore.Database.Models; +using SharedLibraryCore.Dtos.Meta; using SharedLibraryCore.Formatting; +using SharedLibraryCore.Helpers; +using SharedLibraryCore.Interfaces; +using SharedLibraryCore.Localization; +using SharedLibraryCore.RCon; +using static SharedLibraryCore.Server; +using static Data.Models.Client.EFClient; using static Data.Models.EFPenalty; +using ILogger = Microsoft.Extensions.Logging.ILogger; namespace SharedLibraryCore { @@ -33,48 +37,53 @@ namespace SharedLibraryCore #if DEBUG == true public static string OperatingDirectory => $"{Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)}{Path.DirectorySeparatorChar}"; #else - public static string OperatingDirectory => $"{Path.GetDirectoryName(Assembly.GetEntryAssembly().Location)}{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}"; + public static string OperatingDirectory => + $"{Path.GetDirectoryName(Assembly.GetEntryAssembly().Location)}{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}"; #endif public static Encoding EncodingType; - public static Localization.Layout CurrentLocalization = new Localization.Layout(new Dictionary()); + public static Layout CurrentLocalization = new Layout(new Dictionary()); public static TimeSpan DefaultCommandTimeout { get; set; } = new TimeSpan(0, 0, 25); - public static char[] DirectorySeparatorChars = new[] { '\\', '/' }; + public static char[] DirectorySeparatorChars = { '\\', '/' }; public static char CommandPrefix { get; set; } = '!'; + public static EFClient IW4MAdminClient(Server server = null) { - return new EFClient() + return new EFClient { ClientId = 1, State = EFClient.ClientState.Connected, - Level = EFClient.Permission.Console, + Level = Permission.Console, CurrentServer = server, - CurrentAlias = new EFAlias() + CurrentAlias = new EFAlias { Name = "IW4MAdmin" }, AdministeredPenalties = new List() }; } + /// - /// fallback id for world events + /// fallback id for world events /// public const long WORLD_ID = -1; - public static Dictionary PermissionLevelOverrides { get; } = new Dictionary(); + + public static Dictionary PermissionLevelOverrides { get; } = + new Dictionary(); public static string HttpRequest(string location, string header, string headerValue) { - using (var RequestClient = new System.Net.Http.HttpClient()) + using (var RequestClient = new HttpClient()) { RequestClient.DefaultRequestHeaders.Add(header, headerValue); - string response = RequestClient.GetStringAsync(location).Result; + var response = RequestClient.GetStringAsync(location).Result; return response; } } //Get string with specified number of spaces -- really only for visual output - public static String GetSpaces(int Num) + public static string GetSpaces(int Num) { - String SpaceString = String.Empty; + var SpaceString = string.Empty; while (Num > 0) { SpaceString += ' '; @@ -85,48 +94,46 @@ namespace SharedLibraryCore } //Remove words from a space delimited string - public static String RemoveWords(this string str, int num) + public static string RemoveWords(this string str, int num) { if (str == null || str.Length == 0) { return ""; } - String newStr = String.Empty; - String[] tmp = str.Split(' '); + var newStr = string.Empty; + var tmp = str.Split(' '); - for (int i = 0; i < tmp.Length; i++) - { + for (var i = 0; i < tmp.Length; i++) if (i >= num) { newStr += tmp[i] + ' '; } - } return newStr; } /// - /// caps client name to the specified character length - 3 - /// and adds ellipses to the end of the reamining client name + /// caps client name to the specified character length - 3 + /// and adds ellipses to the end of the reamining client name /// /// client name /// max number of characters for the name /// - public static string CapClientName(this string str, int maxLength) => - str.Length > maxLength ? - $"{str.Substring(0, maxLength - 3)}..." : - str; + public static string CapClientName(this string str, int maxLength) + { + return str.Length > maxLength ? $"{str.Substring(0, maxLength - 3)}..." : str; + } /// - /// helper method to get the information about an exception and inner exceptions + /// helper method to get the information about an exception and inner exceptions /// /// /// public static string GetExceptionInfo(this Exception ex) { var sb = new StringBuilder(); - int depth = 0; + var depth = 0; while (ex != null) { sb.AppendLine($"Exception[{depth}] Name: {ex.GetType().FullName}"); @@ -140,24 +147,23 @@ namespace SharedLibraryCore return sb.ToString(); } - public static EFClient.Permission MatchPermission(String str) + public static Permission MatchPermission(string str) { - String lookingFor = str.ToLower(); + var lookingFor = str.ToLower(); - for (EFClient.Permission Perm = EFClient.Permission.User; Perm < EFClient.Permission.Console; Perm++) - { + for (var Perm = Permission.User; Perm < Permission.Console; Perm++) if (lookingFor.Contains(Perm.ToString().ToLower()) - || lookingFor.Contains(CurrentLocalization.LocalizationIndex[$"GLOBAL_PERMISSION_{Perm.ToString().ToUpper()}"].ToLower())) + || lookingFor.Contains(CurrentLocalization + .LocalizationIndex[$"GLOBAL_PERMISSION_{Perm.ToString().ToUpper()}"].ToLower())) { return Perm; } - } - return EFClient.Permission.Banned; + return Permission.Banned; } /// - /// Remove all IW Engine color codes + /// Remove all IW Engine color codes /// /// String containing color codes /// @@ -173,11 +179,14 @@ namespace SharedLibraryCore } /// - /// returns a "fixed" string that prevents message truncation in IW4 (and probably other Q3 clients) + /// returns a "fixed" string that prevents message truncation in IW4 (and probably other Q3 clients) /// /// /// - public static string FixIW4ForwardSlash(this string str) => str.Replace("//", "/ /"); + public static string FixIW4ForwardSlash(this string str) + { + return str.Replace("//", "/ /"); + } public static string FormatMessageForEngine(this string str, ColorCodeMapping mapping) { @@ -187,7 +196,8 @@ namespace SharedLibraryCore } var output = str; - var colorCodeMatches = Regex.Matches(output, @"\(Color::(.{1,16})\)", RegexOptions.IgnoreCase | RegexOptions.Compiled); + var colorCodeMatches = Regex.Matches(output, @"\(Color::(.{1,16})\)", + RegexOptions.IgnoreCase | RegexOptions.Compiled); foreach (var match in colorCodeMatches.Where(m => m.Success)) { var key = match.Groups[1].ToString(); @@ -198,17 +208,24 @@ namespace SharedLibraryCore } private static readonly IList _zmGameTypes = new[] { "zclassic", "zstandard", "zcleansed", "zgrief" }; + /// - /// indicates if the given server is running a zombie game mode + /// indicates if the given server is running a zombie game mode /// /// /// - public static bool IsZombieServer(this Server server) => server.GameName == Game.T6 && _zmGameTypes.Contains(server.Gametype.ToLower()); + public static bool IsZombieServer(this Server server) + { + return server.GameName == Game.T6 && _zmGameTypes.Contains(server.Gametype.ToLower()); + } - public static bool IsCodGame(this Server server) => server.RconParser?.RConEngine == "COD"; + public static bool IsCodGame(this Server server) + { + return server.RconParser?.RConEngine == "COD"; + } /// - /// Get the color key corresponding to a given user level + /// Get the color key corresponding to a given user level /// /// Specified player level /// @@ -231,19 +248,20 @@ namespace SharedLibraryCore public static string ToLocalizedLevelName(this Permission permission) { - var localized = CurrentLocalization.LocalizationIndex[$"GLOBAL_PERMISSION_{permission.ToString().ToUpper()}"]; + var localized = + CurrentLocalization.LocalizationIndex[$"GLOBAL_PERMISSION_{permission.ToString().ToUpper()}"]; return PermissionLevelOverrides.ContainsKey(permission) && PermissionLevelOverrides[permission] != localized ? PermissionLevelOverrides[permission] : localized; } - public async static Task ProcessMessageToken(this Server server, IList tokens, String str) + public static async Task ProcessMessageToken(this Server server, IList tokens, string str) { - MatchCollection RegexMatches = Regex.Matches(str, @"\{\{[A-Z]+\}\}", RegexOptions.IgnoreCase); + var RegexMatches = Regex.Matches(str, @"\{\{[A-Z]+\}\}", RegexOptions.IgnoreCase); foreach (Match M in RegexMatches) { - String Match = M.Value; - String Identifier = M.Value.Substring(2, M.Length - 4); + var Match = M.Value; + var Identifier = M.Value.Substring(2, M.Length - 4); var found = tokens.FirstOrDefault(t => t.Name.ToLower() == Identifier.ToLower()); @@ -267,11 +285,11 @@ namespace SharedLibraryCore } /// - /// Get the full gametype name + /// Get the full gametype name /// /// Shorthand gametype reported from server /// - public static string GetLocalizedGametype(String input) + public static string GetLocalizedGametype(string input) { switch (input) { @@ -323,7 +341,7 @@ namespace SharedLibraryCore } /// - /// converts a string to numerical guid + /// converts a string to numerical guid /// /// source string for guid /// how to parse the guid @@ -341,7 +359,7 @@ namespace SharedLibraryCore return z * 2 + 0x0110000100000000 + y; } - + str = str.Substring(0, Math.Min(str.Length, 19)); var parsableAsNumber = Regex.Match(str, @"([A-F]|[a-f]|[0-9])+").Value; @@ -365,7 +383,8 @@ namespace SharedLibraryCore else { - long.TryParse(str.Length > 16 ? str.Substring(0, 16) : str, numberStyle, CultureInfo.InvariantCulture, out id); + long.TryParse(str.Length > 16 ? str.Substring(0, 16) : str, numberStyle, + CultureInfo.InvariantCulture, out id); } } @@ -384,8 +403,8 @@ namespace SharedLibraryCore } /// - /// determines if the guid provided appears to be a bot guid - /// "1277538174" - (Pluto?)WaW (T4) + /// determines if the guid provided appears to be a bot guid + /// "1277538174" - (Pluto?)WaW (T4) /// /// value of the guid /// true if is bot guid, otherwise false @@ -395,38 +414,44 @@ namespace SharedLibraryCore } /// - /// generates a numerical hashcode from a string value + /// generates a numerical hashcode from a string value /// /// value string /// - public static long GenerateGuidFromString(this string value) => string.IsNullOrEmpty(value) ? -1 : GetStableHashCode(value.StripColors()); + public static long GenerateGuidFromString(this string value) + { + return string.IsNullOrEmpty(value) ? -1 : GetStableHashCode(value.StripColors()); + } /// https://stackoverflow.com/questions/36845430/persistent-hashcode-for-strings public static int GetStableHashCode(this string str) { unchecked { - int hash1 = 5381; - int hash2 = hash1; + var hash1 = 5381; + var hash2 = hash1; - for (int i = 0; i < str.Length && str[i] != '\0'; i += 2) + for (var i = 0; i < str.Length && str[i] != '\0'; i += 2) { hash1 = ((hash1 << 5) + hash1) ^ str[i]; if (i == str.Length - 1 || str[i + 1] == '\0') + { break; + } + hash2 = ((hash2 << 5) + hash2) ^ str[i + 1]; } - return hash1 + (hash2 * 1566083941); + return hash1 + hash2 * 1566083941; } } public static int? ConvertToIP(this string str) { - bool success = IPAddress.TryParse(str, out IPAddress ip); - return success && ip.GetAddressBytes().Count(_byte => _byte == 0) != 4 ? - (int?)BitConverter.ToInt32(ip.GetAddressBytes(), 0) : - null; + var success = IPAddress.TryParse(str, out var ip); + return success && ip.GetAddressBytes().Count(_byte => _byte == 0) != 4 + ? (int?)BitConverter.ToInt32(ip.GetAddressBytes(), 0) + : null; } public static string ConvertIPtoString(this int? ip) @@ -488,8 +513,8 @@ namespace SharedLibraryCore return new TimeSpan(1, 0, 0); } - char lengthDenote = expressionMatch.Groups[2].ToString()[0]; - int length = Int32.Parse(expressionMatch.Groups[1].ToString()); + var lengthDenote = expressionMatch.Groups[2].ToString()[0]; + var length = int.Parse(expressionMatch.Groups[1].ToString()); var loc = CurrentLocalization.LocalizationIndex; @@ -522,7 +547,7 @@ namespace SharedLibraryCore } /// - /// returns a list of penalty types that should be shown across all profiles + /// returns a list of penalty types that should be shown across all profiles /// /// public static PenaltyType[] LinkedPenaltyTypes() @@ -533,22 +558,22 @@ namespace SharedLibraryCore PenaltyType.Unban, PenaltyType.TempBan, PenaltyType.Flag, - PenaltyType.Unflag, + PenaltyType.Unflag }; } /// - /// Helper extension that determines if a user is a privileged client + /// Helper extension that determines if a user is a privileged client /// /// /// public static bool IsPrivileged(this EFClient p) { - return p.Level > EFClient.Permission.Flagged; + return p.Level > Permission.Flagged; } /// - /// prompt user to answer a yes/no question + /// prompt user to answer a yes/no question /// /// question to prompt the user with /// description of the question's value @@ -557,12 +582,12 @@ namespace SharedLibraryCore public static bool PromptBool(this string question, string description = null, bool defaultValue = true) { Console.Write($"{question}?{(string.IsNullOrEmpty(description) ? " " : $" ({description}) ")}[y/n]: "); - char response = Console.ReadLine().ToLower().FirstOrDefault(); + var response = Console.ReadLine().ToLower().FirstOrDefault(); return response != 0 ? response == 'y' : defaultValue; } /// - /// prompt user to make a selection + /// prompt user to make a selection /// /// type of selection /// question to prompt the user with @@ -570,38 +595,38 @@ namespace SharedLibraryCore /// description of the question's value /// array of possible selections (should be able to convert to string) /// - public static Tuple PromptSelection(this string question, T defaultValue, string description = null, params T[] selections) + public static Tuple PromptSelection(this string question, T defaultValue, string description = null, + params T[] selections) { - bool hasDefault = false; + var hasDefault = false; if (defaultValue != null) { hasDefault = true; - selections = (new T[] { defaultValue }).Union(selections).ToArray(); + selections = new[] { defaultValue }.Union(selections).ToArray(); } - Console.WriteLine($"{question}{(string.IsNullOrEmpty(description) ? "" : $" [{ description}:]")}"); + Console.WriteLine($"{question}{(string.IsNullOrEmpty(description) ? "" : $" [{description}:]")}"); Console.WriteLine(new string('=', 52)); - for (int index = 0; index < selections.Length; index++) - { + for (var index = 0; index < selections.Length; index++) Console.WriteLine($"{(hasDefault ? index : index + 1)}] {selections[index]}"); - } Console.WriteLine(new string('=', 52)); - int selectionIndex = PromptInt(CurrentLocalization.LocalizationIndex["SETUP_PROMPT_MAKE_SELECTION"], null, hasDefault ? 0 : 1, selections.Length, hasDefault ? 0 : (int?)null); + var selectionIndex = PromptInt(CurrentLocalization.LocalizationIndex["SETUP_PROMPT_MAKE_SELECTION"], null, + hasDefault ? 0 : 1, selections.Length, hasDefault ? 0 : (int?)null); if (!hasDefault) { selectionIndex--; } - T selection = selections[selectionIndex]; + var selection = selections[selectionIndex]; return Tuple.Create(selectionIndex, selection); } /// - /// prompt user to enter a number + /// prompt user to enter a number /// /// question to prompt with /// maximum value to allow @@ -609,26 +634,29 @@ namespace SharedLibraryCore /// default value to set the return value to /// a description of the question's value /// integer from user's input - public static int PromptInt(this string question, string description = null, int minValue = 0, int maxValue = int.MaxValue, int? defaultValue = null) + public static int PromptInt(this string question, string description = null, int minValue = 0, + int maxValue = int.MaxValue, int? defaultValue = null) { - Console.Write($"{question}{(string.IsNullOrEmpty(description) ? "" : $" ({description})")}{(defaultValue == null ? "" : $" [{CurrentLocalization.LocalizationIndex["SETUP_PROMPT_DEFAULT"]} {defaultValue.Value.ToString()}]")}: "); + Console.Write( + $"{question}{(string.IsNullOrEmpty(description) ? "" : $" ({description})")}{(defaultValue == null ? "" : $" [{CurrentLocalization.LocalizationIndex["SETUP_PROMPT_DEFAULT"]} {defaultValue.Value.ToString()}]")}: "); int response; string inputOrDefault() { - string input = Console.ReadLine(); + var input = Console.ReadLine(); return string.IsNullOrEmpty(input) && defaultValue != null ? defaultValue.ToString() : input; } while (!int.TryParse(inputOrDefault(), out response) || - response < minValue || - response > maxValue) + response < minValue || + response > maxValue) { - string range = ""; + var range = ""; if (minValue != 0 || maxValue != int.MaxValue) { range = $" [{minValue}-{maxValue}]"; } + Console.Write($"{CurrentLocalization.LocalizationIndex["SETUP_PROMPT_INT"]}{range}: "); } @@ -636,7 +664,7 @@ namespace SharedLibraryCore } /// - /// prompt use to enter a string response + /// prompt use to enter a string response /// /// question to prompt with /// description of the question's value @@ -646,14 +674,15 @@ namespace SharedLibraryCore { string inputOrDefault() { - string input = Console.ReadLine(); - return string.IsNullOrEmpty(input) && defaultValue != null ? defaultValue.ToString() : input; + var input = Console.ReadLine(); + return string.IsNullOrEmpty(input) && defaultValue != null ? defaultValue : input; } string response; do { - Console.Write($"{question}{(string.IsNullOrEmpty(description) ? "" : $" ({description})")}{(defaultValue == null ? "" : $" [{CurrentLocalization.LocalizationIndex["SETUP_PROMPT_DEFAULT"]} {defaultValue}]")}: "); + Console.Write( + $"{question}{(string.IsNullOrEmpty(description) ? "" : $" ({description})")}{(defaultValue == null ? "" : $" [{CurrentLocalization.LocalizationIndex["SETUP_PROMPT_DEFAULT"]} {defaultValue}]")}: "); response = inputOrDefault(); } while (string.IsNullOrWhiteSpace(response) && response != defaultValue); @@ -662,17 +691,15 @@ namespace SharedLibraryCore public static Dictionary DictionaryFromKeyValue(this string eventLine) { - string[] values = eventLine.Substring(1).Split('\\'); + var values = eventLine.Substring(1).Split('\\'); Dictionary dict = null; if (values.Length > 1) { dict = new Dictionary(); - for (int i = values.Length % 2 == 0 ? 0 : 1; i < values.Length; i += 2) - { + for (var i = values.Length % 2 == 0 ? 0 : 1; i < values.Length; i += 2) dict.Add(values[i], values[i + 1]); - } } return dict; @@ -681,29 +708,29 @@ namespace SharedLibraryCore /* https://loune.net/2017/06/running-shell-bash-commands-in-net-core/ */ public static string GetCommandLine(int pId) { - var cmdProcess = new Process() + var cmdProcess = new Process { - StartInfo = new ProcessStartInfo() + StartInfo = new ProcessStartInfo { FileName = "cmd.exe", Arguments = $"/c wmic process where processid={pId} get CommandLine", RedirectStandardOutput = true, UseShellExecute = false, - CreateNoWindow = true, + CreateNoWindow = true } }; cmdProcess.Start(); cmdProcess.WaitForExit(); - string[] cmdLine = cmdProcess.StandardOutput.ReadToEnd().Split("\r\n", StringSplitOptions.RemoveEmptyEntries); + var cmdLine = cmdProcess.StandardOutput.ReadToEnd().Split("\r\n", StringSplitOptions.RemoveEmptyEntries); cmdProcess.Dispose(); return cmdLine.Length > 1 ? cmdLine[1] : cmdLine[0]; } /// - /// indicates if the given log path is a remote (http) uri + /// indicates if the given log path is a remote (http) uri /// /// /// @@ -714,7 +741,8 @@ namespace SharedLibraryCore public static string ToBase64UrlSafeString(this string src) { - return Convert.ToBase64String(src.Select(c => Convert.ToByte(c)).ToArray()).Replace('+', '-').Replace('/', '_'); + return Convert.ToBase64String(src.Select(c => Convert.ToByte(c)).ToArray()).Replace('+', '-') + .Replace('/', '_'); } public static Task> GetDvarAsync(this Server server, string dvarName, T fallbackValue = default) @@ -722,13 +750,17 @@ namespace SharedLibraryCore return server.RconParser.GetDvarAsync(server.RemoteConnection, dvarName, fallbackValue); } - public static async Task> GetMappedDvarValueOrDefaultAsync(this Server server, string dvarName, string infoResponseName = null, IDictionary infoResponse = null, T overrideDefault = default) + public static async Task> GetMappedDvarValueOrDefaultAsync(this Server server, string dvarName, + string infoResponseName = null, IDictionary infoResponse = null, + T overrideDefault = default) { // todo: unit test this - string mappedKey = server.RconParser.GetOverrideDvarName(dvarName); + var mappedKey = server.RconParser.GetOverrideDvarName(dvarName); var defaultValue = server.RconParser.GetDefaultDvarValue(mappedKey) ?? overrideDefault; - string foundKey = infoResponse?.Keys.Where(_key => new[] { mappedKey, dvarName, infoResponseName ?? dvarName }.Contains(_key)).FirstOrDefault(); + var foundKey = infoResponse?.Keys + .Where(_key => new[] { mappedKey, dvarName, infoResponseName ?? dvarName }.Contains(_key)) + .FirstOrDefault(); if (!string.IsNullOrEmpty(foundKey)) { @@ -758,7 +790,7 @@ namespace SharedLibraryCore } /// - /// Retrieves the key value pairs for server information usually checked after map rotation + /// Retrieves the key value pairs for server information usually checked after map rotation /// /// /// How long to wait after the map has rotated to query @@ -770,16 +802,16 @@ namespace SharedLibraryCore await Task.Delay(delay.Value); } - var response = await server.RemoteConnection.SendQueryAsync(RCon.StaticHelpers.QueryType.GET_INFO); - string combinedResponse = response.Length > 1 ? - string.Join('\\', response.Where(r => r.Length > 0 && r[0] == '\\')) : - response[0]; + var response = await server.RemoteConnection.SendQueryAsync(StaticHelpers.QueryType.GET_INFO); + var combinedResponse = response.Length > 1 + ? string.Join('\\', response.Where(r => r.Length > 0 && r[0] == '\\')) + : response[0]; return combinedResponse.DictionaryFromKeyValue(); } public static double GetVersionAsDouble() { - string version = Assembly.GetCallingAssembly().GetName().Version.ToString(); + var version = Assembly.GetCallingAssembly().GetName().Version.ToString(); version = version.Replace(".", ""); return double.Parse(version) / 1000.0; } @@ -792,11 +824,11 @@ namespace SharedLibraryCore public static string FormatExt(this string input, params object[] values) { var matches = Regex.Matches(Regex.Unescape(input), @"{{\w+}}"); - string output = input; - int index = 0; + var output = input; + var index = 0; foreach (Match match in matches) { - output = output.Replace(match.Value.ToString(), $"{{{index.ToString()}}}"); + output = output.Replace(match.Value, $"{{{index.ToString()}}}"); index++; } @@ -804,12 +836,15 @@ namespace SharedLibraryCore { return string.Format(output, values); } - catch { return input; } + catch + { + return input; + } } /// - /// https://stackoverflow.com/questions/8113546/how-to-determine-whether-an-ip-address-in-private/39120248 - /// An extension method to determine if an IP address is internal, as specified in RFC1918 + /// https://stackoverflow.com/questions/8113546/how-to-determine-whether-an-ip-address-in-private/39120248 + /// An extension method to determine if an IP address is internal, as specified in RFC1918 /// /// The IP address that will be tested /// Returns true if the IP is internal, false if it is external @@ -820,7 +855,7 @@ namespace SharedLibraryCore return true; } - byte[] bytes = toTest.GetAddressBytes(); + var bytes = toTest.GetAddressBytes(); switch (bytes[0]) { case 0: @@ -837,17 +872,15 @@ namespace SharedLibraryCore } /// - /// retrieves the external IP address of the current running machine + /// retrieves the external IP address of the current running machine /// /// public static async Task GetExternalIP() { try { - using (var wc = new WebClient()) - { - return await wc.DownloadStringTaskAsync("https://api.ipify.org"); - } + using var wc = new HttpClient(); + return await wc.GetStringAsync("https://api.ipify.org"); } catch @@ -857,7 +890,7 @@ namespace SharedLibraryCore } /// - /// Determines if the given message is a quick message + /// Determines if the given message is a quick message /// /// /// true if the @@ -867,41 +900,51 @@ namespace SharedLibraryCore } /// - /// trims new line and whitespace from string + /// trims new line and whitespace from string /// /// source string /// - public static string TrimNewLine(this string str) => str.Trim().TrimEnd('\r', '\n'); + public static string TrimNewLine(this string str) + { + return str.Trim().TrimEnd('\r', '\n'); + } public static Vector3 FixIW4Angles(this Vector3 vector) { - float X = vector.X >= 0 ? vector.X : 360.0f + vector.X; - float Y = vector.Y >= 0 ? vector.Y : 360.0f + vector.Y; - float Z = vector.Z >= 0 ? vector.Z : 360.0f + vector.Z; + var X = vector.X >= 0 ? vector.X : 360.0f + vector.X; + var Y = vector.Y >= 0 ? vector.Y : 360.0f + vector.Y; + var Z = vector.Z >= 0 ? vector.Z : 360.0f + vector.Z; return new Vector3(Y, X, Z); } - public static float ToRadians(this float value) => (float)Math.PI * value / 180.0f; + public static float ToRadians(this float value) + { + return (float)Math.PI * value / 180.0f; + } - public static float ToDegrees(this float value) => value * 180.0f / (float)Math.PI; + public static float ToDegrees(this float value) + { + return value * 180.0f / (float)Math.PI; + } public static double[] AngleStuff(Vector3 a, Vector3 b) { - double deltaX = 180.0 - Math.Abs(Math.Abs(a.X - b.X) - 180.0); - double deltaY = 180.0 - Math.Abs(Math.Abs(a.Y - b.Y) - 180.0); + var deltaX = 180.0 - Math.Abs(Math.Abs(a.X - b.X) - 180.0); + var deltaY = 180.0 - Math.Abs(Math.Abs(a.Y - b.Y) - 180.0); return new[] { deltaX, deltaY }; } /// - /// attempts to create and persist a penalty + /// attempts to create and persist a penalty /// /// /// /// /// true of the create succeeds, false otherwise - public static async Task TryCreatePenalty(this EFPenalty penalty, IEntityService penaltyService, ILogger logger) + public static async Task TryCreatePenalty(this EFPenalty penalty, + IEntityService penaltyService, ILogger logger) { try { @@ -918,12 +961,12 @@ namespace SharedLibraryCore } /// - /// https://www.planetgeek.ch/2016/12/08/async-method-without-cancellation-support-do-it-my-way/ + /// https://www.planetgeek.ch/2016/12/08/async-method-without-cancellation-support-do-it-my-way/ /// public static async Task WithWaitCancellation(this Task task, - CancellationToken cancellationToken) + CancellationToken cancellationToken) { - Task completedTask = await Task.WhenAny(task, Task.Delay(Timeout.Infinite, cancellationToken)); + var completedTask = await Task.WhenAny(task, Task.Delay(Timeout.Infinite, cancellationToken)); if (completedTask == task) { await task; @@ -936,36 +979,39 @@ namespace SharedLibraryCore } public static async Task WithTimeout(this Task task, TimeSpan timeout) - { - await Task.WhenAny(task, Task.Delay(timeout)); - return await task; + { + await Task.WhenAny(task, Task.Delay(timeout)); + return await task; } - + public static async Task WithTimeout(this Task task, TimeSpan timeout) - { + { await Task.WhenAny(task, Task.Delay(timeout)); } - - public static bool ShouldHideLevel(this Permission perm) => perm == Permission.Flagged; + + public static bool ShouldHideLevel(this Permission perm) + { + return perm == Permission.Flagged; + } /// - /// parses translation string into tokens that are able to be formatted by the webfront + /// parses translation string into tokens that are able to be formatted by the webfront /// /// key for translation lookup /// public static WebfrontTranslationHelper[] SplitTranslationTokens(string translationKey) { - string translationString = CurrentLocalization.LocalizationIndex[translationKey]; + var translationString = CurrentLocalization.LocalizationIndex[translationKey]; var builder = new StringBuilder(); var results = new List(); - foreach (string word in translationString.Split(' ')) + foreach (var word in translationString.Split(' ')) { - string finalWord = word; + var finalWord = word; - if ((word.StartsWith("{{") && !word.EndsWith("}}")) || - (builder.Length > 0 && !word.EndsWith("}}"))) + if (word.StartsWith("{{") && !word.EndsWith("}}") || + builder.Length > 0 && !word.EndsWith("}}")) { builder.Append($"{word} "); continue; @@ -979,12 +1025,14 @@ namespace SharedLibraryCore } var match = Regex.Match(finalWord, @"{{([^}|^-]+)(?:->)([^}]+)}}|{{([^}]+)}}"); - bool isInterpolation = match.Success; + var isInterpolation = match.Success; results.Add(new WebfrontTranslationHelper { IsInterpolation = isInterpolation, - MatchValue = isInterpolation ? match.Groups[3].Length > 0 ? match.Groups[3].ToString() : match.Groups[1].ToString() : finalWord, + MatchValue = isInterpolation + ? match.Groups[3].Length > 0 ? match.Groups[3].ToString() : match.Groups[1].ToString() + : finalWord, TranslationValue = isInterpolation && match.Groups[2].Length > 0 ? match.Groups[2].ToString() : "" }); } @@ -993,39 +1041,41 @@ namespace SharedLibraryCore } /// - /// indicates if running in development mode + /// indicates if running in development mode /// /// - public static bool IsDevelopment => Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development"; + public static bool IsDevelopment => + Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development"; /// - /// replaces any directory separator chars with the platform specific character + /// replaces any directory separator chars with the platform specific character /// /// original file path /// public static string FixDirectoryCharacters(this string path) { - foreach (char separator in DirectorySeparatorChars) - { + foreach (var separator in DirectorySeparatorChars) path = (path ?? "").Replace(separator, Path.DirectorySeparatorChar); - } return path; } /// - /// wrapper method for humanizee that uses current current culture + /// wrapper method for humanizee that uses current current culture /// - public static string HumanizeForCurrentCulture(this TimeSpan timeSpan, int precision = 1, TimeUnit maxUnit = TimeUnit.Week, + public static string HumanizeForCurrentCulture(this TimeSpan timeSpan, int precision = 1, + TimeUnit maxUnit = TimeUnit.Week, TimeUnit minUnit = TimeUnit.Second, string collectionSeparator = ", ", bool toWords = false) { - return timeSpan.Humanize(precision, CurrentLocalization.Culture, maxUnit, minUnit, collectionSeparator, toWords); + return timeSpan.Humanize(precision, CurrentLocalization.Culture, maxUnit, minUnit, collectionSeparator, + toWords); } /// - /// wrapper method for humanizee that uses current current culture + /// wrapper method for humanizee that uses current current culture /// - public static string HumanizeForCurrentCulture(this DateTime input, bool utcDate = true, DateTime? dateToCompareAgainst = null, CultureInfo culture = null) + public static string HumanizeForCurrentCulture(this DateTime input, bool utcDate = true, + DateTime? dateToCompareAgainst = null, CultureInfo culture = null) { return input.Humanize(utcDate, dateToCompareAgainst, CurrentLocalization.Culture); } @@ -1037,7 +1087,7 @@ namespace SharedLibraryCore public static EFClient ToPartialClient(this Data.Models.Client.EFClient client) { - return new EFClient() + return new EFClient { ClientId = client.ClientId, NetworkId = client.NetworkId, @@ -1069,10 +1119,12 @@ namespace SharedLibraryCore { return value.ToString("#,##0", CurrentLocalization.Culture); } - + public static string ToNumericalString(this double value, int precision = 0) { - return value.ToString("#,##0" + $"{(precision > 0 ? "." : "")}" + new string(Enumerable.Repeat('0', precision).ToArray()), CurrentLocalization.Culture); + return value.ToString( + "#,##0" + $"{(precision > 0 ? "." : "")}" + new string(Enumerable.Repeat('0', precision).ToArray()), + CurrentLocalization.Culture); } public static string ToNumericalString(this double? value, int precision = 0) @@ -1087,13 +1139,12 @@ namespace SharedLibraryCore if (message.Length <= length) { - return new[] {message}; + return new[] { message }; } + int i; for (i = 0; i < message.Length - length; i += length) - { messages.Add(new string(message.Skip(i).Take(length).ToArray())); - } var left = message.Length - length; @@ -1112,7 +1163,7 @@ namespace SharedLibraryCore { return appConfig.PresetPenaltyReasons[reason.ToLower()]; } - + var regex = Regex.Match(reason, @"rule(\d+)", RegexOptions.IgnoreCase); if (!regex.Success) { @@ -1139,4 +1190,4 @@ namespace SharedLibraryCore return allRules[index]; } } -} +} \ No newline at end of file diff --git a/Tests/ApplicationTests/ApplicationTests.csproj b/Tests/ApplicationTests/ApplicationTests.csproj index 69f4000ef..0ef0bbc2a 100644 --- a/Tests/ApplicationTests/ApplicationTests.csproj +++ b/Tests/ApplicationTests/ApplicationTests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + net6.0 8.0 diff --git a/WebfrontCore/WebfrontCore.csproj b/WebfrontCore/WebfrontCore.csproj index e5cf83a64..a772fd8e4 100644 --- a/WebfrontCore/WebfrontCore.csproj +++ b/WebfrontCore/WebfrontCore.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + net6.0 true true true @@ -50,18 +50,15 @@ - - + + + - - - - @@ -92,6 +89,6 @@ - +