refactor logging in pretty big overhaul

This commit is contained in:
RaidMax 2020-11-11 17:31:26 -06:00
parent fd7bd7e0da
commit 570a228c92
86 changed files with 1603 additions and 1534 deletions

1
.gitignore vendored
View File

@ -244,3 +244,4 @@ launchSettings.json
/Tests/ApplicationTests/Files/GameEvents.json /Tests/ApplicationTests/Files/GameEvents.json
/Tests/ApplicationTests/Files/replay.json /Tests/ApplicationTests/Files/replay.json
/GameLogServer/game_log_server_env /GameLogServer/game_log_server_env
.idea/*

View File

@ -1,7 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using IW4MAdmin.Application.Helpers; using IW4MAdmin.Application.Misc;
using Newtonsoft.Json; using Newtonsoft.Json;
using RestEase; using RestEase;
using SharedLibraryCore.Helpers; using SharedLibraryCore.Helpers;

View File

@ -21,6 +21,8 @@
<Win32Resource /> <Win32Resource />
<RootNamespace>IW4MAdmin.Application</RootNamespace> <RootNamespace>IW4MAdmin.Application</RootNamespace>
<PublishWithAspNetCoreTargetManifest>false</PublishWithAspNetCoreTargetManifest> <PublishWithAspNetCoreTargetManifest>false</PublishWithAspNetCoreTargetManifest>
<AssemblyVersion>2020.0.0.0</AssemblyVersion>
<FileVersion>2020.0.0.0</FileVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -59,6 +61,9 @@
<None Update="DefaultSettings.json"> <None Update="DefaultSettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>
<None Update="Configuration\LoggingConfiguration.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup> </ItemGroup>
<Target Name="PreBuild" BeforeTargets="PreBuildEvent"> <Target Name="PreBuild" BeforeTargets="PreBuildEvent">

View File

@ -1,5 +1,4 @@
using IW4MAdmin.Application.API.Master; using IW4MAdmin.Application.EventParsers;
using IW4MAdmin.Application.EventParsers;
using IW4MAdmin.Application.Extensions; using IW4MAdmin.Application.Extensions;
using IW4MAdmin.Application.Misc; using IW4MAdmin.Application.Misc;
using IW4MAdmin.Application.RconParsers; using IW4MAdmin.Application.RconParsers;
@ -9,11 +8,9 @@ using SharedLibraryCore.Configuration;
using SharedLibraryCore.Configuration.Validation; using SharedLibraryCore.Configuration.Validation;
using SharedLibraryCore.Database; using SharedLibraryCore.Database;
using SharedLibraryCore.Database.Models; using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Dtos;
using SharedLibraryCore.Exceptions; using SharedLibraryCore.Exceptions;
using SharedLibraryCore.Helpers; using SharedLibraryCore.Helpers;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using SharedLibraryCore.QueryHelper;
using SharedLibraryCore.Services; using SharedLibraryCore.Services;
using System; using System;
using System.Collections; using System.Collections;
@ -24,7 +21,12 @@ using System.Reflection;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Serilog.Context;
using static SharedLibraryCore.GameEvent; using static SharedLibraryCore.GameEvent;
using ILogger = Microsoft.Extensions.Logging.ILogger;
using ObsoleteLogger = SharedLibraryCore.Interfaces.ILogger;
namespace IW4MAdmin.Application namespace IW4MAdmin.Application
{ {
@ -32,7 +34,7 @@ namespace IW4MAdmin.Application
{ {
private readonly ConcurrentBag<Server> _servers; private readonly ConcurrentBag<Server> _servers;
public List<Server> Servers => _servers.OrderByDescending(s => s.ClientNum).ToList(); public List<Server> Servers => _servers.OrderByDescending(s => s.ClientNum).ToList();
public ILogger Logger => GetLogger(0); [Obsolete] public ObsoleteLogger Logger => _serviceProvider.GetRequiredService<ObsoleteLogger>();
public bool IsRunning { get; private set; } public bool IsRunning { get; private set; }
public bool IsInitialized { get; private set; } public bool IsInitialized { get; private set; }
public DateTime StartTime { get; private set; } public DateTime StartTime { get; private set; }
@ -54,7 +56,6 @@ namespace IW4MAdmin.Application
readonly PenaltyService PenaltySvc; readonly PenaltyService PenaltySvc;
public IConfigurationHandler<ApplicationConfiguration> ConfigHandler; public IConfigurationHandler<ApplicationConfiguration> ConfigHandler;
readonly IPageList PageList; readonly IPageList PageList;
private readonly Dictionary<long, ILogger> _loggers = new Dictionary<long, ILogger>();
private readonly IMetaService _metaService; private readonly IMetaService _metaService;
private readonly TimeSpan _throttleTimeout = new TimeSpan(0, 1, 0); private readonly TimeSpan _throttleTimeout = new TimeSpan(0, 1, 0);
private readonly CancellationTokenSource _tokenSource; private readonly CancellationTokenSource _tokenSource;
@ -68,30 +69,33 @@ namespace IW4MAdmin.Application
private readonly IScriptCommandFactory _scriptCommandFactory; private readonly IScriptCommandFactory _scriptCommandFactory;
private readonly IMetaRegistration _metaRegistration; private readonly IMetaRegistration _metaRegistration;
private readonly IScriptPluginServiceResolver _scriptPluginServiceResolver; private readonly IScriptPluginServiceResolver _scriptPluginServiceResolver;
private readonly IServiceProvider _serviceProvider;
private readonly ChangeHistoryService _changeHistoryService;
private readonly ApplicationConfiguration _appConfig;
public ApplicationManager(ILogger logger, IMiddlewareActionHandler actionHandler, IEnumerable<IManagerCommand> commands, public ApplicationManager(ILogger<ApplicationManager> logger, IMiddlewareActionHandler actionHandler, IEnumerable<IManagerCommand> commands,
ITranslationLookup translationLookup, IConfigurationHandler<CommandConfiguration> commandConfiguration, ITranslationLookup translationLookup, IConfigurationHandler<CommandConfiguration> commandConfiguration,
IConfigurationHandler<ApplicationConfiguration> appConfigHandler, IGameServerInstanceFactory serverInstanceFactory, IConfigurationHandler<ApplicationConfiguration> appConfigHandler, IGameServerInstanceFactory serverInstanceFactory,
IEnumerable<IPlugin> plugins, IParserRegexFactory parserRegexFactory, IEnumerable<IRegisterEvent> customParserEvents, IEnumerable<IPlugin> plugins, IParserRegexFactory parserRegexFactory, IEnumerable<IRegisterEvent> customParserEvents,
IEventHandler eventHandler, IScriptCommandFactory scriptCommandFactory, IDatabaseContextFactory contextFactory, IMetaService metaService, IEventHandler eventHandler, IScriptCommandFactory scriptCommandFactory, IDatabaseContextFactory contextFactory, IMetaService metaService,
IMetaRegistration metaRegistration, IScriptPluginServiceResolver scriptPluginServiceResolver) IMetaRegistration metaRegistration, IScriptPluginServiceResolver scriptPluginServiceResolver, ClientService clientService, IServiceProvider serviceProvider,
ChangeHistoryService changeHistoryService, ApplicationConfiguration appConfig)
{ {
MiddlewareActionHandler = actionHandler; MiddlewareActionHandler = actionHandler;
_servers = new ConcurrentBag<Server>(); _servers = new ConcurrentBag<Server>();
MessageTokens = new List<MessageToken>(); MessageTokens = new List<MessageToken>();
ClientSvc = new ClientService(contextFactory); ClientSvc = clientService;
AliasSvc = new AliasService(); AliasSvc = new AliasService();
PenaltySvc = new PenaltyService(); PenaltySvc = new PenaltyService();
ConfigHandler = appConfigHandler; ConfigHandler = appConfigHandler;
StartTime = DateTime.UtcNow; StartTime = DateTime.UtcNow;
PageList = new PageList(); PageList = new PageList();
AdditionalEventParsers = new List<IEventParser>() { new BaseEventParser(parserRegexFactory, logger, appConfigHandler.Configuration()) }; AdditionalEventParsers = new List<IEventParser>() { new BaseEventParser(parserRegexFactory, logger, _appConfig) };
AdditionalRConParsers = new List<IRConParser>() { new BaseRConParser(parserRegexFactory) }; AdditionalRConParsers = new List<IRConParser>() { new BaseRConParser(serviceProvider.GetRequiredService<ILogger<BaseRConParser>>(), parserRegexFactory) };
TokenAuthenticator = new TokenAuthentication(); TokenAuthenticator = new TokenAuthentication();
_logger = logger; _logger = logger;
_metaService = metaService; _metaService = metaService;
_tokenSource = new CancellationTokenSource(); _tokenSource = new CancellationTokenSource();
_loggers.Add(0, logger);
_commands = commands.ToList(); _commands = commands.ToList();
_translationLookup = translationLookup; _translationLookup = translationLookup;
_commandConfiguration = commandConfiguration; _commandConfiguration = commandConfiguration;
@ -102,6 +106,9 @@ namespace IW4MAdmin.Application
_scriptCommandFactory = scriptCommandFactory; _scriptCommandFactory = scriptCommandFactory;
_metaRegistration = metaRegistration; _metaRegistration = metaRegistration;
_scriptPluginServiceResolver = scriptPluginServiceResolver; _scriptPluginServiceResolver = scriptPluginServiceResolver;
_serviceProvider = serviceProvider;
_changeHistoryService = changeHistoryService;
_appConfig = appConfig;
Plugins = plugins; Plugins = plugins;
} }
@ -109,10 +116,6 @@ namespace IW4MAdmin.Application
public async Task ExecuteEvent(GameEvent newEvent) public async Task ExecuteEvent(GameEvent newEvent)
{ {
#if DEBUG == true
Logger.WriteDebug($"Entering event process for {newEvent.Id}");
#endif
// the event has failed already // the event has failed already
if (newEvent.Failed) if (newEvent.Failed)
{ {
@ -124,22 +127,17 @@ namespace IW4MAdmin.Application
await newEvent.Owner.ExecuteEvent(newEvent); await newEvent.Owner.ExecuteEvent(newEvent);
// save the event info to the database // save the event info to the database
var changeHistorySvc = new ChangeHistoryService(); await _changeHistoryService.Add(newEvent);
await changeHistorySvc.Add(newEvent);
#if DEBUG
Logger.WriteDebug($"Processed event with id {newEvent.Id}");
#endif
} }
catch (TaskCanceledException) catch (TaskCanceledException)
{ {
Logger.WriteInfo($"Received quit signal for event id {newEvent.Id}, so we are aborting early"); _logger.LogDebug("Received quit signal for event id {eventId}, so we are aborting early", newEvent.Id);
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
Logger.WriteInfo($"Received quit signal for event id {newEvent.Id}, so we are aborting early"); _logger.LogDebug("Received quit signal for event id {eventId}, so we are aborting early", newEvent.Id);
} }
// this happens if a plugin requires login // this happens if a plugin requires login
@ -152,31 +150,35 @@ namespace IW4MAdmin.Application
catch (NetworkException ex) catch (NetworkException ex)
{ {
newEvent.FailReason = EventFailReason.Exception; newEvent.FailReason = EventFailReason.Exception;
Logger.WriteError(ex.Message); using (LogContext.PushProperty("Server", newEvent.Owner?.ToString()))
Logger.WriteDebug(ex.GetExceptionInfo()); {
_logger.LogError(ex, ex.Message);
}
} }
catch (ServerException ex) catch (ServerException ex)
{ {
newEvent.FailReason = EventFailReason.Exception; newEvent.FailReason = EventFailReason.Exception;
Logger.WriteWarning(ex.Message); using (LogContext.PushProperty("Server", newEvent.Owner?.ToString()))
{
_logger.LogError(ex, ex.Message);
}
} }
catch (Exception ex) catch (Exception ex)
{ {
newEvent.FailReason = EventFailReason.Exception; newEvent.FailReason = EventFailReason.Exception;
Logger.WriteError(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_EXCEPTION"].FormatExt(newEvent.Owner)); Console.WriteLine(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_EXCEPTION"].FormatExt(newEvent.Owner));
Logger.WriteDebug(ex.GetExceptionInfo()); using (LogContext.PushProperty("Server", newEvent.Owner?.ToString()))
{
_logger.LogError(ex, "Unexpected exception");
}
} }
skip: skip:
// tell anyone waiting for the output that we're done // tell anyone waiting for the output that we're done
newEvent.Complete(); newEvent.Complete();
OnGameEventExecuted?.Invoke(this, newEvent); OnGameEventExecuted?.Invoke(this, newEvent);
#if DEBUG == true
Logger.WriteDebug($"Exiting event process for {newEvent.Id}");
#endif
} }
public IList<Server> GetServers() public IList<Server> GetServers()
@ -226,17 +228,14 @@ namespace IW4MAdmin.Application
try try
{ {
await server.ProcessUpdatesAsync(_tokenSource.Token); await server.ProcessUpdatesAsync(_tokenSource.Token);
if (server.Throttled)
{
await Task.Delay((int)_throttleTimeout.TotalMilliseconds, _tokenSource.Token);
}
} }
catch (Exception e) catch (Exception e)
{ {
Logger.WriteWarning($"Failed to update status for {server}"); using (LogContext.PushProperty("Server", server.ToString()))
Logger.WriteDebug(e.GetExceptionInfo()); {
_logger.LogError(e, "Failed to update status");
}
} }
finally finally
@ -245,12 +244,7 @@ namespace IW4MAdmin.Application
} }
})); }));
} }
#if DEBUG
Logger.WriteDebug($"{runningUpdateTasks.Count} servers queued for stats updates");
ThreadPool.GetMaxThreads(out int workerThreads, out int n);
ThreadPool.GetAvailableThreads(out int availableThreads, out int m);
Logger.WriteDebug($"There are {workerThreads - availableThreads} active threading tasks");
#endif
try try
{ {
await Task.Delay(ConfigHandler.Configuration().RConPollRate, _tokenSource.Token); await Task.Delay(ConfigHandler.Configuration().RConPollRate, _tokenSource.Token);
@ -289,8 +283,8 @@ namespace IW4MAdmin.Application
catch (Exception ex) catch (Exception ex)
{ {
Logger.WriteError(Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_IMPORTER_ERROR"].FormatExt(scriptPlugin.Name)); Console.WriteLine(Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_IMPORTER_ERROR"].FormatExt(scriptPlugin.Name));
Logger.WriteDebug(ex.Message); _logger.LogError(ex, "Could not properly load plugin {plugin}", scriptPlugin.Name);
} }
}; };
} }
@ -303,32 +297,29 @@ namespace IW4MAdmin.Application
catch (Exception ex) catch (Exception ex)
{ {
Logger.WriteError($"{_translationLookup["SERVER_ERROR_PLUGIN"]} {plugin.Name}"); _logger.LogError(ex, $"{_translationLookup["SERVER_ERROR_PLUGIN"]} {plugin.Name}");
Logger.WriteDebug(ex.GetExceptionInfo());
} }
} }
#endregion #endregion
#region CONFIG #region CONFIG
var config = ConfigHandler.Configuration();
// copy over default config if it doesn't exist // copy over default config if it doesn't exist
if (config == null) if (!_appConfig.Servers?.Any() ?? true)
{ {
var defaultConfig = new BaseConfigurationHandler<DefaultConfiguration>("DefaultSettings").Configuration(); var defaultConfig = new BaseConfigurationHandler<DefaultConfiguration>("DefaultSettings").Configuration();
ConfigHandler.Set((ApplicationConfiguration)new ApplicationConfiguration().Generate()); //ConfigHandler.Set((ApplicationConfiguration)new ApplicationConfiguration().Generate());
var newConfig = ConfigHandler.Configuration(); //var newConfig = ConfigHandler.Configuration();
newConfig.AutoMessages = defaultConfig.AutoMessages; _appConfig.AutoMessages = defaultConfig.AutoMessages;
newConfig.GlobalRules = defaultConfig.GlobalRules; _appConfig.GlobalRules = defaultConfig.GlobalRules;
newConfig.Maps = defaultConfig.Maps; _appConfig.Maps = defaultConfig.Maps;
newConfig.DisallowedClientNames = defaultConfig.DisallowedClientNames; _appConfig.DisallowedClientNames = defaultConfig.DisallowedClientNames;
newConfig.QuickMessages = defaultConfig.QuickMessages; _appConfig.QuickMessages = defaultConfig.QuickMessages;
if (newConfig.Servers == null) //if (newConfig.Servers == null)
{ {
ConfigHandler.Set(newConfig); ConfigHandler.Set(_appConfig);
newConfig.Servers = new ServerConfiguration[1]; _appConfig.Servers = new ServerConfiguration[1];
do do
{ {
@ -343,30 +334,29 @@ namespace IW4MAdmin.Application
serverConfig.AddEventParser(parser); serverConfig.AddEventParser(parser);
} }
newConfig.Servers = newConfig.Servers.Where(_servers => _servers != null).Append((ServerConfiguration)serverConfig.Generate()).ToArray(); _appConfig.Servers = _appConfig.Servers.Where(_servers => _servers != null).Append((ServerConfiguration)serverConfig.Generate()).ToArray();
} while (Utilities.PromptBool(_translationLookup["SETUP_SERVER_SAVE"])); } while (Utilities.PromptBool(_translationLookup["SETUP_SERVER_SAVE"]));
config = newConfig;
await ConfigHandler.Save(); await ConfigHandler.Save();
} }
} }
else else
{ {
if (string.IsNullOrEmpty(config.Id)) if (string.IsNullOrEmpty(_appConfig.Id))
{ {
config.Id = Guid.NewGuid().ToString(); _appConfig.Id = Guid.NewGuid().ToString();
await ConfigHandler.Save(); await ConfigHandler.Save();
} }
if (string.IsNullOrEmpty(config.WebfrontBindUrl)) if (string.IsNullOrEmpty(_appConfig.WebfrontBindUrl))
{ {
config.WebfrontBindUrl = "http://0.0.0.0:1624"; _appConfig.WebfrontBindUrl = "http://0.0.0.0:1624";
await ConfigHandler.Save(); await ConfigHandler.Save();
} }
var validator = new ApplicationConfigurationValidator(); var validator = new ApplicationConfigurationValidator();
var validationResult = validator.Validate(config); var validationResult = validator.Validate(_appConfig);
if (!validationResult.IsValid) if (!validationResult.IsValid)
{ {
@ -377,7 +367,7 @@ namespace IW4MAdmin.Application
}; };
} }
foreach (var serverConfig in config.Servers) foreach (var serverConfig in _appConfig.Servers)
{ {
Migration.ConfigurationMigration.ModifyLogPath020919(serverConfig); Migration.ConfigurationMigration.ModifyLogPath020919(serverConfig);
@ -399,13 +389,13 @@ namespace IW4MAdmin.Application
} }
} }
if (config.Servers.Length == 0) if (_appConfig.Servers.Length == 0)
{ {
throw new ServerException("A server configuration in IW4MAdminSettings.json is invalid"); throw new ServerException("A server configuration in IW4MAdminSettings.json is invalid");
} }
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
Utilities.EncodingType = Encoding.GetEncoding(!string.IsNullOrEmpty(config.CustomParserEncoding) ? config.CustomParserEncoding : "windows-1252"); Utilities.EncodingType = Encoding.GetEncoding(!string.IsNullOrEmpty(_appConfig.CustomParserEncoding) ? _appConfig.CustomParserEncoding : "windows-1252");
#endregion #endregion
@ -440,8 +430,8 @@ namespace IW4MAdmin.Application
// this is because I want to store the command prefix in IW4MAdminSettings, but can't easily // this is because I want to store the command prefix in IW4MAdminSettings, but can't easily
// inject it to all the places that need it // inject it to all the places that need it
cmdConfig.CommandPrefix = config.CommandPrefix; cmdConfig.CommandPrefix = _appConfig.CommandPrefix;
cmdConfig.BroadcastCommandPrefix = config.BroadcastCommandPrefix; cmdConfig.BroadcastCommandPrefix = _appConfig.BroadcastCommandPrefix;
foreach (var cmd in commandsToAddToConfig) foreach (var cmd in commandsToAddToConfig)
{ {
@ -472,6 +462,7 @@ namespace IW4MAdmin.Application
} }
#endregion #endregion
Console.WriteLine(_translationLookup["MANAGER_COMMUNICATION_INFO"]);
await InitializeServers(); await InitializeServers();
} }
@ -487,13 +478,17 @@ namespace IW4MAdmin.Application
{ {
// todo: this might not always be an IW4MServer // todo: this might not always be an IW4MServer
var ServerInstance = _serverInstanceFactory.CreateServer(Conf, this) as IW4MServer; var ServerInstance = _serverInstanceFactory.CreateServer(Conf, this) as IW4MServer;
await ServerInstance.Initialize(); using (LogContext.PushProperty("Server", ServerInstance.ToString()))
{
_logger.LogInformation("Beginning server communication initialization");
await ServerInstance.Initialize();
_servers.Add(ServerInstance); _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.WriteVerbose(Utilities.CurrentLocalization.LocalizationIndex["MANAGER_MONITORING_TEXT"].FormatExt(ServerInstance.Hostname));
// add the start event for this server // add the start event for this server
var e = new GameEvent() var e = new GameEvent()
{ {
Type = GameEvent.EventType.Start, Type = GameEvent.EventType.Start,
@ -507,13 +502,11 @@ namespace IW4MAdmin.Application
catch (ServerException e) catch (ServerException e)
{ {
Logger.WriteError(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_UNFIXABLE"].FormatExt($"[{Conf.IPAddress}:{Conf.Port}]")); Console.WriteLine(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_UNFIXABLE"].FormatExt($"[{Conf.IPAddress}:{Conf.Port}]"));
using (LogContext.PushProperty("Server", $"{Conf.IPAddress}:{Conf.Port}"))
if (e.GetType() == typeof(DvarException))
{ {
Logger.WriteDebug($"{e.Message} {(e.GetType() == typeof(DvarException) ? $"({Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_DVAR_HELP"]})" : "")}"); _logger.LogError(e, "Unexpected exception occurred during initialization");
} }
lastException = e; lastException = e;
} }
} }
@ -548,20 +541,10 @@ namespace IW4MAdmin.Application
Stop(); Stop();
} }
public ILogger GetLogger(long serverId) [Obsolete]
public ObsoleteLogger GetLogger(long serverId)
{ {
if (_loggers.ContainsKey(serverId)) return _serviceProvider.GetRequiredService<ObsoleteLogger>();
{
return _loggers[serverId];
}
else
{
var newLogger = new Logger($"IW4MAdmin-Server-{serverId}");
_loggers.Add(serverId, newLogger);
return newLogger;
}
} }
public IList<MessageToken> GetMessageTokens() public IList<MessageToken> GetMessageTokens()
@ -607,7 +590,7 @@ namespace IW4MAdmin.Application
public IRConParser GenerateDynamicRConParser(string name) public IRConParser GenerateDynamicRConParser(string name)
{ {
return new DynamicRConParser(_parserRegexFactory) return new DynamicRConParser(_serviceProvider.GetRequiredService<ILogger<BaseRConParser>>(), _parserRegexFactory)
{ {
Name = name Name = name
}; };

View File

@ -4,24 +4,13 @@ set TargetDir=%3
set OutDir=%4 set OutDir=%4
set Version=%5 set Version=%5
echo %Version% > "%SolutionDir%DEPLOY\version.txt"
echo Copying dependency configs echo Copying dependency configs
copy "%SolutionDir%WebfrontCore\%OutDir%*.deps.json" "%TargetDir%" copy "%SolutionDir%WebfrontCore\%OutDir%*.deps.json" "%TargetDir%"
copy "%SolutionDir%SharedLibaryCore\%OutDir%*.deps.json" "%TargetDir%" copy "%SolutionDir%SharedLibraryCore\%OutDir%*.deps.json" "%TargetDir%"
if not exist "%TargetDir%Plugins" ( if not exist "%TargetDir%Plugins" (
echo "Making plugin dir" echo "Making plugin dir"
md "%TargetDir%Plugins" md "%TargetDir%Plugins"
) )
xcopy /y "%SolutionDir%Build\Plugins" "%TargetDir%Plugins\" xcopy /y "%SolutionDir%Build\Plugins" "%TargetDir%Plugins\"
echo Copying plugins for publish
del %SolutionDir%BUILD\Plugins\Tests.dll
xcopy /Y "%SolutionDir%BUILD\Plugins" "%SolutionDir%Publish\Windows\Plugins\"
xcopy /Y "%SolutionDir%BUILD\Plugins" "%SolutionDir%Publish\WindowsPrerelease\Plugins\"
echo Copying script plugins for publish
xcopy /Y "%SolutionDir%Plugins\ScriptPlugins" "%SolutionDir%Publish\Windows\Plugins\"
xcopy /Y "%SolutionDir%Plugins\ScriptPlugins" "%SolutionDir%Publish\WindowsPrerelease\Plugins\"

View File

@ -0,0 +1,43 @@
{
"Serilog": {
"Using": [
"Serilog.Sinks.File"
],
"MinimumLevel": "Information",
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "Log/IW4MAdmin-Application.log",
"rollingInterval": "Day",
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff} {Server} {Level:u3}] {Message:lj}{NewLine}{Exception}"
}
}
],
"Enrich": [
"FromLogContext",
"WithMachineName",
"WithThreadId"
],
"Destructure": [
{
"Name": "ToMaximumDepth",
"Args": {
"maximumDestructuringDepth": 4
}
},
{
"Name": "ToMaximumStringLength",
"Args": {
"maximumStringLength": 1000
}
},
{
"Name": "ToMaximumCollectionCount",
"Args": {
"maximumCollectionCount": 24
}
}
]
}
}

View File

@ -5,7 +5,9 @@ using SharedLibraryCore.Interfaces;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Microsoft.Extensions.Logging;
using static SharedLibraryCore.Server; using static SharedLibraryCore.Server;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace IW4MAdmin.Application.EventParsers namespace IW4MAdmin.Application.EventParsers
{ {
@ -348,7 +350,7 @@ namespace IW4MAdmin.Application.EventParsers
catch (Exception e) catch (Exception e)
{ {
_logger.WriteWarning($"Could not handle custom event generation - {e.GetExceptionInfo()}"); _logger.LogError(e, $"Could not handle custom event generation");
} }
} }

View File

@ -1,5 +1,6 @@
using SharedLibraryCore.Configuration; using SharedLibraryCore.Configuration;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace IW4MAdmin.Application.EventParsers namespace IW4MAdmin.Application.EventParsers
{ {

View File

@ -0,0 +1,41 @@
using System.IO;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Serilog;
using SharedLibraryCore;
using SharedLibraryCore.Configuration;
namespace IW4MAdmin.Application.Extensions
{
public static class StartupExtensions
{
private static ILogger _defaultLogger = null;
public static IServiceCollection AddBaseLogger(this IServiceCollection services,
ApplicationConfiguration appConfig)
{
if (_defaultLogger == null)
{
var configuration = new ConfigurationBuilder()
.AddJsonFile(Path.Join(Utilities.OperatingDirectory, "Configuration", "LoggingConfiguration.json"))
.Build();
var loggerConfig = new LoggerConfiguration()
.ReadFrom.Configuration(configuration);
if (Utilities.IsDevelopment)
{
loggerConfig = loggerConfig.WriteTo.Console(
outputTemplate:"[{Timestamp:yyyy-MM-dd HH:mm:ss.fff} {Server} {Level:u3}] {Message:lj}{NewLine}{Exception}")
.MinimumLevel.Debug();
}
_defaultLogger = loggerConfig.CreateLogger();
}
services.AddLogging(builder => builder.AddSerilog(_defaultLogger, dispose: true));
return services;
}
}
}

View File

@ -2,6 +2,7 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using System; using System;
using Microsoft.Extensions.Logging;
namespace IW4MAdmin.Application.Factories namespace IW4MAdmin.Application.Factories
{ {
@ -19,12 +20,12 @@ namespace IW4MAdmin.Application.Factories
var baseUri = logUris[0]; var baseUri = logUris[0];
if (baseUri.Scheme == Uri.UriSchemeHttp) if (baseUri.Scheme == Uri.UriSchemeHttp)
{ {
return new GameLogReaderHttp(logUris, eventParser, _serviceProvider.GetRequiredService<ILogger>()); return new GameLogReaderHttp(logUris, eventParser, _serviceProvider.GetRequiredService<ILogger<GameLogReaderHttp>>());
} }
else if (baseUri.Scheme == Uri.UriSchemeFile) else if (baseUri.Scheme == Uri.UriSchemeFile)
{ {
return new GameLogReader(baseUri.LocalPath, eventParser, _serviceProvider.GetRequiredService<ILogger>()); return new GameLogReader(baseUri.LocalPath, eventParser, _serviceProvider.GetRequiredService<ILogger<GameLogReader>>());
} }
throw new NotImplementedException($"No log reader implemented for Uri scheme \"{baseUri.Scheme}\""); throw new NotImplementedException($"No log reader implemented for Uri scheme \"{baseUri.Scheme}\"");

View File

@ -1,7 +1,7 @@
using SharedLibraryCore; using System;
using SharedLibraryCore;
using SharedLibraryCore.Configuration; using SharedLibraryCore.Configuration;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using System.Collections;
namespace IW4MAdmin.Application.Factories namespace IW4MAdmin.Application.Factories
{ {
@ -11,21 +11,21 @@ namespace IW4MAdmin.Application.Factories
internal class GameServerInstanceFactory : IGameServerInstanceFactory internal class GameServerInstanceFactory : IGameServerInstanceFactory
{ {
private readonly ITranslationLookup _translationLookup; private readonly ITranslationLookup _translationLookup;
private readonly IRConConnectionFactory _rconConnectionFactory;
private readonly IGameLogReaderFactory _gameLogReaderFactory;
private readonly IMetaService _metaService; private readonly IMetaService _metaService;
private readonly IServiceProvider _serviceProvider;
/// <summary> /// <summary>
/// base constructor /// base constructor
/// </summary> /// </summary>
/// <param name="translationLookup"></param> /// <param name="translationLookup"></param>
/// <param name="rconConnectionFactory"></param> /// <param name="rconConnectionFactory"></param>
public GameServerInstanceFactory(ITranslationLookup translationLookup, IRConConnectionFactory rconConnectionFactory, IGameLogReaderFactory gameLogReaderFactory, IMetaService metaService) public GameServerInstanceFactory(ITranslationLookup translationLookup,
IMetaService metaService,
IServiceProvider serviceProvider)
{ {
_translationLookup = translationLookup; _translationLookup = translationLookup;
_rconConnectionFactory = rconConnectionFactory;
_gameLogReaderFactory = gameLogReaderFactory;
_metaService = metaService; _metaService = metaService;
_serviceProvider = serviceProvider;
} }
/// <summary> /// <summary>
@ -36,7 +36,7 @@ namespace IW4MAdmin.Application.Factories
/// <returns></returns> /// <returns></returns>
public Server CreateServer(ServerConfiguration config, IManager manager) public Server CreateServer(ServerConfiguration config, IManager manager)
{ {
return new IW4MServer(manager, config, _translationLookup, _rconConnectionFactory, _gameLogReaderFactory, _metaService); return new IW4MServer(config, _translationLookup, _metaService, _serviceProvider);
} }
} }
} }

View File

@ -1,6 +1,7 @@
using IW4MAdmin.Application.RCon; using IW4MAdmin.Application.RCon;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using System.Text; using System.Text;
using Microsoft.Extensions.Logging;
namespace IW4MAdmin.Application.Factories namespace IW4MAdmin.Application.Factories
{ {
@ -10,13 +11,13 @@ namespace IW4MAdmin.Application.Factories
internal class RConConnectionFactory : IRConConnectionFactory internal class RConConnectionFactory : IRConConnectionFactory
{ {
private static readonly Encoding gameEncoding = Encoding.GetEncoding("windows-1252"); private static readonly Encoding gameEncoding = Encoding.GetEncoding("windows-1252");
private readonly ILogger _logger; private readonly ILogger<RConConnection> _logger;
/// <summary> /// <summary>
/// Base constructor /// Base constructor
/// </summary> /// </summary>
/// <param name="logger"></param> /// <param name="logger"></param>
public RConConnectionFactory(ILogger logger) public RConConnectionFactory(ILogger<RConConnection> logger)
{ {
_logger = logger; _logger = logger;
} }

View File

@ -6,6 +6,8 @@ using SharedLibraryCore.Interfaces;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using static SharedLibraryCore.Database.Models.EFClient; using static SharedLibraryCore.Database.Models.EFClient;
namespace IW4MAdmin.Application.Factories namespace IW4MAdmin.Application.Factories
@ -15,17 +17,20 @@ namespace IW4MAdmin.Application.Factories
/// </summary> /// </summary>
public class ScriptCommandFactory : IScriptCommandFactory public class ScriptCommandFactory : IScriptCommandFactory
{ {
private CommandConfiguration _config; private readonly CommandConfiguration _config;
private readonly ITranslationLookup _transLookup; private readonly ITranslationLookup _transLookup;
private readonly IServiceProvider _serviceProvider;
public ScriptCommandFactory(CommandConfiguration config, ITranslationLookup transLookup) public ScriptCommandFactory(CommandConfiguration config, ITranslationLookup transLookup, IServiceProvider serviceProvider)
{ {
_config = config; _config = config;
_transLookup = transLookup; _transLookup = transLookup;
_serviceProvider = serviceProvider;
} }
/// <inheritdoc/> /// <inheritdoc/>
public IManagerCommand CreateScriptCommand(string name, string alias, string description, string permission, bool isTargetRequired, IEnumerable<(string, bool)> args, Action<GameEvent> executeAction) public IManagerCommand CreateScriptCommand(string name, string alias, string description, string permission,
bool isTargetRequired, IEnumerable<(string, bool)> args, Action<GameEvent> executeAction)
{ {
var permissionEnum = Enum.Parse<Permission>(permission); var permissionEnum = Enum.Parse<Permission>(permission);
var argsArray = args.Select(_arg => new CommandArgument var argsArray = args.Select(_arg => new CommandArgument
@ -34,7 +39,8 @@ namespace IW4MAdmin.Application.Factories
Required = _arg.Item2 Required = _arg.Item2
}).ToArray(); }).ToArray();
return new ScriptCommand(name, alias, description, isTargetRequired, permissionEnum, argsArray, executeAction, _config, _transLookup); return new ScriptCommand(name, alias, description, isTargetRequired, permissionEnum, argsArray, executeAction,
_config, _transLookup, _serviceProvider.GetRequiredService<ILogger<ScriptCommand>>());
} }
} }
} }

View File

@ -1,19 +1,18 @@
using IW4MAdmin.Application.Misc; using IW4MAdmin.Application.Misc;
using Newtonsoft.Json;
using SharedLibraryCore; using SharedLibraryCore;
using SharedLibraryCore.Events; using SharedLibraryCore.Events;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace IW4MAdmin.Application namespace IW4MAdmin.Application
{ {
public class GameEventHandler : IEventHandler public class GameEventHandler : IEventHandler
{ {
private readonly EventLog _eventLog; private readonly EventLog _eventLog;
private readonly ILogger _logger;
private static readonly GameEvent.EventType[] overrideEvents = new[] private static readonly GameEvent.EventType[] overrideEvents = new[]
{ {
GameEvent.EventType.Connect, GameEvent.EventType.Connect,
@ -22,34 +21,23 @@ namespace IW4MAdmin.Application
GameEvent.EventType.Stop GameEvent.EventType.Stop
}; };
public GameEventHandler() public GameEventHandler(ILogger<GameEventHandler> logger)
{ {
_eventLog = new EventLog(); _eventLog = new EventLog();
_logger = logger;
} }
public void HandleEvent(IManager manager, GameEvent gameEvent) public void HandleEvent(IManager manager, GameEvent gameEvent)
{ {
#if DEBUG
ThreadPool.GetMaxThreads(out int workerThreads, out int n);
ThreadPool.GetAvailableThreads(out int availableThreads, out int m);
gameEvent.Owner.Logger.WriteDebug($"There are {workerThreads - availableThreads} active threading tasks");
#endif
if (manager.IsRunning || overrideEvents.Contains(gameEvent.Type)) if (manager.IsRunning || overrideEvents.Contains(gameEvent.Type))
{ {
#if DEBUG
gameEvent.Owner.Logger.WriteDebug($"Adding event with id {gameEvent.Id}");
#endif
EventApi.OnGameEvent(gameEvent); EventApi.OnGameEvent(gameEvent);
Task.Factory.StartNew(() => manager.ExecuteEvent(gameEvent)); Task.Factory.StartNew(() => manager.ExecuteEvent(gameEvent));
} }
#if DEBUG
else else
{ {
gameEvent.Owner.Logger.WriteDebug($"Skipping event as we're shutting down {gameEvent.Id}"); _logger.LogDebug("Skipping event as we're shutting down {eventId}", gameEvent.Id);
} }
#endif
} }
} }
} }

View File

@ -3,6 +3,9 @@ using SharedLibraryCore.Interfaces;
using System; using System;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Serilog.Context;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace IW4MAdmin.Application.IO namespace IW4MAdmin.Application.IO
{ {
@ -12,12 +15,14 @@ namespace IW4MAdmin.Application.IO
private readonly Server _server; private readonly Server _server;
private readonly IGameLogReader _reader; private readonly IGameLogReader _reader;
private readonly bool _ignoreBots; private readonly bool _ignoreBots;
private readonly ILogger _logger;
public GameLogEventDetection(Server server, Uri[] gameLogUris, IGameLogReaderFactory gameLogReaderFactory) public GameLogEventDetection(ILogger<GameLogEventDetection> logger, Server server, Uri[] gameLogUris, IGameLogReaderFactory gameLogReaderFactory)
{ {
_reader = gameLogReaderFactory.CreateGameLogReader(gameLogUris, server.EventParser); _reader = gameLogReaderFactory.CreateGameLogReader(gameLogUris, server.EventParser);
_server = server; _server = server;
_ignoreBots = server?.Manager.GetApplicationSettings().Configuration().IgnoreBots ?? false; _ignoreBots = server?.Manager.GetApplicationSettings().Configuration().IgnoreBots ?? false;
_logger = logger;
} }
public async Task PollForChanges() public async Task PollForChanges()
@ -33,15 +38,17 @@ namespace IW4MAdmin.Application.IO
catch (Exception e) catch (Exception e)
{ {
_server.Logger.WriteWarning($"Failed to update log event for {_server.EndPoint}"); using(LogContext.PushProperty("Server", _server.ToString()))
_server.Logger.WriteDebug(e.GetExceptionInfo()); {
_logger.LogError(e, "Failed to update log event for {endpoint}", _server.EndPoint);
}
} }
} }
await Task.Delay(_reader.UpdateInterval, _server.Manager.CancellationToken); await Task.Delay(_reader.UpdateInterval, _server.Manager.CancellationToken);
} }
_server.Logger.WriteDebug("Stopped polling for changes"); _logger.LogDebug("Stopped polling for changes");
} }
public async Task UpdateLogEvents() public async Task UpdateLogEvents()
@ -68,9 +75,6 @@ namespace IW4MAdmin.Application.IO
{ {
try try
{ {
#if DEBUG
_server.Logger.WriteVerbose(gameEvent.Data);
#endif
gameEvent.Owner = _server; gameEvent.Owner = _server;
// we don't want to add the event if ignoreBots is on and the event comes from a bot // we don't want to add the event if ignoreBots is on and the event comes from a bot
@ -102,10 +106,14 @@ namespace IW4MAdmin.Application.IO
catch (InvalidOperationException) catch (InvalidOperationException)
{ {
if (!_ignoreBots) if (_ignoreBots)
{ {
_server.Logger.WriteWarning("Could not find client in client list when parsing event line"); continue;
_server.Logger.WriteDebug(gameEvent.Data); }
using(LogContext.PushProperty("Server", _server.ToString()))
{
_logger.LogError("Could not find client in client list when parsing event line {data}", gameEvent.Data);
} }
} }
} }

View File

@ -6,6 +6,8 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace IW4MAdmin.Application.IO namespace IW4MAdmin.Application.IO
{ {
@ -19,7 +21,7 @@ namespace IW4MAdmin.Application.IO
public int UpdateInterval => 300; public int UpdateInterval => 300;
public GameLogReader(string logFile, IEventParser parser, ILogger logger) public GameLogReader(string logFile, IEventParser parser, ILogger<GameLogReader> logger)
{ {
_logFile = logFile; _logFile = logFile;
_parser = parser; _parser = parser;
@ -73,9 +75,7 @@ namespace IW4MAdmin.Application.IO
catch (Exception e) catch (Exception e)
{ {
_logger.WriteWarning("Could not properly parse event line"); _logger.LogError(e, "Could not properly parse event line {@eventLine}", eventLine);
_logger.WriteDebug(e.Message);
_logger.WriteDebug(eventLine);
} }
} }

View File

@ -6,6 +6,8 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace IW4MAdmin.Application.IO namespace IW4MAdmin.Application.IO
{ {
@ -20,7 +22,7 @@ namespace IW4MAdmin.Application.IO
private readonly string _safeLogPath; private readonly string _safeLogPath;
private string lastKey = "next"; private string lastKey = "next";
public GameLogReaderHttp(Uri[] gameLogServerUris, IEventParser parser, ILogger logger) public GameLogReaderHttp(Uri[] gameLogServerUris, IEventParser parser, ILogger<GameLogReaderHttp> logger)
{ {
_eventParser = parser; _eventParser = parser;
_logServerApi = RestClient.For<IGameLogServer>(gameLogServerUris[0].ToString()); _logServerApi = RestClient.For<IGameLogServer>(gameLogServerUris[0].ToString());
@ -40,7 +42,7 @@ namespace IW4MAdmin.Application.IO
if (!response.Success && string.IsNullOrEmpty(lastKey)) if (!response.Success && string.IsNullOrEmpty(lastKey))
{ {
_logger.WriteError($"Could not get log server info of {_safeLogPath}"); _logger.LogError("Could not get log server info of {logPath}", _safeLogPath);
return events; return events;
} }
@ -62,9 +64,7 @@ namespace IW4MAdmin.Application.IO
catch (Exception e) catch (Exception e)
{ {
_logger.WriteError("Could not properly parse event line from http"); _logger.LogError(e, "Could not properly parse event line from http {eventLine}", eventLine);
_logger.WriteDebug(e.Message);
_logger.WriteDebug(eventLine);
} }
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -6,15 +6,22 @@ using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Text; using System.Text;
using Microsoft.Extensions.Logging;
using SharedLibraryCore.Configuration;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace IW4MAdmin.Application.Localization namespace IW4MAdmin.Application.Localization
{ {
public class Configure public static class Configure
{ {
public static ITranslationLookup Initialize(bool useLocalTranslation, IMasterApi apiInstance, string customLocale = null) public static ITranslationLookup Initialize(ILogger logger, IMasterApi apiInstance, ApplicationConfiguration applicationConfiguration)
{ {
string currentLocale = string.IsNullOrEmpty(customLocale) ? CultureInfo.CurrentCulture.Name : customLocale; var useLocalTranslation = applicationConfiguration?.UseLocalTranslations ?? true;
string[] localizationFiles = Directory.GetFiles(Path.Join(Utilities.OperatingDirectory, "Localization"), $"*.{currentLocale}.json"); var customLocale = applicationConfiguration?.EnableCustomLocale ?? false
? (applicationConfiguration.CustomLocale ?? "en-US")
: "en-US";
var currentLocale = string.IsNullOrEmpty(customLocale) ? CultureInfo.CurrentCulture.Name : customLocale;
var localizationFiles = Directory.GetFiles(Path.Join(Utilities.OperatingDirectory, "Localization"), $"*.{currentLocale}.json");
if (!useLocalTranslation) if (!useLocalTranslation)
{ {
@ -25,9 +32,10 @@ namespace IW4MAdmin.Application.Localization
return localization.LocalizationIndex; return localization.LocalizationIndex;
} }
catch (Exception) catch (Exception ex)
{ {
// the online localization failed so will default to local files // the online localization failed so will default to local files
logger.LogWarning(ex, "Could not download latest translations");
} }
} }
@ -60,13 +68,11 @@ namespace IW4MAdmin.Application.Localization
{ {
if (!localizationDict.TryAdd(item.Key, item.Value)) if (!localizationDict.TryAdd(item.Key, item.Value))
{ {
Program.ServerManager.GetLogger(0).WriteError($"Could not add locale string {item.Key} to localization"); logger.LogError("Could not add locale string {key} to localization", item.Key);
} }
} }
} }
string localizationFile = $"{Path.Join(Utilities.OperatingDirectory, "Localization")}{Path.DirectorySeparatorChar}IW4MAdmin.{currentLocale}-{currentLocale.ToUpper()}.json";
Utilities.CurrentLocalization = new SharedLibraryCore.Localization.Layout(localizationDict) Utilities.CurrentLocalization = new SharedLibraryCore.Localization.Layout(localizationDict)
{ {
LocalizationName = currentLocale, LocalizationName = currentLocale,

View File

@ -1,7 +1,6 @@
using IW4MAdmin.Application.API.Master; using IW4MAdmin.Application.API.Master;
using IW4MAdmin.Application.EventParsers; using IW4MAdmin.Application.EventParsers;
using IW4MAdmin.Application.Factories; using IW4MAdmin.Application.Factories;
using IW4MAdmin.Application.Helpers;
using IW4MAdmin.Application.Meta; using IW4MAdmin.Application.Meta;
using IW4MAdmin.Application.Migration; using IW4MAdmin.Application.Migration;
using IW4MAdmin.Application.Misc; using IW4MAdmin.Application.Misc;
@ -24,6 +23,10 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using IW4MAdmin.Application.Extensions;
using IW4MAdmin.Application.Localization;
using Microsoft.Extensions.Logging;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace IW4MAdmin.Application namespace IW4MAdmin.Application
{ {
@ -76,29 +79,32 @@ namespace IW4MAdmin.Application
{ {
restart: restart:
ITranslationLookup translationLookup = null; ITranslationLookup translationLookup = null;
var logger = BuildDefaultLogger<Program>(new ApplicationConfiguration());
Utilities.DefaultLogger = logger;
logger.LogInformation("Begin IW4MAdmin startup. Version is {version} {@args}", Version, args);
try try
{ {
// do any needed housekeeping file/folder migrations // do any needed housekeeping file/folder migrations
ConfigurationMigration.MoveConfigFolder10518(null); ConfigurationMigration.MoveConfigFolder10518(null);
ConfigurationMigration.CheckDirectories(); ConfigurationMigration.CheckDirectories();
logger.LogDebug("Configuring services...");
var services = ConfigureServices(args); var services = ConfigureServices(args);
serviceProvider = services.BuildServiceProvider(); serviceProvider = services.BuildServiceProvider();
var versionChecker = serviceProvider.GetRequiredService<IMasterCommunication>(); var versionChecker = serviceProvider.GetRequiredService<IMasterCommunication>();
ServerManager = (ApplicationManager)serviceProvider.GetRequiredService<IManager>(); ServerManager = (ApplicationManager)serviceProvider.GetRequiredService<IManager>();
translationLookup = serviceProvider.GetRequiredService<ITranslationLookup>(); translationLookup = serviceProvider.GetRequiredService<ITranslationLookup>();
ServerManager.Logger.WriteInfo(Utilities.CurrentLocalization.LocalizationIndex["MANAGER_VERSION"].FormatExt(Version));
await versionChecker.CheckVersion(); await versionChecker.CheckVersion();
await ServerManager.Init(); await ServerManager.Init();
} }
catch (Exception e) catch (Exception e)
{ {
string failMessage = translationLookup == null ? "Failed to initalize IW4MAdmin" : translationLookup["MANAGER_INIT_FAIL"]; string failMessage = translationLookup == null ? "Failed to initialize IW4MAdmin" : translationLookup["MANAGER_INIT_FAIL"];
string exitMessage = translationLookup == null ? "Press enter to exit..." : translationLookup["MANAGER_EXIT"]; string exitMessage = translationLookup == null ? "Press enter to exit..." : translationLookup["MANAGER_EXIT"];
logger.LogCritical(e, "Failed to initialize IW4MAdmin");
Console.WriteLine(failMessage); Console.WriteLine(failMessage);
while (e.InnerException != null) while (e.InnerException != null)
@ -131,13 +137,14 @@ namespace IW4MAdmin.Application
try try
{ {
ApplicationTask = RunApplicationTasksAsync(); ApplicationTask = RunApplicationTasksAsync(logger);
await ApplicationTask; await ApplicationTask;
} }
catch (Exception e) catch (Exception e)
{ {
string failMessage = translationLookup == null ? "Failed to initalize IW4MAdmin" : translationLookup["MANAGER_INIT_FAIL"]; logger.LogCritical(e, "Failed to launch IW4MAdmin");
string failMessage = translationLookup == null ? "Failed to launch IW4MAdmin" : translationLookup["MANAGER_INIT_FAIL"];
Console.WriteLine($"{failMessage}: {e.GetExceptionInfo()}"); Console.WriteLine($"{failMessage}: {e.GetExceptionInfo()}");
} }
@ -153,7 +160,7 @@ namespace IW4MAdmin.Application
/// runs the core application tasks /// runs the core application tasks
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
private static async Task RunApplicationTasksAsync() private static async Task RunApplicationTasksAsync(ILogger logger)
{ {
var webfrontTask = ServerManager.GetApplicationSettings().Configuration().EnableWebFront ? var webfrontTask = ServerManager.GetApplicationSettings().Configuration().EnableWebFront ?
WebfrontCore.Program.Init(ServerManager, serviceProvider, ServerManager.CancellationToken) : WebfrontCore.Program.Init(ServerManager, serviceProvider, ServerManager.CancellationToken) :
@ -161,7 +168,7 @@ namespace IW4MAdmin.Application
// we want to run this one on a manual thread instead of letting the thread pool handle it, // we want to run this one on a manual thread instead of letting the thread pool handle it,
// because we can't exit early from waiting on console input, and it prevents us from restarting // because we can't exit early from waiting on console input, and it prevents us from restarting
var inputThread = new Thread(async () => await ReadConsoleInput()); var inputThread = new Thread(async () => await ReadConsoleInput(logger));
inputThread.Start(); inputThread.Start();
var tasks = new[] var tasks = new[]
@ -171,9 +178,11 @@ namespace IW4MAdmin.Application
serviceProvider.GetRequiredService<IMasterCommunication>().RunUploadStatus(ServerManager.CancellationToken) serviceProvider.GetRequiredService<IMasterCommunication>().RunUploadStatus(ServerManager.CancellationToken)
}; };
logger.LogDebug("Starting webfront and input tasks");
await Task.WhenAll(tasks); await Task.WhenAll(tasks);
ServerManager.Logger.WriteVerbose(Utilities.CurrentLocalization.LocalizationIndex["MANAGER_SHUTDOWN_SUCCESS"]); logger.LogInformation("Shutdown completed successfully");
Console.Write(Utilities.CurrentLocalization.LocalizationIndex["MANAGER_SHUTDOWN_SUCCESS"]);
} }
@ -181,11 +190,11 @@ namespace IW4MAdmin.Application
/// reads input from the console and executes entered commands on the default server /// reads input from the console and executes entered commands on the default server
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
private static async Task ReadConsoleInput() private static async Task ReadConsoleInput(ILogger logger)
{ {
if (Console.IsInputRedirected) if (Console.IsInputRedirected)
{ {
ServerManager.Logger.WriteInfo("Disabling console input as it has been redirected"); logger.LogInformation("Disabling console input as it has been redirected");
return; return;
} }
@ -221,71 +230,29 @@ namespace IW4MAdmin.Application
{ } { }
} }
/// <summary> private static IServiceCollection HandlePluginRegistration(ApplicationConfiguration appConfig,
/// Configures the dependency injection services IServiceCollection serviceCollection,
/// </summary> IMasterApi masterApi)
private static IServiceCollection ConfigureServices(string[] args)
{ {
var appConfigHandler = new BaseConfigurationHandler<ApplicationConfiguration>("IW4MAdminSettings"); var defaultLogger = BuildDefaultLogger<Program>(appConfig);
var appConfig = appConfigHandler.Configuration(); var pluginServiceProvider = new ServiceCollection()
var defaultLogger = new Logger("IW4MAdmin-Manager"); .AddBaseLogger(appConfig)
.AddSingleton(appConfig)
var masterUri = Utilities.IsDevelopment ? new Uri("http://127.0.0.1:8080") : appConfig?.MasterUrl ?? new ApplicationConfiguration().MasterUrl; .AddSingleton(masterApi)
var apiClient = RestClient.For<IMasterApi>(masterUri);
var pluginImporter = new PluginImporter(defaultLogger, appConfig, apiClient, new RemoteAssemblyHandler(defaultLogger, appConfig));
var serviceCollection = new ServiceCollection();
serviceCollection.AddSingleton<IServiceCollection>(_serviceProvider => serviceCollection)
.AddSingleton(appConfigHandler as IConfigurationHandler<ApplicationConfiguration>)
.AddSingleton(new BaseConfigurationHandler<CommandConfiguration>("CommandConfiguration") as IConfigurationHandler<CommandConfiguration>)
.AddSingleton(_serviceProvider => _serviceProvider.GetRequiredService<IConfigurationHandler<ApplicationConfiguration>>().Configuration() ?? new ApplicationConfiguration())
.AddSingleton(_serviceProvider => _serviceProvider.GetRequiredService<IConfigurationHandler<CommandConfiguration>>().Configuration() ?? new CommandConfiguration())
.AddSingleton<ILogger>(_serviceProvider => defaultLogger)
.AddSingleton<IPluginImporter, PluginImporter>()
.AddSingleton<IMiddlewareActionHandler, MiddlewareActionHandler>()
.AddSingleton<IRConConnectionFactory, RConConnectionFactory>()
.AddSingleton<IGameServerInstanceFactory, GameServerInstanceFactory>()
.AddSingleton<IConfigurationHandlerFactory, ConfigurationHandlerFactory>()
.AddSingleton<IParserRegexFactory, ParserRegexFactory>()
.AddSingleton<IDatabaseContextFactory, DatabaseContextFactory>()
.AddSingleton<IGameLogReaderFactory, GameLogReaderFactory>()
.AddSingleton<IScriptCommandFactory, ScriptCommandFactory>()
.AddSingleton<IAuditInformationRepository, AuditInformationRepository>()
.AddSingleton<IEntityService<EFClient>, ClientService>()
.AddSingleton<IMetaService, MetaService>()
.AddSingleton<IMetaRegistration, MetaRegistration>()
.AddSingleton<IScriptPluginServiceResolver, ScriptPluginServiceResolver>()
.AddSingleton<IResourceQueryHelper<ClientPaginationRequest, ReceivedPenaltyResponse>, ReceivedPenaltyResourceQueryHelper>()
.AddSingleton<IResourceQueryHelper<ClientPaginationRequest, AdministeredPenaltyResponse>, AdministeredPenaltyResourceQueryHelper>()
.AddSingleton<IResourceQueryHelper<ClientPaginationRequest, UpdatedAliasResponse>, UpdatedAliasResourceQueryHelper>()
.AddSingleton<IResourceQueryHelper<ChatSearchQuery, MessageResponse>, ChatResourceQueryHelper>()
.AddTransient<IParserPatternMatcher, ParserPatternMatcher>()
.AddSingleton<IRemoteAssemblyHandler, RemoteAssemblyHandler>() .AddSingleton<IRemoteAssemblyHandler, RemoteAssemblyHandler>()
.AddSingleton<IMasterCommunication, MasterCommunication>() .AddSingleton<IPluginImporter, PluginImporter>()
.AddSingleton<IManager, ApplicationManager>() .BuildServiceProvider();
.AddSingleton(apiClient)
.AddSingleton(_serviceProvider =>
{
var config = _serviceProvider.GetRequiredService<IConfigurationHandler<ApplicationConfiguration>>().Configuration();
return Localization.Configure.Initialize(useLocalTranslation: config?.UseLocalTranslations ?? false,
apiInstance: _serviceProvider.GetRequiredService<IMasterApi>(),
customLocale: config?.EnableCustomLocale ?? false ? (config.CustomLocale ?? "en-US") : "en-US");
});
if (args.Contains("serialevents"))
{
serviceCollection.AddSingleton<IEventHandler, SerialGameEventHandler>();
}
else
{
serviceCollection.AddSingleton<IEventHandler, GameEventHandler>();
}
var pluginImporter = pluginServiceProvider.GetRequiredService<IPluginImporter>();
// we need to register the rest client with regular collection
serviceCollection.AddSingleton(masterApi);
// register the native commands // register the native commands
foreach (var commandType in typeof(SharedLibraryCore.Commands.QuitCommand).Assembly.GetTypes() foreach (var commandType in typeof(SharedLibraryCore.Commands.QuitCommand).Assembly.GetTypes()
.Where(_command => _command.BaseType == typeof(Command))) .Where(_command => _command.BaseType == typeof(Command)))
{ {
defaultLogger.WriteInfo($"Registered native command type {commandType.Name}"); defaultLogger.LogDebug("Registered native command type {name}", commandType.Name);
serviceCollection.AddSingleton(typeof(IManagerCommand), commandType); serviceCollection.AddSingleton(typeof(IManagerCommand), commandType);
} }
@ -293,14 +260,14 @@ namespace IW4MAdmin.Application
var pluginImplementations = pluginImporter.DiscoverAssemblyPluginImplementations(); var pluginImplementations = pluginImporter.DiscoverAssemblyPluginImplementations();
foreach (var pluginType in pluginImplementations.Item1) foreach (var pluginType in pluginImplementations.Item1)
{ {
defaultLogger.WriteInfo($"Registered plugin type {pluginType.FullName}"); defaultLogger.LogDebug("Registered plugin type {name}", pluginType.FullName);
serviceCollection.AddSingleton(typeof(IPlugin), pluginType); serviceCollection.AddSingleton(typeof(IPlugin), pluginType);
} }
// register the plugin commands // register the plugin commands
foreach (var commandType in pluginImplementations.Item2) foreach (var commandType in pluginImplementations.Item2)
{ {
defaultLogger.WriteInfo($"Registered plugin command type {commandType.FullName}"); defaultLogger.LogDebug("Registered plugin command type {name}", commandType.FullName);
serviceCollection.AddSingleton(typeof(IManagerCommand), commandType); serviceCollection.AddSingleton(typeof(IManagerCommand), commandType);
} }
@ -324,5 +291,86 @@ namespace IW4MAdmin.Application
return serviceCollection; return serviceCollection;
} }
/// <summary>
/// Configures the dependency injection services
/// </summary>
private static IServiceCollection ConfigureServices(string[] args)
{
// setup the static resources (config/master api/translations)
var serviceCollection = new ServiceCollection();
var appConfigHandler = new BaseConfigurationHandler<ApplicationConfiguration>("IW4MAdminSettings");
var appConfig = appConfigHandler.Configuration();
var masterUri = Utilities.IsDevelopment
? new Uri("http://127.0.0.1:8080")
: appConfig?.MasterUrl ?? new ApplicationConfiguration().MasterUrl;
var masterRestClient = RestClient.For<IMasterApi>(masterUri);
var translationLookup = Configure.Initialize(Utilities.DefaultLogger, masterRestClient, appConfig);
if (appConfig == null)
{
appConfig = (ApplicationConfiguration) new ApplicationConfiguration().Generate();
appConfigHandler.Set(appConfig);
appConfigHandler.Save();
}
// build the dependency list
HandlePluginRegistration(appConfig, serviceCollection, masterRestClient);
serviceCollection
.AddBaseLogger(appConfig)
.AddSingleton<IServiceCollection>(_serviceProvider => serviceCollection)
.AddSingleton((IConfigurationHandler<ApplicationConfiguration>) appConfigHandler)
.AddSingleton(new BaseConfigurationHandler<CommandConfiguration>("CommandConfiguration") as IConfigurationHandler<CommandConfiguration>)
.AddSingleton(appConfig)
.AddSingleton(_serviceProvider => _serviceProvider.GetRequiredService<IConfigurationHandler<CommandConfiguration>>().Configuration() ?? new CommandConfiguration())
.AddSingleton<IPluginImporter, PluginImporter>()
.AddSingleton<IMiddlewareActionHandler, MiddlewareActionHandler>()
.AddSingleton<IRConConnectionFactory, RConConnectionFactory>()
.AddSingleton<IGameServerInstanceFactory, GameServerInstanceFactory>()
.AddSingleton<IConfigurationHandlerFactory, ConfigurationHandlerFactory>()
.AddSingleton<IParserRegexFactory, ParserRegexFactory>()
.AddSingleton<IDatabaseContextFactory, DatabaseContextFactory>()
.AddSingleton<IGameLogReaderFactory, GameLogReaderFactory>()
.AddSingleton<IScriptCommandFactory, ScriptCommandFactory>()
.AddSingleton<IAuditInformationRepository, AuditInformationRepository>()
.AddSingleton<IEntityService<EFClient>, ClientService>()
.AddSingleton<IMetaService, MetaService>()
.AddSingleton<ClientService>()
.AddSingleton<ChangeHistoryService>()
.AddSingleton<IMetaRegistration, MetaRegistration>()
.AddSingleton<IScriptPluginServiceResolver, ScriptPluginServiceResolver>()
.AddSingleton<IResourceQueryHelper<ClientPaginationRequest, ReceivedPenaltyResponse>, ReceivedPenaltyResourceQueryHelper>()
.AddSingleton<IResourceQueryHelper<ClientPaginationRequest, AdministeredPenaltyResponse>, AdministeredPenaltyResourceQueryHelper>()
.AddSingleton<IResourceQueryHelper<ClientPaginationRequest, UpdatedAliasResponse>, UpdatedAliasResourceQueryHelper>()
.AddSingleton<IResourceQueryHelper<ChatSearchQuery, MessageResponse>, ChatResourceQueryHelper>()
.AddTransient<IParserPatternMatcher, ParserPatternMatcher>()
.AddSingleton<IRemoteAssemblyHandler, RemoteAssemblyHandler>()
.AddSingleton<IMasterCommunication, MasterCommunication>()
.AddSingleton<IManager, ApplicationManager>()
.AddSingleton<SharedLibraryCore.Interfaces.ILogger, Logger>()
.AddSingleton(translationLookup);
if (args.Contains("serialevents"))
{
serviceCollection.AddSingleton<IEventHandler, SerialGameEventHandler>();
}
else
{
serviceCollection.AddSingleton<IEventHandler, GameEventHandler>();
}
return serviceCollection;
}
private static ILogger BuildDefaultLogger<T>(ApplicationConfiguration appConfig)
{
var collection = new ServiceCollection()
.AddBaseLogger(appConfig)
.BuildServiceProvider();
return collection.GetRequiredService<ILogger<T>>();
}
} }
} }

View File

@ -1,11 +1,13 @@
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using SharedLibraryCore.Database.Models; using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Dtos.Meta.Responses; using SharedLibraryCore.Dtos.Meta.Responses;
using SharedLibraryCore.Helpers; using SharedLibraryCore.Helpers;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using SharedLibraryCore.QueryHelper; using SharedLibraryCore.QueryHelper;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace IW4MAdmin.Application.Meta namespace IW4MAdmin.Application.Meta
{ {
@ -18,7 +20,7 @@ namespace IW4MAdmin.Application.Meta
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IDatabaseContextFactory _contextFactory; private readonly IDatabaseContextFactory _contextFactory;
public AdministeredPenaltyResourceQueryHelper(ILogger logger, IDatabaseContextFactory contextFactory) public AdministeredPenaltyResourceQueryHelper(ILogger<AdministeredPenaltyResourceQueryHelper> logger, IDatabaseContextFactory contextFactory)
{ {
_contextFactory = contextFactory; _contextFactory = contextFactory;
_logger = logger; _logger = logger;

View File

@ -6,6 +6,8 @@ using SharedLibraryCore.QueryHelper;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace IW4MAdmin.Application.Meta namespace IW4MAdmin.Application.Meta
{ {
@ -19,7 +21,7 @@ namespace IW4MAdmin.Application.Meta
private readonly IResourceQueryHelper<ClientPaginationRequest, AdministeredPenaltyResponse> _administeredPenaltyHelper; private readonly IResourceQueryHelper<ClientPaginationRequest, AdministeredPenaltyResponse> _administeredPenaltyHelper;
private readonly IResourceQueryHelper<ClientPaginationRequest, UpdatedAliasResponse> _updatedAliasHelper; private readonly IResourceQueryHelper<ClientPaginationRequest, UpdatedAliasResponse> _updatedAliasHelper;
public MetaRegistration(ILogger logger, IMetaService metaService, ITranslationLookup transLookup, IEntityService<EFClient> clientEntityService, public MetaRegistration(ILogger<MetaRegistration> logger, IMetaService metaService, ITranslationLookup transLookup, IEntityService<EFClient> clientEntityService,
IResourceQueryHelper<ClientPaginationRequest, ReceivedPenaltyResponse> receivedPenaltyHelper, IResourceQueryHelper<ClientPaginationRequest, ReceivedPenaltyResponse> receivedPenaltyHelper,
IResourceQueryHelper<ClientPaginationRequest, AdministeredPenaltyResponse> administeredPenaltyHelper, IResourceQueryHelper<ClientPaginationRequest, AdministeredPenaltyResponse> administeredPenaltyHelper,
IResourceQueryHelper<ClientPaginationRequest, UpdatedAliasResponse> updatedAliasHelper) IResourceQueryHelper<ClientPaginationRequest, UpdatedAliasResponse> updatedAliasHelper)
@ -82,7 +84,7 @@ namespace IW4MAdmin.Application.Meta
if (client == null) if (client == null)
{ {
_logger.WriteWarning($"No client found with id {request.ClientId} when generating profile meta"); _logger.LogWarning("No client found with id {clientId} when generating profile meta", request.ClientId);
return metaList; return metaList;
} }

View File

@ -2,12 +2,14 @@
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using SharedLibraryCore; using SharedLibraryCore;
using SharedLibraryCore.Database.Models; using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Dtos.Meta.Responses; using SharedLibraryCore.Dtos.Meta.Responses;
using SharedLibraryCore.Helpers; using SharedLibraryCore.Helpers;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using SharedLibraryCore.QueryHelper; using SharedLibraryCore.QueryHelper;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace IW4MAdmin.Application.Meta namespace IW4MAdmin.Application.Meta
{ {
@ -20,7 +22,7 @@ namespace IW4MAdmin.Application.Meta
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IDatabaseContextFactory _contextFactory; private readonly IDatabaseContextFactory _contextFactory;
public ReceivedPenaltyResourceQueryHelper(ILogger logger, IDatabaseContextFactory contextFactory) public ReceivedPenaltyResourceQueryHelper(ILogger<ReceivedPenaltyResourceQueryHelper> logger, IDatabaseContextFactory contextFactory)
{ {
_contextFactory = contextFactory; _contextFactory = contextFactory;
_logger = logger; _logger = logger;

View File

@ -6,6 +6,8 @@ using SharedLibraryCore.Interfaces;
using SharedLibraryCore.QueryHelper; using SharedLibraryCore.QueryHelper;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace IW4MAdmin.Application.Meta namespace IW4MAdmin.Application.Meta
{ {
@ -18,7 +20,7 @@ namespace IW4MAdmin.Application.Meta
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IDatabaseContextFactory _contextFactory; private readonly IDatabaseContextFactory _contextFactory;
public UpdatedAliasResourceQueryHelper(ILogger logger, IDatabaseContextFactory contextFactory) public UpdatedAliasResourceQueryHelper(ILogger<UpdatedAliasResourceQueryHelper> logger, IDatabaseContextFactory contextFactory)
{ {
_logger = logger; _logger = logger;
_contextFactory = contextFactory; _contextFactory = contextFactory;

View File

@ -1,11 +1,8 @@
using SharedLibraryCore; using SharedLibraryCore;
using SharedLibraryCore.Interfaces;
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using ILogger = Microsoft.Extensions.Logging.ILogger;
using System.Text.RegularExpressions;
namespace IW4MAdmin.Application.Migration namespace IW4MAdmin.Application.Migration
{ {
@ -56,7 +53,6 @@ namespace IW4MAdmin.Application.Migration
if (!Directory.Exists(configDirectory)) if (!Directory.Exists(configDirectory))
{ {
log?.WriteDebug($"Creating directory for configs {configDirectory}");
Directory.CreateDirectory(configDirectory); Directory.CreateDirectory(configDirectory);
} }
@ -66,7 +62,6 @@ namespace IW4MAdmin.Application.Migration
foreach (var configFile in configurationFiles) foreach (var configFile in configurationFiles)
{ {
log?.WriteDebug($"Moving config file {configFile}");
string destinationPath = Path.Join("Configuration", configFile); string destinationPath = Path.Join("Configuration", configFile);
if (!File.Exists(destinationPath)) if (!File.Exists(destinationPath))
{ {
@ -77,7 +72,6 @@ namespace IW4MAdmin.Application.Migration
if (!File.Exists(Path.Join("Database", "Database.db")) && if (!File.Exists(Path.Join("Database", "Database.db")) &&
File.Exists("Database.db")) File.Exists("Database.db"))
{ {
log?.WriteDebug("Moving database file");
File.Move("Database.db", Path.Join("Database", "Database.db")); File.Move("Database.db", Path.Join("Database", "Database.db"));
} }
} }

View File

@ -1,63 +0,0 @@
using SharedLibraryCore;
using SharedLibraryCore.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
namespace IW4MAdmin.Application.Misc
{
internal class EventPerformance
{
public long ExecutionTime { get; set; }
public GameEvent Event { get; set; }
public string EventInfo => $"{Event.Type}, {Event.FailReason}, {Event.IsBlocking}, {Event.Data}, {Event.Message}, {Event.Extra}";
}
public class DuplicateKeyComparer<TKey> : IComparer<TKey> where TKey : IComparable
{
public int Compare(TKey x, TKey y)
{
int result = x.CompareTo(y);
if (result == 0)
return 1;
else
return result;
}
}
internal class EventProfiler
{
public double AverageEventTime { get; private set; }
public double MaxEventTime => Events.Values.Last().ExecutionTime;
public double MinEventTime => Events.Values[0].ExecutionTime;
public int TotalEventCount => Events.Count;
public SortedList<long, EventPerformance> Events { get; private set; } = new SortedList<long, EventPerformance>(new DuplicateKeyComparer<long>());
private readonly ILogger _logger;
public EventProfiler(ILogger logger)
{
_logger = logger;
}
public void Profile(DateTime start, DateTime end, GameEvent gameEvent)
{
_logger.WriteDebug($"Starting profile of event {gameEvent.Id}");
long executionTime = (long)Math.Round((end - start).TotalMilliseconds);
var perf = new EventPerformance()
{
Event = gameEvent,
ExecutionTime = executionTime
};
lock (Events)
{
Events.Add(executionTime, perf);
}
AverageEventTime = (AverageEventTime * (TotalEventCount - 1) + executionTime) / TotalEventCount;
_logger.WriteDebug($"Finished profile of event {gameEvent.Id}");
}
}
}

View File

@ -1,132 +1,47 @@
using IW4MAdmin.Application.IO; using System;
using SharedLibraryCore; using Microsoft.Extensions.Logging;
using SharedLibraryCore.Interfaces; using ILogger = SharedLibraryCore.Interfaces.ILogger;
using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
namespace IW4MAdmin.Application namespace IW4MAdmin.Application
{ {
[Obsolete]
public class Logger : ILogger public class Logger : ILogger
{ {
enum LogType private readonly Microsoft.Extensions.Logging.ILogger _logger;
public Logger(ILogger<Logger> logger)
{ {
Verbose, _logger = logger;
Info,
Debug,
Warning,
Error,
Assert
}
readonly string FileName;
readonly ReaderWriterLockSlim WritingLock;
static readonly short MAX_LOG_FILES = 10;
public Logger(string fn)
{
FileName = Path.Join(Utilities.OperatingDirectory, "Log", $"{fn}.log");
WritingLock = new ReaderWriterLockSlim();
RotateLogs();
}
~Logger()
{
WritingLock.Dispose();
}
/// <summary>
/// rotates logs when log is initialized
/// </summary>
private void RotateLogs()
{
string maxLog = FileName + MAX_LOG_FILES;
if (File.Exists(maxLog))
{
File.Delete(maxLog);
}
for (int i = MAX_LOG_FILES - 1; i >= 0; i--)
{
string logToMove = i == 0 ? FileName : FileName + i;
string movedLogName = FileName + (i + 1);
if (File.Exists(logToMove))
{
File.Move(logToMove, movedLogName);
}
}
}
void Write(string msg, LogType type)
{
WritingLock.EnterWriteLock();
string stringType = type.ToString();
msg = msg.StripColors();
try
{
stringType = Utilities.CurrentLocalization.LocalizationIndex[$"GLOBAL_{type.ToString().ToUpper()}"];
}
catch (Exception) { }
string LogLine = $"[{DateTime.Now.ToString("MM.dd.yyy HH:mm:ss.fff")}] - {stringType}: {msg}";
try
{
#if DEBUG
// lets keep it simple and dispose of everything quickly as logging wont be that much (relatively)
Console.WriteLine(msg);
#else
if (type == LogType.Error || type == LogType.Verbose)
{
Console.WriteLine(LogLine);
}
File.AppendAllText(FileName, $"{LogLine}{Environment.NewLine}");
#endif
}
catch (Exception ex)
{
Console.WriteLine("Well.. It looks like your machine can't event write to the log file. That's something else...");
Console.WriteLine(ex.GetExceptionInfo());
}
WritingLock.ExitWriteLock();
} }
public void WriteVerbose(string msg) public void WriteVerbose(string msg)
{ {
Write(msg, LogType.Verbose); _logger.LogInformation(msg);
} }
public void WriteDebug(string msg) public void WriteDebug(string msg)
{ {
Write(msg, LogType.Debug); _logger.LogDebug(msg);
} }
public void WriteError(string msg) public void WriteError(string msg)
{ {
Write(msg, LogType.Error); _logger.LogError(msg);
} }
public void WriteInfo(string msg) public void WriteInfo(string msg)
{ {
Write(msg, LogType.Info); WriteVerbose(msg);
} }
public void WriteWarning(string msg) public void WriteWarning(string msg)
{ {
Write(msg, LogType.Warning); _logger.LogWarning(msg);
} }
public void WriteAssert(bool condition, string msg) public void WriteAssert(bool condition, string msg)
{ {
if (!condition) throw new NotImplementedException();
Write(msg, LogType.Assert);
} }
} }
} }

View File

@ -8,6 +8,8 @@ using System;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace IW4MAdmin.Application.Misc namespace IW4MAdmin.Application.Misc
{ {
@ -24,10 +26,9 @@ namespace IW4MAdmin.Application.Misc
private readonly ApplicationConfiguration _appConfig; private readonly ApplicationConfiguration _appConfig;
private readonly BuildNumber _fallbackVersion = BuildNumber.Parse("99.99.99.99"); private readonly BuildNumber _fallbackVersion = BuildNumber.Parse("99.99.99.99");
private readonly int _apiVersion = 1; private readonly int _apiVersion = 1;
private bool firstHeartBeat = true; private bool firstHeartBeat = true;
public MasterCommunication(ILogger logger, ApplicationConfiguration appConfig, ITranslationLookup translationLookup, IMasterApi apiInstance, IManager manager) public MasterCommunication(ILogger<MasterCommunication> logger, ApplicationConfiguration appConfig, ITranslationLookup translationLookup, IMasterApi apiInstance, IManager manager)
{ {
_logger = logger; _logger = logger;
_transLookup = translationLookup; _transLookup = translationLookup;
@ -55,13 +56,7 @@ namespace IW4MAdmin.Application.Misc
catch (Exception e) catch (Exception e)
{ {
_logger.WriteWarning(_transLookup["MANAGER_VERSION_FAIL"]); _logger.LogWarning(e, "Unable to retrieve IW4MAdmin version information");
while (e.InnerException != null)
{
e = e.InnerException;
}
_logger.WriteDebug(e.Message);
} }
if (version.CurrentVersionStable == _fallbackVersion) if (version.CurrentVersionStable == _fallbackVersion)
@ -110,12 +105,12 @@ namespace IW4MAdmin.Application.Misc
catch (System.Net.Http.HttpRequestException e) catch (System.Net.Http.HttpRequestException e)
{ {
_logger.WriteWarning($"Could not send heartbeat - {e.Message}"); _logger.LogWarning(e, "Could not send heartbeat");
} }
catch (AggregateException e) catch (AggregateException e)
{ {
_logger.WriteWarning($"Could not send heartbeat - {e.Message}"); _logger.LogWarning(e, "Could not send heartbeat");
var exceptions = e.InnerExceptions.Where(ex => ex.GetType() == typeof(ApiException)); var exceptions = e.InnerExceptions.Where(ex => ex.GetType() == typeof(ApiException));
foreach (var ex in exceptions) foreach (var ex in exceptions)
@ -129,7 +124,7 @@ namespace IW4MAdmin.Application.Misc
catch (ApiException e) catch (ApiException e)
{ {
_logger.WriteWarning($"Could not send heartbeat - {e.Message}"); _logger.LogWarning(e, "Could not send heartbeat");
if (e.StatusCode == System.Net.HttpStatusCode.Unauthorized) if (e.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{ {
connected = false; connected = false;
@ -138,7 +133,7 @@ namespace IW4MAdmin.Application.Misc
catch (Exception e) catch (Exception e)
{ {
_logger.WriteWarning($"Could not send heartbeat - {e.Message}"); _logger.LogWarning(e, "Could not send heartbeat");
} }
@ -202,7 +197,7 @@ namespace IW4MAdmin.Application.Misc
if (response.ResponseMessage.StatusCode != System.Net.HttpStatusCode.OK) if (response.ResponseMessage.StatusCode != System.Net.HttpStatusCode.OK)
{ {
_logger.WriteWarning($"Response code from master is {response.ResponseMessage.StatusCode}, message is {response.StringContent}"); _logger.LogWarning("Non success response code from master is {statusCode}, message is {message}", response.ResponseMessage.StatusCode, response.StringContent);
} }
} }
} }

View File

@ -7,6 +7,8 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace IW4MAdmin.Application.Misc namespace IW4MAdmin.Application.Misc
{ {
@ -20,7 +22,7 @@ namespace IW4MAdmin.Application.Misc
private readonly IDatabaseContextFactory _contextFactory; private readonly IDatabaseContextFactory _contextFactory;
private readonly ILogger _logger; private readonly ILogger _logger;
public MetaService(ILogger logger, IDatabaseContextFactory contextFactory) public MetaService(ILogger<MetaService> logger, IDatabaseContextFactory contextFactory)
{ {
_logger = logger; _logger = logger;
_metaActions = new Dictionary<MetaType, List<dynamic>>(); _metaActions = new Dictionary<MetaType, List<dynamic>>();

View File

@ -1,8 +1,9 @@
using SharedLibraryCore; using SharedLibraryCore.Interfaces;
using SharedLibraryCore.Interfaces;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace IW4MAdmin.Application.Misc namespace IW4MAdmin.Application.Misc
{ {
@ -11,7 +12,7 @@ namespace IW4MAdmin.Application.Misc
private readonly IDictionary<string, IList<object>> _actions; private readonly IDictionary<string, IList<object>> _actions;
private readonly ILogger _logger; private readonly ILogger _logger;
public MiddlewareActionHandler(ILogger logger) public MiddlewareActionHandler(ILogger<MiddlewareActionHandler> logger)
{ {
_actions = new Dictionary<string, IList<object>>(); _actions = new Dictionary<string, IList<object>>();
_logger = logger; _logger = logger;
@ -38,8 +39,7 @@ namespace IW4MAdmin.Application.Misc
} }
catch (Exception e) catch (Exception e)
{ {
_logger.WriteWarning($"Failed to invoke middleware action {name}"); _logger.LogWarning(e, "Failed to invoke middleware action {name}", name);
_logger.WriteDebug(e.GetExceptionInfo());
} }
} }

View File

@ -5,11 +5,12 @@ using System.Reflection;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using System.Linq; using System.Linq;
using SharedLibraryCore; using SharedLibraryCore;
using IW4MAdmin.Application.Misc;
using IW4MAdmin.Application.API.Master; using IW4MAdmin.Application.API.Master;
using Microsoft.Extensions.Logging;
using SharedLibraryCore.Configuration; using SharedLibraryCore.Configuration;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace IW4MAdmin.Application.Helpers namespace IW4MAdmin.Application.Misc
{ {
/// <summary> /// <summary>
/// implementation of IPluginImporter /// implementation of IPluginImporter
@ -24,7 +25,7 @@ namespace IW4MAdmin.Application.Helpers
private readonly IMasterApi _masterApi; private readonly IMasterApi _masterApi;
private readonly ApplicationConfiguration _appConfig; private readonly ApplicationConfiguration _appConfig;
public PluginImporter(ILogger logger, ApplicationConfiguration appConfig, IMasterApi masterApi, IRemoteAssemblyHandler remoteAssemblyHandler) public PluginImporter(ILogger<PluginImporter> logger, ApplicationConfiguration appConfig, IMasterApi masterApi, IRemoteAssemblyHandler remoteAssemblyHandler)
{ {
_logger = logger; _logger = logger;
_masterApi = masterApi; _masterApi = masterApi;
@ -44,14 +45,14 @@ namespace IW4MAdmin.Application.Helpers
{ {
var scriptPluginFiles = Directory.GetFiles(pluginDir, "*.js").AsEnumerable().Union(GetRemoteScripts()); var scriptPluginFiles = Directory.GetFiles(pluginDir, "*.js").AsEnumerable().Union(GetRemoteScripts());
_logger.WriteInfo($"Discovered {scriptPluginFiles.Count()} potential script plugins"); _logger.LogDebug("Discovered {count} potential script plugins", scriptPluginFiles.Count());
if (scriptPluginFiles.Count() > 0) if (scriptPluginFiles.Count() > 0)
{ {
foreach (string fileName in scriptPluginFiles) foreach (string fileName in scriptPluginFiles)
{ {
_logger.WriteInfo($"Discovered script plugin {fileName}"); _logger.LogDebug("Discovered script plugin {fileName}", fileName);
var plugin = new ScriptPlugin(fileName); var plugin = new ScriptPlugin(_logger, fileName);
yield return plugin; yield return plugin;
} }
} }
@ -71,7 +72,7 @@ namespace IW4MAdmin.Application.Helpers
if (Directory.Exists(pluginDir)) if (Directory.Exists(pluginDir))
{ {
var dllFileNames = Directory.GetFiles(pluginDir, "*.dll"); var dllFileNames = Directory.GetFiles(pluginDir, "*.dll");
_logger.WriteInfo($"Discovered {dllFileNames.Length} potential plugin assemblies"); _logger.LogDebug("Discovered {count} potential plugin assemblies", dllFileNames.Length);
if (dllFileNames.Length > 0) if (dllFileNames.Length > 0)
{ {
@ -84,13 +85,13 @@ namespace IW4MAdmin.Application.Helpers
.SelectMany(_asm => _asm.GetTypes()) .SelectMany(_asm => _asm.GetTypes())
.Where(_assemblyType => _assemblyType.GetInterface(nameof(IPlugin), false) != null); .Where(_assemblyType => _assemblyType.GetInterface(nameof(IPlugin), false) != null);
_logger.WriteInfo($"Discovered {pluginTypes.Count()} plugin implementations"); _logger.LogDebug("Discovered {count} plugin implementations", pluginTypes.Count());
commandTypes = assemblies commandTypes = assemblies
.SelectMany(_asm => _asm.GetTypes()) .SelectMany(_asm => _asm.GetTypes())
.Where(_assemblyType => _assemblyType.IsClass && _assemblyType.BaseType == typeof(Command)); .Where(_assemblyType => _assemblyType.IsClass && _assemblyType.BaseType == typeof(Command));
_logger.WriteInfo($"Discovered {commandTypes.Count()} plugin commands"); _logger.LogDebug("Discovered {count} plugin commands", commandTypes.Count());
} }
} }
@ -109,8 +110,7 @@ namespace IW4MAdmin.Application.Helpers
catch (Exception ex) catch (Exception ex)
{ {
_logger.WriteWarning("Could not load remote assemblies"); _logger.LogWarning(ex, "Could not load remote assemblies");
_logger.WriteDebug(ex.GetExceptionInfo());
return Enumerable.Empty<Assembly>(); return Enumerable.Empty<Assembly>();
} }
} }
@ -127,8 +127,7 @@ namespace IW4MAdmin.Application.Helpers
catch (Exception ex) catch (Exception ex)
{ {
_logger.WriteWarning("Could not load remote assemblies"); _logger.LogWarning(ex,"Could not load remote scripts");
_logger.WriteDebug(ex.GetExceptionInfo());
return Enumerable.Empty<string>(); return Enumerable.Empty<string>();
} }
} }

View File

@ -1,5 +1,4 @@
using SharedLibraryCore; using SharedLibraryCore.Configuration;
using SharedLibraryCore.Configuration;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -7,6 +6,8 @@ using System.Linq;
using System.Reflection; using System.Reflection;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using Microsoft.Extensions.Logging;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace IW4MAdmin.Application.Misc namespace IW4MAdmin.Application.Misc
{ {
@ -20,7 +21,7 @@ namespace IW4MAdmin.Application.Misc
private readonly ApplicationConfiguration _appconfig; private readonly ApplicationConfiguration _appconfig;
private readonly ILogger _logger; private readonly ILogger _logger;
public RemoteAssemblyHandler(ILogger logger, ApplicationConfiguration appconfig) public RemoteAssemblyHandler(ILogger<RemoteAssemblyHandler> logger, ApplicationConfiguration appconfig)
{ {
_appconfig = appconfig; _appconfig = appconfig;
_logger = logger; _logger = logger;
@ -41,7 +42,7 @@ namespace IW4MAdmin.Application.Misc
{ {
if (string.IsNullOrEmpty(_appconfig.Id) || string.IsNullOrWhiteSpace(_appconfig.SubscriptionId)) if (string.IsNullOrEmpty(_appconfig.Id) || string.IsNullOrWhiteSpace(_appconfig.SubscriptionId))
{ {
_logger.WriteWarning($"{nameof(_appconfig.Id)} and {nameof(_appconfig.SubscriptionId)} must be provided to attempt loading remote assemblies/scripts"); _logger.LogWarning($"{nameof(_appconfig.Id)} and {nameof(_appconfig.SubscriptionId)} must be provided to attempt loading remote assemblies/scripts");
return new byte[0][]; return new byte[0][];
} }
@ -63,8 +64,7 @@ namespace IW4MAdmin.Application.Misc
catch (CryptographicException ex) catch (CryptographicException ex)
{ {
_logger.WriteError("Could not obtain remote plugin assemblies"); _logger.LogError(ex, "Could not decrypt remote plugin assemblies");
_logger.WriteDebug(ex.GetExceptionInfo());
} }
return decryptedContent; return decryptedContent;

View File

@ -4,7 +4,9 @@ using SharedLibraryCore.Configuration;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using static SharedLibraryCore.Database.Models.EFClient; using static SharedLibraryCore.Database.Models.EFClient;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace IW4MAdmin.Application.Misc namespace IW4MAdmin.Application.Misc
{ {
@ -14,13 +16,15 @@ namespace IW4MAdmin.Application.Misc
public class ScriptCommand : Command public class ScriptCommand : Command
{ {
private readonly Action<GameEvent> _executeAction; private readonly Action<GameEvent> _executeAction;
private readonly ILogger _logger;
public ScriptCommand(string name, string alias, string description, bool isTargetRequired, Permission permission, public ScriptCommand(string name, string alias, string description, bool isTargetRequired, Permission permission,
CommandArgument[] args, Action<GameEvent> executeAction, CommandConfiguration config, ITranslationLookup layout) CommandArgument[] args, Action<GameEvent> executeAction, CommandConfiguration config, ITranslationLookup layout, ILogger<ScriptCommand> logger)
: base(config, layout) : base(config, layout)
{ {
_executeAction = executeAction; _executeAction = executeAction;
_logger = logger;
Name = name; Name = name;
Alias = alias; Alias = alias;
Description = description; Description = description;
@ -29,14 +33,21 @@ namespace IW4MAdmin.Application.Misc
Arguments = args; Arguments = args;
} }
public override Task ExecuteAsync(GameEvent E) public override async Task ExecuteAsync(GameEvent e)
{ {
if (_executeAction == null) if (_executeAction == null)
{ {
throw new InvalidOperationException($"No execute action defined for command \"{Name}\""); throw new InvalidOperationException($"No execute action defined for command \"{Name}\"");
} }
return Task.Run(() => _executeAction(E)); try
{
await Task.Run(() => _executeAction(e));
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to execute ScriptCommand action for command {command} {@event}", Name, e);
}
} }
} }
} }

View File

@ -1,4 +1,5 @@
using Jint; using System;
using Jint;
using Jint.Native; using Jint.Native;
using Jint.Runtime; using Jint.Runtime;
using Microsoft.CSharp.RuntimeBinder; using Microsoft.CSharp.RuntimeBinder;
@ -12,6 +13,9 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Serilog.Context;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace IW4MAdmin.Application.Misc namespace IW4MAdmin.Application.Misc
{ {
@ -39,9 +43,11 @@ namespace IW4MAdmin.Application.Misc
private readonly SemaphoreSlim _onProcessing; private readonly SemaphoreSlim _onProcessing;
private bool successfullyLoaded; private bool successfullyLoaded;
private readonly List<string> _registeredCommandNames; private readonly List<string> _registeredCommandNames;
private readonly ILogger _logger;
public ScriptPlugin(string filename, string workingDirectory = null) public ScriptPlugin(ILogger logger, string filename, string workingDirectory = null)
{ {
_logger = logger;
_fileName = filename; _fileName = filename;
Watcher = new FileSystemWatcher() Watcher = new FileSystemWatcher()
{ {
@ -84,7 +90,7 @@ namespace IW4MAdmin.Application.Misc
foreach (string commandName in _registeredCommandNames) foreach (string commandName in _registeredCommandNames)
{ {
manager.GetLogger(0).WriteDebug($"Removing plugin registered command \"{commandName}\""); _logger.LogDebug("Removing plugin registered command {command}", commandName);
manager.RemoveCommandByName(commandName); manager.RemoveCommandByName(commandName);
} }
@ -129,7 +135,7 @@ namespace IW4MAdmin.Application.Misc
{ {
foreach (var command in GenerateScriptCommands(commands, scriptCommandFactory)) foreach (var command in GenerateScriptCommands(commands, scriptCommandFactory))
{ {
manager.GetLogger(0).WriteDebug($"Adding plugin registered command \"{command.Name}\""); _logger.LogDebug("Adding plugin registered command {commandName}", command.Name);
manager.AddAdditionalCommand(command); manager.AddAdditionalCommand(command);
_registeredCommandNames.Add(command.Name); _registeredCommandNames.Add(command.Name);
} }
@ -167,12 +173,20 @@ namespace IW4MAdmin.Application.Misc
catch (JavaScriptException ex) catch (JavaScriptException ex)
{ {
throw new PluginException($"An error occured while initializing script plugin: {ex.Error} (Line: {ex.Location.Start.Line}, Character: {ex.Location.Start.Column})") { PluginFile = _fileName }; _logger.LogError(ex,
"Encountered JavaScript runtime error while executing {methodName} for script plugin {plugin} initialization {@locationInfo}",
nameof(OnLoadAsync), _fileName, ex.Location);
throw new PluginException("An error occured while initializing script plugin");
} }
catch catch (Exception ex)
{ {
throw; _logger.LogError(ex,
"Encountered unexpected error while running {methodName} for script plugin {plugin} with event type {eventType}",
nameof(OnLoadAsync), _fileName);
throw new PluginException("An unexpected error occured while initializing script plugin");
} }
finally finally
@ -197,10 +211,29 @@ namespace IW4MAdmin.Application.Misc
_scriptEngine.SetValue("_IW4MAdminClient", Utilities.IW4MAdminClient(S)); _scriptEngine.SetValue("_IW4MAdminClient", Utilities.IW4MAdminClient(S));
_scriptEngine.Execute("plugin.onEventAsync(_gameEvent, _server)").GetCompletionValue(); _scriptEngine.Execute("plugin.onEventAsync(_gameEvent, _server)").GetCompletionValue();
} }
catch catch (JavaScriptException ex)
{ {
throw; using (LogContext.PushProperty("Server", S.ToString()))
{
_logger.LogError(ex,
"Encountered JavaScript runtime error while executing {methodName} for script plugin {plugin} with event type {eventType} {@locationInfo}",
nameof(OnEventAsync), _fileName, E.Type, ex.Location);
}
throw new PluginException($"An error occured while executing action for script plugin");
}
catch (Exception e)
{
using (LogContext.PushProperty("Server", S.ToString()))
{
_logger.LogError(e,
"Encountered unexpected error while running {methodName} for script plugin {plugin} with event type {eventType}",
nameof(OnEventAsync), _fileName, E.Type);
}
throw new PluginException($"An error occured while executing action for script plugin");
} }
finally finally
@ -215,7 +248,7 @@ namespace IW4MAdmin.Application.Misc
public Task OnLoadAsync(IManager manager) public Task OnLoadAsync(IManager manager)
{ {
manager.GetLogger(0).WriteDebug($"OnLoad executing for {Name}"); _logger.LogDebug("OnLoad executing for {name}", Name);
_scriptEngine.SetValue("_manager", manager); _scriptEngine.SetValue("_manager", manager);
return Task.FromResult(_scriptEngine.Execute("plugin.onLoadAsync(_manager)").GetCompletionValue()); return Task.FromResult(_scriptEngine.Execute("plugin.onLoadAsync(_manager)").GetCompletionValue());
} }

View File

@ -11,6 +11,9 @@ using System.Net.Sockets;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Serilog.Context;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace IW4MAdmin.Application.RCon namespace IW4MAdmin.Application.RCon
{ {
@ -27,7 +30,7 @@ namespace IW4MAdmin.Application.RCon
private readonly ILogger _log; private readonly ILogger _log;
private readonly Encoding _gameEncoding; private readonly Encoding _gameEncoding;
public RConConnection(string ipAddress, int port, string password, ILogger log, Encoding gameEncoding) public RConConnection(string ipAddress, int port, string password, ILogger<RConConnection> log, Encoding gameEncoding)
{ {
Endpoint = new IPEndPoint(IPAddress.Parse(ipAddress), port); Endpoint = new IPEndPoint(IPAddress.Parse(ipAddress), port);
_gameEncoding = gameEncoding; _gameEncoding = gameEncoding;
@ -49,10 +52,7 @@ namespace IW4MAdmin.Application.RCon
var connectionState = ActiveQueries[this.Endpoint]; var connectionState = ActiveQueries[this.Endpoint];
if (Utilities.IsDevelopment) _log.LogDebug("Waiting for semaphore to be released [{endpoint}]", Endpoint);
{
_log.WriteDebug($"Waiting for semaphore to be released [{this.Endpoint}]");
}
// enter the semaphore so only one query is sent at a time per server. // enter the semaphore so only one query is sent at a time per server.
await connectionState.OnComplete.WaitAsync(); await connectionState.OnComplete.WaitAsync();
@ -66,11 +66,8 @@ namespace IW4MAdmin.Application.RCon
connectionState.LastQuery = DateTime.Now; connectionState.LastQuery = DateTime.Now;
if (Utilities.IsDevelopment) _log.LogDebug("Semaphore has been released [{endpoint}]", Endpoint);
{ _log.LogDebug("Query {@queryInfo}", new { endpoint=Endpoint.ToString(), type, parameters });
_log.WriteDebug($"Semaphore has been released [{Endpoint}]");
_log.WriteDebug($"Query [{Endpoint},{type},{parameters}]");
}
byte[] payload = null; byte[] payload = null;
bool waitForResponse = config.WaitForResponse; bool waitForResponse = config.WaitForResponse;
@ -115,10 +112,16 @@ namespace IW4MAdmin.Application.RCon
// this happens when someone tries to send something that can't be converted into a 7 bit character set // this happens when someone tries to send something that can't be converted into a 7 bit character set
// e.g: emoji -> windows-1252 // e.g: emoji -> windows-1252
catch (OverflowException) catch (OverflowException ex)
{ {
connectionState.OnComplete.Release(1); connectionState.OnComplete.Release(1);
throw new NetworkException($"Invalid character encountered when converting encodings - {parameters}"); using (LogContext.PushProperty("Server", Endpoint.ToString()))
{
_log.LogError(ex, "Could not convert RCon data payload to desired encoding {encoding} {params}",
_gameEncoding.EncodingName, parameters);
}
throw new RConException($"Invalid character encountered when converting encodings");
} }
byte[][] response = null; byte[][] response = null;
@ -137,16 +140,17 @@ namespace IW4MAdmin.Application.RCon
connectionState.ConnectionAttempts++; connectionState.ConnectionAttempts++;
connectionState.BytesReadPerSegment.Clear(); connectionState.BytesReadPerSegment.Clear();
bool exceptionCaught = false; bool exceptionCaught = false;
#if DEBUG == true
_log.WriteDebug($"Sending {payload.Length} bytes to [{this.Endpoint}] ({connectionState.ConnectionAttempts}/{StaticHelpers.AllowedConnectionFails})"); _log.LogDebug("Sending {payloadLength} bytes to [{endpoint}] ({connectionAttempts}/{allowedConnectionFailures})",
#endif payload.Length, Endpoint, connectionState.ConnectionAttempts, StaticHelpers.AllowedConnectionFails);
try try
{ {
response = await SendPayloadAsync(payload, waitForResponse); response = await SendPayloadAsync(payload, waitForResponse);
if ((response.Length == 0 || response[0].Length == 0) && waitForResponse) if ((response.Length == 0 || response[0].Length == 0) && waitForResponse)
{ {
throw new NetworkException("Expected response but got 0 bytes back"); throw new RConException("Expected response but got 0 bytes back");
} }
connectionState.ConnectionAttempts = 0; connectionState.ConnectionAttempts = 0;
@ -162,7 +166,14 @@ namespace IW4MAdmin.Application.RCon
goto retrySend; goto retrySend;
} }
throw new NetworkException(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMUNICATION"].FormatExt(Endpoint)); using (LogContext.PushProperty("Server", Endpoint.ToString()))
{
_log.LogWarning(
"Made {connectionAttempts} attempts to send RCon data to server, but received no response",
connectionState.ConnectionAttempts);
}
connectionState.ConnectionAttempts = 0;
throw new NetworkException("Reached maximum retry attempts to send RCon data to server");
} }
finally finally
@ -177,7 +188,7 @@ namespace IW4MAdmin.Application.RCon
if (response.Length == 0) if (response.Length == 0)
{ {
_log.WriteWarning($"Received empty response for request [{type}, {parameters}, {Endpoint}]"); _log.LogDebug("Received empty response for RCon request {@query}", new { endpoint=Endpoint.ToString(), type, parameters });
return new string[0]; return new string[0];
} }
@ -187,12 +198,12 @@ namespace IW4MAdmin.Application.RCon
// note: not all games respond if the pasword is wrong or not set // note: not all games respond if the pasword is wrong or not set
if (responseString.Contains("Invalid password") || responseString.Contains("rconpassword")) if (responseString.Contains("Invalid password") || responseString.Contains("rconpassword"))
{ {
throw new NetworkException(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_RCON_INVALID"]); throw new RConException(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_RCON_INVALID"]);
} }
if (responseString.Contains("rcon_password")) if (responseString.Contains("rcon_password"))
{ {
throw new NetworkException(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_RCON_NOTSET"]); throw new RConException(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_RCON_NOTSET"]);
} }
if (responseString.Contains(config.ServerNotRunningResponse)) if (responseString.Contains(config.ServerNotRunningResponse))
@ -205,7 +216,13 @@ namespace IW4MAdmin.Application.RCon
if (headerSplit.Length != 2) if (headerSplit.Length != 2)
{ {
throw new NetworkException("Unexpected response header from server"); using (LogContext.PushProperty("Server", Endpoint.ToString()))
{
_log.LogWarning("Invalid response header from server. Expected {expected}, but got {response}",
config.CommandPrefixes.RConResponse, headerSplit.FirstOrDefault());
}
throw new RConException("Unexpected response header from server");
} }
string[] splitResponse = headerSplit.Last().Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries); string[] splitResponse = headerSplit.Last().Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
@ -293,12 +310,17 @@ namespace IW4MAdmin.Application.RCon
if (sendDataPending) if (sendDataPending)
{ {
// the send has not been completed asyncronously // the send has not been completed asynchronously
// this really shouldn't ever happen because it's UDP // this really shouldn't ever happen because it's UDP
if (!await Task.Run(() => connectionState.OnSentData.Wait(StaticHelpers.SocketTimeout(1)))) if (!await Task.Run(() => connectionState.OnSentData.Wait(StaticHelpers.SocketTimeout(1))))
{ {
using(LogContext.PushProperty("Server", Endpoint.ToString()))
{
_log.LogWarning("Socket timed out while sending RCon data on attempt {attempt}",
connectionState.ConnectionAttempts);
}
rconSocket.Close(); rconSocket.Close();
throw new NetworkException("Timed out sending data", rconSocket); throw new NetworkException("Timed out sending RCon data", rconSocket);
} }
} }
@ -314,14 +336,22 @@ namespace IW4MAdmin.Application.RCon
if (receiveDataPending) if (receiveDataPending)
{ {
if (Utilities.IsDevelopment) _log.LogDebug("Waiting to asynchronously receive data on attempt #{connectionAttempts}", connectionState.ConnectionAttempts);
{
_log.WriteDebug($"Waiting to asynchrously receive data on attempt #{connectionState.ConnectionAttempts}");
}
if (!await Task.Run(() => connectionState.OnReceivedData.Wait(StaticHelpers.SocketTimeout(connectionState.ConnectionAttempts)))) if (!await Task.Run(() => connectionState.OnReceivedData.Wait(StaticHelpers.SocketTimeout(connectionState.ConnectionAttempts))))
{ {
if (connectionState.ConnectionAttempts > 1) // this reduces some spam for unstable connections
{
using (LogContext.PushProperty("Server", Endpoint.ToString()))
{
_log.LogWarning(
"Socket timed out while waiting for RCon response on attempt {attempt} with timeout delay of {timeout}",
connectionState.ConnectionAttempts,
StaticHelpers.SocketTimeout(connectionState.ConnectionAttempts));
}
}
rconSocket.Close(); rconSocket.Close();
throw new NetworkException("Timed out waiting for response", rconSocket); throw new NetworkException("Timed out receiving RCon response", rconSocket);
} }
} }
@ -350,10 +380,7 @@ namespace IW4MAdmin.Application.RCon
private void OnDataReceived(object sender, SocketAsyncEventArgs e) private void OnDataReceived(object sender, SocketAsyncEventArgs e)
{ {
if (Utilities.IsDevelopment) _log.LogDebug("Read {bytesTransferred} bytes from {endpoint}", e.BytesTransferred, e.RemoteEndPoint);
{
_log.WriteDebug($"Read {e.BytesTransferred} bytes from {e.RemoteEndPoint}");
}
// this occurs when we close the socket // this occurs when we close the socket
if (e.BytesTransferred == 0) if (e.BytesTransferred == 0)
@ -376,10 +403,7 @@ namespace IW4MAdmin.Application.RCon
if (!sock.ReceiveAsync(state.ReceiveEventArgs)) if (!sock.ReceiveAsync(state.ReceiveEventArgs))
{ {
if (Utilities.IsDevelopment) _log.LogDebug("Read {bytesTransferred} synchronous bytes from {endpoint}", state.ReceiveEventArgs.BytesTransferred, e.RemoteEndPoint);
{
_log.WriteDebug($"Read {state.ReceiveEventArgs.BytesTransferred} synchronous bytes from {e.RemoteEndPoint}");
}
// we need to increment this here because the callback isn't executed if there's no pending IO // we need to increment this here because the callback isn't executed if there's no pending IO
state.BytesReadPerSegment.Add(state.ReceiveEventArgs.BytesTransferred); state.BytesReadPerSegment.Add(state.ReceiveEventArgs.BytesTransferred);
ActiveQueries[this.Endpoint].OnReceivedData.Set(); ActiveQueries[this.Endpoint].OnReceivedData.Set();
@ -401,10 +425,7 @@ namespace IW4MAdmin.Application.RCon
private void OnDataSent(object sender, SocketAsyncEventArgs e) private void OnDataSent(object sender, SocketAsyncEventArgs e)
{ {
if (Utilities.IsDevelopment) _log.LogDebug("Sent {byteCount} bytes to {endpoint}", e.Buffer?.Length, e.ConnectSocket?.RemoteEndPoint);
{
_log.WriteDebug($"Sent {e.Buffer?.Length} bytes to {e.ConnectSocket?.RemoteEndPoint?.ToString()}");
}
ActiveQueries[this.Endpoint].OnSentData.Set(); ActiveQueries[this.Endpoint].OnSentData.Set();
} }
} }

View File

@ -8,14 +8,19 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using static SharedLibraryCore.Server; using static SharedLibraryCore.Server;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace IW4MAdmin.Application.RconParsers namespace IW4MAdmin.Application.RconParsers
{ {
public class BaseRConParser : IRConParser public class BaseRConParser : IRConParser
{ {
public BaseRConParser(IParserRegexFactory parserRegexFactory) private readonly ILogger _logger;
public BaseRConParser(ILogger<BaseRConParser> logger, IParserRegexFactory parserRegexFactory)
{ {
_logger = logger;
Configuration = new DynamicRConParserConfiguration(parserRegexFactory) Configuration = new DynamicRConParserConfiguration(parserRegexFactory)
{ {
CommandPrefixes = new CommandPrefix() CommandPrefixes = new CommandPrefix()
@ -133,12 +138,7 @@ namespace IW4MAdmin.Application.RconParsers
public virtual async Task<(List<EFClient>, string, string)> GetStatusAsync(IRConConnection connection) public virtual async Task<(List<EFClient>, string, string)> GetStatusAsync(IRConConnection connection)
{ {
string[] response = await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND_STATUS); string[] response = await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND_STATUS);
#if DEBUG _logger.LogDebug("Status Response {@response}", (object)response);
foreach (var line in response)
{
Console.WriteLine(line);
}
#endif
return (ClientsFromStatus(response), MapFromStatus(response), GameTypeFromStatus(response)); return (ClientsFromStatus(response), MapFromStatus(response), GameTypeFromStatus(response));
} }

View File

@ -1,4 +1,5 @@
using SharedLibraryCore.Interfaces; using Microsoft.Extensions.Logging;
using SharedLibraryCore.Interfaces;
namespace IW4MAdmin.Application.RconParsers namespace IW4MAdmin.Application.RconParsers
{ {
@ -8,7 +9,7 @@ namespace IW4MAdmin.Application.RconParsers
/// </summary> /// </summary>
sealed internal class DynamicRConParser : BaseRConParser sealed internal class DynamicRConParser : BaseRConParser
{ {
public DynamicRConParser(IParserRegexFactory parserRegexFactory) : base(parserRegexFactory) public DynamicRConParser(ILogger<BaseRConParser> logger, IParserRegexFactory parserRegexFactory) : base(logger, parserRegexFactory)
{ {
} }
} }

View File

@ -10,7 +10,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.SyndicationFeed.ReaderWriter" Version="1.0.2" /> <PackageReference Include="Microsoft.SyndicationFeed.ReaderWriter" Version="1.0.2" />
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2.4.9" PrivateAssets="All" /> <PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2020.11.11.1" PrivateAssets="All" />
</ItemGroup> </ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent"> <Target Name="PostBuild" AfterTargets="PostBuildEvent">

View File

@ -10,7 +10,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2.4.9" PrivateAssets="All" /> <PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2020.11.11.1" PrivateAssets="All" />
</ItemGroup> </ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent"> <Target Name="PostBuild" AfterTargets="PostBuildEvent">

View File

@ -16,7 +16,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2.4.9" PrivateAssets="All" /> <PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2020.11.11.1" PrivateAssets="All" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -1,11 +1,12 @@
using LiveRadar.Configuration; using LiveRadar.Configuration;
using SharedLibraryCore; using SharedLibraryCore;
using SharedLibraryCore.Configuration;
using SharedLibraryCore.Interfaces;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using SharedLibraryCore.Interfaces;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace LiveRadar namespace LiveRadar
{ {
@ -21,11 +22,13 @@ namespace LiveRadar
private readonly Dictionary<string, long> _botGuidLookups; private readonly Dictionary<string, long> _botGuidLookups;
private bool addedPage; private bool addedPage;
private readonly object lockObject = new object(); private readonly object lockObject = new object();
private readonly ILogger _logger;
public Plugin(IConfigurationHandlerFactory configurationHandlerFactory) public Plugin(ILogger<Plugin> logger, IConfigurationHandlerFactory configurationHandlerFactory)
{ {
_configurationHandler = configurationHandlerFactory.GetConfigurationHandler<LiveRadarConfiguration>("LiveRadarConfiguration"); _configurationHandler = configurationHandlerFactory.GetConfigurationHandler<LiveRadarConfiguration>("LiveRadarConfiguration");
_botGuidLookups = new Dictionary<string, long>(); _botGuidLookups = new Dictionary<string, long>();
_logger = logger;
} }
public Task OnEventAsync(GameEvent E, Server S) public Task OnEventAsync(GameEvent E, Server S)
@ -80,8 +83,7 @@ namespace LiveRadar
catch (Exception e) catch (Exception e)
{ {
S.Logger.WriteWarning($"Could not parse live radar output: {e.Data}"); _logger.LogError(e, "Could not parse live radar output: {data}", e.Data);
S.Logger.WriteDebug(e.GetExceptionInfo());
} }
} }

View File

@ -23,7 +23,7 @@
</Target> </Target>
<ItemGroup> <ItemGroup>
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2.4.9" PrivateAssets="All" /> <PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2020.11.11.1" PrivateAssets="All" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -16,7 +16,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2.4.9" PrivateAssets="All" /> <PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2020.11.11.1" PrivateAssets="All" />
</ItemGroup> </ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent"> <Target Name="PostBuild" AfterTargets="PostBuildEvent">

View File

@ -21,7 +21,7 @@ var plugin = {
rconParser.Configuration.CommandPrefixes.TempBan = 'clientkick {0} "{1}"'; rconParser.Configuration.CommandPrefixes.TempBan = 'clientkick {0} "{1}"';
rconParser.Configuration.CommandPrefixes.RConResponse = '\xff\xff\xff\xffprint\n'; rconParser.Configuration.CommandPrefixes.RConResponse = '\xff\xff\xff\xffprint\n';
rconParser.Configuration.Dvar.Pattern = '^ *\\"(.+)\\" is: \\"(.+)?\\" default: \\"(.+)?\\"\\n(?:latched: \\"(.+)?\\"\\n)? *(.+)$'; rconParser.Configuration.Dvar.Pattern = '^ *\\"(.+)\\" is: \\"(.+)?\\" default: \\"(.+)?\\"\\n(?:latched: \\"(.+)?\\"\\n)? *(.+)$';
rconParser.Configuration.Status.Pattern = '^ *([0-9]+) +-?([0-9]+) +(Yes|No) +((?:[A-Z]+|[0-9]+)) +((?:[a-z]|[0-9]){8,32}|(?:[a-z]|[0-9]){8,32}|bot[0-9]+|(?:[0-9]+)) *(.{0,32}) +(\d+\.\d+\.\d+.\d+\:-*\d{1,5}|0+.0+:-*\d{1,5}|loopback|unknown|bot) +(-*[0-9]+) *$'; rconParser.Configuration.Status.Pattern = '^ *([0-9]+) +-?([0-9]+) +(Yes|No) +((?:[A-Z]+|[0-9]+)) +((?:[a-z]|[0-9]){8,32}|(?:[a-z]|[0-9]){8,32}|bot[0-9]+|(?:[0-9]+)) *(.{0,32}) +(\\d+\\.\\d+\\.\\d+.\\d+\\:-*\\d{1,5}|0+.0+:-*\\d{1,5}|loopback|unknown|bot) +(-*[0-9]+) *$';
rconParser.Configuration.StatusHeader.Pattern = 'num +score +bot +ping +guid +name +address +qport *'; rconParser.Configuration.StatusHeader.Pattern = 'num +score +bot +ping +guid +name +address +qport *';
rconParser.Configuration.WaitForResponse = false; rconParser.Configuration.WaitForResponse = false;
rconParser.Configuration.Status.AddMapping(102, 4); rconParser.Configuration.Status.AddMapping(102, 4);

View File

@ -27,6 +27,7 @@ let commands = [{
// we want to print out a pong message for the number of times they requested // we want to print out a pong message for the number of times they requested
for (var i = 0; i < times; i++) { for (var i = 0; i < times; i++) {
gameEvent.Origin = undefined;
gameEvent.Origin.Tell(`^${i}pong #${i + 1}^7`); gameEvent.Origin.Tell(`^${i}pong #${i + 1}^7`);
// don't want to wait if it's the last pong // don't want to wait if it's the last pong

View File

@ -1,13 +1,13 @@
using IW4MAdmin.Plugins.Stats.Models; using IW4MAdmin.Plugins.Stats.Models;
using SharedLibraryCore;
using SharedLibraryCore.Database.Models; using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Helpers; using SharedLibraryCore.Helpers;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Microsoft.Extensions.Logging;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace IW4MAdmin.Plugins.Stats.Cheat namespace IW4MAdmin.Plugins.Stats.Cheat
{ {
@ -223,11 +223,6 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
if (weightedSessionAverage > Thresholds.MaxOffset(totalSessionHits) && if (weightedSessionAverage > Thresholds.MaxOffset(totalSessionHits) &&
totalSessionHits >= (Thresholds.MediumSampleMinKills * 2)) totalSessionHits >= (Thresholds.MediumSampleMinKills * 2))
{ {
Log.WriteDebug("*** Reached Max Session Average for Angle Difference ***");
Log.WriteDebug($"Session Average = {weightedSessionAverage}");
Log.WriteDebug($"HitCount = {HitCount}");
Log.WriteDebug($"ID = {hit.AttackerId}");
results.Add(new DetectionPenaltyResult() results.Add(new DetectionPenaltyResult()
{ {
ClientPenalty = EFPenalty.PenaltyType.Ban, ClientPenalty = EFPenalty.PenaltyType.Ban,
@ -237,18 +232,14 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
Location = hitLoc.Location Location = hitLoc.Location
}); });
} }
#if DEBUG Log.LogDebug("PredictVsReal={realAgainstPredict}", realAgainstPredict);
Log.WriteDebug($"PredictVsReal={realAgainstPredict}");
#endif
} }
#endregion #endregion
#region STRAIN #region STRAIN
double currentStrain = Strain.GetStrain(hit.Distance / 0.0254, hit.ViewAngles, Math.Max(50, LastOffset == 0 ? 50 : (hit.TimeOffset - LastOffset))); double currentStrain = Strain.GetStrain(hit.Distance / 0.0254, hit.ViewAngles, Math.Max(50, LastOffset == 0 ? 50 : (hit.TimeOffset - LastOffset)));
#if DEBUG == true Log.LogDebug("Current Strain: {currentStrain}", currentStrain);
Log.WriteDebug($"Current Strain: {currentStrain}");
#endif
LastOffset = hit.TimeOffset; LastOffset = hit.TimeOffset;
if (currentStrain > ClientStats.MaxStrain) if (currentStrain > ClientStats.MaxStrain)

View File

@ -27,12 +27,6 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
double[] distance = Utilities.AngleStuff(newAngle, LastAngle); double[] distance = Utilities.AngleStuff(newAngle, LastAngle);
LastDistance = distance[0] + distance[1]; LastDistance = distance[0] + distance[1];
#if DEBUG == true
Console.WriteLine($"Angle Between = {LastDistance}");
Console.WriteLine($"Distance From Target = {killDistance}");
Console.WriteLine($"Time Offset = {deltaTime}");
Console.WriteLine($"Decay Factor = {decayFactor} ");
#endif
// this happens on first kill // this happens on first kill
if ((distance[0] == 0 && distance[1] == 0) || if ((distance[0] == 0 && distance[1] == 0) ||
deltaTime == 0 || deltaTime == 0 ||

View File

@ -0,0 +1,7 @@
namespace Stats.Helpers
{
public class MigrationHelper
{
}
}

View File

@ -4,7 +4,6 @@ using IW4MAdmin.Plugins.Stats.Models;
using IW4MAdmin.Plugins.Stats.Web.Dtos; using IW4MAdmin.Plugins.Stats.Web.Dtos;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using SharedLibraryCore; using SharedLibraryCore;
using SharedLibraryCore.Database;
using SharedLibraryCore.Database.Models; using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Helpers; using SharedLibraryCore.Helpers;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
@ -15,7 +14,9 @@ using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using static IW4MAdmin.Plugins.Stats.Cheat.Detection; using static IW4MAdmin.Plugins.Stats.Cheat.Detection;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace IW4MAdmin.Plugins.Stats.Helpers namespace IW4MAdmin.Plugins.Stats.Helpers
{ {
@ -30,10 +31,10 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
public static string CLIENT_STATS_KEY = "ClientStats"; public static string CLIENT_STATS_KEY = "ClientStats";
public static string CLIENT_DETECTIONS_KEY = "ClientDetections"; public static string CLIENT_DETECTIONS_KEY = "ClientDetections";
public StatManager(IManager mgr, IDatabaseContextFactory contextFactory, IConfigurationHandler<StatsConfiguration> configHandler) public StatManager(ILogger<StatManager> logger, IManager mgr, IDatabaseContextFactory contextFactory, IConfigurationHandler<StatsConfiguration> configHandler)
{ {
_servers = new ConcurrentDictionary<long, ServerStats>(); _servers = new ConcurrentDictionary<long, ServerStats>();
_log = mgr.GetLogger(0); _log = logger;
_contextFactory = contextFactory; _contextFactory = contextFactory;
_configHandler = configHandler; _configHandler = configHandler;
} }
@ -265,8 +266,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
catch (Exception e) catch (Exception e)
{ {
_log.WriteError($"{Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_STATS_ERROR_ADD"]} - {e.Message}"); _log.LogError(e, "{message}", Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_STATS_ERROR_ADD"]);
_log.WriteDebug(e.GetExceptionInfo());
} }
} }
@ -283,7 +283,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
if (!_servers.ContainsKey(serverId)) if (!_servers.ContainsKey(serverId))
{ {
_log.WriteError($"[Stats::AddPlayer] Server with id {serverId} could not be found"); _log.LogError("[Stats::AddPlayer] Server with id {serverId} could not be found", serverId);
return null; return null;
} }
@ -360,7 +360,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
clientStats.LastScore = pl.Score; clientStats.LastScore = pl.Score;
pl.SetAdditionalProperty(CLIENT_DETECTIONS_KEY, new Detection(_log, clientStats)); pl.SetAdditionalProperty(CLIENT_DETECTIONS_KEY, new Detection(_log, clientStats));
pl.CurrentServer.Logger.WriteInfo($"Added {pl} to stats"); _log.LogDebug("Added {client} to stats", pl.ToString());
} }
return clientStats; return clientStats;
@ -368,8 +368,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
catch (Exception ex) catch (Exception ex)
{ {
_log.WriteWarning("Could not add client to stats"); _log.LogError(ex, "Could not add client to stats {@client}", pl);
_log.WriteDebug(ex.GetExceptionInfo());
} }
return null; return null;
@ -382,11 +381,11 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
/// <returns></returns> /// <returns></returns>
public async Task RemovePlayer(EFClient pl) public async Task RemovePlayer(EFClient pl)
{ {
pl.CurrentServer.Logger.WriteInfo($"Removing {pl} from stats"); _log.LogDebug("Removing {client} from stats", pl.ToString());
if (pl.CurrentServer == null) if (pl.CurrentServer == null)
{ {
pl.CurrentServer.Logger.WriteWarning($"Disconnecting client {pl} is not on a server, state is {pl.State}"); _log.LogWarning("Disconnecting client {@client} is not on a server", pl);
return; return;
} }
@ -407,7 +406,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
else else
{ {
pl.CurrentServer.Logger.WriteWarning($"Disconnecting client {pl} has not been added to stats, state is {pl.State}"); _log.LogWarning("Disconnecting client {@client} has not been added to stats", pl);
} }
} }
@ -452,10 +451,9 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
} }
} }
catch (FormatException) catch (FormatException ex)
{ {
_log.WriteError("Could not parse vector data from hit"); _log.LogWarning(ex, "Could not parse vector data from hit");
_log.WriteDebug($"Kill - {killOrigin} Death - {deathOrigin} ViewAngle - {viewAngles} Snapshot - {string.Join(",", snapshotAngles.Select(_a => _a.ToString()))}");
return; return;
} }
@ -527,8 +525,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
catch (Exception e) catch (Exception e)
{ {
_log.WriteError("Could not store client kills"); _log.LogError(e, "Could not store client kills");
_log.WriteDebug(e.GetExceptionInfo());
} }
finally finally
@ -583,9 +580,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
catch (TaskCanceledException) { } catch (TaskCanceledException) { }
catch (Exception ex) catch (Exception ex)
{ {
_log.WriteError("Could not save hit or AC info"); _log.LogError(ex, "Could not save hit or anti-cheat info {@attacker} {@victim} {server}", attacker, victim, serverId);
_log.WriteDebug(ex.GetExceptionInfo());
_log.WriteDebug($"Attacker: {attacker} Victim: {victim}, ServerId {serverId}");
} }
finally finally
@ -714,9 +709,6 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
var attackerStats = attacker.GetAdditionalProperty<EFClientStatistics>(CLIENT_STATS_KEY); var attackerStats = attacker.GetAdditionalProperty<EFClientStatistics>(CLIENT_STATS_KEY);
var victimStats = victim.GetAdditionalProperty<EFClientStatistics>(CLIENT_STATS_KEY); var victimStats = victim.GetAdditionalProperty<EFClientStatistics>(CLIENT_STATS_KEY);
#if DEBUG
_log.WriteDebug("Processing standard kill");
#endif
// update the total stats // update the total stats
_servers[serverId].ServerStatistics.TotalKills += 1; _servers[serverId].ServerStatistics.TotalKills += 1;
@ -754,14 +746,14 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
// fixme: why? // fixme: why?
if (double.IsNaN(victimStats.SPM) || double.IsNaN(victimStats.Skill)) if (double.IsNaN(victimStats.SPM) || double.IsNaN(victimStats.Skill))
{ {
_log.WriteDebug($"[StatManager::AddStandardKill] victim SPM/SKILL {victimStats.SPM} {victimStats.Skill}"); _log.LogWarning("victim SPM/SKILL {@victimStats}", victimStats);
victimStats.SPM = 0.0; victimStats.SPM = 0.0;
victimStats.Skill = 0.0; victimStats.Skill = 0.0;
} }
if (double.IsNaN(attackerStats.SPM) || double.IsNaN(attackerStats.Skill)) if (double.IsNaN(attackerStats.SPM) || double.IsNaN(attackerStats.Skill))
{ {
_log.WriteDebug($"[StatManager::AddStandardKill] attacker SPM/SKILL {victimStats.SPM} {victimStats.Skill}"); _log.LogWarning("attacker SPM/SKILL {@attackerStats}", attackerStats);
attackerStats.SPM = 0.0; attackerStats.SPM = 0.0;
attackerStats.Skill = 0.0; attackerStats.Skill = 0.0;
} }
@ -781,8 +773,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
catch (Exception e) catch (Exception e)
{ {
_log.WriteWarning($"Could not update stat history for {attacker}"); _log.LogWarning(e, "Could not update stat history for {attacker}", attacker.ToString());
_log.WriteDebug(e.GetExceptionInfo());
} }
finally finally
@ -1099,8 +1090,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
if (clientStats.SPM < 0) if (clientStats.SPM < 0)
{ {
_log.WriteWarning("[StatManager:UpdateStats] clientStats SPM < 0"); _log.LogWarning("clientStats SPM < 0 {scoreDifference} {@clientStats}", scoreDifference, clientStats);
_log.WriteDebug($"{scoreDifference}-{clientStats.RoundScore} - {clientStats.LastScore} - {clientStats.SessionScore}");
clientStats.SPM = 0; clientStats.SPM = 0;
} }
@ -1110,8 +1100,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
// fixme: how does this happen? // fixme: how does this happen?
if (double.IsNaN(clientStats.SPM) || double.IsNaN(clientStats.Skill)) if (double.IsNaN(clientStats.SPM) || double.IsNaN(clientStats.Skill))
{ {
_log.WriteWarning("[StatManager::UpdateStats] clientStats SPM/Skill NaN"); _log.LogWarning("clientStats SPM/Skill NaN {@killInfo}", new {killSPM, KDRWeight, totalPlayTime, SPMAgainstPlayWeight, clientStats, scoreDifference});
_log.WriteDebug($"{killSPM}-{KDRWeight}-{totalPlayTime}-{SPMAgainstPlayWeight}-{clientStats.SPM}-{clientStats.Skill}-{scoreDifference}");
clientStats.SPM = 0; clientStats.SPM = 0;
clientStats.Skill = 0; clientStats.Skill = 0;
} }
@ -1133,7 +1122,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
if (serverStats == null) if (serverStats == null)
{ {
_log.WriteDebug($"Initializing server stats for {serverId}"); _log.LogDebug("Initializing server stats for {serverId}", serverId);
// server stats have never been generated before // server stats have never been generated before
serverStats = new EFServerStatistics() serverStats = new EFServerStatistics()
{ {
@ -1225,8 +1214,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
catch (Exception e) catch (Exception e)
{ {
_log.WriteError("There was a probably syncing server stats"); _log.LogError(e, "There was a problem syncing server stats");
_log.WriteDebug(e.GetExceptionInfo());
} }
finally finally

View File

@ -14,6 +14,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
namespace IW4MAdmin.Plugins.Stats namespace IW4MAdmin.Plugins.Stats
{ {
@ -28,23 +29,24 @@ namespace IW4MAdmin.Plugins.Stats
public static StatManager Manager { get; private set; } public static StatManager Manager { get; private set; }
public static IManager ServerManager; public static IManager ServerManager;
public static IConfigurationHandler<StatsConfiguration> Config { get; private set; } public static IConfigurationHandler<StatsConfiguration> Config { get; private set; }
#if DEBUG
int scriptDamageCount;
int scriptKillCount;
#endif
private readonly IDatabaseContextFactory _databaseContextFactory; private readonly IDatabaseContextFactory _databaseContextFactory;
private readonly ITranslationLookup _translationLookup; private readonly ITranslationLookup _translationLookup;
private readonly IMetaService _metaService; private readonly IMetaService _metaService;
private readonly IResourceQueryHelper<ChatSearchQuery, MessageResponse> _chatQueryHelper; private readonly IResourceQueryHelper<ChatSearchQuery, MessageResponse> _chatQueryHelper;
private readonly ILogger<StatManager> _managerLogger;
private readonly ILogger<Plugin> _logger;
public Plugin(IConfigurationHandlerFactory configurationHandlerFactory, IDatabaseContextFactory databaseContextFactory, public Plugin(ILogger<Plugin> logger, IConfigurationHandlerFactory configurationHandlerFactory, IDatabaseContextFactory databaseContextFactory,
ITranslationLookup translationLookup, IMetaService metaService, IResourceQueryHelper<ChatSearchQuery, MessageResponse> chatQueryHelper) ITranslationLookup translationLookup, IMetaService metaService, IResourceQueryHelper<ChatSearchQuery, MessageResponse> chatQueryHelper, ILogger<StatManager> managerLogger)
{ {
Config = configurationHandlerFactory.GetConfigurationHandler<StatsConfiguration>("StatsPluginSettings"); Config = configurationHandlerFactory.GetConfigurationHandler<StatsConfiguration>("StatsPluginSettings");
_databaseContextFactory = databaseContextFactory; _databaseContextFactory = databaseContextFactory;
_translationLookup = translationLookup; _translationLookup = translationLookup;
_metaService = metaService; _metaService = metaService;
_chatQueryHelper = chatQueryHelper; _chatQueryHelper = chatQueryHelper;
_managerLogger = managerLogger;
_logger = logger;
} }
public async Task OnEventAsync(GameEvent E, Server S) public async Task OnEventAsync(GameEvent E, Server S)
@ -102,23 +104,13 @@ namespace IW4MAdmin.Plugins.Stats
{ {
E.Origin = E.Target; E.Origin = E.Target;
} }
#if DEBUG
scriptKillCount++;
S.Logger.WriteInfo($"Start ScriptKill {scriptKillCount}");
#endif
await Manager.AddScriptHit(false, E.Time, E.Origin, E.Target, StatManager.GetIdForServer(S), S.CurrentMap.Name, killInfo[7], killInfo[8], await Manager.AddScriptHit(false, E.Time, E.Origin, E.Target, StatManager.GetIdForServer(S), S.CurrentMap.Name, killInfo[7], killInfo[8],
killInfo[5], killInfo[6], killInfo[3], killInfo[4], killInfo[9], killInfo[10], killInfo[11], killInfo[12], killInfo[13], killInfo[14], killInfo[15], killInfo[16], killInfo[17]); killInfo[5], killInfo[6], killInfo[3], killInfo[4], killInfo[9], killInfo[10], killInfo[11], killInfo[12], killInfo[13], killInfo[14], killInfo[15], killInfo[16], killInfo[17]);
#if DEBUG
S.Logger.WriteInfo($"End ScriptKill {scriptKillCount}");
#endif
} }
else else
{ {
S.Logger.WriteDebug("Skipping script kill as it is ignored or data in customcallbacks is outdated/missing"); _logger.LogDebug("Skipping script kill as it is ignored or data in customcallbacks is outdated/missing");
} }
break; break;
case GameEvent.EventType.Kill: case GameEvent.EventType.Kill:
@ -155,22 +147,13 @@ namespace IW4MAdmin.Plugins.Stats
E.Origin = E.Target; E.Origin = E.Target;
} }
#if DEBUG
scriptDamageCount++;
S.Logger.WriteInfo($"Start ScriptDamage {scriptDamageCount}");
#endif
await Manager.AddScriptHit(true, E.Time, E.Origin, E.Target, StatManager.GetIdForServer(S), S.CurrentMap.Name, killInfo[7], killInfo[8], await Manager.AddScriptHit(true, E.Time, E.Origin, E.Target, StatManager.GetIdForServer(S), S.CurrentMap.Name, killInfo[7], killInfo[8],
killInfo[5], killInfo[6], killInfo[3], killInfo[4], killInfo[9], killInfo[10], killInfo[11], killInfo[12], killInfo[13], killInfo[14], killInfo[15], killInfo[16], killInfo[17]); killInfo[5], killInfo[6], killInfo[3], killInfo[4], killInfo[9], killInfo[10], killInfo[11], killInfo[12], killInfo[13], killInfo[14], killInfo[15], killInfo[16], killInfo[17]);
#if DEBUG
S.Logger.WriteInfo($"End ScriptDamage {scriptDamageCount}");
#endif
} }
else else
{ {
S.Logger.WriteDebug("Skipping script damage as it is ignored or data in customcallbacks is outdated/missing"); _logger.LogDebug("Skipping script damage as it is ignored or data in customcallbacks is outdated/missing");
} }
break; break;
} }
@ -457,7 +440,7 @@ namespace IW4MAdmin.Plugins.Stats
manager.GetMessageTokens().Add(new MessageToken("MOSTKILLS", mostKills)); manager.GetMessageTokens().Add(new MessageToken("MOSTKILLS", mostKills));
ServerManager = manager; ServerManager = manager;
Manager = new StatManager(manager, _databaseContextFactory, Config); Manager = new StatManager(_managerLogger, manager, _databaseContextFactory, Config);
} }
public Task OnTickAsync(Server S) public Task OnTickAsync(Server S)

View File

@ -13,13 +13,15 @@
<Copyright>2018</Copyright> <Copyright>2018</Copyright>
<Configurations>Debug;Release;Prerelease</Configurations> <Configurations>Debug;Release;Prerelease</Configurations>
<LangVersion>8.0</LangVersion> <LangVersion>8.0</LangVersion>
<IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2.4.9" PrivateAssets="All" /> <PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2020.11.11.1" PrivateAssets="All" />
</ItemGroup> </ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent"> <Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="dotnet publish $(ProjectPath) -c $(ConfigurationName) -o $(ProjectDir)..\..\Build\Plugins --no-build --no-restore --no-dependencies" /> <Exec Command="dotnet publish $(ProjectPath) -c $(ConfigurationName) -o $(ProjectDir)..\..\Build\Plugins --no-build --no-restore --no-dependencies" />
</Target> </Target>
</Project> </Project>

View File

@ -1,11 +1,12 @@
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using SharedLibraryCore;
using SharedLibraryCore.Dtos; using SharedLibraryCore.Dtos;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using Stats.Dtos; using Stats.Dtos;
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace StatsWeb.API namespace StatsWeb.API
{ {
@ -16,7 +17,7 @@ namespace StatsWeb.API
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IResourceQueryHelper<StatsInfoRequest, StatsInfoResult> _statsQueryHelper; private readonly IResourceQueryHelper<StatsInfoRequest, StatsInfoResult> _statsQueryHelper;
public StatsController(ILogger logger, IResourceQueryHelper<StatsInfoRequest, StatsInfoResult> statsQueryHelper) public StatsController(ILogger<StatsController> logger, IResourceQueryHelper<StatsInfoRequest, StatsInfoResult> statsQueryHelper)
{ {
_statsQueryHelper = statsQueryHelper; _statsQueryHelper = statsQueryHelper;
_logger = logger; _logger = logger;
@ -56,8 +57,7 @@ namespace StatsWeb.API
catch (Exception e) catch (Exception e)
{ {
_logger.WriteWarning($"Could not get client stats for client id {clientId}"); _logger.LogWarning(e, "Could not get client stats for client id {clientId}", clientId);
_logger.WriteDebug(e.GetExceptionInfo());
return StatusCode(StatusCodes.Status500InternalServerError, new ErrorResponse return StatusCode(StatusCodes.Status500InternalServerError, new ErrorResponse
{ {

View File

@ -10,6 +10,8 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace StatsWeb namespace StatsWeb
{ {
@ -23,7 +25,7 @@ namespace StatsWeb
private readonly ApplicationConfiguration _appConfig; private readonly ApplicationConfiguration _appConfig;
private List<EFServer> serverCache; private List<EFServer> serverCache;
public ChatResourceQueryHelper(ILogger logger, IDatabaseContextFactory contextFactory, ApplicationConfiguration appConfig) public ChatResourceQueryHelper(ILogger<ChatResourceQueryHelper> logger, IDatabaseContextFactory contextFactory, ApplicationConfiguration appConfig)
{ {
_contextFactory = contextFactory; _contextFactory = contextFactory;
_logger = logger; _logger = logger;

View File

@ -12,6 +12,8 @@ using StatsWeb.Extensions;
using System; using System;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace IW4MAdmin.Plugins.Web.StatsWeb.Controllers namespace IW4MAdmin.Plugins.Web.StatsWeb.Controllers
{ {
@ -22,7 +24,7 @@ namespace IW4MAdmin.Plugins.Web.StatsWeb.Controllers
private readonly IResourceQueryHelper<ChatSearchQuery, MessageResponse> _chatResourceQueryHelper; private readonly IResourceQueryHelper<ChatSearchQuery, MessageResponse> _chatResourceQueryHelper;
private readonly ITranslationLookup _translationLookup; private readonly ITranslationLookup _translationLookup;
public StatsController(ILogger logger, IManager manager, IResourceQueryHelper<ChatSearchQuery, MessageResponse> resourceQueryHelper, public StatsController(ILogger<StatsController> logger, IManager manager, IResourceQueryHelper<ChatSearchQuery, MessageResponse> resourceQueryHelper,
ITranslationLookup translationLookup) : base(manager) ITranslationLookup translationLookup) : base(manager)
{ {
_logger = logger; _logger = logger;
@ -108,15 +110,13 @@ namespace IW4MAdmin.Plugins.Web.StatsWeb.Controllers
catch (ArgumentException e) catch (ArgumentException e)
{ {
_logger.WriteWarning($"Could not parse chat message search query - {query}"); _logger.LogWarning(e, "Could not parse chat message search query {query}", query);
_logger.WriteDebug(e.GetExceptionInfo());
ViewBag.Error = e; ViewBag.Error = e;
} }
catch (FormatException e) catch (FormatException e)
{ {
_logger.WriteWarning($"Could not parse chat message search query filter format - {query}"); _logger.LogWarning(e, "Could not parse chat message search query filter format {query}", query);
_logger.WriteDebug(e.GetExceptionInfo());
ViewBag.Error = e; ViewBag.Error = e;
} }
@ -136,15 +136,13 @@ namespace IW4MAdmin.Plugins.Web.StatsWeb.Controllers
catch (ArgumentException e) catch (ArgumentException e)
{ {
_logger.WriteWarning($"Could not parse chat message search query - {query}"); _logger.LogWarning(e, "Could not parse chat message search query {query}", query);
_logger.WriteDebug(e.GetExceptionInfo());
throw; throw;
} }
catch (FormatException e) catch (FormatException e)
{ {
_logger.WriteWarning($"Could not parse chat message search query filter format - {query}"); _logger.LogWarning(e, "Could not parse chat message search query filter format {query}", query);
_logger.WriteDebug(e.GetExceptionInfo());
throw; throw;
} }

View File

@ -14,7 +14,7 @@
<RunPostBuildEvent>Always</RunPostBuildEvent> <RunPostBuildEvent>Always</RunPostBuildEvent>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2.4.9" PrivateAssets="All" /> <PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2020.11.11.1" PrivateAssets="All" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@ -28,7 +28,7 @@
<CopyToPublishDirectory>Never</CopyToPublishDirectory> <CopyToPublishDirectory>Never</CopyToPublishDirectory>
</Content> </Content>
</ItemGroup> </ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent"> <Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="dotnet publish $(ProjectPath) -c $(ConfigurationName) -o $(ProjectDir)..\..\..\BUILD\Plugins --no-build --no-restore --no-dependencies" /> <Exec Command="dotnet publish $(ProjectPath) -c $(ConfigurationName) -o $(ProjectDir)..\..\..\BUILD\Plugins --no-build --no-restore --no-dependencies" />
</Target> </Target>

View File

@ -16,7 +16,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2.4.9" PrivateAssets="All" /> <PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2020.11.11.1" PrivateAssets="All" />
</ItemGroup> </ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent"> <Target Name="PostBuild" AfterTargets="PostBuildEvent">

View File

@ -6,6 +6,7 @@ using SharedLibraryCore.Configuration;
using SharedLibraryCore.Database.Models; using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using static SharedLibraryCore.Server; using static SharedLibraryCore.Server;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace SharedLibraryCore namespace SharedLibraryCore
{ {

View File

@ -12,6 +12,8 @@ using System.Security.Cryptography;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Serilog.Context;
using static SharedLibraryCore.Database.Models.EFClient; using static SharedLibraryCore.Database.Models.EFClient;
namespace SharedLibraryCore.Commands namespace SharedLibraryCore.Commands
@ -646,7 +648,7 @@ namespace SharedLibraryCore.Commands
/// </summary> /// </summary>
public class SetLevelCommand : Command public class SetLevelCommand : Command
{ {
public SetLevelCommand(CommandConfiguration config, ITranslationLookup translationLookup, ILogger logger) : base(config, translationLookup) public SetLevelCommand(CommandConfiguration config, ITranslationLookup translationLookup, ILogger<SetLevelCommand> logger) : base(config, translationLookup)
{ {
Name = "setlevel"; Name = "setlevel";
Description = _translationLookup["COMMANDS_SETLEVEL_DESC"]; Description = _translationLookup["COMMANDS_SETLEVEL_DESC"];
@ -727,13 +729,16 @@ namespace SharedLibraryCore.Commands
gameEvent.Owner.Manager.GetActiveClients() gameEvent.Owner.Manager.GetActiveClients()
.FirstOrDefault(c => c.ClientId == targetClient?.ClientId) ?? targetClient : targetClient; .FirstOrDefault(c => c.ClientId == targetClient?.ClientId) ?? targetClient : targetClient;
logger.WriteInfo($"Beginning set level of client {gameEvent.Origin} to {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) if (result.Failed)
{ {
logger.WriteInfo($"Failed to set level of client {gameEvent.Origin}"); using (LogContext.PushProperty("Server", gameEvent.Origin.CurrentServer?.ToString()))
{
logger.LogWarning("Failed to set level of client {origin}", gameEvent.Origin.ToString());
}
gameEvent.Origin.Tell(_translationLookup["SERVER_ERROR_COMMAND_INGAME"]); gameEvent.Origin.Tell(_translationLookup["SERVER_ERROR_COMMAND_INGAME"]);
return; return;
} }

View File

@ -3,6 +3,8 @@ using SharedLibraryCore.Events;
using System; using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Serilog.Context;
namespace SharedLibraryCore namespace SharedLibraryCore
{ {
@ -254,9 +256,6 @@ namespace SharedLibraryCore
public void Complete() public void Complete()
{ {
_eventFinishedWaiter.Set(); _eventFinishedWaiter.Set();
#if DEBUG
Owner?.Logger.WriteDebug($"Completed internal for event {Id}");
#endif
} }
/// <summary> /// <summary>
@ -266,11 +265,7 @@ namespace SharedLibraryCore
public async Task<GameEvent> WaitAsync(TimeSpan timeSpan, CancellationToken token) public async Task<GameEvent> WaitAsync(TimeSpan timeSpan, CancellationToken token)
{ {
bool processed = false; bool processed = false;
Utilities.DefaultLogger.LogDebug("Begin wait for event {Id}", Id);
#if DEBUG
Owner?.Logger.WriteDebug($"Begin wait for event {Id}");
#endif
try try
{ {
processed = await Task.Run(() => _eventFinishedWaiter.WaitOne(timeSpan), token); processed = await Task.Run(() => _eventFinishedWaiter.WaitOne(timeSpan), token);
@ -283,14 +278,12 @@ namespace SharedLibraryCore
if (!processed) if (!processed)
{ {
Owner?.Logger.WriteError("Waiting for event to complete timed out"); using(LogContext.PushProperty("Server", Owner?.ToString()))
Owner?.Logger.WriteDebug($"{Id}, {Type}, {Data}, {Extra}, {FailReason.ToString()}, {Message}, {Origin}, {Target}"); {
#if DEBUG Utilities.DefaultLogger.LogError("Waiting for event to complete timed out {@eventData}", new { Event = this, Message, Origin = Origin.ToString(), Target = Target.ToString()});
//throw new Exception(); }
#endif
} }
// this lets us know if the the action timed out // this lets us know if the the action timed out
FailReason = FailReason == EventFailReason.None && !processed ? EventFailReason.Timeout : FailReason; FailReason = FailReason == EventFailReason.None && !processed ? EventFailReason.Timeout : FailReason;
return this; return this;

View File

@ -0,0 +1,11 @@
using System;
namespace SharedLibraryCore.Exceptions
{
public class RConException : Exception
{
public RConException(string message) : base(message)
{
}
}
}

View File

@ -1,11 +1,8 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SharedLibraryCore.Interfaces namespace SharedLibraryCore.Interfaces
{ {
[Obsolete]
public interface ILogger public interface ILogger
{ {
void WriteVerbose(string msg); void WriteVerbose(string msg);

View File

@ -6,6 +6,7 @@ using SharedLibraryCore.Database.Models;
using System.Threading; using System.Threading;
using System.Collections; using System.Collections;
using System; using System;
using Microsoft.Extensions.Logging;
namespace SharedLibraryCore.Interfaces namespace SharedLibraryCore.Interfaces
{ {
@ -15,6 +16,7 @@ namespace SharedLibraryCore.Interfaces
Task Start(); Task Start();
void Stop(); void Stop();
void Restart(); void Restart();
[Obsolete]
ILogger GetLogger(long serverId); ILogger GetLogger(long serverId);
IList<Server> GetServers(); IList<Server> GetServers();
IList<IManagerCommand> GetCommands(); IList<IManagerCommand> GetCommands();

View File

@ -8,6 +8,8 @@ using System.Text.Json.Serialization;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Serilog.Context;
namespace SharedLibraryCore.Database.Models namespace SharedLibraryCore.Database.Models
{ {
@ -89,6 +91,7 @@ namespace SharedLibraryCore.Database.Models
SetAdditionalProperty("_reportCount", 0); SetAdditionalProperty("_reportCount", 0);
ReceivedPenalties = new List<EFPenalty>(); ReceivedPenalties = new List<EFPenalty>();
_processingEvent = new SemaphoreSlim(1, 1); _processingEvent = new SemaphoreSlim(1, 1);
} }
~EFClient() ~EFClient()
@ -475,39 +478,42 @@ namespace SharedLibraryCore.Database.Models
{ {
var loc = Utilities.CurrentLocalization.LocalizationIndex; var loc = Utilities.CurrentLocalization.LocalizationIndex;
if (string.IsNullOrWhiteSpace(Name) || CleanedName.Replace(" ", "").Length < 3) using (LogContext.PushProperty("Server", CurrentServer?.ToString()))
{ {
CurrentServer.Logger.WriteDebug($"Kicking {this} because their name is too short"); if (string.IsNullOrWhiteSpace(Name) || CleanedName.Replace(" ", "").Length < 3)
Kick(loc["SERVER_KICK_MINNAME"], Utilities.IW4MAdminClient(CurrentServer)); {
return false; 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() if (CurrentServer.Manager.GetApplicationSettings().Configuration()
.DisallowedClientNames .DisallowedClientNames
?.Any(_name => Regex.IsMatch(Name, _name)) ?? false) ?.Any(_name => Regex.IsMatch(Name, _name)) ?? false)
{ {
CurrentServer.Logger.WriteDebug($"Kicking {this} because their name is not allowed"); Utilities.DefaultLogger.LogInformation("Kicking {client} because their name is not allowed", ToString());
Kick(loc["SERVER_KICK_GENERICNAME"], Utilities.IW4MAdminClient(CurrentServer)); Kick(loc["SERVER_KICK_GENERICNAME"], Utilities.IW4MAdminClient(CurrentServer));
return false; return false;
} }
if (Name.Where(c => char.IsControl(c)).Count() > 0) if (Name.Where(c => char.IsControl(c)).Count() > 0)
{ {
CurrentServer.Logger.WriteDebug($"Kicking {this} because their name contains control characters"); Utilities.DefaultLogger.LogInformation("Kicking {client} because their name contains control characters", ToString());
Kick(loc["SERVER_KICK_CONTROLCHARS"], Utilities.IW4MAdminClient(CurrentServer)); Kick(loc["SERVER_KICK_CONTROLCHARS"], Utilities.IW4MAdminClient(CurrentServer));
return false; return false;
} }
// reserved slots stuff // reserved slots stuff
// todo: bots don't seem to honor party_maxplayers/sv_maxclients // 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 && if (CurrentServer.MaxClients - (CurrentServer.GetClientsAsList().Count(_client => !_client.IsPrivileged() && !_client.IsBot)) < CurrentServer.ServerConfig.ReservedSlotNumber &&
!this.IsPrivileged() && !this.IsPrivileged() &&
CurrentServer.GetClientsAsList().Count <= CurrentServer.MaxClients && CurrentServer.GetClientsAsList().Count <= CurrentServer.MaxClients &&
CurrentServer.MaxClients != 0) CurrentServer.MaxClients != 0)
{ {
CurrentServer.Logger.WriteDebug($"Kicking {this} their spot is reserved"); Utilities.DefaultLogger.LogInformation("Kicking {client} their spot is reserved", ToString());
Kick(loc["SERVER_KICK_SLOT_IS_RESERVED"], Utilities.IW4MAdminClient(CurrentServer)); Kick(loc["SERVER_KICK_SLOT_IS_RESERVED"], Utilities.IW4MAdminClient(CurrentServer));
return false; return false;
}
} }
return true; return true;
@ -515,126 +521,151 @@ namespace SharedLibraryCore.Database.Models
public async Task OnDisconnect() public async Task OnDisconnect()
{ {
TotalConnectionTime += ConnectionLength; using (LogContext.PushProperty("Server", CurrentServer?.ToString()))
LastConnection = DateTime.UtcNow;
try
{ {
await CurrentServer.Manager.GetClientService().Update(this); TotalConnectionTime += ConnectionLength;
} LastConnection = DateTime.UtcNow;
catch (Exception e) Utilities.DefaultLogger.LogInformation("Client {client} is leaving the game", ToString());
{
CurrentServer.Logger.WriteWarning($"Could not update disconnected player {this}");
CurrentServer.Logger.WriteDebug(e.GetExceptionInfo());
}
finally try
{ {
State = ClientState.Unknown; await CurrentServer.Manager.GetClientService().Update(this);
}
catch (Exception e)
{
Utilities.DefaultLogger.LogError(e, "Could not update disconnected client {client}",
ToString());
}
finally
{
State = ClientState.Unknown;
}
} }
} }
public async Task OnJoin(int? ipAddress) public async Task OnJoin(int? ipAddress)
{ {
CurrentServer.Logger.WriteDebug($"Start join for {this}::{ipAddress}::{Level.ToString()}"); using (LogContext.PushProperty("Server", CurrentServer?.ToString()))
if (ipAddress != null)
{ {
IPAddress = ipAddress; Utilities.DefaultLogger.LogInformation("Client {client} is joining the game from {source}", ToString(), ipAddress.HasValue ? "Status" : "Log");
await CurrentServer.Manager.GetClientService().UpdateAlias(this);
CurrentServer.Logger.WriteDebug($"Updated alias for {this}");
await CurrentServer.Manager.GetClientService().Update(this);
CurrentServer.Logger.WriteDebug($"Updated client for {this}");
bool canConnect = await CanConnect(ipAddress); if (ipAddress != null)
if (!canConnect)
{ {
CurrentServer.Logger.WriteDebug($"Client {this} is not allowed to join the server"); IPAddress = ipAddress;
Utilities.DefaultLogger.LogInformation("Received ip from client {client}", ToString());
await CurrentServer.Manager.GetClientService().UpdateAlias(this);
await CurrentServer.Manager.GetClientService().Update(this);
bool canConnect = await CanConnect(ipAddress);
if (!canConnect)
{
Utilities.DefaultLogger.LogInformation("Client {client} is not allowed to join the server",
ToString());
}
else
{
Utilities.DefaultLogger.LogDebug("Creating join event for {client}", ToString());
var e = new GameEvent()
{
Type = GameEvent.EventType.Join,
Origin = this,
Target = this,
Owner = CurrentServer,
};
CurrentServer.Manager.AddEvent(e);
}
} }
else else
{ {
CurrentServer.Logger.WriteDebug($"Creating join event for {this}"); Utilities.DefaultLogger.LogInformation("Waiting to receive ip from client {client}", ToString());
var e = new GameEvent()
{
Type = GameEvent.EventType.Join,
Origin = this,
Target = this,
Owner = CurrentServer,
};
CurrentServer.Manager.AddEvent(e);
} }
}
else Utilities.DefaultLogger.LogDebug("OnJoin finished for {client}", ToString());
{
CurrentServer.Logger.WriteDebug($"Client {this} does not have an IP yet");
} }
CurrentServer.Logger.WriteDebug($"OnJoin finished for {this}");
} }
public async Task<bool> CanConnect(int? ipAddress) public async Task<bool> CanConnect(int? ipAddress)
{ {
var loc = Utilities.CurrentLocalization.LocalizationIndex; using (LogContext.PushProperty("Server", CurrentServer?.ToString()))
var autoKickClient = Utilities.IW4MAdminClient(CurrentServer);
bool isAbleToConnectSimple = IsAbleToConnectSimple();
if (!isAbleToConnectSimple)
{ {
return false; var loc = Utilities.CurrentLocalization.LocalizationIndex;
} var autoKickClient = Utilities.IW4MAdminClient(CurrentServer);
// we want to get any penalties that are tied to their IP or AliasLink (but not necessarily their GUID) bool isAbleToConnectSimple = IsAbleToConnectSimple();
var activePenalties = await CurrentServer.Manager.GetPenaltyService().GetActivePenaltiesAsync(AliasLinkId, ipAddress);
var banPenalty = activePenalties.FirstOrDefault(_penalty => _penalty.Type == EFPenalty.PenaltyType.Ban);
var tempbanPenalty = activePenalties.FirstOrDefault(_penalty => _penalty.Type == EFPenalty.PenaltyType.TempBan);
var flagPenalty = activePenalties.FirstOrDefault(_penalty => _penalty.Type == EFPenalty.PenaltyType.Flag);
// we want to kick them if any account is banned if (!isAbleToConnectSimple)
if (banPenalty != null)
{
if (Level == Permission.Banned)
{ {
CurrentServer.Logger.WriteDebug($"Kicking {this} because they are banned");
Kick(loc["SERVER_BAN_PREV"].FormatExt(banPenalty?.Offense), autoKickClient);
return false; return false;
} }
else // we want to get any penalties that are tied to their IP or AliasLink (but not necessarily their GUID)
var activePenalties = await CurrentServer.Manager.GetPenaltyService()
.GetActivePenaltiesAsync(AliasLinkId, ipAddress);
var banPenalty = activePenalties.FirstOrDefault(_penalty => _penalty.Type == EFPenalty.PenaltyType.Ban);
var tempbanPenalty =
activePenalties.FirstOrDefault(_penalty => _penalty.Type == EFPenalty.PenaltyType.TempBan);
var flagPenalty =
activePenalties.FirstOrDefault(_penalty => _penalty.Type == EFPenalty.PenaltyType.Flag);
// we want to kick them if any account is banned
if (banPenalty != null)
{ {
CurrentServer.Logger.WriteDebug($"Client {this} is banned, but using a new GUID, we we're updating their level and kicking them"); if (Level == Permission.Banned)
await SetLevel(Permission.Banned, autoKickClient).WaitAsync(Utilities.DefaultCommandTimeout, CurrentServer.Manager.CancellationToken); {
Kick(loc["SERVER_BAN_PREV"].FormatExt(banPenalty?.Offense), autoKickClient); Utilities.DefaultLogger.LogInformation("Kicking {client} because they are banned", ToString());
Kick(loc["SERVER_BAN_PREV"].FormatExt(banPenalty?.Offense), autoKickClient);
return false;
}
else
{
Utilities.DefaultLogger.LogInformation(
"Client {client} is banned, but using a new GUID, we we're updating their level and kicking them",
ToString());
await SetLevel(Permission.Banned, autoKickClient).WaitAsync(Utilities.DefaultCommandTimeout,
CurrentServer.Manager.CancellationToken);
Kick(loc["SERVER_BAN_PREV"].FormatExt(banPenalty?.Offense), autoKickClient);
return false;
}
}
// we want to kick them if any account is tempbanned
if (tempbanPenalty != null)
{
Utilities.DefaultLogger.LogInformation("Kicking {client} because their GUID is temporarily banned",
ToString());
Kick(
$"{loc["SERVER_TB_REMAIN"]} ({(tempbanPenalty.Expires.Value - DateTime.UtcNow).HumanizeForCurrentCulture()} {loc["WEBFRONT_PENALTY_TEMPLATE_REMAINING"]})",
autoKickClient);
return false; return false;
} }
}
// we want to kick them if any account is tempbanned // if we found a flag, we need to make sure all the accounts are flagged
if (tempbanPenalty != null) if (flagPenalty != null && Level != Permission.Flagged)
{ {
CurrentServer.Logger.WriteDebug($"Kicking {this} because their GUID is temporarily banned"); Utilities.DefaultLogger.LogInformation(
Kick($"{loc["SERVER_TB_REMAIN"]} ({(tempbanPenalty.Expires.Value - DateTime.UtcNow).HumanizeForCurrentCulture()} {loc["WEBFRONT_PENALTY_TEMPLATE_REMAINING"]})", autoKickClient); "Flagged client {client} joining with new GUID, so we are changing their level to flagged",
return false; ToString());
} await SetLevel(Permission.Flagged, autoKickClient).WaitAsync(Utilities.DefaultCommandTimeout,
CurrentServer.Manager.CancellationToken);
}
// if we found a flag, we need to make sure all the accounts are flagged // remove their auto flag
if (flagPenalty != null && Level != Permission.Flagged) if (Level == Permission.Flagged &&
{ !activePenalties.Any(_penalty => _penalty.Type == EFPenalty.PenaltyType.Flag))
CurrentServer.Logger.WriteDebug($"Flagged client {this} joining with new GUID, so we are changing their level to flagged"); {
await SetLevel(Permission.Flagged, autoKickClient).WaitAsync(Utilities.DefaultCommandTimeout, CurrentServer.Manager.CancellationToken); // remove their auto flag status after a week
} Utilities.DefaultLogger.LogInformation("Unflagging {client} because the auto flag time has expired",
ToString());
// remove their auto flag Unflag(Utilities.CurrentLocalization.LocalizationIndex["SERVER_AUTOFLAG_UNFLAG"], autoKickClient);
if (Level == Permission.Flagged && !activePenalties.Any(_penalty => _penalty.Type == EFPenalty.PenaltyType.Flag)) }
{
// remove their auto flag status after a week
CurrentServer.Logger.WriteInfo($"Unflagging {this} because the auto flag time has expired");
Unflag(Utilities.CurrentLocalization.LocalizationIndex["SERVER_AUTOFLAG_UNFLAG"], autoKickClient);
} }
return true; return true;

View File

@ -56,7 +56,8 @@ namespace SharedLibraryCore.RCon
1 => TimeSpan.FromMilliseconds(550), 1 => TimeSpan.FromMilliseconds(550),
2 => TimeSpan.FromMilliseconds(1000), 2 => TimeSpan.FromMilliseconds(1000),
3 => TimeSpan.FromMilliseconds(2000), 3 => TimeSpan.FromMilliseconds(2000),
_ => TimeSpan.FromMilliseconds(5000), 4 => TimeSpan.FromMilliseconds(5000),
_ => TimeSpan.FromMilliseconds(10000),
}; };
} }
/// <summary> /// <summary>
@ -66,6 +67,6 @@ namespace SharedLibraryCore.RCon
/// <summary> /// <summary>
/// how many failed connection attempts before aborting connection /// how many failed connection attempts before aborting connection
/// </summary> /// </summary>
public static readonly int AllowedConnectionFails = 4; public static readonly int AllowedConnectionFails = 5;
} }
} }

View File

@ -3,12 +3,14 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Serilog.Context;
using SharedLibraryCore.Helpers; using SharedLibraryCore.Helpers;
using SharedLibraryCore.Dtos; using SharedLibraryCore.Dtos;
using SharedLibraryCore.Configuration; using SharedLibraryCore.Configuration;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using SharedLibraryCore.Database.Models; using SharedLibraryCore.Database.Models;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace SharedLibraryCore namespace SharedLibraryCore
{ {
@ -28,14 +30,15 @@ namespace SharedLibraryCore
T7 = 8 T7 = 8
} }
public Server(ServerConfiguration config, IManager mgr, IRConConnectionFactory rconConnectionFactory, IGameLogReaderFactory gameLogReaderFactory) public Server(ILogger<Server> logger, SharedLibraryCore.Interfaces.ILogger deprecatedLogger,
ServerConfiguration config, IManager mgr, IRConConnectionFactory rconConnectionFactory,
IGameLogReaderFactory gameLogReaderFactory)
{ {
Password = config.Password; Password = config.Password;
IP = config.IPAddress; IP = config.IPAddress;
Port = config.Port; Port = config.Port;
Manager = mgr; Manager = mgr;
Logger = Manager.GetLogger(this.EndPoint); Logger = deprecatedLogger;
Logger.WriteInfo(this.ToString());
ServerConfig = config; ServerConfig = config;
RemoteConnection = rconConnectionFactory.CreateConnection(IP, Port, Password); RemoteConnection = rconConnectionFactory.CreateConnection(IP, Port, Password);
EventProcessing = new SemaphoreSlim(1, 1); EventProcessing = new SemaphoreSlim(1, 1);
@ -47,6 +50,7 @@ namespace SharedLibraryCore
CustomSayEnabled = Manager.GetApplicationSettings().Configuration().EnableCustomSayName; CustomSayEnabled = Manager.GetApplicationSettings().Configuration().EnableCustomSayName;
CustomSayName = Manager.GetApplicationSettings().Configuration().CustomSayName; CustomSayName = Manager.GetApplicationSettings().Configuration().CustomSayName;
this.gameLogReaderFactory = gameLogReaderFactory; this.gameLogReaderFactory = gameLogReaderFactory;
ServerLogger = logger;
InitializeTokens(); InitializeTokens();
InitializeAutoMessages(); InitializeAutoMessages();
} }
@ -123,9 +127,7 @@ namespace SharedLibraryCore
public GameEvent Broadcast(string message, EFClient sender = null) public GameEvent Broadcast(string message, EFClient sender = null)
{ {
string formattedMessage = string.Format(RconParser.Configuration.CommandPrefixes.Say ?? "", $"{(CustomSayEnabled && GameName == Game.IW4 ? $"{CustomSayName}: " : "")}{message.FixIW4ForwardSlash()}"); string formattedMessage = string.Format(RconParser.Configuration.CommandPrefixes.Say ?? "", $"{(CustomSayEnabled && GameName == Game.IW4 ? $"{CustomSayName}: " : "")}{message.FixIW4ForwardSlash()}");
#if DEBUG == true ServerLogger.LogDebug("All->" + message.StripColors());
Logger.WriteVerbose(message.StripColors());
#endif
var e = new GameEvent() var e = new GameEvent()
{ {
@ -146,18 +148,27 @@ namespace SharedLibraryCore
/// <param name="target">EFClient to send message to</param> /// <param name="target">EFClient to send message to</param>
protected async Task Tell(string message, EFClient target) protected async Task Tell(string message, EFClient target)
{ {
#if !DEBUG if (!Utilities.IsDevelopment)
string formattedMessage = string.Format(RconParser.Configuration.CommandPrefixes.Tell, target.ClientNumber, $"{(CustomSayEnabled && GameName == Game.IW4 ? $"{CustomSayName}: " : "")}{message.FixIW4ForwardSlash()}"); {
if (target.ClientNumber > -1 && message.Length > 0 && target.Level != EFClient.Permission.Console) var formattedMessage = string.Format(RconParser.Configuration.CommandPrefixes.Tell,
await this.ExecuteCommandAsync(formattedMessage); target.ClientNumber,
#else $"{(CustomSayEnabled && GameName == Game.IW4 ? $"{CustomSayName}: " : "")}{message.FixIW4ForwardSlash()}");
Logger.WriteVerbose($"{target.ClientNumber}->{message.StripColors()}"); if (target.ClientNumber > -1 && message.Length > 0 && target.Level != EFClient.Permission.Console)
await Task.CompletedTask; await this.ExecuteCommandAsync(formattedMessage);
#endif }
else
{
ServerLogger.LogDebug("Tell[{clientNumber}]->{message}", target.ClientNumber, message.StripColors());
}
if (target.Level == EFClient.Permission.Console) if (target.Level == EFClient.Permission.Console)
{ {
Console.ForegroundColor = ConsoleColor.Green; Console.ForegroundColor = ConsoleColor.Green;
using (LogContext.PushProperty("Server", ToString()))
{
ServerLogger.LogInformation("Command output received: {message}", message);
}
Console.WriteLine(message.StripColors()); Console.WriteLine(message.StripColors());
Console.ForegroundColor = ConsoleColor.Gray; Console.ForegroundColor = ConsoleColor.Gray;
} }
@ -280,7 +291,8 @@ namespace SharedLibraryCore
// Objects // Objects
public IManager Manager { get; protected set; } public IManager Manager { get; protected set; }
public ILogger Logger { get; private set; } [Obsolete]
public SharedLibraryCore.Interfaces.ILogger Logger { get; private set; }
public ServerConfiguration ServerConfig { get; private set; } public ServerConfiguration ServerConfig { get; private set; }
public List<Map> Maps { get; protected set; } = new List<Map>(); public List<Map> Maps { get; protected set; } = new List<Map>();
public List<Report> Reports { get; set; } public List<Report> Reports { get; set; }
@ -319,6 +331,7 @@ namespace SharedLibraryCore
public string IP { get; protected set; } public string IP { get; protected set; }
public string Version { get; protected set; } public string Version { get; protected set; }
public bool IsInitialized { get; set; } public bool IsInitialized { get; set; }
protected readonly ILogger ServerLogger;
public int Port { get; private set; } public int Port { get; private set; }
protected string FSGame; protected string FSGame;

View File

@ -5,11 +5,20 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace SharedLibraryCore.Services namespace SharedLibraryCore.Services
{ {
public class ChangeHistoryService : IEntityService<EFChangeHistory> public class ChangeHistoryService : IEntityService<EFChangeHistory>
{ {
private readonly ILogger _logger;
public ChangeHistoryService(ILogger<ChangeHistoryService> logger)
{
_logger = logger;
}
public Task<EFChangeHistory> Create(EFChangeHistory entity) public Task<EFChangeHistory> Create(EFChangeHistory entity)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
@ -79,8 +88,7 @@ namespace SharedLibraryCore.Services
catch (Exception ex) catch (Exception ex)
{ {
e.Owner.Logger.WriteWarning(ex.Message); _logger.LogError(ex, "Could not persist change @{change}", change);
e.Owner.Logger.WriteDebug(ex.GetExceptionInfo());
} }
finally finally

View File

@ -8,22 +8,28 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Serilog.Context;
using static SharedLibraryCore.Database.Models.EFClient; using static SharedLibraryCore.Database.Models.EFClient;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace SharedLibraryCore.Services namespace SharedLibraryCore.Services
{ {
public class ClientService : IEntityService<EFClient>, IResourceQueryHelper<FindClientRequest, FindClientResult> public class ClientService : IEntityService<EFClient>, IResourceQueryHelper<FindClientRequest, FindClientResult>
{ {
private readonly IDatabaseContextFactory _contextFactory; private readonly IDatabaseContextFactory _contextFactory;
private readonly ILogger _logger;
public ClientService(IDatabaseContextFactory databaseContextFactory) public ClientService(ILogger<ClientService> logger, IDatabaseContextFactory databaseContextFactory)
{ {
_contextFactory = databaseContextFactory; _contextFactory = databaseContextFactory;
_logger = logger;
} }
public async Task<EFClient> Create(EFClient entity) public async Task<EFClient> Create(EFClient entity)
{ {
using (var context = new DatabaseContext()) using (var context = new DatabaseContext())
using (LogContext.PushProperty("Server", entity?.CurrentServer?.ToString()))
{ {
int? linkId = null; int? linkId = null;
int? aliasId = null; int? aliasId = null;
@ -40,13 +46,13 @@ namespace SharedLibraryCore.Services
{ {
linkId = existingAliases.OrderBy(_alias => _alias.LinkId).First().LinkId; linkId = existingAliases.OrderBy(_alias => _alias.LinkId).First().LinkId;
entity.CurrentServer.Logger.WriteDebug($"[create] client with new GUID {entity} has existing link {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); var existingExactAlias = existingAliases.FirstOrDefault(_alias => _alias.Name == entity.Name);
if (existingExactAlias != null) if (existingExactAlias != null)
{ {
entity.CurrentServer.Logger.WriteDebug($"[create] client with new GUID {entity} has existing alias {existingExactAlias.AliasId}"); _logger.LogDebug("[create] client with new GUID {entity} has existing alias {aliasId}", entity.ToString(), existingExactAlias.AliasId);
aliasId = existingExactAlias.AliasId; aliasId = existingExactAlias.AliasId;
} }
} }
@ -60,13 +66,13 @@ namespace SharedLibraryCore.Services
NetworkId = entity.NetworkId NetworkId = entity.NetworkId
}; };
entity.CurrentServer.Logger.WriteDebug($"[create] adding {entity} to context"); _logger.LogDebug("[create] adding {entity} to context", entity.ToString());
context.Clients.Add(client); context.Clients.Add(client);
// they're just using a new GUID // they're just using a new GUID
if (aliasId.HasValue) if (aliasId.HasValue)
{ {
entity.CurrentServer.Logger.WriteDebug($"[create] setting {entity}'s alias id and linkid to ({aliasId.Value}, {linkId.Value})"); _logger.LogDebug("[create] setting {entity}'s alias id and linkid to ({aliasId}, {linkId})", entity.ToString(), aliasId, linkId);
client.CurrentAliasId = aliasId.Value; client.CurrentAliasId = aliasId.Value;
client.AliasLinkId = linkId.Value; client.AliasLinkId = linkId.Value;
} }
@ -74,7 +80,7 @@ namespace SharedLibraryCore.Services
// link was found but they don't have an exact alias // link was found but they don't have an exact alias
else if (!aliasId.HasValue && linkId.HasValue) else if (!aliasId.HasValue && linkId.HasValue)
{ {
entity.CurrentServer.Logger.WriteDebug($"[create] setting {entity}'s linkid to {linkId.Value}, but creating new alias"); _logger.LogDebug("[create] setting {entity}'s linkid to {linkId}, but creating new alias", entity.ToString(), linkId);
client.AliasLinkId = linkId.Value; client.AliasLinkId = linkId.Value;
client.CurrentAlias = new EFAlias() client.CurrentAlias = new EFAlias()
{ {
@ -89,7 +95,7 @@ namespace SharedLibraryCore.Services
// brand new players (supposedly) // brand new players (supposedly)
else else
{ {
entity.CurrentServer.Logger.WriteDebug($"[create] creating new Link and Alias for {entity}"); _logger.LogDebug("[create] creating new Link and Alias for {entity}", entity.ToString());
var link = new EFAliasLink(); var link = new EFAliasLink();
var alias = new EFAlias() var alias = new EFAlias()
{ {
@ -114,129 +120,139 @@ namespace SharedLibraryCore.Services
private async Task UpdateAlias(string originalName, int? ip, EFClient entity, DatabaseContext context) private async Task UpdateAlias(string originalName, int? ip, EFClient entity, DatabaseContext context)
{ {
string name = originalName.CapClientName(EFAlias.MAX_NAME_LENGTH); using (LogContext.PushProperty("Server", entity?.CurrentServer?.ToString()))
// entity is the tracked db context item
// get all aliases by IP address and LinkId
var iqAliases = context.Aliases
.Include(a => a.Link)
// we only want alias that have the same IP address or share a link
.Where(_alias => _alias.IPAddress == ip || (_alias.LinkId == entity.AliasLinkId));
var aliases = await iqAliases.ToListAsync();
var currentIPs = aliases.Where(_a2 => _a2.IPAddress != null).Select(_a2 => _a2.IPAddress).Distinct();
var floatingIPAliases = await context.Aliases.Where(_alias => currentIPs.Contains(_alias.IPAddress)).ToListAsync();
aliases.AddRange(floatingIPAliases);
// see if they have a matching IP + Name but new NetworkId
var existingExactAlias = aliases.OrderBy(_alias => _alias.LinkId).FirstOrDefault(a => a.Name == name && a.IPAddress == ip);
bool hasExactAliasMatch = existingExactAlias != null;
// if existing alias matches link them
var newAliasLink = existingExactAlias?.Link;
// if no exact matches find the first IP or LinkId that matches
newAliasLink = newAliasLink ?? aliases.OrderBy(_alias => _alias.LinkId).FirstOrDefault()?.Link;
// if no matches are found, use our current one ( it will become permanent )
newAliasLink = newAliasLink ?? entity.AliasLink;
bool hasExistingAlias = aliases.Count > 0;
bool isAliasLinkUpdated = newAliasLink.AliasLinkId != entity.AliasLink.AliasLinkId;
await context.SaveChangesAsync();
int distinctLinkCount = aliases.Select(_alias => _alias.LinkId).Distinct().Count();
// this happens when the link we found is different than the one we create before adding an IP
if (isAliasLinkUpdated || distinctLinkCount > 1)
{ {
entity.CurrentServer.Logger.WriteDebug($"[updatealias] found a link for {entity} so we are updating link from {entity.AliasLink.AliasLinkId} to {newAliasLink.AliasLinkId}"); string name = originalName.CapClientName(EFAlias.MAX_NAME_LENGTH);
var completeAliasLinkIds = aliases.Select(_item => _item.LinkId) // entity is the tracked db context item
.Append(entity.AliasLinkId) // get all aliases by IP address and LinkId
.Distinct() var iqAliases = context.Aliases
.ToList(); .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));
entity.CurrentServer.Logger.WriteDebug($"[updatealias] updating aliasLinks {string.Join(',', completeAliasLinkIds)} for IP {ip} to {newAliasLink.AliasLinkId}"); var aliases = await iqAliases.ToListAsync();
var currentIPs = aliases.Where(_a2 => _a2.IPAddress != null).Select(_a2 => _a2.IPAddress).Distinct();
var floatingIPAliases = await context.Aliases.Where(_alias => currentIPs.Contains(_alias.IPAddress))
.ToListAsync();
aliases.AddRange(floatingIPAliases);
// update all the clients that have the old alias link // see if they have a matching IP + Name but new NetworkId
await context.Clients var existingExactAlias = aliases.OrderBy(_alias => _alias.LinkId)
.Where(_client => completeAliasLinkIds.Contains(_client.AliasLinkId)) .FirstOrDefault(a => a.Name == name && a.IPAddress == ip);
.ForEachAsync(_client => _client.AliasLinkId = newAliasLink.AliasLinkId); bool hasExactAliasMatch = existingExactAlias != null;
// we also need to update all the penalties or they get deleted // if existing alias matches link them
// scenario var newAliasLink = existingExactAlias?.Link;
// link1 joins with ip1 // if no exact matches find the first IP or LinkId that matches
// link2 joins with ip2, newAliasLink = newAliasLink ?? aliases.OrderBy(_alias => _alias.LinkId).FirstOrDefault()?.Link;
// link2 receives penalty // if no matches are found, use our current one ( it will become permanent )
// link2 joins with ip1 newAliasLink = newAliasLink ?? entity.AliasLink;
// pre existing link for link2 detected
// link2 is deleted
// link2 penalties are orphaned
await context.Penalties
.Where(_penalty => completeAliasLinkIds.Contains(_penalty.LinkId))
.ForEachAsync(_penalty => _penalty.LinkId = newAliasLink.AliasLinkId);
entity.AliasLink = newAliasLink; bool hasExistingAlias = aliases.Count > 0;
entity.AliasLinkId = newAliasLink.AliasLinkId; bool isAliasLinkUpdated = newAliasLink.AliasLinkId != entity.AliasLink.AliasLinkId;
// update all previous aliases
await context.Aliases
.Where(_alias => completeAliasLinkIds.Contains(_alias.LinkId))
.ForEachAsync(_alias => _alias.LinkId = newAliasLink.AliasLinkId);
await context.SaveChangesAsync(); await context.SaveChangesAsync();
// we want to delete the now inactive alias int distinctLinkCount = aliases.Select(_alias => _alias.LinkId).Distinct().Count();
if (newAliasLink.AliasLinkId != entity.AliasLinkId) // this happens when the link we found is different than the one we create before adding an IP
if (isAliasLinkUpdated || distinctLinkCount > 1)
{ {
context.AliasLinks.Remove(entity.AliasLink); _logger.LogDebug(
await context.SaveChangesAsync(); "[updatealias] found a link for {entity} so we are updating link from {oldAliasLinkId} to {newAliasLinkId}",
} entity.ToString(), entity.AliasLink.AliasLinkId, newAliasLink.AliasLinkId);
}
// the existing alias matches ip and name, so we can just ignore the temporary one var completeAliasLinkIds = aliases.Select(_item => _item.LinkId)
if (hasExactAliasMatch) .Append(entity.AliasLinkId)
{ .Distinct()
entity.CurrentServer.Logger.WriteDebug($"[updatealias] {entity} has exact alias match"); .ToList();
var oldAlias = entity.CurrentAlias; _logger.LogDebug("[updatealias] updating aliasLinks {links} for IP {ip} to {linkId}",
entity.CurrentAliasId = existingExactAlias.AliasId; string.Join(',', completeAliasLinkIds), ip, newAliasLink.AliasLinkId);
entity.CurrentAlias = existingExactAlias;
await context.SaveChangesAsync();
// the alias is the same so we can just remove it // update all the clients that have the old alias link
if (oldAlias.AliasId != existingExactAlias.AliasId && oldAlias.AliasId > 0)
{
await context.Clients await context.Clients
.Where(_client => _client.CurrentAliasId == oldAlias.AliasId) .Where(_client => completeAliasLinkIds.Contains(_client.AliasLinkId))
.ForEachAsync(_client => _client.CurrentAliasId = existingExactAlias.AliasId); .ForEachAsync(_client => _client.AliasLinkId = newAliasLink.AliasLinkId);
// we also need to update all the penalties or they get deleted
// scenario
// link1 joins with ip1
// link2 joins with ip2,
// link2 receives penalty
// link2 joins with ip1
// pre existing link for link2 detected
// link2 is deleted
// link2 penalties are orphaned
await context.Penalties
.Where(_penalty => completeAliasLinkIds.Contains(_penalty.LinkId))
.ForEachAsync(_penalty => _penalty.LinkId = newAliasLink.AliasLinkId);
entity.AliasLink = newAliasLink;
entity.AliasLinkId = newAliasLink.AliasLinkId;
// update all previous aliases
await context.Aliases
.Where(_alias => completeAliasLinkIds.Contains(_alias.LinkId))
.ForEachAsync(_alias => _alias.LinkId = newAliasLink.AliasLinkId);
await context.SaveChangesAsync(); await context.SaveChangesAsync();
// we want to delete the now inactive alias
if (context.Entry(oldAlias).State != EntityState.Deleted) if (newAliasLink.AliasLinkId != entity.AliasLinkId)
{ {
entity.CurrentServer.Logger.WriteDebug($"[updatealias] {entity} has exact alias match, so we're going to try to remove aliasId {oldAlias.AliasId} with linkId {oldAlias.AliasId}"); context.AliasLinks.Remove(entity.AliasLink);
context.Aliases.Remove(oldAlias);
await context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
} }
}
// theres no exact match, but they've played before with the GUID or IP // the existing alias matches ip and name, so we can just ignore the temporary one
else if (hasExactAliasMatch)
{
entity.CurrentServer.Logger.WriteDebug($"[updatealias] {entity} is using a new alias");
var newAlias = new EFAlias()
{ {
DateAdded = DateTime.UtcNow, _logger.LogDebug("[updatealias] {entity} has exact alias match", entity.ToString());
IPAddress = ip,
LinkId = newAliasLink.AliasLinkId,
Name = name,
SearchableName = name.StripColors().ToLower(),
Active = true,
};
entity.CurrentAlias = newAlias; var oldAlias = entity.CurrentAlias;
entity.CurrentAliasId = 0; entity.CurrentAliasId = existingExactAlias.AliasId;
await context.SaveChangesAsync(); entity.CurrentAlias = existingExactAlias;
await context.SaveChangesAsync();
// the alias is the same so we can just remove it
if (oldAlias.AliasId != existingExactAlias.AliasId && oldAlias.AliasId > 0)
{
await context.Clients
.Where(_client => _client.CurrentAliasId == oldAlias.AliasId)
.ForEachAsync(_client => _client.CurrentAliasId = existingExactAlias.AliasId);
await context.SaveChangesAsync();
if (context.Entry(oldAlias).State != EntityState.Deleted)
{
_logger.LogDebug(
"[updatealias] {entity} has exact alias match, so we're going to try to remove aliasId {aliasId} with linkId {linkId}",
entity.ToString(), oldAlias.AliasId, oldAlias.LinkId);
context.Aliases.Remove(oldAlias);
await context.SaveChangesAsync();
}
}
}
// theres no exact match, but they've played before with the GUID or IP
else
{
_logger.LogDebug("[updatealias] {entity} is using a new alias", entity.ToString());
var newAlias = new EFAlias()
{
DateAdded = DateTime.UtcNow,
IPAddress = ip,
LinkId = newAliasLink.AliasLinkId,
Name = name,
SearchableName = name.StripColors().ToLower(),
Active = true,
};
entity.CurrentAlias = newAlias;
entity.CurrentAliasId = 0;
await context.SaveChangesAsync();
}
} }
} }
@ -261,29 +277,29 @@ namespace SharedLibraryCore.Services
entity.Level = newPermission; entity.Level = newPermission;
await ctx.SaveChangesAsync(); await ctx.SaveChangesAsync();
#if DEBUG == true using (LogContext.PushProperty("Server", entity?.CurrentServer?.ToString()))
temporalClient.CurrentServer.Logger.WriteDebug($"Updated {temporalClient.ClientId} to {newPermission}");
#endif
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))
{ {
//get all clients that have the same linkId _logger.LogInformation("Updated {clientId} to {newPermission}", temporalClient.ClientId, newPermission);
var iqMatchingClients = ctx.Clients
.Where(_client => _client.AliasLinkId == entity.AliasLinkId);
// this updates the level for all the clients with the same LinkId var linkedPermissionSet = new[] {Permission.Banned, Permission.Flagged};
// only if their new level is flagged or banned // if their permission level has been changed to level that needs to be updated on all accounts
await iqMatchingClients.ForEachAsync(_client => if (linkedPermissionSet.Contains(newPermission) || linkedPermissionSet.Contains(oldPermission))
{ {
_client.Level = newPermission; //get all clients that have the same linkId
#if DEBUG == true var iqMatchingClients = ctx.Clients
temporalClient.CurrentServer.Logger.WriteDebug($"Updated linked {_client.ClientId} to {newPermission}"); .Where(_client => _client.AliasLinkId == entity.AliasLinkId);
#endif
});
await ctx.SaveChangesAsync(); // this updates the level for all the clients with the same LinkId
// only if their new level is flagged or banned
await iqMatchingClients.ForEachAsync(_client =>
{
_client.Level = newPermission;
_logger.LogInformation("Updated linked {clientId} to {newPermission}", _client.ClientId,
newPermission);
});
await ctx.SaveChangesAsync();
}
} }
} }
@ -432,7 +448,7 @@ namespace SharedLibraryCore.Services
{ {
if (temporalClient.ClientId < 1) if (temporalClient.ClientId < 1)
{ {
temporalClient.CurrentServer?.Logger.WriteDebug($"[update] {temporalClient} needs to be updated but they do not have a valid client id, ignoring.."); _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 // note: we never do anything with the result of this so we can safely return null
return null; return null;
} }

View File

@ -3,8 +3,6 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>netcoreapp3.1</TargetFramework>
<ApplicationIcon />
<StartupObject />
<PackageId>RaidMax.IW4MAdmin.SharedLibraryCore</PackageId> <PackageId>RaidMax.IW4MAdmin.SharedLibraryCore</PackageId>
<Version>2.4.10</Version> <Version>2.4.10</Version>
<Authors>RaidMax</Authors> <Authors>RaidMax</Authors>
@ -21,8 +19,9 @@
<IsPackable>true</IsPackable> <IsPackable>true</IsPackable>
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseExpression>MIT</PackageLicenseExpression>
<Description>Shared Library for IW4MAdmin</Description> <Description>Shared Library for IW4MAdmin</Description>
<AssemblyVersion>2.4.10.0</AssemblyVersion> <AssemblyVersion>2020.11.11.1</AssemblyVersion>
<FileVersion>2.4.10.0</FileVersion> <FileVersion>2020.11.11.1</FileVersion>
<PackageVersion>2020.11.11.1</PackageVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Prerelease|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Prerelease|AnyCPU'">
@ -54,6 +53,7 @@
<PackageReference Include="Npgsql" Version="4.1.4" /> <PackageReference Include="Npgsql" Version="4.1.4" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="3.1.4" /> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="3.1.4" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="3.1.2" /> <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="3.1.2" />
<PackageReference Include="Serilog.AspNetCore" Version="3.4.0" />
<PackageReference Include="SimpleCrypto.NetCore" Version="1.0.0" /> <PackageReference Include="SimpleCrypto.NetCore" Version="1.0.0" />
</ItemGroup> </ItemGroup>

View File

@ -17,14 +17,18 @@ using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using static SharedLibraryCore.Database.Models.EFClient; using static SharedLibraryCore.Database.Models.EFClient;
using static SharedLibraryCore.Database.Models.EFPenalty; using static SharedLibraryCore.Database.Models.EFPenalty;
using static SharedLibraryCore.Server; using static SharedLibraryCore.Server;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace SharedLibraryCore namespace SharedLibraryCore
{ {
public static class Utilities public static class Utilities
{ {
// note: this is only to be used by classes not created by dependency injection
public static ILogger DefaultLogger { get; set; }
#if DEBUG == true #if DEBUG == true
public static string OperatingDirectory => $"{Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)}{Path.DirectorySeparatorChar}"; public static string OperatingDirectory => $"{Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)}{Path.DirectorySeparatorChar}";
#else #else
@ -881,8 +885,7 @@ namespace SharedLibraryCore
catch (Exception e) catch (Exception e)
{ {
logger.WriteWarning($"Could not create penalty of type {penalty.Type.ToString()}"); logger.LogError(e, $"Could not create penalty of type {penalty.Type.ToString()}");
logger.WriteDebug(e.GetExceptionInfo());
} }
return false; return false;

View File

@ -9,6 +9,7 @@ using SharedLibraryCore;
using SharedLibraryCore.Configuration; using SharedLibraryCore.Configuration;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using System; using System;
using Microsoft.Extensions.Logging;
using static SharedLibraryCore.GameEvent; using static SharedLibraryCore.GameEvent;
namespace ApplicationTests namespace ApplicationTests
@ -18,7 +19,6 @@ namespace ApplicationTests
{ {
private EventLogTest eventLogData; private EventLogTest eventLogData;
private IServiceProvider serviceProvider; private IServiceProvider serviceProvider;
private ILogger fakeLogger;
private ApplicationConfiguration appConfig; private ApplicationConfiguration appConfig;
[SetUp] [SetUp]
@ -27,13 +27,12 @@ namespace ApplicationTests
eventLogData = JsonConvert.DeserializeObject<EventLogTest>(System.IO.File.ReadAllText("Files/GameEvents.json")); eventLogData = JsonConvert.DeserializeObject<EventLogTest>(System.IO.File.ReadAllText("Files/GameEvents.json"));
appConfig = ConfigurationGenerators.CreateApplicationConfiguration(); appConfig = ConfigurationGenerators.CreateApplicationConfiguration();
fakeLogger = A.Fake<ILogger>();
serviceProvider = new ServiceCollection() serviceProvider = new ServiceCollection()
.AddSingleton(A.Fake<ILogger>())
.AddSingleton<BaseEventParser>() .AddSingleton<BaseEventParser>()
.AddTransient<IParserPatternMatcher, ParserPatternMatcher>() .AddTransient<IParserPatternMatcher, ParserPatternMatcher>()
.AddSingleton<IParserRegexFactory, ParserRegexFactory>() .AddSingleton<IParserRegexFactory, ParserRegexFactory>()
.AddSingleton(appConfig) .AddSingleton(appConfig)
.AddSingleton(fakeLogger)
.BuildServiceProvider(); .BuildServiceProvider();
} }
@ -86,18 +85,6 @@ namespace ApplicationTests
}); });
} }
[Test]
public void TestCustomEventParsingLogsWarningOnException()
{
var eventParser = serviceProvider.GetService<BaseEventParser>();
eventParser.RegisterCustomEvent("test", "test", (a, b, c) => throw new Exception());
eventParser.GenerateGameEvent("12:12 test");
A.CallTo(() => fakeLogger.WriteWarning(A<string>.Ignored))
.MustHaveHappenedOnceExactly();
}
[Test] [Test]
public void Test_CustomCommandPrefix_Parses() public void Test_CustomCommandPrefix_Parses()
{ {

View File

@ -1,5 +1,6 @@
using FakeItEasy; using FakeItEasy;
using IW4MAdmin.Application.RconParsers; using IW4MAdmin.Application.RconParsers;
using Microsoft.Extensions.Logging;
using NUnit.Framework; using NUnit.Framework;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
@ -8,10 +9,12 @@ namespace ApplicationTests
[TestFixture] [TestFixture]
public class BaseRConParserTests public class BaseRConParserTests
{ {
private readonly ILogger<BaseRConParser> _fakeLogger = A.Fake<ILogger<BaseRConParser>>();
[Test] [Test]
public void SetDvarAsync_FormatStringType() public void SetDvarAsync_FormatStringType()
{ {
var parser = new BaseRConParser(A.Fake<IParserRegexFactory>()); var parser = new BaseRConParser(_fakeLogger, A.Fake<IParserRegexFactory>());
var connection = A.Fake<IRConConnection>(); var connection = A.Fake<IRConConnection>();
parser.SetDvarAsync(connection, "test", "test").Wait(); parser.SetDvarAsync(connection, "test", "test").Wait();
@ -23,7 +26,7 @@ namespace ApplicationTests
[Test] [Test]
public void SetDvarAsync_FormatEmptyStringTypeIncludesQuotes() public void SetDvarAsync_FormatEmptyStringTypeIncludesQuotes()
{ {
var parser = new BaseRConParser(A.Fake<IParserRegexFactory>()); var parser = new BaseRConParser(_fakeLogger, A.Fake<IParserRegexFactory>());
var connection = A.Fake<IRConConnection>(); var connection = A.Fake<IRConConnection>();
parser.SetDvarAsync(connection, "test", "").Wait(); parser.SetDvarAsync(connection, "test", "").Wait();
@ -35,7 +38,7 @@ namespace ApplicationTests
[Test] [Test]
public void SetDvarAsync_FormatsNonString() public void SetDvarAsync_FormatsNonString()
{ {
var parser = new BaseRConParser(A.Fake<IParserRegexFactory>()); var parser = new BaseRConParser(_fakeLogger, A.Fake<IParserRegexFactory>());
var connection = A.Fake<IRConConnection>(); var connection = A.Fake<IRConConnection>();
parser.SetDvarAsync(connection, "test", 123).Wait(); parser.SetDvarAsync(connection, "test", 123).Wait();

View File

@ -22,7 +22,6 @@ namespace ApplicationTests
[TestFixture] [TestFixture]
public class CommandTests public class CommandTests
{ {
ILogger logger;
private IServiceProvider serviceProvider; private IServiceProvider serviceProvider;
private ITranslationLookup transLookup; private ITranslationLookup transLookup;
private CommandConfiguration cmdConfig; private CommandConfiguration cmdConfig;
@ -34,8 +33,6 @@ namespace ApplicationTests
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
logger = A.Fake<ILogger>();
serviceProvider = new ServiceCollection() serviceProvider = new ServiceCollection()
.BuildBase(new EventHandlerMock(true)) .BuildBase(new EventHandlerMock(true))
.AddSingleton(A.Fake<ClientService>()) .AddSingleton(A.Fake<ClientService>())

View File

@ -4,10 +4,10 @@ using FakeItEasy;
using IW4MAdmin; using IW4MAdmin;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using SharedLibraryCore.Configuration; using SharedLibraryCore.Configuration;
using SharedLibraryCore.Database;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using SharedLibraryCore.Services;
using System; using System;
using Microsoft.Extensions.Logging;
using SharedLibraryCore;
namespace ApplicationTests namespace ApplicationTests
{ {
@ -34,10 +34,9 @@ namespace ApplicationTests
A.CallTo(() => transLookup[A<string>.Ignored]) A.CallTo(() => transLookup[A<string>.Ignored])
.Returns("test"); .Returns("test");
A.CallTo(() => manager.GetLogger(A<long>.Ignored)) serviceCollection
.Returns(logger); .AddLogging()
.AddSingleton(A.Fake<ILogger>())
serviceCollection.AddSingleton(logger)
.AddSingleton(manager) .AddSingleton(manager)
.AddSingleton<IDatabaseContextFactory, DatabaseContextFactoryMock>() .AddSingleton<IDatabaseContextFactory, DatabaseContextFactoryMock>()
.AddSingleton(A.Fake<IRConConnectionFactory>()) .AddSingleton(A.Fake<IRConConnectionFactory>())
@ -53,8 +52,13 @@ namespace ApplicationTests
.AddSingleton(ConfigurationGenerators.CreateCommandConfiguration()) .AddSingleton(ConfigurationGenerators.CreateCommandConfiguration())
.AddSingleton<IConfigurationHandler<ApplicationConfiguration>, ApplicationConfigurationHandlerMock>(); .AddSingleton<IConfigurationHandler<ApplicationConfiguration>, ApplicationConfigurationHandlerMock>();
serviceCollection.AddSingleton(_sp => new IW4MServer(_sp.GetRequiredService<IManager>(), ConfigurationGenerators.CreateServerConfiguration(), serviceCollection.AddSingleton(_sp => new IW4MServer(_sp.GetRequiredService<IManager>(),
_sp.GetRequiredService<ITranslationLookup>(), _sp.GetRequiredService<IRConConnectionFactory>(), _sp.GetRequiredService<IGameLogReaderFactory>(), _sp.GetRequiredService<IMetaService>()) ConfigurationGenerators.CreateServerConfiguration(),
_sp.GetRequiredService<ITranslationLookup>(),
_sp.GetRequiredService<IRConConnectionFactory>(),
_sp.GetRequiredService<IGameLogReaderFactory>(),
_sp.GetRequiredService<IMetaService>(),
_sp.GetRequiredService<ILogger<Server>>())
{ {
RconParser = _sp.GetRequiredService<IRConParser>() RconParser = _sp.GetRequiredService<IRConParser>()
}); });

View File

@ -11,6 +11,7 @@ using SharedLibraryCore.Database.Models;
using System.Threading.Tasks; using System.Threading.Tasks;
using ApplicationTests.Mocks; using ApplicationTests.Mocks;
using System.Linq; using System.Linq;
using Microsoft.Extensions.Logging;
using SharedLibraryCore; using SharedLibraryCore;
using SharedLibraryCore.Exceptions; using SharedLibraryCore.Exceptions;
using SharedLibraryCore.Configuration; using SharedLibraryCore.Configuration;
@ -529,9 +530,6 @@ namespace ApplicationTests
{ {
await server.ProcessUpdatesAsync(new System.Threading.CancellationToken()); await server.ProcessUpdatesAsync(new System.Threading.CancellationToken());
} }
A.CallTo(() => fakeLogger.WriteError(A<string>.Ignored))
.MustNotHaveHappened();
} }
[Test] [Test]
@ -555,9 +553,6 @@ namespace ApplicationTests
await server.ExecuteEvent(e); await server.ExecuteEvent(e);
} }
A.CallTo(() => fakeLogger.WriteError(A<string>.Ignored))
.MustHaveHappenedOnceExactly();
Assert.IsNotEmpty(mockEventHandler.Events.Where(_event => _event.Type == GameEvent.EventType.ConnectionLost)); Assert.IsNotEmpty(mockEventHandler.Events.Where(_event => _event.Type == GameEvent.EventType.ConnectionLost));
Assert.AreEqual("err", (mockEventHandler.Events[0].Extra as NetworkException).Message); Assert.AreEqual("err", (mockEventHandler.Events[0].Extra as NetworkException).Message);
} }

View File

@ -11,11 +11,9 @@ using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using SharedLibraryCore.Services; using SharedLibraryCore.Services;
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
namespace ApplicationTests namespace ApplicationTests
{ {
@ -53,7 +51,7 @@ namespace ApplicationTests
[Test] [Test]
public async Task Test_GenericGuidClientIsKicked() public async Task Test_GenericGuidClientIsKicked()
{ {
var plugin = new ScriptPlugin(Path.Join(PLUGIN_DIR, "SharedGUIDKick.js"), PLUGIN_DIR); var plugin = new ScriptPlugin(serviceProvider.GetRequiredService<ILogger>(), Path.Join(PLUGIN_DIR, "SharedGUIDKick.js"), PLUGIN_DIR);
var server = serviceProvider.GetRequiredService<IW4MServer>(); var server = serviceProvider.GetRequiredService<IW4MServer>();
server.GameName = Server.Game.IW4; server.GameName = Server.Game.IW4;
var client = ClientGenerators.CreateBasicClient(server, hasIp: false, clientState: EFClient.ClientState.Connecting); var client = ClientGenerators.CreateBasicClient(server, hasIp: false, clientState: EFClient.ClientState.Connecting);

View File

@ -7,25 +7,19 @@ using SharedLibraryCore.Configuration;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using Microsoft.Extensions.Logging;
using SharedLibraryCore;
namespace ApplicationTests namespace ApplicationTests
{ {
[TestFixture] [TestFixture]
public class ServerTests public class ServerTests
{ {
ILogger logger;
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
logger = A.Fake<ILogger>();
void testLog(string msg) => Console.WriteLine(msg);
A.CallTo(() => logger.WriteError(A<string>.Ignored)).Invokes((string msg) => testLog(msg));
A.CallTo(() => logger.WriteWarning(A<string>.Ignored)).Invokes((string msg) => testLog(msg));
A.CallTo(() => logger.WriteInfo(A<string>.Ignored)).Invokes((string msg) => testLog(msg));
A.CallTo(() => logger.WriteDebug(A<string>.Ignored)).Invokes((string msg) => testLog(msg));
} }
[Test] [Test]
@ -34,7 +28,8 @@ namespace ApplicationTests
var mgr = A.Fake<IManager>(); var mgr = A.Fake<IManager>();
var server = new IW4MServer(mgr, var server = new IW4MServer(mgr,
new SharedLibraryCore.Configuration.ServerConfiguration() { IPAddress = "127.0.0.1", Port = 28960 }, new SharedLibraryCore.Configuration.ServerConfiguration() { IPAddress = "127.0.0.1", Port = 28960 },
A.Fake<ITranslationLookup>(), A.Fake<IRConConnectionFactory>(), A.Fake<IGameLogReaderFactory>(), A.Fake<IMetaService>()); A.Fake<ITranslationLookup>(), A.Fake<IRConConnectionFactory>(),
A.Fake<IGameLogReaderFactory>(), A.Fake<IMetaService>(), A.Fake<ILogger<Server>>());
var parser = new BaseEventParser(A.Fake<IParserRegexFactory>(), A.Fake<ILogger>(), A.Fake<ApplicationConfiguration>()); var parser = new BaseEventParser(A.Fake<IParserRegexFactory>(), A.Fake<ILogger>(), A.Fake<ApplicationConfiguration>());
parser.Configuration.GuidNumberStyle = System.Globalization.NumberStyles.Integer; parser.Configuration.GuidNumberStyle = System.Globalization.NumberStyles.Integer;
@ -56,11 +51,11 @@ namespace ApplicationTests
public void LogFileReplay() public void LogFileReplay()
{ {
var mgr = A.Fake<IManager>(); var mgr = A.Fake<IManager>();
A.CallTo(() => mgr.GetLogger(A<long>.Ignored)).Returns(logger);
var server = new IW4MServer(mgr, var server = new IW4MServer(mgr,
new SharedLibraryCore.Configuration.ServerConfiguration() { IPAddress = "127.0.0.1", Port = 28960 }, new SharedLibraryCore.Configuration.ServerConfiguration() { IPAddress = "127.0.0.1", Port = 28960 },
A.Fake<ITranslationLookup>(), A.Fake<IRConConnectionFactory>(), A.Fake<IGameLogReaderFactory>(), A.Fake<IMetaService>()); A.Fake<ITranslationLookup>(), A.Fake<IRConConnectionFactory>(), A.Fake<IGameLogReaderFactory>(), A.Fake<IMetaService>(),
A.Fake<ILogger<Server>>());
var parser = new BaseEventParser(A.Fake<IParserRegexFactory>(), A.Fake<ILogger>(), A.Fake<ApplicationConfiguration>()); var parser = new BaseEventParser(A.Fake<IParserRegexFactory>(), A.Fake<ILogger>(), A.Fake<ApplicationConfiguration>());
parser.Configuration.GuidNumberStyle = System.Globalization.NumberStyles.Integer; parser.Configuration.GuidNumberStyle = System.Globalization.NumberStyles.Integer;
@ -70,7 +65,6 @@ namespace ApplicationTests
foreach (string line in log) foreach (string line in log)
{ {
var e = parser.GenerateGameEvent(line); var e = parser.GenerateGameEvent(line);
server.Logger.WriteInfo($"{e.GameTime}");
if (e.Origin != null) if (e.Origin != null)
{ {
e.Origin.CurrentServer = server; e.Origin.CurrentServer = server;

View File

@ -6,7 +6,6 @@ using FakeItEasy;
using IW4MAdmin.Application.EventParsers; using IW4MAdmin.Application.EventParsers;
using System.Linq; using System.Linq;
using IW4MAdmin.Plugins.Stats.Models; using IW4MAdmin.Plugins.Stats.Models;
using IW4MAdmin.Application.Helpers;
using IW4MAdmin.Plugins.Stats.Config; using IW4MAdmin.Plugins.Stats.Config;
using System.Collections.Generic; using System.Collections.Generic;
using SharedLibraryCore.Database.Models; using SharedLibraryCore.Database.Models;
@ -14,9 +13,12 @@ using Microsoft.Extensions.DependencyInjection;
using IW4MAdmin.Plugins.Stats.Helpers; using IW4MAdmin.Plugins.Stats.Helpers;
using ApplicationTests.Fixtures; using ApplicationTests.Fixtures;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using SharedLibraryCore;
using Stats.Helpers; using Stats.Helpers;
using Stats.Dtos; using Stats.Dtos;
using SharedLibraryCore.Configuration; using SharedLibraryCore.Configuration;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace ApplicationTests namespace ApplicationTests
{ {
@ -41,13 +43,6 @@ namespace ApplicationTests
.BuildServiceProvider(); .BuildServiceProvider();
contextFactory = serviceProvider.GetRequiredService<IDatabaseContextFactory>(); contextFactory = serviceProvider.GetRequiredService<IDatabaseContextFactory>();
void testLog(string msg) => Console.WriteLine(msg);
A.CallTo(() => logger.WriteError(A<string>.Ignored)).Invokes((string msg) => testLog(msg));
A.CallTo(() => logger.WriteWarning(A<string>.Ignored)).Invokes((string msg) => testLog(msg));
A.CallTo(() => logger.WriteInfo(A<string>.Ignored)).Invokes((string msg) => testLog(msg));
A.CallTo(() => logger.WriteDebug(A<string>.Ignored)).Invokes((string msg) => testLog(msg));
} }
[Test] [Test]
@ -66,13 +61,11 @@ namespace ApplicationTests
A.CallTo(() => handlerFactory.GetConfigurationHandler<StatsConfiguration>(A<string>.Ignored)) A.CallTo(() => handlerFactory.GetConfigurationHandler<StatsConfiguration>(A<string>.Ignored))
.Returns(config); .Returns(config);
A.CallTo(() => mgr.GetLogger(A<long>.Ignored))
.Returns(logger);
var server = new IW4MServer(mgr, var server = new IW4MServer(mgr,
new SharedLibraryCore.Configuration.ServerConfiguration() { IPAddress = "127.0.0.1", Port = 28960 }, new SharedLibraryCore.Configuration.ServerConfiguration() { IPAddress = "127.0.0.1", Port = 28960 },
A.Fake<ITranslationLookup>(), A.Fake<ITranslationLookup>(),
A.Fake<IRConConnectionFactory>(), A.Fake<IGameLogReaderFactory>(), A.Fake<IMetaService>()); A.Fake<IRConConnectionFactory>(),
A.Fake<IGameLogReaderFactory>(), A.Fake<IMetaService>(), A.Fake<ILogger<Server>>());
var parser = new BaseEventParser(A.Fake<IParserRegexFactory>(), A.Fake<ILogger>(), A.Fake<ApplicationConfiguration>()); var parser = new BaseEventParser(A.Fake<IParserRegexFactory>(), A.Fake<ILogger>(), A.Fake<ApplicationConfiguration>());
parser.Configuration.GuidNumberStyle = System.Globalization.NumberStyles.Integer; parser.Configuration.GuidNumberStyle = System.Globalization.NumberStyles.Integer;

View File

@ -1,12 +1,13 @@
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using SharedLibraryCore;
using SharedLibraryCore.Dtos; using SharedLibraryCore.Dtos;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using System; using System;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using WebfrontCore.Controllers.API.Dtos; using WebfrontCore.Controllers.API.Dtos;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace WebfrontCore.Controllers.API namespace WebfrontCore.Controllers.API
{ {
@ -20,7 +21,7 @@ namespace WebfrontCore.Controllers.API
private readonly IResourceQueryHelper<FindClientRequest, FindClientResult> _clientQueryHelper; private readonly IResourceQueryHelper<FindClientRequest, FindClientResult> _clientQueryHelper;
private readonly ILogger _logger; private readonly ILogger _logger;
public ClientController(ILogger logger, IResourceQueryHelper<FindClientRequest, FindClientResult> clientQueryHelper) public ClientController(ILogger<ClientController> logger, IResourceQueryHelper<FindClientRequest, FindClientResult> clientQueryHelper)
{ {
_logger = logger; _logger = logger;
_clientQueryHelper = clientQueryHelper; _clientQueryHelper = clientQueryHelper;
@ -53,8 +54,7 @@ namespace WebfrontCore.Controllers.API
catch (Exception e) catch (Exception e)
{ {
_logger.WriteWarning($"Failed to retrieve clients with query - {request.ToDebugString()}"); _logger.LogWarning(e, "Failed to retrieve clients with query - {@request}", request);
_logger.WriteDebug(e.GetExceptionInfo());
return StatusCode(StatusCodes.Status500InternalServerError, new ErrorResponse() return StatusCode(StatusCodes.Status500InternalServerError, new ErrorResponse()
{ {

View File

@ -171,7 +171,7 @@ namespace WebfrontCore.Controllers
var meta = await ProfileMetaListViewComponent.GetClientMeta(_metaService, metaFilterType, Client.Level, request); var meta = await ProfileMetaListViewComponent.GetClientMeta(_metaService, metaFilterType, Client.Level, request);
if (meta.Count() == 0) if (!meta.Any())
{ {
return Ok(); return Ok();
} }

View File

@ -3,19 +3,22 @@ using Microsoft.AspNetCore.Mvc;
using SharedLibraryCore; using SharedLibraryCore;
using SharedLibraryCore.Dtos; using SharedLibraryCore.Dtos;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using static SharedLibraryCore.Server; using static SharedLibraryCore.Server;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace WebfrontCore.Controllers namespace WebfrontCore.Controllers
{ {
public class HomeController : BaseController public class HomeController : BaseController
{ {
private readonly ITranslationLookup _translationLookup; private readonly ITranslationLookup _translationLookup;
private readonly ILogger _logger;
public HomeController(IManager manager, ITranslationLookup translationLookup) : base(manager) public HomeController(ILogger<HomeController> logger, IManager manager, ITranslationLookup translationLookup) : base(manager)
{ {
_logger = logger;
_translationLookup = translationLookup; _translationLookup = translationLookup;
} }
@ -43,10 +46,7 @@ namespace WebfrontCore.Controllers
public IActionResult Error() public IActionResult Error()
{ {
var exceptionFeature = HttpContext.Features.Get<IExceptionHandlerPathFeature>(); var exceptionFeature = HttpContext.Features.Get<IExceptionHandlerPathFeature>();
Manager.GetLogger(0).WriteError($"[Webfront] {exceptionFeature.Error.Message}"); _logger.LogError("[Webfront] {path} {message} {@exception}", exceptionFeature.Path, exceptionFeature.Error.Message, exceptionFeature.Error);
Manager.GetLogger(0).WriteDebug(exceptionFeature.Path);
Manager.GetLogger(0).WriteDebug(exceptionFeature.Error.StackTrace);
ViewBag.Description = Localization["WEBFRONT_ERROR_DESC"]; ViewBag.Description = Localization["WEBFRONT_ERROR_DESC"];
ViewBag.Title = Localization["WEBFRONT_ERROR_TITLE"]; ViewBag.Title = Localization["WEBFRONT_ERROR_TITLE"];
return View(exceptionFeature.Error); return View(exceptionFeature.Error);

View File

@ -1,9 +1,8 @@
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using SharedLibraryCore.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace WebfrontCore.Middleware namespace WebfrontCore.Middleware
{ {
@ -22,7 +21,7 @@ namespace WebfrontCore.Middleware
/// </summary> /// </summary>
/// <param name="nextRequest"></param> /// <param name="nextRequest"></param>
/// <param name="whitelistedIps">list of textual ip addresses</param> /// <param name="whitelistedIps">list of textual ip addresses</param>
public IPWhitelist(RequestDelegate nextRequest, ILogger logger, string[] whitelistedIps) public IPWhitelist(RequestDelegate nextRequest, ILogger<IPWhitelist> logger, string[] whitelistedIps)
{ {
_whitelistedIps = whitelistedIps.Select(_ip => System.Net.IPAddress.Parse(_ip).GetAddressBytes()).ToArray(); _whitelistedIps = whitelistedIps.Select(_ip => System.Net.IPAddress.Parse(_ip).GetAddressBytes()).ToArray();
_nextRequest = nextRequest; _nextRequest = nextRequest;
@ -31,21 +30,21 @@ namespace WebfrontCore.Middleware
public async Task Invoke(HttpContext context) public async Task Invoke(HttpContext context)
{ {
bool isAlllowed = true; var isAllowed = true;
if (_whitelistedIps.Length > 0) if (_whitelistedIps.Length > 0)
{ {
isAlllowed = _whitelistedIps.Any(_ip => _ip.SequenceEqual(context.Connection.RemoteIpAddress.GetAddressBytes())); isAllowed = _whitelistedIps.Any(_ip => _ip.SequenceEqual(context.Connection.RemoteIpAddress.GetAddressBytes()));
} }
if (isAlllowed) if (isAllowed)
{ {
await _nextRequest.Invoke(context); await _nextRequest.Invoke(context);
} }
else else
{ {
_logger.WriteInfo($"Blocking HTTP request from {context.Connection.RemoteIpAddress.ToString()}"); _logger.LogDebug("Blocking HTTP request from {ipAddress}", context.Connection.RemoteIpAddress);
context.Abort(); context.Abort();
} }
} }

View File

@ -79,11 +79,11 @@ namespace WebfrontCore
}); });
#if DEBUG #if DEBUG
mvcBuilder = mvcBuilder.AddRazorRuntimeCompilation(); /*mvcBuilder = mvcBuilder.AddRazorRuntimeCompilation();
services.Configure<RazorViewEngineOptions>(_options => services.Configure<RazorViewEngineOptions>(_options =>
{ {
_options.ViewLocationFormats.Add(@"/Views/Plugins/{1}/{0}" + RazorViewEngine.ViewExtension); _options.ViewLocationFormats.Add(@"/Views/Plugins/{1}/{0}" + RazorViewEngine.ViewExtension);
}); });*/
#endif #endif
foreach (var asm in pluginAssemblies()) foreach (var asm in pluginAssemblies())
@ -103,13 +103,6 @@ namespace WebfrontCore
options.LoginPath = "/"; options.LoginPath = "/";
}); });
#if DEBUG
services.AddLogging(_builder =>
{
_builder.AddDebug();
});
#endif
services.AddSingleton(Program.Manager); services.AddSingleton(Program.Manager);
services.AddSingleton<IResourceQueryHelper<ChatSearchQuery, MessageResponse>, ChatResourceQueryHelper>(); services.AddSingleton<IResourceQueryHelper<ChatSearchQuery, MessageResponse>, ChatResourceQueryHelper>();
services.AddTransient<IValidator<FindClientRequest>, FindClientRequestValidator>(); services.AddTransient<IValidator<FindClientRequest>, FindClientRequestValidator>();
@ -121,7 +114,6 @@ namespace WebfrontCore
services.AddSingleton(Program.ApplicationServiceProvider.GetService<IDatabaseContextFactory>()); services.AddSingleton(Program.ApplicationServiceProvider.GetService<IDatabaseContextFactory>());
services.AddSingleton(Program.ApplicationServiceProvider.GetService<IAuditInformationRepository>()); services.AddSingleton(Program.ApplicationServiceProvider.GetService<IAuditInformationRepository>());
services.AddSingleton(Program.ApplicationServiceProvider.GetService<ITranslationLookup>()); services.AddSingleton(Program.ApplicationServiceProvider.GetService<ITranslationLookup>());
services.AddSingleton(Program.ApplicationServiceProvider.GetService<SharedLibraryCore.Interfaces.ILogger>());
services.AddSingleton(Program.ApplicationServiceProvider.GetService<IEnumerable<IManagerCommand>>()); services.AddSingleton(Program.ApplicationServiceProvider.GetService<IEnumerable<IManagerCommand>>());
services.AddSingleton(Program.ApplicationServiceProvider.GetService<IMetaService>()); services.AddSingleton(Program.ApplicationServiceProvider.GetService<IMetaService>());
services.AddSingleton(Program.ApplicationServiceProvider.GetService<ApplicationConfiguration>()); services.AddSingleton(Program.ApplicationServiceProvider.GetService<ApplicationConfiguration>());
@ -152,7 +144,7 @@ namespace WebfrontCore
if (Program.Manager.GetApplicationSettings().Configuration().EnableWebfrontConnectionWhitelist) if (Program.Manager.GetApplicationSettings().Configuration().EnableWebfrontConnectionWhitelist)
{ {
app.UseMiddleware<IPWhitelist>(manager.GetLogger(0), manager.GetApplicationSettings().Configuration().WebfrontConnectionWhitelist); app.UseMiddleware<IPWhitelist>(Program.ApplicationServiceProvider.GetService<ILogger<IPWhitelist>>(), manager.GetApplicationSettings().Configuration().WebfrontConnectionWhitelist);
} }
app.UseStaticFiles(); app.UseStaticFiles();

