refactor a good bit of stuff for better dependency injection

fix regular expression for T6 log parsing
This commit is contained in:
RaidMax 2020-02-11 16:44:06 -06:00
parent ec053eb854
commit c3c21a7749
56 changed files with 820 additions and 322 deletions

View File

@ -58,11 +58,12 @@ namespace IW4MAdmin.Application
private readonly Dictionary<string, Task<IList>> _operationLookup = new Dictionary<string, Task<IList>>(); private readonly Dictionary<string, Task<IList>> _operationLookup = new Dictionary<string, Task<IList>>();
private readonly ITranslationLookup _translationLookup; private readonly ITranslationLookup _translationLookup;
private readonly IConfigurationHandler<CommandConfiguration> _commandConfiguration; private readonly IConfigurationHandler<CommandConfiguration> _commandConfiguration;
private readonly IPluginImporter _pluginImporter; private readonly IGameServerInstanceFactory _serverInstanceFactory;
public ApplicationManager(ILogger logger, IMiddlewareActionHandler actionHandler, IEnumerable<IManagerCommand> commands, public ApplicationManager(ILogger logger, IMiddlewareActionHandler actionHandler, IEnumerable<IManagerCommand> commands,
ITranslationLookup translationLookup, IConfigurationHandler<CommandConfiguration> commandConfiguration, ITranslationLookup translationLookup, IConfigurationHandler<CommandConfiguration> commandConfiguration,
IConfigurationHandler<ApplicationConfiguration> appConfigHandler, IPluginImporter pluginImporter) IConfigurationHandler<ApplicationConfiguration> appConfigHandler, IGameServerInstanceFactory serverInstanceFactory,
IEnumerable<IPlugin> plugins)
{ {
MiddlewareActionHandler = actionHandler; MiddlewareActionHandler = actionHandler;
_servers = new ConcurrentBag<Server>(); _servers = new ConcurrentBag<Server>();
@ -73,8 +74,8 @@ namespace IW4MAdmin.Application
ConfigHandler = appConfigHandler; ConfigHandler = appConfigHandler;
StartTime = DateTime.UtcNow; StartTime = DateTime.UtcNow;
PageList = new PageList(); PageList = new PageList();
AdditionalEventParsers = new List<IEventParser>(); AdditionalEventParsers = new List<IEventParser>() { new BaseEventParser() };
AdditionalRConParsers = new List<IRConParser>(); AdditionalRConParsers = new List<IRConParser>() { new BaseRConParser() };
TokenAuthenticator = new TokenAuthentication(); TokenAuthenticator = new TokenAuthentication();
_metaService = new MetaService(); _metaService = new MetaService();
_tokenSource = new CancellationTokenSource(); _tokenSource = new CancellationTokenSource();
@ -82,9 +83,12 @@ namespace IW4MAdmin.Application
_commands = commands.ToList(); _commands = commands.ToList();
_translationLookup = translationLookup; _translationLookup = translationLookup;
_commandConfiguration = commandConfiguration; _commandConfiguration = commandConfiguration;
_pluginImporter = pluginImporter; _serverInstanceFactory = serverInstanceFactory;
Plugins = plugins;
} }
public IEnumerable<IPlugin> Plugins { get; }
public async Task ExecuteEvent(GameEvent newEvent) public async Task ExecuteEvent(GameEvent newEvent)
{ {
#if DEBUG == true #if DEBUG == true
@ -249,7 +253,7 @@ namespace IW4MAdmin.Application
ExternalIPAddress = await Utilities.GetExternalIP(); ExternalIPAddress = await Utilities.GetExternalIP();
#region PLUGINS #region PLUGINS
foreach (var plugin in _pluginImporter.ActivePlugins) foreach (var plugin in Plugins)
{ {
try try
{ {
@ -567,7 +571,8 @@ namespace IW4MAdmin.Application
try try
{ {
var ServerInstance = new IW4MServer(this, Conf, _translationLookup, _pluginImporter); // todo: this might not always be an IW4MServer
var ServerInstance = _serverInstanceFactory.CreateServer(Conf, this) as IW4MServer;
await ServerInstance.Initialize(); await ServerInstance.Initialize();
_servers.Add(ServerInstance); _servers.Add(ServerInstance);
@ -759,11 +764,6 @@ namespace IW4MAdmin.Application
return Handler; return Handler;
} }
public IList<Assembly> GetPluginAssemblies()
{
return _pluginImporter.PluginAssemblies.Union(_pluginImporter.Assemblies).ToList();
}
public IPageList GetPageList() public IPageList GetPageList()
{ {
return PageList; return PageList;

View File

@ -9,7 +9,7 @@ using static SharedLibraryCore.Server;
namespace IW4MAdmin.Application.EventParsers namespace IW4MAdmin.Application.EventParsers
{ {
class BaseEventParser : IEventParser public class BaseEventParser : IEventParser
{ {
public BaseEventParser() public BaseEventParser()
{ {
@ -37,7 +37,7 @@ namespace IW4MAdmin.Application.EventParsers
Configuration.Join.AddMapping(ParserRegex.GroupType.OriginClientNumber, 3); Configuration.Join.AddMapping(ParserRegex.GroupType.OriginClientNumber, 3);
Configuration.Join.AddMapping(ParserRegex.GroupType.OriginName, 4); Configuration.Join.AddMapping(ParserRegex.GroupType.OriginName, 4);
Configuration.Damage.Pattern = @"^(D);(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+);(-?[0-9]+);(axis|allies|world)?;(.{1,24});(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+)?;-?([0-9]+);(axis|allies|world)?;(.{1,24})?;((?:[0-9]+|[a-z]+|_)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$"; Configuration.Damage.Pattern = @"^(D);(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+);(-?[0-9]+);(axis|allies|world)?;([^;]{1,24});(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+)?;-?([0-9]+);(axis|allies|world)?;([^;]{1,24})?;((?:[0-9]+|[a-z]+|_|\+)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$";
Configuration.Damage.AddMapping(ParserRegex.GroupType.EventType, 1); Configuration.Damage.AddMapping(ParserRegex.GroupType.EventType, 1);
Configuration.Damage.AddMapping(ParserRegex.GroupType.TargetNetworkId, 2); Configuration.Damage.AddMapping(ParserRegex.GroupType.TargetNetworkId, 2);
Configuration.Damage.AddMapping(ParserRegex.GroupType.TargetClientNumber, 3); Configuration.Damage.AddMapping(ParserRegex.GroupType.TargetClientNumber, 3);
@ -52,7 +52,7 @@ namespace IW4MAdmin.Application.EventParsers
Configuration.Damage.AddMapping(ParserRegex.GroupType.MeansOfDeath, 12); Configuration.Damage.AddMapping(ParserRegex.GroupType.MeansOfDeath, 12);
Configuration.Damage.AddMapping(ParserRegex.GroupType.HitLocation, 13); Configuration.Damage.AddMapping(ParserRegex.GroupType.HitLocation, 13);
Configuration.Kill.Pattern = @"^(K);(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+);(-?[0-9]+);(axis|allies|world)?;(.{1,24});(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+)?;-?([0-9]+);(axis|allies|world)?;(.{1,24})?;((?:[0-9]+|[a-z]+|_)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$"; Configuration.Kill.Pattern = @"^(K);(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+);(-?[0-9]+);(axis|allies|world)?;([^;]{1,24});(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+)?;-?([0-9]+);(axis|allies|world)?;([^;]{1,24})?;((?:[0-9]+|[a-z]+|_|\+)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$";
Configuration.Kill.AddMapping(ParserRegex.GroupType.EventType, 1); Configuration.Kill.AddMapping(ParserRegex.GroupType.EventType, 1);
Configuration.Kill.AddMapping(ParserRegex.GroupType.TargetNetworkId, 2); Configuration.Kill.AddMapping(ParserRegex.GroupType.TargetNetworkId, 2);
Configuration.Kill.AddMapping(ParserRegex.GroupType.TargetClientNumber, 3); Configuration.Kill.AddMapping(ParserRegex.GroupType.TargetClientNumber, 3);

View File

@ -0,0 +1,23 @@
using IW4MAdmin.Application.Misc;
using SharedLibraryCore.Interfaces;
namespace IW4MAdmin.Application.Factories
{
/// <summary>
/// implementation of IConfigurationHandlerFactory
/// provides base functionality to create configuration handlers
/// </summary>
public class ConfigurationHandlerFactory : IConfigurationHandlerFactory
{
/// <summary>
/// creates a base configuration handler
/// </summary>
/// <typeparam name="T">base configuration type</typeparam>
/// <param name="name">name of the config file</param>
/// <returns></returns>
public IConfigurationHandler<T> GetConfigurationHandler<T>(string name) where T : IBaseConfiguration
{
return new BaseConfigurationHandler<T>(name);
}
}
}

View File

@ -0,0 +1,38 @@
using SharedLibraryCore;
using SharedLibraryCore.Configuration;
using SharedLibraryCore.Interfaces;
using System.Collections;
namespace IW4MAdmin.Application.Factories
{
/// <summary>
/// implementation of IGameServerInstanceFactory
/// </summary>
internal class GameServerInstanceFactory : IGameServerInstanceFactory
{
private readonly ITranslationLookup _translationLookup;
private readonly IRConConnectionFactory _rconConnectionFactory;
/// <summary>
/// base constructor
/// </summary>
/// <param name="translationLookup"></param>
/// <param name="rconConnectionFactory"></param>
public GameServerInstanceFactory(ITranslationLookup translationLookup, IRConConnectionFactory rconConnectionFactory)
{
_translationLookup = translationLookup;
_rconConnectionFactory = rconConnectionFactory;
}
/// <summary>
/// creates an IW4MServer instance
/// </summary>
/// <param name="config">server configuration</param>
/// <param name="manager">application manager</param>
/// <returns></returns>
public Server CreateServer(ServerConfiguration config, IManager manager)
{
return new IW4MServer(manager, config, _translationLookup, _rconConnectionFactory);
}
}
}

View File

@ -0,0 +1,36 @@
using IW4MAdmin.Application.RCon;
using SharedLibraryCore.Interfaces;
using System.Text;
namespace IW4MAdmin.Application.Factories
{
/// <summary>
/// implementation of IRConConnectionFactory
/// </summary>
internal class RConConnectionFactory : IRConConnectionFactory
{
private static readonly Encoding gameEncoding = Encoding.GetEncoding("windows-1252");
private readonly ILogger _logger;
/// <summary>
/// Base constructor
/// </summary>
/// <param name="logger"></param>
public RConConnectionFactory(ILogger logger)
{
_logger = logger;
}
/// <summary>
/// creates a new rcon connection instance
/// </summary>
/// <param name="ipAddress">ip address of the server</param>
/// <param name="port">port of the server</param>
/// <param name="password">rcon password of the server</param>
/// <returns></returns>
public IRConConnection CreateConnection(string ipAddress, int port, string password)
{
return new RConConnection(ipAddress, port, password, _logger, gameEncoding);
}
}
}

View File

@ -27,15 +27,14 @@ namespace IW4MAdmin
private GameLogEventDetection LogEvent; private GameLogEventDetection LogEvent;
private readonly ITranslationLookup _translationLookup; private readonly ITranslationLookup _translationLookup;
private const int REPORT_FLAG_COUNT = 4; private const int REPORT_FLAG_COUNT = 4;
private readonly IPluginImporter _pluginImporter;
private int lastGameTime = 0; private int lastGameTime = 0;
public int Id { get; private set; } public int Id { get; private set; }
public IW4MServer(IManager mgr, ServerConfiguration cfg, ITranslationLookup lookup, IPluginImporter pluginImporter) : base(mgr, cfg) public IW4MServer(IManager mgr, ServerConfiguration cfg, ITranslationLookup lookup,
IRConConnectionFactory connectionFactory) : base(mgr, connectionFactory, cfg)
{ {
_translationLookup = lookup; _translationLookup = lookup;
_pluginImporter = pluginImporter;
} }
override public async Task<EFClient> OnClientConnected(EFClient clientFromLog) override public async Task<EFClient> OnClientConnected(EFClient clientFromLog)
@ -66,6 +65,7 @@ namespace IW4MAdmin
client.Score = clientFromLog.Score; client.Score = clientFromLog.Score;
client.Ping = clientFromLog.Ping; client.Ping = clientFromLog.Ping;
client.CurrentServer = this; client.CurrentServer = this;
client.State = ClientState.Connecting;
Clients[client.ClientNumber] = client; Clients[client.ClientNumber] = client;
#if DEBUG == true #if DEBUG == true
@ -153,7 +153,7 @@ namespace IW4MAdmin
} }
} }
foreach (var plugin in _pluginImporter.ActivePlugins) foreach (var plugin in Manager.Plugins)
{ {
try try
{ {
@ -182,6 +182,11 @@ namespace IW4MAdmin
catch (Exception e) catch (Exception e)
{ {
lastException = e; lastException = e;
if (E.Origin != null)
{
E.Origin.Tell(_translationLookup["SERVER_ERROR_COMMAND_INGAME"]);
}
} }
finally finally
@ -695,7 +700,7 @@ namespace IW4MAdmin
await e.WaitAsync(Utilities.DefaultCommandTimeout, new CancellationTokenRegistration().Token); await e.WaitAsync(Utilities.DefaultCommandTimeout, new CancellationTokenRegistration().Token);
} }
foreach (var plugin in _pluginImporter.ActivePlugins) foreach (var plugin in Manager.Plugins)
{ {
await plugin.OnUnloadAsync(); await plugin.OnUnloadAsync();
} }

View File

@ -1,4 +1,6 @@
using IW4MAdmin.Application.Helpers; using IW4MAdmin.Application.Factories;
using IW4MAdmin.Application.Helpers;
using IW4MAdmin.Application.IO;
using IW4MAdmin.Application.Migration; using IW4MAdmin.Application.Migration;
using IW4MAdmin.Application.Misc; using IW4MAdmin.Application.Misc;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@ -8,7 +10,6 @@ using SharedLibraryCore.Exceptions;
using SharedLibraryCore.Helpers; using SharedLibraryCore.Helpers;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
@ -266,27 +267,21 @@ namespace IW4MAdmin.Application
/// </summary> /// </summary>
private static IServiceCollection ConfigureServices() private static IServiceCollection ConfigureServices()
{ {
var defaultLogger = new Logger("IW4MAdmin-Manager");
var pluginImporter = new PluginImporter(defaultLogger);
var serviceCollection = new ServiceCollection(); var serviceCollection = new ServiceCollection();
serviceCollection.AddSingleton<IServiceCollection>(_serviceProvider => serviceCollection) serviceCollection.AddSingleton<IServiceCollection>(_serviceProvider => serviceCollection)
.AddSingleton(new BaseConfigurationHandler<ApplicationConfiguration>("IW4MAdminSettings") as IConfigurationHandler<ApplicationConfiguration>) .AddSingleton(new BaseConfigurationHandler<ApplicationConfiguration>("IW4MAdminSettings") as IConfigurationHandler<ApplicationConfiguration>)
.AddSingleton(new BaseConfigurationHandler<CommandConfiguration>("CommandConfiguration") as IConfigurationHandler<CommandConfiguration>) .AddSingleton(new BaseConfigurationHandler<CommandConfiguration>("CommandConfiguration") as IConfigurationHandler<CommandConfiguration>)
.AddSingleton(_serviceProvider => _serviceProvider.GetRequiredService<IConfigurationHandler<ApplicationConfiguration>>().Configuration()) .AddSingleton(_serviceProvider => _serviceProvider.GetRequiredService<IConfigurationHandler<ApplicationConfiguration>>().Configuration())
.AddSingleton(_serviceProvider => _serviceProvider.GetRequiredService<IConfigurationHandler<CommandConfiguration>>().Configuration() ?? new CommandConfiguration()) .AddSingleton(_serviceProvider => _serviceProvider.GetRequiredService<IConfigurationHandler<CommandConfiguration>>().Configuration() ?? new CommandConfiguration())
.AddSingleton<ILogger>(_serviceProvider => new Logger("IW4MAdmin-Manager")) .AddSingleton<ILogger>(_serviceProvider => defaultLogger)
.AddSingleton<IPluginImporter, PluginImporter>() .AddSingleton<IPluginImporter, PluginImporter>()
.AddSingleton<IMiddlewareActionHandler, MiddlewareActionHandler>() .AddSingleton<IMiddlewareActionHandler, MiddlewareActionHandler>()
.AddTransient(_serviceProvider => .AddSingleton<IRConConnectionFactory, RConConnectionFactory>()
{ .AddSingleton<IGameServerInstanceFactory, GameServerInstanceFactory>()
var importer = _serviceProvider.GetRequiredService<IPluginImporter>(); .AddSingleton<IConfigurationHandlerFactory, ConfigurationHandlerFactory>()
var config = _serviceProvider.GetRequiredService<CommandConfiguration>();
var layout = _serviceProvider.GetRequiredService<ITranslationLookup>();
// todo: this is disgusting, but I need it until I can figure out a way to dynamically load the plugins without creating an instance.
return importer.CommandTypes.
Union(typeof(SharedLibraryCore.Commands.QuitCommand).Assembly.GetTypes()
.Where(_command => _command.BaseType == typeof(Command)))
.Select(_cmdType => Activator.CreateInstance(_cmdType, config, layout) as IManagerCommand);
})
.AddSingleton(_serviceProvider => .AddSingleton(_serviceProvider =>
{ {
var config = _serviceProvider.GetRequiredService<IConfigurationHandler<ApplicationConfiguration>>().Configuration(); var config = _serviceProvider.GetRequiredService<IConfigurationHandler<ApplicationConfiguration>>().Configuration();
@ -295,6 +290,35 @@ namespace IW4MAdmin.Application
}) })
.AddSingleton<IManager, ApplicationManager>(); .AddSingleton<IManager, ApplicationManager>();
// register the native commands
foreach (var commandType in typeof(SharedLibraryCore.Commands.QuitCommand).Assembly.GetTypes()
.Where(_command => _command.BaseType == typeof(Command)))
{
defaultLogger.WriteInfo($"Registered native command type {commandType.Name}");
serviceCollection.AddSingleton(typeof(IManagerCommand), commandType);
}
// register the plugin implementations
var pluginImplementations = pluginImporter.DiscoverAssemblyPluginImplementations();
foreach (var pluginType in pluginImplementations.Item1)
{
defaultLogger.WriteInfo($"Registered plugin type {pluginType.FullName}");
serviceCollection.AddSingleton(typeof(IPlugin), pluginType);
}
// register the plugin commands
foreach (var commandType in pluginImplementations.Item2)
{
defaultLogger.WriteInfo($"Registered plugin command type {commandType.FullName}");
serviceCollection.AddSingleton(typeof(IManagerCommand), commandType);
}
// register any script plugins
foreach (var scriptPlugin in pluginImporter.DiscoverScriptPlugins())
{
serviceCollection.AddSingleton(scriptPlugin);
}
return serviceCollection; return serviceCollection;
} }
} }

View File

@ -34,6 +34,11 @@ namespace IW4MAdmin.Application.Migration
{ {
Directory.CreateDirectory(Path.Join(Utilities.OperatingDirectory, "Log")); Directory.CreateDirectory(Path.Join(Utilities.OperatingDirectory, "Log"));
} }
if (!Directory.Exists(Path.Join(Utilities.OperatingDirectory, "Localization")))
{
Directory.CreateDirectory(Path.Join(Utilities.OperatingDirectory, "Localization"));
}
} }
/// <summary> /// <summary>

View File

@ -1,12 +1,17 @@
using Newtonsoft.Json; using Newtonsoft.Json;
using SharedLibraryCore;
using SharedLibraryCore.Exceptions; using SharedLibraryCore.Exceptions;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using System; using System;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace SharedLibraryCore.Configuration namespace IW4MAdmin.Application.Misc
{ {
/// <summary>
/// default implementation of IConfigurationHandler
/// </summary>
/// <typeparam name="T">base configuration type</typeparam>
public class BaseConfigurationHandler<T> : IConfigurationHandler<T> where T : IBaseConfiguration public class BaseConfigurationHandler<T> : IConfigurationHandler<T> where T : IBaseConfiguration
{ {
T _configuration; T _configuration;

View File

@ -1,12 +1,14 @@
using SharedLibraryCore; using IW4MAdmin.Application.IO;
using SharedLibraryCore;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using System; using System;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
namespace IW4MAdmin.Application namespace IW4MAdmin.Application
{ {
class Logger : ILogger public class Logger : ILogger
{ {
enum LogType enum LogType
{ {
@ -77,9 +79,7 @@ namespace IW4MAdmin.Application
{ {
#if DEBUG #if DEBUG
// lets keep it simple and dispose of everything quickly as logging wont be that much (relatively) // lets keep it simple and dispose of everything quickly as logging wont be that much (relatively)
Console.WriteLine(LogLine); Console.WriteLine(msg);
//File.AppendAllText(FileName, $"{LogLine}{Environment.NewLine}");
//Debug.WriteLine(msg);
#else #else
if (type == LogType.Error || type == LogType.Verbose) if (type == LogType.Error || type == LogType.Verbose)
{ {

View File

@ -5,111 +5,84 @@ using System.Reflection;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using System.Linq; using System.Linq;
using SharedLibraryCore; using SharedLibraryCore;
using IW4MAdmin.Application.Misc;
namespace IW4MAdmin.Application.Helpers namespace IW4MAdmin.Application.Helpers
{ {
/// <summary>
/// implementation of IPluginImporter
/// discovers plugins and script plugins
/// </summary>
public class PluginImporter : IPluginImporter public class PluginImporter : IPluginImporter
{ {
public IList<Type> CommandTypes { get; private set; } = new List<Type>(); private static readonly string PLUGIN_DIR = "Plugins";
public IList<IPlugin> ActivePlugins { get; private set; } = new List<IPlugin>();
public IList<Assembly> PluginAssemblies { get; private set; } = new List<Assembly>();
public IList<Assembly> Assemblies { get; private set; } = new List<Assembly>();
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly ITranslationLookup _translationLookup;
public PluginImporter(ILogger logger, ITranslationLookup translationLookup) public PluginImporter(ILogger logger)
{ {
_logger = logger; _logger = logger;
_translationLookup = translationLookup;
Load();
} }
/// <summary> /// <summary>
/// Loads all the assembly and javascript plugins /// discovers all the script plugins in the plugins dir
/// </summary> /// </summary>
private void Load() /// <returns></returns>
public IEnumerable<IPlugin> DiscoverScriptPlugins()
{ {
string pluginDir = $"{Utilities.OperatingDirectory}Plugins{Path.DirectorySeparatorChar}"; string pluginDir = $"{Utilities.OperatingDirectory}{PLUGIN_DIR}{Path.DirectorySeparatorChar}";
string[] dllFileNames = null;
string[] scriptFileNames = null;
if (Directory.Exists(pluginDir)) if (Directory.Exists(pluginDir))
{ {
dllFileNames = Directory.GetFiles($"{Utilities.OperatingDirectory}Plugins{Path.DirectorySeparatorChar}", "*.dll"); string[] scriptPluginFiles = Directory.GetFiles(pluginDir, "*.js");
scriptFileNames = Directory.GetFiles($"{Utilities.OperatingDirectory}Plugins{Path.DirectorySeparatorChar}", "*.js");
}
else _logger.WriteInfo($"Discovered {scriptPluginFiles.Length} potential script plugins");
{
dllFileNames = new string[0];
scriptFileNames = new string[0];
}
if (dllFileNames.Length == 0 && if (scriptPluginFiles.Length > 0)
scriptFileNames.Length == 0)
{ {
_logger.WriteDebug(_translationLookup["PLUGIN_IMPORTER_NOTFOUND"]); foreach (string fileName in scriptPluginFiles)
return;
}
// load up the script plugins
foreach (string fileName in scriptFileNames)
{ {
_logger.WriteInfo($"Discovered script plugin {fileName}");
var plugin = new ScriptPlugin(fileName); var plugin = new ScriptPlugin(fileName);
_logger.WriteDebug($"Loaded script plugin \"{ plugin.Name }\" [{plugin.Version}]"); yield return plugin;
ActivePlugins.Add(plugin);
}
ICollection<Assembly> assemblies = new List<Assembly>(dllFileNames.Length);
foreach (string dllFile in dllFileNames)
{
assemblies.Add(Assembly.LoadFrom(dllFile));
}
int LoadedCommands = 0;
foreach (Assembly Plugin in assemblies)
{
if (Plugin != null)
{
Assemblies.Add(Plugin);
Type[] types = Plugin.GetTypes();
foreach (Type assemblyType in types)
{
if (assemblyType.IsClass && assemblyType.BaseType == typeof(Command))
{
CommandTypes.Add(assemblyType);
_logger.WriteDebug($"{_translationLookup["PLUGIN_IMPORTER_REGISTERCMD"]} \"{assemblyType.Name}\"");
LoadedCommands++;
continue;
}
try
{
if (assemblyType.GetInterface("IPlugin", false) == null)
continue;
var notifyObject = Activator.CreateInstance(assemblyType);
IPlugin newNotify = (IPlugin)notifyObject;
if (ActivePlugins.FirstOrDefault(x => x.Name == newNotify.Name) == null)
{
ActivePlugins.Add(newNotify);
PluginAssemblies.Add(Plugin);
_logger.WriteDebug($"Loaded plugin \"{newNotify.Name}\" [{newNotify.Version}]");
}
}
catch (Exception e)
{
_logger.WriteWarning(_translationLookup["PLUGIN_IMPORTER_ERROR"].FormatExt(Plugin.Location));
_logger.WriteDebug(e.GetExceptionInfo());
} }
} }
} }
} }
_logger.WriteInfo($"Loaded {ActivePlugins.Count} plugins and registered {LoadedCommands} plugin commands."); /// <summary>
/// discovers all the C# assembly plugins and commands
/// </summary>
/// <returns></returns>
public (IEnumerable<Type>, IEnumerable<Type>) DiscoverAssemblyPluginImplementations()
{
string pluginDir = $"{Utilities.OperatingDirectory}{PLUGIN_DIR}{Path.DirectorySeparatorChar}";
var pluginTypes = Enumerable.Empty<Type>();
var commandTypes = Enumerable.Empty<Type>();
if (Directory.Exists(pluginDir))
{
var dllFileNames = Directory.GetFiles(pluginDir, "*.dll");
_logger.WriteInfo($"Discovered {dllFileNames.Length} potential plugin assemblies");
if (dllFileNames.Length > 0)
{
var assemblies = dllFileNames.Select(_name => Assembly.LoadFrom(_name));
pluginTypes = assemblies
.SelectMany(_asm => _asm.GetTypes())
.Where(_assemblyType => _assemblyType.GetInterface(nameof(IPlugin), false) != null);
_logger.WriteInfo($"Discovered {pluginTypes.Count()} plugin implementations");
commandTypes = assemblies
.SelectMany(_asm => _asm.GetTypes())
.Where(_assemblyType => _assemblyType.IsClass && _assemblyType.BaseType == typeof(Command));
_logger.WriteInfo($"Discovered {commandTypes.Count()} plugin commands");
}
}
return (pluginTypes, commandTypes);
} }
} }
} }

View File

@ -1,15 +1,19 @@
using Jint; using Jint;
using SharedLibraryCore;
using SharedLibraryCore.Database.Models; using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace SharedLibraryCore namespace IW4MAdmin.Application.Misc
{ {
/// <summary>
/// implementation of IPlugin
/// used to proxy script plugin requests
/// </summary>
public class ScriptPlugin : IPlugin public class ScriptPlugin : IPlugin
{ {
public string Name { get; set; } public string Name { get; set; }
@ -45,7 +49,6 @@ namespace SharedLibraryCore
_onProcessing.Dispose(); _onProcessing.Dispose();
} }
public async Task Initialize(IManager manager) public async Task Initialize(IManager manager)
{ {
await _onProcessing.WaitAsync(); await _onProcessing.WaitAsync();

View File

@ -0,0 +1,29 @@
using System;
using System.Net.Sockets;
using System.Threading;
namespace IW4MAdmin.Application.RCon
{
/// <summary>
/// used to keep track of the udp connection state
/// </summary>
internal class ConnectionState
{
~ConnectionState()
{
OnComplete.Dispose();
OnSentData.Dispose();
OnReceivedData.Dispose();
}
public int ConnectionAttempts { get; set; }
const int BufferSize = 4096;
public readonly byte[] ReceiveBuffer = new byte[BufferSize];
public readonly SemaphoreSlim OnComplete = new SemaphoreSlim(1, 1);
public readonly ManualResetEventSlim OnSentData = new ManualResetEventSlim(false);
public readonly ManualResetEventSlim OnReceivedData = new ManualResetEventSlim(false);
public SocketAsyncEventArgs SendEventArgs { get; set; } = new SocketAsyncEventArgs();
public SocketAsyncEventArgs ReceiveEventArgs { get; set; } = new SocketAsyncEventArgs();
public DateTime LastQuery { get; set; } = DateTime.Now;
}
}

View File

@ -1,58 +1,41 @@
using SharedLibraryCore.Exceptions; using SharedLibraryCore;
using SharedLibraryCore.Exceptions;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using SharedLibraryCore.RCon;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text; using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace SharedLibraryCore.RCon namespace IW4MAdmin.Application.RCon
{ {
class ConnectionState /// <summary>
{ /// implementation of IRConConnection
~ConnectionState() /// </summary>
{ public class RConConnection : IRConConnection
OnComplete.Dispose();
OnSentData.Dispose();
OnReceivedData.Dispose();
}
public int ConnectionAttempts { get; set; }
const int BufferSize = 4096;
public readonly byte[] ReceiveBuffer = new byte[BufferSize];
public readonly SemaphoreSlim OnComplete = new SemaphoreSlim(1, 1);
public readonly ManualResetEventSlim OnSentData = new ManualResetEventSlim(false);
public readonly ManualResetEventSlim OnReceivedData = new ManualResetEventSlim(false);
public SocketAsyncEventArgs SendEventArgs { get; set; } = new SocketAsyncEventArgs();
public SocketAsyncEventArgs ReceiveEventArgs { get; set; } = new SocketAsyncEventArgs();
public DateTime LastQuery { get; set; } = DateTime.Now;
}
public class Connection
{ {
static readonly ConcurrentDictionary<EndPoint, ConnectionState> ActiveQueries = new ConcurrentDictionary<EndPoint, ConnectionState>(); static readonly ConcurrentDictionary<EndPoint, ConnectionState> ActiveQueries = new ConcurrentDictionary<EndPoint, ConnectionState>();
public IPEndPoint Endpoint { get; private set; } public IPEndPoint Endpoint { get; private set; }
public string RConPassword { get; private set; } public string RConPassword { get; private set; }
private readonly ILogger Log; private IRConParserConfiguration config;
private IRConParserConfiguration Config; private readonly ILogger _log;
private readonly Encoding defaultEncoding; private readonly Encoding _gameEncoding;
public Connection(string ipAddress, int port, string password, ILogger log, IRConParserConfiguration config) public RConConnection(string ipAddress, int port, string password, ILogger log, Encoding gameEncoding)
{ {
Endpoint = new IPEndPoint(IPAddress.Parse(ipAddress), port); Endpoint = new IPEndPoint(IPAddress.Parse(ipAddress), port);
defaultEncoding = Encoding.GetEncoding("windows-1252"); _gameEncoding = gameEncoding;
RConPassword = password; RConPassword = password;
Log = log; _log = log;
Config = config;
} }
public void SetConfiguration(IRConParserConfiguration config) public void SetConfiguration(IRConParserConfiguration config)
{ {
Config = config; this.config = config;
} }
public async Task<string[]> SendQueryAsync(StaticHelpers.QueryType type, string parameters = "") public async Task<string[]> SendQueryAsync(StaticHelpers.QueryType type, string parameters = "")
@ -65,7 +48,7 @@ namespace SharedLibraryCore.RCon
var connectionState = ActiveQueries[this.Endpoint]; var connectionState = ActiveQueries[this.Endpoint];
#if DEBUG == true #if DEBUG == true
Log.WriteDebug($"Waiting for semaphore to be released [{this.Endpoint}]"); _log.WriteDebug($"Waiting for semaphore to be released [{this.Endpoint}]");
#endif #endif
// 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();
@ -80,17 +63,17 @@ namespace SharedLibraryCore.RCon
connectionState.LastQuery = DateTime.Now; connectionState.LastQuery = DateTime.Now;
#if DEBUG == true #if DEBUG == true
Log.WriteDebug($"Semaphore has been released [{this.Endpoint}]"); _log.WriteDebug($"Semaphore has been released [{this.Endpoint}]");
Log.WriteDebug($"Query [{this.Endpoint},{type.ToString()},{parameters}]"); _log.WriteDebug($"Query [{this.Endpoint},{type.ToString()},{parameters}]");
#endif #endif
byte[] payload = null; byte[] payload = null;
bool waitForResponse = Config.WaitForResponse; bool waitForResponse = config.WaitForResponse;
string convertEncoding(string text) string convertEncoding(string text)
{ {
byte[] convertedBytes = Utilities.EncodingType.GetBytes(text); byte[] convertedBytes = Utilities.EncodingType.GetBytes(text);
return defaultEncoding.GetString(convertedBytes); return _gameEncoding.GetString(convertedBytes);
} }
try try
@ -102,25 +85,25 @@ namespace SharedLibraryCore.RCon
{ {
case StaticHelpers.QueryType.GET_DVAR: case StaticHelpers.QueryType.GET_DVAR:
waitForResponse |= true; waitForResponse |= true;
payload = string.Format(Config.CommandPrefixes.RConGetDvar, convertedRConPassword, convertedParameters + '\0').Select(Convert.ToByte).ToArray(); payload = string.Format(config.CommandPrefixes.RConGetDvar, convertedRConPassword, convertedParameters + '\0').Select(Convert.ToByte).ToArray();
break; break;
case StaticHelpers.QueryType.SET_DVAR: case StaticHelpers.QueryType.SET_DVAR:
payload = string.Format(Config.CommandPrefixes.RConSetDvar, convertedRConPassword, convertedParameters + '\0').Select(Convert.ToByte).ToArray(); payload = string.Format(config.CommandPrefixes.RConSetDvar, convertedRConPassword, convertedParameters + '\0').Select(Convert.ToByte).ToArray();
break; break;
case StaticHelpers.QueryType.COMMAND: case StaticHelpers.QueryType.COMMAND:
payload = string.Format(Config.CommandPrefixes.RConCommand, convertedRConPassword, convertedParameters + '\0').Select(Convert.ToByte).ToArray(); payload = string.Format(config.CommandPrefixes.RConCommand, convertedRConPassword, convertedParameters + '\0').Select(Convert.ToByte).ToArray();
break; break;
case StaticHelpers.QueryType.GET_STATUS: case StaticHelpers.QueryType.GET_STATUS:
waitForResponse |= true; waitForResponse |= true;
payload = (Config.CommandPrefixes.RConGetStatus + '\0').Select(Convert.ToByte).ToArray(); payload = (config.CommandPrefixes.RConGetStatus + '\0').Select(Convert.ToByte).ToArray();
break; break;
case StaticHelpers.QueryType.GET_INFO: case StaticHelpers.QueryType.GET_INFO:
waitForResponse |= true; waitForResponse |= true;
payload = (Config.CommandPrefixes.RConGetInfo + '\0').Select(Convert.ToByte).ToArray(); payload = (config.CommandPrefixes.RConGetInfo + '\0').Select(Convert.ToByte).ToArray();
break; break;
case StaticHelpers.QueryType.COMMAND_STATUS: case StaticHelpers.QueryType.COMMAND_STATUS:
waitForResponse |= true; waitForResponse |= true;
payload = string.Format(Config.CommandPrefixes.RConCommand, convertedRConPassword, "status\0").Select(Convert.ToByte).ToArray(); payload = string.Format(config.CommandPrefixes.RConCommand, convertedRConPassword, "status\0").Select(Convert.ToByte).ToArray();
break; break;
} }
} }
@ -148,7 +131,7 @@ namespace SharedLibraryCore.RCon
connectionState.OnReceivedData.Reset(); connectionState.OnReceivedData.Reset();
connectionState.ConnectionAttempts++; connectionState.ConnectionAttempts++;
#if DEBUG == true #if DEBUG == true
Log.WriteDebug($"Sending {payload.Length} bytes to [{this.Endpoint}] ({connectionState.ConnectionAttempts}/{StaticHelpers.AllowedConnectionFails})"); _log.WriteDebug($"Sending {payload.Length} bytes to [{this.Endpoint}] ({connectionState.ConnectionAttempts}/{StaticHelpers.AllowedConnectionFails})");
#endif #endif
try try
{ {
@ -182,7 +165,7 @@ namespace SharedLibraryCore.RCon
} }
} }
string responseString = defaultEncoding.GetString(response, 0, response.Length) + '\n'; string responseString = _gameEncoding.GetString(response, 0, response.Length) + '\n';
// 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"))
@ -195,7 +178,7 @@ namespace SharedLibraryCore.RCon
throw new NetworkException(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_RCON_NOTSET"]); throw new NetworkException(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_RCON_NOTSET"]);
} }
if (responseString.Contains(Config.ServerNotRunningResponse)) if (responseString.Contains(config.ServerNotRunningResponse))
{ {
throw new ServerException(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_NOT_RUNNING"].FormatExt(Endpoint.ToString())); throw new ServerException(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_NOT_RUNNING"].FormatExt(Endpoint.ToString()));
} }
@ -269,7 +252,7 @@ namespace SharedLibraryCore.RCon
private void OnDataReceived(object sender, SocketAsyncEventArgs e) private void OnDataReceived(object sender, SocketAsyncEventArgs e)
{ {
#if DEBUG == true #if DEBUG == true
Log.WriteDebug($"Read {e.BytesTransferred} bytes from {e.RemoteEndPoint.ToString()}"); _log.WriteDebug($"Read {e.BytesTransferred} bytes from {e.RemoteEndPoint.ToString()}");
#endif #endif
ActiveQueries[this.Endpoint].OnReceivedData.Set(); ActiveQueries[this.Endpoint].OnReceivedData.Set();
} }
@ -277,7 +260,7 @@ namespace SharedLibraryCore.RCon
private void OnDataSent(object sender, SocketAsyncEventArgs e) private void OnDataSent(object sender, SocketAsyncEventArgs e)
{ {
#if DEBUG == true #if DEBUG == true
Log.WriteDebug($"Sent {e.Buffer?.Length} bytes to {e.ConnectSocket?.RemoteEndPoint?.ToString()}"); _log.WriteDebug($"Sent {e.Buffer?.Length} bytes to {e.ConnectSocket?.RemoteEndPoint?.ToString()}");
#endif #endif
ActiveQueries[this.Endpoint].OnSentData.Set(); ActiveQueries[this.Endpoint].OnSentData.Set();
} }

View File

@ -65,13 +65,13 @@ namespace IW4MAdmin.Application.RconParsers
public bool CanGenerateLogPath { get; set; } = true; public bool CanGenerateLogPath { get; set; } = true;
public string Name { get; set; } = "Call of Duty"; public string Name { get; set; } = "Call of Duty";
public async Task<string[]> ExecuteCommandAsync(Connection connection, string command) public async Task<string[]> ExecuteCommandAsync(IRConConnection connection, string command)
{ {
var response = await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND, command); var response = await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND, command);
return response.Skip(1).ToArray(); return response.Skip(1).ToArray();
} }
public async Task<Dvar<T>> GetDvarAsync<T>(Connection connection, string dvarName) public async Task<Dvar<T>> GetDvarAsync<T>(IRConConnection connection, string dvarName)
{ {
string[] lineSplit = await connection.SendQueryAsync(StaticHelpers.QueryType.GET_DVAR, dvarName); string[] lineSplit = await connection.SendQueryAsync(StaticHelpers.QueryType.GET_DVAR, dvarName);
string response = string.Join('\n', lineSplit.Skip(1)); string response = string.Join('\n', lineSplit.Skip(1));
@ -105,7 +105,7 @@ namespace IW4MAdmin.Application.RconParsers
}; };
} }
public virtual async Task<(List<EFClient>, string)> GetStatusAsync(Connection connection) public virtual async Task<(List<EFClient>, string)> GetStatusAsync(IRConConnection connection)
{ {
string[] response = await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND_STATUS); string[] response = await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND_STATUS);
#if DEBUG #if DEBUG
@ -132,7 +132,7 @@ namespace IW4MAdmin.Application.RconParsers
return map; return map;
} }
public async Task<bool> SetDvarAsync(Connection connection, string dvarName, object dvarValue) public async Task<bool> SetDvarAsync(IRConConnection connection, string dvarName, object dvarValue)
{ {
return (await connection.SendQueryAsync(StaticHelpers.QueryType.SET_DVAR, $"{dvarName} {dvarValue}")).Length > 0; return (await connection.SendQueryAsync(StaticHelpers.QueryType.SET_DVAR, $"{dvarName} {dvarValue}")).Length > 0;
} }

View File

@ -6,8 +6,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plugins", "Plugins", "{26E8
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8C8F3945-0AEF-4949-A1F7-B18E952E50BC}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8C8F3945-0AEF-4949-A1F7-B18E952E50BC}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
GameFiles\IW4x\userraw\_commands.gsc = GameFiles\IW4x\userraw\_commands.gsc GameFiles\IW4x\userraw\scripts\_commands.gsc = GameFiles\IW4x\userraw\scripts\_commands.gsc
GameFiles\IW4x\userraw\_customcallbacks.gsc = GameFiles\IW4x\userraw\_customcallbacks.gsc GameFiles\IW4x\userraw\scripts\_customcallbacks.gsc = GameFiles\IW4x\userraw\scripts\_customcallbacks.gsc
azure-pipelines.yml = azure-pipelines.yml azure-pipelines.yml = azure-pipelines.yml
PostPublish.ps1 = PostPublish.ps1 PostPublish.ps1 = PostPublish.ps1
README.md = README.md README.md = README.md
@ -57,6 +57,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutomessageFeed", "Plugins\
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LiveRadar", "Plugins\LiveRadar\LiveRadar.csproj", "{00A1FED2-2254-4AF7-A5DB-2357FA7C88CD}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LiveRadar", "Plugins\LiveRadar\LiveRadar.csproj", "{00A1FED2-2254-4AF7-A5DB-2357FA7C88CD}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{3065279E-17F0-4CE0-AF5B-014E04263D77}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApplicationTests", "Tests\ApplicationTests\ApplicationTests.csproj", "{581FA7AF-FEF6-483C-A7D0-2D13EF50801B}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -405,6 +409,29 @@ Global
{00A1FED2-2254-4AF7-A5DB-2357FA7C88CD}.Release|x64.Build.0 = Release|Any CPU {00A1FED2-2254-4AF7-A5DB-2357FA7C88CD}.Release|x64.Build.0 = Release|Any CPU
{00A1FED2-2254-4AF7-A5DB-2357FA7C88CD}.Release|x86.ActiveCfg = Release|Any CPU {00A1FED2-2254-4AF7-A5DB-2357FA7C88CD}.Release|x86.ActiveCfg = Release|Any CPU
{00A1FED2-2254-4AF7-A5DB-2357FA7C88CD}.Release|x86.Build.0 = Release|Any CPU {00A1FED2-2254-4AF7-A5DB-2357FA7C88CD}.Release|x86.Build.0 = Release|Any CPU
{581FA7AF-FEF6-483C-A7D0-2D13EF50801B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{581FA7AF-FEF6-483C-A7D0-2D13EF50801B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{581FA7AF-FEF6-483C-A7D0-2D13EF50801B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{581FA7AF-FEF6-483C-A7D0-2D13EF50801B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{581FA7AF-FEF6-483C-A7D0-2D13EF50801B}.Debug|x64.ActiveCfg = Debug|Any CPU
{581FA7AF-FEF6-483C-A7D0-2D13EF50801B}.Debug|x64.Build.0 = Debug|Any CPU
{581FA7AF-FEF6-483C-A7D0-2D13EF50801B}.Debug|x86.ActiveCfg = Debug|Any CPU
{581FA7AF-FEF6-483C-A7D0-2D13EF50801B}.Debug|x86.Build.0 = Debug|Any CPU
{581FA7AF-FEF6-483C-A7D0-2D13EF50801B}.Prerelease|Any CPU.ActiveCfg = Debug|Any CPU
{581FA7AF-FEF6-483C-A7D0-2D13EF50801B}.Prerelease|Mixed Platforms.ActiveCfg = Debug|Any CPU
{581FA7AF-FEF6-483C-A7D0-2D13EF50801B}.Prerelease|Mixed Platforms.Build.0 = Debug|Any CPU
{581FA7AF-FEF6-483C-A7D0-2D13EF50801B}.Prerelease|x64.ActiveCfg = Debug|Any CPU
{581FA7AF-FEF6-483C-A7D0-2D13EF50801B}.Prerelease|x64.Build.0 = Debug|Any CPU
{581FA7AF-FEF6-483C-A7D0-2D13EF50801B}.Prerelease|x86.ActiveCfg = Debug|Any CPU
{581FA7AF-FEF6-483C-A7D0-2D13EF50801B}.Prerelease|x86.Build.0 = Debug|Any CPU
{581FA7AF-FEF6-483C-A7D0-2D13EF50801B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{581FA7AF-FEF6-483C-A7D0-2D13EF50801B}.Release|Any CPU.Build.0 = Release|Any CPU
{581FA7AF-FEF6-483C-A7D0-2D13EF50801B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{581FA7AF-FEF6-483C-A7D0-2D13EF50801B}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{581FA7AF-FEF6-483C-A7D0-2D13EF50801B}.Release|x64.ActiveCfg = Release|Any CPU
{581FA7AF-FEF6-483C-A7D0-2D13EF50801B}.Release|x64.Build.0 = Release|Any CPU
{581FA7AF-FEF6-483C-A7D0-2D13EF50801B}.Release|x86.ActiveCfg = Release|Any CPU
{581FA7AF-FEF6-483C-A7D0-2D13EF50801B}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -421,6 +448,7 @@ Global
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B} = {A848FCF1-8527-4AA8-A1AA-50D29695C678} {776B348B-F818-4A0F-A625-D0AF8BAD3E9B} = {A848FCF1-8527-4AA8-A1AA-50D29695C678}
{F5815359-CFC7-44B4-9A3B-C04BACAD5836} = {26E8B310-269E-46D4-A612-24601F16065F} {F5815359-CFC7-44B4-9A3B-C04BACAD5836} = {26E8B310-269E-46D4-A612-24601F16065F}
{00A1FED2-2254-4AF7-A5DB-2357FA7C88CD} = {26E8B310-269E-46D4-A612-24601F16065F} {00A1FED2-2254-4AF7-A5DB-2357FA7C88CD} = {26E8B310-269E-46D4-A612-24601F16065F}
{581FA7AF-FEF6-483C-A7D0-2D13EF50801B} = {3065279E-17F0-4CE0-AF5B-014E04263D77}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {84F8F8E0-1F73-41E0-BD8D-BB6676E2EE87} SolutionGuid = {84F8F8E0-1F73-41E0-BD8D-BB6676E2EE87}

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.2.6" PrivateAssets="All" /> <PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2.2.7" PrivateAssets="All" />
</ItemGroup> </ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent"> <Target Name="PostBuild" AfterTargets="PostBuildEvent">

View File

@ -20,14 +20,19 @@ namespace AutomessageFeed
public string Author => "RaidMax"; public string Author => "RaidMax";
private Configuration _configuration;
private int _currentFeedItem; private int _currentFeedItem;
private readonly IConfigurationHandler<Configuration> _configurationHandler;
public Plugin(IConfigurationHandlerFactory configurationHandlerFactory)
{
_configurationHandler = configurationHandlerFactory.GetConfigurationHandler<Configuration>("AutomessageFeedPluginSettings");
}
private async Task<string> GetNextFeedItem(Server server) private async Task<string> GetNextFeedItem(Server server)
{ {
var items = new List<string>(); var items = new List<string>();
using (var reader = XmlReader.Create(_configuration.FeedUrl, new XmlReaderSettings() { Async = true })) using (var reader = XmlReader.Create(_configurationHandler.Configuration().FeedUrl, new XmlReaderSettings() { Async = true }))
{ {
var feedReader = new RssFeedReader(reader); var feedReader = new RssFeedReader(reader);
@ -43,7 +48,7 @@ namespace AutomessageFeed
} }
} }
if (_currentFeedItem < items.Count && (_configuration.MaxFeedItems == 0 || _currentFeedItem < _configuration.MaxFeedItems)) if (_currentFeedItem < items.Count && (_configurationHandler.Configuration().MaxFeedItems == 0 || _currentFeedItem < _configurationHandler.Configuration().MaxFeedItems))
{ {
_currentFeedItem++; _currentFeedItem++;
return items[_currentFeedItem - 1]; return items[_currentFeedItem - 1];
@ -60,15 +65,12 @@ namespace AutomessageFeed
public async Task OnLoadAsync(IManager manager) public async Task OnLoadAsync(IManager manager)
{ {
var cfg = new BaseConfigurationHandler<Configuration>("AutomessageFeedPluginSettings"); if (_configurationHandler.Configuration() == null)
if (cfg.Configuration() == null)
{ {
cfg.Set((Configuration)new Configuration().Generate()); _configurationHandler.Set((Configuration)new Configuration().Generate());
await cfg.Save(); await _configurationHandler.Save();
} }
_configuration = cfg.Configuration();
manager.GetMessageTokens().Add(new MessageToken("FEED", GetNextFeedItem)); manager.GetMessageTokens().Add(new MessageToken("FEED", GetNextFeedItem));
} }

View File

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

View File

@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Mvc; using LiveRadar.Configuration;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json; using Newtonsoft.Json;
using SharedLibraryCore; using SharedLibraryCore;
using SharedLibraryCore.Dtos; using SharedLibraryCore.Dtos;
@ -16,10 +17,12 @@ namespace LiveRadar.Web.Controllers
}; };
private readonly IManager _manager; private readonly IManager _manager;
private readonly LiveRadarConfiguration _config;
public RadarController(IManager manager) : base(manager) public RadarController(IManager manager, IConfigurationHandlerFactory configurationHandlerFactory) : base(manager)
{ {
_manager = manager; _manager = manager;
_config = configurationHandlerFactory.GetConfigurationHandler<LiveRadarConfiguration>("LiveRadarConfiguration").Configuration() ?? new LiveRadarConfiguration();
} }
[HttpGet] [HttpGet]
@ -45,7 +48,7 @@ namespace LiveRadar.Web.Controllers
public IActionResult Map(long? serverId = null) public IActionResult Map(long? serverId = null)
{ {
var server = serverId == null ? _manager.GetServers().FirstOrDefault() : _manager.GetServers().FirstOrDefault(_server => _server.EndPoint == serverId); var server = serverId == null ? _manager.GetServers().FirstOrDefault() : _manager.GetServers().FirstOrDefault(_server => _server.EndPoint == serverId);
var map = Plugin.Config.Configuration().Maps.FirstOrDefault(_map => _map.Name == server.CurrentMap.Name); var map = _config.Maps.FirstOrDefault(_map => _map.Name == server.CurrentMap.Name);
if (map != null) if (map != null)
{ {

View File

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

View File

@ -16,7 +16,12 @@ namespace LiveRadar
public string Author => "RaidMax"; public string Author => "RaidMax";
internal static BaseConfigurationHandler<LiveRadarConfiguration> Config; private readonly IConfigurationHandler<LiveRadarConfiguration> _configurationHandler;
public Plugin(IConfigurationHandlerFactory configurationHandlerFactory)
{
_configurationHandler = configurationHandlerFactory.GetConfigurationHandler<LiveRadarConfiguration>("LiveRadarConfiguration");
}
public Task OnEventAsync(GameEvent E, Server S) public Task OnEventAsync(GameEvent E, Server S)
{ {
@ -36,7 +41,7 @@ namespace LiveRadar
} }
} }
catch(Exception e) catch (Exception e)
{ {
S.Logger.WriteWarning($"Could not parse live radar output: {e.Data}"); S.Logger.WriteWarning($"Could not parse live radar output: {e.Data}");
S.Logger.WriteDebug(e.GetExceptionInfo()); S.Logger.WriteDebug(e.GetExceptionInfo());
@ -49,12 +54,10 @@ namespace LiveRadar
public async Task OnLoadAsync(IManager manager) public async Task OnLoadAsync(IManager manager)
{ {
// load custom configuration if (_configurationHandler.Configuration() == null)
Config = new BaseConfigurationHandler<LiveRadarConfiguration>("LiveRadarConfiguration");
if (Config.Configuration() == null)
{ {
Config.Set((LiveRadarConfiguration)new LiveRadarConfiguration().Generate()); _configurationHandler.Set((LiveRadarConfiguration)new LiveRadarConfiguration().Generate());
await Config.Save(); await _configurationHandler.Save();
} }
manager.GetPageList().Pages.Add(Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_RADAR_TITLE"], "/Radar/All"); manager.GetPageList().Pages.Add(Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_RADAR_TITLE"], "/Radar/All");

View File

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

View File

@ -4,7 +4,6 @@ using System.Threading.Tasks;
using IW4MAdmin.Plugins.Login.Commands; using IW4MAdmin.Plugins.Login.Commands;
using SharedLibraryCore; using SharedLibraryCore;
using SharedLibraryCore.Commands; using SharedLibraryCore.Commands;
using SharedLibraryCore.Configuration;
using SharedLibraryCore.Database.Models; using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Exceptions; using SharedLibraryCore.Exceptions;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
@ -20,11 +19,16 @@ namespace IW4MAdmin.Plugins.Login
public string Author => "RaidMax"; public string Author => "RaidMax";
public static ConcurrentDictionary<int, bool> AuthorizedClients { get; private set; } public static ConcurrentDictionary<int, bool> AuthorizedClients { get; private set; }
private Configuration Config; private readonly IConfigurationHandler<Configuration> _configHandler;
public Plugin(IConfigurationHandlerFactory configurationHandlerFactory)
{
_configHandler = configurationHandlerFactory.GetConfigurationHandler<Configuration>("LoginPluginSettings");
}
public Task OnEventAsync(GameEvent E, Server S) public Task OnEventAsync(GameEvent E, Server S)
{ {
if (E.IsRemote || Config.RequirePrivilegedClientLogin == false) if (E.IsRemote || _configHandler.Configuration().RequirePrivilegedClientLogin == false)
return Task.CompletedTask; return Task.CompletedTask;
if (E.Type == GameEvent.EventType.Connect) if (E.Type == GameEvent.EventType.Connect)
@ -72,14 +76,11 @@ namespace IW4MAdmin.Plugins.Login
{ {
AuthorizedClients = new ConcurrentDictionary<int, bool>(); AuthorizedClients = new ConcurrentDictionary<int, bool>();
var cfg = new BaseConfigurationHandler<Configuration>("LoginPluginSettings"); if (_configHandler.Configuration() == null)
if (cfg.Configuration() == null)
{ {
cfg.Set((Configuration)new Configuration().Generate()); _configHandler.Set((Configuration)new Configuration().Generate());
await cfg.Save(); await _configHandler.Save();
} }
Config = cfg.Configuration();
} }
public Task OnTickAsync(Server S) => Task.CompletedTask; public Task OnTickAsync(Server S) => Task.CompletedTask;

View File

@ -17,18 +17,23 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment
public string Author => "RaidMax"; public string Author => "RaidMax";
BaseConfigurationHandler<Configuration> Settings; private readonly IConfigurationHandler<Configuration> _configHandler;
public Plugin(IConfigurationHandlerFactory configurationHandlerFactory)
{
_configHandler = configurationHandlerFactory.GetConfigurationHandler<Configuration>("ProfanityDetermentSettings");
}
public Task OnEventAsync(GameEvent E, Server S) public Task OnEventAsync(GameEvent E, Server S)
{ {
if (!Settings.Configuration().EnableProfanityDeterment) if (!_configHandler.Configuration().EnableProfanityDeterment)
return Task.CompletedTask; return Task.CompletedTask;
if (E.Type == GameEvent.EventType.Connect) if (E.Type == GameEvent.EventType.Connect)
{ {
E.Origin.SetAdditionalProperty("_profanityInfringements", 0); E.Origin.SetAdditionalProperty("_profanityInfringements", 0);
var objectionalWords = Settings.Configuration().OffensiveWords; var objectionalWords = _configHandler.Configuration().OffensiveWords;
var matchedFilters = new List<string>(); var matchedFilters = new List<string>();
bool containsObjectionalWord = false; bool containsObjectionalWord = false;
@ -51,7 +56,7 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment
AutomatedOffense = $"{E.Origin.Name} - {string.Join(",", matchedFilters)}" AutomatedOffense = $"{E.Origin.Name} - {string.Join(",", matchedFilters)}"
} }
}; };
E.Origin.Kick(Settings.Configuration().ProfanityKickMessage, sender); E.Origin.Kick(_configHandler.Configuration().ProfanityKickMessage, sender);
}; };
} }
@ -62,7 +67,7 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment
if (E.Type == GameEvent.EventType.Say) if (E.Type == GameEvent.EventType.Say)
{ {
var objectionalWords = Settings.Configuration().OffensiveWords; var objectionalWords = _configHandler.Configuration().OffensiveWords;
bool containsObjectionalWord = false; bool containsObjectionalWord = false;
var matchedFilters = new List<string>(); var matchedFilters = new List<string>();
@ -88,15 +93,15 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment
} }
}; };
if (profanityInfringments >= Settings.Configuration().KickAfterInfringementCount) if (profanityInfringments >= _configHandler.Configuration().KickAfterInfringementCount)
{ {
E.Origin.Kick(Settings.Configuration().ProfanityKickMessage, sender); E.Origin.Kick(_configHandler.Configuration().ProfanityKickMessage, sender);
} }
else if (profanityInfringments < Settings.Configuration().KickAfterInfringementCount) else if (profanityInfringments < _configHandler.Configuration().KickAfterInfringementCount)
{ {
E.Origin.SetAdditionalProperty("_profanityInfringements", profanityInfringments + 1); E.Origin.SetAdditionalProperty("_profanityInfringements", profanityInfringments + 1);
E.Origin.Warn(Settings.Configuration().ProfanityWarningMessage, sender); E.Origin.Warn(_configHandler.Configuration().ProfanityWarningMessage, sender);
} }
} }
} }
@ -105,12 +110,10 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment
public async Task OnLoadAsync(IManager manager) public async Task OnLoadAsync(IManager manager)
{ {
// load custom configuration if (_configHandler.Configuration() == null)
Settings = new BaseConfigurationHandler<Configuration>("ProfanityDetermentSettings");
if (Settings.Configuration() == null)
{ {
Settings.Set((Configuration)new Configuration().Generate()); _configHandler.Set((Configuration)new Configuration().Generate());
await Settings.Save(); await _configHandler.Save();
} }
} }

View File

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

View File

@ -17,7 +17,7 @@ namespace IW4MAdmin.Plugins.Stats.Config
public IDictionary<DetectionType, DistributionConfiguration> DetectionDistributions { get; set; } public IDictionary<DetectionType, DistributionConfiguration> DetectionDistributions { get; set; }
public IDictionary<long, DetectionType[]> ServerDetectionTypes { get; set; } public IDictionary<long, DetectionType[]> ServerDetectionTypes { get; set; }
public string Name() => "Stats"; public string Name() => "StatsPluginSettings";
public IBaseConfiguration Generate() public IBaseConfiguration Generate()
{ {
EnableAntiCheat = Utilities.PromptBool(Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_STATS_SETUP_ENABLEAC"]); EnableAntiCheat = Utilities.PromptBool(Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_STATS_SETUP_ENABLEAC"]);

View File

@ -21,8 +21,8 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
var killstreakMessage = Plugin.Config.Configuration().KillstreakMessages; var killstreakMessage = Plugin.Config.Configuration().KillstreakMessages;
var deathstreakMessage = Plugin.Config.Configuration().DeathstreakMessages; var deathstreakMessage = Plugin.Config.Configuration().DeathstreakMessages;
string message = killstreakMessage.FirstOrDefault(m => m.Count == killStreak)?.Message; string message = killstreakMessage?.FirstOrDefault(m => m.Count == killStreak)?.Message;
message = message ?? deathstreakMessage.FirstOrDefault(m => m.Count == deathStreak)?.Message; message = message ?? deathstreakMessage?.FirstOrDefault(m => m.Count == deathStreak)?.Message;
return message ?? ""; return message ?? "";
} }
} }

View File

@ -3,7 +3,6 @@ using IW4MAdmin.Plugins.Stats.Helpers;
using IW4MAdmin.Plugins.Stats.Models; using IW4MAdmin.Plugins.Stats.Models;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using SharedLibraryCore; using SharedLibraryCore;
using SharedLibraryCore.Configuration;
using SharedLibraryCore.Database; using SharedLibraryCore.Database;
using SharedLibraryCore.Database.Models; using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Dtos; using SharedLibraryCore.Dtos;
@ -13,7 +12,6 @@ using SharedLibraryCore.Services;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace IW4MAdmin.Plugins.Stats namespace IW4MAdmin.Plugins.Stats
@ -28,12 +26,17 @@ 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 BaseConfigurationHandler<StatsConfiguration> Config { get; private set; } public static IConfigurationHandler<StatsConfiguration> Config { get; private set; }
#if DEBUG #if DEBUG
int scriptDamageCount; int scriptDamageCount;
int scriptKillCount; int scriptKillCount;
#endif #endif
public Plugin(IConfigurationHandlerFactory configurationHandlerFactory)
{
Config = configurationHandlerFactory.GetConfigurationHandler<StatsConfiguration>("StatsPluginSettings");
}
public async Task OnEventAsync(GameEvent E, Server S) public async Task OnEventAsync(GameEvent E, Server S)
{ {
switch (E.Type) switch (E.Type)
@ -53,16 +56,16 @@ namespace IW4MAdmin.Plugins.Stats
if (!string.IsNullOrEmpty(E.Data) && if (!string.IsNullOrEmpty(E.Data) &&
E.Origin.ClientId > 1) E.Origin.ClientId > 1)
{ {
await Manager.AddMessageAsync(E.Origin.ClientId, StatManager.GetIdForServer(E.Owner), E.Data); await Manager.AddMessageAsync(E.Origin.ClientId, StatManager.GetIdForServer(S), E.Data);
} }
break; break;
case GameEvent.EventType.MapChange: case GameEvent.EventType.MapChange:
Manager.SetTeamBased(StatManager.GetIdForServer(E.Owner), E.Owner.Gametype != "dm"); Manager.SetTeamBased(StatManager.GetIdForServer(S), S.Gametype != "dm");
Manager.ResetKillstreaks(E.Owner); Manager.ResetKillstreaks(S);
await Manager.Sync(E.Owner); await Manager.Sync(S);
break; break;
case GameEvent.EventType.MapEnd: case GameEvent.EventType.MapEnd:
await Manager.Sync(E.Owner); await Manager.Sync(S);
break; break;
case GameEvent.EventType.JoinTeam: case GameEvent.EventType.JoinTeam:
break; break;
@ -82,7 +85,7 @@ namespace IW4MAdmin.Plugins.Stats
break; break;
case GameEvent.EventType.ScriptKill: case GameEvent.EventType.ScriptKill:
string[] killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0]; string[] killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0];
if ((E.Owner.CustomCallback || ShouldOverrideAnticheatSetting(E.Owner)) && killInfo.Length >= 18 && !ShouldIgnoreEvent(E.Origin, E.Target)) if ((S.CustomCallback || ShouldOverrideAnticheatSetting(S)) && killInfo.Length >= 18 && !ShouldIgnoreEvent(E.Origin, E.Target))
{ {
// this treats "world" damage as self damage // this treats "world" damage as self damage
if (IsWorldDamage(E.Origin)) if (IsWorldDamage(E.Origin))
@ -95,7 +98,7 @@ namespace IW4MAdmin.Plugins.Stats
S.Logger.WriteInfo($"Start ScriptKill {scriptKillCount}"); S.Logger.WriteInfo($"Start ScriptKill {scriptKillCount}");
#endif #endif
await Manager.AddScriptHit(false, E.Time, E.Origin, E.Target, StatManager.GetIdForServer(E.Owner), 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 #if DEBUG
@ -105,7 +108,7 @@ namespace IW4MAdmin.Plugins.Stats
else else
{ {
E.Owner.Logger.WriteDebug("Skipping script kill as it is ignored or data in customcallbacks is outdated/missing"); S.Logger.WriteDebug("Skipping script kill as it is ignored or data in customcallbacks is outdated/missing");
} }
break; break;
case GameEvent.EventType.Kill: case GameEvent.EventType.Kill:
@ -129,12 +132,12 @@ namespace IW4MAdmin.Plugins.Stats
E.Origin = E.Target; E.Origin = E.Target;
} }
Manager.AddDamageEvent(E.Data, E.Origin.ClientId, E.Target.ClientId, StatManager.GetIdForServer(E.Owner)); Manager.AddDamageEvent(E.Data, E.Origin.ClientId, E.Target.ClientId, StatManager.GetIdForServer(S));
} }
break; break;
case GameEvent.EventType.ScriptDamage: case GameEvent.EventType.ScriptDamage:
killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0]; killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0];
if ((E.Owner.CustomCallback || ShouldOverrideAnticheatSetting(E.Owner)) && killInfo.Length >= 18 && !ShouldIgnoreEvent(E.Origin, E.Target)) if ((S.CustomCallback || ShouldOverrideAnticheatSetting(S)) && killInfo.Length >= 18 && !ShouldIgnoreEvent(E.Origin, E.Target))
{ {
// this treats "world" damage as self damage // this treats "world" damage as self damage
if (IsWorldDamage(E.Origin)) if (IsWorldDamage(E.Origin))
@ -147,7 +150,7 @@ namespace IW4MAdmin.Plugins.Stats
S.Logger.WriteInfo($"Start ScriptDamage {scriptDamageCount}"); S.Logger.WriteInfo($"Start ScriptDamage {scriptDamageCount}");
#endif #endif
await Manager.AddScriptHit(true, E.Time, E.Origin, E.Target, StatManager.GetIdForServer(E.Owner), 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 #if DEBUG
@ -157,7 +160,7 @@ namespace IW4MAdmin.Plugins.Stats
else else
{ {
E.Owner.Logger.WriteDebug("Skipping script damage as it is ignored or data in customcallbacks is outdated/missing"); S.Logger.WriteDebug("Skipping script damage as it is ignored or data in customcallbacks is outdated/missing");
} }
break; break;
} }
@ -166,7 +169,6 @@ namespace IW4MAdmin.Plugins.Stats
public async Task OnLoadAsync(IManager manager) public async Task OnLoadAsync(IManager manager)
{ {
// load custom configuration // load custom configuration
Config = new BaseConfigurationHandler<StatsConfiguration>("StatsPluginSettings");
if (Config.Configuration() == null) if (Config.Configuration() == null)
{ {
Config.Set((StatsConfiguration)new StatsConfiguration().Generate()); Config.Set((StatsConfiguration)new StatsConfiguration().Generate());
@ -518,7 +520,7 @@ namespace IW4MAdmin.Plugins.Stats
/// <returns></returns> /// <returns></returns>
private bool ShouldIgnoreEvent(EFClient origin, EFClient target) private bool ShouldIgnoreEvent(EFClient origin, EFClient target)
{ {
return ((origin?.NetworkId == 1 && target?.NetworkId == 1) || (origin?.ClientId <= 1 && target?.ClientId <= 1)); return ((origin?.NetworkId == 1 && target?.NetworkId == 1));
} }
/// <summary> /// <summary>

View File

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

View File

@ -1,4 +1,5 @@
using IW4MAdmin.Application; using IW4MAdmin.Application;
using IW4MAdmin.Application.Misc;
using SharedLibraryCore.Configuration; using SharedLibraryCore.Configuration;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using System; using System;

View File

@ -1,4 +1,5 @@
using SharedLibraryCore.Database.Models; using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Interfaces;
using SharedLibraryCore.RCon; using SharedLibraryCore.RCon;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -14,7 +15,7 @@ namespace Tests
public override string Version => "test"; public override string Version => "test";
public override async Task<(List<EFClient>, string)> GetStatusAsync(Connection connection) public override async Task<(List<EFClient>, string)> GetStatusAsync(IRConConnection connection)
{ {
var clientList = new List<EFClient>(); var clientList = new List<EFClient>();

View File

@ -14,7 +14,7 @@
<RunPostBuildEvent>Always</RunPostBuildEvent> <RunPostBuildEvent>Always</RunPostBuildEvent>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2.2.6" PrivateAssets="All" /> <PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2.2.7" PrivateAssets="All" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -64,16 +64,19 @@ namespace IW4MAdmin.Plugins.Welcome
public string Name => "Welcome Plugin"; public string Name => "Welcome Plugin";
private BaseConfigurationHandler<WelcomeConfiguration> Config; private readonly IConfigurationHandler<WelcomeConfiguration> _configHandler;
public Plugin(IConfigurationHandlerFactory configurationHandlerFactory)
{
_configHandler = configurationHandlerFactory.GetConfigurationHandler<WelcomeConfiguration>("WelcomePluginSettings");
}
public async Task OnLoadAsync(IManager manager) public async Task OnLoadAsync(IManager manager)
{ {
// load custom configuration if (_configHandler.Configuration() == null)
Config = new BaseConfigurationHandler<WelcomeConfiguration>("WelcomePluginSettings");
if (Config.Configuration() == null)
{ {
Config.Set((WelcomeConfiguration)new WelcomeConfiguration().Generate()); _configHandler.Set((WelcomeConfiguration)new WelcomeConfiguration().Generate());
await Config.Save(); await _configHandler.Save();
} }
} }
@ -87,9 +90,9 @@ namespace IW4MAdmin.Plugins.Welcome
{ {
EFClient newPlayer = E.Origin; EFClient newPlayer = E.Origin;
if (newPlayer.Level >= Permission.Trusted && !E.Origin.Masked) if (newPlayer.Level >= Permission.Trusted && !E.Origin.Masked)
E.Owner.Broadcast(await ProcessAnnouncement(Config.Configuration().PrivilegedAnnouncementMessage, newPlayer)); E.Owner.Broadcast(await ProcessAnnouncement(_configHandler.Configuration().PrivilegedAnnouncementMessage, newPlayer));
newPlayer.Tell(await ProcessAnnouncement(Config.Configuration().UserWelcomeMessage, newPlayer)); newPlayer.Tell(await ProcessAnnouncement(_configHandler.Configuration().UserWelcomeMessage, newPlayer));
if (newPlayer.Level == Permission.Flagged) if (newPlayer.Level == Permission.Flagged)
{ {
@ -107,7 +110,7 @@ namespace IW4MAdmin.Plugins.Welcome
E.Owner.ToAdmins($"^1NOTICE: ^7Flagged player ^5{newPlayer.Name} ^7({penaltyReason}) has joined!"); E.Owner.ToAdmins($"^1NOTICE: ^7Flagged player ^5{newPlayer.Name} ^7({penaltyReason}) has joined!");
} }
else else
E.Owner.Broadcast(await ProcessAnnouncement(Config.Configuration().UserAnnouncementMessage, newPlayer)); E.Owner.Broadcast(await ProcessAnnouncement(_configHandler.Configuration().UserAnnouncementMessage, newPlayer));
} }
} }

View File

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

View File

@ -500,18 +500,18 @@ namespace SharedLibraryCore.Commands
public override Task ExecuteAsync(GameEvent E) public override Task ExecuteAsync(GameEvent E)
{ {
String cmd = E.Data.Trim(); string cmd = E.Data.Trim();
if (cmd.Length > 2) if (cmd.Length > 2)
{ {
bool found = false; bool found = false;
foreach (Command C in E.Owner.Manager.GetCommands()) foreach (var command in E.Owner.Manager.GetCommands())
{ {
if (C.Name == cmd.ToLower() || if (command.Name == cmd.ToLower() ||
C.Alias == cmd.ToLower()) command.Alias == cmd.ToLower())
{ {
E.Origin.Tell($"[^3{C.Name}^7] {C.Description}"); E.Origin.Tell($"[^3{command.Name}^7] {command.Description}");
E.Origin.Tell(C.Syntax); E.Origin.Tell(command.Syntax);
found = true; found = true;
} }
} }

View File

@ -56,11 +56,11 @@ namespace SharedLibraryCore.Configuration
{ {
var loc = Utilities.CurrentLocalization.LocalizationIndex; var loc = Utilities.CurrentLocalization.LocalizationIndex;
var parserVersions = rconParsers.Select(_parser => _parser.Name).ToArray(); var parserVersions = rconParsers.Select(_parser => _parser.Name).ToArray();
var selection = Utilities.PromptSelection($"{loc["SETUP_SERVER_RCON_PARSER_VERSION"]} ({IPAddress}:{Port})", $"{loc["SETUP_PROMPT_DEFAULT"]} (Call of Duty)", null, parserVersions); var selection = Utilities.PromptSelection($"{loc["SETUP_SERVER_RCON_PARSER_VERSION"]} ({IPAddress}:{Port})", parserVersions[0], null, parserVersions);
if (selection.Item1 >= 0) if (selection.Item1 >= 0)
{ {
RConParserVersion = rconParsers.First(_parser => _parser.Name == selection.Item2).Version; RConParserVersion = rconParsers.FirstOrDefault(_parser => _parser.Name == selection.Item2)?.Version;
if (selection.Item1 > 0 && !rconParsers[selection.Item1 - 1].CanGenerateLogPath) if (selection.Item1 > 0 && !rconParsers[selection.Item1 - 1].CanGenerateLogPath)
{ {
@ -70,11 +70,11 @@ namespace SharedLibraryCore.Configuration
} }
parserVersions = eventParsers.Select(_parser => _parser.Name).ToArray(); parserVersions = eventParsers.Select(_parser => _parser.Name).ToArray();
selection = Utilities.PromptSelection($"{loc["SETUP_SERVER_EVENT_PARSER_VERSION"]} ({IPAddress}:{Port})", $"{loc["SETUP_PROMPT_DEFAULT"]} (Call of Duty)", null, parserVersions); selection = Utilities.PromptSelection($"{loc["SETUP_SERVER_EVENT_PARSER_VERSION"]} ({IPAddress}:{Port})", parserVersions[0], null, parserVersions);
if (selection.Item1 >= 0) if (selection.Item1 >= 0)
{ {
EventParserVersion = eventParsers.First(_parser => _parser.Name == selection.Item2).Version; EventParserVersion = eventParsers.FirstOrDefault(_parser => _parser.Name == selection.Item2)?.Version;
} }
} }

View File

@ -26,12 +26,6 @@ namespace SharedLibraryCore.Configuration.Validation
RuleForEach(_server => _server.AutoMessages) RuleForEach(_server => _server.AutoMessages)
.NotEmpty(); .NotEmpty();
RuleFor(_server => _server.RConParserVersion)
.NotEmpty();
RuleFor(_server => _server.EventParserVersion)
.NotEmpty();
RuleFor(_server => _server.ReservedSlotNumber) RuleFor(_server => _server.ReservedSlotNumber)
.InclusiveBetween(0, 32); .InclusiveBetween(0, 32);
} }

View File

@ -0,0 +1,14 @@
namespace SharedLibraryCore.Interfaces
{
/// <summary>
/// defines the capabilities for providing a base path
/// unused as of now, will be used later during refactorying
/// </summary>
public interface IBasePathProvider
{
/// <summary>
/// working directory of IW4MAdmin
/// </summary>
string BasePath { get; }
}
}

View File

@ -0,0 +1,17 @@
namespace SharedLibraryCore.Interfaces
{
/// <summary>
/// defines the capabilities of the configuration handler factory
/// used to generate new instance of configuration handlers
/// </summary>
public interface IConfigurationHandlerFactory
{
/// <summary>
/// generates a new configuration handler
/// </summary>
/// <typeparam name="T">base configuration type</typeparam>
/// <param name="name">file name of configuration</param>
/// <returns>new configuration handler instance</returns>
IConfigurationHandler<T> GetConfigurationHandler<T>(string name) where T : IBaseConfiguration;
}
}

View File

@ -0,0 +1,18 @@
using SharedLibraryCore.Configuration;
namespace SharedLibraryCore.Interfaces
{
/// <summary>
/// defines the capabilities of game server instance factory
/// </summary>
public interface IGameServerInstanceFactory
{
/// <summary>
/// creates the instance of a game server
/// </summary>
/// <param name="config">server configuration</param>
/// <param name="manager">application manager</param>
/// <returns></returns>
Server CreateServer(ServerConfiguration config, IManager manager);
}
}

View File

@ -2,7 +2,6 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using SharedLibraryCore.Services; using SharedLibraryCore.Services;
using SharedLibraryCore.Configuration; using SharedLibraryCore.Configuration;
using System.Reflection;
using SharedLibraryCore.Database.Models; using SharedLibraryCore.Database.Models;
using System.Threading; using System.Threading;
using System.Collections; using System.Collections;
@ -29,7 +28,10 @@ namespace SharedLibraryCore.Interfaces
/// </summary> /// </summary>
/// <returns>EventHandler for the manager</returns> /// <returns>EventHandler for the manager</returns>
IEventHandler GetEventHandler(); IEventHandler GetEventHandler();
IList<Assembly> GetPluginAssemblies(); /// <summary>
/// enumerates the registered plugin instances
/// </summary>
IEnumerable<IPlugin> Plugins { get; }
/// <summary> /// <summary>
/// provides a page list to add and remove from /// provides a page list to add and remove from
/// </summary> /// </summary>

View File

@ -1,32 +1,23 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection;
namespace SharedLibraryCore.Interfaces namespace SharedLibraryCore.Interfaces
{ {
/// <summary> /// <summary>
/// Defines the capabilities of the plugin importer /// defines the capabilities of the plugin importer
/// </summary> /// </summary>
public interface IPluginImporter public interface IPluginImporter
{ {
/// <summary> /// <summary>
/// Command types that are defined in plugin assemblies /// discovers C# assembly plugin and command types
/// </summary> /// </summary>
IList<Type> CommandTypes { get; } /// <returns>tuple of IPlugin implementation type definitions, and IManagerCommand type definitions</returns>
(IEnumerable<Type>, IEnumerable<Type>) DiscoverAssemblyPluginImplementations();
/// <summary> /// <summary>
/// The loaded plugins from plugin assemblies /// discovers the script plugins
/// </summary> /// </summary>
IList<IPlugin> ActivePlugins { get; } /// <returns>initialized script plugin collection</returns>
IEnumerable<IPlugin> DiscoverScriptPlugins();
/// <summary>
/// Assemblies that contain plugins
/// </summary>
IList<Assembly> PluginAssemblies { get; }
/// <summary>
/// All assemblies in the plugin folder
/// </summary>
IList<Assembly> Assemblies { get; }
} }
} }

View File

@ -0,0 +1,25 @@
using SharedLibraryCore.RCon;
using System.Threading.Tasks;
namespace SharedLibraryCore.Interfaces
{
/// <summary>
/// defines the capabilities of an RCon connection
/// </summary>
public interface IRConConnection
{
/// <summary>
/// sends a query with the instance of the rcon connection
/// </summary>
/// <param name="type">type of RCon query to perform</param>
/// <param name="parameters">optional parameter list</param>
/// <returns></returns>
Task<string[]> SendQueryAsync(StaticHelpers.QueryType type, string parameters = "");
/// <summary>
/// sets the rcon parser configuration
/// </summary>
/// <param name="config">parser config</param>
void SetConfiguration(IRConParserConfiguration config);
}
}

View File

@ -0,0 +1,17 @@
namespace SharedLibraryCore.Interfaces
{
/// <summary>
/// defines the capabilities of an RCon connection factory
/// </summary>
public interface IRConConnectionFactory
{
/// <summary>
/// creates an rcon connection instance
/// </summary>
/// <param name="ipAddress">ip address of the server</param>
/// <param name="port">port of the server</param>
/// <param name="password"> password of the server</param>
/// <returns>instance of rcon connection</returns>
IRConConnection CreateConnection(string ipAddress, int port, string password);
}
}

View File

@ -1,7 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using SharedLibraryCore.Database.Models; using SharedLibraryCore.Database.Models;
using SharedLibraryCore.RCon;
using static SharedLibraryCore.Server; using static SharedLibraryCore.Server;
namespace SharedLibraryCore.Interfaces namespace SharedLibraryCore.Interfaces
@ -15,7 +14,7 @@ namespace SharedLibraryCore.Interfaces
/// <param name="connection">RCon connection to retrieve with</param> /// <param name="connection">RCon connection to retrieve with</param>
/// <param name="dvarName">name of DVAR</param> /// <param name="dvarName">name of DVAR</param>
/// <returns></returns> /// <returns></returns>
Task<Dvar<T>> GetDvarAsync<T>(Connection connection, string dvarName); Task<Dvar<T>> GetDvarAsync<T>(IRConConnection connection, string dvarName);
/// <summary> /// <summary>
/// set value of DVAR by name /// set value of DVAR by name
@ -24,7 +23,7 @@ namespace SharedLibraryCore.Interfaces
/// <param name="dvarName">name of DVAR to set</param> /// <param name="dvarName">name of DVAR to set</param>
/// <param name="dvarValue">value to set DVAR to</param> /// <param name="dvarValue">value to set DVAR to</param>
/// <returns></returns> /// <returns></returns>
Task<bool> SetDvarAsync(Connection connection, string dvarName, object dvarValue); Task<bool> SetDvarAsync(IRConConnection connection, string dvarName, object dvarValue);
/// <summary> /// <summary>
/// executes a console command on the server /// executes a console command on the server
@ -32,14 +31,14 @@ namespace SharedLibraryCore.Interfaces
/// <param name="connection">RCon connection to use</param> /// <param name="connection">RCon connection to use</param>
/// <param name="command">console command to execute</param> /// <param name="command">console command to execute</param>
/// <returns></returns> /// <returns></returns>
Task<string[]> ExecuteCommandAsync(Connection connection, string command); Task<string[]> ExecuteCommandAsync(IRConConnection connection, string command);
/// <summary> /// <summary>
/// get the list of connected clients from status response /// get the list of connected clients from status response
/// </summary> /// </summary>
/// <param name="connection">RCon connection to use</param> /// <param name="connection">RCon connection to use</param>
/// <returns>list of clients, and current map</returns> /// <returns>list of clients, and current map</returns>
Task<(List<EFClient>, string)> GetStatusAsync(Connection connection); Task<(List<EFClient>, string)> GetStatusAsync(IRConConnection connection);
/// <summary> /// <summary>
/// stores the RCon configuration /// stores the RCon configuration

View File

@ -1,8 +1,4 @@
using System; namespace SharedLibraryCore.RCon
using System.Collections.Generic;
using System.Text;
namespace SharedLibraryCore.RCon
{ {
public class CommandPrefix public class CommandPrefix
{ {

View File

@ -28,7 +28,7 @@ namespace SharedLibraryCore
T7 = 8 T7 = 8
} }
public Server(IManager mgr, ServerConfiguration config) public Server(IManager mgr, IRConConnectionFactory rconConnectionFactory, ServerConfiguration config)
{ {
Password = config.Password; Password = config.Password;
IP = config.IPAddress; IP = config.IPAddress;
@ -37,8 +37,7 @@ namespace SharedLibraryCore
Logger = Manager.GetLogger(this.EndPoint); Logger = Manager.GetLogger(this.EndPoint);
Logger.WriteInfo(this.ToString()); Logger.WriteInfo(this.ToString());
ServerConfig = config; ServerConfig = config;
RemoteConnection = new RCon.Connection(IP, Port, Password, Logger, null); RemoteConnection = rconConnectionFactory.CreateConnection(IP, Port, Password);
EventProcessing = new SemaphoreSlim(1, 1); EventProcessing = new SemaphoreSlim(1, 1);
Clients = new List<EFClient>(new EFClient[18]); Clients = new List<EFClient>(new EFClient[18]);
Reports = new List<Report>(); Reports = new List<Report>();
@ -283,7 +282,7 @@ namespace SharedLibraryCore
public IManager Manager { get; protected set; } public IManager Manager { get; protected set; }
public ILogger Logger { get; private set; } public ILogger Logger { get; private set; }
public ServerConfiguration ServerConfig { get; private set; } public ServerConfiguration ServerConfig { get; private set; }
public List<Map> Maps { get; protected set; } public List<Map> Maps { get; protected set; } = new List<Map>();
public List<Report> Reports { get; set; } public List<Report> Reports { get; set; }
public List<ChatInfo> ChatHistory { get; protected set; } public List<ChatInfo> ChatHistory { get; protected set; }
public Queue<PlayerHistory> ClientHistory { get; private set; } public Queue<PlayerHistory> ClientHistory { get; private set; }
@ -307,7 +306,7 @@ namespace SharedLibraryCore
public bool Throttled { get; protected set; } public bool Throttled { get; protected set; }
public bool CustomCallback { get; protected set; } public bool CustomCallback { get; protected set; }
public string WorkingDirectory { get; protected set; } public string WorkingDirectory { get; protected set; }
public RCon.Connection RemoteConnection { get; protected set; } public IRConConnection RemoteConnection { get; protected set; }
public IRConParser RconParser { get; protected set; } public IRConParser RconParser { get; protected set; }
public IEventParser EventParser { get; set; } public IEventParser EventParser { get; set; }
public string LogPath { get; protected set; } public string LogPath { get; protected set; }

View File

@ -6,7 +6,7 @@
<ApplicationIcon /> <ApplicationIcon />
<StartupObject /> <StartupObject />
<PackageId>RaidMax.IW4MAdmin.SharedLibraryCore</PackageId> <PackageId>RaidMax.IW4MAdmin.SharedLibraryCore</PackageId>
<Version>2.2.6</Version> <Version>2.2.7</Version>
<Authors>RaidMax</Authors> <Authors>RaidMax</Authors>
<Company>Forever None</Company> <Company>Forever None</Company>
<Configurations>Debug;Release;Prerelease</Configurations> <Configurations>Debug;Release;Prerelease</Configurations>
@ -20,8 +20,8 @@
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseExpression>MIT</PackageLicenseExpression>
<Description>Shared Library for IW4MAdmin</Description> <Description>Shared Library for IW4MAdmin</Description>
<AssemblyVersion>2.2.6.0</AssemblyVersion> <AssemblyVersion>2.2.7.0</AssemblyVersion>
<FileVersion>2.2.6.0</FileVersion> <FileVersion>2.2.7.0</FileVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Prerelease|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Prerelease|AnyCPU'">
@ -69,6 +69,10 @@
<PackageReference Include="SimpleCrypto.NetCore" Version="1.0.0" /> <PackageReference Include="SimpleCrypto.NetCore" Version="1.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(Configuration)'=='Debug'">
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="3.1.1" />
</ItemGroup>
<Target Name="PreBuild" BeforeTargets="PreBuildEvent"> <Target Name="PreBuild" BeforeTargets="PreBuildEvent">
<Exec Command="if not exist &quot;$(ProjectDir)..\BUILD&quot; (&#xD;&#xA;if $(ConfigurationName) == Debug (&#xD;&#xA;md &quot;$(ProjectDir)..\BUILD&quot;&#xD;&#xA;)&#xD;&#xA;)&#xD;&#xA;if not exist &quot;$(ProjectDir)..\BUILD\Plugins&quot; (&#xD;&#xA;if $(ConfigurationName) == Debug (&#xD;&#xA;md &quot;$(ProjectDir)..\BUILD\Plugins&quot;&#xD;&#xA;)&#xD;&#xA;)" /> <Exec Command="if not exist &quot;$(ProjectDir)..\BUILD&quot; (&#xD;&#xA;if $(ConfigurationName) == Debug (&#xD;&#xA;md &quot;$(ProjectDir)..\BUILD&quot;&#xD;&#xA;)&#xD;&#xA;)&#xD;&#xA;if not exist &quot;$(ProjectDir)..\BUILD\Plugins&quot; (&#xD;&#xA;if $(ConfigurationName) == Debug (&#xD;&#xA;md &quot;$(ProjectDir)..\BUILD\Plugins&quot;&#xD;&#xA;)&#xD;&#xA;)" />
</Target> </Target>

View File

@ -0,0 +1,34 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FakeItEasy" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.16.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Application\Application.csproj" />
<ProjectReference Include="..\..\Plugins\Stats\Stats.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="Files\T6Game.log">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Files\T6GameStats.log">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Files\T6MapRotation.log">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@ -0,0 +1,85 @@
using FakeItEasy;
using IW4MAdmin;
using IW4MAdmin.Application;
using IW4MAdmin.Application.EventParsers;
using NUnit.Framework;
using SharedLibraryCore.Interfaces;
using System;
using System.Diagnostics;
namespace ApplicationTests
{
[TestFixture]
public class ServerTests
{
ILogger logger;
[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]
public void GameTimeFalseQuitTest()
{
var mgr = A.Fake<IManager>();
var server = new IW4MServer(mgr,
new SharedLibraryCore.Configuration.ServerConfiguration() { IPAddress = "127.0.0.1", Port = 28960 },
A.Fake<ITranslationLookup>(), A.Fake<IRConConnectionFactory>());
var parser = new BaseEventParser();
parser.Configuration.GuidNumberStyle = System.Globalization.NumberStyles.Integer;
var log = System.IO.File.ReadAllLines("Files\\T6MapRotation.log");
foreach (string line in log)
{
var e = parser.GenerateGameEvent(line);
if (e.Origin != null)
{
e.Origin.CurrentServer = server;
}
server.ExecuteEvent(e).Wait();
}
}
[Test]
public void LogFileReplay()
{
var mgr = A.Fake<IManager>();
A.CallTo(() => mgr.GetLogger(A<long>.Ignored)).Returns(logger);
var server = new IW4MServer(mgr,
new SharedLibraryCore.Configuration.ServerConfiguration() { IPAddress = "127.0.0.1", Port = 28960 },
A.Fake<ITranslationLookup>(), A.Fake<IRConConnectionFactory>());
var parser = new BaseEventParser();
parser.Configuration.GuidNumberStyle = System.Globalization.NumberStyles.Integer;
var log = System.IO.File.ReadAllLines("Files\\T6Game.log");
long lastEventId = 0;
foreach (string line in log)
{
var e = parser.GenerateGameEvent(line);
server.Logger.WriteInfo($"{e.GameTime}");
if (e.Origin != null)
{
e.Origin.CurrentServer = server;
}
server.ExecuteEvent(e).Wait();
lastEventId = e.Id;
}
Assert.GreaterOrEqual(lastEventId, log.Length);
}
}
}

View File

@ -0,0 +1,117 @@
using NUnit.Framework;
using System;
using SharedLibraryCore.Interfaces;
using IW4MAdmin;
using FakeItEasy;
using IW4MAdmin.Application.EventParsers;
using System.Linq;
using IW4MAdmin.Plugins.Stats.Models;
using IW4MAdmin.Application.Helpers;
using IW4MAdmin.Plugins.Stats.Config;
using System.Collections.Generic;
using SharedLibraryCore.Database.Models;
namespace ApplicationTests
{
[TestFixture]
public class StatsTests
{
ILogger logger;
[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]
public void TestKDR()
{
var mgr = A.Fake<IManager>();
var handlerFactory = A.Fake<IConfigurationHandlerFactory>();
var config = A.Fake<IConfigurationHandler<StatsConfiguration>>();
var plugin = new IW4MAdmin.Plugins.Stats.Plugin(handlerFactory);
A.CallTo(() => config.Configuration())
.Returns(new StatsConfiguration()
{
EnableAntiCheat = true
});
A.CallTo(() => handlerFactory.GetConfigurationHandler<StatsConfiguration>(A<string>.Ignored))
.Returns(config);
A.CallTo(() => mgr.GetLogger(A<long>.Ignored))
.Returns(logger);
var server = new IW4MServer(mgr,
new SharedLibraryCore.Configuration.ServerConfiguration() { IPAddress = "127.0.0.1", Port = 28960 },
A.Fake<ITranslationLookup>(),
A.Fake<IRConConnectionFactory>());
var parser = new BaseEventParser();
parser.Configuration.GuidNumberStyle = System.Globalization.NumberStyles.Integer;
var log = System.IO.File.ReadAllLines("Files\\T6GameStats.log");
plugin.OnLoadAsync(mgr).Wait();
plugin.OnEventAsync(new SharedLibraryCore.GameEvent() { Type = SharedLibraryCore.GameEvent.EventType.Start, Owner = server }, server).Wait();
var clientList = new Dictionary<long, EFClient>();
foreach (string line in log)
{
var e = parser.GenerateGameEvent(line);
if (e.Origin != null)
{
//if (!clientList.ContainsKey(e.Origin.NetworkId))
//{
// clientList.Add(e.Origin.NetworkId, e.Origin);
//}
//else
//{
// e.Origin = clientList[e.Origin.NetworkId];
//}
e.Origin = server.GetClientsAsList().FirstOrDefault(_client => _client.NetworkId == e.Origin.NetworkId) ?? e.Origin;
e.Origin.CurrentServer = server;
}
if (e.Target != null)
{
//if (!clientList.ContainsKey(e.Target.NetworkId))
//{
// clientList.Add(e.Target.NetworkId, e.Target);
//}
//else
//{
// e.Target = clientList[e.Target.NetworkId];
//}
e.Target = server.GetClientsAsList().FirstOrDefault(_client => _client.NetworkId == e.Target.NetworkId) ?? e.Target;
e.Target.CurrentServer = server;
}
server.ExecuteEvent(e).Wait();
plugin.OnEventAsync(e, server).Wait();
}
var client = server.GetClientsAsList().First(_client => _client?.NetworkId == 2028755667);
var stats = client.GetAdditionalProperty<EFClientStatistics>("ClientStats");
}
class BasePathProvider : IBasePathProvider
{
public string BasePath => @"X:\IW4MAdmin\BUILD\Plugins";
}
}
}

View File

@ -11,11 +11,8 @@ namespace WebfrontCore.Controllers
{ {
public class HomeController : BaseController public class HomeController : BaseController
{ {
private readonly IPluginImporter _pluginImporter; public HomeController(IManager manager) : base(manager)
public HomeController(IManager manager, IPluginImporter importer) : base(manager)
{ {
_pluginImporter = importer;
} }
public async Task<IActionResult> Index() public async Task<IActionResult> Index()
@ -68,7 +65,7 @@ namespace WebfrontCore.Controllers
var pluginType = _cmd.GetType().Assembly.GetTypes().FirstOrDefault(_type => _type.Assembly != excludedAssembly && typeof(IPlugin).IsAssignableFrom(_type)); var pluginType = _cmd.GetType().Assembly.GetTypes().FirstOrDefault(_type => _type.Assembly != excludedAssembly && typeof(IPlugin).IsAssignableFrom(_type));
return pluginType == null ? return pluginType == null ?
Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_HELP_COMMAND_NATIVE"] : Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_HELP_COMMAND_NATIVE"] :
_pluginImporter.ActivePlugins.First(_plugin => _plugin.GetType() == pluginType).Name; // for now we're just returning the name of the plugin, maybe later we'll include more info Manager.Plugins.First(_plugin => _plugin.GetType() == pluginType).Name; // for now we're just returning the name of the plugin, maybe later we'll include more info
}) })
.Select(_grp => (_grp.Key, _grp.AsEnumerable())); .Select(_grp => (_grp.Key, _grp.AsEnumerable()));

View File

@ -7,9 +7,14 @@ using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using SharedLibraryCore;
using SharedLibraryCore.Database; using SharedLibraryCore.Database;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net; using System.Net;
using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using WebfrontCore.Middleware; using WebfrontCore.Middleware;
@ -32,20 +37,33 @@ namespace WebfrontCore
}); });
}); });
IEnumerable<Assembly> pluginAssemblies()
{
string pluginDir = $"{Utilities.OperatingDirectory}Plugins{Path.DirectorySeparatorChar}";
if (Directory.Exists(pluginDir))
{
var dllFileNames = Directory.GetFiles($"{Utilities.OperatingDirectory}Plugins{Path.DirectorySeparatorChar}", "*.dll");
return dllFileNames.Select(_file => Assembly.LoadFrom(_file));
}
return Enumerable.Empty<Assembly>();
}
// Add framework services. // Add framework services.
var mvcBuilder = services.AddMvc(_options => _options.SuppressAsyncSuffixInActionNames = false) var mvcBuilder = services.AddMvc(_options => _options.SuppressAsyncSuffixInActionNames = false)
.ConfigureApplicationPartManager(_ => .ConfigureApplicationPartManager(_partManager =>
{ {
foreach (var assembly in Program.Manager.GetPluginAssemblies()) foreach (var assembly in pluginAssemblies())
{ {
if (assembly.FullName.Contains("Views")) if (assembly.FullName.Contains("Views"))
{ {
_.ApplicationParts.Add(new CompiledRazorAssemblyPart(assembly)); _partManager.ApplicationParts.Add(new CompiledRazorAssemblyPart(assembly));
} }
else if (assembly.FullName.Contains("Web")) else if (assembly.FullName.Contains("Web"))
{ {
_.ApplicationParts.Add(new AssemblyPart(assembly)); _partManager.ApplicationParts.Add(new AssemblyPart(assembly));
} }
} }
}); });
@ -58,7 +76,7 @@ namespace WebfrontCore
}); });
#endif #endif
foreach (var asm in Program.Manager.GetPluginAssemblies()) foreach (var asm in pluginAssemblies())
{ {
mvcBuilder.AddApplicationPart(asm); mvcBuilder.AddApplicationPart(asm);
} }
@ -83,7 +101,7 @@ namespace WebfrontCore
#endif #endif
services.AddSingleton(Program.Manager); services.AddSingleton(Program.Manager);
services.AddSingleton(Program.ApplicationServiceProvider.GetService(typeof(IPluginImporter)) as IPluginImporter); services.AddSingleton(Program.ApplicationServiceProvider.GetService<IConfigurationHandlerFactory>());
} }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.