diff --git a/Application/ApplicationManager.cs b/Application/ApplicationManager.cs index 718b8db9..751088ff 100644 --- a/Application/ApplicationManager.cs +++ b/Application/ApplicationManager.cs @@ -47,7 +47,10 @@ namespace IW4MAdmin.Application { private readonly ConcurrentBag _servers; public List Servers => _servers.OrderByDescending(s => s.ClientNum).ToList(); - [Obsolete] public ObsoleteLogger Logger => _serviceProvider.GetRequiredService(); + + [Obsolete("Use Microsoft.Extensions.Logging.ILogger instead")] + public ObsoleteLogger Logger => _serviceProvider.GetRequiredService(); // TODO: Deprecate this. + public bool IsRunning { get; private set; } public bool IsInitialized { get; private set; } public DateTime StartTime { get; private set; } @@ -55,6 +58,7 @@ namespace IW4MAdmin.Application public IList AdditionalRConParsers { get; } public IList AdditionalEventParsers { get; } + public IList> CommandInterceptors { get; set; } = new List>(); public ITokenAuthentication TokenAuthenticator { get; } @@ -90,13 +94,15 @@ namespace IW4MAdmin.Application private readonly ApplicationConfiguration _appConfig; public ConcurrentDictionary ProcessingEvents { get; } = new(); - public ApplicationManager(ILogger logger, IMiddlewareActionHandler actionHandler, IEnumerable commands, + public ApplicationManager(ILogger logger, IMiddlewareActionHandler actionHandler, + IEnumerable commands, ITranslationLookup translationLookup, IConfigurationHandler commandConfiguration, IConfigurationHandler appConfigHandler, IGameServerInstanceFactory serverInstanceFactory, IEnumerable plugins, IParserRegexFactory parserRegexFactory, IEnumerable customParserEvents, ICoreEventHandler coreEventHandler, IScriptCommandFactory scriptCommandFactory, IDatabaseContextFactory contextFactory, - IMetaRegistration metaRegistration, IScriptPluginServiceResolver scriptPluginServiceResolver, ClientService clientService, IServiceProvider serviceProvider, - ChangeHistoryService changeHistoryService, ApplicationConfiguration appConfig, PenaltyService penaltyService, IAlertManager alertManager, IInteractionRegistration interactionRegistration, IEnumerable v2PLugins, + IMetaRegistration metaRegistration, IScriptPluginServiceResolver scriptPluginServiceResolver, ClientService clientService, + IServiceProvider serviceProvider, ChangeHistoryService changeHistoryService, ApplicationConfiguration appConfig, PenaltyService penaltyService, + IAlertManager alertManager, IInteractionRegistration interactionRegistration, IEnumerable v2PLugins, ConfigurationWatcher watcher) { MiddlewareActionHandler = actionHandler; @@ -302,41 +308,48 @@ namespace IW4MAdmin.Application ExternalIPAddress = await Utilities.GetExternalIP(); #region DATABASE + _logger.LogInformation("Beginning database migration sync"); Console.WriteLine(_translationLookup["MANAGER_MIGRATION_START"]); await ContextSeed.Seed(_serviceProvider.GetRequiredService(), _isRunningTokenSource.Token); - await DatabaseHousekeeping.RemoveOldRatings(_serviceProvider.GetRequiredService(), _isRunningTokenSource.Token); + await DatabaseHousekeeping.RemoveOldRatings(_serviceProvider.GetRequiredService(), + _isRunningTokenSource.Token); _logger.LogInformation("Finished database migration sync"); Console.WriteLine(_translationLookup["MANAGER_MIGRATION_END"]); + #endregion - - #region EVENTS + + #region EVENTS + IGameServerEventSubscriptions.ServerValueRequested += OnServerValueRequested; IGameServerEventSubscriptions.ServerValueSetRequested += OnServerValueSetRequested; IGameServerEventSubscriptions.ServerCommandExecuteRequested += OnServerCommandExecuteRequested; await IManagementEventSubscriptions.InvokeLoadAsync(this, CancellationToken); + # endregion #region PLUGINS + foreach (var plugin in Plugins) { try { if (plugin is ScriptPlugin scriptPlugin && !plugin.IsParser) { - await scriptPlugin.Initialize(this, _scriptCommandFactory, _scriptPluginServiceResolver, + await scriptPlugin.Initialize(this, _scriptCommandFactory, _scriptPluginServiceResolver, _serviceProvider.GetService>()); scriptPlugin.Watcher.Changed += async (sender, e) => { try { - await scriptPlugin.Initialize(this, _scriptCommandFactory, _scriptPluginServiceResolver, + await scriptPlugin.Initialize(this, _scriptCommandFactory, _scriptPluginServiceResolver, _serviceProvider.GetService>()); } catch (Exception ex) { - Console.WriteLine(Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_IMPORTER_ERROR"].FormatExt(scriptPlugin.Name)); + Console.WriteLine(Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_IMPORTER_ERROR"] + .FormatExt(scriptPlugin.Name)); _logger.LogError(ex, "Could not properly load plugin {plugin}", scriptPlugin.Name); } }; @@ -353,16 +366,18 @@ namespace IW4MAdmin.Application _logger.LogError(ex, $"{_translationLookup["SERVER_ERROR_PLUGIN"]} {plugin.Name}"); } } + #endregion #region CONFIG + // copy over default config if it doesn't exist if (!_appConfig.Servers?.Any() ?? true) { var defaultHandler = new BaseConfigurationHandler("DefaultSettings"); await defaultHandler.BuildAsync(); var defaultConfig = defaultHandler.Configuration(); - + _appConfig.AutoMessages = defaultConfig.AutoMessages; _appConfig.GlobalRules = defaultConfig.GlobalRules; _appConfig.DisallowedClientNames = defaultConfig.DisallowedClientNames; @@ -385,8 +400,9 @@ namespace IW4MAdmin.Application serverConfig.AddEventParser(parser); } - _appConfig.Servers = _appConfig.Servers.Where(_servers => _servers != null).Append((ServerConfiguration)serverConfig.Generate()).ToArray(); - } while (Utilities.PromptBool(_translationLookup["SETUP_SERVER_SAVE"])); + _appConfig.Servers = _appConfig.Servers.Where(_servers => _servers != null) + .Append((ServerConfiguration)serverConfig.Generate()).ToArray(); + } while (_translationLookup["SETUP_SERVER_SAVE"].PromptBool()); await ConfigHandler.Save(); } @@ -447,6 +463,7 @@ namespace IW4MAdmin.Application serverConfig.ModifyParsers(); } } + await ConfigHandler.Save(); } @@ -456,7 +473,9 @@ namespace IW4MAdmin.Application } Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); - Utilities.EncodingType = Encoding.GetEncoding(!string.IsNullOrEmpty(_appConfig.CustomParserEncoding) ? _appConfig.CustomParserEncoding : "windows-1252"); + Utilities.EncodingType = Encoding.GetEncoding(!string.IsNullOrEmpty(_appConfig.CustomParserEncoding) + ? _appConfig.CustomParserEncoding + : "windows-1252"); foreach (var parser in AdditionalRConParsers) { @@ -472,6 +491,7 @@ namespace IW4MAdmin.Application #endregion #region COMMANDS + if (await ClientSvc.HasOwnerAsync(_isRunningTokenSource.Token)) { _commands.RemoveAll(_cmd => _cmd.GetType() == typeof(OwnerCommand)); @@ -503,25 +523,28 @@ namespace IW4MAdmin.Application { continue; } + cmdConfig.Commands.Add(cmd.CommandConfigNameForType(), - new CommandProperties - { - Name = cmd.Name, - Alias = cmd.Alias, - MinimumPermission = cmd.Permission, - AllowImpersonation = cmd.AllowImpersonation, - SupportedGames = cmd.SupportedGames - }); + new CommandProperties + { + Name = cmd.Name, + Alias = cmd.Alias, + MinimumPermission = cmd.Permission, + AllowImpersonation = cmd.AllowImpersonation, + SupportedGames = cmd.SupportedGames + }); } _commandConfiguration.Set(cmdConfig); await _commandConfiguration.Save(); + #endregion _metaRegistration.Register(); await _alertManager.Initialize(); #region CUSTOM_EVENTS + foreach (var customEvent in _customParserEvents.SelectMany(_events => _events.Events)) { foreach (var parser in AdditionalEventParsers) @@ -529,8 +552,9 @@ namespace IW4MAdmin.Application parser.RegisterCustomEvent(customEvent.Item1, customEvent.Item2, customEvent.Item3); } } + #endregion - + Console.WriteLine(_translationLookup["MANAGER_COMMUNICATION_INFO"]); await InitializeServers(); _watcher.Enable(); @@ -540,46 +564,10 @@ namespace IW4MAdmin.Application private async Task InitializeServers() { var config = ConfigHandler.Configuration(); - int successServers = 0; + var successServers = 0; Exception lastException = null; - async Task Init(ServerConfiguration Conf) - { - try - { - // todo: this might not always be an IW4MServer - var serverInstance = _serverInstanceFactory.CreateServer(Conf, this) as IW4MServer; - using (LogContext.PushProperty("Server", serverInstance!.ToString())) - { - _logger.LogInformation("Beginning server communication initialization"); - await serverInstance.Initialize(); - - _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); - } - - QueueEvent(new MonitorStartEvent - { - Server = serverInstance, - Source = this - }); - - successServers++; - } - - catch (ServerException e) - { - Console.WriteLine(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_UNFIXABLE"].FormatExt($"[{Conf.IPAddress}:{Conf.Port}]")); - using (LogContext.PushProperty("Server", $"{Conf.IPAddress}:{Conf.Port}")) - { - _logger.LogError(e, "Unexpected exception occurred during initialization"); - } - lastException = e; - } - } - - await Task.WhenAll(config.Servers.Select(c => Init(c)).ToArray()); + await Task.WhenAll(config.Servers.Select(LocalInit).ToArray()); if (successServers == 0) { @@ -593,23 +581,61 @@ namespace IW4MAdmin.Application throw lastException; } } + + return; + + async Task LocalInit(ServerConfiguration conf) + { + try + { + // todo: this might not always be an IW4MServer + var serverInstance = _serverInstanceFactory.CreateServer(conf, this) as IW4MServer; + using (LogContext.PushProperty("Server", serverInstance!.ToString())) + { + _logger.LogInformation("Beginning server communication initialization"); + await serverInstance.Initialize(); + + _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); + } + + QueueEvent(new MonitorStartEvent + { + Server = serverInstance, + Source = this + }); + + successServers++; + } + + catch (ServerException e) + { + Console.WriteLine(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_UNFIXABLE"] + .FormatExt($"[{conf.IPAddress}:{conf.Port}]")); + using (LogContext.PushProperty("Server", $"{conf.IPAddress}:{conf.Port}")) + { + _logger.LogError(e, "Unexpected exception occurred during initialization"); + } + + lastException = e; + } + } } public async Task Start() { _eventHandlerTokenSource = new CancellationTokenSource(); - - var eventHandlerThread = new Thread(() => - { - _coreEventHandler.StartProcessing(_eventHandlerTokenSource.Token); - }) + + var eventHandlerThread = new Thread(() => { _coreEventHandler.StartProcessing(_eventHandlerTokenSource.Token); }) { Name = nameof(CoreEventHandler) }; eventHandlerThread.Start(); await UpdateServerStates(); - _eventHandlerTokenSource.Cancel(); + await _eventHandlerTokenSource.CancelAsync(); eventHandlerThread.Join(); } @@ -636,7 +662,7 @@ namespace IW4MAdmin.Application { IsRestartRequested = true; await Stop(); - + using var subscriptionTimeoutToken = new CancellationTokenSource(); subscriptionTimeoutToken.CancelAfter(Utilities.DefaultCommandTimeout); @@ -645,10 +671,10 @@ namespace IW4MAdmin.Application IGameEventSubscriptions.ClearEventInvocations(); IGameServerEventSubscriptions.ClearEventInvocations(); IManagementEventSubscriptions.ClearEventInvocations(); - + _isRunningTokenSource.Dispose(); _isRunningTokenSource = new CancellationTokenSource(); - + _eventHandlerTokenSource.Dispose(); _eventHandlerTokenSource = new CancellationTokenSource(); } @@ -670,10 +696,10 @@ namespace IW4MAdmin.Application return _servers.SelectMany(s => s.Clients).ToList().Where(p => p != null).ToList(); } - public EFClient FindActiveClient(EFClient client) => client.ClientNumber < 0 ? - GetActiveClients() - .FirstOrDefault(c => c.NetworkId == client.NetworkId && c.GameName == client.GameName) ?? client : - client; + public EFClient FindActiveClient(EFClient client) => client.ClientNumber < 0 + ? GetActiveClients() + .FirstOrDefault(c => c.NetworkId == client.NetworkId && c.GameName == client.GameName) ?? client + : client; public ClientService GetClientService() { @@ -699,7 +725,7 @@ namespace IW4MAdmin.Application { _coreEventHandler.QueueEvent(this, coreEvent); } - + public IPageList GetPageList() { return PageList; @@ -715,7 +741,8 @@ namespace IW4MAdmin.Application public IEventParser GenerateDynamicEventParser(string name) { - return new DynamicEventParser(_parserRegexFactory, _logger, ConfigHandler.Configuration(), _serviceProvider.GetRequiredService()) + return new DynamicEventParser(_parserRegexFactory, _logger, ConfigHandler.Configuration(), + _serviceProvider.GetRequiredService()) { Name = name }; @@ -748,7 +775,7 @@ namespace IW4MAdmin.Application public void RemoveCommandByName(string commandName) => _commands.RemoveAll(_command => _command.Name == commandName); public IAlertManager AlertManager => _alertManager; - + private async Task OnServerValueRequested(ServerValueRequestEvent requestEvent, CancellationToken token) { if (requestEvent.Server is not IW4MServer server) @@ -768,7 +795,7 @@ namespace IW4MAdmin.Application using var timeoutTokenSource = new CancellationTokenSource(); using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(timeoutTokenSource.Token, token); - + if (requestEvent.TimeoutMs is not null) { timeoutTokenSource.CancelAfter(requestEvent.TimeoutMs.Value); diff --git a/Application/Commands/ListClientsCommand.cs b/Application/Commands/ListClientsCommand.cs index 56035546..7ba86df4 100644 --- a/Application/Commands/ListClientsCommand.cs +++ b/Application/Commands/ListClientsCommand.cs @@ -22,16 +22,14 @@ namespace IW4MAdmin.Application.Commands RequiresTarget = false; } - public override Task ExecuteAsync(GameEvent gameEvent) + public override async Task ExecuteAsync(GameEvent gameEvent) { var clientList = gameEvent.Owner.GetClientsAsList() .Select(client => $"[(Color::Accent){client.ClientPermission.Name}(Color::White){(string.IsNullOrEmpty(client.Tag) ? "" : $" {client.Tag}")}(Color::White)][(Color::Yellow)#{client.ClientNumber}(Color::White)] {client.Name}") .ToArray(); - gameEvent.Origin.TellAsync(clientList, gameEvent.Owner.Manager.CancellationToken); - - return Task.CompletedTask; + await gameEvent.Origin.TellAsync(clientList, gameEvent.Owner.Manager.CancellationToken); } } } diff --git a/Application/Factories/GameServerInstanceFactory.cs b/Application/Factories/GameServerInstanceFactory.cs index 9cac20ff..20fae8a9 100644 --- a/Application/Factories/GameServerInstanceFactory.cs +++ b/Application/Factories/GameServerInstanceFactory.cs @@ -11,26 +11,15 @@ namespace IW4MAdmin.Application.Factories /// /// implementation of IGameServerInstanceFactory /// - internal class GameServerInstanceFactory : IGameServerInstanceFactory + /// + /// + /// + internal class GameServerInstanceFactory( + ITranslationLookup translationLookup, + IMetaServiceV2 metaService, + IServiceProvider serviceProvider) + : IGameServerInstanceFactory { - private readonly ITranslationLookup _translationLookup; - private readonly IMetaServiceV2 _metaService; - private readonly IServiceProvider _serviceProvider; - - /// - /// base constructor - /// - /// - /// - public GameServerInstanceFactory(ITranslationLookup translationLookup, - IMetaServiceV2 metaService, - IServiceProvider serviceProvider) - { - _translationLookup = translationLookup; - _metaService = metaService; - _serviceProvider = serviceProvider; - } - /// /// creates an IW4MServer instance /// @@ -40,9 +29,9 @@ namespace IW4MAdmin.Application.Factories public Server CreateServer(ServerConfiguration config, IManager manager) { return new IW4MServer(config, - _serviceProvider.GetRequiredService(), _translationLookup, _metaService, - _serviceProvider, _serviceProvider.GetRequiredService(), - _serviceProvider.GetRequiredService>()); + serviceProvider.GetRequiredService(), translationLookup, metaService, + serviceProvider, serviceProvider.GetRequiredService(), + serviceProvider.GetRequiredService>()); } } } diff --git a/Application/Main.cs b/Application/Main.cs index 2c5bb257..dd6dcfb1 100644 --- a/Application/Main.cs +++ b/Application/Main.cs @@ -213,7 +213,7 @@ namespace IW4MAdmin.Application var masterCommunicator = serviceProvider.GetRequiredService(); var webfrontLifetime = serviceProvider.GetRequiredService(); using var onWebfrontErrored = new ManualResetEventSlim(); - + var webfrontTask = _serverManager.GetApplicationSettings().Configuration().EnableWebFront ? WebfrontCore.Program.GetWebHostTask(_serverManager.CancellationToken).ContinueWith(continuation => { @@ -226,9 +226,9 @@ namespace IW4MAdmin.Application continuation.Exception?.InnerException?.Message); logger.LogDebug(continuation.Exception, "Unable to start webfront task"); - - onWebfrontErrored.Set(); - + + // ReSharper disable once AccessToDisposedClosure + onWebfrontErrored.Set(); // TODO: Check if this dispose warning is a roslyn issue. }) : Task.CompletedTask; diff --git a/Application/Misc/BaseConfigurationHandler.cs b/Application/Misc/BaseConfigurationHandler.cs index b72f24cb..b0d6df68 100644 --- a/Application/Misc/BaseConfigurationHandler.cs +++ b/Application/Misc/BaseConfigurationHandler.cs @@ -52,14 +52,11 @@ namespace IW4MAdmin.Application.Misc await _onSaving.WaitAsync(); await using var fileStream = File.OpenRead(FileName); _configuration = await JsonSerializer.DeserializeAsync(fileStream, _serializerOptions); - await fileStream.DisposeAsync(); } - catch (FileNotFoundException) { _configuration = default; } - catch (Exception e) { throw new ConfigurationException("Could not load configuration") @@ -82,12 +79,9 @@ namespace IW4MAdmin.Application.Misc try { await _onSaving.WaitAsync(); - await using var fileStream = File.Create(FileName); await JsonSerializer.SerializeAsync(fileStream, _configuration, _serializerOptions); - await fileStream.DisposeAsync(); } - finally { if (_onSaving.CurrentCount == 0) diff --git a/Application/Misc/Logger.cs b/Application/Misc/Logger.cs index 4c7d4f80..35b1b48b 100644 --- a/Application/Misc/Logger.cs +++ b/Application/Misc/Logger.cs @@ -4,7 +4,7 @@ using ILogger = SharedLibraryCore.Interfaces.ILogger; namespace IW4MAdmin.Application.Misc { - [Obsolete] + [Obsolete("Use Microsoft.Extensions.Logging.ILogger instead")] public class Logger : ILogger { private readonly Microsoft.Extensions.Logging.ILogger _logger; diff --git a/Application/Misc/MiddlewareActionHandler.cs b/Application/Misc/MiddlewareActionHandler.cs index a3683623..bbb2b513 100644 --- a/Application/Misc/MiddlewareActionHandler.cs +++ b/Application/Misc/MiddlewareActionHandler.cs @@ -7,16 +7,10 @@ using ILogger = Microsoft.Extensions.Logging.ILogger; namespace IW4MAdmin.Application.Misc { - class MiddlewareActionHandler : IMiddlewareActionHandler + internal class MiddlewareActionHandler(ILogger logger) : IMiddlewareActionHandler { - private readonly IDictionary> _actions; - private readonly ILogger _logger; - - public MiddlewareActionHandler(ILogger logger) - { - _actions = new Dictionary>(); - _logger = logger; - } + private readonly Dictionary> _actions = new(); + private readonly ILogger _logger = logger; /// /// Executes the action with the given name @@ -27,23 +21,19 @@ namespace IW4MAdmin.Application.Misc /// public async Task Execute(T value, string name = null) { - string key = string.IsNullOrEmpty(name) ? typeof(T).ToString() : name; + var key = string.IsNullOrEmpty(name) ? typeof(T).ToString() : name; - if (_actions.ContainsKey(key)) + if (!_actions.TryGetValue(key, out var action1)) return value; + foreach (var action in action1) { - foreach (var action in _actions[key]) + try { - try - { - value = await ((IMiddlewareAction)action).Invoke(value); - } - catch (Exception e) - { - _logger.LogWarning(e, "Failed to invoke middleware action {name}", name); - } + value = await ((IMiddlewareAction)action).Invoke(value); + } + catch (Exception e) + { + _logger.LogWarning(e, "Failed to invoke middleware action {Name}", name); } - - return value; } return value; @@ -58,16 +48,15 @@ namespace IW4MAdmin.Application.Misc /// Name of action public void Register(T actionType, IMiddlewareAction action, string name = null) { - string key = string.IsNullOrEmpty(name) ? typeof(T).ToString() : name; + var key = string.IsNullOrEmpty(name) ? typeof(T).ToString() : name; - if (_actions.ContainsKey(key)) + if (_actions.TryGetValue(key, out var action1)) { - _actions[key].Add(action); + action1.Add(action); } - else { - _actions.Add(key, new[] { action }); + _actions.Add(key, [action]); } } } diff --git a/Integrations/Cod/CodRConConnection.cs b/Integrations/Cod/CodRConConnection.cs index 2b299361..9908a6e1 100644 --- a/Integrations/Cod/CodRConConnection.cs +++ b/Integrations/Cod/CodRConConnection.cs @@ -201,7 +201,7 @@ namespace Integrations.Cod DontFragment = false, Ttl = 100, ExclusiveAddressUse = true, - }) + }!) // Suppressing "Initialize object properties inside the 'using' statement to ensure that the object is disposed if an exception is thrown during initialization" { if (!token.IsCancellationRequested) { diff --git a/SharedLibraryCore/Helpers/BuildNumber.cs b/SharedLibraryCore/Helpers/BuildNumber.cs index 801b0556..7ba4736f 100644 --- a/SharedLibraryCore/Helpers/BuildNumber.cs +++ b/SharedLibraryCore/Helpers/BuildNumber.cs @@ -107,9 +107,7 @@ namespace SharedLibraryCore.Helpers private static int ParseVersion(string input) { - int version; - - if (!int.TryParse(input, out version)) + if (!int.TryParse(input, out var version)) { throw new FormatException( "buildNumber string was not in a correct format"); @@ -160,4 +158,4 @@ namespace SharedLibraryCore.Helpers } } } -} \ No newline at end of file +} diff --git a/SharedLibraryCore/Interfaces/ILogger.cs b/SharedLibraryCore/Interfaces/ILogger.cs index efbd65c5..77c359fe 100644 --- a/SharedLibraryCore/Interfaces/ILogger.cs +++ b/SharedLibraryCore/Interfaces/ILogger.cs @@ -2,7 +2,7 @@ namespace SharedLibraryCore.Interfaces { - [Obsolete] + [Obsolete("Use Microsoft.Extensions.Logging.ILogger instead")] public interface ILogger { void WriteVerbose(string msg); @@ -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/Utilities.cs b/SharedLibraryCore/Utilities.cs index 34843cab..db5babbc 100644 --- a/SharedLibraryCore/Utilities.cs +++ b/SharedLibraryCore/Utilities.cs @@ -799,6 +799,8 @@ namespace SharedLibraryCore } public static async Task> GetDvarAsync(this Server server, string dvarName, + // Suppressing as older plugins could reference old signature. + // ReSharper disable once MethodOverloadWithOptionalParameter T fallbackValue = default, CancellationToken token = default) { return await server.RconParser.GetDvarAsync(server.RemoteConnection, dvarName, fallbackValue, token); @@ -818,9 +820,8 @@ namespace SharedLibraryCore var mappedKey = server.RconParser.GetOverrideDvarName(dvarName); var defaultValue = server.RconParser.GetDefaultDvarValue(mappedKey) ?? overrideDefault; - var foundKey = infoResponse?.Keys - .Where(_key => new[] { mappedKey, dvarName, infoResponseName ?? dvarName }.Contains(_key)) - .FirstOrDefault(); + var foundKey = (infoResponse?.Keys ?? Enumerable.Empty()) + .FirstOrDefault(key => new[] { mappedKey, dvarName, infoResponseName ?? dvarName }.Contains(key)); if (!string.IsNullOrEmpty(foundKey)) { diff --git a/WebfrontCore/Views/Client/Statistics/Advanced.cshtml b/WebfrontCore/Views/Client/Statistics/Advanced.cshtml index 09b80ce8..f01763db 100644 --- a/WebfrontCore/Views/Client/Statistics/Advanced.cshtml +++ b/WebfrontCore/Views/Client/Statistics/Advanced.cshtml @@ -129,7 +129,7 @@ .OrderBy(rating => rating.CreatedDateTime) .Select(rating => new PerformanceHistory { Performance = rating.PerformanceMetric, OccurredAt = rating.CreatedDateTime }); - if (performance != null && performance != Model.Ratings.FirstOrDefault().PerformanceMetric) + if (performance != null && !Model.Ratings.FirstOrDefault().PerformanceMetric.Equals(performance)) { performanceHistory = performanceHistory.Append(new PerformanceHistory { Performance = performance.Value, OccurredAt = Model.Ratings.FirstOrDefault()?.CreatedDateTime ?? DateTime.UtcNow }); } diff --git a/WebfrontCore/Views/Configuration/Index.cshtml b/WebfrontCore/Views/Configuration/Index.cshtml index 112deccb..2212e408 100644 --- a/WebfrontCore/Views/Configuration/Index.cshtml +++ b/WebfrontCore/Views/Configuration/Index.cshtml @@ -89,7 +89,7 @@ else if (hasLinkedParent(property)) { -
+
@if (linkedPropertyNames.Length == 0) { @Html.Label(property.Name, null, new {@class = "mt-2 d-block"})