View File

@ -1,4 +1,4 @@
@model List<SharedLibraryCore.Dtos.PlayerInfo> @model IList<SharedLibraryCore.Dtos.PlayerInfo>
@{ @{
var loc = SharedLibraryCore.Utilities.CurrentLocalization.LocalizationIndex; var loc = SharedLibraryCore.Utilities.CurrentLocalization.LocalizationIndex;
} }

View File

@ -1,7 +1,7 @@
@{ @{
Layout = null; Layout = null;
} }
@model List<SharedLibraryCore.Dtos.PenaltyInfo> @model IList<SharedLibraryCore.Dtos.PenaltyInfo>
@{ @{
foreach (var penalty in Model) foreach (var penalty in Model)

View File

@ -1,4 +1,4 @@
@model int @model int?
@{ @{
ViewData["Title"] = "Error"; ViewData["Title"] = "Error";
} }
@ -6,7 +6,7 @@
<h4 class="text-danger">@SharedLibraryCore.Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_ERROR_GENERIC_TITLE"]</h4> <h4 class="text-danger">@SharedLibraryCore.Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_ERROR_GENERIC_TITLE"]</h4>
<h4 class="text-danger">@SharedLibraryCore.Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_ERROR_GENERIC_DESC"]</h4> <h4 class="text-danger">@SharedLibraryCore.Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_ERROR_GENERIC_DESC"]</h4>
<strong class="text-warning"> <strong class="text-warning">
@if (Model == 404) @if (Model.HasValue && Model.Value == 404)
{ {
@SharedLibraryCore.Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_ERROR_NOTFOUND"] @SharedLibraryCore.Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_ERROR_NOTFOUND"]
} }

View File

@ -81,7 +81,13 @@
<ItemGroup> <ItemGroup>
<Folder Include="wwwroot\lib\canvas.js\" /> <Folder Include="wwwroot\lib\canvas.js\" />
</ItemGroup> </ItemGroup>
<Target Name="RemoveSatellitesFromPublish" AfterTargets="ComputeFilesToPublish">
<ItemGroup>
<ResolvedFileToPublish Remove="@(ReferenceSatellitePaths)" />
</ItemGroup>
</Target>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Plugins\Web\StatsWeb\StatsWeb.csproj" /> <ProjectReference Include="..\Plugins\Web\StatsWeb\StatsWeb.csproj" />
<ProjectReference Include="..\SharedLibraryCore\SharedLibraryCore.csproj" /> <ProjectReference Include="..\SharedLibraryCore\SharedLibraryCore.csproj" />